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