• 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.clearTab();
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        if (item.scriptName === 'unknown') {
102          item.symbolName = item.name;
103        } else {
104          item.symbolName = `${item.name  } ${item.scriptName}`;
105        }
106        item.totalTimePercent = `${((item.totalTime / this.totalNs) * 100).toFixed(1)  }%`;
107        item.selfTimePercent = `${((item.selfTime / this.totalNs) * 100).toFixed(1)  }%`;
108        item.selfTimeStr = ns2s(item.selfTime);
109        item.totalTimeStr = ns2s(item.totalTime);
110        item.parent = callTreeMap.get(item.parentId!);
111        setTabData(item.children);
112      });
113    };
114    setTabData(results);
115    this.callTreeSource = this.sortTree(results);
116    this.callTreeTable!.recycleDataSource = this.callTreeSource;
117  }
118
119  private callTreeRowClickHandler(evt: Event): void {
120    const heaviestStack: JsCpuProfilerTabStruct[] = [];
121    const getHeaviestChildren = (children: Array<JsCpuProfilerTabStruct>): void => {
122      if (children.length === 0) {
123        return;
124      }
125      const heaviestChild = children.reduce(
126        (max, struct): JsCpuProfilerTabStruct =>
127          Math.max(max.totalTime, struct.totalTime) === max.totalTime ? max : struct
128      );
129      heaviestStack?.push(heaviestChild);
130      getHeaviestChildren(heaviestChild.children);
131    };
132    const getParent = (list: JsCpuProfilerTabStruct): void => {
133      if (list.parent) {
134        heaviestStack.push(list.parent!);
135        getParent(list.parent!);
136      }
137    };
138    //@ts-ignore
139    const data = evt.detail.data as JsCpuProfilerTabStruct;
140    heaviestStack!.push(data);
141    if (data.parent) {
142      heaviestStack.push(data.parent!);
143      getParent(data.parent!);
144    }
145    heaviestStack.reverse();
146    getHeaviestChildren(data.children);
147    this.stackTable!.recycleDataSource = heaviestStack;
148    data.isSelected = true;
149    this.stackTable?.clearAllSelection(data);
150    this.stackTable?.setCurrentSelection(data);
151    // @ts-ignore
152    if (evt.detail.callBack) {
153      // @ts-ignore
154      evt.detail.callBack(true);
155    }
156  }
157
158  public initElements(): void {
159    this.callTreeTable = this.shadowRoot?.querySelector('#callTreeTable') as LitTable;
160    this.stackTable = this.shadowRoot?.querySelector('#stackTable') as LitTable;
161    this.treeTable = this.callTreeTable!.shadowRoot?.querySelector('.thead') as HTMLDivElement;
162    this.profilerFilter = this.shadowRoot?.querySelector('#filter') as TabPaneFilter;
163    this.callTreeTable!.addEventListener('row-click', (evt): void => {
164      this.callTreeRowClickHandler(evt);
165    });
166    this.stackTable!.addEventListener('row-click', (evt) => {
167      //@ts-ignore
168      const data = evt.detail.data as JsCpuProfilerTabStruct;
169      data.isSelected = true;
170      this.callTreeTable!.clearAllSelection(data);
171      this.callTreeTable!.scrollToData(data);
172      // @ts-ignore
173      if (evt.detail.callBack) {
174        // @ts-ignore
175        evt.detail.callBack(true);
176      }
177    });
178    this.callTreeTable!.addEventListener('column-click', (evt) => {
179      // @ts-ignore
180      this.sortKey = evt.detail.key;
181      // @ts-ignore
182      this.sortType = evt.detail.sort;
183      this.setCallTreeTableData(this.callTreeSource);
184    });
185    this.profilerFilter!.getFilterData((): void => {
186      if (this.searchValue !== this.profilerFilter!.filterValue) {
187        this.searchValue = this.profilerFilter!.filterValue;
188        findSearchNode(this.callTreeSource, this.searchValue, false);
189      }
190      this.callTreeTable!.setStatus(this.callTreeSource, true);
191      this.setCallTreeTableData(this.callTreeSource);
192    });
193  }
194
195  public connectedCallback(): void {
196    super.connectedCallback();
197    new ResizeObserver(() => {
198      // @ts-ignore
199      this.callTreeTable?.shadowRoot.querySelector('.table').style.height =
200        `${this.parentElement!.clientHeight - 32  }px`;
201      this.callTreeTable?.reMeauseHeight();
202      // @ts-ignore
203      this.stackTable?.shadowRoot.querySelector('.table').style.height =
204        `${this.parentElement!.clientHeight - 32 - 22  }px`;
205      this.stackTable?.reMeauseHeight();
206    }).observe(this.parentElement!);
207  }
208
209  private sortTree(arr: Array<JsCpuProfilerTabStruct>): Array<JsCpuProfilerTabStruct> {
210    const that = this;
211    function defaultSort(callTreeLeftData: JsCpuProfilerTabStruct, callTreeRightData: JsCpuProfilerTabStruct): number {
212      if (that.currentType === that.TYPE_TOP_DOWN) {
213        return callTreeRightData.totalTime - callTreeLeftData.totalTime;
214      } else {
215        return callTreeRightData.selfTime - callTreeLeftData.selfTime;
216      }
217    }
218    const CallTreeSortArr = arr.sort((callTreeLeftData, callTreeRightData) => {
219      if (this.sortKey === 'selfTimeStr' || this.sortKey === 'selfTimePercent') {
220        if (this.sortType === 0) {
221          return defaultSort(callTreeLeftData, callTreeRightData);
222        } else if (this.sortType === 1) {
223          return callTreeLeftData.selfTime - callTreeRightData.selfTime;
224        } else {
225          return callTreeRightData.selfTime - callTreeLeftData.selfTime;
226        }
227      } else if (this.sortKey === 'symbolName') {
228        if (this.sortType === 0) {
229          return defaultSort(callTreeLeftData, callTreeRightData);
230        } else if (this.sortType === 1) {
231          return (`${callTreeLeftData.symbolName  }`).localeCompare(`${callTreeRightData.symbolName  }`);
232        } else {
233          return (`${callTreeRightData.symbolName  }`).localeCompare(`${callTreeLeftData.symbolName  }`);
234        }
235      } else {
236        if (this.sortType === 0) {
237          return defaultSort(callTreeLeftData, callTreeRightData);
238        } else if (this.sortType === 1) {
239          return callTreeLeftData.totalTime - callTreeRightData.totalTime;
240        } else {
241          return callTreeRightData.totalTime - callTreeLeftData.totalTime;
242        }
243      }
244    });
245
246    CallTreeSortArr.map((call) => {
247      call.children = this.sortTree(call.children);
248    });
249    return CallTreeSortArr;
250  }
251
252  private clearTab(): void {
253    this.stackTable!.recycleDataSource = [];
254    this.callTreeTable!.recycleDataSource = [];
255  }
256
257  public initHtml(): string {
258    return TabPaneJsCpuHtml;
259  }
260}
261