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