@memlab/core 1.0.8 → 1.1.1

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 (36) hide show
  1. package/dist/index.d.ts +17 -0
  2. package/dist/index.js +17 -0
  3. package/dist/lib/Config.d.ts +6 -2
  4. package/dist/lib/Config.js +11 -2
  5. package/dist/lib/FileManager.d.ts +2 -0
  6. package/dist/lib/FileManager.js +1 -0
  7. package/dist/lib/HeapAnalyzer.d.ts +2 -2
  8. package/dist/lib/HeapAnalyzer.js +27 -60
  9. package/dist/lib/InternalValueSetter.d.ts +2 -0
  10. package/dist/lib/InternalValueSetter.js +2 -0
  11. package/dist/lib/Types.d.ts +363 -6
  12. package/dist/lib/Types.js +1 -0
  13. package/dist/lib/Utils.js +24 -1
  14. package/dist/lib/leak-filters/BaseLeakFilter.rule.d.ts +24 -0
  15. package/dist/lib/leak-filters/BaseLeakFilter.rule.js +22 -0
  16. package/dist/lib/leak-filters/LeakFilterRuleList.d.ts +13 -0
  17. package/dist/lib/leak-filters/LeakFilterRuleList.js +33 -0
  18. package/dist/lib/leak-filters/LeakObjectFilter.d.ts +19 -0
  19. package/dist/lib/leak-filters/LeakObjectFilter.js +36 -0
  20. package/dist/lib/leak-filters/rules/FilterByExternalFilter.rule.d.ts +19 -0
  21. package/dist/lib/leak-filters/rules/FilterByExternalFilter.rule.js +27 -0
  22. package/dist/lib/leak-filters/rules/FilterDetachedDOMElement.rule.d.ts +20 -0
  23. package/dist/lib/leak-filters/rules/FilterDetachedDOMElement.rule.js +40 -0
  24. package/dist/lib/leak-filters/rules/FilterHermesNode.rule.d.ts +16 -0
  25. package/dist/lib/leak-filters/rules/FilterHermesNode.rule.js +27 -0
  26. package/dist/lib/leak-filters/rules/FilterOverSizedNodeAsLeak.rule.d.ts +19 -0
  27. package/dist/lib/leak-filters/rules/FilterOverSizedNodeAsLeak.rule.js +27 -0
  28. package/dist/lib/leak-filters/rules/FilterStackTraceFrame.rule.d.ts +19 -0
  29. package/dist/lib/leak-filters/rules/FilterStackTraceFrame.rule.js +28 -0
  30. package/dist/lib/leak-filters/rules/FilterTrivialNode.rule.d.ts +20 -0
  31. package/dist/lib/leak-filters/rules/FilterTrivialNode.rule.js +33 -0
  32. package/dist/lib/leak-filters/rules/FilterUnmountedFiberNode.rule.d.ts +20 -0
  33. package/dist/lib/leak-filters/rules/FilterUnmountedFiberNode.rule.js +37 -0
  34. package/dist/trace-cluster/TraceBucket.d.ts +1 -0
  35. package/dist/trace-cluster/TraceBucket.js +12 -1
  36. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -8,22 +8,39 @@
8
8
  * @format
9
9
  */
10
10
  export * from './lib/Types';
11
+ /** @internal */
11
12
  export { default as config } from './lib/Config';
13
+ /** @internal */
12
14
  export * from './lib/InternalValueSetter';
15
+ /** @internal */
13
16
  export * from './lib/Config';
17
+ /** @internal */
14
18
  export { default as info } from './lib/Console';
19
+ /** @internal */
15
20
  export { default as BaseOption } from './lib/BaseOption';
21
+ /** @internal */
16
22
  export { default as utils } from './lib/Utils';
23
+ /** @internal */
17
24
  export { default as fileManager } from './lib/FileManager';
25
+ /** @internal */
18
26
  export * from './lib/FileManager';
27
+ /** @internal */
19
28
  export { default as serializer } from './lib/Serializer';
29
+ /** @internal */
20
30
  export { default as browserInfo } from './lib/BrowserInfo';
