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, '<').replace(/>/g, '>'); 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