@memlab/core 1.1.21 → 1.1.23

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 (34) hide show
  1. package/dist/__tests__/lib/TestUtils.d.ts +13 -0
  2. package/dist/__tests__/lib/TestUtils.js +39 -0
  3. package/dist/lib/Config.d.ts +10 -1
  4. package/dist/lib/Config.js +21 -2
  5. package/dist/lib/Constant.js +1 -0
  6. package/dist/lib/FileManager.js +1 -1
  7. package/dist/lib/HeapAnalyzer.js +49 -27
  8. package/dist/lib/RunInfoUtils.d.ts +1 -0
  9. package/dist/lib/RunInfoUtils.js +8 -4
  10. package/dist/lib/SerializationHelper.d.ts +18 -0
  11. package/dist/lib/SerializationHelper.js +36 -0
  12. package/dist/lib/Serializer.d.ts +2 -6
  13. package/dist/lib/Serializer.js +12 -3
  14. package/dist/lib/TraceSampler.d.ts +36 -0
  15. package/dist/lib/TraceSampler.js +78 -0
  16. package/dist/lib/Types.d.ts +26 -3
  17. package/dist/lib/Utils.d.ts +8 -0
  18. package/dist/lib/Utils.js +74 -6
  19. package/dist/lib/charts/MemoryBarChart.js +2 -2
  20. package/dist/lib/heap-data/HeapStringNode.js +25 -16
  21. package/dist/lib/leak-filters/LeakFilterRuleList.js +2 -0
  22. package/dist/lib/leak-filters/rules/FilterDetachedDOMElement.rule.d.ts +0 -1
  23. package/dist/lib/leak-filters/rules/FilterDetachedDOMElement.rule.js +22 -9
  24. package/dist/lib/leak-filters/rules/FilterOverSizedNodeAsLeak.rule.js +50 -0
  25. package/dist/lib/leak-filters/rules/FilterUnmountedFiberNode.rule.js +4 -3
  26. package/dist/lib/leak-filters/rules/FilterXMLHTTPRequest.rule.d.ts +20 -0
  27. package/dist/lib/leak-filters/rules/FilterXMLHTTPRequest.rule.js +30 -0
  28. package/dist/lib/trace-filters/TraceFilterRuleList.js +2 -0
  29. package/dist/lib/trace-filters/rules/FilterCppRootsToDetachedDOMTrace.rule.d.ts +15 -0
  30. package/dist/lib/trace-filters/rules/FilterCppRootsToDetachedDOMTrace.rule.js +44 -0
  31. package/dist/paths/TraceFinder.js +1 -1
  32. package/dist/trace-cluster/TraceBucket.d.ts +1 -1
  33. package/dist/trace-cluster/TraceBucket.js +54 -22
  34. package/package.json +1 -1