31
+ /** @internal */
21
32
  export { default as analysis } from './lib/HeapAnalyzer';
33
+ /** @internal */
22
34
  export { default as constant } from './lib/Constant';
35
+ /** @internal */
23
36
  export { default as modes } from './modes/RunningModes';
37
+ /** @internal */
24
38
  export { default as ProcessManager } from './lib/ProcessManager';
39
+ /** @internal */
25
40
  export { default as leakClusterLogger } from './logger/LeakClusterLogger';
41
+ /** @internal */
26
42
  export { default as NormalizedTrace } from './trace-cluster/TraceBucket';
43
+ /** @internal */
27
44
  export { default as EvaluationMetric } from './trace-cluster/EvalutationMetric';
28
45
  export * from './lib/NodeHeap';
29
46
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -28,35 +28,52 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.EvaluationMetric = exports.NormalizedTrace = exports.leakClusterLogger = exports.ProcessManager = exports.modes = exports.constant = exports.analysis = exports.browserInfo = exports.serializer = exports.fileManager = exports.utils = exports.BaseOption = exports.info = exports.config = void 0;
30
30
  __exportStar(require("./lib/Types"), exports);
31
+ /** @internal */
31
32
  var Config_1 = require("./lib/Config");
32
33
  Object.defineProperty(exports, "config", { enumerable: true, get: function () { return __importDefault(Config_1).default; } });
34
+ /** @internal */
33
35
  __exportStar(require("./lib/InternalValueSetter"), exports);
36
+ /** @internal */
34
37
  __exportStar(require("./lib/Config"), exports);
38
+ /** @internal */
35
39
  var Console_1 = require("./lib/Console");
36
40
  Object.defineProperty(exports, "info", { enumerable: true, get: function () { return __importDefault(Console_1).default; } });
41
+ /** @internal */
37
42
  var BaseOption_1 = require("./lib/BaseOption");
38
43
  Object.defineProperty(exports, "BaseOption", { enumerable: true, get: function () { return __importDefault(BaseOption_1).default; } });
44
+ /** @internal */
39
45
  var Utils_1 = require("./lib/Utils");
40
46
  Object.defineProperty(exports, "utils", { enumerable: true, get: function () { return __importDefault(Utils_1).default; } });
47
+ /** @internal */
41
48
  var FileManager_1 = require("./lib/FileManager");
42
49
  Object.defineProperty(exports, "fileManager", { enumerable: true, get: function () { return __importDefault(FileManager_1).default; } });
50
+ /** @internal */
43
51
  __exportStar(require("./lib/FileManager"), exports);
52
+ /** @internal */
44
53
  var Serializer_1 = require("./lib/Serializer");
45
54
  Object.defineProperty(exports, "serializer", { enumerable: true, get: function () { return __importDefault(Serializer_1).default; } });
55
+ /** @internal */
46
56
  var BrowserInfo_1 = require("./lib/BrowserInfo");
47
57
  Object.defineProperty(exports, "browserInfo", { enumerable: true, get: function () { return __importDefault(BrowserInfo_1).default; } });
58
+ /** @internal */
48
59
  var HeapAnalyzer_1 = require("./lib/HeapAnalyzer");
49
60
  Object.defineProperty(exports, "analysis", { enumerable: true, get: function () { return __importDefault(HeapAnalyzer_1).default; } });
61
+ /** @internal */
50
62
  var Constant_1 = require("./lib/Constant");
51
63
  Object.defineProperty(exports, "constant", { enumerable: true, get: function () { return __importDefault(Constant_1).default; } });
64
+ /** @internal */
52
65
  var RunningModes_1 = require("./modes/RunningModes");
53
66
  Object.defineProperty(exports, "modes", { enumerable: true, get: function () { return __importDefault(RunningModes_1).default; } });
67
+ /** @internal */
54
68
  var ProcessManager_1 = require("./lib/ProcessManager");
55
69
  Object.defineProperty(exports, "ProcessManager", { enumerable: true, get: function () { return __importDefault(ProcessManager_1).default; } });
