Commit 5f72c6508d
Changed files (8)
test
standalone
dwarf_unwinding
lib/std/dwarf/abi.zig
@@ -3,11 +3,6 @@ const std = @import("../std.zig");
const os = std.os;
const mem = std.mem;
-pub const RegisterContext = struct {
- eh_frame: bool,
- is_macho: bool,
-};
-
pub fn isSupportedArch(arch: std.Target.Cpu.Arch) bool {
return switch (arch) {
.x86,
@@ -29,10 +24,10 @@ pub fn ipRegNum() u8 {
};
}
-pub fn fpRegNum(reg_ctx: RegisterContext) u8 {
+pub fn fpRegNum(reg_context: RegisterContext) u8 {
return switch (builtin.cpu.arch) {
// GCC on OS X did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
- .x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 4 else 5,
+ .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
.x86_64 => 6,
.arm => 11,
.aarch64 => 29,
@@ -40,9 +35,9 @@ pub fn fpRegNum(reg_ctx: RegisterContext) u8 {
};
}
-pub fn spRegNum(reg_ctx: RegisterContext) u8 {
+pub fn spRegNum(reg_context: RegisterContext) u8 {
return switch (builtin.cpu.arch) {
- .x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 5 else 4,
+ .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
.x86_64 => 7,
.arm => 13,
.aarch64 => 31,
@@ -52,21 +47,76 @@ pub fn spRegNum(reg_ctx: RegisterContext) u8 {
fn RegBytesReturnType(comptime ContextPtrType: type) type {
const info = @typeInfo(ContextPtrType);
- if (info != .Pointer or info.Pointer.child != os.ucontext_t) {
- @compileError("Expected a pointer to ucontext_t, got " ++ @typeName(@TypeOf(ContextPtrType)));
+ if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
+ @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
}
return if (info.Pointer.is_const) return []const u8 else []u8;
}
+pub const RegisterContext = struct {
+ eh_frame: bool,
+ is_macho: bool,
+};
+
/// Returns a slice containing the backing storage for `reg_number`.
///
-/// `reg_ctx` describes in what context the register number is used, as it can have different
+/// `reg_context` describes in what context the register number is used, as it can have different
/// meanings depending on the DWARF container. It is only required when getting the stack or
/// frame pointer register on some architectures.
-pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext) !RegBytesReturnType(@TypeOf(ucontext_ptr)) {
- var m = &ucontext_ptr.mcontext;
+pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?RegisterContext) !RegBytesReturnType(@TypeOf(thread_context_ptr)) {
+ if (builtin.os.tag == .windows) {
+ return switch (builtin.cpu.arch) {
+ .x86 => switch (reg_number) {
+ 0 => mem.asBytes(&thread_context_ptr.Eax),
+ 1 => mem.asBytes(&thread_context_ptr.Ecx),
+ 2 => mem.asBytes(&thread_context_ptr.Edx),
+ 3 => mem.asBytes(&thread_context_ptr.Ebx),
+ 4 => mem.asBytes(&thread_context_ptr.Esp),
+ 5 => mem.asBytes(&thread_context_ptr.Ebp),
+ 6 => mem.asBytes(&thread_context_ptr.Esi),
+ 7 => mem.asBytes(&thread_context_ptr.Edi),
+ 8 => mem.asBytes(&thread_context_ptr.Eip),
+ 9 => mem.asBytes(&thread_context_ptr.EFlags),
+ 10 => mem.asBytes(&thread_context_ptr.SegCs),
+ 11 => mem.asBytes(&thread_context_ptr.SegSs),
+ 12 => mem.asBytes(&thread_context_ptr.SegDs),
+ 13 => mem.asBytes(&thread_context_ptr.SegEs),
+ 14 => mem.asBytes(&thread_context_ptr.SegFs),
+ 15 => mem.asBytes(&thread_context_ptr.SegGs),
+ else => error.InvalidRegister,
+ },
+ .x86_64 => switch (reg_number) {
+ 0 => mem.asBytes(&thread_context_ptr.Rax),
+ 1 => mem.asBytes(&thread_context_ptr.Rdx),
+ 2 => mem.asBytes(&thread_context_ptr.Rcx),
+ 3 => mem.asBytes(&thread_context_ptr.Rbx),
+ 4 => mem.asBytes(&thread_context_ptr.Rsi),
+ 5 => mem.asBytes(&thread_context_ptr.Rdi),
+ 6 => mem.asBytes(&thread_context_ptr.Rbp),
+ 7 => mem.asBytes(&thread_context_ptr.Rsp),
+ 8 => mem.asBytes(&thread_context_ptr.R8),
+ 9 => mem.asBytes(&thread_context_ptr.R9),
+ 10 => mem.asBytes(&thread_context_ptr.R10),
+ 11 => mem.asBytes(&thread_context_ptr.R11),
+ 12 => mem.asBytes(&thread_context_ptr.R12),
+ 13 => mem.asBytes(&thread_context_ptr.R13),
+ 14 => mem.asBytes(&thread_context_ptr.R14),
+ 15 => mem.asBytes(&thread_context_ptr.R15),
+ 16 => mem.asBytes(&thread_context_ptr.Rip),
+ else => error.InvalidRegister,
+ },
+ .aarch64 => switch (reg_number) {
+ 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),
+ 31 => mem.asBytes(&thread_context_ptr.Sp),
+ 32 => mem.asBytes(&thread_context_ptr.Pc),
+ },
+ else => error.UnimplementedArch,
+ };
+ }
+ const ucontext_ptr = thread_context_ptr;
+ var m = &ucontext_ptr.mcontext;
return switch (builtin.cpu.arch) {
.x86 => switch (builtin.os.tag) {
.linux, .netbsd, .solaris => switch (reg_number) {
@@ -74,7 +124,7 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]),
2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]),
3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]),
- 4...5 => if (reg_ctx) |r| bytes: {
+ 4...5 => if (reg_context) |r| bytes: {
if (reg_number == 4) {
break :bytes if (r.eh_frame and r.is_macho)
mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP])
@@ -98,7 +148,6 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]),
15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]),
16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
- // TODO: Map TRAPNO, ERR, UESP
32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs
else => error.InvalidRegister,
},
lib/std/dwarf/call_frame.zig
@@ -2,10 +2,10 @@ const builtin = @import("builtin");
const std = @import("../std.zig");
const mem = std.mem;
const debug = std.debug;
-const leb = @import("../leb128.zig");
-const abi = @import("abi.zig");
-const dwarf = @import("../dwarf.zig");
-const expressions = @import("expressions.zig");
+const leb = std.leb;
+const dwarf = std.dwarf;
+const abi = dwarf.abi;
+const expressions = dwarf.expressions;
const assert = std.debug.assert;
const Opcode = enum(u8) {
@@ -315,9 +315,9 @@ pub const VirtualMachine = struct {
} else return error.InvalidCFA;
},
.register => |register| {
- const src = try abi.regBytes(&context.ucontext, register, context.reg_ctx);
+ 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.ucontext, register, context.reg_ctx));
+ @memcpy(out, try abi.regBytes(&context.thread_context, register, context.reg_context));
},
.expression => |expression| {
context.stack_machine.reset();
lib/std/dwarf/expressions.zig
@@ -1,8 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
const OP = @import("OP.zig");
-const leb = @import("../leb128.zig");
-const dwarf = @import("../dwarf.zig");
+const leb = std.leb;
+const dwarf = std.dwarf;
const abi = dwarf.abi;
const mem = std.mem;
const assert = std.debug.assert;
@@ -17,12 +17,12 @@ pub const ExpressionContext = struct {
/// The compilation unit this expression relates to, if any
compile_unit: ?*const dwarf.CompileUnit = null,
- /// Register context
- ucontext: ?*std.os.ucontext_t,
- reg_ctx: ?abi.RegisterContext,
+ /// Thread context
+ thread_context: ?*std.debug.ThreadContext = null,
+ reg_context: ?abi.RegisterContext = null,
/// Call frame address, if in a CFI context
- cfa: ?usize,
+ cfa: ?usize = null,
};
pub const ExpressionOptions = struct {
@@ -344,13 +344,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
OP.breg0...OP.breg31,
OP.bregx,
=> {
- if (context.ucontext == null) return error.IncompleteExpressionContext;
+ if (context.thread_context == null) return error.IncompleteExpressionContext;
const base_register = (try readOperand(stream, opcode)).?.base_register;
var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(
- context.ucontext.?,
+ context.thread_context.?,
base_register.base_register,
- context.reg_ctx,
+ context.reg_context,
)));
value += base_register.offset;
try self.stack.append(allocator, .{ .generic = @intCast(value) });
@@ -358,9 +358,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
OP.regval_type => {
const register_type = (try readOperand(stream, opcode)).?.register_type;
const value = mem.readIntSliceNative(usize, try abi.regBytes(
- context.ucontext.?,
+ context.thread_context.?,
register_type.register,
- context.reg_ctx,
+ context.reg_context,
));
try self.stack.append(allocator, .{
.regval_type = .{
@@ -464,7 +464,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
// 2.5.1.4: Arithmetic and Logical Operations
OP.abs => {
if (self.stack.items.len == 0) return error.InvalidExpression;
- const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+ const value: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
self.stack.items[self.stack.items.len - 1] = .{
.generic = std.math.absCast(value),
};
@@ -478,10 +478,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
},
OP.div => {
if (self.stack.items.len < 2) return error.InvalidExpression;
- const a: isize = @bitCast(try self.stack.pop().asIntegral());
- const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+ const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
+ const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
self.stack.items[self.stack.items.len - 1] = .{
- .generic = @bitCast(try std.math.divTrunc(isize, b, a)),
+ .generic = @bitCast(try std.math.divTrunc(addr_type_signed, b, a)),
};
},
OP.minus => {
@@ -493,16 +493,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
},
OP.mod => {
if (self.stack.items.len < 2) return error.InvalidExpression;
- const a: isize = @bitCast(try self.stack.pop().asIntegral());
- const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+ const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
+ const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
self.stack.items[self.stack.items.len - 1] = .{
.generic = @bitCast(@mod(b, a)),
};
},
OP.mul => {
if (self.stack.items.len < 2) return error.InvalidExpression;
- const a: isize = @bitCast(try self.stack.pop().asIntegral());
- const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+ const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
+ const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
self.stack.items[self.stack.items.len - 1] = .{
.generic = @bitCast(@mulWithOverflow(a, b)[0]),
};
@@ -512,7 +512,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
self.stack.items[self.stack.items.len - 1] = .{
.generic = @bitCast(
try std.math.negate(
- @as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
+ @as(addr_type_signed, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
),
),
};
@@ -563,9 +563,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
OP.shra => {
if (self.stack.items.len < 2) return error.InvalidExpression;
const a = try self.stack.pop().asIntegral();
- const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+ const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
self.stack.items[self.stack.items.len - 1] = .{
- .generic = @bitCast(std.math.shr(isize, b, a)),
+ .generic = @bitCast(std.math.shr(addr_type_signed, b, a)),
};
},
OP.xor => {
@@ -589,8 +589,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
const b = self.stack.items[self.stack.items.len - 1];
if (a == .generic and b == .generic) {
- const a_int: isize = @bitCast(a.asIntegral() catch unreachable);
- const b_int: isize = @bitCast(b.asIntegral() catch unreachable);
+ const a_int: addr_type_signed = @bitCast(a.asIntegral() catch unreachable);
+ const b_int: addr_type_signed = @bitCast(b.asIntegral() catch unreachable);
const result = @intFromBool(switch (opcode) {
OP.le => b_int < a_int,
OP.ge => b_int >= a_int,
@@ -617,7 +617,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
if (condition) {
const new_pos = std.math.cast(
usize,
- try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset),
+ try std.math.add(addr_type_signed, @as(addr_type_signed, @intCast(stream.pos)), branch_offset),
) orelse return error.InvalidExpression;
if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression;
@@ -710,7 +710,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
};
}
-pub fn Writer(options: ExpressionOptions) type {
+pub fn Builder(comptime options: ExpressionOptions) type {
const addr_type = switch (options.addr_size) {
2 => u16,
4 => u32,
@@ -959,10 +959,33 @@ fn opcodeValidInCFA(opcode: u8) bool {
};
}
+const testing = std.testing;
test "DWARF expressions" {
const allocator = std.testing.allocator;
const options = ExpressionOptions{};
- const stack_machine = StackMachine(options){};
+ var stack_machine = StackMachine(options){};
defer stack_machine.deinit(allocator);
+
+ const b = Builder(options);
+
+ var program = std.ArrayList(u8).init(allocator);
+ defer program.deinit();
+
+ const writer = program.writer();
+
+ // Literals
+ {
+ const context = ExpressionContext{};
+ for (0..32) |i| {
+ try b.writeLiteral(writer, @intCast(i));
+ }
+
+ _ = try stack_machine.run(program.items, allocator, context, 0);
+
+ for (0..32) |i| {
+ const expected = 31 - i;
+ try testing.expectEqual(expected, stack_machine.stack.popOrNull().?.generic);
+ }
+ }
}
lib/std/debug.zig
@@ -133,7 +133,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
}
}
-pub const StackTraceContext = blk: {
+pub const ThreadContext = blk: {
if (native_os == .windows) {
break :blk std.os.windows.CONTEXT;
} else if (have_ucontext) {
@@ -146,7 +146,7 @@ pub const StackTraceContext = blk: {
/// Tries to print the stack trace starting from the supplied base pointer to stderr,
/// unbuffered, and ignores any error returned.
/// TODO multithreaded awareness
-pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void {
+pub fn dumpStackTraceFromBase(context: *const ThreadContext) void {
nosuspend {
if (comptime builtin.target.isWasm()) {
if (native_os == .wasi) {
@@ -437,7 +437,7 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
else => true,
});
-pub inline fn getContext(context: *StackTraceContext) bool {
+pub inline fn getContext(context: *ThreadContext) bool {
if (native_os == .windows) {
context.* = std.mem.zeroes(windows.CONTEXT);
windows.ntdll.RtlCaptureContext(context);
@@ -606,8 +606,8 @@ pub const StackIterator = struct {
fn next_dwarf(self: *StackIterator) !usize {
const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);
if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {
- self.dwarf_context.reg_ctx.eh_frame = true;
- self.dwarf_context.reg_ctx.is_macho = di.is_macho;
+ self.dwarf_context.reg_context.eh_frame = true;
+ self.dwarf_context.reg_context.is_macho = di.is_macho;
return di.unwindFrame(&self.dwarf_context, module.base_address);
} else return error.MissingDebugInfo;
}
@@ -663,7 +663,7 @@ pub fn writeCurrentStackTrace(
tty_config: io.tty.Config,
start_addr: ?usize,
) !void {
- var context: StackTraceContext = undefined;
+ var context: ThreadContext = undefined;
const has_context = getContext(&context);
if (native_os == .windows) {
return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr);
lib/std/dwarf.zig
@@ -146,11 +146,11 @@ pub const CC = enum(u8) {
pass_by_reference = 0x4,
pass_by_value = 0x5,
- lo_user = 0x40,
- hi_user = 0xff,
-
GNU_renesas_sh = 0x40,
GNU_borland_fastcall_i386 = 0x41,
+
+ pub const lo_user = 0x40;
+ pub const hi_user = 0xff;
};
pub const Format = enum { @"32", @"64" };
@@ -1676,13 +1676,13 @@ pub const DwarfInfo = struct {
var expression_context = .{
.isValidMemory = context.isValidMemory,
.compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
- .ucontext = &context.ucontext,
- .reg_ctx = context.reg_ctx,
+ .thread_context = &context.thread_context,
+ .reg_context = context.reg_context,
.cfa = context.cfa,
};
context.vm.reset();
- context.reg_ctx.eh_frame = cie.version != 4;
+ context.reg_context.eh_frame = cie.version != 4;
_ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde);
const row = &context.vm.current_row;
@@ -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.ucontext, register, context.reg_ctx));
+ 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: {
@@ -1711,14 +1711,14 @@ pub const DwarfInfo = struct {
if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;
expression_context.cfa = context.cfa;
- // Buffering the modifications is done because copying the ucontext is not portable,
+ // Buffering the modifications is done because copying the thread context is not portable,
// some implementations (ie. darwin) use internal pointers to the mcontext.
var arena = std.heap.ArenaAllocator.init(context.allocator);
defer arena.deinit();
const update_allocator = arena.allocator();
const RegisterUpdate = struct {
- // Backed by ucontext
+ // Backed by thread_context
old_value: []u8,
// Backed by arena
new_value: []const u8,
@@ -1733,7 +1733,7 @@ pub const DwarfInfo = struct {
has_next_ip = column.rule != .undefined;
}
- const old_value = try abi.regBytes(&context.ucontext, register, context.reg_ctx);
+ 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.ucontext, comptime abi.ipRegNum(), context.reg_ctx));
+ 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.ucontext, abi.spRegNum(context.reg_ctx), context.reg_ctx), 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,20 +1779,24 @@ pub const UnwindContext = struct {
allocator: mem.Allocator,
cfa: ?usize,
pc: usize,
- ucontext: os.ucontext_t,
- reg_ctx: abi.RegisterContext,
+ thread_context: debug.ThreadContext,
+ reg_context: abi.RegisterContext,
isValidMemory: *const fn (address: usize) bool,
vm: call_frame.VirtualMachine = .{},
stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{},
- pub fn init(allocator: mem.Allocator, ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
- const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null));
+ 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");
+
return .{
.allocator = allocator,
.cfa = null,
.pc = pc,
- .ucontext = ucontext.*,
- .reg_ctx = undefined,
+ // TODO: This is broken on macos, need a function that knows how to copy the OSs mcontext properly
+ .thread_context = thread_context.*,
+ .reg_context = undefined,
.isValidMemory = isValidMemory,
};
}
@@ -1803,7 +1807,7 @@ pub const UnwindContext = struct {
}
pub fn getFp(self: *const UnwindContext) !usize {
- return mem.readIntSliceNative(usize, try abi.regBytes(&self.ucontext, abi.fpRegNum(self.reg_ctx), self.reg_ctx));
+ return mem.readIntSliceNative(usize, try abi.regBytes(&self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context));
}
};
@@ -2388,3 +2392,7 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {
return math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));
}
}
+
+test {
+ std.testing.refAllDecls(@This());
+}
src/crash_report.zig
@@ -271,7 +271,7 @@ const StackContext = union(enum) {
current: struct {
ret_addr: ?usize,
},
- exception: *const debug.StackTraceContext,
+ exception: *const debug.ThreadContext,
not_supported: void,
pub fn dumpStackTrace(ctx: @This()) void {
test/standalone/dwarf_unwinding/zig_unwind.zig
@@ -5,7 +5,7 @@ const testing = std.testing;
noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
expected[0] = @returnAddress();
- var context: debug.StackTraceContext = undefined;
+ var context: debug.ThreadContext = undefined;
testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");