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