aboutsummaryrefslogtreecommitdiff
path: root/test/src/StackTrace.zig
diff options
context:
space:
mode:
Diffstat (limited to 'test/src/StackTrace.zig')
-rw-r--r--test/src/StackTrace.zig215
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);
}