diff --git a/std/debug/index.zig b/std/debug/index.zig index fa2304079404c64d21e9fbcf6c7741d5bbf67549..f7f6ffa323e48ca835113acaf8640fea168d68f7 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; error MissingDebugInfo; error InvalidDebugInfo; error UnsupportedDebugInfo; +error UnknownObjectFormat; +error TodoSupportCoffDebugInfo; +error TodoSupportMachoDebugInfo; +error TodoSupportCOFFDebugInfo; /// Tries to write to stderr, unbuffered, and ignores any error returned. @@ -40,13 +44,29 @@ fn getStderrStream() -> %&io.OutStream { /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. pub fn dumpCurrentStackTrace() { const stderr = getStderrStream() catch return; - writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return; + const debug_info = openSelfDebugInfo(global_allocator) catch |err| { + stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; + return; + }; + defer debug_info.close(); + writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), 1) catch |err| { + stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; + return; + }; } /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) { const stderr = getStderrStream() catch return; - writeStackTrace(stack_trace, stderr, global_allocator, stderr_file.isTty()) catch return; + const debug_info = openSelfDebugInfo(global_allocator) catch |err| { + stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; + return; + }; + defer debug_info.close(); + writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| { + stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; + return; + }; } /// This function invokes undefined behavior when `ok` is `false`. @@ -94,7 +114,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn { const stderr = getStderrStream() catch os.abort(); stderr.print(format ++ "\n", args) catch os.abort(); - writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort(); + dumpCurrentStackTrace(); os.abort(); } @@ -107,108 +127,88 @@ const RESET = "\x1b[0m"; error PathNotFound; error InvalidDebugInfo; -pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void { - switch (builtin.object_format) { - builtin.ObjectFormat.elf => { - var stack_trace = ElfStackTrace { - .self_exe_file = undefined, - .elf = undefined, - .debug_info = undefined, - .debug_abbrev = undefined, - .debug_str = undefined, - .debug_line = undefined, - .debug_ranges = null, - .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), - .compile_unit_list = ArrayList(CompileUnit).init(allocator), - }; - const st = &stack_trace; - st.self_exe_file = try os.openSelfExe(); - defer st.self_exe_file.close(); +pub fn writeStackTrace(stack_trace: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, + debug_info: &ElfStackTrace, tty_color: bool) -> %void +{ + var frame_index: usize = undefined; + var frames_left: usize = undefined; + if (stack_trace.index < stack_trace.instruction_addresses.len) { + frame_index = 0; + frames_left = stack_trace.index; + } else { + frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len; + frames_left = stack_trace.instruction_addresses.len; + } - try st.elf.openFile(allocator, &st.self_exe_file); - defer st.elf.close(); + while (frames_left != 0) : ({ + frames_left -= 1; + frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; + }) { + const return_address = stack_trace.instruction_addresses[frame_index]; + try printSourceAtAddress(debug_info, out_stream, return_address); + } +} - st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; - st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; - st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; - st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; - st.debug_ranges = (try st.elf.findSection(".debug_ranges")); - try scanAllCompileUnits(st); +pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, + debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) -> %void +{ + var ignored_count: usize = 0; - var ignored_count: usize = 0; + var fp = @ptrToInt(@frameAddress()); + while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { + if (ignored_count < ignore_frame_count) { + ignored_count += 1; + continue; + } - var frame_index: usize = undefined; - var frames_left: usize = undefined; - if (st_addrs.index < st_addrs.instruction_addresses.len) { - frame_index = 0; - frames_left = st_addrs.index; + const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); + try printSourceAtAddress(debug_info, out_stream, return_address); + } +} + +fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void { + // 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, usize(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 { - frame_index = (st_addrs.index + 1) % st_addrs.instruction_addresses.len; - frames_left = st_addrs.instruction_addresses.len; + {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"); } - - while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) { - const return_address = st_addrs.instruction_addresses[frame_index]; - - // 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(st, return_address) catch { - try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", - return_address); - continue; - }; - const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name); - if (getLineNumberInfo(st, compile_unit, usize(return_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, - return_address, compile_unit_name); - if (printLineFromFile(st.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, error.PathNotFound => {}, - else => return err, - } - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - try out_stream.print(ptr_hex ++ " in ??? ({})\n", - return_address, compile_unit_name); - }, - else => return err, - } - } - }, - builtin.ObjectFormat.coff => { - try out_stream.write("(stack trace unavailable for COFF object format)\n"); - }, - builtin.ObjectFormat.macho => { - try out_stream.write("(stack trace unavailable for Mach-O object format)\n"); - }, - builtin.ObjectFormat.wasm => { - try out_stream.write("(stack trace unavailable for WASM object format)\n"); - }, - builtin.ObjectFormat.unknown => { - try out_stream.write("(stack trace unavailable for unknown object format)\n"); - }, + } else |err| switch (err) { + error.EndOfFile, error.PathNotFound => {}, + 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, } } -pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, - ignore_frame_count: usize) -> %void -{ +pub fn openSelfDebugInfo(allocator: &mem.Allocator) -> %&ElfStackTrace { switch (builtin.object_format) { builtin.ObjectFormat.elf => { - var stack_trace = ElfStackTrace { + const st = try allocator.create(ElfStackTrace); + *st = ElfStackTrace { .self_exe_file = undefined, .elf = undefined, .debug_info = undefined, @@ -219,12 +219,11 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), .compile_unit_list = ArrayList(CompileUnit).init(allocator), }; - const st = &stack_trace; st.self_exe_file = try os.openSelfExe(); - defer st.self_exe_file.close(); + %defer st.self_exe_file.close(); try st.elf.openFile(allocator, &st.self_exe_file); - defer st.elf.close(); + %defer st.elf.close(); st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; @@ -232,67 +231,19 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; st.debug_ranges = (try st.elf.findSection(".debug_ranges")); try scanAllCompileUnits(st); - - var ignored_count: usize = 0; - - var fp = @ptrToInt(@frameAddress()); - while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { - if (ignored_count < ignore_frame_count) { - ignored_count += 1; - continue; - } - - const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); - - // 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(st, return_address) catch { - try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", - return_address); - continue; - }; - const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name); - if (getLineNumberInfo(st, compile_unit, usize(return_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, - return_address, compile_unit_name); - if (printLineFromFile(st.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, error.PathNotFound => {}, - else => return err, - } - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - try out_stream.print(ptr_hex ++ " in ??? ({})\n", - return_address, compile_unit_name); - }, - else => return err, - } - } + return st; }, builtin.ObjectFormat.coff => { - try out_stream.write("(stack trace unavailable for COFF object format)\n"); + return error.TodoSupportCoffDebugInfo; }, builtin.ObjectFormat.macho => { - try out_stream.write("(stack trace unavailable for Mach-O object format)\n"); + return error.TodoSupportMachoDebugInfo; }, builtin.ObjectFormat.wasm => { - try out_stream.write("(stack trace unavailable for WASM object format)\n"); + return error.TodoSupportCOFFDebugInfo; }, builtin.ObjectFormat.unknown => { - try out_stream.write("(stack trace unavailable for unknown object format)\n"); + return error.UnknownObjectFormat; }, } } @@ -330,7 +281,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_ } } -const ElfStackTrace = struct { +pub const ElfStackTrace = struct { self_exe_file: io.File, elf: elf.Elf, debug_info: &elf.SectionHeader, @@ -350,6 +301,11 @@ const ElfStackTrace = struct { const in_stream = &in_file_stream.stream; return readStringRaw(self.allocator(), in_stream); } + + pub fn close(self: &ElfStackTrace) { + self.self_exe_file.close(); + self.elf.close(); + } }; const PcRange = struct { diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig index 1a36f50b62ba64d1495008bea45186bf5850f135..027251c52c9521465832d6717e4af542f75c1cdd 100644 --- a/std/special/test_runner.zig +++ b/std/special/test_runner.zig @@ -8,10 +8,14 @@ pub fn main() -> %void { for (test_fn_list) |test_fn, i| { warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); - test_fn.func() catch |err| { - warn("{}\n", err); - return err; - }; + if (builtin.is_test) { + test_fn.func() catch unreachable; + } else { + test_fn.func() catch |err| { + warn("{}\n", err); + return err; + }; + } warn("OK\n"); }