Commit c895aa7a35
Changed files (6)
lib
lib/std/debug/SelfInfo/DarwinModule.zig
@@ -7,7 +7,9 @@ pub fn key(m: *const DarwinModule) usize {
return m.text_base;
}
-pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !DarwinModule {
+/// No cache needed, because `_dyld_get_image_header` etc are already fast.
+pub const LookupCache = void;
+pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!DarwinModule {
_ = cache;
_ = gpa;
const image_count = std.c._dyld_image_count();
@@ -186,8 +188,11 @@ fn loadFullInfo(module: *const DarwinModule, gpa: Allocator) !DebugInfo.Full {
.ofiles = .empty,
};
}
-pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
- if (di.full == null) di.full = try module.loadFullInfo(gpa);
+pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) Error!std.debug.Symbol {
+ if (di.full == null) di.full = module.loadFullInfo(gpa) catch |err| switch (err) {
+ error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory, error.Unexpected => |e| return e,
+ else => return error.ReadFailed,
+ };
const full = &di.full.?;
const vaddr = address - module.load_offset;
@@ -215,14 +220,9 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
const gop = try full.ofiles.getOrPut(gpa, symbol.ofile);
if (!gop.found_existing) {
const o_file_path = mem.sliceTo(full.strings[symbol.ofile..], 0);
- gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch |err| {
- defer _ = full.ofiles.pop().?;
- switch (err) {
- error.MissingDebugInfo,
- error.InvalidDebugInfo,
- => return sym_only_result,
- else => |e| return e,
- }
+ gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch {
+ _ = full.ofiles.pop().?;
+ return sym_only_result;
};
}
break :of gop.value_ptr;
@@ -234,10 +234,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
) orelse return sym_only_result;
const symbol_ofile_vaddr = o_file.symtab[symbol_index].n_value;
- const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => return sym_only_result,
- else => |e| return e,
- };
+ const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch return sym_only_result;
return .{
.name = o_file.dwarf.getSymbolName(symbol_ofile_vaddr) orelse stab_symbol,
@@ -255,28 +252,44 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
native_endian,
compile_unit,
symbol_ofile_vaddr + address_symbol_offset,
- ) catch |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => null,
- else => return err,
- },
+ ) catch null,
};
}
/// 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.
-pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
+pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize {
+ return unwindFrameInner(module, gpa, di, context) catch |err| switch (err) {
+ error.InvalidDebugInfo,
+ error.MissingDebugInfo,
+ error.UnsupportedDebugInfo,
+ error.ReadFailed,
+ error.OutOfMemory,
+ error.Unexpected,
+ => |e| return e,
+ error.UnimplementedArch,
+ error.UnimplementedOs,
+ error.ThreadContextNotSupported,
+ => return error.UnsupportedDebugInfo,
+ error.InvalidRegister,
+ error.RegisterContextRequired,
+ error.IncompatibleRegisterSize,
+ => return error.InvalidDebugInfo,
+ };
+}
+fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
_ = gpa;
if (di.unwind == null) di.unwind = module.loadUnwindInfo();
const unwind = &di.unwind.?;
- const unwind_info = unwind.unwind_info orelse return error.MissingUnwindInfo;
- if (unwind_info.len < @sizeOf(macho.unwind_info_section_header)) return error.InvalidUnwindInfo;
+ const unwind_info = unwind.unwind_info orelse return error.MissingDebugInfo;
+ if (unwind_info.len < @sizeOf(macho.unwind_info_section_header)) return error.InvalidDebugInfo;
const header: *align(1) const macho.unwind_info_section_header = @ptrCast(unwind_info);
const index_byte_count = header.indexCount * @sizeOf(macho.unwind_info_section_header_index_entry);
- if (unwind_info.len < header.indexSectionOffset + index_byte_count) return error.InvalidUnwindInfo;
+ if (unwind_info.len < header.indexSectionOffset + index_byte_count) return error.InvalidDebugInfo;
const indices: []align(1) const macho.unwind_info_section_header_index_entry = @ptrCast(unwind_info[header.indexSectionOffset..][0..index_byte_count]);
- if (indices.len == 0) return error.MissingUnwindInfo;
+ if (indices.len == 0) return error.MissingDebugInfo;
// offset of the PC into the `__TEXT` segment
const pc_text_offset = context.pc - module.text_base;
@@ -296,15 +309,15 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
break :index .{ indices[left].secondLevelPagesSectionOffset, indices[left].functionOffset };
};
// An offset of 0 is a sentinel indicating a range does not have unwind info.
- if (start_offset == 0) return error.MissingUnwindInfo;
+ if (start_offset == 0) return error.MissingDebugInfo;
const common_encodings_byte_count = header.commonEncodingsArrayCount * @sizeOf(macho.compact_unwind_encoding_t);
- if (unwind_info.len < header.commonEncodingsArraySectionOffset + common_encodings_byte_count) return error.InvalidUnwindInfo;
+ if (unwind_info.len < header.commonEncodingsArraySectionOffset + common_encodings_byte_count) return error.InvalidDebugInfo;
const common_encodings: []align(1) const macho.compact_unwind_encoding_t = @ptrCast(
unwind_info[header.commonEncodingsArraySectionOffset..][0..common_encodings_byte_count],
);
- if (unwind_info.len < start_offset + @sizeOf(macho.UNWIND_SECOND_LEVEL)) return error.InvalidUnwindInfo;
+ if (unwind_info.len < start_offset + @sizeOf(macho.UNWIND_SECOND_LEVEL)) return error.InvalidDebugInfo;
const kind: *align(1) const macho.UNWIND_SECOND_LEVEL = @ptrCast(unwind_info[start_offset..]);
const entry: struct {
@@ -312,15 +325,15 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
raw_encoding: u32,
} = switch (kind.*) {
.REGULAR => entry: {
- if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_regular_second_level_page_header)) return error.InvalidUnwindInfo;
+ if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_regular_second_level_page_header)) return error.InvalidDebugInfo;
const page_header: *align(1) const macho.unwind_info_regular_second_level_page_header = @ptrCast(unwind_info[start_offset..]);
const entries_byte_count = page_header.entryCount * @sizeOf(macho.unwind_info_regular_second_level_entry);
- if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidUnwindInfo;
+ if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidDebugInfo;
const entries: []align(1) const macho.unwind_info_regular_second_level_entry = @ptrCast(
unwind_info[start_offset + page_header.entryPageOffset ..][0..entries_byte_count],
);
- if (entries.len == 0) return error.InvalidUnwindInfo;
+ if (entries.len == 0) return error.InvalidDebugInfo;
var left: usize = 0;
var len: usize = entries.len;
@@ -339,15 +352,15 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
};
},
.COMPRESSED => entry: {
- if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_compressed_second_level_page_header)) return error.InvalidUnwindInfo;
+ if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_compressed_second_level_page_header)) return error.InvalidDebugInfo;
const page_header: *align(1) const macho.unwind_info_compressed_second_level_page_header = @ptrCast(unwind_info[start_offset..]);
const entries_byte_count = page_header.entryCount * @sizeOf(macho.UnwindInfoCompressedEntry);
- if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidUnwindInfo;
+ if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidDebugInfo;
const entries: []align(1) const macho.UnwindInfoCompressedEntry = @ptrCast(
unwind_info[start_offset + page_header.entryPageOffset ..][0..entries_byte_count],
);
- if (entries.len == 0) return error.InvalidUnwindInfo;
+ if (entries.len == 0) return error.InvalidDebugInfo;
var left: usize = 0;
var len: usize = entries.len;
@@ -372,26 +385,26 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
const local_index = entry.encodingIndex - common_encodings.len;
const local_encodings_byte_count = page_header.encodingsCount * @sizeOf(macho.compact_unwind_encoding_t);
- if (unwind_info.len < start_offset + page_header.encodingsPageOffset + local_encodings_byte_count) return error.InvalidUnwindInfo;
+ if (unwind_info.len < start_offset + page_header.encodingsPageOffset + local_encodings_byte_count) return error.InvalidDebugInfo;
const local_encodings: []align(1) const macho.compact_unwind_encoding_t = @ptrCast(
unwind_info[start_offset + page_header.encodingsPageOffset ..][0..local_encodings_byte_count],
);
- if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
+ if (local_index >= local_encodings.len) return error.InvalidDebugInfo;
break :entry .{
.function_offset = function_offset,
.raw_encoding = local_encodings[local_index],
};
},
- else => return error.InvalidUnwindInfo,
+ else => return error.InvalidDebugInfo,
};
- if (entry.raw_encoding == 0) return error.NoUnwindInfo;
+ if (entry.raw_encoding == 0) return error.MissingDebugInfo;
const reg_context: Dwarf.abi.RegisterContext = .{ .eh_frame = false, .is_macho = true };
const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding);
const new_ip = switch (builtin.cpu.arch) {
.x86_64 => switch (encoding.mode.x86_64) {
- .OLD => return error.UnimplementedUnwindEncoding,
+ .OLD => return error.UnsupportedDebugInfo,
.RBP_FRAME => ip: {
const frame = encoding.value.x86_64.frame;
@@ -493,7 +506,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
break :ip new_ip;
},
.DWARF => {
- const eh_frame = unwind.eh_frame orelse return error.MissingEhFrame;
+ const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo;
const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset;
return context.unwindFrameDwarf(
&.initSection(.eh_frame, eh_frame_vaddr, eh_frame),
@@ -503,7 +516,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
},
},
.aarch64, .aarch64_be => switch (encoding.mode.arm64) {
- .OLD => return error.UnimplementedUnwindEncoding,
+ .OLD => return error.UnsupportedDebugInfo,
.FRAMELESS => ip: {
const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*;
const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;
@@ -512,7 +525,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
break :ip new_ip;
},
.DWARF => {
- const eh_frame = unwind.eh_frame orelse return error.MissingEhFrame;
+ const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo;
const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset;
return context.unwindFrameDwarf(
&.initSection(.eh_frame, eh_frame_vaddr, eh_frame),
@@ -568,8 +581,6 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
if (context.pc > 0) context.pc -= 1;
return new_ip;
}
-/// No cache needed, because `_dyld_get_image_header` etc are already fast.
-pub const LookupCache = void;
pub const DebugInfo = struct {
unwind: ?Unwind,
// MLUGG TODO: awful field name
@@ -785,7 +796,7 @@ const ip_reg_num = Dwarf.abi.ipRegNum(builtin.target.cpu.arch).?;
fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 {
const file = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) {
error.FileNotFound => return error.MissingDebugInfo,
- else => |e| return e,
+ else => return error.ReadFailed,
};
defer file.close();
@@ -812,6 +823,7 @@ const mem = std.mem;
const posix = std.posix;
const testing = std.testing;
const UnwindContext = std.debug.SelfInfo.UnwindContext;
+const Error = std.debug.SelfInfo.Error;
const regBytes = Dwarf.abi.regBytes;
const regValueNative = Dwarf.abi.regValueNative;
lib/std/debug/SelfInfo/ElfModule.zig
@@ -21,7 +21,7 @@ pub const DebugInfo = struct {
pub fn key(m: ElfModule) usize {
return m.load_offset;
}
-pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !ElfModule {
+pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModule {
_ = cache;
_ = gpa;
if (builtin.target.os.tag == .haiku) @panic("TODO implement lookup module for Haiku");
@@ -92,42 +92,79 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !ElfModule {
};
return error.MissingDebugInfo;
}
-fn loadDwarf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !void {
- if (module.name.len > 0) {
- di.loaded_elf = Dwarf.ElfModule.load(gpa, .{
+fn loadDwarf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void {
+ const load_result = if (module.name.len > 0) res: {
+ break :res Dwarf.ElfModule.load(gpa, .{
.root_dir = .cwd(),
.sub_path = module.name,
- }, module.build_id, null, null, null) catch |err| switch (err) {
- error.FileNotFound => return error.MissingDebugInfo,
- error.Overflow => return error.InvalidDebugInfo,
- else => |e| return e,
+ }, module.build_id, null, null, null);
+ } else res: {
+ const path = std.fs.selfExePathAlloc(gpa) catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
+ else => return error.ReadFailed,
};
- } else {
- const path = try std.fs.selfExePathAlloc(gpa);
defer gpa.free(path);
- di.loaded_elf = Dwarf.ElfModule.load(gpa, .{
+ break :res Dwarf.ElfModule.load(gpa, .{
.root_dir = .cwd(),
.sub_path = path,
- }, module.build_id, null, null, null) catch |err| switch (err) {
- error.FileNotFound => return error.MissingDebugInfo,
- error.Overflow => return error.InvalidDebugInfo,
- else => |e| return e,
- };
- }
+ }, module.build_id, null, null, null);
+ };
+ di.loaded_elf = load_result catch |err| switch (err) {
+ error.FileNotFound => return error.MissingDebugInfo,
+
+ error.OutOfMemory,
+ error.InvalidDebugInfo,
+ error.MissingDebugInfo,
+ error.Unexpected,
+ => |e| return e,
+
+ error.InvalidElfEndian,
+ error.InvalidElfMagic,
+ error.InvalidElfVersion,
+ error.InvalidUtf8,
+ error.InvalidWtf8,
+ error.EndOfStream,
+ error.Overflow,
+ error.UnimplementedDwarfForeignEndian, // this should be impossible as we're looking at the debug info for this process
+ => return error.InvalidDebugInfo,
+
+ else => return error.ReadFailed,
+ };
}
-pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
+pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, address: usize) Error!std.debug.Symbol {
if (di.loaded_elf == null) try module.loadDwarf(gpa, di);
const vaddr = address - module.load_offset;
- return di.loaded_elf.?.dwarf.getSymbol(gpa, native_endian, vaddr);
+ return di.loaded_elf.?.dwarf.getSymbol(gpa, native_endian, vaddr) catch |err| switch (err) {
+ error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e,
+ error.ReadFailed,
+ error.EndOfStream,
+ error.Overflow,
+ error.StreamTooLong,
+ => return error.InvalidDebugInfo,
+ };
}
-fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !void {
- const section_bytes = module.gnu_eh_frame orelse return error.MissingUnwindInfo; // MLUGG TODO: load from file
+fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void {
+ const section_bytes = module.gnu_eh_frame orelse return error.MissingDebugInfo; // MLUGG TODO: load from file
+
const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset;
- const header: Dwarf.Unwind.EhFrameHeader = try .parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian);
- di.unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr));
- try di.unwind.?.prepareLookup(gpa, @sizeOf(usize), native_endian);
+ const header = Dwarf.Unwind.EhFrameHeader.parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian) catch |err| switch (err) {
+ error.ReadFailed => unreachable, // it's all fixed buffers
+ error.InvalidDebugInfo => |e| return e,
+ error.EndOfStream, error.Overflow => return error.InvalidDebugInfo,
+ error.UnsupportedAddrSize => return error.UnsupportedDebugInfo,
+ };
+
+ var unwind: Dwarf.Unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr));
+ unwind.prepareLookup(gpa, @sizeOf(usize), native_endian) catch |err| switch (err) {
+ error.ReadFailed => unreachable, // it's all fixed buffers
+ error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e,
+ error.EndOfStream, error.Overflow, error.StreamTooLong => return error.InvalidDebugInfo,
+ error.UnsupportedAddrSize, error.UnsupportedDwarfVersion => return error.UnsupportedDebugInfo,
+ };
+
+ di.unwind = unwind;
}
-pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
+pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize {
if (di.unwind == null) try module.loadUnwindInfo(gpa, di);
return context.unwindFrameDwarf(&di.unwind.?, module.load_offset, null);
}
@@ -140,6 +177,7 @@ const Dwarf = std.debug.Dwarf;
const elf = std.elf;
const mem = std.mem;
const UnwindContext = std.debug.SelfInfo.UnwindContext;
+const Error = std.debug.SelfInfo.Error;
const builtin = @import("builtin");
const native_endian = builtin.target.cpu.arch.endian();
lib/std/debug/SelfInfo/WindowsModule.zig
@@ -5,7 +5,7 @@ handle: windows.HMODULE,
pub fn key(m: WindowsModule) usize {
return m.base_address;
}
-pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !WindowsModule {
+pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) std.debug.SelfInfo.Error!WindowsModule {
if (lookupInCache(cache, address)) |m| return m;
{
// Check a new module hasn't been loaded
@@ -29,18 +29,23 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !WindowsModul
if (lookupInCache(cache, address)) |m| return m;
return error.MissingDebugInfo;
}
-pub fn getSymbolAtAddress(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
- if (!di.loaded) try module.loadLocationInfo(gpa, di);
+pub fn getSymbolAtAddress(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, address: usize) std.debug.SelfInfo.Error!std.debug.Symbol {
+ if (!di.loaded) module.loadDebugInfo(gpa, di) catch |err| switch (err) {
+ error.OutOfMemory, error.InvalidDebugInfo, error.MissingDebugInfo, error.Unexpected => |e| return e,
+ error.FileNotFound => return error.MissingDebugInfo,
+ error.UnknownPDBVersion => return error.UnsupportedDebugInfo,
+ else => return error.ReadFailed,
+ };
// Translate the runtime address into a virtual address into the module
const vaddr = address - module.base_address;
if (di.pdb != null) {
- if (try di.getSymbolFromPdb(vaddr)) |symbol| return symbol;
+ if (di.getSymbolFromPdb(vaddr) catch return error.InvalidDebugInfo) |symbol| return symbol;
}
if (di.dwarf) |*dwarf| {
const dwarf_address = vaddr + di.coff_image_base;
- return dwarf.getSymbol(gpa, native_endian, dwarf_address);
+ return dwarf.getSymbol(gpa, native_endian, dwarf_address) catch return error.InvalidDebugInfo;
}
return error.MissingDebugInfo;
@@ -59,7 +64,7 @@ fn lookupInCache(cache: *const LookupCache, address: usize) ?WindowsModule {
}
return null;
}
-fn loadLocationInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) !void {
+fn loadDebugInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) !void {
const mapped_ptr: [*]const u8 = @ptrFromInt(module.base_address);
const mapped = mapped_ptr[0..module.size];
var coff_obj = coff.Coff.init(mapped, true) catch return error.InvalidDebugInfo;
@@ -151,7 +156,7 @@ fn loadLocationInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo
di.pdb = Pdb.init(gpa, path) catch |err| switch (err) {
error.FileNotFound, error.IsDir => break :pdb,
- else => return err,
+ else => |e| return e,
};
try di.pdb.?.parseInfoStream();
try di.pdb.?.parseDbiStream();
lib/std/debug/Dwarf.zig
@@ -1418,7 +1418,7 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
4 => 14, // R14
5 => 15, // R15
6 => 6, // RBP
- else => error.InvalidUnwindRegisterNumber,
+ else => error.InvalidRegister,
};
}
lib/std/debug/SelfInfo.zig
@@ -1,8 +1,6 @@
//! Cross-platform abstraction for this binary's own debug information, with a
//! goal of minimal code bloat and compilation speed penalty.
-// MLUGG TODO: audit use of errors in this file. ideally, introduce some concrete error sets
-
const builtin = @import("builtin");
const native_os = builtin.os.tag;
const native_endian = native_arch.endian();
@@ -21,6 +19,19 @@ const SelfInfo = @This();
modules: std.AutoArrayHashMapUnmanaged(usize, Module.DebugInfo),
lookup_cache: Module.LookupCache,
+pub const Error = error{
+ /// The required debug info is invalid or corrupted.
+ InvalidDebugInfo,
+ /// The required debug info could not be found.
+ MissingDebugInfo,
+ /// The required debug info was found, and may be valid, but is not supported by this implementation.
+ UnsupportedDebugInfo,
+ /// The required debug info could not be read from disk due to some IO error.
+ ReadFailed,
+ OutOfMemory,
+ Unexpected,
+};
+
/// Indicates whether the `SelfInfo` implementation has support for this target.
pub const target_supported: bool = switch (native_os) {
.linux,
@@ -82,7 +93,7 @@ test {
_ = &deinit;
}
-pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !usize {
+pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error!usize {
comptime assert(supports_unwinding);
const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc);
const gop = try self.modules.getOrPut(gpa, module.key());
@@ -92,7 +103,7 @@ pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !us
return module.unwindFrame(gpa, gop.value_ptr, context);
}
-pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.debug.Symbol {
+pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) Error!std.debug.Symbol {
comptime assert(target_supported);
const module: Module = try .lookup(&self.lookup_cache, gpa, address);
const gop = try self.modules.getOrPut(gpa, module.key());
@@ -102,7 +113,7 @@ pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.
return module.getSymbolAtAddress(gpa, gop.value_ptr, address);
}
-pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) error{ Unexpected, OutOfMemory, MissingDebugInfo }![]const u8 {
+pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) Error![]const u8 {
comptime assert(target_supported);
const module: Module = try .lookup(&self.lookup_cache, gpa, address);
return module.name;
@@ -271,12 +282,61 @@ pub const UnwindContext = struct {
/// may require lazily loading the data in those sections.
///
/// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
- /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
pub fn unwindFrameDwarf(
context: *UnwindContext,
unwind: *const Dwarf.Unwind,
load_offset: usize,
explicit_fde_offset: ?usize,
+ ) Error!usize {
+ return unwindFrameDwarfInner(context, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) {
+ error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e,
+
+ error.UnimplementedArch,
+ error.UnimplementedOs,
+ error.ThreadContextNotSupported,
+ error.UnimplementedRegisterRule,
+ error.UnsupportedAddrSize,
+ error.UnsupportedDwarfVersion,
+ error.UnimplementedUserOpcode,
+ error.UnimplementedExpressionCall,
+ error.UnimplementedOpcode,
+ error.UnimplementedTypedComparison,
+ error.UnimplementedTypeConversion,
+ error.UnknownExpressionOpcode,
+ => return error.UnsupportedDebugInfo,
+
+ error.InvalidRegister,
+ error.RegisterContextRequired,
+ error.ReadFailed,
+ error.EndOfStream,
+ error.IncompatibleRegisterSize,
+ error.Overflow,
+ error.StreamTooLong,
+ error.InvalidOperand,
+ error.InvalidOpcode,
+ error.InvalidOperation,
+ error.InvalidCFARule,
+ error.IncompleteExpressionContext,
+ error.InvalidCFAOpcode,
+ error.InvalidExpression,
+ error.InvalidFrameBase,
+ error.InvalidIntegralTypeSize,
+ error.InvalidSubExpression,
+ error.InvalidTypeLength,
+ error.TruncatedIntegralType,
+ error.DivisionByZero,
+ error.InvalidExpressionValue,
+ error.NoExpressionValue,
+ error.RegisterSizeMismatch,
+ error.InvalidCFA,
+ => return error.InvalidDebugInfo,
+ };
+ }
+ fn unwindFrameDwarfInner(
+ context: *UnwindContext,
+ unwind: *const Dwarf.Unwind,
+ load_offset: usize,
+ explicit_fde_offset: ?usize,
) !usize {
if (!supports_unwinding) return error.UnsupportedCpuArchitecture;
if (context.pc == 0) return 0;
lib/std/debug.zig
@@ -766,11 +766,6 @@ pub fn writeStackTrace(
}
}
-pub const UnwindError = if (have_ucontext)
- @typeInfo(@typeInfo(@TypeOf(SelfInfo.unwindFrame)).@"fn".return_type.?).error_union.error_set
-else
- void;
-
pub const StackIterator = struct {
// Skip every frame before this address is found.
first_address: ?usize,
@@ -783,7 +778,7 @@ pub const StackIterator = struct {
unwind_state: if (have_ucontext) ?struct {
debug_info: *SelfInfo,
dwarf_context: SelfInfo.UnwindContext,
- last_error: ?UnwindError = null,
+ last_error: ?SelfInfo.Error = null,
failed: bool = false,
} else void = if (have_ucontext) null else {},
@@ -821,7 +816,7 @@ pub const StackIterator = struct {
}
pub fn getLastError(it: *StackIterator) ?struct {
- err: UnwindError,
+ err: SelfInfo.Error,
address: usize,
} {
if (!have_ucontext) return null;
@@ -1037,17 +1032,29 @@ fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, writer: *Writ
}
}
-fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwind_err: UnwindError, tty_config: tty.Config) !void {
+fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwind_err: SelfInfo.Error, tty_config: tty.Config) !void {
const module_name = debug_info.getModuleNameForAddress(getDebugInfoAllocator(), address) catch |err| switch (err) {
- error.MissingDebugInfo => "???",
+ error.InvalidDebugInfo, error.MissingDebugInfo, error.UnsupportedDebugInfo, error.ReadFailed => "???",
error.Unexpected, error.OutOfMemory => |e| return e,
};
try tty_config.setColor(writer, .dim);
- // MLUGG TODO this makes no sense given that MissingUnwindInfo exists?
- if (unwind_err == error.MissingDebugInfo) {
- try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
- } else {
- try writer.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, unwind_err });
+ switch (unwind_err) {
+ error.Unexpected, error.OutOfMemory => |e| return e,
+ error.MissingDebugInfo => {
+ try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
+ },
+ error.InvalidDebugInfo,
+ error.UnsupportedDebugInfo,
+ error.ReadFailed,
+ => {
+ const caption: []const u8 = switch (unwind_err) {
+ error.InvalidDebugInfo => "invalid unwind info",
+ error.UnsupportedDebugInfo => "unsupported unwind info",
+ error.ReadFailed => "filesystem error",
+ else => unreachable,
+ };
+ try writer.print("Unwind error at address `{s}:0x{x}` ({s}), trace may be incomplete\n\n", .{ module_name, address, caption });
+ },
}
try tty_config.setColor(writer, .reset);
}
@@ -1055,12 +1062,17 @@ fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwi
pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) !void {
const gpa = getDebugInfoAllocator();
const symbol: Symbol = debug_info.getSymbolAtAddress(gpa, address) catch |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => .{
- .name = null,
- .compile_unit_name = null,
- .source_location = null,
+ error.MissingDebugInfo,
+ error.UnsupportedDebugInfo,
+ error.InvalidDebugInfo,
+ => .{ .name = null, .compile_unit_name = null, .source_location = null },
+ error.ReadFailed => s: {
+ try tty_config.setColor(writer, .dim);
+ try writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{});
+ try tty_config.setColor(writer, .reset);
+ break :s .{ .name = null, .compile_unit_name = null, .source_location = null };
},
- else => |e| return e,
+ error.OutOfMemory, error.Unexpected => |e| return e,
};
defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
return printLineInfo(
@@ -1069,7 +1081,7 @@ pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usi
address,
symbol.name orelse "???",
symbol.compile_unit_name orelse debug_info.getModuleNameForAddress(gpa, address) catch |err| switch (err) {
- error.MissingDebugInfo => "???",
+ error.InvalidDebugInfo, error.MissingDebugInfo, error.UnsupportedDebugInfo, error.ReadFailed => "???",
error.Unexpected, error.OutOfMemory => |e| return e,
},
tty_config,