@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.
- package/dist/lib/Config.d.ts +5 -2
- package/dist/lib/Config.js +35 -1
- package/dist/lib/Console.d.ts +3 -0
- package/dist/lib/Console.js +81 -28
- package/dist/lib/FileManager.d.ts +12 -12
- package/dist/lib/FileManager.js +66 -33
- package/dist/lib/HeapAnalyzer.d.ts +5 -14
- package/dist/lib/HeapAnalyzer.js +9 -285
- package/dist/lib/Types.d.ts +80 -7
- package/dist/lib/Utils.d.ts +7 -1
- package/dist/lib/Utils.js +49 -1
- package/dist/lib/heap-data/HeapNode.js +3 -4
- package/dist/lib/heap-data/HeapSnapshot.js +2 -2
- package/package.json +2 -2
package/dist/lib/Config.d.ts
CHANGED
|
@@ -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?: {
|
package/dist/lib/Config.js
CHANGED
|
@@ -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 =
|
|
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);
|
package/dist/lib/Console.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/Console.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
106
|
-
if (this.log.length >
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
154
|
-
|
|
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
|
-
|
|
233
|
+
this.pushMsg(msg, options);
|
|
234
|
+
if (this.config.isTest) {
|
|
193
235
|
return;
|
|
194
236
|
}
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
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.
|
|
307
|
-
|
|
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,
|
|
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
|
-
|
|
28
|
-
getDataBaseDir(options: FileOption): string;
|
|
22
|
+
getDataBaseDir(options?: FileOption): string;
|
|
29
23
|
getCodeDataDir(): string;
|
|
30
24
|
getClusterSampleDataDir(): string;
|
|
31
|
-
getUserDataDir(options
|
|
32
|
-
|
|
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
|
-
|
|
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;
|
package/dist/lib/FileManager.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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,
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
getOverallHeapInfo(snapshot: IHeapSnapshot, options?: {
|
|
26
|
+
force?: boolean;
|
|
27
|
+
}): Optional<IOveralHeapInfo>;
|
|
34
28
|
getOverallLeakInfo(leakedNodeIds: HeapNodeIdSet, snapshot: IHeapSnapshot): Optional<IOveralLeakInfo>;
|
|
35
|
-
|
|
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<{
|