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