@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
package/dist/lib/Serializer.js
CHANGED
|
@@ -99,7 +99,7 @@ function JSONifyDetachedHTMLElement(node, args, options) {
|
|
|
99
99
|
// options for elem.__reactProps$xxx
|
|
100
100
|
const propsOptions = Object.assign({}, options);
|
|
101
101
|
propsOptions.forceJSONifyDepth = 1;
|
|
102
|
-
|
|
102
|
+
iterateSelectedEdges(node, (edge) => {
|
|
103
103
|
const key = JSONifyEdgeNameAndType(edge);
|
|
104
104
|
if (Utils_1.default.isReactFiberEdge(edge)) {
|
|
105
105
|
info[key] = JSONifyNode(edge.toNode, args, fiberOptions);
|
|
@@ -110,7 +110,8 @@ function JSONifyDetachedHTMLElement(node, args, options) {
|
|
|
110
110
|
else {
|
|
111
111
|
info[key] = JSONifyNodeInShort(edge.toNode);
|
|
112
112
|
}
|
|
113
|
-
|
|
113
|
+
return null;
|
|
114
|
+
});
|
|
114
115
|
return info;
|
|
115
116
|
}
|
|
116
117
|
function calculateReturnTrace(node, cache) {
|
|
@@ -128,15 +129,16 @@ function calculateReturnTrace(node, cache) {
|
|
|
128
129
|
const objectNodeUsefulProps = new Set(['_context']);
|
|
129
130
|
function JSONifyNodeOneLevel(node) {
|
|
130
131
|
const info = Object.create(null);
|
|
131
|
-
|
|
132
|
+
iterateSelectedEdges(node, (edge) => {
|
|
132
133
|
const key = JSONifyEdgeNameAndType(edge);
|
|
133
134
|
info[key] = JSONifyNodeShallow(edge.toNode);
|
|
134
|
-
|
|
135
|
+
return null;
|
|
136
|
+
});
|
|
135
137
|
return info;
|
|
136
138
|
}
|
|
137
139
|
function JSONifyNodeShallow(node) {
|
|
138
140
|
const info = Object.create(null);
|
|
139
|
-
|
|
141
|
+
iterateSelectedEdges(node, (edge) => {
|
|
140
142
|
const key = JSONifyEdgeNameAndType(edge);
|
|
141
143
|
if (objectNodeUsefulProps.has(edge.name_or_index)) {
|
|
142
144
|
info[key] = JSONifyNodeShallow(edge.toNode);
|
|
@@ -144,7 +146,8 @@ function JSONifyNodeShallow(node) {
|
|
|
144
146
|
else {
|
|
145
147
|
info[key] = JSONifyNodeInShort(edge.toNode);
|
|
146
148
|
}
|
|
147
|
-
|
|
149
|
+
return null;
|
|
150
|
+
});
|
|
148
151
|
return info;
|
|
149
152
|
}
|
|
150
153
|
const fiberNodeUsefulProps = new Set([
|
|
@@ -154,15 +157,17 @@ const fiberNodeUsefulProps = new Set([
|
|
|
154
157
|
]);
|
|
155
158
|
function JSONifyFiberNodeShallow(node) {
|
|
156
159
|
const info = Object.create(null);
|
|
157
|
-
|
|
160
|
+
iterateSelectedEdges(node, (edge) => {
|
|
158
161
|
const key = JSONifyEdgeNameAndType(edge);
|
|
159
162
|
if (fiberNodeUsefulProps.has(edge.name_or_index) &&
|
|
160
163
|
Utils_1.default.isObjectNode(edge.toNode)) {
|
|
161
164
|
info[key] = JSONifyNodeShallow(edge.toNode);
|
|
162
|
-
continue;
|
|
163
165
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
+
else {
|
|
167
|
+
info[key] = JSONifyNodeInShort(edge.toNode);
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
});
|
|
166
171
|
return info;
|
|
167
172
|
}
|
|
168
173
|
// calculate the summary of return chain of the FiberNode
|
|
@@ -187,7 +192,9 @@ function JSONifyFiberNodeReturnTrace(node, args, options) {
|
|
|
187
192
|
}
|
|
188
193
|
const parentInfo = getNodeNameInJSON(parent, args);
|
|
189
194
|
key = `${key}: --return (property)---> ${parentInfo}`;
|
|
190
|
-
const info =
|
|
195
|
+
const info = Config_1.default.includeObjectInfoInTraceReturnChain
|
|
196
|
+
? JSONifyFiberNodeShallow(parent)
|
|
197
|
+
: Object.create(null);
|
|
191
198
|
trace[key] = info;
|
|
192
199
|
}
|
|
193
200
|
return trace;
|
|
@@ -206,25 +213,27 @@ function JSONifyFiberNode(node, args, options) {
|
|
|
206
213
|
propsOptions.forceJSONifyDepth = 1;
|
|
207
214
|
}
|
|
208
215
|
propsOptions.forceJSONifyDepth--;
|
|
209
|
-
|
|
216
|
+
iterateSelectedEdges(node, (edge) => {
|
|
210
217
|
const key = JSONifyEdgeNameAndType(edge);
|
|
211
218
|
info[key] =
|
|
212
|
-
propsOptions.forceJSONifyDepth >= 1
|
|
219
|
+
propsOptions.forceJSONifyDepth && propsOptions.forceJSONifyDepth >= 1
|
|
213
220
|
? JSONifyNode(edge.toNode, args, propsOptions)
|
|
214
221
|
: JSONifyNodeInShort(edge.toNode);
|
|
215
|
-
|
|
222
|
+
return null;
|
|
223
|
+
});
|
|
216
224
|
return info;
|
|
217
225
|
}
|
|
218
226
|
function JSONifyClosure(node, args, options) {
|
|
219
227
|
const info = Object.create(null);
|
|
220
|
-
|
|
228
|
+
iterateSelectedEdges(node, (edge) => {
|
|
221
229
|
if (edge.name_or_index === 'shared' ||
|
|
222
230
|
edge.name_or_index === 'context' ||
|
|
223
231
|
edge.name_or_index === 'displayName') {
|
|
224
232
|
const key = filterJSONPropName(edge.name_or_index);
|
|
225
233
|
info[key] = JSONifyNode(edge.toNode, args, options);
|
|
226
234
|
}
|
|
227
|
-
|
|
235
|
+
return null;
|
|
236
|
+
});
|
|
228
237
|
return info;
|
|
229
238
|
}
|
|
230
239
|
function JSONifyNumberNode(node,
|
|
@@ -238,7 +247,7 @@ _options) {
|
|
|
238
247
|
}
|
|
239
248
|
function JSONifyCode(node, args, options) {
|
|
240
249
|
const info = Object.create(null);
|
|
241
|
-
|
|
250
|
+
iterateSelectedEdges(node, (edge) => {
|
|
242
251
|
if (edge.name_or_index === 'name_or_scope_info' &&
|
|
243
252
|
edge.toNode.name === '(function scope info)') {
|
|
244
253
|
const key = 'variables with non-number values in closure scope chain';
|
|
@@ -251,14 +260,15 @@ function JSONifyCode(node, args, options) {
|
|
|
251
260
|
const key = filterJSONPropName(edge.name_or_index);
|
|
252
261
|
info[key] = JSONifyNode(edge.toNode, args, options);
|
|
253
262
|
}
|
|
254
|
-
|
|
263
|
+
return null;
|
|
264
|
+
});
|
|
255
265
|
return info;
|
|
256
266
|
}
|
|
257
267
|
function JSONifyContext(node, args, options) {
|
|
258
268
|
const info = Object.create(null);
|
|
259
269
|
const key = 'variables in scope (used by nested closures)';
|
|
260
270
|
const closure_vars = (info[key] = Object.create(null));
|
|
261
|
-
|
|
271
|
+
iterateSelectedEdges(node, (edge) => {
|
|
262
272
|
const key = filterJSONPropName(edge.name_or_index);
|
|
263
273
|
if (edge.type === 'context') {
|
|
264
274
|
closure_vars[key] = JSONifyNodeInShort(edge.toNode);
|
|
@@ -266,15 +276,27 @@ function JSONifyContext(node, args, options) {
|
|
|
266
276
|
else if (edge.type === '') {
|
|
267
277
|
info[key] = JSONifyNode(edge.toNode, args, options);
|
|
268
278
|
}
|
|
269
|
-
|
|
279
|
+
return null;
|
|
280
|
+
});
|
|
270
281
|
return info;
|
|
271
282
|
}
|
|
283
|
+
function iterateSelectedEdges(node, callback) {
|
|
284
|
+
let edgesProcessed = 0;
|
|
285
|
+
node.forEachReference((edge) => {
|
|
286
|
+
if (edge.type === 'internal') {
|
|
287
|
+
if (edge.name_or_index === 'map' || edge.is_index) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (edgesProcessed++ > 100) {
|
|
292
|
+
return { stop: true };
|
|
293
|
+
}
|
|
294
|
+
return callback(edge);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
272
297
|
function JSONifyOrdinaryValue(node, args, options) {
|
|
273
298
|
const info = Object.create(null);
|
|
274
|
-
|
|
275
|
-
if (edge.name_or_index === 'map' && edge.type === 'internal') {
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
299
|
+
iterateSelectedEdges(node, (edge) => {
|
|
278
300
|
const key = JSONifyEdgeNameAndType(edge);
|
|
279
301
|
const toNode = edge.toNode;
|
|
280
302
|
const toNodeName = toNode.name;
|
|
@@ -293,7 +315,8 @@ function JSONifyOrdinaryValue(node, args, options) {
|
|
|
293
315
|
else {
|
|
294
316
|
info[key] = JSONifyNodeInShort(toNode);
|
|
295
317
|
}
|
|
296
|
-
|
|
318
|
+
return null;
|
|
319
|
+
});
|
|
297
320
|
return info;
|
|
298
321
|
}
|
|
299
322
|
function JSONifyNode(node, args, options) {
|
package/dist/lib/Types.d.ts
CHANGED
|
@@ -32,7 +32,6 @@ export declare type AnyOptions = Record<string, unknown>;
|
|
|
32
32
|
export declare type UnusedOptions = Record<string, never>;
|
|
33
33
|
/** @internal */
|
|
34
34
|
export declare type Command = [string, string[], AnyOptions];
|
|
35
|
-
export declare type Predicator<T> = (node: T) => boolean;
|
|
36
35
|
/** @internal */
|
|
37
36
|
export declare type HeapNodeIdSet = Set<number>;
|
|
38
37
|
/** @internal */
|
|
@@ -86,6 +85,22 @@ export declare type CLIArgs = {
|
|
|
86
85
|
'local-puppeteer': boolean;
|
|
87
86
|
'snapshot-dir': string;
|
|
88
87
|
};
|
|
88
|
+
/**
|
|
89
|
+
* the predicate callback is used to decide if a
|
|
90
|
+
* entity of type `T`.
|
|
91
|
+
* For more concrete examples on where it is used,
|
|
92
|
+
* check out {@link findAnyReference}, {@link findAnyReferrer},
|
|
93
|
+
* and {@link findReferrers}.
|
|
94
|
+
*
|
|
95
|
+
* @typeParam T - the type of the entity to be checked
|
|
96
|
+
* @param entity - the entity to be checked
|
|
97
|
+
* @returns whether the entity passes the predicate check
|
|
98
|
+
*/
|
|
99
|
+
export declare type Predicator<T> = (entity: T) => boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Data structure for holding cookies.
|
|
102
|
+
* For concrete example, check out {@link cookies}.
|
|
103
|
+
*/
|
|
89
104
|
export declare type Cookies = Array<{
|
|
90
105
|
name: string;
|
|
91
106
|
value: string;
|
|
@@ -120,6 +135,12 @@ export interface E2EScenarioSynthesizerConstructor {
|
|
|
120
135
|
new (config: Config): IE2EScenarioSynthesizer;
|
|
121
136
|
}
|
|
122
137
|
/** @internal */
|
|
138
|
+
export interface IPackageInfo {
|
|
139
|
+
name: string;
|
|
140
|
+
version: string;
|
|
141
|
+
packageLocation?: string;
|
|
142
|
+
}
|
|
143
|
+
/** @internal */
|
|
123
144
|
export interface IRunningMode {
|
|
124
145
|
setConfig(config: Config): void;
|
|
125
146
|
beforeRunning(visitPlan: IE2EScenarioVisitPlan): void;
|
|
@@ -282,9 +303,10 @@ export interface ILeakFilter {
|
|
|
282
303
|
/**
|
|
283
304
|
* Lifecycle function callback that is invoked initially once before calling any
|
|
284
305
|
* leak filter function.
|
|
306
|
+
* For concrete example, check out {@link beforeLeakFilter}.
|
|
285
307
|
*
|
|
286
|
-
* @param
|
|
287
|
-
* @param leakedNodeIds
|
|
308
|
+
* @param snapshot heap snapshot see {@link IHeapSnapshot}
|
|
309
|
+
* @param leakedNodeIds the set of leaked object (node) ids.
|
|
288
310
|
*/
|
|
289
311
|
export declare type InitLeakFilterCallback = (snapshot: IHeapSnapshot, leakedNodeIds: HeapNodeIdSet) => void;
|
|
290
312
|
/**
|
|
@@ -293,6 +315,8 @@ export declare type InitLeakFilterCallback = (snapshot: IHeapSnapshot, leakedNod
|
|
|
293
315
|
* allocated but not released from the target interaction
|
|
294
316
|
* in the heap snapshot.
|
|
295
317
|
*
|
|
318
|
+
* For concrete examples, check out {@link leakFilter}.
|
|
319
|
+
*
|
|
296
320
|
* @param node - the node that is kept alive in the memory in the heap snapshot
|
|
297
321
|
* @param snapshot - the snapshot of target interaction
|
|
298
322
|
* @param leakedNodeIds - the set of leaked node ids
|
|
@@ -311,6 +335,11 @@ export declare type LeakFilterCallback = (node: IHeapNode, snapshot: IHeapSnapsh
|
|
|
311
335
|
/**
|
|
312
336
|
* The callback defines browser interactions which are
|
|
313
337
|
* used by memlab to interact with the web app under test.
|
|
338
|
+
* For concrete examples, check out {@link action} or {@link back}.
|
|
339
|
+
*
|
|
340
|
+
* @param page the puppeteer [`Page`](https://pptr.dev/api/puppeteer.page)
|
|
341
|
+
* object, which provides APIs to interact with the web browser
|
|
342
|
+
* @returns no return value
|
|
314
343
|
*/
|
|
315
344
|
export declare type InteractionsCallback = (page: Page, args?: OperationArgs) => Promise<void>;
|
|
316
345
|
/**
|
|
@@ -451,6 +480,10 @@ export interface IScenario {
|
|
|
451
480
|
* `back` is the callback function that specifies how memlab should
|
|
452
481
|
* back/revert the `action` callback. Think of it as an undo action.
|
|
453
482
|
*
|
|
483
|
+
* * **Parameters**:
|
|
484
|
+
* * page: `Page` | the puppeteer [`Page`](https://pptr.dev/api/puppeteer.page)
|
|
485
|
+
* object, which provides APIs to interact with the web browser
|
|
486
|
+
*
|
|
454
487
|
* * **Examples**:
|
|
455
488
|
* ```typescript
|
|
456
489
|
* const scenario = {
|
|
@@ -674,7 +707,12 @@ export interface IDataBuilder {
|
|
|
674
707
|
}
|
|
675
708
|
/**
|
|
676
709
|
* Callback function to provide if the page is loaded.
|
|
710
|
+
* For concrete example, check out {@link isPageLoaded}.
|
|
677
711
|
* @param page - puppeteer's [Page](https://pptr.dev/api/puppeteer.page/) object.
|
|
712
|
+
* @returns a boolean value, if it returns `true`, memlab will consider
|
|
713
|
+
* the navigation completes, if it returns `false`, memlab will keep calling
|
|
714
|
+
* this callback until it returns `true`. This is an async callback, you can
|
|
715
|
+
* also `await` and returns `true` until some async logic is resolved.
|
|
678
716
|
*/
|
|
679
717
|
export declare type CheckPageLoadCallback = (page: Page) => Promise<boolean>;
|
|
680
718
|
/** @internal */
|
|
@@ -724,16 +762,42 @@ export declare type E2EStepInfo = IE2EStepBasic & {
|
|
|
724
762
|
delay?: number;
|
|
725
763
|
metrics: Record<string, number>;
|
|
726
764
|
};
|
|
727
|
-
/**
|
|
765
|
+
/**
|
|
766
|
+
* This data structure contains the input configuration for the browser and
|
|
767
|
+
* output data from the browser. You can retrieve the instance of this type
|
|
768
|
+
* through {@link RunMetaInfo}.
|
|
769
|
+
*/
|
|
728
770
|
export interface IBrowserInfo {
|
|
771
|
+
/**
|
|
772
|
+
* browser version
|
|
773
|
+
*/
|
|
729
774
|
_browserVersion: string;
|
|
775
|
+
/**
|
|
776
|
+
* configuration for puppeteer
|
|
777
|
+
*/
|
|
730
778
|
_puppeteerConfig: LaunchOptions;
|
|
779
|
+
/**
|
|
780
|
+
* all web console output
|
|
781
|
+
*/
|
|
731
782
|
_consoleMessages: string[];
|
|
732
783
|
}
|
|
784
|
+
/**
|
|
785
|
+
* This data structure holds the information about memlab run.
|
|
786
|
+
* You can retrieve the instance of this type through {@link getRunMetaInfo}.
|
|
787
|
+
*/
|
|
733
788
|
export declare type RunMetaInfo = {
|
|
789
|
+
/** @internal */
|
|
734
790
|
app: string;
|
|
791
|
+
/** @internal */
|
|
735
792
|
interaction: string;
|
|
793
|
+
/**
|
|
794
|
+
* type of the memlab run
|
|
795
|
+
*/
|
|
736
796
|
type: string;
|
|
797
|
+
/**
|
|
798
|
+
* input configuration for the browser and
|
|
799
|
+
* output data from the browser
|
|
800
|
+
*/
|
|
737
801
|
browserInfo: IBrowserInfo;
|
|
738
802
|
};
|
|
739
803
|
/**
|
|
@@ -743,6 +807,13 @@ export declare type RunMetaInfo = {
|
|
|
743
807
|
* {@link IHeapNode} and {@link IHeapEdge}.
|
|
744
808
|
*/
|
|
745
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;
|
|
746
817
|
/** @internal */
|
|
747
818
|
snapshot: RawHeapSnapshot;
|
|
748
819
|
/**
|
|
@@ -755,11 +826,11 @@ export interface IHeapSnapshot {
|
|
|
755
826
|
* ```typescript
|
|
756
827
|
* import type {IHeapSnapshot, IHeapNode} from '@memlab/core';
|
|
757
828
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
758
|
-
* import {
|
|
829
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
759
830
|
*
|
|
760
831
|
* (async function () {
|
|
761
832
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
762
|
-
* const heap: IHeapSnapshot = await
|
|
833
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
763
834
|
*
|
|
764
835
|
* // get the total number of heap objects
|
|
765
836
|
* heap.nodes.length;
|
|
@@ -781,11 +852,11 @@ export interface IHeapSnapshot {
|
|
|
781
852
|
* ```typescript
|
|
782
853
|
* import type {IHeapSnapshot, IHeapEdge} from '@memlab/core';
|
|
783
854
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
784
|
-
* import {
|
|
855
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
785
856
|
*
|
|
786
857
|
* (async function () {
|
|
787
858
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
788
|
-
* const heap: IHeapSnapshot = await
|
|
859
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
789
860
|
*
|
|
790
861
|
* // get the total number of heap references
|
|
791
862
|
* heap.edges.length;
|
|
@@ -807,11 +878,11 @@ export interface IHeapSnapshot {
|
|
|
807
878
|
* ```typescript
|
|
808
879
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
809
880
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
810
|
-
* import {
|
|
881
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
811
882
|
*
|
|
812
883
|
* (async function () {
|
|
813
884
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
814
|
-
* const heap: IHeapSnapshot = await
|
|
885
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
815
886
|
*
|
|
816
887
|
* const node = heap.getNodeById(351);
|
|
817
888
|
* node?.id; // should be 351
|
|
@@ -829,7 +900,7 @@ export interface IHeapSnapshot {
|
|
|
829
900
|
* ```typescript
|
|
830
901
|
* // save as example.test.ts
|
|
831
902
|
* import type {IHeapSnapshot, Nullable} from '@memlab/core';
|
|
832
|
-
* import {config,
|
|
903
|
+
* import {config, takeNodeMinimalHeap} from '@memlab/core';
|
|
833
904
|
*
|
|
834
905
|
* class TestObject {
|
|
835
906
|
* public arr1 = [1, 2, 3];
|
|
@@ -841,7 +912,7 @@ export interface IHeapSnapshot {
|
|
|
841
912
|
*
|
|
842
913
|
* let obj: Nullable<TestObject> = new TestObject();
|
|
843
914
|
* // get a heap snapshot of the current program state
|
|
844
|
-
* let heap: IHeapSnapshot = await
|
|
915
|
+
* let heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
845
916
|
*
|
|
846
917
|
* // call some function that may add references to obj
|
|
847
918
|
* rabbitHole(obj)
|
|
@@ -849,7 +920,7 @@ export interface IHeapSnapshot {
|
|
|
849
920
|
* expect(heap.hasObjectWithClassName('TestObject')).toBe(true);
|
|
850
921
|
* obj = null;
|
|
851
922
|
*
|
|
852
|
-
* heap = await
|
|
923
|
+
* heap = await takeNodeMinimalHeap();
|
|
853
924
|
* // if rabbitHole does not have any side effect that
|
|
854
925
|
* // adds new references to obj, then obj can be GCed
|
|
855
926
|
* expect(heap.hasObjectWithClassName('TestObject')).toBe(false);
|
|
@@ -868,7 +939,7 @@ export interface IHeapSnapshot {
|
|
|
868
939
|
* * **Examples**:
|
|
869
940
|
* ```typescript
|
|
870
941
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
871
|
-
* import {
|
|
942
|
+
* import {takeNodeMinimalHeap} from '@memlab/core';
|
|
872
943
|
*
|
|
873
944
|
* class TestObject {
|
|
874
945
|
* public arr1 = [1, 2, 3];
|
|
@@ -878,7 +949,7 @@ export interface IHeapSnapshot {
|
|
|
878
949
|
* (async function () {
|
|
879
950
|
* const obj = new TestObject();
|
|
880
951
|
* // get a heap snapshot of the current program state
|
|
881
|
-
* const heap: IHeapSnapshot = await
|
|
952
|
+
* const heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
882
953
|
*
|
|
883
954
|
* const node = heap.getAnyObjectWithClassName('TestObject');
|
|
884
955
|
* console.log(node?.name); // should be 'TestObject'
|
|
@@ -897,14 +968,14 @@ export interface IHeapSnapshot {
|
|
|
897
968
|
* ```typescript
|
|
898
969
|
* import type {IHeapSnapshot} from '@memlab/core';
|
|
899
970
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
900
|
-
* import {
|
|
971
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
901
972
|
*
|
|
902
973
|
* (async function () {
|
|
903
974
|
* // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
904
975
|
* const object = {'memlab-test-heap-property': 'memlab-test-heap-value'};
|
|
905
976
|
*
|
|
906
977
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
907
|
-
* const heap: IHeapSnapshot = await
|
|
978
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
908
979
|
*
|
|
909
980
|
* // should be true
|
|
910
981
|
* console.log(heap.hasObjectWithPropertyName('memlab-test-heap-property'));
|
|
@@ -924,7 +995,7 @@ export interface IHeapSnapshot {
|
|
|
924
995
|
*
|
|
925
996
|
* ```typescript
|
|
926
997
|
* import type {IHeapSnapshot, AnyValue} from '@memlab/core';
|
|
927
|
-
* import {config,
|
|
998
|
+
* import {config, takeNodeMinimalHeap, tagObject} from '@memlab/core';
|
|
928
999
|
*
|
|
929
1000
|
* test('memory test', async () => {
|
|
930
1001
|
* config.muteConsole = true;
|
|
@@ -938,7 +1009,7 @@ export interface IHeapSnapshot {
|
|
|
938
1009
|
*
|
|
939
1010
|
* o2 = null;
|
|
940
1011
|
*
|
|
941
|
-
* const heap: IHeapSnapshot = await
|
|
1012
|
+
* const heap: IHeapSnapshot = await takeNodeMinimalHeap();
|
|
942
1013
|
*
|
|
943
1014
|
* // expect object with marker "memlab-mark-1" exists
|
|
944
1015
|
* expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
|
|
@@ -962,16 +1033,16 @@ export interface IHeapSnapshot {
|
|
|
962
1033
|
* @readonly it is not recommended to modify any `IHeapLocation` instance
|
|
963
1034
|
*
|
|
964
1035
|
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
965
|
-
* {@link
|
|
1036
|
+
* {@link getFullHeapFromFile} API.
|
|
966
1037
|
*
|
|
967
1038
|
* ```typescript
|
|
968
1039
|
* import type {IHeapSnapshot, IHeapNode, IHeapLocation} from '@memlab/core';
|
|
969
1040
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
970
|
-
* import {
|
|
1041
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
971
1042
|
*
|
|
972
1043
|
* (async function () {
|
|
973
1044
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
974
|
-
* const heap: IHeapSnapshot = await
|
|
1045
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
975
1046
|
*
|
|
976
1047
|
* // iterate over each node (heap object)
|
|
977
1048
|
* heap.nodes.forEach((node: IHeapNode, i: number) => {
|
|
@@ -1024,16 +1095,16 @@ export interface IHeapEdgeBasic {
|
|
|
1024
1095
|
* @readonly it is not recommended to modify any `IHeapEdge` instance
|
|
1025
1096
|
*
|
|
1026
1097
|
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1027
|
-
* {@link
|
|
1098
|
+
* {@link getFullHeapFromFile} API.
|
|
1028
1099
|
*
|
|
1029
1100
|
* ```typescript
|
|
1030
1101
|
* import type {IHeapSnapshot, IHeapEdge} from '@memlab/core';
|
|
1031
1102
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1032
|
-
* import {
|
|
1103
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1033
1104
|
*
|
|
1034
1105
|
* (async function () {
|
|
1035
1106
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1036
|
-
* const heap: IHeapSnapshot = await
|
|
1107
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1037
1108
|
*
|
|
1038
1109
|
* // iterate over each edge (JS reference in heap)
|
|
1039
1110
|
* heap.edges.forEach((edge: IHeapEdge, i: number) => {
|
|
@@ -1087,11 +1158,11 @@ export interface IHeapEdge extends IHeapEdgeBasic {
|
|
|
1087
1158
|
* ```typescript
|
|
1088
1159
|
* import type {IHeapSnapshot, IHeapEdges} from '@memlab/core';
|
|
1089
1160
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1090
|
-
* import {
|
|
1161
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1091
1162
|
*
|
|
1092
1163
|
* (async function () {
|
|
1093
1164
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1094
|
-
* const heap: IHeapSnapshot = await
|
|
1165
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1095
1166
|
*
|
|
1096
1167
|
* const edges: IHeapEdges = heap.edges;
|
|
1097
1168
|
* edges.length;
|
|
@@ -1148,6 +1219,13 @@ export interface IHeapNodeBasic {
|
|
|
1148
1219
|
*/
|
|
1149
1220
|
id: number;
|
|
1150
1221
|
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Executes a provided callback once for JavaScript references.
|
|
1224
|
+
* For concrete examples, check out {@link forEachReference}
|
|
1225
|
+
* or {@link forEachReferrer}.
|
|
1226
|
+
* @param callback the callback for each JavaScript reference from a collection
|
|
1227
|
+
* @returns this API returns void
|
|
1228
|
+
*/
|
|
1151
1229
|
export declare type EdgeIterationCallback = (edge: IHeapEdge) => Optional<{
|
|
1152
1230
|
stop: boolean;
|
|
1153
1231
|
}>;
|
|
@@ -1159,16 +1237,16 @@ export declare type EdgeIterationCallback = (edge: IHeapEdge) => Optional<{
|
|
|
1159
1237
|
* @readonly it is not recommended to modify any `IHeapNode` instance
|
|
1160
1238
|
*
|
|
1161
1239
|
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1162
|
-
* {@link
|
|
1240
|
+
* {@link getFullHeapFromFile} API.
|
|
1163
1241
|
*
|
|
1164
1242
|
* ```typescript
|
|
1165
1243
|
* import type {IHeapSnapshot, IHeapNode} from '@memlab/core';
|
|
1166
1244
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1167
|
-
* import {
|
|
1245
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1168
1246
|
*
|
|
1169
1247
|
* (async function () {
|
|
1170
1248
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1171
|
-
* const heap: IHeapSnapshot = await
|
|
1249
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1172
1250
|
*
|
|
1173
1251
|
* // iterate over each node (heap object)
|
|
1174
1252
|
* heap.nodes.forEach((node: IHeapNode, i: number) => {
|
|
@@ -1467,16 +1545,16 @@ export interface IHeapNode extends IHeapNodeBasic {
|
|
|
1467
1545
|
* @readonly it is not recommended to modify any `IHeapStringNode` instance
|
|
1468
1546
|
*
|
|
1469
1547
|
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1470
|
-
* {@link
|
|
1548
|
+
* {@link getFullHeapFromFile} API.
|
|
1471
1549
|
*
|
|
1472
1550
|
* ```typescript
|
|
1473
1551
|
* import type {IHeapSnapshot, IHeapNode, IHeapStringNode} from '@memlab/core';
|
|
1474
1552
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1475
|
-
* import {
|
|
1553
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1476
1554
|
*
|
|
1477
1555
|
* (async function () {
|
|
1478
1556
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1479
|
-
* const heap: IHeapSnapshot = await
|
|
1557
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1480
1558
|
*
|
|
1481
1559
|
* // iterate over each node (heap object)
|
|
1482
1560
|
* heap.nodes.forEach((node: IHeapNode, i: number) => {
|
|
@@ -1508,11 +1586,11 @@ export interface IHeapStringNode extends IHeapNode {
|
|
|
1508
1586
|
* ```typescript
|
|
1509
1587
|
* import type {IHeapSnapshot, IHeapNodes} from '@memlab/core';
|
|
1510
1588
|
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1511
|
-
* import {
|
|
1589
|
+
* import {getFullHeapFromFile} from '@memlab/heap-analysis';
|
|
1512
1590
|
*
|
|
1513
1591
|
* (async function () {
|
|
1514
1592
|
* const heapFile = dumpNodeHeapSnapshot();
|
|
1515
|
-
* const heap: IHeapSnapshot = await
|
|
1593
|
+
* const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
|
|
1516
1594
|
*
|
|
1517
1595
|
* const nodes: IHeapNodes = heap.nodes;
|
|
1518
1596
|
* nodes.length;
|
|
@@ -1647,5 +1725,11 @@ export interface IClusterStrategy {
|
|
|
1647
1725
|
diffTraces: (newLeakTraces: LeakTrace[], existingLeakTraces: LeakTrace[]) => TraceDiff;
|
|
1648
1726
|
}
|
|
1649
1727
|
/** @internal */
|
|
1728
|
+
export interface IHeapConfig {
|
|
1729
|
+
isCliInteractiveMode: boolean;
|
|
1730
|
+
currentHeapFile: Optional<string>;
|
|
1731
|
+
currentHeap: Optional<IHeapSnapshot>;
|
|
1732
|
+
}
|
|
1733
|
+
/** @internal */
|
|
1650
1734
|
export declare type ErrorWithMessage = Pick<Error, 'message'>;
|
|
1651
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;
|