diff options
Diffstat (limited to 'test/src/StackTrace.zig')
| -rw-r--r-- | test/src/StackTrace.zig | 215 |
1 files changed, 159 insertions, 56 deletions
diff --git a/test/src/StackTrace.zig b/test/src/StackTrace.zig index 9b51f4e4b2..3d35a4f935 100644 --- a/test/src/StackTrace.zig +++ b/test/src/StackTrace.zig @@ -1,75 +1,171 @@ b: *std.Build, step: *Step, -test_index: usize, test_filters: []const []const u8, -optimize_modes: []const OptimizeMode, -check_exe: *std.Build.Step.Compile, +targets: []const std.Build.ResolvedTarget, +convert_exe: *std.Build.Step.Compile, const Config = struct { name: []const u8, source: []const u8, - Debug: ?PerMode = null, - ReleaseSmall: ?PerMode = null, - ReleaseSafe: ?PerMode = null, - ReleaseFast: ?PerMode = null, - - const PerMode = struct { - expect: []const u8, - exclude_arch: []const std.Target.Cpu.Arch = &.{}, - exclude_os: []const std.Target.Os.Tag = &.{}, - error_tracing: ?bool = null, - }; + /// Whether this test case expects to have unwind tables / frame pointers. + unwind: enum { + /// This case assumes that some unwind strategy, safe or unsafe, is available. + any, + /// This case assumes that no unwinding strategy is available. + none, + /// This case assumes that a safe unwind strategy, like DWARF unwinding, is available. + safe, + /// This case assumes that at most, unsafe FP unwinding is available. + no_safe, + }, + /// If `true`, the expected exit code is that of the default panic handler, rather than 0. + expect_panic: bool, + /// When debug info is not stripped, stdout is expected to **contain** (not equal!) this string. + expect: []const u8, + /// When debug info *is* stripped, stdout is expected to **contain** (not equal!) this string. + expect_strip: []const u8, }; pub fn addCase(self: *StackTrace, config: Config) void { - self.addCaseInner(config, true); - if (shouldTestNonLlvm(&self.b.graph.host.result)) { - self.addCaseInner(config, false); + for (self.targets) |*target| { + addCaseTarget( + self, + config, + target, + if (target.query.isNative()) null else t: { + break :t target.query.zigTriple(self.b.graph.arena) catch @panic("OOM"); + }, + ); } } - -fn addCaseInner(self: *StackTrace, config: Config, use_llvm: bool) void { - if (config.Debug) |per_mode| - self.addExpect(config.name, config.source, .Debug, use_llvm, per_mode); - - if (config.ReleaseSmall) |per_mode| - self.addExpect(config.name, config.source, .ReleaseSmall, use_llvm, per_mode); - - if (config.ReleaseFast) |per_mode| - self.addExpect(config.name, config.source, .ReleaseFast, use_llvm, per_mode); - - if (config.ReleaseSafe) |per_mode| - self.addExpect(config.name, config.source, .ReleaseSafe, use_llvm, per_mode); -} - -fn shouldTestNonLlvm(target: *const std.Target) bool { - return switch (target.cpu.arch) { - .x86_64 => switch (target.ofmt) { - .elf => !target.os.tag.isBSD(), +fn addCaseTarget( + self: *StackTrace, + config: Config, + target: *const std.Build.ResolvedTarget, + triple: ?[]const u8, +) void { + const both_backends = switch (target.result.cpu.arch) { + .x86_64 => switch (target.result.ofmt) { + .elf => true, else => false, }, else => false, }; + const both_pie = switch (target.result.os.tag) { + .fuchsia, .openbsd => false, + else => true, + }; + const both_libc = switch (target.result.os.tag) { + .freebsd, .netbsd => false, + else => !target.result.requiresLibC(), + }; + + // On aarch64-macos, FP unwinding is blessed by Apple to always be reliable, and std.debug knows this. + const fp_unwind_is_safe = target.result.cpu.arch == .aarch64 and target.result.os.tag.isDarwin(); + const supports_unwind_tables = switch (target.result.os.tag) { + // x86-windows just has no way to do stack unwinding other then using frame pointers. + .windows => target.result.cpu.arch != .x86, + // We do not yet implement support for the AArch32 exception table section `.ARM.exidx`. + else => !target.result.cpu.arch.isArm(), + }; + + const use_llvm_vals: []const bool = if (both_backends) &.{ true, false } else &.{true}; + const pie_vals: []const ?bool = if (both_pie) &.{ true, false } else &.{null}; + const link_libc_vals: []const ?bool = if (both_libc) &.{ true, false } else &.{null}; + const strip_debug_vals: []const bool = &.{ true, false }; + + const UnwindInfo = packed struct(u2) { + tables: bool, + fp: bool, + const none: @This() = .{ .tables = false, .fp = false }; + const both: @This() = .{ .tables = true, .fp = true }; + const only_tables: @This() = .{ .tables = true, .fp = false }; + const only_fp: @This() = .{ .tables = false, .fp = true }; + }; + const unwind_info_vals: []const UnwindInfo = switch (config.unwind) { + .none => &.{.none}, + .any => &.{ .only_tables, .only_fp, .both }, + .safe => if (fp_unwind_is_safe) &.{ .only_tables, .only_fp, .both } else &.{ .only_tables, .both }, + .no_safe => if (fp_unwind_is_safe) &.{.none} else &.{ .none, .only_fp }, + }; + + for (use_llvm_vals) |use_llvm| { + for (pie_vals) |pie| { + for (link_libc_vals) |link_libc| { + for (strip_debug_vals) |strip_debug| { + for (unwind_info_vals) |unwind_info| { + if (unwind_info.tables and !supports_unwind_tables) continue; + self.addCaseInstance( + target, + triple, + config.name, + config.source, + use_llvm, + pie, + link_libc, + strip_debug, + !unwind_info.tables and supports_unwind_tables, + !unwind_info.fp, + config.expect_panic, + if (strip_debug) config.expect_strip else config.expect, + ); + } + } + } + } + } } -fn addExpect( +fn addCaseInstance( self: *StackTrace, + target: *const std.Build.ResolvedTarget, + triple: ?[]const u8, name: []const u8, source: []const u8, - optimize_mode: OptimizeMode, use_llvm: bool, - mode_config: Config.PerMode, + pie: ?bool, + link_libc: ?bool, + strip_debug: bool, + strip_unwind: bool, + omit_frame_pointer: bool, + expect_panic: bool, + expect_stderr: []const u8, ) void { - for (mode_config.exclude_arch) |tag| if (tag == builtin.cpu.arch) return; - for (mode_config.exclude_os) |tag| if (tag == builtin.os.tag) return; - const b = self.b; - const annotated_case_name = b.fmt("check {s} ({s} {s})", .{ - name, @tagName(optimize_mode), if (use_llvm) "llvm" else "selfhosted", + + if (strip_debug) { + // To enable this coverage, one of two things needs to happen: + // * The compiler needs to gain the ability to strip only debug info (not symbols) + // * `std.Build.Step.ObjCopy` needs to be un-regressed + return; + } + + if (strip_unwind) { + // To enable this coverage, `std.Build.Step.ObjCopy` needs to be un-regressed and gain the + // ability to remove individual sections. `-fno-unwind-tables` is insufficient because it + // does not prevent `.debug_frame` from being emitted. If we could, we would remove the + // following sections: + // * `.eh_frame`, `.eh_frame_hdr`, `.debug_frame` (Linux) + // * `__TEXT,__eh_frame`, `__TEXT,__unwind_info` (macOS) + return; + } + + const annotated_case_name = b.fmt("check {s} ({s}{s}{s}{s}{s}{s}{s}{s})", .{ + name, + triple orelse "", + if (triple != null) " " else "", + if (use_llvm) "llvm" else "selfhosted", + if (pie == true) " pie" else "", + if (link_libc == true) " libc" else "", + if (strip_debug) " strip" else "", + if (strip_unwind) " no_unwind" else "", + if (omit_frame_pointer) " no_fp" else "", }); - for (self.test_filters) |test_filter| { - if (mem.indexOf(u8, annotated_case_name, test_filter)) |_| break; - } else if (self.test_filters.len > 0) return; + if (self.test_filters.len > 0) { + for (self.test_filters) |test_filter| { + if (mem.indexOf(u8, annotated_case_name, test_filter)) |_| break; + } else return; + } const write_files = b.addWriteFiles(); const source_zig = write_files.add("source.zig", source); @@ -77,27 +173,34 @@ fn addExpect( .name = "test", .root_module = b.createModule(.{ .root_source_file = source_zig, - .optimize = optimize_mode, - .target = b.graph.host, - .error_tracing = mode_config.error_tracing, + .optimize = .Debug, + .target = target.*, + .omit_frame_pointer = omit_frame_pointer, + .link_libc = link_libc, + .unwind_tables = if (strip_unwind) .none else null, + // make panics single-threaded so that they don't include a thread ID + .single_threaded = expect_panic, }), .use_llvm = use_llvm, }); + exe.pie = pie; exe.bundle_ubsan_rt = false; const run = b.addRunArtifact(exe); run.removeEnvironmentVariable("CLICOLOR_FORCE"); run.setEnvironmentVariable("NO_COLOR", "1"); - run.expectExitCode(1); + run.addCheck(.{ .expect_term = term: { + if (!expect_panic) break :term .{ .Exited = 0 }; + if (target.result.os.tag == .windows) break :term .{ .Exited = 3 }; + break :term .{ .Signal = 6 }; + } }); run.expectStdOutEqual(""); - const check_run = b.addRunArtifact(self.check_exe); + const check_run = b.addRunArtifact(self.convert_exe); check_run.setName(annotated_case_name); check_run.addFileArg(run.captureStdErr(.{})); - check_run.addArgs(&.{ - @tagName(optimize_mode), - }); - check_run.expectStdOutEqual(mode_config.expect); + check_run.expectExitCode(0); + check_run.addCheck(.{ .expect_stdout_match = expect_stderr }); self.step.dependOn(&check_run.step); } |
