| author | |
| committer | |
| log | afddfe25d80ee4db930ba746f25264286af6d325 |
| tree | 13c7fa62cbe65047da0cd109784ef417e92bd6ae |
| parent | 1c35e73b614398529782f8c027366c6d8d51ac4b |
| parent | 688c2df6464bd10a2dcfdf49e89c313e01da9991 |
| signature |
integrate fuzz testing into the build system15 files changed, 663 insertions(+), 76 deletions(-)
lib/compiler/build_runner.zig+33-12| ... | ... | @@ -9,8 +9,10 @@ const ArrayList = std.ArrayList; |
| 9 | 9 | const File = std.fs.File; |
| 10 | 10 | const Step = std.Build.Step; |
| 11 | 11 | const Watch = std.Build.Watch; |
| 12 | const Fuzz = std.Build.Fuzz; | |
| 12 | 13 | const Allocator = std.mem.Allocator; |
| 13 | const fatal = std.zig.fatal; | |
| 14 | const fatal = std.process.fatal; | |
| 15 | const runner = @This(); | |
| 14 | 16 | |
| 15 | 17 | pub const root = @import("@build"); |
| 16 | 18 | pub const dependencies = @import("@dependencies"); |
| ... | ... | @@ -102,6 +104,7 @@ pub fn main() !void { |
| 102 | 104 | var steps_menu = false; |
| 103 | 105 | var output_tmp_nonce: ?[16]u8 = null; |
| 104 | 106 | var watch = false; |
| 107 | var fuzz = false; | |
| 105 | 108 | var debounce_interval_ms: u16 = 50; |
| 106 | 109 | |
| 107 | 110 | while (nextArg(args, &arg_idx)) |arg| { |
| ... | ... | @@ -205,6 +208,8 @@ pub fn main() !void { |
| 205 | 208 | try debug_log_scopes.append(next_arg); |
| 206 | 209 | } else if (mem.eql(u8, arg, "--debug-pkg-config")) { |
| 207 | 210 | builder.debug_pkg_config = true; |
| 211 | } else if (mem.eql(u8, arg, "--debug-rt")) { | |
| 212 | graph.debug_compiler_runtime_libs = true; | |
| 208 | 213 | } else if (mem.eql(u8, arg, "--debug-compile-errors")) { |
| 209 | 214 | builder.debug_compile_errors = true; |
| 210 | 215 | } else if (mem.eql(u8, arg, "--system")) { |
| ... | ... | @@ -234,6 +239,8 @@ pub fn main() !void { |
| 234 | 239 | prominent_compile_errors = true; |
| 235 | 240 | } else if (mem.eql(u8, arg, "--watch")) { |
| 236 | 241 | watch = true; |
| 242 | } else if (mem.eql(u8, arg, "--fuzz")) { | |
| 243 | fuzz = true; | |
| 237 | 244 | } else if (mem.eql(u8, arg, "-fincremental")) { |
| 238 | 245 | graph.incremental = true; |
| 239 | 246 | } else if (mem.eql(u8, arg, "-fno-incremental")) { |
| ... | ... | @@ -353,6 +360,7 @@ pub fn main() !void { |
| 353 | 360 | .max_rss_mutex = .{}, |
| 354 | 361 | .skip_oom_steps = skip_oom_steps, |
| 355 | 362 | .watch = watch, |
| 363 | .fuzz = fuzz, | |
| 356 | 364 | .memory_blocked_steps = std.ArrayList(*Step).init(arena), |
| 357 | 365 | .step_stack = .{}, |
| 358 | 366 | .prominent_compile_errors = prominent_compile_errors, |
| ... | ... | @@ -394,6 +402,10 @@ pub fn main() !void { |
| 394 | 402 | }, |
| 395 | 403 | else => return err, |
| 396 | 404 | }; |
| 405 | if (fuzz) { | |
| 406 | Fuzz.start(&run.thread_pool, run.step_stack.keys(), run.ttyconf, main_progress_node); | |
| 407 | } | |
| 408 | ||
| 397 | 409 | if (!watch) return cleanExit(); |
| 398 | 410 | |
| 399 | 411 | switch (builtin.os.tag) { |
| ... | ... | @@ -457,6 +469,7 @@ const Run = struct { |
| 457 | 469 | max_rss_mutex: std.Thread.Mutex, |
| 458 | 470 | skip_oom_steps: bool, |
| 459 | 471 | watch: bool, |
| 472 | fuzz: bool, | |
| 460 | 473 | memory_blocked_steps: std.ArrayList(*Step), |
| 461 | 474 | step_stack: std.AutoArrayHashMapUnmanaged(*Step, void), |
| 462 | 475 | prominent_compile_errors: bool, |
| ... | ... | @@ -466,6 +479,11 @@ const Run = struct { |
| 466 | 479 | summary: Summary, |
| 467 | 480 | ttyconf: std.io.tty.Config, |
| 468 | 481 | stderr: File, |
| 482 | ||
| 483 | fn cleanExit(run: Run) void { | |
| 484 | if (run.watch or run.fuzz) return; | |
| 485 | return runner.cleanExit(); | |
| 486 | } | |
| 469 | 487 | }; |
| 470 | 488 | |
| 471 | 489 | fn prepare( |
| ... | ... | @@ -614,8 +632,7 @@ fn runStepNames( |
| 614 | 632 | else => false, |
| 615 | 633 | }; |
| 616 | 634 | if (failure_count == 0 and failures_only) { |
| 617 | if (!run.watch) cleanExit(); | |
| 618 | return; | |
| 635 | return run.cleanExit(); | |
| 619 | 636 | } |
| 620 | 637 | |
| 621 | 638 | const ttyconf = run.ttyconf; |
| ... | ... | @@ -672,8 +689,7 @@ fn runStepNames( |
| 672 | 689 | } |
| 673 | 690 | |
| 674 | 691 | if (failure_count == 0) { |
| 675 | if (!run.watch) cleanExit(); | |
| 676 | return; | |
| 692 | return run.cleanExit(); | |
| 677 | 693 | } |
| 678 | 694 | |
| 679 | 695 | // Finally, render compile errors at the bottom of the terminal. |
| ... | ... | @@ -1058,7 +1074,8 @@ fn workerMakeOneStep( |
| 1058 | 1074 | std.debug.lockStdErr(); |
| 1059 | 1075 | defer std.debug.unlockStdErr(); |
| 1060 | 1076 | |
| 1061 | printErrorMessages(b, s, run) catch {}; | |
| 1077 | const gpa = b.allocator; | |
| 1078 | printErrorMessages(gpa, s, run.ttyconf, run.stderr, run.prominent_compile_errors) catch {}; | |
| 1062 | 1079 | } |
| 1063 | 1080 | |
| 1064 | 1081 | handle_result: { |
| ... | ... | @@ -1111,11 +1128,13 @@ fn workerMakeOneStep( |
| 1111 | 1128 | } |
| 1112 | 1129 | } |
| 1113 | 1130 | |
| 1114 | fn printErrorMessages(b: *std.Build, failing_step: *Step, run: *const Run) !void { | |
| 1115 | const gpa = b.allocator; | |
| 1116 | const stderr = run.stderr; | |
| 1117 | const ttyconf = run.ttyconf; | |
| 1118 | ||
| 1131 | pub fn printErrorMessages( | |
| 1132 | gpa: Allocator, | |
| 1133 | failing_step: *Step, | |
| 1134 | ttyconf: std.io.tty.Config, | |
| 1135 | stderr: File, | |
| 1136 | prominent_compile_errors: bool, | |
| 1137 | ) !void { | |
| 1119 | 1138 | // Provide context for where these error messages are coming from by |
| 1120 | 1139 | // printing the corresponding Step subtree. |
| 1121 | 1140 | |
| ... | ... | @@ -1152,7 +1171,7 @@ fn printErrorMessages(b: *std.Build, failing_step: *Step, run: *const Run) !void |
| 1152 | 1171 | } |
| 1153 | 1172 | } |
| 1154 | 1173 | |
| 1155 | if (!run.prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) | |
| 1174 | if (!prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) | |
| 1156 | 1175 | try failing_step.result_error_bundle.renderToWriter(renderOptions(ttyconf), stderr.writer()); |
| 1157 | 1176 | |
| 1158 | 1177 | for (failing_step.result_error_msgs.items) |msg| { |
| ... | ... | @@ -1226,6 +1245,7 @@ fn usage(b: *std.Build, out_stream: anytype) !void { |
| 1226 | 1245 | \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss |
| 1227 | 1246 | \\ --fetch Exit after fetching dependency tree |
| 1228 | 1247 | \\ --watch Continuously rebuild when source files are modified |
| 1248 | \\ --fuzz Continuously search for unit test failures | |
| 1229 | 1249 | \\ --debounce <ms> Delay before rebuilding after changed file detected |
| 1230 | 1250 | \\ -fincremental Enable incremental compilation |
| 1231 | 1251 | \\ -fno-incremental Disable incremental compilation |
| ... | ... | @@ -1294,6 +1314,7 @@ fn usage(b: *std.Build, out_stream: anytype) !void { |
| 1294 | 1314 | \\ --seed [integer] For shuffling dependency traversal order (default: random) |
| 1295 | 1315 | \\ --debug-log [scope] Enable debugging the compiler |
| 1296 | 1316 | \\ --debug-pkg-config Fail if unknown pkg-config flags encountered |
| 1317 | \\ --debug-rt Debug compiler runtime libraries | |
| 1297 | 1318 | \\ --verbose-link Enable compiler debug output for linking |
| 1298 | 1319 | \\ --verbose-air Enable compiler debug output for Zig AIR |
| 1299 | 1320 | \\ --verbose-llvm-ir[=file] Enable compiler debug output for LLVM IR |
lib/compiler/test_runner.zig+89-22| ... | ... | @@ -1,18 +1,26 @@ |
| 1 | 1 | //! Default test runner for unit tests. |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | const std = @import("std"); |
| 3 | 4 | const io = std.io; |
| 4 | const builtin = @import("builtin"); | |
| 5 | const testing = std.testing; | |
| 5 | 6 | |
| 6 | 7 | pub const std_options = .{ |
| 7 | 8 | .logFn = log, |
| 8 | 9 | }; |
| 9 | 10 | |
| 10 | 11 | var log_err_count: usize = 0; |
| 11 | var cmdline_buffer: [4096]u8 = undefined; | |
| 12 | var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer); | |
| 12 | var fba_buffer: [8192]u8 = undefined; | |
| 13 | var fba = std.heap.FixedBufferAllocator.init(&fba_buffer); | |
| 14 | ||
| 15 | const crippled = switch (builtin.zig_backend) { | |
| 16 | .stage2_riscv64 => true, | |
| 17 | else => false, | |
| 18 | }; | |
| 13 | 19 | |
| 14 | 20 | pub fn main() void { |
| 15 | if (builtin.zig_backend == .stage2_riscv64) { | |
| 21 | @disableInstrumentation(); | |
| 22 | ||
| 23 | if (crippled) { | |
| 16 | 24 | return mainSimple() catch @panic("test failure\n"); |
| 17 | 25 | } |
| 18 | 26 | |
| ... | ... | @@ -25,13 +33,15 @@ pub fn main() void { |
| 25 | 33 | if (std.mem.eql(u8, arg, "--listen=-")) { |
| 26 | 34 | listen = true; |
| 27 | 35 | } else if (std.mem.startsWith(u8, arg, "--seed=")) { |
| 28 | std.testing.random_seed = std.fmt.parseUnsigned(u32, arg["--seed=".len..], 0) catch | |
| 36 | testing.random_seed = std.fmt.parseUnsigned(u32, arg["--seed=".len..], 0) catch | |
| 29 | 37 | @panic("unable to parse --seed command line argument"); |
| 30 | 38 | } else { |
| 31 | 39 | @panic("unrecognized command line argument"); |
| 32 | 40 | } |
| 33 | 41 | } |
| 34 | 42 | |
| 43 | fba.reset(); | |
| 44 | ||
| 35 | 45 | if (listen) { |
| 36 | 46 | return mainServer() catch @panic("internal test runner failure"); |
| 37 | 47 | } else { |
| ... | ... | @@ -40,6 +50,7 @@ pub fn main() void { |
| 40 | 50 | } |
| 41 | 51 | |
| 42 | 52 | fn mainServer() !void { |
| 53 | @disableInstrumentation(); | |
| 43 | 54 | var server = try std.zig.Server.init(.{ |
| 44 | 55 | .gpa = fba.allocator(), |
| 45 | 56 | .in = std.io.getStdIn(), |
| ... | ... | @@ -55,24 +66,24 @@ fn mainServer() !void { |
| 55 | 66 | return std.process.exit(0); |
| 56 | 67 | }, |
| 57 | 68 | .query_test_metadata => { |
| 58 | std.testing.allocator_instance = .{}; | |
| 59 | defer if (std.testing.allocator_instance.deinit() == .leak) { | |
| 69 | testing.allocator_instance = .{}; | |
| 70 | defer if (testing.allocator_instance.deinit() == .leak) { | |
| 60 | 71 | @panic("internal test runner memory leak"); |
| 61 | 72 | }; |
| 62 | 73 | |
| 63 | 74 | var string_bytes: std.ArrayListUnmanaged(u8) = .{}; |
| 64 | defer string_bytes.deinit(std.testing.allocator); | |
| 65 | try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null. | |
| 75 | defer string_bytes.deinit(testing.allocator); | |
| 76 | try string_bytes.append(testing.allocator, 0); // Reserve 0 for null. | |
| 66 | 77 | |
| 67 | 78 | const test_fns = builtin.test_functions; |
| 68 | const names = try std.testing.allocator.alloc(u32, test_fns.len); | |
| 69 | defer std.testing.allocator.free(names); | |
| 70 | const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len); | |
| 71 | defer std.testing.allocator.free(expected_panic_msgs); | |
| 79 | const names = try testing.allocator.alloc(u32, test_fns.len); | |
| 80 | defer testing.allocator.free(names); | |
| 81 | const expected_panic_msgs = try testing.allocator.alloc(u32, test_fns.len); | |
| 82 | defer testing.allocator.free(expected_panic_msgs); | |
| 72 | 83 | |
| 73 | 84 | for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| { |
| 74 | 85 | name.* = @as(u32, @intCast(string_bytes.items.len)); |
| 75 | try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1); | |
| 86 | try string_bytes.ensureUnusedCapacity(testing.allocator, test_fn.name.len + 1); | |
| 76 | 87 | string_bytes.appendSliceAssumeCapacity(test_fn.name); |
| 77 | 88 | string_bytes.appendAssumeCapacity(0); |
| 78 | 89 | expected_panic_msg.* = 0; |
| ... | ... | @@ -86,13 +97,13 @@ fn mainServer() !void { |
| 86 | 97 | }, |
| 87 | 98 | |
| 88 | 99 | .run_test => { |
| 89 | std.testing.allocator_instance = .{}; | |
| 100 | testing.allocator_instance = .{}; | |
| 90 | 101 | log_err_count = 0; |
| 91 | 102 | const index = try server.receiveBody_u32(); |
| 92 | 103 | const test_fn = builtin.test_functions[index]; |
| 93 | 104 | var fail = false; |
| 94 | 105 | var skip = false; |
| 95 | var leak = false; | |
| 106 | is_fuzz_test = false; | |
| 96 | 107 | test_fn.func() catch |err| switch (err) { |
| 97 | 108 | error.SkipZigTest => skip = true, |
| 98 | 109 | else => { |
| ... | ... | @@ -102,13 +113,14 @@ fn mainServer() !void { |
| 102 | 113 | } |
| 103 | 114 | }, |
| 104 | 115 | }; |
| 105 | leak = std.testing.allocator_instance.deinit() == .leak; | |
| 116 | const leak = testing.allocator_instance.deinit() == .leak; | |
| 106 | 117 | try server.serveTestResults(.{ |
| 107 | 118 | .index = index, |
| 108 | 119 | .flags = .{ |
| 109 | 120 | .fail = fail, |
| 110 | 121 | .skip = skip, |
| 111 | 122 | .leak = leak, |
| 123 | .fuzz = is_fuzz_test, | |
| 112 | 124 | .log_err_count = std.math.lossyCast( |
| 113 | 125 | @TypeOf(@as(std.zig.Server.Message.TestResults.Flags, undefined).log_err_count), |
| 114 | 126 | log_err_count, |
| ... | ... | @@ -116,9 +128,31 @@ fn mainServer() !void { |
| 116 | 128 | }, |
| 117 | 129 | }); |
| 118 | 130 | }, |
| 131 | .start_fuzzing => { | |
| 132 | const index = try server.receiveBody_u32(); | |
| 133 | const test_fn = builtin.test_functions[index]; | |
| 134 | while (true) { | |
| 135 | testing.allocator_instance = .{}; | |
| 136 | defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1); | |
| 137 | log_err_count = 0; | |
| 138 | is_fuzz_test = false; | |
| 139 | test_fn.func() catch |err| switch (err) { | |
| 140 | error.SkipZigTest => continue, | |
| 141 | else => { | |
| 142 | if (@errorReturnTrace()) |trace| { | |
| 143 | std.debug.dumpStackTrace(trace.*); | |
| 144 | } | |
| 145 | std.debug.print("failed with error.{s}\n", .{@errorName(err)}); | |
| 146 | std.process.exit(1); | |
| 147 | }, | |
| 148 | }; | |
| 149 | if (!is_fuzz_test) @panic("missed call to std.testing.fuzzInput"); | |
| 150 | if (log_err_count != 0) @panic("error logs detected"); | |
| 151 | } | |
| 152 | }, | |
| 119 | 153 | |
| 120 | 154 | else => { |
| 121 | std.debug.print("unsupported message: {x}", .{@intFromEnum(hdr.tag)}); | |
| 155 | std.debug.print("unsupported message: {x}\n", .{@intFromEnum(hdr.tag)}); | |
| 122 | 156 | std.process.exit(1); |
| 123 | 157 | }, |
| 124 | 158 | } |
| ... | ... | @@ -126,10 +160,12 @@ fn mainServer() !void { |
| 126 | 160 | } |
| 127 | 161 | |
| 128 | 162 | fn mainTerminal() void { |
| 163 | @disableInstrumentation(); | |
| 129 | 164 | const test_fn_list = builtin.test_functions; |
| 130 | 165 | var ok_count: usize = 0; |
| 131 | 166 | var skip_count: usize = 0; |
| 132 | 167 | var fail_count: usize = 0; |
| 168 | var fuzz_count: usize = 0; | |
| 133 | 169 | const root_node = std.Progress.start(.{ |
| 134 | 170 | .root_name = "Test", |
| 135 | 171 | .estimated_total_items = test_fn_list.len, |
| ... | ... | @@ -143,18 +179,19 @@ fn mainTerminal() void { |
| 143 | 179 | |
| 144 | 180 | var leaks: usize = 0; |
| 145 | 181 | for (test_fn_list, 0..) |test_fn, i| { |
| 146 | std.testing.allocator_instance = .{}; | |
| 182 | testing.allocator_instance = .{}; | |
| 147 | 183 | defer { |
| 148 | if (std.testing.allocator_instance.deinit() == .leak) { | |
| 184 | if (testing.allocator_instance.deinit() == .leak) { | |
| 149 | 185 | leaks += 1; |
| 150 | 186 | } |
| 151 | 187 | } |
| 152 | std.testing.log_level = .warn; | |
| 188 | testing.log_level = .warn; | |
| 153 | 189 | |
| 154 | 190 | const test_node = root_node.start(test_fn.name, 0); |
| 155 | 191 | if (!have_tty) { |
| 156 | 192 | std.debug.print("{d}/{d} {s}...", .{ i + 1, test_fn_list.len, test_fn.name }); |
| 157 | 193 | } |
| 194 | is_fuzz_test = false; | |
| 158 | 195 | if (test_fn.func()) |_| { |
| 159 | 196 | ok_count += 1; |
| 160 | 197 | test_node.end(); |
| ... | ... | @@ -184,6 +221,7 @@ fn mainTerminal() void { |
| 184 | 221 | test_node.end(); |
| 185 | 222 | }, |
| 186 | 223 | } |
| 224 | fuzz_count += @intFromBool(is_fuzz_test); | |
| 187 | 225 | } |
| 188 | 226 | root_node.end(); |
| 189 | 227 | if (ok_count == test_fn_list.len) { |
| ... | ... | @@ -197,6 +235,9 @@ fn mainTerminal() void { |
| 197 | 235 | if (leaks != 0) { |
| 198 | 236 | std.debug.print("{d} tests leaked memory.\n", .{leaks}); |
| 199 | 237 | } |
| 238 | if (fuzz_count != 0) { | |
| 239 | std.debug.print("{d} fuzz tests found.\n", .{fuzz_count}); | |
| 240 | } | |
| 200 | 241 | if (leaks != 0 or log_err_count != 0 or fail_count != 0) { |
| 201 | 242 | std.process.exit(1); |
| 202 | 243 | } |
| ... | ... | @@ -208,10 +249,11 @@ pub fn log( |
| 208 | 249 | comptime format: []const u8, |
| 209 | 250 | args: anytype, |
| 210 | 251 | ) void { |
| 252 | @disableInstrumentation(); | |
| 211 | 253 | if (@intFromEnum(message_level) <= @intFromEnum(std.log.Level.err)) { |
| 212 | 254 | log_err_count +|= 1; |
| 213 | 255 | } |
| 214 | if (@intFromEnum(message_level) <= @intFromEnum(std.testing.log_level)) { | |
| 256 | if (@intFromEnum(message_level) <= @intFromEnum(testing.log_level)) { | |
| 215 | 257 | std.debug.print( |
| 216 | 258 | "[" ++ @tagName(scope) ++ "] (" ++ @tagName(message_level) ++ "): " ++ format ++ "\n", |
| 217 | 259 | args, |
| ... | ... | @@ -222,6 +264,7 @@ pub fn log( |
| 222 | 264 | /// Simpler main(), exercising fewer language features, so that |
| 223 | 265 | /// work-in-progress backends can handle it. |
| 224 | 266 | pub fn mainSimple() anyerror!void { |
| 267 | @disableInstrumentation(); | |
| 225 | 268 | // is the backend capable of printing to stderr? |
| 226 | 269 | const enable_print = switch (builtin.zig_backend) { |
| 227 | 270 | else => false, |
| ... | ... | @@ -266,3 +309,27 @@ pub fn mainSimple() anyerror!void { |
| 266 | 309 | } |
| 267 | 310 | if (failed != 0) std.process.exit(1); |
| 268 | 311 | } |
| 312 | ||
| 313 | const FuzzerSlice = extern struct { | |
| 314 | ptr: [*]const u8, | |
| 315 | len: usize, | |
| 316 | ||
| 317 | inline fn toSlice(s: FuzzerSlice) []const u8 { | |
| 318 | return s.ptr[0..s.len]; | |
| 319 | } | |
| 320 | }; | |
| 321 | ||
| 322 | var is_fuzz_test: bool = undefined; | |
| 323 | ||
| 324 | extern fn fuzzer_next() FuzzerSlice; | |
| 325 | ||
| 326 | pub fn fuzzInput(options: testing.FuzzInputOptions) []const u8 { | |
| 327 | @disableInstrumentation(); | |
| 328 | if (crippled) return ""; | |
| 329 | is_fuzz_test = true; | |
| 330 | if (builtin.fuzz) return fuzzer_next().toSlice(); | |
| 331 | if (options.corpus.len == 0) return ""; | |
| 332 | var prng = std.Random.DefaultPrng.init(testing.random_seed); | |
| 333 | const random = prng.random(); | |
| 334 | return options.corpus[random.uintLessThan(usize, options.corpus.len)]; | |
| 335 | } |
lib/fuzzer.zig+263-8| ... | ... | @@ -1,13 +1,43 @@ |
| 1 | const builtin = @import("builtin"); | |
| 1 | 2 | const std = @import("std"); |
| 3 | const Allocator = std.mem.Allocator; | |
| 4 | const assert = std.debug.assert; | |
| 5 | ||
| 6 | pub const std_options = .{ | |
| 7 | .logFn = logOverride, | |
| 8 | }; | |
| 9 | ||
| 10 | var log_file: ?std.fs.File = null; | |
| 11 | ||
| 12 | fn logOverride( | |
| 13 | comptime level: std.log.Level, | |
| 14 | comptime scope: @TypeOf(.EnumLiteral), | |
| 15 | comptime format: []const u8, | |
| 16 | args: anytype, | |
| 17 | ) void { | |
| 18 | if (builtin.mode != .Debug) return; | |
| 19 | const f = if (log_file) |f| f else f: { | |
| 20 | const f = std.fs.cwd().createFile("libfuzzer.log", .{}) catch @panic("failed to open fuzzer log file"); | |
| 21 | log_file = f; | |
| 22 | break :f f; | |
| 23 | }; | |
| 24 | const prefix1 = comptime level.asText(); | |
| 25 | const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; | |
| 26 | f.writer().print(prefix1 ++ prefix2 ++ format ++ "\n", args) catch @panic("failed to write to fuzzer log"); | |
| 27 | } | |
| 2 | 28 | |
| 3 | 29 | export threadlocal var __sancov_lowest_stack: usize = 0; |
| 4 | 30 | |
| 5 | 31 | export fn __sanitizer_cov_8bit_counters_init(start: [*]u8, stop: [*]u8) void { |
| 6 | std.debug.print("__sanitizer_cov_8bit_counters_init start={*}, stop={*}\n", .{ start, stop }); | |
| 32 | std.log.debug("__sanitizer_cov_8bit_counters_init start={*}, stop={*}", .{ start, stop }); | |
| 7 | 33 | } |
| 8 | 34 | |
| 9 | export fn __sanitizer_cov_pcs_init(pcs_beg: [*]const usize, pcs_end: [*]const usize) void { | |
| 10 | std.debug.print("__sanitizer_cov_pcs_init pcs_beg={*}, pcs_end={*}\n", .{ pcs_beg, pcs_end }); | |
| 35 | export fn __sanitizer_cov_pcs_init(pc_start: [*]const usize, pc_end: [*]const usize) void { | |
| 36 | std.log.debug("__sanitizer_cov_pcs_init pc_start={*}, pc_end={*}", .{ pc_start, pc_end }); | |
| 37 | fuzzer.pc_range = .{ | |
| 38 | .start = @intFromPtr(pc_start), | |
| 39 | .end = @intFromPtr(pc_start), | |
| 40 | }; | |
| 11 | 41 | } |
| 12 | 42 | |
| 13 | 43 | export fn __sanitizer_cov_trace_const_cmp1(arg1: u8, arg2: u8) void { |
| ... | ... | @@ -47,16 +77,241 @@ export fn __sanitizer_cov_trace_switch(val: u64, cases_ptr: [*]u64) void { |
| 47 | 77 | const len = cases_ptr[0]; |
| 48 | 78 | const val_size_in_bits = cases_ptr[1]; |
| 49 | 79 | const cases = cases_ptr[2..][0..len]; |
| 50 | std.debug.print("0x{x}: switch on value {d} ({d} bits) with {d} cases\n", .{ | |
| 51 | pc, val, val_size_in_bits, cases.len, | |
| 52 | }); | |
| 80 | _ = val; | |
| 81 | fuzzer.visitPc(pc); | |
| 82 | _ = val_size_in_bits; | |
| 83 | _ = cases; | |
| 84 | //std.log.debug("0x{x}: switch on value {d} ({d} bits) with {d} cases", .{ | |
| 85 | // pc, val, val_size_in_bits, cases.len, | |
| 86 | //}); | |
| 53 | 87 | } |
| 54 | 88 | |
| 55 | 89 | export fn __sanitizer_cov_trace_pc_indir(callee: usize) void { |
| 56 | 90 | const pc = @returnAddress(); |
| 57 | std.debug.print("0x{x}: indirect call to 0x{x}\n", .{ pc, callee }); | |
| 91 | _ = callee; | |
| 92 | fuzzer.visitPc(pc); | |
| 93 | //std.log.debug("0x{x}: indirect call to 0x{x}", .{ pc, callee }); | |
| 58 | 94 | } |
| 59 | 95 | |
| 60 | 96 | fn handleCmp(pc: usize, arg1: u64, arg2: u64) void { |
| 61 | std.debug.print("0x{x}: comparison of {d} and {d}\n", .{ pc, arg1, arg2 }); | |
| 97 | fuzzer.visitPc(pc ^ arg1 ^ arg2); | |
| 98 | //std.log.debug("0x{x}: comparison of {d} and {d}", .{ pc, arg1, arg2 }); | |
| 99 | } | |
| 100 | ||
| 101 | const Fuzzer = struct { | |
| 102 | gpa: Allocator, | |
| 103 | rng: std.Random.DefaultPrng, | |
| 104 | input: std.ArrayListUnmanaged(u8), | |
| 105 | pc_range: PcRange, | |
| 106 | count: usize, | |
| 107 | recent_cases: RunMap, | |
| 108 | deduplicated_runs: usize, | |
| 109 | coverage: Coverage, | |
| 110 | ||
| 111 | const RunMap = std.ArrayHashMapUnmanaged(Run, void, Run.HashContext, false); | |
| 112 | ||
| 113 | const Coverage = struct { | |
| 114 | pc_table: std.AutoArrayHashMapUnmanaged(usize, void), | |
| 115 | run_id_hasher: std.hash.Wyhash, | |
| 116 | ||
| 117 | fn reset(cov: *Coverage) void { | |
| 118 | cov.pc_table.clearRetainingCapacity(); | |
| 119 | cov.run_id_hasher = std.hash.Wyhash.init(0); | |
| 120 | } | |
| 121 | }; | |
| 122 | ||
| 123 | const Run = struct { | |
| 124 | id: Id, | |
| 125 | input: []const u8, | |
| 126 | score: usize, | |
| 127 | ||
| 128 | const Id = u64; | |
| 129 | ||
| 130 | const HashContext = struct { | |
| 131 | pub fn eql(ctx: HashContext, a: Run, b: Run, b_index: usize) bool { | |
| 132 | _ = b_index; | |
| 133 | _ = ctx; | |
| 134 | return a.id == b.id; | |
| 135 | } | |
| 136 | pub fn hash(ctx: HashContext, a: Run) u32 { | |
| 137 | _ = ctx; | |
| 138 | return @truncate(a.id); | |
| 139 | } | |
| 140 | }; | |
| 141 | ||
| 142 | fn deinit(run: *Run, gpa: Allocator) void { | |
| 143 | gpa.free(run.input); | |
| 144 | run.* = undefined; | |
| 145 | } | |
| 146 | }; | |
| 147 | ||
| 148 | const Slice = extern struct { | |
| 149 | ptr: [*]const u8, | |
| 150 | len: usize, | |
| 151 | ||
| 152 | fn toZig(s: Slice) []const u8 { | |
| 153 | return s.ptr[0..s.len]; | |
| 154 | } | |
| 155 | ||
| 156 | fn fromZig(s: []const u8) Slice { | |
| 157 | return .{ | |
| 158 | .ptr = s.ptr, | |
| 159 | .len = s.len, | |
| 160 | }; | |
| 161 | } | |
| 162 | }; | |
| 163 | ||
| 164 | const PcRange = struct { | |
| 165 | start: usize, | |
| 166 | end: usize, | |
| 167 | }; | |
| 168 | ||
| 169 | const Analysis = struct { | |
| 170 | score: usize, | |
| 171 | id: Run.Id, | |
| 172 | }; | |
| 173 | ||
| 174 | fn analyzeLastRun(f: *Fuzzer) Analysis { | |
| 175 | return .{ | |
| 176 | .id = f.coverage.run_id_hasher.final(), | |
| 177 | .score = f.coverage.pc_table.count(), | |
| 178 | }; | |
| 179 | } | |
| 180 | ||
| 181 | fn next(f: *Fuzzer) ![]const u8 { | |
| 182 | const gpa = f.gpa; | |
| 183 | const rng = fuzzer.rng.random(); | |
| 184 | ||
| 185 | if (f.recent_cases.entries.len == 0) { | |
| 186 | // Prepare initial input. | |
| 187 | try f.recent_cases.ensureUnusedCapacity(gpa, 100); | |
| 188 | const len = rng.uintLessThanBiased(usize, 80); | |
| 189 | try f.input.resize(gpa, len); | |
| 190 | rng.bytes(f.input.items); | |
| 191 | f.recent_cases.putAssumeCapacity(.{ | |
| 192 | .id = 0, | |
| 193 | .input = try gpa.dupe(u8, f.input.items), | |
| 194 | .score = 0, | |
| 195 | }, {}); | |
| 196 | } else { | |
| 197 | if (f.count % 1000 == 0) f.dumpStats(); | |
| 198 | ||
| 199 | const analysis = f.analyzeLastRun(); | |
| 200 | const gop = f.recent_cases.getOrPutAssumeCapacity(.{ | |
| 201 | .id = analysis.id, | |
| 202 | .input = undefined, | |
| 203 | .score = undefined, | |
| 204 | }); | |
| 205 | if (gop.found_existing) { | |
| 206 | //std.log.info("duplicate analysis: score={d} id={d}", .{ analysis.score, analysis.id }); | |
| 207 | f.deduplicated_runs += 1; | |
| 208 | if (f.input.items.len < gop.key_ptr.input.len or gop.key_ptr.score == 0) { | |
| 209 | gpa.free(gop.key_ptr.input); | |
| 210 | gop.key_ptr.input = try gpa.dupe(u8, f.input.items); | |
| 211 | gop.key_ptr.score = analysis.score; | |
| 212 | } | |
| 213 | } else { | |
| 214 | std.log.info("unique analysis: score={d} id={d}", .{ analysis.score, analysis.id }); | |
| 215 | gop.key_ptr.* = .{ | |
| 216 | .id = analysis.id, | |
| 217 | .input = try gpa.dupe(u8, f.input.items), | |
| 218 | .score = analysis.score, | |
| 219 | }; | |
| 220 | } | |
| 221 | ||
| 222 | if (f.recent_cases.entries.len >= 100) { | |
| 223 | const Context = struct { | |
| 224 | values: []const Run, | |
| 225 | pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { | |
| 226 | return ctx.values[b_index].score < ctx.values[a_index].score; | |
| 227 | } | |
| 228 | }; | |
| 229 | f.recent_cases.sortUnstable(Context{ .values = f.recent_cases.keys() }); | |
| 230 | const cap = 50; | |
| 231 | // This has to be done before deinitializing the deleted items. | |
| 232 | const doomed_runs = f.recent_cases.keys()[cap..]; | |
| 233 | f.recent_cases.shrinkRetainingCapacity(cap); | |
| 234 | for (doomed_runs) |*run| { | |
| 235 | std.log.info("culling score={d} id={d}", .{ run.score, run.id }); | |
| 236 | run.deinit(gpa); | |
| 237 | } | |
| 238 | } | |
| 239 | } | |
| 240 | ||
| 241 | const chosen_index = rng.uintLessThanBiased(usize, f.recent_cases.entries.len); | |
| 242 | const run = &f.recent_cases.keys()[chosen_index]; | |
| 243 | f.input.clearRetainingCapacity(); | |
| 244 | f.input.appendSliceAssumeCapacity(run.input); | |
| 245 | try f.mutate(); | |
| 246 | ||
| 247 | f.coverage.reset(); | |
| 248 | f.count += 1; | |
| 249 | return f.input.items; | |
| 250 | } | |
| 251 | ||
| 252 | fn visitPc(f: *Fuzzer, pc: usize) void { | |
| 253 | errdefer |err| oom(err); | |
| 254 | try f.coverage.pc_table.put(f.gpa, pc, {}); | |
| 255 | f.coverage.run_id_hasher.update(std.mem.asBytes(&pc)); | |
| 256 | } | |
| 257 | ||
| 258 | fn dumpStats(f: *Fuzzer) void { | |
| 259 | std.log.info("stats: runs={d} deduplicated={d}", .{ | |
| 260 | f.count, | |
| 261 | f.deduplicated_runs, | |
| 262 | }); | |
| 263 | for (f.recent_cases.keys()[0..@min(f.recent_cases.entries.len, 5)], 0..) |run, i| { | |
| 264 | std.log.info("best[{d}] id={x} score={d} input: '{}'", .{ | |
| 265 | i, run.id, run.score, std.zig.fmtEscapes(run.input), | |
| 266 | }); | |
| 267 | } | |
| 268 | } | |
| 269 | ||
| 270 | fn mutate(f: *Fuzzer) !void { | |
| 271 | const gpa = f.gpa; | |
| 272 | const rng = fuzzer.rng.random(); | |
| 273 | ||
| 274 | if (f.input.items.len == 0) { | |
| 275 | const len = rng.uintLessThanBiased(usize, 80); | |
| 276 | try f.input.resize(gpa, len); | |
| 277 | rng.bytes(f.input.items); | |
| 278 | return; | |
| 279 | } | |
| 280 | ||
| 281 | const index = rng.uintLessThanBiased(usize, f.input.items.len * 3); | |
| 282 | if (index < f.input.items.len) { | |
| 283 | f.input.items[index] = rng.int(u8); | |
| 284 | } else if (index < f.input.items.len * 2) { | |
| 285 | _ = f.input.orderedRemove(index - f.input.items.len); | |
| 286 | } else if (index < f.input.items.len * 3) { | |
| 287 | try f.input.insert(gpa, index - f.input.items.len * 2, rng.int(u8)); | |
| 288 | } else { | |
| 289 | unreachable; | |
| 290 | } | |
| 291 | } | |
| 292 | }; | |
| 293 | ||
| 294 | fn oom(err: anytype) noreturn { | |
| 295 | switch (err) { | |
| 296 | error.OutOfMemory => @panic("out of memory"), | |
| 297 | } | |
| 298 | } | |
| 299 | ||
| 300 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{}; | |
| 301 | ||
| 302 | var fuzzer: Fuzzer = .{ | |
| 303 | .gpa = general_purpose_allocator.allocator(), | |
| 304 | .rng = std.Random.DefaultPrng.init(0), | |
| 305 | .input = .{}, | |
| 306 | .pc_range = .{ .start = 0, .end = 0 }, | |
| 307 | .count = 0, | |
| 308 | .deduplicated_runs = 0, | |
| 309 | .recent_cases = .{}, | |
| 310 | .coverage = undefined, | |
| 311 | }; | |
| 312 | ||
| 313 | export fn fuzzer_next() Fuzzer.Slice { | |
| 314 | return Fuzzer.Slice.fromZig(fuzzer.next() catch |err| switch (err) { | |
| 315 | error.OutOfMemory => @panic("out of memory"), | |
| 316 | }); | |
| 62 | 317 | } |
lib/std/Build.zig+3| ... | ... | @@ -21,6 +21,7 @@ pub const Cache = @import("Build/Cache.zig"); |
| 21 | 21 | pub const Step = @import("Build/Step.zig"); |
| 22 | 22 | pub const Module = @import("Build/Module.zig"); |
| 23 | 23 | pub const Watch = @import("Build/Watch.zig"); |
| 24 | pub const Fuzz = @import("Build/Fuzz.zig"); | |
| 24 | 25 | |
| 25 | 26 | /// Shared state among all Build instances. |
| 26 | 27 | graph: *Graph, |
| ... | ... | @@ -112,6 +113,7 @@ pub const Graph = struct { |
| 112 | 113 | arena: Allocator, |
| 113 | 114 | system_library_options: std.StringArrayHashMapUnmanaged(SystemLibraryMode) = .{}, |
| 114 | 115 | system_package_mode: bool = false, |
| 116 | debug_compiler_runtime_libs: bool = false, | |
| 115 | 117 | cache: Cache, |
| 116 | 118 | zig_exe: [:0]const u8, |
| 117 | 119 | env_map: EnvMap, |
| ... | ... | @@ -977,6 +979,7 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run { |
| 977 | 979 | // Consider that this is declarative; the run step may not be run unless a user |
| 978 | 980 | // option is supplied. |
| 979 | 981 | const run_step = Step.Run.create(b, b.fmt("run {s}", .{exe.name})); |
| 982 | run_step.producer = exe; | |
| 980 | 983 | if (exe.kind == .@"test") { |
| 981 | 984 | if (exe.exec_cmd_args) |exec_cmd_args| { |
| 982 | 985 | for (exec_cmd_args) |cmd_arg| { |
lib/std/Build/Fuzz.zig created+114| ... | ... | @@ -0,0 +1,114 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const Fuzz = @This(); | |
| 3 | const Step = std.Build.Step; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const fatal = std.process.fatal; | |
| 6 | const build_runner = @import("root"); | |
| 7 | ||
| 8 | pub fn start( | |
| 9 | thread_pool: *std.Thread.Pool, | |
| 10 | all_steps: []const *Step, | |
| 11 | ttyconf: std.io.tty.Config, | |
| 12 | prog_node: std.Progress.Node, | |
| 13 | ) void { | |
| 14 | const count = block: { | |
| 15 | const rebuild_node = prog_node.start("Rebuilding Unit Tests", 0); | |
| 16 | defer rebuild_node.end(); | |
| 17 | var count: usize = 0; | |
| 18 | var wait_group: std.Thread.WaitGroup = .{}; | |
| 19 | defer wait_group.wait(); | |
| 20 | for (all_steps) |step| { | |
| 21 | const run = step.cast(Step.Run) orelse continue; | |
| 22 | if (run.fuzz_tests.items.len > 0 and run.producer != null) { | |
| 23 | thread_pool.spawnWg(&wait_group, rebuildTestsWorkerRun, .{ run, ttyconf, rebuild_node }); | |
| 24 | count += 1; | |
| 25 | } | |
| 26 | } | |
| 27 | if (count == 0) fatal("no fuzz tests found", .{}); | |
| 28 | rebuild_node.setEstimatedTotalItems(count); | |
| 29 | break :block count; | |
| 30 | }; | |
| 31 | ||
| 32 | // Detect failure. | |
| 33 | for (all_steps) |step| { | |
| 34 | const run = step.cast(Step.Run) orelse continue; | |
| 35 | if (run.fuzz_tests.items.len > 0 and run.rebuilt_executable == null) | |
| 36 | fatal("one or more unit tests failed to be rebuilt in fuzz mode", .{}); | |
| 37 | } | |
| 38 | ||
| 39 | { | |
| 40 | const fuzz_node = prog_node.start("Fuzzing", count); | |
| 41 | defer fuzz_node.end(); | |
| 42 | var wait_group: std.Thread.WaitGroup = .{}; | |
| 43 | defer wait_group.wait(); | |
| 44 | ||
| 45 | for (all_steps) |step| { | |
| 46 | const run = step.cast(Step.Run) orelse continue; | |
| 47 | for (run.fuzz_tests.items) |unit_test_index| { | |
| 48 | assert(run.rebuilt_executable != null); | |
| 49 | thread_pool.spawnWg(&wait_group, fuzzWorkerRun, .{ run, unit_test_index, ttyconf, fuzz_node }); | |
| 50 | } | |
| 51 | } | |
| 52 | } | |
| 53 | ||
| 54 | fatal("all fuzz workers crashed", .{}); | |
| 55 | } | |
| 56 | ||
| 57 | fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) void { | |
| 58 | const gpa = run.step.owner.allocator; | |
| 59 | const stderr = std.io.getStdErr(); | |
| 60 | ||
| 61 | const compile = run.producer.?; | |
| 62 | const prog_node = parent_prog_node.start(compile.step.name, 0); | |
| 63 | defer prog_node.end(); | |
| 64 | ||
| 65 | const result = compile.rebuildInFuzzMode(prog_node); | |
| 66 | ||
| 67 | const show_compile_errors = compile.step.result_error_bundle.errorMessageCount() > 0; | |
| 68 | const show_error_msgs = compile.step.result_error_msgs.items.len > 0; | |
| 69 | const show_stderr = compile.step.result_stderr.len > 0; | |
| 70 | ||
| 71 | if (show_error_msgs or show_compile_errors or show_stderr) { | |
| 72 | std.debug.lockStdErr(); | |
| 73 | defer std.debug.unlockStdErr(); | |
| 74 | build_runner.printErrorMessages(gpa, &compile.step, ttyconf, stderr, false) catch {}; | |
| 75 | } | |
| 76 | ||
| 77 | if (result) |rebuilt_bin_path| { | |
| 78 | run.rebuilt_executable = rebuilt_bin_path; | |
| 79 | } else |err| switch (err) { | |
| 80 | error.MakeFailed => {}, | |
| 81 | else => { | |
| 82 | std.debug.print("step '{s}': failed to rebuild in fuzz mode: {s}\n", .{ | |
| 83 | compile.step.name, @errorName(err), | |
| 84 | }); | |
| 85 | }, | |
| 86 | } | |
| 87 | } | |
| 88 | ||
| 89 | fn fuzzWorkerRun( | |
| 90 | run: *Step.Run, | |
| 91 | unit_test_index: u32, | |
| 92 | ttyconf: std.io.tty.Config, | |
| 93 | parent_prog_node: std.Progress.Node, | |
| 94 | ) void { | |
| 95 | const gpa = run.step.owner.allocator; | |
| 96 | const test_name = run.cached_test_metadata.?.testName(unit_test_index); | |
| 97 | ||
| 98 | const prog_node = parent_prog_node.start(test_name, 0); | |
| 99 | defer prog_node.end(); | |
| 100 | ||
| 101 | run.rerunInFuzzMode(unit_test_index, prog_node) catch |err| switch (err) { | |
| 102 | error.MakeFailed => { | |
| 103 | const stderr = std.io.getStdErr(); | |
| 104 | std.debug.lockStdErr(); | |
| 105 | defer std.debug.unlockStdErr(); | |
| 106 | build_runner.printErrorMessages(gpa, &run.step, ttyconf, stderr, false) catch {}; | |
| 107 | }, | |
| 108 | else => { | |
| 109 | std.debug.print("step '{s}': failed to rebuild '{s}' in fuzz mode: {s}\n", .{ | |
| 110 | run.step.name, test_name, @errorName(err), | |
| 111 | }); | |
| 112 | }, | |
| 113 | }; | |
| 114 | } |
lib/std/Build/Step/Compile.zig+31-11| ... | ... | @@ -1004,7 +1004,7 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking |
| 1004 | 1004 | return path; |
| 1005 | 1005 | } |
| 1006 | 1006 | |
| 1007 | fn getZigArgs(compile: *Compile) ![][]const u8 { | |
| 1007 | fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { | |
| 1008 | 1008 | const step = &compile.step; |
| 1009 | 1009 | const b = step.owner; |
| 1010 | 1010 | const arena = b.allocator; |
| ... | ... | @@ -1055,6 +1055,10 @@ fn getZigArgs(compile: *Compile) ![][]const u8 { |
| 1055 | 1055 | try zig_args.append(try std.fmt.allocPrint(arena, "{}", .{stack_size})); |
| 1056 | 1056 | } |
| 1057 | 1057 | |
| 1058 | if (fuzz) { | |
| 1059 | try zig_args.append("-ffuzz"); | |
| 1060 | } | |
| 1061 | ||
| 1058 | 1062 | { |
| 1059 | 1063 | // Stores system libraries that have already been seen for at least one |
| 1060 | 1064 | // module, along with any arguments that need to be passed to the |
| ... | ... | @@ -1479,6 +1483,8 @@ fn getZigArgs(compile: *Compile) ![][]const u8 { |
| 1479 | 1483 | try zig_args.append("--global-cache-dir"); |
| 1480 | 1484 | try zig_args.append(b.graph.global_cache_root.path orelse "."); |
| 1481 | 1485 | |
| 1486 | if (b.graph.debug_compiler_runtime_libs) try zig_args.append("--debug-rt"); | |
| 1487 | ||
| 1482 | 1488 | try zig_args.append("--name"); |
| 1483 | 1489 | try zig_args.append(compile.name); |
| 1484 | 1490 | |
| ... | ... | @@ -1757,7 +1763,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 1757 | 1763 | const b = step.owner; |
| 1758 | 1764 | const compile: *Compile = @fieldParentPtr("step", step); |
| 1759 | 1765 | |
| 1760 | const zig_args = try getZigArgs(compile); | |
| 1766 | const zig_args = try getZigArgs(compile, false); | |
| 1761 | 1767 | |
| 1762 | 1768 | const maybe_output_bin_path = step.evalZigProcess( |
| 1763 | 1769 | zig_args, |
| ... | ... | @@ -1835,6 +1841,20 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 1835 | 1841 | } |
| 1836 | 1842 | } |
| 1837 | 1843 | |
| 1844 | pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) ![]const u8 { | |
| 1845 | const gpa = c.step.owner.allocator; | |
| 1846 | ||
| 1847 | c.step.result_error_msgs.clearRetainingCapacity(); | |
| 1848 | c.step.result_stderr = ""; | |
| 1849 | ||
| 1850 | c.step.result_error_bundle.deinit(gpa); | |
| 1851 | c.step.result_error_bundle = std.zig.ErrorBundle.empty; | |
| 1852 | ||
| 1853 | const zig_args = try getZigArgs(c, true); | |
| 1854 | const maybe_output_bin_path = try c.step.evalZigProcess(zig_args, progress_node, false); | |
| 1855 | return maybe_output_bin_path.?; | |
| 1856 | } | |
| 1857 | ||
| 1838 | 1858 | pub fn doAtomicSymLinks( |
| 1839 | 1859 | step: *Step, |
| 1840 | 1860 | output_path: []const u8, |
| ... | ... | @@ -1861,10 +1881,10 @@ pub fn doAtomicSymLinks( |
| 1861 | 1881 | }; |
| 1862 | 1882 | } |
| 1863 | 1883 | |
| 1864 | fn execPkgConfigList(compile: *std.Build, out_code: *u8) (PkgConfigError || RunError)![]const PkgConfigPkg { | |
| 1865 | const pkg_config_exe = compile.graph.env_map.get("PKG_CONFIG") orelse "pkg-config"; | |
| 1866 | const stdout = try compile.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .Ignore); | |
| 1867 | var list = ArrayList(PkgConfigPkg).init(compile.allocator); | |
| 1884 | fn execPkgConfigList(b: *std.Build, out_code: *u8) (PkgConfigError || RunError)![]const PkgConfigPkg { | |
| 1885 | const pkg_config_exe = b.graph.env_map.get("PKG_CONFIG") orelse "pkg-config"; | |
| 1886 | const stdout = try b.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .Ignore); | |
| 1887 | var list = ArrayList(PkgConfigPkg).init(b.allocator); | |
| 1868 | 1888 | errdefer list.deinit(); |
| 1869 | 1889 | var line_it = mem.tokenizeAny(u8, stdout, "\r\n"); |
| 1870 | 1890 | while (line_it.next()) |line| { |
| ... | ... | @@ -1878,13 +1898,13 @@ fn execPkgConfigList(compile: *std.Build, out_code: *u8) (PkgConfigError || RunE |
| 1878 | 1898 | return list.toOwnedSlice(); |
| 1879 | 1899 | } |
| 1880 | 1900 | |
| 1881 | fn getPkgConfigList(compile: *std.Build) ![]const PkgConfigPkg { | |
| 1882 | if (compile.pkg_config_pkg_list) |res| { | |
| 1901 | fn getPkgConfigList(b: *std.Build) ![]const PkgConfigPkg { | |
| 1902 | if (b.pkg_config_pkg_list) |res| { | |
| 1883 | 1903 | return res; |
| 1884 | 1904 | } |
| 1885 | 1905 | var code: u8 = undefined; |
| 1886 | if (execPkgConfigList(compile, &code)) |list| { | |
| 1887 | compile.pkg_config_pkg_list = list; | |
| 1906 | if (execPkgConfigList(b, &code)) |list| { | |
| 1907 | b.pkg_config_pkg_list = list; | |
| 1888 | 1908 | return list; |
| 1889 | 1909 | } else |err| { |
| 1890 | 1910 | const result = switch (err) { |
| ... | ... | @@ -1896,7 +1916,7 @@ fn getPkgConfigList(compile: *std.Build) ![]const PkgConfigPkg { |
| 1896 | 1916 | error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, |
| 1897 | 1917 | else => return err, |
| 1898 | 1918 | }; |
| 1899 | compile.pkg_config_pkg_list = result; | |
| 1919 | b.pkg_config_pkg_list = result; | |
| 1900 | 1920 | return result; |
| 1901 | 1921 | } |
| 1902 | 1922 | } |
lib/std/Build/Step/Run.zig+101-11| ... | ... | @@ -86,6 +86,18 @@ dep_output_file: ?*Output, |
| 86 | 86 | |
| 87 | 87 | has_side_effects: bool, |
| 88 | 88 | |
| 89 | /// If this is a Zig unit test binary, this tracks the indexes of the unit | |
| 90 | /// tests that are also fuzz tests. | |
| 91 | fuzz_tests: std.ArrayListUnmanaged(u32), | |
| 92 | cached_test_metadata: ?CachedTestMetadata = null, | |
| 93 | ||
| 94 | /// Populated during the fuzz phase if this run step corresponds to a unit test | |
| 95 | /// executable that contains fuzz tests. | |
| 96 | rebuilt_executable: ?[]const u8, | |
| 97 | ||
| 98 | /// If this Run step was produced by a Compile step, it is tracked here. | |
| 99 | producer: ?*Step.Compile, | |
| 100 | ||
| 89 | 101 | pub const StdIn = union(enum) { |
| 90 | 102 | none, |
| 91 | 103 | bytes: []const u8, |
| ... | ... | @@ -175,6 +187,9 @@ pub fn create(owner: *std.Build, name: []const u8) *Run { |
| 175 | 187 | .captured_stderr = null, |
| 176 | 188 | .dep_output_file = null, |
| 177 | 189 | .has_side_effects = false, |
| 190 | .fuzz_tests = .{}, | |
| 191 | .rebuilt_executable = null, | |
| 192 | .producer = null, | |
| 178 | 193 | }; |
| 179 | 194 | return run; |
| 180 | 195 | } |
| ... | ... | @@ -741,7 +756,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 741 | 756 | b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); |
| 742 | 757 | } |
| 743 | 758 | |
| 744 | try runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node); | |
| 759 | try runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node, null); | |
| 745 | 760 | if (!has_side_effects) try step.writeManifestAndWatch(&man); |
| 746 | 761 | return; |
| 747 | 762 | }; |
| ... | ... | @@ -771,7 +786,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 771 | 786 | b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); |
| 772 | 787 | } |
| 773 | 788 | |
| 774 | try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node); | |
| 789 | try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, null); | |
| 775 | 790 | |
| 776 | 791 | const dep_file_dir = std.fs.cwd(); |
| 777 | 792 | const dep_file_basename = dep_output_file.generated_file.getPath(); |
| ... | ... | @@ -830,6 +845,41 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 830 | 845 | ); |
| 831 | 846 | } |
| 832 | 847 | |
| 848 | pub fn rerunInFuzzMode(run: *Run, unit_test_index: u32, prog_node: std.Progress.Node) !void { | |
| 849 | const step = &run.step; | |
| 850 | const b = step.owner; | |
| 851 | const arena = b.allocator; | |
| 852 | var argv_list: std.ArrayListUnmanaged([]const u8) = .{}; | |
| 853 | for (run.argv.items) |arg| { | |
| 854 | switch (arg) { | |
| 855 | .bytes => |bytes| { | |
| 856 | try argv_list.append(arena, bytes); | |
| 857 | }, | |
| 858 | .lazy_path => |file| { | |
| 859 | const file_path = file.lazy_path.getPath2(b, step); | |
| 860 | try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, file_path })); | |
| 861 | }, | |
| 862 | .directory_source => |file| { | |
| 863 | const file_path = file.lazy_path.getPath2(b, step); | |
| 864 | try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, file_path })); | |
| 865 | }, | |
| 866 | .artifact => |pa| { | |
| 867 | const artifact = pa.artifact; | |
| 868 | const file_path = if (artifact == run.producer.?) | |
| 869 | run.rebuilt_executable.? | |
| 870 | else | |
| 871 | (artifact.installed_path orelse artifact.generated_bin.?.path.?); | |
| 872 | try argv_list.append(arena, b.fmt("{s}{s}", .{ pa.prefix, file_path })); | |
| 873 | }, | |
| 874 | .output_file, .output_directory => unreachable, | |
| 875 | } | |
| 876 | } | |
| 877 | const has_side_effects = false; | |
| 878 | const rand_int = std.crypto.random.int(u64); | |
| 879 | const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); | |
| 880 | try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, unit_test_index); | |
| 881 | } | |
| 882 | ||
| 833 | 883 | fn populateGeneratedPaths( |
| 834 | 884 | arena: std.mem.Allocator, |
| 835 | 885 | output_placeholders: []const IndexedOutput, |
| ... | ... | @@ -908,6 +958,7 @@ fn runCommand( |
| 908 | 958 | has_side_effects: bool, |
| 909 | 959 | output_dir_path: []const u8, |
| 910 | 960 | prog_node: std.Progress.Node, |
| 961 | fuzz_unit_test_index: ?u32, | |
| 911 | 962 | ) !void { |
| 912 | 963 | const step = &run.step; |
| 913 | 964 | const b = step.owner; |
| ... | ... | @@ -926,7 +977,7 @@ fn runCommand( |
| 926 | 977 | var interp_argv = std.ArrayList([]const u8).init(b.allocator); |
| 927 | 978 | defer interp_argv.deinit(); |
| 928 | 979 | |
| 929 | const result = spawnChildAndCollect(run, argv, has_side_effects, prog_node) catch |err| term: { | |
| 980 | const result = spawnChildAndCollect(run, argv, has_side_effects, prog_node, fuzz_unit_test_index) catch |err| term: { | |
| 930 | 981 | // InvalidExe: cpu arch mismatch |
| 931 | 982 | // FileNotFound: can happen with a wrong dynamic linker path |
| 932 | 983 | if (err == error.InvalidExe or err == error.FileNotFound) interpret: { |
| ... | ... | @@ -1062,7 +1113,7 @@ fn runCommand( |
| 1062 | 1113 | |
| 1063 | 1114 | try Step.handleVerbose2(step.owner, cwd, run.env_map, interp_argv.items); |
| 1064 | 1115 | |
| 1065 | break :term spawnChildAndCollect(run, interp_argv.items, has_side_effects, prog_node) catch |e| { | |
| 1116 | break :term spawnChildAndCollect(run, interp_argv.items, has_side_effects, prog_node, fuzz_unit_test_index) catch |e| { | |
| 1066 | 1117 | if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped; |
| 1067 | 1118 | |
| 1068 | 1119 | return step.fail("unable to spawn interpreter {s}: {s}", .{ |
| ... | ... | @@ -1077,6 +1128,15 @@ fn runCommand( |
| 1077 | 1128 | step.result_duration_ns = result.elapsed_ns; |
| 1078 | 1129 | step.result_peak_rss = result.peak_rss; |
| 1079 | 1130 | step.test_results = result.stdio.test_results; |
| 1131 | if (result.stdio.test_metadata) |tm| | |
| 1132 | run.cached_test_metadata = tm.toCachedTestMetadata(); | |
| 1133 | ||
| 1134 | const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items; | |
| 1135 | ||
| 1136 | if (fuzz_unit_test_index != null) { | |
| 1137 | try step.handleChildProcessTerm(result.term, cwd, final_argv); | |
| 1138 | return; | |
| 1139 | } | |
| 1080 | 1140 | |
| 1081 | 1141 | // Capture stdout and stderr to GeneratedFile objects. |
| 1082 | 1142 | const Stream = struct { |
| ... | ... | @@ -1113,8 +1173,6 @@ fn runCommand( |
| 1113 | 1173 | } |
| 1114 | 1174 | } |
| 1115 | 1175 | |
| 1116 | const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items; | |
| 1117 | ||
| 1118 | 1176 | switch (run.stdio) { |
| 1119 | 1177 | .check => |checks| for (checks.items) |check| switch (check) { |
| 1120 | 1178 | .expect_stderr_exact => |expected_bytes| { |
| ... | ... | @@ -1240,10 +1298,16 @@ fn spawnChildAndCollect( |
| 1240 | 1298 | argv: []const []const u8, |
| 1241 | 1299 | has_side_effects: bool, |
| 1242 | 1300 | prog_node: std.Progress.Node, |
| 1301 | fuzz_unit_test_index: ?u32, | |
| 1243 | 1302 | ) !ChildProcResult { |
| 1244 | 1303 | const b = run.step.owner; |
| 1245 | 1304 | const arena = b.allocator; |
| 1246 | 1305 | |
| 1306 | if (fuzz_unit_test_index != null) { | |
| 1307 | assert(!has_side_effects); | |
| 1308 | assert(run.stdio == .zig_test); | |
| 1309 | } | |
| 1310 | ||
| 1247 | 1311 | var child = std.process.Child.init(argv, arena); |
| 1248 | 1312 | if (run.cwd) |lazy_cwd| { |
| 1249 | 1313 | child.cwd = lazy_cwd.getPath2(b, &run.step); |
| ... | ... | @@ -1293,7 +1357,7 @@ fn spawnChildAndCollect( |
| 1293 | 1357 | var timer = try std.time.Timer.start(); |
| 1294 | 1358 | |
| 1295 | 1359 | const result = if (run.stdio == .zig_test) |
| 1296 | evalZigTest(run, &child, prog_node) | |
| 1360 | evalZigTest(run, &child, prog_node, fuzz_unit_test_index) | |
| 1297 | 1361 | else |
| 1298 | 1362 | evalGeneric(run, &child); |
| 1299 | 1363 | |
| ... | ... | @@ -1319,6 +1383,7 @@ fn evalZigTest( |
| 1319 | 1383 | run: *Run, |
| 1320 | 1384 | child: *std.process.Child, |
| 1321 | 1385 | prog_node: std.Progress.Node, |
| 1386 | fuzz_unit_test_index: ?u32, | |
| 1322 | 1387 | ) !StdIoResult { |
| 1323 | 1388 | const gpa = run.step.owner.allocator; |
| 1324 | 1389 | const arena = run.step.owner.allocator; |
| ... | ... | @@ -1329,7 +1394,12 @@ fn evalZigTest( |
| 1329 | 1394 | }); |
| 1330 | 1395 | defer poller.deinit(); |
| 1331 | 1396 | |
| 1332 | try sendMessage(child.stdin.?, .query_test_metadata); | |
| 1397 | if (fuzz_unit_test_index) |index| { | |
| 1398 | try sendRunTestMessage(child.stdin.?, .start_fuzzing, index); | |
| 1399 | } else { | |
| 1400 | run.fuzz_tests.clearRetainingCapacity(); | |
| 1401 | try sendMessage(child.stdin.?, .query_test_metadata); | |
| 1402 | } | |
| 1333 | 1403 | |
| 1334 | 1404 | const Header = std.zig.Server.Message.Header; |
| 1335 | 1405 | |
| ... | ... | @@ -1367,6 +1437,7 @@ fn evalZigTest( |
| 1367 | 1437 | } |
| 1368 | 1438 | }, |
| 1369 | 1439 | .test_metadata => { |
| 1440 | assert(fuzz_unit_test_index == null); | |
| 1370 | 1441 | const TmHdr = std.zig.Server.Message.TestMetadata; |
| 1371 | 1442 | const tm_hdr = @as(*align(1) const TmHdr, @ptrCast(body)); |
| 1372 | 1443 | test_count = tm_hdr.tests_len; |
| ... | ... | @@ -1395,6 +1466,7 @@ fn evalZigTest( |
| 1395 | 1466 | try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); |
| 1396 | 1467 | }, |
| 1397 | 1468 | .test_results => { |
| 1469 | assert(fuzz_unit_test_index == null); | |
| 1398 | 1470 | const md = metadata.?; |
| 1399 | 1471 | |
| 1400 | 1472 | const TrHdr = std.zig.Server.Message.TestResults; |
| ... | ... | @@ -1404,6 +1476,8 @@ fn evalZigTest( |
| 1404 | 1476 | leak_count +|= @intFromBool(tr_hdr.flags.leak); |
| 1405 | 1477 | log_err_count +|= tr_hdr.flags.log_err_count; |
| 1406 | 1478 | |
| 1479 | if (tr_hdr.flags.fuzz) try run.fuzz_tests.append(gpa, tr_hdr.index); | |
| 1480 | ||
| 1407 | 1481 | if (tr_hdr.flags.fail or tr_hdr.flags.leak or tr_hdr.flags.log_err_count > 0) { |
| 1408 | 1482 | const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0); |
| 1409 | 1483 | const orig_msg = stderr.readableSlice(0); |
| ... | ... | @@ -1462,7 +1536,23 @@ const TestMetadata = struct { |
| 1462 | 1536 | next_index: u32, |
| 1463 | 1537 | prog_node: std.Progress.Node, |
| 1464 | 1538 | |
| 1539 | fn toCachedTestMetadata(tm: TestMetadata) CachedTestMetadata { | |
| 1540 | return .{ | |
| 1541 | .names = tm.names, | |
| 1542 | .string_bytes = tm.string_bytes, | |
| 1543 | }; | |
| 1544 | } | |
| 1545 | ||
| 1465 | 1546 | fn testName(tm: TestMetadata, index: u32) []const u8 { |
| 1547 | return tm.toCachedTestMetadata().testName(index); | |
| 1548 | } | |
| 1549 | }; | |
| 1550 | ||
| 1551 | pub const CachedTestMetadata = struct { | |
| 1552 | names: []const u32, | |
| 1553 | string_bytes: []const u8, | |
| 1554 | ||
| 1555 | pub fn testName(tm: CachedTestMetadata, index: u32) []const u8 { | |
| 1466 | 1556 | return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0); |
| 1467 | 1557 | } |
| 1468 | 1558 | }; |
| ... | ... | @@ -1478,7 +1568,7 @@ fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Pr |
| 1478 | 1568 | if (sub_prog_node.*) |n| n.end(); |
| 1479 | 1569 | sub_prog_node.* = metadata.prog_node.start(name, 0); |
| 1480 | 1570 | |
| 1481 | try sendRunTestMessage(in, i); | |
| 1571 | try sendRunTestMessage(in, .run_test, i); | |
| 1482 | 1572 | return; |
| 1483 | 1573 | } else { |
| 1484 | 1574 | try sendMessage(in, .exit); |
| ... | ... | @@ -1493,9 +1583,9 @@ fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { |
| 1493 | 1583 | try file.writeAll(std.mem.asBytes(&header)); |
| 1494 | 1584 | } |
| 1495 | 1585 | |
| 1496 | fn sendRunTestMessage(file: std.fs.File, index: u32) !void { | |
| 1586 | fn sendRunTestMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag, index: u32) !void { | |
| 1497 | 1587 | const header: std.zig.Client.Message.Header = .{ |
| 1498 | .tag = .run_test, | |
| 1588 | .tag = tag, | |
| 1499 | 1589 | .bytes_len = 4, |
| 1500 | 1590 | }; |
| 1501 | 1591 | const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index); |
lib/std/io/test.zig+1-1| ... | ... | @@ -16,7 +16,7 @@ test "write a file, read it, then delete it" { |
| 16 | 16 | defer tmp.cleanup(); |
| 17 | 17 | |
| 18 | 18 | var data: [1024]u8 = undefined; |
| 19 | var prng = DefaultPrng.init(1234); | |
| 19 | var prng = DefaultPrng.init(std.testing.random_seed); | |
| 20 | 20 | const random = prng.random(); |
| 21 | 21 | random.bytes(data[0..]); |
| 22 | 22 | const tmp_file_name = "temp_test_file.txt"; |
lib/std/testing.zig+8| ... | ... | @@ -1136,3 +1136,11 @@ pub fn refAllDeclsRecursive(comptime T: type) void { |
| 1136 | 1136 | _ = &@field(T, decl.name); |
| 1137 | 1137 | } |
| 1138 | 1138 | } |
| 1139 | ||
| 1140 | pub const FuzzInputOptions = struct { | |
| 1141 | corpus: []const []const u8 = &.{}, | |
| 1142 | }; | |
| 1143 | ||
| 1144 | pub inline fn fuzzInput(options: FuzzInputOptions) []const u8 { | |
| 1145 | return @import("root").fuzzInput(options); | |
| 1146 | } |
lib/std/zig/Client.zig+3| ... | ... | @@ -33,6 +33,9 @@ pub const Message = struct { |
| 33 | 33 | /// Ask the test runner to run a particular test. |
| 34 | 34 | /// The message body is a u32 test index. |
| 35 | 35 | run_test, |
| 36 | /// Ask the test runner to start fuzzing a particular test. | |
| 37 | /// The message body is a u32 test index. | |
| 38 | start_fuzzing, | |
| 36 | 39 | |
| 37 | 40 | _, |
| 38 | 41 | }; |
lib/std/zig/Server.zig+3-2| ... | ... | @@ -53,7 +53,7 @@ pub const Message = struct { |
| 53 | 53 | /// - null-terminated string_bytes index |
| 54 | 54 | /// * expected_panic_msg: [tests_len]u32, |
| 55 | 55 | /// - null-terminated string_bytes index |
| 56 | /// - 0 means does not expect pani | |
| 56 | /// - 0 means does not expect panic | |
| 57 | 57 | /// * string_bytes: [string_bytes_len]u8, |
| 58 | 58 | pub const TestMetadata = extern struct { |
| 59 | 59 | string_bytes_len: u32, |
| ... | ... | @@ -68,7 +68,8 @@ pub const Message = struct { |
| 68 | 68 | fail: bool, |
| 69 | 69 | skip: bool, |
| 70 | 70 | leak: bool, |
| 71 | log_err_count: u29 = 0, | |
| 71 | fuzz: bool, | |
| 72 | log_err_count: u28 = 0, | |
| 72 | 73 | }; |
| 73 | 74 | }; |
| 74 | 75 |
src/Compilation.zig+3-1| ... | ... | @@ -2180,7 +2180,9 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2180 | 2180 | comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts); |
| 2181 | 2181 | } |
| 2182 | 2182 | }, |
| 2183 | .incremental => {}, | |
| 2183 | .incremental => { | |
| 2184 | log.debug("Compilation.update for {s}, CacheMode.incremental", .{comp.root_name}); | |
| 2185 | }, | |
| 2184 | 2186 | } |
| 2185 | 2187 | |
| 2186 | 2188 | // From this point we add a preliminary set of file system inputs that |
src/codegen/llvm.zig+4-8| ... | ... | @@ -1392,16 +1392,12 @@ pub const Object = struct { |
| 1392 | 1392 | } |
| 1393 | 1393 | if (owner_mod.fuzz and !func_analysis.disable_instrumentation) { |
| 1394 | 1394 | try attributes.addFnAttr(.optforfuzzing, &o.builder); |
| 1395 | if (comp.config.any_fuzz) { | |
| 1396 | _ = try attributes.removeFnAttr(.skipprofile); | |
| 1397 | _ = try attributes.removeFnAttr(.nosanitize_coverage); | |
| 1398 | } | |
| 1395 | _ = try attributes.removeFnAttr(.skipprofile); | |
| 1396 | _ = try attributes.removeFnAttr(.nosanitize_coverage); | |
| 1399 | 1397 | } else { |
| 1400 | 1398 | _ = try attributes.removeFnAttr(.optforfuzzing); |
| 1401 | if (comp.config.any_fuzz) { | |
| 1402 | try attributes.addFnAttr(.skipprofile, &o.builder); | |
| 1403 | try attributes.addFnAttr(.nosanitize_coverage, &o.builder); | |
| 1404 | } | |
| 1399 | try attributes.addFnAttr(.skipprofile, &o.builder); | |
| 1400 | try attributes.addFnAttr(.nosanitize_coverage, &o.builder); | |
| 1405 | 1401 | } |
| 1406 | 1402 | |
| 1407 | 1403 | // TODO: disable this if safety is off for the function scope |
src/link/Elf.zig+2| ... | ... | @@ -2286,6 +2286,8 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s |
| 2286 | 2286 | } |
| 2287 | 2287 | try man.addOptionalFile(module_obj_path); |
| 2288 | 2288 | try man.addOptionalFile(compiler_rt_path); |
| 2289 | try man.addOptionalFile(if (comp.tsan_lib) |l| l.full_object_path else null); | |
| 2290 | try man.addOptionalFile(if (comp.fuzzer_lib) |l| l.full_object_path else null); | |
| 2289 | 2291 | |
| 2290 | 2292 | // We can skip hashing libc and libc++ components that we are in charge of building from Zig |
| 2291 | 2293 | // installation sources because they are always a product of the compiler version + target information. |
src/main.zig+5| ... | ... | @@ -655,6 +655,7 @@ const usage_build_generic = |
| 655 | 655 | \\ --debug-log [scope] Enable printing debug/info log messages for scope |
| 656 | 656 | \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error |
| 657 | 657 | \\ --debug-link-snapshot Enable dumping of the linker's state in JSON format |
| 658 | \\ --debug-rt Debug compiler runtime libraries | |
| 658 | 659 | \\ |
| 659 | 660 | ; |
| 660 | 661 | |
| ... | ... | @@ -912,6 +913,7 @@ fn buildOutputType( |
| 912 | 913 | var minor_subsystem_version: ?u16 = null; |
| 913 | 914 | var mingw_unicode_entry_point: bool = false; |
| 914 | 915 | var enable_link_snapshots: bool = false; |
| 916 | var debug_compiler_runtime_libs = false; | |
| 915 | 917 | var opt_incremental: ?bool = null; |
| 916 | 918 | var install_name: ?[]const u8 = null; |
| 917 | 919 | var hash_style: link.File.Elf.HashStyle = .both; |
| ... | ... | @@ -1367,6 +1369,8 @@ fn buildOutputType( |
| 1367 | 1369 | } else { |
| 1368 | 1370 | enable_link_snapshots = true; |
| 1369 | 1371 | } |
| 1372 | } else if (mem.eql(u8, arg, "--debug-rt")) { | |
| 1373 | debug_compiler_runtime_libs = true; | |
| 1370 | 1374 | } else if (mem.eql(u8, arg, "-fincremental")) { |
| 1371 | 1375 | dev.check(.incremental); |
| 1372 | 1376 | opt_incremental = true; |
| ... | ... | @@ -3408,6 +3412,7 @@ fn buildOutputType( |
| 3408 | 3412 | // noise when --search-prefix and --mod are combined. |
| 3409 | 3413 | .global_cc_argv = try cc_argv.toOwnedSlice(arena), |
| 3410 | 3414 | .file_system_inputs = &file_system_inputs, |
| 3415 | .debug_compiler_runtime_libs = debug_compiler_runtime_libs, | |
| 3411 | 3416 | }) catch |err| switch (err) { |
| 3412 | 3417 | error.LibCUnavailable => { |
| 3413 | 3418 | const triple_name = try target.zigTriple(arena); |