• 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,
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