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 queryPerfFiles, 18 query, threadPool, 19} from "../../database/SqlLite.js"; 20import {PerfCall, PerfCallChain, PerfCallChainMerageData, PerfFile} from "../../bean/PerfProfile.js"; 21import {info} from "../../../log/Log.js"; 22import {SpHiPerf} from "./SpHiPerf.js"; 23import {procedurePool} from "../../database/Procedure.js"; 24 25export class PerfDataQuery { 26 27 filesData: any = {} 28 samplesData: any = {} 29 threadData: any = {} 30 callChainData: any = {} 31 splitMapData: any = {} 32 currentTreeMapData: any = {} 33 currentTreeList: any[] = [] 34 searchValue: string = "" 35 callChainMap: Map<number,PerfCall> = new Map<number, PerfCall>() 36 37 async initPerfCache(){ 38 await this.initPerfCallChainMap() 39 await this.initPerfFiles(); 40 } 41 42 async initPerfCallChainMap(){ 43 this.callChainMap.clear(); 44 } 45 46 async initPerfFiles() { 47 let files = await queryPerfFiles() 48 info("PerfFiles Data size is: ", files!.length) 49 files.forEach((file) => { 50 this.filesData[file.fileId] = this.filesData[file.fileId] || [] 51 PerfFile.setFileName(file) 52 this.filesData[file.fileId].push(file) 53 }) 54 let results = await new Promise<any>((resolve, reject) => { 55 procedurePool.submitWithName("logic0","perf-init",SpHiPerf.stringResult,undefined,(res:any)=>{ 56 resolve(res) 57 }) 58 }) 59 this.callChainMap = (results as any) 60 info("Perf Files Data initialized") 61 } 62 63 initCallChainBottomUp(callChains: PerfCallChain[]) { 64 callChains.forEach((callChain, index) => { 65 if (this.threadData[callChain.tid] == undefined) { 66 return 67 } 68 callChain.name = this.setCallChainName(callChain) 69 this.addGroupData(callChain) 70 if (index + 1 < callChains.length && callChains[index + 1].sampleId == callChain.sampleId) { 71 PerfCallChain.setPreviousNode(callChain, callChains[index + 1]) 72 } 73 if (callChains.length == index + 1 || callChains[index + 1].sampleId != callChain.sampleId) { 74 this.addProcessThreadStateData(callChain) 75 } 76 }) 77 } 78 79 setCallChainName(callChain: PerfCallChain): string {//设置调用栈的名称 80 callChain.pid = this.threadData[callChain.tid]?.pid; 81 callChain.canCharge = true; 82 if (callChain.symbolId == -1) { 83 if (this.filesData[callChain.fileId] && this.filesData[callChain.fileId].length > 0) { 84 callChain.fileName = this.filesData[callChain.fileId][0].fileName 85 callChain.path = this.filesData[callChain.fileId][0].path 86 return this.filesData[callChain.fileId][0].fileName + "+0x" + callChain.vaddrInFile 87 } else { 88 callChain.fileName = "unkown" 89 return "+0x" + callChain.vaddrInFile 90 } 91 } else { 92 if (this.filesData[callChain.fileId] && this.filesData[callChain.fileId].length > callChain.symbolId) { 93 callChain.fileName = this.filesData[callChain.fileId][callChain.symbolId].fileName 94 callChain.path = this.filesData[callChain.fileId][callChain.symbolId].path 95 return this.filesData[callChain.fileId][callChain.symbolId].symbol 96 } else { 97 callChain.fileName = "unkown" 98 return "+0x" + callChain.vaddrInFile 99 } 100 } 101 } 102 103 addProcessThreadStateData(callChain: PerfCallChain) {//当调用栈为调用的根节点时 104 let threadCallChain = new PerfCallChain()//新增的线程数据 105 threadCallChain.depth = 0 106 PerfCallChain.merageCallChain(threadCallChain, callChain) 107 threadCallChain.canCharge = false 108 threadCallChain.name = this.threadData[callChain.tid].threadName||"Thead" + "(" + callChain.tid + ")" 109 let threadStateCallChain = new PerfCallChain()//新增的线程状态数据 110 PerfCallChain.merageCallChain(threadStateCallChain, callChain) 111 threadStateCallChain.name = callChain.threadState || "Unkown State" 112 threadStateCallChain.fileName = threadStateCallChain.name == "-" ? "Unkown Thead State" : "" 113 threadStateCallChain.canCharge = false 114 this.addGroupData(threadStateCallChain) 115 this.addGroupData(threadCallChain) 116 PerfCallChain.setNextNode(threadCallChain, threadStateCallChain) 117 PerfCallChain.setNextNode(threadStateCallChain, callChain) 118 } 119 120 addGroupData(callChain: PerfCallChain) { 121 this.callChainData[callChain.sampleId] = this.callChainData[callChain.sampleId] || [] 122 this.callChainData[callChain.sampleId].push(callChain) 123 } 124 125 getCallChainsBySampleIds(sampleIds: string[], isTopDown: boolean) { 126 return this.groupNewTreeNoId(sampleIds, isTopDown) 127 } 128 129 130 groupNewTreeNoId(sampleIds: string[], isTopDown: boolean) { 131 this.currentTreeMapData = {} 132 this.currentTreeList = [] 133 for (let i = 0; i < sampleIds.length; i++) { 134 let callChains = this.callChainData[sampleIds[i]] 135 if (callChains == undefined) continue 136 let topIndex = isTopDown ? (callChains.length - 1) : 0; 137 if (callChains.length > 0) { 138 let root = this.currentTreeMapData[callChains[topIndex].name + callChains[topIndex].pid]; 139 if (root == undefined) { 140 root = new PerfCallChainMerageData(); 141 this.currentTreeMapData[callChains[topIndex].name + callChains[topIndex].pid] = root; 142 this.currentTreeList.push(root) 143 } 144 PerfCallChainMerageData.merageCallChain(root, callChains[topIndex], isTopDown); 145 this.merageChildren(root, callChains[topIndex], isTopDown); 146 } 147 } 148 let rootMerageMap: any = {} 149 Object.values(this.currentTreeMapData).forEach((merageData: any) => { 150 if (rootMerageMap[merageData.pid] == undefined) { 151 let processMerageData = new PerfCallChainMerageData()//新增进程的节点数据 152 processMerageData.canCharge = false 153 processMerageData.symbolName = this.threadData[merageData.tid].processName||`Process(${merageData.pid})` 154 processMerageData.symbol = processMerageData.symbolName 155 processMerageData.tid = merageData.tid 156 processMerageData.children.push(merageData) 157 processMerageData.initChildren.push(merageData) 158 processMerageData.dur = merageData.dur; 159 processMerageData.count = merageData.dur; 160 processMerageData.total = sampleIds.length; 161 rootMerageMap[merageData.pid] = processMerageData 162 } else { 163 rootMerageMap[merageData.pid].children.push(merageData) 164 rootMerageMap[merageData.pid].initChildren.push(merageData) 165 rootMerageMap[merageData.pid].dur += merageData.dur; 166 rootMerageMap[merageData.pid].count += merageData.dur; 167 rootMerageMap[merageData.pid].total = sampleIds.length; 168 } 169 merageData.parentNode = rootMerageMap[merageData.pid]//子节点添加父节点的引用 170 }) 171 let id = 0; 172 this.currentTreeList.forEach((node) => { 173 node.total = sampleIds.length; 174 if (node.id == "") { 175 node.id = id + "" 176 id++ 177 } 178 if(node.parentNode){ 179 if (node.parentNode.id == "") { 180 node.parentNode.id = id + "" 181 id++ 182 } 183 node.parentId = node.parentNode.id 184 } 185 }) 186 return Object.values(rootMerageMap) 187 } 188 189 merageChildren(currentNode: PerfCallChainMerageData, callChain: any, isTopDown: boolean) { 190 let nextNodeKey = isTopDown ? "nextNode" : "previousNode" 191 if (callChain[nextNodeKey] == undefined) return 192 let node; 193 if (currentNode.initChildren.filter((child: PerfCallChainMerageData) => { 194 if (child.symbolName == callChain[nextNodeKey]?.name) { 195 node = child; 196 PerfCallChainMerageData.merageCallChain(child, callChain[nextNodeKey], isTopDown) 197 return true; 198 } 199 return false; 200 }).length == 0) { 201 node = new PerfCallChainMerageData() 202 PerfCallChainMerageData.merageCallChain(node, callChain[nextNodeKey], isTopDown) 203 currentNode.children.push(node) 204 currentNode.initChildren.push(node) 205 this.currentTreeList.push(node) 206 node.parentNode = currentNode 207 } 208 if (node) this.merageChildren(node, callChain[nextNodeKey], isTopDown) 209 } 210 211 //所有的操作都是针对整个树结构的 不区分特定的数据 212 splitTree(data: PerfCallChainMerageData[], name: string, isCharge: boolean, isSymbol: boolean) { 213 data.forEach((process) => { 214 process.children = [] 215 if (isCharge) { 216 this.recursionChargeInitTree(process, name, isSymbol) 217 } else { 218 this.recursionPruneInitTree(process, name, isSymbol) 219 } 220 }) 221 this.resetAllNode(data) 222 } 223 224 recursionChargeInitTree(node: PerfCallChainMerageData, symbolName: string, isSymbol: boolean) { 225 if ((isSymbol && node.symbolName == symbolName) || (!isSymbol && node.libName == symbolName)) { 226 (this.splitMapData[symbolName] = this.splitMapData[symbolName] || []).push(node) 227 node.isStore++; 228 } 229 if (node.initChildren.length > 0) { 230 node.initChildren.forEach((child) => { 231 this.recursionChargeInitTree(child, symbolName, isSymbol) 232 }) 233 } 234 } 235 236 //symbol lib charge 237 recursionChargeTree(node: PerfCallChainMerageData, symbolName: string, isSymbol: boolean) { 238 if ((isSymbol && node.symbolName == symbolName) || (!isSymbol && node.libName == symbolName)) { 239 node.currentTreeParentNode && node.currentTreeParentNode.children.splice(node.currentTreeParentNode.children.indexOf(node), 1, ...node.children); 240 node.children.forEach((child) => { 241 child.currentTreeParentNode = node.currentTreeParentNode 242 }) 243 } 244 if (node.children.length > 0) { 245 node.children.forEach((child) => { 246 this.recursionChargeTree(child, symbolName, isSymbol) 247 }) 248 } 249 } 250 251 recursionPruneInitTree(node: PerfCallChainMerageData, symbolName: string, isSymbol: boolean) { 252 if (isSymbol && node.symbolName == symbolName || (!isSymbol && node.libName == symbolName)) { 253 (this.splitMapData[symbolName] = this.splitMapData[symbolName] || []).push(node) 254 node.isStore++; 255 this.pruneChildren(node, symbolName) 256 } else if (node.initChildren.length > 0) { 257 node.initChildren.forEach((child) => { 258 this.recursionPruneInitTree(child, symbolName, isSymbol) 259 }) 260 } 261 } 262 263 //symbol lib prune 264 recursionPruneTree(node: PerfCallChainMerageData, symbolName: string, isSymbol: boolean) { 265 if (isSymbol && node.symbolName == symbolName || (!isSymbol && node.libName == symbolName)) { 266 node.currentTreeParentNode && node.currentTreeParentNode.children.splice(node.currentTreeParentNode.children.indexOf(node), 1); 267 } else { 268 node.children.forEach((child) => { 269 this.recursionPruneTree(child, symbolName, isSymbol) 270 }) 271 } 272 } 273 274 recursionChargeByRule(node: PerfCallChainMerageData, ruleName: string, rule: (node: PerfCallChainMerageData) => boolean) { 275 if (node.initChildren.length > 0) { 276 node.initChildren.forEach((child) => { 277 if (rule(child)) { 278 (this.splitMapData[ruleName] = this.splitMapData[ruleName] || []).push(child) 279 child.isStore++; 280 } 281 this.recursionChargeByRule(child, ruleName, rule) 282 }) 283 } 284 } 285 286 pruneChildren(node: PerfCallChainMerageData, symbolName: string) { 287 if (node.initChildren.length > 0) { 288 node.initChildren.forEach((child) => { 289 child.isStore++; 290 (this.splitMapData[symbolName] = this.splitMapData[symbolName] || []).push(child); 291 this.pruneChildren(child, symbolName) 292 }) 293 } 294 } 295 296 clearSplitMapData(symbolName: string) { 297 delete this.splitMapData[symbolName] 298 } 299 300 resotreAllNode(symbols: string[]) { 301 symbols.forEach((symbol) => { 302 let list = this.splitMapData[symbol]; 303 if (list != undefined) { 304 list.forEach((item: any) => { 305 item.isStore-- 306 }) 307 } 308 }) 309 } 310 311 resetAllNode(data: PerfCallChainMerageData[]) { 312 this.clearSearchNode() 313 data.forEach((process) => { 314 process.searchShow = true 315 }) 316 this.resetNewAllNode(data) 317 if (this.searchValue != "") { 318 this.findSearchNode(data, this.searchValue, false) 319 this.resetNewAllNode(data) 320 } 321 } 322 323 resetNewAllNode(data: PerfCallChainMerageData[]) { 324 data.forEach((process) => { 325 process.children = [] 326 }) 327 let values = this.currentTreeList.map((item: any) => { 328 item.children = [] 329 return item 330 }) 331 values.forEach((item: any) => { 332 if (item.parentNode != undefined) { 333 if (item.isStore == 0 && item.searchShow) { 334 let parentNode = item.parentNode 335 while (parentNode != undefined && !(parentNode.isStore == 0 && parentNode.searchShow)) { 336 parentNode = parentNode.parentNode 337 } 338 if (parentNode) { 339 item.currentTreeParentNode = parentNode 340 parentNode.children.push(item) 341 } 342 } 343 } 344 }) 345 } 346 347 findSearchNode(data: PerfCallChainMerageData[], search: string, parentSearch: boolean) { 348 data.forEach((node) => { 349 if ((node.symbol&&node.symbol.includes(search)) || parentSearch) { 350 node.searchShow = true 351 let parentNode = node.currentTreeParentNode 352 while (parentNode != undefined && !parentNode.searchShow) { 353 parentNode.searchShow = true 354 parentNode = parentNode.currentTreeParentNode 355 } 356 } else { 357 node.searchShow = false 358 } 359 if (node.children.length > 0) { 360 this.findSearchNode(node.children, search, node.searchShow) 361 } 362 }) 363 } 364 365 clearSearchNode() { 366 this.currentTreeList.forEach((node) => { 367 node.searchShow = true 368 }) 369 } 370} 371 372export const perfDataQuery = new PerfDataQuery() 373