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