• 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 LitTable } from '../../../../../base-ui/table/lit-table';
18import '../TabPaneFilter';
19import { TabPaneFilter } from '../TabPaneFilter';
20import { SelectionParam } from '../../../../bean/BoxSelection';
21import '../../../chart/FrameChart';
22import '../../../../../base-ui/slicer/lit-slicer';
23import '../../../../../base-ui/progress-bar/LitProgressBar';
24import { procedurePool } from '../../../../database/Procedure';
25import { type LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar';
26import { type PerfBottomUpStruct } from '../../../../bean/PerfBottomUpStruct';
27import { findSearchNode, HiPerfStruct } from '../../../../database/ui-worker/ProcedureWorkerCommon';
28
29@element('tabpane-perf-bottom-up')
30export class TabpanePerfBottomUp extends BaseElement {
31  private bottomUpTable: LitTable | null | undefined;
32  private stackTable: LitTable | null | undefined;
33  private sortKey = '';
34  private sortType = 0;
35  private bottomUpSource: Array<PerfBottomUpStruct> = [];
36  private bottomUpFilter: TabPaneFilter | undefined | null;
37  private progressEL: LitProgressBar | null | undefined;
38  private searchValue: string = '';
39  private currentSelection: SelectionParam | undefined;
40  private static instance: TabpanePerfBottomUp | null;
41
42  public initElements(): void {
43    this.bottomUpTable = this.shadowRoot?.querySelector('#callTreeTable') as LitTable;
44    this.stackTable = this.shadowRoot?.querySelector('#stackTable') as LitTable;
45    this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar;
46    this.bottomUpFilter = this.shadowRoot?.querySelector('#filter') as TabPaneFilter;
47    this.bottomUpTable!.addEventListener('row-click', (evt) => this.bottomUpTableRowClickHandler(evt));
48    this.stackTable!.addEventListener('row-click', (evt) => this.stackTableRowClick(evt));
49    this.bottomUpTable!.addEventListener('column-click', (evt) => this.bottomUpTableColumnClickHandler(evt));
50    this.bottomUpFilter!.getFilterData(() => {
51      if (this.searchValue !== this.bottomUpFilter!.filterValue) {
52        this.searchValue = this.bottomUpFilter!.filterValue;
53        HiPerfStruct.bottomFindCount = 0;
54        findSearchNode(this.bottomUpSource, this.searchValue, false);
55      }
56      if (HiPerfStruct.bottomFindCount === 0 && this.bottomUpFilter!.filterValue !== '') {
57        this.bottomUpTable!.recycleDataSource = [];
58      } else {
59        this.bottomUpTable!.setStatus(this.bottomUpSource, true);
60        this.setBottomUpTableData(this.bottomUpSource);
61      }
62    });
63  }
64
65  public getBottomData(data: SelectionParam): void {
66    this.getDataByWorker(data, (results: Array<PerfBottomUpStruct>) => {
67      this.setBottomUpTableData(results);
68    });
69  }
70
71  private getDataByWorker(val: SelectionParam, handler: (results: Array<PerfBottomUpStruct>) => void): void {
72    this.progressEL!.loading = true;
73    const args = [
74      {
75        funcName: 'setSearchValue',
76        funcArgs: [''],
77      },
78      {
79        funcName: 'getCurrentDataFromDbBottomUp',
80        funcArgs: [val],
81      },
82    ];
83    procedurePool.submitWithName('logic0', 'perf-action', args, undefined, (results: Array<PerfBottomUpStruct>) => {
84      handler(results);
85      this.progressEL!.loading = false;
86    });
87  }
88
89  set data(data: SelectionParam) {
90    if (data == this.currentSelection) {
91      return;
92    }
93    this.currentSelection = data;
94    this.sortKey = '';
95    this.sortType = 0;
96    this.bottomUpFilter!.filterValue = '';
97    this.getBottomData(data);
98  }
99
100  private setBottomUpTableData(results: Array<PerfBottomUpStruct>): void {
101    const percentageDenominator = 100;
102    const percentFraction = 1;
103    this.stackTable!.recycleDataSource = [];
104    let sum = results.reduce(
105      (sum, struct) => {
106        sum.totalCount += struct.selfTime;
107        sum.totalEvent += struct.eventCount;
108        return sum;
109      },
110      {
111        totalCount: 0,
112        totalEvent: 0,
113      }
114    );
115    const setTabData = (array: Array<PerfBottomUpStruct>): void => {
116      array.forEach((data) => {
117        data.totalTimePercent = `${((data.totalTime / sum.totalCount) * percentageDenominator).toFixed(
118          percentFraction
119        )}%`;
120        data.selfTimePercent = `${((data.selfTime / sum.totalCount) * percentageDenominator).toFixed(
121          percentFraction
122        )}%`;
123        data.eventPercent = `${((data.eventCount / sum.totalEvent) * percentageDenominator).toFixed(percentFraction)}%`;
124        setTabData(data.children);
125      });
126    };
127    setTabData(results);
128    this.bottomUpSource = this.sortTree(results);
129    this.bottomUpTable!.recycleDataSource = this.bottomUpSource;
130  }
131
132  private bottomUpTableRowClickHandler(evt: Event): void {
133    const callStack: Array<PerfBottomUpStruct> = [];
134    const getCallStackChildren = (children: Array<PerfBottomUpStruct>): void => {
135      if (children.length === 0) {
136        return;
137      }
138      const heaviestChild = children.reduce((max, struct) =>
139        Math.max(max.totalTime, struct.totalTime) === max.totalTime ? max : struct
140      );
141      callStack?.push(heaviestChild);
142      getCallStackChildren(heaviestChild.children);
143    };
144    const getParent = (list: PerfBottomUpStruct): void => {
145      if (list.parentNode && list.parentNode!.symbolName !== 'root') {
146        callStack.push(list.parentNode!);
147        getParent(list.parentNode!);
148      }
149    };
150
151    //@ts-ignore
152    const bottomUpData = evt.detail.data as PerfBottomUpStruct;
153    document.dispatchEvent(
154      new CustomEvent('number_calibration', {
155        detail: { time: bottomUpData.tsArray },
156      })
157    );
158    callStack!.push(bottomUpData);
159    if (bottomUpData.parentNode && bottomUpData.parentNode!.symbolName !== 'root') {
160      callStack.push(bottomUpData.parentNode!);
161      getParent(bottomUpData.parentNode!);
162    }
163    callStack.reverse();
164    getCallStackChildren(bottomUpData.children);
165    this.stackTable!.recycleDataSource = callStack;
166    bottomUpData.isSelected = true;
167    this.stackTable?.clearAllSelection(bottomUpData);
168    this.stackTable?.setCurrentSelection(bottomUpData);
169    // @ts-ignore
170    if (evt.detail.callBack) {
171      // @ts-ignore
172      evt.detail.callBack(true);
173    }
174  }
175
176  private bottomUpTableColumnClickHandler(evt: Event): void {
177    // @ts-ignore
178    this.sortKey = evt.detail.key;
179    // @ts-ignore
180    this.sortType = evt.detail.sort;
181    this.setBottomUpTableData(this.bottomUpSource);
182  }
183
184  private stackTableRowClick(evt: Event): void {
185    //@ts-ignore
186    const data = evt.detail.data as PerfBottomUpStruct;
187    data.isSelected = true;
188    this.bottomUpTable!.clearAllSelection(data);
189    this.bottomUpTable!.scrollToData(data);
190    // @ts-ignore
191    if (evt.detail.callBack) {
192      // @ts-ignore
193      evt.detail.callBack(true);
194    }
195  }
196
197  public connectedCallback(): void {
198    const tableOffsetHeight = 32;
199    const spanHeight = 22;
200    super.connectedCallback();
201    new ResizeObserver(() => {
202      // @ts-ignore
203      this.bottomUpTable?.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - tableOffsetHeight
204        }px`;
205      this.bottomUpTable?.reMeauseHeight();
206      // @ts-ignore
207      this.stackTable?.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - tableOffsetHeight - spanHeight
208        }px`;
209      this.stackTable?.reMeauseHeight();
210    }).observe(this.parentElement!);
211  }
212
213  private sortTree(arr: Array<PerfBottomUpStruct>): Array<PerfBottomUpStruct> {
214    const defaultSortType = 0;
215
216    function defaultSort(callTreeLeftData: PerfBottomUpStruct, callTreeRightData: PerfBottomUpStruct): number {
217      return callTreeRightData.totalTime - callTreeLeftData.totalTime;
218    }
219
220    const CallTreeSortArr = arr.sort((callTreeLeftData, callTreeRightData) => {
221      if (this.sortKey === 'selfTime' || this.sortKey === 'selfTimePercent') {
222        if (this.sortType === defaultSortType) {
223          return defaultSort(callTreeLeftData, callTreeRightData);
224        } else if (this.sortType === 1) {
225          return callTreeLeftData.selfTime - callTreeRightData.selfTime;
226        } else {
227          return callTreeRightData.selfTime - callTreeLeftData.selfTime;
228        }
229      } else if (this.sortKey === 'symbolName') {
230        if (this.sortType === defaultSortType) {
231          return defaultSort(callTreeLeftData, callTreeRightData);
232        } else if (this.sortType === 1) {
233          return `${callTreeLeftData.symbolName}`.localeCompare(`${callTreeRightData.symbolName}`);
234        } else {
235          return `${callTreeRightData.symbolName}`.localeCompare(`${callTreeLeftData.symbolName}`);
236        }
237      } else {
238        if (this.sortType === defaultSortType) {
239          return defaultSort(callTreeLeftData, callTreeRightData);
240        } else if (this.sortType === 1) {
241          return callTreeLeftData.totalTime - callTreeRightData.totalTime;
242        } else {
243          return callTreeRightData.totalTime - callTreeLeftData.totalTime;
244        }
245      }
246    });
247
248    CallTreeSortArr.map((call) => {
249      call.children = this.sortTree(call.children);
250    });
251    return CallTreeSortArr;
252  }
253
254  private initHtmlStyle(): string {
255    return `
256     <style>
257        :host{
258            display: flex;
259            flex-direction: column;
260            padding: 0 10px 0 10px;
261        }
262        .show-bottom-up{
263            display: flex;
264            flex: 1;
265        }
266        .perf-bottom-up-progress{
267            bottom: 33px;
268            position: absolute;
269            height: 1px;
270            left: 0;
271            right: 0;
272        }
273    </style>
274    `;
275  }
276
277  public initHtml(): string {
278    return `
279    ${this.initHtmlStyle()}
280    <div class="perf-bottom-up-content">
281    <selector id='show_table' class="show-bottom-up">
282        <lit-slicer style="width:100%">
283        <div id="left_table" style="width: 65%">
284            <lit-table id="callTreeTable" style="height: 100%" tree>
285                <lit-table-column width="50%" title="Symbol" data-index="symbolName" key="symbolName"
286                align="flex-start" order retract></lit-table-column>
287                <lit-table-column width="1fr" title="Local" data-index="selfTime" key="selfTime"
288                align="flex-start"  order></lit-table-column>
289                <lit-table-column width="1fr" title="%" data-index="selfTimePercent" key="selfTimePercent"
290                align="flex-start"  order></lit-table-column>
291                <lit-table-column width="1fr" title="Sample Count" data-index="totalTime" key="totalTime"
292                align="flex-start"  order></lit-table-column>
293                <lit-table-column width="1fr" title="%" data-index="totalTimePercent" key="totalTimePercent"
294                 align="flex-start"  order></lit-table-column>
295                <lit-table-column width="1fr" title="Event Count" data-index="eventCount" key="eventCount"
296                align="flex-start"  order></lit-table-column>
297                <lit-table-column width="1fr" title="%" data-index="eventPercent" key="eventPercent"
298                align="flex-start"  order></lit-table-column>
299            </lit-table>
300        </div>
301        <lit-slicer-track ></lit-slicer-track>
302        <div class="right" style="flex: 1;display: flex; flex-direction: row;">
303            <div style="flex: 1;display: block;">
304              <span slot="head" style="height: 22px">Call Stack</span>
305              <lit-table id="stackTable" style="height: auto;">
306                  <lit-table-column width="50%" title="Symbol" data-index="symbolName" key="symbolName"
307                   align="flex-start"></lit-table-column>
308                  <lit-table-column width="1fr" title="Sample Count" data-index="totalTime" key="totalTime"
309                   align="flex-start" ></lit-table-column>
310                  <lit-table-column width="1fr" title="%" data-index="totalTimePercent" key="totalTimePercent"
311                    align="flex-start"></lit-table-column>
312              </lit-table>
313          </div>
314        </div>
315        </lit-slicer>
316     </selector>
317     <tab-pane-filter id="filter" input inputLeftText ></tab-pane-filter>
318     <lit-progress-bar class="progress perf-bottom-up-progress"></lit-progress-bar>
319    </div>
320        `;
321  }
322}
323