• 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';
17import { type LitChartPie } from '../../../../../base-ui/chart/pie/LitChartPie';
18import { type LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar';
19import { type LitTable } from '../../../../../base-ui/table/lit-table';
20import { SelectionParam } from '../../../../bean/BoxSelection';
21import { type JsCpuProfilerChartFrame, JsCpuProfilerStatisticsStruct } from '../../../../bean/JsStruct';
22import { procedurePool } from '../../../../database/Procedure';
23import { type SampleType } from '../../../../database/logic-worker/ProcedureLogicWorkerJsCpuProfiler';
24import { ns2s } from '../../../../database/ui-worker/ProcedureWorkerCommon';
25import { resizeObserver } from '../SheetUtils';
26import { TabPaneJsCpuStatisticsHtml } from './TabPaneJsCpuStatistics.html';
27
28@element('tabpane-js-cpu-statistics')
29export class TabPaneJsCpuStatistics extends BaseElement {
30  private progress: LitProgressBar | null | undefined;
31  private statisticsTable: LitTable | null | undefined;
32  private statisticsSource: Array<JsCpuProfilerStatisticsStruct> = [];
33  private tabTitle: HTMLDivElement | undefined | null;
34  private sortKey = 'timeStr';
35  private sortType = 2;
36  private statisticsPie: LitChartPie | null | undefined;
37  private currentSelection: SelectionParam | undefined;
38
39  set data(data: SelectionParam | Array<JsCpuProfilerChartFrame>) {
40    if (data instanceof SelectionParam) {
41      if (data === this.currentSelection) {
42        return;
43      }
44      this.currentSelection = data;
45    }
46
47    this.init();
48    this.clearData();
49    this.progress!.loading = true;
50
51    this.getDataByWorker(data, (results: Map<SampleType, number>) => {
52      this.progress!.loading = false;
53      this.statisticsSource = results.size > 0 ? this.setStatisticsData(results) : [];
54      this.queryPieChartDataByType(this.statisticsSource || []);
55    });
56  }
57
58  private init(): void {
59    const thTable = this.tabTitle!.querySelector('.th');
60    const jsCpuStatTblNodes = thTable!.querySelectorAll('div');
61    if (this.tabTitle!.hasAttribute('sort')) {
62      this.tabTitle!.removeAttribute('sort');
63      jsCpuStatTblNodes.forEach((item) => {
64        item.querySelectorAll('svg').forEach((svg) => {
65          svg.style.display = 'none';
66        });
67      });
68    }
69    this.sortKey = 'timeStr';
70    this.sortType = 2;
71  }
72
73  private getDataByWorker(data: SelectionParam | Array<JsCpuProfilerChartFrame>, handler: Function): void {
74    let params;
75    if (data instanceof SelectionParam) {
76      params = {
77        data: data.jsCpuProfilerData,
78        leftNs: data.leftNs,
79        rightNs: data.rightNs,
80      };
81    } else {
82      params = {
83        data: data,
84      };
85    }
86    procedurePool.submitWithName(
87      'logic0',
88      'jsCpuProfiler-statistics',
89      params,
90      undefined,
91      (results: Map<SampleType, number>) => {
92        handler(results);
93      }
94    );
95  }
96
97  private queryPieChartDataByType(res: Array<JsCpuProfilerStatisticsStruct>): void {
98    this.statisticsPie!.config = {
99      appendPadding: 0,
100      data: res,
101      angleField: 'time',
102      colorField: 'type',
103      radius: 1,
104      label: {
105        type: 'outer',
106      },
107      tip: (obj): string => {
108        return `<div>
109                    <div>type: ${
110                      // @ts-ignore
111                      obj.obj.type
112                    }</div>
113                    <div>total: ${
114                      // @ts-ignore
115                      ns2s(obj.obj.time)
116                    } (${
117          // @ts-ignore
118          obj.obj.percentage
119        }%)</div>
120                </div> `;
121      },
122      hoverHandler: (data): void => {
123        if (data) {
124          this.statisticsTable!.setCurrentHover(data);
125        } else {
126          this.statisticsTable!.mouseOut();
127        }
128      },
129      interactions: [
130        {
131          type: 'element-active',
132        },
133      ],
134    };
135    this.statisticsTable!.addEventListener('row-hover', (statisticsRowHover: unknown) => {
136      // @ts-ignore
137      if (statisticsRowHover.detail.data) {
138        // @ts-ignore
139        let data = statisticsRowHover.detail.data; // @ts-ignore
140        data.isHover = true; // @ts-ignore
141        if ((statisticsRowHover.detail as unknown).callBack) {
142          // @ts-ignore
143          (statisticsRowHover.detail as unknown).callBack(true);
144        }
145      }
146      this.statisticsPie?.showHover();
147      this.statisticsPie?.hideTip();
148    });
149
150    if (this.statisticsSource.length > 0) {
151      this.sortByColumn({ key: this.sortKey, sort: this.sortType });
152      let total = this.totalData(this.statisticsSource);
153      this.statisticsSource.unshift(total);
154      this.statisticsTable!.recycleDataSource = this.statisticsSource;
155      this.statisticsSource.shift();
156    }
157    this.statisticsTable?.reMeauseHeight();
158  }
159
160  private totalData(source: Array<JsCpuProfilerStatisticsStruct>): JsCpuProfilerStatisticsStruct {
161    // 计算总的time作为表格的第一行显示
162    let totalTime = 0;
163    for (let item of source) {
164      totalTime += item.time;
165    }
166    return this.toStatisticsStruct('', totalTime, totalTime);
167  }
168
169  private clearData(): void {
170    this.statisticsPie!.dataSource = [];
171    this.statisticsTable!.recycleDataSource = [];
172  }
173
174  private setStatisticsData(results: Map<SampleType, number>): Array<JsCpuProfilerStatisticsStruct> {
175    this.statisticsTable!.recycleDataSource = [];
176    this.statisticsSource = [];
177    let statisticsData: JsCpuProfilerStatisticsStruct;
178    const totalTime = [...results.values()].reduce((prev, curr) => prev + curr);
179    for (let [key, value] of results.entries()) {
180      statisticsData = this.toStatisticsStruct(key, value, totalTime);
181      this.statisticsSource.push(statisticsData);
182    }
183    return this.statisticsSource.sort((a, b) => b.time - a.time);
184  }
185
186  private toStatisticsStruct(
187    type: string | SampleType,
188    time: number,
189    percentage: number
190  ): JsCpuProfilerStatisticsStruct {
191    return new JsCpuProfilerStatisticsStruct(type, time, ns2s(time), ((time / percentage || 0) * 100).toFixed(1));
192  }
193
194  private sortByColumn(detail: unknown): void {
195    // @ts-ignore
196    function compare(key, sort, type) {
197      return function (a: unknown, b: unknown) {
198        // 不管哪一列的排序方式是0(默认排序),都按照time列从大到小排序
199        if (sort === 0) {
200          sort = 2;
201          key = 'time';
202          type = 'number';
203        }
204        if (type === 'number') {
205          // @ts-ignore
206          return sort === 2 ? parseFloat(b[key]) - parseFloat(a[key]) : parseFloat(a[key]) - parseFloat(b[key]);
207        } else {
208          if (sort === 2) {
209            // @ts-ignore
210            return b[key].toString().localeCompare(a[key].toString());
211          } else {
212            // @ts-ignore
213            return a[key].toString().localeCompare(b[key].toString());
214          }
215        }
216      };
217    } // @ts-ignore
218    if (detail.key === 'timeStr' || detail.key === 'percentage') {
219      // @ts-ignore
220      this.statisticsSource.sort(compare('time', detail.sort, 'number')); // @ts-ignore
221    } else if (detail.key === 'type') {
222      // @ts-ignore
223      this.statisticsSource.sort(compare(detail.key, detail.sort, 'string'));
224    }
225    if (this.statisticsSource.length > 0) {
226      let total = this.totalData(this.statisticsSource);
227      this.statisticsSource.unshift(total);
228      this.statisticsTable!.recycleDataSource = this.statisticsSource;
229      this.statisticsSource.shift();
230    }
231  }
232
233  public connectedCallback(): void {
234    super.connectedCallback();
235    resizeObserver(this.parentElement!, this.statisticsTable!);
236  }
237
238  public initElements(): void {
239    this.progress = this.shadowRoot!.querySelector<LitProgressBar>('#loading');
240    this.statisticsTable = this.shadowRoot?.querySelector('#statistics-table') as LitTable;
241    this.statisticsPie = this.shadowRoot?.querySelector('#chart-pie') as LitChartPie;
242    this.tabTitle = this.statisticsTable!.shadowRoot?.querySelector('.thead') as HTMLDivElement;
243
244    this.statisticsTable!.addEventListener('column-click', (evt: unknown) => {
245      // @ts-ignore
246      this.sortKey = evt.detail.key; // @ts-ignore
247      this.sortType = evt.detail.sort;
248      // @ts-ignore
249      this.sortByColumn(evt.detail);
250    });
251  }
252
253  public initHtml(): string {
254    return TabPaneJsCpuStatisticsHtml;
255  }
256}
257