| ... | ... | @@ -12,12 +12,66 @@ const Path = std.Build.Cache.Path; |
| 12 | 12 | const Dwarf = std.debug.Dwarf; |
| 13 | 13 | const page_size = std.mem.page_size; |
| 14 | 14 | const assert = std.debug.assert; |
| 15 | const Hash = std.hash.Wyhash; |
| 15 | 16 | |
| 16 | 17 | const Info = @This(); |
| 17 | 18 | |
| 18 | 19 | /// Sorted by key, ascending. |
| 19 | 20 | address_map: std.AutoArrayHashMapUnmanaged(u64, Dwarf.ElfModule), |
| 20 | 21 | |
| 22 | /// Provides a globally-scoped integer index for directories. |
| 23 | /// |
| 24 | /// As opposed to, for example, a directory index that is compilation-unit |
| 25 | /// scoped inside a single ELF module. |
| 26 | /// |
| 27 | /// String memory references the memory-mapped debug information. |
| 28 | /// |
| 29 | /// Protected by `mutex`. |
| 30 | directories: std.StringArrayHashMapUnmanaged(void), |
| 31 | /// Provides a globally-scoped integer index for files. |
| 32 | /// |
| 33 | /// String memory references the memory-mapped debug information. |
| 34 | /// |
| 35 | /// Protected by `mutex`. |
| 36 | files: std.ArrayHashMapUnmanaged(File, void, File.MapContext, false), |
| 37 | /// Protects `directories` and `files`. |
| 38 | mutex: std.Thread.Mutex, |
| 39 | |
| 40 | pub const SourceLocation = struct { |
| 41 | file: File.Index, |
| 42 | line: u32, |
| 43 | column: u32, |
| 44 | |
| 45 | pub const invalid: SourceLocation = .{ |
| 46 | .file = .invalid, |
| 47 | .line = 0, |
| 48 | .column = 0, |
| 49 | }; |
| 50 | }; |
| 51 | |
| 52 | pub const File = struct { |
| 53 | directory_index: u32, |
| 54 | basename: []const u8, |
| 55 | |
| 56 | pub const Index = enum(u32) { |
| 57 | invalid = std.math.maxInt(u32), |
| 58 | _, |
| 59 | }; |
| 60 | |
| 61 | pub const MapContext = struct { |
| 62 | pub fn hash(ctx: MapContext, a: File) u32 { |
| 63 | _ = ctx; |
| 64 | return @truncate(Hash.hash(a.directory_index, a.basename)); |
| 65 | } |
| 66 | |
| 67 | pub fn eql(ctx: MapContext, a: File, b: File, b_index: usize) bool { |
| 68 | _ = ctx; |
| 69 | _ = b_index; |
| 70 | return a.directory_index == b.directory_index and std.mem.eql(u8, a.basename, b.basename); |
| 71 | } |
| 72 | }; |
| 73 | }; |
| 74 | |
| 21 | 75 | pub const LoadError = Dwarf.ElfModule.LoadError; |
| 22 | 76 | |
| 23 | 77 | pub fn load(gpa: Allocator, path: Path) LoadError!Info { |
| ... | ... | @@ -26,12 +80,17 @@ pub fn load(gpa: Allocator, path: Path) LoadError!Info { |
| 26 | 80 | try elf_module.dwarf.sortCompileUnits(); |
| 27 | 81 | var info: Info = .{ |
| 28 | 82 | .address_map = .{}, |
| 83 | .directories = .{}, |
| 84 | .files = .{}, |
| 85 | .mutex = .{}, |
| 29 | 86 | }; |
| 30 | 87 | try info.address_map.put(gpa, elf_module.base_address, elf_module); |
| 31 | 88 | return info; |
| 32 | 89 | } |
| 33 | 90 | |
| 34 | 91 | pub fn deinit(info: *Info, gpa: Allocator) void { |
| 92 | info.directories.deinit(gpa); |
| 93 | info.files.deinit(gpa); |
| 35 | 94 | for (info.address_map.values()) |*elf_module| { |
| 36 | 95 | elf_module.dwarf.deinit(gpa); |
| 37 | 96 | } |
| ... | ... | @@ -39,17 +98,98 @@ pub fn deinit(info: *Info, gpa: Allocator) void { |
| 39 | 98 | info.* = undefined; |
| 40 | 99 | } |
| 41 | 100 | |
| 42 | | pub const ResolveSourceLocationsError = Dwarf.ResolveSourceLocationsError; |
| 101 | pub fn fileAt(info: *Info, index: File.Index) *File { |
| 102 | return &info.files.keys()[@intFromEnum(index)]; |
| 103 | } |
| 104 | |
| 105 | pub const ResolveSourceLocationsError = Dwarf.ScanError; |
| 43 | 106 | |
| 107 | /// Given an array of virtual memory addresses, sorted ascending, outputs a |
| 108 | /// corresponding array of source locations. |
| 44 | 109 | pub fn resolveSourceLocations( |
| 45 | 110 | info: *Info, |
| 46 | 111 | gpa: Allocator, |
| 47 | 112 | sorted_pc_addrs: []const u64, |
| 48 | 113 | /// Asserts its length equals length of `sorted_pc_addrs`. |
| 49 | | output: []std.debug.SourceLocation, |
| 114 | output: []SourceLocation, |
| 50 | 115 | ) ResolveSourceLocationsError!void { |
| 51 | 116 | assert(sorted_pc_addrs.len == output.len); |
| 52 | 117 | if (info.address_map.entries.len != 1) @panic("TODO"); |
| 53 | 118 | const elf_module = &info.address_map.values()[0]; |
| 54 | | return elf_module.dwarf.resolveSourceLocations(gpa, sorted_pc_addrs, output); |
| 119 | return resolveSourceLocationsDwarf(info, gpa, sorted_pc_addrs, output, &elf_module.dwarf); |
| 120 | } |
| 121 | |
| 122 | pub fn resolveSourceLocationsDwarf( |
| 123 | info: *Info, |
| 124 | gpa: Allocator, |
| 125 | sorted_pc_addrs: []const u64, |
| 126 | /// Asserts its length equals length of `sorted_pc_addrs`. |
| 127 | output: []SourceLocation, |
| 128 | d: *Dwarf, |
| 129 | ) ResolveSourceLocationsError!void { |
| 130 | assert(sorted_pc_addrs.len == output.len); |
| 131 | assert(d.compile_units_sorted); |
| 132 | |
| 133 | var cu_i: usize = 0; |
| 134 | var line_table_i: usize = 0; |
| 135 | var cu: *Dwarf.CompileUnit = &d.compile_unit_list.items[0]; |
| 136 | var range = cu.pc_range.?; |
| 137 | // Protects directories and files tables from other threads. |
| 138 | info.mutex.lock(); |
| 139 | defer info.mutex.unlock(); |
| 140 | next_pc: for (sorted_pc_addrs, output) |pc, *out| { |
| 141 | while (pc >= range.end) { |
| 142 | cu_i += 1; |
| 143 | if (cu_i >= d.compile_unit_list.items.len) { |
| 144 | out.* = SourceLocation.invalid; |
| 145 | continue :next_pc; |
| 146 | } |
| 147 | cu = &d.compile_unit_list.items[cu_i]; |
| 148 | line_table_i = 0; |
| 149 | range = cu.pc_range orelse { |
| 150 | out.* = SourceLocation.invalid; |
| 151 | continue :next_pc; |
| 152 | }; |
| 153 | } |
| 154 | if (pc < range.start) { |
| 155 | out.* = SourceLocation.invalid; |
| 156 | continue :next_pc; |
| 157 | } |
| 158 | if (line_table_i == 0) { |
| 159 | line_table_i = 1; |
| 160 | info.mutex.unlock(); |
| 161 | defer info.mutex.lock(); |
| 162 | d.populateSrcLocCache(gpa, cu) catch |err| switch (err) { |
| 163 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 164 | out.* = SourceLocation.invalid; |
| 165 | cu_i += 1; |
| 166 | if (cu_i < d.compile_unit_list.items.len) { |
| 167 | cu = &d.compile_unit_list.items[cu_i]; |
| 168 | line_table_i = 0; |
| 169 | if (cu.pc_range) |r| range = r; |
| 170 | } |
| 171 | continue :next_pc; |
| 172 | }, |
| 173 | else => |e| return e, |
| 174 | }; |
| 175 | } |
| 176 | const slc = &cu.src_loc_cache.?; |
| 177 | const table_addrs = slc.line_table.keys(); |
| 178 | while (line_table_i < table_addrs.len and table_addrs[line_table_i] < pc) line_table_i += 1; |
| 179 | |
| 180 | const entry = slc.line_table.values()[line_table_i - 1]; |
| 181 | const corrected_file_index = entry.file - @intFromBool(slc.version < 5); |
| 182 | const file_entry = slc.files[corrected_file_index]; |
| 183 | const dir_path = slc.directories[file_entry.dir_index].path; |
| 184 | const dir_gop = try info.directories.getOrPut(gpa, dir_path); |
| 185 | const file_gop = try info.files.getOrPut(gpa, .{ |
| 186 | .directory_index = @intCast(dir_gop.index), |
| 187 | .basename = file_entry.path, |
| 188 | }); |
| 189 | out.* = .{ |
| 190 | .file = @enumFromInt(file_gop.index), |
| 191 | .line = entry.line, |
| 192 | .column = entry.column, |
| 193 | }; |
| 194 | } |
| 55 | 195 | } |