authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-06-27 02:02:49-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:14-04:00
log89ef004646896a145ec0607678882a395fabda3d
treec1461f03122e27657ccd7a037b65be33d3575520
parent5cd8ab2473a4255f081f417eb3ec95e1a3a9e9d8

debug: x86 unwinding support, more unwinding fixes

- Fix unwindFrame using the previous FDE row instead of the current one - Handle unwinding through noreturn functions - Add x86-linux getcontext - Fixup x86_64-linux getcontext not restoring the fp env - Fix start_addr filtering on x86-windows

6 files changed, 123 insertions(+), 33 deletions(-)

lib/std/debug.zig+45-22
...@@ -135,7 +135,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -135,7 +135,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
135135
136pub const StackTraceContext = blk: {136pub const StackTraceContext = blk: {
137 if (native_os == .windows) {137 if (native_os == .windows) {
138 break :blk @typeInfo(@TypeOf(os.windows.CONTEXT.getRegs)).Fn.return_type.?;138 break :blk std.os.windows.CONTEXT;
139 } else if (@hasDecl(os.system, "ucontext_t")) {139 } else if (@hasDecl(os.system, "ucontext_t")) {
140 break :blk os.ucontext_t;140 break :blk os.ucontext_t;
141 } else {141 } else {
...@@ -166,7 +166,14 @@ pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void {...@@ -166,7 +166,14 @@ pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void {
166 };166 };
167 const tty_config = io.tty.detectConfig(io.getStdErr());167 const tty_config = io.tty.detectConfig(io.getStdErr());
168 if (native_os == .windows) {168 if (native_os == .windows) {
169 writeCurrentStackTraceWindows(stderr, debug_info, tty_config, context.ip) catch return;169 // On x86_64 and aarch64, the stack will be unwound using RtlVirtualUnwind using the context
170 // provided by the exception handler. On x86, RtlVirtualUnwind doesn't exist. Instead, a new backtrace
171 // will be captured and frames prior to the exception will be filtered.
172 // The caveat is that RtlCaptureStackBackTrace does not include the KiUserExceptionDispatcher frame,
173 // which is where the IP in `context` points to, so it can't be used as start_addr.
174 // Instead, start_addr is recovered from the stack.
175 const start_addr = if (builtin.cpu.arch == .x86) @as(*const usize, @ptrFromInt(context.getRegs().bp + 4)).* else null;
176 writeStackTraceWindows(stderr, debug_info, tty_config, context, start_addr) catch return;
170 return;177 return;
171 }178 }
172179
...@@ -196,12 +203,12 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT...@@ -196,12 +203,12 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT
196 if (native_os == .windows) {203 if (native_os == .windows) {
197 const addrs = stack_trace.instruction_addresses;204 const addrs = stack_trace.instruction_addresses;
198 const first_addr = first_address orelse {205 const first_addr = first_address orelse {
199 stack_trace.index = walkStackWindows(addrs[0..]);206 stack_trace.index = walkStackWindows(addrs[0..], null);
200 return;207 return;
201 };208 };
202 var addr_buf_stack: [32]usize = undefined;209 var addr_buf_stack: [32]usize = undefined;
203 const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs;210 const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs;
204 const n = walkStackWindows(addr_buf[0..]);211 const n = walkStackWindows(addr_buf[0..], null);
205 const first_index = for (addr_buf[0..n], 0..) |addr, i| {212 const first_index = for (addr_buf[0..n], 0..) |addr, i| {
206 if (addr == first_addr) {213 if (addr == first_addr) {
207 break i;214 break i;
...@@ -218,7 +225,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT...@@ -218,7 +225,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT
218 }225 }
219 stack_trace.index = slice.len;226 stack_trace.index = slice.len;
220 } else {227 } else {
221 // TODO: This should use the dwarf unwinder if it's available228 // TODO: This should use the DWARF unwinder if .eh_frame_hdr is available (so that full debug info parsing isn't required)
222 var it = StackIterator.init(first_address, null);229 var it = StackIterator.init(first_address, null);
223 defer it.deinit();230 defer it.deinit();
224 for (stack_trace.instruction_addresses, 0..) |*addr, i| {231 for (stack_trace.instruction_addresses, 0..) |*addr, i| {
...@@ -415,10 +422,18 @@ pub fn writeStackTrace(...@@ -415,10 +422,18 @@ pub fn writeStackTrace(
415422
416inline fn getContext(context: *StackTraceContext) bool {423inline fn getContext(context: *StackTraceContext) bool {
417 if (native_os == .windows) {424 if (native_os == .windows) {
418 @compileError("Syscall please!");425 context.* = std.mem.zeroes(windows.CONTEXT);
426 windows.ntdll.RtlCaptureContext(context);
427 return true;
419 }428 }
420429
421 return @hasDecl(os.system, "getcontext") and os.system.getcontext(context) == 0;430 const supports_getcontext = @hasDecl(os.system, "getcontext") and
431 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {
432 .x86, .x86_64 => true,
433 else => false,
434 });
435
436 return supports_getcontext and os.system.getcontext(context) == 0;
422}437}
423438
424pub const StackIterator = struct {439pub const StackIterator = struct {
...@@ -431,6 +446,7 @@ pub const StackIterator = struct {...@@ -431,6 +446,7 @@ pub const StackIterator = struct {
431 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer).446 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer).
432 debug_info: ?*DebugInfo,447 debug_info: ?*DebugInfo,
433 dwarf_context: if (supports_context) DW.UnwindContext else void = undefined,448 dwarf_context: if (supports_context) DW.UnwindContext else void = undefined,
449
434 pub const supports_context = @hasDecl(os.system, "ucontext_t") and450 pub const supports_context = @hasDecl(os.system, "ucontext_t") and
435 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {451 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {
436 .mips, .mipsel, .mips64, .mips64el, .riscv64 => false,452 .mips, .mipsel, .mips64, .mips64el, .riscv64 => false,
...@@ -548,19 +564,20 @@ pub const StackIterator = struct {...@@ -548,19 +564,20 @@ pub const StackIterator = struct {
548 }564 }
549 }565 }
550566
551 fn next_dwarf(self: *StackIterator) !void {567 fn next_dwarf(self: *StackIterator) !usize {
552 const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);568 const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);
553 if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {569 if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {
554 self.dwarf_context.reg_ctx.eh_frame = true;570 self.dwarf_context.reg_ctx.eh_frame = true;
555 self.dwarf_context.reg_ctx.is_macho = di.is_macho;571 self.dwarf_context.reg_ctx.is_macho = di.is_macho;
556 try di.unwindFrame(self.debug_info.?.allocator, &self.dwarf_context, module.base_address);572 return di.unwindFrame(self.debug_info.?.allocator, &self.dwarf_context, module.base_address);
557 } else return error.MissingDebugInfo;573 } else return error.MissingDebugInfo;
558 }574 }
559575
560 fn next_internal(self: *StackIterator) ?usize {576 fn next_internal(self: *StackIterator) ?usize {
561 if (supports_context and self.debug_info != null) {577 if (supports_context and self.debug_info != null) {
562 if (self.next_dwarf()) |_| {578 if (self.dwarf_context.pc == 0) return null;
563 return self.dwarf_context.pc;579 if (self.next_dwarf()) |return_address| {
580 return return_address;
564 } else |err| {581 } else |err| {
565 if (err != error.MissingFDE) print("DWARF unwind error: {}\n", .{err});582 if (err != error.MissingFDE) print("DWARF unwind error: {}\n", .{err});
566583
...@@ -611,12 +628,13 @@ pub fn writeCurrentStackTrace(...@@ -611,12 +628,13 @@ pub fn writeCurrentStackTrace(
611 tty_config: io.tty.Config,628 tty_config: io.tty.Config,
612 start_addr: ?usize,629 start_addr: ?usize,
613) !void {630) !void {
631 var context: StackTraceContext = undefined;
632 const has_context = getContext(&context);
614 if (native_os == .windows) {633 if (native_os == .windows) {
615 return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr);634 return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr);
616 }635 }
617636
618 var context: StackTraceContext = undefined;637 var it = (if (has_context) blk: {
619 var it = (if (getContext(&context)) blk: {
620 break :blk StackIterator.initWithContext(start_addr, debug_info, &context) catch null;638 break :blk StackIterator.initWithContext(start_addr, debug_info, &context) catch null;
621 } else null) orelse StackIterator.init(start_addr, null);639 } else null) orelse StackIterator.init(start_addr, null);
622 defer it.deinit();640 defer it.deinit();
...@@ -632,7 +650,7 @@ pub fn writeCurrentStackTrace(...@@ -632,7 +650,7 @@ pub fn writeCurrentStackTrace(
632 }650 }
633}651}
634652
635pub noinline fn walkStackWindows(addresses: []usize) usize {653pub noinline fn walkStackWindows(addresses: []usize, existing_context: ?*const windows.CONTEXT) usize {
636 if (builtin.cpu.arch == .x86) {654 if (builtin.cpu.arch == .x86) {
637 // RtlVirtualUnwind doesn't exist on x86655 // RtlVirtualUnwind doesn't exist on x86
638 return windows.ntdll.RtlCaptureStackBackTrace(0, addresses.len, @as(**anyopaque, @ptrCast(addresses.ptr)), null);656 return windows.ntdll.RtlCaptureStackBackTrace(0, addresses.len, @as(**anyopaque, @ptrCast(addresses.ptr)), null);
...@@ -640,8 +658,13 @@ pub noinline fn walkStackWindows(addresses: []usize) usize {...@@ -640,8 +658,13 @@ pub noinline fn walkStackWindows(addresses: []usize) usize {
640658
641 const tib = @as(*const windows.NT_TIB, @ptrCast(&windows.teb().Reserved1));659 const tib = @as(*const windows.NT_TIB, @ptrCast(&windows.teb().Reserved1));
642660
643 var context: windows.CONTEXT = std.mem.zeroes(windows.CONTEXT);661 var context: windows.CONTEXT = undefined;
644 windows.ntdll.RtlCaptureContext(&context);662 if (existing_context) |context_ptr| {
663 context = context_ptr.*;
664 } else {
665 context = std.mem.zeroes(windows.CONTEXT);
666 windows.ntdll.RtlCaptureContext(&context);
667 }
645668
646 var i: usize = 0;669 var i: usize = 0;
647 var image_base: usize = undefined;670 var image_base: usize = undefined;
...@@ -683,14 +706,15 @@ pub noinline fn walkStackWindows(addresses: []usize) usize {...@@ -683,14 +706,15 @@ pub noinline fn walkStackWindows(addresses: []usize) usize {
683 return i;706 return i;
684}707}
685708
686pub fn writeCurrentStackTraceWindows(709pub fn writeStackTraceWindows(
687 out_stream: anytype,710 out_stream: anytype,
688 debug_info: *DebugInfo,711 debug_info: *DebugInfo,
689 tty_config: io.tty.Config,712 tty_config: io.tty.Config,
713 context: *const windows.CONTEXT,
690 start_addr: ?usize,714 start_addr: ?usize,
691) !void {715) !void {
692 var addr_buf: [1024]usize = undefined;716 var addr_buf: [1024]usize = undefined;
693 const n = walkStackWindows(addr_buf[0..]);717 const n = walkStackWindows(addr_buf[0..], context);
694 const addrs = addr_buf[0..n];718 const addrs = addr_buf[0..n];
695 var start_i: usize = if (start_addr) |saddr| blk: {719 var start_i: usize = if (start_addr) |saddr| blk: {
696 for (addrs, 0..) |addr, i| {720 for (addrs, 0..) |addr, i| {
...@@ -2164,16 +2188,15 @@ fn handleSegfaultWindowsExtra(...@@ -2164,16 +2188,15 @@ fn handleSegfaultWindowsExtra(
2164}2188}
21652189
2166fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void {2190fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void {
2167 const regs = info.ContextRecord.getRegs();
2168 const stderr = io.getStdErr().writer();2191 const stderr = io.getStdErr().writer();
2169 _ = switch (msg) {2192 _ = switch (msg) {
2170 0 => stderr.print("{s}\n", .{label.?}),2193 0 => stderr.print("{s}\n", .{label.?}),
2171 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),2194 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),
2172 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{regs.ip}),2195 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{info.ContextRecord.getRegs().ip}),
2173 else => unreachable,2196 else => unreachable,
2174 } catch os.abort();2197 } catch os.abort();
21752198
2176 dumpStackTraceFromBase(&regs);2199 dumpStackTraceFromBase(info.ContextRecord);
2177}2200}
21782201
2179pub fn dumpStackPointerAddr(prefix: []const u8) void {2202pub fn dumpStackPointerAddr(prefix: []const u8) void {
lib/std/dwarf.zig+15-4
...@@ -1577,9 +1577,9 @@ pub const DwarfInfo = struct {...@@ -1577,9 +1577,9 @@ pub const DwarfInfo = struct {
1577 }1577 }
1578 }1578 }
15791579
1580 pub fn unwindFrame(di: *const DwarfInfo, allocator: mem.Allocator, context: *UnwindContext, module_base_address: usize) !void {1580 pub fn unwindFrame(di: *const DwarfInfo, allocator: mem.Allocator, context: *UnwindContext, module_base_address: usize) !usize {
1581 if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;1581 if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;
1582 if (context.pc == 0) return;1582 if (context.pc == 0) return 0;
15831583
1584 // TODO: Handle unwinding from a signal frame (ie. use_prev_instr in libunwind)1584 // TODO: Handle unwinding from a signal frame (ie. use_prev_instr in libunwind)
15851585
...@@ -1626,8 +1626,11 @@ pub const DwarfInfo = struct {...@@ -1626,8 +1626,11 @@ pub const DwarfInfo = struct {
1626 }1626 }
16271627
1628 context.vm.reset();1628 context.vm.reset();
1629 context.reg_ctx.eh_frame = cie.version != 4;
1630
1631 _ = try context.vm.runToNative(allocator, mapped_pc, cie, fde);
1632 const row = &context.vm.current_row;
16291633
1630 const row = try context.vm.runToNative(allocator, mapped_pc, cie, fde);
1631 context.cfa = switch (row.cfa.rule) {1634 context.cfa = switch (row.cfa.rule) {
1632 .val_offset => |offset| blk: {1635 .val_offset => |offset| blk: {
1633 const register = row.cfa.register orelse return error.InvalidCFARule;1636 const register = row.cfa.register orelse return error.InvalidCFARule;
...@@ -1650,7 +1653,7 @@ pub const DwarfInfo = struct {...@@ -1650,7 +1653,7 @@ pub const DwarfInfo = struct {
1650 var next_ucontext = context.ucontext;1653 var next_ucontext = context.ucontext;
16511654
1652 var has_next_ip = false;1655 var has_next_ip = false;
1653 for (context.vm.rowColumns(row)) |column| {1656 for (context.vm.rowColumns(row.*)) |column| {
1654 if (column.register) |register| {1657 if (column.register) |register| {
1655 const dest = try abi.regBytes(&next_ucontext, register, context.reg_ctx);1658 const dest = try abi.regBytes(&next_ucontext, register, context.reg_ctx);
1656 if (register == cie.return_address_register) {1659 if (register == cie.return_address_register) {
...@@ -1670,6 +1673,14 @@ pub const DwarfInfo = struct {...@@ -1670,6 +1673,14 @@ pub const DwarfInfo = struct {
1670 }1673 }
16711674
1672 mem.writeIntSliceNative(usize, try abi.regBytes(&context.ucontext, abi.spRegNum(context.reg_ctx), context.reg_ctx), context.cfa.?);1675 mem.writeIntSliceNative(usize, try abi.regBytes(&context.ucontext, abi.spRegNum(context.reg_ctx), context.reg_ctx), context.cfa.?);
1676
1677 // The call instruction will have pushed the address of the instruction that follows the call as the return address
1678 // However, this return address may be past the end of the function if the caller was `noreturn`.
1679 // TODO: Check this on non-x86_64
1680 const return_address = context.pc;
1681 if (context.pc > 0) context.pc -= 1;
1682
1683 return return_address;
1673 }1684 }
1674};1685};
16751686
lib/std/dwarf/call_frame.zig+3-1
...@@ -386,7 +386,9 @@ pub const VirtualMachine = struct {...@@ -386,7 +386,9 @@ pub const VirtualMachine = struct {
386 }386 }
387387
388 /// Runs the CIE instructions, then the FDE instructions. Execution halts388 /// Runs the CIE instructions, then the FDE instructions. Execution halts
389 /// once the row that corresponds to `pc` is known, and it is returned.389 /// once the row that corresponds to `pc` is known (and set as `current_row`).
390 ///
391 /// The state of the row prior to the last execution step is returned.
390 pub fn runTo(392 pub fn runTo(
391 self: *VirtualMachine,393 self: *VirtualMachine,
392 allocator: std.mem.Allocator,394 allocator: std.mem.Allocator,
lib/std/os/linux/x86.zig+54
...@@ -389,3 +389,57 @@ pub const SC = struct {...@@ -389,3 +389,57 @@ pub const SC = struct {
389 pub const recvmmsg = 19;389 pub const recvmmsg = 19;
390 pub const sendmmsg = 20;390 pub const sendmmsg = 20;
391};391};
392
393fn gpRegisterOffset(comptime reg_index: comptime_int) usize {
394 return @offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "gregs") + @sizeOf(usize) * reg_index;
395}
396
397pub inline fn getcontext(context: *ucontext_t) usize {
398 asm volatile (
399 \\ movl %%edi, (%[edi_offset])(%[context])
400 \\ movl %%esi, (%[esi_offset])(%[context])
401 \\ movl %%ebp, (%[ebp_offset])(%[context])
402 \\ movl %%esp, (%[esp_offset])(%[context])
403 \\ movl %%ebx, (%[ebx_offset])(%[context])
404 \\ movl %%edx, (%[edx_offset])(%[context])
405 \\ movl %%ecx, (%[ecx_offset])(%[context])
406 \\ movl %%eax, (%[eax_offset])(%[context])
407 \\ xorl %%ecx, %%ecx
408 \\ movw %%fs, %%cx
409 \\ movl %%ecx, (%[fs_offset])(%[context])
410 \\ leal (%[regspace_offset])(%[context]), %%ecx
411 \\ movl %%ecx, (%[fpregs_offset])(%[context])
412 \\ fnstenv (%%ecx)
413 \\ fldenv (%%ecx)
414 \\ call getcontext_read_eip
415 \\ getcontext_read_eip: pop %%ecx
416 \\ movl %%ecx, (%[eip_offset])(%[context])
417 :
418 : [context] "{edi}" (context),
419 [edi_offset] "p" (comptime gpRegisterOffset(REG.EDI)),
420 [esi_offset] "p" (comptime gpRegisterOffset(REG.ESI)),
421 [ebp_offset] "p" (comptime gpRegisterOffset(REG.EBP)),
422 [esp_offset] "p" (comptime gpRegisterOffset(REG.ESP)),
423 [ebx_offset] "p" (comptime gpRegisterOffset(REG.EBX)),
424 [edx_offset] "p" (comptime gpRegisterOffset(REG.EDX)),
425 [ecx_offset] "p" (comptime gpRegisterOffset(REG.ECX)),
426 [eax_offset] "p" (comptime gpRegisterOffset(REG.EAX)),
427 [eip_offset] "p" (comptime gpRegisterOffset(REG.EIP)),
428 [fs_offset] "p" (comptime gpRegisterOffset(REG.FS)),
429 [fpregs_offset] "p" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")),
430 [regspace_offset] "p" (@offsetOf(ucontext_t, "regspace")),
431 : "memory", "ecx"
432 );
433
434 // TODO: Read CS/SS registers?
435 // TODO: Store mxcsr state, need an actual definition of fpstate for that
436
437 // TODO: `flags` isn't present in the getcontext man page, figure out what to write here
438 context.flags = 0;
439 context.link = null;
440
441 const altstack_result = linux.sigaltstack(null, &context.stack);
442 if (altstack_result != 0) return altstack_result;
443
444 return linux.sigprocmask(0, null, &context.sigmask);
445}
lib/std/os/linux/x86_64.zig+1
...@@ -425,6 +425,7 @@ pub inline fn getcontext(context: *ucontext_t) usize {...@@ -425,6 +425,7 @@ pub inline fn getcontext(context: *ucontext_t) usize {
425 \\ leaq (%[fpmem_offset])(%[context]), %%rcx425 \\ leaq (%[fpmem_offset])(%[context]), %%rcx
426 \\ movq %%rcx, (%[fpstate_offset])(%[context])426 \\ movq %%rcx, (%[fpstate_offset])(%[context])
427 \\ fnstenv (%%rcx)427 \\ fnstenv (%%rcx)
428 \\ fldenv (%%rcx)
428 \\ stmxcsr (%[mxcsr_offset])(%[context])429 \\ stmxcsr (%[mxcsr_offset])(%[context])
429 :430 :
430 : [context] "{rdi}" (context),431 : [context] "{rdi}" (context),
src/crash_report.zig+5-6
...@@ -233,10 +233,9 @@ fn handleSegfaultWindows(info: *os.windows.EXCEPTION_POINTERS) callconv(os.windo...@@ -233,10 +233,9 @@ fn handleSegfaultWindows(info: *os.windows.EXCEPTION_POINTERS) callconv(os.windo
233fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn {233fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn {
234 PanicSwitch.preDispatch();234 PanicSwitch.preDispatch();
235235
236 const stack_ctx = if (@hasDecl(os.windows, "CONTEXT")) ctx: {236 const stack_ctx = if (@hasDecl(os.windows, "CONTEXT"))
237 const regs = info.ContextRecord.getRegs();237 StackContext{ .exception = info.ContextRecord }
238 break :ctx StackContext{ .exception = regs };238 else ctx: {
239 } else ctx: {
240 const addr = @intFromPtr(info.ExceptionRecord.ExceptionAddress);239 const addr = @intFromPtr(info.ExceptionRecord.ExceptionAddress);
241 break :ctx StackContext{ .current = .{ .ret_addr = addr } };240 break :ctx StackContext{ .current = .{ .ret_addr = addr } };
242 };241 };
...@@ -251,7 +250,7 @@ fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg...@@ -251,7 +250,7 @@ fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg
251 },250 },
252 .illegal_instruction => {251 .illegal_instruction => {
253 const ip: ?usize = switch (stack_ctx) {252 const ip: ?usize = switch (stack_ctx) {
254 .exception => |ex| ex.ip,253 .exception => |ex| ex.getRegs().ip,
255 .current => |cur| cur.ret_addr,254 .current => |cur| cur.ret_addr,
256 .not_supported => null,255 .not_supported => null,
257 };256 };
...@@ -272,7 +271,7 @@ const StackContext = union(enum) {...@@ -272,7 +271,7 @@ const StackContext = union(enum) {
272 current: struct {271 current: struct {
273 ret_addr: ?usize,272 ret_addr: ?usize,
274 },273 },
275 exception: debug.StackTraceContext,274 exception: *const debug.StackTraceContext,
276 not_supported: void,275 not_supported: void,
277276
278 pub fn dumpStackTrace(ctx: @This()) void {277 pub fn dumpStackTrace(ctx: @This()) void {