| ... | @@ -12,12 +12,66 @@ const Path = std.Build.Cache.Path; | ... | @@ -12,12 +12,66 @@ const Path = std.Build.Cache.Path; |
| 12 | const Dwarf = std.debug.Dwarf; | 12 | const Dwarf = std.debug.Dwarf; |
| 13 | const page_size = std.mem.page_size; | 13 | const page_size = std.mem.page_size; |
| 14 | const assert = std.debug.assert; | 14 | const assert = std.debug.assert; |
| | 15 | const Hash = std.hash.Wyhash; |
| 15 | | 16 | |
| 16 | const Info = @This(); | 17 | const Info = @This(); |
| 17 | | 18 | |
| 18 | /// Sorted by key, ascending. | 19 | /// Sorted by key, ascending. |
| 19 | address_map: std.AutoArrayHashMapUnmanaged(u64, Dwarf.ElfModule), | 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 | pub const LoadError = Dwarf.ElfModule.LoadError; | 75 | pub const LoadError = Dwarf.ElfModule.LoadError; |
| 22 | | 76 | |
| 23 | pub fn load(gpa: Allocator, path: Path) LoadError!Info { | 77 | pub fn load(gpa: Allocator, path: Path) LoadError!Info { |
| ... | @@ -26,12 +80,17 @@ pub fn load(gpa: Allocator, path: Path) LoadError!Info { | ... | @@ -26,12 +80,17 @@ pub fn load(gpa: Allocator, path: Path) LoadError!Info { |
| 26 | try elf_module.dwarf.sortCompileUnits(); | 80 | try elf_module.dwarf.sortCompileUnits(); |
| 27 | var info: Info = .{ | 81 | var info: Info = .{ |
| 28 | .address_map = .{}, | 82 | .address_map = .{}, |
| | 83 | .directories = .{}, |
| | 84 | .files = .{}, |
| | 85 | .mutex = .{}, |
| 29 | }; | 86 | }; |
| 30 | try info.address_map.put(gpa, elf_module.base_address, elf_module); | 87 | try info.address_map.put(gpa, elf_module.base_address, elf_module); |
| 31 | return info; | 88 | return info; |
| 32 | } | 89 | } |
| 33 | | 90 | |
| 34 | pub fn deinit(info: *Info, gpa: Allocator) void { | 91 | pub fn deinit(info: *Info, gpa: Allocator) void { |
| | 92 | info.directories.deinit(gpa); |
| | 93 | info.files.deinit(gpa); |
| 35 | for (info.address_map.values()) |*elf_module| { | 94 | for (info.address_map.values()) |*elf_module| { |
| 36 | elf_module.dwarf.deinit(gpa); | 95 | elf_module.dwarf.deinit(gpa); |
| 37 | } | 96 | } |
| ... | @@ -39,17 +98,98 @@ pub fn deinit(info: *Info, gpa: Allocator) void { | ... | @@ -39,17 +98,98 @@ pub fn deinit(info: *Info, gpa: Allocator) void { |
| 39 | info.* = undefined; | 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 | pub fn resolveSourceLocations( | 109 | pub fn resolveSourceLocations( |
| 45 | info: *Info, | 110 | info: *Info, |
| 46 | gpa: Allocator, | 111 | gpa: Allocator, |
| 47 | sorted_pc_addrs: []const u64, | 112 | sorted_pc_addrs: []const u64, |
| 48 | /// Asserts its length equals length of `sorted_pc_addrs`. | 113 | /// Asserts its length equals length of `sorted_pc_addrs`. |
| 49 | output: []std.debug.SourceLocation, | 114 | output: []SourceLocation, |
| 50 | ) ResolveSourceLocationsError!void { | 115 | ) ResolveSourceLocationsError!void { |
| 51 | assert(sorted_pc_addrs.len == output.len); | 116 | assert(sorted_pc_addrs.len == output.len); |
| 52 | if (info.address_map.entries.len != 1) @panic("TODO"); | 117 | if (info.address_map.entries.len != 1) @panic("TODO"); |
| 53 | const elf_module = &info.address_map.values()[0]; | 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 | } |