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