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,631 @@
1
+ import * as path from 'path';
2
+ import * as fs from 'fs';
3
+ import { IHeapSnapshot, IHeapNode, IHeapEdge } from "@memlab/core";
4
+ import { getFullHeapFromFile } from "@memlab/heap-analysis";
5
+ import { ArkAnalyzer } from "./ArkAnalyzer";
6
+ import { Finder } from '../utils/Finder';
7
+ import { Log, ILoggable } from "../utils/Log";
8
+ import {
9
+ AggregationByClassName,
10
+ AggregationByShortestPath,
11
+ AggregationByProperty,
12
+ StatisticsByClassName,
13
+ StatisticsByDistance,
14
+ IBaseInfo,
15
+ ClassInfo,
16
+ DistanceInfo,
17
+ ShortestPathInfo,
18
+ PropertyInfo
19
+ } from "./AnalysisInfo";
20
+ import { ReporterHTML, ReportData } from '../report/Reporter';
21
+
22
+ export enum ArkXSortType {
23
+ BY_RETAINED_SIZE,
24
+ BY_COUNT
25
+ }
26
+
27
+ export type ArkXAnalyzerConfig = {
28
+ topN: number;
29
+ sortType: ArkXSortType;
30
+ tags: string[];
31
+ };
32
+
33
+ /**
34
+ * Ark Heapsnapshot 统计分析器
35
+ */
36
+ export class ArkXAnalyzer extends ArkAnalyzer implements ILoggable {
37
+ public readonly DOMAIN = 'meminsight';
38
+ public readonly TAG = ArkXAnalyzer.name;
39
+ public static readonly NAME = 'ark-x-analyzer';
40
+ public static readonly DESC = "Analyze heapsnapshot files and aggregate & statistics.";
41
+
42
+ private snapshotFiles: string[];
43
+ private config: ArkXAnalyzerConfig;
44
+
45
+ constructor() {
46
+ super();
47
+ this.type = 'ark-x-analyzer';
48
+ this.config = {
49
+ topN: 10,
50
+ sortType: ArkXSortType.BY_RETAINED_SIZE,
51
+ tags: []
52
+ }
53
+ }
54
+
55
+ public setConfig(config: ArkXAnalyzerConfig) {
56
+ this.config = config;
57
+ }
58
+
59
+ public override async analyze(args: any[]) {
60
+ let file = args[0] as string;
61
+ let list = args[1] as string[];
62
+ let topN = args[2] as number;
63
+ let sortType = args[3] as number;
64
+ let tags = args[4] as string[];
65
+
66
+ // 处理文件路径
67
+ this.snapshotFiles = [];
68
+ if (file !== undefined && file !== '') {
69
+ this.snapshotFiles.push(path.resolve(file));
70
+ }
71
+ if (list !== undefined && list.length > 0) {
72
+ list.forEach(item => {
73
+ let itemPath = path.resolve(item);
74
+ if (this.snapshotFiles.indexOf(itemPath) === -1) {
75
+ this.snapshotFiles.push(itemPath);
76
+ }
77
+ });
78
+ }
79
+
80
+ console.info(`Heapsnapshot Files:`);
81
+ this.snapshotFiles.forEach(item => {
82
+ console.info(item);
83
+ })
84
+
85
+ // 设置 config
86
+ this.config.topN = topN <= 0 ? 10 : topN;
87
+ this.config.sortType = sortType == 0 ? ArkXSortType.BY_RETAINED_SIZE : ArkXSortType.BY_COUNT;
88
+ console.log(`TopN: ${topN}\nSortType: ${this.config.sortType.toString()}`);
89
+ if (tags != undefined && tags.length > 0) {
90
+ this.config.tags = tags;
91
+ console.info(`Tags:`);
92
+ tags.forEach(tag => {
93
+ console.info(tag);
94
+ });
95
+ }
96
+
97
+ for (let i = 0; i < this.snapshotFiles.length; i++) {
98
+ let filepath = this.snapshotFiles.at(i)!!;
99
+ if (!fs.existsSync(filepath)) {
100
+ Log.errorX(this, `File not found, ${filepath}`);
101
+ return;
102
+ }
103
+ let infos = await this.analyzeOne(filepath);
104
+ let datas: ReportData[] = [];
105
+ infos.forEach((info) => {
106
+ let data = info.toReportData();
107
+ datas.push(data);
108
+ });
109
+ ReporterHTML.generateByTemplate(datas, path.basename(filepath));
110
+ }
111
+ }
112
+
113
+ private async analyzeOne(filepath: string) : Promise<IBaseInfo[]> {
114
+ let snapshot: IHeapSnapshot;
115
+ const results: IBaseInfo[] = []
116
+ try {
117
+ console.info("Start to parse heapsnapshot: ", filepath);
118
+ snapshot = await getFullHeapFromFile(filepath);
119
+ if (snapshot === undefined) {
120
+ console.info(`Failed to parse heapsnapshot: ${filepath}, snapshot is undefined.`);
121
+ return results;
122
+ }
123
+ console.info('Success to parse heapsnapshot: ', filepath);
124
+
125
+ let result: IBaseInfo;
126
+ let TopN = this.config.topN;
127
+
128
+ // 同类名的基础对象统计&聚合 (如 JSObject, global_env...)
129
+ const baseObjectWithSameNameList = [
130
+ 'JSObject', 'js_shared_object',
131
+ 'js_array', 'js_map', 'js_set',
132
+ 'js_proxy', 'js_native_pointer', 'js_weak_ref'];
133
+ baseObjectWithSameNameList.forEach(objectName => {
134
+ result = this.statSpecificObjectNameByDistance(snapshot, objectName);
135
+ results.push(result);
136
+
137
+ result = this.aggregateSpecificObjectNameByReference(snapshot, objectName, TopN);
138
+ results.push(result);
139
+
140
+ result = this.aggregateSpecificObjectNameByDistance1(snapshot, objectName, TopN);
141
+ results.push(result);
142
+ });
143
+
144
+ // 同类型的基础对象统计&聚合
145
+ const baseObjectWithSameTypeList = ['string', 'system', 'framework', 'array'];
146
+ baseObjectWithSameTypeList.forEach(objectType => {
147
+ result = this.aggregateSpecificObjectInfoByClassName(snapshot, objectType, [], TopN);
148
+ results.push(result);
149
+
150
+ result = this.aggregateSpecificObjectInfoByReference(snapshot, '', objectType, [], TopN);
151
+ results.push(result);
152
+ });
153
+
154
+ // 业务对象统计&聚合
155
+ const tags: string[] = this.config.tags;
156
+ if (tags.length > 0) {
157
+ result = this.aggregateSpecificObjectInfoByClassName(snapshot, '', tags, TopN);
158
+ results.push(result);
159
+
160
+ result = this.aggregateSpecificObjectInfoByReference(snapshot, '', '', tags, TopN);
161
+ results.push(result);
162
+ }
163
+
164
+ return results;
165
+
166
+ } catch(error) {
167
+ console.error('Failed to parse heapsnapshot, ', error);
168
+ } finally {
169
+ // TODO:
170
+ }
171
+
172
+ return [] as IBaseInfo[];
173
+ }
174
+
175
+ /**
176
+ * 指定类名对象按 Distance 统计
177
+ */
178
+ public statSpecificObjectNameByDistance(snapshot: IHeapSnapshot, objectName: string): IBaseInfo {
179
+ console.log(`\n\n========================${objectName} 对象按 Distance 统计=========================\n`)
180
+ let totalHeapSize: number = 0;
181
+ let totalRetainSize: number = 0;
182
+ let totalSelfSize: number = 0;
183
+ let totalCount = 0;
184
+ let countMap: Map<number, number> = new Map();
185
+ let retainSizeMap: Map<number, number> = new Map();
186
+ let selfSizeMap: Map<number, number> = new Map();
187
+ snapshot.nodes.forEach((node: IHeapNode) => {
188
+ totalHeapSize += node.self_size;
189
+ if (node.name === objectName) {
190
+ totalCount++;
191
+ totalRetainSize += node.retainedSize;
192
+ totalSelfSize += node.self_size;
193
+ let distance = Finder.getShortestPath(node)[2]
194
+ countMap.set(distance, (countMap.get(distance) || 0) + 1)
195
+ selfSizeMap.set(distance, (selfSizeMap.get(distance) || 0) + node.self_size)
196
+ retainSizeMap.set(distance, (retainSizeMap.get(distance) || 0) + node.retainedSize)
197
+ }
198
+ })
199
+
200
+ console.log(`Total Heap Size: ${(totalHeapSize / 1024 / 1024).toFixed(2)} MB`);
201
+ console.log(`Total ${objectName} Self Size: ${(totalSelfSize / 1024).toFixed(2)} KB, Ratio: ${(totalSelfSize / totalHeapSize * 100).toFixed(2)}%`);
202
+ console.log(`Total ${objectName} Retained Size: ${(totalRetainSize / 1024).toFixed(2)} KB, Ratio: ${(totalRetainSize / totalHeapSize * 100).toFixed(2)}%`);
203
+ console.log(`Total ${objectName} Count: ${totalCount}`);
204
+
205
+ // 按距离统计取全部数据
206
+ let sortedeMap: Map<number, number>;
207
+ if (this.config.sortType === ArkXSortType.BY_COUNT) {
208
+ console.log(`\n${objectName} Info By Count:`);
209
+ sortedeMap = new Map([...countMap].sort((a, b) => b[1] - a[1]));
210
+ } else {
211
+ console.log(`\n${objectName} Info By Retained Size:`);
212
+ sortedeMap = new Map([...retainSizeMap].sort((a, b) => b[1] - a[1]));
213
+ }
214
+
215
+ let num = 1;
216
+ let distanceInfos: DistanceInfo[] = [];
217
+ sortedeMap.forEach((value, key) => {
218
+ let distanceInfo: DistanceInfo = new DistanceInfo();
219
+ distanceInfo.objectNameOrType = objectName;
220
+ distanceInfo.count = value;
221
+ distanceInfo.distance = key;
222
+ distanceInfo.retainedSize = retainSizeMap.get(key)!!;
223
+ distanceInfo.selfSize = selfSizeMap.get(key)!!;
224
+ distanceInfo.retainedSizeTotalHeapPercent = distanceInfo.retainedSize / totalHeapSize * 100;
225
+ distanceInfo.selfSizeTotalHeapPercent = distanceInfo.selfSize / totalHeapSize * 100;
226
+ distanceInfo.retainedSizeTotalRetainedPercent = distanceInfo.retainedSize / totalRetainSize * 100;
227
+ distanceInfo.selfSizeTotalSelfPercent = distanceInfo.selfSize / totalSelfSize * 100;
228
+ distanceInfos.push(distanceInfo);
229
+
230
+ console.log(`Index-${num++}, ${distanceInfo.toString()}`);
231
+ });
232
+
233
+ let targetNode: StatisticsByDistance = new StatisticsByDistance();
234
+ targetNode.type = objectName;
235
+ targetNode.desc = `指定类名对象按 Distance 统计`;
236
+ targetNode.totalCount = totalCount;
237
+ targetNode.totalHeapSize = totalHeapSize;
238
+ targetNode.totalRetainedSize = totalRetainSize;
239
+ targetNode.totalSelfSize = totalSelfSize;
240
+ targetNode.distanceInfos = distanceInfos;
241
+ return targetNode;
242
+ }
243
+
244
+ /**
245
+ * 指定类名的最短引用链聚合
246
+ * @param snapshot 快照
247
+ * @param topNum TopN
248
+ */
249
+ public aggregateSpecificObjectNameByReference(snapshot: IHeapSnapshot, objectName: string, topNum: number = 10): IBaseInfo {
250
+ console.log(`\n\n========================${objectName} 对象按最短引用链聚合=========================\n`)
251
+ let totalHeapSize: number = 0;
252
+ let totalRetainSize: number = 0;
253
+ let totalSelfSize: number = 0;
254
+ let totalCount = 0;
255
+ let countMap: Map<string, number> = new Map();
256
+ let retainSizeMap: Map<string, number> = new Map();
257
+ let selfSizeMap: Map<string, number> = new Map();
258
+
259
+ snapshot.nodes.forEach((node: IHeapNode) => {
260
+ totalHeapSize += node.self_size;
261
+ if (node.name === objectName) {
262
+ totalCount++;
263
+ totalRetainSize += node.retainedSize;
264
+ totalSelfSize += node.self_size;
265
+ let [shortestPathIds, shortestPathStrs] = Finder.getShortestPath(node);
266
+ let shortestPathStr = shortestPathStrs.join(' <- ')
267
+ countMap.set(shortestPathStr, (countMap.get(shortestPathStr) || 0) + 1)
268
+ retainSizeMap.set(shortestPathStr, (retainSizeMap.get(shortestPathStr) || 0) + node.retainedSize);
269
+ selfSizeMap.set(shortestPathStr, (selfSizeMap.get(shortestPathStr) || 0) + node.self_size);
270
+ }
271
+ });
272
+ console.log(`Total Heap Size: ${(totalHeapSize / 1024 / 1024).toFixed(2)} MB`);
273
+ console.log(`Total ${objectName} Self Size: ${(totalSelfSize / 1024).toFixed(2)} KB, Ratio: ${(totalSelfSize / totalHeapSize * 100).toFixed(2)}%`);
274
+ console.log(`Total ${objectName} Retained Size: ${(totalRetainSize / 1024).toFixed(2)} KB, Ratio: ${(totalRetainSize / totalHeapSize * 100).toFixed(2)}%`);
275
+ console.log(`Total ${objectName} Count: ${totalCount}`);
276
+
277
+ let sortedeMap: Map<string, number>;
278
+ if (this.config.sortType === ArkXSortType.BY_COUNT) {
279
+ console.log(`\n${objectName} Info By Count:`);
280
+ sortedeMap = countMap.size < topNum
281
+ ? new Map([...countMap].sort((a, b) => b[1] - a[1]))
282
+ : new Map([...countMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
283
+ } else {
284
+ console.log(`\n${objectName} Info By Retained Size:`);
285
+ sortedeMap = retainSizeMap.size < topNum
286
+ ? new Map([...retainSizeMap].sort((a, b) => b[1] - a[1]))
287
+ : new Map([...retainSizeMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
288
+ }
289
+
290
+ let num = 1;
291
+ let shortestPathInfos: ShortestPathInfo[] = [];
292
+ sortedeMap.forEach((_, key) => {
293
+ let info: ShortestPathInfo = new ShortestPathInfo();
294
+ info.shortestPathStr = key;
295
+ info.count = countMap.get(key)!!;
296
+ info.retainedSize = retainSizeMap.get(key)!!;
297
+ info.selfSize = selfSizeMap.get(key)!!;
298
+ info.retainedSizeTotalHeapPercent = info.retainedSize / totalHeapSize * 100;
299
+ info.selfSizeTotalHeapPercent = info.selfSize / totalHeapSize * 100;
300
+ info.retainedSizeTotalRetainedPercent = info.retainedSize / totalRetainSize * 100;
301
+ info.selfSizeTotalSelfPercent = info.selfSize / totalSelfSize * 100;
302
+ shortestPathInfos.push(info);
303
+ console.log(`Index-${num++}, ${info.toString()}`);
304
+ });
305
+
306
+ let targetNode: AggregationByShortestPath = new AggregationByShortestPath();
307
+ targetNode.shortestPathInfos = [];
308
+ targetNode.type = objectName;
309
+ targetNode.desc = `按最短引用链聚合`
310
+ targetNode.totalHeapSize = totalHeapSize;
311
+ targetNode.totalRetainedSize = totalRetainSize;
312
+ targetNode.totalSelfSize = totalSelfSize;
313
+ targetNode.totalCount = totalCount;
314
+ targetNode.shortestPathInfos = shortestPathInfos;
315
+ return targetNode;
316
+ }
317
+
318
+ /**
319
+ * 指定类名的Distance=1的对象属性按最短引用链聚合
320
+ * @param snapshot 快照
321
+ * @param objectName
322
+ * @param topNum TopN
323
+ */
324
+ public aggregateSpecificObjectNameByDistance1(snapshot: IHeapSnapshot, objectName: string, topNum: number = 10, maxRetainedSize: number = -1): IBaseInfo {
325
+ console.log(`\n========================${objectName} 按 Distance=1 的对象属性聚合分析=========================\n`)
326
+ let totalHeapSize: number = 0;
327
+ let totalRetainedSize: number = 0;
328
+ let totalSelfSize: number = 0;
329
+ let totalCount = 0;
330
+ let countMap: Map<string, number> = new Map();
331
+ let retainedSizeMap: Map<string, number> = new Map();
332
+ let selfSizeMap: Map<string, number> = new Map();
333
+ snapshot.nodes.forEach((node: IHeapNode) => {
334
+ totalHeapSize += node.self_size;
335
+ if (node.name !== objectName) {
336
+ return;
337
+ }
338
+
339
+ if (node.retainedSize / 1024 < maxRetainedSize) {
340
+ return;
341
+ }
342
+
343
+ let distance = Finder.getShortestPath(node)[2];
344
+ if (distance > 1) {
345
+ return;
346
+ }
347
+ node.forEachReference((edge: IHeapEdge) => {
348
+ let toNode = edge.toNode;
349
+ totalRetainedSize += toNode.retainedSize;
350
+ totalSelfSize += toNode.self_size;
351
+ totalCount++;
352
+ let [shortestPathIds, shortestPathStrs, distance] = Finder.getShortestPath(toNode);
353
+ let shortestPath = shortestPathStrs.join(' <- ');
354
+ countMap.set(shortestPath, (countMap.get(shortestPath) || 0) + 1);
355
+ retainedSizeMap.set(shortestPath, (retainedSizeMap.get(shortestPath) || 0) + toNode.retainedSize)
356
+ selfSizeMap.set(shortestPath, (selfSizeMap.get(shortestPath) || 0) + toNode.self_size)
357
+ });
358
+ });
359
+
360
+ // 打印总堆大小
361
+ console.log(`Total Heap Size: ${(totalHeapSize / 1024 / 1024).toFixed(2)} MB`)
362
+ console.log(`All Properties Retained Size in ${objectName} Distance=1: ${(totalRetainedSize / 1024).toFixed(2)} KB, Ratio: ${(totalRetainedSize / totalHeapSize * 100).toFixed(2)}%`)
363
+ console.log(`All Properties Self Size in ${objectName} Distance=1: ${(totalSelfSize / 1024).toFixed(2)} KB, Ratio: ${(totalSelfSize / totalHeapSize * 100).toFixed(2)}%`)
364
+ console.log(`Count of Properties in ${objectName} Distance=1: ${totalCount}`);
365
+
366
+ let sortedeMap: Map<string, number>;
367
+ if (this.config.sortType === ArkXSortType.BY_COUNT) {
368
+ console.log(`\n${objectName} Info By Count:`);
369
+ sortedeMap = countMap.size < topNum
370
+ ? new Map([...countMap].sort((a, b) => b[1] - a[1]))
371
+ : new Map([...countMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
372
+ } else {
373
+ console.log(`\n${objectName} Info By Retained Size:`);
374
+ sortedeMap = retainedSizeMap.size < topNum
375
+ ? new Map([...retainedSizeMap].sort((a, b) => b[1] - a[1]))
376
+ : new Map([...retainedSizeMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
377
+ }
378
+
379
+ let propInfos: PropertyInfo[] = [];
380
+ let num: number = 1;
381
+ sortedeMap.forEach((_, key) => {
382
+ let propInfo: PropertyInfo = new PropertyInfo();
383
+ propInfo.propName = '-';
384
+ propInfo.propShortestPath = key;
385
+ propInfo.retainedSize = retainedSizeMap.get(key)!!;
386
+ propInfo.selfSize = selfSizeMap.get(key)!!;
387
+ propInfo.count = countMap.get(key)!!;
388
+ propInfo.retainedSizeTotalHeapPercent = propInfo.retainedSize / totalHeapSize * 100;
389
+ propInfo.selfSizeTotalHeapPercent = propInfo.selfSize / totalHeapSize * 100;
390
+ propInfo.retainedSizeTotalRetainedPercent = propInfo.retainedSize / totalRetainedSize * 100;
391
+ propInfo.selfSizeTotalSelfPercent = propInfo.selfSize / totalSelfSize * 100;
392
+ propInfos.push(propInfo);
393
+ console.log(`${num++}.${propInfo.toString()}`);
394
+ });
395
+
396
+ let targetNode: AggregationByProperty = new AggregationByProperty();
397
+ targetNode.type = objectName;
398
+ targetNode.desc = `按 Distance=1 的对象属性聚合`;
399
+ targetNode.totalCount = totalCount;
400
+ targetNode.totalHeapSize = totalHeapSize;
401
+ targetNode.totalRetainedSize = totalRetainedSize;
402
+ targetNode.totalSelfSize = totalSelfSize;
403
+ targetNode.propertyInfos = propInfos;
404
+ return targetNode;
405
+ }
406
+
407
+ /**
408
+ * 指定类名的最短引用链聚合
409
+ * @param snapshot 快照
410
+ * @param topNum TopN
411
+ */
412
+ public aggregateSpecificObjectTypeByReference(snapshot: IHeapSnapshot, objectType: string, topNum: number = 10): IBaseInfo {
413
+ console.log(`\n\n========================${objectType} 类型的对象按最短引用链聚合=========================\n`)
414
+ let totalHeapSize: number = 0;
415
+ let totalRetainSize: number = 0;
416
+ let totalSelfSize: number = 0;
417
+ let totalCount = 0;
418
+ let countMap: Map<string, number> = new Map();
419
+ let retainSizeMap: Map<string, number> = new Map();
420
+ let selfSizeMap: Map<string, number> = new Map();
421
+
422
+ snapshot.nodes.forEach((node: IHeapNode) => {
423
+ totalHeapSize += node.self_size;
424
+ if (node.type === objectType) {
425
+ totalCount++;
426
+ totalRetainSize += node.retainedSize;
427
+ totalSelfSize += node.self_size;
428
+ let [shortestPathIds, shortestPathStrs] = Finder.getShortestPath(node);
429
+ let shortestPathStr = shortestPathStrs.join(' <- ')
430
+ countMap.set(shortestPathStr, (countMap.get(shortestPathStr) || 0) + 1)
431
+ retainSizeMap.set(shortestPathStr, (retainSizeMap.get(shortestPathStr) || 0) + node.retainedSize);
432
+ selfSizeMap.set(shortestPathStr, (selfSizeMap.get(shortestPathStr) || 0) + node.self_size);
433
+ }
434
+ });
435
+ console.log(`Total Heap Size: ${(totalHeapSize / 1024 / 1024).toFixed(2)} MB`);
436
+ console.log(`Total Type ${objectType} Self Size: ${(totalSelfSize / 1024).toFixed(2)} KB, Ratio: ${(totalSelfSize / totalHeapSize * 100).toFixed(2)}%`);
437
+ console.log(`Total Type ${objectType} Retained Size: ${(totalRetainSize / 1024).toFixed(2)} KB, Ratio: ${(totalRetainSize / totalHeapSize * 100).toFixed(2)}%`);
438
+ console.log(`Total Type ${objectType} Count: ${totalCount}`);
439
+
440
+ let sortedeMap: Map<string, number>;
441
+ if (this.config.sortType === ArkXSortType.BY_COUNT) {
442
+ console.log(`\nType ${objectType} Info By Count:`);
443
+ sortedeMap = countMap.size < topNum
444
+ ? new Map([...countMap].sort((a, b) => b[1] - a[1]))
445
+ : new Map([...countMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
446
+ } else {
447
+ console.log(`\nType ${objectType} Info By Retained Size:`);
448
+ sortedeMap = retainSizeMap.size < topNum
449
+ ? new Map([...retainSizeMap].sort((a, b) => b[1] - a[1]))
450
+ : new Map([...retainSizeMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
451
+ }
452
+
453
+ let num = 1;
454
+ let shortestPathInfos: ShortestPathInfo[] = [];
455
+ sortedeMap.forEach((_, key) => {
456
+ let info: ShortestPathInfo = new ShortestPathInfo();
457
+ info.shortestPathStr = key;
458
+ info.count = countMap.get(key)!!;
459
+ info.retainedSize = retainSizeMap.get(key)!!;
460
+ info.selfSize = selfSizeMap.get(key)!!;
461
+ info.retainedSizeTotalHeapPercent = info.retainedSize / totalHeapSize * 100;
462
+ info.selfSizeTotalHeapPercent = info.selfSize / totalHeapSize * 100;
463
+ info.retainedSizeTotalRetainedPercent = info.retainedSize / totalRetainSize * 100;
464
+ info.selfSizeTotalSelfPercent = info.selfSize / totalSelfSize * 100;
465
+ shortestPathInfos.push(info);
466
+ console.log(`Index-${num++}, ${info.toString()}`);
467
+ });
468
+
469
+ let targetNode: AggregationByShortestPath = new AggregationByShortestPath();
470
+ targetNode.type = objectType;
471
+ targetNode.desc = `按最短引用链聚合`
472
+ targetNode.totalHeapSize = totalHeapSize;
473
+ targetNode.totalRetainedSize = totalRetainSize;
474
+ targetNode.totalSelfSize = totalSelfSize;
475
+ targetNode.totalCount = totalCount;
476
+ targetNode.shortestPathInfos = shortestPathInfos;
477
+ return targetNode;
478
+ }
479
+
480
+ /**
481
+ * 指定类名/类型/标签的最短引用链聚合
482
+ * @param snapshot 快照
483
+ * @param objectName 对象类名,为空则不判断
484
+ * @param objectType 对象类型,为空则不判断
485
+ * @param tags 对象标签,比如业务标签
486
+ * @param topNum TopN
487
+ */
488
+ public aggregateSpecificObjectInfoByReference(snapshot: IHeapSnapshot, objectName: string, objectType: string, tags: string[], topNum: number = 10): IBaseInfo {
489
+ console.log(`\n\n========================Name:${objectName}-Type:${objectType}-Tags:${tags} 对象按最短引用链聚合=========================\n`)
490
+ let totalHeapSize: number = 0;
491
+ let totalRetainSize: number = 0;
492
+ let totalSelfSize: number = 0;
493
+ let totalCount = 0;
494
+ let countMap: Map<string, number> = new Map();
495
+ let retainSizeMap: Map<string, number> = new Map();
496
+ let selfSizeMap: Map<string, number> = new Map();
497
+
498
+ snapshot.nodes.forEach((node: IHeapNode) => {
499
+ totalHeapSize += node.self_size;
500
+ if ((node.name === objectName)
501
+ || (node.type === objectType)
502
+ || tags.some(tag => node.name.includes(tag))) {
503
+ totalCount++;
504
+ totalRetainSize += node.retainedSize;
505
+ totalSelfSize += node.self_size;
506
+ let [shortestPathIds, shortestPathStrs] = Finder.getShortestPath(node);
507
+ let shortestPathStr = shortestPathStrs.join(' <- ')
508
+ countMap.set(shortestPathStr, (countMap.get(shortestPathStr) || 0) + 1)
509
+ retainSizeMap.set(shortestPathStr, (retainSizeMap.get(shortestPathStr) || 0) + node.retainedSize);
510
+ selfSizeMap.set(shortestPathStr, (selfSizeMap.get(shortestPathStr) || 0) + node.self_size);
511
+ }
512
+ });
513
+ console.log(`Total Heap Size: ${(totalHeapSize / 1024 / 1024).toFixed(2)} MB`);
514
+ console.log(`Total Self Size: ${(totalSelfSize / 1024).toFixed(2)} KB, Ratio: ${(totalSelfSize / totalHeapSize * 100).toFixed(2)}%`);
515
+ console.log(`Total Retained Size: ${(totalRetainSize / 1024).toFixed(2)} KB, Ratio: ${(totalRetainSize / totalHeapSize * 100).toFixed(2)}%`);
516
+ console.log(`Total Count: ${totalCount}`);
517
+
518
+ let sortedeMap: Map<string, number>;
519
+ if (this.config.sortType === ArkXSortType.BY_COUNT) {
520
+ console.log(`\nInfo By Count:`);
521
+ sortedeMap = countMap.size < topNum
522
+ ? new Map([...countMap].sort((a, b) => b[1] - a[1]))
523
+ : new Map([...countMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
524
+ } else {
525
+ console.log(`\nInfo By Retained Size:`);
526
+ sortedeMap = retainSizeMap.size < topNum
527
+ ? new Map([...retainSizeMap].sort((a, b) => b[1] - a[1]))
528
+ : new Map([...retainSizeMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
529
+ }
530
+
531
+ let num = 1;
532
+ let shortestPathInfos: ShortestPathInfo[] = [];
533
+ sortedeMap.forEach((_, key) => {
534
+ let info: ShortestPathInfo = new ShortestPathInfo();
535
+ info.shortestPathStr = key;
536
+ info.count = countMap.get(key)!!;
537
+ info.retainedSize = retainSizeMap.get(key)!!;
538
+ info.selfSize = selfSizeMap.get(key)!!;
539
+ info.retainedSizeTotalHeapPercent = info.retainedSize / totalHeapSize * 100;
540
+ info.selfSizeTotalHeapPercent = info.selfSize / totalHeapSize * 100;
541
+ info.retainedSizeTotalRetainedPercent = info.retainedSize / totalRetainSize * 100;
542
+ info.selfSizeTotalSelfPercent = info.selfSize / totalSelfSize * 100;
543
+ shortestPathInfos.push(info);
544
+ console.log(`Index-${num++}, ${info.toString()}`);
545
+ });
546
+
547
+ let targetNode: AggregationByShortestPath = new AggregationByShortestPath();
548
+ targetNode.type = `[Name:${objectName}-Type:${objectType}-Tags:${tags}]`;
549
+ targetNode.desc = `按指定类名/类型/标签的对象的最短引用链聚合`
550
+ targetNode.totalHeapSize = totalHeapSize;
551
+ targetNode.totalRetainedSize = totalRetainSize;
552
+ targetNode.totalSelfSize = totalSelfSize;
553
+ targetNode.totalCount = totalCount;
554
+ targetNode.shortestPathInfos = shortestPathInfos;
555
+ return targetNode;
556
+ }
557
+
558
+ /**
559
+ * 指定类名/类型/标签的最短引用链聚合
560
+ * @param snapshot 快照
561
+ * @param objectType 对象类型,为空则不判断
562
+ * @param tags 对象标签,比如业务标签
563
+ * @param topNum TopN
564
+ */
565
+ public aggregateSpecificObjectInfoByClassName(snapshot: IHeapSnapshot, objectType: string, tags: string[], topNum: number = 10): IBaseInfo {
566
+ console.log(`\n\n========================Type:${objectType}-Tags:${tags} 对象按对象名称聚合=========================\n`)
567
+ let totalHeapSize: number = 0;
568
+ let totalRetainSize: number = 0;
569
+ let totalSelfSize: number = 0;
570
+ let totalCount = 0;
571
+ let countMap: Map<string, number> = new Map();
572
+ let retainSizeMap: Map<string, number> = new Map();
573
+ let selfSizeMap: Map<string, number> = new Map();
574
+
575
+ snapshot.nodes.forEach((node: IHeapNode) => {
576
+ totalHeapSize += node.self_size;
577
+ if ((node.type === objectType) || tags.some(tag => node.name.includes(tag))) {
578
+ totalCount++;
579
+ totalRetainSize += node.retainedSize;
580
+ totalSelfSize += node.self_size;
581
+ let name = Finder.getClassName(node.name);
582
+ countMap.set(name, (countMap.get(name) || 0) + 1)
583
+ retainSizeMap.set(name, (retainSizeMap.get(name) || 0) + node.retainedSize);
584
+ selfSizeMap.set(name, (selfSizeMap.get(name) || 0) + node.self_size);
585
+ }
586
+ });
587
+ console.log(`Total Heap Size: ${(totalHeapSize / 1024 / 1024).toFixed(2)} MB`);
588
+ console.log(`Total Self Size: ${(totalSelfSize / 1024).toFixed(2)} KB, Ratio: ${(totalSelfSize / totalHeapSize * 100).toFixed(2)}%`);
589
+ console.log(`Total Retained Size: ${(totalRetainSize / 1024).toFixed(2)} KB, Ratio: ${(totalRetainSize / totalHeapSize * 100).toFixed(2)}%`);
590
+ console.log(`Total Count: ${totalCount}`);
591
+
592
+ let sortedeMap: Map<string, number>;
593
+ if (this.config.sortType === ArkXSortType.BY_COUNT) {
594
+ console.log(`\nInfo By Count:`);
595
+ sortedeMap = countMap.size < topNum
596
+ ? new Map([...countMap].sort((a, b) => b[1] - a[1]))
597
+ : new Map([...countMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
598
+ } else {
599
+ console.log(`\nInfo By Retained Size:`);
600
+ sortedeMap = retainSizeMap.size < topNum
601
+ ? new Map([...retainSizeMap].sort((a, b) => b[1] - a[1]))
602
+ : new Map([...retainSizeMap].sort((a, b) => b[1] - a[1]).slice(0, topNum));
603
+ }
604
+
605
+ let num = 1;
606
+ let classInfos: ClassInfo[] = [];
607
+ sortedeMap.forEach((_, key) => {
608
+ let info: ClassInfo = new ClassInfo();
609
+ info.className = key;
610
+ info.count = countMap.get(key)!!;
611
+ info.retainedSize = retainSizeMap.get(key)!!;
612
+ info.selfSize = selfSizeMap.get(key)!!;
613
+ info.retainedSizeTotalHeapPercent = info.retainedSize / totalHeapSize * 100;
614
+ info.selfSizeTotalHeapPercent = info.selfSize / totalHeapSize * 100;
615
+ info.retainedSizeTotalRetainedPercent = info.retainedSize / totalRetainSize * 100;
616
+ info.selfSizeTotalSelfPercent = info.selfSize / totalSelfSize * 100;
617
+ classInfos.push(info);
618
+ console.log(`Index-${num++}, ${info.toString()}`);
619
+ });
620
+
621
+ let targetNode: AggregationByClassName = new AggregationByClassName();
622
+ targetNode.type = `[Type:${objectType}-Tags:${tags}]`;
623
+ targetNode.desc = `按对象类型/标签聚合`;
624
+ targetNode.totalHeapSize = totalHeapSize;
625
+ targetNode.totalRetainedSize = totalRetainSize;
626
+ targetNode.totalSelfSize = totalSelfSize;
627
+ targetNode.totalCount = totalCount;
628
+ targetNode.classInfos = classInfos;
629
+ return targetNode;
630
+ }
631
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Analyzer interface
3
+ */
4
+ export interface IAnalyzer {
5
+ /**
6
+ * initialize command program
7
+ * @param proc command
8
+ */
9
+ analyze(args: any[]) : any;
10
+
11
+ /**
12
+ * set configuration
13
+ * @param cfg configuration
14
+ */
15
+ setConfig(cfg: any) : void;
16
+
17
+ /**
18
+ * get configuration
19
+ * @return configuration
20
+ */
21
+ getConfig(): any;
22
+
23
+ /**
24
+ * get type
25
+ */
26
+ getType(): string;
27
+ }