@zigc/lib 0.16.0-test.1 → 0.17.0-dev.9

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.
Files changed (242) hide show
  1. package/LICENSE +19 -0
  2. package/c/math.zig +135 -35
  3. package/c/stropts.zig +17 -0
  4. package/c.zig +1 -0
  5. package/compiler/aro/aro/Attribute/names.zig +604 -589
  6. package/compiler/aro/aro/Attribute.zig +202 -116
  7. package/compiler/aro/aro/Builtins/common.zig +874 -863
  8. package/compiler/aro/aro/Builtins/eval.zig +15 -7
  9. package/compiler/aro/aro/Builtins.zig +0 -1
  10. package/compiler/aro/aro/CodeGen.zig +3 -1
  11. package/compiler/aro/aro/Compilation.zig +120 -97
  12. package/compiler/aro/aro/Diagnostics.zig +21 -17
  13. package/compiler/aro/aro/Driver/GCCDetector.zig +635 -0
  14. package/compiler/aro/aro/Driver.zig +124 -50
  15. package/compiler/aro/aro/LangOpts.zig +12 -2
  16. package/compiler/aro/aro/Parser/Diagnostic.zig +79 -19
  17. package/compiler/aro/aro/Parser.zig +336 -142
  18. package/compiler/aro/aro/Preprocessor/Diagnostic.zig +21 -0
  19. package/compiler/aro/aro/Preprocessor.zig +127 -56
  20. package/compiler/aro/aro/Target.zig +17 -12
  21. package/compiler/aro/aro/Tokenizer.zig +31 -14
  22. package/compiler/aro/aro/Toolchain.zig +4 -7
  23. package/compiler/aro/aro/Tree.zig +178 -148
  24. package/compiler/aro/aro/TypeStore.zig +82 -24
  25. package/compiler/aro/aro/Value.zig +13 -17
  26. package/compiler/aro/aro/features.zig +1 -0
  27. package/compiler/aro/aro/pragmas/once.zig +0 -1
  28. package/compiler/aro/aro/record_layout.zig +3 -3
  29. package/compiler/aro/assembly_backend/x86_64.zig +3 -4
  30. package/compiler/aro/backend/Assembly.zig +1 -2
  31. package/compiler/aro/backend/Interner.zig +2 -2
  32. package/compiler/aro/backend/Ir.zig +100 -92
  33. package/compiler/aro/include/ptrcheck.h +49 -0
  34. package/compiler/aro/main.zig +26 -10
  35. package/compiler/build_runner.zig +1 -0
  36. package/compiler/objdump.zig +93 -0
  37. package/compiler/reduce.zig +5 -1
  38. package/compiler/resinator/compile.zig +2 -2
  39. package/compiler/resinator/main.zig +7 -1
  40. package/compiler/resinator/preprocess.zig +1 -3
  41. package/compiler/std-docs.zig +8 -1
  42. package/compiler/test_runner.zig +193 -61
  43. package/compiler/translate-c/MacroTranslator.zig +80 -11
  44. package/compiler/translate-c/PatternList.zig +1 -9
  45. package/compiler/translate-c/Scope.zig +43 -6
  46. package/compiler/translate-c/Translator.zig +364 -126
  47. package/compiler/translate-c/ast.zig +19 -11
  48. package/compiler/translate-c/main.zig +75 -16
  49. package/compiler_rt/cos.zig +141 -52
  50. package/compiler_rt/limb64.zig +266 -0
  51. package/compiler_rt/long_double.zig +37 -0
  52. package/compiler_rt/mulo.zig +6 -1
  53. package/compiler_rt/rem_pio2l.zig +173 -0
  54. package/compiler_rt/sin.zig +140 -55
  55. package/compiler_rt/sincos.zig +279 -72
  56. package/compiler_rt/tan.zig +118 -47
  57. package/compiler_rt/trig.zig +256 -6
  58. package/compiler_rt.zig +2 -0
  59. package/fuzzer.zig +855 -307
  60. package/libc/musl/src/math/pow.c +343 -0
  61. package/package.json +1 -1
  62. package/std/Build/Fuzz.zig +6 -19
  63. package/std/Build/Module.zig +1 -1
  64. package/std/Build/Step/CheckObject.zig +3 -3
  65. package/std/Build/Step/Compile.zig +18 -0
  66. package/std/Build/Step/ConfigHeader.zig +49 -33
  67. package/std/Build/Step/InstallArtifact.zig +18 -0
  68. package/std/Build/Step/Run.zig +536 -87
  69. package/std/Build/Step/TranslateC.zig +0 -6
  70. package/std/Build/Step.zig +8 -15
  71. package/std/Build/WebServer.zig +29 -17
  72. package/std/Build/abi.zig +47 -11
  73. package/std/Build.zig +17 -14
  74. package/std/Io/Dispatch.zig +2 -0
  75. package/std/Io/File/Reader.zig +3 -1
  76. package/std/Io/File.zig +1 -0
  77. package/std/Io/Kqueue.zig +2 -2
  78. package/std/Io/Threaded.zig +181 -143
  79. package/std/Io/Uring.zig +2 -1
  80. package/std/Io.zig +970 -2
  81. package/std/Target.zig +3 -2
  82. package/std/Thread.zig +8 -3
  83. package/std/array_hash_map.zig +96 -555
  84. package/std/array_list.zig +22 -31
  85. package/std/bit_set.zig +22 -6
  86. package/std/builtin/assembly.zig +68 -0
  87. package/std/c.zig +17 -17
  88. package/std/compress/flate/Compress.zig +3 -3
  89. package/std/crypto/Certificate/Bundle.zig +15 -1
  90. package/std/crypto/codecs/asn1.zig +33 -18
  91. package/std/crypto/codecs/base64_hex_ct.zig +14 -4
  92. package/std/debug/Dwarf.zig +29 -9
  93. package/std/debug/Info.zig +4 -0
  94. package/std/debug/MachOFile.zig +46 -8
  95. package/std/debug/Pdb.zig +539 -36
  96. package/std/debug/SelfInfo/Elf.zig +19 -18
  97. package/std/debug/SelfInfo/MachO.zig +18 -7
  98. package/std/debug/SelfInfo/Windows.zig +138 -36
  99. package/std/debug.zig +179 -65
  100. package/std/enums.zig +25 -19
  101. package/std/heap/ArenaAllocator.zig +145 -154
  102. package/std/heap/debug_allocator.zig +7 -7
  103. package/std/http/Client.zig +10 -6
  104. package/std/http.zig +11 -9
  105. package/std/json/Stringify.zig +3 -3
  106. package/std/json/dynamic.zig +4 -4
  107. package/std/math/big/int.zig +16 -17
  108. package/std/mem/Allocator.zig +4 -5
  109. package/std/mem.zig +48 -0
  110. package/std/os/emscripten.zig +2 -18
  111. package/std/os/linux/arc.zig +144 -0
  112. package/std/os/linux.zig +21 -4
  113. package/std/os/windows.zig +2 -2
  114. package/std/pdb.zig +143 -4
  115. package/std/posix.zig +6 -12
  116. package/std/priority_dequeue.zig +13 -12
  117. package/std/priority_queue.zig +5 -4
  118. package/std/process/Child.zig +1 -1
  119. package/std/process/Environ.zig +1 -1
  120. package/std/start.zig +17 -4
  121. package/std/std.zig +19 -6
  122. package/std/testing/FailingAllocator.zig +4 -4
  123. package/std/testing/Smith.zig +37 -2
  124. package/std/zig/Ast/Render.zig +186 -458
  125. package/std/zig/Ast.zig +0 -4
  126. package/std/zig/AstGen.zig +44 -7
  127. package/std/zig/AstSmith.zig +2602 -0
  128. package/std/zig/Client.zig +8 -3
  129. package/std/zig/Parse.zig +83 -74
  130. package/std/zig/Server.zig +26 -0
  131. package/std/zig/Zir.zig +17 -0
  132. package/std/zig/c_translation/helpers.zig +14 -9
  133. package/std/zig/llvm/Builder.zig +107 -48
  134. package/std/zig/system.zig +20 -4
  135. package/std/zig/tokenizer.zig +2 -1
  136. package/std/zig.zig +6 -0
  137. package/compiler/aro/aro/Driver/Filesystem.zig +0 -241
  138. package/libc/mingw/complex/cabs.c +0 -48
  139. package/libc/mingw/complex/cabsf.c +0 -48
  140. package/libc/mingw/complex/cacos.c +0 -50
  141. package/libc/mingw/complex/cacosf.c +0 -50
  142. package/libc/mingw/complex/carg.c +0 -48
  143. package/libc/mingw/complex/cargf.c +0 -48
  144. package/libc/mingw/complex/casin.c +0 -50
  145. package/libc/mingw/complex/casinf.c +0 -50
  146. package/libc/mingw/complex/catan.c +0 -50
  147. package/libc/mingw/complex/catanf.c +0 -50
  148. package/libc/mingw/complex/ccos.c +0 -50
  149. package/libc/mingw/complex/ccosf.c +0 -50
  150. package/libc/mingw/complex/cexp.c +0 -48
  151. package/libc/mingw/complex/cexpf.c +0 -48
  152. package/libc/mingw/complex/cimag.c +0 -48
  153. package/libc/mingw/complex/cimagf.c +0 -48
  154. package/libc/mingw/complex/clog.c +0 -48
  155. package/libc/mingw/complex/clog10.c +0 -49
  156. package/libc/mingw/complex/clog10f.c +0 -49
  157. package/libc/mingw/complex/clogf.c +0 -48
  158. package/libc/mingw/complex/conj.c +0 -48
  159. package/libc/mingw/complex/conjf.c +0 -48
  160. package/libc/mingw/complex/cpow.c +0 -48
  161. package/libc/mingw/complex/cpowf.c +0 -48
  162. package/libc/mingw/complex/cproj.c +0 -48
  163. package/libc/mingw/complex/cprojf.c +0 -48
  164. package/libc/mingw/complex/creal.c +0 -48
  165. package/libc/mingw/complex/crealf.c +0 -48
  166. package/libc/mingw/complex/csin.c +0 -50
  167. package/libc/mingw/complex/csinf.c +0 -50
  168. package/libc/mingw/complex/csqrt.c +0 -48
  169. package/libc/mingw/complex/csqrtf.c +0 -48
  170. package/libc/mingw/complex/ctan.c +0 -50
  171. package/libc/mingw/complex/ctanf.c +0 -50
  172. package/libc/mingw/math/arm/s_rint.c +0 -86
  173. package/libc/mingw/math/arm/s_rintf.c +0 -51
  174. package/libc/mingw/math/arm/sincos.S +0 -30
  175. package/libc/mingw/math/arm-common/sincosl.c +0 -13
  176. package/libc/mingw/math/arm64/rint.c +0 -12
  177. package/libc/mingw/math/arm64/rintf.c +0 -12
  178. package/libc/mingw/math/arm64/sincos.S +0 -32
  179. package/libc/mingw/math/bsd_private_base.h +0 -148
  180. package/libc/mingw/math/frexpf.c +0 -13
  181. package/libc/mingw/math/frexpl.c +0 -71
  182. package/libc/mingw/math/x86/acosf.c +0 -29
  183. package/libc/mingw/math/x86/atanf.c +0 -23
  184. package/libc/mingw/math/x86/atanl.c +0 -18
  185. package/libc/mingw/math/x86/cos.def.h +0 -65
  186. package/libc/mingw/math/x86/cosl.c +0 -46
  187. package/libc/mingw/math/x86/cosl_internal.S +0 -55
  188. package/libc/mingw/math/x86/ldexp.c +0 -23
  189. package/libc/mingw/math/x86/scalbn.S +0 -41
  190. package/libc/mingw/math/x86/scalbnf.S +0 -40
  191. package/libc/mingw/math/x86/sin.def.h +0 -65
  192. package/libc/mingw/math/x86/sinl.c +0 -46
  193. package/libc/mingw/math/x86/sinl_internal.S +0 -58
  194. package/libc/mingw/math/x86/tanl.S +0 -62
  195. package/libc/mingw/misc/btowc.c +0 -28
  196. package/libc/mingw/misc/wcstof.c +0 -66
  197. package/libc/mingw/misc/wcstoimax.c +0 -132
  198. package/libc/mingw/misc/wcstoumax.c +0 -126
  199. package/libc/mingw/misc/wctob.c +0 -29
  200. package/libc/mingw/misc/winbs_uint64.c +0 -6
  201. package/libc/mingw/misc/winbs_ulong.c +0 -6
  202. package/libc/mingw/misc/winbs_ushort.c +0 -6
  203. package/libc/mingw/stdio/_Exit.c +0 -10
  204. package/libc/mingw/stdio/_findfirst64i32.c +0 -21
  205. package/libc/mingw/stdio/_findnext64i32.c +0 -21
  206. package/libc/mingw/stdio/_fstat64i32.c +0 -37
  207. package/libc/mingw/stdio/_stat64i32.c +0 -37
  208. package/libc/mingw/stdio/_wfindfirst64i32.c +0 -21
  209. package/libc/mingw/stdio/_wfindnext64i32.c +0 -21
  210. package/libc/mingw/stdio/_wstat64i32.c +0 -37
  211. package/libc/musl/src/legacy/isastream.c +0 -7
  212. package/libc/musl/src/legacy/valloc.c +0 -8
  213. package/libc/musl/src/math/__cosl.c +0 -96
  214. package/libc/musl/src/math/__sinl.c +0 -78
  215. package/libc/musl/src/math/__tanl.c +0 -143
  216. package/libc/musl/src/math/aarch64/lrint.c +0 -10
  217. package/libc/musl/src/math/aarch64/lrintf.c +0 -10
  218. package/libc/musl/src/math/aarch64/rintf.c +0 -7
  219. package/libc/musl/src/math/cosl.c +0 -39
  220. package/libc/musl/src/math/fdim.c +0 -10
  221. package/libc/musl/src/math/finite.c +0 -7
  222. package/libc/musl/src/math/finitef.c +0 -7
  223. package/libc/musl/src/math/frexp.c +0 -23
  224. package/libc/musl/src/math/frexpf.c +0 -23
  225. package/libc/musl/src/math/frexpl.c +0 -29
  226. package/libc/musl/src/math/i386/lrint.c +0 -8
  227. package/libc/musl/src/math/i386/lrintf.c +0 -8
  228. package/libc/musl/src/math/i386/rintf.c +0 -7
  229. package/libc/musl/src/math/lrint.c +0 -72
  230. package/libc/musl/src/math/lrintf.c +0 -8
  231. package/libc/musl/src/math/powerpc64/lrint.c +0 -16
  232. package/libc/musl/src/math/powerpc64/lrintf.c +0 -16
  233. package/libc/musl/src/math/rintf.c +0 -30
  234. package/libc/musl/src/math/s390x/rintf.c +0 -15
  235. package/libc/musl/src/math/sincosl.c +0 -60
  236. package/libc/musl/src/math/sinl.c +0 -41
  237. package/libc/musl/src/math/tanl.c +0 -29
  238. package/libc/musl/src/math/x32/lrint.s +0 -5
  239. package/libc/musl/src/math/x32/lrintf.s +0 -5
  240. package/libc/musl/src/math/x86_64/lrint.c +0 -8
  241. package/libc/musl/src/math/x86_64/lrintf.c +0 -8
  242. package/libc/wasi/libc-bottom-half/sources/reallocarray.c +0 -14
