@andersbakken/fisk 5.0.7 → 5.0.11
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/builder/VM_runtime.js +49 -5
- package/builder/fisk-builder.js +13 -8
- package/daemon/fisk-daemon.js +5 -6
- package/package.json +1 -1
- package/scheduler/fisk-scheduler.js +39 -1
package/builder/VM_runtime.js
CHANGED
|
@@ -3027,8 +3027,20 @@ var output = {
|
|
|
3027
3027
|
|
|
3028
3028
|
var fs = libExports;
|
|
3029
3029
|
|
|
3030
|
+
// Padded canonical prefixes for DWARF path patching. The fisk client scans
|
|
3031
|
+
// compiled objects for these byte patterns and overwrites them in-place with
|
|
3032
|
+
// the real paths. Because the replacement is always shorter, a NUL terminator
|
|
3033
|
+
// followed by NUL fill fits inside the original region. This works on both ELF
|
|
3034
|
+
// objects and LTO bitcode.
|
|
3035
|
+
//
|
|
3036
|
+
// The length is PATH_MAX so any path the client could legally hand us fits
|
|
3037
|
+
// without truncation.
|
|
3038
|
+
// Keep in sync with FISK_PAD_LENGTH in src/client/FiskPathPatcher.cpp.
|
|
3039
|
+
const FISK_PAD_LENGTH = 4096;
|
|
3040
|
+
const FISK_NAME_PAD = "/fisk-name" + "_".repeat(FISK_PAD_LENGTH - "/fisk-name".length);
|
|
3041
|
+
const FISK_CDIR_PAD = "/fisk-cdir" + "_".repeat(FISK_PAD_LENGTH - "/fisk-cdir".length);
|
|
3030
3042
|
class Compile extends EventEmitter__default["default"] {
|
|
3031
|
-
constructor(args, argv0, dir, debug) {
|
|
3043
|
+
constructor(args, argv0, dir, debug, sourceFileName, paddedPaths) {
|
|
3032
3044
|
super();
|
|
3033
3045
|
if (!args || !args.length || !dir || !argv0) {
|
|
3034
3046
|
console.error(argv0, args, dir);
|
|
@@ -3141,7 +3153,10 @@ class Compile extends EventEmitter__default["default"] {
|
|
|
3141
3153
|
throw new Error("More than one source file");
|
|
3142
3154
|
}
|
|
3143
3155
|
sourcePath = args[i];
|
|
3144
|
-
|
|
3156
|
+
if (!sourceFileName) {
|
|
3157
|
+
sourceFileName = path__default["default"].basename(sourcePath);
|
|
3158
|
+
}
|
|
3159
|
+
args[i] = path__default["default"].join(dir, sourceFileName);
|
|
3145
3160
|
}
|
|
3146
3161
|
break;
|
|
3147
3162
|
}
|
|
@@ -3149,6 +3164,27 @@ class Compile extends EventEmitter__default["default"] {
|
|
|
3149
3164
|
if (!sourcePath) {
|
|
3150
3165
|
throw new Error("No sourcefile");
|
|
3151
3166
|
}
|
|
3167
|
+
const sourceFileInDir = path__default["default"].join(dir, sourceFileName || path__default["default"].basename(sourcePath));
|
|
3168
|
+
if (paddedPaths) {
|
|
3169
|
+
// -grecord-command-line embeds our own argv verbatim into
|
|
3170
|
+
// DW_AT_producer, which would (a) add two PATH_MAX pads to every CU
|
|
3171
|
+
// and (b) leave the builder's /compiles path in the producer, where
|
|
3172
|
+
// the client's byte scan would hit the embedded pad and NUL-truncate
|
|
3173
|
+
// the rest of the recorded command line. The recorded line would be
|
|
3174
|
+
// the builder's rewritten argv anyway -- not the client's -- so drop
|
|
3175
|
+
// it rather than record something both wrong and mangled.
|
|
3176
|
+
for (let i = args.length - 1; i >= 0; --i) {
|
|
3177
|
+
if (args[i] === "-grecord-command-line" || args[i] === "-frecord-command-line") {
|
|
3178
|
+
args.splice(i, 1);
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
args.push("-gno-record-command-line");
|
|
3182
|
+
// The more specific source-file rule must come last: both clang and
|
|
3183
|
+
// gcc let a later -fdebug-prefix-map win over an earlier one, and
|
|
3184
|
+
// the directory rule is a prefix of the source-file rule.
|
|
3185
|
+
args.push(`-fdebug-prefix-map=${dir}=${FISK_CDIR_PAD}`);
|
|
3186
|
+
args.push(`-fdebug-prefix-map=${sourceFileInDir}=${FISK_NAME_PAD}`);
|
|
3187
|
+
}
|
|
3152
3188
|
if (!hasDashX) {
|
|
3153
3189
|
switch (path__default["default"].extname(sourcePath)) {
|
|
3154
3190
|
case ".C":
|
|
@@ -3216,7 +3252,14 @@ class Compile extends EventEmitter__default["default"] {
|
|
|
3216
3252
|
if (!fs.existsSync("/usr/bin/as")) {
|
|
3217
3253
|
this.emit("stderr", "as doesn't exist");
|
|
3218
3254
|
}
|
|
3219
|
-
console.log(`Compiling source file: ${sourcePath}\n${[compiler, ...args]
|
|
3255
|
+
console.log(`Compiling source file: ${sourcePath}\n${[compiler, ...args]
|
|
3256
|
+
.map((x) => {
|
|
3257
|
+
if (x.startsWith("-fdebug-prefix-map=")) {
|
|
3258
|
+
x = x.replace(/_+$/, "___");
|
|
3259
|
+
}
|
|
3260
|
+
return x;
|
|
3261
|
+
})
|
|
3262
|
+
.join(" ")}`);
|
|
3220
3263
|
// const env = Object.assign({ TMPDIR: dir, TEMPDIR: dir, TEMP: dir }, process.env);
|
|
3221
3264
|
const proc = child_process__default["default"].spawn(compiler, args, {
|
|
3222
3265
|
/*env: env, */ cwd: dir // , maxBuffer: 1024 * 1024 * 16
|
|
@@ -3239,8 +3282,9 @@ class Compile extends EventEmitter__default["default"] {
|
|
|
3239
3282
|
let addDirError;
|
|
3240
3283
|
const addDir = (directory, prefix) => {
|
|
3241
3284
|
try {
|
|
3285
|
+
const sourceBaseName = sourceFileName || path__default["default"].basename(sourcePath);
|
|
3242
3286
|
fs.readdirSync(directory).forEach((file) => {
|
|
3243
|
-
if (file ===
|
|
3287
|
+
if (file === sourceBaseName) {
|
|
3244
3288
|
return;
|
|
3245
3289
|
}
|
|
3246
3290
|
try {
|
|
@@ -3727,7 +3771,7 @@ process.on("message", (msg) => {
|
|
|
3727
3771
|
if (argv.debug) {
|
|
3728
3772
|
console.log("Creating new compile", msg.commandLine, msg.argv0, msg.dir);
|
|
3729
3773
|
}
|
|
3730
|
-
const compile = new Compile(msg.commandLine, msg.argv0, msg.dir, argv.debug);
|
|
3774
|
+
const compile = new Compile(msg.commandLine, msg.argv0, msg.dir, argv.debug, msg.sourceFileName, msg.paddedPaths);
|
|
3731
3775
|
// console.log("running thing", msg.commandLine);
|
|
3732
3776
|
compile.on("stdout", (data) => {
|
|
3733
3777
|
send({ type: "compileStdOut", id: msg.id, data: data });
|
package/builder/fisk-builder.js
CHANGED
|
@@ -8171,6 +8171,7 @@ class Job extends EventEmitter__default["default"] {
|
|
|
8171
8171
|
this.sha1 = data.sha1;
|
|
8172
8172
|
this.id = data.id;
|
|
8173
8173
|
this.builderIp = data.builderIp;
|
|
8174
|
+
this.paddedPaths = data.paddedPaths;
|
|
8174
8175
|
this.supportsCompressedResponse = data.supportsCompressedResponse;
|
|
8175
8176
|
}
|
|
8176
8177
|
get readyState() {
|
|
@@ -54954,6 +54955,7 @@ class Server extends EventEmitter__default["default"] {
|
|
|
54954
54955
|
sourcePath: String(req.headers["x-fisk-sourcefile"]),
|
|
54955
54956
|
user: String(req.headers["x-fisk-user"]),
|
|
54956
54957
|
supportsCompressedResponse: req.headers["x-fisk-supports-compressed-response"] === "true",
|
|
54958
|
+
paddedPaths: req.headers["x-fisk-padded-paths"] === "true",
|
|
54957
54959
|
ws
|
|
54958
54960
|
});
|
|
54959
54961
|
break;
|
|
@@ -55049,16 +55051,18 @@ class Server extends EventEmitter__default["default"] {
|
|
|
55049
55051
|
}
|
|
55050
55052
|
|
|
55051
55053
|
class CompileJob extends EventEmitter__default["default"] {
|
|
55052
|
-
constructor(commandLine, argv0, id, vm) {
|
|
55054
|
+
constructor(commandLine, argv0, id, vm, sourcePath, paddedPaths) {
|
|
55053
55055
|
super();
|
|
55054
55056
|
this.commandLine = commandLine;
|
|
55055
55057
|
this.argv0 = argv0;
|
|
55056
55058
|
this.id = id;
|
|
55057
55059
|
this.vm = vm;
|
|
55060
|
+
this.paddedPaths = paddedPaths;
|
|
55058
55061
|
this.dir = path__default["default"].join(vm.root, "compiles", String(this.id));
|
|
55059
55062
|
this.vmDir = path__default["default"].join("/", "compiles", String(this.id));
|
|
55063
|
+
this.sourceFileName = sourcePath ? path__default["default"].basename(sourcePath) : "sourcefile";
|
|
55060
55064
|
fs$3.mkdirpSync(this.dir);
|
|
55061
|
-
this.fd = fs$3.openSync(path__default["default"].join(this.dir,
|
|
55065
|
+
this.fd = fs$3.openSync(path__default["default"].join(this.dir, this.sourceFileName), "w");
|
|
55062
55066
|
this.cppSize = 0;
|
|
55063
55067
|
this.startCompile = undefined;
|
|
55064
55068
|
}
|
|
@@ -55084,7 +55088,7 @@ class CompileJob extends EventEmitter__default["default"] {
|
|
|
55084
55088
|
this.startCompile = Date.now();
|
|
55085
55089
|
fs$3.closeSync(this.fd);
|
|
55086
55090
|
this.fd = undefined;
|
|
55087
|
-
this.vm.child.send({ type: "compile", commandLine: this.commandLine, argv0: this.argv0, id: this.id, dir: this.vmDir }, this.sendCallback.bind(this));
|
|
55091
|
+
this.vm.child.send({ type: "compile", commandLine: this.commandLine, argv0: this.argv0, id: this.id, dir: this.vmDir, sourceFileName: this.sourceFileName, paddedPaths: this.paddedPaths }, this.sendCallback.bind(this));
|
|
55088
55092
|
}
|
|
55089
55093
|
cancel() {
|
|
55090
55094
|
this.vm.child.send({ type: "cancel", id: this.id }, this.sendCallback.bind(this));
|
|
@@ -55195,8 +55199,8 @@ class VM extends EventEmitter__default["default"] {
|
|
|
55195
55199
|
}
|
|
55196
55200
|
});
|
|
55197
55201
|
}
|
|
55198
|
-
startCompile(commandLine, argv0, id) {
|
|
55199
|
-
const compile = new CompileJob(commandLine, argv0, id, this);
|
|
55202
|
+
startCompile(commandLine, argv0, id, sourcePath, paddedPaths) {
|
|
55203
|
+
const compile = new CompileJob(commandLine, argv0, id, this, sourcePath, paddedPaths);
|
|
55200
55204
|
this.compiles[compile.id] = compile;
|
|
55201
55205
|
// console.log("startCompile " + compile.id);
|
|
55202
55206
|
return compile;
|
|
@@ -55207,7 +55211,7 @@ class VM extends EventEmitter__default["default"] {
|
|
|
55207
55211
|
}
|
|
55208
55212
|
|
|
55209
55213
|
const Version = 5;
|
|
55210
|
-
const ObjectCacheFormatVersion =
|
|
55214
|
+
const ObjectCacheFormatVersion = 5;
|
|
55211
55215
|
function cacheDir(option) {
|
|
55212
55216
|
let dir = option("cache-dir");
|
|
55213
55217
|
if (!dir) {
|
|
@@ -60918,7 +60922,7 @@ server.on("job", (job) => {
|
|
|
60918
60922
|
console.log("Starting job", j.id, jobJob.sourcePath, "for", jobJob.ip, jobJob.name, "wait", jobJob.wait);
|
|
60919
60923
|
assert__default["default"](jobJob.commandLine, "Must have commandLine");
|
|
60920
60924
|
assert__default["default"](jobJob.argv0, "Must have argv0");
|
|
60921
|
-
j.op = vm.startCompile(jobJob.commandLine, jobJob.argv0, jobJob.id);
|
|
60925
|
+
j.op = vm.startCompile(jobJob.commandLine, jobJob.argv0, jobJob.id, jobJob.sourcePath, jobJob.paddedPaths);
|
|
60922
60926
|
if (j.buffer) {
|
|
60923
60927
|
j.op.feed(j.buffer);
|
|
60924
60928
|
j.buffer = undefined;
|
|
@@ -60984,6 +60988,8 @@ server.on("job", (job) => {
|
|
|
60984
60988
|
success: event.success,
|
|
60985
60989
|
exitCode: event.exitCode,
|
|
60986
60990
|
sha1: jobJob.sha1,
|
|
60991
|
+
sourcePath: path__default["default"].join(j.op.vmDir, j.op.sourceFileName),
|
|
60992
|
+
originalSourcePath: jobJob.sourcePath,
|
|
60987
60993
|
stderr: j.stderr,
|
|
60988
60994
|
stdout: j.stdout
|
|
60989
60995
|
};
|
|
@@ -61008,7 +61014,6 @@ server.on("job", (job) => {
|
|
|
61008
61014
|
uncompressedSize: item.uncompressed.byteLength
|
|
61009
61015
|
};
|
|
61010
61016
|
}) });
|
|
61011
|
-
cacheResponse.sourcePath = jobJob.sourcePath;
|
|
61012
61017
|
cacheResponse.commandLine = jobJob.commandLine;
|
|
61013
61018
|
cacheResponse.environment = jobJob.hash;
|
|
61014
61019
|
objectCache.add(cacheResponse, contents);
|
package/daemon/fisk-daemon.js
CHANGED
|
@@ -3762,7 +3762,7 @@ class Slots extends EventEmitter__default["default"] {
|
|
|
3762
3762
|
}
|
|
3763
3763
|
|
|
3764
3764
|
const Version = 5;
|
|
3765
|
-
const ObjectCacheFormatVersion =
|
|
3765
|
+
const ObjectCacheFormatVersion = 5;
|
|
3766
3766
|
function cacheDir(option) {
|
|
3767
3767
|
let dir = option("cache-dir");
|
|
3768
3768
|
if (!dir) {
|
|
@@ -4511,7 +4511,7 @@ const option = createOptions({
|
|
|
4511
4511
|
const common = common$1(option);
|
|
4512
4512
|
const debug = option("debug");
|
|
4513
4513
|
process.on("unhandledRejection", (reason, p) => {
|
|
4514
|
-
console.
|
|
4514
|
+
console.error("Unhandled Rejection at: Promise", p, "reason:", reason === null || reason === void 0 ? void 0 : reason.stack);
|
|
4515
4515
|
process.exit();
|
|
4516
4516
|
// if (client)
|
|
4517
4517
|
// client.send('log', { message: `Unhandled Rejection at: Promise ${p}, reason: ${reason.stack}` });
|
|
@@ -4535,6 +4535,7 @@ const compileSlots = new Slots(option.int("slots", Math.max(os__default["default
|
|
|
4535
4535
|
const localSlotCount = option.int("local-slots", 0);
|
|
4536
4536
|
const localSlots = new Slots(localSlotCount, "local", debug);
|
|
4537
4537
|
const localSlotsMaxLoad = option("local-slots-max-load") || 0;
|
|
4538
|
+
console.log(`cpp slots: ${cppSlots.capacity}, compile slots: ${compileSlots.capacity}, local slots: ${localSlots.capacity}, local max load: ${localSlotsMaxLoad}`);
|
|
4538
4539
|
const compilerInfoCache = new CompilerInfoCache();
|
|
4539
4540
|
const slotSubscribers = [];
|
|
4540
4541
|
function slotsInfo() {
|
|
@@ -4659,9 +4660,7 @@ server.on("compile", (compile) => {
|
|
|
4659
4660
|
}
|
|
4660
4661
|
});
|
|
4661
4662
|
compile.on("acquireSlot", (msg) => {
|
|
4662
|
-
|
|
4663
|
-
console.log("acquireSlot", msg);
|
|
4664
|
-
}
|
|
4663
|
+
console.log("acquireSlot", msg);
|
|
4665
4664
|
const compilerPath = msg && typeof msg.compiler === "string" && msg.compiler.length > 0 ? msg.compiler : null;
|
|
4666
4665
|
const infoResult = compilerPath
|
|
4667
4666
|
? compilerInfoCache.get(compilerPath).then((info) => ({ info, error: null }), (err) => {
|
|
@@ -4688,7 +4687,7 @@ server.on("compile", (compile) => {
|
|
|
4688
4687
|
}
|
|
4689
4688
|
compile.send(response);
|
|
4690
4689
|
};
|
|
4691
|
-
if (canAcquireLocalSlot() && localSlots.tryAcquire(compile.id, { pid: compile.pid })) {
|
|
4690
|
+
if (!(msg === null || msg === void 0 ? void 0 : msg["no-local"]) && canAcquireLocalSlot() && localSlots.tryAcquire(compile.id, { pid: compile.pid })) {
|
|
4692
4691
|
if (debug) {
|
|
4693
4692
|
console.log("acquireSlot -> local slot granted");
|
|
4694
4693
|
}
|
package/package.json
CHANGED
|
@@ -56418,7 +56418,7 @@ class Server extends require$$0__default$2["default"] {
|
|
|
56418
56418
|
}
|
|
56419
56419
|
|
|
56420
56420
|
const Version = 5;
|
|
56421
|
-
const ObjectCacheFormatVersion =
|
|
56421
|
+
const ObjectCacheFormatVersion = 5;
|
|
56422
56422
|
function cacheDir(option) {
|
|
56423
56423
|
let dir = option("cache-dir");
|
|
56424
56424
|
if (!dir) {
|
|
@@ -57979,6 +57979,9 @@ Options:
|
|
|
57979
57979
|
--max-file-descriptors=N File descriptor limit
|
|
57980
57980
|
--ping-interval=MS WebSocket ping interval
|
|
57981
57981
|
--monitor-log=PATH Log file for monitor events
|
|
57982
|
+
--event-loop-lag-interval=MS Event loop lag sample interval (0 disables)
|
|
57983
|
+
--event-loop-lag-threshold=MS Log a stall at or above this lag
|
|
57984
|
+
--event-loop-lag-summary-interval=MS Lag summary interval (0 disables)
|
|
57982
57985
|
--env-dir=PATH Directory for compiler environments
|
|
57983
57986
|
--ui=PATH Path to scheduler UI static files
|
|
57984
57987
|
--cache-dir=PATH Cache directory (default: ~/.cache/fisk/scheduler)
|
|
@@ -57997,6 +58000,41 @@ let nextCommandId = 0;
|
|
|
57997
58000
|
const server = new Server(option, common.Version);
|
|
57998
58001
|
const clientMinimumVersion = "5.0.6";
|
|
57999
58002
|
const serverStartTime = Date.now();
|
|
58003
|
+
// A stalled event loop stops calling accept(), the listen backlog fills and the
|
|
58004
|
+
// kernel then silently drops SYNs, which clients see as a connect timeout rather
|
|
58005
|
+
// than a refusal. It leaves no trace by the time anyone runs ss(8), so record it
|
|
58006
|
+
// here with timestamps that can be correlated against client-side timeouts.
|
|
58007
|
+
const intervalMs = option.int("event-loop-lag-interval", 100);
|
|
58008
|
+
const thresholdMs = option.int("event-loop-lag-threshold", 250);
|
|
58009
|
+
const summaryIntervalMs = option.int("event-loop-lag-summary-interval", 60000);
|
|
58010
|
+
if (intervalMs > 0) {
|
|
58011
|
+
let maxLag = 0;
|
|
58012
|
+
let stalls = 0;
|
|
58013
|
+
let expected = Date.now() + intervalMs;
|
|
58014
|
+
const tick = () => {
|
|
58015
|
+
const now = Date.now();
|
|
58016
|
+
const lag = now - expected;
|
|
58017
|
+
if (lag > maxLag) {
|
|
58018
|
+
maxLag = lag;
|
|
58019
|
+
}
|
|
58020
|
+
if (lag >= thresholdMs) {
|
|
58021
|
+
++stalls;
|
|
58022
|
+
console.log(`event-loop-lag stall ${lag}ms at ${new Date(now).toISOString()}`);
|
|
58023
|
+
}
|
|
58024
|
+
// Schedule off "now" rather than accumulating on expected, otherwise a
|
|
58025
|
+
// single long stall reports as a stall on every subsequent tick.
|
|
58026
|
+
expected = now + intervalMs;
|
|
58027
|
+
setTimeout(tick, intervalMs).unref();
|
|
58028
|
+
};
|
|
58029
|
+
setTimeout(tick, intervalMs).unref();
|
|
58030
|
+
if (summaryIntervalMs > 0) {
|
|
58031
|
+
setInterval(() => {
|
|
58032
|
+
console.log(`event-loop-lag summary max=${maxLag}ms stalls=${stalls} over last ${summaryIntervalMs}ms (threshold ${thresholdMs}ms)`);
|
|
58033
|
+
maxLag = 0;
|
|
58034
|
+
stalls = 0;
|
|
58035
|
+
}, summaryIntervalMs).unref();
|
|
58036
|
+
}
|
|
58037
|
+
}
|
|
58000
58038
|
process.on("unhandledRejection", (reason, p) => {
|
|
58001
58039
|
console.error("Unhandled Rejection at: Promise", p, "reason:", reason === null || reason === void 0 ? void 0 : reason.stack);
|
|
58002
58040
|
addLogFile({ source: "no source file", ip: "self", contents: `reason: ${reason.stack} promise` });
|