• 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 { SpSystemTrace } from '../SpSystemTrace';
17import { TraceRow } from '../trace/base/TraceRow';
18import { Utils } from '../trace/base/Utils';
19import { PerfThread } from '../../bean/PerfProfile';
20import { HiPerfCpuStruct } from '../../database/ui-worker/hiperf/ProcedureWorkerHiPerfCPU2';
21import {
22  HiPerfCallChartRender,
23  HiPerfCallChartStruct,
24} from '../../database/ui-worker/hiperf/ProcedureWorkerHiPerfCallChart';
25import { HiPerfThreadStruct } from '../../database/ui-worker/hiperf/ProcedureWorkerHiPerfThread2';
26import { HiPerfProcessStruct } from '../../database/ui-worker/hiperf/ProcedureWorkerHiPerfProcess2';
27import { info } from '../../../log/Log';
28import { HiPerfEventStruct } from '../../database/ui-worker/hiperf/ProcedureWorkerHiPerfEvent';
29import { perfDataQuery } from './PerfDataQuery';
30import { type HiPerfReportStruct } from '../../database/ui-worker/hiperf/ProcedureWorkerHiPerfReport';
31import { folderThreadHandler, getRowContext, rowThreadHandler, SpChartManager } from './SpChartManager';
32import { HiperfCpuRender2 } from '../../database/ui-worker/hiperf/ProcedureWorkerHiPerfCPU2';
33import { hiperfCpuDataSender } from '../../database/data-trafic/hiperf/HiperfCpuDataSender';
34import { hiperfProcessDataSender } from '../../database/data-trafic/hiperf/HiperfProcessDataSender';
35import { HiperfProcessRender2 } from '../../database/ui-worker/hiperf/ProcedureWorkerHiPerfProcess2';
36import { hiperfThreadDataSender } from '../../database/data-trafic/hiperf/HiperfThreadDataSender';
37import { HiperfThreadRender2 } from '../../database/ui-worker/hiperf/ProcedureWorkerHiPerfThread2';
38import {
39  hiperfCallChartDataCacheSender,
40  hiperfCallChartDataSender,
41  hiperfCallStackCacheSender,
42} from '../../database/data-trafic/hiperf/HiperfCallChartSender';
43import {
44  queryHiPerfCpuMergeData2,
45  queryPerfCmdline,
46  queryPerfEventType,
47  queryPerfThread,
48} from '../../database/sql/Perf.sql';
49import { renders } from '../../database/ui-worker/ProcedureWorker';
50
51export interface ResultData {
52  existA: boolean | null | undefined;
53  existF: boolean | null | undefined;
54  fValue: number;
55}
56const FOLD_HEIGHT = 20;
57export class SpHiPerf {
58  static selectCpuStruct: HiPerfCpuStruct | undefined;
59  static stringResult: ResultData | undefined;
60
61  private cpuData: Array<unknown> | undefined;
62  public maxCpuId: number = 0; //@ts-ignore
63  private rowFolder!: TraceRow<unknown>;
64  private perfThreads: Array<PerfThread> | undefined;
65  private trace: SpSystemTrace;
66  private group: unknown; //@ts-ignore
67  private rowList: TraceRow<unknown>[] | undefined;
68  private eventTypeList: Array<{ id: number; report: string }> = [];
69  private callChartType: number = 0;
70  private callChartId: number = 0;
71  private eventTypeId: number = -2;
72
73  constructor(trace: SpSystemTrace) {
74    this.trace = trace;
75  }
76
77  async init(): Promise<void> {
78    await this.initCmdLine();
79    this.eventTypeList = await queryPerfEventType();
80    this.rowList = [];
81    this.perfThreads = await queryPerfThread();
82    info('PerfThread Data size is: ', this.perfThreads!.length);
83    this.group = Utils.groupBy(this.perfThreads || [], 'pid');
84    this.cpuData = await queryHiPerfCpuMergeData2();
85    this.callChartType = 0;
86    this.callChartId = 0;
87    this.eventTypeId = -2; //@ts-ignore
88    this.maxCpuId = this.cpuData.length > 0 ? this.cpuData[0].cpu_id : -Infinity;
89    if (this.cpuData.length > 0) {
90      await this.initFolder();
91      await this.initCallChart();
92      await this.initCpuMerge();
93      await this.initCpu();
94      await this.initProcess();
95    }
96    info('HiPerf Data initialized');
97  }
98
99  getStringResult(s: string = ''): void {
100    let list = s.split(' ');
101    let sA = list.findIndex((item) => item === '-a');
102    let sF = list.findIndex((item) => item === '-f');
103    SpHiPerf.stringResult = {
104      existA: sA !== -1,
105      existF: sF !== -1,
106      fValue: Number((1000 / (sF !== -1 ? parseInt(list[sF + 1]) : 1000)).toFixed(2)),
107    };
108  }
109
110  async initCmdLine(): Promise<void> {
111    let perfCmdLines = await queryPerfCmdline();
112    if (perfCmdLines.length > 0) {
113      this.getStringResult(perfCmdLines[0].report_value);
114    } else {
115      SpHiPerf.stringResult = {
116        existA: true,
117        existF: false,
118        fValue: 1,
119      };
120    }
121  }
122
123  async initFolder(): Promise<void> {
124    let row = TraceRow.skeleton();
125    row.rowId = 'HiPerf';
126    row.index = 0;
127    row.rowType = TraceRow.ROW_TYPE_HIPERF;
128    row.rowParentId = '';
129    row.folder = true;
130    row.drawType = -2;
131    row.addRowSettingPop();
132    row.rowSetting = 'enable';
133    row.rowSettingPopoverDirection = 'bottomLeft';
134    row.style.height = '40px';
135    this.folderRowSettingConfig(row);
136    if (SpHiPerf.stringResult?.existA === true) {
137      row.name = 'HiPerf (All)';
138    } else {
139      //@ts-ignore
140      let names = Reflect.ownKeys(this.group)
141        .map((pid: unknown) => {
142          //@ts-ignore
143          let array = this.group[pid] as Array<PerfThread>;
144          let process = array.filter((th) => th.pid === th.tid)[0];
145          return process.processName;
146        })
147        .join(',');
148      row.name = `HiPerf (${names})`;
149    } //@ts-ignore
150    row.supplier = (): Promise<Array<unknown>> => new Promise<Array<unknown>>((resolve) => resolve([]));
151    row.onThreadHandler = folderThreadHandler(row, this.trace);
152    this.rowFolder = row;
153    this.trace.rowsEL?.appendChild(row);
154  }
155
156  //@ts-ignore
157  folderRowSettingConfig(row: TraceRow<unknown>): void {
158    row.rowSettingList = [
159      {
160        key: '-2',
161        title: 'Cpu Usage',
162        checked: true,
163      },
164      ...this.eventTypeList.map((et) => {
165        return {
166          key: `${et.id}`,
167          title: et.report,
168        };
169      }),
170    ];
171    row.onRowSettingChangeHandler = (value): void => {
172      let drawType = parseInt(value[0]);
173      if (this.eventTypeId !== drawType) {
174        this.eventTypeId = drawType;
175        row.drawType = drawType;
176        row.childrenList.forEach((child): void => {
177          if (child.drawType !== drawType) {
178            child.drawType = drawType;
179            child.needRefresh = true;
180            child.isComplete = false;
181            child.childrenList.forEach((sz) => {
182              sz.drawType = drawType;
183              sz.isComplete = false;
184              sz.needRefresh = true;
185            });
186          }
187        });
188        TraceRow.range!.refresh = true;
189        this.trace.refreshCanvas(false);
190      }
191    };
192  }
193
194  async initCallChart(): Promise<void> {
195    await hiperfCallStackCacheSender();
196    await hiperfCallChartDataCacheSender();
197    let perfCallCutRow = TraceRow.skeleton<HiPerfCallChartStruct>();
198    perfCallCutRow.rowId = 'HiPerf-callchart';
199    perfCallCutRow.index = 0;
200    perfCallCutRow.rowType = TraceRow.ROW_TYPE_PERF_CALLCHART;
201    perfCallCutRow.enableCollapseChart(FOLD_HEIGHT, this.trace);
202    perfCallCutRow.rowParentId = 'HiPerf';
203    perfCallCutRow.rowHidden = !this.rowFolder.expansion;
204    perfCallCutRow.folder = false;
205    perfCallCutRow.drawType = -2;
206    perfCallCutRow.name = 'CallChart [cpu0]';
207    perfCallCutRow.funcExpand = false;
208    perfCallCutRow.setAttribute('children', '');
209    perfCallCutRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
210    perfCallCutRow.selectChangeHandler = this.trace.selectChangeHandler;
211    this.rowFolder.addChildTraceRow(perfCallCutRow);
212    perfCallCutRow.focusHandler = (): void => {
213      this.callChartRowFocusHandler(perfCallCutRow);
214    };
215    // @ts-ignore
216    perfCallCutRow.supplierFrame = async (): Promise<unknown> => {
217      const res = await hiperfCallChartDataSender(perfCallCutRow, {
218        startTime: window.recordStartNS,
219        eventTypeId: this.eventTypeId,
220        type: this.callChartType,
221        id: this.callChartId,
222      });
223      // @ts-ignore
224      let maxHeight = res.maxDepth * FOLD_HEIGHT;
225      perfCallCutRow.funcMaxHeight = maxHeight;
226      if (perfCallCutRow.funcExpand) {
227        perfCallCutRow!.style.height = `${maxHeight}px`;
228        if (perfCallCutRow.collect) {
229          window.publish(window.SmartEvent.UI.RowHeightChange, {
230            expand: true,
231            value: perfCallCutRow.funcMaxHeight - FOLD_HEIGHT,
232          });
233        }
234      }
235      // @ts-ignore
236      return res.dataList;
237    };
238    perfCallCutRow.findHoverStruct = (): void => {
239      HiPerfCallChartStruct.hoverPerfCallCutStruct = perfCallCutRow.getHoverStruct();
240    };
241    await this.setCallTotalRow(perfCallCutRow, this.cpuData, this.perfThreads);
242  }
243
244  callChartRowFocusHandler(perfCallCutRow: TraceRow<HiPerfCallChartStruct>): void {
245    let hoverStruct = HiPerfCallChartStruct.hoverPerfCallCutStruct;
246    if (hoverStruct) {
247      let callName = hoverStruct.name;
248      callName = callName.replace(/</g, '&lt;').replace(/>/g, '&gt;');
249      this.trace?.displayTip(
250        perfCallCutRow!,
251        hoverStruct,
252        `<span style="font-weight: bold;color:'#000'">Name: </span>
253        <span>${callName}</span><br>
254        <span style='font-weight: bold;'>Lib: </span>
255        <span>${perfDataQuery.getLibName(hoverStruct!.fileId, hoverStruct!.symbolId)}</span><br>
256        <span style='font-weight: bold;'>Self Time: </span>
257        <span>${Utils.getProbablyTime(hoverStruct.selfDur || 0)}</span><br>
258        <span style='font-weight: bold;'>Duration: </span>
259        <span>${Utils.getProbablyTime(hoverStruct.totalTime)}</span><br>
260        <span style='font-weight: bold;'>Event Count: </span>
261        <span>${hoverStruct.eventCount || ''}</span><br>`
262      );
263    }
264  }
265
266  //@ts-ignore
267  async setCallTotalRow(row: TraceRow<unknown>, cpuData: unknown = Array, threadData: unknown = Array): Promise<void> {
268    //@ts-ignore
269    let pt: Map<string, unknown> = threadData.reduce((map: Map<string, unknown>, current: unknown) => {
270      //@ts-ignore
271      const key = `${current.processName || 'Process'}(${current.pid})`;
272      const thread = {
273        //@ts-ignore
274        key: `${current.tid}-t`, //@ts-ignore
275        title: `${current.threadName || 'Thread'}(${current.tid})`,
276      };
277      if (map.has(key)) {
278        //@ts-ignore
279        if (map.get(key).children) {
280          //@ts-ignore
281          map.get(key).children.push(thread);
282        } else {
283          //@ts-ignore
284          map.get(key).children = [thread];
285        }
286      } else {
287        map.set(key, {
288          //@ts-ignore
289          key: `${current.pid}-p`,
290          title: key,
291          children: [thread],
292          disable: true,
293        });
294      }
295      return map;
296    }, new Map<string, unknown>());
297    row.addTemplateTypes('hiperf-callchart');
298    row.addRowSettingPop();
299    row.rowSetting = 'enable'; //@ts-ignore
300    this.setCallChartRowSetting(row, cpuData, pt);
301    row.onThreadHandler = rowThreadHandler<HiPerfCallChartRender>(
302      'HiPerf-callchart',
303      'context',
304      {
305        type: 'HiPerf-callchart',
306      },
307      row,
308      this.trace
309    );
310  }
311
312  setCallChartRowSetting(
313    row: TraceRow<HiPerfCallChartStruct>,
314    cpuData: Array<unknown>,
315    pt: Map<string, unknown>
316  ): void {
317    //@ts-ignore
318    row.rowSettingList = [
319      ...cpuData.reverse().map(
320        (
321          it: unknown
322        ): {
323          key: string;
324          title: string;
325          checked?: boolean;
326        } => {
327          return {
328            //@ts-ignore
329            key: `${it.cpu_id}-c`,
330            //@ts-ignore
331            checked: it.cpu_id === 0,
332            //@ts-ignore
333            title: `cpu${it.cpu_id}`,
334          };
335        }
336      ),
337      ...Array.from(pt.values()),
338    ];
339    row.onRowSettingChangeHandler = (setting: unknown, nodes): void => {
340      //@ts-ignore
341      if (setting && setting.length > 0) {
342        //type 0:cpu,1:process,2:thread
343        //@ts-ignore
344        let key: string = setting[0];
345        let type = this.callChartType;
346        if (key.includes('p')) {
347          type = 1;
348        } else if (key.includes('t')) {
349          type = 2;
350        } else {
351          type = 0;
352        }
353        let id = Number(key.split('-')[0]);
354        if (this.callChartType === type && this.callChartId === id) {
355          return;
356        }
357        this.callChartType = type;
358        this.callChartId = id; // @ts-ignore
359        row.name = `CallChart [${nodes[0].title}]`;
360        row.isComplete = false;
361        row.needRefresh = true;
362        row.drawFrame();
363      }
364    };
365  }
366
367  async initCpuMerge(): Promise<void> {
368    let cpuMergeRow = TraceRow.skeleton<HiPerfCpuStruct>();
369    cpuMergeRow.rowId = 'HiPerf-cpu-merge';
370    cpuMergeRow.index = 0;
371    cpuMergeRow.rowType = TraceRow.ROW_TYPE_HIPERF_CPU;
372    cpuMergeRow.rowParentId = 'HiPerf';
373    cpuMergeRow.rowHidden = !this.rowFolder.expansion;
374    cpuMergeRow.folder = false;
375    cpuMergeRow.drawType = -2;
376    cpuMergeRow.name = 'HiPerf';
377    cpuMergeRow.style.height = '40px';
378    cpuMergeRow.setAttribute('children', '');
379    cpuMergeRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
380    cpuMergeRow.selectChangeHandler = this.trace.selectChangeHandler; //@ts-ignore
381    cpuMergeRow.supplierFrame = (): Promise<unknown> => {
382      return hiperfCpuDataSender(
383        -1,
384        cpuMergeRow.drawType,
385        this.maxCpuId + 1,
386        SpHiPerf.stringResult?.fValue || 1,
387        TraceRow.range?.scale || 50,
388        cpuMergeRow
389      );
390    };
391    cpuMergeRow.focusHandler = (): void => this.hoverTip(cpuMergeRow, HiPerfCpuStruct.hoverStruct);
392    cpuMergeRow.findHoverStruct = (): void => {
393      HiPerfCpuStruct.hoverStruct = cpuMergeRow.getHoverStruct(false, (TraceRow.range?.scale || 50) <= 30_000_000);
394    };
395    cpuMergeRow.onThreadHandler = this.rowThreadHandler<HiperfCpuRender2>(
396      'HiPerf-Cpu-2',
397      'context',
398      {
399        type: 'HiPerf-Cpu-Merge',
400        maxCpu: this.maxCpuId + 1,
401        intervalPerf: SpHiPerf.stringResult?.fValue || 1,
402      },
403      cpuMergeRow,
404      this.trace
405    );
406    this.rowFolder.addChildTraceRow(cpuMergeRow);
407    this.rowList?.push(cpuMergeRow);
408  }
409
410  //@ts-ignore
411  rowThreadHandler<T>(tag: string, contextField: string, arg: unknown, row: TraceRow<unknown>, trace: SpSystemTrace) {
412    return (useCache: boolean): void => {
413      let context: CanvasRenderingContext2D = getRowContext(row, trace);
414      row.canvasSave(context); //@ts-ignore
415      arg.useCache = useCache; //@ts-ignore
416      arg.scale = TraceRow.range?.scale || 50; //@ts-ignore
417      arg.range = TraceRow.range;
418      if (contextField) {
419        //@ts-ignore
420        arg[contextField] = context;
421      } //@ts-ignore
422      (renders[tag] as unknown).renderMainThread(arg, row);
423      row.canvasRestore(context, trace);
424    };
425  }
426
427  async initCpu(): Promise<void> {
428    for (let i = 0; i <= this.maxCpuId; i++) {
429      let perfCpuRow = TraceRow.skeleton<HiPerfCpuStruct>();
430      perfCpuRow.rowId = `HiPerf-cpu-${i}`;
431      perfCpuRow.index = i;
432      perfCpuRow.rowType = TraceRow.ROW_TYPE_HIPERF_CPU;
433      perfCpuRow.rowParentId = 'HiPerf';
434      perfCpuRow.rowHidden = !this.rowFolder.expansion;
435      perfCpuRow.folder = false;
436      perfCpuRow.drawType = -2;
437      perfCpuRow.name = `Cpu ${i}`;
438      perfCpuRow.setAttribute('children', '');
439      perfCpuRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
440      perfCpuRow.selectChangeHandler = this.trace.selectChangeHandler;
441      perfCpuRow.style.height = '40px'; //@ts-ignore
442      perfCpuRow.supplierFrame = (): Promise<unknown> => {
443        return hiperfCpuDataSender(
444          i,
445          perfCpuRow.drawType,
446          this.maxCpuId + 1,
447          SpHiPerf.stringResult?.fValue || 1,
448          TraceRow.range?.scale || 50,
449          perfCpuRow
450        );
451      };
452      perfCpuRow.focusHandler = (): void => this.hoverTip(perfCpuRow, HiPerfCpuStruct.hoverStruct);
453      perfCpuRow.findHoverStruct = (): void => {
454        HiPerfCpuStruct.hoverStruct = perfCpuRow.getHoverStruct(false, (TraceRow.range?.scale || 50) <= 30_000_000);
455      };
456      perfCpuRow.onThreadHandler = this.rowThreadHandler<HiperfCpuRender2>(
457        'HiPerf-Cpu-2',
458        'context',
459        {
460          type: `HiPerf-Cpu-${i}`,
461          maxCpu: this.maxCpuId + 1,
462          intervalPerf: SpHiPerf.stringResult?.fValue || 1,
463        },
464        perfCpuRow,
465        this.trace
466      );
467      this.rowFolder.addChildTraceRow(perfCpuRow);
468      this.rowList?.push(perfCpuRow);
469    }
470  }
471
472  async initProcess(): Promise<void> {
473    //@ts-ignore
474    Reflect.ownKeys(this.group).forEach((key, index): void => {
475      //@ts-ignore
476      let array = this.group[key] as Array<PerfThread>;
477      let process = array.filter((th): boolean => th.pid === th.tid)[0];
478      let row = TraceRow.skeleton<HiPerfProcessStruct>();
479      row.rowId = `${process.pid}-Perf-Process`;
480      row.index = index;
481      row.rowType = TraceRow.ROW_TYPE_HIPERF_PROCESS;
482      row.rowParentId = 'HiPerf';
483      row.rowHidden = !this.rowFolder.expansion;
484      row.folder = true;
485      row.drawType = -2;
486      if (SpChartManager.APP_STARTUP_PID_ARR.find((pid) => pid === process.pid) !== undefined) {
487        row.addTemplateTypes('AppStartup');
488      }
489      row.addTemplateTypes('HiPerf');
490      row.name = `${process.processName || 'Process'} [${process.pid}]`;
491      row.folderPaddingLeft = 6;
492      row.style.height = '40px';
493      row.favoriteChangeHandler = this.trace.favoriteChangeHandler;
494      row.selectChangeHandler = this.trace.selectChangeHandler; //@ts-ignore
495      row.supplierFrame = (): Promise<unknown> => {
496        return hiperfProcessDataSender(
497          process.pid,
498          row.drawType,
499          this.maxCpuId + 1,
500          SpHiPerf.stringResult?.fValue || 1,
501          TraceRow.range?.scale || 50,
502          row
503        );
504      };
505      row.focusHandler = (): void => this.hoverTip(row, HiPerfProcessStruct.hoverStruct);
506      row.findHoverStruct = (): void => {
507        HiPerfProcessStruct.hoverStruct = row.getHoverStruct(false, (TraceRow.range?.scale || 50) <= 30_000_000);
508      };
509      row.onThreadHandler = this.rowThreadHandler<HiperfProcessRender2>(
510        'HiPerf-Process-2',
511        'context',
512        {
513          type: `HiPerf-Process-${row.index}`,
514          intervalPerf: SpHiPerf.stringResult?.fValue || 1,
515        },
516        row,
517        this.trace
518      );
519      this.rowFolder.addChildTraceRow(row);
520      this.rowList?.push(row);
521      this.addHiPerfThreadRow(array, row);
522    });
523  }
524
525  addHiPerfThreadRow(array: PerfThread[], row: TraceRow<HiPerfProcessStruct>): void {
526    array.forEach((thObj, thIdx): void => {
527      let thread = TraceRow.skeleton<HiPerfThreadStruct>();
528      thread.rowId = `${thObj.tid}-Perf-Thread`;
529      thread.index = thIdx;
530      thread.rowType = TraceRow.ROW_TYPE_HIPERF_THREAD;
531      thread.rowParentId = row.rowId;
532      thread.rowHidden = !row.expansion;
533      thread.folder = false;
534      thread.drawType = -2;
535      thread.name = `${thObj.threadName || 'Thread'} [${thObj.tid}]`;
536      thread.setAttribute('children', '');
537      thread.folderPaddingLeft = 0;
538      thread.style.height = '40px';
539      thread.favoriteChangeHandler = this.trace.favoriteChangeHandler;
540      thread.selectChangeHandler = this.trace.selectChangeHandler; //@ts-ignore
541      thread.supplierFrame = (): Promise<unknown> => {
542        return hiperfThreadDataSender(
543          thObj.tid,
544          thread.drawType,
545          this.maxCpuId + 1,
546          SpHiPerf.stringResult?.fValue || 1,
547          TraceRow.range?.scale || 50,
548          thread
549        );
550      };
551      thread.focusHandler = (): void => this.hoverTip(thread, HiPerfThreadStruct.hoverStruct);
552      thread.findHoverStruct = (): void => {
553        HiPerfThreadStruct.hoverStruct = thread.getHoverStruct(false, (TraceRow.range?.scale || 50) <= 30_000_000);
554      };
555      thread.onThreadHandler = this.rowThreadHandler<HiperfThreadRender2>(
556        'HiPerf-Thread-2',
557        'context',
558        {
559          type: `HiPerf-Thread-${row.index}-${thread.index}`,
560          intervalPerf: SpHiPerf.stringResult?.fValue || 1,
561        },
562        thread,
563        this.trace
564      );
565      row.addChildTraceRow(thread);
566      this.rowList?.push(thread);
567    });
568  }
569
570  //@ts-ignore
571  resetChartData(row: TraceRow<unknown>): void {
572    row.dataList = [];
573    row.dataList2 = [];
574    row.dataListCache = [];
575    row.isComplete = false;
576  }
577
578  resetAllChartData(): void {
579    const callChartRow = this.rowList?.find(row => row.rowId === 'HiPerf-callchart');
580    if (callChartRow) {
581      this.resetChartData(callChartRow);
582    }
583  }
584
585  hoverTip(
586    //@ts-ignore
587    row: TraceRow<unknown>,
588    struct:
589      | HiPerfThreadStruct
590      | HiPerfProcessStruct
591      | HiPerfEventStruct
592      | HiPerfReportStruct
593      | HiPerfCpuStruct
594      | undefined
595  ): void {
596    let tip = '';
597    let groupBy10MS = (TraceRow.range?.scale || 50) > 30_000_000;
598    if (struct) {
599      if (groupBy10MS) {
600        if (row.drawType === -2) {
601          let num: number | string = 0;
602          if (struct instanceof HiPerfEventStruct) {
603            num = Math.trunc(((struct.sum || 0) / (struct.max || 0)) * 100);
604          } else {
605            let interval = SpHiPerf.stringResult?.fValue || 1;
606            num = ((struct.sampleCount! / (10 / interval)) * 100).toFixed(2);
607          }
608          tip = `<span>${num}% (10.00ms)</span>`;
609        } else {
610          tip = `<span>${struct.event_count || struct.eventCount} (10.00ms)</span>`;
611        }
612      } else {
613        let perfCall = perfDataQuery.callChainMap.get(struct.callchain_id || 0);
614        if (perfCall) {
615          let perfName;
616          typeof perfCall.name === 'number'
617            ? (perfName = SpSystemTrace.DATA_DICT.get(parseInt(perfCall.name)))
618            : (perfName = perfCall.name);
619          tip = `<span>${perfCall ? perfName : ''} (${perfCall ? perfCall.depth : '0'} other frames)</span>`;
620        }
621      }
622    }
623    this.trace?.displayTip(row, struct, tip);
624  }
625}
626