• 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        let isTwoArray: boolean = results.some(item => {
139          return Array.isArray(item);
140        });
141        let dataList: unknown = [];
142        if (isTwoArray) {
143          results.forEach(v => {
144            // @ts-ignore
145            dataList = dataList.concat(v);
146          });
147        } else {
148          dataList = results;
149        }
150        // @ts-ignore
151        dataList.forEach((item) => {
152          //@ts-ignore
153          let tmpNumber = item.addr.split('x');
154          //@ts-ignore
155          item.addr = '0x' + Number(tmpNumber[1]).toString(16);
156        });
157        this.memorySource = results;
158        this.memoryTbl!.recycleDataSource = this.memorySource;
159      } else {
160        this.memorySource = [];
161        this.memoryTbl!.recycleDataSource = [];
162      }
163    });
164  }
165
166  startNmMemoryWorker(type: string, args: unknown, handler: Function): void {
167    this.setNmMemoryLoading(true);
168    procedurePool.submitWithName('logic0', type, args, undefined, (res: unknown) => {
169      //@ts-ignore
170      if (Array.isArray(res) || (res.tag === 'end' && res.index === 0)) {
171        //@ts-ignore
172        handler(res.data ? res.data : res);
173        this.setNmMemoryLoading(false);
174      } else {
175        //@ts-ignore
176        this.memorySource.push(res.data); //@ts-ignore
177        if (res.tag === 'end') {
178          handler(this.memorySource);
179          this.setNmMemoryLoading(false);
180        }
181      }
182    });
183  }
184
185  setNmMemoryLoading(loading: boolean): void {
186    if (loading) {
187      this.nmMemoryLoadingList.push(1);
188      this.progressEL!.loading = true; //@ts-ignore
189      this.loadingPage.style.visibility = 'visible';
190    } else {
191      this.nmMemoryLoadingList.splice(0, 1);
192      if (this.nmMemoryLoadingList.length === 0) {
193        this.progressEL!.loading = false; //@ts-ignore
194        this.loadingPage.style.visibility = 'hidden';
195      }
196    }
197  }
198
199  fromStastics(val: SelectionParam | unknown): void {
200    let nmFilterEl = this.shadowRoot?.querySelector<TabPaneFilter>('#filter');
201    if (this.currentSelection !== val) {
202      this.resetFilter();
203      this.initFilterTypes(() => {
204        //@ts-ignore
205        this.currentSelection = val;
206        let typeIndexOf = this.setFilterNativeTypeSelection(this.currentSelection!);
207        this.filterSetSelectList(nmFilterEl!, typeIndexOf);
208        this.filterNativeType = `${typeIndexOf}`;
209        this.rowSelectData = undefined;
210        this.queryData(val, true);
211        this.fromStastics(val);
212      });
213    } else {
214      //@ts-ignore
215      let typeIndexOf = this.setFilterNativeTypeSelection(val);
216      this.tblData!.recycleDataSource = [];
217      this.rowSelectData = undefined;
218      this.filterSetSelectList(nmFilterEl!, typeIndexOf);
219      this.filterNativeType = `${typeIndexOf}`;
220      //直接将当前数据过滤即可
221      this.getDataByNativeMemoryWorker(val);
222    }
223  }
224
225  private setFilterNativeTypeSelection(val: SelectionParam): number {
226    let typeIndexOf = -1;
227    if (val.statisticsSelectData) {
228      // @ts-ignore
229      typeIndexOf = this.nativeType.indexOf(val.statisticsSelectData.memoryTap);
230      if (this.statsticsSelection.indexOf(val.statisticsSelectData) === -1 && typeIndexOf === -1) {
231        this.statsticsSelection.push(val.statisticsSelectData);
232        // @ts-ignore
233        this.nativeType.push(val.statisticsSelectData.memoryTap);
234        typeIndexOf = this.nativeType.length - 1;
235      } else {
236        // @ts-ignore
237        let index = this.statsticsSelection.findIndex((mt) => mt.memoryTap === val.statisticsSelectData.memoryTap);
238        if (index !== -1) {
239          this.statsticsSelection[index] = val.statisticsSelectData;
240        }
241      }
242    }
243    return typeIndexOf;
244  }
245
246  private filterSetSelectList(nmFilterEl: TabPaneFilter, typeIndexOf: number): void {
247    nmFilterEl!.setSelectList(
248      null,
249      this.nativeType,
250      'Allocation Lifespan',
251      'Allocation Type',
252      this.responseTypes.map((item: unknown) => {
253        //@ts-ignore
254        return item.value;
255      })
256    );
257    nmFilterEl!.secondSelect = `${typeIndexOf}`;
258    nmFilterEl!.thirdSelect = this.filterResponseSelect;
259  }
260
261  initFilterTypes(initCallback?: () => void): void {
262    this.nativeType = [...this.defaultNativeTypes];
263    this.statsticsSelection = [];
264    if (this.currentSelection) {
265      this.setFilterNativeTypeSelection(this.currentSelection);
266    }
267    procedurePool.submitWithName('logic0', 'native-memory-get-responseType', {}, undefined, (res: unknown) => {
268      this.filter!.setSelectList(
269        null,
270        this.nativeType,
271        'Allocation Lifespan',
272        'Allocation Type', //@ts-ignore
273        res.map((item: unknown) => {
274          //@ts-ignore
275          return item.value;
276        })
277      );
278      this.filter!.setFilterModuleSelect('#first-select', 'width', '150px');
279      this.filter!.setFilterModuleSelect('#second-select', 'width', '150px');
280      this.filter!.setFilterModuleSelect('#third-select', 'width', '150px'); //@ts-ignore
281      this.responseTypes = res;
282      this.rowSelectData = undefined;
283      if (initCallback) {
284        initCallback();
285      }
286    });
287  }
288
289  resetFilter(): void {
290    this.filter!.firstSelect = '0';
291    this.filter!.secondSelect = '0';
292    this.filter!.thirdSelect = '0';
293    this.filterResponseSelect = '0';
294    this.filterAllocationType = '0';
295    this.filterNativeType = '0';
296    this.filterResponseType = -1;
297  }
298
299  initElements(): void {
300    this.systemTrace = document
301      .querySelector('body > sp-application')
302      ?.shadowRoot!.querySelector<SpSystemTrace>('#sp-system-trace');
303    this.loadingPage = this.shadowRoot?.querySelector('.loading');
304    this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar;
305    this.memoryTbl = this.shadowRoot?.querySelector<LitPageTable>('#tb-native-memory');
306    this.tblData = this.shadowRoot?.querySelector<LitTable>('#tb-native-data');
307    this.filter = this.shadowRoot?.querySelector<TabPaneFilter>('#filter');
308    this.memoryTbl!.addEventListener('row-click', (e) => {
309      // @ts-ignore
310      let data = e.detail.data as NativeMemory;
311      data.isSelected = true;
312      this.rowSelectData = data;
313      this.setRightTableData(data);
314      // @ts-ignore
315      if ((e.detail as unknown).callBack) {
316        // @ts-ignore
317        (e.detail as unknown).callBack(true);
318      }
319      let flagList = this.systemTrace?.timerShaftEL!.sportRuler?.flagList || [];
320      flagList.forEach((it, i) => {
321        if (it.type === 'triangle') {
322          flagList.splice(i, 1);
323        }
324      });
325
326      for (let i = 0; i < flagList!.length; i++) {
327        if (flagList[i].time === data.startTs) {
328          flagList[i].type = 'triangle';
329          flagList[i].selected = true;
330        } else {
331          flagList[i].type = '';
332          flagList[i].selected = false;
333        }
334      }
335      document.dispatchEvent(
336        new CustomEvent('triangle-flag', {
337          detail: { time: [data.startTs], type: 'triangle' },
338        })
339      );
340    });
341    this.memoryTbl!.addEventListener('column-click', (evt: unknown) => {
342      //@ts-ignore
343      this.sortColumn = evt.detail.key; //@ts-ignore
344      this.sortType = evt.detail.sort;
345      this.getDataByNativeMemoryWorker(this.currentSelection);
346    });
347    this.setItemTextHandleMapByMemoryTbl();
348    this.memoryTbl!.exportTextHandleMap.set('heapSize', (value) => {
349      // @ts-ignore
350      return `${value['heapSize']}`;
351    });
352    this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!.getFilterData((data: FilterData) => {
353      if (data.mark) {
354        this.getFilterDataByMark();
355      } else {
356        this.filterAllocationType = data.firstSelect || '0';
357        this.filterNativeType = data.secondSelect || '0';
358        this.filterResponseSelect = data.thirdSelect || '0';
359        let thirdIndex = parseInt(data.thirdSelect || '0');
360        if (this.responseTypes.length > thirdIndex) {
361          this.filterResponseType = //@ts-ignore
362            this.responseTypes[thirdIndex].key === undefined ? -1 : this.responseTypes[thirdIndex].key;
363        }
364        this.getDataByNativeMemoryWorker(this.currentSelection);
365      }
366    });
367    this.filter!.firstSelect = '1';
368  }
369
370  private setItemTextHandleMapByMemoryTbl(): void {
371    this.memoryTbl!.itemTextHandleMap.set('startTs', (startTs) => {
372      return SpNativeMemoryChart.REAL_TIME_DIF === 0 // @ts-ignore
373        ? getTimeString(startTs) // @ts-ignore
374        : formatRealDateMs(startTs + SpNativeMemoryChart.REAL_TIME_DIF);
375    });
376    this.memoryTbl!.itemTextHandleMap.set('endTs', (endTs) => {
377      // @ts-ignore
378      return endTs > this.currentSelection!.leftNs && // @ts-ignore
379        endTs <= this.currentSelection!.rightNs &&
380        endTs !== 0 &&
381        endTs !== null
382        ? 'Freed'
383        : 'Existing';
384    });
385    this.memoryTbl!.itemTextHandleMap.set('heapSize', (heapSize) => {
386      // @ts-ignore
387      return getByteWithUnit(heapSize);
388    });
389  }
390
391  private getFilterDataByMark(): void {
392    document.dispatchEvent(
393      new CustomEvent('triangle-flag', {
394        detail: {
395          time: '',
396          type: 'square',
397          timeCallback: (timeArr: number[]): void => {
398            if (timeArr && timeArr.length > 0) {
399              let checkTs = timeArr[0];
400              let minTs = 0;
401              let minItem: unknown = undefined;
402              let filterTemp = this.memorySource.filter((tempItem) => {
403                //@ts-ignore
404                if (minTs === 0 || (tempItem.startTs - checkTs !== 0 && Math.abs(tempItem.startTs - checkTs) < minTs)) {
405                  //@ts-ignore
406                  minTs = Math.abs(tempItem.startTs - checkTs);
407                  minItem = tempItem;
408                } //@ts-ignore
409                return tempItem.startTs === checkTs;
410              });
411              if (filterTemp.length > 0) {
412                //@ts-ignore
413                filterTemp[0].isSelected = true;
414              } else {
415                if (minItem) {
416                  filterTemp.push(minItem); //@ts-ignore
417                  minItem.isSelected = true;
418                }
419              }
420              if (filterTemp.length > 0) {
421                this.rowSelectData = filterTemp[0];
422                let args = new Map<string, unknown>(); //@ts-ignore
423                args.set('startTs', this.rowSelectData.startTs);
424                args.set('actionType', 'native-memory-state-change');
425                this.startNmMemoryWorker('native-memory-action', args, (results: unknown[]) => { });
426                TabPaneNMSampleList.addSampleData(this.rowSelectData, this.currentSelection!.nativeMemoryCurrentIPid);
427                this.memoryTbl!.scrollToData(this.rowSelectData);
428              }
429            }
430          },
431        },
432      })
433    );
434  }
435
436  connectedCallback(): void {
437    super.connectedCallback();
438    new ResizeObserver((entries) => {
439      if (this.parentElement?.clientHeight !== 0) {
440        if (this.memoryTbl) {
441          // @ts-ignore
442          this.memoryTbl.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - 10 - 31
443            }px`;
444        }
445        this.memoryTbl?.reMeauseHeight();
446        if (this.tblData) {
447          // @ts-ignore
448          this.tblData.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - 10 - 31
449            }px`;
450        }
451        this.tblData?.reMeauseHeight(); //@ts-ignore
452        this.loadingPage.style.height = `${this.parentElement!.clientHeight - 24}px`;
453      }
454    }).observe(this.parentElement!);
455  }
456
457  setRightTableData(nativeMemoryHook: NativeMemory): void {
458    let args = new Map<string, unknown>();
459    args.set('eventId', nativeMemoryHook.eventId);
460    args.set('actionType', 'memory-stack');
461    this.startNmMemoryWorker('native-memory-action', args, (results: unknown[]) => {
462      let thread = new NativeHookCallInfo();
463      thread.threadId = nativeMemoryHook.threadId;
464      thread.threadName = Utils.getInstance().getThreadMap().get(thread.threadId) || 'Thread';
465      thread.symbol = `${nativeMemoryHook.threadName ?? ''}【${nativeMemoryHook.threadId}】`;
466      thread.type = -1;
467      let currentSource = [];
468      currentSource.push(thread);
469      currentSource.push(...results);
470      this.progressEL!.loading = false;
471      this.tblData!.dataSource = currentSource;
472    });
473  }
474
475  initHtml(): string {
476    return TabPaneNMemoryHtml;
477  }
478}
479