diff --git a/test/standalone.zig b/test/standalone.zig index cfdb09ea07149d7abfefd64dd1f03e1f8e8be45d..c9277e26a872d95a5ac3c52df0c7f6a0199d8d58 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -230,6 +230,10 @@ pub const build_cases = [_]BuildCase{ .build_root = "test/standalone/zerolength_check", .import = @import("standalone/zerolength_check/build.zig"), }, + .{ + .build_root = "test/standalone/dwarf_unwinding", + .import = @import("standalone/dwarf_unwinding/build.zig"), + }, }; const std = @import("std"); diff --git a/test/standalone/dwarf_unwinding/build.zig b/test/standalone/dwarf_unwinding/build.zig new file mode 100644 index 0000000000000000000000000000000000000000..85d0a9ac7d7a21cfeea5e1b69b77f4ff15ee79d9 --- /dev/null +++ b/test/standalone/dwarf_unwinding/build.zig @@ -0,0 +1,37 @@ +const std = @import("std"); + +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 (!std.debug.StackIterator.supports_context) return; + + const c_shared_lib = b.addSharedLibrary(.{ + .name = "c_shared_lib", + .target = target, + .optimize = optimize, + }); + + if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)"); + + c_shared_lib.strip = false; + c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"}); + c_shared_lib.linkLibC(); + + const exe = b.addExecutable(.{ + .name = "main", + .root_source_file = .{ .path = "main.zig" }, + .target = target, + .optimize = optimize, + }); + + exe.omit_frame_pointer = true; + exe.linkLibrary(c_shared_lib); + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + test_step.dependOn(&run_cmd.step); +} diff --git a/test/standalone/dwarf_unwinding/main.zig b/test/standalone/dwarf_unwinding/main.zig new file mode 100644 index 0000000000000000000000000000000000000000..2edf93ea9454eb815c79ac2255d3aae61a9c3c7f --- /dev/null +++ b/test/standalone/dwarf_unwinding/main.zig @@ -0,0 +1,40 @@ +const std = @import("std"); +const debug = std.debug; +const testing = std.testing; + +noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void { + expected[0] = @returnAddress(); + + var context: debug.StackTraceContext = undefined; + testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); + + var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); + var it = debug.StackIterator.initWithContext(null, 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: *[4]usize, unwound: *[4]usize) void { + expected[1] = @returnAddress(); + frame4(expected, unwound); +} + +fn frame2(expected: *[4]usize, unwound: *[4]usize) callconv(.C) void { + frame3(expected, unwound); +} + +extern fn frame0( + expected: *[4]usize, + unwound: *[4]usize, + frame_2: *const fn (expected: *[4]usize, unwound: *[4]usize) callconv(.C) void, +) void; + +pub fn main() !void { + var expected: [4]usize = undefined; + var unwound: [4]usize = undefined; + frame0(&expected, &unwound, &frame2); + try testing.expectEqual(expected, unwound); +} diff --git a/test/standalone/dwarf_unwinding/shared_lib.c b/test/standalone/dwarf_unwinding/shared_lib.c new file mode 100644 index 0000000000000000000000000000000000000000..2329fedda9b6545fc39f45b3ec4f1c8d43d49af1 --- /dev/null +++ b/test/standalone/dwarf_unwinding/shared_lib.c @@ -0,0 +1,24 @@ +#include + +#ifndef LIB_API +#define LIB_API +#endif + +__attribute__((noinline)) void frame1( + void** expected, + void** unwound, + void (*frame2)(void** expected, void** unwound)) { + expected[2] = &&frame_2_ret; + frame2(expected, unwound); + frame_2_ret: +} + +LIB_API void frame0( + void** expected, + void** unwound, + void (*frame2)(void** expected, void** unwound)) { + expected[3] = &&frame_1_ret; + frame1(expected, unwound, frame2); + frame_1_ret: +} +