@memlab/core 1.1.4 → 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 +5 -1
- package/dist/index.js +22 -2
- package/dist/lib/Config.d.ts +16 -9
- package/dist/lib/Config.js +15 -0
- package/dist/lib/FileManager.js +4 -2
- package/dist/lib/HeapAnalyzer.js +25 -9
- package/dist/lib/NodeHeap.d.ts +52 -9
- package/dist/lib/NodeHeap.js +72 -21
- package/dist/lib/PackageInfoLoader.d.ts +7 -0
- package/dist/lib/PackageInfoLoader.js +66 -0
- package/dist/lib/Serializer.js +48 -25
- package/dist/lib/Types.d.ts +119 -35
- 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/TraceBucket.js +6 -1
- package/dist/trace-cluster/strategies/MLTraceSimilarityStrategy.d.ts +15 -0
- package/dist/trace-cluster/strategies/MLTraceSimilarityStrategy.js +61 -0
- package/dist/trace-cluster/strategies/machine-learning/DistanceMatrix.d.ts +11 -0
- package/dist/trace-cluster/strategies/machine-learning/DistanceMatrix.js +54 -0
- package/dist/trace-cluster/strategies/machine-learning/HAC.d.ts +17 -0
- package/dist/trace-cluster/strategies/machine-learning/HAC.js +122 -0
- package/dist/trace-cluster/strategies/machine-learning/Ngram.d.ts +11 -0
- package/dist/trace-cluster/strategies/machine-learning/Ngram.js +22 -0
- package/dist/trace-cluster/strategies/machine-learning/TfidfVectorizer.d.ts +38 -0
- package/dist/trace-cluster/strategies/machine-learning/TfidfVectorizer.js +144 -0
- 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
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
* @emails oncall+ws_labs
|
|
8
8
|
* @format
|
|
9
9
|
*/
|
|
10
|
+
/** @internal */
|
|
11
|
+
export declare function registerPackage(): Promise<void>;
|
|
10
12
|
export * from './lib/Types';
|
|
13
|
+
export * from './lib/NodeHeap';
|
|
11
14
|
/** @internal */
|
|
12
15
|
export { default as config } from './lib/Config';
|
|
13
16
|
/** @internal */
|
|
@@ -42,5 +45,6 @@ export { default as leakClusterLogger } from './logger/LeakClusterLogger';
|
|
|
42
45
|
export { default as NormalizedTrace } from './trace-cluster/TraceBucket';
|
|
43
46
|
/** @internal */
|
|
44
47
|
export { default as EvaluationMetric } from './trace-cluster/EvalutationMetric';
|
|
45
|
-
|
|
48
|
+
/** @internal */
|
|
49
|
+
export * from './lib/PackageInfoLoader';
|
|
46
50
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -22,12 +22,31 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
22
22
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
23
23
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
24
24
|
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
25
34
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
36
|
};
|
|
28
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.EvaluationMetric = exports.NormalizedTrace = exports.leakClusterLogger = exports.ProcessManager = exports.modes = exports.constant = exports.analysis = exports.browserInfo = exports.serializer = exports.fileManager = exports.utils = exports.BaseOption = exports.info = exports.config = void 0;
|
|
38
|
+
exports.EvaluationMetric = exports.NormalizedTrace = exports.leakClusterLogger = exports.ProcessManager = exports.modes = exports.constant = exports.analysis = exports.browserInfo = exports.serializer = exports.fileManager = exports.utils = exports.BaseOption = exports.info = exports.config = exports.registerPackage = void 0;
|
|
39
|
+
const path_1 = __importDefault(require("path"));
|
|
40
|
+
const PackageInfoLoader_1 = require("./lib/PackageInfoLoader");
|
|
41
|
+
/** @internal */
|
|
42
|
+
function registerPackage() {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
return PackageInfoLoader_1.PackageInfoLoader.registerPackage(path_1.default.join(__dirname, '..'));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
exports.registerPackage = registerPackage;
|
|
30
48
|
__exportStar(require("./lib/Types"), exports);
|
|
49
|
+
__exportStar(require("./lib/NodeHeap"), exports);
|
|
31
50
|
/** @internal */
|
|
32
51
|
var Config_1 = require("./lib/Config");
|
|
33
52
|
Object.defineProperty(exports, "config", { enumerable: true, get: function () { return __importDefault(Config_1).default; } });
|
|
@@ -76,4 +95,5 @@ Object.defineProperty(exports, "NormalizedTrace", { enumerable: true, get: funct
|
|
|
76
95
|
/** @internal */
|
|
77
96
|
var EvalutationMetric_1 = require("./trace-cluster/EvalutationMetric");
|
|
78
97
|
Object.defineProperty(exports, "EvaluationMetric", { enumerable: true, get: function () { return __importDefault(EvalutationMetric_1).default; } });
|
|
79
|
-
|
|
98
|
+
/** @internal */
|
|
99
|
+
__exportStar(require("./lib/PackageInfoLoader"), exports);
|
package/dist/lib/Config.d.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* @format
|
|
9
9
|
*/
|
|
10
10
|
import type { LaunchOptions, Permission } from 'puppeteer';
|
|
11
|
-
import type { AnyFunction, AnyValue, IClusterStrategy, IRunningMode, IScenario, Nullable, Optional, QuickExperiment, ILeakFilter } from './Types';
|
|
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;
|
|
@@ -44,14 +45,18 @@ export declare enum ErrorHandling {
|
|
|
44
45
|
}
|
|
45
46
|
/** @internal */
|
|
46
47
|
export declare class MemLabConfig {
|
|
47
|
-
snapshotHasDetachedness: boolean;
|
|
48
|
-
specifiedEngine: boolean;
|
|
49
|
-
verbose: boolean;
|
|
50
|
-
jsEngine: string;
|
|
51
48
|
_reportLeaksInTimers: boolean;
|
|
52
49
|
_deviceManualOverridden: boolean;
|
|
53
50
|
_timerNodes: string[];
|
|
54
51
|
_timerEdges: string[];
|
|
52
|
+
_isFullRun: boolean;
|
|
53
|
+
_scenario: Optional<IScenario>;
|
|
54
|
+
_isHeadfulBrowser: boolean;
|
|
55
|
+
_browser: string;
|
|
56
|
+
snapshotHasDetachedness: boolean;
|
|
57
|
+
specifiedEngine: boolean;
|
|
58
|
+
verbose: boolean;
|
|
59
|
+
jsEngine: string;
|
|
55
60
|
targetApp: string;
|
|
56
61
|
targetTab: string;
|
|
57
62
|
analysisMode: string;
|
|
@@ -91,10 +96,10 @@ export declare class MemLabConfig {
|
|
|
91
96
|
dataBuilderDataDir: string;
|
|
92
97
|
unclassifiedClusterDir: string;
|
|
93
98
|
externalCookiesFile: Optional<string>;
|
|
99
|
+
heapConfig: Optional<IHeapConfig>;
|
|
94
100
|
puppeteerConfig: LaunchOptions & BrowserLaunchArgumentOptions & BrowserConnectOptions;
|
|
95
101
|
openDevtoolsConsole: boolean;
|
|
96
102
|
emulateDevice: Nullable<Device>;
|
|
97
|
-
_browser: string;
|
|
98
103
|
addEnableGK: Set<string>;
|
|
99
104
|
addDisableGK: Set<string>;
|
|
100
105
|
qes: QuickExperiment[];
|
|
@@ -169,15 +174,17 @@ export declare class MemLabConfig {
|
|
|
169
174
|
oversizeObjectAsLeak: boolean;
|
|
170
175
|
oversizeThreshold: number;
|
|
171
176
|
clusterRetainedSizeThreshold: number;
|
|
172
|
-
_isFullRun: boolean;
|
|
173
|
-
_scenario: Optional<IScenario>;
|
|
174
|
-
_isHeadfulBrowser: boolean;
|
|
175
177
|
externalLeakFilter?: Optional<ILeakFilter>;
|
|
176
178
|
monoRepoDir: string;
|
|
177
179
|
muteConsole: boolean;
|
|
180
|
+
includeObjectInfoInTraceReturnChain: boolean;
|
|
178
181
|
logUnclassifiedClusters: boolean;
|
|
179
182
|
errorHandling: ErrorHandling;
|
|
180
183
|
clusterStrategy: Optional<IClusterStrategy>;
|
|
184
|
+
packageInfo: IPackageInfo[];
|
|
185
|
+
isMLClustering: boolean;
|
|
186
|
+
mlClusteringLinkageMaxDistance: number;
|
|
187
|
+
mlMaxDF: number;
|
|
181
188
|
constructor(options?: ConfigOption);
|
|
182
189
|
private initInternalConfigs;
|
|
183
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,
|
|
@@ -97,6 +99,8 @@ class MemLabConfig {
|
|
|
97
99
|
this.jsEngine = Constant_1.default.defaultEngine;
|
|
98
100
|
// the default browser (Chromium)
|
|
99
101
|
this._browser = 'chrome';
|
|
102
|
+
// a list of package information
|
|
103
|
+
this.packageInfo = [];
|
|
100
104
|
// a set of additional GKs to be enabled
|
|
101
105
|
this.addEnableGK = new Set();
|
|
102
106
|
// a set of additional GKs to be disabled
|
|
@@ -117,8 +121,19 @@ class MemLabConfig {
|
|
|
117
121
|
this.muteConsole = false;
|
|
118
122
|
// log all leak traces, each as an unclassified cluster
|
|
119
123
|
this.logUnclassifiedClusters = false;
|
|
124
|
+
// If true, the detailed JSON file of each representative
|
|
125
|
+
// trace (for visualization) will include detailed object
|
|
126
|
+
// info for each Fiber node on the return chain.
|
|
127
|
+
// This may bloat the trace size from 100KB to 50MB.
|
|
128
|
+
this.includeObjectInfoInTraceReturnChain = false;
|
|
120
129
|
// by default halt the program when utils.haltOrThrow is calleds
|
|
121
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
|
|
134
|
+
this.mlClusteringLinkageMaxDistance = 0.7;
|
|
135
|
+
// TF/IDF maximum document frequency
|
|
136
|
+
this.mlMaxDF = 1;
|
|
122
137
|
}
|
|
123
138
|
// initialize configurable parameters
|
|
124
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
|
@@ -33,6 +33,7 @@ const Console_1 = __importDefault(require("./Console"));
|
|
|
33
33
|
const Serializer_1 = __importDefault(require("./Serializer"));
|
|
34
34
|
const Utils_1 = __importDefault(require("./Utils"));
|
|
35
35
|
const LeakObjectFilter_1 = require("./leak-filters/LeakObjectFilter");
|
|
36
|
+
const MLTraceSimilarityStrategy_1 = __importDefault(require("../trace-cluster/strategies/MLTraceSimilarityStrategy"));
|
|
36
37
|
class MemoryAnalyst {
|
|
37
38
|
checkLeak() {
|
|
38
39
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -249,9 +250,17 @@ class MemoryAnalyst {
|
|
|
249
250
|
let snapshotLeakedHeapNodeIdSet = new Set();
|
|
250
251
|
let nodeIdsInSnapshots = [];
|
|
251
252
|
let snapshot;
|
|
253
|
+
// if specified a heap file
|
|
252
254
|
if (options.file) {
|
|
253
255
|
const opt = { buildNodeIdIndex: true, verbose: true };
|
|
254
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
|
|
255
264
|
}
|
|
256
265
|
else {
|
|
257
266
|
Utils_1.default.checkSnapshots();
|
|
@@ -367,14 +376,17 @@ class MemoryAnalyst {
|
|
|
367
376
|
// initialize the path finder
|
|
368
377
|
preparePathFinder(snapshot) {
|
|
369
378
|
const finder = new TraceFinder_1.default();
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
+
}
|
|
378
390
|
return finder;
|
|
379
391
|
}
|
|
380
392
|
// summarize the page interaction and dump to the leak text summary file
|
|
@@ -630,7 +642,11 @@ class MemoryAnalyst {
|
|
|
630
642
|
Console_1.default.midLevel(`${numOfLeakedObjects} leaked objects`);
|
|
631
643
|
}
|
|
632
644
|
// cluster traces from the current run
|
|
633
|
-
const clusters = TraceBucket_1.default.clusterPaths(paths, snapshot, this.aggregateDominatorMetrics
|
|
645
|
+
const clusters = TraceBucket_1.default.clusterPaths(paths, snapshot, this.aggregateDominatorMetrics, {
|
|
646
|
+
strategy: Config_1.default.isMLClustering
|
|
647
|
+
? new MLTraceSimilarityStrategy_1.default()
|
|
648
|
+
: undefined,
|
|
649
|
+
});
|
|
634
650
|
yield this.serializeClusterUpdate(clusters);
|
|
635
651
|
if (Config_1.default.logUnclassifiedClusters) {
|
|
636
652
|
// cluster traces from the current run
|
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);
|
|
@@ -48,6 +48,26 @@ import type { IHeapSnapshot } from './Types';
|
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
50
|
export declare function tagObject<T extends object>(o: T, tag: string): T;
|
|
51
|
+
/**
|
|
52
|
+
* Take a heap snapshot of the current program state and save it as a
|
|
53
|
+
* `.heapsnapshot` file under a randomly generated folder inside the system's
|
|
54
|
+
* temp folder.
|
|
55
|
+
*
|
|
56
|
+
* **Note**: All `.heapsnapshot` files could also be loaded by Chrome DevTools.
|
|
57
|
+
* @returns the absolute file path to the saved `.heapsnapshot` file.
|
|
58
|
+
*
|
|
59
|
+
* * **Examples**:
|
|
60
|
+
* ```typescript
|
|
61
|
+
* import type {IHeapSnapshot} from '@memlab/core';
|
|
62
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
63
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
64
|
+
*
|
|
65
|
+
* (async function () {
|
|
66
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
67
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
68
|
+
* })();
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
51
71
|
export declare function dumpNodeHeapSnapshot(): string;
|
|
52
72
|
/**
|
|
53
73
|
* Take a heap snapshot of the current program state
|
|
@@ -57,19 +77,42 @@ export declare function dumpNodeHeapSnapshot(): string;
|
|
|
57
77
|
*
|
|
58
78
|
* @returns heap representation without heap analysis meta data.
|
|
59
79
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
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:
|
|
63
86
|
* ```typescript
|
|
64
87
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
65
|
-
* import {
|
|
66
|
-
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
88
|
+
* import {takeNodeFullHeap} from '@memlab/heap-analysis';
|
|
67
89
|
*
|
|
68
90
|
* (async function () {
|
|
69
|
-
* const
|
|
70
|
-
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
91
|
+
* const heap: IHeapSnapshot = await takeNodeFullHeap();
|
|
71
92
|
* })();
|
|
72
93
|
* ```
|
|
73
94
|
*/
|
|
74
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>;
|
|
75
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,15 +68,36 @@ 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;
|
|
75
|
+
/**
|
|
76
|
+
* Take a heap snapshot of the current program state and save it as a
|
|
77
|
+
* `.heapsnapshot` file under a randomly generated folder inside the system's
|
|
78
|
+
* temp folder.
|
|
79
|
+
*
|
|
80
|
+
* **Note**: All `.heapsnapshot` files could also be loaded by Chrome DevTools.
|
|
81
|
+
* @returns the absolute file path to the saved `.heapsnapshot` file.
|
|
82
|
+
*
|
|
83
|
+
* * **Examples**:
|
|
84
|
+
* ```typescript
|
|
85
|
+
* import type {IHeapSnapshot} from '@memlab/core';
|
|
86
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
87
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
88
|
+
*
|
|
89
|
+
* (async function () {
|
|
90
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
91
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
92
|
+
* })();
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
83
95
|
function dumpNodeHeapSnapshot() {
|
|
84
|
-
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
|
+
}
|
|
85
101
|
v8_1.default.writeHeapSnapshot(file);
|
|
86
102
|
return file;
|
|
87
103
|
}
|
|
@@ -94,17 +110,18 @@ exports.dumpNodeHeapSnapshot = dumpNodeHeapSnapshot;
|
|
|
94
110
|
*
|
|
95
111
|
* @returns heap representation without heap analysis meta data.
|
|
96
112
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
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:
|
|
100
119
|
* ```typescript
|
|
101
120
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
102
|
-
* import {
|
|
103
|
-
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
121
|
+
* import {takeNodeFullHeap} from '@memlab/heap-analysis';
|
|
104
122
|
*
|
|
105
123
|
* (async function () {
|
|
106
|
-
* const
|
|
107
|
-
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
124
|
+
* const heap: IHeapSnapshot = await takeNodeFullHeap();
|
|
108
125
|
* })();
|
|
109
126
|
* ```
|
|
110
127
|
*/
|
|
@@ -121,3 +138,37 @@ function getNodeInnocentHeap() {
|
|
|
121
138
|
});
|
|
122
139
|
}
|
|
123
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;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.PackageInfoLoader = void 0;
|
|
16
|
+
/**
|
|
17
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
18
|
+
*
|
|
19
|
+
* This source code is licensed under the MIT license found in the
|
|
20
|
+
* LICENSE file in the root directory of this source tree.
|
|
21
|
+
*
|
|
22
|
+
* @emails oncall+ws_labs
|
|
23
|
+
* @format
|
|
24
|
+
*/
|
|
25
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
26
|
+
const path_1 = __importDefault(require("path"));
|
|
27
|
+
const Config_1 = __importDefault(require("./Config"));
|
|
28
|
+
const Utils_1 = __importDefault(require("./Utils"));
|
|
29
|
+
/** @internal */
|
|
30
|
+
class PackageInfoLoader {
|
|
31
|
+
static loadFrom(packageDirectory) {
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
let exists = yield fs_extra_1.default.pathExists(packageDirectory);
|
|
34
|
+
if (!exists) {
|
|
35
|
+
throw Utils_1.default.haltOrThrow(`package directory doesn't exist: ${packageDirectory}`);
|
|
36
|
+
}
|
|
37
|
+
let packageJSONFile = path_1.default.join(packageDirectory, 'package-oss.json');
|
|
38
|
+
exists = yield fs_extra_1.default.pathExists(packageJSONFile);
|
|
39
|
+
if (!exists) {
|
|
40
|
+
packageJSONFile = path_1.default.join(packageDirectory, 'package.json');
|
|
41
|
+
}
|
|
42
|
+
exists = yield fs_extra_1.default.pathExists(packageJSONFile);
|
|
43
|
+
if (!exists) {
|
|
44
|
+
throw Utils_1.default.haltOrThrow(`package.json doesn't exist: ${packageJSONFile}`);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const metaData = yield fs_extra_1.default.readJSON(packageJSONFile, 'UTF-8');
|
|
48
|
+
return Object.assign(Object.assign({}, metaData), { packageLocation: packageDirectory });
|
|
49
|
+
}
|
|
50
|
+
catch (ex) {
|
|
51
|
+
throw Utils_1.default.haltOrThrow(Utils_1.default.getError(ex));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
static registerPackage(packageDirectory) {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
if (!PackageInfoLoader.registeredPackages.has(packageDirectory)) {
|
|
58
|
+
PackageInfoLoader.registeredPackages.add(packageDirectory);
|
|
59
|
+
const packageInfo = yield PackageInfoLoader.loadFrom(packageDirectory);
|
|
60
|
+
Config_1.default.packageInfo.push(packageInfo);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.PackageInfoLoader = PackageInfoLoader;
|
|
66
|
+
PackageInfoLoader.registeredPackages = new Set();
|