• 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 */
15import {
16  convertJSON,
17  DataCache,
18  getByteWithUnit,
19  HeapTreeDataBean,
20  LogicHandler,
21  MerageBean,
22  merageBeanDataSplit,
23  postMessage,
24  setFileName,
25} from './ProcedureLogicWorkerCommon.js';
26
27export class ProcedureLogicWorkerNativeMemory extends LogicHandler {
28  selectTotalSize = 0;
29  selectTotalCount = 0;
30  stackCount = 0;
31  NATIVE_MEMORY_DATA: Array<NativeEvent> = [];
32  currentTreeMapData: any = {};
33  currentTreeList: any[] = [];
34  queryAllCallchainsSamples: NativeHookStatistics[] = [];
35  currentSamples: NativeHookStatistics[] = [];
36  allThreads: NativeHookCallInfo[] = [];
37  splitMapData: any = {};
38  searchValue: string = '';
39  currentEventId: string = '';
40  chartComplete: Map<number, boolean> = new Map<number, boolean>();
41  realTimeDif: number = 0;
42  responseTypes: { key: number; value: string }[] = [];
43  totalNS: number = 0;
44  isAnalysis: boolean = false;
45  isStatistic: boolean = false;
46  boxRangeNativeHook: Array<NativeMemory> = [];
47  clearBoxSelectionData: boolean = false;
48  nativeMemoryArgs?: Map<string, any>
49  private dataCache = DataCache.getInstance();
50
51  handle(data: any): void {
52    this.currentEventId = data.id;
53    if (data && data.type) {
54      switch (data.type) {
55        case 'native-memory-init':
56          this.clearAll();
57          if (data.params.isRealtime) {
58            this.realTimeDif = data.params.realTimeDif;
59          }
60          this.dataCache.dataDict = data.params.dataDict;
61          this.initNMChartData();
62          break;
63        case 'native-memory-queryNMChartData':
64          this.NATIVE_MEMORY_DATA = convertJSON(data.params.list) || [];
65          this.initNMFrameData();
66          break;
67        case 'native-memory-queryNMFrameData':
68          let arr = convertJSON(data.params.list) || [];
69          this.initNMStack(arr);
70          arr = [];
71          self.postMessage({
72            id: data.id,
73            action: 'native-memory-init',
74            results: [],
75          });
76          break;
77        case 'native-memory-queryCallchainsSamples':
78          this.searchValue = '';
79          if (data.params.list) {
80            let callchainsSamples = convertJSON(data.params.list) || [];
81            this.queryAllCallchainsSamples = callchainsSamples;
82            this.freshCurrentCallchains(callchainsSamples, true);
83            // @ts-ignore
84            self.postMessage({
85              id: data.id,
86              action: data.action,
87              results: this.allThreads,
88            });
89          } else {
90            this.queryCallchainsSamples(
91              'native-memory-queryCallchainsSamples',
92              data.params.leftNs,
93              data.params.rightNs,
94              data.params.types
95            );
96          }
97          break;
98        case 'native-memory-queryStatisticCallchainsSamples':
99          this.searchValue = '';
100          if (data.params.list) {
101            let samples = convertJSON(data.params.list) || [];
102            this.queryAllCallchainsSamples = samples;
103            this.freshCurrentCallchains(samples, true);
104            // @ts-ignore
105            self.postMessage({
106              id: data.id,
107              action: data.action,
108              results: this.allThreads,
109            });
110          } else {
111            this.queryStatisticCallchainsSamples(
112              'native-memory-queryStatisticCallchainsSamples',
113              data.params.leftNs,
114              data.params.rightNs,
115              data.params.types
116            );
117          }
118          break;
119        case 'native-memory-queryAnalysis':
120          if (data.params.list) {
121            let samples = convertJSON(data.params.list) || [];
122            this.queryAllCallchainsSamples = samples;
123            self.postMessage({
124              id: data.id,
125              action: data.action,
126              results: this.combineStatisticAndCallChain(samples),
127            });
128          } else {
129            if (data.params.isStatistic) {
130              this.isStatistic = true;
131              this.queryStatisticCallchainsSamples(
132                'native-memory-queryAnalysis',
133                data.params.leftNs,
134                data.params.rightNs,
135                data.params.types
136              );
137            } else {
138              this.isStatistic = false;
139              this.queryCallchainsSamples(
140                'native-memory-queryAnalysis',
141                data.params.leftNs,
142                data.params.rightNs,
143                data.params.types
144              );
145            }
146          }
147          break;
148        case 'native-memory-queryNativeHookEvent':
149          if (data.params) {
150            if (data.params.list) {
151              this.boxRangeNativeHook = convertJSON(data.params.list);
152              if (this.nativeMemoryArgs?.get('refresh')) {
153                this.clearBoxSelectionData = this.boxRangeNativeHook.length > 100_0000;
154              }
155              this.supplementNativeHoodData();
156              postMessage(data.id, data.action, this.resolvingActionNativeMemory(this.nativeMemoryArgs!), 100_0000);
157              if (this.clearBoxSelectionData) {
158                this.boxRangeNativeHook = [];
159              }
160            } else if (data.params.get('refresh') || this.boxRangeNativeHook.length === 0) {
161              this.nativeMemoryArgs = data.params;
162              let leftNs = data.params.get('leftNs');
163              let rightNs = data.params.get('rightNs');
164              let types = data.params.get('types');
165              this.boxRangeNativeHook = [];
166              this.queryNativeHookEvent(leftNs, rightNs, types);
167            } else {
168              this.nativeMemoryArgs = data.params;
169              postMessage(data.id, data.action, this.resolvingActionNativeMemory(this.nativeMemoryArgs!), 100_0000);
170              if (this.clearBoxSelectionData) {
171                this.boxRangeNativeHook = [];
172              }
173            }
174          }
175          break;
176        case 'native-memory-action':
177          if (data.params) {
178            self.postMessage({
179              id: data.id,
180              action: data.action,
181              results: this.resolvingAction(data.params),
182            });
183          }
184          break;
185        case 'native-memory-chart-action':
186          if (data.params) {
187            postMessage(data.id, data.action, this.resolvingActionNativeMemoryChartData(data.params));
188          }
189          break;
190        case 'native-memory-calltree-action':
191          if (data.params) {
192            self.postMessage({
193              id: data.id,
194              action: data.action,
195              results: this.resolvingNMCallAction(data.params),
196            });
197          }
198          break;
199        case 'native-memory-init-responseType':
200          this.initResponseTypeList(data.params);
201          self.postMessage({
202            id: data.id,
203            action: data.action,
204            results: [],
205          });
206          break;
207        case 'native-memory-get-responseType':
208          self.postMessage({
209            id: data.id,
210            action: data.action,
211            results: this.responseTypes,
212          });
213          break;
214        case 'native-memory-queryNativeHookStatistic':
215          if (data.params.list) {
216            let arr = this.statisticDataHandler(convertJSON(data.params.list));
217            postMessage(data.id, data.action, this.handleNativeHookStatisticData(arr));
218          } else {
219            this.totalNS = data.params.totalNS;
220            this.queryNativeHookStatistic(data.params.type);
221          }
222          break;
223      }
224    }
225  }
226
227  initNMChartData() {
228    this.queryData(
229      this.currentEventId,
230      'native-memory-queryNMChartData',
231      `
232            select * from (
233                select
234                    h.start_ts - t.start_ts as startTime,
235                    h.heap_size as heapSize,
236                    (case when h.event_type = 'AllocEvent' then 0 else 1 end) as eventType
237                from native_hook h ,trace_range t
238                where h.start_ts between t.start_ts and t.end_ts
239                    and (h.event_type = 'AllocEvent' or h.event_type = 'MmapEvent')
240                union all
241                select
242                    h.end_ts - t.start_ts as startTime,
243                    h.heap_size as heapSize,
244                    (case when h.event_type = 'AllocEvent' then 2 else 3 end) as eventType
245                from native_hook h ,trace_range t
246                where
247                  h.start_ts between t.start_ts and t.end_ts
248                  and h.end_ts between t.start_ts and t.end_ts
249                  and (h.event_type = 'AllocEvent' or h.event_type = 'MmapEvent')
250            )
251            order by startTime;
252        `,
253      {}
254    );
255  }
256
257  queryNativeHookStatistic(type: number) {
258    let condition: string;
259    if (type === 0) {
260      condition = 'and type = 0';
261    } else if (type === 1) {
262      condition = 'and type > 0';
263    } else {
264      condition = '';
265    }
266    let sql = `
267select callchain_id callchainId,
268       ts - start_ts as ts,
269       apply_count applyCount,
270       apply_size applySize,
271       release_count releaseCount,
272       release_size releaseSize
273from native_hook_statistic,trace_range
274where ts between start_ts and end_ts ${condition};
275        `;
276    this.queryData(this.currentEventId, 'native-memory-queryNativeHookStatistic', sql, {});
277  }
278
279  queryNativeHookEvent(leftNs: number, rightNs: number, types: Array<string>) {
280    let condition = types.length === 1 ? `and A.event_type = ${types[0]}` : `and (A.event_type = 'AllocEvent' or A.event_type = 'MmapEvent')`;
281    let libId = this.nativeMemoryArgs?.get('filterResponseType');
282    let allocType = this.nativeMemoryArgs?.get('filterAllocType');
283    let eventType = this.nativeMemoryArgs?.get('filterEventType');
284    if (libId !== undefined && libId !== -1) {
285      condition = `${condition} and last_lib_id = ${libId}`;// filter lib
286    }
287    if (eventType === '1') {
288      condition = `${condition} and event_type = 'AllocEvent'`;
289    }
290    if (eventType === '2') {
291      condition = `${condition} and event_type = 'MmapEvent'`;
292    }
293    if (allocType === '1') {
294      condition = `${condition} and ((A.end_ts - B.start_ts) > ${rightNs} or A.end_ts is null)`;
295    }
296    if (allocType === '2') {
297      condition = `${condition} and (A.end_ts - B.start_ts) <= ${rightNs}`;
298    }
299    let sql = `
300    select
301      callchain_id as eventId,
302      event_type as eventType,
303      heap_size as heapSize,
304      ('0x' || addr) as addr,
305      (A.start_ts - B.start_ts) as startTs,
306      (A.end_ts - B.start_ts) as endTs,
307      tid as threadId,
308      sub_type_id as subTypeId,
309      ifnull(last_lib_id,0) as lastLibId
310    from
311      native_hook A,
312      trace_range B
313    left join
314      thread t
315    on
316      A.itid = t.id
317    where
318    A.start_ts - B.start_ts between ${leftNs} and ${rightNs} ${condition}
319    `;
320    this.queryData(this.currentEventId, 'native-memory-queryNativeHookEvent', sql, {});
321  }
322
323  supplementNativeHoodData() {
324    let len = this.boxRangeNativeHook.length;
325    for(let i = 0, j = len -1; i <= j; i++, j--){
326      this.fillNativeHook(this.boxRangeNativeHook[i], i);
327      if (i !== j) {
328        this.fillNativeHook(this.boxRangeNativeHook[j], j);
329      }
330    }
331  }
332
333  fillNativeHook(memory: NativeMemory, index: number) {
334    if (memory.subTypeId !== null && memory.subType === undefined) {
335      memory.subType = this.dataCache.dataDict.get(memory.subTypeId) || '-';
336    }
337    memory.index = index;
338    let arr = this.dataCache.nmHeapFrameMap.get(memory.eventId) || [];
339    let frame = Array.from(arr)
340      .reverse()
341      .find((item) => {
342        let fileName = this.dataCache.dataDict.get(item.fileId);
343        return !((fileName ?? '').includes('libc++') || (fileName ?? '').includes('musl'));
344      });
345    if (frame === null || frame === undefined) {
346      if (arr.length > 0) {
347        frame = arr[0];
348      }
349    }
350    if (frame !== null && frame !== undefined) {
351      memory.symbol = this.groupCutFilePath(frame.symbolId, this.dataCache.dataDict.get(frame.symbolId) || '');
352      memory.library = this.groupCutFilePath(
353        frame.fileId,
354        this.dataCache.dataDict.get(frame.fileId) || 'Unknown Path'
355      );
356    } else {
357      memory.symbol = '-';
358      memory.library = '-';
359    }
360  }
361
362  statisticDataHandler(arr: Array<any>) {
363    let callGroupMap: Map<number, any[]> = new Map<number, any[]>();
364    let obj = {};
365    for (let hook of arr) {
366      if ((obj as any)[hook.ts]) {
367        let data = (obj as any)[hook.ts] as any;
368        data.startTime = hook.ts;
369        data.dur = 0;
370        if (callGroupMap.has(hook.callchainId)) {
371          let calls = callGroupMap.get(hook.callchainId);
372          let last = calls![calls!.length - 1];
373          data.heapsize += hook.applySize - last.applySize - (hook.releaseSize - last.releaseSize);
374          data.density += hook.applyCount - last.applyCount - (hook.releaseCount - last.releaseCount);
375          calls!.push(hook);
376        } else {
377          data.heapsize += hook.applySize - hook.releaseSize;
378          data.density += hook.applyCount - hook.releaseCount;
379          callGroupMap.set(hook.callchainId, [hook]);
380        }
381      } else {
382        let data: any = {};
383        data.startTime = hook.ts;
384        data.dur = 0;
385        if (callGroupMap.has(hook.callchainId)) {
386          let calls = callGroupMap.get(hook.callchainId);
387          let last = calls![calls!.length - 1];
388          data.heapsize = hook.applySize - last.applySize - (hook.releaseSize - last.releaseSize);
389          data.density = hook.applyCount - last.applyCount - (hook.releaseCount - last.releaseCount);
390          calls!.push(hook);
391        } else {
392          data.heapsize = hook.applySize - hook.releaseSize;
393          data.density = hook.applyCount - hook.releaseCount;
394          callGroupMap.set(hook.callchainId, [hook]);
395        }
396        (obj as any)[hook.ts] = data;
397      }
398    }
399    return Object.values(obj) as {
400      startTime: number;
401      heapsize: number;
402      density: number;
403      dur: number;
404    }[];
405  }
406
407  handleNativeHookStatisticData(
408    arr: {
409      startTime: number;
410      heapsize: number;
411      density: number;
412      dur: number;
413    }[]
414  ) {
415    let maxSize = 0,
416      maxDensity = 0,
417      minSize = 0,
418      minDensity = 0;
419    for (let i = 0, len = arr.length; i < len; i++) {
420      if (i == len - 1) {
421        arr[i].dur = this.totalNS - arr[i].startTime;
422      } else {
423        arr[i + 1].heapsize = arr[i].heapsize + arr[i + 1].heapsize;
424        arr[i + 1].density = arr[i].density + arr[i + 1].density;
425        arr[i].dur = arr[i + 1].startTime - arr[i].startTime;
426      }
427      maxSize = Math.max(maxSize, arr[i].heapsize);
428      maxDensity = Math.max(maxDensity, arr[i].density);
429      minSize = Math.min(minSize, arr[i].heapsize);
430      minDensity = Math.min(minDensity, arr[i].density);
431    }
432    return arr.map((it) => {
433      (it as any).maxHeapSize = maxSize;
434      (it as any).maxDensity = maxDensity;
435      (it as any).minHeapSize = minSize;
436      (it as any).minDensity = minDensity;
437      return it;
438    });
439  }
440  initResponseTypeList(list: any[]) {
441    this.responseTypes = [
442      {
443        key: -1,
444        value: 'ALL',
445      },
446    ];
447    list.forEach((item) => {
448      if (item.lastLibId == null) {
449        this.responseTypes.push({
450          key: 0,
451          value: '-',
452        });
453      } else {
454        this.responseTypes.push({
455          key: item.lastLibId,
456          value: this.groupCutFilePath(item.lastLibId, item.value) || '-',
457        });
458      }
459    });
460  }
461  initNMFrameData() {
462    this.queryData(
463      this.currentEventId,
464      'native-memory-queryNMFrameData',
465      `
466            select h.symbol_id as symbolId, h.file_id as fileId, h.depth, h.callchain_id as eventId, h.vaddr as addr
467                    from native_hook_frame h
468        `,
469      {}
470    );
471  }
472  initNMStack(frameArr: Array<HeapTreeDataBean>) {
473    frameArr.map((frame) => {
474      let frameEventId = frame.eventId;
475      if (this.dataCache.nmHeapFrameMap.has(frameEventId)) {
476        this.dataCache.nmHeapFrameMap.get(frameEventId)!.push(frame);
477      } else {
478        this.dataCache.nmHeapFrameMap.set(frameEventId, [frame]);
479      }
480    });
481  }
482  resolvingAction(paramMap: Map<string, any>): Array<NativeHookCallInfo | NativeMemory | HeapStruct> {
483    let actionType = paramMap.get('actionType');
484    if (actionType === 'memory-stack') {
485      return this.resolvingActionNativeMemoryStack(paramMap);
486    } else if (actionType === 'native-memory-state-change') {
487      let startTs = paramMap.get('startTs');
488      let currentSelection = this.boxRangeNativeHook.filter((item) => {
489        return item.startTs === startTs;
490      });
491      if (currentSelection.length > 0) {
492        currentSelection[0].isSelected = true;
493      }
494      return [];
495    } else {
496      return [];
497    }
498  }
499  resolvingActionNativeMemoryChartData(paramMap: Map<string, any>): Array<HeapStruct> {
500    let nativeMemoryType: number = paramMap.get('nativeMemoryType') as number;
501    let totalNS: number = paramMap.get('totalNS') as number;
502    let arr: Array<HeapStruct> = [];
503    let nmMaxSize = 0;
504    let nmMaxDensity = 0;
505    let nmMinSize = 0;
506    let nmMinDensity = 0;
507    let nmTempSize = 0;
508    let nmTempDensity = 0;
509    let nmFilterLen = 0;
510    let nmFilterLevel = 0;
511    let putArr = (ne: NativeEvent, filterLevel: number, finish: boolean) => {
512      let nmHeapStruct = new HeapStruct();
513      nmHeapStruct.startTime = ne.startTime;
514      if (arr.length == 0) {
515        if (ne.eventType == 0 || ne.eventType == 1) {
516          nmHeapStruct.density = 1;
517          nmHeapStruct.heapsize = ne.heapSize;
518        } else {
519          nmHeapStruct.density = -1;
520          nmHeapStruct.heapsize = 0 - ne.heapSize;
521        }
522        nmMaxSize = nmHeapStruct.heapsize;
523        nmMaxDensity = nmHeapStruct.density;
524        nmMinSize = nmHeapStruct.heapsize;
525        nmMinDensity = nmHeapStruct.density;
526        arr.push(nmHeapStruct);
527      } else {
528        let last = arr[arr.length - 1];
529        last.dur = nmHeapStruct.startTime! - last.startTime!;
530        if (last.dur > filterLevel || finish) {
531          if (ne.eventType == 0 || ne.eventType == 1) {
532            nmHeapStruct.density = last.density! + nmTempDensity + 1;
533            nmHeapStruct.heapsize = last.heapsize! + nmTempSize + ne.heapSize;
534          } else {
535            nmHeapStruct.density = last.density! + nmTempDensity - 1;
536            nmHeapStruct.heapsize = last.heapsize! + nmTempSize - ne.heapSize;
537          }
538          nmTempDensity = 0;
539          nmTempSize = 0;
540          if (nmHeapStruct.density > nmMaxDensity) {
541            nmMaxDensity = nmHeapStruct.density;
542          }
543          if (nmHeapStruct.density < nmMinDensity) {
544            nmMinDensity = nmHeapStruct.density;
545          }
546          if (nmHeapStruct.heapsize > nmMaxSize) {
547            nmMaxSize = nmHeapStruct.heapsize;
548          }
549          if (nmHeapStruct.heapsize < nmMinSize) {
550            nmMinSize = nmHeapStruct.heapsize;
551          }
552          arr.push(nmHeapStruct);
553        } else {
554          if (ne.eventType == 0 || ne.eventType == 1) {
555            nmTempDensity = nmTempDensity + 1;
556            nmTempSize = nmTempSize + ne.heapSize;
557          } else {
558            nmTempDensity = nmTempDensity - 1;
559            nmTempSize = nmTempSize - ne.heapSize;
560          }
561        }
562      }
563    };
564    if (nativeMemoryType == 1) {
565      let temp = this.NATIVE_MEMORY_DATA.filter((ne) => ne.eventType === 0 || ne.eventType === 2);
566      nmFilterLen = temp.length;
567      nmFilterLevel = this.getFilterLevel(nmFilterLen);
568      temp.map((ne, index) => putArr(ne, nmFilterLevel, index === nmFilterLen - 1));
569      temp.length = 0;
570    } else if (nativeMemoryType == 2) {
571      let temp = this.NATIVE_MEMORY_DATA.filter((ne) => ne.eventType === 1 || ne.eventType === 3);
572      nmFilterLen = temp.length;
573      nmFilterLevel = this.getFilterLevel(nmFilterLen);
574      temp.map((ne, index) => putArr(ne, nmFilterLevel, index === nmFilterLen - 1));
575      temp.length = 0;
576    } else {
577      nmFilterLen = this.NATIVE_MEMORY_DATA.length;
578      let filterLevel = this.getFilterLevel(nmFilterLen);
579      this.NATIVE_MEMORY_DATA.map((ne, index) => putArr(ne, filterLevel, index === nmFilterLen - 1));
580    }
581    if (arr.length > 0) {
582      arr[arr.length - 1].dur = totalNS - arr[arr.length - 1].startTime!;
583    }
584    arr.map((heapStruct) => {
585      heapStruct.maxHeapSize = nmMaxSize;
586      heapStruct.maxDensity = nmMaxDensity;
587      heapStruct.minHeapSize = nmMinSize;
588      heapStruct.minDensity = nmMinDensity;
589    });
590    this.chartComplete.set(nativeMemoryType, true);
591    if (this.chartComplete.has(0) && this.chartComplete.has(1) && this.chartComplete.has(2)) {
592      this.NATIVE_MEMORY_DATA = [];
593    }
594    return arr;
595  }
596  resolvingActionNativeMemoryStack(paramMap: Map<string, any>) {
597    let eventId = paramMap.get('eventId');
598    let frameArr = this.dataCache.nmHeapFrameMap.get(eventId) || [];
599    let arr: Array<NativeHookCallInfo> = [];
600    frameArr.map((frame) => {
601      let target = new NativeHookCallInfo();
602      target.eventId = frame.eventId;
603      target.depth = frame.depth;
604      target.addr = frame.addr;
605      target.symbol = this.groupCutFilePath(frame.symbolId, this.dataCache.dataDict.get(frame.symbolId) || '') ?? '';
606      target.library = this.groupCutFilePath(frame.fileId, this.dataCache.dataDict.get(frame.fileId) || '') ?? '';
607      target.title = `[ ${target.symbol} ]  ${target.library}`;
608      target.type =
609        target.library.endsWith('.so.1') || target.library.endsWith('.dll') || target.library.endsWith('.so') ? 0 : 1;
610      arr.push(target);
611    });
612    return arr;
613  }
614
615  resolvingActionNativeMemory(paramMap: Map<string, any>): Array<NativeMemory> {
616    let filterAllocType = paramMap.get('filterAllocType');
617    let filterEventType = paramMap.get('filterEventType');
618    let filterResponseType = paramMap.get('filterResponseType');
619    let leftNs = paramMap.get('leftNs');
620    let rightNs = paramMap.get('rightNs');
621    let sortColumn = paramMap.get('sortColumn');
622    let sortType = paramMap.get('sortType');
623    let statisticsSelection = paramMap.get('statisticsSelection');
624    let filter = this.boxRangeNativeHook;
625    if ((filterAllocType !== undefined && filterAllocType !== 0) ||
626      (filterEventType !== undefined && filterEventType !== 0) ||
627      (filterResponseType !== undefined && filterResponseType !== -1)
628    ) {
629      filter = this.boxRangeNativeHook.filter((item) => {
630        let filterAllocation = true;
631        let freed = item.endTs > leftNs &&
632          item.endTs <= rightNs &&
633          item.endTs !== 0 &&
634          item.endTs !== null;
635        if (filterAllocType === '1') {
636          filterAllocation = !freed;
637        } else if (filterAllocType == '2') {
638          filterAllocation = freed;
639        }
640        let filterNative = this.getTypeFromIndex(parseInt(filterEventType), item, statisticsSelection);
641        let filterLastLib = filterResponseType == -1 ? true : filterResponseType == item.lastLibId;
642        return filterAllocation && filterNative && filterLastLib;
643      });
644    }
645    if (sortColumn !== undefined && sortType !== undefined && sortColumn !== '' && sortType !== 0) {
646      return this.sortByNativeMemoryColumn(sortColumn, sortType, filter);
647    } else {
648      return filter;
649    }
650  }
651
652  sortByNativeMemoryColumn(nmMemoryColumn: string, nmMemorySort: number, list: Array<NativeMemory>) {
653    if (nmMemorySort === 0) {
654      return list;
655    } else {
656      return list.sort((memoryLeftData: any, memoryRightData: any) => {
657        if (nmMemoryColumn === 'index' || nmMemoryColumn === 'startTs' || nmMemoryColumn === 'heapSize') {
658          return nmMemorySort == 1
659            ? memoryLeftData[nmMemoryColumn] - memoryRightData[nmMemoryColumn]
660            : memoryRightData[nmMemoryColumn] - memoryLeftData[nmMemoryColumn];
661        } else {
662          if (nmMemorySort == 1) {
663            if (memoryLeftData[nmMemoryColumn] > memoryRightData[nmMemoryColumn]) {
664              return 1;
665            } else if (memoryLeftData[nmMemoryColumn] === memoryRightData[nmMemoryColumn]) {
666              return 0;
667            } else {
668              return -1;
669            }
670          } else {
671            if (memoryRightData[nmMemoryColumn] > memoryLeftData[nmMemoryColumn]) {
672              return 1;
673            } else if (memoryLeftData[nmMemoryColumn] == memoryRightData[nmMemoryColumn]) {
674              return 0;
675            } else {
676              return -1;
677            }
678          }
679        }
680      });
681    }
682  }
683
684  groupCutFilePath(fileId: number, path: string): string {
685    let name: string;
686    if (this.dataCache.nmFileDict.has(fileId)) {
687      name = this.dataCache.nmFileDict.get(fileId) ?? '';
688    } else {
689      let currentPath = path.substring(path.lastIndexOf('/') + 1);
690      this.dataCache.nmFileDict.set(fileId, currentPath);
691      name = currentPath;
692    }
693    return name == '' ? '-' : name;
694  }
695
696  traverseSampleTree(stack: NativeHookCallInfo, hook: NativeHookStatistics) {
697    stack.count += 1;
698    stack.countValue = `${stack.count}`;
699    stack.countPercent = `${((stack.count / this.selectTotalCount) * 100).toFixed(1)}%`;
700    stack.size += hook.heapSize;
701    stack.tid = hook.tid;
702    stack.threadName = hook.threadName;
703    stack.heapSizeStr = `${getByteWithUnit(stack.size)}`;
704    stack.heapPercent = `${((stack.size / this.selectTotalSize) * 100).toFixed(1)}%`;
705    if (stack.children.length > 0) {
706      stack.children.map((child) => {
707        this.traverseSampleTree(child as NativeHookCallInfo, hook);
708      });
709    }
710  }
711  traverseTree(stack: NativeHookCallInfo, hook: NativeHookStatistics) {
712    stack.count = 1;
713    stack.countValue = `${stack.count}`;
714    stack.countPercent = `${((stack!.count / this.selectTotalCount) * 100).toFixed(1)}%`;
715    stack.size = hook.heapSize;
716    stack.tid = hook.tid;
717    stack.threadName = hook.threadName;
718    stack.heapSizeStr = `${getByteWithUnit(stack!.size)}`;
719    stack.heapPercent = `${((stack!.size / this.selectTotalSize) * 100).toFixed(1)}%`;
720    if (stack.children.length > 0) {
721      stack.children.map((child) => {
722        this.traverseTree(child as NativeHookCallInfo, hook);
723      });
724    }
725  }
726  getTypeFromIndex(
727    indexOf: number,
728    item: NativeHookStatistics | NativeMemory,
729    statisticsSelection: Array<StatisticsSelection>
730  ): boolean {
731    if (indexOf == -1) {
732      return false;
733    }
734    if (indexOf < 3) {
735      if (indexOf == 0) {
736        return true;
737      } else if (indexOf == 1) {
738        return item.eventType == 'AllocEvent';
739      } else if (indexOf == 2) {
740        return item.eventType == 'MmapEvent';
741      }
742    } else if (indexOf - 3 < statisticsSelection.length) {
743      let selectionElement = statisticsSelection[indexOf - 3];
744      if (selectionElement.memoryTap != undefined && selectionElement.max != undefined) {
745        if (selectionElement.memoryTap.indexOf('Malloc') != -1) {
746          return item.eventType == 'AllocEvent' && item.heapSize == selectionElement.max;
747        } else if (selectionElement.memoryTap.indexOf('Mmap') != -1) {
748          return item.eventType == 'MmapEvent' && item.heapSize == selectionElement.max && item.subTypeId === null;
749        } else {
750          return item.subType == selectionElement.memoryTap;
751        }
752      }
753      if (selectionElement.max === undefined && typeof selectionElement.memoryTap === 'number') {
754        return item.subTypeId === selectionElement.memoryTap;
755      }
756    }
757    return false;
758  }
759  clearAll() {
760    this.dataCache.clearNM();
761    this.splitMapData = {};
762    this.currentSamples = [];
763    this.allThreads = [];
764    this.queryAllCallchainsSamples = [];
765    this.NATIVE_MEMORY_DATA = [];
766    this.chartComplete.clear();
767    this.realTimeDif = 0;
768    this.currentTreeMapData = {};
769    this.currentTreeList.length = 0;
770    this.responseTypes.length = 0;
771    this.boxRangeNativeHook = [];
772    this.nativeMemoryArgs?.clear();
773  }
774
775  queryCallchainsSamples(action: string, leftNs: number, rightNs: number, types: Array<string>) {
776    this.queryData(
777      this.currentEventId,
778      action,
779      `select A.id,
780                callchain_id as eventId,
781                event_type as eventType,
782                heap_size as heapSize,
783                (A.start_ts - B.start_ts) as startTs,
784                (A.end_ts - B.start_ts) as endTs,
785                tid,
786                ifnull(last_lib_id,0) as lastLibId,
787                t.name as threadName,
788                A.addr,
789                A.sub_type_id as subTypeId
790            from
791                native_hook A,
792                trace_range B
793                left join
794                thread t
795                on
796                A.itid = t.id
797            where
798                A.start_ts - B.start_ts
799                between ${leftNs} and ${rightNs} and A.event_type in (${types.join(',')})
800        `,
801      {}
802    );
803  }
804  queryStatisticCallchainsSamples(action: string, leftNs: number, rightNs: number, types: Array<number>) {
805    let condition = '';
806    if (types.length === 1) {
807      if (types[0] === 0) {
808        condition = 'and type = 0';
809      } else {
810        condition = 'and type != 0';
811      }
812    }
813    this.queryData(
814      this.currentEventId,
815      action,
816      `select A.id,
817                0 as tid,
818                callchain_id as eventId,
819                (case when type = 0 then 'AllocEvent' else 'MmapEvent' end) as eventType,
820                (case when sub_type_id not null then sub_type_id else type end) as subTypeId,
821                apply_size as heapSize,
822                release_size as freeSize,
823                apply_count as count,
824                release_count as freeCount,
825                (max(A.ts) - B.start_ts) as startTs
826            from
827                native_hook_statistic A,
828                trace_range B
829            where
830                A.ts - B.start_ts
831                between ${leftNs} and ${rightNs}
832                ${condition}
833            group by callchain_id;
834        `,
835      {}
836    );
837  }
838
839  combineStatisticAndCallChain(samples: NativeHookStatistics[]) {
840    samples.sort((a, b) => a.id - b.id);
841    const analysisSampleList = new Array<AnalysisSample>();
842    const applyAllocSamples = new Array<AnalysisSample>();
843    const applyMmapSamples = new Array<AnalysisSample>();
844
845    for (const sample of samples) {
846      const count = this.isStatistic ? sample.count : 1;
847      const analysisSample = new AnalysisSample(sample.id, sample.heapSize, count, sample.eventType, sample.startTs);
848
849      if (this.isStatistic) {
850        analysisSample.releaseCount = sample.freeCount;
851        analysisSample.releaseSize = sample.freeSize;
852        switch (sample.subTypeId) {
853          case 1:
854            analysisSample.subType = 'MmapEvent';
855            break;
856          case 2:
857            analysisSample.subType = 'FILE_PAGE_MSG';
858            break;
859          case 3:
860            analysisSample.subType = 'MEMORY_USING_MSG';
861            break;
862          default:
863            analysisSample.subType = this.dataCache.dataDict.get(sample.subTypeId);
864        }
865      } else {
866        let subType = undefined;
867        if (sample.subTypeId) {
868          subType = this.dataCache.dataDict.get(sample.subTypeId);
869        }
870        analysisSample.endTs = sample.endTs;
871        analysisSample.addr = sample.addr;
872        analysisSample.tid = sample.tid;
873        analysisSample.subType = subType;
874      }
875
876      if (['FreeEvent', 'MunmapEvent'].includes(sample.eventType)) {
877        if (sample.eventType === 'FreeEvent') {
878          this.setApplyIsRelease(analysisSample, applyAllocSamples);
879        } else {
880          this.setApplyIsRelease(analysisSample, applyMmapSamples);
881        }
882        continue;
883      } else {
884        if (sample.eventType === 'AllocEvent') {
885          applyAllocSamples.push(analysisSample);
886        } else {
887          applyMmapSamples.push(analysisSample);
888        }
889      }
890
891      const callChains = this.dataCache.nmHeapFrameMap.get(sample.eventId) || [];
892      if (!callChains || callChains.length === 0) {
893        return;
894      }
895      let index = callChains.length - 1;
896      let lastFilterCallChain: HeapTreeDataBean | undefined | null;
897      while (true) {
898        // if all call stack is musl or libc++. use stack top lib
899        if (index < 0) {
900          lastFilterCallChain = callChains[callChains.length - 1];
901          break;
902        }
903
904        lastFilterCallChain = callChains[index];
905        const libPath = this.dataCache.dataDict.get(lastFilterCallChain.fileId);
906        //ignore musl and libc++ so
907        if (libPath?.includes('musl') || libPath?.includes('libc++')) {
908          index--;
909        } else {
910          lastFilterCallChain = lastFilterCallChain;
911          break;
912        }
913      }
914
915      const filePath = this.dataCache.dataDict.get(lastFilterCallChain.fileId)!;
916      let libName = '';
917      if (filePath) {
918        const path = filePath.split('/');
919        libName = path[path.length - 1];
920      }
921      const symbolName =
922        this.dataCache.dataDict.get(lastFilterCallChain.symbolId) || libName + ' (' + sample.addr + ')';
923
924      analysisSample.libId = lastFilterCallChain.fileId;
925      analysisSample.libName = libName;
926      analysisSample.symbolId = lastFilterCallChain.symbolId;
927      analysisSample.symbolName = symbolName;
928
929      analysisSampleList.push(analysisSample);
930    }
931    return analysisSampleList;
932  }
933
934  setApplyIsRelease(sample: AnalysisSample, arr: Array<AnalysisSample>) {
935    let idx = arr.length - 1;
936    for (idx; idx >= 0; idx--) {
937      let item = arr[idx];
938      if (item.endTs === sample.startTs && item.addr === sample.addr) {
939        arr.splice(idx, 1);
940        item.isRelease = true;
941        return;
942      }
943    }
944  }
945
946  freshCurrentCallchains(samples: NativeHookStatistics[], isTopDown: boolean) {
947    this.currentTreeMapData = {};
948    this.currentTreeList = [];
949    let totalSize = 0;
950    let totalCount = 0;
951    samples.forEach((nativeHookSample) => {
952      if (nativeHookSample.eventId == -1) {
953        return;
954      }
955      totalSize += nativeHookSample.heapSize;
956      totalCount += nativeHookSample.count || 1;
957      let callChains = this.createThreadSample(nativeHookSample);
958      let topIndex = isTopDown ? 0 : callChains.length - 1;
959      if (callChains.length > 0) {
960        let root =
961          this.currentTreeMapData[
962            nativeHookSample.tid + '-' + (callChains[topIndex].symbolId || '') + '-' + (callChains[topIndex].fileId || '')
963          ];
964        if (root == undefined) {
965          root = new NativeHookCallInfo();
966          root.threadName = nativeHookSample.threadName;
967          this.currentTreeMapData[
968            nativeHookSample.tid + '-' + (callChains[topIndex].symbolId || '') + '-' + (callChains[topIndex].fileId || '')
969          ] = root;
970          this.currentTreeList.push(root);
971        }
972        NativeHookCallInfo.merageCallChainSample(root, callChains[topIndex], nativeHookSample);
973        if (callChains.length > 1) {
974          this.merageChildrenByIndex(root, callChains, topIndex, nativeHookSample, isTopDown);
975        }
976      }
977    });
978    let rootMerageMap: any = {};
979    // @ts-ignore
980    let threads = Object.values(this.currentTreeMapData);
981    threads.forEach((merageData: any) => {
982      if (rootMerageMap[merageData.tid] == undefined) {
983        let threadMerageData = new NativeHookCallInfo(); //新增进程的节点数据
984        threadMerageData.canCharge = false;
985        threadMerageData.type = -1;
986        threadMerageData.symbolName = `${merageData.threadName || 'Thread'} [${merageData.tid}]`;
987        threadMerageData.symbol = threadMerageData.symbolName;
988        threadMerageData.children.push(merageData);
989        threadMerageData.initChildren.push(merageData);
990        threadMerageData.count = merageData.count || 1;
991        threadMerageData.heapSize = merageData.heapSize;
992        threadMerageData.totalCount = totalCount;
993        threadMerageData.totalSize = totalSize;
994        rootMerageMap[merageData.tid] = threadMerageData;
995      } else {
996        rootMerageMap[merageData.tid].children.push(merageData);
997        rootMerageMap[merageData.tid].initChildren.push(merageData);
998        rootMerageMap[merageData.tid].count += merageData.count || 1;
999        rootMerageMap[merageData.tid].heapSize += merageData.heapSize;
1000        rootMerageMap[merageData.tid].totalCount = totalCount;
1001        rootMerageMap[merageData.tid].totalSize = totalSize;
1002      }
1003      merageData.parentNode = rootMerageMap[merageData.tid]; //子节点添加父节点的引用
1004    });
1005    let id = 0;
1006    this.currentTreeList.forEach((nmTreeNode) => {
1007      nmTreeNode.totalCount = totalCount;
1008      nmTreeNode.totalSize = totalSize;
1009      this.setMerageName(nmTreeNode);
1010      if (nmTreeNode.id == '') {
1011        nmTreeNode.id = id + '';
1012        id++;
1013      }
1014      if (nmTreeNode.parentNode) {
1015        if (nmTreeNode.parentNode.id == '') {
1016          nmTreeNode.parentNode.id = id + '';
1017          id++;
1018        }
1019        nmTreeNode.parentId = nmTreeNode.parentNode.id;
1020      }
1021    });
1022    // @ts-ignore
1023    this.allThreads = Object.values(rootMerageMap) as NativeHookCallInfo[];
1024  }
1025  groupCallchainSample(paramMap: Map<string, any>) {
1026    let groupMap: any = {};
1027    let filterAllocType = paramMap.get('filterAllocType');
1028    let filterEventType = paramMap.get('filterEventType');
1029    let filterResponseType = paramMap.get('filterResponseType');
1030    let leftNs = paramMap.get('leftNs');
1031    let rightNs = paramMap.get('rightNs');
1032    let nativeHookType = paramMap.get('nativeHookType');
1033    let statisticsSelection = paramMap.get('statisticsSelection');
1034    if (filterAllocType == '0' && filterEventType == '0' && filterResponseType == -1) {
1035      this.currentSamples = this.queryAllCallchainsSamples;
1036      return;
1037    }
1038    let filter = this.queryAllCallchainsSamples.filter((item) => {
1039      let filterAllocation = true;
1040      if (nativeHookType === 'native-hook') {
1041        if (filterAllocType == '1') {
1042          filterAllocation =
1043            item.startTs >= leftNs &&
1044            item.startTs <= rightNs &&
1045            (item.endTs > rightNs || item.endTs == 0 || item.endTs == null);
1046        } else if (filterAllocType == '2') {
1047          filterAllocation =
1048            item.startTs >= leftNs &&
1049            item.startTs <= rightNs &&
1050            item.endTs <= rightNs &&
1051            item.endTs != 0 &&
1052            item.endTs != null;
1053        }
1054      } else {
1055        if (filterAllocType == '1') {
1056          filterAllocation = item.heapSize > item.freeSize;
1057        } else if (filterAllocType == '2') {
1058          filterAllocation = item.heapSize === item.freeSize;
1059        }
1060      }
1061      let filterLastLib = filterResponseType == -1 ? true : filterResponseType == item.lastLibId;
1062      let filterNative = this.getTypeFromIndex(parseInt(filterEventType), item, statisticsSelection);
1063      return filterAllocation && filterNative && filterLastLib;
1064    });
1065    filter.forEach((sample) => {
1066      let currentNode = groupMap[sample.tid + '-' + sample.eventId] || new NativeHookStatistics();
1067      if (currentNode.count == 0) {
1068        Object.assign(currentNode, sample);
1069        if (filterAllocType == '1' && nativeHookType !== 'native-hook') {
1070          currentNode.heapSize = sample.heapSize - sample.freeSize;
1071          currentNode.count = sample.count - sample.freeCount;
1072        }
1073        if (currentNode.count === 0) {
1074          currentNode.count++;
1075        }
1076      } else {
1077        currentNode.count++;
1078        currentNode.heapSize += sample.heapSize;
1079      }
1080      groupMap[sample.tid + '-' + sample.eventId] = currentNode;
1081    });
1082    // @ts-ignore
1083    this.currentSamples = Object.values(groupMap);
1084  }
1085  createThreadSample(sample: NativeHookStatistics) {
1086    return this.dataCache.nmHeapFrameMap.get(sample.eventId) || [];
1087  }
1088  merageChildrenByIndex(
1089    currentNode: NativeHookCallInfo,
1090    callChainDataList: any[],
1091    index: number,
1092    sample: NativeHookStatistics,
1093    isTopDown: boolean
1094  ) {
1095    isTopDown ? index++ : index--;
1096    let isEnd = isTopDown ? callChainDataList.length == index + 1 : index == 0;
1097    let node;
1098    if (
1099      currentNode.initChildren.filter((child: any) => {
1100        if (child.symbolId == callChainDataList[index]?.symbolId && child.fileId == callChainDataList[index]?.fileId) {
1101          node = child;
1102          NativeHookCallInfo.merageCallChainSample(child, callChainDataList[index], sample);
1103          return true;
1104        }
1105        return false;
1106      }).length == 0
1107    ) {
1108      node = new NativeHookCallInfo();
1109      NativeHookCallInfo.merageCallChainSample(node, callChainDataList[index], sample);
1110      currentNode.children.push(node);
1111      currentNode.initChildren.push(node);
1112      this.currentTreeList.push(node);
1113      node.parentNode = currentNode;
1114    }
1115    if (node && !isEnd) this.merageChildrenByIndex(node, callChainDataList, index, sample, isTopDown);
1116  }
1117  setMerageName(currentNode: NativeHookCallInfo) {
1118    currentNode.symbol =
1119      this.groupCutFilePath(currentNode.symbolId, this.dataCache.dataDict.get(currentNode.symbolId) || '') ?? 'unknown';
1120    currentNode.path = this.dataCache.dataDict.get(currentNode.fileId) || 'unknown';
1121    currentNode.libName = setFileName(currentNode.path);
1122    currentNode.lib = currentNode.path;
1123    currentNode.symbolName = `[${currentNode.symbol}] ${currentNode.libName}`;
1124    currentNode.type =
1125      currentNode.libName.endsWith('.so.1') ||
1126      currentNode.libName.endsWith('.dll') ||
1127      currentNode.libName.endsWith('.so')
1128        ? 0
1129        : 1;
1130  }
1131  clearSplitMapData(symbolName: string) {
1132    delete this.splitMapData[symbolName];
1133  }
1134  resolvingNMCallAction(params: any[]) {
1135    if (params.length > 0) {
1136      params.forEach((item) => {
1137        if (item.funcName && item.funcArgs) {
1138          switch (item.funcName) {
1139            case 'groupCallchainSample':
1140              this.groupCallchainSample(item.funcArgs[0] as Map<string, any>);
1141              break;
1142            case 'getCallChainsBySampleIds':
1143              this.freshCurrentCallchains(this.currentSamples, item.funcArgs[0]);
1144              break;
1145            case 'hideSystemLibrary':
1146              merageBeanDataSplit.hideSystemLibrary(this.allThreads, this.splitMapData);
1147              break;
1148            case 'hideNumMaxAndMin':
1149              merageBeanDataSplit.hideNumMaxAndMin(
1150                this.allThreads,
1151                this.splitMapData,
1152                item.funcArgs[0],
1153                item.funcArgs[1]
1154              );
1155              break;
1156            case 'splitAllProcess':
1157              merageBeanDataSplit.splitAllProcess(this.allThreads, this.splitMapData, item.funcArgs[0]);
1158              break;
1159            case 'resetAllNode':
1160              merageBeanDataSplit.resetAllNode(this.allThreads, this.currentTreeList, this.searchValue);
1161              break;
1162            case 'resotreAllNode':
1163              merageBeanDataSplit.resotreAllNode(this.splitMapData, item.funcArgs[0]);
1164              break;
1165            case 'splitTree':
1166              merageBeanDataSplit.splitTree(
1167                this.splitMapData,
1168                this.allThreads,
1169                item.funcArgs[0],
1170                item.funcArgs[1],
1171                item.funcArgs[2],
1172                this.currentTreeList,
1173                this.searchValue
1174              );
1175              break;
1176            case 'setSearchValue':
1177              this.searchValue = item.funcArgs[0];
1178              break;
1179            case 'clearSplitMapData':
1180              this.clearSplitMapData(item.funcArgs[0]);
1181              break;
1182          }
1183        }
1184      });
1185    }
1186    return this.allThreads.filter((thread) => {
1187      return thread.children && thread.children.length > 0;
1188    });
1189  }
1190  getFilterLevel(len: number): number {
1191    if (len > 300_0000) {
1192      return 50_0000;
1193    } else if (len > 200_0000) {
1194      return 30_0000;
1195    } else if (len > 100_0000) {
1196      return 10_0000;
1197    } else if (len > 50_0000) {
1198      return 5_0000;
1199    } else if (len > 30_0000) {
1200      return 2_0000;
1201    } else if (len > 15_0000) {
1202      return 1_0000;
1203    } else {
1204      return 0;
1205    }
1206  }
1207}
1208
1209export class NativeHookStatistics {
1210  id: number = 0;
1211  eventId: number = 0;
1212  eventType: string = '';
1213  subType: string = '';
1214  subTypeId: number = 0;
1215  heapSize: number = 0;
1216  freeSize: number = 0;
1217  addr: string = '';
1218  startTs: number = 0;
1219  endTs: number = 0;
1220  sumHeapSize: number = 0;
1221  max: number = 0;
1222  count: number = 0;
1223  freeCount: number = 0;
1224  tid: number = 0;
1225  threadName: string = '';
1226  lastLibId: number = 0;
1227  isSelected: boolean = false;
1228}
1229export class NativeHookCallInfo extends MerageBean {
1230  #totalCount: number = 0;
1231  #totalSize: number = 0;
1232  library: string = '';
1233  symbolId: number = 0;
1234  fileId: number = 0;
1235  title: string = '';
1236  count: number = 0;
1237  countValue: string = '';
1238  countPercent: string = '';
1239  type: number = 0;
1240  heapSize: number = 0;
1241  heapPercent: string = '';
1242  heapSizeStr: string = '';
1243  eventId: number = 0;
1244  tid: number = 0;
1245  threadName: string = '';
1246  eventType: string = '';
1247  isSelected: boolean = false;
1248  set totalCount(total: number) {
1249    this.#totalCount = total;
1250    this.countValue = this.count + '';
1251    this.size = this.heapSize;
1252    this.countPercent = `${((this.count / total) * 100).toFixed(1)}%`;
1253  }
1254  get totalCount() {
1255    return this.#totalCount;
1256  }
1257  set totalSize(total: number) {
1258    this.#totalSize = total;
1259    this.heapSizeStr = `${getByteWithUnit(this.heapSize)}`;
1260    this.heapPercent = `${((this.heapSize / total) * 100).toFixed(1)}%`;
1261  }
1262  get totalSize() {
1263    return this.#totalSize;
1264  }
1265  static merageCallChainSample(
1266    currentNode: NativeHookCallInfo,
1267    callChain: HeapTreeDataBean,
1268    sample: NativeHookStatistics
1269  ) {
1270    if (currentNode.symbol == undefined || currentNode.symbol == '') {
1271      currentNode.symbol = callChain.AllocationFunction || '';
1272      currentNode.addr = callChain.addr;
1273      currentNode.eventId = sample.eventId;
1274      currentNode.eventType = sample.eventType;
1275      currentNode.symbolId = callChain.symbolId;
1276      currentNode.fileId = callChain.fileId;
1277      currentNode.tid = sample.tid;
1278    }
1279    currentNode.count += sample.count || 1;
1280    currentNode.heapSize += sample.heapSize;
1281  }
1282}
1283export class NativeMemory {
1284  index: number = 0;
1285  eventId: number = 0;
1286  eventType: string = '';
1287  subType: string = '';
1288  subTypeId: number = 0;
1289  addr: string = '';
1290  startTs: number = 0;
1291  endTs: number = 0;
1292  heapSize: number = 0;
1293  symbol: string = '';
1294  library: string = '';
1295  lastLibId: number = 0;
1296  isSelected: boolean = false;
1297  threadId: number = 0;
1298}
1299
1300export class HeapStruct {
1301  startTime: number | undefined;
1302  endTime: number | undefined;
1303  dur: number | undefined;
1304  density: number | undefined;
1305  heapsize: number | undefined;
1306  maxHeapSize: number = 0;
1307  maxDensity: number = 0;
1308  minHeapSize: number = 0;
1309  minDensity: number = 0;
1310}
1311export class NativeEvent {
1312  startTime: number = 0;
1313  heapSize: number = 0;
1314  eventType: number = 0;
1315}
1316export class StatisticsSelection {
1317  memoryTap: string = '';
1318  max: number = 0;
1319}
1320
1321class AnalysisSample {
1322  id: number;
1323  count: number;
1324  size: number;
1325  type: number;
1326  startTs: number;
1327
1328  isRelease: boolean;
1329  releaseCount?: number;
1330  releaseSize?: number;
1331
1332  endTs?: number;
1333  subType?: string;
1334  tid?: number;
1335  addr?: string;
1336
1337  libId!: number;
1338  libName!: string;
1339  symbolId!: number;
1340  symbolName!: string;
1341
1342  constructor(id: number, size: number, count: number, type: number | string, startTs: number) {
1343    this.id = id;
1344    this.size = size;
1345    this.count = count;
1346    this.startTs = startTs;
1347    switch (type) {
1348      case 'AllocEvent':
1349      case '0':
1350        this.type = 0;
1351        this.isRelease = false;
1352        break;
1353      case 'MmapEvent':
1354      case '1':
1355        this.isRelease = false;
1356        this.type = 1;
1357        break;
1358      case 'FreeEvent':
1359        this.isRelease = true;
1360        this.type = 2;
1361        break;
1362      case 'MunmapEvent':
1363        this.isRelease = true;
1364        this.type = 3;
1365        break;
1366      default:
1367        this.isRelease = false;
1368        this.type = -1;
1369    }
1370  }
1371}
1372