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
415415
416416pub usingnamespace if (builtin.os.tag == .linux and builtin.target.isMusl()) struct {
417417 // musl does not implement getcontext
418 const getcontext = std.os.linux.getcontext;
418 pub const getcontext = std.os.linux.getcontext;
419419} 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;
421421};
422422
423423pub 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 {
136136pub const StackTraceContext = blk: {
137137 if (native_os == .windows) {
138138 break :blk std.os.windows.CONTEXT;
139 } else if (StackIterator.supports_context) {
139 } else if (have_ucontext) {
140140 break :blk os.ucontext_t;
141141 } else {
142142 break :blk void;
......@@ -420,6 +420,18 @@ pub fn writeStackTrace(
420420 }
421421}
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
423435pub inline fn getContext(context: *StackTraceContext) bool {
424436 if (native_os == .windows) {
425437 context.* = std.mem.zeroes(windows.CONTEXT);
......@@ -427,13 +439,7 @@ pub inline fn getContext(context: *StackTraceContext) bool {
427439 return true;
428440 }
429441
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;
437443}
438444
439445pub const StackIterator = struct {
......@@ -445,13 +451,7 @@ pub const StackIterator = struct {
445451 // When DebugInfo and a register context is available, this iterator can unwind
446452 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer).
447453 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,
455455
456456 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
457457 if (native_arch == .sparc64) {
......@@ -476,7 +476,7 @@ pub const StackIterator = struct {
476476 }
477477
478478 pub fn deinit(self: *StackIterator) void {
479 if (supports_context) {
479 if (have_ucontext) {
480480 if (self.debug_info) |debug_info| {
481481 self.dwarf_context.deinit(debug_info.allocator);
482482 }
......@@ -574,7 +574,7 @@ pub const StackIterator = struct {
574574 }
575575
576576 fn next_internal(self: *StackIterator) ?usize {
577 if (supports_context and self.debug_info != null) {
577 if (have_ucontext and self.debug_info != null) {
578578 if (self.dwarf_context.pc == 0) return null;
579579 if (self.next_dwarf()) |return_address| {
580580 return return_address;
test/standalone/dwarf_unwinding/build.zig+42-27
......@@ -7,31 +7,46 @@ pub fn build(b: *std.Build) void {
77 const target = b.standardTargetOptions(.{});
88 const optimize = b.standardOptimizeOption(.{});
99
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 }
3752}
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}