• 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
16
17export class ChartStruct {
18  depth: number = 0;
19  symbol: string = '';
20  lib: string = '';
21  path: string = '';
22  addr: string = '';
23  size: number = 0;
24  count: number = 0;
25  eventCount: number = 0;
26  eventPercent: string = '';
27  dur: number = 0;
28  parent: ChartStruct | undefined;
29  children: Array<ChartStruct> = [];
30  isSearch: boolean = false;
31  tsArray: Array<number> = []; // 每个绘制的函数由哪些时间点的样本组成
32  countArray: Array<number> = []; // native hook统计模式下一个时间点有多次分配
33  durArray: Array<number> = [];
34  isThread: boolean = false;
35  isProcess: boolean = false;
36}
37
38export class Msg {
39  tag: string = '';
40  index: number = 0;
41  isSending: boolean = false;
42  data: Array<unknown> = [];
43}
44
45export class HiPerfSymbol {
46  id: number = 0;
47  startTime: number = 0;
48  eventCount: number = 0;
49  endTime: number = 0;
50  totalTime: number = 0;
51  fileId: number = 0;
52  symbolId: number = 0;
53  cpu_id: number = 0;
54  depth: number = 0;
55  children?: Array<HiPerfSymbol>;
56  callchain_id: number = 0;
57  thread_id: number = 0;
58  name: string = '';
59
60  public clone(): HiPerfSymbol {
61    const cloneSymbol = new HiPerfSymbol();
62    cloneSymbol.children = [];
63    cloneSymbol.depth = this.depth;
64    return cloneSymbol;
65  }
66}
67
68export class MerageBean extends ChartStruct {
69  #parentNode: MerageBean | undefined = undefined;
70  #total = 0;
71  parent: MerageBean | undefined = undefined;
72  id: string = '';
73  parentId: string = '';
74  self?: string = '0s';
75  weight?: string;
76  weightPercent?: string;
77  selfDur: number = 0;
78  dur: number = 0;
79  pid: number = 0;
80  canCharge: boolean = true;
81  isStore = 0;
82  isSelected: boolean = false;
83  searchShow: boolean = true;
84  children: MerageBean[] = [];
85  initChildren: MerageBean[] = [];
86  type: number = 0;
87  set parentNode(data: MerageBean | undefined) {
88    this.parent = data;
89    this.#parentNode = data;
90  }
91
92  get parentNode(): MerageBean | undefined {
93    return this.#parentNode;
94  }
95
96  set total(data: number) {
97    this.#total = data;
98    this.weight = `${getProbablyTime(this.dur)}`;
99    this.weightPercent = `${((this.dur / data) * 100).toFixed(1)}%`;
100  }
101
102  get total(): number {
103    return this.#total;
104  }
105}
106
107class MerageBeanDataSplit {
108  systmeRuleName = '/system/';
109  numRuleName = '/max/min/';
110
111  //所有的操作都是针对整个树结构的, 不区分特定的数据
112  splitTree(
113    splitMapData: unknown,
114    data: MerageBean[],
115    name: string,
116    isCharge: boolean,
117    isSymbol: boolean,
118    currentTreeList: ChartStruct[],
119    searchValue: string
120  ): void {
121    data.forEach((process) => {
122      process.children = [];
123      if (isCharge) {
124        this.recursionChargeInitTree(splitMapData, process, name, isSymbol);
125      } else {
126        this.recursionPruneInitTree(splitMapData, process, name, isSymbol);
127      }
128    });
129    this.resetAllNode(data, currentTreeList, searchValue);
130  }
131
132  recursionChargeInitTree(splitMapData: unknown, node: MerageBean, symbolName: string, isSymbol: boolean): void {
133    if ((isSymbol && node.symbol === symbolName) || (!isSymbol && node.lib === symbolName)) {
134      //@ts-ignore
135      (splitMapData[symbolName] = splitMapData[symbolName] || []).push(node);
136      node.isStore++;
137    }
138    if (node.initChildren.length > 0) {
139      node.initChildren.forEach((child) => {
140        this.recursionChargeInitTree(splitMapData, child, symbolName, isSymbol);
141      });
142    }
143  }
144
145  recursionPruneInitTree(splitMapData: unknown, node: MerageBean, symbolName: string, isSymbol: boolean): void {
146    if ((isSymbol && node.symbol === symbolName) || (!isSymbol && node.lib === symbolName)) {
147      //@ts-ignore
148      (splitMapData[symbolName] = splitMapData[symbolName] || []).push(node);
149      node.isStore++;
150      this.pruneChildren(splitMapData, node, symbolName);
151    } else if (node.initChildren.length > 0) {
152      node.initChildren.forEach((child) => {
153        this.recursionPruneInitTree(splitMapData, child, symbolName, isSymbol);
154      });
155    }
156  }
157
158  //symbol lib prune
159  recursionPruneTree(node: MerageBean, symbolName: string, isSymbol: boolean): void {
160    if ((isSymbol && node.symbol === symbolName) || (!isSymbol && node.lib === symbolName)) {
161      node.parent && node.parent.children.splice(node.parent.children.indexOf(node), 1);
162    } else {
163      node.children.forEach((child) => {
164        this.recursionPruneTree(child, symbolName, isSymbol);
165      });
166    }
167  }
168
169  recursionChargeByRule(
170    splitMapData: unknown,
171    node: MerageBean,
172    ruleName: string,
173    rule: (node: MerageBean) => boolean
174  ): void {
175    if (node.initChildren.length > 0) {
176      node.initChildren.forEach((child) => {
177        if (rule(child)) {
178          //@ts-ignore
179          (splitMapData[ruleName] = splitMapData[ruleName] || []).push(child);
180          child.isStore++;
181        }
182        this.recursionChargeByRule(splitMapData, child, ruleName, rule);
183      });
184    }
185  }
186
187  pruneChildren(splitMapData: unknown, node: MerageBean, symbolName: string): void {
188    if (node.initChildren.length > 0) {
189      node.initChildren.forEach((child) => {
190        child.isStore++;
191        //@ts-ignore
192        (splitMapData[symbolName] = splitMapData[symbolName] || []).push(child);
193        this.pruneChildren(splitMapData, child, symbolName);
194      });
195    }
196  }
197
198  hideSystemLibrary(allProcess: MerageBean[], splitMapData: unknown): void {
199    allProcess.forEach((item) => {
200      item.children = [];
201      this.recursionChargeByRule(splitMapData, item, this.systmeRuleName, (node) => {
202        return node.path.startsWith(this.systmeRuleName);
203      });
204    });
205  }
206
207  hideNumMaxAndMin(allProcess: MerageBean[], splitMapData: unknown, startNum: number, endNum: string): void {
208    let max = endNum === '∞' ? Number.POSITIVE_INFINITY : parseInt(endNum);
209    allProcess.forEach((item) => {
210      item.children = [];
211      this.recursionChargeByRule(splitMapData, item, this.numRuleName, (node) => {
212        return node.count < startNum || node.count > max;
213      });
214    });
215  }
216
217  resotreAllNode(splitMapData: unknown, symbols: string[]): void {
218    symbols.forEach((symbol) => {
219      //@ts-ignore
220      let list = splitMapData[symbol];
221      if (list !== undefined) {
222        list.forEach((item: unknown) => {
223          //@ts-ignore
224          item.isStore--;
225        });
226      }
227    });
228  }
229
230  resetAllNode(data: MerageBean[], currentTreeList: ChartStruct[], searchValue: string): void {
231    this.clearSearchNode(currentTreeList);
232    data.forEach((process) => {
233      process.searchShow = true;
234      process.isSearch = false;
235    });
236    this.resetNewAllNode(data, currentTreeList);
237    if (searchValue !== '') {
238      this.findSearchNode(data, searchValue, false);
239      this.resetNewAllNode(data, currentTreeList);
240    }
241  }
242
243  resetNewAllNode(data: MerageBean[], currentTreeList: ChartStruct[]): void {
244    data.forEach((process) => {
245      process.children = [];
246    });
247    let values = currentTreeList.map((item: ChartStruct) => {
248      item.children = [];
249      return item;
250    });
251    values.forEach((item: unknown) => {
252      //@ts-ignore
253      if (item.parentNode !== undefined) {
254        //@ts-ignore
255        if (item.isStore === 0 && item.searchShow) {
256          //@ts-ignore
257          let parentNode = item.parentNode;
258          while (parentNode !== undefined && !(parentNode.isStore === 0 && parentNode.searchShow)) {
259            parentNode = parentNode.parentNode;
260          }
261          if (parentNode) {
262            //@ts-ignore
263            item.currentTreeParentNode = parentNode;
264            parentNode.children.push(item);
265          }
266        }
267      }
268    });
269  }
270
271  findSearchNode(data: MerageBean[], search: string, parentSearch: boolean): void {
272    search = search.toLocaleLowerCase();
273    data.forEach((item) => {
274      if ((item.symbol && item.symbol.toLocaleLowerCase().includes(search)) || parentSearch) {
275        item.searchShow = true;
276        item.isSearch = item.symbol !== undefined && item.symbol.toLocaleLowerCase().includes(search);
277        let parentNode = item.parent;
278        while (parentNode && !parentNode.searchShow) {
279          parentNode.searchShow = true;
280          parentNode = parentNode.parent;
281        }
282      } else {
283        item.searchShow = false;
284        item.isSearch = false;
285      }
286      if (item.children.length > 0) {
287        this.findSearchNode(item.children, search, item.searchShow);
288      }
289    });
290  }
291
292  clearSearchNode(currentTreeList: ChartStruct[]): void {
293    currentTreeList.forEach((node) => {
294      //@ts-ignore
295      node.searchShow = true;
296      node.isSearch = false;
297    });
298  }
299
300  splitAllProcess(allProcess: unknown[], splitMapData: unknown, list: unknown): void {
301    //@ts-ignore
302    list.forEach((item: unknown) => {
303      allProcess.forEach((process) => {
304        //@ts-ignore
305        if (item.select === '0') {
306          //@ts-ignore
307          this.recursionChargeInitTree(splitMapData, process, item.name, item.type === 'symbol');
308        } else {
309          //@ts-ignore
310          this.recursionPruneInitTree(splitMapData, process, item.name, item.type === 'symbol');
311        }
312      });
313      //@ts-ignore
314      if (!item.checked) {
315        //@ts-ignore
316        this.resotreAllNode(splitMapData, [item.name]);
317      }
318    });
319  }
320}
321
322export let merageBeanDataSplit = new MerageBeanDataSplit();
323
324export abstract class LogicHandler {
325  abstract handle(data: unknown): void;
326  queryData(eventId: string, queryName: string, sql: string, args: unknown): void {
327    self.postMessage({
328      id: eventId,
329      type: queryName,
330      isQuery: true,
331      args: args,
332      sql: sql,
333    });
334  }
335
336  abstract clearAll(): void;
337}
338
339let dec = new TextDecoder();
340
341export let setFileName = (path: string): string => {
342  let fileName = '';
343  if (path) {
344    let number = path.lastIndexOf('/');
345    if (number > 0) {
346      fileName = path.substring(number + 1);
347      return fileName;
348    }
349  }
350  return path;
351};
352
353let pagination = (page: number, pageSize: number, source: Array<unknown>): unknown[] => {
354  let offset = (page - 1) * pageSize;
355  return offset + pageSize >= source.length
356    ? source.slice(offset, source.length)
357    : source.slice(offset, offset + pageSize);
358};
359
360const PAGE_SIZE: number = 50_0000;
361export let postMessage = (id: unknown, action: string, results: Array<unknown>, pageSize: number = PAGE_SIZE): void => {
362  if (results.length > pageSize) {
363    let pageCount = Math.ceil(results.length / pageSize);
364    for (let i = 1; i <= pageCount; i++) {
365      let tag = 'start';
366      if (i === 1) {
367        tag = 'start';
368      } else if (i === pageCount) {
369        tag = 'end';
370      } else {
371        tag = 'sending';
372      }
373      let msg = new Msg();
374      msg.tag = tag;
375      msg.index = i;
376      msg.isSending = tag !== 'end';
377      msg.data = pagination(i, PAGE_SIZE, results);
378      self.postMessage({
379        id: id,
380        action: action,
381        isSending: msg.tag !== 'end',
382        results: msg,
383      });
384    }
385    results.length = 0;
386  } else {
387    let msg = new Msg();
388    msg.tag = 'end';
389    msg.index = 0;
390    msg.isSending = false;
391    msg.data = results;
392    self.postMessage({ id: id, action: action, results: msg });
393    results.length = 0;
394  }
395};
396export let translateJsonString = (str: string): string => {
397  return str //   .padding
398    .replace(/[\t|\r|\n]/g, '')
399    .replace(/\\/g, '\\\\');
400};
401
402export let convertJSON = (arrBuf: ArrayBuffer | Array<unknown>): unknown[] => {
403  if (arrBuf instanceof ArrayBuffer) {
404    let string = dec.decode(arrBuf);
405    let jsonArray = [];
406    string = string.substring(string.indexOf('\n') + 1);
407    if (!string) {
408    } else {
409      let parse;
410      let tansStr = translateJsonString(string);
411      try {
412        parse = JSON.parse(translateJsonString(string));
413      } catch {
414        tansStr = tansStr.replace(/[^\x20-\x7E]/g, '?'); //匹配乱码字符,将其转换为?
415        parse = JSON.parse(tansStr);
416      }
417      let columns = parse.columns;
418      let values = parse.values;
419      for (let i = 0; i < values.length; i++) {
420        let object = {};
421        for (let j = 0; j < columns.length; j++) {
422          //@ts-ignore
423          object[columns[j]] = values[i][j];
424        }
425        jsonArray.push(object);
426      }
427    }
428    return jsonArray;
429  } else {
430    return arrBuf;
431  }
432};
433
434export let getByteWithUnit = (bytes: number): string => {
435  if (bytes < 0) {
436    return '-' + getByteWithUnit(Math.abs(bytes));
437  }
438  let currentBytes = bytes;
439  let kb1 = 1 << 10;
440  let mb = (1 << 10) << 10;
441  let gb = ((1 << 10) << 10) << 10; // 1 gb
442  let res = '';
443  if (currentBytes > gb) {
444    res += (currentBytes / gb).toFixed(2) + ' GB';
445  } else if (currentBytes > mb) {
446    res += (currentBytes / mb).toFixed(2) + ' MB';
447  } else if (currentBytes > kb1) {
448    res += (currentBytes / kb1).toFixed(2) + ' KB';
449  } else {
450    res += Math.round(currentBytes) + ' byte';
451  }
452  return res;
453};
454
455export let getTimeString = (ns: number): string => {
456  let currentNs = ns;
457  let hour1 = 3600_000_000_000;
458  let minute1 = 60_000_000_000;
459  let second1 = 1_000_000_000;
460  let millisecond1 = 1_000_000;
461  let microsecond1 = 1_000;
462  let res = '';
463  if (currentNs >= hour1) {
464    res += Math.floor(currentNs / hour1) + 'h ';
465    currentNs = currentNs - Math.floor(currentNs / hour1) * hour1;
466  }
467  if (currentNs >= minute1) {
468    res += Math.floor(currentNs / minute1) + 'm ';
469    currentNs = currentNs - Math.floor(ns / minute1) * minute1;
470  }
471  if (currentNs >= second1) {
472    res += Math.floor(currentNs / second1) + 's ';
473    currentNs = currentNs - Math.floor(currentNs / second1) * second1;
474  }
475  if (currentNs >= millisecond1) {
476    res += Math.floor(currentNs / millisecond1) + 'ms ';
477    currentNs = currentNs - Math.floor(currentNs / millisecond1) * millisecond1;
478  }
479  if (currentNs >= microsecond1) {
480    res += Math.floor(currentNs / microsecond1) + 'μs ';
481    currentNs = currentNs - Math.floor(currentNs / microsecond1) * microsecond1;
482  }
483  if (currentNs > 0) {
484    res += currentNs + 'ns ';
485  }
486  if (res === '') {
487    res = ns + '';
488  }
489  return res;
490};
491
492export function getProbablyTime(ns: number): string {
493  let currentNs = ns;
494  let hour1 = 3600_000_000_000;
495  let minute1 = 60_000_000_000;
496  let second1 = 1_000_000_000;
497  let millisecond1 = 1_000_000;
498  let microsecond1 = 1_000;
499  let res = '';
500  if (currentNs >= hour1) {
501    res += (currentNs / hour1).toFixed(2) + 'h ';
502  } else if (currentNs >= minute1) {
503    res += (currentNs / minute1).toFixed(2) + 'm ';
504  } else if (currentNs >= second1) {
505    res += (currentNs / second1).toFixed(2) + 's ';
506  } else if (currentNs >= millisecond1) {
507    res += (currentNs / millisecond1).toFixed(2) + 'ms ';
508  } else if (currentNs >= microsecond1) {
509    res += (currentNs / microsecond1).toFixed(2) + 'μs ';
510  } else if (currentNs > 0) {
511    res += currentNs.toFixed(0) + 'ns ';
512  } else if (res === '') {
513    res = ns + '';
514  }
515  return res;
516}
517
518export function getThreadUsageProbablyTime(ns: number): string {
519  let currentNs = ns;
520  let microsecond1 = 1_000;
521  let res = '';
522  if (currentNs > 0) {
523    res += (currentNs / microsecond1).toFixed(2);
524  } else if (res === '') {
525    res = ns + '';
526  }
527  return res;
528}
529
530export function timeMsFormat2p(timeNs: number): string {
531  let currentNs = timeNs;
532  let oneHour = 3600_000;
533  let oneMinute1 = 60_000;
534  let oneSecond = 1_000; // 1 second
535  let commonResult = '';
536  if (currentNs >= oneHour) {
537    commonResult += Math.floor(currentNs / oneHour).toFixed(2) + 'h';
538    return commonResult;
539  }
540  if (currentNs >= oneMinute1) {
541    commonResult += Math.floor(currentNs / oneMinute1).toFixed(2) + 'min';
542    return commonResult;
543  }
544  if (currentNs >= oneSecond) {
545    commonResult += Math.floor(currentNs / oneSecond).toFixed(2) + 's';
546    return commonResult;
547  }
548  if (currentNs > 0) {
549    commonResult += currentNs.toFixed(2) + 'ms';
550    return commonResult;
551  }
552  if (commonResult === '') {
553    commonResult = '0s';
554  }
555  return commonResult;
556}
557
558export function formatRealDate(date: Date, fmt: string): string {
559  let obj = {
560    'M+': date.getMonth() + 1,
561    'd+': date.getDate(),
562    'h+': date.getHours(),
563    'm+': date.getMinutes(),
564    's+': date.getSeconds(),
565    'q+': Math.floor((date.getMonth() + 3) / 3),
566    S: date.getMilliseconds(),
567  };
568  if (/(y+)/.test(fmt)) {
569    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
570  }
571  for (let key in obj) {
572    if (new RegExp('(' + key + ')').test(fmt)) {
573      // @ts-ignore
574      fmt = fmt.replace(
575        RegExp.$1,
576        // @ts-ignore
577        RegExp.$1.length === 1 ? obj[key] : ('00' + obj[key]).substr(('' + obj[key]).length)
578      );
579    }
580  }
581  return fmt;
582}
583
584export function formatRealDateMs(timeNs: number): string {
585  return formatRealDate(new Date(timeNs / 1000000), 'MM-dd hh:mm:ss.S');
586}
587
588export class JsProfilerSymbol {
589  id: number = 0;
590  nameId: number = 0;
591  name: string = '';
592  scriptId: number = 0;
593  urlId: number = 0;
594  url: string = '';
595  line: number = 0;
596  column: number = 0;
597  hitCount: number = 0;
598  childrenString?: string;
599  childrenIds: Array<number> = [];
600  children?: Array<JsProfilerSymbol>;
601  parentId: number = 0;
602  depth: number = -1;
603  cpuProfilerData?: JsProfilerSymbol;
604
605  public clone(): JsProfilerSymbol {
606    const cloneSymbol = new JsProfilerSymbol();
607    cloneSymbol.name = this.name;
608    cloneSymbol.url = this.url;
609    cloneSymbol.hitCount = this.hitCount;
610    cloneSymbol.children = [];
611    cloneSymbol.childrenIds = [];
612    cloneSymbol.parentId = this.parentId;
613    cloneSymbol.depth = this.depth;
614    cloneSymbol.cpuProfilerData = this.cpuProfilerData;
615    return cloneSymbol;
616  }
617}
618
619export class HeapTreeDataBean {
620  MoudleName: string | undefined;
621  AllocationFunction: string | undefined;
622  symbolId: number = 0;
623  fileId: number = 0;
624  startTs: number = 0;
625  endTs: number = 0;
626  eventType: string | undefined;
627  depth: number = 0;
628  heapSize: number = 0;
629  eventId: number = 0;
630  addr: string = '';
631  callChinId: number = 0;
632}
633
634export class PerfCall {
635  sampleId: number = 0;
636  depth: number = 0;
637  name: string = '';
638}
639
640export class FileCallChain {
641  callChainId: number = 0;
642  depth: number = 0;
643  symbolsId: number = 0;
644  pathId: number = 0;
645  ip: string = '';
646  isThread: boolean = false;
647}
648
649export class DataCache {
650  public static instance: DataCache | undefined;
651  public dataDict = new Map<number, string>();
652  public eBpfCallChainsMap = new Map<number, Array<FileCallChain>>();
653  public nmFileDict = new Map<number, string>();
654  public nmHeapFrameMap = new Map<number, Array<HeapTreeDataBean>>();
655  public perfCountToMs = 1; // 1000 / freq
656  public perfCallChainMap: Map<number, PerfCall> = new Map<number, PerfCall>();
657  public jsCallChain: Array<JsProfilerSymbol> | undefined;
658  public jsSymbolMap = new Map<number, JsProfilerSymbol>();
659
660  public static getInstance(): DataCache {
661    if (!this.instance) {
662      this.instance = new DataCache();
663    }
664    return this.instance;
665  }
666
667  public clearAll(): void {
668    if (this.dataDict) {
669      this.dataDict.clear();
670    }
671    this.clearEBpf();
672    this.clearNM();
673    this.clearPerf();
674    this.clearJsCache();
675  }
676
677  public clearNM(): void {
678    this.nmFileDict.clear();
679    this.nmHeapFrameMap.clear();
680  }
681
682  public clearEBpf(): void {
683    this.eBpfCallChainsMap.clear();
684  }
685
686  public clearJsCache(): void {
687    if (this.jsCallChain) {
688      this.jsCallChain.length = 0;
689    }
690    this.jsSymbolMap.clear();
691  }
692
693  public clearPerf(): void {
694    this.perfCallChainMap.clear();
695  }
696}
697
698export class InitAnalysis {
699  public static instance: InitAnalysis | undefined;
700  public isInitAnalysis: boolean = true;
701  public static getInstance(): InitAnalysis {
702    if (!this.instance) {
703      this.instance = new InitAnalysis();
704    }
705    return this.instance;
706  }
707}
708
709interface perfAsyncList {
710  tid?: number;
711  pid?: number;
712  time?: number;
713  symbol?: string;
714  traceid?: string;
715  eventCount?: number;
716  sampleCount?: number;
717  jsFuncName?: string;
718  callerCallchainid?: number;
719  calleeCallchainid?: number;
720  asyncFuncName?: string;
721  eventType?: string;
722  children?: Array<perfAsyncList>;
723  eventTypeId?: number;
724  symbolName?: string;
725  callerCallStack?: Array<perfAsyncList>;
726  calleeCallStack?: Array<perfAsyncList>;
727  callStackList?: Array<perfAsyncList>;
728  parent?: perfAsyncList;
729  isProcess?: boolean;
730  isThread?: boolean;
731  depth?: number;
732  isSearch?: boolean;
733  isJsStack?: boolean;
734  lib?: string;
735  isChartSelectParent?: boolean;
736  isChartSelect?: boolean;
737  isDraw?: boolean;
738  drawDur?: number;
739  drawEventCount?: number;
740  drawCount?: number;
741  drawSize?: number;
742  searchEventCount?: number;
743  searchCount?: number;
744  searchDur?: number;
745  searchSize?: number;
746  size?: number;
747  count?: number;
748  dur?: number;
749  tsArray?: Array<number>;
750  isCharged?: boolean;
751  addr?: string;
752}
753
754export function dealAsyncData(
755  arr: Array<perfAsyncList>,
756  perfCallChain: object,
757  nmCallChain: Map<number, Array<{ addr: string, depth: number, eventId: number, fileId: number, symbolId: number }>>,
758  dataDict: Map<number, string>,
759  searchValue: string
760): Array<perfAsyncList> {
761  // 转换为小写字符
762  searchValue = searchValue.toLocaleLowerCase();
763  // 循环遍历每一条数据
764  for (let i = 0; i < arr.length; i++) {
765    let flag: boolean = false;
766    // 定义每条数据的调用栈与被调用栈数组
767    arr[i].calleeCallStack! = [];
768    arr[i].callerCallStack! = [];
769    // 从前端缓存的perfcallchain表与native_hook_frame表中拿到calleeId与callerId对应的数据
770    // @ts-ignore
771    let calleeCallChain = perfCallChain[arr[i].calleeCallchainid];
772    let callerCallChain = nmCallChain.get(arr[i].callerCallchainid!)!;
773    // 循环被调用栈数组,拿到该条采样数据对应的所有被调用栈信息
774    for (let j = 0; j < calleeCallChain.length; j++) {
775      let calleeStack: perfAsyncList = {};
776      // 拿到每一层被调用栈栈名
777      calleeStack.symbolName = dataDict.get(calleeCallChain[j].name)!;
778      // 判断该条采样数据的被调用栈链中是否包含用户筛选字段
779      if (calleeStack.symbolName.toLocaleLowerCase().indexOf(searchValue) !== -1) {
780        flag = true;
781      }
782      // 获取calleeCallchainid、depth、eventTypeId、lib、addr
783      calleeStack.calleeCallchainid = arr[i].calleeCallchainid!;
784      calleeStack.depth = calleeCallChain[j].depth;
785      calleeStack.eventTypeId = arr[i].eventTypeId!;
786      calleeStack.lib = calleeCallChain[j].fileName;
787      calleeStack.addr = `${'0x'}${calleeCallChain[j].vaddrInFile.toString(16)}`;
788      // 填充到该条数据的被调用栈数组中
789      arr[i].calleeCallStack!.push(calleeStack);
790    }
791    for (let z = 0; z < callerCallChain.length; z++) {
792      let callerStack: perfAsyncList = {};
793      // 拿到每一层被调用栈栈名
794      callerStack.symbolName = dataDict.get(callerCallChain[z].symbolId)!;
795      // 判断该条采样数据的调用栈链中是否包含用户筛选字段
796      if (callerStack.symbolName.toLocaleLowerCase().indexOf(searchValue) !== -1) {
797        flag = true;
798      }
799      // 获取callerCallchainid、depth、eventTypeId、lib、addr
800      callerStack.callerCallchainid = arr[i].callerCallchainid!;
801      callerStack.depth = callerCallChain[z].depth;
802      callerStack.eventTypeId = arr[i].eventTypeId!;
803      callerStack.addr = callerCallChain[z].addr;
804      callerStack.lib = setFileName(dataDict.get(callerCallChain[z].fileId)!);
805      // 填充到该条数据的调用栈数组中
806      arr[i].callerCallStack!.push(callerStack);
807    }
808    // 若存在用户筛选字段内容,数据进行保留。若不存在,则在返回给前端的数据中删除此条数据,减少前端处理的数据量
809    if (!flag) {
810      arr.splice(i, 1);
811      i--;
812    }
813  }
814  return arr;
815}