@@ -1068,7 +1068,6 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
1068
1068
  pub fn rerunInFuzzMode(
1069
1069
  run: *Run,
1070
1070
  fuzz: *std.Build.Fuzz,
1071
- unit_test_name: []const u8,
1072
1071
  prog_node: std.Progress.Node,
1073
1072
  ) !void {
1074
1073
  const step = &run.step;
@@ -1139,7 +1138,6 @@ pub fn rerunInFuzzMode(
1139
1138
  .unit_test_timeout_ns = null, // don't time out fuzz tests for now
1140
1139
  .gpa = fuzz.gpa,
1141
1140
  }, .{
1142
- .unit_test_name = unit_test_name,
1143
1141
  .fuzz = fuzz,
1144
1142
  });
1145
1143
  }
@@ -1175,7 +1173,7 @@ fn formatTerm(term: ?process.Child.Term, w: *std.Io.Writer) std.Io.Writer.Error!
1175
1173
  if (term) |t| switch (t) {
1176
1174
  .exited => |code| try w.print("exited with code {d}", .{code}),
1177
1175
  .signal => |sig| try w.print("terminated with signal {t}", .{sig}),
1178
- .stopped => |sig| try w.print("stopped with signal {d}", .{sig}),
1176
+ .stopped => |sig| try w.print("stopped with signal {t}", .{sig}),
1179
1177
  .unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}),
