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