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 { LogicHandler, ChartStruct, convertJSON, DataCache, PerfCall } from './ProcedureLogicWorkerCommon.js'; 17const systemRuleName = '/system/'; 18const numRuleName = '/max/min/'; 19 20export class ProcedureLogicWorkerPerf extends LogicHandler { 21 filesData: any = {}; 22 samplesData: any = {}; 23 threadData: any = {}; 24 callChainData: any = {}; 25 splitMapData: any = {}; 26 currentTreeMapData: any = {}; 27 currentTreeList: any[] = []; 28 searchValue: string = ''; 29 dataSource: PerfCallChainMerageData[] = []; 30 allProcess: PerfCallChainMerageData[] = []; 31 queryFunc?: Function | undefined; 32 isActualQuery: boolean = false; 33 currentEventId: string = ''; 34 isAnalysis: boolean = false; 35 private dataCache = DataCache.getInstance(); 36 37 handle(data: any): void { 38 this.currentEventId = data.id; 39 if (data && data.type) { 40 switch (data.type) { 41 case 'perf-init': 42 this.dataCache.perfCountToMs = data.params.fValue; 43 this.initPerfFiles(); 44 break; 45 case 'perf-queryPerfFiles': 46 let files = convertJSON(data.params.list) || []; 47 files.forEach((file: any) => { 48 this.filesData[file.fileId] = this.filesData[file.fileId] || []; 49 PerfFile.setFileName(file); 50 this.filesData[file.fileId].push(file); 51 }); 52 this.initPerfThreads(); 53 break; 54 case 'perf-queryPerfThread': 55 let threads = convertJSON(data.params.list) || []; 56 threads.forEach((thread: any) => { 57 this.threadData[thread.tid] = thread; 58 }); 59 this.initPerfCalls(); 60 break; 61 case 'perf-queryPerfCalls': 62 let perfCalls = convertJSON(data.params.list) || []; 63 if (perfCalls.length != 0) { 64 perfCalls.forEach((perfCall: any) => { 65 this.dataCache.perfCallChainMap.set(perfCall.sampleId, perfCall); 66 }); 67 } 68 this.initPerfCallchains(); 69 break; 70 case 'perf-queryPerfCallchains': 71 let arr = convertJSON(data.params.list) || []; 72 this.initPerfCallChainTopDown(arr); 73 // @ts-ignore 74 self.postMessage({ 75 id: data.id, 76 action: data.action, 77 results: this.dataCache.perfCallChainMap, 78 }); 79 break; 80 case 'perf-queryCallchainsGroupSample': 81 this.samplesData = convertJSON(data.params.list) || []; 82 83 let result; 84 if (this.isAnalysis) { 85 result = this.resolvingAction([ 86 { 87 funcName: 'combineAnalysisCallChain', 88 funcArgs: [true], 89 }, 90 ]); 91 } else { 92 result = this.resolvingAction([ 93 { 94 funcName: 'getCallChainsBySampleIds', 95 funcArgs: [true], 96 }, 97 ]); 98 } 99 self.postMessage({ 100 id: data.id, 101 action: data.action, 102 results: result, 103 }); 104 break; 105 case 'perf-action': 106 if (data.params) { 107 let filter = data.params.filter((item: any) => item.funcName == 'getCurrentDataFromDb'); 108 if (filter.length == 0) { 109 // @ts-ignore 110 self.postMessage({ 111 id: data.id, 112 action: data.action, 113 results: this.resolvingAction(data.params), 114 }); 115 } else { 116 this.resolvingAction(data.params); 117 } 118 } 119 break; 120 } 121 } 122 } 123 124 initPerfFiles() { 125 this.clearAll(); 126 this.queryData( 127 this.currentEventId, 128 'perf-queryPerfFiles', 129 `select file_id as fileId,symbol,path from perf_files`, 130 {} 131 ); 132 } 133 134 initPerfThreads() { 135 this.queryData( 136 this.currentEventId, 137 'perf-queryPerfThread', 138 `select a.thread_id as tid,a.thread_name as threadName,a.process_id as pid,b.thread_name as processName from perf_thread a left join (select * from perf_thread where thread_id = process_id) b on a.process_id = b.thread_id`, 139 {} 140 ); 141 } 142 143 initPerfCalls() { 144 this.queryData( 145 this.currentEventId, 146 'perf-queryPerfCalls', 147 `select count(callchain_id) as depth,callchain_id as sampleId,name 148from perf_callchain 149where callchain_id != -1 150group by callchain_id`, 151 {} 152 ); 153 } 154 155 initPerfCallchains() { 156 this.queryData( 157 this.currentEventId, 158 'perf-queryPerfCallchains', 159 `select c.name,c.callchain_id as sampleId,c.vaddr_in_file as vaddrInFile,c.file_id as fileId,c.symbol_id as symbolId 160from perf_callchain c 161where callchain_id != -1;`, 162 {} 163 ); 164 } 165 166 getCurrentDataFromDb(selectionParam: any) { 167 let cpus = selectionParam.perfAll ? [] : selectionParam.perfCpus; 168 let processes = selectionParam.perfAll ? [] : selectionParam.perfProcess; 169 let threads = selectionParam.perfAll ? [] : selectionParam.perfThread; 170 let sql = ''; 171 if (cpus.length != 0 || processes.length != 0 || threads.length != 0) { 172 let arg1 = cpus.length > 0 ? `or s.cpu_id in (${cpus.join(',')}) ` : ''; 173 let arg2 = processes.length > 0 ? `or thread.process_id in (${processes.join(',')}) ` : ''; 174 let arg3 = threads.length > 0 ? `or s.thread_id in (${threads.join(',')})` : ''; 175 let arg = `${arg1}${arg2}${arg3}`.substring(3); 176 sql = ` and (${arg})`; 177 } 178 this.queryData( 179 this.currentEventId, 180 'perf-queryCallchainsGroupSample', 181 ` 182select p.callchain_id as sampleId, p.thread_state as threadState, p.thread_id as tid, p.count, p.process_id as pid,p.event_count as eventCount 183from (select callchain_id, s.thread_id, thread_state, process_id, count(callchain_id) as count,event_count 184 from perf_sample s, 185 trace_range t 186 left join perf_thread thread on s.thread_id = thread.thread_id 187 where timestamp_trace between $startTime + t.start_ts and $endTime + t.start_ts 188 and callchain_id != -1 189 and s.thread_id != 0 190 ${sql} 191 group by callchain_id, s.thread_id, thread_state, process_id) p`, 192 { 193 $startTime: selectionParam.leftNs, 194 $endTime: selectionParam.rightNs, 195 $sql: sql, 196 } 197 ); 198 } 199 200 clearAll() { 201 this.filesData = {}; 202 this.samplesData = {}; 203 this.threadData = {}; 204 this.callChainData = {}; 205 this.splitMapData = {}; 206 this.currentTreeMapData = {}; 207 this.currentTreeList = []; 208 this.searchValue = ''; 209 this.dataSource = []; 210 this.allProcess = []; 211 this.dataCache.clearPerf(); 212 } 213 214 initPerfCallChainBottomUp(callChains: PerfCallChain[]) { 215 callChains.forEach((callChain, index) => { 216 if (this.threadData[callChain.tid] == undefined) { 217 return; 218 } 219 this.setPerfCallChainFrameName(callChain); 220 this.addPerfGroupData(callChain); 221 if (index + 1 < callChains.length && callChains[index + 1].sampleId == callChain.sampleId) { 222 PerfCallChain.setPreviousNode(callChain, callChains[index + 1]); 223 } 224 if (callChains.length == index + 1 || callChains[index + 1].sampleId != callChain.sampleId) { 225 this.addProcessThreadStateData(callChain); 226 } 227 }); 228 } 229 230 initPerfCallChainTopDown(callChains: PerfCallChain[]) { 231 this.callChainData = {}; 232 callChains.forEach((callChain, index) => { 233 this.setPerfCallChainFrameName(callChain); 234 this.addPerfGroupData(callChain); 235 let callChainDatum = this.callChainData[callChain.sampleId]; 236 if (callChainDatum.length > 1) { 237 PerfCallChain.setNextNode(callChainDatum[callChainDatum.length - 2], callChainDatum[callChainDatum.length - 1]); 238 } 239 }); 240 } 241 242 setPerfCallChainFrameName(callChain: PerfCallChain) { 243 //设置调用栈的名称 244 callChain.canCharge = true; 245 if (callChain.symbolId == -1) { 246 if (this.filesData[callChain.fileId] && this.filesData[callChain.fileId].length > 0) { 247 callChain.fileName = this.filesData[callChain.fileId][0].fileName; 248 callChain.path = this.filesData[callChain.fileId][0].path; 249 } else { 250 callChain.fileName = 'unknown'; 251 } 252 } else { 253 if (this.filesData[callChain.fileId] && this.filesData[callChain.fileId].length > callChain.symbolId) { 254 callChain.fileName = this.filesData[callChain.fileId][callChain.symbolId].fileName; 255 callChain.path = this.filesData[callChain.fileId][callChain.symbolId].path; 256 } else { 257 callChain.fileName = 'unknown'; 258 } 259 } 260 } 261 262 addProcessThreadStateData(callChain: PerfCallChain) { 263 //当调用栈为调用的根节点时 264 this.addPerfCallData(callChain); 265 let threadCallChain = new PerfCallChain(); //新增的线程数据 266 threadCallChain.depth = 0; 267 PerfCallChain.merageCallChain(threadCallChain, callChain); 268 threadCallChain.canCharge = false; 269 threadCallChain.name = this.threadData[callChain.tid].threadName || 'Thread' + '(' + callChain.tid + ')'; 270 let threadStateCallChain = new PerfCallChain(); //新增的线程状态数据 271 PerfCallChain.merageCallChain(threadStateCallChain, callChain); 272 threadStateCallChain.name = callChain.threadState || 'Unknown State'; 273 threadStateCallChain.fileName = threadStateCallChain.name == '-' ? 'Unknown Thread State' : ''; 274 threadStateCallChain.canCharge = false; 275 this.addPerfGroupData(threadCallChain); 276 this.addPerfGroupData(threadStateCallChain); 277 PerfCallChain.setNextNode(threadCallChain, threadStateCallChain); 278 PerfCallChain.setNextNode(threadStateCallChain, callChain); 279 } 280 281 addPerfCallData(callChain: PerfCallChain) { 282 let perfCall = new PerfCall(); 283 perfCall.depth = this.callChainData[callChain.sampleId]?.length || 0; 284 perfCall.sampleId = callChain.sampleId; 285 perfCall.name = callChain.name; 286 this.dataCache.perfCallChainMap.set(callChain.sampleId, perfCall); 287 } 288 289 addPerfGroupData(callChain: PerfCallChain) { 290 this.callChainData[callChain.sampleId] = this.callChainData[callChain.sampleId] || []; 291 this.callChainData[callChain.sampleId].push(callChain); 292 } 293 294 getPerfCallChainsBySampleIds(sampleIds: string[], isTopDown: boolean) { 295 this.allProcess = this.groupNewTreeNoId(sampleIds, isTopDown); 296 return this.allProcess; 297 } 298 299 addOtherCallchainsData(countSample: PerfCountSample, list: any[]) { 300 let threadCallChain = new PerfCallChain(); //新增的线程数据 301 threadCallChain.tid = countSample.tid; 302 threadCallChain.canCharge = false; 303 threadCallChain.name = this.threadData[countSample.tid].threadName || 'Thead' + '(' + countSample.tid + ')'; 304 let threadStateCallChain = new PerfCallChain(); //新增的线程状态数据 305 threadStateCallChain.tid = countSample.tid; 306 threadStateCallChain.name = countSample.threadState || 'Unkown State'; 307 threadStateCallChain.fileName = threadStateCallChain.name == '-' ? 'Unkown Thead State' : ''; 308 threadStateCallChain.canCharge = false; 309 list.unshift(threadCallChain, threadStateCallChain); 310 } 311 312 freshPerfCallchains(samples: PerfCountSample[], isTopDown: boolean) { 313 this.currentTreeMapData = {}; 314 this.currentTreeList = []; 315 let totalCount = 0; 316 samples.forEach((sample) => { 317 totalCount += sample.count; 318 if (this.callChainData[sample.sampleId] && this.callChainData[sample.sampleId].length > 0) { 319 let callChains = [...this.callChainData[sample.sampleId]]; 320 this.addOtherCallchainsData(sample, callChains); 321 let topIndex = isTopDown ? 0 : callChains.length - 1; 322 if (callChains.length > 0) { 323 let root = this.currentTreeMapData[callChains[topIndex].name + sample.pid]; 324 if (root == undefined) { 325 root = new PerfCallChainMerageData(); 326 this.currentTreeMapData[callChains[topIndex].name + sample.pid] = root; 327 this.currentTreeList.push(root); 328 } 329 PerfCallChainMerageData.merageCallChainSample(root, callChains[topIndex], sample, false); 330 this.mergeChildrenByIndex(root, callChains, topIndex, sample, isTopDown); 331 } 332 } 333 }); 334 let rootMerageMap: any = {}; 335 // @ts-ignore 336 Object.values(this.currentTreeMapData).forEach((merageData: any) => { 337 if (rootMerageMap[merageData.pid] == undefined) { 338 let processMerageData = new PerfCallChainMerageData(); //新增进程的节点数据 339 processMerageData.canCharge = false; 340 processMerageData.symbolName = this.threadData[merageData.tid].processName || `Process(${merageData.pid})`; 341 processMerageData.symbol = processMerageData.symbolName; 342 processMerageData.tid = merageData.tid; 343 processMerageData.children.push(merageData); 344 processMerageData.initChildren.push(merageData); 345 processMerageData.dur = merageData.dur; 346 processMerageData.count = merageData.dur; 347 processMerageData.total = totalCount; 348 rootMerageMap[merageData.pid] = processMerageData; 349 } else { 350 rootMerageMap[merageData.pid].children.push(merageData); 351 rootMerageMap[merageData.pid].initChildren.push(merageData); 352 rootMerageMap[merageData.pid].dur += merageData.dur; 353 rootMerageMap[merageData.pid].count += merageData.dur; 354 rootMerageMap[merageData.pid].total = totalCount; 355 } 356 merageData.parentNode = rootMerageMap[merageData.pid]; //子节点添加父节点的引用 357 }); 358 let id = 0; 359 this.currentTreeList.forEach((node) => { 360 node.total = totalCount; 361 if (node.id == '') { 362 node.id = id + ''; 363 id++; 364 } 365 if (node.parentNode) { 366 if (node.parentNode.id == '') { 367 node.parentNode.id = id + ''; 368 id++; 369 } 370 node.parentId = node.parentNode.id; 371 } 372 }); 373 // @ts-ignore 374 this.allProcess = Object.values(rootMerageMap); 375 } 376 377 mergeChildrenByIndex( 378 currentNode: PerfCallChainMerageData, 379 callChainDataList: any[], 380 index: number, 381 sample: PerfCountSample, 382 isTopDown: boolean 383 ) { 384 isTopDown ? index++ : index--; 385 let isEnd = isTopDown ? callChainDataList.length == index + 1 : index == 0; 386 let node; 387 if ( 388 currentNode.initChildren.filter((child: PerfCallChainMerageData) => { 389 if (child.symbolName == callChainDataList[index]?.name) { 390 node = child; 391 PerfCallChainMerageData.merageCallChainSample(child, callChainDataList[index], sample, isEnd); 392 return true; 393 } 394 return false; 395 }).length == 0 396 ) { 397 node = new PerfCallChainMerageData(); 398 PerfCallChainMerageData.merageCallChainSample(node, callChainDataList[index], sample, isEnd); 399 currentNode.children.push(node); 400 currentNode.initChildren.push(node); 401 this.currentTreeList.push(node); 402 node.parentNode = currentNode; 403 } 404 if (node && !isEnd) this.mergeChildrenByIndex(node, callChainDataList, index, sample, isTopDown); 405 } 406 407 groupNewTreeNoId(sampleIds: string[], isTopDown: boolean): any[] { 408 this.currentTreeMapData = {}; 409 this.currentTreeList = []; 410 for (let i = 0; i < sampleIds.length; i++) { 411 let callChains = this.callChainData[sampleIds[i]]; 412 if (callChains == undefined) continue; 413 let topIndex = isTopDown ? 0 : callChains.length - 1; 414 if (callChains.length > 0) { 415 let root = this.currentTreeMapData[callChains[topIndex].name + callChains[topIndex].pid]; 416 if (root == undefined) { 417 root = new PerfCallChainMerageData(); 418 this.currentTreeMapData[callChains[topIndex].name + callChains[topIndex].pid] = root; 419 this.currentTreeList.push(root); 420 } 421 PerfCallChainMerageData.merageCallChain(root, callChains[topIndex], isTopDown); 422 this.merageChildren(root, callChains[topIndex], isTopDown); 423 } 424 } 425 let rootMerageMap: any = {}; 426 // @ts-ignore 427 Object.values(this.currentTreeMapData).forEach((merageData: any) => { 428 if (rootMerageMap[merageData.pid] == undefined) { 429 let processMerageData = new PerfCallChainMerageData(); //新增进程的节点数据 430 processMerageData.canCharge = false; 431 processMerageData.symbolName = this.threadData[merageData.tid].processName || `Process(${merageData.pid})`; 432 processMerageData.symbol = processMerageData.symbolName; 433 processMerageData.tid = merageData.tid; 434 processMerageData.children.push(merageData); 435 processMerageData.initChildren.push(merageData); 436 processMerageData.dur = merageData.dur; 437 processMerageData.count = merageData.dur; 438 processMerageData.total = sampleIds.length; 439 rootMerageMap[merageData.pid] = processMerageData; 440 } else { 441 rootMerageMap[merageData.pid].children.push(merageData); 442 rootMerageMap[merageData.pid].initChildren.push(merageData); 443 rootMerageMap[merageData.pid].dur += merageData.dur; 444 rootMerageMap[merageData.pid].count += merageData.dur; 445 rootMerageMap[merageData.pid].total = sampleIds.length; 446 } 447 merageData.parentNode = rootMerageMap[merageData.pid]; //子节点添加父节点的引用 448 }); 449 let id = 0; 450 this.currentTreeList.forEach((node) => { 451 node.total = sampleIds.length; 452 if (node.id == '') { 453 node.id = id + ''; 454 id++; 455 } 456 if (node.parentNode) { 457 if (node.parentNode.id == '') { 458 node.parentNode.id = id + ''; 459 id++; 460 } 461 node.parentId = node.parentNode.id; 462 } 463 }); 464 // @ts-ignore 465 return Object.values(rootMerageMap); 466 } 467 468 merageChildren(currentNode: PerfCallChainMerageData, callChain: any, isTopDown: boolean) { 469 let nextNodeKey = isTopDown ? 'nextNode' : 'previousNode'; 470 if (callChain[nextNodeKey] == undefined) return; 471 let node; 472 if ( 473 currentNode.initChildren.filter((child: PerfCallChainMerageData) => { 474 if (child.symbolName == callChain[nextNodeKey]?.name) { 475 node = child; 476 PerfCallChainMerageData.merageCallChain(child, callChain[nextNodeKey], isTopDown); 477 return true; 478 } 479 return false; 480 }).length == 0 481 ) { 482 node = new PerfCallChainMerageData(); 483 PerfCallChainMerageData.merageCallChain(node, callChain[nextNodeKey], isTopDown); 484 currentNode.children.push(node); 485 currentNode.initChildren.push(node); 486 this.currentTreeList.push(node); 487 node.parentNode = currentNode; 488 } 489 if (node) this.merageChildren(node, callChain[nextNodeKey], isTopDown); 490 } 491 492 //所有的操作都是针对整个树结构的 不区分特定的数据 493 splitPerfTree(samples: PerfCallChainMerageData[], name: string, isCharge: boolean, isSymbol: boolean) { 494 samples.forEach((process) => { 495 process.children = []; 496 if (isCharge) { 497 this.recursionPerfChargeInitTree(process, name, isSymbol); 498 } else { 499 this.recursionPerfPruneInitTree(process, name, isSymbol); 500 } 501 }); 502 this.resetAllNode(samples); 503 } 504 505 recursionPerfChargeInitTree(sample: PerfCallChainMerageData, symbolName: string, isSymbol: boolean) { 506 if ((isSymbol && sample.symbolName == symbolName) || (!isSymbol && sample.libName == symbolName)) { 507 (this.splitMapData[symbolName] = this.splitMapData[symbolName] || []).push(sample); 508 sample.isStore++; 509 } 510 if (sample.initChildren.length > 0) { 511 sample.initChildren.forEach((child) => { 512 this.recursionPerfChargeInitTree(child, symbolName, isSymbol); 513 }); 514 } 515 } 516 517 recursionPerfPruneInitTree(node: PerfCallChainMerageData, symbolName: string, isSymbol: boolean) { 518 if ((isSymbol && node.symbolName == symbolName) || (!isSymbol && node.libName == symbolName)) { 519 (this.splitMapData[symbolName] = this.splitMapData[symbolName] || []).push(node); 520 node.isStore++; 521 this.pruneChildren(node, symbolName); 522 } else if (node.initChildren.length > 0) { 523 node.initChildren.forEach((child) => { 524 this.recursionPerfPruneInitTree(child, symbolName, isSymbol); 525 }); 526 } 527 } 528 529 //symbol lib prune 530 recursionPruneTree(sample: PerfCallChainMerageData, symbolName: string, isSymbol: boolean) { 531 if ((isSymbol && sample.symbolName == symbolName) || (!isSymbol && sample.libName == symbolName)) { 532 sample.currentTreeParentNode && 533 sample.currentTreeParentNode.children.splice(sample.currentTreeParentNode.children.indexOf(sample), 1); 534 } else { 535 sample.children.forEach((child) => { 536 this.recursionPruneTree(child, symbolName, isSymbol); 537 }); 538 } 539 } 540 541 recursionChargeByRule( 542 sample: PerfCallChainMerageData, 543 ruleName: string, 544 rule: (node: PerfCallChainMerageData) => boolean 545 ) { 546 if (sample.initChildren.length > 0) { 547 sample.initChildren.forEach((child) => { 548 if (rule(child)) { 549 (this.splitMapData[ruleName] = this.splitMapData[ruleName] || []).push(child); 550 child.isStore++; 551 } 552 this.recursionChargeByRule(child, ruleName, rule); 553 }); 554 } 555 } 556 557 pruneChildren(sample: PerfCallChainMerageData, symbolName: string) { 558 if (sample.initChildren.length > 0) { 559 sample.initChildren.forEach((child) => { 560 child.isStore++; 561 (this.splitMapData[symbolName] = this.splitMapData[symbolName] || []).push(child); 562 this.pruneChildren(child, symbolName); 563 }); 564 } 565 } 566 567 hideSystemLibrary() { 568 this.allProcess.forEach((item) => { 569 item.children = []; 570 this.recursionChargeByRule(item, systemRuleName, (node) => { 571 return node.path.startsWith(systemRuleName); 572 }); 573 }); 574 } 575 576 hideNumMaxAndMin(startNum: number, endNum: string) { 577 let max = endNum == '∞' ? Number.POSITIVE_INFINITY : parseInt(endNum); 578 this.allProcess.forEach((item) => { 579 item.children = []; 580 this.recursionChargeByRule(item, numRuleName, (node) => { 581 return node.dur < startNum || node.dur > max; 582 }); 583 }); 584 } 585 586 clearSplitMapData(symbolName: string) { 587 delete this.splitMapData[symbolName]; 588 } 589 590 resetAllSymbol(symbols: string[]) { 591 symbols.forEach((symbol) => { 592 let list = this.splitMapData[symbol]; 593 if (list != undefined) { 594 list.forEach((item: any) => { 595 item.isStore--; 596 }); 597 } 598 }); 599 } 600 601 resetAllNode(sample: PerfCallChainMerageData[]) { 602 this.clearSearchNode(); 603 sample.forEach((process) => { 604 process.searchShow = true; 605 process.isSearch = false; 606 }); 607 this.resetNewAllNode(sample); 608 if (this.searchValue != '') { 609 this.findSearchNode(sample, this.searchValue, false); 610 this.resetNewAllNode(sample); 611 } 612 } 613 614 resetNewAllNode(sampleArray: PerfCallChainMerageData[]) { 615 sampleArray.forEach((process) => { 616 process.children = []; 617 }); 618 let values = this.currentTreeList.map((item: any) => { 619 item.children = []; 620 return item; 621 }); 622 values.forEach((sample: any) => { 623 if (sample.parentNode != undefined) { 624 if (sample.isStore == 0 && sample.searchShow) { 625 let parentNode = sample.parentNode; 626 while (parentNode != undefined && !(parentNode.isStore == 0 && parentNode.searchShow)) { 627 parentNode = parentNode.parentNode; 628 } 629 if (parentNode) { 630 sample.currentTreeParentNode = parentNode; 631 parentNode.children.push(sample); 632 } 633 } 634 } 635 }); 636 } 637 638 findSearchNode(sampleArray: PerfCallChainMerageData[], search: string, parentSearch: boolean) { 639 search = search.toLocaleLowerCase(); 640 sampleArray.forEach((sample) => { 641 if ((sample.symbol && sample.symbol.toLocaleLowerCase().includes(search)) || parentSearch) { 642 sample.searchShow = true; 643 let parentNode = sample.currentTreeParentNode; 644 sample.isSearch = sample.symbol != undefined && sample.symbol.toLocaleLowerCase().includes(search); 645 while (parentNode != undefined && !parentNode.searchShow) { 646 parentNode.searchShow = true; 647 parentNode = parentNode.currentTreeParentNode; 648 } 649 } else { 650 sample.searchShow = false; 651 sample.isSearch = false; 652 } 653 if (sample.children.length > 0) { 654 this.findSearchNode(sample.children, search, sample.searchShow); 655 } 656 }); 657 } 658 659 clearSearchNode() { 660 this.currentTreeList.forEach((sample) => { 661 sample.searchShow = true; 662 sample.isSearch = false; 663 }); 664 } 665 666 splitAllProcess(processArray: any[]) { 667 processArray.forEach((item: any) => { 668 this.allProcess.forEach((process) => { 669 if (item.select == '0') { 670 this.recursionPerfChargeInitTree(process, item.name, item.type == 'symbol'); 671 } else { 672 this.recursionPerfPruneInitTree(process, item.name, item.type == 'symbol'); 673 } 674 }); 675 if (!item.checked) { 676 this.resetAllSymbol([item.name]); 677 } 678 }); 679 } 680 681 resolvingAction(params: any[]) { 682 if (params.length > 0) { 683 for (let item of params) { 684 if (item.funcName && item.funcArgs) { 685 switch (item.funcName) { 686 case 'getCallChainsBySampleIds': 687 this.freshPerfCallchains(this.samplesData, item.funcArgs[0]); 688 break; 689 case 'getCurrentDataFromDb': 690 this.getCurrentDataFromDb(item.funcArgs[0]); 691 break; 692 case 'hideSystemLibrary': 693 this.hideSystemLibrary(); 694 break; 695 case 'hideNumMaxAndMin': 696 this.hideNumMaxAndMin(item.funcArgs[0], item.funcArgs[1]); 697 break; 698 case 'splitAllProcess': 699 this.splitAllProcess(item.funcArgs[0]); 700 break; 701 case 'resetAllNode': 702 this.resetAllNode(this.allProcess); 703 break; 704 case 'resotreAllNode': 705 this.resetAllSymbol(item.funcArgs[0]); 706 break; 707 case 'clearSplitMapData': 708 this.clearSplitMapData(item.funcArgs[0]); 709 break; 710 case 'splitTree': 711 this.splitPerfTree(this.allProcess, item.funcArgs[0], item.funcArgs[1], item.funcArgs[2]); 712 break; 713 case 'setSearchValue': 714 this.searchValue = item.funcArgs[0]; 715 break; 716 case 'setCombineCallChain': 717 this.isAnalysis = true; 718 break; 719 case 'combineAnalysisCallChain': 720 return this.combineCallChainForAnalysis(); 721 } 722 } 723 } 724 this.dataSource = this.allProcess.filter((process) => { 725 return process.children && process.children.length > 0; 726 }); 727 } 728 return this.dataSource; 729 } 730 731 combineCallChainForAnalysis() { 732 let sampleCallChainList = new Array<PerfAnalysisSample>(); 733 for (let sample of this.samplesData) { 734 let callChains = [...this.callChainData[sample.sampleId]]; 735 const lastCallChain = callChains[callChains.length - 1]; 736 const threadName = this.threadData[sample.tid].threadName || 'Thead'; 737 const processName = this.threadData[sample.pid].threadName || 'Process'; 738 let analysisSample = new PerfAnalysisSample( 739 threadName, 740 processName, 741 lastCallChain.fileId, 742 lastCallChain.fileName, 743 lastCallChain.symbolId, 744 lastCallChain.name 745 ); 746 analysisSample.tid = sample.tid; 747 analysisSample.pid = sample.pid; 748 analysisSample.count = sample.count; 749 analysisSample.threadState = sample.threadState; 750 analysisSample.eventCount = sample.eventCount; 751 sampleCallChainList.push(analysisSample); 752 } 753 if (this.isAnalysis) { 754 this.isAnalysis = false; 755 } 756 return sampleCallChainList; 757 } 758} 759 760export class PerfFile { 761 fileId: number = 0; 762 symbol: string = ''; 763 path: string = ''; 764 fileName: string = ''; 765 766 static setFileName(data: PerfFile) { 767 if (data.path) { 768 let number = data.path.lastIndexOf('/'); 769 if (number > 0) { 770 data.fileName = data.path.substring(number + 1); 771 return; 772 } 773 } 774 data.fileName = data.path; 775 } 776 777 setFileName() { 778 if (this.path) { 779 let number = this.path.lastIndexOf('/'); 780 if (number > 0) { 781 this.fileName = this.path.substring(number + 1); 782 return; 783 } 784 } 785 this.fileName = this.path; 786 } 787} 788 789export class PerfThread { 790 tid: number = 0; 791 pid: number = 0; 792 threadName: string = ''; 793 processName: string = ''; 794} 795 796export class PerfCallChain { 797 tid: number = 0; 798 pid: number = 0; 799 name: string = ''; 800 fileName: string = ''; 801 threadState: string = ''; 802 startNS: number = 0; 803 dur: number = 0; 804 sampleId: number = 0; 805 callChainId: number = 0; 806 vaddrInFile: number = 0; 807 fileId: number = 0; 808 symbolId: number = 0; 809 path: string = ''; 810 count: number = 0; 811 parentId: string = ''; //合并之后区分的id 812 id: string = ''; 813 topDownMerageId: string = ''; //top down合并使用的id 814 topDownMerageParentId: string = ''; //top down合并使用的id 815 bottomUpMerageId: string = ''; //bottom up合并使用的id 816 bottomUpMerageParentId: string = ''; //bottom up合并使用的id 817 depth: number = 0; 818 canCharge: boolean = true; 819 previousNode: PerfCallChain | undefined = undefined; //将list转换为一个链表结构 820 nextNode: PerfCallChain | undefined = undefined; 821 822 static setNextNode(currentNode: PerfCallChain, nextNode: PerfCallChain) { 823 currentNode.nextNode = nextNode; 824 nextNode.previousNode = currentNode; 825 } 826 827 static setPreviousNode(currentNode: PerfCallChain, prevNode: PerfCallChain) { 828 currentNode.previousNode = prevNode; 829 prevNode.nextNode = currentNode; 830 } 831 832 static merageCallChain(currentNode: PerfCallChain, callChain: PerfCallChain) { 833 currentNode.startNS = callChain.startNS; 834 currentNode.tid = callChain.tid; 835 currentNode.pid = callChain.pid; 836 currentNode.sampleId = callChain.sampleId; 837 currentNode.dur = callChain.dur; 838 currentNode.count = callChain.count; 839 } 840} 841 842export class PerfCallChainMerageData extends ChartStruct { 843 #parentNode: PerfCallChainMerageData | undefined = undefined; 844 #total = 0; 845 id: string = ''; 846 parentId: string = ''; 847 currentTreeParentNode: PerfCallChainMerageData | undefined = undefined; 848 symbolName: string = ''; 849 symbol: string = ''; 850 libName: string = ''; 851 path: string = ''; 852 self: string = '0s'; 853 weight: string = ''; 854 weightPercent: string = ''; 855 selfDur: number = 0; 856 dur: number = 0; 857 tid: number = 0; 858 pid: number = 0; 859 isStore = 0; 860 canCharge: boolean = true; 861 children: PerfCallChainMerageData[] = []; 862 initChildren: PerfCallChainMerageData[] = []; 863 type: number = 0; 864 vaddrInFile: number = 0; 865 isSelected: boolean = false; 866 searchShow: boolean = true; 867 isSearch: boolean = false; 868 set parentNode(data: PerfCallChainMerageData | undefined) { 869 this.currentTreeParentNode = data; 870 this.#parentNode = data; 871 } 872 873 get parentNode() { 874 return this.#parentNode; 875 } 876 877 set total(data: number) { 878 this.#total = data; 879 this.weight = `${timeMsFormat2p(this.dur * (DataCache.getInstance().perfCountToMs || 1))}`; 880 this.weightPercent = `${((this.dur / data) * 100).toFixed(1)}%`; 881 } 882 883 get total() { 884 return this.#total; 885 } 886 887 static merageCallChain(currentNode: PerfCallChainMerageData, callChain: PerfCallChain, isTopDown: boolean) { 888 if (currentNode.symbolName == '') { 889 currentNode.symbol = `${callChain.name} ${callChain.fileName ? `(${callChain.fileName})` : ''}`; 890 currentNode.symbolName = callChain.name; 891 currentNode.pid = callChain.pid; 892 currentNode.tid = callChain.tid; 893 currentNode.libName = callChain.fileName; 894 currentNode.vaddrInFile = callChain.vaddrInFile; 895 currentNode.addr = `${'0x'}${callChain.vaddrInFile.toString(16)}`; 896 currentNode.lib = currentNode.libName; 897 currentNode.canCharge = callChain.canCharge; 898 if (callChain.path) { 899 currentNode.path = callChain.path; 900 } 901 } 902 if (callChain[isTopDown ? 'nextNode' : 'previousNode'] == undefined) { 903 currentNode.selfDur += callChain.count; 904 currentNode.self = timeMsFormat2p(currentNode.selfDur * (DataCache.getInstance().perfCountToMs || 1)); 905 } 906 currentNode.dur += callChain.count; 907 currentNode.count += callChain.count; 908 } 909 910 static merageCallChainSample( 911 currentNode: PerfCallChainMerageData, 912 callChain: PerfCallChain, 913 sample: PerfCountSample, 914 isEnd: boolean 915 ) { 916 if (currentNode.symbolName == '') { 917 currentNode.symbol = `${callChain.name} ${callChain.fileName ? `(${callChain.fileName})` : ''}`; 918 currentNode.symbolName = callChain.name; 919 currentNode.pid = sample.pid; 920 currentNode.tid = sample.tid; 921 currentNode.libName = callChain.fileName; 922 currentNode.vaddrInFile = callChain.vaddrInFile; 923 currentNode.lib = callChain.fileName; 924 currentNode.addr = `${'0x'}${callChain.vaddrInFile.toString(16)}`; 925 currentNode.canCharge = callChain.canCharge; 926 if (callChain.path) { 927 currentNode.path = callChain.path; 928 } 929 } 930 if (isEnd) { 931 currentNode.selfDur += sample.count; 932 currentNode.self = timeMsFormat2p(currentNode.selfDur * (DataCache.getInstance().perfCountToMs || 1)); 933 } 934 currentNode.dur += sample.count; 935 currentNode.count += sample.count; 936 } 937} 938 939export class PerfCountSample { 940 sampleId: number = 0; 941 tid: number = 0; 942 count: number = 0; 943 threadState: string = ''; 944 pid: number = 0; 945 eventCount: number = 0; 946} 947 948export class PerfStack { 949 symbol: string = ''; 950 path: string = ''; 951 fileId: number = 0; 952 type: number = 0; 953 vaddrInFile: number = 0; 954} 955 956export class PerfCmdLine { 957 report_value: string = ''; 958} 959 960 961class PerfAnalysisSample extends PerfCountSample { 962 threadName: string; 963 processName: string; 964 libId: number; 965 libName: string; 966 symbolId: number; 967 symbolName: string; 968 969 constructor( 970 threadName: string, 971 processName: string, 972 libId: number, 973 libName: string, 974 symbolId: number, 975 symbolName: string 976 ) { 977 super(); 978 this.threadName = threadName; 979 this.processName = processName; 980 this.libId = libId; 981 this.libName = libName; 982 this.symbolId = symbolId; 983 this.symbolName = symbolName; 984 } 985} 986 987export function timeMsFormat2p(ns: number) { 988 let currentNs = ns; 989 let hour1 = 3600_000; 990 let minute1 = 60_000; 991 let second1 = 1_000; // 1 second 992 let res = ''; 993 if (currentNs >= hour1) { 994 res += Math.floor(currentNs / hour1).toFixed(2) + 'h'; 995 return res; 996 } 997 if (currentNs >= minute1) { 998 res += Math.floor(currentNs / minute1).toFixed(2) + 'min'; 999 return res; 1000 } 1001 if (currentNs >= second1) { 1002 res += Math.floor(currentNs / second1).toFixed(2) + 's'; 1003 return res; 1004 } 1005 if (currentNs > 0) { 1006 res += currentNs.toFixed(2) + 'ms'; 1007 return res; 1008 } 1009 if (res == '') { 1010 res = '0s'; 1011 } 1012 return res; 1013} 1014