70
+ /** @internal */
56
71
  var LeakClusterLogger_1 = require("./logger/LeakClusterLogger");
57
72
  Object.defineProperty(exports, "leakClusterLogger", { enumerable: true, get: function () { return __importDefault(LeakClusterLogger_1).default; } });
73
+ /** @internal */
58
74
  var TraceBucket_1 = require("./trace-cluster/TraceBucket");
59
75
  Object.defineProperty(exports, "NormalizedTrace", { enumerable: true, get: function () { return __importDefault(TraceBucket_1).default; } });
76
+ /** @internal */
60
77
  var EvalutationMetric_1 = require("./trace-cluster/EvalutationMetric");
61
78
  Object.defineProperty(exports, "EvaluationMetric", { enumerable: true, get: function () { return __importDefault(EvalutationMetric_1).default; } });
62
79
  __exportStar(require("./lib/NodeHeap"), exports);
@@ -37,10 +37,12 @@ interface Device {
37
37
  declare type ConfigOption = {
38
38
  workDir?: string;
39
39
  };
40
+ /** @internal */
40
41
  export declare enum ErrorHandling {
41
42
  Halt = 1,
42
43
  Throw = 2
43
44
  }
45
+ /** @internal */
44
46
  export declare class MemLabConfig {
45
47
  snapshotHasDetachedness: boolean;
46
48
  specifiedEngine: boolean;
@@ -137,6 +139,7 @@ export declare class MemLabConfig {
137
139
  waitAfterOperation: number;
138
140
  waitAfterScrolling: number;
139
141
  waitAfterTyping: number;
142
+ waitForNetworkInDefaultScenario: number;
140
143
  stressTestRepeat: number;
141
144
  avoidLeakWithoutDetachedElements: boolean;
142
145
  hideBrowserLeak: boolean;
@@ -165,6 +168,7 @@ export declare class MemLabConfig {
165
168
  nodeIgnoreSetInShape: Set<string>;
166
169
  oversizeObjectAsLeak: boolean;
167
170
  oversizeThreshold: number;
171
+ clusterRetainedSizeThreshold: number;
168
172
  _isFullRun: boolean;
169
173
  _scenario: Optional<IScenario>;
170
174
  externalLeakFilter?: Optional<ILeakFilter>;
@@ -199,8 +203,8 @@ export declare class MemLabConfig {
199
203
  enableXvfb(display: string): void;
200
204
  disableXvfb(): void;
201
205
  }
202
- /** @ignore */
206
+ /** @internal */
203
207
  declare const config: MemLabConfig;
204
- /** @ignore */
208
+ /** @internal */
205
209
  export default config;
206
210
  //# sourceMappingURL=Config.d.ts.map
@@ -33,11 +33,13 @@ const defaultViewport = {
33
33
  height: windowHeight,
34
34
  deviceScaleFactor: 1,
35
35
  };
36
+ /** @internal */
36
37
  var ErrorHandling;
37
38
  (function (ErrorHandling) {
38
39
  ErrorHandling[ErrorHandling["Halt"] = 1] = "Halt";
39
40
  ErrorHandling[ErrorHandling["Throw"] = 2] = "Throw";
40
41
  })(ErrorHandling = exports.ErrorHandling || (exports.ErrorHandling = {}));
42
+ /** @internal */
41
43
  class MemLabConfig {
42
44
  constructor(options = {}) {
43
45
  // init properties, they can be configured manually
@@ -47,6 +49,7 @@ class MemLabConfig {
47
49
  }
48
50
  initInternalConfigs() {
49
51
  // DO NOT SET PARAMETER HERE
52
+ this._isFullRun = false;
50
53
  this._reportLeaksInTimers = false;
51
54
  this._deviceManualOverridden = false;
52
55
  this._timerNodes = ['Pending activities'];
@@ -189,6 +192,8 @@ class MemLabConfig {
189
192
  this.waitAfterScrolling = 5000;
190
193
  // extra delay after typing
191
194
  this.waitAfterTyping = 1000;
195
+ // page load checker: default wait for network idle in scenario test
196
+ this.waitForNetworkInDefaultScenario = 10000;
192
197
  // default repeat for stress testing
193
198
  this.stressTestRepeat = 5;
194
199
  // only show leaks with detached HTML elements
@@ -239,6 +244,7 @@ class MemLabConfig {
239
244
  this.nodeNameGreyList = new Set([
240
245
  'InternalNode',
241
246
  'Pending activities',
247
+ 'StyleEngine',
242
248
  ...Constant_1.default.V8SyntheticRoots,
243
249
  ]);
244
250
  // edge names less preferable in trace finding
@@ -266,6 +272,9 @@ class MemLabConfig {
266
272
  this.oversizeObjectAsLeak = false;
267
273
  // if larger than this threshold, consider as memory leak
268
274
  this.oversizeThreshold = 0;
275
+ // only report leak clusters with aggregated retained size
276
+ // bigger than this threshold
277
+ this.clusterRetainedSizeThreshold = 0;
269
278
  // initialize file and directory paths
270
279
  FileManager_1.default.initDirs(this, options);
271
280
  }
@@ -424,8 +433,8 @@ class MemLabConfig {
424
433
  }
425
434
  }
426
435
  exports.MemLabConfig = MemLabConfig;
427
- /** @ignore */
436
+ /** @internal */
428
437
  const config = MemLabConfig.getInstance();
429
438
  (0, InternalValueSetter_1.setInternalValue)(config, __filename, Constant_1.default.internalDir);
430
- /** @ignore */
439
+ /** @internal */
431
440
  exports.default = config;
@@ -9,11 +9,13 @@
9
9
  */
10
10
  import type { MemLabConfig } from './Config';
11
11
  import type { AnyValue, Optional } from './Types';
12
+ /** @internal */
12
13
  export declare type FileOption = {
13
14
  workDir?: Optional<string>;
14
15
  clear?: boolean;
15
16
  transcient?: boolean;
16
17
  };
18
+ /** @internal */
17
19
  export declare class FileManager {
18
20
  getDefaultWorkDir(): string;
19
21
  generateTmpHeapDir(): string;
@@ -35,6 +35,7 @@ function joinAndProcessDir(options, ...args) {
35
35
  }
36
36
  return filepath;
37
37
  }
38
+ /** @internal */
38
39
  class FileManager {
39
40
  getDefaultWorkDir() {
40
41
  return path_1.default.join(this.getTmpDir(), 'memlab');
@@ -27,8 +27,6 @@ declare class MemoryAnalyst {
27
27
  preparePathFinder(snapshot: IHeapSnapshot): TraceFinder;
28
28
  private dumpPageInteractionSummary;
29
29
  private dumpLeakSummaryToConsole;
30
- private checkDetachedFiberNode;
31
- private isTrivialNode;
32
30
  private filterLeakedObjects;
33
31
  aggregateDominatorMetrics(ids: HeapNodeIdSet, snapshot: IHeapSnapshot, checkNodeCb: (node: IHeapNode) => boolean, nodeMetricsCb: (node: IHeapNode) => number): number;
34
32
  private getOverallHeapInfo;
@@ -38,6 +36,8 @@ declare class MemoryAnalyst {
38
36
  private breakDownSnapshotByShapes;
39
37
  private isTrivialEdgeForBreakDown;
40
38
  private breakDownByReferrers;
39
+ private printHeapAndLeakInfo;
40
+ private logLeakTraceSummary;
41
41
  searchLeakedTraces(leakedNodeIds: HeapNodeIdSet, snapshot: IHeapSnapshot): Promise<{
42
42
  paths: LeakTracePathItem[];
43
43
  }>;
@@ -32,6 +32,7 @@ const Config_1 = __importDefault(require("./Config"));
32
32
  const Console_1 = __importDefault(require("./Console"));
33
33
  const Serializer_1 = __importDefault(require("./Serializer"));
34
34
  const Utils_1 = __importDefault(require("./Utils"));
35
+ const LeakObjectFilter_1 = require("./leak-filters/LeakObjectFilter");
35
36
  class MemoryAnalyst {
36
37
  checkLeak() {
37
38
  return __awaiter(this, void 0, void 0, function* () {
@@ -416,53 +417,15 @@ class MemoryAnalyst {
416
417
  Console_1.default.topLevel('Alive objects allocated in target page:');
417
418
  Console_1.default.table(list);
418
419
  }
419
- checkDetachedFiberNode(node) {
420
- if (!Config_1.default.detectFiberNodeLeak ||
421
- !Utils_1.default.isFiberNode(node) ||
422
- Utils_1.default.hasHostRoot(node)) {
423
- return false;
424
- }
425
- return !Utils_1.default.isNodeDominatedByDeletionsArray(node);
426
- }
427
- isTrivialNode(node) {
428
- return (node.type === 'number' ||
429
- Utils_1.default.isStringNode(node) ||
430
- node.type === 'hidden');
431
- }
432
420
  filterLeakedObjects(leakedNodeIds, snapshot) {
433
421
  var _a;
434
422
  // call init leak filter hook if exists
435
423
  if ((_a = Config_1.default.externalLeakFilter) === null || _a === void 0 ? void 0 : _a.beforeLeakFilter) {
436
424
  Config_1.default.externalLeakFilter.beforeLeakFilter(snapshot, leakedNodeIds);
437
425
  }
426
+ const leakFilter = new LeakObjectFilter_1.LeakObjectFilter();
438
427
  // start filtering memory leaks
439
- Utils_1.default.filterNodesInPlace(leakedNodeIds, snapshot, node => {
440
- // use external leak filter if exists
441
- if (Config_1.default.externalLeakFilter) {
442
- return Config_1.default.externalLeakFilter.leakFilter(node, snapshot, leakedNodeIds);
443
- }
444
- if (this.isTrivialNode(node)) {
445
- return false;
446
- }
447
- // when analyzing hermes heap snapshots, filter Hermes internal objects
448
- if (Config_1.default.jsEngine === 'hermes' && Utils_1.default.isHermesInternalObject(node)) {
449
- return false;
450
- }
451
- if (Config_1.default.oversizeObjectAsLeak) {
452
- return node.retainedSize > Config_1.default.oversizeThreshold;
453
- }
454
- // check FiberNodes without a Fiber Root
455
- if (this.checkDetachedFiberNode(node)) {
456
- return true;
457
- }
458
- const isDetached = Utils_1.default.isDetachedDOMNode(node, {
459
- ignoreInternalNode: true,
460
- });
461
- if (isDetached && Config_1.default.targetApp === 'ads-manager') {
462
- return Utils_1.default.hasReactEdges(node);
463
- }
464
- return isDetached || Utils_1.default.isStackTraceFrame(node);
465
- });
428
+ Utils_1.default.filterNodesInPlace(leakedNodeIds, snapshot, node => leakFilter.filter(Config_1.default, node, snapshot, leakedNodeIds));
466
429
  if (Config_1.default.verbose) {
467
430
  Console_1.default.midLevel(`${leakedNodeIds.size} Fiber nodes and Detached elements`);
468
431
  }
@@ -615,29 +578,40 @@ class MemoryAnalyst {
615
578
  .join('\n');
616
579
  return referrerInfo;
617
580
  }
581
+ printHeapAndLeakInfo(leakedNodeIds, snapshot) {
582
+ // write page interaction summary to the leaks text file
583
+ this.dumpPageInteractionSummary();
584
+ // dump leak summry to console
585
+ this.dumpLeakSummaryToConsole(leakedNodeIds, snapshot);
586
+ // get aggregated leak info
587
+ const heapInfo = this.getOverallHeapInfo(snapshot);
588
+ if (heapInfo) {
589
+ this.printHeapInfo(heapInfo);
590
+ }
591
+ }
592
+ logLeakTraceSummary(trace, nodeIdInPaths, snapshot) {
593
+ if (!Config_1.default.isFullRun) {
594
+ return;
595
+ }
596
+ // convert the path to a string
597
+ const pathStr = Serializer_1.default.summarizePath(trace, nodeIdInPaths, snapshot);
598
+ fs_1.default.appendFileSync(Config_1.default.exploreResultFile, `\n\n${pathStr}\n\n`, 'UTF-8');
599
+ }
618
600
  // find unique paths of leaked nodes
619
601
  searchLeakedTraces(leakedNodeIds, snapshot) {
620
602
  return __awaiter(this, void 0, void 0, function* () {
621
603
  const finder = this.preparePathFinder(snapshot);
622
- // write page interaction summary to the leaks text file
623
- this.dumpPageInteractionSummary();
624
- // dump leak summry to console
625
- this.dumpLeakSummaryToConsole(leakedNodeIds, snapshot);
604
+ this.printHeapAndLeakInfo(leakedNodeIds, snapshot);
626
605
  // get all leaked objects
627
606
  this.filterLeakedObjects(leakedNodeIds, snapshot);
628
- // get aggregated leak info
629
- const leakInfo = this.getOverallHeapInfo(snapshot);
630
- if (leakInfo) {
631
- this.printHeapInfo(leakInfo);
632
- }
633
607
  if (Config_1.default.verbose) {
634
608
  // show a breakdown of different object structures
635
609
  this.breakDownSnapshotByShapes(snapshot);
636
610
  }
637
- let numOfLeakedObjects = 0;
638
- let i = 0;
639
611
  const nodeIdInPaths = new Set();
640
612
  const paths = [];
613
+ let numOfLeakedObjects = 0;
614
+ let i = 0;
641
615
  // analysis for each node
642
616
  Utils_1.default.applyToNodes(leakedNodeIds, snapshot, node => {
643
617
  if (!Config_1.default.isContinuousTest && ++i % 11 === 0) {
@@ -645,19 +619,12 @@ class MemoryAnalyst {
645
619
  }
646
620
  // BFS search for path from the leaked node to GC roots
647
621
  const p = finder.getPathToGCRoots(snapshot, node);
648
- if (!p) {
649
- return;
650
- }
651
- if (!Utils_1.default.isInterestingPath(p)) {
622
+ if (!p || !Utils_1.default.isInterestingPath(p)) {
652
623
  return;
653
624
  }
654
625
  ++numOfLeakedObjects;
655
626
  paths.push(p);
656
- // convert the path to a string
657
- if (Config_1.default.isFullRun) {
658
- const pathStr = Serializer_1.default.summarizePath(p, nodeIdInPaths, snapshot);
659
- fs_1.default.appendFileSync(Config_1.default.exploreResultFile, `\n\n${pathStr}\n\n`, 'UTF-8');
660
- }
627
+ this.logLeakTraceSummary(p, nodeIdInPaths, snapshot);
661
628
  }, { reverse: true });
662
629
  if (Config_1.default.verbose) {
663
630
  Console_1.default.midLevel(`${numOfLeakedObjects} leaked objects`);
@@ -7,8 +7,10 @@
7
7
  * @emails oncall+ws_labs
8
8
  * @format
9
9
  */
10
+ /** @internal */
10
11
  export declare abstract class InternalValueSetter<T> {
11
12
  fillIn(_module: T): T;
12
13
  }
14
+ /** @internal */
13
15
  export declare function setInternalValue<T>(value: T, callerFilePath: string, internalFolderName: string): T;
14
16
  //# sourceMappingURL=InternalValueSetter.d.ts.map
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.setInternalValue = exports.InternalValueSetter = void 0;
16
16
  const fs_1 = __importDefault(require("fs"));
17
17
  const path_1 = __importDefault(require("path"));
18
+ /** @internal */
18
19
  class InternalValueSetter {
19
20
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
21
  fillIn(_module) {
@@ -22,6 +23,7 @@ class InternalValueSetter {
22
23
  }
23
24
  }
24
25
  exports.InternalValueSetter = InternalValueSetter;
26
+ /** @internal */
25
27
  function setInternalValue(value, callerFilePath, internalFolderName) {
26
28
  const callerDir = path_1.default.dirname(callerFilePath);
27
29
  const callerFile = path_1.default.basename(callerFilePath);