@memlab/core 1.1.19 → 1.1.20
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/__tests__/parser/StringNode.test.js +12 -1
- package/dist/lib/Config.d.ts +2 -0
- package/dist/lib/Config.js +4 -0
- package/dist/lib/FileManager.d.ts +3 -0
- package/dist/lib/FileManager.js +46 -3
- package/dist/lib/Types.d.ts +73 -0
- package/dist/lib/Utils.d.ts +6 -2
- package/dist/lib/Utils.js +30 -4
- package/dist/lib/charts/MemoryBarChart.js +6 -0
- package/dist/lib/heap-data/HeapEdge.d.ts +3 -1
- package/dist/lib/heap-data/HeapEdge.js +13 -0
- package/dist/lib/heap-data/HeapLocation.d.ts +3 -1
- package/dist/lib/heap-data/HeapLocation.js +14 -0
- package/dist/lib/heap-data/HeapNode.d.ts +6 -1
- package/dist/lib/heap-data/HeapNode.js +46 -0
- package/dist/lib/heap-data/HeapSnapshot.d.ts +3 -1
- package/dist/lib/heap-data/HeapSnapshot.js +7 -0
- package/dist/lib/heap-data/HeapStringNode.d.ts +2 -1
- package/dist/lib/heap-data/HeapStringNode.js +5 -0
- package/dist/trace-cluster/TraceBucket.d.ts +1 -0
- package/dist/trace-cluster/TraceBucket.js +15 -1
- package/dist/trace-cluster/TraceElement.d.ts +6 -1
- package/dist/trace-cluster/TraceElement.js +33 -0
- package/dist/trace-cluster/strategies/TraceSimilarityStrategy.js +9 -0
- package/package.json +1 -1
- package/static/visit-order-single-snapshot.json +19 -0
|
@@ -28,7 +28,7 @@ beforeEach(() => {
|
|
|
28
28
|
});
|
|
29
29
|
const timeout = 5 * 60 * 1000;
|
|
30
30
|
test('String heap object APIs work', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
|
-
var _a, _b, _c;
|
|
31
|
+
var _a, _b, _c, _d, _e, _f;
|
|
32
32
|
class TestObject {
|
|
33
33
|
constructor() {
|
|
34
34
|
this.originalString = 'test';
|
|
@@ -44,6 +44,8 @@ test('String heap object APIs work', () => __awaiter(void 0, void 0, void 0, fun
|
|
|
44
44
|
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
45
45
|
const testObject = heap.getAnyObjectWithClassName('TestObject');
|
|
46
46
|
expect(testObject).not.toBe(null);
|
|
47
|
+
// test the number of referrers getter
|
|
48
|
+
expect(testObject === null || testObject === void 0 ? void 0 : testObject.numOfReferrers).toBeGreaterThan(0);
|
|
47
49
|
// testObject.originalString === 'test'
|
|
48
50
|
const originalString = testObject === null || testObject === void 0 ? void 0 : testObject.getReferenceNode('originalString');
|
|
49
51
|
expect(originalString === null || originalString === void 0 ? void 0 : originalString.isString).toBe(true);
|
|
@@ -58,4 +60,13 @@ test('String heap object APIs work', () => __awaiter(void 0, void 0, void 0, fun
|
|
|
58
60
|
expect(complexConcatString === null || complexConcatString === void 0 ? void 0 : complexConcatString.type).toBe('concatenated string');
|
|
59
61
|
expect(complexConcatString === null || complexConcatString === void 0 ? void 0 : complexConcatString.isString).toBe(true);
|
|
60
62
|
expect((_c = complexConcatString === null || complexConcatString === void 0 ? void 0 : complexConcatString.toStringNode()) === null || _c === void 0 ? void 0 : _c.stringValue).toBe('prefix_value_123_suffix');
|
|
63
|
+
// test the toJSONString API
|
|
64
|
+
let strRepresentation = (_d = concatString === null || concatString === void 0 ? void 0 : concatString.toJSONString()) !== null && _d !== void 0 ? _d : '{}';
|
|
65
|
+
let rep = JSON.parse(strRepresentation);
|
|
66
|
+
expect(rep.type).toBe('concatenated string');
|
|
67
|
+
expect(rep.stringValue).toBe(undefined);
|
|
68
|
+
strRepresentation = (_f = (_e = concatString === null || concatString === void 0 ? void 0 : concatString.toStringNode()) === null || _e === void 0 ? void 0 : _e.toJSONString()) !== null && _f !== void 0 ? _f : '{}';
|
|
69
|
+
rep = JSON.parse(strRepresentation);
|
|
70
|
+
expect(rep.type).toBe('concatenated string');
|
|
71
|
+
expect(rep.stringValue).toBe('prefix_suffix');
|
|
61
72
|
}), timeout);
|
package/dist/lib/Config.d.ts
CHANGED
|
@@ -196,6 +196,8 @@ export declare class MemLabConfig {
|
|
|
196
196
|
seqClusteringIsRandomChunks: boolean;
|
|
197
197
|
instrumentJS: boolean;
|
|
198
198
|
interceptScript: boolean;
|
|
199
|
+
isAnalyzingMainThread: boolean;
|
|
200
|
+
targetWorkerTitle: Nullable<string>;
|
|
199
201
|
constructor(options?: ConfigOption);
|
|
200
202
|
private initInternalConfigs;
|
|
201
203
|
private init;
|
package/dist/lib/Config.js
CHANGED
|
@@ -211,6 +211,10 @@ class MemLabConfig {
|
|
|
211
211
|
this.isManualDebug = false;
|
|
212
212
|
// number of warmup repeat in each browser tab instance
|
|
213
213
|
this.warmupRepeat = 2;
|
|
214
|
+
// by default, analyzing heap in main thread
|
|
215
|
+
this.isAnalyzingMainThread = true;
|
|
216
|
+
// target worker's title
|
|
217
|
+
this.targetWorkerTitle = null;
|
|
214
218
|
// default waiting time when there is no page load checker callback
|
|
215
219
|
this.delayWhenNoPageLoadCheck = 2000;
|
|
216
220
|
// repeat the intermediate sequence
|
|
@@ -51,7 +51,9 @@ export declare class FileManager {
|
|
|
51
51
|
getPreviewReportDir(options?: FileOption): string;
|
|
52
52
|
getLeakSummaryFile(options?: FileOption): string;
|
|
53
53
|
getRunMetaFile(options?: FileOption): string;
|
|
54
|
+
getRunMetaExternalTemplateFile(): string;
|
|
54
55
|
getSnapshotSequenceMetaFile(options?: FileOption): string;
|
|
56
|
+
getSnapshotSequenceExternalTemplateFile(): string;
|
|
55
57
|
getInputDataDir(): string;
|
|
56
58
|
getAllFilesInSubDirs(dir: string): string[];
|
|
57
59
|
getTmpDir(): string;
|
|
@@ -72,6 +74,7 @@ export declare class FileManager {
|
|
|
72
74
|
iterateAllFiles(dir: string, callback: (filepath: string) => AnyValue): void;
|
|
73
75
|
rmWorkDir(options?: FileOption): void;
|
|
74
76
|
isWithinInternalDirectory(filePath: string): boolean;
|
|
77
|
+
createDefaultVisitOrderMetaFile(options?: FileOption): void;
|
|
75
78
|
initDirs(config: MemLabConfig, options?: FileOption): void;
|
|
76
79
|
}
|
|
77
80
|
declare const _default: FileManager;
|
package/dist/lib/FileManager.js
CHANGED
|
@@ -179,9 +179,15 @@ class FileManager {
|
|
|
179
179
|
getRunMetaFile(options = FileManager.defaultFileOption) {
|
|
180
180
|
return path_1.default.join(this.getCurDataDir(options), 'run-meta.json');
|
|
181
181
|
}
|
|
182
|
+
getRunMetaExternalTemplateFile() {
|
|
183
|
+
return path_1.default.join(this.getCodeDataDir(), 'run-meta.json');
|
|
184
|
+
}
|
|
182
185
|
getSnapshotSequenceMetaFile(options = FileManager.defaultFileOption) {
|
|
183
186
|
return path_1.default.join(this.getCurDataDir(options), 'snap-seq.json');
|
|
184
187
|
}
|
|
188
|
+
getSnapshotSequenceExternalTemplateFile() {
|
|
189
|
+
return path_1.default.join(this.getCodeDataDir(), 'visit-order.json');
|
|
190
|
+
}
|
|
185
191
|
getInputDataDir() {
|
|
186
192
|
return path_1.default.join(this.getDefaultWorkDir(), 'input');
|
|
187
193
|
}
|
|
@@ -331,6 +337,37 @@ class FileManager {
|
|
|
331
337
|
const internalDir = Constant_1.default.internalDir;
|
|
332
338
|
return filePath.includes(`${sep}${internalDir}${sep}`);
|
|
333
339
|
}
|
|
340
|
+
createDefaultVisitOrderMetaFile(options = FileManager.defaultFileOption) {
|
|
341
|
+
// if memlab/data/cur doesn't exist, return
|
|
342
|
+
const curDataDir = this.getCurDataDir(options);
|
|
343
|
+
if (!fs_extra_1.default.existsSync(curDataDir)) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
// if the snap-seq.json file exists, return
|
|
347
|
+
const snapshotSeqMetaFile = this.getSnapshotSequenceMetaFile(options);
|
|
348
|
+
if (fs_extra_1.default.existsSync(snapshotSeqMetaFile)) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
// if there is no .heapsnapshot file, return
|
|
352
|
+
const files = fs_extra_1.default.readdirSync(curDataDir);
|
|
353
|
+
const snapshotFile = files.find(file => file.endsWith('.heapsnapshot'));
|
|
354
|
+
if (snapshotFile == null) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
// If there is at least one snapshot, create a snap-seq.json file.
|
|
358
|
+
// First, get the meta file for leak detection in a single heap snapshot
|
|
359
|
+
const codeDataDir = this.getCodeDataDir();
|
|
360
|
+
const singleSnapshotMetaFile = path_1.default.join(codeDataDir, 'visit-order-single-snapshot.json');
|
|
361
|
+
const visitOrder = JSON.parse(fs_extra_1.default.readFileSync(singleSnapshotMetaFile, 'UTF-8'));
|
|
362
|
+
// fill in snapshot file name for each entry with snapshot: true
|
|
363
|
+
visitOrder.forEach(step => {
|
|
364
|
+
if (step.snapshot === true) {
|
|
365
|
+
step.snapshotFile = snapshotFile;
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
// save the snapshot meta file
|
|
369
|
+
fs_extra_1.default.writeFileSync(snapshotSeqMetaFile, JSON.stringify(visitOrder, null, 2), 'UTF-8');
|
|
370
|
+
}
|
|
334
371
|
initDirs(config, options = FileManager.defaultFileOption) {
|
|
335
372
|
// cache the last processed memlab config instance
|
|
336
373
|
// the instance should be a singleton
|
|
@@ -339,6 +376,11 @@ class FileManager {
|
|
|
339
376
|
// make sure getWorkDir is called first before
|
|
340
377
|
// any other get file or get dir calls
|
|
341
378
|
const workDir = this.getWorkDir(options);
|
|
379
|
+
// if errorWhenAbsent is set to true, make it
|
|
380
|
+
// an error when the working directory does not exist
|
|
381
|
+
if (options.errorWhenAbsent && !fs_extra_1.default.existsSync(workDir)) {
|
|
382
|
+
throw Utils_1.default.haltOrThrow(`work dir does not exist: ${workDir}`);
|
|
383
|
+
}
|
|
342
384
|
// remember the current working directory
|
|
343
385
|
// especially if this is a transcient working directory
|
|
344
386
|
config.workDir = joinAndProcessDir(options, workDir);
|
|
@@ -376,15 +418,16 @@ class FileManager {
|
|
|
376
418
|
config.heapAnalysisLogDir = joinAndProcessDir(options, this.getHeapAnalysisLogDir(options));
|
|
377
419
|
config.metricsOutDir = joinAndProcessDir(options, loggerOutDir, 'metrics');
|
|
378
420
|
config.reportScreenshotFile = path_1.default.join(outDir, 'report.png');
|
|
379
|
-
|
|
380
|
-
config.
|
|
381
|
-
|
|
421
|
+
config.externalRunMetaFile = this.getRunMetaExternalTemplateFile();
|
|
422
|
+
config.externalSnapshotVisitOrderFile =
|
|
423
|
+
this.getSnapshotSequenceExternalTemplateFile();
|
|
382
424
|
joinAndProcessDir(options, this.getUniqueTraceClusterDir(options));
|
|
383
425
|
config.newUniqueClusterDir = joinAndProcessDir(options, this.getNewUniqueTraceClusterDir(options));
|
|
384
426
|
config.staleUniqueClusterDir = joinAndProcessDir(options, this.getStaleUniqueTraceClusterDir(options));
|
|
385
427
|
config.currentUniqueClusterDir = joinAndProcessDir(options, this.getExistingUniqueTraceClusterDir(options));
|
|
386
428
|
config.unclassifiedClusterDir = joinAndProcessDir(options, this.getUnclassifiedTraceClusterDir(options));
|
|
387
429
|
config.allClusterSummaryFile = this.getAllClusterSummaryFile(options);
|
|
430
|
+
this.createDefaultVisitOrderMetaFile(options);
|
|
388
431
|
}
|
|
389
432
|
}
|
|
390
433
|
exports.FileManager = FileManager;
|
package/dist/lib/Types.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ export declare type FileOption = {
|
|
|
47
47
|
workDir?: Optional<string>;
|
|
48
48
|
clear?: boolean;
|
|
49
49
|
transient?: boolean;
|
|
50
|
+
errorWhenAbsent?: boolean;
|
|
50
51
|
};
|
|
51
52
|
/** @internal */
|
|
52
53
|
export declare type CLIOptions = {
|
|
@@ -897,6 +898,7 @@ export interface IE2EStepBasic {
|
|
|
897
898
|
/** @internal */
|
|
898
899
|
export declare type E2EStepInfo = IE2EStepBasic & {
|
|
899
900
|
snapshot: boolean;
|
|
901
|
+
snapshotFile?: string;
|
|
900
902
|
screenshot: boolean;
|
|
901
903
|
idx: number;
|
|
902
904
|
JSHeapUsedSize: number;
|
|
@@ -1218,6 +1220,16 @@ export interface IHeapLocation {
|
|
|
1218
1220
|
* get the column number
|
|
1219
1221
|
*/
|
|
1220
1222
|
column: number;
|
|
1223
|
+
/**
|
|
1224
|
+
* convert to a concise readable string output
|
|
1225
|
+
* (like calling `JSON.stringify(node, ...args)`).
|
|
1226
|
+
* Note: calling `JSON.stringify(node, ...args)` will not work
|
|
1227
|
+
* since the string is too large and not readable.
|
|
1228
|
+
*
|
|
1229
|
+
* This API does not completely serialize all the information
|
|
1230
|
+
* captured by the hosting object.
|
|
1231
|
+
*/
|
|
1232
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
1221
1233
|
}
|
|
1222
1234
|
/** @internal */
|
|
1223
1235
|
export interface IHeapEdgeBasic {
|
|
@@ -1290,6 +1302,16 @@ export interface IHeapEdge extends IHeapEdgeBasic {
|
|
|
1290
1302
|
* JS heap object where this reference starts
|
|
1291
1303
|
*/
|
|
1292
1304
|
fromNode: IHeapNode;
|
|
1305
|
+
/**
|
|
1306
|
+
* convert to a concise readable string output
|
|
1307
|
+
* (like calling `JSON.stringify(node, ...args)`).
|
|
1308
|
+
* Note: calling `JSON.stringify(node, ...args)` will not work
|
|
1309
|
+
* since the string is too large and not readable.
|
|
1310
|
+
*
|
|
1311
|
+
* This API does not completely serialize all the information
|
|
1312
|
+
* captured by the hosting object.
|
|
1313
|
+
*/
|
|
1314
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
1293
1315
|
}
|
|
1294
1316
|
/**
|
|
1295
1317
|
* A pseudo array containing all heap graph edges (references to heap objects
|
|
@@ -1446,6 +1468,11 @@ export interface IHeapNode extends IHeapNodeBasic {
|
|
|
1446
1468
|
* object (including engine-internal, native, and JS references).
|
|
1447
1469
|
*/
|
|
1448
1470
|
referrers: IHeapEdge[];
|
|
1471
|
+
/**
|
|
1472
|
+
* Get the number of all incoming references pointing to this heap object
|
|
1473
|
+
* (including engine-internal, native, and JS references).
|
|
1474
|
+
*/
|
|
1475
|
+
numOfReferrers: number;
|
|
1449
1476
|
/**
|
|
1450
1477
|
* returns true if the heap node has been set an incoming edge
|
|
1451
1478
|
* which leads to the parent node on the shortest path to GC root.
|
|
@@ -1493,6 +1520,16 @@ export interface IHeapNode extends IHeapNodeBasic {
|
|
|
1493
1520
|
* inside the string node.
|
|
1494
1521
|
*/
|
|
1495
1522
|
toStringNode(): Nullable<IHeapStringNode>;
|
|
1523
|
+
/**
|
|
1524
|
+
* convert to a concise readable string output
|
|
1525
|
+
* (like calling `JSON.stringify(node, ...args)`).
|
|
1526
|
+
* Note: calling `JSON.stringify(node, ...args)` will not work
|
|
1527
|
+
* since the string is too large and not readable.
|
|
1528
|
+
*
|
|
1529
|
+
* This API does not completely serialize all the information
|
|
1530
|
+
* captured by the hosting object.
|
|
1531
|
+
*/
|
|
1532
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
1496
1533
|
/**
|
|
1497
1534
|
* executes a provided callback once for each JavaScript reference in the
|
|
1498
1535
|
* hosting node (or outgoing edges from the node)
|
|
@@ -1563,6 +1600,24 @@ export interface IHeapNode extends IHeapNodeBasic {
|
|
|
1563
1600
|
* ```
|
|
1564
1601
|
*/
|
|
1565
1602
|
findAnyReferrer: (predicate: Predicator<IHeapEdge>) => Nullable<IHeapEdge>;
|
|
1603
|
+
/**
|
|
1604
|
+
* executes a provided predicate callback once for each JavaScript heap
|
|
1605
|
+
* object (heap graph node) pointing to the hosting node
|
|
1606
|
+
* (or nodes having edges to the hosting node) until the predicate
|
|
1607
|
+
* returns `true`
|
|
1608
|
+
* @param predicate the callback for each incoming JavaScript heap object
|
|
1609
|
+
* @returns the first referring node for which the predicate returns `true`,
|
|
1610
|
+
* otherwise returns `null` if no such node is found.
|
|
1611
|
+
*
|
|
1612
|
+
* * **Examples**:
|
|
1613
|
+
* ```typescript
|
|
1614
|
+
* const referrer = node.findAnyReferrerNode((node: IHeapNode) => {
|
|
1615
|
+
* // find the referring node with name "Parent"
|
|
1616
|
+
* return node.name === 'Parent';
|
|
1617
|
+
* });
|
|
1618
|
+
* ```
|
|
1619
|
+
*/
|
|
1620
|
+
findAnyReferrerNode(predicate: Predicator<IHeapNode>): Nullable<IHeapNode>;
|
|
1566
1621
|
/**
|
|
1567
1622
|
* executes a provided predicate callback once for each JavaScript reference
|
|
1568
1623
|
* pointing to the hosting node (or incoming edges to the node)
|
|
@@ -1580,6 +1635,24 @@ export interface IHeapNode extends IHeapNodeBasic {
|
|
|
1580
1635
|
* ```
|
|
1581
1636
|
*/
|
|
1582
1637
|
findReferrers: (predicate: Predicator<IHeapEdge>) => IHeapEdge[];
|
|
1638
|
+
/**
|
|
1639
|
+
* executes a provided predicate callback once for each JavaScript heap
|
|
1640
|
+
* object (heap graph node) pointing to the hosting node
|
|
1641
|
+
* (or nodes having edges to the hosting node)
|
|
1642
|
+
* @param predicate the callback for each referrer nodes
|
|
1643
|
+
* @returns an array containing all the referrer nodes for which the
|
|
1644
|
+
* predicate returns `true`, otherwise returns an empty array if no such
|
|
1645
|
+
* node is found.
|
|
1646
|
+
*
|
|
1647
|
+
* * **Examples**:
|
|
1648
|
+
* ```typescript
|
|
1649
|
+
* const referrerNodes = node.findReferrerNodes((node: IHeapNode) => {
|
|
1650
|
+
* // find all the referring nodes with name "Parent"
|
|
1651
|
+
* return node.name === 'Parent';
|
|
1652
|
+
* });
|
|
1653
|
+
* ```
|
|
1654
|
+
*/
|
|
1655
|
+
findReferrerNodes: (predicate: Predicator<IHeapNode>) => IHeapNode[];
|
|
1583
1656
|
/**
|
|
1584
1657
|
* Given a JS reference's name and type, this API finds an outgoing JS
|
|
1585
1658
|
* reference from the hosting node.
|
package/dist/lib/Utils.d.ts
CHANGED
|
@@ -125,8 +125,10 @@ declare function isNodeDominatedByDeletionsArray(node: IHeapNode): boolean;
|
|
|
125
125
|
declare function getUniqueID(): string;
|
|
126
126
|
declare function getClosureSourceUrl(node: IHeapNode): Nullable<string>;
|
|
127
127
|
export declare function runShell(command: string, options?: ShellOptions): Nullable<string>;
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
declare function getRetainedSize(node: IHeapNode): number;
|
|
129
|
+
declare function aggregateDominatorMetrics(ids: HeapNodeIdSet, snapshot: IHeapSnapshot, checkNodeCb: (node: IHeapNode) => boolean, nodeMetricsCb: (node: IHeapNode) => number): number;
|
|
130
|
+
declare function getLeakTracePathLength(path: LeakTracePathItem): number;
|
|
131
|
+
declare function getNumberAtPercentile(arr: number[], percentile: number): number;
|
|
130
132
|
declare const _default: {
|
|
131
133
|
aggregateDominatorMetrics: typeof aggregateDominatorMetrics;
|
|
132
134
|
applyToNodes: typeof applyToNodes;
|
|
@@ -149,7 +151,9 @@ declare const _default: {
|
|
|
149
151
|
getEdgeByNameAndType: typeof getEdgeByNameAndType;
|
|
150
152
|
getLastNodeId: typeof getLastNodeId;
|
|
151
153
|
getLeakedNode: typeof getLeakedNode;
|
|
154
|
+
getLeakTracePathLength: typeof getLeakTracePathLength;
|
|
152
155
|
getNodesIdSet: typeof getNodesIdSet;
|
|
156
|
+
getNumberAtPercentile: typeof getNumberAtPercentile;
|
|
153
157
|
getNumberNodeValue: typeof getNumberNodeValue;
|
|
154
158
|
getReadableBytes: typeof getReadableBytes;
|
|
155
159
|
getReadablePercent: typeof getReadablePercent;
|
package/dist/lib/Utils.js
CHANGED
|
@@ -44,7 +44,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
44
44
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
45
45
|
};
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
-
exports.
|
|
47
|
+
exports.runShell = exports.resolveSnapshotFilePath = void 0;
|
|
48
48
|
const fs_1 = __importDefault(require("fs"));
|
|
49
49
|
const path_1 = __importDefault(require("path"));
|
|
50
50
|
const child_process_1 = __importDefault(require("child_process"));
|
|
@@ -1272,7 +1272,7 @@ function checkSnapshots(options = {}) {
|
|
|
1272
1272
|
snapshotDir = Config_1.default.externalSnapshotDir || '<missing>';
|
|
1273
1273
|
}
|
|
1274
1274
|
else {
|
|
1275
|
-
snapshotDir = FileManager_1.default.getCurDataDir({});
|
|
1275
|
+
snapshotDir = FileManager_1.default.getCurDataDir({ workDir: Config_1.default.workDir });
|
|
1276
1276
|
}
|
|
1277
1277
|
if (options.snapshotDir) {
|
|
1278
1278
|
const snapshots = getSnapshotFilesInDir(snapshotDir);
|
|
@@ -1347,6 +1347,9 @@ function getSingleSnapshotFileForAnalysis() {
|
|
|
1347
1347
|
return resolveSnapshotFilePath(path);
|
|
1348
1348
|
}
|
|
1349
1349
|
function getSnapshotFilePath(tab, options = {}) {
|
|
1350
|
+
if (tab.snapshotFile) {
|
|
1351
|
+
return path_1.default.join(FileManager_1.default.getCurDataDir(options), tab.snapshotFile);
|
|
1352
|
+
}
|
|
1350
1353
|
const fileName = `s${tab.idx}.heapsnapshot`;
|
|
1351
1354
|
if (options.workDir) {
|
|
1352
1355
|
return path_1.default.join(FileManager_1.default.getCurDataDir(options), fileName);
|
|
@@ -1790,7 +1793,6 @@ exports.runShell = runShell;
|
|
|
1790
1793
|
function getRetainedSize(node) {
|
|
1791
1794
|
return node.retainedSize;
|
|
1792
1795
|
}
|
|
1793
|
-
exports.getRetainedSize = getRetainedSize;
|
|
1794
1796
|
function aggregateDominatorMetrics(ids, snapshot, checkNodeCb, nodeMetricsCb) {
|
|
1795
1797
|
let ret = 0;
|
|
1796
1798
|
const dominators = __1.utils.getConditionalDominatorIds(ids, snapshot, checkNodeCb);
|
|
@@ -1799,7 +1801,29 @@ function aggregateDominatorMetrics(ids, snapshot, checkNodeCb, nodeMetricsCb) {
|
|
|
1799
1801
|
});
|
|
1800
1802
|
return ret;
|
|
1801
1803
|
}
|
|
1802
|
-
|
|
1804
|
+
function getLeakTracePathLength(path) {
|
|
1805
|
+
let len = 0;
|
|
1806
|
+
let p = path;
|
|
1807
|
+
while (p) {
|
|
1808
|
+
p = p.next;
|
|
1809
|
+
++len;
|
|
1810
|
+
}
|
|
1811
|
+
return len;
|
|
1812
|
+
}
|
|
1813
|
+
function getNumberAtPercentile(arr, percentile) {
|
|
1814
|
+
arr.sort(function (a, b) {
|
|
1815
|
+
return a - b;
|
|
1816
|
+
});
|
|
1817
|
+
const index = (percentile / 100) * arr.length;
|
|
1818
|
+
const indexInt = Math.floor(index);
|
|
1819
|
+
if (indexInt === index) {
|
|
1820
|
+
return arr[Math.floor(index)];
|
|
1821
|
+
}
|
|
1822
|
+
if (indexInt + 1 < arr.length) {
|
|
1823
|
+
return (arr[indexInt] + arr[indexInt + 1]) / 2;
|
|
1824
|
+
}
|
|
1825
|
+
return arr[indexInt];
|
|
1826
|
+
}
|
|
1803
1827
|
exports.default = {
|
|
1804
1828
|
aggregateDominatorMetrics,
|
|
1805
1829
|
applyToNodes,
|
|
@@ -1822,7 +1846,9 @@ exports.default = {
|
|
|
1822
1846
|
getEdgeByNameAndType,
|
|
1823
1847
|
getLastNodeId,
|
|
1824
1848
|
getLeakedNode,
|
|
1849
|
+
getLeakTracePathLength,
|
|
1825
1850
|
getNodesIdSet,
|
|
1851
|
+
getNumberAtPercentile,
|
|
1826
1852
|
getNumberNodeValue,
|
|
1827
1853
|
getReadableBytes,
|
|
1828
1854
|
getReadablePercent,
|
|
@@ -30,6 +30,12 @@ class MemoryBarChart {
|
|
|
30
30
|
Console_1.default.warning(`plot data not load correctly: ${Utils_1.default.getError(ex).message}`);
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
+
if (plotData.length === 0) {
|
|
34
|
+
if (Config_1.default.verbose) {
|
|
35
|
+
Console_1.default.warning('no memory usage data to plot');
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
33
39
|
// normalize plot data
|
|
34
40
|
const minY = 1;
|
|
35
41
|
const maxY = plotData.reduce((m, v) => Math.max(m, v[1]), 0) * 1.15;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @lightSyntaxTransform
|
|
9
9
|
* @oncall web_perf_infra
|
|
10
10
|
*/
|
|
11
|
-
import type { IHeapEdge } from '../Types';
|
|
11
|
+
import type { AnyRecord, AnyValue, IHeapEdge } from '../Types';
|
|
12
12
|
import type HeapSnapshot from './HeapSnapshot';
|
|
13
13
|
import HeapNode from './HeapNode';
|
|
14
14
|
export default class HeapEdge implements IHeapEdge {
|
|
@@ -23,5 +23,7 @@ export default class HeapEdge implements IHeapEdge {
|
|
|
23
23
|
get to_node(): number;
|
|
24
24
|
get toNode(): HeapNode;
|
|
25
25
|
get fromNode(): HeapNode;
|
|
26
|
+
getJSONifyableObject(): AnyRecord;
|
|
27
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
26
28
|
}
|
|
27
29
|
//# sourceMappingURL=HeapEdge.d.ts.map
|
|
@@ -71,5 +71,18 @@ class HeapEdge {
|
|
|
71
71
|
const srcNodeIdx = edgeIndex2SrcNodeIndex[this.idx];
|
|
72
72
|
return new HeapNode_1.default(heapSnapshot, srcNodeIdx);
|
|
73
73
|
}
|
|
74
|
+
getJSONifyableObject() {
|
|
75
|
+
return {
|
|
76
|
+
name_or_index: this.name_or_index,
|
|
77
|
+
type: this.type,
|
|
78
|
+
edgeIndex: this.edgeIndex,
|
|
79
|
+
toNode: this.toNode.getJSONifyableObject(),
|
|
80
|
+
fromNode: this.fromNode.getJSONifyableObject(),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
toJSONString(...args) {
|
|
84
|
+
const rep = this.getJSONifyableObject();
|
|
85
|
+
return JSON.stringify(rep, ...args);
|
|
86
|
+
}
|
|
74
87
|
}
|
|
75
88
|
exports.default = HeapEdge;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @lightSyntaxTransform
|
|
9
9
|
* @oncall web_perf_infra
|
|
10
10
|
*/
|
|
11
|
-
import type { IHeapLocation, IHeapNode, Nullable } from '../Types';
|
|
11
|
+
import type { AnyRecord, AnyValue, IHeapLocation, IHeapNode, Nullable } from '../Types';
|
|
12
12
|
import type HeapSnapshot from './HeapSnapshot';
|
|
13
13
|
export default class HeapLocation implements IHeapLocation {
|
|
14
14
|
private heapSnapshot;
|
|
@@ -19,5 +19,7 @@ export default class HeapLocation implements IHeapLocation {
|
|
|
19
19
|
get script_id(): number;
|
|
20
20
|
get line(): number;
|
|
21
21
|
get column(): number;
|
|
22
|
+
getJSONifyableObject(): AnyRecord;
|
|
23
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
22
24
|
}
|
|
23
25
|
//# sourceMappingURL=HeapLocation.d.ts.map
|
|
@@ -43,5 +43,19 @@ class HeapLocation {
|
|
|
43
43
|
const locationFieldsCount = heapSnapshot._locationFieldsCount;
|
|
44
44
|
return locations[this.idx * locationFieldsCount + heapSnapshot._locationColumnOffset];
|
|
45
45
|
}
|
|
46
|
+
getJSONifyableObject() {
|
|
47
|
+
const node = this.node;
|
|
48
|
+
const jsonNode = node == null ? null : node.getJSONifyableObject();
|
|
49
|
+
return {
|
|
50
|
+
node: jsonNode,
|
|
51
|
+
script_id: this.script_id,
|
|
52
|
+
line: this.line,
|
|
53
|
+
column: this.column,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
toJSONString(...args) {
|
|
57
|
+
const rep = this.getJSONifyableObject();
|
|
58
|
+
return JSON.stringify(rep, ...args);
|
|
59
|
+
}
|
|
46
60
|
}
|
|
47
61
|
exports.default = HeapLocation;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @lightSyntaxTransform
|
|
9
9
|
* @oncall web_perf_infra
|
|
10
10
|
*/
|
|
11
|
-
import type { IHeapNode, IHeapEdge, Nullable, EdgeIterationCallback, Predicator, IHeapStringNode } from '../Types';
|
|
11
|
+
import type { IHeapNode, IHeapEdge, Nullable, EdgeIterationCallback, Predicator, IHeapStringNode, AnyRecord, AnyValue } from '../Types';
|
|
12
12
|
import type HeapSnapshot from './HeapSnapshot';
|
|
13
13
|
import HeapEdge from './HeapEdge';
|
|
14
14
|
import HeapLocation from './HeapLocation';
|
|
@@ -33,8 +33,11 @@ export default class HeapNode implements IHeapNode {
|
|
|
33
33
|
forEachReference(callback: EdgeIterationCallback): void;
|
|
34
34
|
findAnyReference(predicate: Predicator<IHeapEdge>): Nullable<IHeapEdge>;
|
|
35
35
|
findAnyReferrer(predicate: Predicator<IHeapEdge>): Nullable<IHeapEdge>;
|
|
36
|
+
findAnyReferrerNode(predicate: Predicator<IHeapNode>): Nullable<IHeapNode>;
|
|
36
37
|
findReferrers(predicate: Predicator<IHeapEdge>): IHeapEdge[];
|
|
38
|
+
findReferrerNodes(predicate: Predicator<IHeapNode>): IHeapNode[];
|
|
37
39
|
get referrers(): HeapEdge[];
|
|
40
|
+
get numOfReferrers(): number;
|
|
38
41
|
forEachReferrer(callback: EdgeIterationCallback): void;
|
|
39
42
|
get hasPathEdge(): boolean;
|
|
40
43
|
get pathEdge(): Nullable<HeapEdge>;
|
|
@@ -53,5 +56,7 @@ export default class HeapNode implements IHeapNode {
|
|
|
53
56
|
getReferrerNodes(edgeName: string | number, edgeType?: string): IHeapNode[];
|
|
54
57
|
get isString(): boolean;
|
|
55
58
|
toStringNode(): Nullable<IHeapStringNode>;
|
|
59
|
+
getJSONifyableObject(): AnyRecord;
|
|
60
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
56
61
|
}
|
|
57
62
|
//# sourceMappingURL=HeapNode.d.ts.map
|
|
@@ -151,6 +151,17 @@ class HeapNode {
|
|
|
151
151
|
});
|
|
152
152
|
return found;
|
|
153
153
|
}
|
|
154
|
+
findAnyReferrerNode(predicate) {
|
|
155
|
+
let found = null;
|
|
156
|
+
this.forEachReferrer((edge) => {
|
|
157
|
+
const node = edge.fromNode;
|
|
158
|
+
if (predicate(node)) {
|
|
159
|
+
found = node;
|
|
160
|
+
return { stop: true };
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return found;
|
|
164
|
+
}
|
|
154
165
|
findReferrers(predicate) {
|
|
155
166
|
const ret = [];
|
|
156
167
|
this.forEachReferrer((edge) => {
|
|
@@ -161,6 +172,17 @@ class HeapNode {
|
|
|
161
172
|
});
|
|
162
173
|
return ret;
|
|
163
174
|
}
|
|
175
|
+
findReferrerNodes(predicate) {
|
|
176
|
+
const ret = [];
|
|
177
|
+
this.forEachReferrer((edge) => {
|
|
178
|
+
const node = edge.fromNode;
|
|
179
|
+
if (predicate(node)) {
|
|
180
|
+
ret.push(node);
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
});
|
|
184
|
+
return ret;
|
|
185
|
+
}
|
|
164
186
|
get referrers() {
|
|
165
187
|
const heapSnapshot = this.heapSnapshot;
|
|
166
188
|
const retainingEdgeIndex2EdgeIndex = heapSnapshot._retainingEdgeIndex2EdgeIndex;
|
|
@@ -174,6 +196,13 @@ class HeapNode {
|
|
|
174
196
|
}
|
|
175
197
|
return ret;
|
|
176
198
|
}
|
|
199
|
+
get numOfReferrers() {
|
|
200
|
+
const heapSnapshot = this.heapSnapshot;
|
|
201
|
+
const firstRetainerIndex = heapSnapshot._firstRetainerIndex;
|
|
202
|
+
const beginIdx = firstRetainerIndex[this.idx];
|
|
203
|
+
const endIdx = firstRetainerIndex[this.idx + 1];
|
|
204
|
+
return endIdx - beginIdx;
|
|
205
|
+
}
|
|
177
206
|
forEachReferrer(callback) {
|
|
178
207
|
const heapSnapshot = this.heapSnapshot;
|
|
179
208
|
const retainingEdgeIndex2EdgeIndex = heapSnapshot._retainingEdgeIndex2EdgeIndex;
|
|
@@ -342,6 +371,23 @@ class HeapNode {
|
|
|
342
371
|
? new HeapStringNode_1.default(this.heapSnapshot, this.idx)
|
|
343
372
|
: null;
|
|
344
373
|
}
|
|
374
|
+
getJSONifyableObject() {
|
|
375
|
+
return {
|
|
376
|
+
id: this.id,
|
|
377
|
+
name: this.name,
|
|
378
|
+
type: this.type,
|
|
379
|
+
self_size: this.self_size,
|
|
380
|
+
trace_node_id: this.trace_node_id,
|
|
381
|
+
nodeIndex: this.nodeIndex,
|
|
382
|
+
outGoingEdgeCount: this.edge_count,
|
|
383
|
+
incomingEdgeCount: this.numOfReferrers,
|
|
384
|
+
contructorName: this.constructor.name,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
toJSONString(...args) {
|
|
388
|
+
const rep = this.getJSONifyableObject();
|
|
389
|
+
return JSON.stringify(rep, ...args);
|
|
390
|
+
}
|
|
345
391
|
}
|
|
346
392
|
exports.default = HeapNode;
|
|
347
393
|
// HeapStringNode has to be imported after exporting HeapNode
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @lightSyntaxTransform
|
|
9
9
|
* @oncall web_perf_infra
|
|
10
10
|
*/
|
|
11
|
-
import type { IHeapNode, IHeapNodes, IHeapEdges, IHeapSnapshot, HeapNodeTypes, HeapEdgeTypes, HeapSnapshotMeta, RawHeapSnapshot, NumericDictionary, Nullable } from '../Types';
|
|
11
|
+
import type { AnyRecord, AnyValue, IHeapNode, IHeapNodes, IHeapEdges, IHeapSnapshot, HeapNodeTypes, HeapEdgeTypes, HeapSnapshotMeta, RawHeapSnapshot, NumericDictionary, Nullable } from '../Types';
|
|
12
12
|
import HeapNode from './HeapNode';
|
|
13
13
|
export default class HeapSnapshot implements IHeapSnapshot {
|
|
14
14
|
snapshot: RawHeapSnapshot;
|
|
@@ -67,6 +67,8 @@ export default class HeapSnapshot implements IHeapSnapshot {
|
|
|
67
67
|
_firstRetainerIndex: Uint32Array;
|
|
68
68
|
_edgeIndex2SrcNodeIndex: Uint32Array;
|
|
69
69
|
constructor(snapshot: RawHeapSnapshot, _options?: Record<string, never>);
|
|
70
|
+
getJSONifyableObject(): AnyRecord;
|
|
71
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
70
72
|
hasObjectWithClassName(className: string): boolean;
|
|
71
73
|
getAnyObjectWithClassName(className: string): Nullable<IHeapNode>;
|
|
72
74
|
hasObjectWithPropertyName(nameOrIndex: string | number): boolean;
|
|
@@ -129,6 +129,13 @@ class HeapSnapshot {
|
|
|
129
129
|
},
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
|
+
getJSONifyableObject() {
|
|
133
|
+
return Object.assign({}, this.snapshot.snapshot);
|
|
134
|
+
}
|
|
135
|
+
toJSONString(...args) {
|
|
136
|
+
const rep = this.getJSONifyableObject();
|
|
137
|
+
return JSON.stringify(rep, ...args);
|
|
138
|
+
}
|
|
132
139
|
hasObjectWithClassName(className) {
|
|
133
140
|
let detected = false;
|
|
134
141
|
this.nodes.forEach((node) => {
|
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
* @lightSyntaxTransform
|
|
9
9
|
* @oncall web_perf_infra
|
|
10
10
|
*/
|
|
11
|
-
import type { IHeapStringNode } from '../Types';
|
|
11
|
+
import type { AnyRecord, IHeapStringNode } from '../Types';
|
|
12
12
|
import type HeapSnapshot from './HeapSnapshot';
|
|
13
13
|
import HeapNode from './HeapNode';
|
|
14
14
|
export default class HeapStringNode extends HeapNode implements IHeapStringNode {
|
|
15
15
|
constructor(heapSnapshot: HeapSnapshot, idx: number);
|
|
16
16
|
get stringValue(): string;
|
|
17
|
+
getJSONifyableObject(): AnyRecord;
|
|
17
18
|
}
|
|
18
19
|
//# sourceMappingURL=HeapStringNode.d.ts.map
|
|
@@ -24,6 +24,7 @@ export default class NormalizedTrace {
|
|
|
24
24
|
getTraceSummary(): string;
|
|
25
25
|
static addLeakedNodeToCluster(cluster: TraceCluster, path: LeakTracePathItem): void;
|
|
26
26
|
static calculateClusterRetainedSize(cluster: TraceCluster, snapshot: IHeapSnapshot, aggregateDominatorMetrics: AggregateNodeCb): number;
|
|
27
|
+
static getSamplePathMaxLength(paths: LeakTracePathItem[]): number;
|
|
27
28
|
static samplePaths(paths: LeakTracePathItem[]): LeakTracePathItem[];
|
|
28
29
|
private static diffTraces;
|
|
29
30
|
static diffClusters(newClusters: TraceCluster[], existingClusters: TraceCluster[]): TraceClusterDiff;
|
|
@@ -122,16 +122,30 @@ class NormalizedTrace {
|
|
|
122
122
|
}
|
|
123
123
|
return (cluster.retainedSize = aggregateDominatorMetrics(cluster.leakedNodeIds, snapshot, () => true, (node) => node.retainedSize));
|
|
124
124
|
}
|
|
125
|
+
static getSamplePathMaxLength(paths) {
|
|
126
|
+
const lengthArr = paths.map(p => Utils_1.default.getLeakTracePathLength(p));
|
|
127
|
+
return Math.max(30, Utils_1.default.getNumberAtPercentile(lengthArr, 80));
|
|
128
|
+
}
|
|
125
129
|
static samplePaths(paths) {
|
|
126
130
|
const maxCount = 5000;
|
|
131
|
+
if (paths.length <= maxCount) {
|
|
132
|
+
return [...paths];
|
|
133
|
+
}
|
|
127
134
|
const sampleRatio = Math.min(1, maxCount / paths.length);
|
|
128
135
|
if (sampleRatio < 1) {
|
|
129
136
|
Console_1.default.warning('Sampling trace due to a large number of traces:');
|
|
130
137
|
Console_1.default.lowLevel(` Number of Traces: ${paths.length}`);
|
|
131
|
-
Console_1.default.lowLevel(` Sampling Ratio:
|
|
138
|
+
Console_1.default.lowLevel(` Sampling Ratio: ${Utils_1.default.getReadablePercent(sampleRatio)}`);
|
|
132
139
|
}
|
|
133
140
|
const ret = [];
|
|
141
|
+
const samplePathMaxLength = NormalizedTrace.getSamplePathMaxLength(paths);
|
|
142
|
+
if (Config_1.default.verbose) {
|
|
143
|
+
Console_1.default.lowLevel(` Sample Trace's Max Length: ${samplePathMaxLength}`);
|
|
144
|
+
}
|
|
134
145
|
for (const p of paths) {
|
|
146
|
+
if (Utils_1.default.getLeakTracePathLength(p) > samplePathMaxLength) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
135
149
|
if (Math.random() < sampleRatio) {
|
|
136
150
|
ret.push(p);
|
|
137
151
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @format
|
|
8
8
|
* @oncall web_perf_infra
|
|
9
9
|
*/
|
|
10
|
-
import type { EdgeIterationCallback, IHeapEdge, IHeapLocation, IHeapNode, IHeapSnapshot, IHeapStringNode, Nullable } from '../lib/Types';
|
|
10
|
+
import type { AnyValue, EdgeIterationCallback, IHeapEdge, IHeapLocation, IHeapNode, IHeapSnapshot, IHeapStringNode, Nullable } from '../lib/Types';
|
|
11
11
|
export declare class NodeRecord implements IHeapNode {
|
|
12
12
|
kind: string;
|
|
13
13
|
name: string;
|
|
@@ -32,11 +32,14 @@ export declare class NodeRecord implements IHeapNode {
|
|
|
32
32
|
forEachReference(_callback: EdgeIterationCallback): void;
|
|
33
33
|
set referrers(r: IHeapEdge[]);
|
|
34
34
|
get referrers(): IHeapEdge[];
|
|
35
|
+
get numOfReferrers(): number;
|
|
35
36
|
toStringNode(): IHeapStringNode;
|
|
36
37
|
forEachReferrer(_callback: EdgeIterationCallback): void;
|
|
37
38
|
findAnyReference(): Nullable<IHeapEdge>;
|
|
38
39
|
findAnyReferrer(): Nullable<IHeapEdge>;
|
|
40
|
+
findAnyReferrerNode(): Nullable<IHeapNode>;
|
|
39
41
|
findReferrers(): IHeapEdge[];
|
|
42
|
+
findReferrerNodes(): IHeapNode[];
|
|
40
43
|
set hasPathEdge(f: boolean);
|
|
41
44
|
get hasPathEdge(): boolean;
|
|
42
45
|
set pathEdge(r: IHeapEdge);
|
|
@@ -51,6 +54,7 @@ export declare class NodeRecord implements IHeapNode {
|
|
|
51
54
|
getAnyReferrerNode(_edgeName: string | number, _edgeType?: string): Nullable<IHeapNode>;
|
|
52
55
|
getReferrers(_edgeName: string | number, _edgeType?: string): IHeapEdge[];
|
|
53
56
|
getReferrerNodes(_edgeName: string | number, _edgeType?: string): IHeapNode[];
|
|
57
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
54
58
|
constructor(node: IHeapNode);
|
|
55
59
|
private extraceNodeName;
|
|
56
60
|
}
|
|
@@ -62,6 +66,7 @@ export declare class EdgeRecord implements IHeapEdge {
|
|
|
62
66
|
is_index: boolean;
|
|
63
67
|
to_node: number;
|
|
64
68
|
constructor(edge: IHeapEdge);
|
|
69
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
65
70
|
set snapshot(s: IHeapSnapshot);
|
|
66
71
|
get snapshot(): IHeapSnapshot;
|
|
67
72
|
set toNode(s: IHeapNode);
|
|
@@ -62,6 +62,9 @@ class NodeRecord {
|
|
|
62
62
|
get referrers() {
|
|
63
63
|
throw new Error('NodeRecord.referrers cannot be read');
|
|
64
64
|
}
|
|
65
|
+
get numOfReferrers() {
|
|
66
|
+
throw new Error('NodeRecord.numOfReferrers cannot be read');
|
|
67
|
+
}
|
|
65
68
|
toStringNode() {
|
|
66
69
|
throw new Error('NodeRecord.toStringNode is not implemented');
|
|
67
70
|
}
|
|
@@ -76,9 +79,15 @@ class NodeRecord {
|
|
|
76
79
|
findAnyReferrer() {
|
|
77
80
|
throw new Error('NodeRecord.findAnyReferrer is not implemented');
|
|
78
81
|
}
|
|
82
|
+
findAnyReferrerNode() {
|
|
83
|
+
throw new Error('NodeRecord.findAnyReferrerNode is not implemented');
|
|
84
|
+
}
|
|
79
85
|
findReferrers() {
|
|
80
86
|
throw new Error('NodeRecord.findReferrers is not implemented');
|
|
81
87
|
}
|
|
88
|
+
findReferrerNodes() {
|
|
89
|
+
throw new Error('NodeRecord.findReferrerNodes is not implemented');
|
|
90
|
+
}
|
|
82
91
|
set hasPathEdge(f) {
|
|
83
92
|
throw new Error('NodeRecord.hasPathEdge cannot be assigned');
|
|
84
93
|
}
|
|
@@ -145,6 +154,20 @@ class NodeRecord {
|
|
|
145
154
|
_edgeType) {
|
|
146
155
|
throw new Error('NodeRecord.getReferrerNodes is not implemented');
|
|
147
156
|
}
|
|
157
|
+
toJSONString(...args) {
|
|
158
|
+
const rep = {
|
|
159
|
+
id: this.id,
|
|
160
|
+
kind: this.kind,
|
|
161
|
+
name: this.name,
|
|
162
|
+
type: this.type,
|
|
163
|
+
self_size: this.self_size,
|
|
164
|
+
trace_node_id: this.trace_node_id,
|
|
165
|
+
nodeIndex: this.nodeIndex,
|
|
166
|
+
incomingEdgeCount: this.numOfReferrers,
|
|
167
|
+
contructorName: this.constructor.name,
|
|
168
|
+
};
|
|
169
|
+
return JSON.stringify(rep, ...args);
|
|
170
|
+
}
|
|
148
171
|
extraceNodeName(node) {
|
|
149
172
|
// deserialized node may not have snapshot info
|
|
150
173
|
if (!node.snapshot || !Utils_1.default.isFiberNode(node)) {
|
|
@@ -163,6 +186,16 @@ class EdgeRecord {
|
|
|
163
186
|
this.is_index = edge.is_index;
|
|
164
187
|
this.to_node = edge.to_node;
|
|
165
188
|
}
|
|
189
|
+
toJSONString(...args) {
|
|
190
|
+
const rep = {
|
|
191
|
+
kind: this.kind,
|
|
192
|
+
name_or_index: this.name_or_index,
|
|
193
|
+
type: this.type,
|
|
194
|
+
edgeIndex: this.edgeIndex,
|
|
195
|
+
to_node: this.to_node,
|
|
196
|
+
};
|
|
197
|
+
return JSON.stringify(rep, ...args);
|
|
198
|
+
}
|
|
166
199
|
set snapshot(s) {
|
|
167
200
|
throw new Error('EdgeRecord.snapshot cannot be assigned.');
|
|
168
201
|
}
|
|
@@ -12,6 +12,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const Config_1 = __importDefault(require("../../lib/Config"));
|
|
16
|
+
const Console_1 = __importDefault(require("../../lib/Console"));
|
|
15
17
|
const ClusterUtils_1 = __importDefault(require("../ClusterUtils"));
|
|
16
18
|
// cluster by putting similar traces together
|
|
17
19
|
class TraceSimilarityStrategy {
|
|
@@ -41,6 +43,13 @@ class TraceSimilarityStrategy {
|
|
|
41
43
|
// checking new clusters
|
|
42
44
|
outer: for (let i = 0; i < newTraces.length; ++i) {
|
|
43
45
|
const traceToCheck = newTraces[i];
|
|
46
|
+
// use an odd number as the divider. If we choose 10 as the divider,
|
|
47
|
+
// when updating the progress indicator, the final digit always ends
|
|
48
|
+
// with a zero, which can appear strange and not representative of
|
|
49
|
+
// the actual progress.
|
|
50
|
+
if (!Config_1.default.isContinuousTest && i % 17 === 0) {
|
|
51
|
+
Console_1.default.overwrite(`clustering trace: ${i} / ${newTraces.length}`);
|
|
52
|
+
}
|
|
44
53
|
for (let j = 0; j < clusters.length; ++j) {
|
|
45
54
|
const repTrace = clusters[j][0];
|
|
46
55
|
if (TraceSimilarityStrategy.isSimilarTrace(repTrace, traceToCheck)) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "target",
|
|
4
|
+
"url": "",
|
|
5
|
+
"snapshot": true,
|
|
6
|
+
"screenshot": false,
|
|
7
|
+
"type": "target",
|
|
8
|
+
"idx": 1
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "final",
|
|
12
|
+
"url": "",
|
|
13
|
+
"delay": 10000,
|
|
14
|
+
"snapshot": true,
|
|
15
|
+
"screenshot": false,
|
|
16
|
+
"type": "final",
|
|
17
|
+
"idx": 2
|
|
18
|
+
}
|
|
19
|
+
]
|