• 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 { LitTable } from '../../../../../base-ui/table/lit-table';
18import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection';
19import { SpSystemTrace } from '../../../SpSystemTrace';
20import { TraceRow } from '../../base/TraceRow';
21import { LitSearch } from '../../search/Search';
22import { resizeObserver } from '../SheetUtils';
23import { getTabSlicesAsyncFunc, getTabSlicesAsyncCatFunc } from '../../../../database/sql/Func.sql';
24import { getTabSlices } from '../../../../database/sql/ProcessThread.sql';
25import { FuncStruct } from '../../../../database/ui-worker/ProcedureWorkerFunc';
26import { Utils } from '../../base/Utils';
27
28@element('tabpane-slices')
29export class TabPaneSlices extends BaseElement {
30  private slicesTbl: LitTable | null | undefined;
31  private slicesRange: HTMLLabelElement | null | undefined;
32  private slicesSource: Array<SelectionData> = [];
33  private currentSelectionParam: SelectionParam | undefined;
34  private sliceSearchCount: Element | undefined | null;
35
36  set data(slicesParam: SelectionParam | unknown) {
37    if (this.currentSelectionParam === slicesParam) {
38      return;
39    } //@ts-ignore
40    this.currentSelectionParam = slicesParam;
41    this.slicesRange!.textContent = `Selected range: ${parseFloat(
42      //@ts-ignore
43      ((slicesParam.rightNs - slicesParam.leftNs) / 1000000.0).toFixed(5)
44    )} ms`;
45    let asyncNames: Array<string> = [];
46    let asyncPid: Array<number> = []; //@ts-ignore
47    slicesParam.funAsync.forEach((it: unknown) => {
48      //@ts-ignore
49      asyncNames.push(it.name); //@ts-ignore
50      asyncPid.push(it.pid);
51    });
52    let asyncCatNames: Array<string> = [];
53    let asyncCatPid: Array<number> = [];//@ts-ignore
54    slicesParam.funCatAsync.forEach((it: unknown) => { //@ts-ignore
55      asyncCatNames.push(it.threadName);//@ts-ignore
56      asyncCatPid.push(it.pid);
57    });
58    this.slicesTbl!.loading = true;
59    let filterNameEL: HTMLInputElement | undefined | null =
60      this.shadowRoot?.querySelector<HTMLInputElement>('#filterName');
61    filterNameEL?.addEventListener('keyup', (ev) => {
62      if (ev.key.toLocaleLowerCase() === String.fromCharCode(47)) {
63        ev.stopPropagation();
64      }
65    });
66    //@ts-ignore
67    getTabSlicesAsyncFunc(asyncNames, asyncPid, slicesParam.leftNs, slicesParam.rightNs).then((res) => {//@ts-ignore
68      getTabSlicesAsyncCatFunc(asyncCatNames, asyncCatPid, slicesParam.leftNs, slicesParam.rightNs).then((res1) => {
69        //@ts-ignore
70        getTabSlices(slicesParam.funTids, slicesParam.processIds, slicesParam.leftNs, slicesParam.rightNs).then(
71          (res2) => {
72            this.slicesTbl!.loading = false;
73            let processSlicesResult = (res || []).concat(res1 || []).concat(res2 || []);
74            if (processSlicesResult !== null && processSlicesResult.length > 0) {
75              let sumWall = 0.0;
76              let sumOcc = 0;
77              for (let processSliceItem of processSlicesResult) {
78                //@ts-ignore
79                processSliceItem.name = processSliceItem.name === null ? '' : processSliceItem.name;
80                //@ts-ignore
81                processSliceItem.tabTitle = processSliceItem.name;
82                //@ts-ignore
83                sumWall += processSliceItem.wallDuration;
84                //@ts-ignore
85                sumOcc += processSliceItem.occurrences;
86                //@ts-ignore
87                processSliceItem.wallDuration = parseFloat((processSliceItem.wallDuration / 1000000.0).toFixed(5));
88                //@ts-ignore
89                processSliceItem.avgDuration = parseFloat((processSliceItem.avgDuration / 1000000.0).toFixed(5));
90                //@ts-ignore
91                processSliceItem.asyncNames = asyncNames;
92                //@ts-ignore
93                processSliceItem.asyncCatNames = asyncCatNames;
94              }
95              let count = new SelectionData();
96              count.process = ' ';
97              count.wallDuration = parseFloat((sumWall / 1000000.0).toFixed(5));
98              count.occurrences = sumOcc;
99              count.tabTitle = 'Summary';//@ts-ignore
100              count.allName = processSlicesResult.map((item: unknown) => item.name);
101              count.asyncNames = asyncNames;
102              count.asyncCatNames = asyncCatNames;
103              processSlicesResult.splice(0, 0, count); //@ts-ignore
104              this.slicesSource = processSlicesResult;
105              this.slicesTbl!.recycleDataSource = processSlicesResult;
106              this.sliceSearchCount!.textContent = this.slicesSource.length - 1 + '';
107              if (filterNameEL && filterNameEL.value.trim() !== '') {
108                this.findName(filterNameEL.value);
109              }
110            } else {
111              this.slicesSource = [];
112              this.slicesTbl!.recycleDataSource = this.slicesSource;
113              this.sliceSearchCount!.textContent = '0';
114            }
115          }
116        );
117      });
118    });
119  }
120
121  initElements(): void {
122    this.sliceSearchCount = this.shadowRoot?.querySelector<LitTable>('#search-count');
123    this.slicesTbl = this.shadowRoot?.querySelector<LitTable>('#tb-slices');
124    this.slicesRange = this.shadowRoot?.querySelector('#time-range');
125    let slicesInput = this.shadowRoot?.querySelector('#filterName');
126    let spApplication = document.querySelector('body > sp-application');
127    let spSystemTrace = spApplication?.shadowRoot?.querySelector(
128      'div > div.content > sp-system-trace'
129    ) as SpSystemTrace;
130    this.slicesTbl!.addEventListener('column-click', (evt) => {
131      // @ts-ignore
132      this.sortByColumn(evt.detail);
133    });
134    // @ts-ignore
135    let data;
136    this.slicesTbl!.addEventListener('row-click', (evt) => {
137      // @ts-ignore
138      data = evt.detail.data;
139    });
140    this.slicesTbl!.addEventListener('click', () => {
141      FuncStruct.funcSelect = false;
142      // @ts-ignore
143      data && this.orgnazitionData(data);
144    });
145    this.slicesTbl!.addEventListener('contextmenu', () => {
146      FuncStruct.funcSelect = true;
147      // @ts-ignore
148      data && this.orgnazitionData(data);
149    });
150    slicesInput?.addEventListener('input', (e) => {
151      // @ts-ignore
152      this.findName(e.target.value);
153    });
154    slicesInput?.addEventListener('focus', (e) => {
155      spSystemTrace.focusTarget = 'slicesInput';
156    });
157    slicesInput?.addEventListener('blur', (e) => {
158      spSystemTrace.focusTarget = '';
159    });
160  }
161  async orgnazitionData(data: Object): Promise<void> {
162    let spApplication = document.querySelector('body > sp-application');
163    let spSystemTrace = spApplication?.shadowRoot?.querySelector(
164      'div > div.content > sp-system-trace'
165    ) as SpSystemTrace;
166    let search = spApplication!.shadowRoot?.querySelector('#lit-search') as LitSearch;
167    spSystemTrace?.visibleRows.forEach((it) => {
168      it.highlight = false;
169      it.draw();
170    });
171    spSystemTrace?.timerShaftEL?.removeTriangle('inverted');
172    // @ts-ignore
173    let asyncFuncArr = spSystemTrace!.seachAsyncFunc(data.name);
174    // @ts-ignore
175    await spSystemTrace!.searchFunction([], asyncFuncArr, data.name).then((mixedResults) => {
176      if (mixedResults && mixedResults.length === 0) {
177        return;
178      }
179      // @ts-ignore
180      search.list = mixedResults.filter((item) => item.funName === data.name); //@ts-ignore
181      const sliceRowList: Array<TraceRow<unknown>> = [];
182      // 框选的slice泳道
183      for (let row of spSystemTrace.rangeSelect.rangeTraceRow!) {
184        if (row.rowType === 'func') {
185          sliceRowList.push(row);
186        }
187        if (row.childrenList) {
188          for (const childrenRow of row.childrenList) {
189            if (childrenRow.rowType === 'func') {
190              sliceRowList.push(childrenRow);
191            }
192          }
193        }
194      }
195      if (sliceRowList.length === 0) {
196        return;
197      }
198      this.slicesTblFreshSearchSelect(search, sliceRowList, data, spSystemTrace);
199    });
200  }
201
202  private slicesTblFreshSearchSelect(
203    search: LitSearch, //@ts-ignore
204    sliceRowList: Array<TraceRow<unknown>>,
205    data: unknown,
206    spSystemTrace: SpSystemTrace
207  ): void {
208    let input = search.shadowRoot?.querySelector('input') as HTMLInputElement;
209    let rangeSelectList: Array<unknown> = []; // 框选范围的数据
210    // search 到的内容与框选泳道的内容取并集
211    for (const searchItem of search.list) {
212      for (const traceRow of sliceRowList) {
213        if (
214          // @ts-ignore
215          Math.max(TraceRow.rangeSelectObject?.startNS!, searchItem.startTime) <=
216          // @ts-ignore
217          Math.min(TraceRow.rangeSelectObject?.endNS!, searchItem.startTime + searchItem.dur) &&
218          !rangeSelectList.includes(searchItem)
219        ) {
220          // 异步调用栈
221          if (traceRow.asyncFuncName) {
222            if (
223              // @ts-ignore
224              `${searchItem.pid}` === `${traceRow.asyncFuncNamePID}` &&
225              traceRow.traceId === Utils.currentSelectTrace
226            ) {
227              rangeSelectList.push(searchItem);
228            }
229          } else {
230            // 线程调用栈
231            // @ts-ignore
232            if (Utils.getDistributedRowId(searchItem.tid) === traceRow.rowId) {
233              rangeSelectList.push(searchItem);
234            }
235          }
236        }
237      }
238    }
239
240    if (rangeSelectList.length === 0) {
241      return;
242    } //@ts-ignore
243    input.value = data.name;
244    //@ts-ignore
245    search.currenSearchValue = data.name;
246    search.list = rangeSelectList;
247    search.total = search.list.length;
248    search.index = spSystemTrace!.showStruct(false, -1, search.list);
249    search.isClearValue = true;
250  }
251
252  connectedCallback(): void {
253    super.connectedCallback();
254    resizeObserver(this.parentElement!, this.slicesTbl!);
255  }
256
257  initHtml(): string {
258    return `
259        <style>
260        .slice-label{
261            height: 20px;
262        }
263        :host{
264            display: flex;
265            padding: 10px 10px;
266            flex-direction: column;
267        }
268        #filterName:focus{
269          outline: none;
270        }
271        </style>
272        <div style="display:flex; justify-content:space-between;">
273        <div style="width: 40%;">
274          <input id="filterName" type="text" style="width:60%;height:18px;border:1px solid #c3c3c3;border-radius:9px" placeholder="Search" value="" />
275          &nbsp;&nbsp;<span style="font-size: 10pt;margin-bottom: 5px">Count:&nbsp;<span id="search-count">0<span></span>
276        </div>
277        <label id="time-range" class="slice-label" style="text-align: end;font-size: 10pt;margin-bottom: 5px">Selected range:0.0 ms</label>
278        </div>
279        <lit-table id="tb-slices" style="height: auto">
280            <lit-table-column class="slices-column" title="Name" width="500px" data-index="name"
281            key="name"  align="flex-start" order>
282            </lit-table-column>
283            <lit-table-column class="slices-column" title="Wall duration(ms)" width="1fr" data-index="wallDuration"
284            key="wallDuration"  align="flex-start" order >
285            </lit-table-column>
286            <lit-table-column class="slices-column" title="Avg Wall duration(ms)" width="1fr" data-index="avgDuration"
287            key="avgDuration"  align="flex-start" order >
288            </lit-table-column>
289            <lit-table-column class="slices-column" title="Occurrences" width="1fr" data-index="occurrences"
290            key="occurrences"  align="flex-start" order tdJump>
291            </lit-table-column>
292        </lit-table>
293        `;
294  }
295
296  sortByColumn(slicesDetail: unknown): void {
297    // @ts-ignore
298    function compare(property, slicesSort, type) {
299      return function (slicesLeftData: SelectionData, slicesRightData: SelectionData) {
300        if (slicesLeftData.process === ' ' || slicesRightData.process === ' ') {
301          return 0;
302        }
303        if (type === 'number') {
304          // @ts-ignore
305          return slicesSort === 2
306            ? // @ts-ignore
307            parseFloat(slicesRightData[property]) - parseFloat(slicesLeftData[property])
308            : // @ts-ignore
309            parseFloat(slicesLeftData[property]) - parseFloat(slicesRightData[property]);
310        } else {
311          // @ts-ignore
312          if (slicesRightData[property] > slicesLeftData[property]) {
313            return slicesSort === 2 ? 1 : -1;
314          } else {
315            // @ts-ignore
316            if (slicesRightData[property] === slicesLeftData[property]) {
317              return 0;
318            } else {
319              return slicesSort === 2 ? -1 : 1;
320            }
321          }
322        }
323      };
324    }
325    // 拷贝当前表格显示的数据
326    let sortData: Array<SelectionData> = JSON.parse(JSON.stringify(this.slicesTbl!.recycleDataSource));
327    // 取出汇总数据,同时将排序数据去掉汇总数据进行后续排序
328    let headData: SelectionData = sortData.splice(0, 1)[0];
329    //@ts-ignore
330    if (slicesDetail.key === 'name') {
331      //@ts-ignore
332      sortData.sort(compare(slicesDetail.key, slicesDetail.sort, 'string'));
333    } else {
334      //@ts-ignore
335      sortData.sort(compare(slicesDetail.key, slicesDetail.sort, 'number'));
336    }
337    // 排序完成后将汇总数据插入到头部
338    sortData.unshift(headData);
339    this.slicesTbl!.recycleDataSource = sortData;
340    this.sliceSearchCount!.textContent = sortData.length - 1 + '';
341  }
342
343  findName(str: string): void {
344    let searchData: Array<SelectionData> = [];
345    let sumWallDuration: number = 0;
346    let sumOccurrences: number = 0;
347    let nameSet: Array<string> = [];
348    if (str === '') {
349      this.slicesTbl!.recycleDataSource = this.slicesSource;
350      this.sliceSearchCount!.textContent = this.slicesSource.length - 1 + '';
351    } else {
352      this.slicesSource.forEach((item) => {
353        if (item.name.toLowerCase().indexOf(str.toLowerCase()) !== -1) {
354          searchData.push(item);
355          nameSet.push(item.name);
356          sumWallDuration += item.wallDuration;
357          sumOccurrences += item.occurrences;
358        }
359      });
360      let count: SelectionData = new SelectionData();
361      count.process = '';
362      count.name = '';
363      count.allName = nameSet;
364      count.tabTitle = 'Summary';
365      count.wallDuration = Number(sumWallDuration.toFixed(3));
366      count.occurrences = sumOccurrences;
367      searchData.unshift(count);
368      this.slicesTbl!.recycleDataSource = searchData;
369      this.sliceSearchCount!.textContent = searchData.length - 1 + '';
370    }
371  }
372}
373