authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-01 15:23:37-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:14-04:00
logccc9f8206898275474b3651a7456ec8b5c6210ff
tree43ace699338ba4277f76f23a7501be06f2191ec5
parent7bc1695f15e83bb7e0e1401d2411660b05ab65e3

c: fixup getcontext

debug: supports_context -> have_ucontext, supports_getcontext -> have_getcontext test: rework dwarf_unwind test case to also test the non-libc path

6 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,9 +415,9 @@ pub extern "c" fn timer_gettime(timerid: c.timer_t, flags: c_int, curr_value: *c
415415
416pub usingnamespace if (builtin.os.tag == .linux and builtin.target.isMusl()) struct {416pub usingnamespace if (builtin.os.tag == .linux and builtin.target.isMusl()) struct {
417 // musl does not implement getcontext417 // musl does not implement getcontext
418 const getcontext = std.os.linux.getcontext;418 pub const getcontext = std.os.linux.getcontext;
419} else struct {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};
422422
423pub const max_align_t = if (builtin.abi == .msvc)423pub 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,7 +136,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
136pub const StackTraceContext = blk: {136pub const StackTraceContext = blk: {
137 if (native_os == .windows) {137 if (native_os == .windows) {
138 break :blk std.os.windows.CONTEXT;138 break :blk std.os.windows.CONTEXT;
139 } else if (StackIterator.supports_context) {139 } else if (have_ucontext) {
140 break :blk os.ucontext_t;140 break :blk os.ucontext_t;
141 } else {141 } else {
142 break :blk void;142 break :blk void;
...@@ -420,6 +420,18 @@ pub fn writeStackTrace(...@@ -420,6 +420,18 @@ pub fn writeStackTrace(
420 }420 }
421}421}
422422
423pub 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
429pub 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
423pub inline fn getContext(context: *StackTraceContext) bool {435pub inline fn getContext(context: *StackTraceContext) bool {
424 if (native_os == .windows) {436 if (native_os == .windows) {
425 context.* = std.mem.zeroes(windows.CONTEXT);437 context.* = std.mem.zeroes(windows.CONTEXT);
...@@ -427,13 +439,7 @@ pub inline fn getContext(context: *StackTraceContext) bool {...@@ -427,13 +439,7 @@ pub inline fn getContext(context: *StackTraceContext) bool {
427 return true;439 return true;
428 }440 }
429441
430 const supports_getcontext = @hasDecl(os.system, "getcontext") and442 return have_getcontext and os.system.getcontext(context) == 0;
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;
437}443}
438444
439pub const StackIterator = struct {445pub const StackIterator = struct {
...@@ -445,13 +451,7 @@ pub const StackIterator = struct {...@@ -445,13 +451,7 @@ pub const StackIterator = struct {
445 // When DebugInfo and a register context is available, this iterator can unwind451 // When DebugInfo and a register context is available, this iterator can unwind
446 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer).452 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer).
447 debug_info: ?*DebugInfo,453 debug_info: ?*DebugInfo,
448 dwarf_context: if (supports_context) DW.UnwindContext else void = undefined,454 dwarf_context: if (have_ucontext) 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 });
455455
456 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {456 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
457 if (native_arch == .sparc64) {457 if (native_arch == .sparc64) {
...@@ -476,7 +476,7 @@ pub const StackIterator = struct {...@@ -476,7 +476,7 @@ pub const StackIterator = struct {
476 }476 }
477477
478 pub fn deinit(self: *StackIterator) void {478 pub fn deinit(self: *StackIterator) void {
479 if (supports_context) {479 if (have_ucontext) {
480 if (self.debug_info) |debug_info| {480 if (self.debug_info) |debug_info| {
481 self.dwarf_context.deinit(debug_info.allocator);481 self.dwarf_context.deinit(debug_info.allocator);
482 }482 }
...@@ -574,7 +574,7 @@ pub const StackIterator = struct {...@@ -574,7 +574,7 @@ pub const StackIterator = struct {
574 }574 }
575575
576 fn next_internal(self: *StackIterator) ?usize {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 if (self.dwarf_context.pc == 0) return null;578 if (self.dwarf_context.pc == 0) return null;
579 if (self.next_dwarf()) |return_address| {579 if (self.next_dwarf()) |return_address| {
580 return return_address;580 return return_address;
test/standalone/dwarf_unwinding/build.zig+42-27
...@@ -7,31 +7,46 @@ pub fn build(b: *std.Build) void {...@@ -7,31 +7,46 @@ pub fn build(b: *std.Build) void {
7 const target = b.standardTargetOptions(.{});7 const target = b.standardTargetOptions(.{});
8 const optimize = b.standardOptimizeOption(.{});8 const optimize = b.standardOptimizeOption(.{});
99
10 if (!std.debug.StackIterator.supports_context) return;10 // Test unwinding pure zig code (no libc)
1111 {
12 const c_shared_lib = b.addSharedLibrary(.{12 const exe = b.addExecutable(.{
13 .name = "c_shared_lib",13 .name = "zig_unwind",
14 .target = target,14 .root_source_file = .{ .path = "zig_unwind.zig" },
15 .optimize = optimize,15 .target = target,
16 });16 .optimize = optimize,
1717 });
18 if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");18
1919 exe.omit_frame_pointer = true;
20 c_shared_lib.strip = false;20
21 c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"});21 const run_cmd = b.addRunArtifact(exe);
22 c_shared_lib.linkLibC();22 test_step.dependOn(&run_cmd.step);
2323 }
24 const exe = b.addExecutable(.{24
25 .name = "main",25 // Test unwinding through a C shared library
26 .root_source_file = .{ .path = "main.zig" },26 {
27 .target = target,27 const c_shared_lib = b.addSharedLibrary(.{
28 .optimize = optimize,28 .name = "c_shared_lib",
29 });29 .target = target,
3030 .optimize = optimize,
31 exe.omit_frame_pointer = true;31 });
32 exe.linkLibrary(c_shared_lib);32
33 b.installArtifact(exe);33 if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");
3434
35 const run_cmd = b.addRunArtifact(exe);35 c_shared_lib.strip = false;
36 test_step.dependOn(&run_cmd.step);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 @@
1const std = @import("std");
2const debug = std.debug;
3const testing = std.testing;
4
5noinline 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
20noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
21 expected[1] = @returnAddress();
22 frame4(expected, unwound);
23}
24
25fn frame2(expected: *[4]usize, unwound: *[4]usize) callconv(.C) void {
26 frame3(expected, unwound);
27}
28
29extern 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
35pub 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 @@
1const std = @import("std");
2const debug = std.debug;
3const testing = std.testing;
4
5noinline 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
20noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
21 expected[1] = @returnAddress();
22 frame4(expected, unwound);
23}
24
25fn frame2(expected: *[4]usize, unwound: *[4]usize) callconv(.C) void {
26 frame3(expected, unwound);
27}
28
29extern 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
35pub 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 @@
1const std = @import("std");
2const debug = std.debug;
3const testing = std.testing;
4
5noinline 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
20noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
21 expected[1] = @returnAddress();
22 frame3(expected, unwound);
23}
24
25noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
26 expected[2] = @returnAddress();
27 frame2(expected, unwound);
28}
29
30noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {
31 expected[3] = @returnAddress();
32 frame1(expected, unwound);
33}
34
35pub 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}