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.
- package/LICENSE +201 -0
- package/README.md +36 -0
- package/docs/Build.md +68 -0
- package/docs/Interface.md +35 -0
- package/docs/UserGuide.md +332 -0
- package/docs/resources/analyzer.png +0 -0
- package/docs/resources/cfg_filter_filetype_json.png +0 -0
- package/docs/resources/filetype_txt.png +0 -0
- package/docs/resources/framework.png +0 -0
- package/docs/resources/output.png +0 -0
- package/docs/resources/process.png +0 -0
- package/package.json +54 -0
- package/packages/cfg/ArkStatCfg.json +33 -0
- package/packages/core/jest.config.js +8 -0
- package/packages/core/package.json +61 -0
- package/packages/core/src/Index.ts +53 -0
- package/packages/core/src/analyzer/AnalysisInfo.ts +298 -0
- package/packages/core/src/analyzer/ArkAnalyzer.ts +42 -0
- package/packages/core/src/analyzer/ArkCmpCfg.ts +22 -0
- package/packages/core/src/analyzer/ArkCompareAnalyzer.ts +173 -0
- package/packages/core/src/analyzer/ArkLeakAnalyzer.ts +196 -0
- package/packages/core/src/analyzer/ArkSerializer.ts +163 -0
- package/packages/core/src/analyzer/ArkStatAnalyzer.ts +191 -0
- package/packages/core/src/analyzer/ArkStatCfg.ts +77 -0
- package/packages/core/src/analyzer/ArkTracePath.ts +269 -0
- package/packages/core/src/analyzer/ArkTracer.ts +42 -0
- package/packages/core/src/analyzer/ArkXAnalyzer.ts +631 -0
- package/packages/core/src/analyzer/IAnalyzer.ts +27 -0
- package/packages/core/src/file/FileReader.ts +82 -0
- package/packages/core/src/file/FileService.ts +50 -0
- package/packages/core/src/file/FileWriter.ts +148 -0
- package/packages/core/src/report/Reporter.ts +81 -0
- package/packages/core/src/report/templates/template.ejs +101 -0
- package/packages/core/src/report/templates/template.ts +103 -0
- package/packages/core/src/shell/DeviceShell.ts +179 -0
- package/packages/core/src/shell/Shell.ts +99 -0
- package/packages/core/src/types/Constants.ts +16 -0
- package/packages/core/src/types/LeakTypes.ts +21 -0
- package/packages/core/src/types/OhosTypes.ts +115 -0
- package/packages/core/src/utils/Common.ts +37 -0
- package/packages/core/src/utils/Finder.ts +390 -0
- package/packages/core/src/utils/Loader.ts +53 -0
- package/packages/core/src/utils/Log.ts +252 -0
- package/packages/core/src/utils/Output.ts +271 -0
- package/packages/core/tsconfig.json +10 -0
- package/packages/exampletools/package.json +52 -0
- package/packages/exampletools/src/MemTest.ts +64 -0
- package/packages/exampletools/tsconfig.json +15 -0
- package/packages/meminsight/jest.config.js +8 -0
- package/packages/meminsight/package.json +52 -0
- package/packages/meminsight/src/Index.ts +4 -0
- package/packages/meminsight/src/Version.ts +7 -0
- package/packages/meminsight/src/process/ArkCompareProc.ts +160 -0
- package/packages/meminsight/src/process/ArkGCProc.ts +61 -0
- package/packages/meminsight/src/process/ArkLeakProc.ts +47 -0
- package/packages/meminsight/src/process/ArkStatProc.ts +320 -0
- package/packages/meminsight/src/process/ArkXProc.ts +73 -0
- package/packages/meminsight/src/process/HMemXProc.ts +50 -0
- package/packages/meminsight/src/process/IProcess.ts +12 -0
- package/packages/meminsight/tsconfig.json +15 -0
- package/packages/stack/README.md +31 -0
- package/packages/stack/libs/hstack_lib-1.0.0.tgz +0 -0
- package/packages/stack/libs/hstack_lib-1.0.1.tgz +0 -0
- package/packages/stack/libs/hstack_lib-1.0.4.tgz +0 -0
- package/packages/stack/libs/lib_list.json +34 -0
- package/packages/stack/package.json +27 -0
- package/packages/stack/src/Index.js +29 -0
- package/packages/stack/src/StackTracer.js +53 -0
- package/packages/templates/ArkLeaks.template +9 -0
- package/packages/templates/ArkNodes.template +9 -0
- package/packages/templates/ArkPaths.template +9 -0
- package/test/scripts/merge.py +145 -0
- package/test/scripts/stat.py +175 -0
- package/test/test_ark_stat_proc.sh +14 -0
- package/tsconfig.base.json +38 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
|
|
2
|
+
import { TextWriter, JsonWriter, HtmlWriter } from '../file/FileWriter';
|
|
3
|
+
import { ILoggable, Log } from './Log';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* output type
|
|
7
|
+
*/
|
|
8
|
+
export enum OutputType {
|
|
9
|
+
CONSOLE = 'console',
|
|
10
|
+
TEXT = 'txt',
|
|
11
|
+
JSON = 'json',
|
|
12
|
+
HTML = 'html'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* output config
|
|
17
|
+
*/
|
|
18
|
+
export type OutputCfg = {
|
|
19
|
+
desc: any;
|
|
20
|
+
output: {
|
|
21
|
+
dir: string;
|
|
22
|
+
filename: string;
|
|
23
|
+
extension: string;
|
|
24
|
+
}
|
|
25
|
+
node?: {
|
|
26
|
+
enableId: boolean,
|
|
27
|
+
enableType: boolean,
|
|
28
|
+
enableName: boolean,
|
|
29
|
+
enableSelfSize: boolean,
|
|
30
|
+
enableRetainedSize: boolean
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* output interface
|
|
36
|
+
*/
|
|
37
|
+
export interface IOutput {
|
|
38
|
+
type: OutputType;
|
|
39
|
+
output(data: any, cfg: OutputCfg): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* console data
|
|
44
|
+
*/
|
|
45
|
+
export class ConsoleData {
|
|
46
|
+
table: boolean;
|
|
47
|
+
data: any;
|
|
48
|
+
|
|
49
|
+
constructor(table: boolean, data: any) {
|
|
50
|
+
this.table = table;
|
|
51
|
+
this.data = data;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* console table data
|
|
57
|
+
*/
|
|
58
|
+
export class ConsoleTableData {
|
|
59
|
+
title: string[];
|
|
60
|
+
data: any[];
|
|
61
|
+
|
|
62
|
+
constructor(title: string[], data: any[]) {
|
|
63
|
+
this.title = title;
|
|
64
|
+
this.data = data;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* text data
|
|
70
|
+
*/
|
|
71
|
+
export class TextData {
|
|
72
|
+
desc: any;
|
|
73
|
+
data: any;
|
|
74
|
+
|
|
75
|
+
constructor(desc: any, data: any) {
|
|
76
|
+
this.desc = desc;
|
|
77
|
+
this.data = data;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* json data
|
|
83
|
+
*/
|
|
84
|
+
export class JsonData {
|
|
85
|
+
desc: any;
|
|
86
|
+
data: any;
|
|
87
|
+
|
|
88
|
+
constructor(desc: any, data: any) {
|
|
89
|
+
this.desc = desc;
|
|
90
|
+
this.data = data;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* html data
|
|
96
|
+
*/
|
|
97
|
+
export class HtmlData {
|
|
98
|
+
desc: any;
|
|
99
|
+
data: any;
|
|
100
|
+
template: string;
|
|
101
|
+
delimiter: string;
|
|
102
|
+
|
|
103
|
+
constructor(desc: any, data: any, template?: string) {
|
|
104
|
+
this.desc = desc;
|
|
105
|
+
this.data = data;
|
|
106
|
+
this.template = template || '';
|
|
107
|
+
this.delimiter = 'DATA=';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* base output
|
|
113
|
+
*/
|
|
114
|
+
abstract class BaseOutput implements IOutput, ILoggable {
|
|
115
|
+
public readonly type: OutputType;
|
|
116
|
+
public readonly DOMAIN: string = 'meminsight';
|
|
117
|
+
public readonly TAG: string;
|
|
118
|
+
|
|
119
|
+
constructor(type: OutputType) {
|
|
120
|
+
this.type = type;
|
|
121
|
+
this.TAG = type.toString();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public output(data: any, cfg?: OutputCfg): void {}
|
|
125
|
+
|
|
126
|
+
protected getFilePathByCfg(cfg?: OutputCfg) : string {
|
|
127
|
+
if (cfg && cfg.output.filename != '') {
|
|
128
|
+
return `${cfg.output.dir}/${cfg.output.filename}.${cfg.output.extension}`;
|
|
129
|
+
} else {
|
|
130
|
+
let dir = './output';
|
|
131
|
+
let filename = `output_${Date.now()}`;
|
|
132
|
+
let extension = this.type.toString();
|
|
133
|
+
return `${dir}/${filename}.${extension}`;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* console output
|
|
140
|
+
*/
|
|
141
|
+
export class ConsoleOutput extends BaseOutput {
|
|
142
|
+
constructor() {
|
|
143
|
+
super(OutputType.CONSOLE);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public output(data: any, cfg?: OutputCfg): void {
|
|
147
|
+
const consoleData = data as ConsoleData;
|
|
148
|
+
if (consoleData) {
|
|
149
|
+
if (cfg?.desc) {
|
|
150
|
+
console.log(cfg?.desc);
|
|
151
|
+
}
|
|
152
|
+
if (consoleData.table) {
|
|
153
|
+
const tableData = consoleData.data as ConsoleTableData;
|
|
154
|
+
console.table(tableData.data, tableData.title);
|
|
155
|
+
} else {
|
|
156
|
+
console.log(consoleData.data);
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
if (typeof data === 'string') {
|
|
160
|
+
console.log(data);
|
|
161
|
+
} else {
|
|
162
|
+
Log.errorX(this, 'No data to output');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* text output
|
|
170
|
+
*/
|
|
171
|
+
export class TextOutput extends BaseOutput {
|
|
172
|
+
constructor() {
|
|
173
|
+
super(OutputType.TEXT);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public output(data: any, cfg?: OutputCfg): void {
|
|
177
|
+
let filePath = this.getFilePathByCfg(cfg);
|
|
178
|
+
let textData = data as TextData;
|
|
179
|
+
if (textData) {
|
|
180
|
+
const tmpData = {
|
|
181
|
+
'meta': cfg?.desc,
|
|
182
|
+
'data': textData,
|
|
183
|
+
}
|
|
184
|
+
TextWriter.instance().writeToFile(filePath, JSON.stringify(tmpData), 'utf-8');
|
|
185
|
+
} else {
|
|
186
|
+
if (typeof data === 'string') {
|
|
187
|
+
TextWriter.instance().writeToFile(filePath, data, 'utf-8');
|
|
188
|
+
} else {
|
|
189
|
+
Log.errorX(this, 'No data to output');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* json output
|
|
197
|
+
*/
|
|
198
|
+
export class JsonOutput extends BaseOutput {
|
|
199
|
+
constructor() {
|
|
200
|
+
super(OutputType.JSON);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public output(data: any, cfg?: OutputCfg): void {
|
|
204
|
+
let filePath = this.getFilePathByCfg(cfg);
|
|
205
|
+
let jsonData = data as JsonData;
|
|
206
|
+
if (jsonData) {
|
|
207
|
+
const tmpData = {
|
|
208
|
+
'meta': jsonData.desc,
|
|
209
|
+
'data': jsonData.data
|
|
210
|
+
}
|
|
211
|
+
JsonWriter.instance().writeToFile(filePath, tmpData, 'utf-8');
|
|
212
|
+
} else {
|
|
213
|
+
if (typeof data === 'string') {
|
|
214
|
+
JsonWriter.instance().writeToFile(filePath, data, 'utf-8');
|
|
215
|
+
} else {
|
|
216
|
+
Log.errorX(this, 'No data to output');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* html output
|
|
224
|
+
*/
|
|
225
|
+
export class HtmlOutput extends BaseOutput {
|
|
226
|
+
constructor() {
|
|
227
|
+
super(OutputType.HTML);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public output(data: any, cfg?: OutputCfg): void {
|
|
231
|
+
let filePath = this.getFilePathByCfg(cfg);
|
|
232
|
+
let htmlData = data as HtmlData;
|
|
233
|
+
if (htmlData) {
|
|
234
|
+
const tmpData = {
|
|
235
|
+
'meta': cfg?.desc,
|
|
236
|
+
'data': htmlData,
|
|
237
|
+
}
|
|
238
|
+
HtmlWriter.instance().writeToFile(filePath, JSON.stringify(tmpData), 'utf-8');
|
|
239
|
+
} else {
|
|
240
|
+
if (typeof data === 'string') {
|
|
241
|
+
HtmlWriter.instance().writeToFile(filePath, data, 'utf-8');
|
|
242
|
+
} else {
|
|
243
|
+
Log.errorX(this, 'No data to output');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* output factory
|
|
251
|
+
*/
|
|
252
|
+
export class OutputFactory {
|
|
253
|
+
private static _defaultOutput: IOutput = new ConsoleOutput();
|
|
254
|
+
private static OUTPUT_MAP: Map<string, IOutput> = new Map(
|
|
255
|
+
[
|
|
256
|
+
[OutputType.CONSOLE.toString(), OutputFactory._defaultOutput],
|
|
257
|
+
[OutputType.HTML.toString(), new HtmlOutput()],
|
|
258
|
+
[OutputType.JSON.toString(), new JsonOutput()],
|
|
259
|
+
[OutputType.TEXT.toString(), new TextOutput()],
|
|
260
|
+
]
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
public static getOutput(type: string): IOutput {
|
|
265
|
+
let output = OutputFactory.OUTPUT_MAP.get(type);
|
|
266
|
+
if (!output) {
|
|
267
|
+
return OutputFactory._defaultOutput;
|
|
268
|
+
}
|
|
269
|
+
return output;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meminsight/example-tools",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "example-tools for memory analysis.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "meminsight-example-tools"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"publish": "npm publish",
|
|
12
|
+
"clean": "rm -rf ./dist && rm -rf ./tsconfig.tsbuildinfo",
|
|
13
|
+
"clean-all": "rm -rf ./dist && rm -rf ./tsconfig.tsbuildinfo && rm -rf ./node_modules && find ./src -type f -name \"*.js\" -exec rm -f {} +"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/MemTest.js",
|
|
16
|
+
"bin": "dist/MemTest.js",
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=16.0.0"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@memlab/core": "^1.0.40",
|
|
22
|
+
"@memlab/heap-analysis": "^1.0.40",
|
|
23
|
+
"@types/node": "^22.14.1",
|
|
24
|
+
"@types/jest": "^29.1.1",
|
|
25
|
+
"@types/ejs": "^3.1.5",
|
|
26
|
+
"ejs": "^3.1.10",
|
|
27
|
+
"axios": "^1.1.3",
|
|
28
|
+
"ansi": "^0.3.1",
|
|
29
|
+
"fs-extra": "^11.2.0",
|
|
30
|
+
"string-width": "^7.2.0",
|
|
31
|
+
"babar": "^0.2.0",
|
|
32
|
+
"chalk": "^5.4.1",
|
|
33
|
+
"minimist": "^1.2.8",
|
|
34
|
+
"util.promisify": "^1.1.1",
|
|
35
|
+
"node-xlsx": "^0.24.0",
|
|
36
|
+
"jest": "^29.1.1",
|
|
37
|
+
"typescript": "^5.3.3"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/fs-extra": "^11.0.4",
|
|
41
|
+
"@types/node": "^22.14.1",
|
|
42
|
+
"@types/minimist": "^1.2.2",
|
|
43
|
+
"@types/jest": "^29.1.1",
|
|
44
|
+
"jest": "^29.7.0",
|
|
45
|
+
"ts-jest": "^29.1.1",
|
|
46
|
+
"ts-node": "^10.9.1",
|
|
47
|
+
"typescript": "^5.3.3"
|
|
48
|
+
},
|
|
49
|
+
"workspaces": [
|
|
50
|
+
"../packages/core"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OutputFactory, OutputCfg, OutputType,
|
|
3
|
+
Finder, Loader,
|
|
4
|
+
ArkSerializer, ArkNodeTracePath,
|
|
5
|
+
Log
|
|
6
|
+
} from '@meminsight/core';
|
|
7
|
+
|
|
8
|
+
const DOMAIN: string = 'exampletools';
|
|
9
|
+
const TAG: string = 'memtest';
|
|
10
|
+
|
|
11
|
+
const args = process.argv.slice();
|
|
12
|
+
const type = args.length > 2 ? Number(args[2]) : 0;
|
|
13
|
+
const count = args.length > 3 ? Number(args[3]) : 5;
|
|
14
|
+
const nodeName = args.length > 4 ? args[4] : '';
|
|
15
|
+
const filepath = args.length > 5 ? args[5] : '';
|
|
16
|
+
|
|
17
|
+
function error(msg: string) {
|
|
18
|
+
Log.error(DOMAIN, TAG, msg);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function info(msg: string) {
|
|
22
|
+
Log.info(DOMAIN, TAG, msg);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
(async function () {
|
|
26
|
+
if (filepath === '') {
|
|
27
|
+
error('filepath is empty.');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const snapshot = await Loader.loadFromFile(filepath);
|
|
31
|
+
|
|
32
|
+
if (type === 0) {
|
|
33
|
+
const nodes = Finder.findNodesByNameCount(snapshot, nodeName, count);
|
|
34
|
+
const cfg: OutputCfg = {
|
|
35
|
+
desc: '',
|
|
36
|
+
output: {
|
|
37
|
+
dir: '',
|
|
38
|
+
filename: '',
|
|
39
|
+
extension: OutputType.CONSOLE.toString(),
|
|
40
|
+
},
|
|
41
|
+
node: {
|
|
42
|
+
enableId: true,
|
|
43
|
+
enableType: true,
|
|
44
|
+
enableName: true,
|
|
45
|
+
enableSelfSize: false,
|
|
46
|
+
enableRetainedSize: false,
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
let nodeData = ArkSerializer.getConsoleDataByNodes(true, nodes, cfg);
|
|
50
|
+
OutputFactory.getOutput(OutputType.CONSOLE).output(nodeData, cfg);
|
|
51
|
+
} else {
|
|
52
|
+
const nodes = Finder.findNodesByNameCount(snapshot, nodeName, count);
|
|
53
|
+
const shortestPaths = Finder.findShortestPaths(snapshot, nodes);
|
|
54
|
+
let set = new Set<number>();
|
|
55
|
+
shortestPaths.forEach(item => {
|
|
56
|
+
if (item) {
|
|
57
|
+
let tracePath = new ArkNodeTracePath(item, true);
|
|
58
|
+
let pathStr = tracePath.getTracePathInfo();
|
|
59
|
+
console.log(pathStr);
|
|
60
|
+
console.log('\n');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meminsight/cmd",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "meminsight binary is a command tool for memory analysis.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "meminsight-cmd"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"publish": "npm publish",
|
|
12
|
+
"clean": "rm -rf ./dist && rm -rf ./tsconfig.tsbuildinfo",
|
|
13
|
+
"clean-all": "rm -rf ./dist && rm -rf ./tsconfig.tsbuildinfo && rm -rf ./node_modules && find ./src -type f -name \"*.js\" -exec rm -f {} +"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/Index.js",
|
|
16
|
+
"bin": "dist/Index.js",
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=16.0.0"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@memlab/core": "^1.0.40",
|
|
22
|
+
"@memlab/heap-analysis": "^1.0.40",
|
|
23
|
+
"@types/node": "^22.14.1",
|
|
24
|
+
"@types/jest": "^29.1.1",
|
|
25
|
+
"@types/ejs": "^3.1.5",
|
|
26
|
+
"ejs": "^3.1.10",
|
|
27
|
+
"axios": "^1.1.3",
|
|
28
|
+
"ansi": "^0.3.1",
|
|
29
|
+
"fs-extra": "^11.2.0",
|
|
30
|
+
"string-width": "^7.2.0",
|
|
31
|
+
"babar": "^0.2.0",
|
|
32
|
+
"chalk": "^5.4.1",
|
|
33
|
+
"minimist": "^1.2.8",
|
|
34
|
+
"util.promisify": "^1.1.1",
|
|
35
|
+
"node-xlsx": "^0.24.0",
|
|
36
|
+
"jest": "^29.1.1",
|
|
37
|
+
"typescript": "^5.3.3"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/fs-extra": "^11.0.4",
|
|
41
|
+
"@types/node": "^22.14.1",
|
|
42
|
+
"@types/minimist": "^1.2.2",
|
|
43
|
+
"@types/jest": "^29.1.1",
|
|
44
|
+
"jest": "^29.7.0",
|
|
45
|
+
"ts-jest": "^29.1.1",
|
|
46
|
+
"ts-node": "^10.9.1",
|
|
47
|
+
"typescript": "^5.3.3"
|
|
48
|
+
},
|
|
49
|
+
"workspaces": [
|
|
50
|
+
"../packages/core"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import {
|
|
3
|
+
ILoggable, Log,
|
|
4
|
+
IAnalyzer, ArkCompareAnalyzer, ArkCmpCfg, defaultArkCmpCfg,
|
|
5
|
+
IOutput, OutputCfg, OutputFactory, OutputType,
|
|
6
|
+
JsonReader,
|
|
7
|
+
ConsoleData,
|
|
8
|
+
JsonData,
|
|
9
|
+
TextData,
|
|
10
|
+
HtmlData
|
|
11
|
+
} from "@meminsight/core";
|
|
12
|
+
import { IProcess } from "./IProcess";
|
|
13
|
+
import { Version } from "../Version";
|
|
14
|
+
import { outputFile } from "fs-extra";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Ark Compare Process
|
|
18
|
+
*/
|
|
19
|
+
export class ArkCompareProc implements IProcess, ILoggable {
|
|
20
|
+
public readonly DOMAIN: string = 'meminsight';
|
|
21
|
+
public readonly TAG: string = ArkCompareProc.name;
|
|
22
|
+
public static readonly CMD: string = 'ark-compare';
|
|
23
|
+
public static readonly DESC: string = "ark compare process.";
|
|
24
|
+
public static readonly VER: string = Version.ARK_COMPARE.toString();
|
|
25
|
+
private static _instance: ArkCompareProc;
|
|
26
|
+
private _analyzer: IAnalyzer = new ArkCompareAnalyzer();
|
|
27
|
+
|
|
28
|
+
private constructor() {}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* get instance
|
|
32
|
+
* @returns instance
|
|
33
|
+
*/
|
|
34
|
+
public static instance(): ArkCompareProc {
|
|
35
|
+
if (!ArkCompareProc._instance) {
|
|
36
|
+
ArkCompareProc._instance = new ArkCompareProc();
|
|
37
|
+
}
|
|
38
|
+
return ArkCompareProc._instance;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public initialize(proc: Command, any: any) {
|
|
42
|
+
proc.command(ArkCompareProc.CMD)
|
|
43
|
+
.description(ArkCompareProc.DESC)
|
|
44
|
+
.requiredOption("-s, --source <file>", "source heapsnapshot file")
|
|
45
|
+
.requiredOption("-t, --target <file>", "target heapsnapshot file")
|
|
46
|
+
.option("-n, --node_name <node name>", undefined)
|
|
47
|
+
.option<number>("-S, --sort_type <sort type: 0 - count | 1 - self_size | 2 - retained_size>", "sort type", parseInt, 0)
|
|
48
|
+
.option("-c, --config <configuration file>", "configuration file")
|
|
49
|
+
.option("-o, --output <file>", "output file")
|
|
50
|
+
.option("-f, --output_file <file type>", "output file type, console | text | json | html.")
|
|
51
|
+
.action(async (options) => {
|
|
52
|
+
await ArkCompareProc.instance().process(new Map<string, any>([
|
|
53
|
+
["source", options.source],
|
|
54
|
+
["target", options.target || undefined],
|
|
55
|
+
["node_name", options.node_name || undefined],
|
|
56
|
+
["sort_type", options.sort_type || "count"],
|
|
57
|
+
["config", options.config || undefined],
|
|
58
|
+
["output", options.output],
|
|
59
|
+
["output_file", options.output_file],
|
|
60
|
+
]));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private async process(args: Map<string, any>): Promise<void> {
|
|
65
|
+
let source = args.get("source");
|
|
66
|
+
if (!source) {
|
|
67
|
+
Log.errorX(this, "Source filepath is required");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
let target = args.get("target");
|
|
71
|
+
if (!target) {
|
|
72
|
+
Log.errorX(this, "Target filepath is required");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let result = await this._analyzer.analyze([source, target]);
|
|
76
|
+
if (!result) {
|
|
77
|
+
Log.errorX(this, "Faled to analyze.");
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
Log.infoX(this, "Analyzed successfully.");
|
|
81
|
+
|
|
82
|
+
let cfgfile = args.get('config');
|
|
83
|
+
let setResult: boolean = false;
|
|
84
|
+
if (cfgfile) {
|
|
85
|
+
let cfg = await JsonReader.instance().readFromFile<ArkCmpCfg>(cfgfile);
|
|
86
|
+
if (cfg) {
|
|
87
|
+
this._analyzer.setConfig(cfg);
|
|
88
|
+
setResult = true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!setResult) {
|
|
92
|
+
Log.warnX(this, "No configuration found, use default config.");
|
|
93
|
+
this._analyzer.setConfig(defaultArkCmpCfg());
|
|
94
|
+
}
|
|
95
|
+
console.log(this._analyzer.getConfig());
|
|
96
|
+
Log.infoX(this, "Set configuration successfully.");
|
|
97
|
+
|
|
98
|
+
let sort_type = args.get("sort_type") || 0;
|
|
99
|
+
let nodeName = args.get("node_name");
|
|
100
|
+
let outputDir: string = args.get("output") || './output';
|
|
101
|
+
let outputFileType: string = args.get("output_file") || 'console';
|
|
102
|
+
Log.infoX(this, `output:${outputDir}; type:${outputFileType}`);
|
|
103
|
+
|
|
104
|
+
this.takeAction(sort_type, outputDir, outputFileType, nodeName)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private async getOutputCfg(
|
|
108
|
+
output: IOutput,
|
|
109
|
+
outputDesc: any,
|
|
110
|
+
outputDir: string,
|
|
111
|
+
outputFile: string) : Promise<OutputCfg> {
|
|
112
|
+
const analyzerCfg = this._analyzer.getConfig() as ArkCmpCfg;
|
|
113
|
+
let cfg: OutputCfg = {
|
|
114
|
+
desc: outputDesc,
|
|
115
|
+
output: {
|
|
116
|
+
dir: outputDir,
|
|
117
|
+
filename: outputFile,
|
|
118
|
+
extension: output.type.toString(),
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return cfg;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private async outputResults(
|
|
125
|
+
results: Array<any>,
|
|
126
|
+
outputEntity: IOutput,
|
|
127
|
+
outputCfg: OutputCfg): Promise<void> {
|
|
128
|
+
if (outputEntity.type === OutputType.CONSOLE) {
|
|
129
|
+
let data = new ConsoleData(false, results);
|
|
130
|
+
outputEntity.output(data, outputCfg);
|
|
131
|
+
} else if (outputEntity.type === OutputType.JSON) {
|
|
132
|
+
let data = new JsonData(outputCfg.desc, results);
|
|
133
|
+
outputEntity.output(data, outputCfg);
|
|
134
|
+
} else if (outputEntity.type === OutputType.TEXT) {
|
|
135
|
+
let data = new TextData(outputCfg.desc, JSON.stringify(results));
|
|
136
|
+
outputEntity.output(data, outputCfg);
|
|
137
|
+
} else if (outputEntity.type === OutputType.HTML) {
|
|
138
|
+
let data = new HtmlData(outputCfg.desc, JSON.stringify(results));
|
|
139
|
+
outputEntity.output(data, outputCfg);
|
|
140
|
+
} else {
|
|
141
|
+
Log.errorX(this, 'Invalid output file type.');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async takeAction(sort_type: number, outputDir:string, output_type: string, node_name?: string): Promise<void> {
|
|
146
|
+
const analyzer = this._analyzer as ArkCompareAnalyzer;
|
|
147
|
+
const data_tmp = await analyzer.diffSnapshots(node_name);
|
|
148
|
+
const results = await analyzer.sortDiffs(sort_type, data_tmp);
|
|
149
|
+
let outputEntity: IOutput = OutputFactory.getOutput(output_type);
|
|
150
|
+
let outputCfg = await this.getOutputCfg(outputEntity, undefined, outputDir, '');
|
|
151
|
+
outputCfg.desc = {
|
|
152
|
+
"SourceSnapshot": analyzer.srcSnapshotFilePath,
|
|
153
|
+
"TargetSnapshot": analyzer.dstSnapshotFilePath,
|
|
154
|
+
"Action": "Compare Snapshots",
|
|
155
|
+
"NodeName": node_name || "None",
|
|
156
|
+
"Time": Date.now()
|
|
157
|
+
};
|
|
158
|
+
this.outputResults(results, outputEntity, outputCfg);
|
|
159
|
+
}
|
|
160
|
+
}
|