• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2024 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, SliceBoxJumpParam } from '../../../../bean/BoxSelection';
19import { Utils } from '../../base/Utils';
20import { resizeObserver } from '../SheetUtils';
21import { getTabDetails, getGhDetails, getSfDetails, getParentDetail, getFuncChildren } from '../../../../database/sql/Func.sql';
22
23@element('box-slice-child')
24export class TabPaneSliceChild extends BaseElement {
25  private sliceChildTbl: LitTable | null | undefined;
26  private boxChildSource: Array<unknown> = [];
27  private sliceChildParam: { param: SliceBoxJumpParam, selection: SelectionParam } | null | undefined;
28
29  set data(boxChildValue: { param: SliceBoxJumpParam, selection: SelectionParam | null | undefined }) {
30    //切换Tab页 保持childTab数据不变 除非重新点击跳转
31    if (boxChildValue === this.sliceChildParam || !boxChildValue.param.isJumpPage) {
32      return;
33    }
34    // @ts-ignore
35    this.sliceChildParam = boxChildValue;
36    this.sliceChildTbl!.recycleDataSource = [];
37    //合并SF异步信息,相同pid和tid的name
38    let sfAsyncFuncMap: Map<string, { name: string[]; pid: number, tid: number | undefined }> = new Map();
39    let filterSfAsyncFuncName = boxChildValue.selection!.funAsync;
40    if (boxChildValue.param.name) {
41      filterSfAsyncFuncName = filterSfAsyncFuncName.filter((item) =>
42          boxChildValue.param.name?.some((boxItem) => item.name === boxItem)
43      );
44    }
45    filterSfAsyncFuncName.forEach((it: { name: string; pid: number, tid: number | undefined }) => {
46      if (sfAsyncFuncMap.has(`${it.pid}-${it.tid}`)) {
47        let item = sfAsyncFuncMap.get(`${it.pid}-${it.tid}`);
48        item?.name.push(it.name);
49      } else {
50        sfAsyncFuncMap.set(`${it.pid}-${it.tid}`, {
51          name: [it.name],
52          pid: it.pid,
53          tid: it.tid
54        });
55      }
56    });
57    //@ts-ignore
58    this.getDataByDB(boxChildValue, sfAsyncFuncMap, boxChildValue.selection!.funCatAsync);
59  }
60
61  initElements(): void {
62    this.sliceChildTbl = this.shadowRoot?.querySelector<LitTable>('#tb-slice-child');
63    this.sliceChildTbl!.addEventListener('column-click', (evt): void => {
64      // @ts-ignore
65      this.sortByColumn(evt.detail);
66    });
67    //监听row的点击事件,在对应起始时间上画标记棋子
68    this.sliceChildTbl!.addEventListener('row-click', (evt): void => {
69      //@ts-ignore
70      let param = evt.detail.data;
71      param.isSelected = true;
72      this.sliceChildTbl!.clearAllSelection(param);
73      this.sliceChildTbl!.setCurrentSelection(param);
74      document.dispatchEvent(
75        new CustomEvent('triangle-flag', {
76          detail: { time: [param.startNs], type: 'triangle' },
77        })
78      );
79    });
80  }
81
82  connectedCallback(): void {
83    super.connectedCallback();
84    resizeObserver(this.parentElement!, this.sliceChildTbl!, 25);
85  }
86
87  getDataByDB(
88    val: { param: SliceBoxJumpParam, selection: SelectionParam },
89    sfAsyncFuncMap: Map<string, { name: string[]; pid: number, tid: number | undefined }>,
90    ghAsyncFunc: { threadName: string; pid: number }[]): void {
91    //获取点击跳转,SF异步Func数据
92    let result1 = (): Array<unknown> => {
93      let promises: unknown[] = [];
94      sfAsyncFuncMap.forEach(async (item: { name: string[]; pid: number, tid: number | undefined }) => {
95        let res = await getSfDetails(item.name, item.pid, item.tid, val.param.leftNs, val.param.rightNs);
96        if (res !== undefined && res.length > 0) {
97          promises.push(...res);
98        }
99      });
100      return promises;
101    };
102
103    //获取点击跳转,GH异步Func数据
104    let result2 = (): unknown => {
105      let promises: unknown[] = [];
106      ghAsyncFunc.forEach(async (item: { pid: number; threadName: string }) => {
107        let res = await getGhDetails(val.param.name!, item.threadName, item.pid, val.param.leftNs, val.param.rightNs);
108        if (res !== undefined && res.length > 0) {
109          promises.push(...res);
110        }
111      });
112      return promises;
113    };
114
115    //获取同步Func数据,同步Func数据
116    let result3 = async (): Promise<unknown> => {
117      let promises: unknown[] = [];
118      let res = await getTabDetails(val.param.name!, val.param.processId, val.param.threadId, val.param.leftNs, val.param.rightNs);
119      if (res !== undefined && res.length > 0) {
120        promises.push(...res);
121      }
122      return promises;
123    };
124    this.sliceChildTbl!.loading = true;
125    Promise.all([result1(), result2(), result3()]).then(async res => {
126      this.sliceChildTbl!.loading = false;
127      let result: unknown = (res[0] || []).concat(res[1] || []).concat(res[2] || []);
128      this.sliceChildTbl!.loading = false;
129      // @ts-ignore
130      if (result.length !== null && result.length > 0) {
131        let funcIdArr: Array<number> = [];
132        let minStartTS = Infinity;
133        let maxEndTS = -Infinity;
134        // @ts-ignore
135        let parentDetail: [{
136          startTS: number,
137          endTS: number,
138          depth: number,
139          id: number,
140          name: string
141        }] = await getParentDetail(
142          val.param.processId,
143          val.param.threadId,
144          val.param.leftNs,
145          val.param.rightNs
146        );
147        // @ts-ignore
148        parentDetail.forEach(item => {
149          funcIdArr.push(item.id);
150          if (item.depth === 0) {
151            if (item.startTS < minStartTS) {
152              minStartTS = item.startTS;
153            }
154            if (item.endTS > maxEndTS) {
155              maxEndTS = item.endTS;
156            }
157          }
158        });
159
160        let FuncChildrenList = await getFuncChildren(funcIdArr, val.param.processId, val.param.threadId, minStartTS, maxEndTS, true);
161        let childDurMap = new Map<number, number>();
162        FuncChildrenList.forEach((it: unknown) => {// @ts-ignore
163          if (!childDurMap.has(it.parentId)) {// @ts-ignore
164            childDurMap.set(it.parentId, it.duration);
165          } else {// @ts-ignore
166            let dur = childDurMap.get(it.parentId); // @ts-ignore
167            dur += it.duration; // @ts-ignore
168            childDurMap.set(it.parentId, dur!);
169          }
170        });
171        // @ts-ignore
172        result.map((e: unknown) => {
173          // @ts-ignore
174          e.selfTime = childDurMap.has(e.id) ? (e.duration - childDurMap.get(e.id)) / 1000000 : e.duration / 1000000;
175          // @ts-ignore
176          e.startTime = Utils.getTimeString(e.startNs);
177          // @ts-ignore
178          e.absoluteTime = ((window as unknown).recordStartNS + e.startNs) / 1000000000;
179          // @ts-ignore
180          e.duration = e.duration / 1000000;
181          // @ts-ignore
182          e.state = Utils.getEndState(e.state)!;
183          // @ts-ignore
184          e.processName = `${e.process === undefined || e.process === null ? 'process' : e.process}[${e.processId}]`;
185          // @ts-ignore
186          e.threadName = `${e.thread === undefined || e.thread === null ? 'thread' : e.thread}[${e.threadId}]`;
187        });
188        // @ts-ignore
189        this.boxChildSource = result;
190        if (this.sliceChildTbl) {
191          // @ts-ignore
192          this.sliceChildTbl.recycleDataSource = result;
193        }
194      } else {
195        this.boxChildSource = [];
196        if (this.sliceChildTbl) {
197          // @ts-ignore
198          this.sliceChildTbl.recycleDataSource = [];
199        };
200      }
201    });
202  }
203
204  initHtml(): string {
205    return `
206        <style>
207        :host{
208            padding: 10px 10px;
209            display: flex;
210            flex-direction: column;
211        }
212        </style>
213        <lit-table id="tb-slice-child" style="height: auto">
214            <lit-table-column order title="StartTime(Relative)" width="15%" data-index="startTime" key="startTime" align="flex-start" >
215            </lit-table-column>
216            <lit-table-column order title="StartTime(Absolute)" width="15%" data-index="absoluteTime" key="absoluteTime" align="flex-start" >
217            </lit-table-column>
218            <lit-table-column order width="15%" data-index="processName" key="processName" title="Process" align="flex-start" >
219            </lit-table-column>
220            <lit-table-column order width="15%" data-index="threadName" key="threadName" align="flex-start" title="Thread" >
221            </lit-table-column>
222            <lit-table-column order width="1fr" data-index="name" key="name" align="flex-start" title="Name">
223            </lit-table-column>
224            <lit-table-column order width="1fr" data-index="duration" key="duration" title="duration(ms)" align="flex-start">
225            </lit-table-column>
226            <lit-table-column order width="1fr" data-index="selfTime" key="selfTime" title="selfTime(ms)" align="flex-start">
227            </lit-table-column>
228        </lit-table>
229        `;
230  }
231
232  sortByColumn(detail: unknown): void {
233    // @ts-ignore
234    function compare(property, sort, type) {
235      return function (boxChildLeftData: SelectionData, boxChildRightData: SelectionData): number {
236        if (type === 'number') {
237          return sort === 2 // @ts-ignore
238            ? parseFloat(boxChildRightData[property]) - parseFloat(boxChildLeftData[property]) // @ts-ignore
239            : parseFloat(boxChildLeftData[property]) - parseFloat(boxChildRightData[property]);
240        } else {
241          // @ts-ignore
242          if (boxChildRightData[property] > boxChildLeftData[property]) {
243            return sort === 2 ? 1 : -1;
244          } else {
245            // @ts-ignore
246            if (boxChildRightData[property] === boxChildLeftData[property]) {
247              return 0;
248            } else {
249              return sort === 2 ? -1 : 1;
250            }
251          }
252        }
253      };
254    }
255    //@ts-ignore
256    if (detail.key === 'startTime' || detail.key === 'processName' || detail.key === 'threadName' ||//@ts-ignore
257      detail.key === 'name') {
258      // @ts-ignore
259      this.boxChildSource.sort(compare(detail.key, detail.sort, 'string'));// @ts-ignore
260    } else if (detail.key === 'absoluteTime' || detail.key === 'duration') {// @ts-ignore
261      this.boxChildSource.sort(compare(detail.key, detail.sort, 'number'));
262    }
263    // @ts-ignore
264    this.boxChildSource.sort(compare(detail.key, detail.sort, 'string'));
265    this.sliceChildTbl!.recycleDataSource = this.boxChildSource;
266  }
267}
268