| ... | ... | @@ -570,6 +570,7 @@ pub const StackIterator = struct { |
| 570 | 570 | first_address: ?usize, |
| 571 | 571 | // Last known value of the frame pointer register. |
| 572 | 572 | fp: usize, |
| 573 | ma: MemoryAccessor = MemoryAccessor.init, |
| 573 | 574 | |
| 574 | 575 | // When DebugInfo and a register context is available, this iterator can unwind |
| 575 | 576 | // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer), |
| ... | ... | @@ -616,16 +617,16 @@ pub const StackIterator = struct { |
| 616 | 617 | } |
| 617 | 618 | } |
| 618 | 619 | |
| 619 | | pub fn deinit(self: *StackIterator) void { |
| 620 | | if (have_ucontext and self.unwind_state != null) self.unwind_state.?.dwarf_context.deinit(); |
| 620 | pub fn deinit(it: *StackIterator) void { |
| 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 | 625 | err: UnwindError, |
| 625 | 626 | address: usize, |
| 626 | 627 | } { |
| 627 | 628 | if (!have_ucontext) return null; |
| 628 | | if (self.unwind_state) |*unwind_state| { |
| 629 | if (it.unwind_state) |*unwind_state| { |
| 629 | 630 | if (unwind_state.last_error) |err| { |
| 630 | 631 | unwind_state.last_error = null; |
| 631 | 632 | return .{ |
| ... | ... | @@ -662,14 +663,14 @@ pub const StackIterator = struct { |
| 662 | 663 | else |
| 663 | 664 | @sizeOf(usize); |
| 664 | 665 | |
| 665 | | pub fn next(self: *StackIterator) ?usize { |
| 666 | | var address = self.next_internal() orelse return null; |
| 666 | pub fn next(it: *StackIterator) ?usize { |
| 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 | 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 | 676 | return address; |
| ... | ... | @@ -718,8 +719,74 @@ pub const StackIterator = struct { |
| 718 | 719 | } |
| 719 | 720 | } |
| 720 | 721 | |
| 721 | | fn next_unwind(self: *StackIterator) !usize { |
| 722 | | const unwind_state = &self.unwind_state.?; |
| 722 | pub const MemoryAccessor = struct { |
| 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 | 790 | const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc); |
| 724 | 791 | switch (native_os) { |
| 725 | 792 | .macos, .ios, .watchos, .tvos, .visionos => { |
| ... | ... | @@ -741,13 +808,13 @@ pub const StackIterator = struct { |
| 741 | 808 | } else return error.MissingDebugInfo; |
| 742 | 809 | } |
| 743 | 810 | |
| 744 | | fn next_internal(self: *StackIterator) ?usize { |
| 811 | fn next_internal(it: *StackIterator) ?usize { |
| 745 | 812 | if (have_ucontext) { |
| 746 | | if (self.unwind_state) |*unwind_state| { |
| 813 | if (it.unwind_state) |*unwind_state| { |
| 747 | 814 | if (!unwind_state.failed) { |
| 748 | 815 | if (unwind_state.dwarf_context.pc == 0) return null; |
| 749 | | defer self.fp = unwind_state.dwarf_context.getFp() catch 0; |
| 750 | | if (self.next_unwind()) |return_address| { |
| 816 | defer it.fp = unwind_state.dwarf_context.getFp() catch 0; |
| 817 | if (it.next_unwind()) |return_address| { |
| 751 | 818 | return return_address; |
| 752 | 819 | } else |err| { |
| 753 | 820 | unwind_state.last_error = err; |
| ... | ... | @@ -763,29 +830,24 @@ pub const StackIterator = struct { |
| 763 | 830 | |
| 764 | 831 | const fp = if (comptime native_arch.isSPARC()) |
| 765 | 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 | 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 | 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 | 840 | return null; |
| 773 | 841 | |
| 774 | | const new_fp = math.add(usize, @as(*const usize, @ptrFromInt(fp)).*, fp_bias) catch return null; |
| 775 | | |
| 776 | 842 | // Sanity check: the stack grows down thus all the parent frames must be |
| 777 | 843 | // be at addresses that are greater (or equal) than the previous one. |
| 778 | 844 | // A zero frame pointer often signals this is the last frame, that case |
| 779 | 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 | 848 | return null; |
| 782 | 849 | |
| 783 | | const new_pc = @as( |
| 784 | | *const usize, |
| 785 | | @ptrFromInt(math.add(usize, fp, pc_offset) catch return null), |
| 786 | | ).*; |
| 787 | | |
| 788 | | self.fp = new_fp; |
| 850 | it.fp = new_fp; |
| 789 | 851 | |
| 790 | 852 | return new_pc; |
| 791 | 853 | } |