| ... | @@ -390,179 +390,6 @@ pub fn writeCurrentStackTraceWindows( | ... | @@ -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 | pub const TTY = struct { | 393 | pub const TTY = struct { |
| 567 | pub const Color = enum { | 394 | pub const Color = enum { |
| 568 | Red, | 395 | Red, |
| ... | @@ -698,23 +525,32 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach | ... | @@ -698,23 +525,32 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach |
| 698 | return null; | 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 | const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { | 530 | const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { |
| 703 | error.MissingDebugInfo, error.InvalidDebugInfo => { | 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 | else => return err, | 542 | else => return err, |
| 707 | }; | 543 | }; |
| 708 | | 544 | |
| 709 | const info = try module.getSymbolAtAddress(address); | 545 | const symbol_info = try module.getSymbolAtAddress(address); |
| 710 | defer info.deinit(); | 546 | defer symbol_info.deinit(); |
| 711 | | 547 | |
| 712 | return printLineInfo( | 548 | return printLineInfo( |
| 713 | out_stream, | 549 | out_stream, |
| 714 | info.line_info, | 550 | symbol_info.line_info, |
| 715 | address, | 551 | address, |
| 716 | info.symbol_name, | 552 | symbol_info.symbol_name, |
| 717 | info.compile_unit_name, | 553 | symbol_info.compile_unit_name, |
| 718 | tty_config, | 554 | tty_config, |
| 719 | printLineFromFileAnyOs, | 555 | printLineFromFileAnyOs, |
| 720 | ); | 556 | ); |
| ... | @@ -972,7 +808,7 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { | ... | @@ -972,7 +808,7 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { |
| 972 | | 808 | |
| 973 | /// TODO resources https://github.com/ziglang/zig/issues/4353 | 809 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 974 | pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo { | 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 | var seekable_stream = io.SliceSeekableInStream.init(mapped_mem); | 813 | var seekable_stream = io.SliceSeekableInStream.init(mapped_mem); |
| 978 | var efile = try elf.Elf.openStream( | 814 | var efile = try elf.Elf.openStream( |
| ... | @@ -1015,7 +851,7 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !M | ... | @@ -1015,7 +851,7 @@ pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !M |
| 1015 | | 851 | |
| 1016 | /// TODO resources https://github.com/ziglang/zig/issues/4353 | 852 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 1017 | fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ModuleDebugInfo { | 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 | const hdr = @ptrCast( | 856 | const hdr = @ptrCast( |
| 1021 | *const macho.mach_header_64, | 857 | *const macho.mach_header_64, |
| ... | @@ -1228,12 +1064,15 @@ pub const DebugInfo = struct { | ... | @@ -1228,12 +1064,15 @@ pub const DebugInfo = struct { |
| 1228 | const obj_di = try self.allocator.create(ModuleDebugInfo); | 1064 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1229 | errdefer self.allocator.destroy(obj_di); | 1065 | errdefer self.allocator.destroy(obj_di); |
| 1230 | | 1066 | |
| 1231 | try self.address_map.putNoClobber(base_address, obj_di); | | |
| 1232 | | | |
| 1233 | const macho_path = mem.toSliceConst(u8, std.c._dyld_get_image_name(i)); | 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 | obj_di.base_address = base_address; | 1072 | obj_di.base_address = base_address; |
| 1236 | | 1073 | |
| | 1074 | try self.address_map.putNoClobber(base_address, obj_di); |
| | 1075 | |
| 1237 | return obj_di; | 1076 | return obj_di; |
| 1238 | } | 1077 | } |
| 1239 | } | 1078 | } |
| ... | @@ -1307,11 +1146,14 @@ pub const DebugInfo = struct { | ... | @@ -1307,11 +1146,14 @@ pub const DebugInfo = struct { |
| 1307 | const obj_di = try self.allocator.create(ModuleDebugInfo); | 1146 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1308 | errdefer self.allocator.destroy(obj_di); | 1147 | errdefer self.allocator.destroy(obj_di); |
| 1309 | | 1148 | |
| 1310 | try self.address_map.putNoClobber(seg_start, obj_di); | 1149 | obj_di.* = openCoffDebugInfo(self.allocator, name_buffer[0..:0]) catch |err| switch (err) { |
| 1311 | | 1150 | error.FileNotFound => return error.MissingDebugInfo, |
| 1312 | obj_di.* = try openCoffDebugInfo(self.allocator, name_buffer[0..:0]); | 1151 | else => return err, |
| | 1152 | }; |
| 1313 | obj_di.base_address = seg_start; | 1153 | obj_di.base_address = seg_start; |
| 1314 | | 1154 | |
| | 1155 | try self.address_map.putNoClobber(seg_start, obj_di); |
| | 1156 | |
| 1315 | return obj_di; | 1157 | return obj_di; |
| 1316 | } | 1158 | } |
| 1317 | } | 1159 | } |
| ... | @@ -1376,11 +1218,14 @@ pub const DebugInfo = struct { | ... | @@ -1376,11 +1218,14 @@ pub const DebugInfo = struct { |
| 1376 | const obj_di = try self.allocator.create(ModuleDebugInfo); | 1218 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1377 | errdefer self.allocator.destroy(obj_di); | 1219 | errdefer self.allocator.destroy(obj_di); |
| 1378 | | 1220 | |
| 1379 | try self.address_map.putNoClobber(ctx.base_address, obj_di); | 1221 | obj_di.* = openElfDebugInfo(self.allocator, elf_path) catch |err| switch (err) { |
| 1380 | | 1222 | error.FileNotFound => return error.MissingDebugInfo, |
| 1381 | obj_di.* = try openElfDebugInfo(self.allocator, elf_path); | 1223 | else => return err, |
| | 1224 | }; |
| 1382 | obj_di.base_address = ctx.base_address; | 1225 | obj_di.base_address = ctx.base_address; |
| 1383 | | 1226 | |
| | 1227 | try self.address_map.putNoClobber(ctx.base_address, obj_di); |
| | 1228 | |
| 1384 | return obj_di; | 1229 | return obj_di; |
| 1385 | } | 1230 | } |
| 1386 | }; | 1231 | }; |
| ... | @@ -1412,7 +1257,7 @@ pub const ModuleDebugInfo = switch (builtin.os) { | ... | @@ -1412,7 +1257,7 @@ pub const ModuleDebugInfo = switch (builtin.os) { |
| 1412 | } | 1257 | } |
| 1413 | | 1258 | |
| 1414 | fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo { | 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 | const hdr = @ptrCast( | 1262 | const hdr = @ptrCast( |
| 1418 | *const macho.mach_header_64, | 1263 | *const macho.mach_header_64, |