1180
1178
  } else {
1181
1179
  try w.writeAll("exited with any code");
@@ -1211,7 +1209,6 @@ fn termMatches(expected: ?process.Child.Term, actual: process.Child.Term) bool {
1211
1209
 
1212
1210
  const FuzzContext = struct {
1213
1211
  fuzz: *std.Build.Fuzz,
1214
- unit_test_name: []const u8,
1215
1212
  };
1216
1213
 
1217
1214
  fn runCommand(
@@ -1326,24 +1323,11 @@ fn runCommand(
1326
1323
  },
1327
1324
  .wasmtime => |bin_name| {
1328
1325
  if (b.enable_wasmtime) {
1329
- // https://github.com/bytecodealliance/wasmtime/issues/7384
1330
- //
1331
- // In Wasmtime versions prior to 14, options passed after the module name
1332
- // could be interpreted by Wasmtime if it recognized them. As with many CLI
1333
- // tools, the `--` token is used to stop that behavior and indicate that the
1334
- // remaining arguments are for the WASM program being executed. Historically,
1335
- // we passed `--` after the module name here.
1336
- //
1337
- // After version 14, the `--` can no longer be passed after the module name,
1338
- // but is also not necessary as Wasmtime will no longer try to interpret
1339
- // options after the module name. So, we could just simply omit `--` for
1340
- // newer Wasmtime versions. But to maintain compatibility for older versions
1341
- // that still try to interpret options after the module name, we have moved
1342
- // the `--` before the module name. This appears to work for both old and
1343
- // new Wasmtime versions.
1344
1326
  try interp_argv.append(bin_name);
1345
1327
  try interp_argv.append("--dir=.");
1346
- try interp_argv.append("--");
1328
+ // Wasmtime doeesn't inherit environment variables from the parent process
1329
+ // by default. '-S inherit-env' was added in Wasmtime version 20.
1330
+ try interp_argv.append("-Sinherit-env");
1347
1331
  try interp_argv.append(argv[0]);
1348
1332
  try interp_argv.appendSlice(argv[1..]);
1349
1333
  } else {
@@ -1655,6 +1639,11 @@ fn evalZigTest(
1655
1639
  options: Step.MakeOptions,
1656
1640
  fuzz_context: ?FuzzContext,
1657
1641
  ) !void {
1642
+ if (fuzz_context != null) {
1643
+ try evalFuzzTest(run, spawn_options, options, fuzz_context.?);
1644
+ return;
1645
+ }
1646
+
1658
1647
  const step_owner = run.step.owner;
1659
1648
  const gpa = step_owner.allocator;
1660
1649
  const arena = step_owner.allocator;
@@ -1693,7 +1682,6 @@ fn evalZigTest(
1693
1682
  run,
1694
1683
  &child,
1695
1684
  options,
1696
- fuzz_context,
1697
1685
  &multi_reader,
1698
1686
  &test_metadata,
1699
1687
  &test_results,
@@ -1815,7 +1803,6 @@ fn waitZigTest(
1815
1803
  run: *Run,
1816
1804
  child: *process.Child,
1817
1805
  options: Step.MakeOptions,
1818
- fuzz_context: ?FuzzContext,
1819
1806
  multi_reader: *Io.File.MultiReader,
1820
1807
  opt_metadata: *?TestMetadata,
1821
1808
  results: *Step.TestResults,
@@ -1837,29 +1824,7 @@ fn waitZigTest(
1837
1824
  var sub_prog_node: ?std.Progress.Node = null;
1838
1825
  defer if (sub_prog_node) |n| n.end();
1839
1826
 
1840
- if (fuzz_context) |ctx| {
1841
- assert(opt_metadata.* == null); // fuzz processes are never restarted
1842
- switch (ctx.fuzz.mode) {
1843
- .forever => {
1844
- sendRunFuzzTestMessage(
1845
- io,
1846
- child.stdin.?,
1847
- ctx.unit_test_name,
1848
- .forever,
1849
- 0, // instance ID; will be used by multiprocess forever fuzzing in the future
1850
- ) catch |err| return .{ .write_failed = err };
1851
- },
1852
- .limit => |limit| {
1853
- sendRunFuzzTestMessage(
1854
- io,
1855
- child.stdin.?,
1856
- ctx.unit_test_name,
1857
- .iterations,
1858
- limit.amount,
1859
- ) catch |err| return .{ .write_failed = err };
1860
- },
1861
- }
1862
- } else if (opt_metadata.*) |*md| {
1827
+ if (opt_metadata.*) |*md| {
1863
1828
  // Previous unit test process died or was killed; we're continuing where it left off
1864
1829
  requestNextTest(io, child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err };
1865
1830
  } else {
@@ -1872,14 +1837,11 @@ fn waitZigTest(
1872
1837
 
1873
1838
  var last_update: Io.Clock.Timestamp = .now(io, .awake);
1874
1839
 
1875
- var coverage_id: ?u64 = null;
1876
-
1877
1840
  // This timeout is used when we're waiting on the test runner itself rather than a user-specified
1878
1841
  // test. For instance, if the test runner leaves this much time between us requesting a test to
1879
1842
  // start and it acknowledging the test starting, we terminate the child and raise an error. This
1880
1843
  // *should* never happen, but could in theory be caused by some very unlucky IB in a test.
1881
- const response_timeout: ?Io.Clock.Duration = t: {
1882
- if (fuzz_context != null) break :t null; // don't timeout fuzz tests
1844
+ const response_timeout: Io.Clock.Duration = t: {
1883
1845
  const ns = @max(options.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s);
1884
1846
  break :t .{ .clock = .awake, .raw = .fromNanoseconds(ns) };
1885
1847
  };
@@ -1947,8 +1909,6 @@ fn waitZigTest(
1947
1909
  );
1948
1910
  },
1949
1911
  .test_metadata => {
1950
- assert(fuzz_context == null);
1951
-
1952
1912
  // `metadata` would only be populated if we'd already seen a `test_metadata`, but we
1953
1913
  // only request it once (and importantly, we don't re-request it if we kill and
1954
1914
  // restart the test runner).
@@ -1986,7 +1946,6 @@ fn waitZigTest(
1986
1946
  last_update = .now(io, .awake);
1987
1947
  },
1988
1948
  .test_results => {
1989
- assert(fuzz_context == null);
1990
1949
  const md = &opt_metadata.*.?;
1991
1950
 
1992
1951
  const tr_hdr = body_r.takeStruct(std.zig.Server.Message.TestResults, .little) catch unreachable;
@@ -2033,44 +1992,523 @@ fn waitZigTest(
2033
1992
 
2034
1993
  requestNextTest(io, child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err };
2035
1994
  },
1995
+ else => {}, // ignore other messages
1996
+ }
1997
+ }
1998
+ }
1999
+
2000
+ const FuzzTestRunner = struct {
2001
+ run: *Run,
2002
+ ctx: FuzzContext,
2003
+ coverage_id: ?u64,
2004
+
2005
+ instances: []Instance,
2006
+ /// The indexes of this are layed out such that it is effectively an array
2007
+ /// of `[instances.len][3]Io.Operation.Storage` of stdin, stdout, stderr.
2008
+ batch: Io.Batch,
2009
+ /// LIFO. Stream of message bodies trailed by PendingBroadcastFooter.
2010
+ pending_broadcasts: std.ArrayList(u8),
2011
+ broadcast: std.ArrayList(u8),
2012
+ broadcast_undelivered: u32,
2013
+
2014
+ const Instance = struct {
2015
+ child: process.Child,
2016
+ message: std.ArrayListAligned(u8, .@"4"),
2017
+ broadcast_written: usize,
2018
+ stderr: std.ArrayList(u8),
2019
+ stdin_vec: [1][]u8,
2020
+ stdout_vec: [1][]u8,
2021
+ stderr_vec: [1][]u8,
2022
+ progress_node: std.Progress.Node,
2023
+
2024
+ fn messageHeader(instance: *Instance) InHeader {
2025
+ assert(instance.message.items.len >= @sizeOf(InHeader));
2026
+ const header_ptr: *InHeader = @ptrCast(instance.message.items);
2027
+ var header = header_ptr.*;
2028
+ if (std.builtin.Endian.native != .little) {
2029
+ std.mem.byteSwapAllFields(InHeader, &header);
2030
+ }
2031
+ return header;
2032
+ }
2033
+ };
2034
+
2035
+ const PendingBroadcastFooter = struct {
2036
+ from_id: u32,
2037
+ body_len: u32,
2038
+ };
2039
+
2040
+ const InHeader = std.zig.Server.Message.Header;
2041
+ const OutHeader = std.zig.Client.Message.Header;
2042
+
2043
+ const stdin_i = 0;
2044
+ const stdout_i = 1;
2045
+ const stderr_i = 2;
2046
+
2047
+ fn init(
2048
+ run: *Run,
2049
+ ctx: FuzzContext,
2050
+ progress_node: std.Progress.Node,
2051
+ spawn_options: process.SpawnOptions,
2052
+ ) !FuzzTestRunner {
2053
+ const step_owner = run.step.owner;
2054
+ const gpa = step_owner.allocator;
2055
+ const io = step_owner.graph.io;
2056
+
2057
+ const n_instances = switch (ctx.fuzz.mode) {
2058
+ .forever => step_owner.graph.max_jobs orelse @min(
2059
+ std.Thread.getCpuCount() catch 1,
2060
+ (std.math.maxInt(u32) - 2) / 3,
2061
+ ),
2062
+ .limit => 1,
2063
+ };
2064
+ const instances = try gpa.alloc(Instance, n_instances);
2065
+ errdefer gpa.free(instances);
2066
+ const batch_storage = try gpa.alloc(Io.Operation.Storage, instances.len * 3);
2067
+ errdefer gpa.free(batch_storage);
2068
+
2069
+ @memset(instances, .{
2070
+ .child = undefined,
2071
+ .message = .empty,
2072
+ .broadcast_written = undefined,
2073
+ .stderr = .empty,
2074
+ .stdin_vec = undefined,
2075
+ .stdout_vec = undefined,
2076
+ .stderr_vec = undefined,
2077
+ .progress_node = undefined,
2078
+ });
2079
+ for (0.., instances) |id, *instance| {
2080
+ errdefer for (instances[0..id]) |*spawned| {
2081
+ spawned.child.kill(io);
2082
+ spawned.progress_node.end();
2083
+ };
2084
+ instance.child = try process.spawn(io, spawn_options);
2085
+ instance.progress_node = progress_node.start("starting fuzzer", 0);
2086
+ }
2087
+
2088
+ return .{
2089
+ .run = run,
2090
+ .ctx = ctx,
2091
+ .coverage_id = null,
2092
+
2093
+ .instances = instances,
2094
+ .batch = .init(batch_storage),
2095
+ .pending_broadcasts = .empty,
2096
+ .broadcast = .empty,
2097
+ .broadcast_undelivered = 0,
2098
+ };
2099
+ }
2100
+
2101
+ fn deinit(f: *FuzzTestRunner) void {
2102
+ const step_owner = f.run.step.owner;
2103
+ const gpa = step_owner.allocator;
2104
+ const io = step_owner.graph.io;
2105
+
2106
+ f.batch.cancel(io);
2107
+ gpa.free(f.batch.storage);
2108
+ var total_rss: usize = 0;
2109
+ for (f.instances) |*instance| {
2110
+ instance.child.kill(io);
2111
+ instance.message.deinit(gpa);
2112
+ instance.stderr.deinit(gpa);
2113
+ instance.progress_node.end();
2114
+ total_rss += instance.child.resource_usage_statistics.getMaxRss() orelse 0;
2115
+ }
2116
+ f.run.step.result_peak_rss = @max(f.run.step.result_peak_rss, total_rss);
2117
+ gpa.free(f.instances);
2118
+ }
2119
+
2120
+ fn startInstances(f: *FuzzTestRunner) !void {
2121
+ const step_owner = f.run.step.owner;
2122
+ const io = step_owner.graph.io;
2123
+
2124
+ for (0.., f.instances) |id, *instance| {
2125
+ const id32: u32 = @intCast(id);
2126
+ (switch (f.ctx.fuzz.mode) {
2127
+ .forever => sendRunFuzzTestMessage(
2128
+ io,
2129
+ instance.child.stdin.?,
2130
+ f.run.fuzz_tests.items,
2131
+ .forever,
2132
+ id32,
2133
+ ),
2134
+ .limit => |limit| sendRunFuzzTestMessage(
2135
+ io,
2136
+ instance.child.stdin.?,
2137
+ f.run.fuzz_tests.items,
2138
+ .iterations,
2139
+ limit.amount,
2140
+ ),
2141
+ }) catch |write_err| {
2142
+ // The runner unexpectedly closed stdin, which means it crashed during initialization.
2143
+ // Clean up everything and wait for the child to exit.
2144
+ instance.child.stdin.?.close(io);
2145
+ instance.child.stdin = null;
2146
+ const term = try instance.child.wait(io);
2147
+ return f.run.step.fail(
2148
+ "unable to write stdin ({t}); test process unexpectedly {f}",
2149
+ .{ write_err, fmtTerm(term) },
2150
+ );
2151
+ };
2152
+
2153
+ try f.addStdoutRead(id32, @sizeOf(InHeader));
2154
+ try f.addStderrRead(id32);
2155
+ }
2156
+ }
2157
+
2158
+ fn listen(f: *FuzzTestRunner) !void {
2159
+ const step_owner = f.run.step.owner;
2160
+ const io = step_owner.graph.io;
2161
+
2162
+ while (true) {
2163
+ try f.batch.awaitConcurrent(io, .none);
2164
+ while (f.batch.next()) |completion| {
2165
+ const id = completion.index / 3;
2166
+ const result = completion.result;
2167
+ switch (completion.index % 3) {
2168
+ 0 => try f.completeStdinWrite(id, result.file_write_streaming catch |e| switch (e) {
2169
+ error.BrokenPipe => return f.instanceEos(id),
2170
+ else => |write_e| return write_e,
2171
+ }),
2172
+ 1 => try f.completeStdoutRead(id, result.file_read_streaming catch |e| switch (e) {
2173
+ error.EndOfStream => return f.instanceEos(id),
2174
+ else => |read_e| return read_e,
2175
+ }),
2176
+ 2 => try f.completeStderrRead(id, result.file_read_streaming catch |e| switch (e) {
2177
+ error.EndOfStream => return f.instanceEos(id),
2178
+ else => |read_e| return read_e,
2179
+ }),
2180
+ else => unreachable,
2181
+ }
2182
+ }
2183
+ }
2184
+ }
2185
+
2186
+ fn completeStdoutRead(f: *FuzzTestRunner, id: u32, n: usize) !void {
2187
+ const step_owner = f.run.step.owner;
2188
+ const gpa = step_owner.allocator;
2189
+ const io = step_owner.graph.io;
2190
+ const instance = &f.instances[id];
2191
+
2192
+ instance.message.items.len += n;
2193
+ const total_read = instance.message.items.len;
2194
+ if (total_read < @sizeOf(InHeader)) {
2195
+ try f.addStdoutRead(id, @sizeOf(InHeader));
2196
+ return;
2197
+ }
2198
+
2199
+ const header = instance.messageHeader();
2200
+ const body = instance.message.items[@sizeOf(InHeader)..];
2201
+ if (body.len != header.bytes_len) {
2202
+ try f.addStdoutRead(id, @sizeOf(InHeader) + header.bytes_len);
2203
+ return;
2204
+ }
2205
+
2206
+ switch (header.tag) {
2207
+ .zig_version => {
2208
+ if (!std.mem.eql(u8, builtin.zig_version_string, body)) return f.run.step.fail(
2209
+ "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
2210
+ .{ builtin.zig_version_string, body },
2211
+ );
2212
+ },
2036
2213
  .coverage_id => {
2037
- coverage_id = body_r.takeInt(u64, .little) catch unreachable;
2214
+ var body_r: Io.Reader = .fixed(body);
2215
+ f.coverage_id = body_r.takeInt(u64, .little) catch unreachable;
2038
2216
  const cumulative_runs = body_r.takeInt(u64, .little) catch unreachable;
2039
2217
  const cumulative_unique = body_r.takeInt(u64, .little) catch unreachable;
2040
2218
  const cumulative_coverage = body_r.takeInt(u64, .little) catch unreachable;
2041
2219
 
2042
- {
2043
- const fuzz = fuzz_context.?.fuzz;
2044
- fuzz.queue_mutex.lockUncancelable(io);
2045
- defer fuzz.queue_mutex.unlock(io);
2046
- try fuzz.msg_queue.append(fuzz.gpa, .{ .coverage = .{
2047
- .id = coverage_id.?,
2048
- .cumulative = .{
2049
- .runs = cumulative_runs,
2050
- .unique = cumulative_unique,
2051
- .coverage = cumulative_coverage,
2052
- },
2053
- .run = run,
2054
- } });
2055
- fuzz.queue_cond.signal(io);
2056
- }
2220
+ const fuzz = f.ctx.fuzz;
2221
+ fuzz.queue_mutex.lockUncancelable(io);
2222
+ defer fuzz.queue_mutex.unlock(io);
2223
+ try fuzz.msg_queue.append(fuzz.gpa, .{ .coverage = .{
2224
+ .id = f.coverage_id.?,
2225
+ .cumulative = .{
2226
+ .runs = cumulative_runs,
2227
+ .unique = cumulative_unique,
2228
+ .coverage = cumulative_coverage,
2229
+ },
2230
+ .run = f.run,
2231
+ } });
2232
+ fuzz.queue_cond.signal(io);
2057
2233
  },
2058
2234
  .fuzz_start_addr => {
2059
- const fuzz = fuzz_context.?.fuzz;
2235
+ var body_r: Io.Reader = .fixed(body);
2236
+ const fuzz = f.ctx.fuzz;
2060
2237
  const addr = body_r.takeInt(u64, .little) catch unreachable;
2061
- {
2062
- fuzz.queue_mutex.lockUncancelable(io);
2063
- defer fuzz.queue_mutex.unlock(io);
2064
- try fuzz.msg_queue.append(fuzz.gpa, .{ .entry_point = .{
2065
- .addr = addr,
2066
- .coverage_id = coverage_id.?,
2067
- } });
2068
- fuzz.queue_cond.signal(io);
2238
+
2239
+ fuzz.queue_mutex.lockUncancelable(io);
2240
+ defer fuzz.queue_mutex.unlock(io);
2241
+ try fuzz.msg_queue.append(fuzz.gpa, .{ .entry_point = .{
2242
+ .addr = addr,
2243
+ .coverage_id = f.coverage_id.?,
2244
+ } });
2245
+ fuzz.queue_cond.signal(io);
2246
+ },
2247
+ .fuzz_test_change => {
2248
+ const test_i = std.mem.readInt(u32, body[0..4], .little);
2249
+ instance.progress_node.setName(f.run.fuzz_tests.items[test_i]);
2250
+ },
2251
+ .broadcast_fuzz_input => {
2252
+ if (f.instances.len == 1) {
2253
+ // No other processes to broadcast to.
2254
+ } else if (f.broadcast_undelivered == 0) {
2255
+ try f.instanceBroadcast(id, body);
2256
+ } else {
2257
+ const footer: PendingBroadcastFooter = .{
2258
+ .from_id = id,
2259
+ .body_len = @intCast(body.len),
2260
+ };
2261
+ // There is another broadcast in progress so add this one to the queue.
2262
+ const size = @sizeOf(PendingBroadcastFooter) + body.len;
2263
+ try f.pending_broadcasts.ensureUnusedCapacity(gpa, size);
2264
+ f.pending_broadcasts.appendSliceAssumeCapacity(body);
2265
+ f.pending_broadcasts.appendSliceAssumeCapacity(@ptrCast(&footer));
2069
2266
  }
2070
2267
  },
2071
2268
  else => {}, // ignore other messages
2072
2269
  }
2270
+
2271
+ instance.message.clearRetainingCapacity();
2272
+ try f.addStdoutRead(id, @sizeOf(InHeader));
2273
+ }
2274
+
2275
+ fn completeStderrRead(f: *FuzzTestRunner, id: u32, n: usize) !void {
2276
+ const instance = &f.instances[id];
2277
+ instance.stderr.items.len += n;
2278
+ try f.addStderrRead(id);
2279
+ }
2280
+
2281
+ fn completeStdinWrite(f: *FuzzTestRunner, id: u32, n: usize) !void {
2282
+ const instance = &f.instances[id];
2283
+
2284
+ instance.broadcast_written += n;
2285
+ if (instance.broadcast_written == f.broadcast.items.len) {
2286
+ f.broadcast_undelivered -= 1;
2287
+ if (f.broadcast_undelivered == 0) {
2288
+ try f.broadcastComplete();
2289
+ }
2290
+ } else {
2291
+ f.addStdinWrite(id);
2292
+ }
2073
2293
  }
2294
+
2295
+ fn addStdoutRead(f: *FuzzTestRunner, id: u32, end: usize) !void {
2296
+ const step_owner = f.run.step.owner;
2297
+ const gpa = step_owner.allocator;
2298
+ const instance = &f.instances[id];
2299
+
2300
+ try instance.message.ensureTotalCapacity(gpa, end);
2301
+ const start = instance.message.items.len;
2302
+ instance.stdout_vec = .{instance.message.allocatedSlice()[start..end]};
2303
+ f.batch.addAt(id * 3 + stdout_i, .{ .file_read_streaming = .{
2304
+ .file = instance.child.stdout.?,
2305
+ .data = &instance.stdout_vec,
2306
+ } });
2307
+ }
2308
+
2309
+ fn addStderrRead(f: *FuzzTestRunner, id: u32) !void {
2310
+ const step_owner = f.run.step.owner;
2311
+ const gpa = step_owner.allocator;
2312
+ const instance = &f.instances[id];
2313
+
2314
+ try instance.stderr.ensureUnusedCapacity(gpa, 1);
2315
+ instance.stderr_vec = .{instance.stderr.unusedCapacitySlice()};
2316
+ f.batch.addAt(id * 3 + stderr_i, .{ .file_read_streaming = .{
2317
+ .file = instance.child.stderr.?,
2318
+ .data = &instance.stderr_vec,
2319
+ } });
2320
+ }
2321
+
2322
+ fn addStdinWrite(f: *FuzzTestRunner, id: u32) void {
2323
+ const instance = &f.instances[id];
2324
+
2325
+ assert(f.broadcast.items.len != instance.broadcast_written);
2326
+ instance.stdin_vec = .{f.broadcast.items[instance.broadcast_written..]};
2327
+ f.batch.addAt(id * 3 + stdin_i, .{ .file_write_streaming = .{
2328
+ .file = instance.child.stdin.?,
2329
+ .data = &instance.stdin_vec,
2330
+ } });
2331
+ }
2332
+
2333
+ fn instanceEos(f: *FuzzTestRunner, id: u32) !void {
2334
+ const step_owner = f.run.step.owner;
2335
+ const io = step_owner.graph.io;
2336
+ const instance = &f.instances[id];
2337
+
2338
+ instance.child.stdin.?.close(io);
2339
+ instance.child.stdin = null;
2340
+ const term = try instance.child.wait(io);
2341
+ if (!termMatches(.{ .exited = 0 }, term)) {
2342
+ f.run.step.result_stderr = try f.mergedStderr();
2343
+ try f.saveCrash(id, term);
2344
+ return f.run.step.fail("test process unexpectedly {f}", .{fmtTerm(term)});
2345
+ }
2346
+ }
2347
+
2348
+ fn saveCrash(f: *FuzzTestRunner, id: u32, term: process.Child.Term) !void {
2349
+ const step = &f.run.step;
2350
+ const b = step.owner;
2351
+ const io = b.graph.io;
2352
+
2353
+ if (f.coverage_id == null) return;
2354
+
2355
+ // Search for the input file corresponding to the instance
2356
+ const InputHeader = Build.abi.fuzz.MmapInputHeader;
2357
+ var in_r_buf: [@sizeOf(InputHeader)]u8 = undefined;
2358
+ var in_r: Io.File.Reader = undefined;
2359
+ var in_f: Io.File = undefined;
2360
+ var in_name_buf: [12]u8 = undefined;
2361
+ var in_name: []const u8 = undefined;
2362
+ var i: u32 = 0;
2363
+ const header: InputHeader = while (true) {
2364
+ const name_prefix = "f" ++ Io.Dir.path.sep_str ++ "in";
2365
+ in_name = std.fmt.bufPrint(&in_name_buf, name_prefix ++ "{x}", .{i}) catch unreachable;
2366
+ in_f = b.cache_root.handle.openFile(io, in_name, .{
2367
+ .lock = .exclusive,
2368
+ .lock_nonblocking = true,
2369
+ }) catch |e| switch (e) {
2370
+ error.FileNotFound => return,
2371
+ error.WouldBlock => continue, // Can not be from
2372
+ // the crashed instance since it is still locked.
2373
+ else => return step.fail("failed to open file '{f}{s}': {t}", .{
2374
+ b.cache_root, in_name, e,
2375
+ }),
2376
+ };
2377
+
2378
+ in_r = in_f.readerStreaming(io, &in_r_buf);
2379
+ const header = in_r.interface.takeStruct(InputHeader, .little) catch |e| {
2380
+ in_f.close(io);
2381
+ switch (e) {
2382
+ error.ReadFailed => return step.fail("failed to read file '{f}{s}': {t}", .{
2383
+ b.cache_root, in_name, in_r.err.?,
2384
+ }),
2385
+ error.EndOfStream => continue,
2386
+ }
2387
+ };
2388
+
2389
+ if (header.pc_digest == f.coverage_id.? and
2390
+ header.instance_id == id and
2391
+ header.test_i < f.run.fuzz_tests.items.len)
2392
+ {
2393
+ break header;
2394
+ }
2395
+
2396
+ in_f.close(io);
2397
+ if (i == std.math.maxInt(u32)) return;
2398
+ i += 1;
2399
+ };
2400
+ defer in_f.close(io);
2401
+
2402
+ // Save it to a seperate file
2403
+ const crash_name = "f" ++ Io.Dir.path.sep_str ++ "crash";
2404
+ const out = b.cache_root.handle.createFile(io, crash_name, .{
2405
+ .lock = .exclusive, // Multiple run steps could have found a crash at the same time
2406
+ }) catch |e| return step.fail("failed to create file '{f}{s}': {t}", .{
2407
+ b.cache_root, crash_name, e,
2408
+ });
2409
+ defer out.close(io);
2410
+
2411
+ var out_w_buf: [512]u8 = undefined;
2412
+ var out_w = out.writerStreaming(io, &out_w_buf);
2413
+ _ = out_w.interface.sendFileAll(&in_r, .limited(header.len)) catch |e| switch (e) {
2414
+ error.ReadFailed => return step.fail("failed to read file '{f}{s}': {t}", .{
2415
+ b.cache_root, in_name, in_r.err.?,
2416
+ }),
2417
+ error.WriteFailed => return step.fail("failed to write file '{f}{s}': {t}", .{
2418
+ b.cache_root, crash_name, out_w.err.?,
2419
+ }),
2420
+ };
2421
+
2422
+ return f.run.step.fail("test '{s}' {f}; input saved to '{f}{s}'", .{
2423
+ f.run.fuzz_tests.items[header.test_i],
2424
+ fmtTerm(term),
2425
+ b.cache_root,
2426
+ crash_name,
2427
+ });
2428
+ }
2429
+
2430
+ fn instanceBroadcast(f: *FuzzTestRunner, from_id: u32, bytes: []const u8) !void {
2431
+ assert(f.instances.len > 1);
2432
+ assert(f.broadcast_undelivered == 0); // no other broadcast is progress
2433
+ assert(f.broadcast.items.len == 0);
2434
+ assert(from_id < f.instances.len);
2435
+
2436
+ const step_owner = f.run.step.owner;
2437
+ const gpa = step_owner.allocator;
2438
+
2439
+ var out_header: OutHeader = .{
2440
+ .tag = .new_fuzz_input,
2441
+ .bytes_len = @intCast(bytes.len),
2442
+ };
2443
+ if (std.builtin.Endian.native != .little) {
2444
+ std.mem.byteSwapAllFields(OutHeader, &out_header);
2445
+ }
2446
+ try f.broadcast.ensureTotalCapacity(gpa, @sizeOf(OutHeader) + bytes.len);
2447
+ f.broadcast.appendSliceAssumeCapacity(@ptrCast(&out_header));
2448
+ f.broadcast.appendSliceAssumeCapacity(bytes);
2449
+
2450
+ f.broadcast_undelivered = @intCast(f.instances.len - 1);
2451
+ for (0.., f.instances) |to_id, *instance| {
2452
+ if (to_id == from_id) continue;
2453
+ instance.broadcast_written = 0;
2454
+ f.addStdinWrite(@intCast(to_id));
2455
+ }
2456
+ }
2457
+
2458
+ fn broadcastComplete(f: *FuzzTestRunner) !void {
2459
+ assert(f.instances.len > 1);
2460
+ assert(f.broadcast_undelivered == 0);
2461
+ f.broadcast.clearRetainingCapacity();
2462
+
2463
+ const pending = &f.pending_broadcasts;
2464
+ if (pending.items.len != 0) {
2465
+ // Another broadcast is pending; copy it over to `broadcast`
2466
+
2467
+ const footer_len = @sizeOf(PendingBroadcastFooter);
2468
+ const footer_bytes = pending.items[pending.items.len - footer_len ..];
2469
+ const footer: *align(1) PendingBroadcastFooter = @ptrCast(footer_bytes);
2470
+ pending.items.len -= footer_len;
2471
+
2472
+ const body = pending.items[pending.items.len - footer.body_len ..];
2473
+ try f.instanceBroadcast(footer.from_id, body);
2474
+ pending.items.len -= body.len;
2475
+ }
2476
+ }
2477
+
2478
+ fn mergedStderr(f: *FuzzTestRunner) std.mem.Allocator.Error![]const u8 {
2479
+ const step_owner = f.run.step.owner;
2480
+ const arena = step_owner.allocator;
2481
+
2482
+ // Collect any remaining stderr
2483
+ while (f.batch.next()) |completion| {
2484
+ if (completion.index % 3 != 2) continue;
2485
+ const len = completion.result.file_read_streaming catch continue;
2486
+ f.instances[completion.index / 3].stderr.items.len += len;
2487
+ }
2488
+
2489
+ var stderr_len: usize = 0;
2490
+ for (f.instances) |*instance| stderr_len += instance.stderr.items.len;
2491
+ const stderr = try arena.alloc(u8, stderr_len);
2492
+
2493
+ stderr_len = 0;
2494
+ for (f.instances) |*instance| {
2495
+ @memcpy(stderr[stderr_len..][0..instance.stderr.items.len], instance.stderr.items);
2496
+ stderr_len += instance.stderr.items.len;
2497
+ }
2498
+ return stderr;
2499
+ }
2500
+ };
2501
+
2502
+ fn evalFuzzTest(
2503
+ run: *Run,
2504
+ spawn_options: process.SpawnOptions,
2505
+ options: Step.MakeOptions,
2506
+ fuzz_context: FuzzContext,
2507
+ ) !void {
2508
+ var f: FuzzTestRunner = try .init(run, fuzz_context, options.progress_node, spawn_options);
2509
+ defer f.deinit();
2510
+ try f.startInstances();
2511
+ try f.listen();
2074
2512
  }
2075
2513
 
2076
2514
  const TestMetadata = struct {
@@ -2149,30 +2587,41 @@ fn sendRunTestMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag, in
2149
2587
  fn sendRunFuzzTestMessage(
2150
2588
  io: Io,
2151
2589
  file: Io.File,
2152
- test_name: []const u8,
2590
+ test_names: []const []const u8,
2153
2591
  kind: std.Build.abi.fuzz.LimitKind,
2154
2592
  amount_or_instance: u64,
2155
2593
  ) !void {
2156
2594
  const header: std.zig.Client.Message.Header = .{
2157
2595
  .tag = .start_fuzzing,
2158
- .bytes_len = 4 + 1 + 8,
2596
+ .bytes_len = 1 + 8 + 4 + count: {
2597
+ var c: u32 = @intCast(test_names.len * 4);
2598
+ for (test_names) |name| {
2599
+ c += @intCast(name.len);
2600
+ }
2601
+ break :count c;
2602
+ },
2159
2603
  };
2160
2604
  var w = file.writerStreaming(io, &.{});
2161
2605
  w.interface.writeStruct(header, .little) catch |err| switch (err) {
2162
2606
  error.WriteFailed => return w.err.?,
2163
2607
  };
2164
- w.interface.writeInt(u32, @intCast(test_name.len), .little) catch |err| switch (err) {
2165
- error.WriteFailed => return w.err.?,
2166
- };
2167
- w.interface.writeAll(test_name) catch |err| switch (err) {
2168
- error.WriteFailed => return w.err.?,
2169
- };
2170
2608
  w.interface.writeByte(@intFromEnum(kind)) catch |err| switch (err) {
2171
2609
  error.WriteFailed => return w.err.?,
2172
2610
  };
2173
2611
  w.interface.writeInt(u64, amount_or_instance, .little) catch |err| switch (err) {
2174
2612
  error.WriteFailed => return w.err.?,
2175
2613
  };
2614
+ w.interface.writeInt(u32, @intCast(test_names.len), .little) catch |err| switch (err) {
2615
+ error.WriteFailed => return w.err.?,
2616
+ };
2617
+ for (test_names) |test_name| {
2618
+ w.interface.writeInt(u32, @intCast(test_name.len), .little) catch |err| switch (err) {
2619
+ error.WriteFailed => return w.err.?,
2620
+ };
2621
+ w.interface.writeAll(test_name) catch |err| switch (err) {
2622
+ error.WriteFailed => return w.err.?,
2623
+ };
2624
+ }
2176
2625
  }
2177
2626
 
2178
2627
  fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResult {
@@ -2342,8 +2791,8 @@ fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void {
2342
2791
  .expect_term => |term| {
2343
2792
  hh.add(@as(std.meta.Tag(process.Child.Term), term));
2344
2793
  switch (term) {
2345
- inline .exited, .signal => |x| hh.add(x),
2346
- .stopped, .unknown => |x| hh.add(x),
2794
+ inline .exited, .signal, .stopped => |x| hh.add(x),
2795
+ .unknown => |x| hh.add(x),
2347
2796
  }
2348
2797
  },
2349
2798
  }