| ... | @@ -14,6 +14,8 @@ const Dwarf = std.debug.Dwarf; | ... | @@ -14,6 +14,8 @@ const Dwarf = std.debug.Dwarf; |
| 14 | const regBytes = Dwarf.abi.regBytes; | 14 | const regBytes = Dwarf.abi.regBytes; |
| 15 | const regValueNative = Dwarf.abi.regValueNative; | 15 | const regValueNative = Dwarf.abi.regValueNative; |
| 16 | | 16 | |
| | 17 | const root = @import("root"); |
| | 18 | |
| 17 | const SelfInfo = @This(); | 19 | const SelfInfo = @This(); |
| 18 | | 20 | |
| 19 | modules: std.AutoArrayHashMapUnmanaged(usize, Module.DebugInfo), | 21 | modules: std.AutoArrayHashMapUnmanaged(usize, Module.DebugInfo), |
| ... | @@ -33,49 +35,12 @@ pub const Error = error{ | ... | @@ -33,49 +35,12 @@ pub const Error = error{ |
| 33 | }; | 35 | }; |
| 34 | | 36 | |
| 35 | /// Indicates whether the `SelfInfo` implementation has support for this target. | 37 | /// Indicates whether the `SelfInfo` implementation has support for this target. |
| 36 | pub const target_supported: bool = switch (native_os) { | 38 | pub const target_supported: bool = Module != void; |
| 37 | .linux, | | |
| 38 | .freebsd, | | |
| 39 | .netbsd, | | |
| 40 | .dragonfly, | | |
| 41 | .openbsd, | | |
| 42 | .macos, | | |
| 43 | .solaris, | | |
| 44 | .illumos, | | |
| 45 | .windows, | | |
| 46 | => true, | | |
| 47 | else => false, | | |
| 48 | }; | | |
| 49 | | 39 | |
| 50 | /// Indicates whether unwinding for the host is *implemented* here in the Zig | 40 | /// Indicates whether the `SelfInfo` implementation has support for unwinding on this target. |
| 51 | /// standard library. | | |
| 52 | /// | 41 | /// |
| 53 | /// See also `Dwarf.abi.supportsUnwinding` which tells whether Dwarf supports | 42 | /// For whether DWARF unwinding is *theoretically* possible, see `Dwarf.abi.supportsUnwinding`. |
| 54 | /// unwinding on a target *in theory*. | 43 | pub const supports_unwinding: bool = Module.supports_unwinding; |
| 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 | } | | |
| 79 | | 44 | |
| 80 | pub const init: SelfInfo = .{ | 45 | pub const init: SelfInfo = .{ |
| 81 | .modules = .empty, | 46 | .modules = .empty, |
| ... | @@ -114,48 +79,61 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) | ... | @@ -114,48 +79,61 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) |
| 114 | return module.name; | 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`: | 84 | /// This type contains the target-specific implementation. Logically, a `Module` represents a subset |
| 120 | /// * `init: LookupCache` | 85 | /// of the executable with its own debug information. This typically corresponds to what ELF calls a |
| 121 | /// * `deinit: fn (*LookupCache, Allocator) void` | 86 | /// module, i.e. a shared library or executable image, but could be anything. For instance, it would |
| 122 | /// * `lookup: fn (*LookupCache, Allocator, address: usize) !Module` | 87 | /// be valid to consider the entire application one module, or on the other hand to consider each |
| 123 | /// * `key: fn (*const Module) usize` | 88 | /// object file a module. |
| 124 | /// * `DebugInfo: type`, with the following declarations: | | |
| 125 | /// * `DebugInfo.init: DebugInfo` | | |
| 126 | /// * `getSymbolAtAddress: fn (*const Module, Allocator, *DebugInfo, address: usize) !std.debug.Symbol` | | |
| 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` | 92 | /// ``` |
| 131 | const Module = switch (native_os) { | 93 | /// /// Holds state cached by the implementation between calls to `lookup`. |
| 132 | else => {}, // Dwarf, // TODO MLUGG: it's this on master but that's definitely broken atm... | 94 | /// /// This may be `void`, in which case the inner declarations can be omitted. |
| 133 | .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"), | 95 | /// pub const LookupCache = struct { |
| 134 | .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"), | 96 | /// pub const init: LookupCache; |
| 135 | .uefi, .windows => @import("SelfInfo/WindowsModule.zig"), | 97 | /// pub fn deinit(lc: *LookupCache, gpa: Allocator) void; |
| 136 | .wasi, .emscripten => struct { | 98 | /// }; |
| 137 | const LookupCache = void; | 99 | /// /// Holds debug information associated with a particular `Module`. |
| 138 | fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module { | 100 | /// pub const DebugInfo = struct { |
| 139 | _ = cache; | 101 | /// pub const init: DebugInfo; |
| 140 | _ = gpa; | 102 | /// }; |
| 141 | _ = address; | 103 | /// /// Finds the `Module` corresponding to `address`. |
| 142 | @panic("TODO implement lookup module for Wasm"); | 104 | /// pub fn lookup(lc: *LookupCache, gpa: Allocator, address: usize) SelfInfo.Error!Module; |
| 143 | } | 105 | /// /// Returns a unique identifier for this `Module`, such as a load address. |
| 144 | const DebugInfo = struct { | 106 | /// pub fn key(mod: *const Module) usize; |
| 145 | const init: DebugInfo = .{}; | 107 | /// /// Locates and loads location information for the symbol corresponding to `address`. |
| 146 | }; | 108 | /// pub fn getSymbolAtAddress( |
| 147 | fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { | 109 | /// mod: *const Module, |
| 148 | _ = module; | 110 | /// gpa: Allocator, |
| 149 | _ = gpa; | 111 | /// di: *DebugInfo, |
| 150 | _ = di; | 112 | /// address: usize, |
| 151 | _ = address; | 113 | /// ) SelfInfo.Error!std.debug.Symbol; |
| 152 | unreachable; | 114 | /// /// Whether a reliable stack unwinding strategy, such as DWARF unwinding, is available. |
| 153 | } | 115 | /// pub const supports_unwinding: bool; |
| 154 | }, | 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 | pub const UnwindContext = struct { | 138 | pub const UnwindContext = struct { |
| 161 | gpa: Allocator, // MLUGG TODO: make unmanaged (also maybe rename this type, DwarfUnwindContext or smth idk) | 139 | gpa: Allocator, // MLUGG TODO: make unmanaged (also maybe rename this type, DwarfUnwindContext or smth idk) |