@memlab/core
Advanced tools
Comparing version 1.1.9 to 1.1.10
@@ -209,3 +209,3 @@ /** | ||
get browserBinaryPath(): string; | ||
set reportLeaksInTimers(flag: boolean); | ||
set reportLeaksInTimers(shouldReport: boolean); | ||
get reportLeaksInTimers(): boolean; | ||
@@ -212,0 +212,0 @@ setDevice(deviceName: string, options?: { |
@@ -53,3 +53,3 @@ "use strict"; | ||
this._isFullRun = false; | ||
this._reportLeaksInTimers = false; | ||
this._reportLeaksInTimers = true; | ||
this._deviceManualOverridden = false; | ||
@@ -317,3 +317,3 @@ this._timerNodes = ['Pending activities']; | ||
const config = new MemLabConfig(); | ||
// do not consider objects kept alive by timers as leaks | ||
// consider objects kept alive by timers as leaks | ||
config.reportLeaksInTimers = true; | ||
@@ -401,7 +401,4 @@ // assign configuration to console manager | ||
} | ||
set reportLeaksInTimers(flag) { | ||
if (typeof flag !== 'boolean') { | ||
return; | ||
} | ||
if (flag) { | ||
set reportLeaksInTimers(shouldReport) { | ||
if (shouldReport) { | ||
this.removeFromSet(this.nodeNameBlockList, this._timerNodes); | ||
@@ -414,3 +411,3 @@ this.removeFromSet(this.edgeNameBlockList, this._timerEdges); | ||
} | ||
this._reportLeaksInTimers = flag; | ||
this._reportLeaksInTimers = shouldReport; | ||
} | ||
@@ -417,0 +414,0 @@ get reportLeaksInTimers() { |
@@ -7,4 +7,4 @@ /** | ||
* | ||
* @emails oncall+ws_labs | ||
* @format | ||
* @oncall ws_labs | ||
*/ | ||
@@ -11,0 +11,0 @@ import type { AnyValue, IHeapSnapshot } from '../Types'; |
@@ -8,4 +8,4 @@ "use strict"; | ||
* | ||
* @emails oncall+ws_labs | ||
* @format | ||
* @oncall ws_labs | ||
*/ | ||
@@ -12,0 +12,0 @@ Object.defineProperty(exports, "__esModule", { value: true }); |
@@ -44,2 +44,10 @@ /** | ||
}>; | ||
/** | ||
* Given a set of heap object ids, cluster them based on the similarity | ||
* of their retainer traces and return a | ||
* @param leakedNodeIds | ||
* @param snapshot | ||
* @returns | ||
*/ | ||
clusterHeapObjects(objectIds: HeapNodeIdSet, snapshot: IHeapSnapshot): TraceCluster[]; | ||
serializeClusterUpdate(clusters: TraceCluster[], options?: { | ||
@@ -46,0 +54,0 @@ reclusterOnly?: boolean; |
@@ -650,2 +650,3 @@ /** | ||
}); | ||
Console_1.default.midLevel(`MemLab found ${clusters.length} leak(s)`); | ||
yield this.serializeClusterUpdate(clusters); | ||
@@ -662,2 +663,32 @@ if (Config_1.default.logUnclassifiedClusters) { | ||
} | ||
/** | ||
* Given a set of heap object ids, cluster them based on the similarity | ||
* of their retainer traces and return a | ||
* @param leakedNodeIds | ||
* @param snapshot | ||
* @returns | ||
*/ | ||
clusterHeapObjects(objectIds, snapshot) { | ||
const finder = this.preparePathFinder(snapshot); | ||
const paths = []; | ||
let i = 0; | ||
// analysis for each node | ||
Utils_1.default.applyToNodes(objectIds, snapshot, node => { | ||
if (++i % 11 === 0) { | ||
Console_1.default.overwrite(`progress: ${i} / ${objectIds.size} @${node.id}`); | ||
} | ||
// BFS search for path from the leaked node to GC roots | ||
const p = finder.getPathToGCRoots(snapshot, node); | ||
if (p) { | ||
paths.push(p); | ||
} | ||
}, { reverse: true }); | ||
// cluster traces from the current run | ||
const clusters = TraceBucket_1.default.clusterPaths(paths, snapshot, this.aggregateDominatorMetrics, { | ||
strategy: Config_1.default.isMLClustering | ||
? new MLTraceSimilarityStrategy_1.default() | ||
: undefined, | ||
}); | ||
return clusters; | ||
} | ||
serializeClusterUpdate(clusters, options = {}) { | ||
@@ -664,0 +695,0 @@ return __awaiter(this, void 0, void 0, function* () { |
@@ -1231,3 +1231,3 @@ /** | ||
stop: boolean; | ||
}>; | ||
}> | void; | ||
/** | ||
@@ -1234,0 +1234,0 @@ * An `IHeapNode` instance represents a JS heap object in a heap snapshot. |
@@ -25,2 +25,3 @@ /** | ||
declare function isPendingActivityNode(node: IHeapNode): boolean; | ||
declare function isDOMNodeIncomplete(node: IHeapNode): boolean; | ||
declare function isRootNode(node: IHeapNode, opt?: AnyOptions): boolean; | ||
@@ -175,2 +176,3 @@ declare function isDirectPropEdge(edge: IHeapEdge): boolean; | ||
isDocumentDOMTreesRoot: typeof isDocumentDOMTreesRoot; | ||
isDOMNodeIncomplete: typeof isDOMNodeIncomplete; | ||
isEssentialEdge: typeof isEssentialEdge; | ||
@@ -177,0 +179,0 @@ isFiberNode: typeof isFiberNode; |
@@ -215,2 +215,13 @@ "use strict"; | ||
} | ||
// check the node against a curated list of known HTML Elements | ||
// the list may be incomplete | ||
function isDOMNodeIncomplete(node) { | ||
let name = node.name; | ||
const pattern = /^HTML.*Element$/; | ||
const detachedPrefix = 'Detached '; | ||
if (name.startsWith(detachedPrefix)) { | ||
name = name.substring(detachedPrefix.length); | ||
} | ||
return pattern.test(name); | ||
} | ||
function isRootNode(node, opt = {}) { | ||
@@ -362,6 +373,7 @@ if (!node) { | ||
} | ||
// given a set of nodes S, return a subset S' where | ||
// given a set of nodes S, return a minimal subset S' where | ||
// no nodes are dominated by nodes in S | ||
function getConditionalDominatorIds(ids, snapshot, condCb) { | ||
const dominatorIds = new Set(); | ||
const fullDominatorIds = new Set(); | ||
// set all node ids | ||
@@ -371,2 +383,3 @@ applyToNodes(ids, snapshot, node => { | ||
dominatorIds.add(node.id); | ||
fullDominatorIds.add(node.id); | ||
} | ||
@@ -383,3 +396,3 @@ }); | ||
} | ||
if (dominatorIds.has(cur.id)) { | ||
if (fullDominatorIds.has(cur.id)) { | ||
dominatorIds.delete(node.id); | ||
@@ -1740,2 +1753,3 @@ break; | ||
isDocumentDOMTreesRoot, | ||
isDOMNodeIncomplete, | ||
isEssentialEdge, | ||
@@ -1742,0 +1756,0 @@ isFiberNode, |
@@ -391,6 +391,14 @@ "use strict"; | ||
const toNode = edge.toNode; | ||
return (Config_1.default.hideBrowserLeak && | ||
(Utils_1.default.isBlinkRootNode(fromNode) || | ||
Utils_1.default.isPendingActivityNode(fromNode)) && | ||
Utils_1.default.isDetachedDOMNode(toNode)); | ||
const isDetachedNode = Utils_1.default.isDetachedDOMNode(toNode); | ||
if (Config_1.default.hideBrowserLeak && | ||
Utils_1.default.isBlinkRootNode(fromNode) && | ||
isDetachedNode) { | ||
return true; | ||
} | ||
if (!Config_1.default.reportLeaksInTimers && | ||
Utils_1.default.isPendingActivityNode(fromNode) && | ||
isDetachedNode) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
@@ -415,5 +423,16 @@ shouldTraverseEdge(edge, options = {}) { | ||
} | ||
if (Config_1.default.nodeNameBlockList.has(edge.toNode.name)) { | ||
return true; | ||
} | ||
if (Config_1.default.nodeNameBlockList.has(edge.fromNode.name)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
isLessPreferableEdge(edge) { | ||
// pending activities -> DOM element is less preferrable | ||
if (Utils_1.default.isPendingActivityNode(edge.fromNode) && | ||
Utils_1.default.isDOMNodeIncomplete(edge.toNode)) { | ||
return true; | ||
} | ||
return Config_1.default.edgeNameGreyList.has(String(edge.name_or_index)); | ||
@@ -420,0 +439,0 @@ } |
@@ -214,4 +214,5 @@ "use strict"; | ||
let clusters = allClusters.map((traces) => { | ||
const representativeTrace = traces[0]; | ||
const cluster = { | ||
path: traceToPathMap.get(traces[0]), | ||
path: traceToPathMap.get(representativeTrace), | ||
count: traces.length, | ||
@@ -221,2 +222,7 @@ snapshot, | ||
}; | ||
// add representative object id if there is one | ||
const lastNode = representativeTrace[representativeTrace.length - 1]; | ||
if ('id' in lastNode) { | ||
cluster.id = lastNode.id; | ||
} | ||
traces.forEach((trace) => { | ||
@@ -230,3 +236,2 @@ NormalizedTrace.addLeakedNodeToCluster(cluster, traceToPathMap.get(trace)); | ||
clusters.sort((c1, c2) => { var _a, _b; return ((_a = c2.retainedSize) !== null && _a !== void 0 ? _a : 0) - ((_b = c1.retainedSize) !== null && _b !== void 0 ? _b : 0); }); | ||
Console_1.default.midLevel(`MemLab found ${clusters.length} leak(s)`); | ||
return clusters; | ||
@@ -233,0 +238,0 @@ } |
{ | ||
"name": "@memlab/core", | ||
"version": "1.1.9", | ||
"version": "1.1.10", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "description": "memlab core libraries", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
533495
13847