From d97954a8ea1ecc48916f234a429e5cd6a2e38310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 1 Oct 2025 01:02:58 +0200 Subject: [PATCH] test: remove stack_iterator standalone test Our new stack trace tests cover all the important parts of this. --- test/standalone/build.zig.zon | 3 - test/standalone/stack_iterator/build.zig | 167 ------------------ test/standalone/stack_iterator/shared_lib.c | 22 --- .../stack_iterator/shared_lib_unwind.zig | 48 ----- test/standalone/stack_iterator/unwind.zig | 101 ----------- .../stack_iterator/unwind_freestanding.zig | 66 ------- 6 files changed, 407 deletions(-) delete mode 100644 test/standalone/stack_iterator/build.zig delete mode 100644 test/standalone/stack_iterator/shared_lib.c delete mode 100644 test/standalone/stack_iterator/shared_lib_unwind.zig delete mode 100644 test/standalone/stack_iterator/unwind.zig delete mode 100644 test/standalone/stack_iterator/unwind_freestanding.zig diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index 981e69d915..79164fe7e0 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -163,9 +163,6 @@ .zerolength_check = .{ .path = "zerolength_check", }, - .stack_iterator = .{ - .path = "stack_iterator", - }, .coff_dwarf = .{ .path = "coff_dwarf", }, diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig deleted file mode 100644 index b6ac2a9aee..0000000000 --- a/test/standalone/stack_iterator/build.zig +++ /dev/null @@ -1,167 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); - - if (target.result.cpu.arch.isRISCV() and target.result.os.tag == .linux) { - // https://github.com/ziglang/zig/issues/24310 - return; - } - - // Unwinding with a frame pointer - // - // getcontext version: zig std - // - // Unwind info type: - // - ELF: DWARF .debug_frame - // - MachO: __unwind_info encodings: - // - x86_64: RBP_FRAME - // - aarch64: FRAME, DWARF - { - const exe = b.addExecutable(.{ - .name = "unwind_fp", - .root_module = b.createModule(.{ - .root_source_file = b.path("unwind.zig"), - .target = target, - .optimize = optimize, - .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null, - .omit_frame_pointer = false, - }), - }); - - const run_cmd = b.addRunArtifact(exe); - test_step.dependOn(&run_cmd.step); - } - - // Unwinding without a frame pointer - // - // getcontext version: zig std - // - // Unwind info type: - // - ELF: DWARF .eh_frame_hdr + .eh_frame - // - MachO: __unwind_info encodings: - // - x86_64: STACK_IMMD, STACK_IND - // - aarch64: FRAMELESS, DWARF - { - const exe = b.addExecutable(.{ - .name = "unwind_nofp", - .root_module = b.createModule(.{ - .root_source_file = b.path("unwind.zig"), - .target = target, - .optimize = optimize, - .unwind_tables = .async, - .omit_frame_pointer = true, - }), - // self-hosted lacks omit_frame_pointer support - .use_llvm = true, - }); - - if (builtin.os.tag != .freebsd) { - const run_cmd = b.addRunArtifact(exe); - test_step.dependOn(&run_cmd.step); - } else { - test_step.dependOn(&exe.step); - } - } - - // https://github.com/ziglang/zig/issues/24522 - //// Unwinding through a C shared library without a frame pointer (libc) - //// - //// getcontext version: libc - //// - //// Unwind info type: - //// - ELF: DWARF .eh_frame + .debug_frame - //// - MachO: __unwind_info encodings: - //// - x86_64: STACK_IMMD, STACK_IND - //// - aarch64: FRAMELESS, DWARF - //{ - // const c_shared_lib = b.addLibrary(.{ - // .linkage = .dynamic, - // .name = "c_shared_lib", - // .root_module = b.createModule(.{ - // .root_source_file = null, - // .target = target, - // .optimize = optimize, - // .link_libc = true, - // .strip = false, - // }), - // }); - - // if (target.result.os.tag == .windows) - // c_shared_lib.root_module.addCMacro("LIB_API", "__declspec(dllexport)"); - - // c_shared_lib.root_module.addCSourceFile(.{ - // .file = b.path("shared_lib.c"), - // .flags = &.{"-fomit-frame-pointer"}, - // }); - - // const exe = b.addExecutable(.{ - // .name = "shared_lib_unwind", - // .root_module = b.createModule(.{ - // .root_source_file = b.path("shared_lib_unwind.zig"), - // .target = target, - // .optimize = optimize, - // .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null, - // .omit_frame_pointer = true, - // }), - // // zig objcopy doesn't support incremental binaries - // .use_llvm = true, - // }); - - // exe.root_module.linkLibrary(c_shared_lib); - - // const run_cmd = b.addRunArtifact(exe); - // test_step.dependOn(&run_cmd.step); - - // // Separate debug info ELF file - // if (target.result.ofmt == .elf) { - // const filename = b.fmt("{s}_stripped", .{exe.out_filename}); - // const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{ - // .basename = filename, // set the name for the debuglink - // .compress_debug = true, - // .strip = .debug, - // .extract_to_separate_file = true, - // }); - - // const run_stripped = std.Build.Step.Run.create(b, b.fmt("run {s}", .{filename})); - // run_stripped.addFileArg(stripped_exe.getOutput()); - // test_step.dependOn(&run_stripped.step); - // } - //} - - // Unwinding without libc/posix - // - // No "getcontext" or "ucontext_t" - const no_os_targets = [_]std.Target.Os.Tag{ .freestanding, .other }; - inline for (no_os_targets) |os_tag| { - const exe = b.addExecutable(.{ - .name = "unwind_freestanding", - .root_module = b.createModule(.{ - .root_source_file = b.path("unwind_freestanding.zig"), - .target = b.resolveTargetQuery(.{ - .cpu_arch = .x86_64, - .os_tag = os_tag, - }), - .optimize = optimize, - .unwind_tables = null, - .omit_frame_pointer = false, - }), - // self-hosted lacks omit_frame_pointer support - .use_llvm = true, - }); - - // This "freestanding" binary is runnable because it invokes the - // Linux exit syscall directly. - if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) { - const run_cmd = b.addRunArtifact(exe); - test_step.dependOn(&run_cmd.step); - } else { - test_step.dependOn(&exe.step); - } - } -} diff --git a/test/standalone/stack_iterator/shared_lib.c b/test/standalone/stack_iterator/shared_lib.c deleted file mode 100644 index c3170f2dc0..0000000000 --- a/test/standalone/stack_iterator/shared_lib.c +++ /dev/null @@ -1,22 +0,0 @@ -#include - -#ifndef LIB_API -#define LIB_API -#endif - -__attribute__((noinline)) void frame1( - void** expected, - void** unwound, - void (*frame2)(void** expected, void** unwound)) { - expected[3] = __builtin_extract_return_addr(__builtin_return_address(0)); - frame2(expected, unwound); -} - -LIB_API void frame0( - void** expected, - void** unwound, - void (*frame2)(void** expected, void** unwound)) { - expected[4] = __builtin_extract_return_addr(__builtin_return_address(0)); - frame1(expected, unwound, frame2); -} - diff --git a/test/standalone/stack_iterator/shared_lib_unwind.zig b/test/standalone/stack_iterator/shared_lib_unwind.zig deleted file mode 100644 index 2f933e5075..0000000000 --- a/test/standalone/stack_iterator/shared_lib_unwind.zig +++ /dev/null @@ -1,48 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const debug = std.debug; -const testing = std.testing; - -noinline fn frame4(expected: *[5]usize, unwound: *[5]usize) void { - expected[0] = @returnAddress(); - - var context: debug.ThreadContext = undefined; - testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); - - const debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); - var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext"); - defer it.deinit(); - - for (unwound) |*addr| { - if (it.next()) |return_address| addr.* = return_address; - } -} - -noinline fn frame3(expected: *[5]usize, unwound: *[5]usize) void { - expected[1] = @returnAddress(); - frame4(expected, unwound); -} - -fn frame2(expected: *[5]usize, unwound: *[5]usize) callconv(.c) void { - expected[2] = @returnAddress(); - frame3(expected, unwound); -} - -extern fn frame0( - expected: *[5]usize, - unwound: *[5]usize, - frame_2: *const fn (expected: *[5]usize, unwound: *[5]usize) callconv(.c) void, -) void; - -pub fn main() !void { - // Disabled until the DWARF unwinder bugs on .aarch64 are solved - if (builtin.omit_frame_pointer and comptime builtin.target.os.tag.isDarwin() and builtin.cpu.arch == .aarch64) return; - if (builtin.target.os.tag.isDarwin() and builtin.cpu.arch == .x86_64) return; // https://github.com/ziglang/zig/issues/21337 - - if (!std.debug.have_ucontext or !std.debug.have_getcontext) return; - - var expected: [5]usize = undefined; - var unwound: [5]usize = undefined; - frame0(&expected, &unwound, &frame2); - try testing.expectEqual(expected, unwound); -} diff --git a/test/standalone/stack_iterator/unwind.zig b/test/standalone/stack_iterator/unwind.zig deleted file mode 100644 index 69a766f58b..0000000000 --- a/test/standalone/stack_iterator/unwind.zig +++ /dev/null @@ -1,101 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const fatal = std.process.fatal; - -noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { - expected[0] = @returnAddress(); - return std.debug.captureCurrentStackTrace(.{ - .first_address = @returnAddress(), - .allow_unsafe_unwind = true, - }, addr_buf); -} - -noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { - // Exercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored - if (builtin.target.ofmt != .c) { - switch (builtin.target.cpu.arch) { - .x86 => { - if (builtin.omit_frame_pointer) { - asm volatile ( - \\movl $3, %%ebx - \\movl $1, %%ecx - \\movl $2, %%edx - \\movl $7, %%edi - \\movl $6, %%esi - \\movl $5, %%ebp - ::: .{ .ebx = true, .ecx = true, .edx = true, .edi = true, .esi = true, .ebp = true }); - } else { - asm volatile ( - \\movl $3, %%ebx - \\movl $1, %%ecx - \\movl $2, %%edx - \\movl $7, %%edi - \\movl $6, %%esi - ::: .{ .ebx = true, .ecx = true, .edx = true, .edi = true, .esi = true }); - } - }, - .x86_64 => { - if (builtin.omit_frame_pointer) { - asm volatile ( - \\movq $3, %%rbx - \\movq $12, %%r12 - \\movq $13, %%r13 - \\movq $14, %%r14 - \\movq $15, %%r15 - \\movq $6, %%rbp - ::: .{ .rbx = true, .r12 = true, .r13 = true, .r14 = true, .r15 = true, .rbp = true }); - } else { - asm volatile ( - \\movq $3, %%rbx - \\movq $12, %%r12 - \\movq $13, %%r13 - \\movq $14, %%r14 - \\movq $15, %%r15 - ::: .{ .rbx = true, .r12 = true, .r13 = true, .r14 = true, .r15 = true }); - } - }, - else => {}, - } - } - - expected[1] = @returnAddress(); - return frame3(expected, addr_buf); -} - -noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { - expected[2] = @returnAddress(); - - // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding - // to exercise the stack-indirect encoding path - var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined; - _ = std.mem.doNotOptimizeAway(&pad); - - return frame2(expected, addr_buf); -} - -noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { - expected[3] = @returnAddress(); - return frame1(expected, addr_buf); -} - -pub fn main() void { - if (std.debug.cpu_context.Native == noreturn and builtin.omit_frame_pointer) { - // Stack unwinding is impossible. - return; - } - - var expected: [4]usize = undefined; - var addr_buf: [4]usize = undefined; - const trace = frame0(&expected, &addr_buf); - // There may be *more* than 4 frames (due to the caller of `main`); that's okay. - if (trace.index < 4) { - fatal("expected at least 4 frames, got '{d}':\n{f}", .{ trace.index, &trace }); - } - if (!std.mem.eql(usize, trace.instruction_addresses, &expected)) { - const expected_trace: std.builtin.StackTrace = .{ - .index = 4, - .instruction_addresses = &expected, - }; - fatal("expected trace:\n{f}\nactual trace:\n{f}", .{ &expected_trace, &trace }); - } -} diff --git a/test/standalone/stack_iterator/unwind_freestanding.zig b/test/standalone/stack_iterator/unwind_freestanding.zig deleted file mode 100644 index 866f73d9bd..0000000000 --- a/test/standalone/stack_iterator/unwind_freestanding.zig +++ /dev/null @@ -1,66 +0,0 @@ -//! Test StackIterator on 'freestanding' target. Based on unwind.zig. - -const std = @import("std"); - -noinline fn frame3(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { - expected[0] = @returnAddress(); - return std.debug.captureCurrentStackTrace(.{ - .first_address = @returnAddress(), - .allow_unsafe_unwind = true, - }, addr_buf); -} - -noinline fn frame2(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { - expected[1] = @returnAddress(); - return frame3(expected, addr_buf); -} - -noinline fn frame1(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { - expected[2] = @returnAddress(); - - // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding - // to exercise the stack-indirect encoding path - var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined; - _ = std.mem.doNotOptimizeAway(&pad); - - return frame2(expected, addr_buf); -} - -noinline fn frame0(expected: *[4]usize, addr_buf: *[4]usize) std.builtin.StackTrace { - expected[3] = @returnAddress(); - return frame1(expected, addr_buf); -} - -/// No-OS entrypoint -export fn _start() callconv(.withStackAlign(.c, 1)) noreturn { - var expected: [4]usize = undefined; - var addr_buf: [4]usize = undefined; - const trace = frame0(&expected, &addr_buf); - - // Since we can't print from this freestanding test, we'll just use the exit - // code to communicate error conditions. - - // Unlike `unwind.zig`, we can expect *exactly* 4 frames, as we are the - // actual entry point and the frame pointer will be 0 on entry. - if (trace.index != 4) exit(1); - if (trace.instruction_addresses[0] != expected[0]) exit(2); - if (trace.instruction_addresses[1] != expected[1]) exit(3); - if (trace.instruction_addresses[2] != expected[2]) exit(4); - if (trace.instruction_addresses[3] != expected[3]) exit(5); - - exit(0); -} - -fn exit(code: u8) noreturn { - // We are intentionally compiling with the target OS being "freestanding" to - // exercise std.debug, but we still need to exit the process somehow; so do - // the appropriate x86_64-linux syscall. - asm volatile ( - \\movl $60, %%eax - \\syscall - : - : [code] "{edi}" (code), - : .{ .edi = true, .eax = true }); - - unreachable; -}