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