test: remove stack_iterator standalone test

Our new stack trace tests cover all the important parts of this.
This commit is contained in:
Alex Rønne Petersen 2025-10-01 01:02:58 +02:00
parent 5a71e15f1f
commit d97954a8ea
No known key found for this signature in database
6 changed files with 0 additions and 407 deletions

View file

@ -163,9 +163,6 @@
.zerolength_check = .{ .zerolength_check = .{
.path = "zerolength_check", .path = "zerolength_check",
}, },
.stack_iterator = .{
.path = "stack_iterator",
},
.coff_dwarf = .{ .coff_dwarf = .{
.path = "coff_dwarf", .path = "coff_dwarf",
}, },

View file

@ -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);
}
}
}

View file

@ -1,22 +0,0 @@
#include <stdint.h>
#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);
}

View file

@ -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);
}

View file

@ -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 });
}
}

View file

@ -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;
}