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.js'; 17import { TraceRow } from '../trace/base/TraceRow.js'; 18import { procedurePool } from '../../database/Procedure.js'; 19import { 20 queryHiPerfCpuData, 21 queryHiPerfCpuMergeData, 22 queryHiPerfCpuMergeData2, 23 queryHiPerfEventData, 24 queryHiPerfEventList, 25 queryHiPerfEventListData, 26 queryHiPerfProcessData, 27 queryHiPerfThreadData, 28 queryPerfCmdline, 29 queryPerfThread, 30} from '../../database/SqlLite.js'; 31import { Utils } from '../trace/base/Utils.js'; 32import { PerfThread } from '../../bean/PerfProfile.js'; 33import { HiperfCpuRender, HiPerfCpuStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfCPU.js'; 34import { HiperfThreadRender, HiPerfThreadStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfThread.js'; 35import { HiperfProcessRender, HiPerfProcessStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfProcess.js'; 36import { info } from '../../../log/Log.js'; 37import { HiperfEventRender, HiPerfEventStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfEvent.js'; 38import { perfDataQuery } from './PerfDataQuery.js'; 39import { renders } from '../../database/ui-worker/ProcedureWorker.js'; 40import { EmptyRender } from '../../database/ui-worker/ProcedureWorkerCPU.js'; 41import { HiperfReportRender, HiPerfReportStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfReport.js'; 42 43 44export interface ResultData { 45 existA: boolean | null | undefined; 46 existF: boolean | null | undefined; 47 fValue: number; 48} 49 50export class SpHiPerf { 51 static selectCpuStruct: HiPerfCpuStruct | undefined; 52 static selectProcessStruct: HiPerfProcessStruct | undefined; 53 static selectThreadStruct: HiPerfThreadStruct | undefined; 54 static stringResult: ResultData | undefined; 55 56 private cpuData: Array<any> | undefined; 57 public maxCpuId: number = 0; 58 private rowFolder!: TraceRow<any>; 59 private perfThreads: Array<PerfThread> | undefined; 60 private trace: SpSystemTrace; 61 private group: any; 62 private rowList: TraceRow<any>[] | undefined; 63 private eventTypeList: Array<{ id: number; report_value: string }> = []; 64 65 constructor(trace: SpSystemTrace) { 66 this.trace = trace; 67 } 68 69 async init() { 70 await this.initCmdLine(); 71 this.rowList = []; 72 this.perfThreads = await queryPerfThread(); 73 this.eventTypeList = await queryHiPerfEventList(); 74 info('PerfThread Data size is: ', this.perfThreads!.length); 75 this.group = Utils.groupBy(this.perfThreads || [], 'pid'); 76 this.cpuData = await queryHiPerfCpuMergeData2(); 77 this.maxCpuId = this.cpuData.length > 0 ? this.cpuData[0].cpu_id : -Infinity; 78 if (this.cpuData.length > 0) { 79 await this.initFolder(); 80 await this.initCpuMerge(); 81 await this.initCpu(); 82 await this.initProcess(); 83 } 84 info('HiPerf Data initialized'); 85 } 86 87 getStringResult(s: string = '') { 88 let list = s.split(' ').filter((e) => e); 89 let sA = list.findIndex((item) => item == '-a'); 90 let sF = list.findIndex((item) => item == '-f'); 91 SpHiPerf.stringResult = { 92 existA: sA !== -1, 93 existF: sF !== -1, 94 fValue: Number((1000 / (sF !== -1 ? parseInt(list[sF + 1]) : 1000)).toFixed(2)), 95 }; 96 } 97 98 async initCmdLine() { 99 let perfCmdLines = await queryPerfCmdline(); 100 if (perfCmdLines.length > 0) { 101 this.getStringResult(perfCmdLines[0].report_value); 102 } else { 103 SpHiPerf.stringResult = { 104 existA: true, 105 existF: false, 106 fValue: 1, 107 }; 108 } 109 } 110 111 async initFolder() { 112 let row = TraceRow.skeleton(); 113 row.setAttribute('disabled-check', ''); 114 row.rowId = `HiPerf`; 115 row.index = 0; 116 row.rowType = TraceRow.ROW_TYPE_HIPERF; 117 row.rowParentId = ''; 118 row.folder = true; 119 row.style.height = '40px'; 120 if (SpHiPerf.stringResult?.existA === true) { 121 row.name = `HiPerf (All)`; 122 } else { 123 let names = Reflect.ownKeys(this.group) 124 .map((pid: any) => { 125 let array = this.group[pid] as Array<PerfThread>; 126 let process = array.filter((th) => th.pid === th.tid)[0]; 127 return process.processName; 128 }) 129 .join(','); 130 row.name = `HiPerf (${names})`; 131 } 132 row.supplier = () => new Promise<Array<any>>((resolve) => resolve([])); 133 row.onThreadHandler = (useCache) => { 134 row.canvasSave(this.trace.canvasPanelCtx!); 135 if (row.expansion) { 136 this.trace.canvasPanelCtx?.clearRect(0, 0, row.frame.width, row.frame.height); 137 } else { 138 (renders['empty'] as EmptyRender).renderMainThread( 139 { 140 context: this.trace.canvasPanelCtx, 141 useCache: useCache, 142 type: ``, 143 }, 144 row 145 ); 146 } 147 row.canvasRestore(this.trace.canvasPanelCtx!); 148 }; 149 this.rowFolder = row; 150 this.trace.rowsEL?.appendChild(row); 151 } 152 153 async initCpuMerge() { 154 let cpuMergeRow = TraceRow.skeleton<HiPerfCpuStruct>(); 155 cpuMergeRow.rowId = `HiPerf-cpu-merge`; 156 cpuMergeRow.index = 0; 157 cpuMergeRow.rowType = TraceRow.ROW_TYPE_HIPERF_CPU; 158 cpuMergeRow.rowParentId = 'HiPerf'; 159 cpuMergeRow.rowHidden = !this.rowFolder.expansion; 160 cpuMergeRow.folder = false; 161 cpuMergeRow.name = `HiPerf`; 162 cpuMergeRow.style.height = '40px'; 163 cpuMergeRow.setAttribute('children', ''); 164 cpuMergeRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 165 cpuMergeRow.selectChangeHandler = this.trace.selectChangeHandler; 166 cpuMergeRow.supplier = () => queryHiPerfCpuMergeData(); 167 cpuMergeRow.focusHandler = () => this.hoverTip(cpuMergeRow, HiPerfCpuStruct.hoverStruct); 168 cpuMergeRow.onThreadHandler = (useCache) => { 169 let context = cpuMergeRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 170 cpuMergeRow.canvasSave(context); 171 (renders['HiPerf-Cpu'] as HiperfCpuRender).renderMainThread( 172 { 173 context: context, 174 useCache: useCache, 175 scale: TraceRow.range?.scale || 50, 176 type: `HiPerf-Cpu-Merge`, 177 maxCpu: this.maxCpuId + 1, 178 intervalPerf: SpHiPerf.stringResult?.fValue || 1, 179 range: TraceRow.range, 180 }, 181 cpuMergeRow 182 ); 183 cpuMergeRow.canvasRestore(context); 184 }; 185 this.rowFolder.addChildTraceRow(cpuMergeRow); 186 this.rowList?.push(cpuMergeRow); 187 } 188 189 async initCpu() { 190 for (let i = 0; i <= this.maxCpuId; i++) { 191 let perfCpuRow = TraceRow.skeleton<HiPerfCpuStruct>(); 192 perfCpuRow.rowId = `HiPerf-cpu-${i}`; 193 perfCpuRow.index = i; 194 perfCpuRow.rowType = TraceRow.ROW_TYPE_HIPERF_CPU; 195 perfCpuRow.rowParentId = 'HiPerf'; 196 perfCpuRow.rowHidden = !this.rowFolder.expansion; 197 perfCpuRow.folder = false; 198 perfCpuRow.name = `Cpu ${i}`; 199 perfCpuRow.setAttribute('children', ''); 200 perfCpuRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 201 perfCpuRow.selectChangeHandler = this.trace.selectChangeHandler; 202 perfCpuRow.style.height = '40px'; 203 perfCpuRow.supplier = () => queryHiPerfCpuData(i); 204 perfCpuRow.focusHandler = () => this.hoverTip(perfCpuRow, HiPerfCpuStruct.hoverStruct); 205 perfCpuRow.onThreadHandler = (useCache) => { 206 let context = perfCpuRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 207 perfCpuRow.canvasSave(context); 208 (renders['HiPerf-Cpu'] as HiperfCpuRender).renderMainThread( 209 { 210 context: context, 211 useCache: useCache, 212 scale: TraceRow.range?.scale || 50, 213 type: `HiPerf-Cpu-${i}`, 214 maxCpu: this.maxCpuId + 1, 215 intervalPerf: SpHiPerf.stringResult?.fValue || 1, 216 range: TraceRow.range, 217 }, 218 perfCpuRow 219 ); 220 perfCpuRow.canvasRestore(context); 221 }; 222 this.rowFolder.addChildTraceRow(perfCpuRow); 223 this.rowList?.push(perfCpuRow); 224 } 225 } 226 227 async initReport() { 228 this.eventTypeList.forEach((it, index) => { 229 let fold = TraceRow.skeleton<HiPerfReportStruct>(); 230 fold.rowId = `Perf-Report-${it.id}-${it.report_value}`; 231 fold.index = index; 232 fold.rowType = TraceRow.ROW_TYPE_HIPERF_REPORT; 233 fold.rowParentId = 'HiPerf'; 234 fold.rowHidden = !this.rowFolder.expansion; 235 fold.folder = true; 236 fold.name = `Event :${it.report_value}`; 237 fold.folderPaddingLeft = 6; 238 fold.favoriteChangeHandler = this.trace.favoriteChangeHandler; 239 fold.selectChangeHandler = this.trace.selectChangeHandler; 240 fold.supplier = () => queryHiPerfEventListData(it.id); 241 fold.onThreadHandler = (useCache) => { 242 let context = fold.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 243 fold.canvasSave(context); 244 (renders['HiPerf-Report-Fold'] as HiperfReportRender).renderMainThread( 245 { 246 context: context, 247 useCache: useCache, 248 scale: TraceRow.range?.scale || 50, 249 type: `HiPerf-Report-Fold-${it.report_value}-${it.id}`, 250 maxCpu: this.maxCpuId + 1, 251 intervalPerf: SpHiPerf.stringResult?.fValue || 1, 252 range: TraceRow.range, 253 }, 254 fold 255 ); 256 fold.canvasRestore(context); 257 }; 258 this.trace.rowsEL?.appendChild(fold); 259 this.rowList?.push(fold); 260 for (let i = 0; i <= this.maxCpuId; i++) { 261 let row = TraceRow.skeleton<HiPerfEventStruct>(); 262 row.rowId = `HiPerf-Report-Event-${it.report_value}-${i}`; 263 row.index = i; 264 row.rowType = TraceRow.ROW_TYPE_HIPERF_EVENT; 265 row.rowParentId = fold.rowId; 266 row.rowHidden = !fold.expansion; 267 row.folder = false; 268 row.name = `Cpu ${i}`; 269 row.style.height = '40px'; 270 row.setAttribute('children', ''); 271 row.favoriteChangeHandler = this.trace.favoriteChangeHandler; 272 row.selectChangeHandler = this.trace.selectChangeHandler; 273 row.supplier = () => queryHiPerfEventData(it.id, row.index); 274 row.focusHandler = () => this.hoverTip(row, HiPerfEventStruct.hoverStruct); 275 row.onThreadHandler = (useCache) => { 276 let context = fold.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 277 fold.canvasSave(context); 278 (renders['HiPerf-Report-Event'] as HiperfEventRender).renderMainThread( 279 { 280 context: context, 281 useCache: useCache, 282 scale: TraceRow.range?.scale || 50, 283 type: `HiPerf-Report-Event-${it.report_value}-${i}`, 284 maxCpu: this.maxCpuId + 1, 285 intervalPerf: SpHiPerf.stringResult?.fValue || 1, 286 range: TraceRow.range, 287 }, 288 row 289 ); 290 fold.canvasRestore(context); 291 }; 292 this.trace.rowsEL?.appendChild(row); 293 this.rowList?.push(row); 294 } 295 }); 296 } 297 298 async initProcess() { 299 Reflect.ownKeys(this.group).forEach((key, index) => { 300 let array = this.group[key] as Array<PerfThread>; 301 let process = array.filter((th) => th.pid === th.tid)[0]; 302 let row = TraceRow.skeleton<HiPerfProcessStruct>(); 303 row.rowId = `${process.pid}-Perf-Process`; 304 row.index = index; 305 row.rowType = TraceRow.ROW_TYPE_HIPERF_PROCESS; 306 row.rowParentId = 'HiPerf'; 307 row.rowHidden = !this.rowFolder.expansion; 308 row.folder = true; 309 row.name = `${process.processName || 'Process'} [${process.pid}]`; 310 row.folderPaddingLeft = 6; 311 row.style.height = '40px'; 312 row.favoriteChangeHandler = this.trace.favoriteChangeHandler; 313 row.selectChangeHandler = this.trace.selectChangeHandler; 314 row.supplier = () => queryHiPerfProcessData(process.pid); 315 row.focusHandler = () => this.hoverTip(row, HiPerfProcessStruct.hoverStruct); 316 row.onThreadHandler = (useCache) => { 317 let context = this.trace.canvasPanelCtx!; 318 row.canvasSave(context); 319 if (row.expansion) { 320 this.trace.canvasPanelCtx?.clearRect(0, 0, row.frame.width, row.frame.height); 321 } else { 322 (renders['HiPerf-Process'] as HiperfProcessRender).renderMainThread( 323 { 324 context: context, 325 useCache: useCache, 326 scale: TraceRow.range?.scale || 50, 327 type: `HiPerf-Process-${row.index}`, 328 intervalPerf: SpHiPerf.stringResult?.fValue || 1, 329 range: TraceRow.range, 330 }, 331 row 332 ); 333 } 334 row.canvasRestore(context); 335 }; 336 this.rowFolder.addChildTraceRow(row); 337 this.rowList?.push(row); 338 array.forEach((thObj, thIdx) => { 339 let thread = TraceRow.skeleton<HiPerfThreadStruct>(); 340 thread.rowId = `${thObj.tid}-Perf-Thread`; 341 thread.index = thIdx; 342 thread.rowType = TraceRow.ROW_TYPE_HIPERF_THREAD; 343 thread.rowParentId = row.rowId; 344 thread.rowHidden = !row.expansion; 345 thread.folder = false; 346 thread.name = `${thObj.threadName || 'Thread'} [${thObj.tid}]`; 347 thread.setAttribute('children', ''); 348 thread.folderPaddingLeft = 0; 349 thread.style.height = '40px'; 350 thread.favoriteChangeHandler = this.trace.favoriteChangeHandler; 351 thread.selectChangeHandler = this.trace.selectChangeHandler; 352 thread.supplier = () => queryHiPerfThreadData(thObj.tid); 353 thread.focusHandler = () => this.hoverTip(thread, HiPerfThreadStruct.hoverStruct); 354 thread.onThreadHandler = (useCache) => { 355 let context = thread.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 356 thread.canvasSave(context); 357 (renders['HiPerf-Thread'] as HiperfThreadRender).renderMainThread( 358 { 359 context: context, 360 useCache: useCache, 361 scale: TraceRow.range?.scale || 50, 362 type: `HiPerf-Thread-${row.index}-${thread.index}`, 363 intervalPerf: SpHiPerf.stringResult?.fValue || 1, 364 range: TraceRow.range, 365 }, 366 thread 367 ); 368 thread.canvasRestore(context); 369 }; 370 row.addChildTraceRow(thread); 371 this.rowList?.push(thread); 372 }); 373 }); 374 } 375 376 updateChartData() { 377 this.rowList?.forEach((it) => { 378 it.dataList = []; 379 it.dataList2 = []; 380 it.dataListCache = []; 381 it.isComplete = false; 382 }); 383 } 384 385 hoverTip( 386 row: TraceRow<any>, 387 struct: 388 | HiPerfThreadStruct 389 | HiPerfProcessStruct 390 | HiPerfEventStruct 391 | HiPerfReportStruct 392 | HiPerfCpuStruct 393 | undefined 394 ) { 395 let tip = ''; 396 if (struct) { 397 let num = 0; 398 if (struct instanceof HiPerfEventStruct) { 399 num = Math.trunc(((struct.sum || 0) / (struct.max || 0)) * 100); 400 } else { 401 num = Math.trunc(((struct.height || 0) / 40) * 100); 402 } 403 if (num > 0) { 404 tip = `<span>${num * (this.maxCpuId + 1)}% (10.00ms)</span>`; 405 } else { 406 let perfCall = perfDataQuery.callChainMap.get(struct.callchain_id || 0); 407 tip = `<span>${perfCall ? perfCall.name : ''} (${perfCall ? perfCall.depth : '0'} other frames)</span>`; 408 } 409 } 410 this.trace?.displayTip(row, struct, tip); 411 } 412} 413