@memlab/core 1.1.19 → 1.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/parser/StringNode.test.js +12 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -1
- package/dist/lib/Config.d.ts +4 -0
- package/dist/lib/Config.js +13 -0
- package/dist/lib/FileManager.d.ts +6 -0
- package/dist/lib/FileManager.js +56 -4
- package/dist/lib/HeapAnalyzer.d.ts +5 -1
- package/dist/lib/HeapAnalyzer.js +33 -21
- package/dist/lib/RunInfoUtils.d.ts +39 -0
- package/dist/lib/RunInfoUtils.js +86 -0
- package/dist/lib/Types.d.ts +80 -2
- package/dist/lib/Utils.d.ts +30 -20
- package/dist/lib/Utils.js +119 -151
- package/dist/lib/charts/MemoryBarChart.d.ts +1 -0
- package/dist/lib/charts/MemoryBarChart.js +23 -2
- package/dist/lib/heap-data/HeapEdge.d.ts +3 -1
- package/dist/lib/heap-data/HeapEdge.js +13 -0
- package/dist/lib/heap-data/HeapLocation.d.ts +3 -1
- package/dist/lib/heap-data/HeapLocation.js +14 -0
- package/dist/lib/heap-data/HeapNode.d.ts +6 -1
- package/dist/lib/heap-data/HeapNode.js +46 -0
- package/dist/lib/heap-data/HeapSnapshot.d.ts +3 -1
- package/dist/lib/heap-data/HeapSnapshot.js +7 -0
- package/dist/lib/heap-data/HeapStringNode.d.ts +2 -1
- package/dist/lib/heap-data/HeapStringNode.js +5 -0
- package/dist/lib/trace-filters/BaseTraceFilter.rule.d.ts +29 -0
- package/dist/lib/trace-filters/BaseTraceFilter.rule.js +22 -0
- package/dist/lib/trace-filters/LeakTraceFilter.d.ts +20 -0
- package/dist/lib/trace-filters/LeakTraceFilter.js +37 -0
- package/dist/lib/trace-filters/TraceFilterRuleList.d.ts +13 -0
- package/dist/lib/trace-filters/TraceFilterRuleList.js +33 -0
- package/dist/lib/trace-filters/rules/FilterAttachedDOMToDetachedDOMTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterAttachedDOMToDetachedDOMTrace.rule.js +55 -0
- package/dist/lib/trace-filters/rules/FilterDOMNodeChainTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterDOMNodeChainTrace.rule.js +41 -0
- package/dist/lib/trace-filters/rules/FilterHermesTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterHermesTrace.rule.js +29 -0
- package/dist/lib/trace-filters/rules/FilterInternalNodeTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterInternalNodeTrace.rule.js +57 -0
- package/dist/lib/trace-filters/rules/FilterPendingActivitiesTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterPendingActivitiesTrace.rule.js +62 -0
- package/dist/lib/trace-filters/rules/FilterShadowRootTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterShadowRootTrace.rule.js +44 -0
- package/dist/lib/trace-filters/rules/FilterStyleEngineTrace.rule.d.ts +15 -0
- package/dist/lib/trace-filters/rules/FilterStyleEngineTrace.rule.js +49 -0
- package/dist/logger/LeakClusterLogger.js +1 -0
- package/dist/paths/TraceFinder.js +16 -2
- package/dist/trace-cluster/TraceBucket.d.ts +2 -1
- package/dist/trace-cluster/TraceBucket.js +22 -4
- package/dist/trace-cluster/TraceElement.d.ts +6 -1
- package/dist/trace-cluster/TraceElement.js +33 -0
- package/dist/trace-cluster/strategies/TraceSimilarityStrategy.js +10 -0
- package/package.json +1 -1
- package/static/visit-order-single-snapshot.json +19 -0
|
@@ -187,6 +187,7 @@ class LeakClusterLogger {
|
|
|
187
187
|
leak_trace_summary: trace.getTraceSummary(),
|
|
188
188
|
interaction_vector: interactionVector,
|
|
189
189
|
meta_data: JSON.stringify({
|
|
190
|
+
extraRunInfo: Utils_1.default.mapToObject(Config_1.default.extraRunInfoMap),
|
|
190
191
|
browser_info: BrowserInfo_1.default,
|
|
191
192
|
visit_plan: tabsOrder,
|
|
192
193
|
trace_record: TraceBucket_1.default.pathToTrace(cluster.path),
|
|
@@ -428,9 +428,23 @@ class TraceFinder {
|
|
|
428
428
|
return false;
|
|
429
429
|
}
|
|
430
430
|
isLessPreferableEdge(edge) {
|
|
431
|
+
const fromNode = edge.fromNode;
|
|
432
|
+
const toNode = edge.toNode;
|
|
431
433
|
// pending activities -> DOM element is less preferrable
|
|
432
|
-
if (Utils_1.default.isPendingActivityNode(
|
|
433
|
-
Utils_1.default.isDOMNodeIncomplete(
|
|
434
|
+
if (Utils_1.default.isPendingActivityNode(fromNode) &&
|
|
435
|
+
Utils_1.default.isDOMNodeIncomplete(toNode)) {
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
// detached DOM node -> non-detached DOM node is less preferable
|
|
439
|
+
if (Utils_1.default.isDetachedDOMNode(fromNode) &&
|
|
440
|
+
Utils_1.default.isDOMNodeIncomplete(toNode) &&
|
|
441
|
+
!Utils_1.default.isDetachedDOMNode(toNode)) {
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
// non-detached DOM node -> detached DOM node is less preferable
|
|
445
|
+
if (Utils_1.default.isDOMNodeIncomplete(fromNode) &&
|
|
446
|
+
!Utils_1.default.isDetachedDOMNode(fromNode) &&
|
|
447
|
+
Utils_1.default.isDetachedDOMNode(toNode)) {
|
|
434
448
|
return true;
|
|
435
449
|
}
|
|
436
450
|
return Config_1.default.edgeNameGreyList.has(String(edge.name_or_index));
|
|
@@ -24,6 +24,7 @@ export default class NormalizedTrace {
|
|
|
24
24
|
getTraceSummary(): string;
|
|
25
25
|
static addLeakedNodeToCluster(cluster: TraceCluster, path: LeakTracePathItem): void;
|
|
26
26
|
static calculateClusterRetainedSize(cluster: TraceCluster, snapshot: IHeapSnapshot, aggregateDominatorMetrics: AggregateNodeCb): number;
|
|
27
|
+
static getSamplePathMaxLength(paths: LeakTracePathItem[]): number;
|
|
27
28
|
static samplePaths(paths: LeakTracePathItem[]): LeakTracePathItem[];
|
|
28
29
|
private static diffTraces;
|
|
29
30
|
static diffClusters(newClusters: TraceCluster[], existingClusters: TraceCluster[]): TraceClusterDiff;
|
|
@@ -36,7 +37,7 @@ export default class NormalizedTrace {
|
|
|
36
37
|
private static buildTraceToPathMap;
|
|
37
38
|
private static pushLeakPathToCluster;
|
|
38
39
|
private static initEmptyCluster;
|
|
39
|
-
static clusterControlTreatmentPaths(
|
|
40
|
+
static clusterControlTreatmentPaths(leakPathsFromControlRuns: LeakTracePathItem[][], controlSnapshots: IHeapSnapshot[], treatmentPaths: LeakTracePathItem[], treatmentSnapshot: IHeapSnapshot, aggregateDominatorMetrics: AggregateNodeCb, option?: {
|
|
40
41
|
strategy?: IClusterStrategy;
|
|
41
42
|
}): ControlTreatmentClusterResult;
|
|
42
43
|
static generateUnClassifiedClusters(paths: LeakTracePathItem[], snapshot: IHeapSnapshot, aggregateDominatorMetrics: AggregateNodeCb): TraceCluster[];
|
|
@@ -122,16 +122,30 @@ class NormalizedTrace {
|
|
|
122
122
|
}
|
|
123
123
|
return (cluster.retainedSize = aggregateDominatorMetrics(cluster.leakedNodeIds, snapshot, () => true, (node) => node.retainedSize));
|
|
124
124
|
}
|
|
125
|
+
static getSamplePathMaxLength(paths) {
|
|
126
|
+
const lengthArr = paths.map(p => Utils_1.default.getLeakTracePathLength(p));
|
|
127
|
+
return Math.max(30, Utils_1.default.getNumberAtPercentile(lengthArr, 80));
|
|
128
|
+
}
|
|
125
129
|
static samplePaths(paths) {
|
|
126
130
|
const maxCount = 5000;
|
|
131
|
+
if (paths.length <= maxCount) {
|
|
132
|
+
return [...paths];
|
|
133
|
+
}
|
|
127
134
|
const sampleRatio = Math.min(1, maxCount / paths.length);
|
|
128
135
|
if (sampleRatio < 1) {
|
|
129
136
|
Console_1.default.warning('Sampling trace due to a large number of traces:');
|
|
130
137
|
Console_1.default.lowLevel(` Number of Traces: ${paths.length}`);
|
|
131
|
-
Console_1.default.lowLevel(` Sampling Ratio:
|
|
138
|
+
Console_1.default.lowLevel(` Sampling Ratio: ${Utils_1.default.getReadablePercent(sampleRatio)}`);
|
|
132
139
|
}
|
|
133
140
|
const ret = [];
|
|
141
|
+
const samplePathMaxLength = NormalizedTrace.getSamplePathMaxLength(paths);
|
|
142
|
+
if (Config_1.default.verbose) {
|
|
143
|
+
Console_1.default.lowLevel(` Sample Trace's Max Length: ${samplePathMaxLength}`);
|
|
144
|
+
}
|
|
134
145
|
for (const p of paths) {
|
|
146
|
+
if (Utils_1.default.getLeakTracePathLength(p) > samplePathMaxLength) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
135
149
|
if (Math.random() < sampleRatio) {
|
|
136
150
|
ret.push(p);
|
|
137
151
|
}
|
|
@@ -294,19 +308,21 @@ class NormalizedTrace {
|
|
|
294
308
|
leakedNodeIds: new Set(),
|
|
295
309
|
};
|
|
296
310
|
}
|
|
297
|
-
static clusterControlTreatmentPaths(
|
|
311
|
+
static clusterControlTreatmentPaths(leakPathsFromControlRuns, controlSnapshots, treatmentPaths, treatmentSnapshot, aggregateDominatorMetrics, option = {}) {
|
|
298
312
|
const result = {
|
|
299
313
|
controlOnlyClusters: [],
|
|
300
314
|
treatmentOnlyClusters: [],
|
|
301
315
|
hybridClusters: [],
|
|
302
316
|
};
|
|
303
317
|
Console_1.default.overwrite('Clustering leak traces');
|
|
304
|
-
|
|
318
|
+
const totalControlPaths = leakPathsFromControlRuns.reduce((count, leakPaths) => count + leakPaths.length, 0);
|
|
319
|
+
if (totalControlPaths === 0 && treatmentPaths.length === 0) {
|
|
305
320
|
Console_1.default.midLevel('No leaks found');
|
|
306
321
|
return result;
|
|
307
322
|
}
|
|
308
323
|
// sample paths if there are too many
|
|
309
|
-
|
|
324
|
+
const flattenedLeakPathsFromControlRuns = leakPathsFromControlRuns.reduce((arr, leakPaths) => [...arr, ...leakPaths], []);
|
|
325
|
+
const controlPaths = this.samplePaths(flattenedLeakPathsFromControlRuns);
|
|
310
326
|
treatmentPaths = this.samplePaths(treatmentPaths);
|
|
311
327
|
// build control trace to control path map
|
|
312
328
|
const controlTraceToPathMap = NormalizedTrace.buildTraceToPathMap(controlPaths);
|
|
@@ -316,6 +332,8 @@ class NormalizedTrace {
|
|
|
316
332
|
const treatmentTraces = Array.from(treatmentTraceToPathMap.keys());
|
|
317
333
|
// cluster traces from both the control group and the treatment group
|
|
318
334
|
const { allClusters } = NormalizedTrace.diffTraces([...controlTraces, ...treatmentTraces], [], option);
|
|
335
|
+
// pick one of the control heap snapshots
|
|
336
|
+
const controlSnapshot = controlSnapshots[0];
|
|
319
337
|
// construct TraceCluster from clustering result
|
|
320
338
|
allClusters.forEach((traces) => {
|
|
321
339
|
var _a, _b;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @format
|
|
8
8
|
* @oncall web_perf_infra
|
|
9
9
|
*/
|
|
10
|
-
import type { EdgeIterationCallback, IHeapEdge, IHeapLocation, IHeapNode, IHeapSnapshot, IHeapStringNode, Nullable } from '../lib/Types';
|
|
10
|
+
import type { AnyValue, EdgeIterationCallback, IHeapEdge, IHeapLocation, IHeapNode, IHeapSnapshot, IHeapStringNode, Nullable } from '../lib/Types';
|
|
11
11
|
export declare class NodeRecord implements IHeapNode {
|
|
12
12
|
kind: string;
|
|
13
13
|
name: string;
|
|
@@ -32,11 +32,14 @@ export declare class NodeRecord implements IHeapNode {
|
|
|
32
32
|
forEachReference(_callback: EdgeIterationCallback): void;
|
|
33
33
|
set referrers(r: IHeapEdge[]);
|
|
34
34
|
get referrers(): IHeapEdge[];
|
|
35
|
+
get numOfReferrers(): number;
|
|
35
36
|
toStringNode(): IHeapStringNode;
|
|
36
37
|
forEachReferrer(_callback: EdgeIterationCallback): void;
|
|
37
38
|
findAnyReference(): Nullable<IHeapEdge>;
|
|
38
39
|
findAnyReferrer(): Nullable<IHeapEdge>;
|
|
40
|
+
findAnyReferrerNode(): Nullable<IHeapNode>;
|
|
39
41
|
findReferrers(): IHeapEdge[];
|
|
42
|
+
findReferrerNodes(): IHeapNode[];
|
|
40
43
|
set hasPathEdge(f: boolean);
|
|
41
44
|
get hasPathEdge(): boolean;
|
|
42
45
|
set pathEdge(r: IHeapEdge);
|
|
@@ -51,6 +54,7 @@ export declare class NodeRecord implements IHeapNode {
|
|
|
51
54
|
getAnyReferrerNode(_edgeName: string | number, _edgeType?: string): Nullable<IHeapNode>;
|
|
52
55
|
getReferrers(_edgeName: string | number, _edgeType?: string): IHeapEdge[];
|
|
53
56
|
getReferrerNodes(_edgeName: string | number, _edgeType?: string): IHeapNode[];
|
|
57
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
54
58
|
constructor(node: IHeapNode);
|
|
55
59
|
private extraceNodeName;
|
|
56
60
|
}
|
|
@@ -62,6 +66,7 @@ export declare class EdgeRecord implements IHeapEdge {
|
|
|
62
66
|
is_index: boolean;
|
|
63
67
|
to_node: number;
|
|
64
68
|
constructor(edge: IHeapEdge);
|
|
69
|
+
toJSONString(...args: Array<AnyValue>): string;
|
|
65
70
|
set snapshot(s: IHeapSnapshot);
|
|
66
71
|
get snapshot(): IHeapSnapshot;
|
|
67
72
|
set toNode(s: IHeapNode);
|
|
@@ -62,6 +62,9 @@ class NodeRecord {
|
|
|
62
62
|
get referrers() {
|
|
63
63
|
throw new Error('NodeRecord.referrers cannot be read');
|
|
64
64
|
}
|
|
65
|
+
get numOfReferrers() {
|
|
66
|
+
throw new Error('NodeRecord.numOfReferrers cannot be read');
|
|
67
|
+
}
|
|
65
68
|
toStringNode() {
|
|
66
69
|
throw new Error('NodeRecord.toStringNode is not implemented');
|
|
67
70
|
}
|
|
@@ -76,9 +79,15 @@ class NodeRecord {
|
|
|
76
79
|
findAnyReferrer() {
|
|
77
80
|
throw new Error('NodeRecord.findAnyReferrer is not implemented');
|
|
78
81
|
}
|
|
82
|
+
findAnyReferrerNode() {
|
|
83
|
+
throw new Error('NodeRecord.findAnyReferrerNode is not implemented');
|
|
84
|
+
}
|
|
79
85
|
findReferrers() {
|
|
80
86
|
throw new Error('NodeRecord.findReferrers is not implemented');
|
|
81
87
|
}
|
|
88
|
+
findReferrerNodes() {
|
|
89
|
+
throw new Error('NodeRecord.findReferrerNodes is not implemented');
|
|
90
|
+
}
|
|
82
91
|
set hasPathEdge(f) {
|
|
83
92
|
throw new Error('NodeRecord.hasPathEdge cannot be assigned');
|
|
84
93
|
}
|
|
@@ -145,6 +154,20 @@ class NodeRecord {
|
|
|
145
154
|
_edgeType) {
|
|
146
155
|
throw new Error('NodeRecord.getReferrerNodes is not implemented');
|
|
147
156
|
}
|
|
157
|
+
toJSONString(...args) {
|
|
158
|
+
const rep = {
|
|
159
|
+
id: this.id,
|
|
160
|
+
kind: this.kind,
|
|
161
|
+
name: this.name,
|
|
162
|
+
type: this.type,
|
|
163
|
+
self_size: this.self_size,
|
|
164
|
+
trace_node_id: this.trace_node_id,
|
|
165
|
+
nodeIndex: this.nodeIndex,
|
|
166
|
+
incomingEdgeCount: this.numOfReferrers,
|
|
167
|
+
contructorName: this.constructor.name,
|
|
168
|
+
};
|
|
169
|
+
return JSON.stringify(rep, ...args);
|
|
170
|
+
}
|
|
148
171
|
extraceNodeName(node) {
|
|
149
172
|
// deserialized node may not have snapshot info
|
|
150
173
|
if (!node.snapshot || !Utils_1.default.isFiberNode(node)) {
|
|
@@ -163,6 +186,16 @@ class EdgeRecord {
|
|
|
163
186
|
this.is_index = edge.is_index;
|
|
164
187
|
this.to_node = edge.to_node;
|
|
165
188
|
}
|
|
189
|
+
toJSONString(...args) {
|
|
190
|
+
const rep = {
|
|
191
|
+
kind: this.kind,
|
|
192
|
+
name_or_index: this.name_or_index,
|
|
193
|
+
type: this.type,
|
|
194
|
+
edgeIndex: this.edgeIndex,
|
|
195
|
+
to_node: this.to_node,
|
|
196
|
+
};
|
|
197
|
+
return JSON.stringify(rep, ...args);
|
|
198
|
+
}
|
|
166
199
|
set snapshot(s) {
|
|
167
200
|
throw new Error('EdgeRecord.snapshot cannot be assigned.');
|
|
168
201
|
}
|
|
@@ -12,6 +12,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const Config_1 = __importDefault(require("../../lib/Config"));
|
|
16
|
+
const Console_1 = __importDefault(require("../../lib/Console"));
|
|
15
17
|
const ClusterUtils_1 = __importDefault(require("../ClusterUtils"));
|
|
16
18
|
// cluster by putting similar traces together
|
|
17
19
|
class TraceSimilarityStrategy {
|
|
@@ -41,6 +43,13 @@ class TraceSimilarityStrategy {
|
|
|
41
43
|
// checking new clusters
|
|
42
44
|
outer: for (let i = 0; i < newTraces.length; ++i) {
|
|
43
45
|
const traceToCheck = newTraces[i];
|
|
46
|
+
// use an odd number as the divider. If we choose 10 as the divider,
|
|
47
|
+
// when updating the progress indicator, the final digit always ends
|
|
48
|
+
// with a zero, which can appear strange and not representative of
|
|
49
|
+
// the actual progress.
|
|
50
|
+
if (!Config_1.default.isContinuousTest && i % 17 === 0) {
|
|
51
|
+
Console_1.default.overwrite(`clustering trace: ${i} / ${newTraces.length}`);
|
|
52
|
+
}
|
|
44
53
|
for (let j = 0; j < clusters.length; ++j) {
|
|
45
54
|
const repTrace = clusters[j][0];
|
|
46
55
|
if (TraceSimilarityStrategy.isSimilarTrace(repTrace, traceToCheck)) {
|
|
@@ -51,6 +60,7 @@ class TraceSimilarityStrategy {
|
|
|
51
60
|
clustersToAdd.push(traceToCheck);
|
|
52
61
|
clusters.push([traceToCheck]);
|
|
53
62
|
}
|
|
63
|
+
Console_1.default.overwrite('');
|
|
54
64
|
return { staleClusters, clustersToAdd, allClusters: clusters };
|
|
55
65
|
}
|
|
56
66
|
static isSimilarTrace(t1, t2) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "target",
|
|
4
|
+
"url": "",
|
|
5
|
+
"snapshot": true,
|
|
6
|
+
"screenshot": false,
|
|
7
|
+
"type": "target",
|
|
8
|
+
"idx": 1
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "final",
|
|
12
|
+
"url": "",
|
|
13
|
+
"delay": 10000,
|
|
14
|
+
"snapshot": true,
|
|
15
|
+
"screenshot": false,
|
|
16
|
+
"type": "final",
|
|
17
|
+
"idx": 2
|
|
18
|
+
}
|
|
19
|
+
]
|