| ... | ... | @@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; |
| 13 | 13 | error MissingDebugInfo; |
| 14 | 14 | error InvalidDebugInfo; |
| 15 | 15 | error UnsupportedDebugInfo; |
| 16 | error UnknownObjectFormat; |
| 17 | error TodoSupportCoffDebugInfo; |
| 18 | error TodoSupportMachoDebugInfo; |
| 19 | error TodoSupportCOFFDebugInfo; |
| 16 | 20 | |
| 17 | 21 | |
| 18 | 22 | /// Tries to write to stderr, unbuffered, and ignores any error returned. |
| ... | ... | @@ -40,13 +44,29 @@ fn getStderrStream() -> %&io.OutStream { |
| 40 | 44 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. |
| 41 | 45 | pub fn dumpCurrentStackTrace() { |
| 42 | 46 | const stderr = getStderrStream() catch return; |
| 43 | | writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return; |
| 47 | const debug_info = openSelfDebugInfo(global_allocator) catch |err| { |
| 48 | stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; |
| 49 | return; |
| 50 | }; |
| 51 | defer debug_info.close(); |
| 52 | writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), 1) catch |err| { |
| 53 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; |
| 54 | return; |
| 55 | }; |
| 44 | 56 | } |
| 45 | 57 | |
| 46 | 58 | /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. |
| 47 | 59 | pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) { |
| 48 | 60 | const stderr = getStderrStream() catch return; |
| 49 | | writeStackTrace(stack_trace, stderr, global_allocator, stderr_file.isTty()) catch return; |
| 61 | const debug_info = openSelfDebugInfo(global_allocator) catch |err| { |
| 62 | stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; |
| 63 | return; |
| 64 | }; |
| 65 | defer debug_info.close(); |
| 66 | writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| { |
| 67 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; |
| 68 | return; |
| 69 | }; |
| 50 | 70 | } |
| 51 | 71 | |
| 52 | 72 | /// This function invokes undefined behavior when `ok` is `false`. |
| ... | ... | @@ -94,7 +114,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn { |
| 94 | 114 | |
| 95 | 115 | const stderr = getStderrStream() catch os.abort(); |
| 96 | 116 | stderr.print(format ++ "\n", args) catch os.abort(); |
| 97 | | writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort(); |
| 117 | dumpCurrentStackTrace(); |
| 98 | 118 | |
| 99 | 119 | os.abort(); |
| 100 | 120 | } |
| ... | ... | @@ -107,108 +127,88 @@ const RESET = "\x1b[0m"; |
| 107 | 127 | error PathNotFound; |
| 108 | 128 | error InvalidDebugInfo; |
| 109 | 129 | |
| 110 | | pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void { |
| 111 | | switch (builtin.object_format) { |
| 112 | | builtin.ObjectFormat.elf => { |
| 113 | | var stack_trace = ElfStackTrace { |
| 114 | | .self_exe_file = undefined, |
| 115 | | .elf = undefined, |
| 116 | | .debug_info = undefined, |
| 117 | | .debug_abbrev = undefined, |
| 118 | | .debug_str = undefined, |
| 119 | | .debug_line = undefined, |
| 120 | | .debug_ranges = null, |
| 121 | | .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), |
| 122 | | .compile_unit_list = ArrayList(CompileUnit).init(allocator), |
| 123 | | }; |
| 124 | | const st = &stack_trace; |
| 125 | | st.self_exe_file = try os.openSelfExe(); |
| 126 | | defer st.self_exe_file.close(); |
| 130 | pub fn writeStackTrace(stack_trace: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, |
| 131 | debug_info: &ElfStackTrace, tty_color: bool) -> %void |
| 132 | { |
| 133 | var frame_index: usize = undefined; |
| 134 | var frames_left: usize = undefined; |
| 135 | if (stack_trace.index < stack_trace.instruction_addresses.len) { |
| 136 | frame_index = 0; |
| 137 | frames_left = stack_trace.index; |
| 138 | } else { |
| 139 | frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len; |
| 140 | frames_left = stack_trace.instruction_addresses.len; |
| 141 | } |
| 127 | 142 | |
| 128 | | try st.elf.openFile(allocator, &st.self_exe_file); |
| 129 | | defer st.elf.close(); |
| 143 | while (frames_left != 0) : ({ |
| 144 | frames_left -= 1; |
| 145 | frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; |
| 146 | }) { |
| 147 | const return_address = stack_trace.instruction_addresses[frame_index]; |
| 148 | try printSourceAtAddress(debug_info, out_stream, return_address); |
| 149 | } |
| 150 | } |
| 130 | 151 | |
| 131 | | st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; |
| 132 | | st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; |
| 133 | | st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; |
| 134 | | st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; |
| 135 | | st.debug_ranges = (try st.elf.findSection(".debug_ranges")); |
| 136 | | try scanAllCompileUnits(st); |
| 152 | pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, |
| 153 | debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) -> %void |
| 154 | { |
| 155 | var ignored_count: usize = 0; |
| 156 | |
| 157 | var fp = @ptrToInt(@frameAddress()); |
| 158 | while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { |
| 159 | if (ignored_count < ignore_frame_count) { |
| 160 | ignored_count += 1; |
| 161 | continue; |
| 162 | } |
| 137 | 163 | |
| 138 | | var ignored_count: usize = 0; |
| 164 | const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); |
| 165 | try printSourceAtAddress(debug_info, out_stream, return_address); |
| 166 | } |
| 167 | } |
| 139 | 168 | |
| 140 | | var frame_index: usize = undefined; |
| 141 | | var frames_left: usize = undefined; |
| 142 | | if (st_addrs.index < st_addrs.instruction_addresses.len) { |
| 143 | | frame_index = 0; |
| 144 | | frames_left = st_addrs.index; |
| 145 | | } else { |
| 146 | | frame_index = (st_addrs.index + 1) % st_addrs.instruction_addresses.len; |
| 147 | | frames_left = st_addrs.instruction_addresses.len; |
| 148 | | } |
| 169 | fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void { |
| 170 | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal |
| 171 | // at compile time. I'll call it issue #313 |
| 172 | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; |
| 149 | 173 | |
| 150 | | while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) { |
| 151 | | const return_address = st_addrs.instruction_addresses[frame_index]; |
| 152 | | |
| 153 | | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal |
| 154 | | // at compile time. I'll call it issue #313 |
| 155 | | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; |
| 156 | | |
| 157 | | const compile_unit = findCompileUnit(st, return_address) catch { |
| 158 | | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", |
| 159 | | return_address); |
| 160 | | continue; |
| 161 | | }; |
| 162 | | const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name); |
| 163 | | if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| { |
| 164 | | defer line_info.deinit(); |
| 165 | | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ |
| 166 | | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", |
| 167 | | line_info.file_name, line_info.line, line_info.column, |
| 168 | | return_address, compile_unit_name); |
| 169 | | if (printLineFromFile(st.allocator(), out_stream, line_info)) { |
| 170 | | if (line_info.column == 0) { |
| 171 | | try out_stream.write("\n"); |
| 172 | | } else { |
| 173 | | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { |
| 174 | | try out_stream.writeByte(' '); |
| 175 | | }} |
| 176 | | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); |
| 177 | | } |
| 178 | | } else |err| switch (err) { |
| 179 | | error.EndOfFile, error.PathNotFound => {}, |
| 180 | | else => return err, |
| 181 | | } |
| 182 | | } else |err| switch (err) { |
| 183 | | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 184 | | try out_stream.print(ptr_hex ++ " in ??? ({})\n", |
| 185 | | return_address, compile_unit_name); |
| 186 | | }, |
| 187 | | else => return err, |
| 188 | | } |
| 174 | const compile_unit = findCompileUnit(debug_info, address) catch { |
| 175 | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", |
| 176 | address); |
| 177 | return; |
| 178 | }; |
| 179 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); |
| 180 | if (getLineNumberInfo(debug_info, compile_unit, usize(address) - 1)) |line_info| { |
| 181 | defer line_info.deinit(); |
| 182 | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ |
| 183 | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", |
| 184 | line_info.file_name, line_info.line, line_info.column, |
| 185 | address, compile_unit_name); |
| 186 | if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { |
| 187 | if (line_info.column == 0) { |
| 188 | try out_stream.write("\n"); |
| 189 | } else { |
| 190 | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { |
| 191 | try out_stream.writeByte(' '); |
| 192 | }} |
| 193 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); |
| 189 | 194 | } |
| 195 | } else |err| switch (err) { |
| 196 | error.EndOfFile, error.PathNotFound => {}, |
| 197 | else => return err, |
| 198 | } |
| 199 | } else |err| switch (err) { |
| 200 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 201 | try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); |
| 190 | 202 | }, |
| 191 | | builtin.ObjectFormat.coff => { |
| 192 | | try out_stream.write("(stack trace unavailable for COFF object format)\n"); |
| 193 | | }, |
| 194 | | builtin.ObjectFormat.macho => { |
| 195 | | try out_stream.write("(stack trace unavailable for Mach-O object format)\n"); |
| 196 | | }, |
| 197 | | builtin.ObjectFormat.wasm => { |
| 198 | | try out_stream.write("(stack trace unavailable for WASM object format)\n"); |
| 199 | | }, |
| 200 | | builtin.ObjectFormat.unknown => { |
| 201 | | try out_stream.write("(stack trace unavailable for unknown object format)\n"); |
| 202 | | }, |
| 203 | else => return err, |
| 203 | 204 | } |
| 204 | 205 | } |
| 205 | 206 | |
| 206 | | pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, |
| 207 | | ignore_frame_count: usize) -> %void |
| 208 | | { |
| 207 | pub fn openSelfDebugInfo(allocator: &mem.Allocator) -> %&ElfStackTrace { |
| 209 | 208 | switch (builtin.object_format) { |
| 210 | 209 | builtin.ObjectFormat.elf => { |
| 211 | | var stack_trace = ElfStackTrace { |
| 210 | const st = try allocator.create(ElfStackTrace); |
| 211 | *st = ElfStackTrace { |
| 212 | 212 | .self_exe_file = undefined, |
| 213 | 213 | .elf = undefined, |
| 214 | 214 | .debug_info = undefined, |
| ... | ... | @@ -219,12 +219,11 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat |
| 219 | 219 | .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), |
| 220 | 220 | .compile_unit_list = ArrayList(CompileUnit).init(allocator), |
| 221 | 221 | }; |
| 222 | | const st = &stack_trace; |
| 223 | 222 | st.self_exe_file = try os.openSelfExe(); |
| 224 | | defer st.self_exe_file.close(); |
| 223 | %defer st.self_exe_file.close(); |
| 225 | 224 | |
| 226 | 225 | try st.elf.openFile(allocator, &st.self_exe_file); |
| 227 | | defer st.elf.close(); |
| 226 | %defer st.elf.close(); |
| 228 | 227 | |
| 229 | 228 | st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; |
| 230 | 229 | 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 |
| 232 | 231 | st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; |
| 233 | 232 | st.debug_ranges = (try st.elf.findSection(".debug_ranges")); |
| 234 | 233 | try scanAllCompileUnits(st); |
| 235 | | |
| 236 | | var ignored_count: usize = 0; |
| 237 | | |
| 238 | | var fp = @ptrToInt(@frameAddress()); |
| 239 | | while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { |
| 240 | | if (ignored_count < ignore_frame_count) { |
| 241 | | ignored_count += 1; |
| 242 | | continue; |
| 243 | | } |
| 244 | | |
| 245 | | const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); |
| 246 | | |
| 247 | | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal |
| 248 | | // at compile time. I'll call it issue #313 |
| 249 | | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; |
| 250 | | |
| 251 | | const compile_unit = findCompileUnit(st, return_address) catch { |
| 252 | | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", |
| 253 | | return_address); |
| 254 | | continue; |
| 255 | | }; |
| 256 | | const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name); |
| 257 | | if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| { |
| 258 | | defer line_info.deinit(); |
| 259 | | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ |
| 260 | | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", |
| 261 | | line_info.file_name, line_info.line, line_info.column, |
| 262 | | return_address, compile_unit_name); |
| 263 | | if (printLineFromFile(st.allocator(), out_stream, line_info)) { |
| 264 | | if (line_info.column == 0) { |
| 265 | | try out_stream.write("\n"); |
| 266 | | } else { |
| 267 | | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { |
| 268 | | try out_stream.writeByte(' '); |
| 269 | | }} |
| 270 | | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); |
| 271 | | } |
| 272 | | } else |err| switch (err) { |
| 273 | | error.EndOfFile, error.PathNotFound => {}, |
| 274 | | else => return err, |
| 275 | | } |
| 276 | | } else |err| switch (err) { |
| 277 | | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 278 | | try out_stream.print(ptr_hex ++ " in ??? ({})\n", |
| 279 | | return_address, compile_unit_name); |
| 280 | | }, |
| 281 | | else => return err, |
| 282 | | } |
| 283 | | } |
| 234 | return st; |
| 284 | 235 | }, |
| 285 | 236 | builtin.ObjectFormat.coff => { |
| 286 | | try out_stream.write("(stack trace unavailable for COFF object format)\n"); |
| 237 | return error.TodoSupportCoffDebugInfo; |
| 287 | 238 | }, |
| 288 | 239 | builtin.ObjectFormat.macho => { |
| 289 | | try out_stream.write("(stack trace unavailable for Mach-O object format)\n"); |
| 240 | return error.TodoSupportMachoDebugInfo; |
| 290 | 241 | }, |
| 291 | 242 | builtin.ObjectFormat.wasm => { |
| 292 | | try out_stream.write("(stack trace unavailable for WASM object format)\n"); |
| 243 | return error.TodoSupportCOFFDebugInfo; |
| 293 | 244 | }, |
| 294 | 245 | builtin.ObjectFormat.unknown => { |
| 295 | | try out_stream.write("(stack trace unavailable for unknown object format)\n"); |
| 246 | return error.UnknownObjectFormat; |
| 296 | 247 | }, |
| 297 | 248 | } |
| 298 | 249 | } |
| ... | ... | @@ -330,7 +281,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_ |
| 330 | 281 | } |
| 331 | 282 | } |
| 332 | 283 | |
| 333 | | const ElfStackTrace = struct { |
| 284 | pub const ElfStackTrace = struct { |
| 334 | 285 | self_exe_file: io.File, |
| 335 | 286 | elf: elf.Elf, |
| 336 | 287 | debug_info: &elf.SectionHeader, |
| ... | ... | @@ -350,6 +301,11 @@ const ElfStackTrace = struct { |
| 350 | 301 | const in_stream = &in_file_stream.stream; |
| 351 | 302 | return readStringRaw(self.allocator(), in_stream); |
| 352 | 303 | } |
| 304 | |
| 305 | pub fn close(self: &ElfStackTrace) { |
| 306 | self.self_exe_file.close(); |
| 307 | self.elf.close(); |
| 308 | } |
| 353 | 309 | }; |
| 354 | 310 | |
| 355 | 311 | const PcRange = struct { |