• 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 {LitProgressBar} from "../../../../../base-ui/progress-bar/LitProgressBar.js";
19import {FrameChart} from "../../../chart/FrameChart.js";
20import {SelectionParam} from "../../../../bean/BoxSelection.js";
21import {ChartMode} from "../../../../bean/FrameChartStruct.js";
22import {FilterData, TabPaneFilter} from "../TabPaneFilter.js";
23import {procedurePool} from "../../../../database/Procedure.js";
24import {MerageBean} from "../../../../database/logic-worker/ProcedureLogicWorkerCommon.js";
25
26@element('tabpane-calltree')
27export class TabPaneCallTree extends BaseElement {
28    public queryFuncName:string = ""
29    public procedureAction:string = ""
30    private tbl: LitTable | null | undefined;
31    private tbr: LitTable | null | undefined;
32    private progressEL:LitProgressBar | null | undefined;
33    private rightSource: Array<MerageBean> = [];
34    private filter: any
35    private dataSource: any[] = []
36    private sortKey = "weight";
37    private sortType = 0;
38    private currentSelectedData: any = undefined
39    private frameChart: FrameChart | null | undefined;
40    private isChartShow: boolean = false;
41    private systmeRuleName = "/system/"
42    private numRuleName = "/max/min/"
43    private needShowMenu = true;
44    private searchValue: string = ""
45    private loadingList:number[] = []
46    private loadingPage:any;
47    private currentSelection:SelectionParam|undefined
48    private flameChartMode:ChartMode = ChartMode.Duration
49
50    set data(val: SelectionParam | any) {
51        this.initModeAndAction()
52        this.currentSelection = val
53        this.tbl!.style.visibility = "visible";
54        if (this.parentElement!.clientHeight > this.filter!.clientHeight) {
55            this.filter!.style.display = "flex";
56        } else {
57            this.filter!.style.display = "none";
58        }
59        this.filter!.initializeFilterTree(true, true, true)
60        this.filter!.filterValue = ""
61        this.progressEL!.loading = true
62        this.loadingPage.style.visibility = "visible"
63        this.getDataByWorker([{
64            funcName: "setSearchValue",
65            funcArgs: [""]
66        }, {
67            funcName: "getCurrentDataFromDb",
68            funcArgs: [{queryFuncName:this.queryFuncName,...val}]
69        }], (results: any[]) => {
70            this.setLTableData(results)
71            this.tbr!.recycleDataSource = []
72            this.frameChart!.mode = this.flameChartMode;
73            this.frameChart!.data = this.dataSource;
74            this.frameChart?.updateCanvas(true, this.clientWidth);
75            this.frameChart?.calculateChartData();
76        })
77    }
78
79    initModeAndAction(){
80        if (this.procedureAction == ""&&this.hasAttribute("action")) {
81            this.procedureAction = this.getAttribute("action")||""
82        }
83        if(this.hasAttribute("flame-mode")){
84            let mode = this.getAttribute("flame-mode")
85            switch (mode){
86                case "Call":
87                    this.flameChartMode = ChartMode.Call
88                    break;
89                case "Byte":
90                    this.flameChartMode = ChartMode.Byte
91                    break;
92                case "Count":
93                    this.flameChartMode = ChartMode.Count
94                    break;
95                case "Duration":
96                    this.flameChartMode = ChartMode.Duration
97                    break;
98            }
99        }
100        if(this.hasAttribute("query")){
101            this.queryFuncName = this.getAttribute("query")||""
102        }
103    }
104
105    getParentTree(src: Array<MerageBean>, target: MerageBean, parents: Array<MerageBean>): boolean {
106        for (let call of src) {
107            if (call.id == target.id) {
108                parents.push(call)
109                return true
110            } else {
111                if (this.getParentTree(call.children as Array<MerageBean>, target, parents)) {
112                    parents.push(call);
113                    return true;
114                }
115            }
116        }
117        return false;
118    }
119
120    getChildTree(src: Array<MerageBean>, id: string, children: Array<MerageBean>): boolean {
121        for (let call of src) {
122            if (call.id == id && call.children.length == 0) {
123                children.push(call)
124                return true
125            } else {
126                if (this.getChildTree(call.children as Array<MerageBean>, id, children)) {
127                    children.push(call);
128                    return true;
129                }
130            }
131        }
132        return false;
133    }
134
135    setRightTableData(call: MerageBean) {
136        let parents: Array<MerageBean> = [];
137        let children: Array<MerageBean> = [];
138        this.getParentTree(this.dataSource, call, parents);
139        let maxId = call.id;
140        let maxDur = 0;
141
142        function findMaxStack(call: MerageBean) {
143            if (call.children.length == 0) {
144                if (call.dur > maxDur) {
145                    maxDur = call.dur;
146                    maxId = call.id;
147                }
148            } else {
149                call.children.map((callChild:any) => {
150                    findMaxStack(<MerageBean>callChild);
151                })
152            }
153        }
154
155        findMaxStack(call);
156        this.getChildTree(call.children as Array<MerageBean>, maxId, children);
157        let arr = parents.reverse().concat(children.reverse());
158        for (let data of arr) {
159            data.type = (data.libName.endsWith(".so.1") || data.libName.endsWith(".dll") || data.libName.endsWith(".so")) ? 0 : 1;
160        }
161        let len = arr.length;
162        this.rightSource = arr;
163        this.tbr!.dataSource = len == 0 ? [] : arr
164    }
165
166    showButtomMenu(isShow: boolean) {
167        if (isShow) {
168            this.filter.setAttribute('tree', '');
169            this.filter.setAttribute('input', '');
170            this.filter.setAttribute('inputLeftText', '');
171        } else {
172            this.filter.removeAttribute('tree');
173            this.filter.removeAttribute('input');
174            this.filter.removeAttribute('inputLeftText')
175        }
176    }
177
178    connectedCallback() {
179        this.parentElement!.onscroll = () => {
180            this.frameChart!.tabPaneScrollTop = this.parentElement!.scrollTop;
181        };
182        this.frameChart!.addChartClickListener((needShowMenu: boolean) => {
183            this.parentElement!.scrollTo(0, 0);
184            this.showButtomMenu(needShowMenu)
185            this.needShowMenu = needShowMenu;
186        });
187        let filterHeight = 0;
188        new ResizeObserver((entries) => {
189            let tabPaneFilter = this.shadowRoot!.querySelector("#filter") as HTMLElement;
190            if (tabPaneFilter.clientHeight > 0) filterHeight = tabPaneFilter.clientHeight;
191            if (this.parentElement!.clientHeight > filterHeight) {
192                tabPaneFilter.style.display = "flex";
193            } else {
194                tabPaneFilter.style.display = "none";
195            }
196            if (this.tbl!.style.visibility == "hidden") {
197                tabPaneFilter.style.display = "none";
198            }
199            if (this.parentElement?.clientHeight != 0) {
200                if (this.isChartShow) {
201                    this.frameChart?.updateCanvas(false, entries[0].contentRect.width);
202                    this.frameChart?.calculateChartData();
203                }
204                // @ts-ignore
205                this.tbl?.shadowRoot.querySelector(".table").style.height = (this.parentElement.clientHeight - 10 - 35) + "px"
206                this.tbl?.reMeauseHeight()
207                // @ts-ignore
208                this.tbr?.shadowRoot.querySelector(".table").style.height = (this.parentElement.clientHeight - 45 - 21) + "px"
209                this.tbr?.reMeauseHeight()
210                this.loadingPage.style.height = (this.parentElement!.clientHeight - 24) + "px"
211            }
212        }).observe(this.parentElement!)
213    }
214
215    initElements(): void {
216        this.tbl = this.shadowRoot?.querySelector<LitTable>('#tb-calltree');
217        this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar
218        this.frameChart = this.shadowRoot?.querySelector<FrameChart>('#framechart');
219        this.loadingPage = this.shadowRoot?.querySelector('.loading');
220
221        this.tbl!.rememberScrollTop = true;
222        this.filter = this.shadowRoot?.querySelector<TabPaneFilter>("#filter")
223        this.tbl!.addEventListener('row-click', (evt: any) => {
224            // @ts-ignore
225            let data = (evt.detail.data as MerageBean);
226            this.setRightTableData(data);
227            data.isSelected = true;
228            this.currentSelectedData = data;
229            this.tbr?.clearAllSelection(data);
230            this.tbr?.setCurrentSelection(data);
231            // @ts-ignore
232            if ((evt.detail as any).callBack) {
233                // @ts-ignore
234                (evt.detail as any).callBack(true)
235            }
236        })
237        this.tbr = this.shadowRoot?.querySelector<LitTable>('#tb-list');
238        this.tbr!.addEventListener('row-click', (evt: any) => {
239            // @ts-ignore
240            let data = (evt.detail.data as MerageBean);
241            this.tbl?.clearAllSelection(data);
242            (data as any).isSelected = true
243            this.tbl!.scrollToData(data)
244            // @ts-ignore
245            if ((evt.detail as any).callBack) {
246                // @ts-ignore
247                (evt.detail as any).callBack(true)
248            }
249        })
250        let filterFunc = (data: any) => {
251            let args: any[] = []
252            if (data.type == "check") {
253                if (data.item.checked) {
254                    args.push({
255                        funcName: "splitTree",
256                        funcArgs: [data.item.name, data.item.select == "0", data.item.type == "symbol"]
257                    })
258                } else {
259                    args.push({
260                        funcName: "resotreAllNode",
261                        funcArgs: [[data.item.name]]
262                    })
263                    args.push({
264                        funcName: "resetAllNode",
265                        funcArgs: []
266                    })
267                    args.push({
268                        funcName: "clearSplitMapData",
269                        funcArgs: [data.item.name]
270                    })
271                }
272            } else if (data.type == "select") {
273                args.push({
274                    funcName: "resotreAllNode",
275                    funcArgs: [[data.item.name]]
276                })
277                args.push({
278                    funcName: "clearSplitMapData",
279                    funcArgs: [data.item.name]
280                })
281                args.push({
282                    funcName: "splitTree",
283                    funcArgs: [data.item.name, data.item.select == "0", data.item.type == "symbol"]
284                })
285            } else if (data.type == "button") {
286                if (data.item == "symbol") {
287                    if (this.currentSelectedData && !this.currentSelectedData.canCharge) {
288                        return
289                    }
290                    if (this.currentSelectedData != undefined) {
291                        this.filter!.addDataMining({name: this.currentSelectedData.symbolName}, data.item)
292                        args.push({
293                            funcName: "splitTree",
294                            funcArgs: [this.currentSelectedData.symbolName, false, true]
295                        })
296                    } else {
297                        return
298                    }
299                } else if (data.item == "library") {
300                    if (this.currentSelectedData && !this.currentSelectedData.canCharge) {
301                        return
302                    }
303                    if (this.currentSelectedData != undefined && this.currentSelectedData.libName != "") {
304                        this.filter!.addDataMining({name: this.currentSelectedData.libName}, data.item)
305                        args.push({
306                            funcName: "splitTree",
307                            funcArgs: [this.currentSelectedData.libName, false, false]
308                        })
309                    } else {
310                        return
311                    }
312                } else if (data.item == "restore") {
313                    if (data.remove != undefined && data.remove.length > 0) {
314                        let list = data.remove.map((item: any) => {
315                            return item.name
316                        })
317                        args.push({
318                            funcName: "resotreAllNode",
319                            funcArgs: [list]
320                        })
321                        args.push({
322                            funcName: "resetAllNode",
323                            funcArgs: []
324                        })
325                        list.forEach((symbolName: string) => {
326                            args.push({
327                                funcName: "clearSplitMapData",
328                                funcArgs: [symbolName]
329                            })
330                        })
331                    }
332                }
333            }
334            this.getDataByWorker(args, (result: any[]) => {
335                this.setLTableData(result)
336                this.frameChart!.data = this.dataSource;
337                if (this.isChartShow) this.frameChart?.calculateChartData();
338                this.tbl!.move1px()
339                if (this.currentSelectedData) {
340                    this.currentSelectedData.isSelected = false;
341                    this.tbl?.clearAllSelection(this.currentSelectedData)
342                    this.tbr!.recycleDataSource = []
343                    this.currentSelectedData = undefined
344                }
345            })
346        }
347        this.filter!.getDataLibrary(filterFunc)
348        this.filter!.getDataMining(filterFunc)
349        this.filter!.getCallTreeData((data: any) => {
350            if (data.value == 0) {
351                this.refreshAllNode({...this.filter!.getFilterTreeData(), callTree: data.checks})
352            } else {
353                let args: any[] = []
354                if (data.checks[1]) {
355                    args.push({
356                        funcName: "hideSystemLibrary",
357                        funcArgs: []
358                    })
359                    args.push({
360                        funcName: "resetAllNode",
361                        funcArgs: []
362                    })
363                } else {
364                    args.push({
365                        funcName: "resotreAllNode",
366                        funcArgs: [[this.systmeRuleName]]
367                    })
368                    args.push({
369                        funcName: "resetAllNode",
370                        funcArgs: []
371                    })
372                    args.push({
373                        funcName: "clearSplitMapData",
374                        funcArgs: [this.systmeRuleName]
375                    })
376                }
377                this.getDataByWorker(args, (result: any[]) => {
378                    this.setLTableData(result)
379                    this.frameChart!.data = this.dataSource;
380                    if (this.isChartShow) this.frameChart?.calculateChartData();
381                })
382
383            }
384        })
385        this.filter!.getCallTreeConstraintsData((data: any) => {
386            let args: any[] = [{
387                funcName: "resotreAllNode",
388                funcArgs: [[this.numRuleName]]
389            }, {
390                funcName: "clearSplitMapData",
391                funcArgs: [this.numRuleName]
392            }]
393            if (data.checked) {
394                args.push({
395                    funcName: "hideNumMaxAndMin",
396                    funcArgs: [parseInt(data.min), data.max]
397                })
398            }
399            args.push({
400                funcName: "resetAllNode",
401                funcArgs: []
402            })
403            this.getDataByWorker(args, (result: any[]) => {
404                this.setLTableData(result)
405                this.frameChart!.data = this.dataSource;
406                if (this.isChartShow) this.frameChart?.calculateChartData();
407            })
408
409        })
410        this.filter!.getFilterData((data: FilterData) => {
411            if (this.searchValue != this.filter!.filterValue) {
412                this.searchValue = this.filter!.filterValue
413                let args = [
414                    {
415                        funcName: "setSearchValue",
416                        funcArgs: [this.searchValue]
417                    },
418                    {
419                        funcName: "resetAllNode",
420                        funcArgs: []
421                    }
422                ]
423                this.getDataByWorker(args, (result: any[]) => {
424                    this.setLTableData(result)
425                    this.frameChart!.data = this.dataSource;
426                    this.switchFlameChart(data)
427                })
428            }else {
429                this.switchFlameChart(data)
430            }
431
432        })
433        this.tbl!.addEventListener('column-click', (evt) => {
434            // @ts-ignore
435            this.sortKey = evt.detail.key
436            // @ts-ignore
437            this.sortType = evt.detail.sort
438            // @ts-ignore
439            this.setLTableData(this.dataSource)
440            this.frameChart!.data = this.dataSource;
441        });
442    }
443
444    switchFlameChart(data:any){
445        let pageTab = this.shadowRoot?.querySelector('#show_table');
446        let pageChart = this.shadowRoot?.querySelector('#show_chart');
447        if (data.icon == 'block') {
448            pageChart?.setAttribute('class', 'show');
449            pageTab?.setAttribute('class', '');
450            this.isChartShow = true;
451            this.filter!.disabledMining = true;
452            this.showButtomMenu(this.needShowMenu);
453            this.frameChart!.data = this.dataSource;
454            this.frameChart?.calculateChartData();
455        } else if (data.icon == 'tree') {
456            pageChart?.setAttribute('class', '');
457            pageTab?.setAttribute('class', 'show');
458            this.showButtomMenu(true);
459            this.isChartShow = false;
460            this.filter!.disabledMining = false;
461            this.frameChart!.clearCanvas();
462            this.tbl!.reMeauseHeight()
463        }
464    }
465
466
467    refreshAllNode(filterData: any) {
468        let args:any[] = []
469        let isTopDown: boolean = !filterData.callTree[0];
470        let isHideSystemLibrary = filterData.callTree[1];
471        let list = filterData.dataMining.concat(filterData.dataLibrary);
472        args.push({
473            funcName: "getCallChainsBySampleIds",
474            funcArgs: [isTopDown]
475        })
476        this.tbr!.recycleDataSource = []
477        if (isHideSystemLibrary) {
478            args.push({
479                funcName: "hideSystemLibrary",
480                funcArgs: []
481            })
482        }
483        if (filterData.callTreeConstraints.checked) {
484            args.push({
485                funcName: "hideNumMaxAndMin",
486                funcArgs: [parseInt(filterData.callTreeConstraints.inputs[0]), filterData.callTreeConstraints.inputs[1]]
487            })
488        }
489        args.push({
490            funcName: "splitAllProcess",
491            funcArgs: [list]
492        })
493        args.push({
494            funcName: "resetAllNode",
495            funcArgs: []
496        })
497        this.getDataByWorker(args, (result: any[]) => {
498            this.setLTableData(result)
499            this.frameChart!.data = this.dataSource;
500            if (this.isChartShow) this.frameChart?.calculateChartData();
501        })
502    }
503
504    setLTableData(resultData:any[]) {
505        this.dataSource = this.sortTree(resultData)
506        this.tbl!.recycleDataSource = this.dataSource
507    }
508
509    sortTree(arr: Array<any>): Array<any> {
510        let sortArr = arr.sort((a, b) => {
511            if (this.sortKey == 'self') {
512                if (this.sortType == 0) {
513                    return b.dur - a.dur;
514                } else if (this.sortType == 1) {
515                    return a.selfDur - b.selfDur;
516                } else {
517                    return b.selfDur - a.selfDur;
518                }
519            } else {
520                if (this.sortType == 0) {
521                    return b.dur - a.dur;
522                } else if (this.sortType == 1) {
523                    return a.dur - b.dur;
524                } else {
525                    return b.dur - a.dur;
526                }
527            }
528        })
529        sortArr.map((call) => {
530            call.children = this.sortTree(call.children);
531        })
532        return sortArr;
533    }
534
535    getDataByWorker(args: any[], handler: Function) {
536        this.loadingList.push(1)
537        this.progressEL!.loading = true
538        this.loadingPage.style.visibility = "visible"
539        procedurePool.submitWithName("logic0",this.procedureAction,args,undefined,(results:any)=>{
540            handler(results)
541            this.loadingList.splice(0,1)
542            if(this.loadingList.length == 0) {
543                this.progressEL!.loading = false
544                this.loadingPage.style.visibility = "hidden"
545            }
546        })
547    }
548
549    initHtml(): string {
550        return `
551        <style>
552        :host{
553            display: flex;
554            flex-direction: column;
555            padding: 10px 10px 0 10px;
556        }
557        tab-pane-filter {
558            border: solid rgb(216,216,216) 1px;
559            float: left;
560            position: fixed;
561            bottom: 0;
562            width: 100%;
563        }
564        selector{
565            display: none;
566        }
567        .show{
568            display: flex;
569            flex: 1;
570        }
571        .progress{
572            bottom: 33px;
573            position: absolute;
574            height: 1px;
575            left: 0;
576            right: 0;
577        }
578        .loading{
579            bottom: 0;
580            position: absolute;
581            left: 0;
582            right: 0;
583            width:100%;
584            background:transparent;
585            z-index: 999999;
586        }
587    </style>
588    <div style="display: flex;flex-direction: row">
589
590    <selector id='show_table' class="show">
591        <lit-slicer style="width:100%">
592        <div id="left_table" style="width: 65%">
593            <lit-table id="tb-calltree" style="height: auto" tree>
594                <lit-table-column width="70%" title="Call Stack" data-index="symbolName" key="symbolName"  align="flex-start" ></lit-table-column>
595                <lit-table-column width="1fr" title="Local" data-index="self" key="self"  align="flex-start"  order></lit-table-column>
596                <lit-table-column width="1fr" title="Weight" data-index="weight" key="weight"  align="flex-start"  order></lit-table-column>
597                <lit-table-column width="1fr" title="%" data-index="weightPercent" key="weightPercent"  align="flex-start"  order></lit-table-column>
598            </lit-table>
599
600        </div>
601        <lit-slicer-track ></lit-slicer-track>
602        <lit-table id="tb-list" no-head style="height: auto;border-left: 1px solid var(--dark-border1,#e2e2e2)">
603            <span slot="head">Heaviest Stack Trace</span>
604            <lit-table-column width="30px" title="" data-index="type" key="type"  align="flex-start" >
605                <template>
606                    <img src="img/library.png" size="20" v-if=" type == 1 ">
607                    <img src="img/function.png" size="20" v-if=" type == 0 ">
608                </template>
609            </lit-table-column>
610            <lit-table-column width="60px" title="" data-index="count" key="count"  align="flex-start"></lit-table-column>
611            <lit-table-column width="1fr" title="" data-index="symbolName" key="symbolName"  align="flex-start"></lit-table-column>
612        </lit-table>
613        </div>
614        </lit-slicer>
615     </selector>
616     <tab-pane-filter id="filter" input inputLeftText icon tree></tab-pane-filter>
617     <lit-progress-bar class="progress"></lit-progress-bar>
618    <selector id='show_chart'>
619        <tab-framechart id='framechart' style='width: 100%;height: auto'> </tab-framechart>
620    </selector>
621    <div class="loading"></div>
622    </div>`;
623    }
624
625}