| author | |
| committer | |
| log | 5f72c6508d78291d8f0358d06d407a8d6b4a28c9 |
| tree | a5f2b6043ee8ef90a64679cc5299e7d55981d384 |
| parent | 8547c42ba542f77c55ce50253446fd2bf4f3592b |
dwarf: use ThreadContext instead of os.ucontext_t
dwarf: add regBytes impl for windows
dwarf: fixup expression types for non-native8 files changed, 157 insertions(+), 77 deletions(-)
lib/std/debug.zig+6-6| ... | @@ -133,7 +133,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { | ... | @@ -133,7 +133,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 133 | } | 133 | } |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | pub const StackTraceContext = blk: { | 136 | pub const ThreadContext = blk: { |
| 137 | if (native_os == .windows) { | 137 | if (native_os == .windows) { |
| 138 | break :blk std.os.windows.CONTEXT; | 138 | break :blk std.os.windows.CONTEXT; |
| 139 | } else if (have_ucontext) { | 139 | } else if (have_ucontext) { |
| ... | @@ -146,7 +146,7 @@ pub const StackTraceContext = blk: { | ... | @@ -146,7 +146,7 @@ pub const StackTraceContext = blk: { |
| 146 | /// Tries to print the stack trace starting from the supplied base pointer to stderr, | 146 | /// Tries to print the stack trace starting from the supplied base pointer to stderr, |
| 147 | /// unbuffered, and ignores any error returned. | 147 | /// unbuffered, and ignores any error returned. |
| 148 | /// TODO multithreaded awareness | 148 | /// TODO multithreaded awareness |
| 149 | pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void { | 149 | pub fn dumpStackTraceFromBase(context: *const ThreadContext) void { |
| 150 | nosuspend { | 150 | nosuspend { |
| 151 | if (comptime builtin.target.isWasm()) { | 151 | if (comptime builtin.target.isWasm()) { |
| 152 | if (native_os == .wasi) { | 152 | if (native_os == .wasi) { |
| ... | @@ -437,7 +437,7 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and | ... | @@ -437,7 +437,7 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and |
| 437 | else => true, | 437 | else => true, |
| 438 | }); | 438 | }); |
| 439 | 439 | ||
| 440 | pub inline fn getContext(context: *StackTraceContext) bool { | 440 | pub inline fn getContext(context: *ThreadContext) bool { |
| 441 | if (native_os == .windows) { | 441 | if (native_os == .windows) { |
| 442 | context.* = std.mem.zeroes(windows.CONTEXT); | 442 | context.* = std.mem.zeroes(windows.CONTEXT); |
| 443 | windows.ntdll.RtlCaptureContext(context); | 443 | windows.ntdll.RtlCaptureContext(context); |
| ... | @@ -606,8 +606,8 @@ pub const StackIterator = struct { | ... | @@ -606,8 +606,8 @@ pub const StackIterator = struct { |
| 606 | fn next_dwarf(self: *StackIterator) !usize { | 606 | fn next_dwarf(self: *StackIterator) !usize { |
| 607 | const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc); | 607 | const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc); |
| 608 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { | 608 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { |
| 609 | self.dwarf_context.reg_ctx.eh_frame = true; | 609 | self.dwarf_context.reg_context.eh_frame = true; |
| 610 | self.dwarf_context.reg_ctx.is_macho = di.is_macho; | 610 | self.dwarf_context.reg_context.is_macho = di.is_macho; |
| 611 | return di.unwindFrame(&self.dwarf_context, module.base_address); | 611 | return di.unwindFrame(&self.dwarf_context, module.base_address); |
| 612 | } else return error.MissingDebugInfo; | 612 | } else return error.MissingDebugInfo; |
| 613 | } | 613 | } |
| ... | @@ -663,7 +663,7 @@ pub fn writeCurrentStackTrace( | ... | @@ -663,7 +663,7 @@ pub fn writeCurrentStackTrace( |
| 663 | tty_config: io.tty.Config, | 663 | tty_config: io.tty.Config, |
| 664 | start_addr: ?usize, | 664 | start_addr: ?usize, |
| 665 | ) !void { | 665 | ) !void { |
| 666 | var context: StackTraceContext = undefined; | 666 | var context: ThreadContext = undefined; |
| 667 | const has_context = getContext(&context); | 667 | const has_context = getContext(&context); |
| 668 | if (native_os == .windows) { | 668 | if (native_os == .windows) { |
| 669 | return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr); | 669 | return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr); |
lib/std/dwarf.zig+27-19| ... | @@ -146,11 +146,11 @@ pub const CC = enum(u8) { | ... | @@ -146,11 +146,11 @@ pub const CC = enum(u8) { |
| 146 | pass_by_reference = 0x4, | 146 | pass_by_reference = 0x4, |
| 147 | pass_by_value = 0x5, | 147 | pass_by_value = 0x5, |
| 148 | 148 | ||
| 149 | lo_user = 0x40, | ||
| 150 | hi_user = 0xff, | ||
| 151 | |||
| 152 | GNU_renesas_sh = 0x40, | 149 | GNU_renesas_sh = 0x40, |
| 153 | GNU_borland_fastcall_i386 = 0x41, | 150 | GNU_borland_fastcall_i386 = 0x41, |
| 151 | |||
| 152 | pub const lo_user = 0x40; | ||
| 153 | pub const hi_user = 0xff; | ||
| 154 | }; | 154 | }; |
| 155 | 155 | ||
| 156 | pub const Format = enum { @"32", @"64" }; | 156 | pub const Format = enum { @"32", @"64" }; |
| ... | @@ -1676,13 +1676,13 @@ pub const DwarfInfo = struct { | ... | @@ -1676,13 +1676,13 @@ pub const DwarfInfo = struct { |
| 1676 | var expression_context = .{ | 1676 | var expression_context = .{ |
| 1677 | .isValidMemory = context.isValidMemory, | 1677 | .isValidMemory = context.isValidMemory, |
| 1678 | .compile_unit = di.findCompileUnit(fde.pc_begin) catch null, | 1678 | .compile_unit = di.findCompileUnit(fde.pc_begin) catch null, |
| 1679 | .ucontext = &context.ucontext, | 1679 | .thread_context = &context.thread_context, |
| 1680 | .reg_ctx = context.reg_ctx, | 1680 | .reg_context = context.reg_context, |
| 1681 | .cfa = context.cfa, | 1681 | .cfa = context.cfa, |
| 1682 | }; | 1682 | }; |
| 1683 | 1683 | ||
| 1684 | context.vm.reset(); | 1684 | context.vm.reset(); |
| 1685 | context.reg_ctx.eh_frame = cie.version != 4; | 1685 | context.reg_context.eh_frame = cie.version != 4; |
| 1686 | 1686 | ||
| 1687 | _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde); | 1687 | _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde); |
| 1688 | const row = &context.vm.current_row; | 1688 | const row = &context.vm.current_row; |
| ... | @@ -1690,7 +1690,7 @@ pub const DwarfInfo = struct { | ... | @@ -1690,7 +1690,7 @@ pub const DwarfInfo = struct { |
| 1690 | context.cfa = switch (row.cfa.rule) { | 1690 | context.cfa = switch (row.cfa.rule) { |
| 1691 | .val_offset => |offset| blk: { | 1691 | .val_offset => |offset| blk: { |
| 1692 | const register = row.cfa.register orelse return error.InvalidCFARule; | 1692 | const register = row.cfa.register orelse return error.InvalidCFARule; |
| 1693 | const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); | 1693 | const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, register, context.reg_context)); |
| 1694 | break :blk try call_frame.applyOffset(value, offset); | 1694 | break :blk try call_frame.applyOffset(value, offset); |
| 1695 | }, | 1695 | }, |
| 1696 | .expression => |expression| blk: { | 1696 | .expression => |expression| blk: { |
| ... | @@ -1711,14 +1711,14 @@ pub const DwarfInfo = struct { | ... | @@ -1711,14 +1711,14 @@ pub const DwarfInfo = struct { |
| 1711 | if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; | 1711 | if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; |
| 1712 | expression_context.cfa = context.cfa; | 1712 | expression_context.cfa = context.cfa; |
| 1713 | 1713 | ||
| 1714 | // Buffering the modifications is done because copying the ucontext is not portable, | 1714 | // Buffering the modifications is done because copying the thread context is not portable, |
| 1715 | // some implementations (ie. darwin) use internal pointers to the mcontext. | 1715 | // some implementations (ie. darwin) use internal pointers to the mcontext. |
| 1716 | var arena = std.heap.ArenaAllocator.init(context.allocator); | 1716 | var arena = std.heap.ArenaAllocator.init(context.allocator); |
| 1717 | defer arena.deinit(); | 1717 | defer arena.deinit(); |
| 1718 | const update_allocator = arena.allocator(); | 1718 | const update_allocator = arena.allocator(); |
| 1719 | 1719 | ||
| 1720 | const RegisterUpdate = struct { | 1720 | const RegisterUpdate = struct { |
| 1721 | // Backed by ucontext | 1721 | // Backed by thread_context |
| 1722 | old_value: []u8, | 1722 | old_value: []u8, |
| 1723 | // Backed by arena | 1723 | // Backed by arena |
| 1724 | new_value: []const u8, | 1724 | new_value: []const u8, |
| ... | @@ -1733,7 +1733,7 @@ pub const DwarfInfo = struct { | ... | @@ -1733,7 +1733,7 @@ pub const DwarfInfo = struct { |
| 1733 | has_next_ip = column.rule != .undefined; | 1733 | has_next_ip = column.rule != .undefined; |
| 1734 | } | 1734 | } |
| 1735 | 1735 | ||
| 1736 | const old_value = try abi.regBytes(&context.ucontext, register, context.reg_ctx); | 1736 | const old_value = try abi.regBytes(&context.thread_context, register, context.reg_context); |
| 1737 | const new_value = try update_allocator.alloc(u8, old_value.len); | 1737 | const new_value = try update_allocator.alloc(u8, old_value.len); |
| 1738 | 1738 | ||
| 1739 | const prev = update_tail; | 1739 | const prev = update_tail; |
| ... | @@ -1758,12 +1758,12 @@ pub const DwarfInfo = struct { | ... | @@ -1758,12 +1758,12 @@ pub const DwarfInfo = struct { |
| 1758 | } | 1758 | } |
| 1759 | 1759 | ||
| 1760 | if (has_next_ip) { | 1760 | if (has_next_ip) { |
| 1761 | context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, comptime abi.ipRegNum(), context.reg_ctx)); | 1761 | context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, comptime abi.ipRegNum(), context.reg_context)); |
| 1762 | } else { | 1762 | } else { |
| 1763 | context.pc = 0; | 1763 | context.pc = 0; |
| 1764 | } | 1764 | } |
| 1765 | 1765 | ||
| 1766 | mem.writeIntSliceNative(usize, try abi.regBytes(&context.ucontext, abi.spRegNum(context.reg_ctx), context.reg_ctx), context.cfa.?); | 1766 | mem.writeIntSliceNative(usize, try abi.regBytes(&context.thread_context, abi.spRegNum(context.reg_context), context.reg_context), context.cfa.?); |
| 1767 | 1767 | ||
| 1768 | // The call instruction will have pushed the address of the instruction that follows the call as the return address | 1768 | // The call instruction will have pushed the address of the instruction that follows the call as the return address |
| 1769 | // However, this return address may be past the end of the function if the caller was `noreturn`. | 1769 | // 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 { | ... | @@ -1779,20 +1779,24 @@ pub const UnwindContext = struct { |
| 1779 | allocator: mem.Allocator, | 1779 | allocator: mem.Allocator, |
| 1780 | cfa: ?usize, | 1780 | cfa: ?usize, |
| 1781 | pc: usize, | 1781 | pc: usize, |
| 1782 | ucontext: os.ucontext_t, | 1782 | thread_context: debug.ThreadContext, |
| 1783 | reg_ctx: abi.RegisterContext, | 1783 | reg_context: abi.RegisterContext, |
| 1784 | isValidMemory: *const fn (address: usize) bool, | 1784 | isValidMemory: *const fn (address: usize) bool, |
| 1785 | vm: call_frame.VirtualMachine = .{}, | 1785 | vm: call_frame.VirtualMachine = .{}, |
| 1786 | stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{}, | 1786 | stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{}, |
| 1787 | 1787 | ||
| 1788 | pub fn init(allocator: mem.Allocator, ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext { | 1788 | pub fn init(allocator: mem.Allocator, thread_context: *const debug.ThreadContext, isValidMemory: *const fn (address: usize) bool) !UnwindContext { |
| 1789 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null)); | 1789 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(thread_context, abi.ipRegNum(), null)); |
| 1790 | |||
| 1791 | if (builtin.os.tag == .macos) @compileError("Fix below TODO"); | ||
| 1792 | |||
| 1790 | return .{ | 1793 | return .{ |
| 1791 | .allocator = allocator, | 1794 | .allocator = allocator, |
| 1792 | .cfa = null, | 1795 | .cfa = null, |
| 1793 | .pc = pc, | 1796 | .pc = pc, |
| 1794 | .ucontext = ucontext.*, | 1797 | // TODO: This is broken on macos, need a function that knows how to copy the OSs mcontext properly |
| 1795 | .reg_ctx = undefined, | 1798 | .thread_context = thread_context.*, |
| 1799 | .reg_context = undefined, | ||
| 1796 | .isValidMemory = isValidMemory, | 1800 | .isValidMemory = isValidMemory, |
| 1797 | }; | 1801 | }; |
| 1798 | } | 1802 | } |
| ... | @@ -1803,7 +1807,7 @@ pub const UnwindContext = struct { | ... | @@ -1803,7 +1807,7 @@ pub const UnwindContext = struct { |
| 1803 | } | 1807 | } |
| 1804 | 1808 | ||
| 1805 | pub fn getFp(self: *const UnwindContext) !usize { | 1809 | pub fn getFp(self: *const UnwindContext) !usize { |
| 1806 | return mem.readIntSliceNative(usize, try abi.regBytes(&self.ucontext, abi.fpRegNum(self.reg_ctx), self.reg_ctx)); | 1810 | return mem.readIntSliceNative(usize, try abi.regBytes(&self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context)); |
| 1807 | } | 1811 | } |
| 1808 | }; | 1812 | }; |
| 1809 | 1813 | ||
| ... | @@ -2388,3 +2392,7 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize { | ... | @@ -2388,3 +2392,7 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize { |
| 2388 | return math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset))); | 2392 | return math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset))); |
| 2389 | } | 2393 | } |
| 2390 | } | 2394 | } |
| 2395 | |||
| 2396 | test { | ||
| 2397 | std.testing.refAllDecls(@This()); | ||
| 2398 | } |
lib/std/dwarf/abi.zig+65-16| ... | @@ -3,11 +3,6 @@ const std = @import("../std.zig"); | ... | @@ -3,11 +3,6 @@ const std = @import("../std.zig"); |
| 3 | const os = std.os; | 3 | const os = std.os; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | 5 | ||
| 6 | pub const RegisterContext = struct { | ||
| 7 | eh_frame: bool, | ||
| 8 | is_macho: bool, | ||
| 9 | }; | ||
| 10 | |||
| 11 | pub fn isSupportedArch(arch: std.Target.Cpu.Arch) bool { | 6 | pub fn isSupportedArch(arch: std.Target.Cpu.Arch) bool { |
| 12 | return switch (arch) { | 7 | return switch (arch) { |
| 13 | .x86, | 8 | .x86, |
| ... | @@ -29,10 +24,10 @@ pub fn ipRegNum() u8 { | ... | @@ -29,10 +24,10 @@ pub fn ipRegNum() u8 { |
| 29 | }; | 24 | }; |
| 30 | } | 25 | } |
| 31 | 26 | ||
| 32 | pub fn fpRegNum(reg_ctx: RegisterContext) u8 { | 27 | pub fn fpRegNum(reg_context: RegisterContext) u8 { |
| 33 | return switch (builtin.cpu.arch) { | 28 | return switch (builtin.cpu.arch) { |
| 34 | // GCC on OS X did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO | 29 | // GCC on OS X did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO |
| 35 | .x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 4 else 5, | 30 | .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5, |
| 36 | .x86_64 => 6, | 31 | .x86_64 => 6, |
| 37 | .arm => 11, | 32 | .arm => 11, |
| 38 | .aarch64 => 29, | 33 | .aarch64 => 29, |
| ... | @@ -40,9 +35,9 @@ pub fn fpRegNum(reg_ctx: RegisterContext) u8 { | ... | @@ -40,9 +35,9 @@ pub fn fpRegNum(reg_ctx: RegisterContext) u8 { |
| 40 | }; | 35 | }; |
| 41 | } | 36 | } |
| 42 | 37 | ||
| 43 | pub fn spRegNum(reg_ctx: RegisterContext) u8 { | 38 | pub fn spRegNum(reg_context: RegisterContext) u8 { |
| 44 | return switch (builtin.cpu.arch) { | 39 | return switch (builtin.cpu.arch) { |
| 45 | .x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 5 else 4, | 40 | .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4, |
| 46 | .x86_64 => 7, | 41 | .x86_64 => 7, |
| 47 | .arm => 13, | 42 | .arm => 13, |
| 48 | .aarch64 => 31, | 43 | .aarch64 => 31, |
| ... | @@ -52,21 +47,76 @@ pub fn spRegNum(reg_ctx: RegisterContext) u8 { | ... | @@ -52,21 +47,76 @@ pub fn spRegNum(reg_ctx: RegisterContext) u8 { |
| 52 | 47 | ||
| 53 | fn RegBytesReturnType(comptime ContextPtrType: type) type { | 48 | fn RegBytesReturnType(comptime ContextPtrType: type) type { |
| 54 | const info = @typeInfo(ContextPtrType); | 49 | const info = @typeInfo(ContextPtrType); |
| 55 | if (info != .Pointer or info.Pointer.child != os.ucontext_t) { | 50 | if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) { |
| 56 | @compileError("Expected a pointer to ucontext_t, got " ++ @typeName(@TypeOf(ContextPtrType))); | 51 | @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType))); |
| 57 | } | 52 | } |
| 58 | 53 | ||
| 59 | return if (info.Pointer.is_const) return []const u8 else []u8; | 54 | return if (info.Pointer.is_const) return []const u8 else []u8; |
| 60 | } | 55 | } |
| 61 | 56 | ||
| 57 | pub const RegisterContext = struct { | ||
| 58 | eh_frame: bool, | ||
| 59 | is_macho: bool, | ||
| 60 | }; | ||
| 61 | |||
| 62 | /// Returns a slice containing the backing storage for `reg_number`. | 62 | /// Returns a slice containing the backing storage for `reg_number`. |
| 63 | /// | 63 | /// |
| 64 | /// `reg_ctx` describes in what context the register number is used, as it can have different | 64 | /// `reg_context` describes in what context the register number is used, as it can have different |
| 65 | /// meanings depending on the DWARF container. It is only required when getting the stack or | 65 | /// meanings depending on the DWARF container. It is only required when getting the stack or |
| 66 | /// frame pointer register on some architectures. | 66 | /// frame pointer register on some architectures. |
| 67 | pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext) !RegBytesReturnType(@TypeOf(ucontext_ptr)) { | 67 | pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?RegisterContext) !RegBytesReturnType(@TypeOf(thread_context_ptr)) { |
| 68 | var m = &ucontext_ptr.mcontext; | 68 | if (builtin.os.tag == .windows) { |
| 69 | return switch (builtin.cpu.arch) { | ||
| 70 | .x86 => switch (reg_number) { | ||
| 71 | 0 => mem.asBytes(&thread_context_ptr.Eax), | ||
| 72 | 1 => mem.asBytes(&thread_context_ptr.Ecx), | ||
| 73 | 2 => mem.asBytes(&thread_context_ptr.Edx), | ||
| 74 | 3 => mem.asBytes(&thread_context_ptr.Ebx), | ||
| 75 | 4 => mem.asBytes(&thread_context_ptr.Esp), | ||
| 76 | 5 => mem.asBytes(&thread_context_ptr.Ebp), | ||
| 77 | 6 => mem.asBytes(&thread_context_ptr.Esi), | ||
| 78 | 7 => mem.asBytes(&thread_context_ptr.Edi), | ||
| 79 | 8 => mem.asBytes(&thread_context_ptr.Eip), | ||
| 80 | 9 => mem.asBytes(&thread_context_ptr.EFlags), | ||
| 81 | 10 => mem.asBytes(&thread_context_ptr.SegCs), | ||
| 82 | 11 => mem.asBytes(&thread_context_ptr.SegSs), | ||
| 83 | 12 => mem.asBytes(&thread_context_ptr.SegDs), | ||
| 84 | 13 => mem.asBytes(&thread_context_ptr.SegEs), | ||
| 85 | 14 => mem.asBytes(&thread_context_ptr.SegFs), | ||
| 86 | 15 => mem.asBytes(&thread_context_ptr.SegGs), | ||
| 87 | else => error.InvalidRegister, | ||
| 88 | }, | ||
| 89 | .x86_64 => switch (reg_number) { | ||
| 90 | 0 => mem.asBytes(&thread_context_ptr.Rax), | ||
| 91 | 1 => mem.asBytes(&thread_context_ptr.Rdx), | ||
| 92 | 2 => mem.asBytes(&thread_context_ptr.Rcx), | ||
| 93 | 3 => mem.asBytes(&thread_context_ptr.Rbx), | ||
| 94 | 4 => mem.asBytes(&thread_context_ptr.Rsi), | ||
| 95 | 5 => mem.asBytes(&thread_context_ptr.Rdi), | ||
| 96 | 6 => mem.asBytes(&thread_context_ptr.Rbp), | ||
| 97 | 7 => mem.asBytes(&thread_context_ptr.Rsp), | ||
| 98 | 8 => mem.asBytes(&thread_context_ptr.R8), | ||
| 99 | 9 => mem.asBytes(&thread_context_ptr.R9), | ||
| 100 | 10 => mem.asBytes(&thread_context_ptr.R10), | ||
| 101 | 11 => mem.asBytes(&thread_context_ptr.R11), | ||
| 102 | 12 => mem.asBytes(&thread_context_ptr.R12), | ||
| 103 | 13 => mem.asBytes(&thread_context_ptr.R13), | ||
| 104 | 14 => mem.asBytes(&thread_context_ptr.R14), | ||
| 105 | 15 => mem.asBytes(&thread_context_ptr.R15), | ||
| 106 | 16 => mem.asBytes(&thread_context_ptr.Rip), | ||
| 107 | else => error.InvalidRegister, | ||
| 108 | }, | ||
| 109 | .aarch64 => switch (reg_number) { | ||
| 110 | 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]), | ||
| 111 | 31 => mem.asBytes(&thread_context_ptr.Sp), | ||
| 112 | 32 => mem.asBytes(&thread_context_ptr.Pc), | ||
| 113 | }, | ||
| 114 | else => error.UnimplementedArch, | ||
| 115 | }; | ||
| 116 | } | ||
| 69 | 117 | ||
| 118 | const ucontext_ptr = thread_context_ptr; | ||
| 119 | var m = &ucontext_ptr.mcontext; | ||
| 70 | return switch (builtin.cpu.arch) { | 120 | return switch (builtin.cpu.arch) { |
| 71 | .x86 => switch (builtin.os.tag) { | 121 | .x86 => switch (builtin.os.tag) { |
| 72 | .linux, .netbsd, .solaris => switch (reg_number) { | 122 | .linux, .netbsd, .solaris => switch (reg_number) { |
| ... | @@ -74,7 +124,7 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext | ... | @@ -74,7 +124,7 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext |
| 74 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), | 124 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), |
| 75 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), | 125 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), |
| 76 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]), | 126 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]), |
| 77 | 4...5 => if (reg_ctx) |r| bytes: { | 127 | 4...5 => if (reg_context) |r| bytes: { |
| 78 | if (reg_number == 4) { | 128 | if (reg_number == 4) { |
| 79 | break :bytes if (r.eh_frame and r.is_macho) | 129 | break :bytes if (r.eh_frame and r.is_macho) |
| 80 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]) | 130 | 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 | ... | @@ -98,7 +148,6 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext |
| 98 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), | 148 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), |
| 99 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), | 149 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), |
| 100 | 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs | 150 | 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs |
| 101 | // TODO: Map TRAPNO, ERR, UESP | ||
| 102 | 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs | 151 | 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs |
| 103 | else => error.InvalidRegister, | 152 | else => error.InvalidRegister, |
| 104 | }, | 153 | }, |
lib/std/dwarf/call_frame.zig+6-6| ... | @@ -2,10 +2,10 @@ const builtin = @import("builtin"); | ... | @@ -2,10 +2,10 @@ const builtin = @import("builtin"); |
| 2 | const std = @import("../std.zig"); | 2 | const std = @import("../std.zig"); |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const debug = std.debug; | 4 | const debug = std.debug; |
| 5 | const leb = @import("../leb128.zig"); | 5 | const leb = std.leb; |
| 6 | const abi = @import("abi.zig"); | 6 | const dwarf = std.dwarf; |
| 7 | const dwarf = @import("../dwarf.zig"); | 7 | const abi = dwarf.abi; |
| 8 | const expressions = @import("expressions.zig"); | 8 | const expressions = dwarf.expressions; |
| 9 | const assert = std.debug.assert; | 9 | const assert = std.debug.assert; |
| 10 | 10 | ||
| 11 | const Opcode = enum(u8) { | 11 | const Opcode = enum(u8) { |
| ... | @@ -315,9 +315,9 @@ pub const VirtualMachine = struct { | ... | @@ -315,9 +315,9 @@ pub const VirtualMachine = struct { |
| 315 | } else return error.InvalidCFA; | 315 | } else return error.InvalidCFA; |
| 316 | }, | 316 | }, |
| 317 | .register => |register| { | 317 | .register => |register| { |
| 318 | const src = try abi.regBytes(&context.ucontext, register, context.reg_ctx); | 318 | const src = try abi.regBytes(&context.thread_context, register, context.reg_context); |
| 319 | if (src.len != out.len) return error.RegisterTypeMismatch; | 319 | if (src.len != out.len) return error.RegisterTypeMismatch; |
| 320 | @memcpy(out, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); | 320 | @memcpy(out, try abi.regBytes(&context.thread_context, register, context.reg_context)); |
| 321 | }, | 321 | }, |
| 322 | .expression => |expression| { | 322 | .expression => |expression| { |
| 323 | context.stack_machine.reset(); | 323 | context.stack_machine.reset(); |
lib/std/dwarf/expressions.zig+50-27| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const OP = @import("OP.zig"); | 3 | const OP = @import("OP.zig"); |
| 4 | const leb = @import("../leb128.zig"); | 4 | const leb = std.leb; |
| 5 | const dwarf = @import("../dwarf.zig"); | 5 | const dwarf = std.dwarf; |
| 6 | const abi = dwarf.abi; | 6 | const abi = dwarf.abi; |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| ... | @@ -17,12 +17,12 @@ pub const ExpressionContext = struct { | ... | @@ -17,12 +17,12 @@ pub const ExpressionContext = struct { |
| 17 | /// The compilation unit this expression relates to, if any | 17 | /// The compilation unit this expression relates to, if any |
| 18 | compile_unit: ?*const dwarf.CompileUnit = null, | 18 | compile_unit: ?*const dwarf.CompileUnit = null, |
| 19 | 19 | ||
| 20 | /// Register context | 20 | /// Thread context |
| 21 | ucontext: ?*std.os.ucontext_t, | 21 | thread_context: ?*std.debug.ThreadContext = null, |
| 22 | reg_ctx: ?abi.RegisterContext, | 22 | reg_context: ?abi.RegisterContext = null, |
| 23 | 23 | ||
| 24 | /// Call frame address, if in a CFI context | 24 | /// Call frame address, if in a CFI context |
| 25 | cfa: ?usize, | 25 | cfa: ?usize = null, |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | pub const ExpressionOptions = struct { | 28 | pub const ExpressionOptions = struct { |
| ... | @@ -344,13 +344,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -344,13 +344,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 344 | OP.breg0...OP.breg31, | 344 | OP.breg0...OP.breg31, |
| 345 | OP.bregx, | 345 | OP.bregx, |
| 346 | => { | 346 | => { |
| 347 | if (context.ucontext == null) return error.IncompleteExpressionContext; | 347 | if (context.thread_context == null) return error.IncompleteExpressionContext; |
| 348 | 348 | ||
| 349 | const base_register = (try readOperand(stream, opcode)).?.base_register; | 349 | const base_register = (try readOperand(stream, opcode)).?.base_register; |
| 350 | var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes( | 350 | var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes( |
| 351 | context.ucontext.?, | 351 | context.thread_context.?, |
| 352 | base_register.base_register, | 352 | base_register.base_register, |
| 353 | context.reg_ctx, | 353 | context.reg_context, |
| 354 | ))); | 354 | ))); |
| 355 | value += base_register.offset; | 355 | value += base_register.offset; |
| 356 | try self.stack.append(allocator, .{ .generic = @intCast(value) }); | 356 | try self.stack.append(allocator, .{ .generic = @intCast(value) }); |
| ... | @@ -358,9 +358,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -358,9 +358,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 358 | OP.regval_type => { | 358 | OP.regval_type => { |
| 359 | const register_type = (try readOperand(stream, opcode)).?.register_type; | 359 | const register_type = (try readOperand(stream, opcode)).?.register_type; |
| 360 | const value = mem.readIntSliceNative(usize, try abi.regBytes( | 360 | const value = mem.readIntSliceNative(usize, try abi.regBytes( |
| 361 | context.ucontext.?, | 361 | context.thread_context.?, |
| 362 | register_type.register, | 362 | register_type.register, |
| 363 | context.reg_ctx, | 363 | context.reg_context, |
| 364 | )); | 364 | )); |
| 365 | try self.stack.append(allocator, .{ | 365 | try self.stack.append(allocator, .{ |
| 366 | .regval_type = .{ | 366 | .regval_type = .{ |
| ... | @@ -464,7 +464,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -464,7 +464,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 464 | // 2.5.1.4: Arithmetic and Logical Operations | 464 | // 2.5.1.4: Arithmetic and Logical Operations |
| 465 | OP.abs => { | 465 | OP.abs => { |
| 466 | if (self.stack.items.len == 0) return error.InvalidExpression; | 466 | if (self.stack.items.len == 0) return error.InvalidExpression; |
| 467 | const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 467 | const value: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 468 | self.stack.items[self.stack.items.len - 1] = .{ | 468 | self.stack.items[self.stack.items.len - 1] = .{ |
| 469 | .generic = std.math.absCast(value), | 469 | .generic = std.math.absCast(value), |
| 470 | }; | 470 | }; |
| ... | @@ -478,10 +478,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -478,10 +478,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 478 | }, | 478 | }, |
| 479 | OP.div => { | 479 | OP.div => { |
| 480 | if (self.stack.items.len < 2) return error.InvalidExpression; | 480 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 481 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); | 481 | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); |
| 482 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 482 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 483 | self.stack.items[self.stack.items.len - 1] = .{ | 483 | self.stack.items[self.stack.items.len - 1] = .{ |
| 484 | .generic = @bitCast(try std.math.divTrunc(isize, b, a)), | 484 | .generic = @bitCast(try std.math.divTrunc(addr_type_signed, b, a)), |
| 485 | }; | 485 | }; |
| 486 | }, | 486 | }, |
| 487 | OP.minus => { | 487 | OP.minus => { |
| ... | @@ -493,16 +493,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -493,16 +493,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 493 | }, | 493 | }, |
| 494 | OP.mod => { | 494 | OP.mod => { |
| 495 | if (self.stack.items.len < 2) return error.InvalidExpression; | 495 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 496 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); | 496 | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); |
| 497 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 497 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 498 | self.stack.items[self.stack.items.len - 1] = .{ | 498 | self.stack.items[self.stack.items.len - 1] = .{ |
| 499 | .generic = @bitCast(@mod(b, a)), | 499 | .generic = @bitCast(@mod(b, a)), |
| 500 | }; | 500 | }; |
| 501 | }, | 501 | }, |
| 502 | OP.mul => { | 502 | OP.mul => { |
| 503 | if (self.stack.items.len < 2) return error.InvalidExpression; | 503 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 504 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); | 504 | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); |
| 505 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 505 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 506 | self.stack.items[self.stack.items.len - 1] = .{ | 506 | self.stack.items[self.stack.items.len - 1] = .{ |
| 507 | .generic = @bitCast(@mulWithOverflow(a, b)[0]), | 507 | .generic = @bitCast(@mulWithOverflow(a, b)[0]), |
| 508 | }; | 508 | }; |
| ... | @@ -512,7 +512,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -512,7 +512,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 512 | self.stack.items[self.stack.items.len - 1] = .{ | 512 | self.stack.items[self.stack.items.len - 1] = .{ |
| 513 | .generic = @bitCast( | 513 | .generic = @bitCast( |
| 514 | try std.math.negate( | 514 | try std.math.negate( |
| 515 | @as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())), | 515 | @as(addr_type_signed, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())), |
| 516 | ), | 516 | ), |
| 517 | ), | 517 | ), |
| 518 | }; | 518 | }; |
| ... | @@ -563,9 +563,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -563,9 +563,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 563 | OP.shra => { | 563 | OP.shra => { |
| 564 | if (self.stack.items.len < 2) return error.InvalidExpression; | 564 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 565 | const a = try self.stack.pop().asIntegral(); | 565 | const a = try self.stack.pop().asIntegral(); |
| 566 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | 566 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); |
| 567 | self.stack.items[self.stack.items.len - 1] = .{ | 567 | self.stack.items[self.stack.items.len - 1] = .{ |
| 568 | .generic = @bitCast(std.math.shr(isize, b, a)), | 568 | .generic = @bitCast(std.math.shr(addr_type_signed, b, a)), |
| 569 | }; | 569 | }; |
| 570 | }, | 570 | }, |
| 571 | OP.xor => { | 571 | OP.xor => { |
| ... | @@ -589,8 +589,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -589,8 +589,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 589 | const b = self.stack.items[self.stack.items.len - 1]; | 589 | const b = self.stack.items[self.stack.items.len - 1]; |
| 590 | 590 | ||
| 591 | if (a == .generic and b == .generic) { | 591 | if (a == .generic and b == .generic) { |
| 592 | const a_int: isize = @bitCast(a.asIntegral() catch unreachable); | 592 | const a_int: addr_type_signed = @bitCast(a.asIntegral() catch unreachable); |
| 593 | const b_int: isize = @bitCast(b.asIntegral() catch unreachable); | 593 | const b_int: addr_type_signed = @bitCast(b.asIntegral() catch unreachable); |
| 594 | const result = @intFromBool(switch (opcode) { | 594 | const result = @intFromBool(switch (opcode) { |
| 595 | OP.le => b_int < a_int, | 595 | OP.le => b_int < a_int, |
| 596 | OP.ge => b_int >= a_int, | 596 | OP.ge => b_int >= a_int, |
| ... | @@ -617,7 +617,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -617,7 +617,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 617 | if (condition) { | 617 | if (condition) { |
| 618 | const new_pos = std.math.cast( | 618 | const new_pos = std.math.cast( |
| 619 | usize, | 619 | usize, |
| 620 | try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset), | 620 | try std.math.add(addr_type_signed, @as(addr_type_signed, @intCast(stream.pos)), branch_offset), |
| 621 | ) orelse return error.InvalidExpression; | 621 | ) orelse return error.InvalidExpression; |
| 622 | 622 | ||
| 623 | if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression; | 623 | if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression; |
| ... | @@ -710,7 +710,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -710,7 +710,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 710 | }; | 710 | }; |
| 711 | } | 711 | } |
| 712 | 712 | ||
| 713 | pub fn Writer(options: ExpressionOptions) type { | 713 | pub fn Builder(comptime options: ExpressionOptions) type { |
| 714 | const addr_type = switch (options.addr_size) { | 714 | const addr_type = switch (options.addr_size) { |
| 715 | 2 => u16, | 715 | 2 => u16, |
| 716 | 4 => u32, | 716 | 4 => u32, |
| ... | @@ -959,10 +959,33 @@ fn opcodeValidInCFA(opcode: u8) bool { | ... | @@ -959,10 +959,33 @@ fn opcodeValidInCFA(opcode: u8) bool { |
| 959 | }; | 959 | }; |
| 960 | } | 960 | } |
| 961 | 961 | ||
| 962 | const testing = std.testing; | ||
| 962 | test "DWARF expressions" { | 963 | test "DWARF expressions" { |
| 963 | const allocator = std.testing.allocator; | 964 | const allocator = std.testing.allocator; |
| 964 | 965 | ||
| 965 | const options = ExpressionOptions{}; | 966 | const options = ExpressionOptions{}; |
| 966 | const stack_machine = StackMachine(options){}; | 967 | var stack_machine = StackMachine(options){}; |
| 967 | defer stack_machine.deinit(allocator); | 968 | defer stack_machine.deinit(allocator); |
| 969 | |||
| 970 | const b = Builder(options); | ||
| 971 | |||
| 972 | var program = std.ArrayList(u8).init(allocator); | ||
| 973 | defer program.deinit(); | ||
| 974 | |||
| 975 | const writer = program.writer(); | ||
| 976 | |||
| 977 | // Literals | ||
| 978 | { | ||
| 979 | const context = ExpressionContext{}; | ||
| 980 | for (0..32) |i| { | ||
| 981 | try b.writeLiteral(writer, @intCast(i)); | ||
| 982 | } | ||
| 983 | |||
| 984 | _ = try stack_machine.run(program.items, allocator, context, 0); | ||
| 985 | |||
| 986 | for (0..32) |i| { | ||
| 987 | const expected = 31 - i; | ||
| 988 | try testing.expectEqual(expected, stack_machine.stack.popOrNull().?.generic); | ||
| 989 | } | ||
| 990 | } | ||
| 968 | } | 991 | } |
src/crash_report.zig+1-1| ... | @@ -271,7 +271,7 @@ const StackContext = union(enum) { | ... | @@ -271,7 +271,7 @@ const StackContext = union(enum) { |
| 271 | current: struct { | 271 | current: struct { |
| 272 | ret_addr: ?usize, | 272 | ret_addr: ?usize, |
| 273 | }, | 273 | }, |
| 274 | exception: *const debug.StackTraceContext, | 274 | exception: *const debug.ThreadContext, |
| 275 | not_supported: void, | 275 | not_supported: void, |
| 276 | 276 | ||
| 277 | pub fn dumpStackTrace(ctx: @This()) void { | 277 | pub fn dumpStackTrace(ctx: @This()) void { |
test/standalone/dwarf_unwinding/shared_lib_unwind.zig+1-1| ... | @@ -5,7 +5,7 @@ const testing = std.testing; | ... | @@ -5,7 +5,7 @@ const testing = std.testing; |
| 5 | noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void { | 5 | noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void { |
| 6 | expected[0] = @returnAddress(); | 6 | expected[0] = @returnAddress(); |
| 7 | 7 | ||
| 8 | var context: debug.StackTraceContext = undefined; | 8 | var context: debug.ThreadContext = undefined; |
| 9 | testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); | 9 | testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); |
| 10 | 10 | ||
| 11 | var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); | 11 | var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); |
test/standalone/dwarf_unwinding/zig_unwind.zig+1-1| ... | @@ -5,7 +5,7 @@ const testing = std.testing; | ... | @@ -5,7 +5,7 @@ const testing = std.testing; |
| 5 | noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { | 5 | noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { |
| 6 | expected[0] = @returnAddress(); | 6 | expected[0] = @returnAddress(); |
| 7 | 7 | ||
| 8 | var context: debug.StackTraceContext = undefined; | 8 | var context: debug.ThreadContext = undefined; |
| 9 | testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); | 9 | testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); |
| 10 | 10 | ||
| 11 | var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); | 11 | var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); |