| ... | ... | @@ -390,179 +390,6 @@ pub fn writeCurrentStackTraceWindows( |
| 390 | 390 | } |
| 391 | 391 | } |
| 392 | 392 | |
| 393 | | /// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented, |
| 394 | | /// make this `noasync fn` and remove the individual noasync calls. |
| 395 | | pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { |
| 396 | | // if (builtin.os == .windows) { |
| 397 | | // return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_config); |
| 398 | | // } |
| 399 | | return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_config); |
| 400 | | } |
| 401 | | |
| 402 | | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 403 | | fn printSourceAtAddressWindows( |
| 404 | | debug_info: *DebugInfo, |
| 405 | | out_stream: var, |
| 406 | | address: usize, |
| 407 | | tty_config: TTY.Config, |
| 408 | | ) !void { |
| 409 | | const allocator = debug_info.allocator; |
| 410 | | |
| 411 | | const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { |
| 412 | | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 413 | | return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); |
| 414 | | }, |
| 415 | | else => return err, |
| 416 | | }; |
| 417 | | const relocated_address = address - module.base_address; |
| 418 | | |
| 419 | | var coff_section: *coff.Section = undefined; |
| 420 | | const mod_index = for (module.sect_contribs) |sect_contrib| { |
| 421 | | if (sect_contrib.Section > module.coff.sections.len) continue; |
| 422 | | // Remember that SectionContribEntry.Section is 1-based. |
| 423 | | coff_section = &module.coff.sections.toSlice()[sect_contrib.Section - 1]; |
| 424 | | |
| 425 | | const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; |
| 426 | | const vaddr_end = vaddr_start + sect_contrib.Size; |
| 427 | | if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { |
| 428 | | break sect_contrib.ModuleIndex; |
| 429 | | } |
| 430 | | } else { |
| 431 | | // we have no information to add to the address |
| 432 | | return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_config, printLineFromFileAnyOs); |
| 433 | | }; |
| 434 | | |
| 435 | | const mod = &module.modules[mod_index]; |
| 436 | | try populateModule(module, mod); |
| 437 | | const obj_basename = fs.path.basename(mod.obj_file_name); |
| 438 | | |
| 439 | | var symbol_i: usize = 0; |
| 440 | | const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) { |
| 441 | | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); |
| 442 | | if (prefix.RecordLen < 2) |
| 443 | | return error.InvalidDebugInfo; |
| 444 | | switch (prefix.RecordKind) { |
| 445 | | .S_LPROC32, .S_GPROC32 => { |
| 446 | | const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); |
| 447 | | const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; |
| 448 | | const vaddr_end = vaddr_start + proc_sym.CodeSize; |
| 449 | | if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { |
| 450 | | break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym)); |
| 451 | | } |
| 452 | | }, |
| 453 | | else => {}, |
| 454 | | } |
| 455 | | symbol_i += prefix.RecordLen + @sizeOf(u16); |
| 456 | | if (symbol_i > mod.symbols.len) |
| 457 | | return error.InvalidDebugInfo; |
| 458 | | } else "???"; |
| 459 | | |
| 460 | | const subsect_info = mod.subsect_info; |
| 461 | | |
| 462 | | var sect_offset: usize = 0; |
| 463 | | var skip_len: usize = undefined; |
| 464 | | const opt_line_info = subsections: { |
| 465 | | const checksum_offset = mod.checksum_offset orelse break :subsections null; |
| 466 | | while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { |
| 467 | | const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); |
| 468 | | skip_len = subsect_hdr.Length; |
| 469 | | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); |
| 470 | | |
| 471 | | switch (subsect_hdr.Kind) { |
| 472 | | pdb.DebugSubsectionKind.Lines => { |
| 473 | | var line_index = sect_offset; |
| 474 | | |
| 475 | | const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); |
| 476 | | if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; |
| 477 | | line_index += @sizeOf(pdb.LineFragmentHeader); |
| 478 | | const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; |
| 479 | | const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; |
| 480 | | |
| 481 | | if (relocated_address >= frag_vaddr_start and relocated_address < frag_vaddr_end) { |
| 482 | | // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) |
| 483 | | // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, |
| 484 | | // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. |
| 485 | | const subsection_end_index = sect_offset + subsect_hdr.Length; |
| 486 | | |
| 487 | | while (line_index < subsection_end_index) { |
| 488 | | const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); |
| 489 | | line_index += @sizeOf(pdb.LineBlockFragmentHeader); |
| 490 | | const start_line_index = line_index; |
| 491 | | |
| 492 | | const has_column = line_hdr.Flags.LF_HaveColumns; |
| 493 | | |
| 494 | | // All line entries are stored inside their line block by ascending start address. |
| 495 | | // Heuristic: we want to find the last line entry |
| 496 | | // that has a vaddr_start <= relocated_address. |
| 497 | | // This is done with a simple linear search. |
| 498 | | var line_i: u32 = 0; |
| 499 | | while (line_i < block_hdr.NumLines) : (line_i += 1) { |
| 500 | | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); |
| 501 | | line_index += @sizeOf(pdb.LineNumberEntry); |
| 502 | | |
| 503 | | const vaddr_start = frag_vaddr_start + line_num_entry.Offset; |
| 504 | | if (relocated_address < vaddr_start) { |
| 505 | | break; |
| 506 | | } |
| 507 | | } |
| 508 | | |
| 509 | | // line_i == 0 would mean that no matching LineNumberEntry was found. |
| 510 | | if (line_i > 0) { |
| 511 | | const subsect_index = checksum_offset + block_hdr.NameIndex; |
| 512 | | const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); |
| 513 | | const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; |
| 514 | | try module.pdb.string_table.seekTo(strtab_offset); |
| 515 | | const source_file_name = try module.pdb.string_table.readNullTermString(allocator); |
| 516 | | |
| 517 | | const line_entry_idx = line_i - 1; |
| 518 | | |
| 519 | | const column = if (has_column) blk: { |
| 520 | | const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; |
| 521 | | const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; |
| 522 | | const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); |
| 523 | | break :blk col_num_entry.StartColumn; |
| 524 | | } else 0; |
| 525 | | |
| 526 | | const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); |
| 527 | | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); |
| 528 | | const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); |
| 529 | | |
| 530 | | break :subsections LineInfo{ |
| 531 | | .allocator = allocator, |
| 532 | | .file_name = source_file_name, |
| 533 | | .line = flags.Start, |
| 534 | | .column = column, |
| 535 | | }; |
| 536 | | } |
| 537 | | } |
| 538 | | |
| 539 | | // Checking that we are not reading garbage after the (possibly) multiple block fragments. |
| 540 | | if (line_index != subsection_end_index) { |
| 541 | | return error.InvalidDebugInfo; |
| 542 | | } |
| 543 | | } |
| 544 | | }, |
| 545 | | else => {}, |
| 546 | | } |
| 547 | | |
| 548 | | if (sect_offset > subsect_info.len) |
| 549 | | return error.InvalidDebugInfo; |
| 550 | | } else { |
| 551 | | break :subsections null; |
| 552 | | } |
| 553 | | }; |
| 554 | | |
| 555 | | try printLineInfo( |
| 556 | | out_stream, |
| 557 | | opt_line_info, |
| 558 | | relocated_address, |
| 559 | | symbol_name, |
| 560 | | obj_basename, |
| 561 | | tty_config, |
| 562 | | printLineFromFileAnyOs, |
| 563 | | ); |
| 564 | | } |
| 565 | | |
| 566 | 393 | pub const TTY = struct { |
| 567 | 394 | pub const Color = enum { |
| 568 | 395 | Red, |
| ... | ... | @@ -698,23 +525,32 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach |
| 698 | 525 | return null; |
| 699 | 526 | } |
| 700 | 527 | |
| 701 | | pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { |
| 528 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 529 | pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { |
| 702 | 530 | const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { |
| 703 | 531 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 704 | | return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); |
| 532 | return printLineInfo( |
| 533 | out_stream, |
| 534 | null, |
| 535 | address, |
| 536 | "???", |
| 537 | "???", |
| 538 | tty_config, |
| 539 | printLineFromFileAnyOs, |
| 540 | ); |
| 705 | 541 | }, |
| 706 | 542 | else => return err, |
| 707 | 543 | }; |
| 708 | 544 | |
| 709 | | const info = try module.getSymbolAtAddress(address); |
| 710 | | defer info.deinit(); |
| 545 | const symbol_info = try module.getSymbolAtAddress(address); |
| 546 | defer symbol_info.deinit(); |
| 711 | 547 | |
| 712 | 548 | return printLineInfo( |
| 713 | 549 | out_stream, |
| 714 | | info.line_info, |
| 550 | symbol_info.line_info, |
| 715 | 551 | address, |
| 716 | | info.symbol_name, |
| 717 | | info.compile_unit_name, |
| 552 | symbol_info.symbol_name, |
| 553 | symbol_info.compile_unit_name, |
| 718 | 554 | tty_config, |
| 719 | 555 | printLineFromFileAnyOs, |
| 720 | 556 | ); |
| ... | ... | @@ -972,7 +808,7 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { |
| 972 | 808 | |
| 973 | 809 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 974 | 810 | pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo { |
| 975 | | const mapped_mem = mapWholeFile(elf_file_path) catch |_| return error.InvalidDebugInfo; |
| 811 | const mapped_mem = try mapWholeFile(elf_file_path); |
| 976 | 812 | |
| 977 | 813 | var seekable_stream = io.SliceSeekableInStream.init(mapped_mem); |
| 978 | 814 | var efile = try elf.Elf.openStream( |
| ... | ... | @@ -1015,7 +851,7 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !M |
| 1015 | 851 | |
| 1016 | 852 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 1017 | 853 | fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ModuleDebugInfo { |
| 1018 | | const mapped_mem = mapWholeFile(macho_file_path) catch |_| return error.InvalidDebugInfo; |
| 854 | const mapped_mem = try mapWholeFile(macho_file_path); |
| 1019 | 855 | |
| 1020 | 856 | const hdr = @ptrCast( |
| 1021 | 857 | *const macho.mach_header_64, |
| ... | ... | @@ -1228,12 +1064,15 @@ pub const DebugInfo = struct { |
| 1228 | 1064 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1229 | 1065 | errdefer self.allocator.destroy(obj_di); |
| 1230 | 1066 | |
| 1231 | | try self.address_map.putNoClobber(base_address, obj_di); |
| 1232 | | |
| 1233 | 1067 | const macho_path = mem.toSliceConst(u8, std.c._dyld_get_image_name(i)); |
| 1234 | | obj_di.* = try openMachODebugInfo(self.allocator, macho_path); |
| 1068 | obj_di.* = openMachODebugInfo(self.allocator, macho_path) catch |err| switch (err) { |
| 1069 | error.FileNotFound => return error.MissingDebugInfo, |
| 1070 | else => return err, |
| 1071 | }; |
| 1235 | 1072 | obj_di.base_address = base_address; |
| 1236 | 1073 | |
| 1074 | try self.address_map.putNoClobber(base_address, obj_di); |
| 1075 | |
| 1237 | 1076 | return obj_di; |
| 1238 | 1077 | } |
| 1239 | 1078 | } |
| ... | ... | @@ -1307,11 +1146,14 @@ pub const DebugInfo = struct { |
| 1307 | 1146 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1308 | 1147 | errdefer self.allocator.destroy(obj_di); |
| 1309 | 1148 | |
| 1310 | | try self.address_map.putNoClobber(seg_start, obj_di); |
| 1311 | | |
| 1312 | | obj_di.* = try openCoffDebugInfo(self.allocator, name_buffer[0..:0]); |
| 1149 | obj_di.* = openCoffDebugInfo(self.allocator, name_buffer[0..:0]) catch |err| switch (err) { |
| 1150 | error.FileNotFound => return error.MissingDebugInfo, |
| 1151 | else => return err, |
| 1152 | }; |
| 1313 | 1153 | obj_di.base_address = seg_start; |
| 1314 | 1154 | |
| 1155 | try self.address_map.putNoClobber(seg_start, obj_di); |
| 1156 | |
| 1315 | 1157 | return obj_di; |
| 1316 | 1158 | } |
| 1317 | 1159 | } |
| ... | ... | @@ -1376,11 +1218,14 @@ pub const DebugInfo = struct { |
| 1376 | 1218 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1377 | 1219 | errdefer self.allocator.destroy(obj_di); |
| 1378 | 1220 | |
| 1379 | | try self.address_map.putNoClobber(ctx.base_address, obj_di); |
| 1380 | | |
| 1381 | | obj_di.* = try openElfDebugInfo(self.allocator, elf_path); |
| 1221 | obj_di.* = openElfDebugInfo(self.allocator, elf_path) catch |err| switch (err) { |
| 1222 | error.FileNotFound => return error.MissingDebugInfo, |
| 1223 | else => return err, |
| 1224 | }; |
| 1382 | 1225 | obj_di.base_address = ctx.base_address; |
| 1383 | 1226 | |
| 1227 | try self.address_map.putNoClobber(ctx.base_address, obj_di); |
| 1228 | |
| 1384 | 1229 | return obj_di; |
| 1385 | 1230 | } |
| 1386 | 1231 | }; |
| ... | ... | @@ -1412,7 +1257,7 @@ pub const ModuleDebugInfo = switch (builtin.os) { |
| 1412 | 1257 | } |
| 1413 | 1258 | |
| 1414 | 1259 | fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo { |
| 1415 | | const mapped_mem = mapWholeFile(o_file_path) catch |_| return error.InvalidDebugInfo; |
| 1260 | const mapped_mem = try mapWholeFile(o_file_path); |
| 1416 | 1261 | |
| 1417 | 1262 | const hdr = @ptrCast( |
| 1418 | 1263 | *const macho.mach_header_64, |