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