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 { 17 LogicHandler, 18 ChartStruct, 19 convertJSON, 20 DataCache, 21 HiPerfSymbol, 22 PerfCall, 23} from './ProcedureLogicWorkerCommon'; 24import { PerfBottomUpStruct } from '../../bean/PerfBottomUpStruct'; 25import { SelectionParam } from '../../bean/BoxSelection'; 26import { dealAsyncData } from './ProcedureLogicWorkerCommon'; 27 28const systemRuleName: string = '/system/'; 29const numRuleName: string = '/max/min/'; 30const maxDepth: number = 256; 31 32type PerfThreadMap = { 33 [pid: string]: PerfThread; 34}; 35type PerfCallChainMap = { 36 [id: number]: PerfCallChain[]; 37}; 38type FileMap = { 39 [id: number]: PerfFile[]; 40}; 41 42type MergeMap = { 43 [id: string]: PerfCallChainMerageData; 44}; 45 46type spiltMap = { 47 [id: string]: PerfCallChainMerageData[]; 48}; 49 50export class ProcedureLogicWorkerPerf extends LogicHandler { 51 filesData: FileMap = {}; 52 samplesData: Array<PerfCountSample> = []; 53 threadData: PerfThreadMap = {}; 54 callChainData: PerfCallChainMap = {}; 55 splitMapData: spiltMap = {}; 56 currentTreeMapData: MergeMap = {}; 57 currentTreeList: PerfCallChainMerageData[] = []; 58 searchValue: string = ''; 59 dataSource: PerfCallChainMerageData[] = []; 60 allProcess: PerfCallChainMerageData[] = []; 61 currentEventId: string = ''; 62 isAnalysis: boolean = false; 63 isPerfBottomUp: boolean = false; 64 isHideThread: boolean = false; 65 isHideThreadState: boolean = false; 66 isOnlyKernel: boolean = false; 67 private lib: object | undefined; 68 private symbol: object | undefined; 69 private dataCache = DataCache.getInstance(); 70 private isTopDown: boolean = true; 71 72 handle(data: unknown): void { 73 //@ts-ignore 74 this.currentEventId = data.id; 75 //@ts-ignore 76 if (data && data.type) { 77 //@ts-ignore 78 switch (data.type) { 79 case 'perf-init': 80 //@ts-ignore 81 this.dataCache.perfCountToMs = data.params.fValue; 82 this.initPerfFiles(); 83 break; 84 case 'perf-queryPerfFiles': 85 //@ts-ignore 86 this.perfQueryPerfFiles(data.params.list); 87 break; 88 case 'perf-queryPerfThread': 89 //@ts-ignore 90 this.perfQueryPerfThread(data.params.list); 91 break; 92 case 'perf-queryPerfCalls': 93 //@ts-ignore 94 this.perfQueryPerfCalls(data.params.list); 95 break; 96 case 'perf-queryPerfCallchains': 97 this.perfQueryPerfCallchains(data); 98 break; 99 case 'perf-queryCallchainsGroupSample': 100 this.perfQueryCallchainsGroupSample(data); 101 break; 102 case 'perf-action': 103 this.perfAction(data); 104 break; 105 case 'perf-reset': 106 this.perfReset(); 107 break; 108 case 'perf-async': 109 this.perfAsync(data); 110 break; 111 } 112 } 113 } 114 private perfQueryPerfFiles(list: Array<PerfFile>): void { 115 let files = convertJSON(list) || []; 116 //@ts-ignore 117 files.forEach((file: PerfFile) => { 118 this.filesData[file.fileId] = this.filesData[file.fileId] || []; 119 PerfFile.setFileName(file); 120 this.filesData[file.fileId].push(file); 121 }); 122 this.initPerfThreads(); 123 } 124 private perfQueryPerfThread(list: Array<PerfThread>): void { 125 let threads = convertJSON(list) || []; 126 //@ts-ignore 127 threads.forEach((thread: PerfThread): void => { 128 this.threadData[thread.tid] = thread; 129 }); 130 this.initPerfCalls(); 131 } 132 private perfQueryPerfCalls(list: Array<PerfCall>): void { 133 let perfCalls = convertJSON(list) || []; 134 if (perfCalls.length !== 0) { 135 //@ts-ignore 136 perfCalls.forEach((perfCall: PerfCall): void => { 137 this.dataCache.perfCallChainMap.set(perfCall.sampleId, perfCall); 138 }); 139 } 140 this.initPerfCallchains(); 141 } 142 private perfQueryPerfCallchains(data: unknown): void { 143 //@ts-ignore 144 let arr = convertJSON(data.params.list) || []; 145 this.initPerfCallChainTopDown(arr as PerfCallChain[]); 146 147 self.postMessage({ 148 // @ts-ignore 149 id: data.id, 150 // @ts-ignore 151 action: data.action, 152 results: this.dataCache.perfCallChainMap, 153 }); 154 } 155 private perfQueryCallchainsGroupSample(data: unknown): void { 156 //@ts-ignore 157 this.samplesData = convertJSON(data.params.list) || []; 158 let result; 159 if (this.isAnalysis) { 160 result = this.resolvingAction([ 161 { 162 funcName: 'combineAnalysisCallChain', 163 funcArgs: [true], 164 }, 165 ]); 166 } else if (this.isPerfBottomUp) { 167 result = this.resolvingAction([ 168 { 169 funcName: 'getBottomUp', 170 funcArgs: [true], 171 }, 172 ]); 173 } else { 174 if (this.lib) { 175 let libData = this.combineCallChainForAnalysis(this.lib); 176 this.freshPerfCallchains(libData, this.isTopDown); 177 result = this.allProcess; 178 this.lib = undefined; 179 } else if (this.symbol) { 180 let funData = this.combineCallChainForAnalysis(this.symbol); 181 this.freshPerfCallchains(funData, this.isTopDown); 182 result = this.allProcess; 183 this.symbol = undefined; 184 } else { 185 result = this.resolvingAction([ 186 { 187 funcName: 'getCallChainsBySampleIds', 188 funcArgs: [this.isTopDown], 189 }, 190 ]); 191 } 192 } 193 self.postMessage({ 194 //@ts-ignore 195 id: data.id, 196 //@ts-ignore 197 action: data.action, 198 results: result, 199 }); 200 if (this.isAnalysis) { 201 this.isAnalysis = false; 202 } 203 } 204 private perfAction(data: unknown): void { 205 //@ts-ignore 206 const params = data.params; 207 if (params) { 208 let filter = params.filter((item: { funcName: string }): boolean => item.funcName === 'getCurrentDataFromDb'); 209 let libFilter = params.filter((item: { funcName: string }): boolean => item.funcName === 'showLibLevelData'); 210 let funFilter = params.filter((item: { funcName: string }): boolean => item.funcName === 'showFunLevelData'); 211 if (libFilter.length !== 0) { 212 this.setLib(libFilter); 213 } 214 if (funFilter.length !== 0) { 215 this.setSymbol(funFilter); 216 } 217 if (filter.length === 0) { 218 let result = this.calReturnData(params); 219 self.postMessage({ 220 //@ts-ignore 221 id: data.id, 222 //@ts-ignore 223 action: data.action, 224 results: result, 225 }); 226 } else { 227 this.resolvingAction(params); 228 } 229 } 230 } 231 private perfReset(): void { 232 this.isHideThread = false; 233 this.isHideThreadState = false; 234 this.isTopDown = true; 235 this.isOnlyKernel = false; 236 } 237 238 private perfAsync(data: unknown): void { 239 //@ts-ignore 240 if (data.params.list) { 241 // 若前端存储过调用栈信息与被调用栈信息,可考虑从此处一起返回给主线程 242 //@ts-ignore 243 let arr = convertJSON(data.params.list) || []; 244 //@ts-ignore 245 let result = dealAsyncData(arr, this.callChainData, this.dataCache.nmHeapFrameMap, this.dataCache.dataDict, this.searchValue); 246 this.searchValue = ''; 247 self.postMessage({ 248 //@ts-ignore 249 id: data.id, 250 //@ts-ignore 251 action: data.action, 252 results: result, 253 }); 254 arr = []; 255 result = []; 256 } else { 257 //@ts-ignore 258 this.searchValue = data.params.searchValue; 259 //@ts-ignore 260 this.queryPerfAsync(data.params); 261 } 262 } 263 264 private setLib(libFilter: unknown): void { 265 this.lib = { 266 //@ts-ignore 267 libId: libFilter[0].funcArgs[0], 268 //@ts-ignore 269 libName: libFilter[0].funcArgs[1], 270 }; 271 } 272 private setSymbol(funFilter: unknown): void { 273 this.symbol = { 274 //@ts-ignore 275 symbolId: funFilter[0].funcArgs[0], 276 //@ts-ignore 277 symbolName: funFilter[0].funcArgs[1], 278 }; 279 } 280 private calReturnData(params: unknown): Array<unknown> { 281 let result: unknown[]; 282 //@ts-ignore 283 let callChainsFilter = params.filter( 284 (item: { funcName: string }): boolean => item.funcName === 'getCallChainsBySampleIds' 285 ); 286 callChainsFilter.length > 0 ? (this.isTopDown = callChainsFilter[0].funcArgs[0]) : (this.isTopDown = true); 287 //@ts-ignore 288 let isHideSystemSoFilter = params.filter( 289 (item: { funcName: string }): boolean => item.funcName === 'hideSystemLibrary' 290 ); 291 //@ts-ignore 292 let hideThreadFilter = params.filter((item: { funcName: string }): boolean => item.funcName === 'hideThread'); 293 //@ts-ignore 294 let hideThreadStateFilter = params.filter( 295 (item: { funcName: string }): boolean => item.funcName === 'hideThreadState' 296 ); 297 //@ts-ignore 298 let onlyKernelFilter = [true]; 299 if (this.lib) { 300 if ( 301 callChainsFilter.length > 0 || 302 isHideSystemSoFilter.length > 0 || 303 hideThreadFilter.length > 0 || 304 hideThreadStateFilter.length > 0 || 305 onlyKernelFilter.length > 0 306 ) { 307 this.samplesData = this.combineCallChainForAnalysis(this.lib); 308 //@ts-ignore 309 result = this.resolvingAction(params); 310 } else { 311 let libData = this.combineCallChainForAnalysis(this.lib); 312 this.freshPerfCallchains(libData, this.isTopDown); 313 result = this.allProcess; 314 this.lib = undefined; 315 } 316 } else if (this.symbol) { 317 if ( 318 callChainsFilter.length > 0 || 319 isHideSystemSoFilter.length > 0 || 320 hideThreadFilter.length > 0 || 321 hideThreadStateFilter.length > 0 || 322 onlyKernelFilter.length > 0 323 ) { 324 this.samplesData = this.combineCallChainForAnalysis(this.symbol); 325 //@ts-ignore 326 result = this.resolvingAction(params); 327 } else { 328 let funData = this.combineCallChainForAnalysis(this.symbol); 329 this.freshPerfCallchains(funData, this.isTopDown); 330 result = this.allProcess; 331 this.symbol = undefined; 332 } 333 } else { 334 //@ts-ignore 335 result = this.resolvingAction(params); 336 } 337 return result; 338 } 339 initPerfFiles(): void { 340 this.clearAll(); 341 this.queryData( 342 this.currentEventId, 343 'perf-queryPerfFiles', 344 `select file_id as fileId, symbol, path 345 from perf_files`, 346 {} 347 ); 348 } 349 350 initPerfThreads(): void { 351 this.queryData( 352 this.currentEventId, 353 'perf-queryPerfThread', 354 `select a.thread_id as tid, a.thread_name as threadName, a.process_id as pid, b.thread_name as processName 355 from perf_thread a 356 left join (select * from perf_thread where thread_id = process_id) b on a.process_id = b.thread_id`, 357 {} 358 ); 359 } 360 361 initPerfCalls(): void { 362 this.queryData( 363 this.currentEventId, 364 'perf-queryPerfCalls', 365 `select count(callchain_id) as depth, callchain_id as sampleId, name 366 from perf_callchain 367 where callchain_id != -1 368 group by callchain_id`, 369 {} 370 ); 371 } 372 373 initPerfCallchains(): void { 374 this.queryData( 375 this.currentEventId, 376 'perf-queryPerfCallchains', 377 `select c.name, 378 c.callchain_id as sampleId, 379 c.vaddr_in_file as vaddrInFile, 380 c.file_id as fileId, 381 c.depth, 382 c.symbol_id as symbolId 383 from perf_callchain c 384 where callchain_id != -1;`, 385 {} 386 ); 387 } 388 389 queryPerfAsync(args: unknown): void { 390 let str: string = ``; 391 //@ts-ignore 392 if (args.cpu.length > 0) { 393 //@ts-ignore 394 str += `or cpu_id in (${args.cpu.join(',')})`; 395 } 396 //@ts-ignore 397 if (args.tid.length > 0) { 398 //@ts-ignore 399 str += `or tid in (${args.tid.join(',')})`; 400 } 401 //@ts-ignore 402 if (args.pid.length > 0) { 403 //@ts-ignore 404 str += `or process_id in (${args.pid.join(',')})`; 405 } 406 str = str.slice(3); 407 let eventStr: string = ``; 408 //@ts-ignore 409 if (args.eventId) { 410 //@ts-ignore 411 eventStr = `AND eventTypeId = ${args.eventId}`; 412 } 413 this.queryData(this.currentEventId, 'perf-async', ` 414 select 415 ts - R.start_ts as time, 416 traceid, 417 thread_id as tid, 418 process_id as pid, 419 caller_callchainid as callerCallchainid, 420 callee_callchainid as calleeCallchainid, 421 perf_sample_id as perfSampleId, 422 event_count as eventCount, 423 event_type_id as eventTypeId 424 from 425 perf_napi_async A, trace_range R 426 WHERE 427 (` + str + `)` + eventStr + ` 428 AND 429 time between ${ 430 //@ts-ignore 431 args.leftNs} and ${args.rightNs} 432 `, {}); 433 } 434 435 /** 436 * 437 * @param selectionParam 438 * @param sql 从饼图进程或者线程层点击进入Perf Profile时传入 439 */ 440 private getCurrentDataFromDb(selectionParam: SelectionParam, sql?: string): void { 441 let filterSql = this.setFilterSql(selectionParam, sql); 442 this.queryData( 443 this.currentEventId, 444 'perf-queryCallchainsGroupSample', 445 `select p.callchain_id as sampleId, 446 p.thread_state as threadState, 447 p.thread_id as tid, 448 p.count as count, 449 p.process_id as pid, 450 p.event_count as eventCount, 451 p.ts as ts, 452 p.event_type_id as eventTypeId 453 from (select callchain_id, s.thread_id, s.event_type_id, thread_state, process_id, 454 count(callchain_id) as count,SUM(event_count) as event_count, 455 group_concat(s.timestamp_trace - t.start_ts,',') as ts 456 from perf_sample s, trace_range t 457 left join perf_thread thread on s.thread_id = thread.thread_id 458 where timestamp_trace between ${selectionParam.leftNs} + t.start_ts 459 and ${selectionParam.rightNs} + t.start_ts 460 and callchain_id != -1 461 and s.thread_id != 0 ${filterSql} 462 group by callchain_id, s.thread_id, thread_state, process_id) p`, 463 { 464 $startTime: selectionParam.leftNs, 465 $endTime: selectionParam.rightNs, 466 $sql: filterSql, 467 } 468 ); 469 } 470 private setFilterSql(selectionParam: SelectionParam, sql?: string): string { 471 let filterSql = ''; 472 if (sql) { 473 const cpus = selectionParam.perfAll ? [] : selectionParam.perfCpus; 474 const cpuFilter = cpus.length > 0 ? ` and s.cpu_id in (${cpus.join(',')}) ` : ''; 475 let arg = `${sql}${cpuFilter}`.substring(3); 476 filterSql = `and ${arg}`; 477 } else { 478 const cpus = selectionParam.perfAll ? [] : selectionParam.perfCpus; 479 const processes = selectionParam.perfAll ? [] : selectionParam.perfProcess; 480 const threads = selectionParam.perfAll ? [] : selectionParam.perfThread; 481 if (cpus.length !== 0 || processes.length !== 0 || threads.length !== 0) { 482 const cpuFilter = cpus.length > 0 ? `or s.cpu_id in (${cpus.join(',')}) ` : ''; 483 const processFilter = processes.length > 0 ? `or thread.process_id in (${processes.join(',')}) ` : ''; 484 const threadFilter = threads.length > 0 ? `or s.thread_id in (${threads.join(',')})` : ''; 485 let arg = `${cpuFilter}${processFilter}${threadFilter}`.substring(3); 486 filterSql = ` and (${arg})`; 487 } 488 } 489 let eventTypeId = selectionParam.perfEventTypeId; 490 const eventTypeFilter = eventTypeId !== undefined ? ` and s.event_type_id = ${eventTypeId}` : ''; 491 filterSql += eventTypeFilter; 492 return filterSql; 493 } 494 495 clearAll(): void { 496 this.filesData = {}; 497 this.samplesData = []; 498 this.threadData = {}; 499 this.callChainData = {}; 500 this.splitMapData = {}; 501 this.currentTreeMapData = {}; 502 this.currentTreeList = []; 503 this.searchValue = ''; 504 this.dataSource = []; 505 this.allProcess = []; 506 this.dataCache.clearPerf(); 507 } 508 509 initPerfCallChainTopDown(callChains: PerfCallChain[]): void { 510 this.callChainData = {}; 511 callChains.forEach((callChain: PerfCallChain, index: number): void => { 512 this.setPerfCallChainFrameName(callChain); 513 this.addPerfGroupData(callChain); 514 let callChainDatum = this.callChainData[callChain.sampleId]; 515 if (callChainDatum.length > 1) { 516 PerfCallChain.setNextNode(callChainDatum[callChainDatum.length - 2], callChainDatum[callChainDatum.length - 1]); 517 } 518 }); 519 } 520 521 setPerfCallChainFrameName(callChain: PerfCallChain): void { 522 //设置调用栈的名称 523 callChain.canCharge = true; 524 if (this.filesData[callChain.fileId] && this.filesData[callChain.fileId].length > 0) { 525 callChain.fileName = this.filesData[callChain.fileId][0].fileName; 526 callChain.path = this.filesData[callChain.fileId][0].path; 527 } else { 528 callChain.fileName = 'unknown'; 529 } 530 } 531 532 addPerfGroupData(callChain: PerfCallChain): void { 533 const currentCallChain = this.callChainData[callChain.sampleId] || []; 534 this.callChainData[callChain.sampleId] = currentCallChain; 535 if (currentCallChain.length > maxDepth) { 536 currentCallChain.splice(0, 1); 537 } 538 currentCallChain.push(callChain); 539 } 540 541 addOtherCallchainsData(countSample: PerfCountSample, list: PerfCallChain[]): void { 542 let threadCallChain = new PerfCallChain(); //新增的线程数据 543 threadCallChain.tid = countSample.tid; 544 threadCallChain.canCharge = false; 545 threadCallChain.isThread = true; 546 threadCallChain.name = `${this.threadData[countSample.tid].threadName || 'Thread'}(${countSample.tid})`; 547 let threadStateCallChain = new PerfCallChain(); //新增的线程状态数据 548 threadStateCallChain.tid = countSample.tid; 549 threadStateCallChain.isThreadState = true; 550 threadStateCallChain.name = countSample.threadState || 'Unknown State'; 551 threadStateCallChain.fileName = threadStateCallChain.name === '-' ? 'Unknown Thread State' : ''; 552 threadStateCallChain.canCharge = false; 553 if (!this.isHideThreadState) { 554 list.unshift(threadStateCallChain); 555 } 556 if (!this.isHideThread) { 557 list.unshift(threadCallChain); 558 } 559 560 if (this.isOnlyKernel) { 561 const flag = '[kernel.kallsyms]'; 562 const newList = list.filter(i => i.fileName === flag || i.path === flag); 563 list.splice(0); 564 list.push(...newList); 565 } 566 } 567 568 private freshPerfCallchains(perfCountSamples: PerfCountSample[], isTopDown: boolean): void { 569 this.currentTreeMapData = {}; 570 this.currentTreeList = []; 571 let totalSamplesCount = 0; 572 let totalEventCount = 0; 573 perfCountSamples.forEach((perfSample): void => { 574 totalSamplesCount += perfSample.count; 575 totalEventCount += perfSample.eventCount; 576 if (this.callChainData[perfSample.sampleId] && this.callChainData[perfSample.sampleId].length > 0) { 577 let perfCallChains = [...this.callChainData[perfSample.sampleId]]; 578 this.addOtherCallchainsData(perfSample, perfCallChains); 579 let topIndex = isTopDown ? 0 : perfCallChains.length - 1; 580 if (perfCallChains.length > 0) { 581 let symbolName = ''; 582 if (typeof perfCallChains[topIndex].name === 'number') { 583 //@ts-ignore 584 symbolName = this.dataCache.dataDict.get(perfCallChains[topIndex].name) || ''; 585 } else { 586 //@ts-ignore 587 symbolName = perfCallChains[topIndex].name; 588 } 589 let perfRootNode = this.currentTreeMapData[symbolName + perfSample.pid]; 590 if (perfRootNode === undefined) { 591 perfRootNode = new PerfCallChainMerageData(); 592 this.currentTreeMapData[symbolName + perfSample.pid] = perfRootNode; 593 this.currentTreeList.push(perfRootNode); 594 } 595 PerfCallChainMerageData.merageCallChainSample(perfRootNode, perfCallChains[topIndex], perfSample, false); 596 this.mergeChildrenByIndex(perfRootNode, perfCallChains, topIndex, perfSample, isTopDown); 597 } 598 } 599 }); 600 let rootMerageMap = this.mergeNodeData(totalEventCount, totalSamplesCount); 601 this.handleCurrentTreeList(totalEventCount, totalSamplesCount); 602 this.allProcess = Object.values(rootMerageMap); 603 } 604 private mergeNodeData(totalEventCount: number, totalSamplesCount: number): MergeMap { 605 let rootMerageMap: MergeMap = {}; 606 // @ts-ignore 607 Object.values(this.currentTreeMapData).forEach((merageData: PerfCallChainMerageData): void => { 608 if (rootMerageMap[merageData.pid] === undefined) { 609 let perfProcessMerageData = new PerfCallChainMerageData(); //新增进程的节点数据 610 perfProcessMerageData.canCharge = false; 611 perfProcessMerageData.symbolName = 612 (this.threadData[merageData.tid].processName || 'Process') + `(${merageData.pid})`; 613 perfProcessMerageData.isProcess = true; 614 perfProcessMerageData.symbol = perfProcessMerageData.symbolName; 615 perfProcessMerageData.tid = merageData.tid; 616 perfProcessMerageData.children.push(merageData); 617 perfProcessMerageData.initChildren.push(merageData); 618 perfProcessMerageData.dur = merageData.dur; 619 perfProcessMerageData.count = merageData.dur; 620 perfProcessMerageData.eventCount = merageData.eventCount; 621 perfProcessMerageData.total = totalSamplesCount; 622 perfProcessMerageData.totalEvent = totalEventCount; 623 perfProcessMerageData.tsArray = [...merageData.tsArray]; 624 rootMerageMap[merageData.pid] = perfProcessMerageData; 625 } else { 626 rootMerageMap[merageData.pid].children.push(merageData); 627 rootMerageMap[merageData.pid].initChildren.push(merageData); 628 rootMerageMap[merageData.pid].dur += merageData.dur; 629 rootMerageMap[merageData.pid].count += merageData.dur; 630 rootMerageMap[merageData.pid].eventCount += merageData.eventCount; 631 rootMerageMap[merageData.pid].total = totalSamplesCount; 632 rootMerageMap[merageData.pid].totalEvent = totalEventCount; 633 for (const ts of merageData.tsArray) { 634 rootMerageMap[merageData.pid].tsArray.push(ts); 635 } 636 } 637 merageData.parentNode = rootMerageMap[merageData.pid]; //子节点添加父节点的引用 638 }); 639 return rootMerageMap; 640 } 641 private handleCurrentTreeList(totalEventCount: number, totalSamplesCount: number): void { 642 let id = 0; 643 this.currentTreeList.forEach((perfTreeNode: PerfCallChainMerageData): void => { 644 perfTreeNode.total = totalSamplesCount; 645 perfTreeNode.totalEvent = totalEventCount; 646 if (perfTreeNode.id === '') { 647 perfTreeNode.id = id + ''; 648 id++; 649 } 650 if (perfTreeNode.parentNode) { 651 if (perfTreeNode.parentNode.id === '') { 652 perfTreeNode.parentNode.id = id + ''; 653 id++; 654 } 655 perfTreeNode.parentId = perfTreeNode.parentNode.id; 656 } 657 }); 658 } 659 660 mergeChildrenByIndex( 661 currentNode: PerfCallChainMerageData, 662 callChainDataList: PerfCallChain[], 663 index: number, 664 sample: PerfCountSample, 665 isTopDown: boolean 666 ): void { 667 if ((isTopDown && index >= callChainDataList.length - 1) || (!isTopDown && index <= 0)) { 668 return; 669 } 670 isTopDown ? index++ : index--; 671 let isEnd = isTopDown ? callChainDataList.length === index + 1 : index === 0; 672 let node: PerfCallChainMerageData; 673 if ( 674 currentNode.initChildren.filter((child: PerfCallChainMerageData): boolean => { 675 let name: number | string | undefined = callChainDataList[index].name; 676 if (typeof name === 'number') { 677 name = this.dataCache.dataDict.get(name); 678 } 679 if (child.symbolName === name) { 680 node = child; 681 PerfCallChainMerageData.merageCallChainSample(child, callChainDataList[index], sample, isEnd); 682 return true; 683 } 684 return false; 685 }).length === 0 686 ) { 687 node = new PerfCallChainMerageData(); 688 PerfCallChainMerageData.merageCallChainSample(node, callChainDataList[index], sample, isEnd); 689 currentNode.children.push(node); 690 currentNode.initChildren.push(node); 691 this.currentTreeList.push(node); 692 node.parentNode = currentNode; 693 } 694 if (node! && !isEnd) { 695 this.mergeChildrenByIndex(node, callChainDataList, index, sample, isTopDown); 696 } 697 } 698 699 //所有的操作都是针对整个树结构的 不区分特定的数据 700 splitPerfTree(samples: PerfCallChainMerageData[], name: string, isCharge: boolean, isSymbol: boolean): void { 701 samples.forEach((process: PerfCallChainMerageData): void => { 702 process.children = []; 703 if (isCharge) { 704 this.recursionPerfChargeInitTree(process, name, isSymbol); 705 } else { 706 this.recursionPerfPruneInitTree(process, name, isSymbol); 707 } 708 }); 709 this.resetAllNode(samples); 710 } 711 712 recursionPerfChargeInitTree(sample: PerfCallChainMerageData, symbolName: string, isSymbol: boolean): void { 713 if ((isSymbol && sample.symbolName === symbolName) || (!isSymbol && sample.libName === symbolName)) { 714 (this.splitMapData[symbolName] = this.splitMapData[symbolName] || []).push(sample); 715 sample.isStore++; 716 } 717 if (sample.initChildren.length > 0) { 718 sample.initChildren.forEach((child: PerfCallChainMerageData): void => { 719 this.recursionPerfChargeInitTree(child, symbolName, isSymbol); 720 }); 721 } 722 } 723 724 recursionPerfPruneInitTree(node: PerfCallChainMerageData, symbolName: string, isSymbol: boolean): void { 725 if ((isSymbol && node.symbolName === symbolName) || (!isSymbol && node.libName === symbolName)) { 726 (this.splitMapData[symbolName] = this.splitMapData[symbolName] || []).push(node); 727 node.isStore++; 728 this.pruneChildren(node, symbolName); 729 } else if (node.initChildren.length > 0) { 730 node.initChildren.forEach((child): void => { 731 this.recursionPerfPruneInitTree(child, symbolName, isSymbol); 732 }); 733 } 734 } 735 736 //symbol lib prune 737 recursionPruneTree(sample: PerfCallChainMerageData, symbolName: string, isSymbol: boolean): void { 738 if ((isSymbol && sample.symbolName === symbolName) || (!isSymbol && sample.libName === symbolName)) { 739 sample.parent && sample.parent.children.splice(sample.parent.children.indexOf(sample), 1); 740 } else { 741 sample.children.forEach((child: PerfCallChainMerageData): void => { 742 this.recursionPruneTree(child, symbolName, isSymbol); 743 }); 744 } 745 } 746 747 recursionChargeByRule( 748 sample: PerfCallChainMerageData, 749 ruleName: string, 750 rule: (node: PerfCallChainMerageData) => boolean 751 ): void { 752 if (sample.initChildren.length > 0) { 753 sample.initChildren.forEach((child): void => { 754 if (rule(child) && !child.isThread && !child.isState) { 755 (this.splitMapData[ruleName] = this.splitMapData[ruleName] || []).push(child); 756 child.isStore++; 757 } 758 this.recursionChargeByRule(child, ruleName, rule); 759 }); 760 } 761 } 762 763 pruneChildren(sample: PerfCallChainMerageData, symbolName: string): void { 764 if (sample.initChildren.length > 0) { 765 sample.initChildren.forEach((child: PerfCallChainMerageData): void => { 766 child.isStore++; 767 (this.splitMapData[symbolName] = this.splitMapData[symbolName] || []).push(child); 768 this.pruneChildren(child, symbolName); 769 }); 770 } 771 } 772 773 hideSystemLibrary(): void { 774 this.allProcess.forEach((item: PerfCallChainMerageData): void => { 775 item.children = []; 776 this.recursionChargeByRule(item, systemRuleName, (node: PerfCallChainMerageData): boolean => { 777 return node.path.startsWith(systemRuleName); 778 }); 779 }); 780 } 781 782 onlyKernel(): void { 783 this.allProcess.forEach((item: PerfCallChainMerageData): void => { 784 item.children = []; 785 786 function recursionHideChildren( 787 sample: PerfCallChainMerageData, 788 rule: (node: PerfCallChainMerageData) => boolean 789 ): void { 790 if (sample.initChildren.length > 0) { 791 sample.initChildren.forEach((child): void => { 792 if (rule(child)) { 793 child.isStore++; 794 } 795 recursionHideChildren(child, rule); 796 }); 797 } 798 } 799 recursionHideChildren(item, (node: PerfCallChainMerageData): boolean => { 800 return node.libName !== '[kernel.kallsyms]'; 801 }); 802 }); 803 } 804 805 hideNumMaxAndMin(startNum: number, endNum: string): void { 806 let max = endNum === '∞' ? Number.POSITIVE_INFINITY : parseInt(endNum); 807 this.allProcess.forEach((item: PerfCallChainMerageData): void => { 808 item.children = []; 809 this.recursionChargeByRule(item, numRuleName, (node: PerfCallChainMerageData): boolean => { 810 return node.dur < startNum || node.dur > max; 811 }); 812 }); 813 } 814 815 clearSplitMapData(symbolName: string): void { 816 if (symbolName in this.splitMapData) { 817 Reflect.deleteProperty(this.splitMapData, symbolName); 818 } 819 } 820 821 resetAllSymbol(symbols: string[]): void { 822 symbols.forEach((symbol: string): void => { 823 let list = this.splitMapData[symbol]; 824 if (list !== undefined) { 825 list.forEach((item: PerfCallChainMerageData): void => { 826 item.isStore--; 827 }); 828 } 829 }); 830 } 831 832 resetAllNode(sample: PerfCallChainMerageData[]): void { 833 this.clearSearchNode(); 834 sample.forEach((process: PerfCallChainMerageData): void => { 835 process.searchShow = true; 836 process.isSearch = false; 837 }); 838 this.resetNewAllNode(sample); 839 if (this.searchValue !== '') { 840 this.findSearchNode(sample, this.searchValue, false); 841 this.resetNewAllNode(sample); 842 } 843 } 844 845 resetNewAllNode(sampleArray: PerfCallChainMerageData[]): void { 846 sampleArray.forEach((process: PerfCallChainMerageData): void => { 847 process.children = []; 848 }); 849 let values = this.currentTreeList.map((item: PerfCallChainMerageData): PerfCallChainMerageData => { 850 item.children = []; 851 return item; 852 }); 853 values.forEach((sample: PerfCallChainMerageData): void => { 854 if (sample.parentNode !== undefined) { 855 if (sample.isStore === 0 && sample.searchShow) { 856 let parentNode = sample.parentNode; 857 while (parentNode !== undefined && !(parentNode.isStore === 0 && parentNode.searchShow)) { 858 parentNode = parentNode.parentNode!; 859 } 860 if (parentNode) { 861 sample.parent = parentNode; 862 parentNode.children.push(sample); 863 } 864 } 865 } 866 }); 867 } 868 869 kernelCombination(): void { 870 function mergeChildren(item: PerfCallChainMerageData): void { 871 if (item.children.length <= 0) { 872 return; 873 } 874 item.children = item.children.reduce((total: PerfCallChainMerageData[], pfcall: PerfCallChainMerageData): PerfCallChainMerageData[] => { 875 for (const prev of total) { 876 if (pfcall.symbol === prev.symbol) { 877 prev.children.push(...pfcall.children); 878 prev.total += pfcall.total; 879 prev.count += pfcall.count; 880 prev.totalEvent += pfcall.totalEvent; 881 prev.eventCount += pfcall.eventCount; 882 return total; 883 } 884 } 885 total.push(pfcall); 886 return total; 887 }, [] as PerfCallChainMerageData[]); 888 for (const child of item.children) { 889 mergeChildren(child); 890 } 891 } 892 this.allProcess.forEach((item: PerfCallChainMerageData): void => { 893 mergeChildren(item); 894 }); 895 } 896 897 findSearchNode(sampleArray: PerfCallChainMerageData[], search: string, parentSearch: boolean): void { 898 search = search.toLocaleLowerCase(); 899 sampleArray.forEach((sample: PerfCallChainMerageData): void => { 900 if ((sample.symbol && sample.symbol.toLocaleLowerCase().includes(search)) || parentSearch) { 901 sample.searchShow = true; 902 sample.isSearch = sample.symbol !== undefined && sample.symbol.toLocaleLowerCase().includes(search); 903 let parentNode = sample.parent; 904 while (parentNode !== undefined && !parentNode.searchShow) { 905 parentNode.searchShow = true; 906 parentNode = parentNode.parent; 907 } 908 } else { 909 sample.searchShow = false; 910 sample.isSearch = false; 911 } 912 if (sample.children.length > 0) { 913 this.findSearchNode(sample.children, search, sample.searchShow); 914 } 915 }); 916 } 917 918 clearSearchNode(): void { 919 this.currentTreeList.forEach((sample: PerfCallChainMerageData): void => { 920 sample.searchShow = true; 921 sample.isSearch = false; 922 }); 923 } 924 925 splitAllProcess(processArray: { select: string; name: string; type: string; checked: boolean }[]): void { 926 processArray.forEach((item: { select: string; name: string; type: string; checked: boolean }): void => { 927 this.allProcess.forEach((process): void => { 928 if (item.select === '0') { 929 this.recursionPerfChargeInitTree(process, item.name, item.type === 'symbol'); 930 } else { 931 this.recursionPerfPruneInitTree(process, item.name, item.type === 'symbol'); 932 } 933 }); 934 if (!item.checked) { 935 this.resetAllSymbol([item.name]); 936 } 937 }); 938 } 939 resolvingAction(params: unknown[]): unknown[] { 940 if (params.length > 0) { 941 for (let item of params) { 942 //@ts-ignore 943 if (item.funcName && item.funcArgs) { 944 //@ts-ignore 945 let result = this.handleDataByFuncName(item.funcName, item.funcArgs); 946 if (result) { 947 //@ts-ignore 948 return result; 949 } 950 } 951 } 952 this.dataSource = this.allProcess.filter((process: PerfCallChainMerageData): boolean => { 953 return process.children && process.children.length > 0; 954 }); 955 } 956 return this.dataSource; 957 } 958 private queryDataFromDb(funcArgs: unknown[]): void { 959 if (funcArgs[1]) { 960 let sql = ''; 961 //@ts-ignore 962 if (funcArgs[1].processId !== undefined) { 963 //@ts-ignore 964 sql += `and thread.process_id = ${funcArgs[1].processId}`; 965 } 966 //@ts-ignore 967 if (funcArgs[1].threadId !== undefined) { 968 //@ts-ignore 969 sql += ` and s.thread_id = ${funcArgs[1].threadId}`; 970 } 971 //@ts-ignore 972 this.getCurrentDataFromDb(funcArgs[0], sql); 973 } else { 974 //@ts-ignore 975 this.getCurrentDataFromDb(funcArgs[0]); 976 } 977 } 978 private handleDataByFuncName(funcName: string, funcArgs: unknown[]): unknown { 979 let result; 980 switch (funcName) { 981 case 'getCallChainsBySampleIds': 982 this.freshPerfCallchains(this.samplesData, funcArgs[0] as boolean); 983 break; 984 case 'getCurrentDataFromDb': 985 this.queryDataFromDb(funcArgs); 986 break; 987 case 'hideSystemLibrary': 988 this.hideSystemLibrary(); 989 break; 990 case 'hideThread': 991 this.isHideThread = funcArgs[0] as boolean; 992 break; 993 case 'hideThreadState': 994 this.isHideThreadState = funcArgs[0] as boolean; 995 break; 996 case 'onlyKernel': 997 this.isOnlyKernel = funcArgs[0] as boolean; 998 break; 999 case 'hideNumMaxAndMin': 1000 this.hideNumMaxAndMin(funcArgs[0] as number, funcArgs[1] as string); 1001 break; 1002 case 'splitAllProcess': 1003 //@ts-ignore 1004 this.splitAllProcess(funcArgs[0]); 1005 break; 1006 case 'resetAllNode': 1007 this.resetAllNode(this.allProcess); 1008 break; 1009 case 'resotreAllNode': 1010 this.resetAllSymbol(funcArgs[0] as string[]); 1011 break; 1012 case 'clearSplitMapData': 1013 this.clearSplitMapData(funcArgs[0] as string); 1014 break; 1015 case 'splitTree': 1016 this.splitPerfTree(this.allProcess, funcArgs[0] as string, funcArgs[1] as boolean, funcArgs[2] as boolean); 1017 break; 1018 case 'setSearchValue': 1019 this.searchValue = funcArgs[0] as string; 1020 break; 1021 case 'setCombineCallChain': 1022 this.isAnalysis = true; 1023 break; 1024 case 'setPerfBottomUp': 1025 this.isPerfBottomUp = true; 1026 break; 1027 case 'combineAnalysisCallChain': 1028 result = this.combineCallChainForAnalysis(); 1029 break; 1030 case 'getBottomUp': 1031 result = this.getBottomUp(); 1032 break; 1033 case 'kernelCombination': 1034 this.kernelCombination(); 1035 break; 1036 } 1037 return result; 1038 } 1039 1040 combineCallChainForAnalysis(obj?: unknown): PerfAnalysisSample[] { 1041 let sampleCallChainList: Array<PerfAnalysisSample> = []; 1042 for (let sample of this.samplesData) { 1043 let callChains = [...this.callChainData[sample.sampleId]]; 1044 const lastCallChain = callChains[callChains.length - 1]; 1045 const threadName = this.threadData[sample.tid].threadName || 'Thread'; 1046 const processName = this.threadData[sample.pid].threadName || 'Process'; 1047 const funcName = this.dataCache.dataDict.get(lastCallChain.name as number); 1048 if ( 1049 //@ts-ignore 1050 (obj && obj.libId === lastCallChain.fileId && obj.libName === lastCallChain.fileName) || 1051 //@ts-ignore 1052 (obj && obj.symbolId === lastCallChain.symbolId && obj.symbolName === funcName) || 1053 !obj 1054 ) { 1055 let analysisSample = new PerfAnalysisSample( 1056 threadName, 1057 processName, 1058 lastCallChain.fileId, 1059 lastCallChain.fileName, 1060 lastCallChain.symbolId, 1061 this.dataCache.dataDict.get(lastCallChain.name as number) || '' 1062 ); 1063 analysisSample.tid = sample.tid; 1064 analysisSample.pid = sample.pid; 1065 analysisSample.count = sample.count; 1066 analysisSample.threadState = sample.threadState; 1067 analysisSample.eventCount = sample.eventCount; 1068 analysisSample.sampleId = sample.sampleId; 1069 sampleCallChainList.push(analysisSample); 1070 } 1071 } 1072 return sampleCallChainList; 1073 } 1074 1075 getBottomUp(): PerfBottomUpStruct[] { 1076 const topUp = new PerfBottomUpStruct('topUp'); 1077 let perfTime = 1; 1078 for (let sample of this.samplesData) { 1079 let currentNode = topUp; 1080 let callChains = this.callChainData[sample.sampleId]; 1081 for (let i = 0; i < callChains.length; i++) { 1082 if (i === 0) { 1083 currentNode = topUp; 1084 } 1085 let item = callChains[i]; 1086 const existingNode = currentNode.children.find( 1087 (child) => child.symbolName === `${item.name}(${item.fileName})` 1088 ); 1089 if (existingNode) { 1090 existingNode.tsArray.push(...sample.ts.split(',').map(Number)); 1091 currentNode = existingNode; 1092 existingNode.totalTime += perfTime * sample.count; 1093 existingNode.eventCount += sample.eventCount; 1094 existingNode.calculateSelfTime(); 1095 existingNode.notifyParentUpdateSelfTime(); 1096 } else { 1097 const symbolName = this.dataCache.dataDict.get(item.name as number) || ''; 1098 let newNode = new PerfBottomUpStruct(`${symbolName}(${item.fileName})`); 1099 newNode.totalTime = perfTime * sample.count; 1100 newNode.eventCount = sample.eventCount; 1101 newNode.tsArray = sample.ts.split(',').map(Number); 1102 currentNode.addChildren(newNode); 1103 newNode.calculateSelfTime(); 1104 newNode.notifyParentUpdateSelfTime(); 1105 currentNode = newNode; 1106 } 1107 } 1108 } 1109 topUp.children.forEach((child: PerfBottomUpStruct): void => { 1110 child.parentNode = undefined; 1111 }); 1112 1113 let date = this.topUpDataToBottomUpData(topUp.children); 1114 if (this.isPerfBottomUp) { 1115 this.isPerfBottomUp = false; 1116 } 1117 return date; 1118 } 1119 1120 private topUpDataToBottomUpData(perfPositiveArray: Array<PerfBottomUpStruct>): Array<PerfBottomUpStruct> { 1121 let reverseTreeArray: Array<PerfBottomUpStruct> = []; 1122 const recursionTree = (perfBottomUpStruct: PerfBottomUpStruct): void => { 1123 if (perfBottomUpStruct.selfTime > 0) { 1124 const clonePerfBottomUpStruct = new PerfBottomUpStruct(perfBottomUpStruct.symbolName); 1125 clonePerfBottomUpStruct.selfTime = perfBottomUpStruct.selfTime; 1126 clonePerfBottomUpStruct.totalTime = perfBottomUpStruct.totalTime; 1127 clonePerfBottomUpStruct.eventCount = perfBottomUpStruct.eventCount; 1128 clonePerfBottomUpStruct.tsArray = [...perfBottomUpStruct.tsArray]; 1129 reverseTreeArray.push(clonePerfBottomUpStruct); 1130 this.copyParentNode(clonePerfBottomUpStruct, perfBottomUpStruct); 1131 } 1132 if (perfBottomUpStruct.children.length > 0) { 1133 for (const children of perfBottomUpStruct.children) { 1134 children.parentNode = perfBottomUpStruct; 1135 recursionTree(children); 1136 } 1137 } 1138 }; 1139 for (const perfBottomUpStruct of perfPositiveArray) { 1140 recursionTree(perfBottomUpStruct); 1141 } 1142 return this.mergeTreeBifurcation(reverseTreeArray, null); 1143 } 1144 1145 private mergeTreeBifurcation( 1146 reverseTreeArray: Array<PerfBottomUpStruct> | null, 1147 parent: PerfBottomUpStruct | null 1148 ): Array<PerfBottomUpStruct> { 1149 const sameSymbolMap = new Map<string, PerfBottomUpStruct>(); 1150 const currentLevelData: Array<PerfBottomUpStruct> = []; 1151 const dataArray = reverseTreeArray || parent?.frameChildren; 1152 if (!dataArray) { 1153 return []; 1154 } 1155 for (const perfBottomUpStruct of dataArray) { 1156 let symbolKey = perfBottomUpStruct.symbolName; 1157 let bottomUpStruct: PerfBottomUpStruct; 1158 if (sameSymbolMap.has(symbolKey)) { 1159 bottomUpStruct = sameSymbolMap.get(symbolKey)!; 1160 bottomUpStruct.totalTime += perfBottomUpStruct.totalTime; 1161 bottomUpStruct.selfTime += perfBottomUpStruct.selfTime; 1162 for (const ts of perfBottomUpStruct.tsArray) { 1163 bottomUpStruct.tsArray.push(ts); 1164 } 1165 } else { 1166 bottomUpStruct = perfBottomUpStruct; 1167 sameSymbolMap.set(symbolKey, bottomUpStruct); 1168 currentLevelData.push(bottomUpStruct); 1169 if (parent) { 1170 parent.addChildren(bottomUpStruct); 1171 } 1172 } 1173 bottomUpStruct.frameChildren?.push(...perfBottomUpStruct.children); 1174 } 1175 1176 for (const data of currentLevelData) { 1177 this.mergeTreeBifurcation(null, data); 1178 data.frameChildren = []; 1179 } 1180 if (reverseTreeArray) { 1181 return currentLevelData; 1182 } else { 1183 return []; 1184 } 1185 } 1186 1187 /** 1188 * copy整体调用链,从栈顶函数一直copy到栈底函数, 1189 * 给Parent设置selfTime,totalTime设置为children的selfTime,totalTime 1190 * */ 1191 private copyParentNode(perfBottomUpStruct: PerfBottomUpStruct, bottomUpStruct: PerfBottomUpStruct): void { 1192 if (bottomUpStruct.parentNode) { 1193 const copyParent = new PerfBottomUpStruct(bottomUpStruct.parentNode.symbolName); 1194 copyParent.selfTime = perfBottomUpStruct.selfTime; 1195 copyParent.totalTime = perfBottomUpStruct.totalTime; 1196 copyParent.eventCount = perfBottomUpStruct.eventCount; 1197 copyParent.tsArray = [...perfBottomUpStruct.tsArray]; 1198 perfBottomUpStruct.addChildren(copyParent); 1199 this.copyParentNode(copyParent, bottomUpStruct.parentNode); 1200 } 1201 } 1202} 1203 1204export class PerfFile { 1205 fileId: number = 0; 1206 symbol: string = ''; 1207 path: string = ''; 1208 fileName: string = ''; 1209 1210 static setFileName(data: PerfFile): void { 1211 if (data.path) { 1212 let number = data.path.lastIndexOf('/'); 1213 if (number > 0) { 1214 data.fileName = data.path.substring(number + 1); 1215 return; 1216 } 1217 } 1218 data.fileName = data.path; 1219 } 1220 1221 setFileName(): void { 1222 if (this.path) { 1223 let number = this.path.lastIndexOf('/'); 1224 if (number > 0) { 1225 this.fileName = this.path.substring(number + 1); 1226 return; 1227 } 1228 } 1229 this.fileName = this.path; 1230 } 1231} 1232 1233export class PerfThread { 1234 tid: number = 0; 1235 pid: number = 0; 1236 threadName: string = ''; 1237 processName: string = ''; 1238} 1239 1240export class PerfCallChain { 1241 startNS: number = 0; 1242 dur: number = 0; 1243 sampleId: number = 0; 1244 callChainId: number = 0; 1245 vaddrInFile: number = 0; 1246 tid: number = 0; 1247 pid: number = 0; 1248 name: number | string = 0; 1249 fileName: string = ''; 1250 threadState: string = ''; 1251 fileId: number = 0; 1252 symbolId: number = 0; 1253 path: string = ''; 1254 count: number = 0; 1255 eventCount: number = 0; 1256 parentId: string = ''; //合并之后区分的id 1257 id: string = ''; 1258 topDownMerageId: string = ''; //top down合并使用的id 1259 topDownMerageParentId: string = ''; //top down合并使用的id 1260 bottomUpMerageId: string = ''; //bottom up合并使用的id 1261 bottomUpMerageParentId: string = ''; //bottom up合并使用的id 1262 depth: number = 0; 1263 canCharge: boolean = true; 1264 previousNode: PerfCallChain | undefined = undefined; //将list转换为一个链表结构 1265 nextNode: PerfCallChain | undefined = undefined; 1266 isThread: boolean = false; 1267 isProcess: boolean = false; 1268 isThreadState: boolean = false; 1269 1270 static setNextNode(currentNode: PerfCallChain, nextNode: PerfCallChain): void { 1271 currentNode.nextNode = nextNode; 1272 nextNode.previousNode = currentNode; 1273 } 1274 1275 static setPreviousNode(currentNode: PerfCallChain, prevNode: PerfCallChain): void { 1276 currentNode.previousNode = prevNode; 1277 prevNode.nextNode = currentNode; 1278 } 1279 1280 static merageCallChain(currentNode: PerfCallChain, callChain: PerfCallChain): void { 1281 currentNode.startNS = callChain.startNS; 1282 currentNode.tid = callChain.tid; 1283 currentNode.pid = callChain.pid; 1284 currentNode.sampleId = callChain.sampleId; 1285 currentNode.dur = callChain.dur; 1286 currentNode.count = callChain.count; 1287 currentNode.eventCount = callChain.eventCount; 1288 } 1289} 1290 1291export class PerfCallChainMerageData extends ChartStruct { 1292 // @ts-ignore 1293 #parentNode: PerfCallChainMerageData | undefined = undefined; 1294 // @ts-ignore 1295 #total = 0; 1296 // @ts-ignore 1297 #totalEvent = 0; 1298 id: string = ''; 1299 parentId: string = ''; 1300 parent: PerfCallChainMerageData | undefined = undefined; 1301 symbolName: string = ''; 1302 symbol: string = ''; 1303 libName: string = ''; 1304 path: string = ''; 1305 weight: string = ''; 1306 weightPercent: string = ''; 1307 selfDur: number = 0; 1308 dur: number = 0; 1309 tid: number = 0; 1310 pid: number = 0; 1311 isStore = 0; 1312 canCharge: boolean = true; 1313 children: PerfCallChainMerageData[] = []; 1314 initChildren: PerfCallChainMerageData[] = []; 1315 type: number = 0; 1316 vaddrInFile: number = 0; 1317 isSelected: boolean = false; 1318 searchShow: boolean = true; 1319 isSearch: boolean = false; 1320 isState: boolean = false; 1321 set parentNode(data: PerfCallChainMerageData | undefined) { 1322 this.parent = data; 1323 this.#parentNode = data; 1324 } 1325 1326 get parentNode(): PerfCallChainMerageData | undefined { 1327 return this.#parentNode; 1328 } 1329 1330 set total(data: number) { 1331 this.#total = data; 1332 this.weight = `${this.dur}`; 1333 this.weightPercent = `${((this.dur / data) * 100).toFixed(1)}%`; 1334 } 1335 1336 get total(): number { 1337 return this.#total; 1338 } 1339 1340 set totalEvent(data: number) { 1341 this.#totalEvent = data; 1342 this.eventPercent = `${((this.eventCount / data) * 100).toFixed(1)}%`; 1343 } 1344 1345 get totalEvent(): number { 1346 return this.#totalEvent; 1347 } 1348 1349 static merageCallChainSample( 1350 currentNode: PerfCallChainMerageData, 1351 callChain: PerfCallChain, 1352 sample: PerfCountSample, 1353 isEnd: boolean 1354 ): void { 1355 if (currentNode.symbolName === '') { 1356 let symbolName = ''; 1357 if (typeof callChain.name === 'number') { 1358 symbolName = DataCache.getInstance().dataDict.get(callChain.name) || ''; 1359 } else { 1360 symbolName = callChain.name; 1361 } 1362 currentNode.symbol = `${symbolName} ${callChain.fileName ? `(${callChain.fileName})` : ''}`; 1363 currentNode.symbolName = symbolName; 1364 currentNode.pid = sample.pid; 1365 currentNode.tid = sample.tid; 1366 currentNode.libName = callChain.fileName; 1367 currentNode.vaddrInFile = callChain.vaddrInFile; 1368 currentNode.lib = callChain.fileName; 1369 currentNode.addr = `${'0x'}${callChain.vaddrInFile.toString(16)}`; 1370 currentNode.canCharge = callChain.canCharge; 1371 if (callChain.path) { 1372 currentNode.path = callChain.path; 1373 } 1374 } 1375 if (isEnd) { 1376 currentNode.selfDur += sample.count; 1377 } 1378 if (callChain.isThread && !currentNode.isThread) { 1379 currentNode.isThread = callChain.isThread; 1380 } 1381 if (callChain.isThreadState && !currentNode.isState) { 1382 currentNode.isState = callChain.isThreadState; 1383 } 1384 currentNode.dur += sample.count; 1385 currentNode.count += sample.count; 1386 currentNode.eventCount += sample.eventCount; 1387 currentNode.tsArray.push(...sample.ts.split(',').map(Number)); 1388 } 1389} 1390 1391export class PerfCountSample { 1392 sampleId: number = 0; 1393 tid: number = 0; 1394 count: number = 0; 1395 threadState: string = ''; 1396 pid: number = 0; 1397 eventCount: number = 0; 1398 ts: string = ''; 1399} 1400 1401export class PerfStack { 1402 symbol: string = ''; 1403 path: string = ''; 1404 fileId: number = 0; 1405 type: number = 0; 1406 vaddrInFile: number = 0; 1407} 1408 1409export class PerfCmdLine { 1410 report_value: string = ''; 1411} 1412 1413class PerfAnalysisSample extends PerfCountSample { 1414 threadName: string; 1415 processName: string; 1416 libId: number; 1417 libName: string; 1418 symbolId: number; 1419 symbolName: string; 1420 1421 constructor( 1422 threadName: string, 1423 processName: string, 1424 libId: number, 1425 libName: string, 1426 symbolId: number, 1427 symbolName: string 1428 ) { 1429 super(); 1430 this.threadName = threadName; 1431 this.processName = processName; 1432 this.libId = libId; 1433 this.libName = libName; 1434 this.symbolId = symbolId; 1435 this.symbolName = symbolName; 1436 } 1437} 1438 1439export function timeMsFormat2p(ns: number): string { 1440 let currentNs = ns; 1441 let hour1 = 3600_000; 1442 let minute1 = 60_000; 1443 let second1 = 1_000; // 1 second 1444 let perfResult = ''; 1445 if (currentNs >= hour1) { 1446 perfResult += `${Math.floor(currentNs / hour1).toFixed(2)}h`; 1447 return perfResult; 1448 } 1449 if (currentNs >= minute1) { 1450 perfResult += `${Math.floor(currentNs / minute1).toFixed(2)}min`; 1451 return perfResult; 1452 } 1453 if (currentNs >= second1) { 1454 perfResult += `${Math.floor(currentNs / second1).toFixed(2)}s`; 1455 return perfResult; 1456 } 1457 if (currentNs > 0) { 1458 perfResult += `${currentNs.toFixed(2)}ms`; 1459 return perfResult; 1460 } 1461 if (perfResult === '') { 1462 perfResult = '0s'; 1463 } 1464 return perfResult; 1465} 1466 1467class HiPrefSample { 1468 name: string = ''; 1469 depth: number = 0; 1470 callchain_id: number = 0; 1471 totalTime: number = 0; 1472 thread_id: number = 0; 1473 id: number = 0; 1474 eventCount: number = 0; 1475 startTime: number = 0; 1476 endTime: number = 0; 1477 timeTip: number = 0; 1478 cpu_id: number = 0; 1479 stack?: Array<HiPerfSymbol>; 1480} 1481