@memlab/core 1.1.16 → 1.1.18

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.
@@ -8,8 +8,7 @@
8
8
  * @oncall web_perf_infra
9
9
  */
10
10
  import type { LaunchOptions, Permission } from 'puppeteer';
11
- import type { AnyFunction, AnyValue, IClusterStrategy, IRunningMode, IScenario, Nullable, Optional, QuickExperiment, ILeakFilter, IPackageInfo } from './Types';
12
- import { IHeapConfig } from '..';
11
+ import type { AnyFunction, AnyValue, FileOption, IClusterStrategy, IRunningMode, IScenario, IHeapConfig, Nullable, Optional, QuickExperiment, ILeakFilter, IPackageInfo } from './Types';
13
12
  interface BrowserLaunchArgumentOptions {
14
13
  headless?: boolean;
15
14
  userDataDir?: string;
@@ -71,6 +70,7 @@ export declare class MemLabConfig {
71
70
  userDataDir: string;
72
71
  curDataDir: string;
73
72
  webSourceDir: string;
73
+ debugDataDir: string;
74
74
  runMetaFile: string;
75
75
  snapshotSequenceFile: string;
76
76
  exploreResultFile: string;
@@ -90,6 +90,7 @@ export declare class MemLabConfig {
90
90
  traceClusterOutDir: string;
91
91
  traceJsonOutDir: string;
92
92
  metricsOutDir: string;
93
+ heapAnalysisLogDir: string;
93
94
  reportScreenshotFile: string;
94
95
  newUniqueClusterDir: string;
95
96
  staleUniqueClusterDir: string;
@@ -215,6 +216,8 @@ export declare class MemLabConfig {
215
216
  set disableWebSecurity(disable: boolean);
216
217
  get disableWebSecurity(): boolean;
217
218
  get browserBinaryPath(): string;
219
+ set defaultFileManagerOption(fileOption: FileOption);
220
+ get defaultFileManagerOption(): FileOption;
218
221
  set reportLeaksInTimers(shouldReport: boolean);
219
222
  get reportLeaksInTimers(): boolean;
220
223
  setDevice(deviceName: string, options?: {
@@ -8,6 +8,29 @@
8
8
  * @format
9
9
  * @oncall web_perf_infra
10
10
  */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
11
34
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
36
  };
@@ -17,7 +40,7 @@ const path_1 = __importDefault(require("path"));
17
40
  const RunningModes_1 = __importDefault(require("../modes/RunningModes"));
18
41
  const Console_1 = __importDefault(require("./Console"));
19
42
  const Constant_1 = __importDefault(require("./Constant"));
20
- const FileManager_1 = __importDefault(require("./FileManager"));
43
+ const FileManager_1 = __importStar(require("./FileManager"));
21
44
  const InternalValueSetter_1 = require("./InternalValueSetter");
22
45
  const devices = Constant_1.default.isFRL
23
46
  ? {}
@@ -429,6 +452,17 @@ class MemLabConfig {
429
452
  get browserBinaryPath() {
430
453
  return path_1.default.join(this.browserDir, this.browser);
431
454
  }
455
+ // Default input option for file manager.
456
+ // If no other input option is provided, the file manager
457
+ // will generate directories and files based on this default option.
458
+ set defaultFileManagerOption(fileOption) {
459
+ FileManager_1.FileManager.defaultFileOption = fileOption;
460
+ // initialize file and directory paths
461
+ FileManager_1.default.initDirs(this, fileOption);
462
+ }
463
+ get defaultFileManagerOption() {
464
+ return FileManager_1.FileManager.defaultFileOption;
465
+ }
432
466
  set reportLeaksInTimers(shouldReport) {
433
467
  if (shouldReport) {
434
468
  this.removeFromSet(this.nodeNameBlockList, this._timerNodes);
@@ -23,6 +23,7 @@ declare class MemLabConsole {
23
23
  private config;
24
24
  private sections;
25
25
  private log;
26
+ private logFileSet;
26
27
  private styles;
27
28
  private static singleton;
28
29
  protected constructor();
@@ -40,6 +41,8 @@ declare class MemLabConsole {
40
41
  private shouldBeConcise;
41
42
  private clearPrevOverwriteMsg;
42
43
  private printStr;
44
+ registerLogFile(logFile: string): void;
45
+ unregisterLogFile(logFile: string): void;
43
46
  beginSection(name: string): void;
44
47
  endSection(name: string): void;
45
48
  setConfig(config: MemLabConfig): void;
@@ -14,10 +14,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const chalk_1 = __importDefault(require("chalk"));
16
16
  const fs_1 = __importDefault(require("fs"));
17
+ const path_1 = __importDefault(require("path"));
17
18
  const readline_1 = __importDefault(require("readline"));
18
19
  const string_width_1 = __importDefault(require("string-width"));
19
20
  const stdout = process.stdout;
20
21
  const TABLE_MAX_WIDTH = 50;
22
+ const LOG_BUFFER_LENGTH = 100;
21
23
  const prevLine = '\x1b[F';
22
24
  const eraseLine = '\x1b[K';
23
25
  const barComplete = chalk_1.default.green('\u2588');
@@ -42,10 +44,21 @@ function formatTableArg(arg) {
42
44
  }
43
45
  });
44
46
  }
47
+ function registerExitCleanup(inst, exitHandler) {
48
+ const p = process;
49
+ // normal exit
50
+ p.on('exit', exitHandler.bind(null, { cleanup: true }));
51
+ // ctrl + c event
52
+ p.on('SIGINT', exitHandler.bind(null, { exit: true }));
53
+ // kill pid
54
+ p.on('SIGUSR1', exitHandler.bind(null, { exit: true }));
55
+ p.on('SIGUSR2', exitHandler.bind(null, { exit: true }));
56
+ }
45
57
  class MemLabConsole {
46
58
  constructor() {
47
59
  this.config = {};
48
60
  this.log = [];
61
+ this.logFileSet = new Set();
49
62
  this.styles = {
50
63
  top: (msg) => msg,
51
64
  high: chalk_1.default.dim.bind(chalk_1.default),
@@ -67,12 +80,15 @@ class MemLabConsole {
67
80
  }
68
81
  const inst = new MemLabConsole();
69
82
  MemLabConsole.singleton = inst;
70
- // clean up output
83
+ const exitHandler = (
84
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
+ _options,
71
86
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
72
- process.on('exit', (_code) => {
73
- inst.flushLog();
87
+ _exitCode) => {
88
+ inst.flushLog({ sync: true });
74
89
  inst.clearPrevOverwriteMsg();
75
- });
90
+ };
91
+ registerExitCleanup(inst, exitHandler);
76
92
  return inst;
77
93
  }
78
94
  style(msg, name) {
@@ -98,28 +114,49 @@ class MemLabConsole {
98
114
  }
99
115
  logMsg(msg) {
100
116
  // remove control characters
101
- const rawMsg = msg
117
+ const lines = msg.split('\n').map(line => line
102
118
  // eslint-disable-next-line no-control-regex
103
119
  .replace(/[\u0000-\u001F\u007F-\u009F]/g, '')
104
- .replace(/\[\d{1,3}m/g, '');
105
- this.log.push(rawMsg);
106
- if (this.log.length > 20) {
107
- this.flushLog();
120
+ .replace(/\[\d{1,3}m/g, ''));
121
+ this.log.push(...lines);
122
+ if (this.log.length > LOG_BUFFER_LENGTH) {
123
+ this.flushLog({ sync: true });
108
124
  }
109
125
  }
110
- flushLog() {
126
+ flushLog(options = {}) {
111
127
  const str = this.log.join('\n');
112
- if (str.length > 0) {
113
- const file = this.config.consoleLogFile;
114
- fs_1.default.appendFile(file, str + '\n', 'UTF-8', () => {
115
- // NOOP
116
- });
117
- }
118
128
  this.log = [];
129
+ if (str.length === 0) {
130
+ return;
131
+ }
132
+ // synchronous logging
133
+ if (options.sync) {
134
+ for (const logFile of this.logFileSet) {
135
+ try {
136
+ fs_1.default.appendFileSync(logFile, str + '\n', 'UTF-8');
137
+ }
138
+ catch (_a) {
139
+ // fail silently
140
+ }
141
+ }
142
+ }
143
+ else {
144
+ // async logging
145
+ const emptyCallback = () => {
146
+ // no op
147
+ };
148
+ for (const logFile of this.logFileSet) {
149
+ try {
150
+ fs_1.default.appendFile(logFile, str + '\n', 'UTF-8', emptyCallback);
151
+ }
152
+ catch (_b) {
153
+ // fail silently
154
+ }
155
+ }
156
+ }
119
157
  }
120
158
  pushMsg(msg, options = {}) {
121
- const len = this.sections.arr.length;
122
- if (this.config.isContinuousTest || len === 0) {
159
+ if (this.sections.arr.length === 0) {
123
160
  return;
124
161
  }
125
162
  // calculate each line's visible width
@@ -140,7 +177,9 @@ class MemLabConsole {
140
177
  if (!section || section.msgs.length === 0) {
141
178
  return;
142
179
  }
143
- stdout.write(eraseLine);
180
+ if (!this.config.muteConsole) {
181
+ stdout.write(eraseLine);
182
+ }
144
183
  const msg = section.msgs.pop();
145
184
  if (!msg) {
146
185
  return;
@@ -150,8 +189,10 @@ class MemLabConsole {
150
189
  const line = (_a = lines.pop()) !== null && _a !== void 0 ? _a : 0;
151
190
  const width = stdout.columns;
152
191
  let n = line === 0 ? 1 : Math.ceil(line / width);
153
- while (n-- > 0) {
154
- stdout.write(prevLine + eraseLine);
192
+ if (!this.config.muteConsole && !this.config.isTest) {
193
+ while (n-- > 0) {
194
+ stdout.write(prevLine + eraseLine);
195
+ }
155
196
  }
156
197
  }
157
198
  }
@@ -189,11 +230,21 @@ class MemLabConsole {
189
230
  this.clearPrevMsgInLastSection();
190
231
  }
191
232
  printStr(msg, options = {}) {
192
- if (this.config.isTest || this.config.muteConsole) {
233
+ this.pushMsg(msg, options);
234
+ if (this.config.isTest) {
193
235
  return;
194
236
  }
195
- console.log(msg);
196
- this.pushMsg(msg, options);
237
+ if (this.config.isContinuousTest || !this.config.muteConsole) {
238
+ console.log(msg);
239
+ }
240
+ }
241
+ registerLogFile(logFile) {
242
+ this.flushLog({ sync: true });
243
+ this.logFileSet.add(path_1.default.resolve(logFile));
244
+ }
245
+ unregisterLogFile(logFile) {
246
+ this.flushLog({ sync: true });
247
+ this.logFileSet.delete(path_1.default.resolve(logFile));
197
248
  }
198
249
  beginSection(name) {
199
250
  if (this.config.isContinuousTest) {
@@ -300,14 +351,16 @@ class MemLabConsole {
300
351
  this.printStr('');
301
352
  }
302
353
  overwrite(msg, options = {}) {
303
- if (this.config.isTest || this.config.muteConsole) {
354
+ const str = this.style(msg, options.level || 'low');
355
+ if (this.config.isContinuousTest) {
356
+ this.printStr(msg, { isOverwrite: false });
304
357
  return;
305
358
  }
306
- if (this.config.isContinuousTest) {
307
- return console.log(msg);
359
+ if (this.config.isTest || this.config.muteConsole) {
360
+ this.printStr(str, { isOverwrite: true });
361
+ return;
308
362
  }
309
363
  this.clearPrevOverwriteMsg();
310
- const str = this.style(msg, options.level || 'low');
311
364
  this.printStr(str, { isOverwrite: true });
312
365
  }
313
366
  waitForConsole(query) {
@@ -8,32 +8,31 @@
8
8
  * @oncall web_perf_infra
9
9
  */
10
10
  import type { MemLabConfig } from './Config';
11
- import type { AnyValue, Optional } from './Types';
12
- /** @internal */
13
- export declare type FileOption = {
14
- workDir?: Optional<string>;
15
- clear?: boolean;
16
- transient?: boolean;
17
- };
11
+ import type { AnyValue, FileOption } from './Types';
18
12
  /** @internal */
19
13
  export declare class FileManager {
20
14
  getDefaultWorkDir(): string;
21
15
  generateTmpHeapDir(): string;
22
16
  private static transientInstanceIdx;
17
+ static defaultFileOption: FileOption;
23
18
  getWorkDir(options?: FileOption): string;
24
19
  getChromeBinaryZipFile(): string;
25
20
  getChromeBinaryTimeStampFile(): string;
26
21
  getChromeBinaryDir(): string;
27
- clearUserDataDir(options: FileOption): void;
28
- getDataBaseDir(options: FileOption): string;
22
+ getDataBaseDir(options?: FileOption): string;
29
23
  getCodeDataDir(): string;
30
24
  getClusterSampleDataDir(): string;
31
- getUserDataDir(options: FileOption): string;
32
- getCurDataDir(options: FileOption): string;
25
+ getUserDataDir(options?: FileOption): string;
26
+ clearUserDataDir(options?: FileOption): void;
27
+ getCurDataDir(options?: FileOption): string;
33
28
  getWebSourceDir(options?: FileOption): string;
34
29
  getWebSourceMetaFile(options?: FileOption): string;
35
- getPersistDataDir(options: FileOption): string;
30
+ getDebugDataDir(options?: FileOption): string;
31
+ getDebugSourceFile(options?: FileOption): string;
32
+ getPersistDataDir(options?: FileOption): string;
36
33
  getLoggerOutDir(options?: FileOption): string;
34
+ getHeapAnalysisLogDir(options?: FileOption): string;
35
+ getHeapSaveLogJSONFile(options?: FileOption): string;
37
36
  getTraceClustersDir(options?: FileOption): string;
38
37
  getTraceJSONDir(options?: FileOption): string;
39
38
  getUnclassifiedTraceClusterDir(options?: FileOption): string;
@@ -61,6 +60,7 @@ export declare class FileManager {
61
60
  controlWorkDir: string;
62
61
  testWorkDir: string;
63
62
  };
63
+ initNewHeapAnalysisLogFile(options?: FileOption): string;
64
64
  getAndInitTSCompileIntermediateDir(): string;
65
65
  clearDataDirs(options?: FileOption): void;
66
66
  emptyDirIfExists(dir: string): void;
@@ -24,7 +24,7 @@ function joinAndProcessDir(options, ...args) {
24
24
  const filepath = path_1.default.join(...args);
25
25
  if (!fs_extra_1.default.existsSync(filepath)) {
26
26
  try {
27
- fs_extra_1.default.mkdirSync(filepath);
27
+ fs_extra_1.default.mkdirpSync(filepath);
28
28
  }
29
29
  catch (ex) {
30
30
  const err = Utils_1.default.getError(ex);
@@ -43,11 +43,11 @@ class FileManager {
43
43
  generateTmpHeapDir() {
44
44
  const dirPath = path_1.default.join(this.getTmpDir(), 'memlab-' + Utils_1.default.getUniqueID());
45
45
  if (!fs_extra_1.default.existsSync(dirPath)) {
46
- fs_extra_1.default.mkdirSync(dirPath);
46
+ fs_extra_1.default.mkdirpSync(dirPath);
47
47
  }
48
48
  return dirPath;
49
49
  }
50
- getWorkDir(options = {}) {
50
+ getWorkDir(options = FileManager.defaultFileOption) {
51
51
  // workDir options supercedes all the other options
52
52
  if (options.workDir) {
53
53
  return path_1.default.resolve(options.workDir);
@@ -73,11 +73,7 @@ class FileManager {
73
73
  getChromeBinaryDir() {
74
74
  return path_1.default.join(this.getDefaultWorkDir(), 'chrome');
75
75
  }
76
- clearUserDataDir(options) {
77
- const userDataDir = this.getUserDataDir(options);
78
- this.rmDir(userDataDir);
79
- }
80
- getDataBaseDir(options) {
76
+ getDataBaseDir(options = FileManager.defaultFileOption) {
81
77
  return path_1.default.join(this.getWorkDir(options), 'data');
82
78
  }
83
79
  getCodeDataDir() {
@@ -86,56 +82,74 @@ class FileManager {
86
82
  getClusterSampleDataDir() {
87
83
  return path_1.default.join(this.getCodeDataDir(), 'cluster');
88
84
  }
89
- getUserDataDir(options) {
85
+ getUserDataDir(options = FileManager.defaultFileOption) {
90
86
  return path_1.default.join(this.getDataBaseDir(options), 'profile');
91
87
  }
92
- getCurDataDir(options) {
88
+ clearUserDataDir(options = FileManager.defaultFileOption) {
89
+ const userDataDir = this.getUserDataDir(options);
90
+ this.rmDir(userDataDir);
91
+ }
92
+ getCurDataDir(options = FileManager.defaultFileOption) {
93
93
  return path_1.default.join(this.getDataBaseDir(options), 'cur');
94
94
  }
95
- getWebSourceDir(options = {}) {
95
+ getWebSourceDir(options = FileManager.defaultFileOption) {
96
96
  return path_1.default.join(this.getCurDataDir(options), 'code');
97
97
  }
98
- getWebSourceMetaFile(options = {}) {
98
+ getWebSourceMetaFile(options = FileManager.defaultFileOption) {
99
99
  return path_1.default.join(this.getWebSourceDir(options), 'files.json');
100
100
  }
101
- getPersistDataDir(options) {
101
+ getDebugDataDir(options = FileManager.defaultFileOption) {
102
+ return path_1.default.join(this.getDataBaseDir(options), 'debug');
103
+ }
104
+ getDebugSourceFile(options = FileManager.defaultFileOption) {
105
+ return path_1.default.join(this.getDebugDataDir(options), 'file.js');
106
+ }
107
+ getPersistDataDir(options = FileManager.defaultFileOption) {
102
108
  return path_1.default.join(this.getDataBaseDir(options), 'persist');
103
109
  }
104
- getLoggerOutDir(options = {}) {
110
+ getLoggerOutDir(options = FileManager.defaultFileOption) {
105
111
  return path_1.default.join(this.getDataBaseDir(options), 'logger');
106
112
  }
113
+ // all heap analysis results generated
114
+ getHeapAnalysisLogDir(options = FileManager.defaultFileOption) {
115
+ return path_1.default.join(this.getLoggerOutDir(options), 'heap-analysis');
116
+ }
117
+ // memlab save-heap result log file
118
+ getHeapSaveLogJSONFile(options = FileManager.defaultFileOption) {
119
+ return path_1.default.join(this.getHeapAnalysisLogDir(options), 'save-heap.json');
120
+ }
107
121
  // all trace clusters generated from the current run
108
- getTraceClustersDir(options = {}) {
122
+ getTraceClustersDir(options = FileManager.defaultFileOption) {
109
123
  return path_1.default.join(this.getLoggerOutDir(options), 'trace-clusters');
110
124
  }
111
125
  // stores JSON file (with heap object and reference details for visualization)
112
126
  // of all trace clusters (each cluster has a representative trace)
113
- getTraceJSONDir(options = {}) {
127
+ getTraceJSONDir(options = FileManager.defaultFileOption) {
114
128
  return path_1.default.join(this.getLoggerOutDir(options), 'trace-jsons');
115
129
  }
116
- getUnclassifiedTraceClusterDir(options = {}) {
130
+ getUnclassifiedTraceClusterDir(options = FileManager.defaultFileOption) {
117
131
  return path_1.default.join(this.getLoggerOutDir(options), 'unclassified-clusters');
118
132
  }
119
- getUniqueTraceClusterDir(options = {}) {
133
+ getUniqueTraceClusterDir(options = FileManager.defaultFileOption) {
120
134
  return path_1.default.join(this.getLoggerOutDir(options), 'unique-trace-clusters');
121
135
  }
122
- getNewUniqueTraceClusterDir(options = {}) {
136
+ getNewUniqueTraceClusterDir(options = FileManager.defaultFileOption) {
123
137
  return path_1.default.join(this.getUniqueTraceClusterDir(options), 'add');
124
138
  }
125
- getStaleUniqueTraceClusterDir(options = {}) {
139
+ getStaleUniqueTraceClusterDir(options = FileManager.defaultFileOption) {
126
140
  return path_1.default.join(this.getUniqueTraceClusterDir(options), 'delete');
127
141
  }
128
- getAllClusterSummaryFile(options = {}) {
142
+ getAllClusterSummaryFile(options = FileManager.defaultFileOption) {
129
143
  return path_1.default.join(this.getUniqueTraceClusterDir(options), 'unique-clusters-summary.txt');
130
144
  }
131
- getExistingUniqueTraceClusterDir(options = {}) {
145
+ getExistingUniqueTraceClusterDir(options = FileManager.defaultFileOption) {
132
146
  return path_1.default.join(this.getUniqueTraceClusterDir(options), 'existing');
133
147
  }
134
148
  getAllFilesInDir(dir) {
135
149
  const files = fs_extra_1.default.readdirSync(dir);
136
150
  return files.map((file) => path_1.default.join(dir, file));
137
151
  }
138
- getDataOutDir(options = {}) {
152
+ getDataOutDir(options = FileManager.defaultFileOption) {
139
153
  return path_1.default.join(this.getDataBaseDir(options), 'out');
140
154
  }
141
155
  getCoreProjectBaseDir() {
@@ -147,19 +161,19 @@ class FileManager {
147
161
  getDocDir() {
148
162
  return path_1.default.join(this.getMonoRepoDir(), 'website', 'docs');
149
163
  }
150
- getReportOutDir(options = {}) {
164
+ getReportOutDir(options = FileManager.defaultFileOption) {
151
165
  return path_1.default.join(this.getPersistDataDir(options), 'reports');
152
166
  }
153
- getPreviewReportDir(options = {}) {
167
+ getPreviewReportDir(options = FileManager.defaultFileOption) {
154
168
  return path_1.default.join(this.getPersistDataDir(options), 'report-preview');
155
169
  }
156
- getLeakSummaryFile(options = {}) {
170
+ getLeakSummaryFile(options = FileManager.defaultFileOption) {
157
171
  return path_1.default.join(this.getDataOutDir(options), 'leaks.txt');
158
172
  }
159
- getRunMetaFile(options = {}) {
173
+ getRunMetaFile(options = FileManager.defaultFileOption) {
160
174
  return path_1.default.join(this.getCurDataDir(options), 'run-meta.json');
161
175
  }
162
- getSnapshotSequenceMetaFile(options = {}) {
176
+ getSnapshotSequenceMetaFile(options = FileManager.defaultFileOption) {
163
177
  return path_1.default.join(this.getCurDataDir(options), 'snap-seq.json');
164
178
  }
165
179
  getInputDataDir() {
@@ -182,6 +196,7 @@ class FileManager {
182
196
  }
183
197
  return ret;
184
198
  }
199
+ // system default tmp dir
185
200
  getTmpDir() {
186
201
  return os_1.default.tmpdir();
187
202
  }
@@ -200,18 +215,28 @@ class FileManager {
200
215
  const testWorkDir = joinAndProcessDir({}, expDir, 'test');
201
216
  return { controlWorkDir, testWorkDir };
202
217
  }
218
+ // create a unique log file created for heap analysis output
219
+ initNewHeapAnalysisLogFile(options = FileManager.defaultFileOption) {
220
+ const dir = this.getHeapAnalysisLogDir(options);
221
+ const file = path_1.default.join(dir, `analysis-${Utils_1.default.getUniqueID()}-out.log`);
222
+ if (!fs_extra_1.default.existsSync(file)) {
223
+ fs_extra_1.default.createFileSync(file);
224
+ }
225
+ return file;
226
+ }
203
227
  getAndInitTSCompileIntermediateDir() {
204
228
  const dir = path_1.default.join(this.getTmpDir(), 'memlab-code');
205
229
  this.rmDir(dir);
206
- fs_extra_1.default.mkdirSync(dir);
230
+ fs_extra_1.default.mkdirpSync(dir);
207
231
  return dir;
208
232
  }
209
- clearDataDirs(options = {}) {
233
+ clearDataDirs(options = FileManager.defaultFileOption) {
210
234
  const curDataDir = this.getCurDataDir(options);
211
235
  if (!fs_extra_1.default.existsSync(curDataDir)) {
212
236
  return;
213
237
  }
214
238
  this.emptyDirIfExists(this.getWebSourceDir(options));
239
+ this.emptyDirIfExists(this.getDebugDataDir(options));
215
240
  const dataSuffix = ['.heapsnapshot', '.json', '.png'];
216
241
  const files = fs_extra_1.default.readdirSync(curDataDir);
217
242
  for (const file of files) {
@@ -229,7 +254,7 @@ class FileManager {
229
254
  fs_extra_1.default.emptyDirSync(dir);
230
255
  }
231
256
  }
232
- emptyTraceLogDataDir(options = {}) {
257
+ emptyTraceLogDataDir(options = FileManager.defaultFileOption) {
233
258
  // all leak trace clusters
234
259
  this.emptyDirIfExists(this.getTraceClustersDir(options));
235
260
  // all JSON files for trace visualization
@@ -238,6 +263,8 @@ class FileManager {
238
263
  this.emptyDirIfExists(this.getUnclassifiedTraceClusterDir(options));
239
264
  // all unique cluster info
240
265
  this.emptyDirIfExists(this.getUniqueTraceClusterDir(options));
266
+ // all heap analysis results
267
+ this.emptyDirIfExists(this.getHeapAnalysisLogDir(options));
241
268
  }
242
269
  resetBrowserDir() {
243
270
  try {
@@ -268,7 +295,7 @@ class FileManager {
268
295
  this.iterateAllFiles(filepath, callback);
269
296
  });
270
297
  }
271
- rmWorkDir(options = {}) {
298
+ rmWorkDir(options = FileManager.defaultFileOption) {
272
299
  try {
273
300
  this.rmDir(this.getWorkDir(options));
274
301
  }
@@ -281,7 +308,7 @@ class FileManager {
281
308
  const internalDir = Constant_1.default.internalDir;
282
309
  return filePath.includes(`${sep}${internalDir}${sep}`);
283
310
  }
284
- initDirs(config, options = {}) {
311
+ initDirs(config, options = FileManager.defaultFileOption) {
285
312
  config.monoRepoDir = Constant_1.default.monoRepoDir;
286
313
  // make sure getWorkDir is called first before
287
314
  // any other get file or get dir calls
@@ -294,9 +321,12 @@ class FileManager {
294
321
  const outDir = joinAndProcessDir(options, this.getDataOutDir(options));
295
322
  config.curDataDir = joinAndProcessDir(options, this.getCurDataDir(options));
296
323
  config.webSourceDir = joinAndProcessDir(options, this.getWebSourceDir(options));
324
+ config.debugDataDir = joinAndProcessDir(options, this.getDebugDataDir(options));
297
325
  config.dataBuilderDataDir = joinAndProcessDir(options, config.dataBaseDir, 'dataBuilder');
298
326
  config.persistentDataDir = joinAndProcessDir(options, this.getPersistDataDir(options));
327
+ // register the default log file
299
328
  config.consoleLogFile = path_1.default.join(config.curDataDir, 'console-log.txt');
329
+ Console_1.default.registerLogFile(config.consoleLogFile);
300
330
  config.runMetaFile = this.getRunMetaFile(options);
301
331
  config.snapshotSequenceFile = this.getSnapshotSequenceMetaFile(options);
302
332
  config.browserInfoSummary = path_1.default.join(config.curDataDir, 'browser-info.txt');
@@ -314,6 +344,8 @@ class FileManager {
314
344
  config.traceClusterOutDir = joinAndProcessDir(options, this.getTraceClustersDir(options));
315
345
  // detailed trace json files for visualization
316
346
  config.traceJsonOutDir = joinAndProcessDir(options, this.getTraceJSONDir(options));
347
+ // heap analysis results
348
+ config.heapAnalysisLogDir = joinAndProcessDir(options, this.getHeapAnalysisLogDir(options));
317
349
  config.metricsOutDir = joinAndProcessDir(options, loggerOutDir, 'metrics');
318
350
  config.reportScreenshotFile = path_1.default.join(outDir, 'report.png');
319
351
  const codeDataDir = this.getCodeDataDir();
@@ -329,4 +361,5 @@ class FileManager {
329
361
  }
330
362
  exports.FileManager = FileManager;
331
363
  FileManager.transientInstanceIdx = 0;
364
+ FileManager.defaultFileOption = {};
332
365
  exports.default = new FileManager();
@@ -7,15 +7,10 @@
7
7
  * @format
8
8
  * @oncall web_perf_infra
9
9
  */
10
- import type { E2EStepInfo, HeapNodeIdSet, IHeapNode, IHeapSnapshot, IMemoryAnalystOptions, IMemoryAnalystSnapshotDiff, LeakTracePathItem, Optional, IOveralLeakInfo, TraceCluster, ISerializedInfo } from './Types';
10
+ import type { E2EStepInfo, HeapNodeIdSet, IHeapSnapshot, IMemoryAnalystOptions, IMemoryAnalystSnapshotDiff, IOveralHeapInfo, LeakTracePathItem, Optional, IOveralLeakInfo, TraceCluster, ISerializedInfo } from './Types';
11
11
  import TraceFinder from '../paths/TraceFinder';
12
12
  declare class MemoryAnalyst {
13
13
  checkLeak(): Promise<ISerializedInfo[]>;
14
- checkUnbound(options?: IMemoryAnalystOptions): Promise<void>;
15
- breakDownMemoryByShapes(options?: {
16
- file?: string;
17
- }): Promise<void>;
18
- detectUnboundGrowth(options?: IMemoryAnalystOptions): Promise<void>;
19
14
  detectMemoryLeaks(): Promise<ISerializedInfo[]>;
20
15
  visualizeMemoryUsage(options?: IMemoryAnalystOptions): void;
21
16
  focus(options?: {
@@ -23,19 +18,15 @@ declare class MemoryAnalyst {
23
18
  }): Promise<void>;
24
19
  shouldLoadCompleteSnapshot(tabsOrder: E2EStepInfo[], tab: E2EStepInfo): boolean;
25
20
  diffSnapshots(loadAll?: boolean): Promise<IMemoryAnalystSnapshotDiff>;
26
- private calculateRetainedSizes;
27
21
  preparePathFinder(snapshot: IHeapSnapshot): TraceFinder;
28
22
  private dumpPageInteractionSummary;
29
23
  private dumpLeakSummaryToConsole;
30
24
  private filterLeakedObjects;
31
- aggregateDominatorMetrics(ids: HeapNodeIdSet, snapshot: IHeapSnapshot, checkNodeCb: (node: IHeapNode) => boolean, nodeMetricsCb: (node: IHeapNode) => number): number;
32
- private getOverallHeapInfo;
33
- getRetainedSize(node: IHeapNode): number;
25
+ getOverallHeapInfo(snapshot: IHeapSnapshot, options?: {
26
+ force?: boolean;
27
+ }): Optional<IOveralHeapInfo>;
34
28
  getOverallLeakInfo(leakedNodeIds: HeapNodeIdSet, snapshot: IHeapSnapshot): Optional<IOveralLeakInfo>;
35
- private printHeapInfo;
36
- private breakDownSnapshotByShapes;
37
- private isTrivialEdgeForBreakDown;
38
- private breakDownByReferrers;
29
+ printHeapInfo(leakInfo: IOveralHeapInfo): void;
39
30
  private printHeapAndLeakInfo;
40
31
  private logLeakTraceSummary;
41
32
  searchLeakedTraces(leakedNodeIds: HeapNodeIdSet, snapshot: IHeapSnapshot): Promise<{