gen_fixtures.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #!/usr/bin/env node
  2. "use strict";
  3. const assert = require("assert").strict;
  4. const fs = require("fs");
  5. const os = require("os");
  6. const path = require("path");
  7. const { spawn, spawnSync } = require("child_process");
  8. const DEBUG = process.env.DEBUG || false;
  9. // Maximum number of gdb processes to spawn in parallel
  10. const MAX_PARALLEL_PROCS = +process.env.MAX_PARALLEL_PROCS || 32;
  11. // Default to true for now. It's slower, but async execution occasionally gets stuck
  12. const SYNC_GDB_EXECUTION = process.env.SYNC_GDB_EXECUTION || true;
  13. // Usage: console.log(CYAN_FMT, "This shows up in cyan!")
  14. const CYAN_FMT = "\x1b[36m%s\x1b[0m";
  15. const YELLOW_FMT = "\x1b[33m%s\x1b[0m";
  16. const TEST_DIR = __dirname + "/";
  17. const BUILD_DIR = path.join(TEST_DIR, "build");
  18. const GDB_DEFAULT_ARGS = [
  19. "-batch",
  20. "--eval-command=set disable-randomization off", // allow execution on docker
  21. `--command=${TEST_DIR}gdb-extract-def`,
  22. // Set a breakpoint "in the future", which all the test binaries can then share
  23. "--eval-command=set breakpoint pending on",
  24. "--eval-command=break loop",
  25. "--eval-command=catch signal SIGFPE",
  26. "--eval-command=catch signal SIGILL",
  27. "--eval-command=catch signal SIGSEGV",
  28. "--eval-command=catch signal SIGBUS",
  29. ];
  30. /* Split up an array into semi-evenly sized chunks */
  31. function chunk(source, num_chunks)
  32. {
  33. const arr = source.slice();
  34. const ret = [];
  35. let rem_chunks = num_chunks;
  36. while(rem_chunks > 0)
  37. {
  38. // We guarantee that the entire array is processed because when rem_chunk=1 -> len/1 = len
  39. ret.push(arr.splice(0, Math.floor(arr.length / rem_chunks)));
  40. rem_chunks--;
  41. }
  42. return ret;
  43. }
  44. assert(
  45. JSON.stringify(chunk("0 0 1 1 2 2 2 3 3 3".split(" "), 4)) ===
  46. JSON.stringify([["0", "0"],
  47. ["1", "1"],
  48. ["2", "2", "2"],
  49. ["3", "3", "3"]]),
  50. "Chunk"
  51. );
  52. const dir_files = fs.readdirSync(BUILD_DIR);
  53. const test_files = dir_files.filter(name => {
  54. return name.endsWith(".bin");
  55. }).map(name => {
  56. return name.slice(0, -4);
  57. }).filter(name => {
  58. const bin_file = path.join(BUILD_DIR, `${name}.bin`);
  59. const fixture_file = path.join(BUILD_DIR, `${name}.fixture`);
  60. if(!fs.existsSync(fixture_file))
  61. {
  62. return true;
  63. }
  64. return fs.statSync(bin_file).mtime > fs.statSync(fixture_file).mtime;
  65. });
  66. const nr_of_cpus = Math.min(
  67. os.cpus().length || 1,
  68. test_files.length,
  69. MAX_PARALLEL_PROCS
  70. );
  71. if(SYNC_GDB_EXECUTION)
  72. {
  73. console.log("[+] Generating %d fixtures", test_files.length);
  74. }
  75. else
  76. {
  77. console.log("[+] Using %d cpus to generate %d fixtures", nr_of_cpus, test_files.length);
  78. }
  79. const workloads = chunk(test_files, nr_of_cpus);
  80. function test_arg_formatter(workload)
  81. {
  82. return workload.map(test => {
  83. const test_path = path.join(BUILD_DIR, test);
  84. return `--eval-command=extract-state ${test_path}.bin ${test_path}.fixture`;
  85. });
  86. }
  87. function set_proc_handlers(proc, n)
  88. {
  89. proc.on("close", (code) => on_proc_close(code, n));
  90. if(DEBUG)
  91. {
  92. proc.stdout.on("data", (data) => {
  93. console.log(CYAN_FMT, "stdout", `${n}: ${data}`);
  94. });
  95. proc.stderr.on("data", (data) => {
  96. console.log(YELLOW_FMT, "stderr", `${n}: ${data}`);
  97. });
  98. }
  99. }
  100. function on_proc_close(code, n)
  101. {
  102. console.log(`[+] child process ${n} exited with code ${code}`);
  103. if(code !== 0)
  104. {
  105. process.exit(code);
  106. }
  107. }
  108. for(let i = 0; i < nr_of_cpus; i++)
  109. {
  110. const gdb_args = GDB_DEFAULT_ARGS.concat(test_arg_formatter(workloads[i]));
  111. if(DEBUG)
  112. {
  113. console.log(CYAN_FMT, "[DEBUG]", "gdb", gdb_args.join(" "));
  114. }
  115. if(SYNC_GDB_EXECUTION || nr_of_cpus === 1)
  116. {
  117. const { status: code } = spawnSync("gdb", gdb_args);
  118. on_proc_close(code, i);
  119. }
  120. else
  121. {
  122. const gdb = spawn("gdb", gdb_args);
  123. set_proc_handlers(gdb, i);
  124. }
  125. }