Commit 5c0d4cef1a

kcbanner <kcbanner@gmail.com>
2023-07-07 16:13:48
debug: add dupeContext, store a pointer to a copy of ThreadContext on UnwindContext
1 parent 463bbe7
lib/std/dwarf/call_frame.zig
@@ -315,9 +315,9 @@ pub const VirtualMachine = struct {
                     } else return error.InvalidCFA;
                 },
                 .register => |register| {
-                    const src = try abi.regBytes(&context.thread_context, register, context.reg_context);
+                    const src = try abi.regBytes(context.thread_context, register, context.reg_context);
                     if (src.len != out.len) return error.RegisterTypeMismatch;
-                    @memcpy(out, try abi.regBytes(&context.thread_context, register, context.reg_context));
+                    @memcpy(out, try abi.regBytes(context.thread_context, register, context.reg_context));
                 },
                 .expression => |expression| {
                     context.stack_machine.reset();
lib/std/dwarf/expressions.zig
@@ -1070,8 +1070,10 @@ test "DWARF expressions" {
     }
 
     // Register values
-    var thread_context: std.debug.ThreadContext = undefined;
-    if (std.debug.getContext(&thread_context)) {
+    if (@TypeOf(std.debug.ThreadContext) != void) {
+        var thread_context: std.debug.ThreadContext = undefined;
+        _ = thread_context;
+
         // TODO: Test fbreg, breg0..31, bregx, regval_type
     }
 }
lib/std/debug.zig
@@ -133,6 +133,9 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
     }
 }
 
+/// 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 `dupeContext`.
 pub const ThreadContext = blk: {
     if (native_os == .windows) {
         break :blk std.os.windows.CONTEXT;
@@ -457,6 +460,19 @@ pub inline fn getContext(context: *ThreadContext) bool {
     return result;
 }
 
+pub fn dupeContext(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_dwarf)).Fn.return_type.?).ErrorUnion.error_set
 else
lib/std/dwarf.zig
@@ -1676,7 +1676,7 @@ pub const DwarfInfo = struct {
         var expression_context = .{
             .isValidMemory = context.isValidMemory,
             .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
-            .thread_context = &context.thread_context,
+            .thread_context = context.thread_context,
             .reg_context = context.reg_context,
             .cfa = context.cfa,
         };
@@ -1690,7 +1690,7 @@ pub const DwarfInfo = struct {
         context.cfa = switch (row.cfa.rule) {
             .val_offset => |offset| blk: {
                 const register = row.cfa.register orelse return error.InvalidCFARule;
-                const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, register, context.reg_context));
+                const value = mem.readIntSliceNative(usize, try abi.regBytes(context.thread_context, register, context.reg_context));
                 break :blk try call_frame.applyOffset(value, offset);
             },
             .expression => |expression| blk: {
@@ -1733,7 +1733,7 @@ pub const DwarfInfo = struct {
                     has_next_ip = column.rule != .undefined;
                 }
 
-                const old_value = try abi.regBytes(&context.thread_context, register, context.reg_context);
+                const old_value = try abi.regBytes(context.thread_context, register, context.reg_context);
                 const new_value = try update_allocator.alloc(u8, old_value.len);
 
                 const prev = update_tail;
@@ -1758,12 +1758,12 @@ pub const DwarfInfo = struct {
         }
 
         if (has_next_ip) {
-            context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, comptime abi.ipRegNum(), context.reg_context));
+            context.pc = mem.readIntSliceNative(usize, try abi.regBytes(context.thread_context, comptime abi.ipRegNum(), context.reg_context));
         } else {
             context.pc = 0;
         }
 
-        mem.writeIntSliceNative(usize, try abi.regBytes(&context.thread_context, abi.spRegNum(context.reg_context), context.reg_context), context.cfa.?);
+        mem.writeIntSliceNative(usize, try abi.regBytes(context.thread_context, abi.spRegNum(context.reg_context), context.reg_context), context.cfa.?);
 
         // The call instruction will have pushed the address of the instruction that follows the call as the return address
         // However, this return address may be past the end of the function if the caller was `noreturn`.
@@ -1779,7 +1779,7 @@ pub const UnwindContext = struct {
     allocator: mem.Allocator,
     cfa: ?usize,
     pc: usize,
-    thread_context: debug.ThreadContext,
+    thread_context: *debug.ThreadContext,
     reg_context: abi.RegisterContext,
     isValidMemory: *const fn (address: usize) bool,
     vm: call_frame.VirtualMachine = .{},
@@ -1788,14 +1788,14 @@ pub const UnwindContext = struct {
     pub fn init(allocator: mem.Allocator, thread_context: *const debug.ThreadContext, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
         const pc = mem.readIntSliceNative(usize, try abi.regBytes(thread_context, abi.ipRegNum(), null));
 
-        if (builtin.os.tag == .macos) @compileError("Fix below TODO");
+        const context_copy = try allocator.create(debug.ThreadContext);
+        debug.dupeContext(thread_context, context_copy);
 
         return .{
             .allocator = allocator,
             .cfa = null,
             .pc = pc,
-            // TODO: This is broken on macos, need a function that knows how to copy the OSs mcontext properly
-            .thread_context = thread_context.*,
+            .thread_context = context_copy,
             .reg_context = undefined,
             .isValidMemory = isValidMemory,
         };
@@ -1804,10 +1804,11 @@ pub const UnwindContext = struct {
     pub fn deinit(self: *UnwindContext) void {
         self.vm.deinit(self.allocator);
         self.stack_machine.deinit(self.allocator);
+        self.allocator.destroy(self.thread_context);
     }
 
     pub fn getFp(self: *const UnwindContext) !usize {
-        return mem.readIntSliceNative(usize, try abi.regBytes(&self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context));
+        return mem.readIntSliceNative(usize, try abi.regBytes(self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context));
     }
 };