mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
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
This commit is contained in:
parent
7bc1695f15
commit
ccc9f82068
@ -415,9 +415,9 @@ pub extern "c" fn timer_gettime(timerid: c.timer_t, flags: c_int, curr_value: *c
|
||||
|
||||
pub usingnamespace if (builtin.os.tag == .linux and builtin.target.isMusl()) struct {
|
||||
// musl does not implement getcontext
|
||||
const getcontext = std.os.linux.getcontext;
|
||||
pub const getcontext = std.os.linux.getcontext;
|
||||
} else struct {
|
||||
extern "c" fn getcontext(ucp: *std.os.ucontext_t) c_int;
|
||||
pub extern "c" fn getcontext(ucp: *std.os.ucontext_t) c_int;
|
||||
};
|
||||
|
||||
pub const max_align_t = if (builtin.abi == .msvc)
|
||||
|
||||
@ -136,7 +136,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
|
||||
pub const StackTraceContext = blk: {
|
||||
if (native_os == .windows) {
|
||||
break :blk std.os.windows.CONTEXT;
|
||||
} else if (StackIterator.supports_context) {
|
||||
} else if (have_ucontext) {
|
||||
break :blk os.ucontext_t;
|
||||
} else {
|
||||
break :blk void;
|
||||
@ -420,6 +420,18 @@ pub fn writeStackTrace(
|
||||
}
|
||||
}
|
||||
|
||||
pub const have_getcontext = @hasDecl(os.system, "getcontext") and
|
||||
(builtin.os.tag != .linux or switch (builtin.cpu.arch) {
|
||||
.x86, .x86_64 => true,
|
||||
else => false,
|
||||
});
|
||||
|
||||
pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
|
||||
(builtin.os.tag != .linux or switch (builtin.cpu.arch) {
|
||||
.mips, .mipsel, .mips64, .mips64el, .riscv64 => false,
|
||||
else => true,
|
||||
});
|
||||
|
||||
pub inline fn getContext(context: *StackTraceContext) bool {
|
||||
if (native_os == .windows) {
|
||||
context.* = std.mem.zeroes(windows.CONTEXT);
|
||||
@ -427,13 +439,7 @@ pub inline fn getContext(context: *StackTraceContext) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
const supports_getcontext = @hasDecl(os.system, "getcontext") and
|
||||
(builtin.os.tag != .linux or switch (builtin.cpu.arch) {
|
||||
.x86, .x86_64 => true,
|
||||
else => false,
|
||||
});
|
||||
|
||||
return supports_getcontext and os.system.getcontext(context) == 0;
|
||||
return have_getcontext and os.system.getcontext(context) == 0;
|
||||
}
|
||||
|
||||
pub const StackIterator = struct {
|
||||
@ -445,13 +451,7 @@ pub const StackIterator = struct {
|
||||
// When DebugInfo and a register context is available, this iterator can unwind
|
||||
// stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer).
|
||||
debug_info: ?*DebugInfo,
|
||||
dwarf_context: if (supports_context) DW.UnwindContext else void = undefined,
|
||||
|
||||
pub const supports_context = @hasDecl(os.system, "ucontext_t") and
|
||||
(builtin.os.tag != .linux or switch (builtin.cpu.arch) {
|
||||
.mips, .mipsel, .mips64, .mips64el, .riscv64 => false,
|
||||
else => true,
|
||||
});
|
||||
dwarf_context: if (have_ucontext) DW.UnwindContext else void = undefined,
|
||||
|
||||
pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
|
||||
if (native_arch == .sparc64) {
|
||||
@ -476,7 +476,7 @@ pub const StackIterator = struct {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *StackIterator) void {
|
||||
if (supports_context) {
|
||||
if (have_ucontext) {
|
||||
if (self.debug_info) |debug_info| {
|
||||
self.dwarf_context.deinit(debug_info.allocator);
|
||||
}
|
||||
@ -574,7 +574,7 @@ pub const StackIterator = struct {
|
||||
}
|
||||
|
||||
fn next_internal(self: *StackIterator) ?usize {
|
||||
if (supports_context and self.debug_info != null) {
|
||||
if (have_ucontext and self.debug_info != null) {
|
||||
if (self.dwarf_context.pc == 0) return null;
|
||||
if (self.next_dwarf()) |return_address| {
|
||||
return return_address;
|
||||
|
||||
@ -7,31 +7,46 @@ pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
if (!std.debug.StackIterator.supports_context) return;
|
||||
// Test unwinding pure zig code (no libc)
|
||||
{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "zig_unwind",
|
||||
.root_source_file = .{ .path = "zig_unwind.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const c_shared_lib = b.addSharedLibrary(.{
|
||||
.name = "c_shared_lib",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
exe.omit_frame_pointer = true;
|
||||
|
||||
if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
|
||||
c_shared_lib.strip = false;
|
||||
c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"});
|
||||
c_shared_lib.linkLibC();
|
||||
// Test unwinding through a C shared library
|
||||
{
|
||||
const c_shared_lib = b.addSharedLibrary(.{
|
||||
.name = "c_shared_lib",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "main",
|
||||
.root_source_file = .{ .path = "main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");
|
||||
|
||||
exe.omit_frame_pointer = true;
|
||||
exe.linkLibrary(c_shared_lib);
|
||||
b.installArtifact(exe);
|
||||
c_shared_lib.strip = false;
|
||||
c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"});
|
||||
c_shared_lib.linkLibC();
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "shared_lib_unwind",
|
||||
.root_source_file = .{ .path = "shared_lib_unwind.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.omit_frame_pointer = true;
|
||||
exe.linkLibrary(c_shared_lib);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void {
|
||||
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");
|
||||
var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext");
|
||||
defer it.deinit();
|
||||
|
||||
for (unwound) |*addr| {
|
||||
@ -33,6 +33,8 @@ extern fn frame0(
|
||||
) void;
|
||||
|
||||
pub fn main() !void {
|
||||
if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
|
||||
|
||||
var expected: [4]usize = undefined;
|
||||
var unwound: [4]usize = undefined;
|
||||
frame0(&expected, &unwound, &frame2);
|
||||
42
test/standalone/dwarf_unwinding/zig_unwind.zig
Normal file
42
test/standalone/dwarf_unwinding/zig_unwind.zig
Normal file
@ -0,0 +1,42 @@
|
||||
const std = @import("std");
|
||||
const debug = std.debug;
|
||||
const testing = std.testing;
|
||||
|
||||
noinline fn frame3(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(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 frame2(expected: *[4]usize, unwound: *[4]usize) void {
|
||||
expected[1] = @returnAddress();
|
||||
frame3(expected, unwound);
|
||||
}
|
||||
|
||||
noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
|
||||
expected[2] = @returnAddress();
|
||||
frame2(expected, unwound);
|
||||
}
|
||||
|
||||
noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {
|
||||
expected[3] = @returnAddress();
|
||||
frame1(expected, unwound);
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
|
||||
|
||||
var expected: [4]usize = undefined;
|
||||
var unwound: [4]usize = undefined;
|
||||
frame0(&expected, &unwound);
|
||||
try testing.expectEqual(expected, unwound);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user