meminsight-test-demo 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +36 -0
  3. package/docs/Build.md +68 -0
  4. package/docs/Interface.md +35 -0
  5. package/docs/UserGuide.md +332 -0
  6. package/docs/resources/analyzer.png +0 -0
  7. package/docs/resources/cfg_filter_filetype_json.png +0 -0
  8. package/docs/resources/filetype_txt.png +0 -0
  9. package/docs/resources/framework.png +0 -0
  10. package/docs/resources/output.png +0 -0
  11. package/docs/resources/process.png +0 -0
  12. package/package.json +54 -0
  13. package/packages/cfg/ArkStatCfg.json +33 -0
  14. package/packages/core/jest.config.js +8 -0
  15. package/packages/core/package.json +61 -0
  16. package/packages/core/src/Index.ts +53 -0
  17. package/packages/core/src/analyzer/AnalysisInfo.ts +298 -0
  18. package/packages/core/src/analyzer/ArkAnalyzer.ts +42 -0
  19. package/packages/core/src/analyzer/ArkCmpCfg.ts +22 -0
  20. package/packages/core/src/analyzer/ArkCompareAnalyzer.ts +173 -0
  21. package/packages/core/src/analyzer/ArkLeakAnalyzer.ts +196 -0
  22. package/packages/core/src/analyzer/ArkSerializer.ts +163 -0
  23. package/packages/core/src/analyzer/ArkStatAnalyzer.ts +191 -0
  24. package/packages/core/src/analyzer/ArkStatCfg.ts +77 -0
  25. package/packages/core/src/analyzer/ArkTracePath.ts +269 -0
  26. package/packages/core/src/analyzer/ArkTracer.ts +42 -0
  27. package/packages/core/src/analyzer/ArkXAnalyzer.ts +631 -0
  28. package/packages/core/src/analyzer/IAnalyzer.ts +27 -0
  29. package/packages/core/src/file/FileReader.ts +82 -0
  30. package/packages/core/src/file/FileService.ts +50 -0
  31. package/packages/core/src/file/FileWriter.ts +148 -0
  32. package/packages/core/src/report/Reporter.ts +81 -0
  33. package/packages/core/src/report/templates/template.ejs +101 -0
  34. package/packages/core/src/report/templates/template.ts +103 -0
  35. package/packages/core/src/shell/DeviceShell.ts +179 -0
  36. package/packages/core/src/shell/Shell.ts +99 -0
  37. package/packages/core/src/types/Constants.ts +16 -0
  38. package/packages/core/src/types/LeakTypes.ts +21 -0
  39. package/packages/core/src/types/OhosTypes.ts +115 -0
  40. package/packages/core/src/utils/Common.ts +37 -0
  41. package/packages/core/src/utils/Finder.ts +390 -0
  42. package/packages/core/src/utils/Loader.ts +53 -0
  43. package/packages/core/src/utils/Log.ts +252 -0
  44. package/packages/core/src/utils/Output.ts +271 -0
  45. package/packages/core/tsconfig.json +10 -0
  46. package/packages/exampletools/package.json +52 -0
  47. package/packages/exampletools/src/MemTest.ts +64 -0
  48. package/packages/exampletools/tsconfig.json +15 -0
  49. package/packages/meminsight/jest.config.js +8 -0
  50. package/packages/meminsight/package.json +52 -0
  51. package/packages/meminsight/src/Index.ts +4 -0
  52. package/packages/meminsight/src/Version.ts +7 -0
  53. package/packages/meminsight/src/process/ArkCompareProc.ts +160 -0
  54. package/packages/meminsight/src/process/ArkGCProc.ts +61 -0
  55. package/packages/meminsight/src/process/ArkLeakProc.ts +47 -0
  56. package/packages/meminsight/src/process/ArkStatProc.ts +320 -0
  57. package/packages/meminsight/src/process/ArkXProc.ts +73 -0
  58. package/packages/meminsight/src/process/HMemXProc.ts +50 -0
  59. package/packages/meminsight/src/process/IProcess.ts +12 -0
  60. package/packages/meminsight/tsconfig.json +15 -0
  61. package/packages/stack/README.md +31 -0
  62. package/packages/stack/libs/hstack_lib-1.0.0.tgz +0 -0
  63. package/packages/stack/libs/hstack_lib-1.0.1.tgz +0 -0
  64. package/packages/stack/libs/hstack_lib-1.0.4.tgz +0 -0
  65. package/packages/stack/libs/lib_list.json +34 -0
  66. package/packages/stack/package.json +27 -0
  67. package/packages/stack/src/Index.js +29 -0
  68. package/packages/stack/src/StackTracer.js +53 -0
  69. package/packages/templates/ArkLeaks.template +9 -0
  70. package/packages/templates/ArkNodes.template +9 -0
  71. package/packages/templates/ArkPaths.template +9 -0
  72. package/test/scripts/merge.py +145 -0
  73. package/test/scripts/stat.py +175 -0
  74. package/test/test_ark_stat_proc.sh +14 -0
  75. package/tsconfig.base.json +38 -0
  76. package/tsconfig.json +8 -0
