@memlab/core 1.1.6 → 1.1.7
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/HeapParser.test.js +2 -2
- package/dist/__tests__/parser/NodeHeap.test.js +5 -5
- package/dist/__tests__/parser/StringNode.test.js +1 -1
- package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/Config.d.ts +3 -0
- package/dist/lib/Config.js +7 -0
- package/dist/lib/FileManager.js +4 -2
- package/dist/lib/HeapAnalyzer.js +19 -8
- package/dist/lib/NodeHeap.d.ts +34 -11
- package/dist/lib/NodeHeap.js +54 -23
- package/dist/lib/Types.d.ts +44 -31
- package/dist/lib/Utils.js +24 -9
- package/dist/lib/heap-data/HeapSnapshot.d.ts +1 -0
- package/dist/lib/heap-data/HeapSnapshot.js +3 -30
- package/dist/lib/heap-data/HeapStringNode.js +2 -0
- package/dist/lib/heap-data/MemLabTagStore.d.ts +23 -0
- package/dist/lib/heap-data/MemLabTagStore.js +110 -0
- package/dist/trace-cluster/strategies/MLTraceSimilarityStrategy.js +1 -1
- package/dist/trace-cluster/strategies/machine-learning/HAC.d.ts +1 -1
- package/dist/trace-cluster/strategies/machine-learning/HAC.js +4 -6
- package/dist/trace-cluster/strategies/machine-learning/TfidfVectorizer.js +6 -2
- package/package.json +1 -1
|
@@ -36,7 +36,7 @@ test('Capture inserted object', () => __awaiter(void 0, void 0, void 0, function
|
|
|
36
36
|
}
|
|
37
37
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
38
38
|
const injected = new TestObject();
|
|
39
|
-
const heap = yield (0, NodeHeap_1.
|
|
39
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
40
40
|
expect(heap.hasObjectWithClassName('TestObject')).toBe(true);
|
|
41
41
|
}), timeout);
|
|
42
42
|
test('Does not capture transcient object', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -49,6 +49,6 @@ test('Does not capture transcient object', () => __awaiter(void 0, void 0, void
|
|
|
49
49
|
let injected = new TestObject();
|
|
50
50
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
51
51
|
injected = null;
|
|
52
|
-
const heap = yield (0, NodeHeap_1.
|
|
52
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
53
53
|
expect(heap.hasObjectWithClassName('TestObject')).toBe(false);
|
|
54
54
|
}), timeout);
|
|
@@ -30,7 +30,7 @@ const timeout = 5 * 60 * 1000;
|
|
|
30
30
|
test('Capture current node heap snapshot', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
31
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
32
32
|
const object = { 'memlab-test-heap-property': 'memlab-test-heap-value' };
|
|
33
|
-
const heap = yield (0, NodeHeap_1.
|
|
33
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
34
34
|
expect(heap.hasObjectWithPropertyName('memlab-test-heap-property')).toBe(true);
|
|
35
35
|
}), timeout);
|
|
36
36
|
test('Nullified Object should not exist in heap', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -39,7 +39,7 @@ test('Nullified Object should not exist in heap', () => __awaiter(void 0, void 0
|
|
|
39
39
|
};
|
|
40
40
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
41
41
|
object = null;
|
|
42
|
-
const heap = yield (0, NodeHeap_1.
|
|
42
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
43
43
|
expect(heap.hasObjectWithPropertyName('memlab-test-heap-property')).toBe(false);
|
|
44
44
|
}), timeout);
|
|
45
45
|
test('Strongly referenced object should exist in heap', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -59,7 +59,7 @@ test('Strongly referenced object should exist in heap', () => __awaiter(void 0,
|
|
|
59
59
|
}
|
|
60
60
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
61
61
|
const object = buildTest();
|
|
62
|
-
const heap = yield (0, NodeHeap_1.
|
|
62
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
63
63
|
expect(heap.hasObjectWithClassName('TestClass1')).toBe(true);
|
|
64
64
|
expect(heap.hasObjectWithClassName('TestClass2')).toBe(true);
|
|
65
65
|
}), timeout);
|
|
@@ -80,7 +80,7 @@ test('Weakly referenced object should not exist in heap', () => __awaiter(void 0
|
|
|
80
80
|
}
|
|
81
81
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
82
82
|
const object = buildTest();
|
|
83
|
-
const heap = yield (0, NodeHeap_1.
|
|
83
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
84
84
|
expect(heap.hasObjectWithClassName('TestClass3')).toBe(true);
|
|
85
85
|
expect(heap.hasObjectWithClassName('TestClass4')).toBe(false);
|
|
86
86
|
}), timeout);
|
|
@@ -90,7 +90,7 @@ test('Check annotated objects', () => __awaiter(void 0, void 0, void 0, function
|
|
|
90
90
|
(0, NodeHeap_1.tagObject)(o1, 'memlab-mark-1');
|
|
91
91
|
(0, NodeHeap_1.tagObject)(o2, 'memlab-mark-2');
|
|
92
92
|
o2 = null;
|
|
93
|
-
const heap = yield (0, NodeHeap_1.
|
|
93
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
94
94
|
expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
|
|
95
95
|
expect(heap.hasObjectWithTag('memlab-mark-2')).toBe(false);
|
|
96
96
|
}), timeout);
|
|
@@ -41,7 +41,7 @@ test('String heap object APIs work', () => __awaiter(void 0, void 0, void 0, fun
|
|
|
41
41
|
injected.complexConcatString += 'value_';
|
|
42
42
|
injected.complexConcatString += 123;
|
|
43
43
|
injected.complexConcatString += '_suffix';
|
|
44
|
-
const heap = yield (0, NodeHeap_1.
|
|
44
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
45
45
|
const testObject = heap.getAnyObjectWithClassName('TestObject');
|
|
46
46
|
expect(testObject).not.toBe(null);
|
|
47
47
|
// testObject.originalString === 'test'
|
|
@@ -74,7 +74,7 @@ test('Check getReference and getReferenceNode', () => __awaiter(void 0, void 0,
|
|
|
74
74
|
});
|
|
75
75
|
return detected;
|
|
76
76
|
};
|
|
77
|
-
const heap = yield (0, NodeHeap_1.
|
|
77
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
78
78
|
expect(checker(heap)).toBe(true);
|
|
79
79
|
}), timeout);
|
|
80
80
|
test('Check getReferrers and getReferrerNodes', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -135,6 +135,6 @@ test('Check getReferrers and getReferrerNodes', () => __awaiter(void 0, void 0,
|
|
|
135
135
|
}
|
|
136
136
|
return true;
|
|
137
137
|
};
|
|
138
|
-
const heap = yield (0, NodeHeap_1.
|
|
138
|
+
const heap = yield (0, NodeHeap_1.takeNodeMinimalHeap)();
|
|
139
139
|
expect(checker(heap)).toBe(true);
|
|
140
140
|
}), timeout);
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
/** @internal */
|
|
11
11
|
export declare function registerPackage(): Promise<void>;
|
|
12
12
|
export * from './lib/Types';
|
|
13
|
+
export * from './lib/NodeHeap';
|
|
13
14
|
/** @internal */
|
|
14
15
|
export { default as config } from './lib/Config';
|
|
15
16
|
/** @internal */
|
|
@@ -46,5 +47,4 @@ export { default as NormalizedTrace } from './trace-cluster/TraceBucket';
|
|
|
46
47
|
export { default as EvaluationMetric } from './trace-cluster/EvalutationMetric';
|
|
47
48
|
/** @internal */
|
|
48
49
|
export * from './lib/PackageInfoLoader';
|
|
49
|
-
export * from './lib/NodeHeap';
|
|
50
50
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,7 @@ function registerPackage() {
|
|
|
46
46
|
}
|
|
47
47
|
exports.registerPackage = registerPackage;
|
|
48
48
|
__exportStar(require("./lib/Types"), exports);
|
|
49
|
+
__exportStar(require("./lib/NodeHeap"), exports);
|
|
49
50
|
/** @internal */
|
|
50
51
|
var Config_1 = require("./lib/Config");
|
|
51
52
|
Object.defineProperty(exports, "config", { enumerable: true, get: function () { return __importDefault(Config_1).default; } });
|
|
@@ -96,4 +97,3 @@ var EvalutationMetric_1 = require("./trace-cluster/EvalutationMetric");
|
|
|
96
97
|
Object.defineProperty(exports, "EvaluationMetric", { enumerable: true, get: function () { return __importDefault(EvalutationMetric_1).default; } });
|
|
97
98
|
/** @internal */
|
|
98
99
|
__exportStar(require("./lib/PackageInfoLoader"), exports);
|
|
99
|
-
__exportStar(require("./lib/NodeHeap"), exports);
|
package/dist/lib/Config.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { LaunchOptions, Permission } from 'puppeteer';
|
|
11
11
|
import type { AnyFunction, AnyValue, IClusterStrategy, IRunningMode, IScenario, Nullable, Optional, QuickExperiment, ILeakFilter, IPackageInfo } from './Types';
|
|
12
|
+
import { IHeapConfig } from '..';
|
|
12
13
|
interface BrowserLaunchArgumentOptions {
|
|
13
14
|
headless?: boolean;
|
|
14
15
|
userDataDir?: string;
|
|
@@ -95,6 +96,7 @@ export declare class MemLabConfig {
|
|
|
95
96
|
dataBuilderDataDir: string;
|
|
96
97
|
unclassifiedClusterDir: string;
|
|
97
98
|
externalCookiesFile: Optional<string>;
|
|
99
|
+
heapConfig: Optional<IHeapConfig>;
|
|
98
100
|
puppeteerConfig: LaunchOptions & BrowserLaunchArgumentOptions & BrowserConnectOptions;
|
|
99
101
|
openDevtoolsConsole: boolean;
|
|
100
102
|
emulateDevice: Nullable<Device>;
|
|
@@ -182,6 +184,7 @@ export declare class MemLabConfig {
|
|
|
182
184
|
packageInfo: IPackageInfo[];
|
|
183
185
|
isMLClustering: boolean;
|
|
184
186
|
mlClusteringLinkageMaxDistance: number;
|
|
187
|
+
mlMaxDF: number;
|
|
185
188
|
constructor(options?: ConfigOption);
|
|
186
189
|
private initInternalConfigs;
|
|
187
190
|
private init;
|
package/dist/lib/Config.js
CHANGED
|
@@ -65,6 +65,8 @@ class MemLabConfig {
|
|
|
65
65
|
// by default we want to use Xvfb if the Env supports it
|
|
66
66
|
this.useXVFB = true;
|
|
67
67
|
this.specifiedEngine = false;
|
|
68
|
+
// set by the heap-analysis package if HeapConfig is used
|
|
69
|
+
this.heapConfig = null;
|
|
68
70
|
// set puppeteer configuration
|
|
69
71
|
this.puppeteerConfig = {
|
|
70
72
|
headless: !this._isHeadfulBrowser,
|
|
@@ -126,7 +128,12 @@ class MemLabConfig {
|
|
|
126
128
|
this.includeObjectInfoInTraceReturnChain = false;
|
|
127
129
|
// by default halt the program when utils.haltOrThrow is calleds
|
|
128
130
|
this.errorHandling = ErrorHandling.Halt;
|
|
131
|
+
// by default use clustering based on heurastics
|
|
132
|
+
this.isMLClustering = false;
|
|
133
|
+
// linkage max distance for caclulating distances among clusters
|
|
129
134
|
this.mlClusteringLinkageMaxDistance = 0.7;
|
|
135
|
+
// TF/IDF maximum document frequency
|
|
136
|
+
this.mlMaxDF = 1;
|
|
130
137
|
}
|
|
131
138
|
// initialize configurable parameters
|
|
132
139
|
init(options = {}) {
|
package/dist/lib/FileManager.js
CHANGED
|
@@ -41,8 +41,10 @@ class FileManager {
|
|
|
41
41
|
return path_1.default.join(this.getTmpDir(), 'memlab');
|
|
42
42
|
}
|
|
43
43
|
generateTmpHeapDir() {
|
|
44
|
-
const dirPath = path_1.default.join(this.getTmpDir(), Utils_1.default.getUniqueID());
|
|
45
|
-
fs_extra_1.default.
|
|
44
|
+
const dirPath = path_1.default.join(this.getTmpDir(), 'memlab-' + Utils_1.default.getUniqueID());
|
|
45
|
+
if (!fs_extra_1.default.existsSync(dirPath)) {
|
|
46
|
+
fs_extra_1.default.mkdirSync(dirPath);
|
|
47
|
+
}
|
|
46
48
|
return dirPath;
|
|
47
49
|
}
|
|
48
50
|
getWorkDir(options = {}) {
|
package/dist/lib/HeapAnalyzer.js
CHANGED
|
@@ -250,9 +250,17 @@ class MemoryAnalyst {
|
|
|
250
250
|
let snapshotLeakedHeapNodeIdSet = new Set();
|
|
251
251
|
let nodeIdsInSnapshots = [];
|
|
252
252
|
let snapshot;
|
|
253
|
+
// if specified a heap file
|
|
253
254
|
if (options.file) {
|
|
254
255
|
const opt = { buildNodeIdIndex: true, verbose: true };
|
|
255
256
|
snapshot = yield Utils_1.default.getSnapshotFromFile(options.file, opt);
|
|
257
|
+
// if running in interactive heap analysis mode
|
|
258
|
+
}
|
|
259
|
+
else if (Config_1.default.heapConfig &&
|
|
260
|
+
Config_1.default.heapConfig.isCliInteractiveMode &&
|
|
261
|
+
Config_1.default.heapConfig.currentHeap) {
|
|
262
|
+
snapshot = Config_1.default.heapConfig.currentHeap;
|
|
263
|
+
// otherwise diff heap snapshots
|
|
256
264
|
}
|
|
257
265
|
else {
|
|
258
266
|
Utils_1.default.checkSnapshots();
|
|
@@ -368,14 +376,17 @@ class MemoryAnalyst {
|
|
|
368
376
|
// initialize the path finder
|
|
369
377
|
preparePathFinder(snapshot) {
|
|
370
378
|
const finder = new TraceFinder_1.default();
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
+
if (!snapshot.isProcessed) {
|
|
380
|
+
// shortest path for all nodes
|
|
381
|
+
finder.annotateShortestPaths(snapshot);
|
|
382
|
+
// dominator and retained size
|
|
383
|
+
finder.calculateAllNodesRetainedSizes(snapshot);
|
|
384
|
+
// mark detached Fiber nodes
|
|
385
|
+
Utils_1.default.markAllDetachedFiberNode(snapshot);
|
|
386
|
+
// mark alternate Fiber nodes
|
|
387
|
+
Utils_1.default.markAlternateFiberNode(snapshot);
|
|
388
|
+
snapshot.isProcessed = true;
|
|
389
|
+
}
|
|
379
390
|
return finder;
|
|
380
391
|
}
|
|
381
392
|
// summarize the page interaction and dump to the leak text summary file
|
package/dist/lib/NodeHeap.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ import type { IHeapSnapshot } from './Types';
|
|
|
22
22
|
* * **Examples**:
|
|
23
23
|
* ```typescript
|
|
24
24
|
* import type {IHeapSnapshot, AnyValue} from '@memlab/core';
|
|
25
|
-
* import {config,
|
|
25
|
+
* import {config, takeNodeMinimalHeap, tagObject} from '@memlab/core';
|
|
26
26
|
*
|
|
27
27
|
* test('memory test', async () => {
|
|
28
28
|
* config.muteConsole = true;
|
|
@@ -36,7 +36,7 @@ import type { IHeapSnapshot } from './Types';
|
|
|
36
36
|
*
|
|
37
37
|
* o2 = null;
|
|
38
38
|
*
|
|
39
|
-
* const heap: IHeapSnapshot = await
|
|
39
|
+
* const heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
40
40
|
*
|
|
41
41
|
* // expect object with marker "memlab-mark-1" exists
|
|
42
42
|
* expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
|
|
@@ -60,11 +60,11 @@ export declare function tagObject<T extends object>(o: T, tag: string): T;
|
|
|
60
60
|
* ```typescript
|
|
61
61
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
62
62
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
63
|
-
* import {
|
|
63
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
64
64
|
*
|
|
65
65
|
* (async function () {
|
|
66
66
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
67
|
-
* const heap: IHeapSnapshot = await
|
|
67
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
68
68
|
* })();
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
@@ -77,19 +77,42 @@ export declare function dumpNodeHeapSnapshot(): string;
|
|
|
77
77
|
*
|
|
78
78
|
* @returns heap representation without heap analysis meta data.
|
|
79
79
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
80
|
+
* @deprecated
|
|
81
|
+
* @internal
|
|
82
|
+
*
|
|
83
|
+
* If you need to get the heap snapshot with heap analysis meta data, please
|
|
84
|
+
* use {@link takeNodeFullHeap}.
|
|
85
|
+
* For example:
|
|
83
86
|
* ```typescript
|
|
84
87
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
85
|
-
* import {
|
|
86
|
-
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
88
|
+
* import {takeNodeFullHeap} from '@memlab/heap-analysis';
|
|
87
89
|
*
|
|
88
90
|
* (async function () {
|
|
89
|
-
* const
|
|
90
|
-
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
91
|
+
* const heap: IHeapSnapshot = await takeNodeFullHeap();
|
|
91
92
|
* })();
|
|
92
93
|
* ```
|
|
93
94
|
*/
|
|
94
95
|
export declare function getNodeInnocentHeap(): Promise<IHeapSnapshot>;
|
|
96
|
+
/**
|
|
97
|
+
* Take a heap snapshot of the current program state
|
|
98
|
+
* and parse it as {@link IHeapSnapshot}. Notice that
|
|
99
|
+
* this API does not calculate some heap analysis meta data
|
|
100
|
+
* for heap analysis. But this also means faster heap parsing.
|
|
101
|
+
*
|
|
102
|
+
* @returns heap representation without heap analysis meta data.
|
|
103
|
+
*
|
|
104
|
+
* * **Examples:**
|
|
105
|
+
* ```typescript
|
|
106
|
+
* import type {IHeapSnapshot} from '@memlab/core';
|
|
107
|
+
* import {takeNodeMinimalHeap} from '@memlab/core';
|
|
108
|
+
*
|
|
109
|
+
* (async function () {
|
|
110
|
+
* const heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
111
|
+
* })();
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* If you need to get the heap snapshot with heap analysis meta data, please
|
|
115
|
+
* use {@link getFullHeapFromFile}.
|
|
116
|
+
*/
|
|
117
|
+
export declare function takeNodeMinimalHeap(): Promise<IHeapSnapshot>;
|
|
95
118
|
//# sourceMappingURL=NodeHeap.d.ts.map
|
package/dist/lib/NodeHeap.js
CHANGED
|
@@ -21,18 +21,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
22
|
};
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.getNodeInnocentHeap = exports.dumpNodeHeapSnapshot = exports.tagObject = void 0;
|
|
24
|
+
exports.takeNodeMinimalHeap = exports.getNodeInnocentHeap = exports.dumpNodeHeapSnapshot = exports.tagObject = void 0;
|
|
25
25
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
26
26
|
const path_1 = __importDefault(require("path"));
|
|
27
27
|
const v8_1 = __importDefault(require("v8"));
|
|
28
28
|
const FileManager_1 = __importDefault(require("./FileManager"));
|
|
29
29
|
const Utils_1 = __importDefault(require("./Utils"));
|
|
30
|
-
|
|
31
|
-
constructor() {
|
|
32
|
-
this.taggedObjects = Object.create(null);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
const store = new MemLabTaggedStore();
|
|
30
|
+
const MemLabTagStore_1 = __importDefault(require("./heap-data/MemLabTagStore"));
|
|
36
31
|
/**
|
|
37
32
|
* Tags a string marker to an object instance, which can later be checked by
|
|
38
33
|
* {@link hasObjectWithTag}. This API does not modify the object instance in
|
|
@@ -47,7 +42,7 @@ const store = new MemLabTaggedStore();
|
|
|
47
42
|
* * **Examples**:
|
|
48
43
|
* ```typescript
|
|
49
44
|
* import type {IHeapSnapshot, AnyValue} from '@memlab/core';
|
|
50
|
-
* import {config,
|
|
45
|
+
* import {config, takeNodeMinimalHeap, tagObject} from '@memlab/core';
|
|
51
46
|
*
|
|
52
47
|
* test('memory test', async () => {
|
|
53
48
|
* config.muteConsole = true;
|
|
@@ -61,7 +56,7 @@ const store = new MemLabTaggedStore();
|
|
|
61
56
|
*
|
|
62
57
|
* o2 = null;
|
|
63
58
|
*
|
|
64
|
-
* const heap: IHeapSnapshot = await
|
|
59
|
+
* const heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
65
60
|
*
|
|
66
61
|
* // expect object with marker "memlab-mark-1" exists
|
|
67
62
|
* expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
|
|
@@ -73,10 +68,7 @@ const store = new MemLabTaggedStore();
|
|
|
73
68
|
* ```
|
|
74
69
|
*/
|
|
75
70
|
function tagObject(o, tag) {
|
|
76
|
-
|
|
77
|
-
store.taggedObjects[tag] = new WeakSet();
|
|
78
|
-
}
|
|
79
|
-
store.taggedObjects[tag].add(o);
|
|
71
|
+
MemLabTagStore_1.default.tagObject(o, tag);
|
|
80
72
|
return o;
|
|
81
73
|
}
|
|
82
74
|
exports.tagObject = tagObject;
|
|
@@ -92,16 +84,20 @@ exports.tagObject = tagObject;
|
|
|
92
84
|
* ```typescript
|
|
93
85
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
94
86
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
95
|
-
* import {
|
|
87
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
96
88
|
*
|
|
97
89
|
* (async function () {
|
|
98
90
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
99
|
-
* const heap: IHeapSnapshot = await
|
|
91
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
100
92
|
* })();
|
|
101
93
|
* ```
|
|
102
94
|
*/
|
|
103
95
|
function dumpNodeHeapSnapshot() {
|
|
104
|
-
const
|
|
96
|
+
const randomID = `Math.random()`.replace('0.', '');
|
|
97
|
+
const file = path_1.default.join(FileManager_1.default.generateTmpHeapDir(), `nodejs-${randomID}.heapsnapshot`);
|
|
98
|
+
if (fs_extra_1.default.existsSync(file)) {
|
|
99
|
+
fs_extra_1.default.removeSync(file);
|
|
100
|
+
}
|
|
105
101
|
v8_1.default.writeHeapSnapshot(file);
|
|
106
102
|
return file;
|
|
107
103
|
}
|
|
@@ -114,17 +110,18 @@ exports.dumpNodeHeapSnapshot = dumpNodeHeapSnapshot;
|
|
|
114
110
|
*
|
|
115
111
|
* @returns heap representation without heap analysis meta data.
|
|
116
112
|
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
113
|
+
* @deprecated
|
|
114
|
+
* @internal
|
|
115
|
+
*
|
|
116
|
+
* If you need to get the heap snapshot with heap analysis meta data, please
|
|
117
|
+
* use {@link takeNodeFullHeap}.
|
|
118
|
+
* For example:
|
|
120
119
|
* ```typescript
|
|
121
120
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
122
|
-
* import {
|
|
123
|
-
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
121
|
+
* import {takeNodeFullHeap} from '@memlab/heap-analysis';
|
|
124
122
|
*
|
|
125
123
|
* (async function () {
|
|
126
|
-
* const
|
|
127
|
-
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
124
|
+
* const heap: IHeapSnapshot = await takeNodeFullHeap();
|
|
128
125
|
* })();
|
|
129
126
|
* ```
|
|
130
127
|
*/
|
|
@@ -141,3 +138,37 @@ function getNodeInnocentHeap() {
|
|
|
141
138
|
});
|
|
142
139
|
}
|
|
143
140
|
exports.getNodeInnocentHeap = getNodeInnocentHeap;
|
|
141
|
+
/**
|
|
142
|
+
* Take a heap snapshot of the current program state
|
|
143
|
+
* and parse it as {@link IHeapSnapshot}. Notice that
|
|
144
|
+
* this API does not calculate some heap analysis meta data
|
|
145
|
+
* for heap analysis. But this also means faster heap parsing.
|
|
146
|
+
*
|
|
147
|
+
* @returns heap representation without heap analysis meta data.
|
|
148
|
+
*
|
|
149
|
+
* * **Examples:**
|
|
150
|
+
* ```typescript
|
|
151
|
+
* import type {IHeapSnapshot} from '@memlab/core';
|
|
152
|
+
* import {takeNodeMinimalHeap} from '@memlab/core';
|
|
153
|
+
*
|
|
154
|
+
* (async function () {
|
|
155
|
+
* const heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
156
|
+
* })();
|
|
157
|
+
* ```
|
|
158
|
+
*
|
|
159
|
+
* If you need to get the heap snapshot with heap analysis meta data, please
|
|
160
|
+
* use {@link getFullHeapFromFile}.
|
|
161
|
+
*/
|
|
162
|
+
function takeNodeMinimalHeap() {
|
|
163
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
+
const file = dumpNodeHeapSnapshot();
|
|
165
|
+
const snapshot = yield Utils_1.default.getSnapshotFromFile(file, {
|
|
166
|
+
buildNodeIdIndex: true,
|
|
167
|
+
});
|
|
168
|
+
fs_extra_1.default.unlink(file, () => {
|
|
169
|
+
// do nothing
|
|
170
|
+
});
|
|
171
|
+
return snapshot;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
exports.takeNodeMinimalHeap = takeNodeMinimalHeap;
|
package/dist/lib/Types.d.ts
CHANGED
|
@@ -807,6 +807,13 @@ export declare type RunMetaInfo = {
|
|
|
807
807
|
* {@link IHeapNode} and {@link IHeapEdge}.
|
|
808
808
|
*/
|
|
809
809
|
export interface IHeapSnapshot {
|
|
810
|
+
/**
|
|
811
|
+
* flag indicating if the heap snapshot has included
|
|
812
|
+
* the post-processing meta data (e.g., shortest path to GC root,
|
|
813
|
+
* dominator info and retainer size etc.)
|
|
814
|
+
* @internal
|
|
815
|
+
*/
|
|
816
|
+
isProcessed: boolean;
|
|
810
817
|
/** @internal */
|
|
811
818
|
snapshot: RawHeapSnapshot;
|
|
812
819
|
/**
|
|
@@ -819,11 +826,11 @@ export interface IHeapSnapshot {
|
|
|
819
826
|
* ```typescript
|
|
820
827
|
* import type {IHeapSnapshot, IHeapNode} from '@memlab/core';
|
|
821
828
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
822
|
-
* import {
|
|
829
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
823
830
|
*
|
|
824
831
|
* (async function () {
|
|
825
832
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
826
|
-
* const heap: IHeapSnapshot = await
|
|
833
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
827
834
|
*
|
|
828
835
|
* // get the total number of heap objects
|
|
829
836
|
* heap.nodes.length;
|
|
@@ -845,11 +852,11 @@ export interface IHeapSnapshot {
|
|
|
845
852
|
* ```typescript
|
|
846
853
|
* import type {IHeapSnapshot, IHeapEdge} from '@memlab/core';
|
|
847
854
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
848
|
-
* import {
|
|
855
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
849
856
|
*
|
|
850
857
|
* (async function () {
|
|
851
858
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
852
|
-
* const heap: IHeapSnapshot = await
|
|
859
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
853
860
|
*
|
|
854
861
|
* // get the total number of heap references
|
|
855
862
|
* heap.edges.length;
|
|
@@ -871,11 +878,11 @@ export interface IHeapSnapshot {
|
|
|
871
878
|
* ```typescript
|
|
872
879
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
873
880
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
874
|
-
* import {
|
|
881
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
875
882
|
*
|
|
876
883
|
* (async function () {
|
|
877
884
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
878
|
-
* const heap: IHeapSnapshot = await
|
|
885
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
879
886
|
*
|
|
880
887
|
* const node = heap.getNodeById(351);
|
|
881
888
|
* node?.id; // should be 351
|
|
@@ -893,7 +900,7 @@ export interface IHeapSnapshot {
|
|
|
893
900
|
* ```typescript
|
|
894
901
|
* // save as example.test.ts
|
|
895
902
|
* import type {IHeapSnapshot, Nullable} from '@memlab/core';
|
|
896
|
-
* import {config,
|
|
903
|
+
* import {config, takeNodeMinimalHeap} from '@memlab/core';
|
|
897
904
|
*
|
|
898
905
|
* class TestObject {
|
|
899
906
|
* public arr1 = [1, 2, 3];
|
|
@@ -905,7 +912,7 @@ export interface IHeapSnapshot {
|
|
|
905
912
|
*
|
|
906
913
|
* let obj: Nullable<TestObject> = new TestObject();
|
|
907
914
|
* // get a heap snapshot of the current program state
|
|
908
|
-
* let heap: IHeapSnapshot = await
|
|
915
|
+
* let heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
909
916
|
*
|
|
910
917
|
* // call some function that may add references to obj
|
|
911
918
|
* rabbitHole(obj)
|
|
@@ -913,7 +920,7 @@ export interface IHeapSnapshot {
|
|
|
913
920
|
* expect(heap.hasObjectWithClassName('TestObject')).toBe(true);
|
|
914
921
|
* obj = null;
|
|
915
922
|
*
|
|
916
|
-
* heap = await
|
|
923
|
+
* heap = await takeNodeMinimalHeap();
|
|
917
924
|
* // if rabbitHole does not have any side effect that
|
|
918
925
|
* // adds new references to obj, then obj can be GCed
|
|
919
926
|
* expect(heap.hasObjectWithClassName('TestObject')).toBe(false);
|
|
@@ -932,7 +939,7 @@ export interface IHeapSnapshot {
|
|
|
932
939
|
* * **Examples**:
|
|
933
940
|
* ```typescript
|
|
934
941
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
935
|
-
* import {
|
|
942
|
+
* import {takeNodeMinimalHeap} from '@memlab/core';
|
|
936
943
|
*
|
|
937
944
|
* class TestObject {
|
|
938
945
|
* public arr1 = [1, 2, 3];
|
|
@@ -942,7 +949,7 @@ export interface IHeapSnapshot {
|
|
|
942
949
|
* (async function () {
|
|
943
950
|
* const obj = new TestObject();
|
|
944
951
|
* // get a heap snapshot of the current program state
|
|
945
|
-
* const heap: IHeapSnapshot = await
|
|
952
|
+
* const heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
946
953
|
*
|
|
947
954
|
* const node = heap.getAnyObjectWithClassName('TestObject');
|
|
948
955
|
* console.log(node?.name); // should be 'TestObject'
|
|
@@ -961,14 +968,14 @@ export interface IHeapSnapshot {
|
|
|
961
968
|
* ```typescript
|
|
962
969
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
963
970
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
964
|
-
* import {
|
|
971
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
965
972
|
*
|
|
966
973
|
* (async function () {
|
|
967
974
|
* // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
968
975
|
* const object = {'memlab-test-heap-property': 'memlab-test-heap-value'};
|
|
969
976
|
*
|
|
970
977
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
971
|
-
* const heap: IHeapSnapshot = await
|
|
978
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
972
979
|
*
|
|
973
980
|
* // should be true
|
|
974
981
|
* console.log(heap.hasObjectWithPropertyName('memlab-test-heap-property'));
|
|
@@ -988,7 +995,7 @@ export interface IHeapSnapshot {
|
|
|
988
995
|
*
|
|
989
996
|
* ```typescript
|
|
990
997
|
* import type {IHeapSnapshot, AnyValue} from '@memlab/core';
|
|
991
|
-
* import {config,
|
|
998
|
+
* import {config, takeNodeMinimalHeap, tagObject} from '@memlab/core';
|
|
992
999
|
*
|
|
993
1000
|
* test('memory test', async () => {
|
|
994
1001
|
* config.muteConsole = true;
|
|
@@ -1002,7 +1009,7 @@ export interface IHeapSnapshot {
|
|
|
1002
1009
|
*
|
|
1003
1010
|
* o2 = null;
|
|
1004
1011
|
*
|
|
1005
|
-
* const heap: IHeapSnapshot = await
|
|
1012
|
+
* const heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
1006
1013
|
*
|
|
1007
1014
|
* // expect object with marker "memlab-mark-1" exists
|
|
1008
1015
|
* expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
|
|
@@ -1026,16 +1033,16 @@ export interface IHeapSnapshot {
|
|
|
1026
1033
|
* @readonly it is not recommended to modify any `IHeapLocation` instance
|
|
1027
1034
|
*
|
|
1028
1035
|
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1029
|
-
* {@link
|
|
1036
|
+
* {@link getFullHeapFromFile} API.
|
|
1030
1037
|
*
|
|
1031
1038
|
* ```typescript
|
|
1032
1039
|
* import type {IHeapSnapshot, IHeapNode, IHeapLocation} from '@memlab/core';
|
|
1033
1040
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1034
|
-
* import {
|
|
1041
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1035
1042
|
*
|
|
1036
1043
|
* (async function () {
|
|
1037
1044
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1038
|
-
* const heap: IHeapSnapshot = await
|
|
1045
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1039
1046
|
*
|
|
1040
1047
|
* // iterate over each node (heap object)
|
|
1041
1048
|
* heap.nodes.forEach((node: IHeapNode, i: number) => {
|
|
@@ -1088,16 +1095,16 @@ export interface IHeapEdgeBasic {
|
|
|
1088
1095
|
* @readonly it is not recommended to modify any `IHeapEdge` instance
|
|
1089
1096
|
*
|
|
1090
1097
|
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1091
|
-
* {@link
|
|
1098
|
+
* {@link getFullHeapFromFile} API.
|
|
1092
1099
|
*
|
|
1093
1100
|
* ```typescript
|
|
1094
1101
|
* import type {IHeapSnapshot, IHeapEdge} from '@memlab/core';
|
|
1095
1102
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1096
|
-
* import {
|
|
1103
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1097
1104
|
*
|
|
1098
1105
|
* (async function () {
|
|
1099
1106
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1100
|
-
* const heap: IHeapSnapshot = await
|
|
1107
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1101
1108
|
*
|
|
1102
1109
|
* // iterate over each edge (JS reference in heap)
|
|
1103
1110
|
* heap.edges.forEach((edge: IHeapEdge, i: number) => {
|
|
@@ -1151,11 +1158,11 @@ export interface IHeapEdge extends IHeapEdgeBasic {
|
|
|
1151
1158
|
* ```typescript
|
|
1152
1159
|
* import type {IHeapSnapshot, IHeapEdges} from '@memlab/core';
|
|
1153
1160
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1154
|
-
* import {
|
|
1161
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1155
1162
|
*
|
|
1156
1163
|
* (async function () {
|
|
1157
1164
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1158
|
-
* const heap: IHeapSnapshot = await
|
|
1165
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1159
1166
|
*
|
|
1160
1167
|
* const edges: IHeapEdges = heap.edges;
|
|
1161
1168
|
* edges.length;
|
|
@@ -1230,16 +1237,16 @@ export declare type EdgeIterationCallback = (edge: IHeapEdge) => Optional<{
|
|
|
1230
1237
|
* @readonly it is not recommended to modify any `IHeapNode` instance
|
|
1231
1238
|
*
|
|
1232
1239
|
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1233
|
-
* {@link
|
|
1240
|
+
* {@link getFullHeapFromFile} API.
|
|
1234
1241
|
*
|
|
1235
1242
|
* ```typescript
|
|
1236
1243
|
* import type {IHeapSnapshot, IHeapNode} from '@memlab/core';
|
|
1237
1244
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1238
|
-
* import {
|
|
1245
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1239
1246
|
*
|
|
1240
1247
|
* (async function () {
|
|
1241
1248
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1242
|
-
* const heap: IHeapSnapshot = await
|
|
1249
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1243
1250
|
*
|
|
1244
1251
|
* // iterate over each node (heap object)
|
|
1245
1252
|
* heap.nodes.forEach((node: IHeapNode, i: number) => {
|
|
@@ -1538,16 +1545,16 @@ export interface IHeapNode extends IHeapNodeBasic {
|
|
|
1538
1545
|
* @readonly it is not recommended to modify any `IHeapStringNode` instance
|
|
1539
1546
|
*
|
|
1540
1547
|
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1541
|
-
* {@link
|
|
1548
|
+
* {@link getFullHeapFromFile} API.
|
|
1542
1549
|
*
|
|
1543
1550
|
* ```typescript
|
|
1544
1551
|
* import type {IHeapSnapshot, IHeapNode, IHeapStringNode} from '@memlab/core';
|
|
1545
1552
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1546
|
-
* import {
|
|
1553
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1547
1554
|
*
|
|
1548
1555
|
* (async function () {
|
|
1549
1556
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1550
|
-
* const heap: IHeapSnapshot = await
|
|
1557
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1551
1558
|
*
|
|
1552
1559
|
* // iterate over each node (heap object)
|
|
1553
1560
|
* heap.nodes.forEach((node: IHeapNode, i: number) => {
|
|
@@ -1579,11 +1586,11 @@ export interface IHeapStringNode extends IHeapNode {
|
|
|
1579
1586
|
* ```typescript
|
|
1580
1587
|
* import type {IHeapSnapshot, IHeapNodes} from '@memlab/core';
|
|
1581
1588
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1582
|
-
* import {
|
|
1589
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1583
1590
|
*
|
|
1584
1591
|
* (async function () {
|
|
1585
1592
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1586
|
-
* const heap: IHeapSnapshot = await
|
|
1593
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1587
1594
|
*
|
|
1588
1595
|
* const nodes: IHeapNodes = heap.nodes;
|
|
1589
1596
|
* nodes.length;
|
|
@@ -1718,5 +1725,11 @@ export interface IClusterStrategy {
|
|
|
1718
1725
|
diffTraces: (newLeakTraces: LeakTrace[], existingLeakTraces: LeakTrace[]) => TraceDiff;
|
|
1719
1726
|
}
|
|
1720
1727
|
/** @internal */
|
|
1728
|
+
export interface IHeapConfig {
|
|
1729
|
+
isCliInteractiveMode: boolean;
|
|
1730
|
+
currentHeapFile: Optional<string>;
|
|
1731
|
+
currentHeap: Optional<IHeapSnapshot>;
|
|
1732
|
+
}
|
|
1733
|
+
/** @internal */
|
|
1721
1734
|
export declare type ErrorWithMessage = Pick<Error, 'message'>;
|
|
1722
1735
|
//# sourceMappingURL=Types.d.ts.map
|
package/dist/lib/Utils.js
CHANGED
|
@@ -390,7 +390,6 @@ function setFiberNodeAttribute(node, flag) {
|
|
|
390
390
|
if (!node || !isFiberNode(node)) {
|
|
391
391
|
return;
|
|
392
392
|
}
|
|
393
|
-
// eslint-disable-next-line no-bitwise
|
|
394
393
|
node.attributes |= flag;
|
|
395
394
|
}
|
|
396
395
|
function hasFiberNodeAttribute(node, flag) {
|
|
@@ -447,7 +446,6 @@ function filterNodesInPlace(idSet, snapshot, cb) {
|
|
|
447
446
|
function applyToNodes(idSet, snapshot, cb, options = {}) {
|
|
448
447
|
let ids = Array.from(idSet.keys());
|
|
449
448
|
if (options.shuffle) {
|
|
450
|
-
// eslint-disable-next-line fb-www/unsafe-math-random
|
|
451
449
|
ids.sort(() => Math.random() - 0.5);
|
|
452
450
|
}
|
|
453
451
|
else if (options.reverse) {
|
|
@@ -508,7 +506,6 @@ function loadScenario(filename) {
|
|
|
508
506
|
}
|
|
509
507
|
let scenario;
|
|
510
508
|
try {
|
|
511
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
512
509
|
scenario = require(filepath);
|
|
513
510
|
scenario = checkScenarioInstance(scenario);
|
|
514
511
|
if (scenario.name == null) {
|
|
@@ -534,6 +531,12 @@ function handleSnapshotError(e) {
|
|
|
534
531
|
}
|
|
535
532
|
function getSnapshotFromFile(filename, options) {
|
|
536
533
|
return __awaiter(this, void 0, void 0, function* () {
|
|
534
|
+
const heapConfig = Config_1.default.heapConfig;
|
|
535
|
+
if (heapConfig &&
|
|
536
|
+
heapConfig.currentHeapFile === filename &&
|
|
537
|
+
heapConfig.currentHeap) {
|
|
538
|
+
return heapConfig.currentHeap;
|
|
539
|
+
}
|
|
537
540
|
Console_1.default.overwrite('parsing ' + filename + ' ...');
|
|
538
541
|
let ret = null;
|
|
539
542
|
try {
|
|
@@ -812,7 +815,6 @@ function loadRunMetaInfo(metaFile = undefined) {
|
|
|
812
815
|
try {
|
|
813
816
|
const content = fs_1.default.readFileSync(file, 'UTF-8');
|
|
814
817
|
return JSON.parse(content);
|
|
815
|
-
// eslint-disable-next-line fb-www/no-unused-catch-bindings
|
|
816
818
|
}
|
|
817
819
|
catch (_) {
|
|
818
820
|
throw haltOrThrow('Run info missing. Please make sure `memlab run` is complete.');
|
|
@@ -1271,9 +1273,23 @@ function getSnapshotDirForAnalysis() {
|
|
|
1271
1273
|
return dir;
|
|
1272
1274
|
}
|
|
1273
1275
|
function getSingleSnapshotFileForAnalysis() {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1276
|
+
let path = null;
|
|
1277
|
+
// if an external snapshot file is specified
|
|
1278
|
+
if (Config_1.default.useExternalSnapshot &&
|
|
1279
|
+
Config_1.default.externalSnapshotFilePaths.length > 0) {
|
|
1280
|
+
path =
|
|
1281
|
+
Config_1.default.externalSnapshotFilePaths[Config_1.default.externalSnapshotFilePaths.length - 1];
|
|
1282
|
+
// if running in interactive heap analysis mode
|
|
1283
|
+
}
|
|
1284
|
+
else if (Config_1.default.heapConfig &&
|
|
1285
|
+
Config_1.default.heapConfig.isCliInteractiveMode &&
|
|
1286
|
+
Config_1.default.heapConfig.currentHeapFile) {
|
|
1287
|
+
path = Config_1.default.heapConfig.currentHeapFile;
|
|
1288
|
+
// search for snapshot labeled as baseline, target, or final
|
|
1289
|
+
}
|
|
1290
|
+
else {
|
|
1291
|
+
path = getSnapshotFilePathWithTabType(/(final)|(target)|(baseline)/);
|
|
1292
|
+
}
|
|
1277
1293
|
return resolveSnapshotFilePath(path);
|
|
1278
1294
|
}
|
|
1279
1295
|
function getSnapshotFilePath(tab) {
|
|
@@ -1286,7 +1302,7 @@ function getSnapshotFilePath(tab) {
|
|
|
1286
1302
|
}
|
|
1287
1303
|
return Config_1.default.externalSnapshotFilePaths[tab.idx - 1];
|
|
1288
1304
|
}
|
|
1289
|
-
// eslint-disable-next-line @typescript-eslint/
|
|
1305
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1290
1306
|
function equalOrMatch(v1, v2) {
|
|
1291
1307
|
const t1 = typeof v1;
|
|
1292
1308
|
const t2 = typeof v2;
|
|
@@ -1421,7 +1437,6 @@ function isURLEqual(url1, url2) {
|
|
|
1421
1437
|
try {
|
|
1422
1438
|
u1 = new URL(url1);
|
|
1423
1439
|
u2 = new URL(url2);
|
|
1424
|
-
// eslint-disable-next-line fb-www/no-unused-catch-bindings
|
|
1425
1440
|
}
|
|
1426
1441
|
catch (_e) {
|
|
1427
1442
|
return false;
|
|
@@ -12,6 +12,7 @@ import type { IHeapNode, IHeapNodes, IHeapEdges, IHeapSnapshot, HeapNodeTypes, H
|
|
|
12
12
|
import HeapNode from './HeapNode';
|
|
13
13
|
export default class HeapSnapshot implements IHeapSnapshot {
|
|
14
14
|
snapshot: RawHeapSnapshot;
|
|
15
|
+
isProcessed: boolean;
|
|
15
16
|
nodes: IHeapNodes;
|
|
16
17
|
_nodeCount: number;
|
|
17
18
|
edges: IHeapEdges;
|
|
@@ -18,11 +18,13 @@ const Console_1 = __importDefault(require("../Console"));
|
|
|
18
18
|
const HeapNode_1 = __importDefault(require("./HeapNode"));
|
|
19
19
|
const HeapEdge_1 = __importDefault(require("./HeapEdge"));
|
|
20
20
|
const HeapUtils_1 = require("./HeapUtils");
|
|
21
|
+
const MemLabTagStore_1 = __importDefault(require("./MemLabTagStore"));
|
|
21
22
|
const EMPTY_UINT8_ARRAY = new Uint8Array(0);
|
|
22
23
|
const EMPTY_UINT32_ARRAY = new Uint32Array(0);
|
|
23
24
|
class HeapSnapshot {
|
|
24
25
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
25
26
|
constructor(snapshot, _options = {}) {
|
|
27
|
+
this.isProcessed = false;
|
|
26
28
|
this._nodeCount = -1;
|
|
27
29
|
this._edgeCount = -1;
|
|
28
30
|
this._nodeId2NodeIdx = {};
|
|
@@ -158,36 +160,7 @@ class HeapSnapshot {
|
|
|
158
160
|
return detected;
|
|
159
161
|
}
|
|
160
162
|
hasObjectWithTag(tag) {
|
|
161
|
-
|
|
162
|
-
let tagStore = null;
|
|
163
|
-
this.nodes.forEach((node) => {
|
|
164
|
-
if (node.name === 'MemLabTaggedStore' && node.type === 'object') {
|
|
165
|
-
tagStore = node;
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
if (tagStore == null) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
const store = tagStore;
|
|
173
|
-
// get tagStore.taggedObjects
|
|
174
|
-
const taggedObjects = store.getReferenceNode('taggedObjects', 'property');
|
|
175
|
-
if (taggedObjects == null) {
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
// get taggedObjects[tag]
|
|
179
|
-
const weakSet = taggedObjects.getReferenceNode(tag, 'property');
|
|
180
|
-
if (weakSet == null) {
|
|
181
|
-
return false;
|
|
182
|
-
}
|
|
183
|
-
// get weakSet.table
|
|
184
|
-
const table = weakSet.getReferenceNode('table');
|
|
185
|
-
if (table == null) {
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
// check if the table has any weak reference to any object
|
|
189
|
-
const ref = table.findAnyReference((edge) => edge.type === 'weak' && edge.toNode.name !== 'system / Oddball');
|
|
190
|
-
return ref != null;
|
|
163
|
+
return MemLabTagStore_1.default.hasObjectWithTag(this, tag);
|
|
191
164
|
}
|
|
192
165
|
getNodeById(id) {
|
|
193
166
|
if (!(id in this._nodeId2NodeIdx)) {
|
|
@@ -35,6 +35,8 @@ class HeapStringNode extends HeapNode_1.default {
|
|
|
35
35
|
if (parentNode == null) {
|
|
36
36
|
throw (0, HeapUtils_1.throwError)(new Error('broken sliced string'));
|
|
37
37
|
}
|
|
38
|
+
// sliced string in heap snapshot doesn't include
|
|
39
|
+
// the start index and the end index, so this may be inaccurate
|
|
38
40
|
return parentNode.stringValue;
|
|
39
41
|
}
|
|
40
42
|
return this.name;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @emails oncall+ws_labs
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
import type { AnyValue, IHeapSnapshot } from '../Types';
|
|
11
|
+
declare type AnyObject = Record<AnyValue, AnyValue>;
|
|
12
|
+
/** @internal */
|
|
13
|
+
export default class MemLabTaggedStore {
|
|
14
|
+
taggedObjects: Record<string, WeakSet<AnyObject>>;
|
|
15
|
+
private constructor();
|
|
16
|
+
private static instance;
|
|
17
|
+
readonly id: string;
|
|
18
|
+
static getInstance(): MemLabTaggedStore;
|
|
19
|
+
static tagObject<T>(o: T, tag: string): void;
|
|
20
|
+
static hasObjectWithTag(heap: IHeapSnapshot, tag: string): boolean;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=MemLabTagStore.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*
|
|
8
|
+
* @emails oncall+ws_labs
|
|
9
|
+
* @format
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const __1 = require("../..");
|
|
13
|
+
let uindex = 1;
|
|
14
|
+
function getUniqueID() {
|
|
15
|
+
const randId = `${Math.random()}`;
|
|
16
|
+
return `${process.pid}-${Date.now()}-${randId}-${uindex++}`;
|
|
17
|
+
}
|
|
18
|
+
/** @internal */
|
|
19
|
+
class MemLabTaggedStore {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.id = getUniqueID();
|
|
22
|
+
this.taggedObjects = Object.create(null);
|
|
23
|
+
}
|
|
24
|
+
// make sure it's a singleton
|
|
25
|
+
static getInstance() {
|
|
26
|
+
if (!MemLabTaggedStore.instance) {
|
|
27
|
+
MemLabTaggedStore.instance = new MemLabTaggedStore();
|
|
28
|
+
}
|
|
29
|
+
return MemLabTaggedStore.instance;
|
|
30
|
+
}
|
|
31
|
+
// tag an object with a mark
|
|
32
|
+
static tagObject(o, tag) {
|
|
33
|
+
const store = MemLabTaggedStore.getInstance();
|
|
34
|
+
if (!store.taggedObjects[tag]) {
|
|
35
|
+
store.taggedObjects[tag] = new WeakSet();
|
|
36
|
+
}
|
|
37
|
+
store.taggedObjects[tag].add(o);
|
|
38
|
+
}
|
|
39
|
+
// check if any object in the heap snapshot has the mark
|
|
40
|
+
// tagged by this MemLabTaggedStore in this execution context
|
|
41
|
+
static hasObjectWithTag(heap, tag) {
|
|
42
|
+
const curContextTagStoreID = MemLabTaggedStore.getInstance().id;
|
|
43
|
+
let tagStore = null;
|
|
44
|
+
// get all MemLabTaggedStore instances in the heap snapshot
|
|
45
|
+
const stores = [];
|
|
46
|
+
heap.nodes.forEach((node) => {
|
|
47
|
+
if (node.name === 'MemLabTaggedStore' && node.type === 'object') {
|
|
48
|
+
stores.push(node);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// if no tag store found
|
|
52
|
+
if (stores.length === 0) {
|
|
53
|
+
return false;
|
|
54
|
+
// if there is only one store found
|
|
55
|
+
}
|
|
56
|
+
else if (stores.length === 1) {
|
|
57
|
+
tagStore = stores[0];
|
|
58
|
+
// if there are multiple MemLabTagStore instances
|
|
59
|
+
// found in the heap snapshot
|
|
60
|
+
}
|
|
61
|
+
else if (stores.length > 1) {
|
|
62
|
+
stores.forEach((node) => {
|
|
63
|
+
// in case multiple instances of MemLabTaggedStore exists
|
|
64
|
+
// in the heap snapshot, we need to make sure that the
|
|
65
|
+
// tag store is the one matching the current execution context
|
|
66
|
+
let storeID = '';
|
|
67
|
+
// match tag store id
|
|
68
|
+
node.forEachReference(edge => {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
if (edge.name_or_index === 'id' && edge.toNode.isString) {
|
|
71
|
+
storeID = (_b = (_a = edge.toNode.toStringNode()) === null || _a === void 0 ? void 0 : _a.stringValue) !== null && _b !== void 0 ? _b : '';
|
|
72
|
+
return { stop: true };
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
if (curContextTagStoreID === storeID) {
|
|
76
|
+
tagStore = node;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
if (tagStore == null) {
|
|
80
|
+
throw __1.utils.haltOrThrow('Multiple MemLabTagStore instances found in heap snapshot ' +
|
|
81
|
+
'when checking object tags, please make sure only one memlab ' +
|
|
82
|
+
'instance is running at a time and double check that memlab is ' +
|
|
83
|
+
'not running in Jest concurrent mode.');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (tagStore == null) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const store = tagStore;
|
|
90
|
+
// get tagStore.taggedObjects
|
|
91
|
+
const taggedObjects = store.getReferenceNode('taggedObjects', 'property');
|
|
92
|
+
if (taggedObjects == null) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
// get taggedObjects[tag]
|
|
96
|
+
const weakSet = taggedObjects.getReferenceNode(tag, 'property');
|
|
97
|
+
if (weakSet == null) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
// get weakSet.table
|
|
101
|
+
const table = weakSet.getReferenceNode('table');
|
|
102
|
+
if (table == null) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
// check if the table has any weak reference to any object
|
|
106
|
+
const ref = table.findAnyReference((edge) => edge.type === 'weak' && edge.toNode.name !== 'system / Oddball');
|
|
107
|
+
return ref != null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.default = MemLabTaggedStore;
|
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
* @param {*} D condenced distance matrix
|
|
14
14
|
* @returns labels - list of doc ids as clusters
|
|
15
15
|
*/
|
|
16
|
-
export declare const cluster: (nDocs: number, condensedDistanceMatrix: Float32Array, maxDistanceThreshold: number) => number[];
|
|
16
|
+
export declare const cluster: (nDocs: number, condensedDistanceMatrix: Float32Array, maxDistanceThreshold: number) => number[] | Uint32Array;
|
|
17
17
|
//# sourceMappingURL=HAC.d.ts.map
|
|
@@ -18,13 +18,13 @@ const condensedIndex = (n, i, j) => {
|
|
|
18
18
|
// (n - 1) + (n - 2) + ... + (n - i) + (j - i) - 1
|
|
19
19
|
return n * i - (i * (i + 1)) / 2 + (j - i - 1);
|
|
20
20
|
};
|
|
21
|
-
|
|
21
|
+
function getRootLabel(array, idx) {
|
|
22
22
|
let rootIdx = idx;
|
|
23
23
|
while (array[rootIdx] !== rootIdx) {
|
|
24
24
|
rootIdx = array[rootIdx];
|
|
25
25
|
}
|
|
26
26
|
return rootIdx;
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
28
|
/**
|
|
29
29
|
*
|
|
30
30
|
* @param {*} nDocs number of docs
|
|
@@ -42,9 +42,7 @@ const cluster = (nDocs, condensedDistanceMatrix, maxDistanceThreshold) => {
|
|
|
42
42
|
let traceBIdx = -1;
|
|
43
43
|
let currentMin = Number.MAX_SAFE_INTEGER;
|
|
44
44
|
let distanceBetweenTraces;
|
|
45
|
-
const labels =
|
|
46
|
-
.fill(0)
|
|
47
|
-
.map((_, idx) => idx);
|
|
45
|
+
const labels = new Uint32Array(nDocs).map((_, idx) => idx);
|
|
48
46
|
for (let k = 0; k < nDocs - 1; k++) {
|
|
49
47
|
traceBIdx = -1;
|
|
50
48
|
if (chainLength === 0) {
|
|
@@ -77,7 +75,7 @@ const cluster = (nDocs, condensedDistanceMatrix, maxDistanceThreshold) => {
|
|
|
77
75
|
traceBIdx = i;
|
|
78
76
|
}
|
|
79
77
|
}
|
|
80
|
-
//
|
|
78
|
+
// make sure that traceA and traceB are closest to each other
|
|
81
79
|
if (chainLength > 1 &&
|
|
82
80
|
traceBIdx !== -1 &&
|
|
83
81
|
traceBIdx === clusterChain[chainLength - 2]) {
|
|
@@ -8,19 +8,23 @@
|
|
|
8
8
|
* @emails oncall+ws_labs
|
|
9
9
|
* @format
|
|
10
10
|
*/
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
exports.TfidfVectorizer = void 0;
|
|
16
|
+
const Config_1 = __importDefault(require("../../../lib/Config"));
|
|
13
17
|
const Ngram_1 = require("./Ngram");
|
|
14
18
|
const SMOOTHING_KEY = '__smoothObjectKey';
|
|
15
19
|
const VOCAB_IDX_FOR_DOC_WITH_HIGH_DF = '-1';
|
|
16
20
|
class TfidfVectorizer {
|
|
17
|
-
constructor({ rawDocuments, maxDF
|
|
21
|
+
constructor({ rawDocuments, maxDF }) {
|
|
18
22
|
this.rawDocuments = [];
|
|
19
23
|
this.vocabulary = Object.create(null);
|
|
20
24
|
this.documentFrequency = Object.create(null);
|
|
21
25
|
this.documents = [];
|
|
22
26
|
this.rawDocuments = rawDocuments;
|
|
23
|
-
this.maxDF = maxDF;
|
|
27
|
+
this.maxDF = maxDF !== null && maxDF !== void 0 ? maxDF : Config_1.default.mlMaxDF;
|
|
24
28
|
}
|
|
25
29
|
computeTfidfs() {
|
|
26
30
|
const tokenizedDocuments = this.rawDocuments.map(this.tokenize);
|