| ... | @@ -5,6 +5,7 @@ const io = std.io; | ... | @@ -5,6 +5,7 @@ const io = std.io; |
| 5 | const os = std.os; | 5 | const os = std.os; |
| 6 | const elf = std.elf; | 6 | const elf = std.elf; |
| 7 | const DW = std.dwarf; | 7 | const DW = std.dwarf; |
| | 8 | const macho = std.macho; |
| 8 | const ArrayList = std.ArrayList; | 9 | const ArrayList = std.ArrayList; |
| 9 | const builtin = @import("builtin"); | 10 | const builtin = @import("builtin"); |
| 10 | | 11 | |
| ... | @@ -180,43 +181,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, | ... | @@ -180,43 +181,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, |
| 180 | } | 181 | } |
| 181 | | 182 | |
| 182 | fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void { | 183 | fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void { |
| 183 | if (builtin.os == builtin.Os.windows) { | | |
| 184 | return error.UnsupportedDebugInfo; | | |
| 185 | } | | |
| 186 | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal | 184 | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal |
| 187 | // at compile time. I'll call it issue #313 | 185 | // at compile time. I'll call it issue #313 |
| 188 | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; | 186 | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; |
| 189 | | 187 | |
| 190 | const compile_unit = findCompileUnit(debug_info, address) catch { | 188 | switch (builtin.os) { |
| 191 | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", | 189 | builtin.Os.windows => return error.UnsupportedDebugInfo, |
| 192 | address); | 190 | builtin.Os.macosx => { |
| 193 | return; | 191 | // TODO(bnoordhuis) It's theoretically possible to obtain the |
| 194 | }; | 192 | // compilation unit from the symbtab but it's not that useful |
| 195 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); | 193 | // in practice because the compiler dumps everything in a single |
| 196 | if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { | 194 | // object file. Future improvement: use external dSYM data when |
| 197 | defer line_info.deinit(); | 195 | // available. |
| 198 | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ | 196 | const unknown = macho.Symbol { .name = "???", .address = address }; |
| 199 | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", | 197 | const symbol = debug_info.symbol_table.search(address) ?? &unknown; |
| 200 | line_info.file_name, line_info.line, line_info.column, | 198 | try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ |
| 201 | address, compile_unit_name); | 199 | DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", |
| 202 | if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { | 200 | symbol.name, address); |
| 203 | if (line_info.column == 0) { | 201 | }, |
| 204 | try out_stream.write("\n"); | 202 | else => { |
| 205 | } else { | 203 | const compile_unit = findCompileUnit(debug_info, address) catch { |
| 206 | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { | 204 | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", |
| 207 | try out_stream.writeByte(' '); | 205 | address); |
| 208 | }} | 206 | return; |
| 209 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); | 207 | }; |
| | 208 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); |
| | 209 | if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { |
| | 210 | defer line_info.deinit(); |
| | 211 | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ |
| | 212 | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", |
| | 213 | line_info.file_name, line_info.line, line_info.column, |
| | 214 | address, compile_unit_name); |
| | 215 | if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { |
| | 216 | if (line_info.column == 0) { |
| | 217 | try out_stream.write("\n"); |
| | 218 | } else { |
| | 219 | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { |
| | 220 | try out_stream.writeByte(' '); |
| | 221 | }} |
| | 222 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); |
| | 223 | } |
| | 224 | } else |err| switch (err) { |
| | 225 | error.EndOfFile => {}, |
| | 226 | else => return err, |
| | 227 | } |
| | 228 | } else |err| switch (err) { |
| | 229 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| | 230 | try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); |
| | 231 | }, |
| | 232 | else => return err, |
| 210 | } | 233 | } |
| 211 | } else |err| switch (err) { | | |
| 212 | error.EndOfFile => {}, | | |
| 213 | else => return err, | | |
| 214 | } | | |
| 215 | } else |err| switch (err) { | | |
| 216 | error.MissingDebugInfo, error.InvalidDebugInfo => { | | |
| 217 | try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); | | |
| 218 | }, | 234 | }, |
| 219 | else => return err, | | |
| 220 | } | 235 | } |
| 221 | } | 236 | } |
| 222 | | 237 | |
| ... | @@ -249,12 +264,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { | ... | @@ -249,12 +264,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { |
| 249 | try scanAllCompileUnits(st); | 264 | try scanAllCompileUnits(st); |
| 250 | return st; | 265 | return st; |
| 251 | }, | 266 | }, |
| | 267 | builtin.ObjectFormat.macho => { |
| | 268 | var exe_file = try os.openSelfExe(); |
| | 269 | defer exe_file.close(); |
| | 270 | |
| | 271 | const st = try allocator.create(ElfStackTrace); |
| | 272 | errdefer allocator.destroy(st); |
| | 273 | |
| | 274 | *st = ElfStackTrace { |
| | 275 | .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)), |
| | 276 | }; |
| | 277 | |
| | 278 | return st; |
| | 279 | }, |
| 252 | builtin.ObjectFormat.coff => { | 280 | builtin.ObjectFormat.coff => { |
| 253 | return error.TodoSupportCoffDebugInfo; | 281 | return error.TodoSupportCoffDebugInfo; |
| 254 | }, | 282 | }, |
| 255 | builtin.ObjectFormat.macho => { | | |
| 256 | return error.TodoSupportMachoDebugInfo; | | |
| 257 | }, | | |
| 258 | builtin.ObjectFormat.wasm => { | 283 | builtin.ObjectFormat.wasm => { |
| 259 | return error.TodoSupportCOFFDebugInfo; | 284 | return error.TodoSupportCOFFDebugInfo; |
| 260 | }, | 285 | }, |
| ... | @@ -297,31 +322,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con | ... | @@ -297,31 +322,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con |
| 297 | } | 322 | } |
| 298 | } | 323 | } |
| 299 | | 324 | |
| 300 | pub const ElfStackTrace = struct { | 325 | pub const ElfStackTrace = switch (builtin.os) { |
| 301 | self_exe_file: os.File, | 326 | builtin.Os.macosx => struct { |
| 302 | elf: elf.Elf, | 327 | symbol_table: macho.SymbolTable, |
| 303 | debug_info: &elf.SectionHeader, | | |
| 304 | debug_abbrev: &elf.SectionHeader, | | |
| 305 | debug_str: &elf.SectionHeader, | | |
| 306 | debug_line: &elf.SectionHeader, | | |
| 307 | debug_ranges: ?&elf.SectionHeader, | | |
| 308 | abbrev_table_list: ArrayList(AbbrevTableHeader), | | |
| 309 | compile_unit_list: ArrayList(CompileUnit), | | |
| 310 | | | |
| 311 | pub fn allocator(self: &const ElfStackTrace) &mem.Allocator { | | |
| 312 | return self.abbrev_table_list.allocator; | | |
| 313 | } | | |
| 314 | | 328 | |
| 315 | pub fn readString(self: &ElfStackTrace) ![]u8 { | 329 | pub fn close(self: &ElfStackTrace) void { |
| 316 | var in_file_stream = io.FileInStream.init(&self.self_exe_file); | 330 | self.symbol_table.deinit(); |
| 317 | const in_stream = &in_file_stream.stream; | 331 | } |
| 318 | return readStringRaw(self.allocator(), in_stream); | 332 | }, |
| 319 | } | 333 | else => struct { |
| | 334 | self_exe_file: os.File, |
| | 335 | elf: elf.Elf, |
| | 336 | debug_info: &elf.SectionHeader, |
| | 337 | debug_abbrev: &elf.SectionHeader, |
| | 338 | debug_str: &elf.SectionHeader, |
| | 339 | debug_line: &elf.SectionHeader, |
| | 340 | debug_ranges: ?&elf.SectionHeader, |
| | 341 | abbrev_table_list: ArrayList(AbbrevTableHeader), |
| | 342 | compile_unit_list: ArrayList(CompileUnit), |
| | 343 | |
| | 344 | pub fn allocator(self: &const ElfStackTrace) &mem.Allocator { |
| | 345 | return self.abbrev_table_list.allocator; |
| | 346 | } |
| 320 | | 347 | |
| 321 | pub fn close(self: &ElfStackTrace) void { | 348 | pub fn readString(self: &ElfStackTrace) ![]u8 { |
| 322 | self.self_exe_file.close(); | 349 | var in_file_stream = io.FileInStream.init(&self.self_exe_file); |
| 323 | self.elf.close(); | 350 | const in_stream = &in_file_stream.stream; |
| 324 | } | 351 | return readStringRaw(self.allocator(), in_stream); |
| | 352 | } |
| | 353 | |
| | 354 | pub fn close(self: &ElfStackTrace) void { |
| | 355 | self.self_exe_file.close(); |
| | 356 | self.elf.close(); |
| | 357 | } |
| | 358 | }, |
| 325 | }; | 359 | }; |
| 326 | | 360 | |
| 327 | const PcRange = struct { | 361 | const PcRange = struct { |