@@ -0,0 +1,191 @@
1
+ import * as fs from 'fs';
2
+ import { IHeapSnapshot, IHeapNode, LeakTracePathItem } from "@memlab/core";
3
+ import { ArkAnalyzer } from "./ArkAnalyzer";
4
+ import { Loader } from "../utils/Loader";
5
+ import { Finder } from '../utils/Finder';
6
+ import { Log, ILoggable } from "../utils/Log";
7
+ import { ArkStatCfg } from './ArkStatCfg';
8
+ import { ArkNodesMap, ArkTracePathItem } from './ArkTracePath';
9
+
10
+ /**
11
+ * Ark Heapsnapshot 统计分析器
12
+ */
13
+ export class ArkStatAnalyzer extends ArkAnalyzer implements ILoggable {
14
+ public readonly DOMAIN = 'meminsight';
15
+ public readonly TAG = ArkStatAnalyzer.name;
16
+ public static readonly NAME = 'ark-stat-analyzer';
17
+ public static readonly DESC = "Analyze single heapsnapshot file and show statistics.";
18
+
19
+ protected snapshot: IHeapSnapshot;
20
+ protected snapshotFilePath: string;
21
+
22
+ constructor() {
23
+ super();
24
+ this.type = 'ark-stat-analyzer';
25
+ }
26
+
27
+ public override async analyze(args: any[]) : Promise<any> {
28
+ if (args.length < 1) {
29
+ Log.errorX(this, 'Argument count is not enough.');
30
+ return undefined;
31
+ }
32
+ this.snapshotFilePath = args[0] as string;
33
+ if (!fs.existsSync(this.snapshotFilePath)) {
34
+ Log.errorX(this, `File not found, ${this.snapshotFilePath}`);
35
+ return undefined;
36
+ }
37
+ this.snapshot = await Loader.loadFromFile(this.snapshotFilePath);
38
+ return this.snapshot;
39
+ }
40
+
41
+ /**
42
+ * get snapshot file path
43
+ * @returns snapshot file path
44
+ */
45
+ public getSnapshotFilePath() : string {
46
+ return this.snapshotFilePath;
47
+ }
48
+
49
+ public async getGCRoots() : Promise<Array<IHeapNode>> {
50
+ if (!this.snapshot) {
51
+ Log.errorX(this, 'Heapsnapshot has not been loaded.');
52
+ return [];
53
+ }
54
+ return Finder.findGCRoots(this.snapshot);
55
+ }
56
+
57
+ public async getDetachedNodes() : Promise<Array<IHeapNode>> {
58
+ if (!this.snapshot) {
59
+ Log.errorX(this, 'Heapsnapshot has not been loaded.');
60
+ return [];
61
+ }
62
+ return Finder.findDetachedNodes(this.snapshot);
63
+ }
64
+
65
+ public async getNodesByFilterCondition(
66
+ filter: (condition: any) => boolean,
67
+ condition: any) : Promise<Array<IHeapNode>> {
68
+ if (!this.snapshot) {
69
+ Log.errorX(this, 'Heapsnapshot has not been loaded.');
70
+ return [];
71
+ }
72
+ const result = new Array<IHeapNode>();
73
+ this.snapshot.nodes.forEach((node, ) => {
74
+ if (filter(condition)) {
75
+ result.push(node);
76
+ }
77
+ });
78
+ return result;
79
+ }
80
+
81
+ public async getNodesByConfig() : Promise<Array<IHeapNode>> {
82
+ if (!this.snapshot) {
83
+ Log.errorX(this, 'Heapsnapshot has not been loaded.');
84
+ return [];
85
+ }
86
+ const result = new Array<IHeapNode>();
87
+ let cfg = this.configuration as ArkStatCfg;
88
+ if (cfg) {
89
+ this.snapshot.nodes.forEach((node, ) => {
90
+ if (!((cfg.Node.enableTypeWhiteList && !cfg.Node.typeWhiteList.includes(node.type))
91
+ || (cfg.Node.enableTypeBlackList && cfg.Node.typeBlackList.includes(node.type))
92
+ || (cfg.Node.enableNameWhiteList && !cfg.Node.nameWhiteList.includes(node.name))
93
+ || (cfg.Node.enableNameBlackList && cfg.Node.nameBlackList.includes(node.name)))) {
94
+ result.push(node);
95
+ }
96
+ });
97
+ } else {
98
+ Log.errorX(this, 'No analyzer configuration found.');
99
+ }
100
+ return result;
101
+ }
102
+
103
+ /**
104
+ * 获取指定节点的最短引用路径
105
+ * @param nodeId node id
106
+ * @returns 最短引用路径
107
+ */
108
+ public async getShortestPathToNode(nodeId: number) : Promise<ArkTracePathItem | undefined> {
109
+ if (!this.snapshot) {
110
+ Log.errorX(this, 'Heapsnapshot has not been loaded.');
111
+ return undefined;
112
+ }
113
+ const node = this.snapshot.nodes.get(nodeId);
114
+ if (!node) {
115
+ Log.errorX(this, `Node(${nodeId}) does not exist.`);
116
+ return undefined;
117
+ }
118
+ return Finder.findShortestPathToGCRoot(this.snapshot, node);
119
+ }
120
+
121
+ /**
122
+ * 根据配置参数获取符合条件的节点信息
123
+ * @param nameWhiteList class name 白名单
124
+ * @param typeWhiteList node type 白名单
125
+ * @param enableShortestPath 是否生成最短路径
126
+ * @param enableAggrationPath 是否生成聚合路径,enableShortestPath 为 true 时有效
127
+ * @param maxAggrationLength 最大聚合路径的个数,enableShortestPath 为 true 时有效
128
+ * @param enableMinRetainedSize 是否按最小retained size过滤,enableShortestPath 为 true 时有效
129
+ * @param minRetainedSize 最小retained size,enableShortestPath 为 true 时有效
130
+ * @param descending 聚合路径是否自上而下排序,enableShortestPath 为 true 时有效
131
+ * @returns 节点路径信息,可以按节点名、节点类型、节点到GC Roots的最短路径等信息过滤
132
+ */
133
+ public async getArkNodesMap(
134
+ nameWhiteList: string[],
135
+ typeWhiteList: string[] = ['object'],
136
+ enableShortestPath: boolean = true,
137
+ enableAggrationPath: boolean = true,
138
+ maxAggrationLength = 3,
139
+ enableMinRetainedSize = false,
140
+ minRetainedSize = 1024 * 1024,
141
+ descending: boolean = true): Promise<ArkNodesMap | undefined> {
142
+ if (!this.snapshot) {
143
+ Log.errorX(this, 'Heapsnapshot has not been loaded.');
144
+ return undefined;
145
+ }
146
+ const result = new ArkNodesMap();
147
+ this.snapshot.nodes.forEach((node, ) => {
148
+ result.addNode(this.snapshot, node, nameWhiteList, typeWhiteList, enableShortestPath);
149
+ });
150
+ if (enableShortestPath && enableAggrationPath) {
151
+ for (const [key, value] of result) {
152
+ value.getAggrationPathIndexes(
153
+ descending,
154
+ maxAggrationLength,
155
+ enableMinRetainedSize ? minRetainedSize : -1);
156
+ }
157
+ }
158
+ return result;
159
+ }
160
+
161
+ /**
162
+ * 根据配置获取符合条件的节点信息
163
+ * 配置通过 setConfig(cfg: any) 设置,默认配置参见 ArkStatCfg.defaultArkStatCfg()
164
+ */
165
+ public async getArkNodesMapByConfig() : Promise<ArkNodesMap | undefined> {
166
+ let cfg = this.configuration as ArkStatCfg;
167
+ if (!cfg) {
168
+ Log.errorX(this, 'No analyzer configuration found.');
169
+ return undefined;
170
+ }
171
+ let nameWhiteList = cfg.Node.nameWhiteList;
172
+ let typeWhiteList = cfg.Node.typeWhiteList;
173
+ let enableShortestPath = cfg.Path.enableShortest;
174
+ let enableAggrationPath = cfg.Path.enableAggration;
175
+ let maxAggrationLength = cfg.Path.aggrationLength;
176
+ let enableMinRetainedSize = cfg.Path.enableMinRetainedSize;
177
+ let minRetainedSize = cfg.Path.minRetainedSize;
178
+ let descending = cfg.Path.aggrationDescending;
179
+ return this.getArkNodesMap(nameWhiteList, typeWhiteList,
180
+ enableShortestPath, enableAggrationPath, maxAggrationLength,
181
+ enableMinRetainedSize, minRetainedSize, descending);
182
+ }
183
+
184
+ /**
185
+ * 获取所有共享对象,Map 以对象tag作为 key,相同tag的对象作为 value
186
+ * @returns Map<string, Array<IHeapNode>>
187
+ */
188
+ public async getSharedHeapNodesMap() : Promise<Map<string, Array<IHeapNode>>> {
189
+ return Finder.getSharedHeapNodesMap(this.snapshot);
190
+ }
191
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Ark Statistics Configuration
3
+ */
4
+ export type ArkStatCfg = {
5
+ Node: {
6
+ enableTypeWhiteList: boolean,
7
+ typeWhiteList: string[],
8
+ enableTypeBlackList: boolean,
9
+ typeBlackList: string[],
10
+ enableNameWhiteList: boolean,
11
+ nameWhiteList: string[],
12
+ enableNameBlackList: boolean,
13
+ nameBlackList: string[],
14
+ enableId: boolean,
15
+ enableType: boolean,
16
+ enableName: boolean,
17
+ enableSelfSize: boolean,
18
+ enableRetainedSize: boolean,
19
+ },
20
+ Path: {
21
+ enableDistance: boolean,
22
+ enableSelfSize: boolean,
23
+ enableRetainedSize: boolean,
24
+ enableNativeSize: boolean,
25
+ enableShortest: boolean,
26
+ enableShortestLength: boolean,
27
+ shortestLength: number,
28
+ enableAggration: boolean,
29
+ aggrationLength: number,
30
+ aggrationDescending: boolean,
31
+ enableMinRetainedSize: boolean,
32
+ minRetainedSize: number,
33
+ enableMaxRetainedSize: boolean,
34
+ maxRetainedSize: number,
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Ark Statistics default configuration
40
+ * @returns default configuration
41
+ */
42
+ export function defaultArkStatCfg() : ArkStatCfg {
43
+ return {
44
+ Node: {
45
+ enableTypeWhiteList: true,
46
+ typeWhiteList: ["object", "closure", "string", "array"],
47
+ enableTypeBlackList: false,
48
+ typeBlackList: [],
49
+ enableNameWhiteList: false,
50
+ nameWhiteList: [],
51
+ enableNameBlackList: false,
52
+ nameBlackList: [],
53
+ enableId: true,
54
+ enableType: true,
55
+ enableName: true,
56
+ enableSelfSize: false,
57
+ enableRetainedSize: false,
58
+ },
59
+ Path: {
60
+ enableDistance: true,
61
+ enableSelfSize: true,
62
+ enableRetainedSize: true,
63
+ enableNativeSize: false,
64
+ enableShortest: true,
65
+ enableShortestLength: false,
66
+ shortestLength: 0,
67
+ enableAggration: true,
68
+ aggrationLength: 3,
69
+ aggrationDescending: true,
70
+ enableMinRetainedSize: true,
71
+ minRetainedSize: 1024 * 1024, // 1MB
72
+ enableMaxRetainedSize: false,
73
+ maxRetainedSize: 0,
74
+ },
75
+
76
+ }
77
+ }
@@ -0,0 +1,269 @@
1
+ import { IHeapSnapshot, IHeapNode, LeakTracePathItem } from '@memlab/core'
2
+ import { Finder } from '../utils/Finder';
3
+ import { ILoggable, Log } from '../utils/Log';
4
+
5
+ /**
6
+ * ArkTracePathItem
7
+ */
8
+ export type ArkTracePathItem = LeakTracePathItem;
9
+
10
+ /**
11
+ * ArkNodeTracePath
12
+ */
13
+ export class ArkNodeTracePath {
14
+ public readonly tracePathItem: ArkTracePathItem;
15
+ public readonly uid: string;
16
+ public readonly nodeIds: number[];
17
+ public readonly reverse: boolean;
18
+ public readonly retainedSize: number;
19
+
20
+ constructor(item: ArkTracePathItem, reverse: boolean = false) {
21
+ this.tracePathItem = item;
22
+ this.nodeIds = ArkNodeTracePath.getNodeIdTrace(item);
23
+ this.retainedSize = ArkNodeTracePath.getRetainedSize(item);
24
+ this.reverse = reverse;
25
+ this.uid = reverse ? this.nodeIds.reverse().join('<-') : this.nodeIds.join('->');
26
+ }
27
+
28
+ public getTracePathInfo(joinStr: string = '===>') : string {
29
+ if (this.nodeIds.length === 0) {
30
+ return '';
31
+ }
32
+ const snapshot = this.tracePathItem.node?.snapshot;
33
+ if (!snapshot) {
34
+ Log.error('meminsight', ArkNodeTracePath.name, 'Failed to get snapshot.');
35
+ return '';
36
+ }
37
+ const result: string[] = [];
38
+ this.nodeIds.forEach(id => {
39
+ let node = snapshot.getNodeById(id);
40
+ if (node) {
41
+ let sizeStr = node.retainedSize >= 1024 * 1024 ?
42
+ (node.retainedSize / (1024 * 1024)).toFixed(2) + 'MB' :
43
+ (node.retainedSize / 1024).toFixed(2) + 'KB';
44
+ result.push(`@${id} (${node.type}) [${sizeStr}] ${node.name}`)
45
+ }
46
+ })
47
+ return result.join(`\n${joinStr}`);
48
+ }
49
+
50
+ public static getNodeIdTrace(item: ArkTracePathItem) : number[] {
51
+ const nodeIds: number[] = [];
52
+ let curItem : ArkTracePathItem | undefined = item;
53
+ while (curItem) {
54
+ let node: IHeapNode | undefined = curItem.node;
55
+ if (!node) {
56
+ break;
57
+ }
58
+ nodeIds.push(node.id);
59
+ curItem = curItem.next;
60
+ }
61
+ return nodeIds;
62
+ }
63
+
64
+ /**
65
+ * 获取本路径上的节点retained size
66
+ * @param item
67
+ * @returns
68
+ */
69
+ public static getRetainedSize(item: ArkTracePathItem) : number {
70
+ let retained: number = 0;
71
+ let curItem : ArkTracePathItem | undefined = item;
72
+ while (curItem) {
73
+ let node: IHeapNode | undefined = curItem.node;
74
+ if (!node) {
75
+ break;
76
+ }
77
+ retained += node.self_size;
78
+ curItem = curItem.next;
79
+ }
80
+ return retained;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * ArkNode最短路径uid和最短路径索引关系对象
86
+ */
87
+ export class ArkNodePathIndexes {
88
+ public readonly uid: string;
89
+ public indexes: number[];
90
+
91
+ constructor(uid: string) {
92
+ this.uid = uid;
93
+ }
94
+
95
+ public addIndex(index: number) {
96
+ this.indexes.push(index);
97
+ }
98
+
99
+ public get(filter: (condition: any) => boolean, condition: any) {
100
+ return this.indexes.filter((index) => {
101
+ return filter(condition);
102
+ });
103
+ }
104
+ }
105
+
106
+ /**
107
+ * ArkNodes
108
+ */
109
+ export class ArkNodes extends Array<number> implements ILoggable {
110
+ public readonly DOMAIN: string = 'meminsight';
111
+ public readonly TAG: string = ArkNodes.name;
112
+ public className: string = ''; // node class name
113
+ public selfSize: number = 0; // node total self size
114
+ public retainedSize: number = 0; // node total retained size
115
+ public shortestPaths: ArkNodeTracePath[]; // shortest path array of all node with same calss name
116
+ public aggrationPathIndexes: Map<string, ArkNodePathIndexes>; // <path uid, path indexes>
117
+
118
+ /**
119
+ * ArkNodes constructor
120
+ * @param className node name
121
+ * @param selfSize node self size
122
+ * @param retainedSize node retained size
123
+ */
124
+ constructor(className: string, selfSize: number, retainedSize: number) {
125
+ super();
126
+ this.className = className;
127
+ this.selfSize = selfSize;
128
+ this.retainedSize = retainedSize;
129
+ }
130
+
131
+ /**
132
+ * add shortest node trace path
133
+ * @param path shortest node trace path
134
+ */
135
+ public addShortestPath(path: ArkNodeTracePath) : void {
136
+ this.shortestPaths.push(path);
137
+ }
138
+
139
+ /**
140
+ * add node id
141
+ * @param nodeId node id
142
+ */
143
+ public addNodeId(nodeId: number) : void {
144
+ this.push(nodeId);
145
+ }
146
+
147
+ /**
148
+ * 获取最短路径uid和最短路径索引和引用次数的映射数据
149
+ * uid = [...node.id].join('->')
150
+ * @param descending 按引用次数降序排序,否则升序排序
151
+ * @param maxIndexLength 最大引用路径个数
152
+ */
153
+ public getAggrationPathIndexes(descending: boolean = true,
154
+ maxIndexLength: number = -1, minRetainedSize = -1) : void {
155
+ if (this.shortestPaths.length === 0) {
156
+ Log.warnX(this, 'No shortest path found');
157
+ return;
158
+ }
159
+ const indexes = new Map<string, ArkNodePathIndexes>();
160
+ this.shortestPaths.forEach((path, index) => {
161
+ const indexesItem = indexes.get(path.uid);
162
+ if (!indexesItem) {
163
+ const pathIndexes = new ArkNodePathIndexes(path.uid);
164
+ pathIndexes.addIndex(index);
165
+ indexes.set(path.uid, pathIndexes);
166
+ } else {
167
+ indexesItem.addIndex(index);
168
+ }
169
+ });
170
+ const sortedIndexes = descending
171
+ ? new Map([...indexes.entries()].sort((a, b) => a[1].indexes.length - b[1].indexes.length))
172
+ : new Map([...indexes.entries()].sort((a, b) => b[1].indexes.length - a[1].indexes.length));
173
+ if ((maxIndexLength <= 0) || (sortedIndexes.size <= maxIndexLength)) {
174
+ this.aggrationPathIndexes = sortedIndexes;
175
+ } else {
176
+ this.aggrationPathIndexes = new Map([...sortedIndexes.entries()].slice(0, maxIndexLength));
177
+ }
178
+ if (minRetainedSize > 0) {
179
+ this.aggrationPathIndexes.forEach((pathIndexes, uid) => {
180
+ const tmpIndexes: number[] = [];
181
+ let idx = pathIndexes.indexes.pop();
182
+ while (idx) {
183
+ let size = this.shortestPaths.at(idx)?.retainedSize || 0;
184
+ if (size >= minRetainedSize) {
185
+ tmpIndexes.push(idx);
186
+ }
187
+ idx = pathIndexes.indexes.pop();
188
+ }
189
+ pathIndexes.indexes = tmpIndexes;
190
+ });
191
+ }
192
+ }
193
+
194
+ public getPathIndexesRetainedSize(uid: string) : number {
195
+ let pathIndexes = this.aggrationPathIndexes.get(uid);
196
+ if (!pathIndexes) {
197
+ return 0;
198
+ }
199
+ let preSize = 0;
200
+ let curSize: number | undefined;
201
+ return pathIndexes.indexes.reduce((pre, cur) => {
202
+ curSize = this.shortestPaths.at(cur)?.retainedSize;
203
+ if (!curSize) {
204
+ return preSize;
205
+ }
206
+ preSize = curSize;
207
+ return preSize + curSize;
208
+ }, 0);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * ArkNodes Map<nodeName, nodeId array>
214
+ */
215
+ export class ArkNodesMap extends Map<string, ArkNodes> implements ILoggable {
216
+ DOMAIN: string = 'meminsight'
217
+ TAG: string = ArkNodesMap.name;
218
+
219
+ constructor() {
220
+ super();
221
+ }
222
+
223
+ /**
224
+ * add node to map
225
+ * @param snapshot heap snapshot
226
+ * @param node heap node
227
+ * @param nameWhiteList node names
228
+ * @param typeWhiteList node types
229
+ * @param enableShortestPath enable shortest path
230
+ */
231
+ public addNode(
232
+ snapshot: IHeapSnapshot,
233
+ node: IHeapNode,
234
+ nameWhiteList: string[],
235
+ typeWhiteList: string[] = ['object'],
236
+ enableShortestPath: boolean = true) : void {
237
+ // true if names is empty or types include node.type
238
+ if ((nameWhiteList.length == 0
239
+ || nameWhiteList.includes(node.name))
240
+ && typeWhiteList.includes(node.type)) {
241
+ const arkNodes: ArkNodes | undefined = this.get(node.name);
242
+ if (!arkNodes) {
243
+ let arkNodes: ArkNodes = new ArkNodes(node.name, node.self_size, node.retainedSize);
244
+ if (enableShortestPath) {
245
+ const arkTracePathItem: ArkTracePathItem | undefined = Finder.findShortestPathToGCRoot(snapshot, node);
246
+ if (arkTracePathItem) {
247
+ arkNodes.addShortestPath(new ArkNodeTracePath(arkTracePathItem));
248
+ } else {
249
+ Log.errorX(this, 'ArkNodesMap1: Finder.findShortestPathToGCRoot return undefined.');
250
+ }
251
+ }
252
+ arkNodes.addNodeId(node.id);
253
+ this.set(node.name, arkNodes);
254
+ } else {
255
+ arkNodes.selfSize += node.self_size;
256
+ arkNodes.retainedSize += node.retainedSize;
257
+ if (enableShortestPath) {
258
+ const arkTracePathItem: ArkTracePathItem | undefined = Finder.findShortestPathToGCRoot(snapshot, node);
259
+ if (arkTracePathItem) {
260
+ arkNodes.addShortestPath(new ArkNodeTracePath(arkTracePathItem));
261
+ } else {
262
+ Log.errorX(this, 'ArkNodesMap2: Finder.findShortestPathToGCRoot return undefined.');
263
+ }
264
+ }
265
+ arkNodes.addNodeId(node.id);
266
+ }
267
+ }
268
+ }
269
+ }
@@ -0,0 +1,42 @@
1
+ import { serializer } from "@memlab/core"
2
+ import { ArkTracePathItem } from "./ArkTracePath";
3
+
4
+ /**
5
+ * ArkTracePath Filter function
6
+ */
7
+ export type ArkTracePathFilter = (condition?: any) => boolean;
8
+ /**
9
+ * ArkTracePath Output function
10
+ */
11
+ export type ArkTracePathOutput = (path: ArkTracePathItem, filter?: ArkTracePathFilter, condition?: any) => string;
12
+
13
+ const defaultOutput: ArkTracePathOutput = (path: ArkTracePathItem) => {
14
+ let snapshot = path?.node?.snapshot;
15
+ return snapshot ? serializer.summarizePath(path, new Set<number>(), snapshot, {color: true}) : '';
16
+ };
17
+
18
+ /**
19
+ * ArkTracer
20
+ */
21
+ export class ArkTracer {
22
+ public static outputTracePath(
23
+ tracePath: ArkTracePathItem,
24
+ output?: ArkTracePathOutput,
25
+ filter?: ArkTracePathFilter,
26
+ condition?: any) : void {
27
+ let result: string = output ? output(tracePath, filter, condition) : defaultOutput(tracePath);
28
+ if (result !== '') {
29
+ console.log(result);
30
+ } else {
31
+ console.log('No leak trace path.');
32
+ }
33
+ }
34
+
35
+ public static getTracePathString(
36
+ tracePath: ArkTracePathItem,
37
+ output?: ArkTracePathOutput,
38
+ filter?: ArkTracePathFilter,
39
+ condition?: any) : string {
40
+ return output ? output(tracePath, filter, condition) : defaultOutput(tracePath);
41
+ }
42
+ }