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