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,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
|
+
}
|