meminsight-core-publish 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/dist/Index.d.ts +21 -0
- package/dist/Index.d.ts.map +1 -0
- package/dist/Index.js +65 -0
- package/dist/analyzer/AnalysisInfo.d.ts +112 -0
- package/dist/analyzer/AnalysisInfo.d.ts.map +1 -0
- package/dist/analyzer/AnalysisInfo.js +217 -0
- package/dist/analyzer/ArkAnalyzer.d.ts +30 -0
- package/dist/analyzer/ArkAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/ArkAnalyzer.js +52 -0
- package/dist/analyzer/ArkCmpCfg.d.ts +15 -0
- package/dist/analyzer/ArkCmpCfg.d.ts.map +1 -0
- package/dist/analyzer/ArkCmpCfg.js +15 -0
- package/dist/analyzer/ArkCompareAnalyzer.d.ts +52 -0
- package/dist/analyzer/ArkCompareAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/ArkCompareAnalyzer.js +144 -0
- package/dist/analyzer/ArkLeakAnalyzer.d.ts +14 -0
- package/dist/analyzer/ArkLeakAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/ArkLeakAnalyzer.js +18 -0
- package/dist/analyzer/ArkSerializer.d.ts +18 -0
- package/dist/analyzer/ArkSerializer.d.ts.map +1 -0
- package/dist/analyzer/ArkSerializer.js +155 -0
- package/dist/analyzer/ArkStatAnalyzer.d.ts +56 -0
- package/dist/analyzer/ArkStatAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/ArkStatAnalyzer.js +224 -0
- package/dist/analyzer/ArkStatCfg.d.ts +42 -0
- package/dist/analyzer/ArkStatCfg.d.ts.map +1 -0
- package/dist/analyzer/ArkStatCfg.js +42 -0
- package/dist/analyzer/ArkTracePath.d.ts +90 -0
- package/dist/analyzer/ArkTracePath.d.ts.map +1 -0
- package/dist/analyzer/ArkTracePath.js +245 -0
- package/dist/analyzer/ArkTracer.d.ts +17 -0
- package/dist/analyzer/ArkTracer.d.ts.map +1 -0
- package/dist/analyzer/ArkTracer.js +27 -0
- package/dist/analyzer/ArkXAnalyzer.d.ts +69 -0
- package/dist/analyzer/ArkXAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/ArkXAnalyzer.js +616 -0
- package/dist/analyzer/IAnalyzer.d.ts +25 -0
- package/dist/analyzer/IAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/IAnalyzer.js +2 -0
- package/dist/file/FileReader.d.ts +51 -0
- package/dist/file/FileReader.d.ts.map +1 -0
- package/dist/file/FileReader.js +110 -0
- package/dist/file/FileService.d.ts +23 -0
- package/dist/file/FileService.d.ts.map +1 -0
- package/dist/file/FileService.js +65 -0
- package/dist/file/FileWriter.d.ts +82 -0
- package/dist/file/FileWriter.d.ts.map +1 -0
- package/dist/file/FileWriter.js +160 -0
- package/dist/report/Reporter.d.ts +27 -0
- package/dist/report/Reporter.d.ts.map +1 -0
- package/dist/report/Reporter.js +61 -0
- package/dist/report/templates/template.d.ts +3 -0
- package/dist/report/templates/template.d.ts.map +1 -0
- package/dist/report/templates/template.js +106 -0
- package/dist/shell/DeviceShell.d.ts +96 -0
- package/dist/shell/DeviceShell.d.ts.map +1 -0
- package/dist/shell/DeviceShell.js +180 -0
- package/dist/shell/Shell.d.ts +52 -0
- package/dist/shell/Shell.d.ts.map +1 -0
- package/dist/shell/Shell.js +79 -0
- package/dist/types/Constants.d.ts +15 -0
- package/dist/types/Constants.d.ts.map +1 -0
- package/dist/types/Constants.js +18 -0
- package/dist/types/LeakTypes.d.ts +18 -0
- package/dist/types/LeakTypes.d.ts.map +1 -0
- package/dist/types/LeakTypes.js +2 -0
- package/dist/types/OhosTypes.d.ts +74 -0
- package/dist/types/OhosTypes.d.ts.map +1 -0
- package/dist/types/OhosTypes.js +83 -0
- package/dist/utils/Common.d.ts +14 -0
- package/dist/utils/Common.d.ts.map +1 -0
- package/dist/utils/Common.js +38 -0
- package/dist/utils/Finder.d.ts +163 -0
- package/dist/utils/Finder.d.ts.map +1 -0
- package/dist/utils/Finder.js +355 -0
- package/dist/utils/Loader.d.ts +30 -0
- package/dist/utils/Loader.d.ts.map +1 -0
- package/dist/utils/Loader.js +71 -0
- package/dist/utils/Log.d.ts +140 -0
- package/dist/utils/Log.d.ts.map +1 -0
- package/dist/utils/Log.js +177 -0
- package/dist/utils/Output.d.ts +126 -0
- package/dist/utils/Output.d.ts.map +1 -0
- package/dist/utils/Output.js +225 -0
- package/package.json +61 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HeapInfos = exports.HeapFileInfo = exports.UrlInfo = exports.AppInfo = exports.DeviceInfo = exports.OsInfo = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* os info
|
|
6
|
+
*/
|
|
7
|
+
class OsInfo {
|
|
8
|
+
constructor(name, version) {
|
|
9
|
+
this.type = "os";
|
|
10
|
+
this.name = name || "ohos";
|
|
11
|
+
this.version = version;
|
|
12
|
+
}
|
|
13
|
+
toString() {
|
|
14
|
+
return `${this.type} ${this.name} ${this.version}`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.OsInfo = OsInfo;
|
|
18
|
+
/**
|
|
19
|
+
* device info
|
|
20
|
+
*/
|
|
21
|
+
class DeviceInfo {
|
|
22
|
+
constructor(deviceId) {
|
|
23
|
+
this.type = 'ohos-device';
|
|
24
|
+
this.deviceId = deviceId || '';
|
|
25
|
+
}
|
|
26
|
+
toString() {
|
|
27
|
+
return `${this.deviceId}`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.DeviceInfo = DeviceInfo;
|
|
31
|
+
/**
|
|
32
|
+
* app info
|
|
33
|
+
*/
|
|
34
|
+
class AppInfo {
|
|
35
|
+
constructor(name, pid, version) {
|
|
36
|
+
this.type = 'app';
|
|
37
|
+
this.name = name || '';
|
|
38
|
+
this.pid = pid;
|
|
39
|
+
this.version = version;
|
|
40
|
+
}
|
|
41
|
+
toString() {
|
|
42
|
+
return `${this.name}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.AppInfo = AppInfo;
|
|
46
|
+
/**
|
|
47
|
+
* url info
|
|
48
|
+
*/
|
|
49
|
+
class UrlInfo {
|
|
50
|
+
constructor() {
|
|
51
|
+
this.type = 'url';
|
|
52
|
+
}
|
|
53
|
+
toString() {
|
|
54
|
+
return `${this.url}`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.UrlInfo = UrlInfo;
|
|
58
|
+
/**
|
|
59
|
+
* heap file info
|
|
60
|
+
*/
|
|
61
|
+
class HeapFileInfo {
|
|
62
|
+
constructor(path) {
|
|
63
|
+
this.type = 'heap';
|
|
64
|
+
this.createTime = 0; // create time
|
|
65
|
+
this.path = path;
|
|
66
|
+
}
|
|
67
|
+
toString() {
|
|
68
|
+
return `${this.name}:${this.path}`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.HeapFileInfo = HeapFileInfo;
|
|
72
|
+
/**
|
|
73
|
+
* heap infos
|
|
74
|
+
*/
|
|
75
|
+
class HeapInfos {
|
|
76
|
+
constructor(os, device, app, heapFile) {
|
|
77
|
+
this.osInfo = os;
|
|
78
|
+
this.deviceInfo = device;
|
|
79
|
+
this.appInfo = app;
|
|
80
|
+
this.heapFileInfo = heapFile;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.HeapInfos = HeapInfos;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T != null
|
|
3
|
+
* @param value T instance
|
|
4
|
+
* @returns true if T instance is not null or undefined
|
|
5
|
+
*/
|
|
6
|
+
export declare function isDefined<T>(value: T | null | undefined): value is T;
|
|
7
|
+
/**
|
|
8
|
+
* 格式化 Date 为指定字符串
|
|
9
|
+
* @param date 要格式化的日期(默认当前时间)
|
|
10
|
+
* @param format 格式模板:YYYY=年, MM=月, DD=日, HH=时, mm=分, ss=秒, SSS=毫秒
|
|
11
|
+
* @returns 格式化后的字符串
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatDate(date: Date, format?: string): string;
|
|
14
|
+
//# sourceMappingURL=Common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Common.d.ts","sourceRoot":"","sources":["../../src/utils/Common.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,CAAC,CAEpE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAE,MAAyB,GAAG,MAAM,CAqBhF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isDefined = isDefined;
|
|
4
|
+
exports.formatDate = formatDate;
|
|
5
|
+
/**
|
|
6
|
+
* T != null
|
|
7
|
+
* @param value T instance
|
|
8
|
+
* @returns true if T instance is not null or undefined
|
|
9
|
+
*/
|
|
10
|
+
function isDefined(value) {
|
|
11
|
+
return value != null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 格式化 Date 为指定字符串
|
|
15
|
+
* @param date 要格式化的日期(默认当前时间)
|
|
16
|
+
* @param format 格式模板:YYYY=年, MM=月, DD=日, HH=时, mm=分, ss=秒, SSS=毫秒
|
|
17
|
+
* @returns 格式化后的字符串
|
|
18
|
+
*/
|
|
19
|
+
function formatDate(date, format = 'YYYYMMDDHHmmss') {
|
|
20
|
+
// 补零函数:确保数字为两位数(如 9 → 09)
|
|
21
|
+
const padZero = (num, length = 2) => num.toString().padStart(length, '0');
|
|
22
|
+
const year = date.getFullYear();
|
|
23
|
+
const month = padZero(date.getMonth() + 1); // 月份从 0 开始,需 +1
|
|
24
|
+
const day = padZero(date.getDate());
|
|
25
|
+
const hour = padZero(date.getHours());
|
|
26
|
+
const minute = padZero(date.getMinutes());
|
|
27
|
+
const second = padZero(date.getSeconds());
|
|
28
|
+
const millisecond = padZero(date.getMilliseconds(), 3); // 三位数毫秒
|
|
29
|
+
// 替换格式模板
|
|
30
|
+
return format
|
|
31
|
+
.replace('YYYY', year.toString())
|
|
32
|
+
.replace('MM', month)
|
|
33
|
+
.replace('DD', day)
|
|
34
|
+
.replace('HH', hour)
|
|
35
|
+
.replace('mm', minute)
|
|
36
|
+
.replace('ss', second)
|
|
37
|
+
.replace('SSS', millisecond);
|
|
38
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { IHeapSnapshot, IHeapNode, IHeapEdge, Nullable, LeakTracePathItem } from "@memlab/core";
|
|
2
|
+
/**
|
|
3
|
+
* 查找器
|
|
4
|
+
*/
|
|
5
|
+
export declare class Finder {
|
|
6
|
+
private static traceFinder;
|
|
7
|
+
/**
|
|
8
|
+
* 判断是否是指定名称属性的边
|
|
9
|
+
* @param edge 边
|
|
10
|
+
* @param propName 名称
|
|
11
|
+
* @returns true/false
|
|
12
|
+
*/
|
|
13
|
+
static isPropEdgeWithName(edge: IHeapEdge, propName: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* 判断是否是Proto属性的边
|
|
16
|
+
* @param edge 边
|
|
17
|
+
* @returns true/false
|
|
18
|
+
*/
|
|
19
|
+
static isProtoPropEdge(edge: IHeapEdge): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* 判断是否是hclass属性的边
|
|
22
|
+
* @param edge 边
|
|
23
|
+
* @returns true/false
|
|
24
|
+
*/
|
|
25
|
+
static isHClassPropEdge(edge: IHeapEdge): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* 共享内存类型
|
|
28
|
+
* 例举关键类型
|
|
29
|
+
*/
|
|
30
|
+
static OOM_SHARED_TYPES: string[];
|
|
31
|
+
static STRING_UNIQUE_ID: string;
|
|
32
|
+
static isSharedHeapNode(node: IHeapNode): boolean;
|
|
33
|
+
static getSharedHeapNodesMap(snapshot: IHeapSnapshot): Map<string, Array<IHeapNode>>;
|
|
34
|
+
/**
|
|
35
|
+
* 获取所有shared heap节点的retained size大小总和
|
|
36
|
+
* @param snapshot heapsnapshot
|
|
37
|
+
*/
|
|
38
|
+
static getSharedHeapNodeRetainedSize(snapshot: IHeapSnapshot): number;
|
|
39
|
+
/**
|
|
40
|
+
* 获取所有string类型对象的retained size大小总和
|
|
41
|
+
* @param snapshot heapsnapshot
|
|
42
|
+
* @returns retained size of shared heap string
|
|
43
|
+
*/
|
|
44
|
+
static getStringRetainedSize(snapshot: IHeapSnapshot): number;
|
|
45
|
+
static isSharedHeapNodeId(snapshot: IHeapSnapshot, nodeId: number): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* 是否是GCRoot节点
|
|
48
|
+
* @param node 目标节点
|
|
49
|
+
* @returns true/false
|
|
50
|
+
*/
|
|
51
|
+
static isGCRoot(targetNode: IHeapNode): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* 是否是Distance为1的节点
|
|
54
|
+
* @param targetNode 目标节点
|
|
55
|
+
* @returns true/false
|
|
56
|
+
*/
|
|
57
|
+
static isEqualDistanceOne(targetNode: IHeapNode): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* 查找指定ID的节点
|
|
60
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
61
|
+
* @param nodeId 节点ID
|
|
62
|
+
* @returns
|
|
63
|
+
*/
|
|
64
|
+
static findNodeById(snapshot: IHeapSnapshot, nodeId: number): Nullable<IHeapNode>;
|
|
65
|
+
/**
|
|
66
|
+
* 查找满足过滤条件的节点
|
|
67
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
68
|
+
* @param filter 过滤器
|
|
69
|
+
* @returns IHeapNode Array
|
|
70
|
+
*/
|
|
71
|
+
static findNodesByFilter(snapshot: IHeapSnapshot, filter: (node: IHeapNode) => boolean): Array<IHeapNode>;
|
|
72
|
+
/**
|
|
73
|
+
* 按过滤器和过滤条件查找节点
|
|
74
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
75
|
+
* @param filter 过滤器
|
|
76
|
+
* @param condition 过滤条件
|
|
77
|
+
* @returns IHeapNode Array
|
|
78
|
+
*/
|
|
79
|
+
static findNodesByFilterCondition(snapshot: IHeapSnapshot, filter: (node: IHeapNode, condition: any) => boolean, condition: any): Array<IHeapNode>;
|
|
80
|
+
/**
|
|
81
|
+
* 查找所有GCRoot节点
|
|
82
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
83
|
+
* @returns IHeapNode Array
|
|
84
|
+
*/
|
|
85
|
+
static findGCRoots(snapshot: IHeapSnapshot): Array<IHeapNode>;
|
|
86
|
+
/**
|
|
87
|
+
* 查找Detached节点
|
|
88
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
89
|
+
* @returns IHeapNode Array
|
|
90
|
+
*/
|
|
91
|
+
static findDetachedNodes(snapshot: IHeapSnapshot): Array<IHeapNode>;
|
|
92
|
+
/**
|
|
93
|
+
* 查找指定个数的Detached节点
|
|
94
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
95
|
+
* @param nodeName 节点名称
|
|
96
|
+
* @returns IHeapNode Array
|
|
97
|
+
*/
|
|
98
|
+
static findDetachedNodesByName(snapshot: IHeapSnapshot, nodeName: string): Array<IHeapNode>;
|
|
99
|
+
/**
|
|
100
|
+
* find nodes by distance from gc root
|
|
101
|
+
* @param snapshot snapshot
|
|
102
|
+
* @param distance distance <= 1
|
|
103
|
+
* @returns Array<IHeapNode>
|
|
104
|
+
*/
|
|
105
|
+
static findNodesByDistanceOne(snapshot: IHeapSnapshot, includeGCRoot?: boolean): Array<IHeapNode>;
|
|
106
|
+
/**
|
|
107
|
+
* 查找指定节点名称的节点
|
|
108
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
109
|
+
* @param nodeName 节点名称
|
|
110
|
+
* @returns IHeapNode Array
|
|
111
|
+
*/
|
|
112
|
+
static findNodesByName(snapshot: IHeapSnapshot, nodeName: string): Array<IHeapNode>;
|
|
113
|
+
/**
|
|
114
|
+
* 查找指定节点名称的节点,最大个数为
|
|
115
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
116
|
+
* @param nodeName 节点名称
|
|
117
|
+
* @param count 查找对象个数(按节点id号排序)
|
|
118
|
+
* @param reverse 是否倒序查找
|
|
119
|
+
* @returns IHeapNode Array
|
|
120
|
+
*/
|
|
121
|
+
static findNodesByNameCount(snapshot: IHeapSnapshot, nodeName: string, count: number): Array<IHeapNode>;
|
|
122
|
+
/**
|
|
123
|
+
*
|
|
124
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
125
|
+
* @param nodeName 节点名称
|
|
126
|
+
* @returns
|
|
127
|
+
*/
|
|
128
|
+
static findNodesByNameDistanceOne(snapshot: IHeapSnapshot, nodeName: string, includeGCRoot?: boolean): Array<IHeapNode>;
|
|
129
|
+
/**
|
|
130
|
+
* 查找指定节点的最短引用路径
|
|
131
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
132
|
+
* @param targetNode 节点对象
|
|
133
|
+
* @returns 引用链路径
|
|
134
|
+
*/
|
|
135
|
+
static findShortestPathToGCRoot(snapshot: IHeapSnapshot, targetNode: IHeapNode): LeakTracePathItem | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* 查找指定节点数组的所有最短引用路径
|
|
138
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
139
|
+
* @param targetNodes 节点对象数组
|
|
140
|
+
* @returns 引用链路径数组
|
|
141
|
+
*/
|
|
142
|
+
static findShortestPaths(snapshot: IHeapSnapshot, targetNodes: IHeapNode[]): Array<LeakTracePathItem>;
|
|
143
|
+
/**
|
|
144
|
+
* 获取堆节点的最短路径
|
|
145
|
+
* @param node 堆节点
|
|
146
|
+
* @returns 最短路径的节点名称数组
|
|
147
|
+
* @example
|
|
148
|
+
*/
|
|
149
|
+
static getShortestPath(node: IHeapNode): [number[], string[], number];
|
|
150
|
+
/**
|
|
151
|
+
* 获取节点模块名(兼容5.x & 6.0)
|
|
152
|
+
* @param name 节点名称
|
|
153
|
+
* @returns module name
|
|
154
|
+
*/
|
|
155
|
+
static getModuleName(name: string): string;
|
|
156
|
+
/**
|
|
157
|
+
* 获取节点Class名(兼容5.x & 6.0)
|
|
158
|
+
* @param name 节点名称
|
|
159
|
+
* @returns class name
|
|
160
|
+
*/
|
|
161
|
+
static getClassName(name: string): string;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=Finder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Finder.d.ts","sourceRoot":"","sources":["../../src/utils/Finder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Finder = void 0;
|
|
4
|
+
const core_1 = require("@memlab/core");
|
|
5
|
+
/**
|
|
6
|
+
* 查找器
|
|
7
|
+
*/
|
|
8
|
+
class Finder {
|
|
9
|
+
/**
|
|
10
|
+
* 判断是否是指定名称属性的边
|
|
11
|
+
* @param edge 边
|
|
12
|
+
* @param propName 名称
|
|
13
|
+
* @returns true/false
|
|
14
|
+
*/
|
|
15
|
+
static isPropEdgeWithName(edge, propName) {
|
|
16
|
+
return edge.type === 'property' && edge.name_or_index === propName;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 判断是否是Proto属性的边
|
|
20
|
+
* @param edge 边
|
|
21
|
+
* @returns true/false
|
|
22
|
+
*/
|
|
23
|
+
static isProtoPropEdge(edge) {
|
|
24
|
+
return edge.type === 'property' && edge.name_or_index === 'Proto';
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 判断是否是hclass属性的边
|
|
28
|
+
* @param edge 边
|
|
29
|
+
* @returns true/false
|
|
30
|
+
*/
|
|
31
|
+
static isHClassPropEdge(edge) {
|
|
32
|
+
return edge.type === 'property' && edge.name_or_index === 'hclass';
|
|
33
|
+
}
|
|
34
|
+
static isSharedHeapNode(node) {
|
|
35
|
+
// String 类对象属于 shared heap
|
|
36
|
+
if (node.isString) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
// 共享内存类型与节点名称匹配的
|
|
40
|
+
if (Finder.OOM_SHARED_TYPES.includes(node.name)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
// 先判断 Proto
|
|
44
|
+
let protoEdge = node.findAnyReference((edge) => { return Finder.isProtoPropEdge(edge); });
|
|
45
|
+
if (protoEdge) {
|
|
46
|
+
// 通过 proto 查找是否是 shared heap
|
|
47
|
+
if (this.OOM_SHARED_TYPES.includes(protoEdge.toNode.name)) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return this.isSharedHeapNode(protoEdge.toNode);
|
|
51
|
+
}
|
|
52
|
+
// 再判断 hclass
|
|
53
|
+
let hclassEdge = node.findAnyReference((edge) => { return this.isHClassPropEdge(edge); });
|
|
54
|
+
if (hclassEdge === null) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return this.isSharedHeapNode(hclassEdge.toNode);
|
|
58
|
+
}
|
|
59
|
+
static getSharedHeapNodesMap(snapshot) {
|
|
60
|
+
let nodesMap = new Map();
|
|
61
|
+
snapshot.nodes.forEach((node) => {
|
|
62
|
+
if (Finder.isSharedHeapNode(node)) {
|
|
63
|
+
let tag = node.isString ? Finder.STRING_UNIQUE_ID : node.name;
|
|
64
|
+
let nodes = nodesMap.get(tag);
|
|
65
|
+
if (nodes === undefined) {
|
|
66
|
+
nodes = new Array();
|
|
67
|
+
}
|
|
68
|
+
nodes.push(node);
|
|
69
|
+
nodesMap.set(tag, nodes);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return nodesMap;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 获取所有shared heap节点的retained size大小总和
|
|
76
|
+
* @param snapshot heapsnapshot
|
|
77
|
+
*/
|
|
78
|
+
static getSharedHeapNodeRetainedSize(snapshot) {
|
|
79
|
+
let retained_size = 0;
|
|
80
|
+
snapshot.nodes.forEach((node) => {
|
|
81
|
+
if (Finder.isSharedHeapNode(node)) {
|
|
82
|
+
retained_size += node.retainedSize;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return retained_size;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 获取所有string类型对象的retained size大小总和
|
|
89
|
+
* @param snapshot heapsnapshot
|
|
90
|
+
* @returns retained size of shared heap string
|
|
91
|
+
*/
|
|
92
|
+
static getStringRetainedSize(snapshot) {
|
|
93
|
+
let retained_size = 0;
|
|
94
|
+
snapshot.nodes.forEach((node) => {
|
|
95
|
+
if (node.isString) {
|
|
96
|
+
retained_size += node.retainedSize;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return retained_size;
|
|
100
|
+
}
|
|
101
|
+
static isSharedHeapNodeId(snapshot, nodeId) {
|
|
102
|
+
const node = snapshot.getNodeById(nodeId);
|
|
103
|
+
if (node === null) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return Finder.isSharedHeapNode(node);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 是否是GCRoot节点
|
|
110
|
+
* @param node 目标节点
|
|
111
|
+
* @returns true/false
|
|
112
|
+
*/
|
|
113
|
+
static isGCRoot(targetNode) {
|
|
114
|
+
return targetNode.numOfReferrers === 0;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 是否是Distance为1的节点
|
|
118
|
+
* @param targetNode 目标节点
|
|
119
|
+
* @returns true/false
|
|
120
|
+
*/
|
|
121
|
+
static isEqualDistanceOne(targetNode) {
|
|
122
|
+
let result = false;
|
|
123
|
+
targetNode.pathEdge;
|
|
124
|
+
targetNode.referrers.forEach((referrer) => {
|
|
125
|
+
if (referrer.fromNode.numOfReferrers === 0) {
|
|
126
|
+
result = true;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 查找指定ID的节点
|
|
133
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
134
|
+
* @param nodeId 节点ID
|
|
135
|
+
* @returns
|
|
136
|
+
*/
|
|
137
|
+
static findNodeById(snapshot, nodeId) {
|
|
138
|
+
return snapshot.getNodeById(nodeId);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 查找满足过滤条件的节点
|
|
142
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
143
|
+
* @param filter 过滤器
|
|
144
|
+
* @returns IHeapNode Array
|
|
145
|
+
*/
|
|
146
|
+
static findNodesByFilter(snapshot, filter) {
|
|
147
|
+
const result = new Array();
|
|
148
|
+
snapshot.nodes.forEach((node) => {
|
|
149
|
+
if (filter(node)) {
|
|
150
|
+
result.push(node);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 按过滤器和过滤条件查找节点
|
|
157
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
158
|
+
* @param filter 过滤器
|
|
159
|
+
* @param condition 过滤条件
|
|
160
|
+
* @returns IHeapNode Array
|
|
161
|
+
*/
|
|
162
|
+
static findNodesByFilterCondition(snapshot, filter, condition) {
|
|
163
|
+
const result = new Array();
|
|
164
|
+
snapshot.nodes.forEach((node) => {
|
|
165
|
+
if (filter(node, condition)) {
|
|
166
|
+
result.push(node);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 查找所有GCRoot节点
|
|
173
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
174
|
+
* @returns IHeapNode Array
|
|
175
|
+
*/
|
|
176
|
+
static findGCRoots(snapshot) {
|
|
177
|
+
let filter = (node) => Finder.isGCRoot(node);
|
|
178
|
+
return Finder.findNodesByFilter(snapshot, filter);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 查找Detached节点
|
|
182
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
183
|
+
* @returns IHeapNode Array
|
|
184
|
+
*/
|
|
185
|
+
static findDetachedNodes(snapshot) {
|
|
186
|
+
let filter = (node) => node.is_detached;
|
|
187
|
+
return Finder.findNodesByFilter(snapshot, filter);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 查找指定个数的Detached节点
|
|
191
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
192
|
+
* @param nodeName 节点名称
|
|
193
|
+
* @returns IHeapNode Array
|
|
194
|
+
*/
|
|
195
|
+
static findDetachedNodesByName(snapshot, nodeName) {
|
|
196
|
+
const result = new Array();
|
|
197
|
+
snapshot.nodes.forEach((node) => {
|
|
198
|
+
if (node.is_detached && node.name === nodeName) {
|
|
199
|
+
result.push(node);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* find nodes by distance from gc root
|
|
206
|
+
* @param snapshot snapshot
|
|
207
|
+
* @param distance distance <= 1
|
|
208
|
+
* @returns Array<IHeapNode>
|
|
209
|
+
*/
|
|
210
|
+
static findNodesByDistanceOne(snapshot, includeGCRoot = false) {
|
|
211
|
+
let filter = (node) => {
|
|
212
|
+
return (includeGCRoot && Finder.isGCRoot(node)) || Finder.isEqualDistanceOne(node);
|
|
213
|
+
};
|
|
214
|
+
return Finder.findNodesByFilter(snapshot, filter);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* 查找指定节点名称的节点
|
|
218
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
219
|
+
* @param nodeName 节点名称
|
|
220
|
+
* @returns IHeapNode Array
|
|
221
|
+
*/
|
|
222
|
+
static findNodesByName(snapshot, nodeName) {
|
|
223
|
+
let filter = (node) => {
|
|
224
|
+
return node.name == nodeName;
|
|
225
|
+
};
|
|
226
|
+
return Finder.findNodesByFilter(snapshot, filter);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* 查找指定节点名称的节点,最大个数为
|
|
230
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
231
|
+
* @param nodeName 节点名称
|
|
232
|
+
* @param count 查找对象个数(按节点id号排序)
|
|
233
|
+
* @param reverse 是否倒序查找
|
|
234
|
+
* @returns IHeapNode Array
|
|
235
|
+
*/
|
|
236
|
+
static findNodesByNameCount(snapshot, nodeName, count) {
|
|
237
|
+
const result = new Array();
|
|
238
|
+
let nodeCount = 0;
|
|
239
|
+
snapshot.nodes.forEach((node) => {
|
|
240
|
+
if ((node.name == nodeName) && (nodeCount < count)) {
|
|
241
|
+
result.push(node);
|
|
242
|
+
nodeCount += 1;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
*
|
|
249
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
250
|
+
* @param nodeName 节点名称
|
|
251
|
+
* @returns
|
|
252
|
+
*/
|
|
253
|
+
static findNodesByNameDistanceOne(snapshot, nodeName, includeGCRoot = false) {
|
|
254
|
+
let filter = (node) => {
|
|
255
|
+
return (node.name === nodeName)
|
|
256
|
+
&& ((includeGCRoot && Finder.isGCRoot(node)) || Finder.isEqualDistanceOne(node));
|
|
257
|
+
};
|
|
258
|
+
return Finder.findNodesByFilter(snapshot, filter);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* 查找指定节点的最短引用路径
|
|
262
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
263
|
+
* @param targetNode 节点对象
|
|
264
|
+
* @returns 引用链路径
|
|
265
|
+
*/
|
|
266
|
+
static findShortestPathToGCRoot(snapshot, targetNode) {
|
|
267
|
+
Finder.traceFinder.annotateShortestPaths(snapshot); // 标记最短路径
|
|
268
|
+
const optPath = Finder.traceFinder.getPathToGCRoots(snapshot, targetNode);
|
|
269
|
+
return optPath ? optPath : undefined;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* 查找指定节点数组的所有最短引用路径
|
|
273
|
+
* @param snapshot 快照对象 IHeapSnapshot
|
|
274
|
+
* @param targetNodes 节点对象数组
|
|
275
|
+
* @returns 引用链路径数组
|
|
276
|
+
*/
|
|
277
|
+
static findShortestPaths(snapshot, targetNodes) {
|
|
278
|
+
const result = new Array();
|
|
279
|
+
Finder.traceFinder.annotateShortestPaths(snapshot);
|
|
280
|
+
targetNodes.forEach(node => {
|
|
281
|
+
const optPath = Finder.traceFinder.getPathToGCRoots(snapshot, node);
|
|
282
|
+
if (optPath) {
|
|
283
|
+
result.push(optPath);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* 获取堆节点的最短路径
|
|
290
|
+
* @param node 堆节点
|
|
291
|
+
* @returns 最短路径的节点名称数组
|
|
292
|
+
* @example
|
|
293
|
+
*/
|
|
294
|
+
static getShortestPath(node) {
|
|
295
|
+
const shortestPathNodeIds = [];
|
|
296
|
+
const shortestPathStrings = [];
|
|
297
|
+
let distance = 0;
|
|
298
|
+
let shortestPathNode = node;
|
|
299
|
+
while (shortestPathNode && shortestPathNode.hasPathEdge) {
|
|
300
|
+
let edge = shortestPathNode.pathEdge;
|
|
301
|
+
if (edge) {
|
|
302
|
+
distance++;
|
|
303
|
+
shortestPathNodeIds.push(edge.fromNode.id);
|
|
304
|
+
shortestPathStrings.push(edge.fromNode.name);
|
|
305
|
+
shortestPathNode = edge.fromNode;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return [shortestPathNodeIds, shortestPathStrings, distance];
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* 获取节点模块名(兼容5.x & 6.0)
|
|
312
|
+
* @param name 节点名称
|
|
313
|
+
* @returns module name
|
|
314
|
+
*/
|
|
315
|
+
static getModuleName(name) {
|
|
316
|
+
let nameSplit = name.split('#');
|
|
317
|
+
if (nameSplit.length > 1) {
|
|
318
|
+
// 6.0
|
|
319
|
+
return nameSplit[0];
|
|
320
|
+
}
|
|
321
|
+
// 5.x
|
|
322
|
+
return name.split(' ')[0];
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* 获取节点Class名(兼容5.x & 6.0)
|
|
326
|
+
* @param name 节点名称
|
|
327
|
+
* @returns class name
|
|
328
|
+
*/
|
|
329
|
+
static getClassName(name) {
|
|
330
|
+
let regex = /^(.*?)\(line:\d+\).*?$/;
|
|
331
|
+
const match = regex.exec(name);
|
|
332
|
+
if (match) {
|
|
333
|
+
return match[1];
|
|
334
|
+
}
|
|
335
|
+
let regex5 = /^(.*?)\(.*?\)$/;
|
|
336
|
+
const match5 = regex5.exec(name);
|
|
337
|
+
if (match5) {
|
|
338
|
+
return match5[1];
|
|
339
|
+
}
|
|
340
|
+
return name;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
exports.Finder = Finder;
|
|
344
|
+
Finder.traceFinder = new core_1.TraceFinder();
|
|
345
|
+
/**
|
|
346
|
+
* 共享内存类型
|
|
347
|
+
* 例举关键类型
|
|
348
|
+
*/
|
|
349
|
+
Finder.OOM_SHARED_TYPES = [
|
|
350
|
+
'js_shared_object',
|
|
351
|
+
'js_shared_array',
|
|
352
|
+
'js_shared_map',
|
|
353
|
+
'js_shared_set'
|
|
354
|
+
];
|
|
355
|
+
Finder.STRING_UNIQUE_ID = 'shared_heap_string_unique_id_0123456789';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { IHeapSnapshot, AnyOptions } from "@memlab/core";
|
|
2
|
+
/**
|
|
3
|
+
* Loader
|
|
4
|
+
*/
|
|
5
|
+
export declare class Loader {
|
|
6
|
+
static readonly DOMAIN = "HeapSnapshot";
|
|
7
|
+
static readonly TAG = "Loader";
|
|
8
|
+
/**
|
|
9
|
+
* 加载HeapSnapshot文件
|
|
10
|
+
* @param file 文件路径
|
|
11
|
+
* @param log 是否打印日志
|
|
12
|
+
* @returns IHeapSnapshot 对象
|
|
13
|
+
*/
|
|
14
|
+
static loadFromFile(file: string, log?: boolean): Promise<IHeapSnapshot>;
|
|
15
|
+
/**
|
|
16
|
+
* 加载HeapSnapshot文件
|
|
17
|
+
* @param file 文件路径
|
|
18
|
+
* @param options 可选参数选项
|
|
19
|
+
* @param log 是否打印日志
|
|
20
|
+
* @returns IHeapSnapshot 对象
|
|
21
|
+
*/
|
|
22
|
+
static loadFromFileOption(file: string, options: AnyOptions, log?: boolean): Promise<IHeapSnapshot>;
|
|
23
|
+
/**
|
|
24
|
+
* Heapsnapshot 多文件加载
|
|
25
|
+
* @param files 多文件路径集合
|
|
26
|
+
* @returns IHeapSnapshot 对象集合
|
|
27
|
+
*/
|
|
28
|
+
static loadFromFiles(files: Array<string>): Promise<Array<IHeapSnapshot>>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=Loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Loader.d.ts","sourceRoot":"","sources":["../../src/utils/Loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAS,UAAU,EAAE,MAAM,cAAc,CAAC;AAIhE;;GAEG;AACH,qBAAa,MAAM;IACf,gBAAuB,MAAM,kBAAkB;IAC/C,gBAAuB,GAAG,YAAY;IAEtC;;;;;OAKG;WACiB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,OAAe,GAAI,OAAO,CAAC,aAAa,CAAC;IAO7F;;;;;;OAMG;WACiB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAC/C,OAAO,EAAE,UAAU,EAAE,GAAG,GAAE,OAAe,GAAI,OAAO,CAAC,aAAa,CAAC;IAOvE;;;;OAIG;WACiB,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAI,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;CAQ1F"}
|