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