• 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 { ColorUtils } from '../base/ColorUtils';
30import { IrqStruct } from '../../../database/ui-worker/ProcedureWorkerIrq';
31import { BinderArgBean } from '../../../bean/BinderArgBean';
32import { JankStruct } from '../../../database/ui-worker/ProcedureWorkerJank';
33import { Utils } from '../base/Utils';
34import { SpSystemTrace } from '../../SpSystemTrace';
35import { AppStartupStruct } from '../../../database/ui-worker/ProcedureWorkerAppStartup';
36import { SoStruct } from '../../../database/ui-worker/ProcedureWorkerSoInit';
37import { type SelectionParam } from '../../../bean/BoxSelection';
38import {
39  type FrameAnimationStruct
40} from '../../../database/ui-worker/ProcedureWorkerFrameAnimation';
41import {
42  queryBinderByArgsId,
43  queryBinderBySliceId,
44  queryFlowsData,
45  queryPrecedingData,
46  queryThreadByItid
47} from '../../../database/sql/SqlLite.sql';
48import {
49  queryBinderArgsByArgset,
50  queryRunnableTimeByRunning,
51  queryThreadNearData,
52  queryThreadStateArgs,
53  queryThreadWakeUp,
54  queryThreadWakeUpFrom
55} from '../../../database/sql/ProcessThread.sql';
56import { queryGpuDur } from '../../../database/sql/Gpu.sql';
57import { queryWakeupListPriority } from '../../../database/sql/Cpu.sql';
58import { TabPaneCurrentSelectionHtml } from './TabPaneCurrentSelection.html';
59
60const INPUT_WORD =
61  '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.';
62
63export function getTimeString(ns: number): string {
64
65  if (ns === 0) {
66    return '0';
67  }
68  let currentTimeNs = ns;
69  let hour1 = 3600_000_000_000;
70  let minute1 = 60_000_000_000;
71  let second1 = 1_000_000_000; // 1 second
72  let millisecond1 = 1_000_000; // 1 millisecond
73  let microsecond1 = 1_000; // 1 microsecond
74  let res = '';
75  if (currentTimeNs >= hour1) {
76
77    res += Math.floor(currentTimeNs / hour1) + 'h ';
78    currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / hour1) * hour1;
79  }
80  if (currentTimeNs >= minute1) {
81    res += Math.floor(currentTimeNs / minute1) + 'm ';
82    currentTimeNs = currentTimeNs - Math.floor(ns / minute1) * minute1;
83  }
84  if (currentTimeNs >= second1) {
85    res += Math.floor(currentTimeNs / second1) + 's ';
86    currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / second1) * second1;
87  }
88  if (currentTimeNs >= millisecond1) {
89    res += Math.floor(currentTimeNs / millisecond1) + 'ms ';
90    currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / millisecond1) * millisecond1;
91  }
92  if (currentTimeNs >= microsecond1) {
93    res += Math.floor(currentTimeNs / microsecond1) + 'μs ';
94    currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / microsecond1) * microsecond1;
95  }
96  if (currentTimeNs > 0) {
97    res += currentTimeNs.toFixed(0) + 'ns ';
98  }
99  return res;
100}
101
102@element('tabpane-current-selection')
103export class TabPaneCurrentSelection extends BaseElement {
104  weakUpBean: WakeupBean | null | undefined;
105  selectWakeupBean: any;
106  private currentSelectionTbl: LitTable | null | undefined;
107  private tableObserver: MutationObserver | undefined;
108  private wakeupListTbl: LitTable | undefined | null;
109  private scrollView: HTMLDivElement | null | undefined;
110  // @ts-ignore
111  private dpr: any = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1;
112
113  set data(currentSelectionValue: any) {
114    if (
115      currentSelectionValue !== undefined &&
116      currentSelectionValue.constructor &&
117      currentSelectionValue.constructor.name !== 'SelectionParam'
118    ) {
119      this.setCpuData(currentSelectionValue);
120    }
121  }
122
123  setCpuData(
124    data: CpuStruct,
125    callback: ((data: WakeupBean | null) => void) | undefined = undefined,
126    scrollCallback?: (data: CpuStruct) => void
127  ): void {
128    this.setTableHeight('650px');
129    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
130    if (leftTitle) {
131      leftTitle.innerText = 'Slice Details';
132    }
133    let list: any[] = [];
134    this.updateUI(data, list);
135    Promise.all([this.queryThreadStateDArgs(data.argSetID), this.queryCPUWakeUpFromData(data)]).then((resArr) => {
136      let args = resArr[0];
137      let bean = resArr[1];
138      if (callback) {
139        callback(bean);
140      }
141      if (args.length > 0) {
142        args.forEach((arg) => {
143          list.push({name: arg.keyName, value: arg.strValue});
144        });
145      }
146      this.currentSelectionTbl!.dataSource = list;
147      let rightArea: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#table-right');
148      let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle');
149      let rightButton: HTMLElement | null | undefined = this?.shadowRoot
150      ?.querySelector('#rightButton')
151      ?.shadowRoot?.querySelector('#custom-button');
152      let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star');
153      this.threadClickEvent(scrollCallback, data);
154      let canvas = this.initCanvas();
155      if (bean !== null) {
156        this.selectWakeupBean = {
157          process: `${this.transferString(data.processName || 'Process')}(${data.processId})`,
158          thread: `${this.transferString(data.name || 'Thread')}(${data.tid})`,
159          cpu: data.cpu,
160          dur: data.dur,
161          priority: data.priority,
162          isSelected: false,
163        };
164
165        this.weakUpBean = bean;
166        this.updateRightTitleUI(rightArea!, rightTitle!, rightButton!, rightStar!);
167        this.drawRight(canvas, bean);
168      } else {
169        this.handleNullBeanUI(rightArea!, rightTitle!, rightButton!, rightStar!);
170      }
171    });
172  }
173
174  private threadClickEvent(scrollCallback: ((data: CpuStruct) => void) | undefined, data: CpuStruct): void {
175    let threadClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#thread-id');
176    threadClick?.addEventListener('click', () => {
177      //cpu点击
178      if (scrollCallback) {
179        data.state = 'Running';
180        scrollCallback(data);
181      }
182    });
183  }
184
185  private updateRightTitleUI(rightArea: HTMLElement, rightTitle: HTMLElement, rightButton: HTMLElement,
186                             rightStar: HTMLElement) {
187    if (rightArea !== null && rightArea) {
188      rightArea.style.visibility = 'visible';
189    }
190    if (rightTitle !== null && rightTitle) {
191      rightTitle.style.visibility = 'visible';
192      rightButton!.style.visibility = 'visible';
193      rightStar!.style.visibility = 'hidden';
194      SpSystemTrace.btnTimer = null;
195    }
196  }
197
198  private handleNullBeanUI(rightArea: HTMLElement, rightTitle: HTMLElement, rightButton: HTMLElement,
199                           rightStar: HTMLElement) {
200    this.weakUpBean = null;
201    if (rightArea !== null && rightArea) {
202      rightArea.style.visibility = 'hidden';
203    }
204    if (rightTitle !== null && rightTitle) {
205      rightTitle.style.visibility = 'hidden';
206      rightButton!.style.visibility = 'hidden';
207      rightStar!.style.visibility = 'hidden';
208    }
209  }
210
211  private updateUI(data: CpuStruct, list: any[]) {
212    let process = this.transferString(data.processName || 'Process');
213    let processId = data.processId || data.tid;
214    let state = '';
215    if (data.end_state) {
216      state = Utils.getEndState(data.end_state);
217    } else if (data.end_state === '' || data.end_state === null) {
218      state = '';
219    } else {
220      state = 'Unknown State';
221    }
222    list.push({
223      name: 'Process',
224      value: `${process || 'Process'} [${processId}]`,
225    });
226    let name = this.transferString(data.name ?? '');
227    if (data.processId) {
228      list.push({
229        name: 'Thread',
230        value: `<div style="white-space: nowrap;display: flex;align-items: center">
231<div style="white-space:pre-wrap">${name || 'Process'} [${data.tid}]</div>
232<lit-icon style="cursor:pointer;margin-left: 5px" id="thread-id" name="select" color="#7fa1e7" size="20"></lit-icon>
233</div>`,
234      });
235    } else {
236      list.push({
237        name: 'Thread',
238        value: `<div style="white-space: nowrap;display: flex;align-items: center">
239<div style="white-space:pre-wrap">${name || 'Process'} [${data.tid}]</div>
240</div>`,
241      });
242    }
243    list.push({name: 'StartTime(Relative)', value: getTimeString(data.startTime || 0),});
244    list.push({
245      name: 'StartTime(Absolute)',
246      value: ((data.startTime || 0) + (window as any).recordStartNS) / 1000000000 + 's',
247    });
248    list.push({name: 'Duration', value: getTimeString(data.dur || 0)});
249    list.push({name: 'Prio', value: data.priority || 0});
250    list.push({name: 'End State', value: state});
251  }
252
253  setFunctionData(data: FuncStruct, scrollCallback: Function): void {
254    //方法信息
255    this.tabCurrentSelectionInit('Slice Details');
256    let list: any[] = [];
257    let name = this.transferString(data.funName ?? '');
258    let isBinder = FuncStruct.isBinder(data);
259    let isAsyncBinder = isBinder && FuncStruct.isBinderAsync(data);
260    if (data.argsetid !== undefined && data.argsetid !== null && data.argsetid >= 0) {
261      this.setTableHeight('700px');
262      if (isAsyncBinder) {
263        this.handleAsyncBinder(data, list, name, scrollCallback);
264      } else if (isBinder) {
265        this.handleBinder(data, list, name, scrollCallback);
266      } else {
267        this.handleNonBinder(data, list, name);
268      }
269    } else {
270      this.setTableHeight('auto');
271      list.push({name: 'Name', value: name});
272      list.push({
273        name: 'StartTime(Relative)',
274        value: getTimeString(data.startTs || 0),
275      });
276      list.push({
277        name: 'StartTime(Absolute)',
278        value: ((data.startTs || 0) + (window as any).recordStartNS) / 1000000000 + 's',
279      });
280      list.push({
281        name: 'Duration',
282        value: getTimeString(data.dur || 0),
283      });
284      list.push({name: 'depth', value: data.depth});
285      this.currentSelectionTbl!.dataSource = list;
286    }
287  }
288
289  private handleNonBinder(data: FuncStruct, list: any[], name: string): void {
290    queryBinderArgsByArgset(data.argsetid!).then((argset) => {
291      list.push({name: 'Name', value: name});
292      argset.forEach((item) => {
293        list.push({name: item.keyName, value: item.strValue});
294      });
295      this.addTabPanelContent(list, data);
296      this.currentSelectionTbl!.dataSource = list;
297    });
298  }
299
300  private handleBinder(data: FuncStruct, list: any[], name: string, scrollCallback: Function): void {
301    queryBinderArgsByArgset(data.argsetid!).then((argset) => {
302      let binderSliceId = -1;
303      argset.forEach((item) => {
304        if (item.keyName === 'destination slice id') {
305          binderSliceId = Number(item.strValue);
306          list.unshift({
307            name: 'Name',
308            value: `<div style="white-space: nowrap;display: flex;align-items: center">
309<div style="white-space:pre-wrap">${name || 'binder'}</div>
310<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="function-jump" name="select" color="#7fa1e7" size="20"></lit-icon>
311</div>`,
312          });
313        }
314        list.push({name: item.keyName, value: item.strValue});
315      });
316      if (binderSliceId === -1) {
317        list.unshift({name: 'Name', value: name});
318      }
319      this.addTabPanelContent(list, data);
320      this.currentSelectionTbl!.dataSource = list;
321      let funcClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#function-jump');
322      funcClick?.addEventListener('click', () => {
323        if (!Number.isNaN(binderSliceId) && binderSliceId !== -1) {
324          queryBinderBySliceId(binderSliceId).then((result: any[]) => {
325            if (result.length > 0) {
326              result[0].type = TraceRow.ROW_TYPE_FUNC;
327              scrollCallback(result[0]);
328            }
329          });
330        }
331      });
332    });
333  }
334
335  private handleAsyncBinder(data: FuncStruct, list: any[], name: string, scrollCallback: Function): void {
336    Promise.all([
337      queryBinderByArgsId(data.argsetid!, data.startTs!, !data.funName!.endsWith('rcv')),
338      queryBinderArgsByArgset(data.argsetid!),
339    ]).then((result) => {
340      let asyncBinderRes = result[0];
341      let argsBinderRes = result[1];
342      let asyncBinderStract: any;
343      if (asyncBinderRes.length > 0) {
344        asyncBinderRes[0].type = TraceRow.ROW_TYPE_FUNC;
345        asyncBinderStract = asyncBinderRes[0];
346      }
347      if (argsBinderRes.length > 0) {
348        argsBinderRes.forEach((item) => {
349          list.push({
350            name: item.keyName,
351            value: item.strValue,
352          });
353        });
354      }
355      if (asyncBinderStract !== undefined) {
356        list.unshift({
357          name: 'Name',
358          value: `<div style="white-space: nowrap;display: flex;align-items: center">
359<div style="white-space:pre-wrap">${name || 'binder'}</div>
360<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="function-jump" name="select" color="#7fa1e7" size="20"></lit-icon>
361</div>`,
362        });
363      } else {
364        list.unshift({name: 'Name', value: name});
365      }
366      this.addTabPanelContent(list, data);
367      this.currentSelectionTbl!.dataSource = list;
368      let funcClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#function-jump');
369      funcClick?.addEventListener('click', () => {
370        scrollCallback(asyncBinderStract);
371      });
372    });
373  }
374
375  private addTabPanelContent(contentList: any[], data: FuncStruct): void {
376    contentList.push({
377      name: 'StartTime(Relative)',
378      value: getTimeString(data.startTs || 0),
379    });
380    contentList.push({
381      name: 'StartTime(Absolute)',
382      value: ((data.startTs || 0) + (window as any).recordStartNS) / 1000000000 + 's',
383    });
384    contentList.push({
385      name: 'Duration',
386      value: getTimeString(data.dur || 0),
387    });
388    contentList.push({name: 'depth', value: data.depth});
389    if (data.argsetid && data.argsetid > -1) {
390      contentList.push({name: 'arg_set_id', value: data.argsetid});
391    }
392  }
393
394  private tabCurrentSelectionInit(leftTitleStr: string): void {
395    this.initCanvas();
396    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
397    this.setTitleAndButtonStyle();
398    if (leftTitle) {
399      leftTitle.innerText = leftTitleStr;
400    }
401  }
402
403  private setTitleAndButtonStyle(): void {
404    let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle');
405    let rightButton: HTMLElement | null | undefined = this?.shadowRoot
406    ?.querySelector('#rightButton')
407    ?.shadowRoot?.querySelector('#custom-button');
408    let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star');
409    if (rightTitle) {
410      rightTitle.style.visibility = 'hidden';
411      rightButton!.style.visibility = 'hidden';
412      rightStar!.style.visibility = 'hidden';
413    }
414  }
415
416  setClockData(data: ClockStruct): void {
417    this.setTableHeight('auto');
418    //时钟信息
419    this.tabCurrentSelectionInit('Counter Details');
420    let list: any[] = [];
421    list.push({
422      name: 'StartTime(Relative)',
423      value: getTimeString(data.startNS || 0),
424    });
425    list.push({
426      name: 'StartTime(Absolute)',
427      value: ((data.startNS || 0) + (window as any).recordStartNS) / 1000000000 + 's',
428    });
429    list.push({
430      name: 'Value',
431      value: ColorUtils.formatNumberComma(data.value || 0),
432    });
433    list.push({name: 'Duration', value: getTimeString(data.dur || 0)});
434    this.currentSelectionTbl!.dataSource = list;
435  }
436
437  setMemData(data: ProcessMemStruct): void {
438    this.setTableHeight('auto');
439    //时钟信息
440    this.initCanvas();
441    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
442    if (leftTitle) {
443      leftTitle.innerText = 'Counter Details';
444    }
445    let list: object[] = [];
446    list.push({
447      name: 'StartTime(Relative)',
448      value: getTimeString(data.startTime || 0),
449    });
450    list.push({
451      name: 'StartTime(Absolute)',
452      value: ((data.startTime || 0) + (window as any).recordStartNS) / 1000000000 + 's',
453    });
454    list.push({name: 'Value', value: data.value});
455    list.push({name: 'Delta', value: data.delta});
456    list.push({
457      name: 'Duration',
458      value: getTimeString(data.duration || 0),
459    });
460    this.currentSelectionTbl!.dataSource = list;
461  }
462
463  setIrqData(data: IrqStruct): void {
464    this.setTableHeight('550px');
465    this.initCanvas();
466    this.setTitleAndButtonStyle();
467    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
468    if (leftTitle) {
469      leftTitle.innerText = 'Counter Details';
470    }
471    let list: object[] = [];
472    list.push({
473      name: 'StartTime(Relative)',
474      value: getTimeString(data.startNS || 0),
475    });
476    list.push({
477      name: 'StartTime(Absolute)',
478      value: ((data.startNS || 0) + (window as any).recordStartNS) / 1000000000 + 's',
479    });
480    list.push({name: 'Name', value: data.name});
481    list.push({name: 'Duration', value: getTimeString(data.dur || 0)});
482    queryBinderArgsByArgset(data.argSetId || 0).then((argsBinderRes) => {
483      if (argsBinderRes.length > 0) {
484        argsBinderRes.forEach((item) => {
485          list.push({name: item.keyName, value: item.strValue});
486        });
487      }
488      this.currentSelectionTbl!.dataSource = list;
489    });
490  }
491
492  setThreadData(
493    data: ThreadStruct,
494    scrollCallback: ((d: any) => void) | undefined,
495    scrollWakeUp: (d: any) => void | undefined,
496    callback: ((data: Array<any>) => void) | undefined = undefined
497  ): void {
498    //线程信息
499    this.setTableHeight('550px');
500    this.initCanvas();
501    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
502    this.setTitleAndButtonStyle();
503    if (leftTitle) {
504      leftTitle.innerText = 'Thread State';
505    }
506    let list: any[] = [];
507    let jankJumperList = new Array<ThreadTreeNode>();
508    this.prepareThreadInfo(list, data);
509    let cpu = new CpuStruct();
510    cpu.id = data.id;
511    cpu.startTime = data.startTime;
512    this.queryThreadDetails(data, list, jankJumperList, callback, scrollWakeUp, scrollCallback);
513  }
514
515  private sortByNearData(nearData: any[], data: ThreadStruct, list: any[]): any[] {
516    let preData: any = undefined;
517    let nextData: any = undefined;
518    nearData.sort((near1, near2) => near1.startTime - near2.startTime).forEach((near) => {
519      if (near.itid === data.id) {
520        if (near.startTime < data.startTime!) {
521          preData = near;
522          list.push({
523            name: 'Previous State',
524            value: `<div style="white-space: nowrap;display: flex;align-items: center">
525              <div style="white-space:pre-wrap">${Utils.getEndState(near.state)}</div>
526              <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="previous-state-click" name="select" color="#7fa1e7" size="20"></lit-icon>
527              </div>`,
528          });
529        } else {
530          nextData = near;
531          list.push({
532            name: 'Next State',
533            value: `<div style="white-space: nowrap;display: flex;align-items: center">
534              <div style="white-space:pre-wrap">${Utils.getEndState(near.state)}</div>
535              <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="next-state-click" name="select" color="#7fa1e7" size="20"></lit-icon>
536              </div>`,
537          });
538        }
539      }
540    });
541    return [preData, nextData];
542  }
543
544  private setWakeupData(fromBean: WakeupBean | undefined, wakeUps: WakeupBean[], list: any[]): void {
545    if (fromBean !== null && fromBean !== undefined && fromBean.pid !== 0 && fromBean.tid !== 0) {
546      list.push({
547        name: 'wakeup from tid',
548        value: `<div style="white-space: nowrap;display: flex;align-items: center">
549            <div style="white-space:pre-wrap">${fromBean.tid}</div>
550            <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="wakeup-from"
551            class="wakeup-click"  name="select" color="#7fa1e7" size="20"></lit-icon>
552            </div>`,
553      });
554    }
555    if (wakeUps !== null) {
556      wakeUps.map((e) => {
557        list.push({
558          name: 'wakeup tid',
559          value: `<div style="white-space: nowrap;display: flex;align-items: center">
560            <div style="white-space:pre-wrap">${e.tid}</div>
561            <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="wakeup-${e.tid}"
562            class="wakeup-click" name="select" color="#7fa1e7" size="20"></lit-icon>
563            </div>`,
564        });
565      });
566    }
567  }
568
569  private queryThreadDetails(
570    data: ThreadStruct,
571    list: any[],
572    jankJumperList: ThreadTreeNode[],
573    callback: ((data: Array<any>) => void) | undefined,
574    scrollWakeUp: (d: any) => (void | undefined),
575    scrollCallback: ((d: any) => void) | undefined
576  ): void {
577    Promise.all([
578      this.queryThreadWakeUpFromData(data.id!, data.startTime!, data.dur!),
579      this.queryThreadWakeUpData(data.id!, data.startTime!, data.dur!),
580      this.queryThreadStateDArgs(data.argSetID),
581      queryThreadNearData(data.id!, data.startTime!),
582    ]).then((result) => {
583      let fromBean = result[0];
584      let wakeUps = result[1];
585      let args = result[2];
586      let [preData, nextData] = this.sortByNearData(result[3], data, list);
587      this.setWakeupData(fromBean, wakeUps, list);
588      let timeLineNode = new ThreadTreeNode(data.tid!, data.pid!, data.startTime!);
589      jankJumperList.push(timeLineNode);
590      if (args.length > 0) {
591        args.forEach((arg) => {
592          list.push({name: arg.keyName, value: arg.strValue});
593        });
594      }
595      this.currentSelectionTbl!.dataSource = list;
596      if (callback) {
597        callback(jankJumperList);
598      }
599      this.stateClickHandler(preData, nextData, data, scrollWakeUp, scrollCallback);
600      this.wakeupClickHandler(wakeUps, fromBean, scrollWakeUp);
601    });
602  }
603
604  private wakeupClickHandler(
605    wakeUps: WakeupBean[],
606    fromBean: WakeupBean | undefined,
607    scrollWakeUp: (d: any) => (void | undefined)
608  ): void {
609    this.currentSelectionTbl?.shadowRoot?.querySelector('#wakeup-from')?.addEventListener('click', () => {
610      //点击跳转,唤醒和被唤醒的 线程
611      if (fromBean && scrollWakeUp) {
612        scrollWakeUp({
613          processId: fromBean.pid,
614          tid: fromBean.tid,
615          startTime: fromBean.ts,
616          dur: fromBean.dur,
617          cpu: fromBean.cpu,
618          id: fromBean.itid,
619          state: 'Running',
620          argSetID: fromBean.argSetID,
621        });
622      }
623    });
624    if (wakeUps) {
625      wakeUps.map((up) => {
626        this.currentSelectionTbl?.shadowRoot?.querySelector(`#wakeup-${up.tid}`)?.addEventListener('click', () => {
627          //点击跳转,唤醒和被唤醒的 线程
628          if (up && scrollWakeUp !== undefined) {
629            scrollWakeUp({
630              processId: up.pid,
631              tid: up.tid,
632              startTime: up.ts,
633              dur: up.dur,
634              cpu: up.cpu,
635              id: up.itid,
636              state: up.state,
637              argSetID: up.argSetID,
638            });
639          }
640        });
641      });
642    }
643  }
644
645  private stateClickHandler(
646    preData: any,
647    nextData: any,
648    data: ThreadStruct,
649    scrollWakeUp: (d: any) => (void | undefined),
650    scrollCallback: ((d: any) => void) | undefined
651  ): void {
652    this.currentSelectionTbl?.shadowRoot?.querySelector('#next-state-click')?.addEventListener('click', () => {
653      if (nextData && scrollWakeUp !== undefined) {
654        scrollWakeUp({
655          processId: nextData.pid,
656          tid: nextData.tid,
657          startTime: nextData.startTime,
658          dur: nextData.dur,
659          cpu: nextData.cpu,
660          id: nextData.itid,
661          state: nextData.state,
662          argSetID: nextData.argSetID,
663        });
664      }
665    });
666
667    this.currentSelectionTbl?.shadowRoot?.querySelector('#previous-state-click')?.addEventListener('click', () => {
668      if (preData && scrollWakeUp !== undefined) {
669        scrollWakeUp({
670          processId: preData.pid,
671          tid: preData.tid,
672          startTime: preData.startTime,
673          dur: preData.dur,
674          cpu: preData.cpu,
675          id: preData.itid,
676          state: preData.state,
677          argSetID: preData.argSetID,
678        });
679      }
680    });
681
682    this.currentSelectionTbl?.shadowRoot?.querySelector('#state-click')?.addEventListener('click', () => {
683      //线程点击
684      if (scrollCallback) {
685        scrollCallback(data);
686      }
687    });
688  }
689
690  private prepareThreadInfo(list: any[], data: ThreadStruct): void {
691    list.push({
692      name: 'StartTime(Relative)',
693      value: getTimeString(data.startTime || 0),
694    });
695    list.push({
696      name: 'StartTime(Absolute)',
697      value: ((data.startTime || 0) + (window as any).recordStartNS) / 1000000000 + 's',
698    });
699    list.push({name: 'Duration', value: getTimeString(data.dur || 0)});
700    let state;
701    if (data.state) {
702      state = Utils.getEndState(data.state);
703    } else if (data.state === '' || data.state === null) {
704      state = '';
705    } else {
706      state = 'Unknown State';
707    }
708    if ('Running' === state) {
709      state = state + ' on CPU ' + data.cpu;
710      list.push({
711        name: 'State',
712        value: `<div style="white-space: nowrap;display: flex;align-items: center">
713            <div style="white-space:pre-wrap">${state}</div>
714            <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="state-click" name="select" color="#7fa1e7" size="20"></lit-icon>
715            </div>`,
716      });
717    } else {
718      list.push({name: 'State', value: `${state}`});
719    }
720    let slice = Utils.SCHED_SLICE_MAP.get(`${data.id}-${data.startTime}`);
721    if (slice) {
722      list.push({name: 'Prio', value: `${slice.priority}`});
723    }
724    let processName = Utils.PROCESS_MAP.get(data.pid!);
725    if (
726      processName === null ||
727      processName === undefined ||
728      processName === '' ||
729      processName.toLowerCase() == 'null'
730    ) {
731      processName = Utils.THREAD_MAP.get(data.tid!) || 'null';
732    }
733    list.push({
734      name: 'Process',
735      value: this.transferString(processName ?? '') + ' [' + data.pid + '] ',
736    });
737  }
738
739  setJankData(
740    data: JankStruct,
741    callback: ((data: Array<any>) => void) | undefined = undefined,
742    scrollCallback: ((d: any) => void) | undefined
743  ): void {
744    //线程信息
745    this.setTableHeight('750px');
746    this.tabCurrentSelectionInit('Slice Details');
747    let list: any[] = [];
748    this.setJankCommonMessage(list, data);
749    if (data.type == '0') {
750      this.handleTypeJank(data, list, scrollCallback, callback);
751    } else {
752      this.currentSelectionTbl!.dataSource = list;
753    }
754  }
755
756  private handleTypeJank(data: JankStruct, list: any[], scrollCallback: ((d: any) => void) | undefined,
757                         callback: ((data: Array<any>) => void) | undefined): void {
758    this.setJankType(data, list);
759    let jankJumperList = new Array<JankTreeNode>();
760    if (data.frame_type === 'render_service') {
761      queryGpuDur(data.id!).then((it) => {
762        if (it.length > 0) {
763          list.push({name: 'Gpu Duration', value: getTimeString(it[0].gpu_dur)});
764        }
765      });
766      this.handleRenderServiceJank(data, list, jankJumperList, scrollCallback, callback);
767    } else if (data.frame_type === 'app') {
768      this.handleAppJank(list, data, jankJumperList, scrollCallback, callback);
769    } else if (data.frame_type === 'frameTime') {
770      this.handleFrameTimeJank(data, list, jankJumperList, scrollCallback, callback);
771    }
772  }
773
774  private handleFrameTimeJank(data: JankStruct, list: any[], jankJumperList: JankTreeNode[],
775                              scrollCallback: ((d: any) => void) | undefined, callback: ((data: Array<any>) => void) | undefined): void {
776    queryGpuDur(data.id!).then((it) => {
777      if (it.length > 0) {
778        list.push({
779          name: 'Gpu Duration',
780          value: getTimeString(it[0].gpu_dur),
781        });
782      }
783      this.addAppFrameDetails(data, list);
784      this.addRenderServiceFrameDetails(data, list);
785      this.addFollowingDetails(list, data);
786      let appNode = new JankTreeNode(data.name!, data.pid!, 'app');
787      let rsNode = new JankTreeNode(data.rs_vsync!, data.rs_pid!, 'render_service');
788      appNode.children.push(rsNode);
789      jankJumperList.push(appNode);
790      this.currentSelectionTbl!.dataSource = list;
791      this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList);
792    });
793  }
794
795  private addRenderServiceFrameDetails(data: JankStruct, list: any[]): void {
796    if (data.rs_name) {
797      list.push({
798        name: 'RenderService Frame',
799        value: '',
800      });
801      list.push({
802        name: 'Process',
803        value: 'render_service ' + data.rs_pid,
804      });
805      list.push({
806        name: 'StartTime(Relative)',
807        value: getTimeString(data.rs_ts || 0),
808      });
809      list.push({
810        name: 'StartTime(Absolute)',
811        value: ((data.rs_ts || 0) + (window as any).recordStartNS) / 1000000000 + 's',
812      });
813      list.push({
814        name: 'end time',
815        value: getTimeString(data.rs_ts! + data.rs_dur! || 0),
816      });
817    }
818  }
819
820  private addFollowingDetails(list: any[], data: JankStruct): void {
821    list.push({
822      name: 'Following',
823      value: '',
824    });
825    list.push({
826      name: 'Slice',
827      value:
828        data.cmdline +
829        ' [' +
830        data.name +
831        ']' +
832        `<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>`,
833    });
834  }
835
836  private addAppFrameDetails(data: JankStruct, list: any[]): void {
837    if (data.name) {
838      list.push({
839        name: 'App Frame',
840        value: '',
841      });
842      list.push({
843        name: 'Process',
844        value: data.cmdline + ' ' + data.pid,
845      });
846      list.push({
847        name: 'StartTime(Relative)',
848        value: getTimeString(data.ts || 0),
849      });
850      list.push({
851        name: 'StartTime(Absolute)',
852        value: ((data.ts || 0) + (window as any).recordStartNS) / 1000000000 + 's',
853      });
854      list.push({
855        name: 'end time',
856        value: getTimeString(data!.ts! + data.dur! || 0),
857      });
858    }
859  }
860
861  private handleAppJank(list: any[], data: JankStruct, jankJumperList: JankTreeNode[],
862                        scrollCallback: ((d: any) => void) | undefined, callback: ((data: Array<any>) => void) | undefined): void {
863    list.push({
864      name: 'FrameTimeLine flows',
865      value: '',
866    });
867    list.push({
868      name: 'Slice',
869      value:
870        data.cmdline +
871        ' [' +
872        data.name +
873        ']' +
874        `<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>`,
875    });
876    let timeLineNode = new JankTreeNode(data.name!, data.pid!, 'frameTime');
877    jankJumperList.push(timeLineNode);
878    if (data.dst_slice) {
879      queryPrecedingData(data.dst_slice).then((it) => {
880        if (it.length > 0) {
881          list.push({
882            name: 'Preceding flows',
883            value: '',
884          });
885          it.forEach((a: any) => {
886            let rsNode = new JankTreeNode(a.name, a.pid, 'render_service');
887            jankJumperList.push(rsNode);
888            list.push({
889              name: 'Slice',
890              value:
891                a.cmdline +
892                ' [' +
893                a.name +
894                ']' +
895                `<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>`,
896            });
897          });
898          this.currentSelectionTbl!.dataSource = list;
899          this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList);
900        }
901      });
902    } else {
903      this.currentSelectionTbl!.dataSource = list;
904      this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList);
905    }
906  }
907
908  private handleRenderServiceJank(data: JankStruct, list: any[], jankJumperList: JankTreeNode[],
909                                  scrollCallback: ((d: any) => void) | undefined, callback: ((data: Array<any>) => void) | undefined): void {
910    if (data.src_slice) {
911      queryFlowsData(data.src_slice!.split(',')).then((it) => {
912        if (it.length > 0) {
913          list.push({
914            name: 'FrameTimeLine flows',
915            value: '',
916          });
917          it.forEach((a: any) => {
918            let appNode = new JankTreeNode(a.name, a.pid, 'app');
919            appNode.children.push(new JankTreeNode(a.name, a.pid, 'frameTime'));
920            jankJumperList.push(appNode);
921            list.push({
922              name: 'Slice',
923              value:
924                a.cmdline +
925                ' [' +
926                a.name +
927                ']' +
928                `<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>`,
929            });
930          });
931          list.push({
932            name: 'Following flows',
933            value: '',
934          });
935          it.forEach((a: any) => {
936            list.push({
937              name: 'Slice',
938              value:
939                a.cmdline +
940                ' [' +
941                a.name +
942                ']' +
943                `<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>`,
944            });
945          });
946          this.currentSelectionTbl!.dataSource = list;
947          this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList);
948        }
949      });
950    } else {
951      this.currentSelectionTbl!.dataSource = list;
952    }
953  }
954
955  setAllStartupData(data: AllAppStartupStruct, scrollCallback: Function): void {
956    this.setTableHeight('550px');
957    this.initCanvas();
958    let allStartUpLeftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
959    let allStartUpmiddleTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightText');
960    let allStartUpRightButton: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightButton');
961    if (allStartUpmiddleTitle) allStartUpmiddleTitle.style.visibility = 'hidden';
962    if (allStartUpRightButton) allStartUpRightButton.style.visibility = 'hidden';
963    if (allStartUpLeftTitle) {
964      allStartUpLeftTitle.innerText = 'Details';
965    }
966    let list: any[] = [];
967    list.push({name: 'Name', value: data.stepName!});
968    list.push({
969      name: 'StartTime(Relative)',
970      value: getTimeString(data.startTs || 0),
971    });
972    list.push({
973      name: 'StartTime(Absolute)',
974      value: ((data.startTs || 0) + (window as any).recordStartNS) / 1000000000 + 's',
975    });
976    list.push({
977      name: 'EndTime(Relative)',
978      value: getTimeString((data.startTs || 0) + (data.dur || 0)),
979    });
980    list.push({
981      name: 'EndTime(Abslute)',
982      value: ((data.startTs || 0) + (data.dur || 0) + (window as any).recordStartNS) / 1000000000 + 's',
983    });
984    list.push({
985      name: 'Dur',
986      value: getTimeString(data.dur || 0),
987    });
988    this.currentSelectionTbl!.dataSource = list;
989  }
990
991  setStartupData(data: AppStartupStruct, scrollCallback: Function): void {
992    this.setTableHeight('550px');
993    this.initCanvas();
994    this.setStartUpStyle();
995    let list: any[] = [];
996    list.push({name: 'Name', value: AppStartupStruct.getStartupName(data.startName)});
997    list.push({
998      name: 'StartTime(Relative)',
999      value: `
1000      <div style="display: flex;white-space: nowrap;align-items: center">
1001<div style="white-space:pre-wrap">${getTimeString(data.startTs || 0)}</div>
1002<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="start-jump" name="select" color="#7fa1e7" size="20"></lit-icon>
1003</div>`,
1004    });
1005    list.push({
1006      name: 'StartTime(Absolute)',
1007      value: ((data.startTs || 0) + (window as any).recordStartNS) / 1000000000 + 's',
1008    });
1009    if (data.dur && data.dur > 0) {
1010      list.push({
1011        name: 'EndTime(Relative)',
1012        value: `<div style="white-space: nowrap;display: flex;align-items: center">
1013<div style="white-space:pre-wrap">${getTimeString((data.startTs || 0) + (data.dur || 0))}</div>
1014<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="end-jump" name="select" color="#7fa1e7" size="20"></lit-icon>
1015</div>`,
1016      });
1017      list.push({
1018        name: 'EndTime(Absolute)',
1019        value: ((data.startTs || 0) + (data.dur || 0) + (window as any).recordStartNS) / 1000000000 + 's',
1020      });
1021    } else {
1022      list.push({
1023        name: 'EndTime(Relative)',
1024        value: `Unknown Time`,
1025      });
1026      list.push({
1027        name: 'EndTime(Absolute)',
1028        value: 'Unknown Time',
1029      });
1030    }
1031    list.push({name: 'Duration', value: getTimeString(data.dur || 0)});
1032    this.currentSelectionTbl!.dataSource = list;
1033    this.attachScrollHandlers(data, scrollCallback);
1034  }
1035
1036  private setStartUpStyle() {
1037    let rightButton: HTMLElement | null | undefined = this?.shadowRoot
1038    ?.querySelector('#rightButton')
1039    ?.shadowRoot?.querySelector('#custom-button');
1040    let startUpRightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle');
1041    if (startUpRightTitle) {
1042      startUpRightTitle.style.visibility = 'hidden';
1043      rightButton!.style.visibility = 'hidden';
1044    }
1045    let startUpLeftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
1046    if (startUpLeftTitle) {
1047      startUpLeftTitle.innerText = 'Details';
1048    }
1049  }
1050
1051  private attachScrollHandlers(data: AppStartupStruct, scrollCallback: Function): void {
1052    let startIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#start-jump');
1053    let endIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#end-jump');
1054    let scrollClick = (type: number): void => {
1055      let recordNs: number = (window as any).recordStartNS;
1056      let useEnd = type === 1 && data.startName! < 6;
1057      queryThreadByItid(
1058        useEnd ? data.endItid! : data.itid!,
1059        useEnd ? recordNs + data.startTs! + data.dur! : recordNs + data.startTs!
1060      ).then((result) => {
1061        if (result.length > 0) {
1062          let pt: {
1063            pid: number;
1064            tid: number;
1065            dur: number;
1066            name: string;
1067            depth: number
1068          } = result[0];
1069          scrollCallback({
1070            pid: pt.pid,
1071            tid: pt.tid,
1072            type: 'func',
1073            dur: pt.dur,
1074            depth: pt.depth,
1075            funName: pt.name,
1076            startTs: useEnd ? (data.startTs || 0) + (data.dur || 0) : data.startTs,
1077            keepOpen: true,
1078          });
1079        }
1080      });
1081    };
1082    if (startIcon) {
1083      startIcon.addEventListener('click', () => scrollClick(0));
1084    }
1085    if (endIcon) {
1086      endIcon.addEventListener('click', () => scrollClick(1));
1087    }
1088  }
1089
1090  setStaticInitData(data: SoStruct, scrollCallback: Function): void {
1091    this.setTableHeight('550px');
1092    this.initCanvas();
1093    this.setStaticInitStyle();
1094    let list: any[] = [];
1095    list.push({name: 'Name', value: data.soName});
1096    list.push({
1097      name: 'StartTime(Relative)',
1098      value: `<div style="white-space: nowrap;display: flex;align-items: center">
1099<div style="white-space:pre-wrap">${getTimeString(data.startTs || 0)}</div>
1100<lit-icon id="start-jump" style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" name="select" color="#7fa1e7" size="20"></lit-icon>
1101</div>`,
1102    });
1103    list.push({
1104      name: 'StartTime(Absolute)',
1105      value: ((data.startTs || 0) + (window as any).recordStartNS) / 1000000000 + 's',
1106    });
1107    list.push({name: 'Duration', value: getTimeString(data.dur || 0)});
1108    this.currentSelectionTbl!.dataSource = list;
1109    this.startIconClickEvent(data, scrollCallback);
1110  }
1111
1112  private setStaticInitStyle(): void {
1113    let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle');
1114    let rightButton: HTMLElement | null | undefined = this?.shadowRoot
1115    ?.querySelector('#rightButton')
1116    ?.shadowRoot?.querySelector('#custom-button');
1117    if (rightTitle) {
1118      rightTitle.style.visibility = 'hidden';
1119      rightButton!.style.visibility = 'hidden';
1120    }
1121    let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle');
1122    if (leftTitle) {
1123      leftTitle.innerText = 'Details';
1124    }
1125  }
1126
1127  private startIconClickEvent(data: SoStruct, scrollCallback: Function): void {
1128    let startIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#start-jump');
1129    if (startIcon) {
1130      startIcon.addEventListener('click', () => {
1131        let recordNs: number = (window as any).recordStartNS;
1132        queryThreadByItid(data.itid!, recordNs + data.startTs!).then((result) => {
1133          if (result.length > 0) {
1134            let pt: {
1135              pid: number;
1136              tid: number;
1137              dur: number;
1138              name: string;
1139              depth: number
1140            } = result[0];
1141            scrollCallback({
1142              pid: pt.pid,
1143              tid: pt.tid,
1144              type: 'func',
1145              dur: pt.dur,
1146              depth: pt.depth,
1147              funName: pt.name,
1148              startTs: data.startTs,
1149              keepOpen: true,
1150            });
1151          }
1152        });
1153      });
1154    }
1155  }
1156
1157  async setFrameAnimationData(data: FrameAnimationStruct): Promise<void> {
1158    this.setTableHeight('550px');
1159    this.tabCurrentSelectionInit('Animation Details');
1160    let list = [];
1161    let dataTs: number = data.startTs < 0 ? 0 : data.startTs;
1162    list.push({name: 'Name', value: data.name});
1163    list.push({name: 'Start time(Relative)', value: `${Utils.getTimeString(dataTs)}`});
1164    list.push({
1165      name: 'Start time(Absolute)',
1166      value: ((dataTs || 0) + (window as any).recordStartNS) / 1000000000 + 's',
1167    });
1168    list.push({
1169      name: 'End time(Relative)',
1170      value: `${Utils.getTimeString(dataTs + (data.dur || 0))}`
1171    });
1172    list.push({
1173      name: 'End time(Absolute)',
1174      value: (dataTs + (data.dur || 0) + (window as any).recordStartNS) / 1000000000 + 's',
1175    });
1176    list.push({name: 'Duration', value: `${Utils.getTimeString(data.dur || 0)}`});
1177    if (data.status === 'Completion delay') {
1178      let frameFpsMessage = data.frameInfo?.split(':');
1179      if (frameFpsMessage) {
1180        if (frameFpsMessage[1] !== '0') {
1181          list.push({name: 'FPS', value: `${frameFpsMessage[1]}`});
1182        } else {
1183          let fixedNumber: number = 2;
1184          let fpsValue: number = Number(frameFpsMessage[0]) / (data.dur / 1000_000_000);
1185          list.push({name: 'FPS', value: `${fpsValue.toFixed(fixedNumber) || 0}`});
1186        }
1187      }
1188    }
1189    this.currentSelectionTbl!.dataSource = list;
1190  }
1191
1192  private setJankType(data: JankStruct, list: any[]): void {
1193    if (data.jank_tag === 1) {
1194      if (data.frame_type === 'render_service') {
1195        list.push({name: 'Jank Type', value: 'RenderService Deadline Missed'});
1196      } else if (data.frame_type === 'app') {
1197        list.push({name: 'Jank Type', value: 'APP Deadline Missed'});
1198      } else if (data.frame_type === 'frameTime') {
1199        list.push({name: 'Jank Type', value: 'Deadline Missed'});
1200      }
1201    } else if (data.jank_tag === 3) {
1202      list.push({name: 'Jank Type', value: 'Deadline Missed'});
1203    } else {
1204      list.push({name: 'Jank Type', value: 'NONE'});
1205    }
1206  }
1207
1208  private setJankCommonMessage(list: any[], data: JankStruct): void {
1209    list.push({name: 'Name', value: data.name});
1210    list.push({name: 'StartTime(Relative)', value: getTimeString(data.ts || 0)});
1211    list.push({
1212      name: 'StartTime(Absolute)',
1213      value: ((data.ts || 0) + (window as any).recordStartNS) / 1000000000 + 's',
1214    });
1215    list.push({name: 'Duration', value: data.dur ? getTimeString(data.dur) : ' '});
1216    if (data.frame_type !== 'frameTime') {
1217      list.push({name: 'Process', value: data.cmdline + ' ' + data.pid});
1218    }
1219  }
1220
1221  private setTableHeight(height: string): void {
1222    this.scrollView!.scrollTop = 0;
1223    this.currentSelectionTbl!.style.height = height;
1224    this.wakeupListTbl!.style.display = 'none';
1225  }
1226
1227  private addJankScrollCallBackEvent(
1228    scrollCallback: ((d: any) => void) | undefined,
1229    callback: ((data: Array<any>) => void) | undefined,
1230    jankJumperList: JankTreeNode[]
1231  ): void {
1232    let all = this.currentSelectionTbl?.shadowRoot?.querySelectorAll(`.jank_cla`);
1233    all!.forEach((a) => {
1234      a.addEventListener('click', () => {
1235        if (scrollCallback) {
1236          scrollCallback({
1237            rowId: a.id,
1238            name: a.getAttribute('slice_name'),
1239            pid: a.getAttribute('pid'),
1240          });
1241        }
1242      });
1243    });
1244    if (callback) {
1245      callback(jankJumperList);
1246    }
1247  }
1248
1249  async queryThreadStateDArgs(argSetID: number | undefined): Promise<BinderArgBean[]> {
1250    let list: Array<BinderArgBean> = [];
1251    if (argSetID !== undefined && argSetID > 0) {
1252      list = await queryThreadStateArgs(argSetID);
1253    }
1254    return list;
1255  }
1256
1257  /**
1258   * 查询出 线程被唤醒的 线程信息
1259   * @param data
1260   */
1261  async queryCPUWakeUpFromData(data: CpuStruct): Promise<WakeupBean | null> {
1262    let wb: WakeupBean | null = null;
1263    if (data.id === undefined || data.startTime === undefined) {
1264      return null;
1265    }
1266    let wakeup = await queryRunnableTimeByRunning(data.tid!, data.startTime);
1267    if (wakeup && wakeup[0]) {
1268      let wakeupTs = wakeup[0].ts as number;
1269      let recordStartTs = (window as any).recordStartNS;
1270      let wf = await queryThreadWakeUpFrom(data.id, wakeupTs);
1271      if (wf && wf[0]) {
1272        wb = wf[0];
1273        if (wb !== null) {
1274          wb.wakeupTime = wakeupTs - recordStartTs;
1275          wb.process = Utils.PROCESS_MAP.get(wb.pid!) || 'Process';
1276          wb.thread = Utils.THREAD_MAP.get(wb.tid!) || 'Thread';
1277          wb.schedulingLatency = (data.startTime || 0) - (wb.wakeupTime || 0);
1278          wb.schedulingDesc = INPUT_WORD;
1279        }
1280      }
1281    }
1282    return wb;
1283  }
1284
1285  /**
1286   * 查询出 线程被唤醒的 线程链信息
1287   * @param data
1288   */
1289  static async queryCPUWakeUpListFromBean(data: WakeupBean): Promise<WakeupBean | null> {
1290    let wb: WakeupBean | null = null;
1291    let wakeup = await queryRunnableTimeByRunning(data.tid!, data.ts!);
1292    if (wakeup && wakeup[0]) {
1293      let wakeupTs = wakeup[0].ts as number;
1294      let recordStartTs = (window as any).recordStartNS;
1295      let wf = await queryThreadWakeUpFrom(data.itid!, wakeupTs);
1296      if (wf && wf[0]) {
1297        wb = wf[0];
1298        if (wb !== null) {
1299          wb.wakeupTime = wakeupTs - recordStartTs;
1300          wb.process = Utils.PROCESS_MAP.get(wb.pid!) || 'Process';
1301          wb.thread = Utils.THREAD_MAP.get(wb.tid!) || 'Thread';
1302          wb.schedulingLatency = (data.ts || 0) - (wb.wakeupTime || 0);
1303          wb.schedulingDesc = INPUT_WORD;
1304        }
1305      }
1306    }
1307    return wb;
1308  }
1309
1310  /**
1311   * 查询出 线程唤醒了哪些线程信息
1312   */
1313  async queryThreadWakeUpFromData(itid: number, startTime: number, dur: number): Promise<WakeupBean | undefined> {
1314    let wakeUps = await queryThreadWakeUpFrom(itid, startTime + (window as any).recordStartNS);
1315    if (wakeUps !== undefined && wakeUps.length > 0) {
1316      return wakeUps[0];
1317    }
1318  }
1319
1320  /**
1321   * 查询出 线程唤醒了哪些线程信息
1322   */
1323  async queryThreadWakeUpData(itid: number, startTime: number, dur: number): Promise<Array<WakeupBean>> {
1324    let list: Array<WakeupBean> = [];
1325    if (itid === undefined || startTime === undefined) {
1326      return list;
1327    }
1328    let wakeUps = await queryThreadWakeUp(itid, startTime, dur); //  3,4835380000
1329    if (wakeUps !== undefined && wakeUps.length > 0) {
1330      list.push(...wakeUps);
1331    }
1332    return list;
1333  }
1334
1335  initCanvas(): HTMLCanvasElement | null {
1336    let canvas = this.shadowRoot!.querySelector<HTMLCanvasElement>('#rightDraw');
1337    let width = getComputedStyle(this.currentSelectionTbl!).getPropertyValue('width');
1338    if (canvas !== null) {
1339      canvas.width = Math.round(Number(width.replace('px', '')) * this.dpr);
1340      canvas.height = Math.round(Number(200 * this.dpr));
1341      canvas.style.width = width;
1342      canvas.style.height = '200px';
1343      canvas.getContext('2d')!.scale(this.dpr, this.dpr);
1344    }
1345    SpApplication.skinChange = (val: boolean): void => {
1346      this.drawRight(canvas, this.weakUpBean!);
1347    };
1348    return canvas;
1349  }
1350
1351  drawRight(cavs: HTMLCanvasElement | null, wakeupBean: WakeupBean | null): void {
1352    if (cavs === null) {
1353      return;
1354    }
1355    let context = cavs.getContext('2d');
1356    if (context !== null) {
1357      //绘制竖线
1358      this.drawVerticalLine(context);
1359      //绘制菱形
1360      context.lineWidth = 1;
1361      context.beginPath();
1362      context.moveTo(10, 30);
1363      context.lineTo(4, 40);
1364      context.lineTo(10, 50);
1365      context.lineTo(16, 40);
1366      context.lineTo(10, 30);
1367      context.closePath();
1368      context.fill();
1369      context.font = 12 + 'px sans-serif';
1370      //绘制wake up 文字
1371      let strList = [];
1372      strList.push('wakeup @ ' + getTimeString(wakeupBean?.wakeupTime || 0) + ' on CPU ' + wakeupBean?.cpu + ' by');
1373      strList.push('P:' + wakeupBean?.process + ' [ ' + wakeupBean?.pid + ' ]');
1374      strList.push('T:' + wakeupBean?.thread + ' [ ' + wakeupBean?.tid + ' ]');
1375      strList.forEach((str, index) => {
1376        if (context !== null) {
1377          context.fillText(str, 40, 40 + 16 * index);
1378        }
1379      });
1380      context.lineWidth = 2;
1381      context.lineJoin = 'bevel';
1382      context.moveTo(10, 95);
1383      context.lineTo(20, 90);
1384      context.moveTo(10, 95);
1385      context.lineTo(20, 100);
1386      context.moveTo(10, 95);
1387      context.lineTo(80, 95);
1388      context.lineTo(70, 90);
1389      context.moveTo(80, 95);
1390      context.lineTo(70, 100);
1391      context.stroke();
1392      //绘制latency
1393      context.font = 12 + 'px sans-serif';
1394      context.fillText('Scheduling latency:' + getTimeString(wakeupBean?.schedulingLatency || 0), 90, 100);
1395      //绘制最下方提示语句
1396      context.font = 10 + 'px sans-serif';
1397      INPUT_WORD.split('\n').forEach((str, index) => {
1398        context?.fillText(str, 90, 120 + 12 * index);
1399      });
1400    }
1401  }
1402
1403  private drawVerticalLine(context: CanvasRenderingContext2D): void {
1404    if (document.querySelector<SpApplication>('sp-application')!.dark) {
1405      context.strokeStyle = '#ffffff';
1406      context.fillStyle = '#ffffff';
1407    } else {
1408      context.strokeStyle = '#000000';
1409      context.fillStyle = '#000000';
1410    }
1411    context.lineWidth = 2;
1412    context.moveTo(10, 15);
1413    context.lineTo(10, 125);
1414    context.stroke();
1415  }
1416
1417  transferString(str: string): string {
1418    let s = '';
1419    if (str.length === 0) {
1420      return '';
1421    }
1422    s = str.replace(/&/g, '&amp;');
1423    s = s.replace(/</g, '&lt;');
1424    s = s.replace(/>/g, '&gt;');
1425    s = s.replace(/\'/g, '&#39;');
1426    s = s.replace(/\"/g, '&#quat;');
1427    return s;
1428  }
1429
1430  initElements(): void {
1431    this.currentSelectionTbl = this.shadowRoot?.querySelector<LitTable>('#selectionTbl');
1432    this.wakeupListTbl = this.shadowRoot?.querySelector<LitTable>('#wakeupListTbl');
1433    this.scrollView = this.shadowRoot?.querySelector<HTMLDivElement>('#scroll_view');
1434    this.currentSelectionTbl?.addEventListener('column-click', (ev: any) => {
1435    });
1436    window.subscribe(window.SmartEvent.UI.WakeupList, (data: Array<WakeupBean>) => this.showWakeupListTableData(data));
1437  }
1438
1439  showWakeupListTableData(data: Array<WakeupBean>) {
1440    this.wakeupListTbl!.style.display = 'flex';
1441    let cpus: number[] = [];
1442    let itids: number[] = [];
1443    let ts: number[] = [];
1444    let maxPriority = 0;
1445    let maxPriorityDuration = 0;
1446    let maxDuration = 0;
1447    data.forEach((it) => {
1448      cpus.push(it.cpu!);
1449      itids.push(it.itid!);
1450      ts.push(it.ts!);
1451    });
1452    queryWakeupListPriority(itids, ts, cpus).then((res) => {
1453      let resource = data.map((it) => {
1454        let wake = {
1455          process: `${it.process}(${it.pid})`,
1456          thread: `${it.thread}(${it.tid})`,
1457          cpu: it.cpu,
1458          dur: it.dur,
1459          priority: 0,
1460          isSelected: false,
1461        };
1462        let find = res.find((re) => re.cpu === it.cpu && re.itid === it.itid && re.ts === it.ts);
1463        if (find) {
1464          wake.priority = find.priority;
1465        }
1466        maxDuration = Math.max(maxDuration, it.dur!);
1467        maxPriority = Math.max(maxPriority, wake.priority);
1468        return wake;
1469      });
1470      if (this.selectWakeupBean) {
1471        // 点击第一层唤醒树时向数组头部添加当前点击信息
1472        if (data[0].schedulingLatency) {
1473          resource.unshift(this.selectWakeupBean);
1474        }
1475        maxDuration = Math.max(maxDuration, this.selectWakeupBean.dur);
1476        maxPriority = Math.max(maxPriority, this.selectWakeupBean.priority);
1477      }
1478      resource.forEach((it) => {
1479        if (it.priority === maxPriority) {
1480          maxPriorityDuration = Math.max(it.dur || 0, maxPriorityDuration);
1481        }
1482      });
1483      this.updateTableSettings(maxPriority, maxPriorityDuration, maxDuration);
1484      this.wakeupListTbl!.recycleDataSource = resource;
1485    });
1486  }
1487
1488  private updateTableSettings(maxPriority: number, maxPriorityDuration: number, maxDuration: number) {
1489    this.wakeupListTbl!.getItemTextColor = (data: any) => {
1490      if ((data.priority === maxPriority && data.dur === maxPriorityDuration) || data.dur === maxDuration) {
1491        return '#f44336';
1492      } else {
1493        return '#262626';
1494      }
1495    };
1496  }
1497
1498  addTableObserver(): void {
1499    let leftTable = this.shadowRoot?.querySelector('.table-left');
1500    this.tableObserver?.observe(leftTable!, {
1501      attributes: true,
1502      attributeFilter: ['style'],
1503      attributeOldValue: true,
1504    });
1505  }
1506
1507  initHtml(): string {
1508    return TabPaneCurrentSelectionHtml;
1509  }
1510}
1511
1512export class JankTreeNode {
1513  name: string = '';
1514  pid: number = -1;
1515  frame_type: string = '';
1516  type: number = 0;
1517
1518  constructor(name: string, pid: number, frame_type: string) {
1519    this.name = name;
1520    this.pid = pid;
1521    this.frame_type = frame_type;
1522  }
1523
1524  children: Array<JankTreeNode> = [];
1525}
1526
1527export class ThreadTreeNode {
1528  tid: number = 0;
1529  pid: number = -1;
1530  startTime: number = 1;
1531
1532  constructor(tid: number, pid: number, startTime: number) {
1533    this.tid = tid;
1534    this.pid = pid;
1535    this.startTime = startTime;
1536  }
1537}
1538