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