| ... | ... | @@ -14,6 +14,8 @@ const Dwarf = std.debug.Dwarf; |
| 14 | 14 | const regBytes = Dwarf.abi.regBytes; |
| 15 | 15 | const regValueNative = Dwarf.abi.regValueNative; |
| 16 | 16 | |
| 17 | const root = @import("root"); |
| 18 | |
| 17 | 19 | const SelfInfo = @This(); |
| 18 | 20 | |
| 19 | 21 | modules: std.AutoArrayHashMapUnmanaged(usize, Module.DebugInfo), |
| ... | ... | @@ -33,49 +35,12 @@ pub const Error = error{ |
| 33 | 35 | }; |
| 34 | 36 | |
| 35 | 37 | /// Indicates whether the `SelfInfo` implementation has support for this target. |
| 36 | | pub const target_supported: bool = switch (native_os) { |
| 37 | | .linux, |
| 38 | | .freebsd, |
| 39 | | .netbsd, |
| 40 | | .dragonfly, |
| 41 | | .openbsd, |
| 42 | | .macos, |
| 43 | | .solaris, |
| 44 | | .illumos, |
| 45 | | .windows, |
| 46 | | => true, |
| 47 | | else => false, |
| 48 | | }; |
| 38 | pub const target_supported: bool = Module != void; |
| 49 | 39 | |
| 50 | | /// Indicates whether unwinding for the host is *implemented* here in the Zig |
| 51 | | /// standard library. |
| 40 | /// Indicates whether the `SelfInfo` implementation has support for unwinding on this target. |
| 52 | 41 | /// |
| 53 | | /// See also `Dwarf.abi.supportsUnwinding` which tells whether Dwarf supports |
| 54 | | /// unwinding on a target *in theory*. |
| 55 | | pub const supports_unwinding: bool = switch (builtin.target.cpu.arch) { |
| 56 | | .x86 => switch (builtin.target.os.tag) { |
| 57 | | .linux, .netbsd, .solaris, .illumos => true, |
| 58 | | else => false, |
| 59 | | }, |
| 60 | | .x86_64 => switch (builtin.target.os.tag) { |
| 61 | | .linux, .netbsd, .freebsd, .openbsd, .macos, .ios, .solaris, .illumos => true, |
| 62 | | else => false, |
| 63 | | }, |
| 64 | | .arm, .armeb, .thumb, .thumbeb => switch (builtin.target.os.tag) { |
| 65 | | .linux => true, |
| 66 | | else => false, |
| 67 | | }, |
| 68 | | .aarch64, .aarch64_be => switch (builtin.target.os.tag) { |
| 69 | | .linux, .netbsd, .freebsd, .macos, .ios => true, |
| 70 | | else => false, |
| 71 | | }, |
| 72 | | // Unwinding is possible on other targets but this implementation does |
| 73 | | // not support them...yet! |
| 74 | | else => false, |
| 75 | | }; |
| 76 | | comptime { |
| 77 | | if (supports_unwinding) assert(Dwarf.abi.supportsUnwinding(&builtin.target)); |
| 78 | | } |
| 42 | /// For whether DWARF unwinding is *theoretically* possible, see `Dwarf.abi.supportsUnwinding`. |
| 43 | pub const supports_unwinding: bool = Module.supports_unwinding; |
| 79 | 44 | |
| 80 | 45 | pub const init: SelfInfo = .{ |
| 81 | 46 | .modules = .empty, |
| ... | ... | @@ -114,48 +79,61 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) |
| 114 | 79 | return module.name; |
| 115 | 80 | } |
| 116 | 81 | |
| 117 | | /// This type contains the target-specific implementation. It must expose the following declarations: |
| 82 | /// `void` indicates that `SelfInfo` is not supported for this target. |
| 118 | 83 | /// |
| 119 | | /// * `LookupCache: type`, with the following declarations unless `LookupCache == void`: |
| 120 | | /// * `init: LookupCache` |
| 121 | | /// * `deinit: fn (*LookupCache, Allocator) void` |
| 122 | | /// * `lookup: fn (*LookupCache, Allocator, address: usize) !Module` |
| 123 | | /// * `key: fn (*const Module) usize` |
| 124 | | /// * `DebugInfo: type`, with the following declarations: |
| 125 | | /// * `DebugInfo.init: DebugInfo` |
| 126 | | /// * `getSymbolAtAddress: fn (*const Module, Allocator, *DebugInfo, address: usize) !std.debug.Symbol` |
| 84 | /// This type contains the target-specific implementation. Logically, a `Module` represents a subset |
| 85 | /// of the executable with its own debug information. This typically corresponds to what ELF calls a |
| 86 | /// module, i.e. a shared library or executable image, but could be anything. For instance, it would |
| 87 | /// be valid to consider the entire application one module, or on the other hand to consider each |
| 88 | /// object file a module. |
| 127 | 89 | /// |
| 128 | | /// If unwinding is supported on this target, it must additionally expose the following declarations: |
| 90 | /// This type must must expose the following declarations: |
| 129 | 91 | /// |
| 130 | | /// * `unwindFrame: fn (*const Module, Allocator, *DebugInfo, *UnwindContext) !usize` |
| 131 | | const Module = switch (native_os) { |
| 132 | | else => {}, // Dwarf, // TODO MLUGG: it's this on master but that's definitely broken atm... |
| 133 | | .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"), |
| 134 | | .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"), |
| 135 | | .uefi, .windows => @import("SelfInfo/WindowsModule.zig"), |
| 136 | | .wasi, .emscripten => struct { |
| 137 | | const LookupCache = void; |
| 138 | | fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module { |
| 139 | | _ = cache; |
| 140 | | _ = gpa; |
| 141 | | _ = address; |
| 142 | | @panic("TODO implement lookup module for Wasm"); |
| 143 | | } |
| 144 | | const DebugInfo = struct { |
| 145 | | const init: DebugInfo = .{}; |
| 146 | | }; |
| 147 | | fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { |
| 148 | | _ = module; |
| 149 | | _ = gpa; |
| 150 | | _ = di; |
| 151 | | _ = address; |
| 152 | | unreachable; |
| 153 | | } |
| 154 | | }, |
| 92 | /// ``` |
| 93 | /// /// Holds state cached by the implementation between calls to `lookup`. |
| 94 | /// /// This may be `void`, in which case the inner declarations can be omitted. |
| 95 | /// pub const LookupCache = struct { |
| 96 | /// pub const init: LookupCache; |
| 97 | /// pub fn deinit(lc: *LookupCache, gpa: Allocator) void; |
| 98 | /// }; |
| 99 | /// /// Holds debug information associated with a particular `Module`. |
| 100 | /// pub const DebugInfo = struct { |
| 101 | /// pub const init: DebugInfo; |
| 102 | /// }; |
| 103 | /// /// Finds the `Module` corresponding to `address`. |
| 104 | /// pub fn lookup(lc: *LookupCache, gpa: Allocator, address: usize) SelfInfo.Error!Module; |
| 105 | /// /// Returns a unique identifier for this `Module`, such as a load address. |
| 106 | /// pub fn key(mod: *const Module) usize; |
| 107 | /// /// Locates and loads location information for the symbol corresponding to `address`. |
| 108 | /// pub fn getSymbolAtAddress( |
| 109 | /// mod: *const Module, |
| 110 | /// gpa: Allocator, |
| 111 | /// di: *DebugInfo, |
| 112 | /// address: usize, |
| 113 | /// ) SelfInfo.Error!std.debug.Symbol; |
| 114 | /// /// Whether a reliable stack unwinding strategy, such as DWARF unwinding, is available. |
| 115 | /// pub const supports_unwinding: bool; |
| 116 | /// /// Only required if `supports_unwinding == true`. Unwinds a single stack frame and returns |
| 117 | /// /// the next return address (which may be 0 indicating end of stack). This is currently |
| 118 | /// /// specialized to DWARF unwinding. |
| 119 | /// pub fn unwindFrame( |
| 120 | /// mod: *const Module, |
| 121 | /// gpa: Allocator, |
| 122 | /// di: *DebugInfo, |
| 123 | /// ctx: *SelfInfo.UnwindContext, |
| 124 | /// ) SelfInfo.Error!usize; |
| 125 | /// ``` |
| 126 | const Module: type = Module: { |
| 127 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "Module")) { |
| 128 | break :Module root.debug.Module; |
| 129 | } |
| 130 | break :Module switch (native_os) { |
| 131 | .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"), |
| 132 | .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"), |
| 133 | .uefi, .windows => @import("SelfInfo/WindowsModule.zig"), |
| 134 | else => void, |
| 135 | }; |
| 155 | 136 | }; |
| 156 | | test { |
| 157 | | _ = Module; |
| 158 | | } |
| 159 | 137 | |
| 160 | 138 | pub const UnwindContext = struct { |
| 161 | 139 | gpa: Allocator, // MLUGG TODO: make unmanaged (also maybe rename this type, DwarfUnwindContext or smth idk) |