• 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 '../../../../../base-ui/table/lit-table-column';
17import { BaseElement, element } from '../../../../../base-ui/BaseElement';
18import { LitTable } from '../../../../../base-ui/table/lit-table';
19import { SelectionParam } from '../../../../bean/BoxSelection';
20import {
21  NativeHookCallInfo,
22  NativeHookSampleQueryInfo,
23  NativeHookSamplerInfo,
24  NativeMemory,
25} from '../../../../bean/NativeHook';
26import { Utils } from '../../base/Utils';
27import '../TabPaneFilter';
28import { FilterData, TabPaneFilter } from '../TabPaneFilter';
29import '../../../../../base-ui/slicer/lit-slicer';
30import { procedurePool } from '../../../../database/Procedure';
31import {
32  formatRealDateMs,
33  getTimeString
34} from '../../../../database/logic-worker/ProcedureLogicWorkerCommon';
35import { SpNativeMemoryChart } from '../../../chart/SpNativeMemoryChart';
36import {
37  queryAllHookData,
38  queryNativeHookSnapshotTypes
39} from '../../../../database/sql/NativeHook.sql';
40
41@element('tabpane-native-sample')
42export class TabPaneNMSampleList extends BaseElement {
43  static tblData: LitTable | null | undefined;
44  static sampleTbl: LitTable | null | undefined;
45  static filter: any;
46  static filterSelect: string = '0';
47  static samplerInfoSource: Array<NativeHookSamplerInfo> = [];
48  static types: Array<string> = [];
49  static nativeType: Array<string> = ['All Heap & Anonymous VM', 'All Heap', 'All Anonymous VM'];
50  static tableMarkData: Array<NativeMemory> = [];
51  static selectionParam: SelectionParam | undefined = undefined;
52  static sampleTypes: Array<NativeHookSampleQueryInfo> = [];
53  static sampleTypesList: any[] = [];
54  private currentSelection: SelectionParam | undefined;
55
56  set data(sampleParam: SelectionParam | any) {
57    if (sampleParam === this.currentSelection) {
58      return;
59    }
60    this.currentSelection = sampleParam;
61    TabPaneNMSampleList.serSelection(sampleParam);
62    this.filterAllList();
63  }
64
65  static serSelection(sampleParam: SelectionParam): void {
66    if (this.selectionParam !== sampleParam) {
67      this.clearData();
68      this.selectionParam = sampleParam;
69      this.initTypes(sampleParam.nativeMemoryCurrentIPid);
70    }
71    if (sampleParam.nativeMemory.indexOf(this.nativeType[0]) !== -1) {
72      this.types.push('\'AllocEvent\'');
73      this.types.push('\'MmapEvent\'');
74    } else {
75      if (sampleParam.nativeMemory.indexOf(this.nativeType[1]) !== -1) {
76        this.types.push('\'AllocEvent\'');
77      }
78      if (sampleParam.nativeMemory.indexOf(this.nativeType[2]) !== -1) {
79        this.types.push('\'MmapEvent\'');
80      }
81    }
82  }
83
84  static initTypes(ipid: number): void {
85    queryNativeHookSnapshotTypes(ipid).then((result) => {
86      if (result.length > 0) {
87        this.sampleTypes = result;
88      }
89    });
90  }
91
92  static addSampleData(data: any, ipid: number): void {
93    if (TabPaneNMSampleList.tableMarkData.indexOf(data) !== -1) {
94      return;
95    }
96    TabPaneNMSampleList.tableMarkData.push(data);
97    let rootSample = new NativeHookSamplerInfo();
98    rootSample.snapshot = `Snapshot${this.numberToWord(this.samplerInfoSource.length + 1)}`;
99    rootSample.startTs = data.startTs;
100    rootSample.timestamp =
101      SpNativeMemoryChart.REAL_TIME_DIF === 0 ? getTimeString(data.startTs) :
102        formatRealDateMs(data.startTs + SpNativeMemoryChart.REAL_TIME_DIF);
103    rootSample.eventId = data.eventId;
104    rootSample.threadId = data.threadId;
105    rootSample.threadName = data.threadName;
106    this.queryAllHookInfo(data, rootSample, ipid);
107  }
108
109  static merageSampleData(
110    leftTime: number,
111    startNs: number,
112    rootSample: NativeHookSampleQueryInfo,
113    merageSample: NativeHookSampleQueryInfo
114  ): void {
115    if (merageSample.endTs >= startNs) {
116      rootSample.growth += merageSample.growth;
117    }
118    if (merageSample.startTs > leftTime) {
119      rootSample.existing++;
120      let childSample = new NativeHookSamplerInfo(); //新增最下层的叶子节点
121      childSample.snapshot = `0x${merageSample.addr}`;
122      childSample.eventId = merageSample.eventId;
123      childSample.heapSize = merageSample.growth;
124      childSample.growth = Utils.getByteWithUnit(merageSample.growth);
125      childSample.totalGrowth = childSample.growth;
126      childSample.startTs = merageSample.startTs;
127      childSample.timestamp =
128        SpNativeMemoryChart.REAL_TIME_DIF === 0
129          ? getTimeString(merageSample.startTs)
130          : formatRealDateMs(merageSample.startTs + SpNativeMemoryChart.REAL_TIME_DIF);
131      childSample.threadId = merageSample.threadId;
132      childSample.threadName = merageSample.threadName;
133      (childSample as any).existing = '';
134      rootSample.children.push(childSample);
135    }
136    rootSample.total += merageSample.growth;
137  }
138
139  static queryAllHookInfo(data: any, rootSample: NativeHookSamplerInfo, ipid: number): void {
140    let copyTypes = this.sampleTypes.map((type) => {
141      let copyType = new NativeHookSampleQueryInfo();
142      copyType.eventType = type.eventType;
143      copyType.subType = type.subType;
144      return copyType;
145    });
146    queryAllHookData(data.startTs, ipid).then((nmSamplerHookResult) => {
147      if (nmSamplerHookResult.length > 0) {
148        let nameGroup: any = {};
149        copyTypes.forEach((item) => {
150          nameGroup[item.eventType] = nameGroup[item.eventType] || [];
151          nameGroup[item.eventType].push(item);
152        });
153        let leftTime =
154          TabPaneNMSampleList.tableMarkData.length === 1 ? 0 :
155            TabPaneNMSampleList.tableMarkData[TabPaneNMSampleList.tableMarkData.length - 2].startTs;
156        nmSamplerHookResult.forEach((item) => {
157          item.threadId = rootSample.threadId;
158          item.threadName = rootSample.threadName;
159          if (nameGroup[item.eventType] !== undefined) {
160            if (item.subType === null) {
161              this.merageSampleData(leftTime, data.startTs, nameGroup[item.eventType][0], item);
162            } else {
163              let filter = nameGroup[item.eventType].filter((type: any) => {
164                return type.subType === item.subType;
165              });
166              if (filter.length > 0) {
167                this.merageSampleData(leftTime, data.startTs, filter[0], item);
168              }
169            }
170          }
171        });
172        this.updateSampleTypesList(copyTypes);
173        this.createTree(nameGroup, rootSample);
174        rootSample.tempList = [...rootSample.children];
175        this.samplerInfoSource.push(rootSample);
176        TabPaneNMSampleList.sampleTbl!.recycleDataSource = TabPaneNMSampleList.samplerInfoSource;
177      }
178    });
179  }
180
181  private static updateSampleTypesList(copyTypes: NativeHookSampleQueryInfo[]): void {
182    if (this.sampleTypesList.length > 0) {
183      let sampleTypesListElement = this.sampleTypesList[this.sampleTypesList.length - 1];
184      sampleTypesListElement.forEach((item: any, index: number) => {
185        copyTypes[index].current = copyTypes[index].growth;
186        if (index < copyTypes.length) {
187          copyTypes[index].growth -= item.current;
188          copyTypes[index].total -= item.total;
189        }
190      });
191    } else {
192      copyTypes.forEach((item: any) => {
193        item.current = item.growth;
194      });
195    }
196    this.sampleTypesList.push(copyTypes);
197  }
198
199  static createTree(nameGroup: any, rootSample: NativeHookSamplerInfo): void {
200    Object.keys(nameGroup).forEach((key) => {
201      let parentSample = new NativeHookSamplerInfo();
202      parentSample.snapshot = key;
203      if (nameGroup[key].length > 0) {
204        nameGroup[key].forEach((child: any) => {
205          let childSample = new NativeHookSamplerInfo();
206          childSample.snapshot = child.subType || child.eventType;
207          childSample.heapSize = child.growth;
208          childSample.growth = Utils.getByteWithUnit(child.growth);
209          childSample.total = child.total;
210          childSample.totalGrowth = Utils.getByteWithUnit(child.total);
211          childSample.existing = child.existing;
212          childSample.currentSize = child.current;
213          childSample.current = Utils.getByteWithUnit(child.current);
214          childSample.threadName = rootSample.threadName;
215          childSample.threadId = rootSample.threadId;
216          parentSample.merageObj(childSample);
217          if (childSample.snapshot !== parentSample.snapshot) {
218            //根据名称是否一致来判断是否需要添加子节点
219            childSample.children.push(...child.children);
220            parentSample.children.push(childSample);
221          } else {
222            parentSample.children.push(...child.children);
223          }
224        });
225      }
226      rootSample.merageObj(parentSample);
227      rootSample.children.push(parentSample);
228    });
229  }
230
231  static prepChild(currentSample: NativeHookSamplerInfo, rootSample: NativeHookSamplerInfo) {
232    currentSample.heapSize -= rootSample.heapSize;
233    currentSample.growth = Utils.getByteWithUnit(currentSample.heapSize);
234    let currentMap: any = {};
235    currentSample.children.forEach((currentChild) => {
236      currentMap[currentChild.snapshot] = currentChild;
237    });
238    rootSample.children.forEach((rootChild) => {
239      if (currentMap[rootChild.snapshot] === undefined) {
240        let perpSample = new NativeHookSamplerInfo();
241        perpSample.snapshot = rootChild.snapshot;
242        currentMap[rootChild.snapshot] = perpSample;
243        currentSample.children.push(perpSample);
244      }
245      this.prepChild(currentMap[rootChild.snapshot], rootChild);
246    });
247  }
248
249  static clearData() {
250    this.types = [];
251    this.samplerInfoSource = [];
252    this.tblData!.dataSource = [];
253    this.sampleTbl!.recycleDataSource = [];
254    TabPaneNMSampleList.sampleTbl!.recycleDataSource = [];
255    this.sampleTypesList = [];
256    this.tableMarkData = [];
257    TabPaneNMSampleList.filter!.firstSelect = '0';
258  }
259
260  static numberToWord(num: number) {
261    let word = '';
262    while (num > 0) {
263      let end = num % 26;
264      end = end === 0 ? (end = 26) : end;
265      word = String.fromCharCode(96 + end) + word;
266      num = (num - end) / 26;
267    }
268    return word.toUpperCase();
269  }
270
271  startWorker(args: Map<string, any>, handler: Function) {
272    procedurePool.submitWithName('logic0', 'native-memory-action', args, undefined, (res: any) => {
273      handler(res);
274    });
275  }
276
277  setRightTableData(hookSamplerInfo: NativeHookSamplerInfo) {
278    let nmSamplerArgs = new Map<string, any>();
279    nmSamplerArgs.set('eventId', hookSamplerInfo.eventId);
280    nmSamplerArgs.set('actionType', 'memory-stack');
281    this.startWorker(nmSamplerArgs, (results: any[]) => {
282      let source = [];
283      if (results.length > 0) {
284        let hookCallInfo = new NativeHookCallInfo();
285        hookCallInfo.threadId = hookSamplerInfo.threadId;
286        hookCallInfo.threadName = hookSamplerInfo.threadName;
287        hookCallInfo.title = `${hookSamplerInfo.threadName ?? ''}【${hookSamplerInfo.threadId}】`;
288        hookCallInfo.type = -1;
289        source.push(hookCallInfo);
290        source.push(...results);
291      }
292      // @ts-ignore
293      TabPaneNMSampleList.tblData?.dataSource = source;
294    });
295  }
296
297  initElements(): void {
298    TabPaneNMSampleList.sampleTbl = this.shadowRoot?.querySelector<LitTable>('#tb-native-sample');
299    TabPaneNMSampleList.sampleTbl!.addEventListener('row-click', (evt: any) => {
300      // @ts-ignore
301      this.setRightTableData(evt.detail.data);
302    });
303    TabPaneNMSampleList.tblData = this.shadowRoot?.querySelector<LitTable>('#tb-native-data');
304    TabPaneNMSampleList.filter = this.shadowRoot?.querySelector<TabPaneFilter>('#filter');
305    this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!.setSelectList(TabPaneNMSampleList.nativeType, null);
306    this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!.getFilterData((data: FilterData) => {
307      if (data.firstSelect) {
308        TabPaneNMSampleList.filterSelect = data.firstSelect;
309        this.filterAllList();
310      }
311    });
312    TabPaneNMSampleList.filter!.firstSelect = TabPaneNMSampleList.filterSelect;
313    TabPaneNMSampleList.sampleTbl!.exportTextHandleMap.set('totalGrowth', (value) => {
314      return `${value['total']}`;
315    });
316    TabPaneNMSampleList.sampleTbl!.exportTextHandleMap.set('growth', (value) => {
317      return `${value['heapSize']}`;
318    });
319  }
320
321  connectedCallback() {
322    super.connectedCallback();
323    new ResizeObserver((entries) => {
324      if (this.parentElement?.clientHeight !== 0) {
325        // @ts-ignore
326        TabPaneNMSampleList.sampleTbl?.shadowRoot.querySelector('.table').style.height =
327          this.parentElement!.clientHeight - 10 - 31 + 'px';
328        TabPaneNMSampleList.sampleTbl?.reMeauseHeight();
329        // @ts-ignore
330        TabPaneNMSampleList.tblData?.shadowRoot.querySelector('.table').style.height =
331          this.parentElement!.clientHeight - 10 + 'px';
332        TabPaneNMSampleList.tblData?.reMeauseHeight();
333      }
334    }).observe(this.parentElement!);
335  }
336
337  filterAllList() {
338    TabPaneNMSampleList.samplerInfoSource.forEach((nmRootSample) => {
339      nmRootSample.heapSize = 0;
340      nmRootSample.existing = 0;
341      nmRootSample.total = 0;
342      if (TabPaneNMSampleList.filterSelect === '0') {
343        nmRootSample.children = [...nmRootSample.tempList];
344        nmRootSample.tempList.forEach((parentSample) => {
345          nmRootSample.heapSize += parentSample.heapSize;
346          nmRootSample.existing += parentSample.existing;
347          nmRootSample.total += parentSample.total;
348        });
349        nmRootSample.growth = Utils.getByteWithUnit(nmRootSample.heapSize);
350        nmRootSample.totalGrowth = Utils.getByteWithUnit(nmRootSample.total);
351      } else if (TabPaneNMSampleList.filterSelect === '2') {
352        if (nmRootSample.tempList.length > 1) {
353          nmRootSample.children = [nmRootSample.tempList[1]];
354          nmRootSample.heapSize += nmRootSample.tempList[1].heapSize;
355          nmRootSample.existing += nmRootSample.tempList[1].existing;
356          nmRootSample.growth = Utils.getByteWithUnit(nmRootSample.heapSize);
357          nmRootSample.total += nmRootSample.tempList[1].total;
358          nmRootSample.totalGrowth = Utils.getByteWithUnit(nmRootSample.total);
359        } else {
360          nmRootSample.children = [];
361          nmRootSample.growth = '';
362          nmRootSample.totalGrowth = '';
363        }
364      } else {
365        if (nmRootSample.tempList.length > 0) {
366          nmRootSample.children = [nmRootSample.tempList[0]];
367          nmRootSample.heapSize += nmRootSample.tempList[0].heapSize;
368          nmRootSample.existing += nmRootSample.tempList[0].existing;
369          nmRootSample.growth = Utils.getByteWithUnit(nmRootSample.heapSize);
370          nmRootSample.total += nmRootSample.tempList[0].total;
371          nmRootSample.totalGrowth = Utils.getByteWithUnit(nmRootSample.total);
372        } else {
373          nmRootSample.children = [];
374          nmRootSample.growth = '';
375          nmRootSample.totalGrowth = '';
376        }
377      }
378    });
379    TabPaneNMSampleList.sampleTbl!.recycleDataSource = TabPaneNMSampleList.samplerInfoSource;
380  }
381
382  initHtml(): string {
383    return `
384        <style>
385        .nm-sample-tbl {
386            height: auto;
387        }
388        :host{
389            padding: 10px 10px 0 10px;
390            display: flex;
391            flex-direction: column;
392        }
393        </style>
394        <lit-slicer style="width:100%">
395        <div class="nm-sample-content" style="width: 65%">
396            <lit-table id="tb-native-sample" class="nm-sample-tbl" tree>
397                <lit-table-column class="nm-sample-column" width="25%" title="Snapshot" data-index="snapshot" key="snapshot"  align="flex-start"retract>
398                </lit-table-column>
399                <lit-table-column class="nm-sample-column" width="1fr" title="Timestamp" data-index="timestamp" key="timestamp"  align="flex-start"  >
400                </lit-table-column>
401                <lit-table-column class="nm-sample-column" width="1fr" title="Net Growth" data-index="growth" key="growth"  align="flex-start"  >
402                </lit-table-column>
403                <lit-table-column class="nm-sample-column" width="1fr" title="Total Growth" data-index="totalGrowth" key="totalGrowth"  align="flex-start"  >
404                </lit-table-column>
405                <lit-table-column class="nm-sample-column" width="1fr" title="# Existing" data-index="existing" key="existing"  align="flex-start"  >
406                </lit-table-column>
407            </lit-table>
408            <tab-pane-filter id="filter" first></tab-pane-filter>
409        </div>
410        <lit-slicer-track ></lit-slicer-track>
411        <lit-table id="tb-native-data" no-head style="height: auto;border-left: 1px solid var(--dark-border1,#e2e2e2)" hideDownload>
412            <lit-table-column class="nm-sample-column" width="80px" title="" data-index="type" key="type"  align="flex-start" >
413                <template>
414                    <div v-if=" type === -1 ">Thread:</div>
415                    <img src="img/library.png" size="20" v-if=" type === 1 ">
416                    <img src="img/function.png" size="20" v-if=" type === 0 ">
417                </template>
418            </lit-table-column>
419            <lit-table-column class="nm-sample-column" width="1fr" title="" data-index="title" key="title"  align="flex-start">
420            </lit-table-column>
421        </lit-table>
422        </lit-slicer>
423        `;
424  }
425}
426