@memlab/core 1.1.1 → 1.1.4
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/README.md +3 -2
- 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/lib/Config.d.ts +3 -0
- package/dist/lib/Config.js +13 -0
- package/dist/lib/Console.d.ts +1 -0
- package/dist/lib/Console.js +3 -0
- package/dist/lib/Constant.js +2 -2
- package/dist/lib/FileManager.d.ts +2 -0
- package/dist/lib/FileManager.js +6 -0
- package/dist/lib/HeapParser.js +1 -1
- package/dist/lib/NodeHeap.d.ts +64 -5
- package/dist/lib/NodeHeap.js +64 -3
- package/dist/lib/Serializer.js +4 -1
- package/dist/lib/Types.d.ts +884 -22
- package/dist/lib/Utils.js +3 -2
- package/dist/lib/heap-data/HeapNode.d.ts +1 -1
- package/dist/lib/heap-data/HeapNode.js +1 -1
- package/dist/lib/heap-data/HeapSnapshot.js +7 -1
- package/dist/paths/TraceFinder.js +4 -2
- package/dist/trace-cluster/TraceElement.d.ts +1 -1
- package/dist/trace-cluster/TraceElement.js +2 -2
- package/package.json +2 -1
package/dist/lib/Types.d.ts
CHANGED
|
@@ -89,6 +89,7 @@ export declare type CLIArgs = {
|
|
|
89
89
|
export declare type Cookies = Array<{
|
|
90
90
|
name: string;
|
|
91
91
|
value: string;
|
|
92
|
+
domain?: string;
|
|
92
93
|
}>;
|
|
93
94
|
/** @internal */
|
|
94
95
|
export interface IE2EScenarioSynthesizer {
|
|
@@ -96,7 +97,7 @@ export interface IE2EScenarioSynthesizer {
|
|
|
96
97
|
getOrigin(): Nullable<string>;
|
|
97
98
|
getDomain(): string;
|
|
98
99
|
getDomainPrefixes(): string[];
|
|
99
|
-
getCookieFile(visitPlan: IE2EScenarioVisitPlan): string
|
|
100
|
+
getCookieFile(visitPlan: IE2EScenarioVisitPlan): Nullable<string>;
|
|
100
101
|
getAvailableSteps(): IE2EStepBasic[];
|
|
101
102
|
getNodeNameBlocklist(): string[];
|
|
102
103
|
getEdgeNameBlocklist(): string[];
|
|
@@ -143,16 +144,49 @@ export declare type QuickExperiment = {
|
|
|
143
144
|
group: string;
|
|
144
145
|
};
|
|
145
146
|
/**
|
|
146
|
-
* The
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* const scenario = {
|
|
147
|
+
* The `ILeakFilter` interface allows you to define a leak detector and
|
|
148
|
+
* customize the leak filtering logic in memlab (instead of using the
|
|
149
|
+
* built-in leak filters).
|
|
150
150
|
*
|
|
151
|
-
*
|
|
151
|
+
* Use the leak filter definition in command line interface to filter
|
|
152
|
+
* leaks detected from browser interactions
|
|
153
|
+
* ```bash
|
|
154
|
+
* memlab run --scenario <SCENARIO FILE> --leak-filter <PATH TO leak-filter.js>
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* If you have already run `memlab run` or `memlab snapshot` which saved
|
|
158
|
+
* heap snapshot and other meta data on disk, use the following command
|
|
159
|
+
* to filter leaks based on those saved heap snapshots (query the default
|
|
160
|
+
* data location by `memlab get-default-work-dir`).
|
|
161
|
+
*
|
|
162
|
+
* ```bash
|
|
163
|
+
* memlab find-leaks --leak-filter <PATH TO leak-filter.js>
|
|
164
|
+
* ```
|
|
165
|
+
* Here is an example TypeScript file defining a leak filter.
|
|
166
|
+
* The command line interface only accepts compiled JavaScript file.
|
|
167
|
+
* You can also define the leak filter in JavaScript (without the
|
|
168
|
+
* type annotations.
|
|
169
|
+
*
|
|
170
|
+
* ```typescript
|
|
171
|
+
* import {IHeapNode, IHeapSnapshot, HeapNodeIdSet, utils} from '@memlab/core';
|
|
152
172
|
*
|
|
153
|
-
*
|
|
173
|
+
* function initMap(snapshot: IHeapSnapshot): Record<string, number> {
|
|
174
|
+
* const map = Object.create(null);
|
|
175
|
+
* snapshot.nodes.forEach(node => {
|
|
176
|
+
* if (node.type !== 'string') {
|
|
177
|
+
* return;
|
|
178
|
+
* }
|
|
179
|
+
* const str = utils.getStringNodeValue(node);
|
|
180
|
+
* if (str in map) {
|
|
181
|
+
* ++map[str];
|
|
182
|
+
* } else {
|
|
183
|
+
* map[str] = 1;
|
|
184
|
+
* }
|
|
185
|
+
* });
|
|
186
|
+
* return map;
|
|
187
|
+
* }
|
|
154
188
|
* const beforeLeakFilter = (snapshot: IHeapSnapshot, _leakedNodeIds: HeapNodeIdSet): void => {
|
|
155
|
-
* map =
|
|
189
|
+
* map = initMap(snapshot);
|
|
156
190
|
* };
|
|
157
191
|
*
|
|
158
192
|
* // duplicated string with size > 1KB as memory leak
|
|
@@ -168,7 +202,81 @@ export declare type QuickExperiment = {
|
|
|
168
202
|
* ```
|
|
169
203
|
*/
|
|
170
204
|
export interface ILeakFilter {
|
|
205
|
+
/**
|
|
206
|
+
* Lifecycle function callback that is invoked initially once before
|
|
207
|
+
* the subsequent `leakFilter` function calls. This callback could
|
|
208
|
+
* be used to initialize some data stores or any one-off
|
|
209
|
+
* preprocessings.
|
|
210
|
+
*
|
|
211
|
+
* * **Parameters**:
|
|
212
|
+
* * snapshot: `IHeapSnapshot` | the final heap snapshot taken after
|
|
213
|
+
* all browser interactions are done.
|
|
214
|
+
* Check out {@link IHeapSnapshot} for more APIs that queries the heap snapshot.
|
|
215
|
+
* * leakedNodeIds: `Set<number>` | the set of ids of all JS heap objects
|
|
216
|
+
* allocated by the `action` call but not released after the `back` call
|
|
217
|
+
* in browser.
|
|
218
|
+
*
|
|
219
|
+
* * **Examples**:
|
|
220
|
+
* ```typescript
|
|
221
|
+
* module.exports = {
|
|
222
|
+
* beforeLeakFilter: (snapshot, leakedNodeIds) {
|
|
223
|
+
* // initialize some data stores
|
|
224
|
+
* },
|
|
225
|
+
* leakFilter(node, snapshot, leakedNodeIds) {
|
|
226
|
+
* // use the data stores
|
|
227
|
+
* },
|
|
228
|
+
* };
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
171
231
|
beforeLeakFilter?: InitLeakFilterCallback;
|
|
232
|
+
/**
|
|
233
|
+
* This callback defines how you want to filter out the
|
|
234
|
+
* leaked objects. The callback is called for every node (JS heap
|
|
235
|
+
* object in browser) allocated by the `action` callback, but not
|
|
236
|
+
* released after the `back` callback. Those objects could be caches
|
|
237
|
+
* that are retained in memory on purpose, or they are memory leaks.
|
|
238
|
+
*
|
|
239
|
+
* This optional callback allows you to define your own algorithm
|
|
240
|
+
* to cherry pick memory leaks for specific JS program under test.
|
|
241
|
+
*
|
|
242
|
+
* If this optional callback is not defined, memlab will use its
|
|
243
|
+
* built-in leak filter, which considers detached DOM elements
|
|
244
|
+
* and unmounted Fiber nodes (detached from React Fiber tree) as
|
|
245
|
+
* memory leaks.
|
|
246
|
+
*
|
|
247
|
+
* * **Parameters**:
|
|
248
|
+
* * node: `IHeapNode` | one of the heap object allocated but not released.
|
|
249
|
+
* * snapshot: `IHeapSnapshot` | the final heap snapshot taken after
|
|
250
|
+
* all browser interactions are done.
|
|
251
|
+
* Check out {@link IHeapSnapshot} for more APIs that queries the heap snapshot.
|
|
252
|
+
* * leakedNodeIds: `Set<number>` | the set of ids of all JS heap objects
|
|
253
|
+
* allocated by the `action` call but not released after the `back` call
|
|
254
|
+
* in browser.
|
|
255
|
+
*
|
|
256
|
+
* * **Returns**: the boolean value indicating whether the given node in
|
|
257
|
+
* the snapshot should be considered as leaked.
|
|
258
|
+
*
|
|
259
|
+
*
|
|
260
|
+
* ```javascript
|
|
261
|
+
* // save as leak-filter.js
|
|
262
|
+
* module.exports = {
|
|
263
|
+
* leakFilter(node, snapshot, leakedNodeIds) {
|
|
264
|
+
* // any unreleased node (JS heap object) with 1MB+
|
|
265
|
+
* // retained size is considered a memory leak
|
|
266
|
+
* return node.retainedSize > 1000000;
|
|
267
|
+
* },
|
|
268
|
+
* };
|
|
269
|
+
* ```
|
|
270
|
+
*
|
|
271
|
+
* Use the leak filter definition in command line interface:
|
|
272
|
+
* ```bash
|
|
273
|
+
* memlab find-leaks --leak-filter <PATH TO leak-filter.js>
|
|
274
|
+
* ```
|
|
275
|
+
*
|
|
276
|
+
* ```bash
|
|
277
|
+
* memlab run --scenario <SCENARIO FILE> --leak-filter <PATH TO leak-filter.js>
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
172
280
|
leakFilter: LeakFilterCallback;
|
|
173
281
|
}
|
|
174
282
|
/**
|
|
@@ -217,6 +325,9 @@ export declare type InteractionsCallback = (page: Page, args?: OperationArgs) =>
|
|
|
217
325
|
* url: () => 'https://www.npmjs.com/',
|
|
218
326
|
* action: async () => ... ,
|
|
219
327
|
* back: async () => ... ,
|
|
328
|
+
* cookies: () => ... , // optional
|
|
329
|
+
* repeat: () => ... , // optional
|
|
330
|
+
* ...
|
|
220
331
|
* };
|
|
221
332
|
* ```
|
|
222
333
|
*
|
|
@@ -230,6 +341,9 @@ export declare type InteractionsCallback = (page: Page, args?: OperationArgs) =>
|
|
|
230
341
|
* url: () => 'https://www.facebook.com',
|
|
231
342
|
* action: async () => ... ,
|
|
232
343
|
* back: async () => ... ,
|
|
344
|
+
* cookies: () => ... , // optional
|
|
345
|
+
* repeat: () => ... , // optional
|
|
346
|
+
* ...
|
|
233
347
|
* };
|
|
234
348
|
* const leaks = await run({scenario});
|
|
235
349
|
* })();
|
|
@@ -243,20 +357,30 @@ export interface IScenario {
|
|
|
243
357
|
/**
|
|
244
358
|
* If the page you are running memlab against requires authentication or
|
|
245
359
|
* specific cookie(s) to be set, you can pass them as
|
|
246
|
-
* a list of
|
|
360
|
+
* a list of `<name, value, domain>` tuples.
|
|
361
|
+
*
|
|
362
|
+
* **Note**: please make sure that you provide the correct `domain` field for
|
|
363
|
+
* the cookies tuples. If no `domain` field is specified, memlab will try
|
|
364
|
+
* to fill in a domain based on the `url` callback.
|
|
365
|
+
* For example, when the `domain` field is absent,
|
|
366
|
+
* memlab will auto fill in `.facebook.com` as domain base
|
|
367
|
+
* on the initial page load's url: `https://www.facebook.com/`.
|
|
368
|
+
*
|
|
247
369
|
* @returns cookie list
|
|
248
370
|
* * **Examples**:
|
|
249
371
|
* ```typescript
|
|
250
372
|
* const scenario = {
|
|
251
373
|
* url: () => 'https://www.facebook.com/',
|
|
252
374
|
* cookies: () => [
|
|
253
|
-
* {
|
|
254
|
-
* {
|
|
255
|
-
* {
|
|
256
|
-
* {
|
|
375
|
+
* {name:'cm_j', value: 'none', domain: '.facebook.com'},
|
|
376
|
+
* {name:'datr', value: 'yJvIY...', domain: '.facebook.com'},
|
|
377
|
+
* {name:'c_user', value: '8917...', domain: '.facebook.com'},
|
|
378
|
+
* {name:'xs', value: '95:9WQ...', domain: '.facebook.com'},
|
|
257
379
|
* // ...
|
|
258
380
|
* ],
|
|
259
381
|
* };
|
|
382
|
+
*
|
|
383
|
+
* module.exports = scenario;
|
|
260
384
|
* ```
|
|
261
385
|
*/
|
|
262
386
|
cookies?: () => Cookies;
|
|
@@ -269,6 +393,8 @@ export interface IScenario {
|
|
|
269
393
|
* const scenario = {
|
|
270
394
|
* url: () => 'https://www.npmjs.com/',
|
|
271
395
|
* };
|
|
396
|
+
*
|
|
397
|
+
* module.exports = scenario;
|
|
272
398
|
* ```
|
|
273
399
|
* If a test scenario only specifies the `url` callback (without the `action`
|
|
274
400
|
* callback), memlab will try to detect memory leaks from the initial page
|
|
@@ -297,6 +423,8 @@ export interface IScenario {
|
|
|
297
423
|
* await page.click('a[href="/back"]');
|
|
298
424
|
* },
|
|
299
425
|
* }
|
|
426
|
+
*
|
|
427
|
+
* module.exports = scenario;
|
|
300
428
|
* ```
|
|
301
429
|
* Note: always clean up external puppeteer references to JS objects
|
|
302
430
|
* in the browser context.
|
|
@@ -314,6 +442,8 @@ export interface IScenario {
|
|
|
314
442
|
* },
|
|
315
443
|
* back: async (page) => ... ,
|
|
316
444
|
* }
|
|
445
|
+
*
|
|
446
|
+
* module.exports = scenario;
|
|
317
447
|
```
|
|
318
448
|
*/
|
|
319
449
|
action?: InteractionsCallback;
|
|
@@ -354,7 +484,7 @@ export interface IScenario {
|
|
|
354
484
|
repeat?: () => number;
|
|
355
485
|
/**
|
|
356
486
|
* Optional callback function that checks if the web page is loaded
|
|
357
|
-
*
|
|
487
|
+
* for the initial page load and subsequent browser interactions.
|
|
358
488
|
*
|
|
359
489
|
* If this callback is not provided, memlab by default
|
|
360
490
|
* considers a navigation to be finished when there are no network
|
|
@@ -390,7 +520,7 @@ export interface IScenario {
|
|
|
390
520
|
/**
|
|
391
521
|
* Lifecycle function callback that is invoked initially once before
|
|
392
522
|
* the subsequent `leakFilter` function calls. This callback could
|
|
393
|
-
* be used to initialize some data stores or to
|
|
523
|
+
* be used to initialize some data stores or to any one-off
|
|
394
524
|
* preprocessings.
|
|
395
525
|
*
|
|
396
526
|
* * **Parameters**:
|
|
@@ -415,7 +545,7 @@ export interface IScenario {
|
|
|
415
545
|
*/
|
|
416
546
|
beforeLeakFilter?: InitLeakFilterCallback;
|
|
417
547
|
/**
|
|
418
|
-
* This callback
|
|
548
|
+
* This callback defines how you want to filter out the
|
|
419
549
|
* leaked objects. The callback is called for every node (JS heap
|
|
420
550
|
* object in browser) allocated by the `action` callback, but not
|
|
421
551
|
* released after the `back` callback. Those objects could be caches
|
|
@@ -606,86 +736,818 @@ export declare type RunMetaInfo = {
|
|
|
606
736
|
type: string;
|
|
607
737
|
browserInfo: IBrowserInfo;
|
|
608
738
|
};
|
|
739
|
+
/**
|
|
740
|
+
* A heap snapshot is generally a graph where graph nodes are JS heap objects
|
|
741
|
+
* and graph edges are JS references among JS heap objects. For more details
|
|
742
|
+
* on the structure of nodes and edges in the heap graph, check out
|
|
743
|
+
* {@link IHeapNode} and {@link IHeapEdge}.
|
|
744
|
+
*/
|
|
609
745
|
export interface IHeapSnapshot {
|
|
746
|
+
/** @internal */
|
|
610
747
|
snapshot: RawHeapSnapshot;
|
|
748
|
+
/**
|
|
749
|
+
* A pseudo array containing all heap graph nodes (JS objects in heap).
|
|
750
|
+
* A JS heap could contain millions of heap objects, so memlab uses
|
|
751
|
+
* a pseudo array as the collection of all the heap objects. The pseudo
|
|
752
|
+
* array provides API to query and traverse all heap objects.
|
|
753
|
+
*
|
|
754
|
+
* * **Examples**:
|
|
755
|
+
* ```typescript
|
|
756
|
+
* import type {IHeapSnapshot, IHeapNode} from '@memlab/core';
|
|
757
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
758
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
759
|
+
*
|
|
760
|
+
* (async function () {
|
|
761
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
762
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
763
|
+
*
|
|
764
|
+
* // get the total number of heap objects
|
|
765
|
+
* heap.nodes.length;
|
|
766
|
+
*
|
|
767
|
+
* heap.nodes.forEach((node: IHeapNode) => {
|
|
768
|
+
* // traverse each heap object
|
|
769
|
+
* });
|
|
770
|
+
* })();
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
611
773
|
nodes: IHeapNodes;
|
|
774
|
+
/**
|
|
775
|
+
* A pseudo array containing all heap graph edges (references to heap objects
|
|
776
|
+
* in heap). A JS heap could contain millions of references, so memlab uses
|
|
777
|
+
* a pseudo array as the collection of all the heap edges. The pseudo
|
|
778
|
+
* array provides API to query and traverse all heap references.
|
|
779
|
+
*
|
|
780
|
+
* * **Examples**:
|
|
781
|
+
* ```typescript
|
|
782
|
+
* import type {IHeapSnapshot, IHeapEdge} from '@memlab/core';
|
|
783
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
784
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
785
|
+
*
|
|
786
|
+
* (async function () {
|
|
787
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
788
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
789
|
+
*
|
|
790
|
+
* // get the total number of heap references
|
|
791
|
+
* heap.edges.length;
|
|
792
|
+
*
|
|
793
|
+
* heap.edges.forEach((edge: IHeapEdge) => {
|
|
794
|
+
* // traverse each reference in the heap
|
|
795
|
+
* });
|
|
796
|
+
* })();
|
|
797
|
+
* ```
|
|
798
|
+
*/
|
|
612
799
|
edges: IHeapEdges;
|
|
800
|
+
/**
|
|
801
|
+
* If you have the id of a heap node (JS object in heap), use this API
|
|
802
|
+
* to get an {@link IHeapNode} associated with the id.
|
|
803
|
+
* @param id id of the heap node (JS object in heap) you would like to query
|
|
804
|
+
* @returns the API returns `null` if no heap object has the specified id.
|
|
805
|
+
*
|
|
806
|
+
* * **Examples**:
|
|
807
|
+
* ```typescript
|
|
808
|
+
* import type {IHeapSnapshot} from '@memlab/core';
|
|
809
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
810
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
811
|
+
*
|
|
812
|
+
* (async function () {
|
|
813
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
814
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
815
|
+
*
|
|
816
|
+
* const node = heap.getNodeById(351);
|
|
817
|
+
* node?.id; // should be 351
|
|
818
|
+
* })();
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
613
821
|
getNodeById(id: number): Nullable<IHeapNode>;
|
|
614
|
-
|
|
822
|
+
/**
|
|
823
|
+
* Search for the heap and check if there is any JS object instance with
|
|
824
|
+
* a specified constructor name.
|
|
825
|
+
* @param className The contructor name of the object instance
|
|
826
|
+
* @returns `true` if there is at least one such object in the heap
|
|
827
|
+
*
|
|
828
|
+
* * **Examples**: you can write a jest unit test with memory assertions:
|
|
829
|
+
* ```typescript
|
|
830
|
+
* // save as example.test.ts
|
|
831
|
+
* import type {IHeapSnapshot, Nullable} from '@memlab/core';
|
|
832
|
+
* import {config, getNodeInnocentHeap} from '@memlab/core';
|
|
833
|
+
*
|
|
834
|
+
* class TestObject {
|
|
835
|
+
* public arr1 = [1, 2, 3];
|
|
836
|
+
* public arr2 = ['1', '2', '3'];
|
|
837
|
+
* }
|
|
838
|
+
*
|
|
839
|
+
* test('memory test with heap assertion', async () => {
|
|
840
|
+
* config.muteConsole = true; // no console output
|
|
841
|
+
*
|
|
842
|
+
* let obj: Nullable<TestObject> = new TestObject();
|
|
843
|
+
* // get a heap snapshot of the current program state
|
|
844
|
+
* let heap: IHeapSnapshot = await getNodeInnocentHeap();
|
|
845
|
+
*
|
|
846
|
+
* // call some function that may add references to obj
|
|
847
|
+
* rabbitHole(obj)
|
|
848
|
+
*
|
|
849
|
+
* expect(heap.hasObjectWithClassName('TestObject')).toBe(true);
|
|
850
|
+
* obj = null;
|
|
851
|
+
*
|
|
852
|
+
* heap = await getNodeInnocentHeap();
|
|
853
|
+
* // if rabbitHole does not have any side effect that
|
|
854
|
+
* // adds new references to obj, then obj can be GCed
|
|
855
|
+
* expect(heap.hasObjectWithClassName('TestObject')).toBe(false);
|
|
856
|
+
*
|
|
857
|
+
* }, 30000);
|
|
858
|
+
* ```
|
|
859
|
+
*/
|
|
615
860
|
hasObjectWithClassName(className: string): boolean;
|
|
861
|
+
/**
|
|
862
|
+
* Search for the heap and get one of the JS object instances with
|
|
863
|
+
* a specified constructor name (if there is any).
|
|
864
|
+
* @param className The contructor name of the object instance
|
|
865
|
+
* @returns a handle pointing to any one of the object instances, returns
|
|
866
|
+
* `null` if no such object exists in the heap.
|
|
867
|
+
*
|
|
868
|
+
* * **Examples**:
|
|
869
|
+
* ```typescript
|
|
870
|
+
* import type {IHeapSnapshot} from '@memlab/core';
|
|
871
|
+
* import {getNodeInnocentHeap} from '@memlab/core';
|
|
872
|
+
*
|
|
873
|
+
* class TestObject {
|
|
874
|
+
* public arr1 = [1, 2, 3];
|
|
875
|
+
* public arr2 = ['1', '2', '3'];
|
|
876
|
+
* }
|
|
877
|
+
*
|
|
878
|
+
* (async function () {
|
|
879
|
+
* const obj = new TestObject();
|
|
880
|
+
* // get a heap snapshot of the current program state
|
|
881
|
+
* const heap: IHeapSnapshot = await getNodeInnocentHeap();
|
|
882
|
+
*
|
|
883
|
+
* const node = heap.getAnyObjectWithClassName('TestObject');
|
|
884
|
+
* console.log(node?.name); // should be 'TestObject'
|
|
885
|
+
* })();
|
|
886
|
+
* ```
|
|
887
|
+
*/
|
|
616
888
|
getAnyObjectWithClassName(className: string): Nullable<IHeapNode>;
|
|
889
|
+
/**
|
|
890
|
+
* Search for the heap and check if there is any JS object instance with
|
|
891
|
+
* a specified property name.
|
|
892
|
+
* @param nameOrIndex The property name (string) or element index (number)
|
|
893
|
+
* on the object instance
|
|
894
|
+
* @returns returns `true` if there is at least one such object in the heap
|
|
895
|
+
*
|
|
896
|
+
* * **Examples**:
|
|
897
|
+
* ```typescript
|
|
898
|
+
* import type {IHeapSnapshot} from '@memlab/core';
|
|
899
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
900
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
901
|
+
*
|
|
902
|
+
* (async function () {
|
|
903
|
+
* // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
904
|
+
* const object = {'memlab-test-heap-property': 'memlab-test-heap-value'};
|
|
905
|
+
*
|
|
906
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
907
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
908
|
+
*
|
|
909
|
+
* // should be true
|
|
910
|
+
* console.log(heap.hasObjectWithPropertyName('memlab-test-heap-property'));
|
|
911
|
+
* })();
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
617
914
|
hasObjectWithPropertyName(nameOrIndex: string | number): boolean;
|
|
915
|
+
/**
|
|
916
|
+
* Search for the heap and check if there is any JS object instance with
|
|
917
|
+
* a marker tagged by {@link tagObject}.
|
|
918
|
+
*
|
|
919
|
+
* The `tagObject` API does not modify the object instance in any way
|
|
920
|
+
* (e.g., no additional or hidden properties added to the tagged object).
|
|
921
|
+
*
|
|
922
|
+
* @param tag marker name on the object instances tagged by {@link tagObject}
|
|
923
|
+
* @returns returns `true` if there is at least one such object in the heap
|
|
924
|
+
*
|
|
925
|
+
* ```typescript
|
|
926
|
+
* import type {IHeapSnapshot, AnyValue} from '@memlab/core';
|
|
927
|
+
* import {config, getNodeInnocentHeap, tagObject} from '@memlab/core';
|
|
928
|
+
*
|
|
929
|
+
* test('memory test', async () => {
|
|
930
|
+
* config.muteConsole = true;
|
|
931
|
+
* const o1: AnyValue = {};
|
|
932
|
+
* let o2: AnyValue = {};
|
|
933
|
+
*
|
|
934
|
+
* // tag o1 with marker: "memlab-mark-1", does not modify o1 in any way
|
|
935
|
+
* tagObject(o1, 'memlab-mark-1');
|
|
936
|
+
* // tag o2 with marker: "memlab-mark-2", does not modify o2 in any way
|
|
937
|
+
* tagObject(o2, 'memlab-mark-2');
|
|
938
|
+
*
|
|
939
|
+
* o2 = null;
|
|
940
|
+
*
|
|
941
|
+
* const heap: IHeapSnapshot = await getNodeInnocentHeap();
|
|
942
|
+
*
|
|
943
|
+
* // expect object with marker "memlab-mark-1" exists
|
|
944
|
+
* expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
|
|
945
|
+
*
|
|
946
|
+
* // expect object with marker "memlab-mark-2" can be GCed
|
|
947
|
+
* expect(heap.hasObjectWithTag('memlab-mark-2')).toBe(false);
|
|
948
|
+
*
|
|
949
|
+
* }, 30000);
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
618
952
|
hasObjectWithTag(tag: string): boolean;
|
|
953
|
+
/** @internal */
|
|
954
|
+
clearShortestPathInfo(): void;
|
|
619
955
|
}
|
|
956
|
+
/**
|
|
957
|
+
* An `IHeapLocation` instance contains a source location information
|
|
958
|
+
* associated with a JS heap object.
|
|
959
|
+
* A heap snapshot is generally a graph where graph nodes are JS heap objects
|
|
960
|
+
* and graph edges are JS references among JS heap objects.
|
|
961
|
+
*
|
|
962
|
+
* @readonly it is not recommended to modify any `IHeapLocation` instance
|
|
963
|
+
*
|
|
964
|
+
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
965
|
+
* {@link getHeapFromFile} API.
|
|
966
|
+
*
|
|
967
|
+
* ```typescript
|
|
968
|
+
* import type {IHeapSnapshot, IHeapNode, IHeapLocation} from '@memlab/core';
|
|
969
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
970
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
971
|
+
*
|
|
972
|
+
* (async function () {
|
|
973
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
974
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
975
|
+
*
|
|
976
|
+
* // iterate over each node (heap object)
|
|
977
|
+
* heap.nodes.forEach((node: IHeapNode, i: number) => {
|
|
978
|
+
* const location: Nullable<IHeapLocation> = node.location;
|
|
979
|
+
* if (location) {
|
|
980
|
+
* // use the location API here
|
|
981
|
+
* location.line;
|
|
982
|
+
* // ...
|
|
983
|
+
* }
|
|
984
|
+
* });
|
|
985
|
+
* })();
|
|
986
|
+
* ```
|
|
987
|
+
*/
|
|
620
988
|
export interface IHeapLocation {
|
|
989
|
+
/**
|
|
990
|
+
* get the {@link IHeapSnapshot} containing this location instance
|
|
991
|
+
*/
|
|
621
992
|
snapshot: IHeapSnapshot;
|
|
993
|
+
/**
|
|
994
|
+
* get the script ID of the source file
|
|
995
|
+
*/
|
|
622
996
|
script_id: number;
|
|
997
|
+
/**
|
|
998
|
+
* get the line number
|
|
999
|
+
*/
|
|
623
1000
|
line: number;
|
|
1001
|
+
/**
|
|
1002
|
+
* get the column number
|
|
1003
|
+
*/
|
|
624
1004
|
column: number;
|
|
625
1005
|
}
|
|
1006
|
+
/** @internal */
|
|
626
1007
|
export interface IHeapEdgeBasic {
|
|
1008
|
+
/**
|
|
1009
|
+
* name of the JS reference. If this is a reference to an array element
|
|
1010
|
+
* or internal table element, it is an numeric index
|
|
1011
|
+
*/
|
|
627
1012
|
name_or_index: number | string;
|
|
1013
|
+
/**
|
|
1014
|
+
* type of the JS reference, all types:
|
|
1015
|
+
* `context`, `element`, `property`, `internal`, `hidden`, `shortcut`, `weak`
|
|
1016
|
+
*/
|
|
628
1017
|
type: string;
|
|
629
1018
|
}
|
|
1019
|
+
/**
|
|
1020
|
+
* An `IHeapEdge` instance represents a JS reference in a heap snapshot.
|
|
1021
|
+
* A heap snapshot is generally a graph where graph nodes are JS heap objects
|
|
1022
|
+
* and graph edges are JS references among JS heap objects.
|
|
1023
|
+
*
|
|
1024
|
+
* @readonly it is not recommended to modify any `IHeapEdge` instance
|
|
1025
|
+
*
|
|
1026
|
+
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1027
|
+
* {@link getHeapFromFile} API.
|
|
1028
|
+
*
|
|
1029
|
+
* ```typescript
|
|
1030
|
+
* import type {IHeapSnapshot, IHeapEdge} from '@memlab/core';
|
|
1031
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1032
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
1033
|
+
*
|
|
1034
|
+
* (async function () {
|
|
1035
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
1036
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
1037
|
+
*
|
|
1038
|
+
* // iterate over each edge (JS reference in heap)
|
|
1039
|
+
* heap.edges.forEach((edge: IHeapEdge, i: number) => {
|
|
1040
|
+
* // use the heap edge APIs here
|
|
1041
|
+
* const nameOrIndex = edge.name_or_index;
|
|
1042
|
+
* // ...
|
|
1043
|
+
* });
|
|
1044
|
+
* })();
|
|
1045
|
+
* ```
|
|
1046
|
+
*/
|
|
630
1047
|
export interface IHeapEdge extends IHeapEdgeBasic {
|
|
1048
|
+
/**
|
|
1049
|
+
* get the {@link IHeapSnapshot} containing this JS reference
|
|
1050
|
+
*/
|
|
631
1051
|
snapshot: IHeapSnapshot;
|
|
1052
|
+
/**
|
|
1053
|
+
* index of this JS reference inside the `edge.snapshot.edges` pseudo array
|
|
1054
|
+
*/
|
|
632
1055
|
edgeIndex: number;
|
|
1056
|
+
/**
|
|
1057
|
+
* if `true`, means this is a reference to an array element
|
|
1058
|
+
* or internal table element (`edge.name_or_index` will return a number),
|
|
1059
|
+
* otherwise this is a reference with a string name (`edge.name_or_index`
|
|
1060
|
+
* will return a string)
|
|
1061
|
+
*/
|
|
633
1062
|
is_index: boolean;
|
|
1063
|
+
/**
|
|
1064
|
+
* the index of the JS heap object pointed to by this reference
|
|
1065
|
+
*/
|
|
634
1066
|
to_node: number;
|
|
1067
|
+
/**
|
|
1068
|
+
* returns an {@link IHeapNode} instance representing the JS heap object
|
|
1069
|
+
* pointed to by this reference
|
|
1070
|
+
*/
|
|
635
1071
|
toNode: IHeapNode;
|
|
1072
|
+
/**
|
|
1073
|
+
* returns an {@link IHeapNode} instance representing the hosting
|
|
1074
|
+
* JS heap object where this reference starts
|
|
1075
|
+
*/
|
|
636
1076
|
fromNode: IHeapNode;
|
|
637
1077
|
}
|
|
1078
|
+
/**
|
|
1079
|
+
* A pseudo array containing all heap graph edges (references to heap objects
|
|
1080
|
+
* in heap). A JS heap could contain millions of references, so memlab uses
|
|
1081
|
+
* a pseudo array as the collection of all the heap edges. The pseudo
|
|
1082
|
+
* array provides API to query and traverse all heap references.
|
|
1083
|
+
*
|
|
1084
|
+
* @readonly modifying this pseudo array is not recommended
|
|
1085
|
+
*
|
|
1086
|
+
* * **Examples**:
|
|
1087
|
+
* ```typescript
|
|
1088
|
+
* import type {IHeapSnapshot, IHeapEdges} from '@memlab/core';
|
|
1089
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1090
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
1091
|
+
*
|
|
1092
|
+
* (async function () {
|
|
1093
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
1094
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
1095
|
+
*
|
|
1096
|
+
* const edges: IHeapEdges = heap.edges;
|
|
1097
|
+
* edges.length;
|
|
1098
|
+
* edges.get(0);
|
|
1099
|
+
* edges.forEach((edge, i) => {
|
|
1100
|
+
* if (stopIteration) {
|
|
1101
|
+
* return false;
|
|
1102
|
+
* }
|
|
1103
|
+
* });
|
|
1104
|
+
* })();
|
|
1105
|
+
* ```
|
|
1106
|
+
*/
|
|
638
1107
|
export interface IHeapEdges {
|
|
1108
|
+
/**
|
|
1109
|
+
* The total number of edges in heap graph (or JS references in heap
|
|
1110
|
+
* snapshot).
|
|
1111
|
+
*/
|
|
639
1112
|
length: number;
|
|
640
|
-
|
|
1113
|
+
/**
|
|
1114
|
+
* get an {@link IHeapEdge} element at the specified index
|
|
1115
|
+
* @param index the index of an element in the pseudo array, the index ranges
|
|
1116
|
+
* from 0 to array length - 1. Notice that this is not the heap node id.
|
|
1117
|
+
* @returns When 0 <= `index` < array.length, this API returns the element
|
|
1118
|
+
* at the specified index, otherwise it returns `null`.
|
|
1119
|
+
*/
|
|
1120
|
+
get(index: number): Nullable<IHeapEdge>;
|
|
1121
|
+
/**
|
|
1122
|
+
* Iterate over all array elements and apply the callback
|
|
1123
|
+
* to each element in ascending order of element index.
|
|
1124
|
+
* @param callback the callback does not need to return any value, if
|
|
1125
|
+
* the callback returns `false` when iterating on element at index `i`,
|
|
1126
|
+
* then all elements after `i` won't be iterated.
|
|
1127
|
+
*/
|
|
641
1128
|
forEach(callback: (edge: IHeapEdge, index: number) => void | boolean): void;
|
|
642
1129
|
}
|
|
1130
|
+
/** @internal */
|
|
643
1131
|
export interface IHeapNodeBasic {
|
|
1132
|
+
/**
|
|
1133
|
+
* the type of the heap node object. All possible types:
|
|
1134
|
+
* This is engine-specific, for example all types in V8:
|
|
1135
|
+
* `hidden`, `array`, `string`, `object`, `code`, `closure`, `regexp`,
|
|
1136
|
+
* `number`, `native`, `synthetic`, `concatenated string`, `sliced string`,
|
|
1137
|
+
* `symbol`, `bigint`
|
|
1138
|
+
*/
|
|
644
1139
|
type: string;
|
|
1140
|
+
/**
|
|
1141
|
+
* this is the `name` field associated with the heap object,
|
|
1142
|
+
* for JS object instances (type `object`), `name` is the constructor's name
|
|
1143
|
+
* of the object instance. for `string`, `name` is the string value.
|
|
1144
|
+
*/
|
|
645
1145
|
name: string;
|
|
1146
|
+
/**
|
|
1147
|
+
* unique id of the heap object
|
|
1148
|
+
*/
|
|
646
1149
|
id: number;
|
|
647
1150
|
}
|
|
648
1151
|
export declare type EdgeIterationCallback = (edge: IHeapEdge) => Optional<{
|
|
649
1152
|
stop: boolean;
|
|
650
1153
|
}>;
|
|
1154
|
+
/**
|
|
1155
|
+
* An `IHeapNode` instance represents a JS heap object in a heap snapshot.
|
|
1156
|
+
* A heap snapshot is generally a graph where graph nodes are JS heap objects
|
|
1157
|
+
* and graph edges are JS references among JS heap objects.
|
|
1158
|
+
*
|
|
1159
|
+
* @readonly it is not recommended to modify any `IHeapNode` instance
|
|
1160
|
+
*
|
|
1161
|
+
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1162
|
+
* {@link getHeapFromFile} API.
|
|
1163
|
+
*
|
|
1164
|
+
* ```typescript
|
|
1165
|
+
* import type {IHeapSnapshot, IHeapNode} from '@memlab/core';
|
|
1166
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1167
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
1168
|
+
*
|
|
1169
|
+
* (async function () {
|
|
1170
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
1171
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
1172
|
+
*
|
|
1173
|
+
* // iterate over each node (heap object)
|
|
1174
|
+
* heap.nodes.forEach((node: IHeapNode, i: number) => {
|
|
1175
|
+
* // use the heap node APIs here
|
|
1176
|
+
* const id = node.id;
|
|
1177
|
+
* const type = node.type;
|
|
1178
|
+
* // ...
|
|
1179
|
+
* });
|
|
1180
|
+
* })();
|
|
1181
|
+
* ```
|
|
1182
|
+
*/
|
|
651
1183
|
export interface IHeapNode extends IHeapNodeBasic {
|
|
1184
|
+
/**
|
|
1185
|
+
* get the {@link IHeapSnapshot} containing this heap object
|
|
1186
|
+
*/
|
|
652
1187
|
snapshot: IHeapSnapshot;
|
|
1188
|
+
/**
|
|
1189
|
+
* * If the heap object is a DOM element and the DOM element is detached
|
|
1190
|
+
* from the DOM tree, `is_detached` will be `true`;
|
|
1191
|
+
* * If the heap object is a React Fiber node and the Fiber node is unmounted
|
|
1192
|
+
* from the React Fiber tree, `is_detached` will be `true`;
|
|
1193
|
+
* otherwise it will be `false`
|
|
1194
|
+
*/
|
|
653
1195
|
is_detached: boolean;
|
|
1196
|
+
/** @internal */
|
|
654
1197
|
detachState: number;
|
|
1198
|
+
/** @internal */
|
|
655
1199
|
markAsDetached(): void;
|
|
1200
|
+
/** @internal */
|
|
656
1201
|
attributes: number;
|
|
1202
|
+
/**
|
|
1203
|
+
* The *shallow size* of the heap object (i.e., the size of memory that is held
|
|
1204
|
+
* by the object itself.). For difference between **shallow size** and
|
|
1205
|
+
* **retained size**, check out
|
|
1206
|
+
* [this doc](https://developer.chrome.com/docs/devtools/memory-problems/memory-101/#object_sizes).
|
|
1207
|
+
*/
|
|
657
1208
|
self_size: number;
|
|
1209
|
+
/**
|
|
1210
|
+
* The total number of outgoing JS references (including engine-internal,
|
|
1211
|
+
* native, and JS references).
|
|
1212
|
+
*/
|
|
658
1213
|
edge_count: number;
|
|
1214
|
+
/** @internal */
|
|
659
1215
|
trace_node_id: number;
|
|
1216
|
+
/**
|
|
1217
|
+
* Get a JS array containing all outgoing JS references from this heap object
|
|
1218
|
+
* (including engine-internal, native, and JS references).
|
|
1219
|
+
*/
|
|
660
1220
|
references: IHeapEdge[];
|
|
1221
|
+
/**
|
|
1222
|
+
* Get a JS array containing all incoming JS references pointing to this heap
|
|
1223
|
+
* object (including engine-internal, native, and JS references).
|
|
1224
|
+
*/
|
|
661
1225
|
referrers: IHeapEdge[];
|
|
1226
|
+
/**
|
|
1227
|
+
* The incoming edge which leads to the parent node
|
|
1228
|
+
* on the shortest path to GC root.
|
|
1229
|
+
*/
|
|
662
1230
|
pathEdge: IHeapEdge | null;
|
|
1231
|
+
/**
|
|
1232
|
+
* index of this heap object inside the `node.snapshot.nodes` pseudo array
|
|
1233
|
+
*/
|
|
663
1234
|
nodeIndex: number;
|
|
1235
|
+
/**
|
|
1236
|
+
* The *retained size* of the heap object (i.e., the total size of memory that
|
|
1237
|
+
* could be released if this object is released). For difference between
|
|
1238
|
+
* **retained size** and **shallow size**, check out
|
|
1239
|
+
* [this doc](https://developer.chrome.com/docs/devtools/memory-problems/memory-101/#object_sizes).
|
|
1240
|
+
*/
|
|
664
1241
|
retainedSize: number;
|
|
665
|
-
|
|
666
|
-
|
|
1242
|
+
/**
|
|
1243
|
+
* get the dominator node of this node. If the dominator node gets released
|
|
1244
|
+
* there will be no path from GC to this node, and therefore this node can
|
|
1245
|
+
* also be released.
|
|
1246
|
+
* For more information on what a dominator node is, please check out
|
|
1247
|
+
* [this doc](https://developer.chrome.com/docs/devtools/memory-problems/memory-101/#dominators).
|
|
1248
|
+
*/
|
|
1249
|
+
dominatorNode: Nullable<IHeapNode>;
|
|
1250
|
+
/**
|
|
1251
|
+
* source location information of this heap object (if it is recorded by
|
|
1252
|
+
* the heap snapshot).
|
|
1253
|
+
*/
|
|
1254
|
+
location: Nullable<IHeapLocation>;
|
|
1255
|
+
/** @internal */
|
|
667
1256
|
highlight?: boolean;
|
|
1257
|
+
/**
|
|
1258
|
+
* check if this a string node (normal string node, concatenated string node
|
|
1259
|
+
* or sliced string node)
|
|
1260
|
+
*/
|
|
668
1261
|
isString: boolean;
|
|
1262
|
+
/**
|
|
1263
|
+
* convert to an {@link IHeapStringNode} object if this node is a string node.
|
|
1264
|
+
* The {@link IHeapStringNode} object supports querying the string content
|
|
1265
|
+
* inside the string node.
|
|
1266
|
+
*/
|
|
669
1267
|
toStringNode(): Nullable<IHeapStringNode>;
|
|
1268
|
+
/**
|
|
1269
|
+
* executes a provided callback once for each JavaScript reference in the
|
|
1270
|
+
* hosting node (or outgoing edges from the node)
|
|
1271
|
+
* @param callback the callback for each outgoing JavaScript reference
|
|
1272
|
+
* @returns this API returns void
|
|
1273
|
+
*
|
|
1274
|
+
* * **Examples**:
|
|
1275
|
+
* ```typescript
|
|
1276
|
+
* node.forEachReference((edge: IHeapEdge) => {
|
|
1277
|
+
* // process edge ...
|
|
1278
|
+
*
|
|
1279
|
+
* // if no need to iterate over remaining edges after
|
|
1280
|
+
* // the current edge in the node.references list
|
|
1281
|
+
* return {stop: true};
|
|
1282
|
+
* });
|
|
1283
|
+
* ```
|
|
1284
|
+
*/
|
|
670
1285
|
forEachReference(callback: EdgeIterationCallback): void;
|
|
1286
|
+
/**
|
|
1287
|
+
* executes a provided callback once for each JavaScript reference pointing
|
|
1288
|
+
* to the hosting node (or incoming edges to the node)
|
|
1289
|
+
* @param callback the callback for each incoming JavaScript reference
|
|
1290
|
+
* @returns this API returns void
|
|
1291
|
+
*
|
|
1292
|
+
* * **Examples**:
|
|
1293
|
+
* ```typescript
|
|
1294
|
+
* node.forEachReferrer((edge: IHeapEdge) => {
|
|
1295
|
+
* // process edge ...
|
|
1296
|
+
*
|
|
1297
|
+
* // if no need to iterate over remaining edges after
|
|
1298
|
+
* // the current edge in the node.referrers list
|
|
1299
|
+
* return {stop: true};
|
|
1300
|
+
* });
|
|
1301
|
+
* ```
|
|
1302
|
+
*/
|
|
671
1303
|
forEachReferrer(callback: EdgeIterationCallback): void;
|
|
672
|
-
|
|
1304
|
+
/**
|
|
1305
|
+
* executes a provided predicate callback once for each JavaScript reference
|
|
1306
|
+
* in the hosting node (or outgoing edges from the node) until the predicate
|
|
1307
|
+
* returns `true`
|
|
1308
|
+
* @param predicate the callback for each outgoing JavaScript reference
|
|
1309
|
+
* @returns the first outgoing edge for which the predicate returns `true`,
|
|
1310
|
+
* otherwise returns `null` if no such edge is found.
|
|
1311
|
+
*
|
|
1312
|
+
* * **Examples**:
|
|
1313
|
+
* ```typescript
|
|
1314
|
+
* const reference = node.findAnyReference((edge: IHeapEdge) => {
|
|
1315
|
+
* // find the outgoing reference with name "ref"
|
|
1316
|
+
* return edge.name_or_index === 'ref';
|
|
1317
|
+
* });
|
|
1318
|
+
* ```
|
|
1319
|
+
*/
|
|
1320
|
+
findAnyReference: (predicate: Predicator<IHeapEdge>) => Nullable<IHeapEdge>;
|
|
1321
|
+
/**
|
|
1322
|
+
* executes a provided predicate callback once for each JavaScript reference
|
|
1323
|
+
* pointing to the hosting node (or incoming edges to the node) until the
|
|
1324
|
+
* predicate returns `true`
|
|
1325
|
+
* @param predicate the callback for each incoming JavaScript reference
|
|
1326
|
+
* @returns the first incoming edge for which the predicate returns `true`,
|
|
1327
|
+
* otherwise returns `null` if no such edge is found.
|
|
1328
|
+
*
|
|
1329
|
+
* * **Examples**:
|
|
1330
|
+
* ```typescript
|
|
1331
|
+
* const referrer = node.findAnyReferrer((edge: IHeapEdge) => {
|
|
1332
|
+
* // find the incoming reference with name "ref"
|
|
1333
|
+
* return edge.name_or_index === 'ref';
|
|
1334
|
+
* });
|
|
1335
|
+
* ```
|
|
1336
|
+
*/
|
|
673
1337
|
findAnyReferrer: (predicate: Predicator<IHeapEdge>) => Nullable<IHeapEdge>;
|
|
1338
|
+
/**
|
|
1339
|
+
* executes a provided predicate callback once for each JavaScript reference
|
|
1340
|
+
* pointing to the hosting node (or incoming edges to the node)
|
|
1341
|
+
* @param predicate the callback for each incoming JavaScript reference
|
|
1342
|
+
* @returns an array containing all the incoming edges for which the
|
|
1343
|
+
* predicate returns `true`, otherwise returns an empty array if no such
|
|
1344
|
+
* edge is found.
|
|
1345
|
+
*
|
|
1346
|
+
* * **Examples**:
|
|
1347
|
+
* ```typescript
|
|
1348
|
+
* const referrers = node.findReferrers((edge: IHeapEdge) => {
|
|
1349
|
+
* // find all the incoming references with name "ref"
|
|
1350
|
+
* return edge.name_or_index === 'ref';
|
|
1351
|
+
* });
|
|
1352
|
+
* ```
|
|
1353
|
+
*/
|
|
674
1354
|
findReferrers: (predicate: Predicator<IHeapEdge>) => IHeapEdge[];
|
|
1355
|
+
/**
|
|
1356
|
+
* Given a JS reference's name and type, this API finds an outgoing JS
|
|
1357
|
+
* reference from the hosting node.
|
|
1358
|
+
* @param edgeName the name of the outgoing JavaScript reference
|
|
1359
|
+
* @param edgeType optional parameter specifying the type of the outgoing
|
|
1360
|
+
* JavaScript reference
|
|
1361
|
+
* @returns the outgoing edge that meets the specification
|
|
1362
|
+
*
|
|
1363
|
+
* * **Examples**:
|
|
1364
|
+
* ```typescript
|
|
1365
|
+
* // find the internal reference to node's hidden class
|
|
1366
|
+
* const reference = node.getReference('map', 'hidden');
|
|
1367
|
+
* ```
|
|
1368
|
+
*/
|
|
675
1369
|
getReference: (edgeName: string | number, edgeType?: string) => Nullable<IHeapEdge>;
|
|
1370
|
+
/**
|
|
1371
|
+
* Given a JS reference's name and type, this API finds the outgoing JS
|
|
1372
|
+
* reference from the hosting node, and returns the JS heap object pointed to
|
|
1373
|
+
* by the outgoing JS reference.
|
|
1374
|
+
* @param edgeName the name of the outgoing JavaScript reference
|
|
1375
|
+
* @param edgeType optional parameter specifying the type of the outgoing
|
|
1376
|
+
* JavaScript reference
|
|
1377
|
+
* @returns the node pointed to by the outgoing reference that meets
|
|
1378
|
+
* the specification
|
|
1379
|
+
*
|
|
1380
|
+
* * **Examples**:
|
|
1381
|
+
* ```typescript
|
|
1382
|
+
* // find the node's hidden class
|
|
1383
|
+
* const hiddenClassNode = node.getReferenceNode('map', 'hidden');
|
|
1384
|
+
* // this is equivalent to
|
|
1385
|
+
* const hiddenClassNode2 = node.getReference('map', 'hidden')?.toNode;
|
|
1386
|
+
* ```
|
|
1387
|
+
*/
|
|
676
1388
|
getReferenceNode: (edgeName: string | number, edgeType?: string) => Nullable<IHeapNode>;
|
|
1389
|
+
/**
|
|
1390
|
+
* Given a JS reference's name and type, this API finds an incoming JS
|
|
1391
|
+
* reference pointing to the hosting node.
|
|
1392
|
+
* @param edgeName the name of the incoming JavaScript reference
|
|
1393
|
+
* @param edgeType optional parameter specifying the type of the incoming
|
|
1394
|
+
* JavaScript reference
|
|
1395
|
+
* @returns the incoming edge that meets the specification
|
|
1396
|
+
*
|
|
1397
|
+
* * **Examples**:
|
|
1398
|
+
* ```typescript
|
|
1399
|
+
* // find one of the JS reference named "ref" pointing to node
|
|
1400
|
+
* const reference = node.getAnyReferrer('ref', 'property');
|
|
1401
|
+
* ```
|
|
1402
|
+
*/
|
|
677
1403
|
getAnyReferrer: (edgeName: string | number, edgeType?: string) => Nullable<IHeapEdge>;
|
|
1404
|
+
/**
|
|
1405
|
+
* Given a JS reference's name and type, this API finds one of the incoming JS
|
|
1406
|
+
* references pointing to the hosting node, and returns the JS heap object
|
|
1407
|
+
* containing the incoming reference.
|
|
1408
|
+
* @param edgeName the name of the incoming JavaScript reference
|
|
1409
|
+
* @param edgeType optional parameter specifying the type of the incoming
|
|
1410
|
+
* JavaScript reference
|
|
1411
|
+
* @returns the node containing the incoming JS reference that meets
|
|
1412
|
+
* the specification
|
|
1413
|
+
*
|
|
1414
|
+
* * **Examples**:
|
|
1415
|
+
* ```typescript
|
|
1416
|
+
* // find one of the JS heap object with a JS reference
|
|
1417
|
+
* // named "ref" pointing to node
|
|
1418
|
+
* const n1 = node.getAnyReferrerNode('ref', 'property');
|
|
1419
|
+
* // this is equivalent to
|
|
1420
|
+
* const n2 = node.getAnyReferrer('ref', 'property')?.fromNode;
|
|
1421
|
+
* ```
|
|
1422
|
+
*/
|
|
678
1423
|
getAnyReferrerNode: (edgeName: string | number, edgeType?: string) => Nullable<IHeapNode>;
|
|
1424
|
+
/**
|
|
1425
|
+
* Given a JS reference's name and type, this API finds all the incoming JS
|
|
1426
|
+
* reference pointing to the hosting node.
|
|
1427
|
+
* @param edgeName the name of the incoming JavaScript reference
|
|
1428
|
+
* @param edgeType optional parameter specifying the type of the incoming
|
|
1429
|
+
* JavaScript reference
|
|
1430
|
+
* @returns an array containing all the incoming edges that
|
|
1431
|
+
* meet the specification
|
|
1432
|
+
*
|
|
1433
|
+
* * **Examples**:
|
|
1434
|
+
* ```typescript
|
|
1435
|
+
* // find all of of the JS reference named "ref" pointing to node
|
|
1436
|
+
* const referrers = node.getReferrers('ref', 'property');
|
|
1437
|
+
* ```
|
|
1438
|
+
*/
|
|
679
1439
|
getReferrers: (edgeName: string | number, edgeType?: string) => IHeapEdge[];
|
|
1440
|
+
/**
|
|
1441
|
+
* Given a JS reference's name and type, this API finds all of the incoming JS
|
|
1442
|
+
* references pointing to the hosting node, and returns an array containing
|
|
1443
|
+
* the hosting node for each of the incoming JS references.
|
|
1444
|
+
* @param edgeName the name of the incoming JavaScript reference
|
|
1445
|
+
* @param edgeType optional parameter specifying the type of the incoming
|
|
1446
|
+
* JavaScript reference
|
|
1447
|
+
* @returns an array containing the hosting nodes, with each node corresponds
|
|
1448
|
+
* to each incoming JS reference that meets the specification
|
|
1449
|
+
*
|
|
1450
|
+
* * **Examples**:
|
|
1451
|
+
* ```typescript
|
|
1452
|
+
* // find all of the JS heap object with a JS reference
|
|
1453
|
+
* // named "ref" pointing to node
|
|
1454
|
+
* const nodes1 = node.getReferrerNodes('ref', 'property');
|
|
1455
|
+
* // this is equivalent to
|
|
1456
|
+
* const nodes2 = node.getReferrers('ref', 'property')
|
|
1457
|
+
* .map(edge => edge.fromNode);
|
|
1458
|
+
* ```
|
|
1459
|
+
*/
|
|
680
1460
|
getReferrerNodes: (edgeName: string | number, edgeType?: string) => IHeapNode[];
|
|
681
1461
|
}
|
|
1462
|
+
/**
|
|
1463
|
+
* An `IHeapStringNode` instance represents a JS string in a heap snapshot.
|
|
1464
|
+
* A heap snapshot is generally a graph where graph nodes are JS heap objects
|
|
1465
|
+
* and graph edges are JS references among JS heap objects.
|
|
1466
|
+
*
|
|
1467
|
+
* @readonly it is not recommended to modify any `IHeapStringNode` instance
|
|
1468
|
+
*
|
|
1469
|
+
* * **Examples**: V8 or hermes heap snapshot can be parsed by the
|
|
1470
|
+
* {@link getHeapFromFile} API.
|
|
1471
|
+
*
|
|
1472
|
+
* ```typescript
|
|
1473
|
+
* import type {IHeapSnapshot, IHeapNode, IHeapStringNode} from '@memlab/core';
|
|
1474
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1475
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
1476
|
+
*
|
|
1477
|
+
* (async function () {
|
|
1478
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
1479
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
1480
|
+
*
|
|
1481
|
+
* // iterate over each node (heap object)
|
|
1482
|
+
* heap.nodes.forEach((node: IHeapNode, i: number) => {
|
|
1483
|
+
* if (node.isString) {
|
|
1484
|
+
* const stringNode: IheapStringNode = node.toStringNode();
|
|
1485
|
+
* // get the string value
|
|
1486
|
+
* stringNode.stringValue;
|
|
1487
|
+
* }
|
|
1488
|
+
* });
|
|
1489
|
+
* })();
|
|
1490
|
+
* ```
|
|
1491
|
+
*/
|
|
682
1492
|
export interface IHeapStringNode extends IHeapNode {
|
|
1493
|
+
/**
|
|
1494
|
+
* get the string value of the JS string heap object associated with
|
|
1495
|
+
* this `IHeapStringNode` instance in heap
|
|
1496
|
+
*/
|
|
683
1497
|
stringValue: string;
|
|
684
1498
|
}
|
|
1499
|
+
/**
|
|
1500
|
+
* A pseudo array containing all heap graph nodes (JS objects
|
|
1501
|
+
* in heap). A JS heap could contain millions of objects, so memlab uses
|
|
1502
|
+
* a pseudo array as the collection of all the heap nodes. The pseudo
|
|
1503
|
+
* array provides API to query and traverse all heap objects.
|
|
1504
|
+
*
|
|
1505
|
+
* @readonly modifying this pseudo array is not recommended
|
|
1506
|
+
*
|
|
1507
|
+
* * **Examples**:
|
|
1508
|
+
* ```typescript
|
|
1509
|
+
* import type {IHeapSnapshot, IHeapNodes} from '@memlab/core';
|
|
1510
|
+
* import {dumpNodeHeapSnapshot} from '@memlab/core';
|
|
1511
|
+
* import {getHeapFromFile} from '@memlab/heap-analysis';
|
|
1512
|
+
*
|
|
1513
|
+
* (async function () {
|
|
1514
|
+
* const heapFile = dumpNodeHeapSnapshot();
|
|
1515
|
+
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
|
|
1516
|
+
*
|
|
1517
|
+
* const nodes: IHeapNodes = heap.nodes;
|
|
1518
|
+
* nodes.length;
|
|
1519
|
+
* nodes.get(0);
|
|
1520
|
+
* nodes.forEach((node, i) => {
|
|
1521
|
+
* if (stopIteration) {
|
|
1522
|
+
* return false;
|
|
1523
|
+
* }
|
|
1524
|
+
* });
|
|
1525
|
+
* })();
|
|
1526
|
+
* ```
|
|
1527
|
+
*/
|
|
685
1528
|
export interface IHeapNodes {
|
|
1529
|
+
/**
|
|
1530
|
+
* The total number of nodes in heap graph (or JS objects in heap
|
|
1531
|
+
* snapshot).
|
|
1532
|
+
*/
|
|
686
1533
|
length: number;
|
|
687
|
-
|
|
1534
|
+
/**
|
|
1535
|
+
* get an {@link IHeapNode} element at the specified index
|
|
1536
|
+
* @param index the index of an element in the pseudo array, the index ranges
|
|
1537
|
+
* from 0 to array length - 1. Notice that this is not the heap node id.
|
|
1538
|
+
* @returns When 0 <= `index` < array.length, this API returns the element
|
|
1539
|
+
* at the specified index, otherwise it returns `null`.
|
|
1540
|
+
*/
|
|
1541
|
+
get(index: number): Nullable<IHeapNode>;
|
|
1542
|
+
/**
|
|
1543
|
+
* Iterate over all array elements and apply the callback
|
|
1544
|
+
* to each element in ascending order of element index.
|
|
1545
|
+
* @param callback the callback does not need to return any value, if
|
|
1546
|
+
* the callback returns `false` when iterating on element at index `i`,
|
|
1547
|
+
* then all elements after `i` won't be iterated.
|
|
1548
|
+
*/
|
|
688
1549
|
forEach(callback: (node: IHeapNode, index: number) => void | boolean): void;
|
|
1550
|
+
/** @internal */
|
|
689
1551
|
forEachTraceable(callback: (node: IHeapNode, index: number) => void | boolean): void;
|
|
690
1552
|
}
|
|
691
1553
|
/** @internal */
|