@memlab/core
Advanced tools
Comparing version 1.1.2 to 1.1.3
@@ -174,2 +174,3 @@ /** | ||
_scenario: Optional<IScenario>; | ||
_isHeadfulBrowser: boolean; | ||
externalLeakFilter?: Optional<ILeakFilter>; | ||
@@ -196,2 +197,4 @@ monoRepoDir: string; | ||
get browser(): string; | ||
set isHeadfulBrowser(isHeadful: boolean); | ||
get isHeadfulBrowser(): boolean; | ||
get browserBinaryPath(): string; | ||
@@ -198,0 +201,0 @@ set reportLeaksInTimers(flag: boolean); |
@@ -57,2 +57,3 @@ "use strict"; | ||
this._timerEdges = []; | ||
this._isHeadfulBrowser = false; | ||
this.targetApp = Constant_1.default.unset; | ||
@@ -70,2 +71,3 @@ this.targetTab = Constant_1.default.unset; | ||
this.puppeteerConfig = { | ||
headless: !this._isHeadfulBrowser, | ||
devtools: this.openDevtoolsConsole, | ||
@@ -363,2 +365,13 @@ // IMPORTANT: test ContinuousTest before change this config | ||
} | ||
set isHeadfulBrowser(isHeadful) { | ||
this._isHeadfulBrowser = isHeadful; | ||
this.puppeteerConfig.headless = !isHeadful; | ||
if (isHeadful) { | ||
// if running in headful mode | ||
this.disableXvfb(); | ||
} | ||
} | ||
get isHeadfulBrowser() { | ||
return this._isHeadfulBrowser; | ||
} | ||
get browserBinaryPath() { | ||
@@ -365,0 +378,0 @@ return path_1.default.join(this.browserDir, this.browser); |
@@ -16,4 +16,4 @@ "use strict"; | ||
isFRL: false, | ||
defaultEngine: 'v8', | ||
supportedEngines: ['v8', 'hermes'], | ||
defaultEngine: 'V8', | ||
supportedEngines: ['V8', 'hermes'], | ||
supportedBrowsers: Object.create(null), | ||
@@ -20,0 +20,0 @@ internalDir: 'fb-internal', |
@@ -34,3 +34,3 @@ /** | ||
forEachReference(callback: EdgeIterationCallback): void; | ||
findReference(predicate: Predicator<IHeapEdge>): Nullable<IHeapEdge>; | ||
findAnyReference(predicate: Predicator<IHeapEdge>): Nullable<IHeapEdge>; | ||
findAnyReferrer(predicate: Predicator<IHeapEdge>): Nullable<IHeapEdge>; | ||
@@ -37,0 +37,0 @@ findReferrers(predicate: Predicator<IHeapEdge>): IHeapEdge[]; |
@@ -134,3 +134,3 @@ /** | ||
} | ||
findReference(predicate) { | ||
findAnyReference(predicate) { | ||
let found = null; | ||
@@ -137,0 +137,0 @@ this.forEachReference((edge) => { |
@@ -85,2 +85,5 @@ /** | ||
get(idx) { | ||
if (idx < 0 || idx >= self._nodeCount) { | ||
return null; | ||
} | ||
return new HeapNode_1.default(self, idx); | ||
@@ -113,2 +116,5 @@ }, | ||
get(idx) { | ||
if (idx < 0 || idx >= self._edgeCount) { | ||
return null; | ||
} | ||
return new HeapEdge_1.default(self, idx); | ||
@@ -185,3 +191,3 @@ }, | ||
// check if the table has any weak reference to any object | ||
const ref = table.findReference((edge) => edge.type === 'weak' && edge.toNode.name !== 'system / Oddball'); | ||
const ref = table.findAnyReference((edge) => edge.type === 'weak' && edge.toNode.name !== 'system / Oddball'); | ||
return ref != null; | ||
@@ -188,0 +194,0 @@ } |
@@ -105,3 +105,3 @@ /** | ||
Console_1.default.overwrite('identifying snapshot engine...'); | ||
let engine = 'v8'; | ||
let engine = 'V8'; | ||
snapshot.nodes.forEach((node) => { | ||
@@ -108,0 +108,0 @@ if (node.type === 'object' && node.name.startsWith('Object(')) { |
@@ -185,2 +185,5 @@ "use strict"; | ||
const parent = node.snapshot.nodes.get(index); | ||
if (!parent) { | ||
continue; | ||
} | ||
const parentInfo = getNodeNameInJSON(parent, args); | ||
@@ -597,3 +600,3 @@ key = `${key}: --return (property)---> ${parentInfo}`; | ||
nodeImpact = options.color | ||
? chalk_1.default.grey('[') + chalk_1.default.blue(nodeRetainSize) + chalk_1.default.grey(']') | ||
? chalk_1.default.grey('[') + chalk_1.default.blue.bold(nodeRetainSize) + chalk_1.default.grey(']') | ||
: `[${nodeRetainSize}]`; | ||
@@ -600,0 +603,0 @@ } |
@@ -99,3 +99,3 @@ /** | ||
getDomainPrefixes(): string[]; | ||
getCookieFile(visitPlan: IE2EScenarioVisitPlan): string | null; | ||
getCookieFile(visitPlan: IE2EScenarioVisitPlan): Nullable<string>; | ||
getAvailableSteps(): IE2EStepBasic[]; | ||
@@ -147,12 +147,45 @@ getNodeNameBlocklist(): string[]; | ||
/** | ||
* The type for defining custom leak-filtering logic. | ||
* * **Examples**: | ||
* The `ILeakFilter` interface allows you to define a leak detector and | ||
* customize the leak filtering logic in memlab (instead of using the | ||
* built-in leak filters). | ||
* | ||
* Use the leak filter definition in command line interface to filter | ||
* leaks detected from browser interactions | ||
* ```bash | ||
* memlab run --scenario <SCENARIO FILE> --leak-filter <PATH TO leak-filter.js> | ||
* ``` | ||
* | ||
* If you have already run `memlab run` or `memlab snapshot` which saved | ||
* heap snapshot and other meta data on disk, use the following command | ||
* to filter leaks based on those saved heap snapshots (query the default | ||
* data location by `memlab get-default-work-dir`). | ||
* | ||
* ```bash | ||
* memlab find-leaks --leak-filter <PATH TO leak-filter.js> | ||
* ``` | ||
* Here is an example TypeScript file defining a leak filter. | ||
* The command line interface only accepts compiled JavaScript file. | ||
* You can also define the leak filter in JavaScript (without the | ||
* type annotations. | ||
* | ||
* ```typescript | ||
* const scenario = { | ||
* import {IHeapNode, IHeapSnapshot, HeapNodeIdSet, utils} from '@memlab/core'; | ||
* | ||
* }; | ||
* | ||
* let map = Object.create(null); | ||
* function initMap(snapshot: IHeapSnapshot): Record<string, number> { | ||
* const map = Object.create(null); | ||
* snapshot.nodes.forEach(node => { | ||
* if (node.type !== 'string') { | ||
* return; | ||
* } | ||
* const str = utils.getStringNodeValue(node); | ||
* if (str in map) { | ||
* ++map[str]; | ||
* } else { | ||
* map[str] = 1; | ||
* } | ||
* }); | ||
* return map; | ||
* } | ||
* const beforeLeakFilter = (snapshot: IHeapSnapshot, _leakedNodeIds: HeapNodeIdSet): void => { | ||
* map = initializeMapUsingSnapshot(snapshot); | ||
* map = initMap(snapshot); | ||
* }; | ||
@@ -173,3 +206,77 @@ * | ||
export interface ILeakFilter { | ||
/** | ||
* Lifecycle function callback that is invoked initially once before | ||
* the subsequent `leakFilter` function calls. This callback could | ||
* be used to initialize some data stores or any one-off | ||
* preprocessings. | ||
* | ||
* * **Parameters**: | ||
* * snapshot: `IHeapSnapshot` | the final heap snapshot taken after | ||
* all browser interactions are done. | ||
* Check out {@link IHeapSnapshot} for more APIs that queries the heap snapshot. | ||
* * leakedNodeIds: `Set<number>` | the set of ids of all JS heap objects | ||
* allocated by the `action` call but not released after the `back` call | ||
* in browser. | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* module.exports = { | ||
* beforeLeakFilter: (snapshot, leakedNodeIds) { | ||
* // initialize some data stores | ||
* }, | ||
* leakFilter(node, snapshot, leakedNodeIds) { | ||
* // use the data stores | ||
* }, | ||
* }; | ||
* ``` | ||
*/ | ||
beforeLeakFilter?: InitLeakFilterCallback; | ||
/** | ||
* This callback defines how you want to filter out the | ||
* leaked objects. The callback is called for every node (JS heap | ||
* object in browser) allocated by the `action` callback, but not | ||
* released after the `back` callback. Those objects could be caches | ||
* that are retained in memory on purpose, or they are memory leaks. | ||
* | ||
* This optional callback allows you to define your own algorithm | ||
* to cherry pick memory leaks for specific JS program under test. | ||
* | ||
* If this optional callback is not defined, memlab will use its | ||
* built-in leak filter, which considers detached DOM elements | ||
* and unmounted Fiber nodes (detached from React Fiber tree) as | ||
* memory leaks. | ||
* | ||
* * **Parameters**: | ||
* * node: `IHeapNode` | one of the heap object allocated but not released. | ||
* * snapshot: `IHeapSnapshot` | the final heap snapshot taken after | ||
* all browser interactions are done. | ||
* Check out {@link IHeapSnapshot} for more APIs that queries the heap snapshot. | ||
* * leakedNodeIds: `Set<number>` | the set of ids of all JS heap objects | ||
* allocated by the `action` call but not released after the `back` call | ||
* in browser. | ||
* | ||
* * **Returns**: the boolean value indicating whether the given node in | ||
* the snapshot should be considered as leaked. | ||
* | ||
* | ||
* ```javascript | ||
* // save as leak-filter.js | ||
* module.exports = { | ||
* leakFilter(node, snapshot, leakedNodeIds) { | ||
* // any unreleased node (JS heap object) with 1MB+ | ||
* // retained size is considered a memory leak | ||
* return node.retainedSize > 1000000; | ||
* }, | ||
* }; | ||
* ``` | ||
* | ||
* Use the leak filter definition in command line interface: | ||
* ```bash | ||
* memlab find-leaks --leak-filter <PATH TO leak-filter.js> | ||
* ``` | ||
* | ||
* ```bash | ||
* memlab run --scenario <SCENARIO FILE> --leak-filter <PATH TO leak-filter.js> | ||
* ``` | ||
*/ | ||
leakFilter: LeakFilterCallback; | ||
@@ -223,2 +330,5 @@ } | ||
* back: async () => ... , | ||
* cookies: () => ... , // optional | ||
* repeat: () => ... , // optional | ||
* ... | ||
* }; | ||
@@ -237,2 +347,5 @@ * ``` | ||
* back: async () => ... , | ||
* cookies: () => ... , // optional | ||
* repeat: () => ... , // optional | ||
* ... | ||
* }; | ||
@@ -251,3 +364,7 @@ * const leaks = await run({scenario}); | ||
* specific cookie(s) to be set, you can pass them as | ||
* a list of <name, value> pairs. | ||
* a list of `<name, value, domain>` tuples. | ||
* | ||
* **Note**: please make sure that you provide the correct `domain` field for | ||
* the cookies tuples. | ||
* | ||
* @returns cookie list | ||
@@ -259,6 +376,6 @@ * * **Examples**: | ||
* cookies: () => [ | ||
* {"name":"cm_j","value":"none"}, | ||
* {"name":"datr","value":"yJvIY..."}, | ||
* {"name":"c_user","value":"8917..."}, | ||
* {"name":"xs","value":"95:9WQ..."}, | ||
* {name:'cm_j', value: 'none', domain: '.facebook.com'}, | ||
* {name:'datr', value: 'yJvIY...', domain: '.facebook.com'}, | ||
* {name:'c_user', value: '8917...', domain: '.facebook.com'}, | ||
* {name:'xs', value: '95:9WQ...', domain: '.facebook.com'}, | ||
* // ... | ||
@@ -364,3 +481,3 @@ * ], | ||
* Optional callback function that checks if the web page is loaded | ||
* after for initial page loading and subsequent browser interactions. | ||
* for the initial page load and subsequent browser interactions. | ||
* | ||
@@ -401,3 +518,3 @@ * If this callback is not provided, memlab by default | ||
* the subsequent `leakFilter` function calls. This callback could | ||
* be used to initialize some data stores or to do some one-off | ||
* be used to initialize some data stores or to any one-off | ||
* preprocessings. | ||
@@ -427,3 +544,3 @@ * | ||
/** | ||
* This callback that defines how you want to filter out the | ||
* This callback defines how you want to filter out the | ||
* leaked objects. The callback is called for every node (JS heap | ||
@@ -619,2 +736,8 @@ * object in browser) allocated by the `action` callback, but not | ||
}; | ||
/** | ||
* A heap snapshot is generally a graph where graph nodes are JS heap objects | ||
* and graph edges are JS references among JS heap objects. For more details | ||
* on the structure of nodes and edges in the heap graph, check out | ||
* {@link IHeapNode} and {@link IHeapEdge}. | ||
*/ | ||
export interface IHeapSnapshot { | ||
@@ -793,2 +916,6 @@ /** @internal */ | ||
* a marker tagged by {@link tagObject}. | ||
* | ||
* The `tagObject` API does not modify the object instance in any way | ||
* (e.g., no additional or hidden properties added to the tagged object). | ||
* | ||
* @param tag marker name on the object instances tagged by {@link tagObject} | ||
@@ -828,28 +955,195 @@ * @returns returns `true` if there is at least one such object in the heap | ||
} | ||
/** | ||
* An `IHeapLocation` instance contains a source location information | ||
* associated with a JS heap object. | ||
* A heap snapshot is generally a graph where graph nodes are JS heap objects | ||
* and graph edges are JS references among JS heap objects. | ||
* | ||
* @readonly it is not recommended to modify any `IHeapLocation` instance | ||
* | ||
* * **Examples**: V8 or hermes heap snapshot can be parsed by the | ||
* {@link getHeapFromFile} API. | ||
* | ||
* ```typescript | ||
* import type {IHeapSnapshot, IHeapNode, IHeapLocation} from '@memlab/core'; | ||
* import {dumpNodeHeapSnapshot} from '@memlab/core'; | ||
* import {getHeapFromFile} from '@memlab/heap-analysis'; | ||
* | ||
* (async function () { | ||
* const heapFile = dumpNodeHeapSnapshot(); | ||
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile); | ||
* | ||
* // iterate over each node (heap object) | ||
* heap.nodes.forEach((node: IHeapNode, i: number) => { | ||
* const location: Nullable<IHeapLocation> = node.location; | ||
* if (location) { | ||
* // use the location API here | ||
* location.line; | ||
* // ... | ||
* } | ||
* }); | ||
* })(); | ||
* ``` | ||
*/ | ||
export interface IHeapLocation { | ||
/** | ||
* get the {@link IHeapSnapshot} containing this location instance | ||
*/ | ||
snapshot: IHeapSnapshot; | ||
/** | ||
* get the script ID of the source file | ||
*/ | ||
script_id: number; | ||
/** | ||
* get the line number | ||
*/ | ||
line: number; | ||
/** | ||
* get the column number | ||
*/ | ||
column: number; | ||
} | ||
/** @internal */ | ||
export interface IHeapEdgeBasic { | ||
/** | ||
* name of the JS reference. If this is a reference to an array element | ||
* or internal table element, it is an numeric index | ||
*/ | ||
name_or_index: number | string; | ||
/** | ||
* type of the JS reference, all types: | ||
* `context`, `element`, `property`, `internal`, `hidden`, `shortcut`, `weak` | ||
*/ | ||
type: string; | ||
} | ||
/** | ||
* An `IHeapEdge` instance represents a JS reference in a heap snapshot. | ||
* A heap snapshot is generally a graph where graph nodes are JS heap objects | ||
* and graph edges are JS references among JS heap objects. | ||
* | ||
* @readonly it is not recommended to modify any `IHeapEdge` instance | ||
* | ||
* * **Examples**: V8 or hermes heap snapshot can be parsed by the | ||
* {@link getHeapFromFile} API. | ||
* | ||
* ```typescript | ||
* import type {IHeapSnapshot, IHeapEdge} from '@memlab/core'; | ||
* import {dumpNodeHeapSnapshot} from '@memlab/core'; | ||
* import {getHeapFromFile} from '@memlab/heap-analysis'; | ||
* | ||
* (async function () { | ||
* const heapFile = dumpNodeHeapSnapshot(); | ||
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile); | ||
* | ||
* // iterate over each edge (JS reference in heap) | ||
* heap.edges.forEach((edge: IHeapEdge, i: number) => { | ||
* // use the heap edge APIs here | ||
* const nameOrIndex = edge.name_or_index; | ||
* // ... | ||
* }); | ||
* })(); | ||
* ``` | ||
*/ | ||
export interface IHeapEdge extends IHeapEdgeBasic { | ||
/** | ||
* get the {@link IHeapSnapshot} containing this JS reference | ||
*/ | ||
snapshot: IHeapSnapshot; | ||
/** | ||
* index of this JS reference inside the `edge.snapshot.edges` pseudo array | ||
*/ | ||
edgeIndex: number; | ||
/** | ||
* if `true`, means this is a reference to an array element | ||
* or internal table element (`edge.name_or_index` will return a number), | ||
* otherwise this is a reference with a string name (`edge.name_or_index` | ||
* will return a string) | ||
*/ | ||
is_index: boolean; | ||
/** | ||
* the index of the JS heap object pointed to by this reference | ||
*/ | ||
to_node: number; | ||
/** | ||
* returns an {@link IHeapNode} instance representing the JS heap object | ||
* pointed to by this reference | ||
*/ | ||
toNode: IHeapNode; | ||
/** | ||
* returns an {@link IHeapNode} instance representing the hosting | ||
* JS heap object where this reference starts | ||
*/ | ||
fromNode: IHeapNode; | ||
} | ||
/** | ||
* A pseudo array containing all heap graph edges (references to heap objects | ||
* in heap). A JS heap could contain millions of references, so memlab uses | ||
* a pseudo array as the collection of all the heap edges. The pseudo | ||
* array provides API to query and traverse all heap references. | ||
* | ||
* @readonly modifying this pseudo array is not recommended | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* import type {IHeapSnapshot, IHeapEdges} from '@memlab/core'; | ||
* import {dumpNodeHeapSnapshot} from '@memlab/core'; | ||
* import {getHeapFromFile} from '@memlab/heap-analysis'; | ||
* | ||
* (async function () { | ||
* const heapFile = dumpNodeHeapSnapshot(); | ||
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile); | ||
* | ||
* const edges: IHeapEdges = heap.edges; | ||
* edges.length; | ||
* edges.get(0); | ||
* edges.forEach((edge, i) => { | ||
* if (stopIteration) { | ||
* return false; | ||
* } | ||
* }); | ||
* })(); | ||
* ``` | ||
*/ | ||
export interface IHeapEdges { | ||
/** | ||
* The total number of edges in heap graph (or JS references in heap | ||
* snapshot). | ||
*/ | ||
length: number; | ||
get(index: number): IHeapEdge; | ||
/** | ||
* get an {@link IHeapEdge} element at the specified index | ||
* @param index the index of an element in the pseudo array, the index ranges | ||
* from 0 to array length - 1. Notice that this is not the heap node id. | ||
* @returns When 0 <= `index` < array.length, this API returns the element | ||
* at the specified index, otherwise it returns `null`. | ||
*/ | ||
get(index: number): Nullable<IHeapEdge>; | ||
/** | ||
* Iterate over all array elements and apply the callback | ||
* to each element in ascending order of element index. | ||
* @param callback the callback does not need to return any value, if | ||
* the callback returns `false` when iterating on element at index `i`, | ||
* then all elements after `i` won't be iterated. | ||
*/ | ||
forEach(callback: (edge: IHeapEdge, index: number) => void | boolean): void; | ||
} | ||
/** @internal */ | ||
export interface IHeapNodeBasic { | ||
/** | ||
* the type of the heap node object. All possible types: | ||
* This is engine-specific, for example all types in V8: | ||
* `hidden`, `array`, `string`, `object`, `code`, `closure`, `regexp`, | ||
* `number`, `native`, `synthetic`, `concatenated string`, `sliced string`, | ||
* `symbol`, `bigint` | ||
*/ | ||
type: string; | ||
/** | ||
* this is the `name` field associated with the heap object, | ||
* for JS object instances (type `object`), `name` is the constructor's name | ||
* of the object instance. for `string`, `name` is the string value. | ||
*/ | ||
name: string; | ||
/** | ||
* unique id of the heap object | ||
*/ | ||
id: number; | ||
@@ -860,40 +1154,399 @@ } | ||
}>; | ||
/** | ||
* An `IHeapNode` instance represents a JS heap object in a heap snapshot. | ||
* A heap snapshot is generally a graph where graph nodes are JS heap objects | ||
* and graph edges are JS references among JS heap objects. | ||
* | ||
* @readonly it is not recommended to modify any `IHeapNode` instance | ||
* | ||
* * **Examples**: V8 or hermes heap snapshot can be parsed by the | ||
* {@link getHeapFromFile} API. | ||
* | ||
* ```typescript | ||
* import type {IHeapSnapshot, IHeapNode} from '@memlab/core'; | ||
* import {dumpNodeHeapSnapshot} from '@memlab/core'; | ||
* import {getHeapFromFile} from '@memlab/heap-analysis'; | ||
* | ||
* (async function () { | ||
* const heapFile = dumpNodeHeapSnapshot(); | ||
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile); | ||
* | ||
* // iterate over each node (heap object) | ||
* heap.nodes.forEach((node: IHeapNode, i: number) => { | ||
* // use the heap node APIs here | ||
* const id = node.id; | ||
* const type = node.type; | ||
* // ... | ||
* }); | ||
* })(); | ||
* ``` | ||
*/ | ||
export interface IHeapNode extends IHeapNodeBasic { | ||
/** | ||
* get the {@link IHeapSnapshot} containing this heap object | ||
*/ | ||
snapshot: IHeapSnapshot; | ||
/** | ||
* * If the heap object is a DOM element and the DOM element is detached | ||
* from the DOM tree, `is_detached` will be `true`; | ||
* * If the heap object is a React Fiber node and the Fiber node is unmounted | ||
* from the React Fiber tree, `is_detached` will be `true`; | ||
* otherwise it will be `false` | ||
*/ | ||
is_detached: boolean; | ||
/** @internal */ | ||
detachState: number; | ||
/** @internal */ | ||
markAsDetached(): void; | ||
/** @internal */ | ||
attributes: number; | ||
/** | ||
* The *shallow size* of the heap object (i.e., the size of memory that is held | ||
* by the object itself.). For difference between **shallow size** and | ||
* **retained size**, check out | ||
* [this doc](https://developer.chrome.com/docs/devtools/memory-problems/memory-101/#object_sizes). | ||
*/ | ||
self_size: number; | ||
/** | ||
* The total number of outgoing JS references (including engine-internal, | ||
* native, and JS references). | ||
*/ | ||
edge_count: number; | ||
/** @internal */ | ||
trace_node_id: number; | ||
/** | ||
* Get a JS array containing all outgoing JS references from this heap object | ||
* (including engine-internal, native, and JS references). | ||
*/ | ||
references: IHeapEdge[]; | ||
/** | ||
* Get a JS array containing all incoming JS references pointing to this heap | ||
* object (including engine-internal, native, and JS references). | ||
*/ | ||
referrers: IHeapEdge[]; | ||
/** | ||
* The incoming edge which leads to the parent node | ||
* on the shortest path to GC root. | ||
*/ | ||
pathEdge: IHeapEdge | null; | ||
/** | ||
* index of this heap object inside the `node.snapshot.nodes` pseudo array | ||
*/ | ||
nodeIndex: number; | ||
/** | ||
* The *retained size* of the heap object (i.e., the total size of memory that | ||
* could be released if this object is released). For difference between | ||
* **retained size** and **shallow size**, check out | ||
* [this doc](https://developer.chrome.com/docs/devtools/memory-problems/memory-101/#object_sizes). | ||
*/ | ||
retainedSize: number; | ||
dominatorNode: IHeapNode | null; | ||
location: IHeapLocation | null; | ||
/** | ||
* get the dominator node of this node. If the dominator node gets released | ||
* there will be no path from GC to this node, and therefore this node can | ||
* also be released. | ||
* For more information on what a dominator node is, please check out | ||
* [this doc](https://developer.chrome.com/docs/devtools/memory-problems/memory-101/#dominators). | ||
*/ | ||
dominatorNode: Nullable<IHeapNode>; | ||
/** | ||
* source location information of this heap object (if it is recorded by | ||
* the heap snapshot). | ||
*/ | ||
location: Nullable<IHeapLocation>; | ||
/** @internal */ | ||
highlight?: boolean; | ||
/** | ||
* check if this a string node (normal string node, concatenated string node | ||
* or sliced string node) | ||
*/ | ||
isString: boolean; | ||
/** | ||
* convert to an {@link IHeapStringNode} object if this node is a string node. | ||
* The {@link IHeapStringNode} object supports querying the string content | ||
* inside the string node. | ||
*/ | ||
toStringNode(): Nullable<IHeapStringNode>; | ||
/** | ||
* executes a provided callback once for each JavaScript reference in the | ||
* hosting node (or outgoing edges from the node) | ||
* @param callback the callback for each outgoing JavaScript reference | ||
* @returns this API returns void | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* node.forEachReference((edge: IHeapEdge) => { | ||
* // process edge ... | ||
* | ||
* // if no need to iterate over remaining edges after | ||
* // the current edge in the node.references list | ||
* return {stop: true}; | ||
* }); | ||
* ``` | ||
*/ | ||
forEachReference(callback: EdgeIterationCallback): void; | ||
/** | ||
* executes a provided callback once for each JavaScript reference pointing | ||
* to the hosting node (or incoming edges to the node) | ||
* @param callback the callback for each incoming JavaScript reference | ||
* @returns this API returns void | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* node.forEachReferrer((edge: IHeapEdge) => { | ||
* // process edge ... | ||
* | ||
* // if no need to iterate over remaining edges after | ||
* // the current edge in the node.referrers list | ||
* return {stop: true}; | ||
* }); | ||
* ``` | ||
*/ | ||
forEachReferrer(callback: EdgeIterationCallback): void; | ||
findReference: (predicate: Predicator<IHeapEdge>) => Nullable<IHeapEdge>; | ||
/** | ||
* executes a provided predicate callback once for each JavaScript reference | ||
* in the hosting node (or outgoing edges from the node) until the predicate | ||
* returns `true` | ||
* @param predicate the callback for each outgoing JavaScript reference | ||
* @returns the first outgoing edge for which the predicate returns `true`, | ||
* otherwise returns `null` if no such edge is found. | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* const reference = node.findAnyReference((edge: IHeapEdge) => { | ||
* // find the outgoing reference with name "ref" | ||
* return edge.name_or_index === 'ref'; | ||
* }); | ||
* ``` | ||
*/ | ||
findAnyReference: (predicate: Predicator<IHeapEdge>) => Nullable<IHeapEdge>; | ||
/** | ||
* executes a provided predicate callback once for each JavaScript reference | ||
* pointing to the hosting node (or incoming edges to the node) until the | ||
* predicate returns `true` | ||
* @param predicate the callback for each incoming JavaScript reference | ||
* @returns the first incoming edge for which the predicate returns `true`, | ||
* otherwise returns `null` if no such edge is found. | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* const referrer = node.findAnyReferrer((edge: IHeapEdge) => { | ||
* // find the incoming reference with name "ref" | ||
* return edge.name_or_index === 'ref'; | ||
* }); | ||
* ``` | ||
*/ | ||
findAnyReferrer: (predicate: Predicator<IHeapEdge>) => Nullable<IHeapEdge>; | ||
/** | ||
* executes a provided predicate callback once for each JavaScript reference | ||
* pointing to the hosting node (or incoming edges to the node) | ||
* @param predicate the callback for each incoming JavaScript reference | ||
* @returns an array containing all the incoming edges for which the | ||
* predicate returns `true`, otherwise returns an empty array if no such | ||
* edge is found. | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* const referrers = node.findReferrers((edge: IHeapEdge) => { | ||
* // find all the incoming references with name "ref" | ||
* return edge.name_or_index === 'ref'; | ||
* }); | ||
* ``` | ||
*/ | ||
findReferrers: (predicate: Predicator<IHeapEdge>) => IHeapEdge[]; | ||
/** | ||
* Given a JS reference's name and type, this API finds an outgoing JS | ||
* reference from the hosting node. | ||
* @param edgeName the name of the outgoing JavaScript reference | ||
* @param edgeType optional parameter specifying the type of the outgoing | ||
* JavaScript reference | ||
* @returns the outgoing edge that meets the specification | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* // find the internal reference to node's hidden class | ||
* const reference = node.getReference('map', 'hidden'); | ||
* ``` | ||
*/ | ||
getReference: (edgeName: string | number, edgeType?: string) => Nullable<IHeapEdge>; | ||
/** | ||
* Given a JS reference's name and type, this API finds the outgoing JS | ||
* reference from the hosting node, and returns the JS heap object pointed to | ||
* by the outgoing JS reference. | ||
* @param edgeName the name of the outgoing JavaScript reference | ||
* @param edgeType optional parameter specifying the type of the outgoing | ||
* JavaScript reference | ||
* @returns the node pointed to by the outgoing reference that meets | ||
* the specification | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* // find the node's hidden class | ||
* const hiddenClassNode = node.getReferenceNode('map', 'hidden'); | ||
* // this is equivalent to | ||
* const hiddenClassNode2 = node.getReference('map', 'hidden')?.toNode; | ||
* ``` | ||
*/ | ||
getReferenceNode: (edgeName: string | number, edgeType?: string) => Nullable<IHeapNode>; | ||
/** | ||
* Given a JS reference's name and type, this API finds an incoming JS | ||
* reference pointing to the hosting node. | ||
* @param edgeName the name of the incoming JavaScript reference | ||
* @param edgeType optional parameter specifying the type of the incoming | ||
* JavaScript reference | ||
* @returns the incoming edge that meets the specification | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* // find one of the JS reference named "ref" pointing to node | ||
* const reference = node.getAnyReferrer('ref', 'property'); | ||
* ``` | ||
*/ | ||
getAnyReferrer: (edgeName: string | number, edgeType?: string) => Nullable<IHeapEdge>; | ||
/** | ||
* Given a JS reference's name and type, this API finds one of the incoming JS | ||
* references pointing to the hosting node, and returns the JS heap object | ||
* containing the incoming reference. | ||
* @param edgeName the name of the incoming JavaScript reference | ||
* @param edgeType optional parameter specifying the type of the incoming | ||
* JavaScript reference | ||
* @returns the node containing the incoming JS reference that meets | ||
* the specification | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* // find one of the JS heap object with a JS reference | ||
* // named "ref" pointing to node | ||
* const n1 = node.getAnyReferrerNode('ref', 'property'); | ||
* // this is equivalent to | ||
* const n2 = node.getAnyReferrer('ref', 'property')?.fromNode; | ||
* ``` | ||
*/ | ||
getAnyReferrerNode: (edgeName: string | number, edgeType?: string) => Nullable<IHeapNode>; | ||
/** | ||
* Given a JS reference's name and type, this API finds all the incoming JS | ||
* reference pointing to the hosting node. | ||
* @param edgeName the name of the incoming JavaScript reference | ||
* @param edgeType optional parameter specifying the type of the incoming | ||
* JavaScript reference | ||
* @returns an array containing all the incoming edges that | ||
* meet the specification | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* // find all of of the JS reference named "ref" pointing to node | ||
* const referrers = node.getReferrers('ref', 'property'); | ||
* ``` | ||
*/ | ||
getReferrers: (edgeName: string | number, edgeType?: string) => IHeapEdge[]; | ||
/** | ||
* Given a JS reference's name and type, this API finds all of the incoming JS | ||
* references pointing to the hosting node, and returns an array containing | ||
* the hosting node for each of the incoming JS references. | ||
* @param edgeName the name of the incoming JavaScript reference | ||
* @param edgeType optional parameter specifying the type of the incoming | ||
* JavaScript reference | ||
* @returns an array containing the hosting nodes, with each node corresponds | ||
* to each incoming JS reference that meets the specification | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* // find all of the JS heap object with a JS reference | ||
* // named "ref" pointing to node | ||
* const nodes1 = node.getReferrerNodes('ref', 'property'); | ||
* // this is equivalent to | ||
* const nodes2 = node.getReferrers('ref', 'property') | ||
* .map(edge => edge.fromNode); | ||
* ``` | ||
*/ | ||
getReferrerNodes: (edgeName: string | number, edgeType?: string) => IHeapNode[]; | ||
} | ||
/** | ||
* An `IHeapStringNode` instance represents a JS string in a heap snapshot. | ||
* A heap snapshot is generally a graph where graph nodes are JS heap objects | ||
* and graph edges are JS references among JS heap objects. | ||
* | ||
* @readonly it is not recommended to modify any `IHeapStringNode` instance | ||
* | ||
* * **Examples**: V8 or hermes heap snapshot can be parsed by the | ||
* {@link getHeapFromFile} API. | ||
* | ||
* ```typescript | ||
* import type {IHeapSnapshot, IHeapNode, IHeapStringNode} from '@memlab/core'; | ||
* import {dumpNodeHeapSnapshot} from '@memlab/core'; | ||
* import {getHeapFromFile} from '@memlab/heap-analysis'; | ||
* | ||
* (async function () { | ||
* const heapFile = dumpNodeHeapSnapshot(); | ||
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile); | ||
* | ||
* // iterate over each node (heap object) | ||
* heap.nodes.forEach((node: IHeapNode, i: number) => { | ||
* if (node.isString) { | ||
* const stringNode: IheapStringNode = node.toStringNode(); | ||
* // get the string value | ||
* stringNode.stringValue; | ||
* } | ||
* }); | ||
* })(); | ||
* ``` | ||
*/ | ||
export interface IHeapStringNode extends IHeapNode { | ||
/** | ||
* get the string value of the JS string heap object associated with | ||
* this `IHeapStringNode` instance in heap | ||
*/ | ||
stringValue: string; | ||
} | ||
/** | ||
* A pseudo array containing all heap graph nodes (JS objects | ||
* in heap). A JS heap could contain millions of objects, so memlab uses | ||
* a pseudo array as the collection of all the heap nodes. The pseudo | ||
* array provides API to query and traverse all heap objects. | ||
* | ||
* @readonly modifying this pseudo array is not recommended | ||
* | ||
* * **Examples**: | ||
* ```typescript | ||
* import type {IHeapSnapshot, IHeapNodes} from '@memlab/core'; | ||
* import {dumpNodeHeapSnapshot} from '@memlab/core'; | ||
* import {getHeapFromFile} from '@memlab/heap-analysis'; | ||
* | ||
* (async function () { | ||
* const heapFile = dumpNodeHeapSnapshot(); | ||
* const heap: IHeapSnapshot = await getHeapFromFile(heapFile); | ||
* | ||
* const nodes: IHeapNodes = heap.nodes; | ||
* nodes.length; | ||
* nodes.get(0); | ||
* nodes.forEach((node, i) => { | ||
* if (stopIteration) { | ||
* return false; | ||
* } | ||
* }); | ||
* })(); | ||
* ``` | ||
*/ | ||
export interface IHeapNodes { | ||
/** | ||
* The total number of nodes in heap graph (or JS objects in heap | ||
* snapshot). | ||
*/ | ||
length: number; | ||
get(index: number): IHeapNode; | ||
/** | ||
* get an {@link IHeapNode} element at the specified index | ||
* @param index the index of an element in the pseudo array, the index ranges | ||
* from 0 to array length - 1. Notice that this is not the heap node id. | ||
* @returns When 0 <= `index` < array.length, this API returns the element | ||
* at the specified index, otherwise it returns `null`. | ||
*/ | ||
get(index: number): Nullable<IHeapNode>; | ||
/** | ||
* Iterate over all array elements and apply the callback | ||
* to each element in ascending order of element index. | ||
* @param callback the callback does not need to return any value, if | ||
* the callback returns `false` when iterating on element at index `i`, | ||
* then all elements after `i` won't be iterated. | ||
*/ | ||
forEach(callback: (node: IHeapNode, index: number) => void | boolean): void; | ||
/** @internal */ | ||
forEachTraceable(callback: (node: IHeapNode, index: number) => void | boolean): void; | ||
@@ -900,0 +1553,0 @@ } |
@@ -584,3 +584,3 @@ "use strict"; | ||
} | ||
return node.findReference((edge) => edge.name_or_index === edgeName && | ||
return node.findAnyReference((edge) => edge.name_or_index === edgeName && | ||
(type === undefined || edge.type === type)); | ||
@@ -592,3 +592,3 @@ } | ||
} | ||
return node.findReference(edge => typeof edge.name_or_index === 'string' && | ||
return node.findAnyReference(edge => typeof edge.name_or_index === 'string' && | ||
edge.name_or_index.startsWith(prefix)); | ||
@@ -595,0 +595,0 @@ } |
@@ -162,3 +162,4 @@ "use strict"; | ||
} | ||
const childNodeIndex = forwardEdges.get(edgeIndex).toNode.nodeIndex; | ||
const childNodeIndex = forwardEdges.get(edgeIndex) | ||
.toNode.nodeIndex; | ||
if (visited[childNodeIndex]) { | ||
@@ -282,3 +283,4 @@ continue; | ||
} | ||
const childNodeIndex = forwardEdges.get(edgeIndex).toNode.nodeIndex; | ||
const childNodeIndex = forwardEdges.get(edgeIndex).toNode | ||
.nodeIndex; | ||
nodesWithOutdatedDominatorInfo[nodeIndex2PostOrderIndex[childNodeIndex]] = 1; | ||
@@ -285,0 +287,0 @@ } |
@@ -37,3 +37,3 @@ /** | ||
forEachReferrer(_callback: EdgeIterationCallback): void; | ||
findReference(): Nullable<IHeapEdge>; | ||
findAnyReference(): Nullable<IHeapEdge>; | ||
findAnyReferrer(): Nullable<IHeapEdge>; | ||
@@ -40,0 +40,0 @@ findReferrers(): IHeapEdge[]; |
@@ -73,4 +73,4 @@ "use strict"; | ||
} | ||
findReference() { | ||
throw new Error('NodeRecord.findReference is not implemented'); | ||
findAnyReference() { | ||
throw new Error('NodeRecord.findAnyReference is not implemented'); | ||
} | ||
@@ -77,0 +77,0 @@ findAnyReferrer() { |
{ | ||
"name": "@memlab/core", | ||
"version": "1.1.2", | ||
"version": "1.1.3", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "description": "memlab core libraries", |
@@ -5,3 +5,4 @@ ## memlab Core Library | ||
## Full documentation | ||
https://facebookincubator.github.io/memlab | ||
## Online Resources | ||
* [Official Website and Demo](https://facebookincubator.github.io/memlab) | ||
* [Documentation](https://facebookincubator.github.io/memlab/docs/intro) |
490213
12695
8