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