| ... | @@ -44,9 +44,11 @@ abbrev_table_offset: ?u64 = null, | ... | @@ -44,9 +44,11 @@ abbrev_table_offset: ?u64 = null, |
| 44 | /// Table of debug symbol names. | 44 | /// Table of debug symbol names. |
| 45 | strtab: std.ArrayListUnmanaged(u8) = .{}, | 45 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 46 | | 46 | |
| 47 | di_files: std.ArrayListUnmanaged(DIFile) = .{}, | 47 | /// Quick lookup array of all defined source files referenced by at least one Decl. |
| 48 | di_files_free_list: std.ArrayListUnmanaged(u28) = .{}, | 48 | /// They will end up in the DWARF debug_line header as two lists: |
| 49 | di_files_lookup: std.AutoHashMapUnmanaged(*const Module.File, u28) = .{}, | 49 | /// * []include_directory |
| | 50 | /// * []file_names |
| | 51 | di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{}, |
| 50 | | 52 | |
| 51 | /// List of atoms that are owned directly by the DWARF module. | 53 | /// List of atoms that are owned directly by the DWARF module. |
| 52 | /// TODO convert links in DebugInfoAtom into indices and make | 54 | /// TODO convert links in DebugInfoAtom into indices and make |
| ... | @@ -55,17 +57,6 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, | ... | @@ -55,17 +57,6 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, |
| 55 | | 57 | |
| 56 | global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, | 58 | global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, |
| 57 | | 59 | |
| 58 | const DIFile = struct { | | |
| 59 | file_source: *const Module.File, | | |
| 60 | ref_count: u32, | | |
| 61 | | | |
| 62 | fn getFullyResolvedPath(dif: DIFile, allocator: Allocator) ![]const u8 { | | |
| 63 | const path = try dif.file_source.fullPath(allocator); | | |
| 64 | defer allocator.free(path); | | |
| 65 | return fs.realpathAlloc(allocator, path); | | |
| 66 | } | | |
| 67 | }; | | |
| 68 | | | |
| 69 | pub const Atom = struct { | 60 | pub const Atom = struct { |
| 70 | /// Previous/next linked list pointers. | 61 | /// Previous/next linked list pointers. |
| 71 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | 62 | /// This is the linked list node for this Decl's corresponding .debug_info tag. |
| ... | @@ -922,8 +913,6 @@ pub fn deinit(self: *Dwarf) void { | ... | @@ -922,8 +913,6 @@ pub fn deinit(self: *Dwarf) void { |
| 922 | self.atom_free_list.deinit(gpa); | 913 | self.atom_free_list.deinit(gpa); |
| 923 | self.strtab.deinit(gpa); | 914 | self.strtab.deinit(gpa); |
| 924 | self.di_files.deinit(gpa); | 915 | self.di_files.deinit(gpa); |
| 925 | self.di_files_free_list.deinit(gpa); | | |
| 926 | self.di_files_lookup.deinit(gpa); | | |
| 927 | self.global_abbrev_relocs.deinit(gpa); | 916 | self.global_abbrev_relocs.deinit(gpa); |
| 928 | | 917 | |
| 929 | for (self.managed_atoms.items) |atom| { | 918 | for (self.managed_atoms.items) |atom| { |
| ... | @@ -1154,7 +1143,7 @@ pub fn commitDeclState( | ... | @@ -1154,7 +1143,7 @@ pub fn commitDeclState( |
| 1154 | self.dbg_line_fn_last = src_fn; | 1143 | self.dbg_line_fn_last = src_fn; |
| 1155 | | 1144 | |
| 1156 | // TODO TEXME JK | 1145 | // TODO TEXME JK |
| 1157 | src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(module) * 100); | 1146 | src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{}) * 100); |
| 1158 | } | 1147 | } |
| 1159 | | 1148 | |
| 1160 | const last_src_fn = self.dbg_line_fn_last.?; | 1149 | const last_src_fn = self.dbg_line_fn_last.?; |
| ... | @@ -1649,16 +1638,6 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { | ... | @@ -1649,16 +1638,6 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { |
| 1649 | if (self.dbg_line_fn_last == fn_link) { | 1638 | if (self.dbg_line_fn_last == fn_link) { |
| 1650 | self.dbg_line_fn_last = fn_link.prev; | 1639 | self.dbg_line_fn_last = fn_link.prev; |
| 1651 | } | 1640 | } |
| 1652 | | | |
| 1653 | const file_source = decl.getFileScope(); | | |
| 1654 | if (self.di_files_lookup.get(file_source)) |dif_index| { | | |
| 1655 | const dif = &self.di_files.items[dif_index]; | | |
| 1656 | dif.ref_count -= 1; | | |
| 1657 | if (dif.ref_count == 0) { | | |
| 1658 | self.di_files_free_list.append(gpa, dif_index) catch {}; | | |
| 1659 | } | | |
| 1660 | _ = self.di_files_lookup.remove(file_source); | | |
| 1661 | } | | |
| 1662 | } | 1641 | } |
| 1663 | | 1642 | |
| 1664 | pub fn writeDbgAbbrev(self: *Dwarf) !void { | 1643 | pub fn writeDbgAbbrev(self: *Dwarf) !void { |
| ... | @@ -1902,7 +1881,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u | ... | @@ -1902,7 +1881,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1902 | } | 1881 | } |
| 1903 | // Write the form for the compile unit, which must match the abbrev table above. | 1882 | // Write the form for the compile unit, which must match the abbrev table above. |
| 1904 | const name_strp = try self.makeString(module.root_pkg.root_src_path); | 1883 | const name_strp = try self.makeString(module.root_pkg.root_src_path); |
| 1905 | const comp_dir_strp = try self.makeString(module.root_pkg.root_src_directory.path orelse "."); | 1884 | const compile_unit_dir = self.getCompDir(module); |
| | 1885 | const comp_dir_strp = try self.makeString(compile_unit_dir); |
| 1906 | const producer_strp = try self.makeString(link.producer_string); | 1886 | const producer_strp = try self.makeString(link.producer_string); |
| 1907 | | 1887 | |
| 1908 | di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit)); | 1888 | di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit)); |
| ... | @@ -1954,6 +1934,21 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u | ... | @@ -1954,6 +1934,21 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1954 | } | 1934 | } |
| 1955 | } | 1935 | } |
| 1956 | | 1936 | |
| | 1937 | fn getCompDir(self: Dwarf, module: *Module) []const u8 { |
| | 1938 | // For macOS stack traces, we want to avoid having to parse the compilation unit debug |
| | 1939 | // info. As long as each debug info file has a path independent of the compilation unit |
| | 1940 | // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug |
| | 1941 | // info. If we provide an absolute path to LLVM here for the compilation unit debug |
| | 1942 | // info, LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we |
| | 1943 | // pass "." for the compilation unit directory. This forces each debug file to have a |
| | 1944 | // directory rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug |
| | 1945 | // files will no longer reference DW_AT_comp_dir, for the purpose of being able to |
| | 1946 | // support the common practice of stripping all but the line number sections from an |
| | 1947 | // executable. |
| | 1948 | if (self.bin_file.tag == .macho) return "."; |
| | 1949 | return module.root_pkg.root_src_directory.path orelse "."; |
| | 1950 | } |
| | 1951 | |
| 1957 | fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void { | 1952 | fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void { |
| 1958 | const target_endian = self.target.cpu.arch.endian(); | 1953 | const target_endian = self.target.cpu.arch.endian(); |
| 1959 | switch (self.ptr_width) { | 1954 | switch (self.ptr_width) { |
| ... | @@ -2299,10 +2294,15 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { | ... | @@ -2299,10 +2294,15 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { |
| 2299 | const dbg_line_prg_end = self.getDebugLineProgramEnd().?; | 2294 | const dbg_line_prg_end = self.getDebugLineProgramEnd().?; |
| 2300 | assert(dbg_line_prg_end != 0); | 2295 | assert(dbg_line_prg_end != 0); |
| 2301 | | 2296 | |
| | 2297 | // Convert all input DI files into a set of include dirs and file names. |
| | 2298 | var arena = std.heap.ArenaAllocator.init(self.allocator); |
| | 2299 | defer arena.deinit(); |
| | 2300 | const paths = try self.genIncludeDirsAndFileNames(arena.allocator(), module); |
| | 2301 | |
| 2302 | // The size of this header is variable, depending on the number of directories, | 2302 | // The size of this header is variable, depending on the number of directories, |
| 2303 | // files, and padding. We have a function to compute the upper bound size, however, | 2303 | // files, and padding. We have a function to compute the upper bound size, however, |
| 2304 | // because it's needed for determining where to put the offset of the first `SrcFn`. | 2304 | // because it's needed for determining where to put the offset of the first `SrcFn`. |
| 2305 | const needed_bytes = self.dbgLineNeededHeaderBytes(module); | 2305 | const needed_bytes = self.dbgLineNeededHeaderBytes(paths.dirs, paths.files); |
| 2306 | log.debug("dbg_line_prg_off = {x}, needed_bytes = {x}", .{ dbg_line_prg_off, needed_bytes }); | 2306 | log.debug("dbg_line_prg_off = {x}, needed_bytes = {x}", .{ dbg_line_prg_off, needed_bytes }); |
| 2307 | var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes); | 2307 | var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes); |
| 2308 | defer di_buf.deinit(); | 2308 | defer di_buf.deinit(); |
| ... | @@ -2356,17 +2356,22 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { | ... | @@ -2356,17 +2356,22 @@ pub fn writeDbgLineHeader(self: *Dwarf, module: *Module) !void { |
| 2356 | 0, // `DW.LNS.set_prologue_end` | 2356 | 0, // `DW.LNS.set_prologue_end` |
| 2357 | 0, // `DW.LNS.set_epilogue_begin` | 2357 | 0, // `DW.LNS.set_epilogue_begin` |
| 2358 | 1, // `DW.LNS.set_isa` | 2358 | 1, // `DW.LNS.set_isa` |
| 2359 | 0, // include_directories (none except the compilation unit cwd) | | |
| 2360 | }); | 2359 | }); |
| 2361 | | 2360 | |
| 2362 | for (self.di_files.items) |dif, i| { | 2361 | for (paths.dirs) |dir, i| { |
| 2363 | const full_path = try dif.getFullyResolvedPath(self.allocator); | 2362 | log.debug("adding new include dir at {d} of '{s}'", .{ i + 1, dir }); |
| 2364 | defer self.allocator.free(full_path); | 2363 | di_buf.appendSliceAssumeCapacity(dir); |
| 2365 | log.debug("adding new file name at {d} of '{s}'", .{ i + 1, full_path }); | 2364 | di_buf.appendAssumeCapacity(0); |
| 2366 | di_buf.appendSliceAssumeCapacity(full_path); | 2365 | } |
| | 2366 | di_buf.appendAssumeCapacity(0); // include directories sentinel |
| | 2367 | |
| | 2368 | for (paths.files) |file, i| { |
| | 2369 | const dir_index = paths.files_dirs_indexes[i]; |
| | 2370 | log.debug("adding new file name at {d} of '{s}' referencing directory {d}", .{ i + 1, file, dir_index + 1 }); |
| | 2371 | di_buf.appendSliceAssumeCapacity(file); |
| 2367 | di_buf.appendSliceAssumeCapacity(&[_]u8{ | 2372 | di_buf.appendSliceAssumeCapacity(&[_]u8{ |
| 2368 | 0, // null byte for the relative path name | 2373 | 0, // null byte for the relative path name |
| 2369 | 0, // directory_index | 2374 | @intCast(u8, dir_index + 1), // directory_index |
| 2370 | 0, // mtime (TODO supply this) | 2375 | 0, // mtime (TODO supply this) |
| 2371 | 0, // file size bytes (TODO supply this) | 2376 | 0, // file size bytes (TODO supply this) |
| 2372 | }); | 2377 | }); |
| ... | @@ -2442,25 +2447,28 @@ fn ptrWidthBytes(self: Dwarf) u8 { | ... | @@ -2442,25 +2447,28 @@ fn ptrWidthBytes(self: Dwarf) u8 { |
| 2442 | }; | 2447 | }; |
| 2443 | } | 2448 | } |
| 2444 | | 2449 | |
| 2445 | fn dbgLineNeededHeaderBytes(self: Dwarf, module: *Module) u32 { | 2450 | fn dbgLineNeededHeaderBytes(self: Dwarf, dirs: []const []const u8, files: []const []const u8) u32 { |
| | 2451 | _ = self; |
| 2446 | const directory_entry_format_count = 1; | 2452 | const directory_entry_format_count = 1; |
| 2447 | const file_name_entry_format_count = 1; | 2453 | const file_name_entry_format_count = 1; |
| 2448 | const directory_count = 1; | 2454 | const directory_count = dirs.len + 1; |
| 2449 | const file_name_count = self.di_files.items.len; | 2455 | const file_name_count = files.len; |
| 2450 | const root_src_dir_path_len = if (module.root_pkg.root_src_directory.path) |p| p.len else 1; // "." | 2456 | |
| | 2457 | var dir_names_len: usize = 0; |
| | 2458 | for (dirs) |dir| { |
| | 2459 | dir_names_len += dir.len + 1; |
| | 2460 | } |
| 2451 | | 2461 | |
| 2452 | var file_names_len: usize = 0; | 2462 | var file_names_len: usize = 0; |
| 2453 | for (self.di_files.items) |dif| { | 2463 | for (files) |file| { |
| 2454 | const dir_path = dif.file_source.pkg.root_src_directory.path orelse "."; | 2464 | file_names_len += file.len + 1; |
| 2455 | file_names_len += dir_path.len + dif.file_source.sub_file_path.len + 1; | | |
| 2456 | } | 2465 | } |
| 2457 | | 2466 | |
| 2458 | return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + | 2467 | return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + |
| 2459 | directory_count * 8 + file_name_count * 8 + | 2468 | directory_count * 8 + file_name_count * 8 + |
| 2460 | // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like | 2469 | // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like |
| 2461 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. | 2470 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. |
| 2462 | root_src_dir_path_len + | 2471 | dir_names_len + file_names_len); |
| 2463 | file_names_len); | | |
| 2464 | } | 2472 | } |
| 2465 | | 2473 | |
| 2466 | /// The reloc offset for the line offset of a function from the previous function's line. | 2474 | /// The reloc offset for the line offset of a function from the previous function's line. |
| ... | @@ -2571,46 +2579,60 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { | ... | @@ -2571,46 +2579,60 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { |
| 2571 | } | 2579 | } |
| 2572 | } | 2580 | } |
| 2573 | | 2581 | |
| 2574 | fn allocateDIFileIndex(self: *Dwarf) !u28 { | | |
| 2575 | try self.di_files.ensureUnusedCapacity(self.allocator, 1); | | |
| 2576 | | | |
| 2577 | const index = blk: { | | |
| 2578 | if (self.di_files_free_list.popOrNull()) |index| { | | |
| 2579 | log.debug(" (reusing DIFile index {d})", .{index}); | | |
| 2580 | break :blk index; | | |
| 2581 | } else { | | |
| 2582 | const index = @intCast(u28, self.di_files.items.len); | | |
| 2583 | log.debug(" (allocating DIFile index {d})", .{index}); | | |
| 2584 | _ = self.di_files.addOneAssumeCapacity(); | | |
| 2585 | break :blk index; | | |
| 2586 | } | | |
| 2587 | }; | | |
| 2588 | | | |
| 2589 | return index; | | |
| 2590 | } | | |
| 2591 | | | |
| 2592 | fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 { | 2582 | fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 { |
| 2593 | const decl = mod.declPtr(decl_index); | 2583 | const decl = mod.declPtr(decl_index); |
| 2594 | const file_scope = decl.getFileScope(); | 2584 | const file_scope = decl.getFileScope(); |
| 2595 | const gop = try self.di_files_lookup.getOrPut(self.allocator, file_scope); | 2585 | const gop = try self.di_files.getOrPut(self.allocator, file_scope); |
| 2596 | if (!gop.found_existing) { | 2586 | if (!gop.found_existing) { |
| 2597 | gop.value_ptr.* = try self.allocateDIFileIndex(); | | |
| 2598 | self.di_files.items[gop.value_ptr.*] = .{ | | |
| 2599 | .file_source = file_scope, | | |
| 2600 | .ref_count = 1, | | |
| 2601 | }; | | |
| 2602 | | | |
| 2603 | switch (self.bin_file.tag) { | 2587 | switch (self.bin_file.tag) { |
| 2604 | .elf => self.bin_file.cast(File.Elf).?.debug_line_header_dirty = true, | 2588 | .elf => self.bin_file.cast(File.Elf).?.debug_line_header_dirty = true, |
| 2605 | .macho => self.bin_file.cast(File.MachO).?.d_sym.?.debug_line_header_dirty = true, | 2589 | .macho => self.bin_file.cast(File.MachO).?.d_sym.?.debug_line_header_dirty = true, |
| 2606 | .wasm => {}, | 2590 | .wasm => {}, |
| 2607 | else => unreachable, | 2591 | else => unreachable, |
| 2608 | } | 2592 | } |
| 2609 | } else { | | |
| 2610 | const dif = &self.di_files.items[gop.value_ptr.*]; | | |
| 2611 | dif.ref_count += 1; | | |
| 2612 | } | 2593 | } |
| 2613 | return gop.value_ptr.*; | 2594 | return @intCast(u28, gop.index); |
| | 2595 | } |
| | 2596 | |
| | 2597 | fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator, module: *Module) !struct { |
| | 2598 | dirs: []const []const u8, |
| | 2599 | files: []const []const u8, |
| | 2600 | files_dirs_indexes: []u28, |
| | 2601 | } { |
| | 2602 | var dirs = std.StringArrayHashMap(void).init(arena); |
| | 2603 | try dirs.ensureTotalCapacity(self.di_files.count()); |
| | 2604 | |
| | 2605 | var files = std.ArrayList([]const u8).init(arena); |
| | 2606 | try files.ensureTotalCapacityPrecise(self.di_files.count()); |
| | 2607 | |
| | 2608 | var files_dir_indexes = std.ArrayList(u28).init(arena); |
| | 2609 | try files_dir_indexes.ensureTotalCapacity(self.di_files.count()); |
| | 2610 | |
| | 2611 | const comp_dir = self.getCompDir(module); |
| | 2612 | |
| | 2613 | for (self.di_files.keys()) |dif| { |
| | 2614 | const full_path = try dif.fullPath(arena); |
| | 2615 | const dir_path = std.fs.path.dirname(full_path) orelse "."; |
| | 2616 | const sub_file_path = std.fs.path.basename(full_path); |
| | 2617 | |
| | 2618 | const dir_index: u28 = blk: { |
| | 2619 | const actual_dir_path = if (mem.indexOf(u8, dir_path, comp_dir)) |_| inner: { |
| | 2620 | if (comp_dir.len == dir_path.len) break :blk 0; |
| | 2621 | break :inner dir_path[comp_dir.len + 1 ..]; |
| | 2622 | } else dir_path; |
| | 2623 | const dirs_gop = dirs.getOrPutAssumeCapacity(actual_dir_path); |
| | 2624 | break :blk @intCast(u28, dirs_gop.index); |
| | 2625 | }; |
| | 2626 | |
| | 2627 | files_dir_indexes.appendAssumeCapacity(dir_index); |
| | 2628 | files.appendAssumeCapacity(sub_file_path); |
| | 2629 | } |
| | 2630 | |
| | 2631 | return .{ |
| | 2632 | .dirs = dirs.keys(), |
| | 2633 | .files = files.items, |
| | 2634 | .files_dirs_indexes = files_dir_indexes.items, |
| | 2635 | }; |
| 2614 | } | 2636 | } |
| 2615 | | 2637 | |
| 2616 | fn addDbgInfoErrorSet( | 2638 | fn addDbgInfoErrorSet( |