@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.
Files changed (55) hide show
  1. package/dist/__tests__/parser/StringNode.test.js +12 -1
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +4 -1
  4. package/dist/lib/Config.d.ts +4 -0
  5. package/dist/lib/Config.js +13 -0
  6. package/dist/lib/FileManager.d.ts +6 -0
  7. package/dist/lib/FileManager.js +56 -4
  8. package/dist/lib/HeapAnalyzer.d.ts +5 -1
  9. package/dist/lib/HeapAnalyzer.js +33 -21
  10. package/dist/lib/RunInfoUtils.d.ts +39 -0
  11. package/dist/lib/RunInfoUtils.js +86 -0
  12. package/dist/lib/Types.d.ts +80 -2
  13. package/dist/lib/Utils.d.ts +30 -20
  14. package/dist/lib/Utils.js +119 -151
  15. package/dist/lib/charts/MemoryBarChart.d.ts +1 -0
  16. package/dist/lib/charts/MemoryBarChart.js +23 -2
  17. package/dist/lib/heap-data/HeapEdge.d.ts +3 -1
  18. package/dist/lib/heap-data/HeapEdge.js +13 -0
  19. package/dist/lib/heap-data/HeapLocation.d.ts +3 -1
  20. package/dist/lib/heap-data/HeapLocation.js +14 -0
  21. package/dist/lib/heap-data/HeapNode.d.ts +6 -1
  22. package/dist/lib/heap-data/HeapNode.js +46 -0
  23. package/dist/lib/heap-data/HeapSnapshot.d.ts +3 -1
  24. package/dist/lib/heap-data/HeapSnapshot.js +7 -0
  25. package/dist/lib/heap-data/HeapStringNode.d.ts +2 -1
  26. package/dist/lib/heap-data/HeapStringNode.js +5 -0
  27. package/dist/lib/trace-filters/BaseTraceFilter.rule.d.ts +29 -0
  28. package/dist/lib/trace-filters/BaseTraceFilter.rule.js +22 -0
  29. package/dist/lib/trace-filters/LeakTraceFilter.d.ts +20 -0
  30. package/dist/lib/trace-filters/LeakTraceFilter.js +37 -0
  31. package/dist/lib/trace-filters/TraceFilterRuleList.d.ts +13 -0
  32. package/dist/lib/trace-filters/TraceFilterRuleList.js +33 -0
  33. package/dist/lib/trace-filters/rules/FilterAttachedDOMToDetachedDOMTrace.rule.d.ts +15 -0
  34. package/dist/lib/trace-filters/rules/FilterAttachedDOMToDetachedDOMTrace.rule.js +55 -0
  35. package/dist/lib/trace-filters/rules/FilterDOMNodeChainTrace.rule.d.ts +15 -0
  36. package/dist/lib/trace-filters/rules/FilterDOMNodeChainTrace.rule.js +41 -0
  37. package/dist/lib/trace-filters/rules/FilterHermesTrace.rule.d.ts +15 -0
  38. package/dist/lib/trace-filters/rules/FilterHermesTrace.rule.js +29 -0
  39. package/dist/lib/trace-filters/rules/FilterInternalNodeTrace.rule.d.ts +15 -0
  40. package/dist/lib/trace-filters/rules/FilterInternalNodeTrace.rule.js +57 -0
  41. package/dist/lib/trace-filters/rules/FilterPendingActivitiesTrace.rule.d.ts +15 -0
  42. package/dist/lib/trace-filters/rules/FilterPendingActivitiesTrace.rule.js +62 -0
  43. package/dist/lib/trace-filters/rules/FilterShadowRootTrace.rule.d.ts +15 -0
  44. package/dist/lib/trace-filters/rules/FilterShadowRootTrace.rule.js +44 -0
  45. package/dist/lib/trace-filters/rules/FilterStyleEngineTrace.rule.d.ts +15 -0
  46. package/dist/lib/trace-filters/rules/FilterStyleEngineTrace.rule.js +49 -0
  47. package/dist/logger/LeakClusterLogger.js +1 -0
  48. package/dist/paths/TraceFinder.js +16 -2
  49. package/dist/trace-cluster/TraceBucket.d.ts +2 -1
  50. package/dist/trace-cluster/TraceBucket.js +22 -4
  51. package/dist/trace-cluster/TraceElement.d.ts +6 -1
  52. package/dist/trace-cluster/TraceElement.js +33 -0
  53. package/dist/trace-cluster/strategies/TraceSimilarityStrategy.js +10 -0
  54. package/package.json +1 -1
  55. 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(edge.fromNode) &&
433
- Utils_1.default.isDOMNodeIncomplete(edge.toNode)) {
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(controlPaths: LeakTracePathItem[], controlSnapshot: IHeapSnapshot, treatmentPaths: LeakTracePathItem[], treatmentSnapshot: IHeapSnapshot, aggregateDominatorMetrics: AggregateNodeCb, option?: {
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: ${Utils_1.default.getReadablePercent(sampleRatio)}`);
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(controlPaths, controlSnapshot, treatmentPaths, treatmentSnapshot, aggregateDominatorMetrics, option = {}) {
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
- if (controlPaths.length === 0 && treatmentPaths.length === 0) {
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
- controlPaths = this.samplePaths(controlPaths);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memlab/core",
3
- "version": "1.1.19",
3
+ "version": "1.1.21",
4
4
  "license": "MIT",
5
5
  "description": "memlab core libraries",
6
6
  "author": "Liang Gong <lgong@fb.com>",
@@ -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
+ ]