Commit 203d96ae97

kcbanner <kcbanner@gmail.com>
2023-07-09 20:45:34
debug: add relocateContext dwarf: fixup tests that used a ThreadContext
1 parent 94354aa
Changed files (3)
lib
test
standalone
dwarf_unwinding
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,
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 => {},
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 => {},
         }
     }