• 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 '../../../../base-ui/table/lit-table-column';
19import { AllAppStartupStruct } from '../../../database/ui-worker/ProcedureWorkerAllAppStartup';
20
21import { type WakeupBean } from '../../../bean/WakeupBean';
22import { SpApplication } from '../../../SpApplication';
23import { TraceRow } from '../base/TraceRow';
24import { CpuStruct } from '../../../database/ui-worker/cpu/ProcedureWorkerCPU';
25import { ThreadStruct } from '../../../database/ui-worker/ProcedureWorkerThread';
26import { FuncStruct } from '../../../database/ui-worker/ProcedureWorkerFunc';
27import { ProcessMemStruct } from '../../../database/ui-worker/ProcedureWorkerMem';
28import { ClockStruct } from '../../../database/ui-worker/ProcedureWorkerClock';
29import { DmaFenceStruct } from '../../../database/ui-worker/ProcedureWorkerDmaFence';
30import { ColorUtils } from '../base/ColorUtils';
31import { IrqStruct } from '../../../database/ui-worker/ProcedureWorkerIrq';
32import { BinderArgBean } from '../../../bean/BinderArgBean';
33import { JankStruct } from '../../../database/ui-worker/ProcedureWorkerJank';
34import { Utils } from '../base/Utils';
35import { SpSystemTrace } from '../../SpSystemTrace';
36import { AppStartupStruct } from '../../../database/ui-worker/ProcedureWorkerAppStartup';
37import { SoStruct } from '../../../database/ui-worker/ProcedureWorkerSoInit';
38import { type SelectionParam } from '../../../bean/BoxSelection';
39import { type FrameAnimationStruct } from '../../../database/ui-worker/ProcedureWorkerFrameAnimation';
40import {
41  queryBinderByArgsId,
42  queryBinderBySliceId,
43  queryFlowsData,
44  queryPrecedingData,
45  queryThreadByItid,
46  queryFpsSourceList,
47  queryStateFreqList,
48} from '../../../database/sql/SqlLite.sql';
49import {
50  queryBinderArgsByArgset,
51  queryDistributedRelationAllData,
52  queryRWakeUpFrom,
53  queryRunnableTimeByRunning,
54  queryThreadStateArgs,
55  queryThreadWakeUp,
56  queryThreadWakeUpFrom,
57  sqlPrioCount,
58} from '../../../database/sql/ProcessThread.sql';
59import { queryGpuDur } from '../../../database/sql/Gpu.sql';
60import { queryWakeupListPriority } from '../../../database/sql/Cpu.sql';
61import { TabPaneCurrentSelectionHtml } from './TabPaneCurrentSelection.html';
62import { queryRealTime } from '../../../database/sql/Clock.sql';
63import { PerfToolStruct } from '../../../database/ui-worker/ProcedureWorkerPerfTool';
64import { TraceMode } from '../../../SpApplicationPublicFunc';
65import { threadPool, threadPool2 } from '../../../database/SqlLite';
66import { threadNearData } from '../../../database/data-trafic/SliceSender';
67import { HangStruct } from '../../../database/ui-worker/ProcedureWorkerHang';
68import { XpowerStruct } from '../../../database/ui-worker/ProcedureWorkerXpower';
69import { XpowerAppDetailStruct } from '../../../database/ui-worker/ProcedureWorkerXpowerAppDetail';
70import { XpowerWifiStruct } from '../../../database/ui-worker/ProcedureWorkerXpowerWifi';
71
72const INPUT_WORD =
73  'This is the interval from when the task became eligible to run \n(e.g.because of notifying a wait queue it was a suspended on) to\n when it started running.';
74
75const CLOCK_STARTTIME_ABSALUTED_ID: string = 'clockStartTimeAbsaluteId';
76const CLOCK_TRANSF_BTN_ID: string = 'clockTransfBtnId';
77const CPU_STARTTIME_ABSALUTED_ID: string = 'cpuStartTimeAbsaluteId';
78const CPU_TRANSF_BTN_ID: string = 'cpuTransfBtnId';
79const THREAD_STARTTIME_ABSALUTED_ID: string = 'threadStartTimeAbsaluteId';
80const THREAD_TRANSF_BTN_ID: string = 'threadTransfBtnId';
81const FUN_STARTTIME_ABSALUTED_ID: string = 'funStartTimeAbsaluteId';
82const FUN_TRANSF_BTN_ID: string = 'funTransfBtnId';
83
84export function getTimeString(ns: number): string {
85  if (ns === 0) {
86    return '0';
87  }
88  let currentTimeNs = ns;
89  let hour1 = 3600_000_000_000;
90  let minute1 = 60_000_000_000;
91  let second1 = 1_000_000_000; // 1 second
92  let millisecond1 = 1_000_000; // 1 millisecond
93  let microsecond1 = 1_000; // 1 microsecond
94  let res = '';
95  if (currentTimeNs >= hour1) {
96    res += Math.floor(currentTimeNs / hour1) + 'h ';
97    currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / hour1) * hour1;
98  }
99  if (currentTimeNs >= minute1) {
100    res += Math.floor(currentTimeNs / minute1) + 'm ';
101    currentTimeNs = currentTimeNs - Math.floor(ns / minute1) * minute1;
102  }
103  if (currentTimeNs >= second1) {
104    res += Math.floor(currentTimeNs / second1) + 's ';
105    currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / second1) * second1;
106  }
107  if (currentTimeNs >= millisecond1) {
108    res += Math.floor(currentTimeNs / millisecond1) + 'ms ';
109    currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / millisecond1) * millisecond1;
110  }
111  if (currentTimeNs >= microsecond1) {
112    res += Math.floor(currentTimeNs / microsecond1) + 'μs ';
113    currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / microsecond1) * microsecond1;
114  }
115  if (currentTimeNs > 0) {
116    res += currentTimeNs.toFixed(0) + 'ns ';
117  }
118  return res;
119}
120
121function compare(property: string, sort: number, type: string) {
122  return function (xpowerSortLeftData: SortData, xpowerSortRightData: SortData): number {
123    if (type === 'number') {
124      return sort === 2 // @ts-ignore
125        ? parseFloat(xpowerSortRightData[property]) - parseFloat(xpowerSortLeftData[property]) // @ts-ignore
126        : parseFloat(xpowerSortLeftData[property]) - parseFloat(xpowerSortRightData[property]);
127    } else if (type === 'duration') {
128      return sort === 2
129        ? xpowerSortRightData.dur - xpowerSortLeftData.dur
130        : xpowerSortLeftData.dur - xpowerSortRightData.dur;
131    } else if (type === 'bytes') {
132      return sort === 2
133        ? xpowerSortRightData.bytes - xpowerSortLeftData.bytes
134        : xpowerSortLeftData.bytes - xpowerSortRightData.bytes;
135    } {
136      // @ts-ignore
137      if (xpowerSortRightData[property] > xpowerSortLeftData[property]) {
138        return sort === 2 ? 1 : -1;
139      } else {
140        // @ts-ignore
141        if (xpowerSortRightData[property] === xpowerSortLeftData[property]) {
142          return 0;
143        } else {
144          return sort === 2 ? -1 : 1;
145        }
146      }
147    }
148  };
149}
150
151@element('tabpane-current-selection')
152export class TabPaneCurrentSelection extends BaseElement {
153  weakUpBean: WakeupBean | null | undefined;
154  selectWakeupBean: unknown;
155  private currentSelectionTbl: LitTable | null | undefined;
156  private tableObserver: MutationObserver | undefined;
157  private wakeupListTbl: LitTable | undefined | null;
158  private scrollView: HTMLDivElement | null | undefined;
159  // @ts-ignore
160  private dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1;
161  private wakeUp: string = '';
162  private isFpsAvailable: boolean = true;
163  private realTime: number = 0;
164  private bootTime: number = 0;
165  private funcDetailMap: Map<string, Array<object>> = new Map();
166  private topChainStr: string = '';
167  private fpsResult: Array<unknown> = [];
168
169  set data(selection: unknown) {
170    // @ts-ignore
171    if (selection !== undefined && selection.constructor && selection.constructor.name !== 'SelectionParam') {
172      // @ts-ignore
173      this.setCpuData(selection);
174    }
175  }
176
177  /**
178   * 创建StartTime的Dom节点
179   * @param selectTable面板的dom树对象
180   * @param startTs 开始时间
181   * @param transfBtnId 转换按钮id
182   * @param startTimeAbsaluteId 开始时间的id
183   */
184  createStartTimeNode(list: unknown[], startTs: number, transfBtnId: string, startTimeAbsaluteId: string): void {
185    let timeStr: string = '';
186    let startTimeValue: string = '';
187    let startTimeAbsolute = startTs + Utils.getInstance().getRecordStartNS();
188    if (this.realTime > 0) {
189      if (Utils.isTransformed) {
190        timeStr = this.getRealTimeStr(startTimeAbsolute);
191      } else {
192        timeStr = startTimeAbsolute / 1000000000 + 's';
193      }
194      startTimeValue = `<div style="white-space: nowrap;display: flex;align-items: center">
195                            <div id="${startTimeAbsaluteId}" style="white-space:pre-wrap" >${timeStr}</div>
196                            <lit-icon id="${transfBtnId}" class="temp-icon" title="Convert to realtime" name="restore" size="30"
197                                  style="position: relative; top: 5px; left: 10px;"></lit-icon>
198                        </div>`;
199    } else {
200      startTimeValue = startTimeAbsolute / 1000000000 + 's';
201    }
202    list.push({
203      name: 'StartTime(Absolute)',
204      value: startTimeValue,
205    });
206  }
207
208  /**
209   * 给转换按钮添加点击事件
210   * @param startTimeAbsolute 开始时间
211   * @param transfBtnId 转换按钮id
212   * @param startTimeAbsaluteId 开始时间ID
213   */
214  addClickToTransfBtn(startTimeAbsolute: number, transfBtnId: string, startTimeAbsaluteId: string): void {
215    let transfBtn = this.currentSelectionTbl?.shadowRoot?.querySelector(`#${transfBtnId}`);
216    transfBtn?.addEventListener('click', () => {
217      let startTimeAbsalute = this.currentSelectionTbl?.shadowRoot?.querySelector(`#${startTimeAbsaluteId}`);
218      if (startTimeAbsalute) {
219        if (Utils.isTransformed) {
220          startTimeAbsalute!.innerHTML = startTimeAbsolute / 1000000000 + 's';
221          Utils.isTransformed = false;
222        } else {
223          startTimeAbsalute!.innerHTML = this.getRealTimeStr(startTimeAbsolute);
224          Utils.isTransformed = true;
225        }
226      }
227    });
228  }
229
230  async setCpuData(
231    data: CpuStruct,
232    callback: ((data: WakeupBean | null) => void) | undefined = undefined,
233    scrollCallback?: (data: CpuStruct) => void
234  ): Promise<void> {
235    if (SpApplication.traceType.indexOf('SQLite') === -1) {
236      await this.setRealTime();
237    }
238    this.setTableHeight('650px');
239    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
240    if (leftTitle) {
241      leftTitle.innerText = 'Slice Details';
242    }
243    this.currentSelectionTbl!.loading = true;
244    let list: unknown[] = [];
245    this.updateUI(data, list);
246    Promise.all([this.queryThreadStateDArgs(data.argSetID), this.queryCPUWakeUpFromData(data)]).then((resArr) => {
247      let args = resArr[0];
248      let bean = resArr[1];
249      if (callback) {
250        callback(bean);
251      }
252      if (args.length > 0) {
253        args.forEach((arg) => {
254          list.push({ name: arg.keyName, value: arg.strValue });
255        });
256      }
257      this.currentSelectionTbl!.dataSource = list;
258      this.currentSelectionTbl!.loading = false;
259      let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS();
260      this.addClickToTransfBtn(startTimeAbsolute, CPU_TRANSF_BTN_ID, CPU_STARTTIME_ABSALUTED_ID);
261
262      let rightArea: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#table-right');
263      let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle');
264      let rightButton: HTMLElement | null | undefined = this?.shadowRoot
265        ?.querySelector('#rightButton')
266        ?.shadowRoot?.querySelector('#custom-button');
267      let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star');
268      this.threadClickEvent(scrollCallback, data);
269      let canvas = this.initCanvas();
270      if (bean !== null) {
271        this.selectWakeupBean = {
272          process: `${this.transferString(data.processName || 'Process')}(${data.processId})`,
273          thread: `${this.transferString(data.name || 'Thread')}(${data.tid})`,
274          cpu: data.cpu,
275          dur: data.dur,
276          priority: data.priority,
277          isSelected: false,
278        };
279
280        this.weakUpBean = bean;
281        this.updateRightTitleUI(rightArea!, rightTitle!, rightButton!, rightStar!);
282        this.drawRight(canvas, bean);
283      } else {
284        this.handleNullBeanUI(rightArea!, rightTitle!, rightButton!, rightStar!);
285      }
286    });
287  }
288
289  private threadClickEvent(scrollCallback: ((data: CpuStruct) => void) | undefined, data: CpuStruct): void {
290    let threadClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#thread-id');
291    threadClick?.addEventListener('click', () => {
292      //cpu点击
293      if (scrollCallback) {
294        data.state = 'Running';
295        scrollCallback(data);
296      }
297    });
298  }
299
300  private updateRightTitleUI(
301    rightArea: HTMLElement,
302    rightTitle: HTMLElement,
303    rightButton: HTMLElement,
304    rightStar: HTMLElement
305  ): void {
306    if (rightArea !== null && rightArea) {
307      rightArea.style.visibility = 'visible';
308    }
309    if (rightTitle !== null && rightTitle) {
310      rightTitle.style.visibility = 'visible';
311      rightButton!.style.visibility = 'visible';
312      rightStar!.style.visibility = 'hidden';
313      SpSystemTrace.btnTimer = null;
314    }
315  }
316
317  private handleNullBeanUI(
318    rightArea: HTMLElement,
319    rightTitle: HTMLElement,
320    rightButton: HTMLElement,
321    rightStar: HTMLElement
322  ): void {
323    this.weakUpBean = null;
324    if (rightArea !== null && rightArea) {
325      rightArea.style.visibility = 'hidden';
326    }
327    if (rightTitle !== null && rightTitle) {
328      rightTitle.style.visibility = 'hidden';
329      rightButton!.style.visibility = 'hidden';
330      rightStar!.style.visibility = 'hidden';
331    }
332  }
333
334  private updateUI(data: CpuStruct, list: unknown[]): void {
335    let process = this.transferString(data.processName || 'Process');
336    let processId = data.processId || data.tid;
337    let state = '';
338    if (data.end_state) {
339      state = Utils.getEndState(data.end_state);
340    } else if (data.end_state === '' || data.end_state === null) {
341      state = '';
342    } else {
343      state = 'Unknown State';
344    }
345    list.push({
346      name: 'Process',
347      value: `${process || 'Process'} [${processId}]`,
348    });
349    let name = this.transferString(data.name ?? '');
350    if (data.processId) {
351      list.push({
352        name: 'Thread',
353        value: `<div style="white-space: nowrap;display: flex;align-items: center">
354<div style="white-space:pre-wrap">${name || 'Process'} [${data.tid}]</div>
355<lit-icon style="cursor:pointer;margin-left: 5px" id="thread-id" name="select" color="#7fa1e7" size="20"></lit-icon>
356</div>`,
357      });
358    } else {
359      list.push({
360        name: 'Thread',
361        value: `<div style="white-space: nowrap;display: flex;align-items: center">
362<div style="white-space:pre-wrap">${name || 'Process'} [${data.tid}]</div>
363</div>`,
364      });
365    }
366
367    list.push({ name: 'StartTime(Relative)', value: getTimeString(data.startTime || 0) });
368    this.createStartTimeNode(list, data.startTime || 0, CPU_TRANSF_BTN_ID, CPU_STARTTIME_ABSALUTED_ID);
369    list.push({ name: 'Duration', value: getTimeString(data.dur || 0) });
370    list.push({ name: 'Prio', value: data.priority || 0 });
371    list.push({ name: 'End State', value: state });
372  }
373  // 设置真实时间和启动时间的值
374  private async setRealTime(): Promise<unknown> {
375    return queryRealTime().then((result) => {
376      if (result && result.length > 0) {
377        result.forEach((item) => {
378          if (item.name === 'realtime') {
379            this.realTime = item.ts;
380          } else {
381            this.bootTime = item.ts;
382          }
383        });
384      }
385    });
386  }
387
388  async setFunctionData(
389    data: FuncStruct,
390    threadName: string,
391    scrollCallback: Function,
392    callback?: (data: Array<unknown>, str: string, binderTid: number) => void,
393    distributedCallback?: Function,
394  ): Promise<void> {
395    //方法信息
396    if (SpApplication.traceType.indexOf('SQLite') === -1) {
397      await this.setRealTime();
398    }
399    this.tabCurrentSelectionInit('Slice Details');
400    let list: unknown[] = [];
401    let name = this.transferString(data.funName ?? '');
402    // 从缓存中拿到详细信息的表
403    let information: string = '';
404    let FunDetailList: Array<FunDetail> = new Array();
405    if (this.funcDetailMap.size === 0) {
406      await caches.match('/funDetail').then((res) => {
407        let result: Promise<string> | undefined;
408        if (res) {
409          result = res!.text();
410        }
411        return result;
412      }).then((res) => {
413        if (res) {
414          let funcDetail = JSON.parse(res);
415          for (let key in funcDetail) {
416            this.funcDetailMap.set(key, funcDetail[key]);
417          }
418        }
419      });
420    }
421    // @ts-ignore
422    FunDetailList = this.funcDetailMap.has(threadName) ? this.funcDetailMap.get('threadName') : this.funcDetailMap.get('anothers');
423    if (Array.isArray(FunDetailList)) {
424      // 筛选当前函数块的信息项
425      let informationList: Array<FunDetail> = FunDetailList.filter((v: FunDetail) => {
426        return v.slice.indexOf(name) > -1 || name.indexOf(v.slice) > -1;
427      });
428      information = informationList && informationList.length > 0 ? informationList[0].CN :
429        `<div style="white-space: nowrap;display: flex;align-items: center">
430           <div style="white-space:pre-wrap">无相关描述,如您知道具体含义可点击反馈</div>
431               <lit-icon style="cursor:pointer;margin-left: 5px; margin-top:5px" id="informationJump" name="select" color="#7fa1e7" size="20">
432               </lit-icon>
433           </a>
434       </div>`;
435    }
436    let isBinder = FuncStruct.isBinder(data);
437    let jankJumperList = new Array<ThreadTreeNode>();
438    let isAsyncBinder = isBinder && FuncStruct.isBinderAsync(data);
439    if (data.argsetid !== undefined && data.argsetid !== null && data.argsetid >= 0) {
440      this.setTableHeight('700px');
441      if (isAsyncBinder) {
442        this.handleAsyncBinder(data, list, jankJumperList, name, scrollCallback, information, callback);
443      } else if (isBinder) {
444        this.handleBinder(data, list, jankJumperList, name, scrollCallback, information, callback);
445      } else {
446        this.handleNonBinder(data, list, name, information);
447      }
448    } else if (data.funName && data.funName!.startsWith('H:Et') && (data.depth === 1 || data.depth === 0)) {
449      list.push({
450        name: 'StartTime(Relative)',
451        value: getTimeString(data.startTs || 0),
452      });
453      this.createStartTimeNode(list, data.startTs || 0, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID);
454      list.push({
455        name: 'Duration',
456        value: getTimeString(data.dur || 0),
457      });
458      data.funName!.split(',').map((item, index) => ({
459        name: [
460          'Sender tid',
461          'Send time',
462          'Expect handle time',
463          'Task name/ID',
464          'Prio',
465          'Sender'
466        ][index],
467        value: item,
468      })).forEach((item, index) => {
469        if (index === 0) {
470          item.value = item.value.split(':').at(-1)!;
471        }
472        list.push(item);
473      });
474      this.currentSelectionTbl!.dataSource = list;
475      // @ts-ignore
476      let startTimeAbsolute = (data.startTs || 0) + (window as unknown).recordStartNS;
477      this.addClickToTransfBtn(startTimeAbsolute, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID);
478    } else {
479      this.setTableHeight('auto');
480      list.push({ name: 'Name', value: name });
481      if (data.cookie || data.cookie === 0) {
482        list.push({ name: 'TaskId', value: data.cookie });
483      }
484      let processName = Utils.getInstance().getProcessMap().get(data.pid!);
485      let threadName = Utils.getInstance().getThreadMap().get(data.tid!);
486      let dataTid = isNaN(data.tid!) ? 'NULL' : data.tid;
487      list.push({
488        name: 'Process',
489        value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ',
490      });
491      list.push({
492        name: 'Thread',
493        value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + dataTid + '] ',
494      });
495      list.push({
496        name: 'StartTime(Relative)',
497        value: getTimeString(data.startTs || 0),
498      });
499      this.createStartTimeNode(list, data.startTs || 0, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID);
500      list.push({
501        name: 'Duration',
502        value: getTimeString(data.dur || 0),
503      });
504      list.push({ name: 'depth', value: data.depth });
505      list.push({ name: 'information:', value: information });
506      if (data.chainId) {
507        list.push({ name: 'ChainId', value: data.chainId });
508        list.push({ name: 'SpanId', value: data.spanId });
509        list.push({ name: 'ParentSpanId', value: data.parentSpanId });
510        list.push({ name: 'ChainFlag', value: data.chainFlag });
511        await this.chainSpanListCallBackHandle(data, distributedCallback);
512      }
513      this.currentSelectionTbl!.dataSource = list;
514      // @ts-ignore
515      let startTimeAbsolute = (data.startTs || 0) + (window as unknown).recordStartNS;
516      this.addClickToTransfBtn(startTimeAbsolute, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID);
517    }
518  }
519
520  // 计算真实时间
521  private getRealTimeStr(startTs: number): string {
522    let time = (startTs || 0) - Utils.getInstance().getRecordStartNS() - this.bootTime + this.realTime;
523    const formateDateStr =
524      this.getDate(parseInt(time.toString().substring(0, 13))) + '.' + time.toString().substring(10);
525    return formateDateStr;
526  }
527
528  // 格式化时间戳为字符串格式 yyyy/mm/dd hh:mi:ss
529  private getDate(timestamp: number): string {
530    let date = new Date(timestamp);
531    let gmt = date.toLocaleString();
532    return gmt;
533  }
534
535  private async chainSpanListCallBackHandle(data: FuncStruct, callBack: Function | undefined): Promise<void> {
536    let allMainChainList: FuncStruct[];
537    if (Utils.currentTraceMode === TraceMode.DISTRIBUTED) {
538      let chainAllData = await Promise.all([
539        queryDistributedRelationAllData(data.chainId!, threadPool.traceId),
540        queryDistributedRelationAllData(data.chainId!, threadPool2.traceId),
541      ]);
542      allMainChainList = [...chainAllData[0], ...chainAllData[1]];
543    } else {
544      allMainChainList = await queryDistributedRelationAllData(data.chainId!);
545    }
546    let finalChainSpanList: FuncStruct[] = allMainChainList.filter((spanNode) => spanNode.spanId === data.spanId);
547    let setParentChainList = (currentData: FuncStruct): void => {
548      let currentParentSpanList = allMainChainList.filter((spanNode) => spanNode.spanId === currentData.parentSpanId);
549      for (let index = currentParentSpanList.length - 1; index >= 0; index--) {
550        let spanStruct = currentParentSpanList[index];
551        if (finalChainSpanList.indexOf(spanStruct) < 0) {
552          finalChainSpanList.unshift(spanStruct);
553        }
554      }
555      if (
556        currentParentSpanList.length > 0 &&
557        currentParentSpanList[0].parentSpanId !== '' &&
558        currentParentSpanList[0].parentSpanId !== '0'
559      ) {
560        setParentChainList(currentParentSpanList[0]);
561      }
562    };
563    setParentChainList(data);
564    let setChildChainList = (currentData: FuncStruct): void => {
565      let currentChildSpanList = allMainChainList.filter((spanNode) => spanNode.parentSpanId === currentData.spanId);
566      currentChildSpanList.forEach((childSpan) => {
567        if (finalChainSpanList.indexOf(childSpan) < 0) {
568          finalChainSpanList.push(childSpan);
569        }
570      });
571      if (
572        currentChildSpanList.length > 0 &&
573        currentChildSpanList[0].spanId !== '' &&
574        currentChildSpanList[0].spanId !== '0'
575      ) {
576        setChildChainList(currentChildSpanList[0]);
577      }
578    };
579    setChildChainList(data);
580    if (callBack) {
581      callBack(finalChainSpanList);
582    }
583  }
584
585  private handleNonBinder(data: FuncStruct, list: unknown[], name: string, information: string): void {
586    queryBinderArgsByArgset(data.argsetid!).then((argset) => {
587      list.push({ name: 'Name', value: name });
588      let processName = Utils.getInstance().getProcessMap().get(data.pid!);
589      let threadName = Utils.getInstance().getThreadMap().get(data.tid!);
590      list.push({ name: 'Name', value: name }, {
591        name: 'Process',
592        value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ',
593      }, {
594        name: 'Thread',
595        value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ',
596      });
597      argset.forEach((item) => {
598        list.push({ name: item.keyName, value: item.strValue });
599      });
600      this.addTabPanelContent(list, data, information);
601      this.currentSelectionTbl!.dataSource = list;
602    });
603  }
604
605  private handleBinder(
606    data: FuncStruct,
607    list: unknown[],
608    jankJumperList: ThreadTreeNode[],
609    name: string,
610    scrollCallback: Function,
611    information: string,
612    callback?: (data: Array<unknown>, str: string, binderTid: number) => void
613  ): void {
614    queryBinderArgsByArgset(data.argsetid!).then((argset) => {
615      let binderSliceId = -1;
616      let binderTid = -1;
617      let processName = Utils.getInstance().getProcessMap().get(data.pid!);
618      let threadName = Utils.getInstance().getThreadMap().get(data.tid!);
619      argset.forEach((item) => {
620        if (item.keyName === 'calling tid') {
621          binderTid = Number(item.strValue);
622        }
623        if (item.keyName === 'destination slice id') {
624          binderSliceId = Number(item.strValue);
625          list.unshift({
626            name: 'Name',
627            value: `<div style="white-space: nowrap;display: flex;align-items: center">
628<div style="white-space:pre-wrap">${name || 'binder'}</div>
629<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="function-jump" name="select" color="#7fa1e7" size="20"></lit-icon>
630</div>`,
631          }, {
632            name: 'Process',
633            value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ',
634          }, {
635            name: 'Thread',
636            value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ',
637          }
638          );
639        }
640        list.push({ name: item.keyName, value: item.strValue });
641      });
642      if (binderSliceId === -1) {
643        list.unshift({
644          name: 'Name', value: name
645        }, {
646          name: 'Process',
647          value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ',
648        }, {
649          name: 'Thread',
650          value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ',
651        }
652        );
653      }
654      this.addTabPanelContent(list, data, information);
655      this.currentSelectionTbl!.dataSource = list;
656      let funcClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#function-jump');
657      funcClick?.addEventListener('click', () => {
658        if (!Number.isNaN(binderSliceId) && binderSliceId !== -1) {
659          queryBinderBySliceId(binderSliceId).then((result: unknown[]) => {
660            if (result.length > 0) {
661              // @ts-ignore
662              result[0].type = TraceRow.ROW_TYPE_FUNC;
663              scrollCallback(result[0]);
664              let timeLineNode = new ThreadTreeNode(
665                // @ts-ignore
666                result[0]?.tid,
667                // @ts-ignore
668                result[0]?.pid,
669                // @ts-ignore
670                result[0].startTs,
671                // @ts-ignore
672                result[0]?.depth
673              );
674              jankJumperList.push(timeLineNode);
675              if (callback) {
676                let linkTo = 'binder-to';
677                callback(jankJumperList, linkTo, binderTid);
678                linkTo = '';
679              }
680            }
681          });
682        }
683      });
684    });
685  }
686
687  private handleAsyncBinder(
688    data: FuncStruct,
689    list: unknown[],
690    jankJumperList: ThreadTreeNode[],
691    name: string,
692    scrollCallback: Function,
693    information: string,
694    callback?: (data: Array<unknown>, str: string, binderTid: number) => void
695  ): void {
696    let binderTid = -1;
697    Promise.all([
698      queryBinderByArgsId(data.argsetid!, data.startTs!, !data.funName!.endsWith('rcv')),
699      queryBinderArgsByArgset(data.argsetid!),
700    ]).then((result) => {
701      let asyncBinderRes = result[0];
702      let argsBinderRes = result[1];
703      let asyncBinderStract: unknown;
704      let processName = Utils.getInstance().getProcessMap().get(data.pid!);
705      let threadName = Utils.getInstance().getThreadMap().get(data.tid!);
706      if (asyncBinderRes.length > 0) {
707        //@ts-ignore
708        asyncBinderRes[0].type = TraceRow.ROW_TYPE_FUNC;
709        asyncBinderStract = asyncBinderRes[0];
710      }
711      if (argsBinderRes.length > 0) {
712        argsBinderRes.forEach((item) => {
713          list.push({
714            name: item.keyName,
715            value: item.strValue,
716          });
717          if (item.keyName === 'calling tid') {
718            binderTid = Number(item.strValue);
719          }
720        });
721      }
722
723      if (asyncBinderStract !== undefined) {
724        list.unshift({
725          name: 'Name',
726          value: `<div style="white-space: nowrap;display: flex;align-items: center">
727<div style="white-space:pre-wrap">${name || 'binder'}</div>
728<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="function-jump" name="select" color="#7fa1e7" size="20"></lit-icon>
729</div>`,
730        }, {
731          name: 'Process',
732          value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ',
733        }, {
734          name: 'Thread',
735          value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ',
736        });
737      } else {
738        list.unshift({
739          name: 'Name',
740          value: name
741        }, {
742          name: 'Process',
743          value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ',
744        }, {
745          name: 'Thread',
746          value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ',
747        });
748      }
749      this.addTabPanelContent(list, data, information);
750      this.currentSelectionTbl!.dataSource = list;
751      let funcClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#function-jump');
752      funcClick?.addEventListener('click', () => {
753        scrollCallback(asyncBinderStract);
754        let timeLineNode = new ThreadTreeNode(
755          // @ts-ignore
756          asyncBinderStract.tid,
757          // @ts-ignore
758          asyncBinderStract.pid,
759          // @ts-ignore
760          asyncBinderStract.startTs,
761          // @ts-ignore
762          asyncBinderStract.depth
763        );
764        jankJumperList.push(timeLineNode);
765        if (callback) {
766          let linkTo = 'binder-to';
767          callback(jankJumperList, linkTo, binderTid);
768          linkTo = '';
769        }
770      });
771
772    });
773  }
774
775  private addTabPanelContent(contentList: unknown[], data: FuncStruct, information: string): void {
776    contentList.push({
777      name: 'StartTime(Relative)',
778      value: getTimeString(data.startTs || 0),
779    });
780    contentList.push({
781      name: 'StartTime(Absolute)',
782      // @ts-ignore
783      value: ((data.startTs || 0) + (window as unknown).recordStartNS) / 1000000000 + 's',
784    });
785    contentList.push({
786      name: 'Duration',
787      value: getTimeString(data.dur || 0),
788    });
789    contentList.push({ name: 'depth', value: data.depth });
790    if (data.argsetid && data.argsetid > -1) {
791      contentList.push({ name: 'arg_set_id', value: data.argsetid });
792    }
793    contentList.push({ name: 'information', value: information });
794  }
795
796  private tabCurrentSelectionInit(leftTitleStr: string): void {
797    this.initCanvas();
798    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
799    this.setTitleAndButtonStyle();
800    if (leftTitle) {
801      leftTitle.innerText = leftTitleStr;
802    }
803  }
804
805  private setTitleAndButtonStyle(): void {
806    let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle');
807    let rightButton: HTMLElement | null | undefined = this?.shadowRoot
808      ?.querySelector('#rightButton')
809      ?.shadowRoot?.querySelector('#custom-button');
810    let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star');
811    if (rightTitle) {
812      rightTitle.style.visibility = 'hidden';
813      rightButton!.style.visibility = 'hidden';
814      rightStar!.style.visibility = 'hidden';
815    }
816  }
817
818  async setClockData(data: ClockStruct): Promise<void> {
819    if (SpApplication.traceType.indexOf('SQLite') === -1) {
820      await this.setRealTime();
821    }
822    this.setTableHeight('auto');
823    //时钟信息
824    this.tabCurrentSelectionInit('Counter Details');
825    let list: unknown[] = [];
826    list.push({
827      name: 'StartTime(Relative)',
828      value: getTimeString(data.startNS || 0),
829    });
830    this.createStartTimeNode(list, data.startNS || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
831    list.push({
832      name: 'Value',
833      value: ColorUtils.formatNumberComma(data.value || 0),
834    });
835    list.push({ name: 'Duration', value: getTimeString(data.dur || 0) });
836    this.currentSelectionTbl!.dataSource = list;
837    let startTimeAbsolute = (data.startNS || 0) + Utils.getInstance().getRecordStartNS();
838    this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
839  }
840
841  setDmaFenceData(data: DmaFenceStruct, rowData: unknown[]): void {
842    this.setTableHeight('auto');
843    this.tabCurrentSelectionInit('Slice Details');
844    let list: unknown[] = [];
845    list.push({
846      name: 'Title',
847      value: data.sliceName,
848    });
849    list.push({
850      name: 'StartTime(Relative)',
851      value: getTimeString(data.startTime || 0),
852    });
853    list.push({
854      name: 'StartTime(Absolute)',
855      // @ts-ignore
856      value: ((data.startTime || 0) + (window as unknown).recordStartNS) / 1000000000 + 's',
857    });
858    if (data.dur !== 0) {
859      list.push({
860        name: 'Wall Duration',
861        value: getTimeString(data.dur || 0)
862      });
863    }
864    list.push({
865      name: 'driver',
866      value: data.driver,
867    });
868    list.push({
869      name: 'context',
870      value: data.context,
871    });
872    this.currentSelectionTbl!.dataSource = list;
873  }
874
875  async setXpowerData(data: XpowerStruct): Promise<void> {
876    if (SpApplication.traceType.indexOf('SQLite') === -1) {
877      await this.setRealTime();
878    }
879    this.setTableHeight('auto');
880    this.tabCurrentSelectionInit('Counter Details');
881    let list: unknown[] = [];
882    list.push({
883      name: 'StartTime(Relative)',
884      value: getTimeString(data.startNS || 0),
885    });
886    this.createStartTimeNode(list, data.startNS || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
887    list.push({
888      name: 'Value',
889      value: String(data.value).indexOf('.') > -1 ? data.value || 0 : ColorUtils.formatNumberComma(data.value || 0),
890    });
891    list.push({ name: 'Duration', value: getTimeString(data.dur || 0) });
892    this.currentSelectionTbl!.dataSource = list;
893    let startTimeAbsolute = (data.startNS || 0) + Utils.getInstance().getRecordStartNS();
894    this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
895  }
896
897  async setXpowerDisplayData(data: XpowerAppDetailStruct): Promise<void> {
898    if (SpApplication.traceType.indexOf('SQLite') === -1) {
899      await this.setRealTime();
900    }
901    this.setTableHeight('auto');
902    this.tabCurrentSelectionInit('Display Details');
903    let list: unknown[] = [];
904    list.push({
905      name: 'StartTime(Relative)',
906      value: getTimeString(data.startTime || 0),
907    });
908    this.createStartTimeNode(list, data.startTime || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
909    let vals: { name: string, dur: number, value: string }[] = [];
910    let column = ['1hz', '5hz', '10hz', '15hz', '24hz', '30hz', '45hz', '60hz', '90hz', '120hz', '180hz'];
911    column.forEach((item) => {
912      // @ts-ignore
913      data['c' + item] !== 0 && vals.push({
914        name: item,
915        // @ts-ignore
916        dur: data['c' + item],
917        // @ts-ignore
918        value: data['c' + item] + ' ms',
919      });
920    });
921    // @ts-ignore
922    vals.sort(compare('value', 2, 'duration'));
923    list.push(...vals);
924    this.currentSelectionTbl!.dataSource = list;
925    let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS();
926    this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
927  }
928
929  async setXpowerWifiBytesData(data: XpowerWifiStruct): Promise<void> {
930    if (SpApplication.traceType.indexOf('SQLite') === -1) {
931      await this.setRealTime();
932    }
933    this.setTableHeight('auto');
934    this.tabCurrentSelectionInit('WIFIBytes Details');
935    let list: unknown[] = [];
936    list.push({
937      name: 'StartTime(Relative)',
938      value: getTimeString(data.startTime || 0),
939    });
940    this.createStartTimeNode(list, data.startTime || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
941    let vals: { name: string, bytes: number, value: string }[] = [];
942    data.tx !== 0 && vals.push({
943      name: 'send',
944      bytes: data.tx,
945      value: data.tx + ' B',
946    });
947    data.rx !== 0 && vals.push({
948      name: 'receiver',
949      bytes: data.rx,
950      value: data.rx + ' B',
951    });
952    // @ts-ignore
953    vals.sort(compare('value', 2, 'bytes'));
954    list.push(...vals);
955    this.currentSelectionTbl!.dataSource = list;
956    let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS();
957    this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
958  }
959
960  async setXpowerWifiPacketsData(data: XpowerWifiStruct): Promise<void> {
961    if (SpApplication.traceType.indexOf('SQLite') === -1) {
962      await this.setRealTime();
963    }
964    this.setTableHeight('auto');
965    this.tabCurrentSelectionInit('WIFIPackets Details');
966    let list: unknown[] = [];
967    list.push({
968      name: 'StartTime(Relative)',
969      value: getTimeString(data.startTime || 0),
970    });
971    this.createStartTimeNode(list, data.startTime || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
972    let vals: { name: string, value: string }[] = [];
973    data.tx !== 0 && vals.push({
974      name: 'send',
975      value: data.tx.toString(),
976    });
977    data.rx !== 0 && vals.push({
978      name: 'receiver',
979      value: data.rx.toString(),
980    });
981    // @ts-ignore
982    vals.sort(compare('value', 2, 'number'));
983    list.push(...vals);
984    this.currentSelectionTbl!.dataSource = list;
985    let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS();
986    this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
987  }
988
989  async setHangData(data: HangStruct, sp: SpSystemTrace, scrollCallback: Function): Promise<void> {
990    await this.setRealTime();
991    this.setTableHeight('auto');
992    this.tabCurrentSelectionInit('Hang Details');
993    let list: unknown[] = [];
994    list.push({
995      name: 'StartTime(Relative)',
996      value: getTimeString(data.startTime || 0),
997    });
998    this.createStartTimeNode(list, data.startTime || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
999    list.push({ name: 'Duration', value: getTimeString(data.dur || 0) });
1000    list.push({
1001      name: 'Hang type',
1002      value: `<div style="white-space: nowrap;display: flex;align-items: center">
1003<div style="white-space:pre-wrap">${data.type}</div>
1004<lit-icon style="cursor:pointer;margin-left: 5px" id="scroll-to-process" name="select" color="#7fa1e7" size="20"></lit-icon>
1005</div>`
1006    });
1007    data.content!.split(',').map((item, index) => ({
1008      name: [
1009        'Sender tid',
1010        'Send time',
1011        'Expect handle time',
1012        'Task name/ID',
1013        'Prio',
1014        'Sender'
1015      ][index],
1016      value: item,
1017    })).forEach((item, index) => {
1018      if (index === 0) {
1019        item.value = item.value.split(':').at(-1)!;
1020      }
1021      list.push(item);
1022    });
1023
1024    this.currentSelectionTbl!.dataSource = list;
1025    // @ts-ignore
1026    let startTimeAbsolute = (data.startTime || 0) + window.recordStartNS;
1027    this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID);
1028    this.hangScrollHandler(data, sp, scrollCallback);
1029  }
1030
1031  private hangScrollHandler(data: HangStruct, sp: SpSystemTrace, scrollCallback: Function): void {
1032    let scrollIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#scroll-to-process');
1033    scrollIcon?.addEventListener('click', async () => {
1034      //@ts-ignore
1035      let folderRow = sp.shadowRoot?.querySelector<TraceRow<unknown>>(
1036        `trace-row[row-id='${Utils.getDistributedRowId(data.pid)}'][row-type='process']`
1037      );
1038      if (folderRow) {
1039        folderRow.expansion = true;
1040      }
1041      let funcRow = sp.queryAllTraceRow<TraceRow<FuncStruct>>(
1042        `trace-row[row-id='${Utils.getDistributedRowId(data.tid)}'][row-type='func']`,
1043        (row) => row.rowId === `${data.tid}` && row.rowType === 'func'
1044      )[0];
1045      sp.currentRow = funcRow;
1046      if (!funcRow.dataListCache || funcRow.dataListCache.length === 0) {
1047        funcRow.dataListCache = await funcRow.supplierFrame!();
1048      }
1049      const findEntry = funcRow?.dataListCache.find((funcstruct: unknown) => {
1050        //@ts-ignore
1051        return (funcstruct.startTs === data.startTime && funcstruct.funName === data.content);
1052      })
1053      scrollCallback({
1054        //@ts-ignore
1055        pid: findEntry.pid,
1056        //@ts-ignore
1057        tid: findEntry.tid,
1058        type: 'func',
1059        //@ts-ignore
1060        dur: findEntry.dur,
1061        //@ts-ignore
1062        depth: findEntry.depth,
1063        //@ts-ignore
1064        funName: findEntry.funName,
1065        //@ts-ignore
1066        startTs: findEntry.startTs,
1067        keepOpen: true
1068      })
1069    })
1070  }
1071
1072  setPerfToolsData(data: PerfToolStruct): void {
1073    this.setTableHeight('auto');
1074    //Perf Tools info
1075    this.tabCurrentSelectionInit('Slice Details');
1076    let list: unknown[] = [];
1077    list.push({
1078      name: 'Name',
1079      value: data.name,
1080    });
1081    list.push({
1082      name: 'StartTime(Relative)',
1083      value: getTimeString(data.startTs || 0),
1084    });
1085    list.push({
1086      name: 'StartTime(Absolute)',
1087      // @ts-ignore
1088      value: ((data.startTs || 0) + (window as unknown).recordStartNS) / 1000000000 + 's',
1089    });
1090    list.push({
1091      name: 'Value',
1092      value: data.count,
1093    });
1094    list.push({ name: 'Duration', value: getTimeString(data.dur || 0) });
1095    this.currentSelectionTbl!.dataSource = list;
1096  }
1097
1098  setMemData(data: ProcessMemStruct): void {
1099    this.setTableHeight('auto');
1100    //时钟信息
1101    this.initCanvas();
1102    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
1103    if (leftTitle) {
1104      leftTitle.innerText = 'Counter Details';
1105    }
1106    let list: object[] = [];
1107    list.push({
1108      name: 'StartTime(Relative)',
1109      value: getTimeString(data.startTime || 0),
1110    });
1111    list.push({
1112      name: 'StartTime(Absolute)',
1113      value: ((data.startTime || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1114    });
1115    list.push({ name: 'Value', value: data.value });
1116    list.push({ name: 'Delta', value: data.delta });
1117    list.push({
1118      name: 'Duration',
1119      value: getTimeString(data.duration || 0),
1120    });
1121    this.currentSelectionTbl!.dataSource = list;
1122  }
1123
1124  setIrqData(data: IrqStruct): void {
1125    this.setTableHeight('550px');
1126    this.initCanvas();
1127    this.setTitleAndButtonStyle();
1128    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
1129    if (leftTitle) {
1130      leftTitle.innerText = 'Counter Details';
1131    }
1132    let list: object[] = [];
1133    list.push({
1134      name: 'StartTime(Relative)',
1135      value: getTimeString(data.startNS || 0),
1136    });
1137    list.push({
1138      name: 'StartTime(Absolute)',
1139      value: ((data.startNS || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1140    });
1141    list.push({ name: 'Name', value: data.name });
1142    list.push({ name: 'Duration', value: getTimeString(data.dur || 0) });
1143    queryBinderArgsByArgset(data.argSetId || 0).then((argsBinderRes) => {
1144      if (argsBinderRes.length > 0) {
1145        argsBinderRes.forEach((item) => {
1146          list.push({ name: item.keyName, value: item.strValue });
1147        });
1148      }
1149      this.currentSelectionTbl!.dataSource = list;
1150    });
1151  }
1152
1153  async setThreadData(
1154    data: ThreadStruct,
1155    scrollCallback: ((d: unknown) => void) | undefined,
1156    scrollWakeUp: (d: unknown) => void | undefined,
1157    scrollPrio: (d: unknown) => void | undefined,
1158    callback?: (data: Array<unknown>, str: string) => void
1159  ): Promise<void> {
1160    //线程信息
1161    if (SpApplication.traceType.indexOf('SQLite') === -1) {
1162      await this.setRealTime();
1163    }
1164    this.setTableHeight('550px');
1165    this.initCanvas();
1166    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
1167    this.setTitleAndButtonStyle();
1168    if (leftTitle) {
1169      leftTitle.innerText = 'Thread State';
1170    }
1171    let list: unknown[] = [];
1172    let jankJumperList: Array<ThreadTreeNode> = [];
1173    this.prepareThreadInfo(list, data);
1174    let cpu = new CpuStruct();
1175    cpu.id = data.id;
1176    cpu.startTime = data.startTime;
1177    this.queryThreadDetails(data, list, jankJumperList, callback, scrollWakeUp, scrollPrio, scrollCallback);
1178  }
1179
1180  private sortByNearData(nearData: unknown[], data: ThreadStruct, list: unknown[]): unknown[] {
1181    let preData: unknown = undefined;
1182    let nextData: unknown = undefined;
1183
1184    nearData
1185      // @ts-ignore
1186      .sort((near1, near2) => near1.startTime - near2.startTime)
1187      .forEach((near) => {
1188        // @ts-ignore
1189        if (near.id === data.id) {
1190          // @ts-ignore
1191          if (near.startTime < data.startTime!) {
1192            preData = near;
1193            list.push({
1194              name: 'Previous State',
1195              value: `<div style="white-space: nowrap;display: flex;align-items: center">
1196              <div style="white-space:pre-wrap">${
1197                // @ts-ignore
1198                Utils.getEndState(near.state)
1199                }</div>
1200              <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="previous-state-click" name="select" color="#7fa1e7" size="20"></lit-icon>
1201              </div>`,
1202            });
1203          } else {
1204            nextData = near;
1205            list.push({
1206              name: 'Next State',
1207              value: `<div style="white-space: nowrap;display: flex;align-items: center">
1208              <div style="white-space:pre-wrap">${
1209                // @ts-ignore
1210                Utils.getEndState(near.state)
1211                }</div>
1212              <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="next-state-click" name="select" color="#7fa1e7" size="20"></lit-icon>
1213              </div>`,
1214            });
1215          }
1216        }
1217      });
1218    return [preData, nextData];
1219  }
1220
1221  private setWakeupData(fromBean: WakeupBean | undefined, wakeUps: WakeupBean[], list: unknown[]): void {
1222    if (fromBean !== null && fromBean !== undefined && fromBean.pid !== 0 && fromBean.tid !== 0) {
1223      list.push({
1224        name: 'wakeup from tid',
1225        value: `<div style="white-space: nowrap;display: flex;align-items: center">
1226            <div style="white-space:pre-wrap">${fromBean.tid}</div>
1227            <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="wakeup-from"
1228            class="wakeup-click"  name="select" color="#7fa1e7" size="20"></lit-icon>
1229            </div>`,
1230      });
1231      list.push({
1232        name: 'wakeup from top',
1233        value: `<div style="white-space: nowrap;display: flex;align-items: center">
1234            <div style="white-space:pre-wrap" id="wakeup-top-content" style="display: none"></div>
1235            <lit-icon id="wakeup-top" class="temp-icon" title="Get to chain" name="restore" size="30"
1236              style="position: relative; top: 5px; left: 10px;"></lit-icon>
1237            </div>`,
1238      });
1239    }
1240    if (wakeUps !== null) {
1241      wakeUps.map((e) => {
1242        list.push({
1243          name: 'wakeup tid',
1244          value: `<div style="white-space: nowrap;display: flex;align-items: center">
1245            <div style="white-space:pre-wrap">${e.tid}</div>
1246            <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="wakeup-${e.tid}"
1247            class="wakeup-click" name="select" color="#7fa1e7" size="20"></lit-icon>
1248            </div>`,
1249        });
1250      });
1251    }
1252  }
1253
1254  private queryThreadDetails(
1255    data: ThreadStruct,
1256    list: unknown[],
1257    jankJumperList: ThreadTreeNode[],
1258    callback: ((data: Array<unknown>, str: string) => void) | undefined,
1259    scrollWakeUp: (d: unknown) => void | undefined,
1260    scrollPrio: (d: unknown) => void | undefined,
1261    scrollCallback: ((d: unknown) => void) | undefined
1262  ): void {
1263    Promise.all([
1264      this.queryThreadWakeUpFromData(data.id!, data.startTime!, data.dur!),
1265      this.queryThreadWakeUpData(data.id!, data.startTime!, data.dur!),
1266      this.queryThreadStateDArgs(data.argSetID),
1267      threadNearData('near-data', data.pid!, data.tid!, data.startTime!),
1268    ]).then((result) => {
1269      let fromBean = result[0];
1270      let wakeUps = result[1];
1271      let args = result[2];
1272      let [preData, nextData] = this.sortByNearData(result[3], data, list);
1273      this.setWakeupData(fromBean, wakeUps, list);
1274      if (args.length > 0) {
1275        args.forEach((arg: unknown) => {
1276          // @ts-ignore
1277          list.push({ name: arg.keyName, value: arg.strValue });
1278        });
1279      }
1280      this.currentSelectionTbl!.dataSource = list;
1281      let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS();
1282      this.addClickToTransfBtn(startTimeAbsolute, THREAD_TRANSF_BTN_ID, THREAD_STARTTIME_ABSALUTED_ID);
1283      let timeLineNode = new ThreadTreeNode(data.tid!, data.pid!, data.startTime!);
1284      jankJumperList.push(timeLineNode);
1285      if (callback) {
1286        callback(jankJumperList, this.wakeUp);
1287        this.wakeUp = '';
1288      }
1289      this.stateClickHandler(preData, nextData, data, scrollWakeUp, scrollCallback, scrollPrio);
1290      this.wakeupClickHandler(wakeUps, fromBean, scrollWakeUp);
1291      this.getWakeupChainClickHandler(fromBean, list);
1292    });
1293  }
1294
1295  private wakeupClickHandler(
1296    wakeUps: WakeupBean[],
1297    fromBean: WakeupBean | undefined,
1298    scrollWakeUp: (d: unknown) => void | undefined
1299  ): void {
1300    this.currentSelectionTbl?.shadowRoot?.querySelector('#wakeup-from')?.addEventListener('click', () => {
1301      //点击跳转,唤醒和被唤醒的 线程
1302      if (fromBean && scrollWakeUp) {
1303        scrollWakeUp({
1304          processId: fromBean.pid,
1305          tid: fromBean.tid,
1306          startTime: fromBean.ts,
1307          dur: fromBean.dur,
1308          cpu: fromBean.cpu,
1309          id: fromBean.itid,
1310          state: 'Running',
1311          argSetID: fromBean.argSetID,
1312        });
1313      }
1314      this.wakeUp = 'wakeup tid';
1315    });
1316    if (wakeUps) {
1317      wakeUps.map((up) => {
1318        this.currentSelectionTbl?.shadowRoot?.querySelector(`#wakeup-${up.tid}`)?.addEventListener('click', () => {
1319          //点击跳转,唤醒和被唤醒的 线程
1320          if (up && scrollWakeUp !== undefined) {
1321            scrollWakeUp({
1322              processId: up.pid,
1323              tid: up.tid,
1324              startTime: up.ts,
1325              dur: up.dur,
1326              cpu: up.cpu,
1327              id: up.itid,
1328              state: up.state,
1329              argSetID: up.argSetID,
1330            });
1331          }
1332          this.wakeUp = 'wakeup tid';
1333        });
1334      });
1335    }
1336  }
1337
1338  private stateClickHandler(
1339    preData: unknown,
1340    nextData: unknown,
1341    data: ThreadStruct,
1342    scrollWakeUp: (d: unknown) => void | undefined,
1343    scrollCallback: ((d: unknown) => void) | undefined,
1344    scrollPrio: (d: unknown) => void | undefined
1345  ): void {
1346    this.currentSelectionTbl?.shadowRoot?.querySelector('#next-state-click')?.addEventListener('click', () => {
1347      if (nextData && scrollWakeUp !== undefined) {
1348        scrollWakeUp({
1349          // @ts-ignore
1350          processId: nextData.pid,
1351          // @ts-ignore
1352          tid: nextData.tid,
1353          // @ts-ignore
1354          startTime: nextData.startTime,
1355          // @ts-ignore
1356          dur: nextData.dur,
1357          // @ts-ignore
1358          cpu: nextData.cpu,
1359          // @ts-ignore
1360          id: nextData.id,
1361          // @ts-ignore
1362          state: nextData.state,
1363          // @ts-ignore
1364          argSetID: nextData.argSetID,
1365        });
1366      }
1367    });
1368
1369    this.currentSelectionTbl?.shadowRoot?.querySelector('#previous-state-click')?.addEventListener('click', () => {
1370      if (preData && scrollWakeUp !== undefined) {
1371        scrollWakeUp({
1372          // @ts-ignore
1373          processId: preData.pid,
1374          // @ts-ignore
1375          tid: preData.tid,
1376          // @ts-ignore
1377          startTime: preData.startTime,
1378          // @ts-ignore
1379          dur: preData.dur,
1380          // @ts-ignore
1381          cpu: preData.cpu,
1382          // @ts-ignore
1383          id: preData.id,
1384          // @ts-ignore
1385          state: preData.state,
1386          // @ts-ignore
1387          argSetID: preData.argSetID,
1388        });
1389      }
1390    });
1391
1392    this.currentSelectionTbl?.shadowRoot?.querySelector('#state-click')?.addEventListener('click', () => {
1393      //线程点击
1394      if (scrollCallback) {
1395        scrollCallback(data);
1396      }
1397    });
1398    this.currentSelectionTbl?.shadowRoot?.querySelector('#prio-click')?.addEventListener('click', (ev) => {
1399      if (scrollPrio) {
1400        sqlPrioCount(data).then((res: unknown) => {
1401          scrollPrio(res);
1402        });
1403      }
1404    });
1405  }
1406  //点击事件获取唤醒链
1407  private getWakeupChainClickHandler(fromBean: WakeupBean | undefined, list: unknown[]): void {
1408    this.currentSelectionTbl?.shadowRoot?.querySelector('#wakeup-top')?.addEventListener('click', async () => {
1409      this.topChainStr = '';
1410      //@ts-ignore
1411      let currentThread = list.filter((item) => item.name === 'Thread')?.[0].value;//点击的当前线程
1412      let previouosWakeupThread = Utils.getInstance().getThreadMap().get(fromBean!.tid!) || 'Thread';//唤醒当前线程的上个线程
1413      this.topChainStr = `-->${previouosWakeupThread} [${fromBean!.tid}]-->${currentThread}`;
1414      this.getRWakeUpChain(fromBean);
1415    })
1416  }
1417  private async prepareThreadInfo(list: unknown[], data: ThreadStruct): Promise<void> {
1418    let processName = Utils.getInstance().getProcessMap().get(data.pid!);
1419    let threadName = Utils.getInstance().getThreadMap().get(data.tid!);
1420    list.push({
1421      name: 'Process',
1422      value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ',
1423    });
1424    list.push({
1425      name: 'Thread',
1426      value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ',
1427    });
1428    list.push({
1429      name: 'StartTime(Relative)',
1430      value: getTimeString(data.startTime || 0),
1431    });
1432    this.createStartTimeNode(list, data.startTime || 0, THREAD_TRANSF_BTN_ID, THREAD_STARTTIME_ABSALUTED_ID);
1433    list.push({ name: 'Duration', value: getTimeString(data.dur || 0) });
1434    let state;
1435    if (data.state) {
1436      state = Utils.getEndState(data.state);
1437    } else if (data.state === '' || data.state === null) {
1438      state = '';
1439    } else {
1440      state = 'Unknown State';
1441    }
1442    if ('Running' === state) {
1443      state = state + ' on CPU ' + data.cpu;
1444      list.push({
1445        name: 'State',
1446        value: `<div style="white-space: nowrap;display: flex;align-items: center">
1447            <div style="white-space:pre-wrap">${state}</div>
1448            <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="state-click" name="select" color="#7fa1e7" size="20"></lit-icon>
1449            </div>`,
1450      });
1451    } else {
1452      list.push({ name: 'State', value: `${state}` });
1453    }
1454    if (state.includes('Running')) {
1455      let startTime: number = data.startTime || 0;
1456      let endTime: number = (data.startTime || 0) + (data.dur || 0);
1457      let freqList: Array<unknown> = [];
1458      let str = '';
1459      freqList = await queryStateFreqList(startTime, endTime, data.cpu || 0);
1460      freqList.forEach((it) => {
1461        // @ts-ignore
1462        if (it.startTime < startTime! && it.endTime > endTime!) {
1463          // @ts-ignore
1464          it.stateDur = data.dur;
1465          // @ts-ignore
1466        } else if (it.startTime < startTime! && startTime! < it.endTime && it.endTime < endTime!) {
1467          // @ts-ignore
1468          it.stateDur = it.endTime - startTime!;
1469          // @ts-ignore
1470        } else if (it.startTime > startTime! && startTime! < it.endTime && it.endTime < endTime!) {
1471          // @ts-ignore
1472          it.stateDur = it.dur;
1473          // @ts-ignore
1474        } else if (it.startTime > startTime! && endTime! > it.startTime && it.endTime > endTime!) {
1475          // @ts-ignore
1476          it.stateDur = endTime! - it.startTime;
1477        }
1478        // @ts-ignore
1479        str += '[' + it.value + ', ' + (it.stateDur || 0) / 1000 + ']' + ',';
1480      });
1481      list.push({ name: 'Freq [KHz, μs]', value: str.substring(0, str.length - 1) });
1482    }
1483    let slice = Utils.getInstance().getSchedSliceMap().get(`${data.id}-${data.startTime}`);
1484    if (slice) {
1485      list.push({
1486        name: 'Prio',
1487        value: `<div style="white-space: nowrap;display: flex;align-item: center">
1488        <div style="white-space: pre-wrap">${slice.priority}</div>
1489        <lit-icon style="cursor:pointer;transform: scalex(-1);margin-left: 5px" id="prio-click" name="select" color="#7fa1e7" size="20"></lit-icon>
1490        </div>`,
1491      });
1492    }
1493  }
1494
1495  setJankData(
1496    data: JankStruct,
1497    callback: ((data: Array<unknown>) => void) | undefined = undefined,
1498    scrollCallback: ((d: unknown) => void) | undefined
1499  ): void {
1500    //线程信息
1501    this.setTableHeight('750px');
1502    this.tabCurrentSelectionInit('Slice Details');
1503    let list: unknown[] = [];
1504    this.setJankCommonMessage(list, data);
1505    if (`${data.type}` === '0') {
1506      this.handleTypeJank(data, list, scrollCallback, callback);
1507    } else {
1508      this.currentSelectionTbl!.dataSource = list;
1509    }
1510  }
1511
1512  private handleTypeJank(
1513    data: JankStruct,
1514    list: unknown[],
1515    scrollCallback: ((d: unknown) => void) | undefined,
1516    callback: ((data: Array<unknown>) => void) | undefined
1517  ): void {
1518    this.setJankType(data, list);
1519    let jankJumperList: Array<JankTreeNode> = [];
1520    if (data.frameType === 'render_service') {
1521      queryGpuDur(data.id!).then((it) => {
1522        if (it.length > 0) {
1523          //@ts-ignore
1524          list.push({ name: 'Gpu Duration', value: getTimeString(it[0].gpu_dur) });
1525        }
1526      });
1527      this.handleRenderServiceJank(data, list, jankJumperList, scrollCallback, callback);
1528    } else if (data.frameType === 'app') {
1529      this.handleAppJank(list, data, jankJumperList, scrollCallback, callback);
1530    } else if (data.frameType === 'frameTime') {
1531      this.handleFrameTimeJank(data, list, jankJumperList, scrollCallback, callback);
1532    }
1533  }
1534
1535  private handleFrameTimeJank(
1536    data: JankStruct,
1537    list: unknown[],
1538    jankJumperList: JankTreeNode[],
1539    scrollCallback: ((d: unknown) => void) | undefined,
1540    callback: ((data: Array<unknown>) => void) | undefined
1541  ): void {
1542    queryGpuDur(data.id!).then((it) => {
1543      if (it.length > 0) {
1544        list.push({
1545          name: 'Gpu Duration',
1546          //@ts-ignore
1547          value: getTimeString(it[0].gpu_dur),
1548        });
1549      }
1550      this.addAppFrameDetails(data, list);
1551      this.addRenderServiceFrameDetails(data, list);
1552      this.addFollowingDetails(list, data);
1553      let appNode = new JankTreeNode(data.name!, data.pid!, 'app');
1554      let rsNode = new JankTreeNode(data.rs_vsync!, data.rs_pid!, 'render_service');
1555      appNode.children.push(rsNode);
1556      jankJumperList.push(appNode);
1557      this.currentSelectionTbl!.dataSource = list;
1558      this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList);
1559    });
1560  }
1561
1562  private addRenderServiceFrameDetails(data: JankStruct, list: unknown[]): void {
1563    if (data.rs_name) {
1564      list.push({
1565        name: 'RenderService Frame',
1566        value: '',
1567      });
1568      list.push({
1569        name: 'Process',
1570        value: 'render_service ' + data.rs_pid,
1571      });
1572      list.push({
1573        name: 'StartTime(Relative)',
1574        value: getTimeString(data.rs_ts || 0),
1575      });
1576      list.push({
1577        name: 'StartTime(Absolute)',
1578        value: ((data.rs_ts || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1579      });
1580      list.push({
1581        name: 'end time',
1582        value: getTimeString(data.rs_ts! + data.rs_dur! || 0),
1583      });
1584    }
1585  }
1586
1587  private addFollowingDetails(list: unknown[], data: JankStruct): void {
1588    list.push({
1589      name: 'Following',
1590      value: '',
1591    });
1592    list.push({
1593      name: 'Slice',
1594      value:
1595        data.cmdline +
1596        ' [' +
1597        data.name +
1598        ']' +
1599        `<lit-icon class="jank_cla" style="display: inline-flex;cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="${data.type}-${data.pid}" slice_name="${data.name}"  pid="${data.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`,
1600    });
1601  }
1602
1603  private addAppFrameDetails(data: JankStruct, list: unknown[]): void {
1604    if (data.name) {
1605      list.push({
1606        name: 'App Frame',
1607        value: '',
1608      });
1609      list.push({
1610        name: 'Process',
1611        value: data.cmdline + ' ' + data.pid,
1612      });
1613      list.push({
1614        name: 'StartTime(Relative)',
1615        value: getTimeString(data.ts || 0),
1616      });
1617      list.push({
1618        name: 'StartTime(Absolute)',
1619        value: ((data.ts || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1620      });
1621      list.push({
1622        name: 'end time',
1623        value: getTimeString(data!.ts! + data.dur! || 0),
1624      });
1625    }
1626  }
1627
1628  private handleAppJank(
1629    list: unknown[],
1630    data: JankStruct,
1631    jankJumperList: JankTreeNode[],
1632    scrollCallback: ((d: unknown) => void) | undefined,
1633    callback: ((data: Array<unknown>) => void) | undefined
1634  ): void {
1635    list.push({
1636      name: 'FrameTimeLine flows',
1637      value: '',
1638    });
1639    list.push({
1640      name: 'Slice',
1641      value:
1642        data.cmdline +
1643        ' [' +
1644        data.name +
1645        ']' +
1646        `<lit-icon  class="jank_cla" style="display: inline-flex;cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="actual frameTime"  slice_name="${data.name}"  pid="${data.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`,
1647    });
1648    let timeLineNode = new JankTreeNode(data.name!, data.pid!, 'frameTime');
1649    jankJumperList.push(timeLineNode);
1650    if (data.dst_slice) {
1651      queryPrecedingData(data.dst_slice).then((it) => {
1652        if (it.length > 0) {
1653          list.push({
1654            name: 'Preceding flows',
1655            value: '',
1656          });
1657          it.forEach((a: unknown) => {
1658            // @ts-ignore
1659            let rsNode = new JankTreeNode(a.name, a.pid, 'render_service');
1660            jankJumperList.push(rsNode);
1661            list.push({
1662              name: 'Slice',
1663              value:
1664                // @ts-ignore
1665                a.cmdline +
1666                ' [' +
1667                // @ts-ignore
1668                a.name +
1669                ']' +
1670                // @ts-ignore
1671                `<lit-icon class="jank_cla" style="display: inline-flex;cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="${a.type}-${a.pid}" slice_name="${a.name}" pid="${a.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`,
1672            });
1673          });
1674          this.currentSelectionTbl!.dataSource = list;
1675          this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList);
1676        }
1677      });
1678    } else {
1679      this.currentSelectionTbl!.dataSource = list;
1680      this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList);
1681    }
1682  }
1683
1684  private handleRenderServiceJank(
1685    data: JankStruct,
1686    list: unknown[],
1687    jankJumperList: JankTreeNode[],
1688    scrollCallback: ((d: unknown) => void) | undefined,
1689    callback: ((data: Array<unknown>) => void) | undefined
1690  ): void {
1691    if (data.src_slice) {
1692      queryFlowsData(data.src_slice!.split(',')).then((it) => {
1693        if (it.length > 0) {
1694          list.push({
1695            name: 'FrameTimeLine flows',
1696            value: '',
1697          });
1698          it.forEach((a: unknown) => {
1699            // @ts-ignore
1700            let appNode = new JankTreeNode(a.name, a.pid, 'app');
1701            // @ts-ignore
1702            appNode.children.push(new JankTreeNode(a.name, a.pid, 'frameTime'));
1703            jankJumperList.push(appNode);
1704            list.push({
1705              name: 'Slice',
1706              value:
1707                // @ts-ignore
1708                a.cmdline +
1709                ' [' +
1710                // @ts-ignore
1711                a.name +
1712                ']' +
1713                // @ts-ignore
1714                `<lit-icon  class="jank_cla" style="display: inline-flex;cursor: pointer;transform: scaleX(-1);margin-left: 5px" id="actual frameTime" slice_name="${a.name}" pid="${a.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`,
1715            });
1716          });
1717          list.push({
1718            name: 'Following flows',
1719            value: '',
1720          });
1721          it.forEach((a: unknown) => {
1722            list.push({
1723              name: 'Slice',
1724              value:
1725                // @ts-ignore
1726                a.cmdline +
1727                ' [' +
1728                // @ts-ignore
1729                a.name +
1730                ']' +
1731                // @ts-ignore
1732                `<lit-icon class="jank_cla" style="display: inline-flex;cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="${a.type}-${a.pid}" slice_name="${a.name}"  pid="${a.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`,
1733            });
1734          });
1735          this.currentSelectionTbl!.dataSource = list;
1736          this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList);
1737        }
1738      });
1739    } else {
1740      this.currentSelectionTbl!.dataSource = list;
1741    }
1742  }
1743
1744  setAllStartupData(data: AllAppStartupStruct, scrollCallback: Function): void {
1745    this.setTableHeight('550px');
1746    this.initCanvas();
1747    let allStartUpLeftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
1748    let allStartUpmiddleTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightText');
1749    let allStartUpRightButton: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightButton');
1750    let rightButton: HTMLElement | null | undefined = this?.shadowRoot
1751      ?.querySelector('#rightButton')
1752      ?.shadowRoot?.querySelector('#custom-button');
1753    let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star');
1754    if (rightButton) {
1755      rightButton!.style.visibility = 'hidden';
1756    }
1757    if (rightStar) {
1758      rightStar!.style.visibility = 'hidden';
1759    }
1760    if (allStartUpmiddleTitle) {
1761      allStartUpmiddleTitle.style.visibility = 'hidden';
1762    }
1763    if (allStartUpRightButton) {
1764      allStartUpRightButton.style.visibility = 'hidden';
1765    }
1766    if (allStartUpLeftTitle) {
1767      allStartUpLeftTitle.innerText = 'Details';
1768    }
1769    let list: unknown[] = [];
1770    list.push({ name: 'Name', value: data.stepName! });
1771    list.push({
1772      name: 'StartTime(Relative)',
1773      value: getTimeString(data.startTs || 0),
1774    });
1775    list.push({
1776      name: 'StartTime(Absolute)',
1777      value: ((data.startTs || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1778    });
1779    list.push({
1780      name: 'EndTime(Relative)',
1781      value: getTimeString((data.startTs || 0) + (data.dur || 0)),
1782    });
1783    list.push({
1784      name: 'EndTime(Abslute)',
1785      value: ((data.startTs || 0) + (data.dur || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1786    });
1787    list.push({
1788      name: 'Dur',
1789      value: getTimeString(data.dur || 0),
1790    });
1791    this.currentSelectionTbl!.dataSource = list;
1792  }
1793
1794  setStartupData(data: AppStartupStruct, scrollCallback: Function, rowData: unknown): void {
1795    this.setTableHeight('550px');
1796    this.initCanvas();
1797    this.setStartUpStyle();
1798    let list: unknown[] = [];
1799    list.push({ name: 'Name', value: AppStartupStruct.getStartupName(data.startName) });
1800    list.push({
1801      name: 'StartTime(Relative)',
1802      value: `
1803      <div style="display: flex;white-space: nowrap;align-items: center">
1804<div style="white-space:pre-wrap">${getTimeString(data.startTs || 0)}</div>
1805<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="start-jump" name="select" color="#7fa1e7" size="20"></lit-icon>
1806</div>`,
1807    });
1808    list.push({
1809      name: 'StartTime(Absolute)',
1810      value: ((data.startTs || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1811    });
1812    if (data.dur && data.dur > 0) {
1813      list.push({
1814        name: 'EndTime(Relative)',
1815        value: `<div style="white-space: nowrap;display: flex;align-items: center">
1816<div style="white-space:pre-wrap">${getTimeString((data.startTs || 0) + (data.dur || 0))}</div>
1817<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="end-jump" name="select" color="#7fa1e7" size="20"></lit-icon>
1818</div>`,
1819      });
1820      list.push({
1821        name: 'EndTime(Absolute)',
1822        value: ((data.startTs || 0) + (data.dur || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1823      });
1824    } else {
1825      list.push({
1826        name: 'EndTime(Relative)',
1827        value: 'Unknown Time',
1828      });
1829      list.push({
1830        name: 'EndTime(Absolute)',
1831        value: 'Unknown Time',
1832      });
1833    }
1834    list.push({ name: 'Duration', value: getTimeString(data.dur || 0) });
1835    // @ts-ignore
1836    let sortedArray = rowData.slice().sort(function (a: { startTs: number }, b: { startTs: number }) {
1837      return a.startTs - b.startTs;
1838    });
1839    sortedArray.forEach((item: unknown, index: number) => {
1840      // @ts-ignore
1841      if (item.startName === data.startName) {
1842        list.push({
1843          name: 'StartSlice',
1844          value:
1845            index === 0
1846              ? 'NULL'
1847              : `${AppStartupStruct.getStartupName(sortedArray[index - 1].startName)}     ${getTimeString(
1848                sortedArray[index - 1].startTs + sortedArray[index - 1].dur
1849              )}`,
1850        });
1851        list.push({
1852          name: 'EndSlice',
1853          value:
1854            index === sortedArray.length - 1
1855              ? 'NULL'
1856              : `${AppStartupStruct.getStartupName(sortedArray[index + 1].startName)}      ${getTimeString(
1857                sortedArray[index + 1].startTs
1858              )}`,
1859        });
1860      }
1861    });
1862    this.currentSelectionTbl!.dataSource = list;
1863    this.attachScrollHandlers(data, scrollCallback);
1864  }
1865
1866  private setStartUpStyle(): void {
1867    let rightButton: HTMLElement | null | undefined = this?.shadowRoot
1868      ?.querySelector('#rightButton')
1869      ?.shadowRoot?.querySelector('#custom-button');
1870    let startUpRightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle');
1871    if (startUpRightTitle) {
1872      startUpRightTitle.style.visibility = 'hidden';
1873      rightButton!.style.visibility = 'hidden';
1874    }
1875    let startUpLeftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
1876    if (startUpLeftTitle) {
1877      startUpLeftTitle.innerText = 'Details';
1878    }
1879  }
1880
1881  private attachScrollHandlers(data: AppStartupStruct, scrollCallback: Function): void {
1882    let startIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#start-jump');
1883    let endIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#end-jump');
1884    let scrollClick = (type: number): void => {
1885      let recordNs: number = Utils.getInstance().getRecordStartNS();
1886      let useEnd = type === 1 && data.startName! < 6;
1887      queryThreadByItid(
1888        useEnd ? data.endItid! : data.itid!,
1889        useEnd ? recordNs + data.startTs! + data.dur! : recordNs + data.startTs!
1890      ).then((result) => {
1891        if (result.length > 0) {
1892          //@ts-ignore
1893          let pt: {
1894            pid: number;
1895            tid: number;
1896            dur: number;
1897            name: string;
1898            depth: number;
1899          } = result[0];
1900          scrollCallback({
1901            pid: pt.pid,
1902            tid: pt.tid,
1903            type: 'func',
1904            dur: pt.dur,
1905            depth: pt.depth,
1906            funName: pt.name,
1907            startTs: useEnd ? (data.startTs || 0) + (data.dur || 0) : data.startTs,
1908            keepOpen: true,
1909          });
1910        }
1911      });
1912    };
1913    if (startIcon) {
1914      startIcon.addEventListener('click', () => scrollClick(0));
1915    }
1916    if (endIcon) {
1917      endIcon.addEventListener('click', () => scrollClick(1));
1918    }
1919  }
1920
1921  setStaticInitData(data: SoStruct, scrollCallback: Function): void {
1922    this.setTableHeight('550px');
1923    this.initCanvas();
1924    this.setStaticInitStyle();
1925    let list: unknown[] = [];
1926    list.push({ name: 'Name', value: data.soName });
1927    list.push({
1928      name: 'StartTime(Relative)',
1929      value: `<div style="white-space: nowrap;display: flex;align-items: center">
1930<div style="white-space:pre-wrap">${getTimeString(data.startTs || 0)}</div>
1931<lit-icon id="start-jump" style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" name="select" color="#7fa1e7" size="20"></lit-icon>
1932</div>`,
1933    });
1934    list.push({
1935      name: 'StartTime(Absolute)',
1936      value: ((data.startTs || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1937    });
1938    list.push({ name: 'Duration', value: getTimeString(data.dur || 0) });
1939    this.currentSelectionTbl!.dataSource = list;
1940    this.startIconClickEvent(data, scrollCallback);
1941  }
1942
1943  private setStaticInitStyle(): void {
1944    let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle');
1945    let rightButton: HTMLElement | null | undefined = this?.shadowRoot
1946      ?.querySelector('#rightButton')
1947      ?.shadowRoot?.querySelector('#custom-button');
1948    if (rightTitle) {
1949      rightTitle.style.visibility = 'hidden';
1950      rightButton!.style.visibility = 'hidden';
1951    }
1952    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
1953    if (leftTitle) {
1954      leftTitle.innerText = 'Details';
1955    }
1956  }
1957
1958  private startIconClickEvent(data: SoStruct, scrollCallback: Function): void {
1959    let startIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#start-jump');
1960    if (startIcon) {
1961      startIcon.addEventListener('click', () => {
1962        let recordNs: number = Utils.getInstance().getRecordStartNS();
1963        queryThreadByItid(data.itid!, recordNs + data.startTs!).then((result) => {
1964          if (result.length > 0) {
1965            //@ts-ignore
1966            let pt: {
1967              pid: number;
1968              tid: number;
1969              dur: number;
1970              name: string;
1971              depth: number;
1972            } = result[0];
1973            scrollCallback({
1974              pid: pt.pid,
1975              tid: pt.tid,
1976              type: 'func',
1977              dur: pt.dur,
1978              depth: pt.depth,
1979              funName: pt.name,
1980              startTs: data.startTs,
1981              keepOpen: true,
1982            });
1983          }
1984        });
1985      });
1986    }
1987  }
1988
1989  async setFrameAnimationData(data: FrameAnimationStruct, scrollCallback: Function): Promise<void> {
1990    this.setTableHeight('550px');
1991    this.tabCurrentSelectionInit('Animation Details');
1992    let list = [];
1993    let dataTs: number = data.startTs < 0 ? 0 : data.startTs;
1994    list.push({ name: 'Name', value: data.name });
1995    list.push({ name: 'Start time(Relative)', value: `${Utils.getTimeString(dataTs)}` });
1996    list.push({
1997      name: 'Start time(Absolute)',
1998      value: ((dataTs || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
1999    });
2000    list.push({
2001      name: 'End time(Relative)',
2002      value: `${Utils.getTimeString(dataTs + (data.dur || 0))}`,
2003    });
2004    list.push({
2005      name: 'End time(Absolute)',
2006      value: (dataTs + (data.dur || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
2007    });
2008    list.push({ name: 'Duration', value: `${Utils.getTimeString(data.dur || 0)}` });
2009    if (data.status === 'Completion delay') {
2010      let queryJoinName = `${data.frameInfo?.split(':')[1]}: ${data.name?.split(':')![1]}`;
2011      let frameFpsMessage = data.frameInfo?.split(':');
2012      await queryFpsSourceList(data.inputTime, data.endTime, queryJoinName).then((result) => {
2013        if (result.length > 0) {
2014          this.isFpsAvailable = true;
2015          this.fpsResult = result;
2016        } else {
2017          this.isFpsAvailable = false;
2018        }
2019      });
2020      if (frameFpsMessage) {
2021        if (frameFpsMessage[1] !== '0') {
2022          if (this.isFpsAvailable) {
2023            list.push({
2024              name: 'FPS',
2025              value: `<div style="white-space: nowrap;display: flex;align-items: center">
2026            <div style="white-space:pre-wrap">${frameFpsMessage[1]}</div>
2027            <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="fps-jump" name="select" color="#7fa1e7" size="20"></lit-icon>
2028            </div>`,
2029            });
2030          } else {
2031            list.push({ name: 'FPS', value: `${frameFpsMessage[1]}` });
2032          }
2033        } else {
2034          let fixedNumber: number = 2;
2035          let fpsValue: number = Number(frameFpsMessage[0]) / (data.dur / 1000_000_000);
2036          list.push({ name: 'FPS', value: `${fpsValue.toFixed(fixedNumber) || 0}` });
2037        }
2038      }
2039    }
2040    this.currentSelectionTbl!.dataSource = list;
2041    this.fpsClickEvent(data, scrollCallback);
2042  }
2043
2044  private fpsClickEvent(data: FrameAnimationStruct, scrollCallback: Function): void {
2045    let recordNs: number = Utils.getInstance().getRecordStartNS();
2046    this.currentSelectionTbl?.shadowRoot?.querySelector('#fps-jump')?.addEventListener('click', () => {// @ts-ignore
2047      let pt: {
2048        pid: number;
2049        tid: number;
2050        name: string;
2051        ts: number;
2052        dur: number;
2053        depth: number;
2054      } = this.fpsResult[0];
2055      scrollCallback({
2056        pid: pt.tid,
2057        tid: pt.tid,
2058        dur: pt.dur,
2059        type: 'func',
2060        depth: pt.depth,
2061        funName: pt.name,
2062        startTs: pt.ts - recordNs,
2063        keepOpen: true,
2064      });
2065    });
2066  }
2067
2068  private setJankType(data: JankStruct, list: unknown[]): void {
2069    if (data.jank_tag === 1) {
2070      if (data.frameType === 'render_service') {
2071        list.push({ name: 'Jank Type', value: 'RenderService Deadline Missed' });
2072      } else if (data.frameType === 'app') {
2073        list.push({ name: 'Jank Type', value: 'APP Deadline Missed' });
2074      } else if (data.frameType === 'frameTime') {
2075        list.push({ name: 'Jank Type', value: 'Deadline Missed' });
2076      }
2077    } else if (data.jank_tag === 3) {
2078      list.push({ name: 'Jank Type', value: 'Deadline Missed' });
2079    } else {
2080      list.push({ name: 'Jank Type', value: 'NONE' });
2081    }
2082  }
2083
2084  private setJankCommonMessage(list: unknown[], data: JankStruct): void {
2085    list.push({ name: 'Name', value: data.name });
2086    list.push({ name: 'StartTime(Relative)', value: getTimeString(data.ts || 0) });
2087    list.push({
2088      name: 'StartTime(Absolute)',
2089      value: ((data.ts || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's',
2090    });
2091    list.push({ name: 'Duration', value: data.dur ? getTimeString(data.dur) : ' ' });
2092    if (data.frameType !== 'frameTime') {
2093      list.push({ name: 'Process', value: data.cmdline + ' ' + data.pid });
2094    }
2095  }
2096
2097  private setTableHeight(height: string): void {
2098    this.scrollView!.scrollTop = 0;
2099    this.currentSelectionTbl!.style.height = height;
2100    this.wakeupListTbl!.style.display = 'none';
2101  }
2102
2103  private addJankScrollCallBackEvent(
2104    scrollCallback: ((d: unknown) => void) | undefined,
2105    callback: ((data: Array<unknown>) => void) | undefined,
2106    jankJumperList: JankTreeNode[]
2107  ): void {
2108    let all = this.currentSelectionTbl?.shadowRoot?.querySelectorAll('.jank_cla');
2109    all!.forEach((a) => {
2110      a.addEventListener('click', () => {
2111        if (scrollCallback) {
2112          scrollCallback({
2113            rowId: a.id,
2114            name: a.getAttribute('slice_name'),
2115            pid: a.getAttribute('pid'),
2116          });
2117        }
2118      });
2119    });
2120    if (callback) {
2121      callback(jankJumperList);
2122    }
2123  }
2124
2125  async queryThreadStateDArgs(argSetID: number | undefined): Promise<BinderArgBean[]> {
2126    let list: Array<BinderArgBean> = [];
2127    if (argSetID !== undefined && argSetID > 0) {
2128      list = await queryThreadStateArgs(argSetID);
2129    }
2130    return list;
2131  }
2132
2133  /**
2134   * 查询出 线程被唤醒的 线程信息
2135   * @param data
2136   */
2137  async queryCPUWakeUpFromData(data: CpuStruct): Promise<WakeupBean | null> {
2138    let wb: WakeupBean | null = null;
2139    if (data.id === undefined || data.startTime === undefined) {
2140      return null;
2141    }
2142    let wakeup = await queryRunnableTimeByRunning(data.tid!, data.startTime);
2143    if (wakeup && wakeup[0]) {
2144      let wakeupTs = wakeup[0].ts as number;
2145      let recordStartTs = Utils.getInstance().getRecordStartNS();
2146      let wf = await queryThreadWakeUpFrom(data.id, wakeupTs);
2147      // @ts-ignore
2148      if (wf && wf[0]) {
2149        // @ts-ignore
2150        wb = wf[0];
2151        if (wb !== null) {
2152          wb.wakeupTime = wakeupTs - recordStartTs;
2153          wb.process = Utils.getInstance().getProcessMap().get(wb.pid!) || 'Process';
2154          wb.thread = Utils.getInstance().getThreadMap().get(wb.tid!) || 'Thread';
2155          wb.schedulingLatency = (data.startTime || 0) - (wb.wakeupTime || 0);
2156          wb.schedulingDesc = INPUT_WORD;
2157        }
2158      }
2159    }
2160    return wb;
2161  }
2162
2163  /**
2164   * 查询出 线程被唤醒的 线程链信息
2165   * @param data
2166   */
2167  static async queryCPUWakeUpListFromBean(data: WakeupBean): Promise<WakeupBean | null> {
2168    let wb: WakeupBean | null = null;
2169    let wakeup = await queryRunnableTimeByRunning(data.tid!, data.ts!);
2170    if (wakeup && wakeup[0]) {
2171      let wakeupTs = wakeup[0].ts as number;
2172      let recordStartTs = Utils.getInstance().getRecordStartNS();
2173      let wf = await queryThreadWakeUpFrom(data.itid!, wakeupTs);
2174      // @ts-ignore
2175      if (wf && wf[0]) {
2176        // @ts-ignore
2177        wb = wf[0];
2178        if (wb !== null) {
2179          wb.wakeupTime = wakeupTs - recordStartTs;
2180          wb.process = Utils.getInstance().getProcessMap().get(wb.pid!) || 'Process';
2181          wb.thread = Utils.getInstance().getThreadMap().get(wb.tid!) || 'Thread';
2182          wb.schedulingLatency = (data.ts || 0) - (wb.wakeupTime || 0);
2183          wb.schedulingDesc = INPUT_WORD;
2184        }
2185      }
2186    }
2187    return wb;
2188  }
2189
2190  /**
2191   * 查询出 线程唤醒了哪些线程信息
2192   */
2193  async queryThreadWakeUpFromData(itid: number, startTime: number, dur: number): Promise<WakeupBean | undefined> {
2194    let wakeUps = await queryThreadWakeUpFrom(itid, startTime + Utils.getInstance().getRecordStartNS());
2195    let item;
2196    // @ts-ignore
2197    if (wakeUps !== undefined && wakeUps.length > 0) {
2198      // @ts-ignore
2199      item = wakeUps[0];
2200    }
2201    return item;
2202  }
2203
2204  /**
2205   * 查询出 线程唤醒了哪些线程信息
2206   */
2207  async queryThreadWakeUpData(itid: number, startTime: number, dur: number): Promise<Array<WakeupBean>> {
2208    let list: Array<WakeupBean> = [];
2209    if (itid === undefined || startTime === undefined) {
2210      return list;
2211    }
2212    let wakeUps = await queryThreadWakeUp(itid, startTime, dur); //  3,4835380000
2213    if (wakeUps !== undefined && wakeUps.length > 0) {
2214      list.push(...wakeUps);
2215    }
2216    return list;
2217  }
2218  //递归查找R唤醒链
2219  getRWakeUpChain(data: WakeupBean | undefined): void {
2220    this.getRWakeUpChainData(data).then((wakeupFrom: unknown) => {
2221      if (wakeupFrom === null) {//当查不到数据时,处理容器状态与样式,展示内容
2222        let wakeupTopContent = this.currentSelectionTbl?.shadowRoot?.getElementById('wakeup-top-content');
2223        let wakeupTopIcon = this.currentSelectionTbl?.shadowRoot?.querySelector<HTMLDivElement>('#wakeup-top');
2224        wakeupTopContent!.innerText = 'idle' + this.topChainStr;//处理链顶部
2225        wakeupTopIcon!.style.display = 'none';
2226        wakeupTopContent!.style.display = 'block';
2227        wakeupTopContent!.style.maxHeight = '100px';//设置最大高度,超出出现滚动条
2228        wakeupTopContent!.style.overflow = 'auto';
2229        return;
2230      }
2231      //@ts-ignore
2232      this.topChainStr = `-->${wakeupFrom!.thread} [${wakeupFrom!.tid}]` + this.topChainStr;//链的拼接
2233      // @ts-ignore
2234      this.getRWakeUpChain(wakeupFrom);
2235    });
2236  }
2237
2238  /**
2239   * 获取 R的唤醒链
2240   * @param data
2241  */
2242  async getRWakeUpChainData(data: unknown): Promise<WakeupBean | null> {
2243    let wakeupFrom: WakeupBean | null = null;
2244    //@ts-ignore
2245    let wakeup = await queryRunnableTimeByRunning(data.tid!, data.ts!);//通过链上的Running块,查找前一条R信息
2246    if (wakeup && wakeup[0]) {
2247      let wakeupTs = wakeup[0].ts as number;
2248      //@ts-ignore
2249      let wf = await queryRWakeUpFrom(data.itid!, wakeupTs);//查找到的前一条R信息,对应的唤醒信息
2250      //@ts-ignore
2251      if (wf && wf[0]) {
2252        //@ts-ignore
2253        wakeupFrom = wf[0];
2254        //@ts-ignore
2255        wakeupFrom.thread = Utils.getInstance().getThreadMap().get(wakeupFrom.tid!) || 'Thread';
2256      }
2257    }
2258    return wakeupFrom;
2259  }
2260
2261  initCanvas(): HTMLCanvasElement | null {
2262    let canvas = this.shadowRoot!.querySelector<HTMLCanvasElement>('#rightDraw');
2263    let width = getComputedStyle(this.currentSelectionTbl!).getPropertyValue('width');
2264    if (canvas !== null) {
2265      canvas.width = Math.round(Number(width.replace('px', '')) * this.dpr);
2266      canvas.height = Math.round(Number(200 * this.dpr));
2267      canvas.style.width = width;
2268      canvas.style.height = '200px';
2269      canvas.getContext('2d')!.scale(this.dpr, this.dpr);
2270    }
2271    SpApplication.skinChange = (val: boolean): void => {
2272      this.drawRight(canvas, this.weakUpBean!);
2273    };
2274    return canvas;
2275  }
2276
2277  drawRight(cavs: HTMLCanvasElement | null, wakeupBean: WakeupBean | null): void {
2278    if (cavs === null) {
2279      return;
2280    }
2281    let context = cavs.getContext('2d');
2282    if (context !== null) {
2283      //绘制竖线
2284      this.drawVerticalLine(context);
2285      //绘制菱形
2286      context.lineWidth = 1;
2287      context.beginPath();
2288      context.moveTo(10, 30);
2289      context.lineTo(4, 40);
2290      context.lineTo(10, 50);
2291      context.lineTo(16, 40);
2292      context.lineTo(10, 30);
2293      context.closePath();
2294      context.fill();
2295      context.font = 12 + 'px sans-serif';
2296      //绘制wake up 文字
2297      let strList = [];
2298      strList.push('wakeup @ ' + getTimeString(wakeupBean?.wakeupTime || 0) + ' on CPU ' + wakeupBean?.cpu + ' by');
2299      strList.push('P:' + wakeupBean?.process + ' [ ' + wakeupBean?.pid + ' ]');
2300      strList.push('T:' + wakeupBean?.thread + ' [ ' + wakeupBean?.tid + ' ]');
2301      strList.forEach((str, index) => {
2302        if (context !== null) {
2303          context.fillText(str, 40, 40 + 16 * index);
2304        }
2305      });
2306      context.lineWidth = 2;
2307      context.lineJoin = 'bevel';
2308      context.moveTo(10, 95);
2309      context.lineTo(20, 90);
2310      context.moveTo(10, 95);
2311      context.lineTo(20, 100);
2312      context.moveTo(10, 95);
2313      context.lineTo(80, 95);
2314      context.lineTo(70, 90);
2315      context.moveTo(80, 95);
2316      context.lineTo(70, 100);
2317      context.stroke();
2318      //绘制latency
2319      context.font = 12 + 'px sans-serif';
2320      context.fillText('Scheduling latency:' + getTimeString(wakeupBean?.schedulingLatency || 0), 90, 100);
2321      //绘制最下方提示语句
2322      context.font = 10 + 'px sans-serif';
2323      INPUT_WORD.split('\n').forEach((str, index) => {
2324        context?.fillText(str, 90, 120 + 12 * index);
2325      });
2326    }
2327  }
2328
2329  private drawVerticalLine(context: CanvasRenderingContext2D): void {
2330    if (document.querySelector<SpApplication>('sp-application')!.dark) {
2331      context.strokeStyle = '#ffffff';
2332      context.fillStyle = '#ffffff';
2333    } else {
2334      context.strokeStyle = '#000000';
2335      context.fillStyle = '#000000';
2336    }
2337    context.lineWidth = 2;
2338    context.moveTo(10, 15);
2339    context.lineTo(10, 125);
2340    context.stroke();
2341  }
2342
2343  transferString(str: string): string {
2344    let s = '';
2345    if (str.length === 0) {
2346      return '';
2347    }
2348    s = str.replace(/&/g, '&amp;');
2349    s = s.replace(/</g, '&lt;');
2350    s = s.replace(/>/g, '&gt;');
2351    s = s.replace(/\'/g, '&#39;');
2352    s = s.replace(/\"/g, '&#quat;');
2353    return s;
2354  }
2355
2356  initElements(): void {
2357    this.currentSelectionTbl = this.shadowRoot?.querySelector<LitTable>('#selectionTbl');
2358    this.wakeupListTbl = this.shadowRoot?.querySelector<LitTable>('#wakeupListTbl');
2359    this.scrollView = this.shadowRoot?.querySelector<HTMLDivElement>('#scroll_view');
2360    this.currentSelectionTbl?.addEventListener('column-click', (ev: unknown): void => { }); //@ts-ignore
2361    window.subscribe(window.SmartEvent.UI.WakeupList, (data: Array<WakeupBean>) => this.showWakeupListTableData(data));
2362  }
2363
2364  showWakeupListTableData(data: Array<WakeupBean>): void {
2365    this.wakeupListTbl!.style.display = 'flex';
2366    let cpus: number[] = [];
2367    let itids: number[] = [];
2368    let ts: number[] = [];
2369    let maxPriority = 0;
2370    let maxPriorityDuration = 0;
2371    let maxDuration = 0;
2372    data.forEach((it) => {
2373      cpus.push(it.cpu!);
2374      itids.push(it.itid!);
2375      ts.push(it.ts!);
2376    });
2377    queryWakeupListPriority(itids, ts, cpus).then((res) => {
2378      let resource = data.map((it) => {
2379        let wake = {
2380          process: `${it.process}(${it.pid})`,
2381          thread: `${it.thread}(${it.tid})`,
2382          cpu: it.cpu,
2383          dur: it.dur,
2384          priority: 0,
2385          isSelected: false,
2386        };
2387        //@ts-ignore
2388        let find = res.find((re) => re.cpu === it.cpu && re.itid === it.itid && re.ts === it.ts);
2389        if (find) {
2390          //@ts-ignore
2391          wake.priority = find.priority;
2392        }
2393        maxDuration = Math.max(maxDuration, it.dur!);
2394        maxPriority = Math.max(maxPriority, wake.priority);
2395        return wake;
2396      });
2397      if (this.selectWakeupBean) {
2398        // 点击第一层唤醒树时向数组头部添加当前点击信息
2399        if (data[0].schedulingLatency) {
2400          // @ts-ignore
2401          resource.unshift(this.selectWakeupBean);
2402        }
2403        // @ts-ignore
2404        maxDuration = Math.max(maxDuration, this.selectWakeupBean.dur);
2405        // @ts-ignore
2406        maxPriority = Math.max(maxPriority, this.selectWakeupBean.priority);
2407      }
2408      resource.forEach((it) => {
2409        if (it.priority === maxPriority) {
2410          maxPriorityDuration = Math.max(it.dur || 0, maxPriorityDuration);
2411        }
2412      });
2413      this.updateTableSettings(maxPriority, maxPriorityDuration, maxDuration);
2414      this.wakeupListTbl!.recycleDataSource = resource;
2415    });
2416  }
2417
2418  private updateTableSettings(maxPriority: number, maxPriorityDuration: number, maxDuration: number): void {
2419    this.wakeupListTbl!.getItemTextColor = (data: unknown): string => {
2420      // @ts-ignore
2421      if ((data.priority === maxPriority && data.dur === maxPriorityDuration) || data.dur === maxDuration) {
2422        return '#f44336';
2423      } else {
2424        return '#262626';
2425      }
2426    };
2427  }
2428
2429  addTableObserver(): void {
2430    let leftTable = this.shadowRoot?.querySelector('.table-left');
2431    this.tableObserver?.observe(leftTable!, {
2432      attributes: true,
2433      attributeFilter: ['style'],
2434      attributeOldValue: true,
2435    });
2436  }
2437
2438  initHtml(): string {
2439    return TabPaneCurrentSelectionHtml;
2440  }
2441}
2442
2443export class JankTreeNode {
2444  name: string = '';
2445  pid: number = -1;
2446  frameType: string = '';
2447  type: number = 0;
2448
2449  constructor(name: string, pid: number, frameType: string) {
2450    this.name = name;
2451    this.pid = pid;
2452    this.frameType = frameType;
2453  }
2454
2455  children: Array<JankTreeNode> = [];
2456}
2457
2458export class ThreadTreeNode {
2459  tid: number = 0;
2460  pid: number = -1;
2461  startTime: number = 1;
2462  depth: number = 0;
2463
2464  constructor(tid: number, pid: number, startTime: number, depth: number = 0) {
2465    this.tid = tid;
2466    this.pid = pid;
2467    this.startTime = startTime;
2468    this.depth = depth;
2469  }
2470}
2471
2472class FunDetail {
2473  slice: string = '';
2474  CN: string = '';
2475  EN: string = '';
2476}
2477
2478class SortData {
2479  value: string = '';
2480  dur: number = 0;
2481  bytes: number = 0;
2482}
2483