• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { BaseElement, element } from '../../../../../base-ui/BaseElement';
17import { type LitTable } from '../../../../../base-ui/table/lit-table';
18import { type LitPageTable } from '../../../../../base-ui/table/LitPageTable';
19import '../../../../../base-ui/table/LitPageTable';
20import '../../../../../base-ui/slicer/lit-slicer';
21import { type SelectionParam } from '../../../../bean/BoxSelection';
22import { type NativeMemory, NativeHookCallInfo } from '../../../../bean/NativeHook';
23import '../TabPaneFilter';
24import { FilterData, TabPaneFilter } from '../TabPaneFilter';
25import { TabPaneNMSampleList } from './TabPaneNMSampleList';
26import { type LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar';
27import { procedurePool } from '../../../../database/Procedure';
28import {
29  formatRealDateMs,
30  getByteWithUnit,
31  getTimeString,
32} from '../../../../database/logic-worker/ProcedureLogicWorkerCommon';
33import { SpNativeMemoryChart } from '../../../chart/SpNativeMemoryChart';
34import { Utils } from '../../base/Utils';
35import { TabPaneNMemoryHtml } from './TabPaneNMemory.html';
36import { SpSystemTrace } from '../../../SpSystemTrace';
37import { TabPaneFlag } from '../../timer-shaft/TabPaneFlag';
38
39@element('tabpane-native-memory')
40export class TabPaneNMemory extends BaseElement {
41  private defaultNativeTypes = ['All Heap & Anonymous VM', 'All Heap', 'All Anonymous VM'];
42  private memoryTbl: LitPageTable | null | undefined;
43  private filter: TabPaneFilter | null | undefined;
44  private tblData: LitTable | null | undefined;
45  private progressEL: LitProgressBar | null | undefined;
46  private nmMemoryLoadingList: number[] = [];
47  private loadingPage: unknown;
48  private memorySource: Array<unknown> = [];
49  private nativeType: Array<string> = [...this.defaultNativeTypes];
50  private statsticsSelection: Array<unknown> = [];
51  private filterAllocationType: string = '0';
52  private filterNativeType: string = '0';
53  private filterResponseType: number = -1;
54  private filterResponseSelect: string = '0';
55  private currentSelection: SelectionParam | undefined;
56  private rowSelectData: unknown = undefined;
57  private sortColumn: string = '';
58  private sortType: number = 0;
59  private responseTypes: unknown[] = [];
60  private eventTypes: string[] = [];
61  private systemTrace: SpSystemTrace | undefined | null;
62
63  set data(memoryParam: SelectionParam | unknown) {
64    if (memoryParam === this.currentSelection) {
65      return;
66    } //@ts-ignore
67    this.currentSelection = memoryParam;
68    this.queryData(memoryParam);
69  }
70
71  queryData(memoryParam: SelectionParam | unknown, resetFilter: boolean = true): void {
72    this.eventTypes = []; //@ts-ignore
73    if (memoryParam.nativeMemory.indexOf(this.defaultNativeTypes[0]) !== -1) {
74      this.eventTypes.push("'AllocEvent'");
75      this.eventTypes.push("'MmapEvent'");
76    } else {
77      //@ts-ignore
78      if (memoryParam.nativeMemory.indexOf(this.defaultNativeTypes[1]) !== -1) {
79        this.eventTypes.push("'AllocEvent'");
80      } //@ts-ignore
81      if (memoryParam.nativeMemory.indexOf(this.defaultNativeTypes[2]) !== -1) {
82        this.eventTypes.push("'MmapEvent'");
83      }
84    } //@ts-ignore
85    TabPaneNMSampleList.serSelection(memoryParam);
86    if (this.memoryTbl) {
87      // @ts-ignore
88      this.memoryTbl.shadowRoot.querySelector('.table').style.height = `${this.parentElement.clientHeight - 20 - 31}px`;
89      // @ts-ignore
90      this.tblData.shadowRoot.querySelector('.table').style.height = `${this.parentElement.clientHeight - 20 - 31}px`;
91      // @ts-ignore
92      this.tblData.recycleDataSource = [];
93      // @ts-ignore
94      this.memoryTbl.recycleDataSource = [];
95    }
96    if (resetFilter) {
97      this.resetFilter();
98      this.setNmMemoryLoading(true);
99      this.initFilterTypes(() => {
100        this.filterSetSelectList(this.filter!, 0);
101        this.getDataByNativeMemoryWorker(memoryParam, resetFilter);
102      });
103    } else {
104      this.getDataByNativeMemoryWorker(memoryParam, resetFilter);
105    }
106  }
107
108  getDataByNativeMemoryWorker(val: SelectionParam | unknown, refresh = false): void {
109    let args = new Map<string, unknown>();
110    args.set('filterAllocType', this.filterAllocationType);
111    args.set('filterEventType', this.filterNativeType);
112    args.set('filterResponseType', this.filterResponseType); //@ts-ignore
113    args.set('leftNs', val.leftNs); //@ts-ignore
114    args.set('rightNs', val.rightNs);
115    args.set('types', this.eventTypes);
116    args.set('refresh', refresh);
117    let selections: Array<unknown> = [];
118    if (this.statsticsSelection.length > 0) {
119      this.statsticsSelection.map((memory) => {
120        selections.push({
121          //@ts-ignore
122          memoryTap: memory.memoryTap, //@ts-ignore
123          max: memory.max,
124        });
125      });
126    }
127    args.set('statisticsSelection', selections);
128    args.set('sortColumn', this.sortColumn);
129    args.set('sortType', this.sortType);
130    this.memorySource = [];
131    if (this.memoryTbl!.recycleDs.length > 1_0000) {
132      this.memoryTbl!.recycleDataSource = [];
133    }
134    this.startNmMemoryWorker('native-memory-queryNativeHookEvent', args, (results: unknown[]) => {
135      this.tblData!.recycleDataSource = [];
136      this.setNmMemoryLoading(false);
137      if (results.length > 0) {
138        this.memorySource = results;
139        this.memoryTbl!.recycleDataSource = this.memorySource;
140      } else {
141        this.memorySource = [];
142        this.memoryTbl!.recycleDataSource = [];
143      }
144    });
145  }
146
147  startNmMemoryWorker(type: string, args: unknown, handler: Function): void {
148    this.setNmMemoryLoading(true);
149    procedurePool.submitWithName('logic0', type, args, undefined, (res: unknown) => {
150      //@ts-ignore
151      if (Array.isArray(res) || (res.tag === 'end' && res.index === 0)) {
152        //@ts-ignore
153        handler(res.data ? res.data : res);
154        this.setNmMemoryLoading(false);
155      } else {
156        //@ts-ignore
157        this.memorySource.push(res.data); //@ts-ignore
158        if (res.tag === 'end') {
159          handler(this.memorySource);
160          this.setNmMemoryLoading(false);
161        }
162      }
163    });
164  }
165
166  setNmMemoryLoading(loading: boolean): void {
167    if (loading) {
168      this.nmMemoryLoadingList.push(1);
169      this.progressEL!.loading = true; //@ts-ignore
170      this.loadingPage.style.visibility = 'visible';
171    } else {
172      this.nmMemoryLoadingList.splice(0, 1);
173      if (this.nmMemoryLoadingList.length === 0) {
174        this.progressEL!.loading = false; //@ts-ignore
175        this.loadingPage.style.visibility = 'hidden';
176      }
177    }
178  }
179
180  fromStastics(val: SelectionParam | unknown): void {
181    let nmFilterEl = this.shadowRoot?.querySelector<TabPaneFilter>('#filter');
182    if (this.currentSelection !== val) {
183      this.resetFilter();
184      this.initFilterTypes(() => {
185        //@ts-ignore
186        this.currentSelection = val;
187        let typeIndexOf = this.setFilterNativeTypeSelection(this.currentSelection!);
188        this.filterSetSelectList(nmFilterEl!, typeIndexOf);
189        this.filterNativeType = `${typeIndexOf}`;
190        this.rowSelectData = undefined;
191        this.queryData(val, false);
192      });
193    } else {
194      //@ts-ignore
195      let typeIndexOf = this.setFilterNativeTypeSelection(val);
196      this.tblData!.recycleDataSource = [];
197      this.rowSelectData = undefined;
198      this.filterSetSelectList(nmFilterEl!, typeIndexOf);
199      this.filterNativeType = `${typeIndexOf}`;
200      //直接将当前数据过滤即可
201      this.getDataByNativeMemoryWorker(val);
202    }
203  }
204
205  private setFilterNativeTypeSelection(val: SelectionParam): number {
206    let typeIndexOf = -1;
207    if (val.statisticsSelectData) {
208      // @ts-ignore
209      typeIndexOf = this.nativeType.indexOf(val.statisticsSelectData.memoryTap);
210      if (this.statsticsSelection.indexOf(val.statisticsSelectData) === -1 && typeIndexOf === -1) {
211        this.statsticsSelection.push(val.statisticsSelectData);
212        // @ts-ignore
213        this.nativeType.push(val.statisticsSelectData.memoryTap);
214        typeIndexOf = this.nativeType.length - 1;
215      } else {
216        // @ts-ignore
217        let index = this.statsticsSelection.findIndex((mt) => mt.memoryTap === val.statisticsSelectData.memoryTap);
218        if (index !== -1) {
219          this.statsticsSelection[index] = val.statisticsSelectData;
220        }
221      }
222    }
223    return typeIndexOf;
224  }
225
226  private filterSetSelectList(nmFilterEl: TabPaneFilter, typeIndexOf: number): void {
227    nmFilterEl!.setSelectList(
228      null,
229      this.nativeType,
230      'Allocation Lifespan',
231      'Allocation Type',
232      this.responseTypes.map((item: unknown) => {
233        //@ts-ignore
234        return item.value;
235      })
236    );
237    nmFilterEl!.secondSelect = `${typeIndexOf}`;
238    nmFilterEl!.thirdSelect = this.filterResponseSelect;
239  }
240
241  initFilterTypes(initCallback?: () => void): void {
242    this.nativeType = [...this.defaultNativeTypes];
243    this.statsticsSelection = [];
244    if (this.currentSelection) {
245      this.setFilterNativeTypeSelection(this.currentSelection);
246    }
247    procedurePool.submitWithName('logic0', 'native-memory-get-responseType', {}, undefined, (res: unknown) => {
248      this.filter!.setSelectList(
249        null,
250        this.nativeType,
251        'Allocation Lifespan',
252        'Allocation Type', //@ts-ignore
253        res.map((item: unknown) => {
254          //@ts-ignore
255          return item.value;
256        })
257      );
258      this.filter!.setFilterModuleSelect('#first-select', 'width', '150px');
259      this.filter!.setFilterModuleSelect('#second-select', 'width', '150px');
260      this.filter!.setFilterModuleSelect('#third-select', 'width', '150px'); //@ts-ignore
261      this.responseTypes = res;
262      this.rowSelectData = undefined;
263      if (initCallback) {
264        initCallback();
265      }
266    });
267  }
268
269  resetFilter(): void {
270    this.filter!.firstSelect = '0';
271    this.filter!.secondSelect = '0';
272    this.filter!.thirdSelect = '0';
273    this.filterResponseSelect = '0';
274    this.filterAllocationType = '0';
275    this.filterNativeType = '0';
276    this.filterResponseType = -1;
277  }
278
279  initElements(): void {
280    this.systemTrace = document
281      .querySelector('body > sp-application')
282      ?.shadowRoot!.querySelector<SpSystemTrace>('#sp-system-trace');
283    this.loadingPage = this.shadowRoot?.querySelector('.loading');
284    this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar;
285    this.memoryTbl = this.shadowRoot?.querySelector<LitPageTable>('#tb-native-memory');
286    this.tblData = this.shadowRoot?.querySelector<LitTable>('#tb-native-data');
287    this.filter = this.shadowRoot?.querySelector<TabPaneFilter>('#filter');
288    this.memoryTbl!.addEventListener('row-click', (e) => {
289      // @ts-ignore
290      let data = e.detail.data as NativeMemory;
291      data.isSelected = true;
292      this.rowSelectData = data;
293      this.setRightTableData(data);
294      // @ts-ignore
295      if ((e.detail as unknown).callBack) {
296        // @ts-ignore
297        (e.detail as unknown).callBack(true);
298      }
299      let flagList = this.systemTrace?.timerShaftEL!.sportRuler?.flagList || [];
300      flagList.forEach((it, i) => {
301        if (it.type === 'triangle') {
302          flagList.splice(i, 1);
303        }
304      });
305
306      for (let i = 0; i < flagList!.length; i++) {
307        if (flagList[i].time === data.startTs) {
308          flagList[i].type = 'triangle';
309          flagList[i].selected = true;
310        } else {
311          flagList[i].type = '';
312          flagList[i].selected = false;
313        }
314      }
315      document.dispatchEvent(
316        new CustomEvent('triangle-flag', {
317          detail: { time: [data.startTs], type: 'triangle' },
318        })
319      );
320    });
321    this.memoryTbl!.addEventListener('column-click', (evt: unknown) => {
322      //@ts-ignore
323      this.sortColumn = evt.detail.key; //@ts-ignore
324      this.sortType = evt.detail.sort;
325      this.getDataByNativeMemoryWorker(this.currentSelection);
326    });
327    this.setItemTextHandleMapByMemoryTbl();
328    this.memoryTbl!.exportTextHandleMap.set('heapSize', (value) => {
329      // @ts-ignore
330      return `${value['heapSize']}`;
331    });
332    this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!.getFilterData((data: FilterData) => {
333      if (data.mark) {
334        this.getFilterDataByMark();
335      } else {
336        this.filterAllocationType = data.firstSelect || '0';
337        this.filterNativeType = data.secondSelect || '0';
338        this.filterResponseSelect = data.thirdSelect || '0';
339        let thirdIndex = parseInt(data.thirdSelect || '0');
340        if (this.responseTypes.length > thirdIndex) {
341          this.filterResponseType = //@ts-ignore
342            this.responseTypes[thirdIndex].key === undefined ? -1 : this.responseTypes[thirdIndex].key;
343        }
344        this.getDataByNativeMemoryWorker(this.currentSelection);
345      }
346    });
347    this.filter!.firstSelect = '1';
348  }
349
350  private setItemTextHandleMapByMemoryTbl(): void {
351    this.memoryTbl!.itemTextHandleMap.set('startTs', (startTs) => {
352      return SpNativeMemoryChart.REAL_TIME_DIF === 0 // @ts-ignore
353        ? getTimeString(startTs) // @ts-ignore
354        : formatRealDateMs(startTs + SpNativeMemoryChart.REAL_TIME_DIF);
355    });
356    this.memoryTbl!.itemTextHandleMap.set('endTs', (endTs) => {
357      // @ts-ignore
358      return endTs > this.currentSelection!.leftNs && // @ts-ignore
359        endTs <= this.currentSelection!.rightNs &&
360        endTs !== 0 &&
361        endTs !== null
362        ? 'Freed'
363        : 'Existing';
364    });
365    this.memoryTbl!.itemTextHandleMap.set('heapSize', (heapSize) => {
366      // @ts-ignore
367      return getByteWithUnit(heapSize);
368    });
369  }
370
371  private getFilterDataByMark(): void {
372    document.dispatchEvent(
373      new CustomEvent('triangle-flag', {
374        detail: {
375          time: '',
376          type: 'square',
377          timeCallback: (timeArr: number[]): void => {
378            if (timeArr && timeArr.length > 0) {
379              let checkTs = timeArr[0];
380              let minTs = 0;
381              let minItem: unknown = undefined;
382              let filterTemp = this.memorySource.filter((tempItem) => {
383                //@ts-ignore
384                if (minTs === 0 || (tempItem.startTs - checkTs !== 0 && Math.abs(tempItem.startTs - checkTs) < minTs)) {
385                  //@ts-ignore
386                  minTs = Math.abs(tempItem.startTs - checkTs);
387                  minItem = tempItem;
388                } //@ts-ignore
389                return tempItem.startTs === checkTs;
390              });
391              if (filterTemp.length > 0) {
392                //@ts-ignore
393                filterTemp[0].isSelected = true;
394              } else {
395                if (minItem) {
396                  filterTemp.push(minItem); //@ts-ignore
397                  minItem.isSelected = true;
398                }
399              }
400              if (filterTemp.length > 0) {
401                this.rowSelectData = filterTemp[0];
402                let args = new Map<string, unknown>(); //@ts-ignore
403                args.set('startTs', this.rowSelectData.startTs);
404                args.set('actionType', 'native-memory-state-change');
405                this.startNmMemoryWorker('native-memory-action', args, (results: unknown[]) => {});
406                TabPaneNMSampleList.addSampleData(this.rowSelectData, this.currentSelection!.nativeMemoryCurrentIPid);
407                this.memoryTbl!.scrollToData(this.rowSelectData);
408              }
409            }
410          },
411        },
412      })
413    );
414  }
415
416  connectedCallback(): void {
417    super.connectedCallback();
418    new ResizeObserver((entries) => {
419      if (this.parentElement?.clientHeight !== 0) {
420        if (this.memoryTbl) {
421          // @ts-ignore
422          this.memoryTbl.shadowRoot.querySelector('.table').style.height = `${
423            this.parentElement!.clientHeight - 10 - 31
424          }px`;
425        }
426        this.memoryTbl?.reMeauseHeight();
427        if (this.tblData) {
428          // @ts-ignore
429          this.tblData.shadowRoot.querySelector('.table').style.height = `${
430            this.parentElement!.clientHeight - 10 - 31
431          }px`;
432        }
433        this.tblData?.reMeauseHeight(); //@ts-ignore
434        this.loadingPage.style.height = `${this.parentElement!.clientHeight - 24}px`;
435      }
436    }).observe(this.parentElement!);
437  }
438
439  setRightTableData(nativeMemoryHook: NativeMemory): void {
440    let args = new Map<string, unknown>();
441    args.set('eventId', nativeMemoryHook.eventId);
442    args.set('actionType', 'memory-stack');
443    this.startNmMemoryWorker('native-memory-action', args, (results: unknown[]) => {
444      let thread = new NativeHookCallInfo();
445      thread.threadId = nativeMemoryHook.threadId;
446      thread.threadName = Utils.getInstance().getThreadMap().get(thread.threadId) || 'Thread';
447      thread.symbol = `${nativeMemoryHook.threadName ?? ''}【${nativeMemoryHook.threadId}】`;
448      thread.type = -1;
449      let currentSource = [];
450      currentSource.push(thread);
451      currentSource.push(...results);
452      this.progressEL!.loading = false;
453      this.tblData!.dataSource = currentSource;
454    });
455  }
456
457  initHtml(): string {
458    return TabPaneNMemoryHtml;
459  }
460}
461