debug: add dupeContext, store a pointer to a copy of ThreadContext on UnwindContext

This commit is contained in:
kcbanner 2023-07-07 10:13:48 -04:00
parent 463bbe7807
commit 5c0d4cef1a
4 changed files with 33 additions and 14 deletions

View File

@ -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

View File

@ -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));
}
};

View File

@ -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();

View File

@ -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
}
}