• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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