| ... | @@ -570,6 +570,7 @@ pub const StackIterator = struct { | ... | @@ -570,6 +570,7 @@ pub const StackIterator = struct { |
| 570 | first_address: ?usize, | 570 | first_address: ?usize, |
| 571 | // Last known value of the frame pointer register. | 571 | // Last known value of the frame pointer register. |
| 572 | fp: usize, | 572 | fp: usize, |
| | 573 | ma: MemoryAccessor = MemoryAccessor.init, |
| 573 | | 574 | |
| 574 | // When DebugInfo and a register context is available, this iterator can unwind | 575 | // When DebugInfo and a register context is available, this iterator can unwind |
| 575 | // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer), | 576 | // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer), |
| ... | @@ -616,16 +617,16 @@ pub const StackIterator = struct { | ... | @@ -616,16 +617,16 @@ pub const StackIterator = struct { |
| 616 | } | 617 | } |
| 617 | } | 618 | } |
| 618 | | 619 | |
| 619 | pub fn deinit(self: *StackIterator) void { | 620 | pub fn deinit(it: *StackIterator) void { |
| 620 | if (have_ucontext and self.unwind_state != null) self.unwind_state.?.dwarf_context.deinit(); | 621 | if (have_ucontext and it.unwind_state != null) it.unwind_state.?.dwarf_context.deinit(); |
| 621 | } | 622 | } |
| 622 | | 623 | |
| 623 | pub fn getLastError(self: *StackIterator) ?struct { | 624 | pub fn getLastError(it: *StackIterator) ?struct { |
| 624 | err: UnwindError, | 625 | err: UnwindError, |
| 625 | address: usize, | 626 | address: usize, |
| 626 | } { | 627 | } { |
| 627 | if (!have_ucontext) return null; | 628 | if (!have_ucontext) return null; |
| 628 | if (self.unwind_state) |*unwind_state| { | 629 | if (it.unwind_state) |*unwind_state| { |
| 629 | if (unwind_state.last_error) |err| { | 630 | if (unwind_state.last_error) |err| { |
| 630 | unwind_state.last_error = null; | 631 | unwind_state.last_error = null; |
| 631 | return .{ | 632 | return .{ |
| ... | @@ -662,14 +663,14 @@ pub const StackIterator = struct { | ... | @@ -662,14 +663,14 @@ pub const StackIterator = struct { |
| 662 | else | 663 | else |
| 663 | @sizeOf(usize); | 664 | @sizeOf(usize); |
| 664 | | 665 | |
| 665 | pub fn next(self: *StackIterator) ?usize { | 666 | pub fn next(it: *StackIterator) ?usize { |
| 666 | var address = self.next_internal() orelse return null; | 667 | var address = it.next_internal() orelse return null; |
| 667 | | 668 | |
| 668 | if (self.first_address) |first_address| { | 669 | if (it.first_address) |first_address| { |
| 669 | while (address != first_address) { | 670 | while (address != first_address) { |
| 670 | address = self.next_internal() orelse return null; | 671 | address = it.next_internal() orelse return null; |
| 671 | } | 672 | } |
| 672 | self.first_address = null; | 673 | it.first_address = null; |
| 673 | } | 674 | } |
| 674 | | 675 | |
| 675 | return address; | 676 | return address; |
| ... | @@ -718,8 +719,74 @@ pub const StackIterator = struct { | ... | @@ -718,8 +719,74 @@ pub const StackIterator = struct { |
| 718 | } | 719 | } |
| 719 | } | 720 | } |
| 720 | | 721 | |
| 721 | fn next_unwind(self: *StackIterator) !usize { | 722 | pub const MemoryAccessor = struct { |
| 722 | const unwind_state = &self.unwind_state.?; | 723 | var cached_pid: posix.pid_t = -1; |
| | 724 | |
| | 725 | mem: switch (native_os) { |
| | 726 | .linux => File, |
| | 727 | else => void, |
| | 728 | }, |
| | 729 | |
| | 730 | pub const init: MemoryAccessor = .{ |
| | 731 | .mem = switch (native_os) { |
| | 732 | .linux => .{ .handle = -1 }, |
| | 733 | else => {}, |
| | 734 | }, |
| | 735 | }; |
| | 736 | |
| | 737 | fn read(ma: *MemoryAccessor, address: usize, buf: []u8) bool { |
| | 738 | switch (native_os) { |
| | 739 | .linux => while (true) switch (ma.mem.handle) { |
| | 740 | -2 => break, |
| | 741 | -1 => { |
| | 742 | const linux = std.os.linux; |
| | 743 | const pid = switch (@atomicLoad(posix.pid_t, &cached_pid, .monotonic)) { |
| | 744 | -1 => pid: { |
| | 745 | const pid = linux.getpid(); |
| | 746 | @atomicStore(posix.pid_t, &cached_pid, pid, .monotonic); |
| | 747 | break :pid pid; |
| | 748 | }, |
| | 749 | else => |pid| pid, |
| | 750 | }; |
| | 751 | const bytes_read = linux.process_vm_readv( |
| | 752 | pid, |
| | 753 | &.{.{ .base = buf.ptr, .len = buf.len }}, |
| | 754 | &.{.{ .base = @ptrFromInt(address), .len = buf.len }}, |
| | 755 | 0, |
| | 756 | ); |
| | 757 | switch (linux.E.init(bytes_read)) { |
| | 758 | .SUCCESS => return bytes_read == buf.len, |
| | 759 | .FAULT => return false, |
| | 760 | .INVAL, .PERM, .SRCH => unreachable, // own pid is always valid |
| | 761 | .NOMEM, .NOSYS => {}, |
| | 762 | else => unreachable, // unexpected |
| | 763 | } |
| | 764 | var path_buf: [ |
| | 765 | std.fmt.count("/proc/{d}/mem", .{math.minInt(posix.pid_t)}) |
| | 766 | ]u8 = undefined; |
| | 767 | const path = std.fmt.bufPrint(&path_buf, "/proc/{d}/mem", .{pid}) catch |
| | 768 | unreachable; |
| | 769 | ma.mem = std.fs.openFileAbsolute(path, .{}) catch { |
| | 770 | ma.mem.handle = -2; |
| | 771 | break; |
| | 772 | }; |
| | 773 | }, |
| | 774 | else => return (ma.mem.pread(buf, address) catch return false) == buf.len, |
| | 775 | }, |
| | 776 | else => {}, |
| | 777 | } |
| | 778 | if (!isValidMemory(address)) return false; |
| | 779 | @memcpy(buf, @as([*]const u8, @ptrFromInt(address))); |
| | 780 | return true; |
| | 781 | } |
| | 782 | pub fn load(ma: *MemoryAccessor, comptime Type: type, address: usize) ?Type { |
| | 783 | var result: Type = undefined; |
| | 784 | return if (ma.read(address, std.mem.asBytes(&result))) result else null; |
| | 785 | } |
| | 786 | }; |
| | 787 | |
| | 788 | fn next_unwind(it: *StackIterator) !usize { |
| | 789 | const unwind_state = &it.unwind_state.?; |
| 723 | const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc); | 790 | const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc); |
| 724 | switch (native_os) { | 791 | switch (native_os) { |
| 725 | .macos, .ios, .watchos, .tvos, .visionos => { | 792 | .macos, .ios, .watchos, .tvos, .visionos => { |
| ... | @@ -741,13 +808,13 @@ pub const StackIterator = struct { | ... | @@ -741,13 +808,13 @@ pub const StackIterator = struct { |
| 741 | } else return error.MissingDebugInfo; | 808 | } else return error.MissingDebugInfo; |
| 742 | } | 809 | } |
| 743 | | 810 | |
| 744 | fn next_internal(self: *StackIterator) ?usize { | 811 | fn next_internal(it: *StackIterator) ?usize { |
| 745 | if (have_ucontext) { | 812 | if (have_ucontext) { |
| 746 | if (self.unwind_state) |*unwind_state| { | 813 | if (it.unwind_state) |*unwind_state| { |
| 747 | if (!unwind_state.failed) { | 814 | if (!unwind_state.failed) { |
| 748 | if (unwind_state.dwarf_context.pc == 0) return null; | 815 | if (unwind_state.dwarf_context.pc == 0) return null; |
| 749 | defer self.fp = unwind_state.dwarf_context.getFp() catch 0; | 816 | defer it.fp = unwind_state.dwarf_context.getFp() catch 0; |
| 750 | if (self.next_unwind()) |return_address| { | 817 | if (it.next_unwind()) |return_address| { |
| 751 | return return_address; | 818 | return return_address; |
| 752 | } else |err| { | 819 | } else |err| { |
| 753 | unwind_state.last_error = err; | 820 | unwind_state.last_error = err; |
| ... | @@ -763,29 +830,24 @@ pub const StackIterator = struct { | ... | @@ -763,29 +830,24 @@ pub const StackIterator = struct { |
| 763 | | 830 | |
| 764 | const fp = if (comptime native_arch.isSPARC()) | 831 | const fp = if (comptime native_arch.isSPARC()) |
| 765 | // On SPARC the offset is positive. (!) | 832 | // On SPARC the offset is positive. (!) |
| 766 | math.add(usize, self.fp, fp_offset) catch return null | 833 | math.add(usize, it.fp, fp_offset) catch return null |
| 767 | else | 834 | else |
| 768 | math.sub(usize, self.fp, fp_offset) catch return null; | 835 | math.sub(usize, it.fp, fp_offset) catch return null; |
| 769 | | 836 | |
| 770 | // Sanity check. | 837 | // Sanity check. |
| 771 | if (fp == 0 or !mem.isAligned(fp, @alignOf(usize)) or !isValidMemory(fp)) | 838 | if (fp == 0 or !mem.isAligned(fp, @alignOf(usize))) return null; |
| | 839 | const new_fp = math.add(usize, it.ma.load(usize, fp) orelse return null, fp_bias) catch |
| 772 | return null; | 840 | return null; |
| 773 | | 841 | |
| 774 | const new_fp = math.add(usize, @as(*const usize, @ptrFromInt(fp)).*, fp_bias) catch return null; | | |
| 775 | | | |
| 776 | // Sanity check: the stack grows down thus all the parent frames must be | 842 | // Sanity check: the stack grows down thus all the parent frames must be |
| 777 | // be at addresses that are greater (or equal) than the previous one. | 843 | // be at addresses that are greater (or equal) than the previous one. |
| 778 | // A zero frame pointer often signals this is the last frame, that case | 844 | // A zero frame pointer often signals this is the last frame, that case |
| 779 | // is gracefully handled by the next call to next_internal. | 845 | // is gracefully handled by the next call to next_internal. |
| 780 | if (new_fp != 0 and new_fp < self.fp) | 846 | if (new_fp != 0 and new_fp < it.fp) return null; |
| | 847 | const new_pc = it.ma.load(usize, math.add(usize, fp, pc_offset) catch return null) orelse |
| 781 | return null; | 848 | return null; |
| 782 | | 849 | |
| 783 | const new_pc = @as( | 850 | it.fp = new_fp; |
| 784 | *const usize, | | |
| 785 | @ptrFromInt(math.add(usize, fp, pc_offset) catch return null), | | |
| 786 | ).*; | | |
| 787 | | | |
| 788 | self.fp = new_fp; | | |
| 789 | | 851 | |
| 790 | return new_pc; | 852 | return new_pc; |
| 791 | } | 853 | } |