diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 9c22ee7f12..26ba36784a 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -133,6 +133,12 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { } } +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, +}); + /// Platform-specific thread state. This contains register state, and on some platforms /// information about the stack. This is not safe to trivially copy, because some platforms /// use internal pointers within this structure. To make a copy, use `copyContext`. @@ -146,6 +152,47 @@ pub const ThreadContext = blk: { } }; +/// Copies one context to another, updating any internal pointers +pub fn copyContext(source: *const ThreadContext, dest: *ThreadContext) void { + if (!have_ucontext) return {}; + dest.* = source.*; + relocateContext(dest); +} + +/// Updates any internal points in the context to reflect its current location +pub fn relocateContext(context: *ThreadContext) void { + return switch (native_os) { + .macos => { + context.mcontext = &context.__mcontext_data; + }, + else => {}, + }; +} + +pub const have_getcontext = @hasDecl(os.system, "getcontext") and + (builtin.os.tag != .linux or switch (builtin.cpu.arch) { + .x86, .x86_64 => true, + else => false, +}); + +/// Capture the current context. The register values in the context will reflect the +/// state after the platform `getcontext` function returned. +/// +/// It is valid to call this if the platform doesn't have context capturing support, +/// in that case false will be returned. +pub inline fn getContext(context: *ThreadContext) bool { + if (native_os == .windows) { + context.* = std.mem.zeroes(windows.CONTEXT); + windows.ntdll.RtlCaptureContext(context); + return true; + } + + const result = have_getcontext and os.system.getcontext(context) == 0; + if (native_os == .macos) assert(context.mcsize == @sizeOf(std.c.mcontext_t)); + + return result; +} + /// Tries to print the stack trace starting from the supplied base pointer to stderr, /// unbuffered, and ignores any error returned. /// TODO multithreaded awareness @@ -428,51 +475,6 @@ 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: *ThreadContext) bool { - if (native_os == .windows) { - context.* = std.mem.zeroes(windows.CONTEXT); - windows.ntdll.RtlCaptureContext(context); - return true; - } - - const result = have_getcontext and os.system.getcontext(context) == 0; - if (native_os == .macos) { - // TODO: Temp, to discover this size via aarch64 CI - if (context.mcsize != @sizeOf(std.c.mcontext_t)) { - print("context.mcsize does not match! {} vs {}\n", .{ context.mcsize, @sizeOf(std.c.mcontext_t) }); - } - - assert(context.mcsize == @sizeOf(std.c.mcontext_t)); - } - - return result; -} - -pub fn copyContext(source: *const ThreadContext, dest: *ThreadContext) void { - if (native_os == .windows) dest.* = source.*; - if (!have_ucontext) return {}; - - return switch (native_os) { - .macos => { - dest.* = source.*; - dest.mcontext = &dest.__mcontext_data; - }, - else => dest.* = source.*, - }; -} - pub const UnwindError = if (have_ucontext) @typeInfo(@typeInfo(@TypeOf(StackIterator.next_unwind)).Fn.return_type.?).ErrorUnion.error_set else @@ -855,7 +857,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void { const module_name = debug_info.getModuleNameForAddress(address) orelse "???"; try tty_config.setColor(out_stream, .dim); - try out_stream.print("Unwind information for {s} was not available ({}), trace may be incomplete\n\n", .{ module_name, err }); + try out_stream.print("Unwind information for `{s}` was not available ({}), trace may be incomplete\n\n", .{ module_name, err }); try tty_config.setColor(out_stream, .reset); } @@ -1641,7 +1643,7 @@ pub const DebugInfo = struct { const seg_start = segment_cmd.vmaddr; const seg_end = seg_start + segment_cmd.vmsize; if (original_address >= seg_start and original_address < seg_end) { - return mem.sliceTo(std.c._dyld_get_image_name(i), 0); + return fs.path.basename(mem.sliceTo(std.c._dyld_get_image_name(i), 0)); } }, else => {}, diff --git a/lib/std/dwarf/expressions.zig b/lib/std/dwarf/expressions.zig index 5708f12dfd..a395c95a89 100644 --- a/lib/std/dwarf/expressions.zig +++ b/lib/std/dwarf/expressions.zig @@ -48,6 +48,7 @@ pub const ExpressionOptions = struct { call_frame_context: bool = false, }; +// Explcitly defined to support executing sub-expressions pub const ExpressionError = error{ UnimplementedExpressionCall, UnimplementedOpcode, @@ -1178,20 +1179,21 @@ test "DWARF expressions" { .is_macho = builtin.os.tag == .macos, }; var thread_context: std.debug.ThreadContext = undefined; + std.debug.relocateContext(&thread_context); const context = ExpressionContext{ .thread_context = &thread_context, .reg_context = reg_context, }; // Only test register operations on arch / os that have them implemented - if (abi.regBytes(&thread_context, 0, reg_context)) |_| { + if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| { // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it - mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, 0, reg_context), 0xee); - mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.fpRegNum(reg_context), reg_context), 1); - mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.spRegNum(reg_context), reg_context), 2); - mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.ipRegNum(), reg_context), 3); + mem.writeIntSliceNative(usize, reg_bytes, 0xee); + (try abi.regValueNative(usize, &thread_context, abi.fpRegNum(reg_context), reg_context)).* = 1; + (try abi.regValueNative(usize, &thread_context, abi.spRegNum(reg_context), reg_context)).* = 2; + (try abi.regValueNative(usize, &thread_context, abi.ipRegNum(), reg_context)).* = 3; try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100)); try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200)); @@ -1609,6 +1611,7 @@ test "DWARF expressions" { .is_macho = builtin.os.tag == .macos, }; var thread_context: std.debug.ThreadContext = undefined; + std.debug.relocateContext(&thread_context); context = ExpressionContext{ .thread_context = &thread_context, .reg_context = reg_context, diff --git a/test/standalone/dwarf_unwinding/zig_unwind.zig b/test/standalone/dwarf_unwinding/zig_unwind.zig index d82bdaa7db..9ef1b57197 100644 --- a/test/standalone/dwarf_unwinding/zig_unwind.zig +++ b/test/standalone/dwarf_unwinding/zig_unwind.zig @@ -19,9 +19,19 @@ noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { } noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void { - if (builtin.os.tag == .macos) { - // Excercise different __unwind_info encodings by forcing some registers to be restored + // Excercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored + if (builtin.target.ofmt != .c) { switch (builtin.cpu.arch) { + .x86 => { + asm volatile ( + \\movl $3, %%ebx + \\movl $1, %%ecx + \\movl $2, %%edx + \\movl $7, %%edi + \\movl $6, %%esi + \\movl $5, %%ebp + ::: "ebx", "ecx", "edx", "edi", "esi", "ebp"); + }, .x86_64 => { asm volatile ( \\movq $3, %%rbx @@ -32,7 +42,6 @@ noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void { \\movq $6, %%rbp ::: "rbx", "r12", "r13", "r14", "r15", "rbp"); }, - .aarch64 => {}, else => {}, } }