aboutsummaryrefslogtreecommitdiff
path: root/std/debug/index.zig
diff options
context:
space:
mode:
authorBen Noordhuis <info@bnoordhuis.nl>2018-02-19 23:06:54 +0100
committerBen Noordhuis <info@bnoordhuis.nl>2018-02-19 23:11:11 +0100
commitab48934e9cefb510d39ba3fe8c0dcf7619bec4cf (patch)
treeb02155986fb9d36b4b5e3ac94263d43c9421c13e /std/debug/index.zig
parentbde15cf0806f2b8d6fb0c90602b42a74863ec515 (diff)
downloadzig-ab48934e9cefb510d39ba3fe8c0dcf7619bec4cf.tar.gz
zig-ab48934e9cefb510d39ba3fe8c0dcf7619bec4cf.zip
add support for stack traces on macosx
Add basic address->symbol resolution support. Uses symtab data from the MachO image, not external dSYM data; that's left as a future exercise. The net effect is that we can now map addresses to function names but not much more. File names and line number data will have to wait until a future pull request. Partially fixes #434.
Diffstat (limited to 'std/debug/index.zig')
-rw-r--r--std/debug/index.zig148
1 files changed, 91 insertions, 57 deletions
diff --git a/std/debug/index.zig b/std/debug/index.zig
index cc4832b1ea..2418654986 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -5,6 +5,7 @@ const io = std.io;
const os = std.os;
const elf = std.elf;
const DW = std.dwarf;
+const macho = std.macho;
const ArrayList = std.ArrayList;
const builtin = @import("builtin");
@@ -180,43 +181,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
}
fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
- if (builtin.os == builtin.Os.windows) {
- return error.UnsupportedDebugInfo;
- }
// TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
// at compile time. I'll call it issue #313
const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
- const compile_unit = findCompileUnit(debug_info, address) catch {
- try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
- address);
- return;
- };
- const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
- if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
- defer line_info.deinit();
- try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
- DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
- line_info.file_name, line_info.line, line_info.column,
- address, compile_unit_name);
- if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
- if (line_info.column == 0) {
- try out_stream.write("\n");
- } else {
- {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
- try out_stream.writeByte(' ');
- }}
- try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
+ switch (builtin.os) {
+ builtin.Os.windows => return error.UnsupportedDebugInfo,
+ builtin.Os.macosx => {
+ // TODO(bnoordhuis) It's theoretically possible to obtain the
+ // compilation unit from the symbtab but it's not that useful
+ // in practice because the compiler dumps everything in a single
+ // object file. Future improvement: use external dSYM data when
+ // available.
+ const unknown = macho.Symbol { .name = "???", .address = address };
+ const symbol = debug_info.symbol_table.search(address) ?? &unknown;
+ try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++
+ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n",
+ symbol.name, address);
+ },
+ else => {
+ const compile_unit = findCompileUnit(debug_info, address) catch {
+ try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
+ address);
+ return;
+ };
+ const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
+ if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
+ defer line_info.deinit();
+ try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
+ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
+ line_info.file_name, line_info.line, line_info.column,
+ address, compile_unit_name);
+ if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
+ if (line_info.column == 0) {
+ try out_stream.write("\n");
+ } else {
+ {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
+ try out_stream.writeByte(' ');
+ }}
+ try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
+ }
+ } else |err| switch (err) {
+ error.EndOfFile => {},
+ else => return err,
+ }
+ } else |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => {
+ try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
+ },
+ else => return err,
}
- } else |err| switch (err) {
- error.EndOfFile => {},
- else => return err,
- }
- } else |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => {
- try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
},
- else => return err,
}
}
@@ -249,12 +264,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
try scanAllCompileUnits(st);
return st;
},
+ builtin.ObjectFormat.macho => {
+ var exe_file = try os.openSelfExe();
+ defer exe_file.close();
+
+ const st = try allocator.create(ElfStackTrace);
+ errdefer allocator.destroy(st);
+
+ *st = ElfStackTrace {
+ .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)),
+ };
+
+ return st;
+ },
builtin.ObjectFormat.coff => {
return error.TodoSupportCoffDebugInfo;
},
- builtin.ObjectFormat.macho => {
- return error.TodoSupportMachoDebugInfo;
- },
builtin.ObjectFormat.wasm => {
return error.TodoSupportCOFFDebugInfo;
},
@@ -297,31 +322,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con
}
}
-pub const ElfStackTrace = struct {
- self_exe_file: os.File,
- elf: elf.Elf,
- debug_info: &elf.SectionHeader,
- debug_abbrev: &elf.SectionHeader,
- debug_str: &elf.SectionHeader,
- debug_line: &elf.SectionHeader,
- debug_ranges: ?&elf.SectionHeader,
- abbrev_table_list: ArrayList(AbbrevTableHeader),
- compile_unit_list: ArrayList(CompileUnit),
-
- pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
- return self.abbrev_table_list.allocator;
- }
+pub const ElfStackTrace = switch (builtin.os) {
+ builtin.Os.macosx => struct {
+ symbol_table: macho.SymbolTable,
- pub fn readString(self: &ElfStackTrace) ![]u8 {
- var in_file_stream = io.FileInStream.init(&self.self_exe_file);
- const in_stream = &in_file_stream.stream;
- return readStringRaw(self.allocator(), in_stream);
- }
+ pub fn close(self: &ElfStackTrace) void {
+ self.symbol_table.deinit();
+ }
+ },
+ else => struct {
+ self_exe_file: os.File,
+ elf: elf.Elf,
+ debug_info: &elf.SectionHeader,
+ debug_abbrev: &elf.SectionHeader,
+ debug_str: &elf.SectionHeader,
+ debug_line: &elf.SectionHeader,
+ debug_ranges: ?&elf.SectionHeader,
+ abbrev_table_list: ArrayList(AbbrevTableHeader),
+ compile_unit_list: ArrayList(CompileUnit),
+
+ pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
+ return self.abbrev_table_list.allocator;
+ }
- pub fn close(self: &ElfStackTrace) void {
- self.self_exe_file.close();
- self.elf.close();
- }
+ pub fn readString(self: &ElfStackTrace) ![]u8 {
+ var in_file_stream = io.FileInStream.init(&self.self_exe_file);
+ const in_stream = &in_file_stream.stream;
+ return readStringRaw(self.allocator(), in_stream);
+ }
+
+ pub fn close(self: &ElfStackTrace) void {
+ self.self_exe_file.close();
+ self.elf.close();
+ }
+ },
};
const PcRange = struct {