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