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