@memlab/core 1.1.1 → 1.1.4

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/README.md CHANGED
@@ -2,5 +2,6 @@
2
2
 
3
3
  This is the memlab core library. It contains V8/Hermes heap snapshot parser, core algorithms, leak trace clustering, utilities, and config.
4
4
 
5
- ## Full documentation
6
- https://facebookincubator.github.io/memlab
5
+ ## Online Resources
6
+ * [Official Website and Demo](https://facebookincubator.github.io/memlab)
7
+ * [Documentation](https://facebookincubator.github.io/memlab/docs/intro)
@@ -36,7 +36,7 @@ test('Capture inserted object', () => __awaiter(void 0, void 0, void 0, function
36
36
  }
37
37
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
38
38
  const injected = new TestObject();
39
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
39
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
40
40
  expect(heap.hasObjectWithClassName('TestObject')).toBe(true);
41
41
  }), timeout);
42
42
  test('Does not capture transcient object', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -49,6 +49,6 @@ test('Does not capture transcient object', () => __awaiter(void 0, void 0, void
49
49
  let injected = new TestObject();
50
50
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
51
51
  injected = null;
52
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
52
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
53
53
  expect(heap.hasObjectWithClassName('TestObject')).toBe(false);
54
54
  }), timeout);
@@ -30,7 +30,7 @@ const timeout = 5 * 60 * 1000;
30
30
  test('Capture current node heap snapshot', () => __awaiter(void 0, void 0, void 0, function* () {
31
31
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
32
32
  const object = { 'memlab-test-heap-property': 'memlab-test-heap-value' };
33
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
33
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
34
34
  expect(heap.hasObjectWithPropertyName('memlab-test-heap-property')).toBe(true);
35
35
  }), timeout);
36
36
  test('Nullified Object should not exist in heap', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -39,7 +39,7 @@ test('Nullified Object should not exist in heap', () => __awaiter(void 0, void 0
39
39
  };
40
40
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
41
41
  object = null;
42
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
42
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
43
43
  expect(heap.hasObjectWithPropertyName('memlab-test-heap-property')).toBe(false);
44
44
  }), timeout);
45
45
  test('Strongly referenced object should exist in heap', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -59,7 +59,7 @@ test('Strongly referenced object should exist in heap', () => __awaiter(void 0,
59
59
  }
60
60
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
61
61
  const object = buildTest();
62
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
62
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
63
63
  expect(heap.hasObjectWithClassName('TestClass1')).toBe(true);
64
64
  expect(heap.hasObjectWithClassName('TestClass2')).toBe(true);
65
65
  }), timeout);
@@ -80,7 +80,7 @@ test('Weakly referenced object should not exist in heap', () => __awaiter(void 0
80
80
  }
81
81
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
82
82
  const object = buildTest();
83
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
83
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
84
84
  expect(heap.hasObjectWithClassName('TestClass3')).toBe(true);
85
85
  expect(heap.hasObjectWithClassName('TestClass4')).toBe(false);
86
86
  }), timeout);
@@ -90,7 +90,7 @@ test('Check annotated objects', () => __awaiter(void 0, void 0, void 0, function
90
90
  (0, NodeHeap_1.tagObject)(o1, 'memlab-mark-1');
91
91
  (0, NodeHeap_1.tagObject)(o2, 'memlab-mark-2');
92
92
  o2 = null;
93
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
93
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
94
94
  expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
95
95
  expect(heap.hasObjectWithTag('memlab-mark-2')).toBe(false);
96
96
  }), timeout);
@@ -41,7 +41,7 @@ test('String heap object APIs work', () => __awaiter(void 0, void 0, void 0, fun
41
41
  injected.complexConcatString += 'value_';
42
42
  injected.complexConcatString += 123;
43
43
  injected.complexConcatString += '_suffix';
44
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
44
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
45
45
  const testObject = heap.getAnyObjectWithClassName('TestObject');
46
46
  expect(testObject).not.toBe(null);
47
47
  // testObject.originalString === 'test'
@@ -74,7 +74,7 @@ test('Check getReference and getReferenceNode', () => __awaiter(void 0, void 0,
74
74
  });
75
75
  return detected;
76
76
  };
77
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
77
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
78
78
  expect(checker(heap)).toBe(true);
79
79
  }), timeout);
80
80
  test('Check getReferrers and getReferrerNodes', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -135,6 +135,6 @@ test('Check getReferrers and getReferrerNodes', () => __awaiter(void 0, void 0,
135
135
  }
136
136
  return true;
137
137
  };
138
- const heap = yield (0, NodeHeap_1.getCurrentNodeHeap)();
138
+ const heap = yield (0, NodeHeap_1.getNodeInnocentHeap)();
139
139
  expect(checker(heap)).toBe(true);
140
140
  }), timeout);
