• 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 } from '../../../../../base-ui/BaseElement';
17import { type LitTable, TableMode } from '../../../../../base-ui/table/lit-table';
18import { SelectionParam } from '../../../../bean/BoxSelection';
19import { type JsCpuProfilerChartFrame, JsCpuProfilerTabStruct } from '../../../../bean/JsStruct';
20import { procedurePool } from '../../../../database/Procedure';
21import { findSearchNode, ns2s } from '../../../../database/ui-worker/ProcedureWorkerCommon';
22import { SpSystemTrace } from '../../../SpSystemTrace';
23import { TabPaneFilter } from '../TabPaneFilter';
24import '../TabPaneFilter';
25import { TabPaneJsCpuHtml } from './TabPaneJsCpu.html';
26
27export class TabPaneJsCpuCallTree extends BaseElement {
28  protected TYPE_TOP_DOWN = 0;
29  protected TYPE_BOTTOM_UP = 1;
30  private treeTable: HTMLDivElement | undefined | null;
31  private callTreeTable: LitTable | null | undefined;
32  private stackTable: LitTable | null | undefined;
33  private sortKey = '';
34  private sortType = 0;
35  private callTreeSource: Array<JsCpuProfilerTabStruct> = [];
36  private currentType = 0;
37  private profilerFilter: TabPaneFilter | undefined | null;
38  private searchValue: string = '';
39  private totalNs: number = 0;
40  private currentSelection: SelectionParam | undefined;
41  private getDataByWorker(args: Array<JsCpuProfilerChartFrame>, handler: Function): void {
42    const key = this.currentType === this.TYPE_TOP_DOWN ? 'jsCpuProfiler-call-tree' : 'jsCpuProfiler-bottom-up';
43    this.callTreeTable!.mode = TableMode.Retract;
44    procedurePool.submitWithName('logic0', key, args, undefined, (results: Array<JsCpuProfilerTabStruct>) => {
45      handler(results);
46    });
47  }
48
49  set data(data: SelectionParam | Array<JsCpuProfilerChartFrame>) {
50    if (data instanceof SelectionParam) {
51      if (data === this.currentSelection) {
52        return;
53      }
54      this.currentSelection = data;
55      let chartData;
56      chartData = data.jsCpuProfilerData;
57      this.totalNs = chartData.reduce((acc, struct) => acc + struct.totalTime, 0);
58      if (data.rightNs && data.leftNs) {
59        this.totalNs = Math.min(data.rightNs - data.leftNs, this.totalNs);
60      }
61
62      this.init();
63      this.getDataByWorker(chartData, (results: Array<JsCpuProfilerTabStruct>): void => {
64        this.setCallTreeTableData(results);
65      });
66    }
67  }
68
69  protected setCurrentType(type: number): void {
70    this.currentType = type;
71  }
72
73  private init(): void {
74    this.sortKey = '';
75    this.sortType = 0;
76    this.profilerFilter!.filterValue = '';
77    const thTable = this.treeTable!.querySelector('.th');
78    const list = thTable!.querySelectorAll('div');
79    if (this.treeTable!.hasAttribute('sort')) {
80      this.treeTable!.removeAttribute('sort');
81      list.forEach((item) => {
82        item.querySelectorAll('svg').forEach((svg): void => {
83          svg.style.display = 'none';
84        });
85      });
86    }
87  }
88
89  private setCallTreeTableData(results: Array<JsCpuProfilerTabStruct>): void {
90    this.stackTable!.recycleDataSource = [];
91    const callTreeMap = new Map<number, JsCpuProfilerTabStruct>();
92    const setTabData = (data: Array<JsCpuProfilerTabStruct>): void => {
93      data.forEach((item) => {
94        if (item.children && item.children.length > 0) {
95          item.children.forEach((it) => {
96            it.parentId = item.id;
97          });
98        }
99        item.name = SpSystemTrace.DATA_DICT.get(item.nameId) || '';
100        callTreeMap.set(item.id, item);
101        item.scriptName === 'unknown'
102          ? (item.symbolName = item.name)
103          : (item.symbolName = `${item.name} ${item.scriptName}`);
104        item.totalTimePercent = `${((item.totalTime / this.totalNs) * 100).toFixed(1)}%`;
105        item.selfTimePercent = `${((item.selfTime / this.totalNs) * 100).toFixed(1)}%`;
106        item.selfTimeStr = ns2s(item.selfTime);
107        item.totalTimeStr = ns2s(item.totalTime);
108        item.parent = callTreeMap.get(item.parentId!);
109        setTabData(item.children);
110      });
111    };
112    setTabData(results);
113    this.callTreeSource = this.sortTree(results);
114    this.callTreeTable!.recycleDataSource = this.callTreeSource;
115  }
116
117  private callTreeRowClickHandler(evt: Event): void {
118    const heaviestStack: JsCpuProfilerTabStruct[] = [];
119    const getHeaviestChildren = (children: Array<JsCpuProfilerTabStruct>): void => {
120      if (children.length === 0) {
121        return;
122      }
123      const heaviestChild = children.reduce(
124        (max, struct): JsCpuProfilerTabStruct =>
125          Math.max(max.totalTime, struct.totalTime) === max.totalTime ? max : struct
126      );
127      heaviestStack?.push(heaviestChild);
128      getHeaviestChildren(heaviestChild.children);
129    };
130    const getParent = (list: JsCpuProfilerTabStruct): void => {
131      if (list.parent) {
132        heaviestStack.push(list.parent!);
133        getParent(list.parent!);
134      }
135    };
136    //@ts-ignore
137    const data = evt.detail.data as JsCpuProfilerTabStruct;
138    heaviestStack!.push(data);
139    if (data.parent) {
140      heaviestStack.push(data.parent!);
141      getParent(data.parent!);
142    }
143    heaviestStack.reverse();
144    getHeaviestChildren(data.children);
145    this.stackTable!.recycleDataSource = heaviestStack;
146    data.isSelected = true;
147    this.stackTable?.clearAllSelection(data);
148    this.stackTable?.setCurrentSelection(data);
149    // @ts-ignore
150    if (evt.detail.callBack) {
151      // @ts-ignore
152      evt.detail.callBack(true);
153    }
154  }
155
156  public initElements(): void {
157    this.callTreeTable = this.shadowRoot?.querySelector('#callTreeTable') as LitTable;
158    this.stackTable = this.shadowRoot?.querySelector('#stackTable') as LitTable;
159    this.treeTable = this.callTreeTable!.shadowRoot?.querySelector('.thead') as HTMLDivElement;
160    this.profilerFilter = this.shadowRoot?.querySelector('#filter') as TabPaneFilter;
161    this.callTreeTable!.addEventListener('row-click', (evt): void => {
162      this.callTreeRowClickHandler(evt);
163    });
164    this.stackTable!.addEventListener('row-click', (evt) => {
165      //@ts-ignore
166      const data = evt.detail.data as JsCpuProfilerTabStruct;
167      data.isSelected = true;
168      this.callTreeTable!.clearAllSelection(data);
169      this.callTreeTable!.scrollToData(data);
170      // @ts-ignore
171      if (evt.detail.callBack) {
172        // @ts-ignore
173        evt.detail.callBack(true);
174      }
175    });
176    this.callTreeTable!.addEventListener('column-click', (evt) => {
177      // @ts-ignore
178      this.sortKey = evt.detail.key;
179      // @ts-ignore
180      this.sortType = evt.detail.sort;
181      this.setCallTreeTableData(this.callTreeSource);
182    });
183    this.profilerFilter!.getFilterData((): void => {
184      if (this.searchValue !== this.profilerFilter!.filterValue) {
185        this.searchValue = this.profilerFilter!.filterValue;
186        findSearchNode(this.callTreeSource, this.searchValue, false);
187      }
188      this.callTreeTable!.setStatus(this.callTreeSource, true);
189      this.setCallTreeTableData(this.callTreeSource);
190    });
191  }
192
193  public connectedCallback(): void {
194    super.connectedCallback();
195    new ResizeObserver(() => {
196      // @ts-ignore
197      this.callTreeTable?.shadowRoot.querySelector('.table').style.height = `${
198        this.parentElement!.clientHeight - 32
199      }px`;
200      this.callTreeTable?.reMeauseHeight();
201      // @ts-ignore
202      this.stackTable?.shadowRoot.querySelector('.table').style.height = `${
203        this.parentElement!.clientHeight - 32 - 22
204      }px`;
205      this.stackTable?.reMeauseHeight();
206    }).observe(this.parentElement!);
207  }
208
209  private sortTree(arr: Array<JsCpuProfilerTabStruct>): Array<JsCpuProfilerTabStruct> {
210    const defaultSort = (callTreeLeftData: JsCpuProfilerTabStruct, callTreeRightData: JsCpuProfilerTabStruct): number => {
211      if (this.currentType === this.TYPE_TOP_DOWN) {
212        return callTreeRightData.totalTime - callTreeLeftData.totalTime;
213      } else {
214        return callTreeRightData.selfTime - callTreeLeftData.selfTime;
215      }
216    };
217    const CallTreeSortArr = arr.sort((callTreeLeftData, callTreeRightData) => {
218      if (this.sortKey === 'selfTimeStr' || this.sortKey === 'selfTimePercent') {
219        if (this.sortType === 0) {
220          return defaultSort(callTreeLeftData, callTreeRightData);
221        } else if (this.sortType === 1) {
222          return callTreeLeftData.selfTime - callTreeRightData.selfTime;
223        } else {
224          return callTreeRightData.selfTime - callTreeLeftData.selfTime;
225        }
226      } else if (this.sortKey === 'symbolName') {
227        if (this.sortType === 0) {
228          return defaultSort(callTreeLeftData, callTreeRightData);
229        } else if (this.sortType === 1) {
230          return `${callTreeLeftData.symbolName}`.localeCompare(`${callTreeRightData.symbolName}`);
231        } else {
232          return `${callTreeRightData.symbolName}`.localeCompare(`${callTreeLeftData.symbolName}`);
233        }
234      } else {
235        if (this.sortType === 0) {
236          return defaultSort(callTreeLeftData, callTreeRightData);
237        } else if (this.sortType === 1) {
238          return callTreeLeftData.totalTime - callTreeRightData.totalTime;
239        } else {
240          return callTreeRightData.totalTime - callTreeLeftData.totalTime;
241        }
242      }
243    });
244
245    CallTreeSortArr.map((call) => {
246      call.children = this.sortTree(call.children);
247    });
248    return CallTreeSortArr;
249  }
250
251  private clearTab(): void {
252    this.stackTable!.recycleDataSource = [];
253    this.callTreeTable!.recycleDataSource = [];
254  }
255
256  public initHtml(): string {
257    return TabPaneJsCpuHtml;
258  }
259}
260