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,61 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ILoggable, Log, DeviceShell } from "@meminsight/core";
|
|
3
|
+
import { IProcess } from "./IProcess";
|
|
4
|
+
import { Version } from "../Version";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Ark GC Process
|
|
8
|
+
*/
|
|
9
|
+
export class ArkGCProc implements IProcess, ILoggable {
|
|
10
|
+
public readonly DOMAIN: string = 'meminsight';
|
|
11
|
+
public readonly TAG: string = ArkGCProc.name;
|
|
12
|
+
public static readonly CMD: string = 'ark-gc';
|
|
13
|
+
public static readonly DESC: string = "ark force gc process.";
|
|
14
|
+
public static readonly VER: string = Version.ARK_GC.toString();
|
|
15
|
+
private static _instance: ArkGCProc;
|
|
16
|
+
|
|
17
|
+
private constructor() {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* get instance
|
|
21
|
+
* @returns instance
|
|
22
|
+
*/
|
|
23
|
+
public static instance(): ArkGCProc {
|
|
24
|
+
if (!ArkGCProc._instance) {
|
|
25
|
+
ArkGCProc._instance = new ArkGCProc();
|
|
26
|
+
}
|
|
27
|
+
return ArkGCProc._instance;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public initialize(proc: Command, any: any) {
|
|
31
|
+
proc.command(ArkGCProc.CMD)
|
|
32
|
+
.description(ArkGCProc.DESC)
|
|
33
|
+
.option("-d, --device <target device id>", "target device id")
|
|
34
|
+
.option("-b, --bundle <bundle name>", "bundle name")
|
|
35
|
+
.option<number>("-p, --pid <target process pid>", "target process pid", parseInt)
|
|
36
|
+
.action(async (options) => {
|
|
37
|
+
await ArkGCProc.instance().process(new Map<string, any>([
|
|
38
|
+
["device", options.device],
|
|
39
|
+
["bundle", options.bundle || undefined],
|
|
40
|
+
["pid", options.pid || -1]
|
|
41
|
+
]));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private async process(args: Map<string, any>): Promise<void> {
|
|
46
|
+
let device = args.get("device");
|
|
47
|
+
|
|
48
|
+
// 优先选择 pid 进行 GC
|
|
49
|
+
let pid : number = args.get('pid');
|
|
50
|
+
if (pid !== -1) {
|
|
51
|
+
return await DeviceShell.getDeviceShell(device).forceGC(pid);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let bundleName = args.get("bundle");
|
|
55
|
+
if (bundleName) {
|
|
56
|
+
return await DeviceShell.getDeviceShell(device).forceGCByName(bundleName);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Log.errorX(this, "Args: bundle name or pid is required");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Command } from "commander"
|
|
2
|
+
import { ArkLeakAnalyzer, ILoggable } from "@meminsight/core";
|
|
3
|
+
import { IProcess } from "./IProcess";
|
|
4
|
+
import { Version } from "../Version";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Ark Leak Process
|
|
8
|
+
*/
|
|
9
|
+
export class ArkLeakProc implements IProcess, ILoggable {
|
|
10
|
+
public readonly DOMAIN: string = 'meminsight';
|
|
11
|
+
public readonly TAG: string = ArkLeakProc.name;
|
|
12
|
+
public static readonly CMD: string = "ark-leak";
|
|
13
|
+
public static readonly DESC: string = "ark leaks analysis process.";
|
|
14
|
+
public static readonly VER: string = Version.ARK_LEAK.toString();
|
|
15
|
+
private static _instance: ArkLeakProc;
|
|
16
|
+
|
|
17
|
+
private _analyzer: ArkLeakAnalyzer;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* get instance
|
|
21
|
+
* @returns instance
|
|
22
|
+
*/
|
|
23
|
+
public static instance(): ArkLeakProc {
|
|
24
|
+
if (!ArkLeakProc._instance) {
|
|
25
|
+
ArkLeakProc._instance = new ArkLeakProc();
|
|
26
|
+
}
|
|
27
|
+
return ArkLeakProc._instance;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public initialize(proc: Command, any: any) {
|
|
31
|
+
this._analyzer = any as ArkLeakAnalyzer;
|
|
32
|
+
proc.command(ArkLeakProc.CMD)
|
|
33
|
+
.description(ArkLeakProc.DESC)
|
|
34
|
+
.option("-1, --from <from heapsnapshot>", "from-scenario heapsnapshot file")
|
|
35
|
+
.option("-2, --to <file>", "to-scenario heapsnapshot file")
|
|
36
|
+
.option("-3, --target <file>", "target heapsnapshot file")
|
|
37
|
+
.option("-o, --output <file>", "output file")
|
|
38
|
+
.option("-f, --output_file <file type>", "output file type, console | text | json | html.")
|
|
39
|
+
.action(async (options) => {
|
|
40
|
+
// TODO
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private async process(args: Map<string, any>): Promise<void> {
|
|
45
|
+
// TODO:
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { IHeapNode } from "@memlab/core";
|
|
3
|
+
import {
|
|
4
|
+
ILoggable, Log,
|
|
5
|
+
IAnalyzer, ArkStatAnalyzer, ArkStatCfg, defaultArkStatCfg, ArkSerializer, ArkNodeTracePath,
|
|
6
|
+
IOutput, OutputCfg, OutputFactory, OutputType,
|
|
7
|
+
JsonReader,
|
|
8
|
+
ArkNodesMap
|
|
9
|
+
} from "@meminsight/core";
|
|
10
|
+
import { IProcess } from "./IProcess";
|
|
11
|
+
import { Version } from "../Version";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Ark Statistics Process
|
|
15
|
+
*/
|
|
16
|
+
export class ArkStatProc implements IProcess, ILoggable {
|
|
17
|
+
public static readonly CMD: string = 'ark-stat';
|
|
18
|
+
public static readonly DESC: string = "ark statistic analysis process.";
|
|
19
|
+
public static readonly VER: string = Version.ARK_STAT.toString();
|
|
20
|
+
private static _instance: ArkStatProc;
|
|
21
|
+
|
|
22
|
+
public readonly DOMAIN: string = 'meminsight';
|
|
23
|
+
public readonly TAG: string = ArkStatProc.name;
|
|
24
|
+
private analyzer: IAnalyzer = new ArkStatAnalyzer();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* get instance
|
|
28
|
+
* @returns instance
|
|
29
|
+
*/
|
|
30
|
+
public static instance(): ArkStatProc {
|
|
31
|
+
if (!ArkStatProc._instance) {
|
|
32
|
+
ArkStatProc._instance = new ArkStatProc();
|
|
33
|
+
}
|
|
34
|
+
return ArkStatProc._instance;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public initialize(proc: Command, any: any) {
|
|
38
|
+
proc.command(ArkStatProc.CMD)
|
|
39
|
+
.description(ArkStatProc.DESC)
|
|
40
|
+
.requiredOption("-t, --target <target heapsnapshot file>", "target heapsnapshot file")
|
|
41
|
+
.requiredOption(
|
|
42
|
+
"-m, --mode <mode type>",
|
|
43
|
+
"\nmode type:\n" +
|
|
44
|
+
"\t1: get all detached nodes\n" +
|
|
45
|
+
"\t2: get all gc roots\n" +
|
|
46
|
+
"\t3: get node shortest path, node id is required\n" +
|
|
47
|
+
"\t4: get node aggration paths by configuration\n" +
|
|
48
|
+
"\t5: get node by configuration\n" +
|
|
49
|
+
"\t6: get shared heap nodes\n")
|
|
50
|
+
.option("-i, --node_id <node id>", "node id")
|
|
51
|
+
.option("-c, --config <configuration file>", "configuration file")
|
|
52
|
+
.option("-o, --output <output folder path>", "output folder path", "./output")
|
|
53
|
+
.option("-f, --output_file <output file type>",
|
|
54
|
+
"output file type, console | txt | json | html.",
|
|
55
|
+
"console")
|
|
56
|
+
.action(async (options) => {
|
|
57
|
+
await ArkStatProc.instance().process(new Map<string, any>([
|
|
58
|
+
["target", options.target],
|
|
59
|
+
["mode", options.mode],
|
|
60
|
+
["node_id", options.node_id],
|
|
61
|
+
["config", options.config || undefined],
|
|
62
|
+
["output", options.output],
|
|
63
|
+
["output_file", options.output_file],
|
|
64
|
+
]));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private async process(args: Map<string, string | undefined>): Promise<void> {
|
|
69
|
+
let target = args.get("target");
|
|
70
|
+
if (!target) {
|
|
71
|
+
Log.errorX(this, "Target filepath is required");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
let result = await this.analyzer.analyze([target]);
|
|
75
|
+
if (!result) {
|
|
76
|
+
Log.errorX(this, "Faled to analyze.");
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
Log.infoX(this, "Analyzed successfully.");
|
|
80
|
+
|
|
81
|
+
let cfgfile = args.get('config');
|
|
82
|
+
let setResult: boolean = false;
|
|
83
|
+
if (cfgfile) {
|
|
84
|
+
let cfg = await JsonReader.instance().readFromFile<ArkStatCfg>(cfgfile);
|
|
85
|
+
if (cfg) {
|
|
86
|
+
this.analyzer.setConfig(cfg);
|
|
87
|
+
setResult = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!setResult) {
|
|
91
|
+
Log.warnX(this, "No configuration found, use default config.");
|
|
92
|
+
this.analyzer.setConfig(defaultArkStatCfg());
|
|
93
|
+
}
|
|
94
|
+
console.log(this.analyzer.getConfig());
|
|
95
|
+
Log.infoX(this, "Set configuration successfully.");
|
|
96
|
+
|
|
97
|
+
let mode = args.get("mode") || '1';
|
|
98
|
+
let outputDir: string = args.get("output") || './output';
|
|
99
|
+
let outputFileType: string = args.get("output_file") || 'console';
|
|
100
|
+
let nodeId: number = parseInt(args.get("node_id") || '1');
|
|
101
|
+
Log.infoX(this, `mode:${mode}; output:${outputDir}; type:${outputFileType}`);
|
|
102
|
+
|
|
103
|
+
this.takeActionByMode(mode, outputDir, outputFileType, nodeId);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private getOutputCfg(
|
|
107
|
+
output: IOutput,
|
|
108
|
+
outputDesc: any,
|
|
109
|
+
outputDir: string,
|
|
110
|
+
outputFile: string,
|
|
111
|
+
enableNode: boolean = true) : OutputCfg {
|
|
112
|
+
const analyzerCfg = this.analyzer.getConfig() as ArkStatCfg;
|
|
113
|
+
let cfg: OutputCfg = {
|
|
114
|
+
desc: outputDesc,
|
|
115
|
+
output: {
|
|
116
|
+
dir: outputDir,
|
|
117
|
+
filename: outputFile,
|
|
118
|
+
extension: output.type.toString(),
|
|
119
|
+
},
|
|
120
|
+
node: enableNode ? {
|
|
121
|
+
enableId: analyzerCfg.Node.enableId,
|
|
122
|
+
enableType: analyzerCfg.Node.enableType,
|
|
123
|
+
enableName: analyzerCfg.Node.enableName,
|
|
124
|
+
enableSelfSize: analyzerCfg.Node.enableSelfSize,
|
|
125
|
+
enableRetainedSize: analyzerCfg.Node.enableRetainedSize,
|
|
126
|
+
} : undefined
|
|
127
|
+
}
|
|
128
|
+
return cfg;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private async outputNodes(
|
|
132
|
+
nodes: IHeapNode[],
|
|
133
|
+
outputEntity: IOutput,
|
|
134
|
+
outputCfg: OutputCfg): Promise<void> {
|
|
135
|
+
if (outputEntity?.type === OutputType.CONSOLE) {
|
|
136
|
+
outputEntity?.output(ArkSerializer.getConsoleDataByNodes(true, nodes, outputCfg), outputCfg);
|
|
137
|
+
} else if (outputEntity?.type === OutputType.TEXT) {
|
|
138
|
+
outputEntity?.output(ArkSerializer.getTextDataByNodes(nodes, outputCfg), outputCfg);
|
|
139
|
+
} else if (outputEntity?.type === OutputType.JSON) {
|
|
140
|
+
outputEntity?.output(ArkSerializer.getJsonDataByNodes(nodes, outputCfg), outputCfg);
|
|
141
|
+
} else if (outputEntity?.type === OutputType.HTML) {
|
|
142
|
+
// TODO
|
|
143
|
+
outputEntity?.output(ArkSerializer.getHtmlDataByNodes(nodes, outputCfg), outputCfg);
|
|
144
|
+
} else {
|
|
145
|
+
Log.errorX(this, 'Invalid output file type.');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private async outputArkNodeTracePath(
|
|
150
|
+
path: ArkNodeTracePath,
|
|
151
|
+
outputEntity: IOutput,
|
|
152
|
+
outputCfg: OutputCfg): Promise<void> {
|
|
153
|
+
if (outputEntity?.type === OutputType.CONSOLE) {
|
|
154
|
+
let dataTmp: string = ArkSerializer.getNodeTracePathString(path.tracePathItem);
|
|
155
|
+
outputEntity?.output(dataTmp, outputCfg);
|
|
156
|
+
} else if (outputEntity?.type === OutputType.TEXT) {
|
|
157
|
+
let dataTmp = {
|
|
158
|
+
desc: outputCfg.desc,
|
|
159
|
+
data: ArkSerializer.getNodeTracePathString(path.tracePathItem)
|
|
160
|
+
};
|
|
161
|
+
outputEntity?.output(dataTmp, outputCfg);
|
|
162
|
+
} else if (outputEntity?.type === OutputType.JSON) {
|
|
163
|
+
let dataTmp = {
|
|
164
|
+
desc: outputCfg.desc,
|
|
165
|
+
data: ArkSerializer.getNodeTracePathString(path.tracePathItem)
|
|
166
|
+
};
|
|
167
|
+
outputEntity?.output(dataTmp, outputCfg);
|
|
168
|
+
} else if (outputEntity?.type === OutputType.HTML) {
|
|
169
|
+
// TODO
|
|
170
|
+
} else {
|
|
171
|
+
Log.errorX(this, 'Invalid output file type.');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private async outputAggrationPath(
|
|
176
|
+
paths: ArkNodesMap,
|
|
177
|
+
outputEntity: IOutput,
|
|
178
|
+
outputCfg: OutputCfg) {
|
|
179
|
+
if (outputEntity?.type === OutputType.CONSOLE) {
|
|
180
|
+
let dataTmp: string = ArkSerializer.getArkNodesMapString(paths);
|
|
181
|
+
outputEntity?.output(dataTmp, outputCfg);
|
|
182
|
+
} else if (outputEntity?.type === OutputType.TEXT) {
|
|
183
|
+
// TODO
|
|
184
|
+
} else if (outputEntity?.type === OutputType.JSON) {
|
|
185
|
+
// TODO
|
|
186
|
+
} else if (outputEntity?.type === OutputType.HTML) {
|
|
187
|
+
// TODO
|
|
188
|
+
} else {
|
|
189
|
+
Log.errorX(this, 'Invalid output file type.');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private async takeActionByMode(mode: string, outputDir:string, type: string, nodeId?: number): Promise<void> {
|
|
194
|
+
const analyzer = this.analyzer as ArkStatAnalyzer;
|
|
195
|
+
let outputEntity: IOutput = OutputFactory.getOutput(type);
|
|
196
|
+
let outputCfg = this.getOutputCfg(outputEntity, undefined, outputDir, '');
|
|
197
|
+
switch (mode) {
|
|
198
|
+
// get detached nodes
|
|
199
|
+
case '1':
|
|
200
|
+
{
|
|
201
|
+
let nodes = await analyzer.getDetachedNodes();
|
|
202
|
+
if (nodes.length === 0) {
|
|
203
|
+
Log.errorX(this, `No detached nodes found.`);
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
outputCfg.desc = {
|
|
207
|
+
"Snapshot": analyzer.getSnapshotFilePath(),
|
|
208
|
+
"Action:": "Get Detached Nodes",
|
|
209
|
+
"NodeLength": nodes.length,
|
|
210
|
+
"Time": Date.now()
|
|
211
|
+
};
|
|
212
|
+
outputCfg.output.filename = `detached-nodes_${Date.now()}`;
|
|
213
|
+
this.outputNodes(nodes, outputEntity, outputCfg);
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
// get gc roots
|
|
217
|
+
case '2':
|
|
218
|
+
{
|
|
219
|
+
let nodes = await analyzer.getGCRoots();
|
|
220
|
+
if (nodes.length === 0) {
|
|
221
|
+
Log.errorX(this, "No gc roots found.");
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
outputCfg.desc = {
|
|
225
|
+
"FilePath": analyzer.getSnapshotFilePath(),
|
|
226
|
+
"Action:": "Get GC Roots",
|
|
227
|
+
"NodeLength": nodes.length,
|
|
228
|
+
"Time": Date.now()
|
|
229
|
+
};
|
|
230
|
+
outputCfg.output.filename = `gc-roots_${Date.now()}`;
|
|
231
|
+
this.outputNodes(nodes, outputEntity, outputCfg);
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
// get shortest path to node
|
|
235
|
+
case '3':
|
|
236
|
+
{
|
|
237
|
+
if (nodeId) {
|
|
238
|
+
let shortestPath = await analyzer.getShortestPathToNode(nodeId);
|
|
239
|
+
if (!shortestPath) {
|
|
240
|
+
Log.errorX(this, `Failed to find shortest path to node ${nodeId}`);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
let nodeTracePath = new ArkNodeTracePath(shortestPath, true);
|
|
244
|
+
outputCfg.desc = {
|
|
245
|
+
"FilePath": analyzer.getSnapshotFilePath(),
|
|
246
|
+
"Action:": "Get Shortest Path to Node",
|
|
247
|
+
"NodeID": nodeId,
|
|
248
|
+
"ShortestPath": nodeTracePath.uid,
|
|
249
|
+
"Time": Date.now()
|
|
250
|
+
};
|
|
251
|
+
outputCfg.output.filename = `shortest-path-to-node${nodeId}_${Date.now()}`;
|
|
252
|
+
this.outputArkNodeTracePath(nodeTracePath, outputEntity, outputCfg);
|
|
253
|
+
} else {
|
|
254
|
+
Log.errorX(this, 'Node id is required.')
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
// get ark nodes/paths
|
|
259
|
+
case '4':
|
|
260
|
+
{
|
|
261
|
+
console.warn('Do not support this function.');
|
|
262
|
+
break;
|
|
263
|
+
// let arkNodesMap = await analyzer.getArkNodesMapByConfig();
|
|
264
|
+
// if (!arkNodesMap) {
|
|
265
|
+
// Log.errorX(this, `Failed to find ark nodes map by configuration`);
|
|
266
|
+
// break;
|
|
267
|
+
// }
|
|
268
|
+
// outputCfg.desc = {
|
|
269
|
+
// "FilePath": analyzer.getSnapshotFilePath(),
|
|
270
|
+
// "Action:": "Get Aggration Paths",
|
|
271
|
+
// "Time": Date.now()
|
|
272
|
+
// };
|
|
273
|
+
// outputCfg.output.filename = `aggration-paths_${Date.now()}`;
|
|
274
|
+
// this.outputAggrationPath(arkNodesMap, outputEntity, outputCfg);
|
|
275
|
+
// break;
|
|
276
|
+
}
|
|
277
|
+
// get node by configuration
|
|
278
|
+
case '5':
|
|
279
|
+
{
|
|
280
|
+
let nodes = await analyzer.getNodesByConfig();
|
|
281
|
+
if (nodes.length === 0) {
|
|
282
|
+
Log.errorX(this, `No nodes found by configuration.`);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
outputCfg.desc = {
|
|
286
|
+
"FilePath": analyzer.getSnapshotFilePath(),
|
|
287
|
+
"Action:": "Get Nodes by Configuration",
|
|
288
|
+
"NodeLength": nodes.length,
|
|
289
|
+
"Time": Date.now()
|
|
290
|
+
};
|
|
291
|
+
this.outputNodes(nodes, outputEntity, outputCfg);
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
case '6':
|
|
295
|
+
{
|
|
296
|
+
console.warn('Do not support this function.');
|
|
297
|
+
break;
|
|
298
|
+
// let map = await analyzer.getSharedHeapNodesMap();
|
|
299
|
+
// if (map.size === 0) {
|
|
300
|
+
// Log.errorX(this, `No shared heap node found.`);
|
|
301
|
+
// break;
|
|
302
|
+
// }
|
|
303
|
+
// outputCfg.desc = {
|
|
304
|
+
// "FilePath": analyzer.getSnapshotFilePath(),
|
|
305
|
+
// "Action:": "Get Shared Heap Nodes Map",
|
|
306
|
+
// "NodesMapLength": map.size,
|
|
307
|
+
// "Time": Date.now()
|
|
308
|
+
// };
|
|
309
|
+
// outputCfg.output.filename = `shared-heap-nodes_${Date.now()}`;
|
|
310
|
+
// map.forEach((nodes, key) => {
|
|
311
|
+
// this.outputNodes(nodes, outputEntity, outputCfg);
|
|
312
|
+
// })
|
|
313
|
+
// break;
|
|
314
|
+
}
|
|
315
|
+
default:
|
|
316
|
+
Log.warnX(this, `Unknown mode: ${mode}`);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { ILoggable, Log, IAnalyzer, ArkXAnalyzer } from "@meminsight/core";
|
|
5
|
+
import { IProcess } from "./IProcess";
|
|
6
|
+
import { Version } from "../Version";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Ark X Process
|
|
10
|
+
*/
|
|
11
|
+
export class ArkXProc implements IProcess, ILoggable {
|
|
12
|
+
public readonly DOMAIN: string = 'meminsight';
|
|
13
|
+
public readonly TAG: string = ArkXProc.name;
|
|
14
|
+
public static readonly CMD: string = 'ark-x';
|
|
15
|
+
public static readonly DESC: string = "ark-x analysis process.";
|
|
16
|
+
public static readonly VER: string = Version.ARK_GC.toString();
|
|
17
|
+
private static _instance: ArkXProc;
|
|
18
|
+
private analyzer: IAnalyzer = new ArkXAnalyzer();
|
|
19
|
+
|
|
20
|
+
private constructor() {}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* get instance
|
|
24
|
+
* @returns instance
|
|
25
|
+
*/
|
|
26
|
+
public static instance(): ArkXProc {
|
|
27
|
+
if (!ArkXProc._instance) {
|
|
28
|
+
ArkXProc._instance = new ArkXProc();
|
|
29
|
+
}
|
|
30
|
+
return ArkXProc._instance;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public initialize(proc: Command, any: any) {
|
|
34
|
+
proc.command(ArkXProc.CMD)
|
|
35
|
+
.description(ArkXProc.DESC)
|
|
36
|
+
.option("-f, --file <filepath>", "target filepath", '')
|
|
37
|
+
.option("-l, --list <list filepath>", "one list file which contains snapshot filepathes", '')
|
|
38
|
+
.option("-s, --sort <sort tyoe>", "sort type, 0 - by retained size | 1 - by count", parseInt, 0)
|
|
39
|
+
.option<number>("-T, --top <top number>", "top number, default is 10", parseInt, 10)
|
|
40
|
+
.option("-t, --tags <object tags>", "tobject tags, as tag1,tag2,tag3...", '')
|
|
41
|
+
.action(async (options) => {
|
|
42
|
+
await ArkXProc.instance().process(new Map<string, any>([
|
|
43
|
+
["file", options.file],
|
|
44
|
+
["list", options.list],
|
|
45
|
+
["top", options.top],
|
|
46
|
+
["sort", options.sort],
|
|
47
|
+
["tags", options.tags]
|
|
48
|
+
]));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private async process(args: Map<string, any>): Promise<void> {
|
|
53
|
+
let file = args.get("file") as string;
|
|
54
|
+
let list = args.get("list") as string;
|
|
55
|
+
let topN = args.get("top") as number;
|
|
56
|
+
let sortType = args.get("sort") as number;
|
|
57
|
+
let tagStr = args.get("tags") as string;
|
|
58
|
+
// 读取 list file 中的快照文件路径
|
|
59
|
+
let fileList;
|
|
60
|
+
if (list != undefined && list !== '') {
|
|
61
|
+
fileList = fs.readFileSync(path.resolve(list), 'utf-8').replace(/\r/g, '').split('\n').filter(value => {
|
|
62
|
+
return value !== '';
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
let tags;
|
|
66
|
+
if (list != undefined && tagStr !== '') {
|
|
67
|
+
tags = tagStr.split(',').filter(value => {
|
|
68
|
+
return value !== '';
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
await this.analyzer.analyze([file, fileList, topN, sortType, tags]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { program, Command } from "commander";
|
|
2
|
+
import { IProcess } from "./IProcess";
|
|
3
|
+
import { Version } from "../Version";
|
|
4
|
+
import { ArkLeakProc } from "./ArkLeakProc";
|
|
5
|
+
import { ArkGCProc } from "./ArkGCProc";
|
|
6
|
+
import { ArkCompareProc } from "./ArkCompareProc";
|
|
7
|
+
import { ArkStatProc } from "./ArkStatProc";
|
|
8
|
+
import { ArkXProc } from "./ArkXProc";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* MemInsight Process
|
|
12
|
+
*/
|
|
13
|
+
export class MemInsight implements IProcess {
|
|
14
|
+
public readonly CMD : string = "meminsight";
|
|
15
|
+
public readonly DESC : string = "meminsight binary is a command tool for memory analysis.";
|
|
16
|
+
public readonly VER : string;
|
|
17
|
+
|
|
18
|
+
public constructor(ver: string, processes: IProcess[]) {
|
|
19
|
+
this.VER = ver;
|
|
20
|
+
this.initialize(new Command(), processes);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public initialize(proc: Command, any?: any) : void{
|
|
24
|
+
program.name(this.CMD);
|
|
25
|
+
program.description(this.DESC);
|
|
26
|
+
program.version(this.VER, "-v, --version", "meminsight version");
|
|
27
|
+
let processes = any as IProcess[];
|
|
28
|
+
if (processes) {
|
|
29
|
+
processes.forEach(process => process.initialize(program));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* work
|
|
35
|
+
*/
|
|
36
|
+
public work() : void{
|
|
37
|
+
program.parse();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const meminsight: MemInsight = new MemInsight(
|
|
42
|
+
Version.HMEMX.toString(),
|
|
43
|
+
[
|
|
44
|
+
ArkStatProc.instance(),
|
|
45
|
+
ArkCompareProc.instance(),
|
|
46
|
+
ArkLeakProc.instance(),
|
|
47
|
+
ArkGCProc.instance(),
|
|
48
|
+
ArkXProc.instance(),
|
|
49
|
+
]
|
|
50
|
+
);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
## Steps
|
|
2
|
+
|
|
3
|
+
1. 进入 stack 目录
|
|
4
|
+
|
|
5
|
+
2. install npm, package.json 中的依赖
|
|
6
|
+
|
|
7
|
+
```JSON
|
|
8
|
+
"hstack_lib": "file:libs/hstack_lib-1.0.1.tgz",
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
3. 替换 src/Index.js 中的需要反混淆解析的字符串
|
|
12
|
+
|
|
13
|
+
```JS
|
|
14
|
+
// input one line
|
|
15
|
+
const line = '';
|
|
16
|
+
// input multi lines
|
|
17
|
+
const lines = [];
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
4. 将 sourcemap 文件、namecache 文件、so文件放置到对应的目录中,在 src/Index.js 中配置对应目录
|
|
21
|
+
|
|
22
|
+
```JS
|
|
23
|
+
const sourcemapFolder = ...;
|
|
24
|
+
const namecacheFolder = ...;
|
|
25
|
+
const libFolder = ...;
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
5. 运行 index.js "node index.js"
|
|
29
|
+
|
|
30
|
+
6. 测试等待结果
|
|
31
|
+
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"list": [
|
|
3
|
+
{
|
|
4
|
+
"name": "hstack_lib-1.0.4.tgz",
|
|
5
|
+
"version": "1.0.4",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"API": [
|
|
8
|
+
"init",
|
|
9
|
+
"parseLine",
|
|
10
|
+
"stop"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "hstack_lib-1.0.1.tgz",
|
|
15
|
+
"version": "1.0.1",
|
|
16
|
+
"type": "commonjs",
|
|
17
|
+
"API": [
|
|
18
|
+
"init",
|
|
19
|
+
"parseLine",
|
|
20
|
+
"stop"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "hstack_lib-1.0.0.tgz",
|
|
25
|
+
"version": "1.0.0",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"API": [
|
|
28
|
+
"init",
|
|
29
|
+
"parseLine",
|
|
30
|
+
"stop"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"dependencies": {
|
|
3
|
+
"@types/jest": "^29.1.1",
|
|
4
|
+
"@types/node": "^22.14.1",
|
|
5
|
+
"ansi": "^0.3.1",
|
|
6
|
+
"babar": "^0.2.0",
|
|
7
|
+
"chalk": "^5.4.1",
|
|
8
|
+
"fs-extra": "^11.2.0",
|
|
9
|
+
"hstack_lib": "file:libs/hstack_lib-1.0.4.tgz",
|
|
10
|
+
"jest": "^29.1.1",
|
|
11
|
+
"minimist": "^1.2.8",
|
|
12
|
+
"node-xlsx": "^0.24.0",
|
|
13
|
+
"string-width": "^7.2.0",
|
|
14
|
+
"typescript": "^5.3.3",
|
|
15
|
+
"util.promisify": "^1.1.1"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/fs-extra": "^11.0.4",
|
|
19
|
+
"@types/jest": "^29.1.1",
|
|
20
|
+
"@types/minimist": "^1.2.2",
|
|
21
|
+
"@types/node": "^22.14.1",
|
|
22
|
+
"jest": "^29.7.0",
|
|
23
|
+
"ts-jest": "^29.1.1",
|
|
24
|
+
"ts-node": "^10.9.1",
|
|
25
|
+
"typescript": "^5.3.3"
|
|
26
|
+
}
|
|
27
|
+
}
|