@@ -22,6 +22,7 @@ const TraceSimilarityStrategy_1 = __importDefault(require("./strategies/TraceSim
22
22
  const TraceAsClusterStrategy_1 = __importDefault(require("./strategies/TraceAsClusterStrategy"));
23
23
  const MLTraceSimilarityStrategy_1 = __importDefault(require("./strategies/MLTraceSimilarityStrategy"));
24
24
  const ClusterUtils_1 = require("./ClusterUtils");
25
+ const TraceSampler_1 = __importDefault(require("../lib/TraceSampler"));
25
26
  // sync up with html/intern/js/webspeed/memlab/lib/LeakCluster.js
26
27
  class NormalizedTrace {
27
28
  constructor(p = null, snapshot = null) {
@@ -127,26 +128,19 @@ class NormalizedTrace {
127
128
  return Math.max(30, Utils_1.default.getNumberAtPercentile(lengthArr, 80));
128
129
  }
129
130
  static samplePaths(paths) {
130
- const maxCount = 5000;
131
+ const maxCount = Config_1.default.maxSamplesForClustering;
131
132
  if (paths.length <= maxCount) {
132
133
  return [...paths];
133
134
  }
134
- const sampleRatio = Math.min(1, maxCount / paths.length);
135
- if (sampleRatio < 1) {
136
- Console_1.default.warning('Sampling trace due to a large number of traces:');
137
- Console_1.default.lowLevel(` Number of Traces: ${paths.length}`);
138
- Console_1.default.lowLevel(` Sampling Ratio: ${Utils_1.default.getReadablePercent(sampleRatio)}`);
139
- }
135
+ const sampler = new TraceSampler_1.default(paths.length);
140
136
  const ret = [];
141
137
  const samplePathMaxLength = NormalizedTrace.getSamplePathMaxLength(paths);
142
138
  if (Config_1.default.verbose) {
143
139
  Console_1.default.lowLevel(` Sample Trace's Max Length: ${samplePathMaxLength}`);
144
140
  }
141
+ paths = paths.filter(p => Utils_1.default.getLeakTracePathLength(p) <= samplePathMaxLength);
145
142
  for (const p of paths) {
146
- if (Utils_1.default.getLeakTracePathLength(p) > samplePathMaxLength) {
147
- continue;
148
- }
149
- if (Math.random() < sampleRatio) {
143
+ if (sampler.sample()) {
150
144
  ret.push(p);
151
145
  }
152
146
  else {
@@ -157,6 +151,9 @@ class NormalizedTrace {
157
151
  }
158
152
  }
159
153
  }
154
+ if (Config_1.default.verbose) {
155
+ Console_1.default.lowLevel(`Number of samples after sampling: ${ret.length}.`);
156
+ }
160
157
  return ret;
161
158
  }
162
159
  static diffTraces(newTraces, existingTraces, // existing representative traces
@@ -194,6 +191,11 @@ class NormalizedTrace {
194
191
  }
195
192
  return traceToClusterMap.get(trace);
196
193
  };
194
+ if (Config_1.default.isContinuousTest) {
195
+ Console_1.default.lowLevel(`${staleClusters.length} stale clusters`);
196
+ Console_1.default.lowLevel(`${clustersToAdd.length} new clusters`);
197
+ Console_1.default.lowLevel(`${allClusters.length} clusters in total`);
198
+ }
197
199
  return {
198
200
  staleClusters: staleClusters.map(traceToCluster),
199
201
  clustersToAdd: clustersToAdd.map(traceToCluster),
@@ -308,43 +310,64 @@ class NormalizedTrace {
308
310
  leakedNodeIds: new Set(),
309
311
  };
310
312
  }
311
- static clusterControlTreatmentPaths(leakPathsFromControlRuns, controlSnapshots, treatmentPaths, treatmentSnapshot, aggregateDominatorMetrics, option = {}) {
313
+ static clusterControlTreatmentPaths(leakPathsFromControlRuns, controlSnapshots, leakPathsFromTreatmentRuns, treatmentSnapshots, aggregateDominatorMetrics, option = {}) {
312
314
  const result = {
313
- controlOnlyClusters: [],
315
+ controlLikelyOrOnlyClusters: [],
314
316
  treatmentOnlyClusters: [],
317
+ treatmentLikelyClusters: [],
315
318
  hybridClusters: [],
316
319
  };
317
320
  Console_1.default.overwrite('Clustering leak traces');
318
321
  const totalControlPaths = leakPathsFromControlRuns.reduce((count, leakPaths) => count + leakPaths.length, 0);
319
- if (totalControlPaths === 0 && treatmentPaths.length === 0) {
322
+ const totalTreatmentPaths = leakPathsFromTreatmentRuns.reduce((count, leakPaths) => count + leakPaths.length, 0);
323
+ if (totalControlPaths === 0 && totalTreatmentPaths === 0) {
320
324
  Console_1.default.midLevel('No leaks found');
321
325
  return result;
322
326
  }
323
327
  // sample paths if there are too many
324
328
  const flattenedLeakPathsFromControlRuns = leakPathsFromControlRuns.reduce((arr, leakPaths) => [...arr, ...leakPaths], []);
325
329
  const controlPaths = this.samplePaths(flattenedLeakPathsFromControlRuns);
326
- treatmentPaths = this.samplePaths(treatmentPaths);
330
+ const pathsForEachTreatmentGroup = leakPathsFromTreatmentRuns.map((treatmentPaths) => this.samplePaths(treatmentPaths));
327
331
  // build control trace to control path map
328
332
  const controlTraceToPathMap = NormalizedTrace.buildTraceToPathMap(controlPaths);
329
333
  const controlTraces = Array.from(controlTraceToPathMap.keys());
330
- // build treatment trace to treatment path map
331
- const treatmentTraceToPathMap = NormalizedTrace.buildTraceToPathMap(treatmentPaths);
332
- const treatmentTraces = Array.from(treatmentTraceToPathMap.keys());
334
+ // build treatment trace to treatment path maps
335
+ // we need to know the mapping to each treatment group
336
+ // to figure out if a trace cluster contains traces from all treatment groups
337
+ const treatmentTraceToPathMaps = pathsForEachTreatmentGroup.map(treatmentPaths => NormalizedTrace.buildTraceToPathMap(treatmentPaths));
338
+ const treatmentTraceToPathMap = new Map();
339
+ const treatmentTraces = [];
340
+ for (const map of treatmentTraceToPathMaps) {
341
+ for (const [key, value] of map.entries()) {
342
+ treatmentTraceToPathMap.set(key, value);
343
+ treatmentTraces.push(key);
344
+ }
345
+ }
333
346
  // cluster traces from both the control group and the treatment group
334
347
  const { allClusters } = NormalizedTrace.diffTraces([...controlTraces, ...treatmentTraces], [], option);
335
- // pick one of the control heap snapshots
348
+ // pick one of the control and treatment heap snapshots
336
349
  const controlSnapshot = controlSnapshots[0];
350
+ const treatmentSnapshot = treatmentSnapshots[0];
337
351
  // construct TraceCluster from clustering result
338
352
  allClusters.forEach((traces) => {
339
353
  var _a, _b;
340
354
  const controlCluster = NormalizedTrace.initEmptyCluster(controlSnapshot);
341
355
  const treatmentCluster = NormalizedTrace.initEmptyCluster(treatmentSnapshot);
356
+ // a set containing each the treatment group that
357
+ // has at least one trace in this cluster
358
+ const treatmentSetWithClusterTrace = new Set();
342
359
  for (const trace of traces) {
343
360
  const normalizedTrace = trace;
344
361
  if (controlTraceToPathMap.has(normalizedTrace)) {
345
362
  NormalizedTrace.pushLeakPathToCluster(controlTraceToPathMap, normalizedTrace, controlCluster);
346
363
  }
347
364
  else {
365
+ for (let i = 0; i < treatmentTraceToPathMaps.length; ++i) {
366
+ if (treatmentTraceToPathMaps[i].has(normalizedTrace)) {
367
+ treatmentSetWithClusterTrace.add(i);
368
+ break;
369
+ }
370
+ }
348
371
  NormalizedTrace.pushLeakPathToCluster(treatmentTraceToPathMap, normalizedTrace, treatmentCluster);
349
372
  }
350
373
  }
@@ -358,11 +381,19 @@ class NormalizedTrace {
358
381
  if (treatmentClusterSize > 0) {
359
382
  this.calculateClusterRetainedSize(treatmentCluster, treatmentSnapshot, aggregateDominatorMetrics);
360
383
  }
361
- if (controlClusterSize === 0) {
384
+ if (controlClusterSize === 0 &&
385
+ treatmentSetWithClusterTrace.size === leakPathsFromTreatmentRuns.length) {
386
+ // only when the leak cluster consists of traces from all treatment groups
362
387
  result.treatmentOnlyClusters.push(treatmentCluster);
363
388
  }
389
+ else if (controlClusterSize === 0) {
390
+ // when the leak cluster consists of traces from
391
+ // some but not all of treatment groups
392
+ result.treatmentLikelyClusters.push(treatmentCluster);
393
+ }
364
394
  else if (treatmentClusterSize === 0) {
365
- result.controlOnlyClusters.push(controlCluster);
395
+ // when the leak cluster consists of traces from any of the control groups
396
+ result.controlLikelyOrOnlyClusters.push(controlCluster);
366
397
  }
367
398
  else {
368
399
  result.hybridClusters.push({
@@ -372,7 +403,8 @@ class NormalizedTrace {
372
403
  }
373
404
  });
374
405
  result.treatmentOnlyClusters.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); });
375
- result.controlOnlyClusters.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); });
406
+ result.treatmentLikelyClusters.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); });
407
+ result.controlLikelyOrOnlyClusters.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); });
376
408
  result.hybridClusters.sort((g1, g2) => {
377
409
  var _a, _b, _c, _d;
378
410
  return ((_a = g2.control.retainedSize) !== null && _a !== void 0 ? _a : 0) +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memlab/core",
3
- "version": "1.1.21",
3
+ "version": "1.1.23",
4
4
  "license": "MIT",
5
5
  "description": "memlab core libraries",
6
6
  "author": "Liang Gong <lgong@fb.com>",