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