145 lines
3.9 KiB
JavaScript
Executable file
145 lines
3.9 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
"use strict";
|
|
|
|
const assert = require("assert").strict;
|
|
const fs = require("fs");
|
|
const os = require("os");
|
|
const path = require("path");
|
|
const { spawn, spawnSync } = require("child_process");
|
|
|
|
const DEBUG = process.env.DEBUG || false;
|
|
// Maximum number of gdb processes to spawn in parallel
|
|
const MAX_PARALLEL_PROCS = +process.env.MAX_PARALLEL_PROCS || 32;
|
|
// Default to true for now. It's slower, but async execution occasionally gets stuck
|
|
const SYNC_GDB_EXECUTION = process.env.SYNC_GDB_EXECUTION || true;
|
|
|
|
// Usage: console.log(CYAN_FMT, "This shows up in cyan!")
|
|
const CYAN_FMT = "\x1b[36m%s\x1b[0m";
|
|
const YELLOW_FMT = "\x1b[33m%s\x1b[0m";
|
|
|
|
const TEST_DIR = __dirname + "/";
|
|
const BUILD_DIR = path.join(TEST_DIR, "build");
|
|
|
|
const GDB_DEFAULT_ARGS = [
|
|
"-batch",
|
|
"--eval-command=set disable-randomization off", // allow execution on docker
|
|
`--command=${TEST_DIR}gdb-extract-def`,
|
|
// Set a breakpoint "in the future", which all the test binaries can then share
|
|
"--eval-command=set breakpoint pending on",
|
|
"--eval-command=break loop",
|
|
"--eval-command=catch signal SIGFPE",
|
|
"--eval-command=catch signal SIGILL",
|
|
"--eval-command=catch signal SIGSEGV",
|
|
"--eval-command=catch signal SIGBUS",
|
|
];
|
|
|
|
/* Split up an array into semi-evenly sized chunks */
|
|
function chunk(source, num_chunks)
|
|
{
|
|
const arr = source.slice();
|
|
const ret = [];
|
|
|
|
let rem_chunks = num_chunks;
|
|
while(rem_chunks > 0)
|
|
{
|
|
// We guarantee that the entire array is processed because when rem_chunk=1 -> len/1 = len
|
|
ret.push(arr.splice(0, Math.floor(arr.length / rem_chunks)));
|
|
rem_chunks--;
|
|
}
|
|
return ret;
|
|
}
|
|
assert(
|
|
JSON.stringify(chunk("0 0 1 1 2 2 2 3 3 3".split(" "), 4)) ===
|
|
JSON.stringify([["0", "0"],
|
|
["1", "1"],
|
|
["2", "2", "2"],
|
|
["3", "3", "3"]]),
|
|
"Chunk"
|
|
);
|
|
|
|
const dir_files = fs.readdirSync(BUILD_DIR);
|
|
const test_files = dir_files.filter(name => {
|
|
return name.endsWith(".bin");
|
|
}).map(name => {
|
|
return name.slice(0, -4);
|
|
}).filter(name => {
|
|
const bin_file = path.join(BUILD_DIR, `${name}.bin`);
|
|
const fixture_file = path.join(BUILD_DIR, `${name}.fixture`);
|
|
if(!fs.existsSync(fixture_file))
|
|
{
|
|
return true;
|
|
}
|
|
return fs.statSync(bin_file).mtime > fs.statSync(fixture_file).mtime;
|
|
});
|
|
|
|
const nr_of_cpus = Math.min(
|
|
os.cpus().length || 1,
|
|
test_files.length,
|
|
MAX_PARALLEL_PROCS
|
|
);
|
|
|
|
if(SYNC_GDB_EXECUTION)
|
|
{
|
|
console.log("[+] Generating %d fixtures", test_files.length);
|
|
}
|
|
else
|
|
{
|
|
console.log("[+] Using %d cpus to generate %d fixtures", nr_of_cpus, test_files.length);
|
|
}
|
|
|
|
const workloads = chunk(test_files, nr_of_cpus);
|
|
|
|
function test_arg_formatter(workload)
|
|
{
|
|
return workload.map(test => {
|
|
const test_path = path.join(BUILD_DIR, test);
|
|
return `--eval-command=extract-state ${test_path}.bin ${test_path}.fixture`;
|
|
});
|
|
}
|
|
|
|
function set_proc_handlers(proc, n)
|
|
{
|
|
proc.on("close", (code) => on_proc_close(code, n));
|
|
|
|
if(DEBUG)
|
|
{
|
|
proc.stdout.on("data", (data) => {
|
|
console.log(CYAN_FMT, "stdout", `${n}: ${data}`);
|
|
});
|
|
|
|
proc.stderr.on("data", (data) => {
|
|
console.log(YELLOW_FMT, "stderr", `${n}: ${data}`);
|
|
});
|
|
}
|
|
}
|
|
|
|
function on_proc_close(code, n)
|
|
{
|
|
console.log(`[+] child process ${n} exited with code ${code}`);
|
|
if(code !== 0)
|
|
{
|
|
process.exit(code);
|
|
}
|
|
}
|
|
|
|
for(let i = 0; i < nr_of_cpus; i++)
|
|
{
|
|
const gdb_args = GDB_DEFAULT_ARGS.concat(test_arg_formatter(workloads[i]));
|
|
|
|
if(DEBUG)
|
|
{
|
|
console.log(CYAN_FMT, "[DEBUG]", "gdb", gdb_args.join(" "));
|
|
}
|
|
|
|
if(SYNC_GDB_EXECUTION || nr_of_cpus === 1)
|
|
{
|
|
const { status: code } = spawnSync("gdb", gdb_args);
|
|
on_proc_close(code, i);
|
|
}
|
|
else
|
|
{
|
|
const gdb = spawn("gdb", gdb_args);
|
|
set_proc_handlers(gdb, i);
|
|
}
|
|
}
|