• 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, getParentDetail, getFuncChildren } 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  private isDbClick: boolean = false;
36  set data(slicesParam: SelectionParam) {
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
46    //合并SF异步信息,相同pid和tid的name
47    let sfAsyncFuncMap: Map<string, { name: string[]; pid: number, tid: number | undefined }> = new Map();
48    slicesParam.funAsync.forEach((it: { name: string; pid: number, tid: number | undefined }) => {
49      if (sfAsyncFuncMap.has(`${it.pid}-${it.tid}`)) {
50        let item = sfAsyncFuncMap.get(`${it.pid}-${it.tid}`);
51        item?.name.push(it.name);
52      } else {
53        sfAsyncFuncMap.set(`${it.pid}-${it.tid}`, {
54          name: [it.name],
55          pid: it.pid,
56          tid: it.tid
57        });
58      }
59    });
60
61    let filterNameEL: HTMLInputElement | undefined | null =
62      this.shadowRoot?.querySelector<HTMLInputElement>('#filterName');
63    filterNameEL?.addEventListener('keyup', (ev) => {
64      if (ev.key.toLocaleLowerCase() === String.fromCharCode(47)) {
65        ev.stopPropagation();
66      }
67    });
68
69    this.getSliceDb(slicesParam, filterNameEL, sfAsyncFuncMap, slicesParam.funCatAsync);
70  }
71
72  initElements(): void {
73    this.sliceSearchCount = this.shadowRoot?.querySelector<LitTable>('#search-count');
74    this.slicesTbl = this.shadowRoot?.querySelector<LitTable>('#tb-slices');
75    this.slicesRange = this.shadowRoot?.querySelector('#time-range');
76    let slicesInput = this.shadowRoot?.querySelector('#filterName');
77    let spApplication = document.querySelector('body > sp-application');
78    let spSystemTrace = spApplication?.shadowRoot?.querySelector(
79      'div > div.content > sp-system-trace'
80    ) as SpSystemTrace;
81    this.slicesTbl!.addEventListener('column-click', (evt) => {
82      // @ts-ignore
83      this.sortByColumn(evt.detail);
84    });
85    // @ts-ignore
86    let data;
87    this.slicesTbl!.addEventListener('row-click', (evt) => {
88      // @ts-ignore
89      data = evt.detail.data;
90    });
91    this.slicesTbl!.addEventListener('click', () => {
92      if (!this.isDbClick) {
93        this.isDbClick = true;
94        FuncStruct.funcSelect = false;
95        // @ts-ignore
96        data && this.orgnazitionData(data, false);
97      }
98    });
99    this.slicesTbl!.addEventListener('contextmenu', () => {
100      FuncStruct.funcSelect = true;
101      // @ts-ignore
102      data && this.orgnazitionData(data);
103    });
104    slicesInput?.addEventListener('input', (e) => {
105      // @ts-ignore
106      this.findName(e.target.value);
107    });
108  }
109
110  getSliceDb(
111    slicesParam: SelectionParam,
112    filterNameEL: HTMLInputElement | undefined | null,
113    sfAsyncFuncMap: Map<string, { name: string[]; pid: number, tid: number | undefined }>,
114    ghAsyncFunc: { threadName: string; pid: number }[]): void {
115    //获取SF异步Func数据
116    let result1 = (): Array<unknown> => {
117      let promises: unknown[] = [];
118      sfAsyncFuncMap.forEach(async (item: { name: string[]; pid: number, tid: number | undefined }) => {
119        let res = await getTabSlicesAsyncFunc(item.name, item.pid, item.tid, slicesParam.leftNs, slicesParam.rightNs);
120        if (res !== undefined && res.length > 0) {
121          promises.push(...res);
122        }
123      });
124      return promises;
125    };
126
127    //获取GH异步Func数据
128    let result2 = (): Array<unknown> => {
129      let promises: unknown[] = [];
130      ghAsyncFunc.forEach(async (item: { pid: number; threadName: string }) => {
131        let res = await getTabSlicesAsyncCatFunc(item.threadName, item.pid, slicesParam.leftNs, slicesParam.rightNs);
132        if (res !== undefined && res.length > 0) {
133          promises.push(...res);
134        }
135      });
136      return promises;
137    };
138
139    //获取同步Func数据
140    let result3 = async (): Promise<unknown> => {
141      let promises: unknown[] = [];
142      let res = await getTabSlices(slicesParam.funTids, slicesParam.processIds, slicesParam.leftNs, slicesParam.rightNs);
143      if (res !== undefined && res.length > 0) {
144        promises.push(...res);
145      }
146      return promises;
147    };
148
149    this.slicesTbl!.loading = true;
150    Promise.all([result1(), result2(), result3()]).then(async res => {
151      let processSlicesResult = (res[0] || []).concat(res[1] || []).concat(res[2] || []);
152      if (processSlicesResult !== null && processSlicesResult.length > 0) {
153        let funcIdArr: Array<number> = [];
154        let minStartTS = Infinity;
155        let maxEndTS = -Infinity;
156        // @ts-ignore
157        let parentDetail: [{
158          startTS: number,
159          endTS: number,
160          depth: number,
161          id: number,
162          name: string
163        }] = await getParentDetail(
164          slicesParam.processIds,
165          slicesParam.funTids,
166          slicesParam.leftNs,
167          slicesParam.rightNs
168        );
169        // @ts-ignore
170        parentDetail.forEach(item => {
171          funcIdArr.push(item.id);
172          if (item.depth === 0) {
173            if (item.startTS < minStartTS) {
174              minStartTS = item.startTS;
175            }
176            if (item.endTS > maxEndTS) {
177              maxEndTS = item.endTS;
178            }
179          }
180        });
181
182        let FuncChildrenList = await getFuncChildren(funcIdArr, slicesParam.processIds, slicesParam.funTids, minStartTS, maxEndTS, false);
183        let childDurMap: Map<number, Map<number, number>> = new Map();
184        FuncChildrenList.forEach((it: unknown) => { // @ts-ignore
185          if (!childDurMap.has(it.parentId)) { // @ts-ignore
186            childDurMap.set(it.parentId, it.duration);
187          } else { // @ts-ignore
188            let dur = childDurMap.get(it.parentId); // @ts-ignore
189            dur += it.duration; // @ts-ignore
190            childDurMap.set(it.parentId, dur!);
191          }
192        });
193        let sumWall = 0.0;
194        let sumOcc = 0;
195        let sumSelf = 0.0;
196        let processSlicesResultMap: Map<string, unknown> = new Map();
197        for (let processSliceItem of processSlicesResult) {
198          //@ts-ignore
199          processSliceItem.selfTime = //@ts-ignore
200          childDurMap.has(processSliceItem.id) ? //@ts-ignore
201          parseFloat(((processSliceItem.wallDuration - childDurMap.get(processSliceItem.id)) / 1000000).toFixed(5)) : //@ts-ignore
202          parseFloat((processSliceItem.wallDuration / 1000000).toFixed(5));
203          //@ts-ignore
204          processSliceItem.name = processSliceItem.name === null ? '' : processSliceItem.name;
205          //@ts-ignore
206          sumWall += processSliceItem.wallDuration;
207          //@ts-ignore
208          sumOcc += processSliceItem.occurrences;
209          //@ts-ignore
210          sumSelf += processSliceItem.selfTime;
211          //@ts-ignore
212          processSliceItem.wallDuration = parseFloat((processSliceItem.wallDuration / 1000000.0).toFixed(5));
213          //@ts-ignore
214          if (processSlicesResultMap.has(processSliceItem.name)) {//@ts-ignore
215            let item = processSlicesResultMap.get(processSliceItem.name);
216            //@ts-ignore
217            item.occurrences = item.occurrences + processSliceItem.occurrences;
218            //@ts-ignore
219            item.wallDuration = parseFloat((item.wallDuration + processSliceItem.wallDuration).toFixed(5));
220          } else {
221            //@ts-ignore
222            processSlicesResultMap.set(processSliceItem.name, {//@ts-ignore
223              ...processSliceItem, //@ts-ignore
224              tabTitle: processSliceItem.name
225            });
226          }
227        }
228        let processSlicesResultsValue = [...processSlicesResultMap.values()];
229        processSlicesResultsValue.forEach(element => {//@ts-ignore
230          element.avgDuration = parseFloat((element.wallDuration / element.occurrences).toFixed(5));
231        });
232        let count = new SelectionData();
233        count.process = ' ';
234        count.wallDuration = parseFloat((sumWall / 1000000.0).toFixed(5));
235        count.occurrences = sumOcc;
236        count.selfTime = parseFloat(sumSelf.toFixed(5));
237        count.tabTitle = 'Summary';
238        //@ts-ignore
239        count.avgDuration = parseFloat((count.wallDuration / count.occurrences).toFixed(5));
240        // @ts-ignore
241        count.allName = processSlicesResultsValue.map((item: unknown) => item.name);
242        processSlicesResultsValue.splice(0, 0, count); //@ts-ignore
243        this.slicesSource = processSlicesResultsValue;
244        this.slicesTbl!.recycleDataSource = processSlicesResultsValue;
245        this.sliceSearchCount!.textContent = this.slicesSource.length - 1 + '';
246        if (filterNameEL && filterNameEL.value.trim() !== '') {
247          this.findName(filterNameEL.value);
248        }
249      } else {
250        this.slicesSource = [];
251        this.slicesTbl!.recycleDataSource = this.slicesSource;
252        this.sliceSearchCount!.textContent = '0';
253      }
254      this.slicesTbl!.loading = false;
255    });
256  }
257  // @ts-ignore
258  async orgnazitionData(data: Object, isDbClick?: false): unknown {
259    // @ts-ignore
260    if (data!.tabTitle === 'Summary') {
261      FuncStruct.funcSelect = true;
262    }
263    let spApplication = document.querySelector('body > sp-application');
264    let spSystemTrace = spApplication?.shadowRoot?.querySelector(
265      'div > div.content > sp-system-trace'
266    ) as SpSystemTrace;
267    let search = spApplication!.shadowRoot?.querySelector('#lit-search') as LitSearch;
268    spSystemTrace?.visibleRows.forEach((it) => {
269      it.highlight = false;
270    });
271    spSystemTrace?.timerShaftEL?.removeTriangle('inverted');
272    // @ts-ignore
273    let asyncFuncArr = spSystemTrace!.seachAsyncFunc(data.name);
274    // @ts-ignore
275    await spSystemTrace!.searchFunction([], asyncFuncArr, data.name).then((mixedResults) => {
276      if (mixedResults && mixedResults.length === 0) {
277        FuncStruct.funcSelect = true;
278        if (!isDbClick) {
279          this.isDbClick = false;
280        }
281        return;
282      }
283      // @ts-ignore
284      search.list = mixedResults.filter((item) => item.funName === data.name); //@ts-ignore
285      const sliceRowList: Array<TraceRow<unknown>> = [];
286      // 框选的slice泳道
287      for (let row of spSystemTrace.rangeSelect.rangeTraceRow!) {
288        if (row.rowType === 'func') {
289          sliceRowList.push(row);
290        }
291        if (row.childrenList) {
292          for (const childrenRow of row.childrenList) {
293            if (childrenRow.rowType === 'func') {
294              sliceRowList.push(childrenRow);
295            }
296          }
297        }
298      }
299      if (sliceRowList.length === 0) {
300        return;
301      }
302      this.slicesTblFreshSearchSelect(search, sliceRowList, data, spSystemTrace);
303      spSystemTrace?.visibleRows.forEach((it) => {
304        it.draw();
305        this.isDbClick = false;
306      });
307    });
308  }
309
310  private slicesTblFreshSearchSelect(
311    search: LitSearch, //@ts-ignore
312    sliceRowList: Array<TraceRow<unknown>>,
313    data: unknown,
314    spSystemTrace: SpSystemTrace
315  ): void {
316    let input = search.shadowRoot?.querySelector('input') as HTMLInputElement;
317    let rangeSelectList: Array<unknown> = []; // 框选范围的数据
318    // search 到的内容与框选泳道的内容取并集
319    for (const searchItem of search.list) {
320      for (const traceRow of sliceRowList) {
321        if (
322          // @ts-ignore
323          Math.max(TraceRow.rangeSelectObject?.startNS!, searchItem.startTime) <=
324          // @ts-ignore
325          Math.min(TraceRow.rangeSelectObject?.endNS!, searchItem.startTime + searchItem.dur) &&
326          !rangeSelectList.includes(searchItem)
327        ) {
328          // 异步调用栈
329          if (traceRow.asyncFuncName) {
330            if (
331              // @ts-ignore
332              `${searchItem.pid}` === `${traceRow.asyncFuncNamePID}` &&
333              traceRow.traceId === Utils.currentSelectTrace
334            ) {
335              rangeSelectList.push(searchItem);
336            }
337          } else {
338            // 线程调用栈
339            // @ts-ignore
340            if (Utils.getDistributedRowId(searchItem.tid) === traceRow.rowId) {
341              rangeSelectList.push(searchItem);
342            }
343          }
344        }
345      }
346    }
347
348    if (rangeSelectList.length === 0) {
349      return;
350    } //@ts-ignore
351    input.value = data.name;
352    //@ts-ignore
353    search.currenSearchValue = data.name;
354    search.list = rangeSelectList;
355    search.total = search.list.length;
356    search.index = spSystemTrace!.showStruct(false, -1, search.list);
357    search.isClearValue = true;
358  }
359
360  connectedCallback(): void {
361    super.connectedCallback();
362    resizeObserver(this.parentElement!, this.slicesTbl!);
363  }
364
365  initHtml(): string {
366    return `
367        <style>
368        .slice-label{
369            height: 20px;
370        }
371        :host{
372            display: flex;
373            padding: 10px 10px;
374            flex-direction: column;
375        }
376        #filterName:focus{
377          outline: none;
378        }
379        </style>
380        <div style="display:flex; justify-content:space-between;">
381        <div style="width: 40%;">
382          <input id="filterName" type="text" style="width:60%;height:18px;border:1px solid #c3c3c3;border-radius:9px" placeholder="Search" value="" />
383          &nbsp;&nbsp;<span style="font-size: 10pt;margin-bottom: 5px">Count:&nbsp;<span id="search-count">0<span></span>
384        </div>
385        <label id="time-range" class="slice-label" style="text-align: end;font-size: 10pt;margin-bottom: 5px">Selected range:0.0 ms</label>
386        </div>
387        <lit-table id="tb-slices" style="height: auto">
388            <lit-table-column class="slices-column" title="Name" width="500px" data-index="name"
389            key="name"  align="flex-start" order>
390            </lit-table-column>
391            <lit-table-column class="slices-column" title="Wall duration(ms)" width="1fr" data-index="wallDuration"
392            key="wallDuration"  align="flex-start" order >
393            </lit-table-column>
394            <lit-table-column class="slices-column" title="Avg Wall duration(ms)" width="1fr" data-index="avgDuration"
395            key="avgDuration"  align="flex-start" order >
396            </lit-table-column>
397            <lit-table-column class="slices-column" title="Occurrences" width="1fr" data-index="occurrences"
398            key="occurrences"  align="flex-start" order tdJump>
399            </lit-table-column>
400            <lit-table-column class="slices-column" title="selfTime(ms)" width="1fr" data-index="selfTime" key="selfTime" align="flex-start" order>
401            </lit-table-column>
402        </lit-table>
403        `;
404  }
405
406  sortByColumn(slicesDetail: unknown): void {
407    // @ts-ignore
408    function compare(property, slicesSort, type) {
409      return function (slicesLeftData: SelectionData, slicesRightData: SelectionData) {
410        if (slicesLeftData.process === ' ' || slicesRightData.process === ' ') {
411          return 0;
412        }
413        if (type === 'number') {
414          // @ts-ignore
415          return slicesSort === 2
416            ? // @ts-ignore
417            parseFloat(slicesRightData[property]) - parseFloat(slicesLeftData[property])
418            : // @ts-ignore
419            parseFloat(slicesLeftData[property]) - parseFloat(slicesRightData[property]);
420        } else {
421          // @ts-ignore
422          if (slicesRightData[property] > slicesLeftData[property]) {
423            return slicesSort === 2 ? 1 : -1;
424          } else {
425            // @ts-ignore
426            if (slicesRightData[property] === slicesLeftData[property]) {
427              return 0;
428            } else {
429              return slicesSort === 2 ? -1 : 1;
430            }
431          }
432        }
433      };
434    }
435    // 拷贝当前表格显示的数据
436    let sortData: Array<SelectionData> = JSON.parse(JSON.stringify(this.slicesTbl!.recycleDataSource));
437    // 取出汇总数据,同时将排序数据去掉汇总数据进行后续排序
438    let headData: SelectionData = sortData.splice(0, 1)[0];
439    //@ts-ignore
440    if (slicesDetail.key === 'name') {
441      //@ts-ignore
442      sortData.sort(compare(slicesDetail.key, slicesDetail.sort, 'string'));
443    } else {
444      //@ts-ignore
445      sortData.sort(compare(slicesDetail.key, slicesDetail.sort, 'number'));
446    }
447    // 排序完成后将汇总数据插入到头部
448    sortData.unshift(headData);
449    this.slicesTbl!.recycleDataSource = sortData;
450    this.sliceSearchCount!.textContent = sortData.length - 1 + '';
451  }
452
453  findName(str: string): void {
454    let searchData: Array<SelectionData> = [];
455    let sumWallDuration: number = 0;
456    let sumOccurrences: number = 0;
457    let nameSet: Array<string> = [];
458    if (str === '') {
459      this.slicesTbl!.recycleDataSource = this.slicesSource;
460      this.sliceSearchCount!.textContent = this.slicesSource.length - 1 + '';
461    } else {
462      this.slicesSource.forEach((item) => {
463        if (item.name.toLowerCase().indexOf(str.toLowerCase()) !== -1) {
464          searchData.push(item);
465          nameSet.push(item.name);
466          sumWallDuration += item.wallDuration;
467          sumOccurrences += item.occurrences;
468        }
469      });
470      let count: SelectionData = new SelectionData();
471      count.process = '';
472      count.name = '';
473      count.allName = nameSet;
474      count.tabTitle = 'Summary';
475      count.wallDuration = Number(sumWallDuration.toFixed(3));
476      count.occurrences = sumOccurrences;
477      searchData.unshift(count);
478      this.slicesTbl!.recycleDataSource = searchData;
479      this.sliceSearchCount!.textContent = searchData.length - 1 + '';
480    }
481  }
482}
483