| author | |
| committer | |
| log | ccc9f8206898275474b3651a7456ec8b5c6210ff |
| tree | 43ace699338ba4277f76f23a7501be06f2191ec5 |
| parent | 7bc1695f15e83bb7e0e1401d2411660b05ab65e3 |
debug: supports_context -> have_ucontext, supports_getcontext -> have_getcontext
test: rework dwarf_unwind test case to also test the non-libc path6 files changed, 145 insertions(+), 86 deletions(-)
lib/std/c.zig+2-2| ... | ... | @@ -415,9 +415,9 @@ pub extern "c" fn timer_gettime(timerid: c.timer_t, flags: c_int, curr_value: *c |
| 415 | 415 | |
| 416 | 416 | pub usingnamespace if (builtin.os.tag == .linux and builtin.target.isMusl()) struct { |
| 417 | 417 | // musl does not implement getcontext |
| 418 | const getcontext = std.os.linux.getcontext; | |
| 418 | pub const getcontext = std.os.linux.getcontext; | |
| 419 | 419 | } else struct { |
| 420 | extern "c" fn getcontext(ucp: *std.os.ucontext_t) c_int; | |
| 420 | pub extern "c" fn getcontext(ucp: *std.os.ucontext_t) c_int; | |
| 421 | 421 | }; |
| 422 | 422 | |
| 423 | 423 | pub const max_align_t = if (builtin.abi == .msvc) |
lib/std/debug.zig+17-17| ... | ... | @@ -136,7 +136,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 136 | 136 | pub const StackTraceContext = blk: { |
| 137 | 137 | if (native_os == .windows) { |
| 138 | 138 | break :blk std.os.windows.CONTEXT; |
| 139 | } else if (StackIterator.supports_context) { | |
| 139 | } else if (have_ucontext) { | |
| 140 | 140 | break :blk os.ucontext_t; |
| 141 | 141 | } else { |
| 142 | 142 | break :blk void; |
| ... | ... | @@ -420,6 +420,18 @@ pub fn writeStackTrace( |
| 420 | 420 | } |
| 421 | 421 | } |
| 422 | 422 | |
| 423 | pub const have_getcontext = @hasDecl(os.system, "getcontext") and | |
| 424 | (builtin.os.tag != .linux or switch (builtin.cpu.arch) { | |
| 425 | .x86, .x86_64 => true, | |
| 426 | else => false, | |
| 427 | }); | |
| 428 | ||
| 429 | pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and | |
| 430 | (builtin.os.tag != .linux or switch (builtin.cpu.arch) { | |
| 431 | .mips, .mipsel, .mips64, .mips64el, .riscv64 => false, | |
| 432 | else => true, | |
| 433 | }); | |
| 434 | ||
| 423 | 435 | pub inline fn getContext(context: *StackTraceContext) bool { |
| 424 | 436 | if (native_os == .windows) { |
| 425 | 437 | context.* = std.mem.zeroes(windows.CONTEXT); |
| ... | ... | @@ -427,13 +439,7 @@ pub inline fn getContext(context: *StackTraceContext) bool { |
| 427 | 439 | return true; |
| 428 | 440 | } |
| 429 | 441 | |
| 430 | const supports_getcontext = @hasDecl(os.system, "getcontext") and | |
| 431 | (builtin.os.tag != .linux or switch (builtin.cpu.arch) { | |
| 432 | .x86, .x86_64 => true, | |
| 433 | else => false, | |
| 434 | }); | |
| 435 | ||
| 436 | return supports_getcontext and os.system.getcontext(context) == 0; | |
| 442 | return have_getcontext and os.system.getcontext(context) == 0; | |
| 437 | 443 | } |
| 438 | 444 | |
| 439 | 445 | pub const StackIterator = struct { |
| ... | ... | @@ -445,13 +451,7 @@ pub const StackIterator = struct { |
| 445 | 451 | // When DebugInfo and a register context is available, this iterator can unwind |
| 446 | 452 | // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer). |
| 447 | 453 | debug_info: ?*DebugInfo, |
| 448 | dwarf_context: if (supports_context) DW.UnwindContext else void = undefined, | |
| 449 | ||
| 450 | pub const supports_context = @hasDecl(os.system, "ucontext_t") and | |
| 451 | (builtin.os.tag != .linux or switch (builtin.cpu.arch) { | |
| 452 | .mips, .mipsel, .mips64, .mips64el, .riscv64 => false, | |
| 453 | else => true, | |
| 454 | }); | |
| 454 | dwarf_context: if (have_ucontext) DW.UnwindContext else void = undefined, | |
| 455 | 455 | |
| 456 | 456 | pub fn init(first_address: ?usize, fp: ?usize) StackIterator { |
| 457 | 457 | if (native_arch == .sparc64) { |
| ... | ... | @@ -476,7 +476,7 @@ pub const StackIterator = struct { |
| 476 | 476 | } |
| 477 | 477 | |
| 478 | 478 | pub fn deinit(self: *StackIterator) void { |
| 479 | if (supports_context) { | |
| 479 | if (have_ucontext) { | |
| 480 | 480 | if (self.debug_info) |debug_info| { |
| 481 | 481 | self.dwarf_context.deinit(debug_info.allocator); |
| 482 | 482 | } |
| ... | ... | @@ -574,7 +574,7 @@ pub const StackIterator = struct { |
| 574 | 574 | } |
| 575 | 575 | |
| 576 | 576 | fn next_internal(self: *StackIterator) ?usize { |
| 577 | if (supports_context and self.debug_info != null) { | |
| 577 | if (have_ucontext and self.debug_info != null) { | |
| 578 | 578 | if (self.dwarf_context.pc == 0) return null; |
| 579 | 579 | if (self.next_dwarf()) |return_address| { |
| 580 | 580 | return return_address; |
test/standalone/dwarf_unwinding/build.zig+42-27| ... | ... | @@ -7,31 +7,46 @@ pub fn build(b: *std.Build) void { |
| 7 | 7 | const target = b.standardTargetOptions(.{}); |
| 8 | 8 | const optimize = b.standardOptimizeOption(.{}); |
| 9 | 9 | |
| 10 | if (!std.debug.StackIterator.supports_context) return; | |
| 11 | ||
| 12 | const c_shared_lib = b.addSharedLibrary(.{ | |
| 13 | .name = "c_shared_lib", | |
| 14 | .target = target, | |
| 15 | .optimize = optimize, | |
| 16 | }); | |
| 17 | ||
| 18 | if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)"); | |
| 19 | ||
| 20 | c_shared_lib.strip = false; | |
| 21 | c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"}); | |
| 22 | c_shared_lib.linkLibC(); | |
| 23 | ||
| 24 | const exe = b.addExecutable(.{ | |
| 25 | .name = "main", | |
| 26 | .root_source_file = .{ .path = "main.zig" }, | |
| 27 | .target = target, | |
| 28 | .optimize = optimize, | |
| 29 | }); | |
| 30 | ||
| 31 | exe.omit_frame_pointer = true; | |
| 32 | exe.linkLibrary(c_shared_lib); | |
| 33 | b.installArtifact(exe); | |
| 34 | ||
| 35 | const run_cmd = b.addRunArtifact(exe); | |
| 36 | test_step.dependOn(&run_cmd.step); | |
| 10 | // Test unwinding pure zig code (no libc) | |
| 11 | { | |
| 12 | const exe = b.addExecutable(.{ | |
| 13 | .name = "zig_unwind", | |
| 14 | .root_source_file = .{ .path = "zig_unwind.zig" }, | |
| 15 | .target = target, | |
| 16 | .optimize = optimize, | |
| 17 | }); | |
| 18 | ||
| 19 | exe.omit_frame_pointer = true; | |
| 20 | ||
| 21 | const run_cmd = b.addRunArtifact(exe); | |
| 22 | test_step.dependOn(&run_cmd.step); | |
| 23 | } | |
| 24 | ||
| 25 | // Test unwinding through a C shared library | |
| 26 | { | |
| 27 | const c_shared_lib = b.addSharedLibrary(.{ | |
| 28 | .name = "c_shared_lib", | |
| 29 | .target = target, | |
| 30 | .optimize = optimize, | |
| 31 | }); | |
| 32 | ||
| 33 | if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)"); | |
| 34 | ||
| 35 | c_shared_lib.strip = false; | |
| 36 | c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"}); | |
| 37 | c_shared_lib.linkLibC(); | |
| 38 | ||
| 39 | const exe = b.addExecutable(.{ | |
| 40 | .name = "shared_lib_unwind", | |
| 41 | .root_source_file = .{ .path = "shared_lib_unwind.zig" }, | |
| 42 | .target = target, | |
| 43 | .optimize = optimize, | |
| 44 | }); | |
| 45 | ||
| 46 | exe.omit_frame_pointer = true; | |
| 47 | exe.linkLibrary(c_shared_lib); | |
| 48 | ||
| 49 | const run_cmd = b.addRunArtifact(exe); | |
| 50 | test_step.dependOn(&run_cmd.step); | |
| 51 | } | |
| 37 | 52 | } |
test/standalone/dwarf_unwinding/main.zig deleted-40| ... | ... | @@ -1,40 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const debug = std.debug; | |
| 3 | const testing = std.testing; | |
| 4 | ||
| 5 | noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void { | |
| 6 | expected[0] = @returnAddress(); | |
| 7 | ||
| 8 | var context: debug.StackTraceContext = undefined; | |
| 9 | testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); | |
| 10 | ||
| 11 | var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); | |
| 12 | var it = debug.StackIterator.initWithContext(null, debug_info, &context) catch @panic("failed to initWithContext"); | |
| 13 | defer it.deinit(); | |
| 14 | ||
| 15 | for (unwound) |*addr| { | |
| 16 | if (it.next()) |return_address| addr.* = return_address; | |
| 17 | } | |
| 18 | } | |
| 19 | ||
| 20 | noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { | |
| 21 | expected[1] = @returnAddress(); | |
| 22 | frame4(expected, unwound); | |
| 23 | } | |
| 24 | ||
| 25 | fn frame2(expected: *[4]usize, unwound: *[4]usize) callconv(.C) void { | |
| 26 | frame3(expected, unwound); | |
| 27 | } | |
| 28 | ||
| 29 | extern fn frame0( | |
| 30 | expected: *[4]usize, | |
| 31 | unwound: *[4]usize, | |
| 32 | frame_2: *const fn (expected: *[4]usize, unwound: *[4]usize) callconv(.C) void, | |
| 33 | ) void; | |
| 34 | ||
| 35 | pub fn main() !void { | |
| 36 | var expected: [4]usize = undefined; | |
| 37 | var unwound: [4]usize = undefined; | |
| 38 | frame0(&expected, &unwound, &frame2); | |
| 39 | try testing.expectEqual(expected, unwound); | |
| 40 | } |
test/standalone/dwarf_unwinding/shared_lib_unwind.zig created+42| ... | ... | @@ -0,0 +1,42 @@ |
| 1 | const std = @import("std"); | |
| 2 | const debug = std.debug; | |
| 3 | const testing = std.testing; | |
| 4 | ||
| 5 | noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void { | |
| 6 | expected[0] = @returnAddress(); | |
| 7 | ||
| 8 | var context: debug.StackTraceContext = undefined; | |
| 9 | testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); | |
| 10 | ||
| 11 | var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); | |
| 12 | var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext"); | |
| 13 | defer it.deinit(); | |
| 14 | ||
| 15 | for (unwound) |*addr| { | |
| 16 | if (it.next()) |return_address| addr.* = return_address; | |
| 17 | } | |
| 18 | } | |
| 19 | ||
| 20 | noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { | |
| 21 | expected[1] = @returnAddress(); | |
| 22 | frame4(expected, unwound); | |
| 23 | } | |
| 24 | ||
| 25 | fn frame2(expected: *[4]usize, unwound: *[4]usize) callconv(.C) void { | |
| 26 | frame3(expected, unwound); | |
| 27 | } | |
| 28 | ||
| 29 | extern fn frame0( | |
| 30 | expected: *[4]usize, | |
| 31 | unwound: *[4]usize, | |
| 32 | frame_2: *const fn (expected: *[4]usize, unwound: *[4]usize) callconv(.C) void, | |
| 33 | ) void; | |
| 34 | ||
| 35 | pub fn main() !void { | |
| 36 | if (!std.debug.have_ucontext or !std.debug.have_getcontext) return; | |
| 37 | ||
| 38 | var expected: [4]usize = undefined; | |
| 39 | var unwound: [4]usize = undefined; | |
| 40 | frame0(&expected, &unwound, &frame2); | |
| 41 | try testing.expectEqual(expected, unwound); | |
| 42 | } |
test/standalone/dwarf_unwinding/zig_unwind.zig created+42| ... | ... | @@ -0,0 +1,42 @@ |
| 1 | const std = @import("std"); | |
| 2 | const debug = std.debug; | |
| 3 | const testing = std.testing; | |
| 4 | ||
| 5 | noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { | |
| 6 | expected[0] = @returnAddress(); | |
| 7 | ||
| 8 | var context: debug.StackTraceContext = undefined; | |
| 9 | testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); | |
| 10 | ||
| 11 | var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); | |
| 12 | var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext"); | |
| 13 | defer it.deinit(); | |
| 14 | ||
| 15 | for (unwound) |*addr| { | |
| 16 | if (it.next()) |return_address| addr.* = return_address; | |
| 17 | } | |
| 18 | } | |
| 19 | ||
| 20 | noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void { | |
| 21 | expected[1] = @returnAddress(); | |
| 22 | frame3(expected, unwound); | |
| 23 | } | |
| 24 | ||
| 25 | noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void { | |
| 26 | expected[2] = @returnAddress(); | |
| 27 | frame2(expected, unwound); | |
| 28 | } | |
| 29 | ||
| 30 | noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void { | |
| 31 | expected[3] = @returnAddress(); | |
| 32 | frame1(expected, unwound); | |
| 33 | } | |
| 34 | ||
| 35 | pub fn main() !void { | |
| 36 | if (!std.debug.have_ucontext or !std.debug.have_getcontext) return; | |
| 37 | ||
| 38 | var expected: [4]usize = undefined; | |
| 39 | var unwound: [4]usize = undefined; | |
| 40 | frame0(&expected, &unwound); | |
| 41 | try testing.expectEqual(expected, unwound); | |
| 42 | } |