• 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.js';
17import { LitTable } from '../../../../../base-ui/table/lit-table.js';
18import { SelectionParam } from '../../../../bean/BoxSelection.js';
19import { query, queryNativeHookEventTid } from '../../../../database/SqlLite.js';
20import { NativeHookCallInfo, NativeHookStatistics } from '../../../../bean/NativeHook.js';
21import '../TabPaneFilter.js';
22import { FilterData, TabPaneFilter } from '../TabPaneFilter.js';
23import '../../../chart/FrameChart.js';
24import '../../../../../base-ui/slicer/lit-slicer.js';
25import { FrameChart } from '../../../chart/FrameChart.js';
26import { ChartMode } from '../../../../bean/FrameChartStruct.js';
27import { LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar.js';
28import { procedurePool } from '../../../../database/Procedure.js';
29
30@element('tabpane-native-callinfo')
31export class TabPaneNMCallInfo extends BaseElement {
32  private callInfoTbl: LitTable | null | undefined;
33  private tblData: LitTable | null | undefined;
34  private progressEL: LitProgressBar | null | undefined;
35  private loadingList: number[] = [];
36  private callInfoLoadingPage: any;
37  private callInfoSource: Array<NativeHookCallInfo> = [];
38  private rightSource: Array<NativeHookCallInfo> = [];
39  private queryResult: Array<NativeHookStatistics> = [];
40  private native_type: Array<string> = ['All Heap & Anonymous VM', 'All Heap', 'All Anonymous VM'];
41  private filterAllocationType: string = '0';
42  private filterNativeType: string = '0';
43  private currentSelection: SelectionParam | undefined;
44  private frameChart: FrameChart | null | undefined;
45  private isChartShow: boolean = false;
46  private sortColumn: string = '';
47  private sortType: number = 0;
48
49  set data(callInfoParam: SelectionParam | any) {
50    if (callInfoParam == this.currentSelection) {
51      return;
52    }
53    this.currentSelection = callInfoParam;
54    this.initFilterTypes();
55    let types: Array<string> = [];
56    if (callInfoParam.nativeMemory.indexOf(this.native_type[0]) != -1) {
57      types.push("'AllocEvent'");
58      types.push("'MmapEvent'");
59    } else {
60      if (callInfoParam.nativeMemory.indexOf(this.native_type[1]) != -1) {
61        types.push("'AllocEvent'");
62      }
63      if (callInfoParam.nativeMemory.indexOf(this.native_type[2]) != -1) {
64        types.push("'MmapEvent'");
65      }
66    }
67    // @ts-ignore
68    this.callInfoTbl?.shadowRoot?.querySelector('.table').style.height = this.parentElement.clientHeight - 20 - 31 + 'px';
69    // @ts-ignore
70    this.tblData?.shadowRoot?.querySelector('.table').style.height = this.parentElement.clientHeight + 'px';
71    // @ts-ignore
72    this.tblData?.recycleDataSource = [];
73    // @ts-ignore
74    this.callInfoTbl?.recycleDataSource = [];
75    this.progressEL!.loading = true;
76    this.callInfoLoadingPage.style.visibility = 'visible';
77    queryNativeHookEventTid(callInfoParam.leftNs, callInfoParam.rightNs, types).then((result) => {
78      this.queryResult = result;
79      this.getDataByNativeMemoryWorker(callInfoParam);
80    });
81  }
82
83  getDataByNativeMemoryWorker(callInfoParam: SelectionParam | any) {
84    let callInfoArgs = new Map<string, any>();
85    callInfoArgs.set('data', this.queryResult);
86    callInfoArgs.set('filterAllocType', this.filterAllocationType);
87    callInfoArgs.set('filterEventType', this.filterNativeType);
88    callInfoArgs.set('leftNs', callInfoParam.leftNs);
89    callInfoArgs.set('rightNs', callInfoParam.rightNs);
90    callInfoArgs.set('actionType', 'call-info');
91    this.startWorker(callInfoArgs, (results: any[]) => {
92      this.tblData!.recycleDataSource = [];
93      this.progressEL!.loading = false;
94      if (results.length > 0) {
95        this.callInfoSource = results;
96        this.sortTreeByColumn(this.sortColumn, this.sortType);
97        this.frameChart!.mode = ChartMode.Byte;
98        this.frameChart!.data = this.callInfoSource;
99        this.frameChart?.updateCanvas(true, this.clientWidth);
100        this.frameChart?.calculateChartData();
101      } else {
102        this.callInfoSource = [];
103        this.callInfoTbl!.recycleDataSource = [];
104        this.frameChart!.data = [];
105        this.frameChart!.clearCanvas();
106      }
107    });
108  }
109
110  startWorker(callInfoArgs: Map<string, any>, handler: Function) {
111    this.loadingList.push(1);
112    this.progressEL!.loading = true;
113    this.callInfoLoadingPage.style.visibility = 'visible';
114    procedurePool.submitWithName('logic1', 'native-memory-action', callInfoArgs, undefined, (res: any) => {
115      handler(res);
116      this.loadingList.splice(0, 1);
117      if (this.loadingList.length == 0) {
118        this.progressEL!.loading = false;
119        this.callInfoLoadingPage.style.visibility = 'hidden';
120      }
121    });
122  }
123
124  getParentTree(
125    src: Array<NativeHookCallInfo>,
126    target: NativeHookCallInfo,
127    parents: Array<NativeHookCallInfo>
128  ): boolean {
129    for (let hook of src) {
130      if (hook.id == target.id) {
131        parents.push(hook);
132        return true;
133      } else {
134        if (this.getParentTree(hook.children as Array<NativeHookCallInfo>, target, parents)) {
135          parents.push(hook);
136          return true;
137        }
138      }
139    }
140    return false;
141  }
142
143  getChildTree(src: Array<NativeHookCallInfo>, eventId: number, children: Array<NativeHookCallInfo>): boolean {
144    for (let hook of src) {
145      if (hook.eventId == eventId && hook.children.length == 0) {
146        children.push(hook);
147        return true;
148      } else {
149        if (this.getChildTree(hook.children as Array<NativeHookCallInfo>, eventId, children)) {
150          children.push(hook);
151          return true;
152        }
153      }
154    }
155    return false;
156  }
157
158  setRightTableData(hook: NativeHookCallInfo) {
159    let parents: Array<NativeHookCallInfo> = [];
160    let children: Array<NativeHookCallInfo> = [];
161    this.getParentTree(this.callInfoSource, hook, parents);
162    let maxEventId = hook.eventId;
163    let maxHeap = 0;
164
165    function findMaxStack(hook: NativeHookCallInfo) {
166      if (hook.children.length == 0) {
167        if (hook.size > maxHeap) {
168          maxHeap = hook.size;
169          maxEventId = hook.eventId;
170        }
171      } else {
172        hook.children.map((hookChild) => {
173          findMaxStack(<NativeHookCallInfo>hookChild);
174        });
175      }
176    }
177
178    findMaxStack(hook);
179    this.getChildTree(hook.children as Array<NativeHookCallInfo>, maxEventId, children);
180    this.rightSource = parents.reverse().concat(children.reverse());
181    let len = this.rightSource.length;
182    // @ts-ignore
183    this.tblData?.dataSource = len == 0 ? [] : this.rightSource.slice(1, len);
184  }
185
186  initFilterTypes() {
187    let filter = this.shadowRoot?.querySelector<TabPaneFilter>('#filter');
188    this.queryResult = [];
189    filter!.firstSelect = '0';
190    filter!.secondSelect = '0';
191    this.filterAllocationType = '0';
192    this.filterNativeType = '0';
193  }
194
195  sortTreeByColumn(column: string, sort: number) {
196    this.sortColumn = column;
197    this.sortType = sort;
198    this.callInfoTbl!.recycleDataSource = this.sortTree(this.callInfoSource, column, sort);
199  }
200
201  sortTree(arr: Array<NativeHookCallInfo>, column: string, sort: number): Array<NativeHookCallInfo> {
202    let sortArr = arr.sort((callInfoLeftData, callInfoRightData) => {
203      if (column == 'size') {
204        if (sort == 0) {
205          return callInfoLeftData.eventId - callInfoRightData.eventId;
206        } else if (sort == 1) {
207          return callInfoLeftData.size - callInfoRightData.size;
208        } else {
209          return callInfoRightData.size - callInfoLeftData.size;
210        }
211      } else {
212        if (sort == 0) {
213          return callInfoLeftData.eventId - callInfoRightData.eventId;
214        } else if (sort == 1) {
215          return callInfoLeftData.count - callInfoRightData.count;
216        } else {
217          return callInfoRightData.count - callInfoLeftData.count;
218        }
219      }
220    });
221    sortArr.map((call) => {
222      call.children = this.sortTree(call.children as Array<NativeHookCallInfo>, column, sort);
223    });
224    return sortArr;
225  }
226
227  showButtomMenu(isShow: boolean) {
228    let filter = this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!;
229    if (isShow) {
230      filter.setAttribute('first', '');
231      filter.setAttribute('second', '');
232    } else {
233      filter.removeAttribute('first');
234      filter.removeAttribute('second');
235    }
236  }
237
238  initElements(): void {
239    this.callInfoLoadingPage = this.shadowRoot?.querySelector('.nm-call-info-loading');
240    this.progressEL = this.shadowRoot?.querySelector('.nm-call-info-progress') as LitProgressBar;
241    this.callInfoTbl = this.shadowRoot?.querySelector<LitTable>('#tb-native-callinfo');
242    this.tblData = this.shadowRoot?.querySelector<LitTable>('#tb-native-data');
243    this.frameChart = this.shadowRoot?.querySelector<FrameChart>('#framechart');
244    let pageTab = this.shadowRoot?.querySelector('#show_table');
245    let pageChart = this.shadowRoot?.querySelector('#show_chart');
246    this.frameChart?.addChartClickListener((showMenu: boolean) => {
247      this.parentElement!.scrollTo(0, 0);
248      this.showButtomMenu(showMenu);
249    });
250
251    this.callInfoTbl!.addEventListener('row-click', (e) => {
252      // @ts-ignore
253      let data = e.detail.data as NativeHookCallInfo;
254      this.setRightTableData(data);
255      data.isSelected = true;
256      this.tblData?.clearAllSelection(data);
257      this.tblData?.setCurrentSelection(data);
258      // @ts-ignore
259      if ((e.detail as any).callBack) {
260        // @ts-ignore
261        (e.detail as any).callBack(true);
262      }
263    });
264    this.tblData!.addEventListener('row-click', (e) => {
265      // @ts-ignore
266      let detail = e.detail.data as NativeHookCallInfo;
267      this.callInfoTbl?.clearAllSelection(detail);
268      detail.isSelected = true;
269      this.callInfoTbl!.scrollToData(detail);
270      // @ts-ignore
271      if ((e.detail as any).callBack) {
272        // @ts-ignore
273        (e.detail as any).callBack(true);
274      }
275    });
276    this.callInfoTbl!.addEventListener('column-click', (evt) => {
277      this.sortTreeByColumn(
278        // @ts-ignore
279        evt.detail.key == 'countValue' || evt.detail.key == 'countPercent' ? 'countValue' : 'size', evt.detail.sort
280      );
281    });
282
283    this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!.getFilterData((data: FilterData) => {
284      this.filterAllocationType = data.firstSelect || '0';
285      this.filterNativeType = data.secondSelect || '0';
286      this.getDataByNativeMemoryWorker(this.currentSelection);
287      if (data.icon == 'block') {
288        pageChart?.setAttribute('class', 'show');
289        pageTab?.setAttribute('class', '');
290        this.isChartShow = true;
291        this.frameChart!.data = this.callInfoSource;
292        this.frameChart?.calculateChartData();
293      } else if (data.icon == 'tree') {
294        pageChart?.setAttribute('class', '');
295        pageTab?.setAttribute('class', 'show');
296        this.isChartShow = false;
297        this.frameChart!.clearCanvas();
298        this.callInfoTbl!.reMeauseHeight();
299      }
300    });
301    this.initFilterTypes();
302  }
303
304  connectedCallback() {
305    super.connectedCallback();
306    this.parentElement!.onscroll = () => {
307      this.frameChart!.tabPaneScrollTop = this.parentElement!.scrollTop;
308    };
309    let filterHeight = 0;
310    new ResizeObserver((entries) => {
311      let nmCallInfoTabFilter = this.shadowRoot!.querySelector('#filter') as HTMLElement;
312      if (nmCallInfoTabFilter.clientHeight > 0) filterHeight = nmCallInfoTabFilter.clientHeight;
313      if (this.parentElement!.clientHeight > filterHeight) {
314        nmCallInfoTabFilter.style.display = 'flex';
315      } else {
316        nmCallInfoTabFilter.style.display = 'none';
317      }
318      if (this.parentElement?.clientHeight != 0) {
319        if (!this.isChartShow) {
320          // @ts-ignore
321          this.callInfoTbl?.shadowRoot.querySelector('.table').style.height = this.parentElement.clientHeight + 'px';
322          this.callInfoTbl?.reMeauseHeight();
323        } else {
324          // @ts-ignore
325          this.frameChart?.updateCanvas(false, entries[0].contentRect.width);
326          this.frameChart?.calculateChartData();
327        }
328        // @ts-ignore
329        this.callInfoTbl?.shadowRoot.querySelector('.table').style.height = this.parentElement.clientHeight - 10 - 31 + 'px';
330        this.callInfoTbl?.reMeauseHeight();
331        // @ts-ignore
332        this.tblData?.shadowRoot.querySelector('.table').style.height = this.parentElement.clientHeight - 10 - 31 + 'px';
333        this.tblData?.reMeauseHeight();
334        this.callInfoLoadingPage.style.height = this.parentElement!.clientHeight - 24 + 'px';
335      }
336    }).observe(this.parentElement!);
337  }
338
339  initHtml(): string {
340    return `
341        <style>
342        :host{
343            padding: 10px 10px 0 10px;
344            display: flex;
345            flex-direction: column;
346        }
347        .nm-call-info-filter {
348            border: solid rgb(216,216,216) 1px;
349            float: left;
350            position: fixed;
351            bottom: 0;
352            width: 100%;
353        }
354        selector{
355            display: none;
356        }
357        .nm-call-info-progress{
358            bottom: 33px;
359            position: absolute;
360            height: 1px;
361            left: 0;
362            right: 0;
363        }
364        .nm-call-info-loading{
365            position: absolute;
366            left: 0;
367            right: 0;
368            bottom: 0;
369            width:100%;
370            background:transparent;
371            z-index: 999999;
372        }
373        .show{
374            display: flex;
375            flex: 1;
376        }
377        </style>
378        <div class="nm-call-info-content" style="display: flex;flex-direction: row">
379            <selector id='show_table' class="show">
380            <lit-slicer style="width:100%">
381                <div style="width: 65%">
382                    <lit-table id="tb-native-callinfo" style="height: auto" tree>
383                        <lit-table-column class="nm-call-info-column" width="60%" title="Symbol Name" data-index="symbolName" key="symbolName"  align="flex-start">
384                        </lit-table-column>
385                        <lit-table-column class="nm-call-info-column" width="1fr" title="Size" data-index="heapSizeStr" key="heapSizeStr"  align="flex-start" order>
386                        </lit-table-column>
387                        <lit-table-column class="nm-call-info-column" width="1fr" title="%" data-index="heapPercent" key="heapPercent" align="flex-start"  order>
388                        </lit-table-column>
389                        <lit-table-column class="nm-call-info-column" width="1fr" title="Count" data-index="countValue" key="countValue" align="flex-start" order>
390                        </lit-table-column>
391                        <lit-table-column class="nm-call-info-column" width="1fr" title="%" data-index="countPercent" key="countPercent" align="flex-start" order>
392                        </lit-table-column>
393                        <lit-table-column class="nm-call-info-column" width="1fr" title="  " data-index="type" key="type"  align="flex-start" >
394                            <template>
395                                <img src="img/library.png" size="20" v-if=" type == 1 ">
396                                <img src="img/function.png" size="20" v-if=" type == 0 ">
397                                <div v-if=" type == - 1 "></div>
398                            </template>
399                        </lit-table-column>
400                    </lit-table>
401                </div>
402                <lit-slicer-track ></lit-slicer-track>
403                <lit-table id="tb-native-data" no-head style="height: auto;border-left: 1px solid var(--dark-border1,#e2e2e2)" hideDownload>
404                    <lit-table-column class="nm-call-info-column" title="" width="60px" data-index="type" key="type"  align="flex-start" >
405                        <template>
406                            <img src="img/library.png" size="20" v-if=" type == 1 ">
407                            <img src="img/function.png" size="20" v-if=" type == 0 ">
408                        </template>
409                    </lit-table-column>
410                    <lit-table-column class="nm-call-info-column" width="1fr" title="" data-index="symbolName" key="symbolName"  align="flex-start">
411                    </lit-table-column>
412                </lit-table>
413                </lit-slicer>
414            </selector>
415            <selector class="nm-call-info-selector" id='show_chart'>
416                <tab-framechart id='framechart' style='width: 100%;height: auto'> </tab-framechart>
417            </selector>
418            <lit-progress-bar class="progress nm-call-info-progress"></lit-progress-bar>
419            <tab-pane-filter id="filter" class="nm-call-info-filter" icon first second></tab-pane-filter>
420            <div class="loading nm-call-info-loading"></div>
421        </div>
422        `;
423  }
424}
425