@@ -171,6 +171,7 @@ export declare class MemLabConfig {
171
171
  clusterRetainedSizeThreshold: number;
172
172
  _isFullRun: boolean;
173
173
  _scenario: Optional<IScenario>;
174
+ _isHeadfulBrowser: boolean;
174
175
  externalLeakFilter?: Optional<ILeakFilter>;
175
176
  monoRepoDir: string;
176
177
  muteConsole: boolean;
@@ -192,6 +193,8 @@ export declare class MemLabConfig {
192
193
  get isFullRun(): boolean;
193
194
  set browser(v: string);
194
195
  get browser(): string;
196
+ set isHeadfulBrowser(isHeadful: boolean);
197
+ get isHeadfulBrowser(): boolean;
195
198
  get browserBinaryPath(): string;
196
199
  set reportLeaksInTimers(flag: boolean);
197
200
  get reportLeaksInTimers(): boolean;
@@ -54,6 +54,7 @@ class MemLabConfig {
54
54
  this._deviceManualOverridden = false;
55
55
  this._timerNodes = ['Pending activities'];
56
56
  this._timerEdges = [];
57
+ this._isHeadfulBrowser = false;
57
58
  this.targetApp = Constant_1.default.unset;
58
59
  this.targetTab = Constant_1.default.unset;
59
60
  this.analysisMode = Constant_1.default.unset;
@@ -66,6 +67,7 @@ class MemLabConfig {
66
67
  this.specifiedEngine = false;
67
68
  // set puppeteer configuration
68
69
  this.puppeteerConfig = {
70
+ headless: !this._isHeadfulBrowser,
69
71
  devtools: this.openDevtoolsConsole,
70
72
  // IMPORTANT: test ContinuousTest before change this config
71
73
  ignoreHTTPSErrors: true,
@@ -358,6 +360,17 @@ class MemLabConfig {
358
360
  get browser() {
359
361
  return this._browser || 'google-chrome';
360
362
  }
363
+ set isHeadfulBrowser(isHeadful) {
364
+ this._isHeadfulBrowser = isHeadful;
365
+ this.puppeteerConfig.headless = !isHeadful;
366
+ if (isHeadful) {
367
+ // if running in headful mode
368
+ this.disableXvfb();
369
+ }
370
+ }
371
+ get isHeadfulBrowser() {
372
+ return this._isHeadfulBrowser;
373
+ }
361
374
  get browserBinaryPath() {
362
375
  return path_1.default.join(this.browserDir, this.browser);
363
376
  }
@@ -61,6 +61,7 @@ declare class MemLabConsole {
61
61
  progress(cur: number, total: number, options?: {
62
62
  message?: string;
63
63
  }): void;
64
+ flush(): void;
64
65
  }
65
66
  declare const _default: MemLabConsole;
66
67
  export default _default;
@@ -340,5 +340,8 @@ class MemLabConsole {
340
340
  const progress = `${message}: |${bar}| ${percent}/100`;
341
341
  this.overwrite(progress, { level: 'top' });
342
342
  }
343
+ flush() {
344
+ this.clearPrevOverwriteMsg();
345
+ }
343
346
  }
344
347
  exports.default = MemLabConsole.getInstance();
@@ -13,8 +13,8 @@ const InternalValueSetter_1 = require("./InternalValueSetter");
13
13
  const constants = {
14
14
  isFB: false,
15
15
  isFRL: false,
16
- defaultEngine: 'v8',
17
- supportedEngines: ['v8', 'hermes'],
16
+ defaultEngine: 'V8',
17
+ supportedEngines: ['V8', 'hermes'],
18
18
  supportedBrowsers: Object.create(null),
19
19
  internalDir: 'fb-internal',
20
20
  monoRepoDir: '',
@@ -43,6 +43,8 @@ export declare class FileManager {
43
43
  getAllFilesInDir(dir: string): string[];
44
44
  getDataOutDir(options?: FileOption): string;
45
45
  getCoreProjectBaseDir(): string;
46
+ getMonoRepoDir(): string;
47
+ getDocDir(): string;
46
48
  getReportOutDir(options?: FileOption): string;
47
49
  getPreviewReportDir(options?: FileOption): string;
48
50
  getLeakSummaryFile(options?: FileOption): string;
@@ -133,6 +133,12 @@ class FileManager {
133
133
  getCoreProjectBaseDir() {
134
134
  return path_1.default.join(__dirname, '..', '..');
135
135
  }
136
+ getMonoRepoDir() {
137
+ return path_1.default.join(this.getCoreProjectBaseDir(), '..', '..');
138
+ }
139
+ getDocDir() {
140
+ return path_1.default.join(this.getMonoRepoDir(), 'website', 'docs');
141
+ }
136
142
  getReportOutDir(options = {}) {
137
143
  return path_1.default.join(this.getPersistDataDir(options), 'reports');
138
144
  }
@@ -102,7 +102,7 @@ function identifyAndSetEngine(snapshot) {
102
102
  return; // skip if engine type is manually set
103
103
  }
104
104
  Console_1.default.overwrite('identifying snapshot engine...');
105
- let engine = 'v8';
105
+ let engine = 'V8';
106
106
  snapshot.nodes.forEach((node) => {
107
107
  if (node.type === 'object' && node.name.startsWith('Object(')) {
108
108
  engine = 'hermes';
@@ -7,10 +7,69 @@
7
7
  * @emails oncall+ws_labs
8
8
  * @format
9
9
  */
10
- import type { AnyValue, IHeapSnapshot } from './Types';
11
- declare type AnyObject = Record<AnyValue, AnyValue>;
12
- export declare function tagObject(o: AnyObject, tag: string): AnyObject;
10
+ import type { IHeapSnapshot } from './Types';
11
+ /**
12
+ * Tags a string marker to an object instance, which can later be checked by
13
+ * {@link hasObjectWithTag}. This API does not modify the object instance in
14
+ * any way (e.g., no additional or hidden properties added to the tagged
15
+ * object).
16
+ *
17
+ * @param o specify the object instance you want to tag, you cannot tag a
18
+ * [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive).
19
+ * @param tag marker name to tag on the object instance
20
+ * @returns returns the tagged object instance (same reference as
21
+ * the input argument `o`)
22
+ * * **Examples**:
23
+ * ```typescript
24
+ * import type {IHeapSnapshot, AnyValue} from '@memlab/core';
25
+ * import {config, getNodeInnocentHeap, tagObject} from '@memlab/core';
26
+ *
27
+ * test('memory test', async () => {
28
+ * config.muteConsole = true;
29
+ * const o1: AnyValue = {};
30
+ * let o2: AnyValue = {};
31
+ *
32
+ * // tag o1 with marker: "memlab-mark-1", does not modify o1 in any way
33
+ * tagObject(o1, 'memlab-mark-1');
34
+ * // tag o2 with marker: "memlab-mark-2", does not modify o2 in any way
35
+ * tagObject(o2, 'memlab-mark-2');
36
+ *
37
+ * o2 = null;
38
+ *
39
+ * const heap: IHeapSnapshot = await getNodeInnocentHeap();
40
+ *
41
+ * // expect object with marker "memlab-mark-1" exists
42
+ * expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
43
+ *
44
+ * // expect object with marker "memlab-mark-2" can be GCed
45
+ * expect(heap.hasObjectWithTag('memlab-mark-2')).toBe(false);
46
+ *
47
+ * }, 30000);
48
+ * ```
49
+ */
50
+ export declare function tagObject<T extends object>(o: T, tag: string): T;
13
51
  export declare function dumpNodeHeapSnapshot(): string;
14
- export declare function getCurrentNodeHeap(): Promise<IHeapSnapshot>;
15
- export {};
52
+ /**
53
+ * Take a heap snapshot of the current program state
54
+ * and parse it as {@link IHeapSnapshot}. Notice that
55
+ * this API does not calculate some heap analysis meta data
56
+ * for heap analysis. But this also means faster heap parsing.
57
+ *
58
+ * @returns heap representation without heap analysis meta data.
59
+ *
60
+ * If you need to get the heap snapshot with heap analysis meta data
61
+ * use {@link dumpNodeHeapSnapshot} and {@link getHeapFromFile},
62
+ * for example:
63
+ * ```typescript
64
+ * import type {IHeapSnapshot} from '@memlab/core';
65
+ * import {dumpNodeHeapSnapshot} from '@memlab/core';
66
+ * import {getHeapFromFile} from '@memlab/heap-analysis';
67
+ *
68
+ * (async function () {
69
+ * const heapFile = dumpNodeHeapSnapshot();
70
+ * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
71
+ * })();
72
+ * ```
73
+ */
74
+ export declare function getNodeInnocentHeap(): Promise<IHeapSnapshot>;
16
75
  //# sourceMappingURL=NodeHeap.d.ts.map
@@ -21,7 +21,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
21
21
  return (mod && mod.__esModule) ? mod : { "default": mod };
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.getCurrentNodeHeap = exports.dumpNodeHeapSnapshot = exports.tagObject = void 0;
24
+ exports.getNodeInnocentHeap = exports.dumpNodeHeapSnapshot = exports.tagObject = void 0;
25
25
  const fs_extra_1 = __importDefault(require("fs-extra"));
26
26
  const path_1 = __importDefault(require("path"));
27
27
  const v8_1 = __importDefault(require("v8"));
@@ -33,6 +33,45 @@ class MemLabTaggedStore {
33
33
  }
34
34
  }
35
35
  const store = new MemLabTaggedStore();
36
+ /**
37
+ * Tags a string marker to an object instance, which can later be checked by
38
+ * {@link hasObjectWithTag}. This API does not modify the object instance in
39
+ * any way (e.g., no additional or hidden properties added to the tagged
40
+ * object).
41
+ *
42
+ * @param o specify the object instance you want to tag, you cannot tag a
43
+ * [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive).
44
+ * @param tag marker name to tag on the object instance
45
+ * @returns returns the tagged object instance (same reference as
46
+ * the input argument `o`)
47
+ * * **Examples**:
48
+ * ```typescript
49
+ * import type {IHeapSnapshot, AnyValue} from '@memlab/core';
50
+ * import {config, getNodeInnocentHeap, tagObject} from '@memlab/core';
51
+ *
52
+ * test('memory test', async () => {
53
+ * config.muteConsole = true;
54
+ * const o1: AnyValue = {};
55
+ * let o2: AnyValue = {};
56
+ *
57
+ * // tag o1 with marker: "memlab-mark-1", does not modify o1 in any way
58
+ * tagObject(o1, 'memlab-mark-1');
59
+ * // tag o2 with marker: "memlab-mark-2", does not modify o2 in any way
60
+ * tagObject(o2, 'memlab-mark-2');
61
+ *
62
+ * o2 = null;
63
+ *
64
+ * const heap: IHeapSnapshot = await getNodeInnocentHeap();
65
+ *
66
+ * // expect object with marker "memlab-mark-1" exists
67
+ * expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
68
+ *
69
+ * // expect object with marker "memlab-mark-2" can be GCed
70
+ * expect(heap.hasObjectWithTag('memlab-mark-2')).toBe(false);
71
+ *
72
+ * }, 30000);
73
+ * ```
74
+ */
36
75
  function tagObject(o, tag) {
37
76
  if (!store.taggedObjects[tag]) {
38
77
  store.taggedObjects[tag] = new WeakSet();
@@ -47,7 +86,29 @@ function dumpNodeHeapSnapshot() {
47
86
  return file;
48
87
  }
49
88
  exports.dumpNodeHeapSnapshot = dumpNodeHeapSnapshot;
50
- function getCurrentNodeHeap() {
89
+ /**
90
+ * Take a heap snapshot of the current program state
91
+ * and parse it as {@link IHeapSnapshot}. Notice that
92
+ * this API does not calculate some heap analysis meta data
93
+ * for heap analysis. But this also means faster heap parsing.
94
+ *
95
+ * @returns heap representation without heap analysis meta data.
96
+ *
97
+ * If you need to get the heap snapshot with heap analysis meta data
98
+ * use {@link dumpNodeHeapSnapshot} and {@link getHeapFromFile},
99
+ * for example:
100
+ * ```typescript
101
+ * import type {IHeapSnapshot} from '@memlab/core';
102
+ * import {dumpNodeHeapSnapshot} from '@memlab/core';
103
+ * import {getHeapFromFile} from '@memlab/heap-analysis';
104
+ *
105
+ * (async function () {
106
+ * const heapFile = dumpNodeHeapSnapshot();
107
+ * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
108
+ * })();
109
+ * ```
110
+ */
111
+ function getNodeInnocentHeap() {
51
112
  return __awaiter(this, void 0, void 0, function* () {
52
113
  const file = dumpNodeHeapSnapshot();
53
114
  const snapshot = yield Utils_1.default.getSnapshotFromFile(file, {
@@ -59,4 +120,4 @@ function getCurrentNodeHeap() {
59
120
  return snapshot;
60
121
  });
61
122
  }
62
- exports.getCurrentNodeHeap = getCurrentNodeHeap;
123
+ exports.getNodeInnocentHeap = getNodeInnocentHeap;
@@ -182,6 +182,9 @@ function JSONifyFiberNodeReturnTrace(node, args, options) {
182
182
  continue;
183
183
  }
184
184
  const parent = node.snapshot.nodes.get(index);
185
+ if (!parent) {
186
+ continue;
187
+ }
185
188
  const parentInfo = getNodeNameInJSON(parent, args);
186
189
  key = `${key}: --return (property)---> ${parentInfo}`;
187
190
  const info = JSONifyFiberNodeShallow(parent);
@@ -593,7 +596,7 @@ function summarizeNode(node, options = {}) {
593
596
  let nodeImpact = '';
594
597
  if (nodeRetainSize) {
595
598
  nodeImpact = options.color
596
- ? chalk_1.default.grey('[') + chalk_1.default.blue(nodeRetainSize) + chalk_1.default.grey(']')
599
+ ? chalk_1.default.grey('[') + chalk_1.default.blue.bold(nodeRetainSize) + chalk_1.default.grey(']')
597
600
  : `[${nodeRetainSize}]`;
598
601
  }
599
602
  const name = summarizeNodeName(node, options);