Commit 9859440d83
Changed files (5)
lib
std
lib/std/debug/SelfInfo/DarwinModule.zig
@@ -251,6 +251,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
) catch null,
};
}
+pub const supports_unwinding: bool = true;
/// Unwind a frame using MachO compact unwind info (from __unwind_info).
/// If the compact encoding can't encode a way to unwind a frame, it will
/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
lib/std/debug/SelfInfo/ElfModule.zig
@@ -205,6 +205,26 @@ pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, con
}
return error.MissingDebugInfo;
}
+pub const supports_unwinding: bool = s: {
+ const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) {
+ .linux => &.{ .x86, .x86_64, .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be },
+ .netbsd => &.{ .x86, .x86_64, .aarch64, .aarch64_be },
+ .freebsd => &.{ .x86_64, .aarch64, .aarch64_be },
+ .openbsd => &.{.x86_64},
+ .solaris => &.{ .x86, .x86_64 },
+ .illumos => &.{ .x86, .x86_64 },
+ else => unreachable,
+ };
+ for (archs) |a| {
+ if (builtin.target.cpu.arch == a) break :s true;
+ }
+ break :s false;
+};
+comptime {
+ if (supports_unwinding) {
+ std.debug.assert(Dwarf.abi.supportsUnwinding(&builtin.target));
+ }
+}
const ElfModule = @This();
lib/std/debug/SelfInfo/WindowsModule.zig
@@ -246,6 +246,7 @@ pub const DebugInfo = struct {
};
}
};
+pub const supports_unwinding: bool = false;
const WindowsModule = @This();
lib/std/debug/SelfInfo.zig
@@ -14,6 +14,8 @@ const Dwarf = std.debug.Dwarf;
const regBytes = Dwarf.abi.regBytes;
const regValueNative = Dwarf.abi.regValueNative;
+const root = @import("root");
+
const SelfInfo = @This();
modules: std.AutoArrayHashMapUnmanaged(usize, Module.DebugInfo),
@@ -33,49 +35,12 @@ pub const Error = error{
};
/// Indicates whether the `SelfInfo` implementation has support for this target.
-pub const target_supported: bool = switch (native_os) {
- .linux,
- .freebsd,
- .netbsd,
- .dragonfly,
- .openbsd,
- .macos,
- .solaris,
- .illumos,
- .windows,
- => true,
- else => false,
-};
+pub const target_supported: bool = Module != void;
-/// Indicates whether unwinding for the host is *implemented* here in the Zig
-/// standard library.
+/// Indicates whether the `SelfInfo` implementation has support for unwinding on this target.
///
-/// See also `Dwarf.abi.supportsUnwinding` which tells whether Dwarf supports
-/// unwinding on a target *in theory*.
-pub const supports_unwinding: bool = switch (builtin.target.cpu.arch) {
- .x86 => switch (builtin.target.os.tag) {
- .linux, .netbsd, .solaris, .illumos => true,
- else => false,
- },
- .x86_64 => switch (builtin.target.os.tag) {
- .linux, .netbsd, .freebsd, .openbsd, .macos, .ios, .solaris, .illumos => true,
- else => false,
- },
- .arm, .armeb, .thumb, .thumbeb => switch (builtin.target.os.tag) {
- .linux => true,
- else => false,
- },
- .aarch64, .aarch64_be => switch (builtin.target.os.tag) {
- .linux, .netbsd, .freebsd, .macos, .ios => true,
- else => false,
- },
- // Unwinding is possible on other targets but this implementation does
- // not support them...yet!
- else => false,
-};
-comptime {
- if (supports_unwinding) assert(Dwarf.abi.supportsUnwinding(&builtin.target));
-}
+/// For whether DWARF unwinding is *theoretically* possible, see `Dwarf.abi.supportsUnwinding`.
+pub const supports_unwinding: bool = Module.supports_unwinding;
pub const init: SelfInfo = .{
.modules = .empty,
@@ -114,48 +79,61 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize)
return module.name;
}
-/// This type contains the target-specific implementation. It must expose the following declarations:
+/// `void` indicates that `SelfInfo` is not supported for this target.
///
-/// * `LookupCache: type`, with the following declarations unless `LookupCache == void`:
-/// * `init: LookupCache`
-/// * `deinit: fn (*LookupCache, Allocator) void`
-/// * `lookup: fn (*LookupCache, Allocator, address: usize) !Module`
-/// * `key: fn (*const Module) usize`
-/// * `DebugInfo: type`, with the following declarations:
-/// * `DebugInfo.init: DebugInfo`
-/// * `getSymbolAtAddress: fn (*const Module, Allocator, *DebugInfo, address: usize) !std.debug.Symbol`
+/// This type contains the target-specific implementation. Logically, a `Module` represents a subset
+/// of the executable with its own debug information. This typically corresponds to what ELF calls a
+/// module, i.e. a shared library or executable image, but could be anything. For instance, it would
+/// be valid to consider the entire application one module, or on the other hand to consider each
+/// object file a module.
///
-/// If unwinding is supported on this target, it must additionally expose the following declarations:
+/// This type must must expose the following declarations:
///
-/// * `unwindFrame: fn (*const Module, Allocator, *DebugInfo, *UnwindContext) !usize`
-const Module = switch (native_os) {
- else => {}, // Dwarf, // TODO MLUGG: it's this on master but that's definitely broken atm...
- .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"),
- .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"),
- .uefi, .windows => @import("SelfInfo/WindowsModule.zig"),
- .wasi, .emscripten => struct {
- const LookupCache = void;
- fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module {
- _ = cache;
- _ = gpa;
- _ = address;
- @panic("TODO implement lookup module for Wasm");
- }
- const DebugInfo = struct {
- const init: DebugInfo = .{};
- };
- fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
- _ = module;
- _ = gpa;
- _ = di;
- _ = address;
- unreachable;
- }
- },
+/// ```
+/// /// Holds state cached by the implementation between calls to `lookup`.
+/// /// This may be `void`, in which case the inner declarations can be omitted.
+/// pub const LookupCache = struct {
+/// pub const init: LookupCache;
+/// pub fn deinit(lc: *LookupCache, gpa: Allocator) void;
+/// };
+/// /// Holds debug information associated with a particular `Module`.
+/// pub const DebugInfo = struct {
+/// pub const init: DebugInfo;
+/// };
+/// /// Finds the `Module` corresponding to `address`.
+/// pub fn lookup(lc: *LookupCache, gpa: Allocator, address: usize) SelfInfo.Error!Module;
+/// /// Returns a unique identifier for this `Module`, such as a load address.
+/// pub fn key(mod: *const Module) usize;
+/// /// Locates and loads location information for the symbol corresponding to `address`.
+/// pub fn getSymbolAtAddress(
+/// mod: *const Module,
+/// gpa: Allocator,
+/// di: *DebugInfo,
+/// address: usize,
+/// ) SelfInfo.Error!std.debug.Symbol;
+/// /// Whether a reliable stack unwinding strategy, such as DWARF unwinding, is available.
+/// pub const supports_unwinding: bool;
+/// /// Only required if `supports_unwinding == true`. Unwinds a single stack frame and returns
+/// /// the next return address (which may be 0 indicating end of stack). This is currently
+/// /// specialized to DWARF unwinding.
+/// pub fn unwindFrame(
+/// mod: *const Module,
+/// gpa: Allocator,
+/// di: *DebugInfo,
+/// ctx: *SelfInfo.UnwindContext,
+/// ) SelfInfo.Error!usize;
+/// ```
+const Module: type = Module: {
+ if (@hasDecl(root, "debug") and @hasDecl(root.debug, "Module")) {
+ break :Module root.debug.Module;
+ }
+ break :Module switch (native_os) {
+ .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"),
+ .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"),
+ .uefi, .windows => @import("SelfInfo/WindowsModule.zig"),
+ else => void,
+ };
};
-test {
- _ = Module;
-}
pub const UnwindContext = struct {
gpa: Allocator, // MLUGG TODO: make unmanaged (also maybe rename this type, DwarfUnwindContext or smth idk)
lib/std/debug.zig
@@ -14,6 +14,8 @@ const builtin = @import("builtin");
const native_arch = builtin.cpu.arch;
const native_os = builtin.os.tag;
+const root = @import("root");
+
pub const Dwarf = @import("debug/Dwarf.zig");
pub const Pdb = @import("debug/Pdb.zig");
pub const SelfInfo = @import("debug/SelfInfo.zig");
@@ -942,19 +944,15 @@ fn printLineInfo(
tty_config.setColor(writer, .reset) catch {};
}
try writer.writeAll("\n");
- } else |err| switch (err) {
- error.WriteFailed => |e| return e,
- else => {
- // Ignore everything else. Seeing some lines in the trace without the associated
- // source line printed is a far better user experience than interleaving the
- // trace with a load of filesystem error crap. The user can always just open the
- // source file themselves to see the line.
- },
}
}
}
}
fn printLineFromFile(writer: *Writer, source_location: SourceLocation) !void {
+ if (@hasDecl(root, "debug") and @hasDecl(root.debug, "printLineFromFile")) {
+ return root.debug.printLineFromFile(writer, source_location);
+ }
+
// Need this to always block even in async I/O mode, because this could potentially
// be called from e.g. the event loop code crashing.
var f = try fs.cwd().openFile(source_location.file_name, .{});
@@ -1139,11 +1137,16 @@ test printLineFromFile {
/// TODO multithreaded awareness
var debug_info_arena: ?std.heap.ArenaAllocator = null;
+var debug_info_fba: std.heap.FixedBufferAllocator = .init(&debug_info_fba_buf);
+var debug_info_fba_buf: [1024 * 1024 * 4]u8 = undefined;
fn getDebugInfoAllocator() mem.Allocator {
- if (debug_info_arena == null) {
- debug_info_arena = .init(std.heap.page_allocator);
+ if (false) {
+ if (debug_info_arena == null) {
+ debug_info_arena = .init(std.heap.page_allocator);
+ }
+ return debug_info_arena.?.allocator();
}
- return debug_info_arena.?.allocator();
+ return debug_info_fba.allocator();
}
/// Whether or not the current target can print useful debug information when a segfault occurs.