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