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