| author | |
| committer | |
| log | 22f9592dc7db869b2a618a72560fd0cf25b62d42 |
| tree | 88e34ab198a4cf85f321a4b18bcbd230a232b802 |
| parent | edfc4727e24b1c75f49aa42f1b84c7fc59176a32 |
5 files changed, 825 insertions(+), 98 deletions(-)
lib/std/debug.zig+38-29| ... | @@ -39,8 +39,8 @@ pub const cpu_context = @import("debug/cpu_context.zig"); | ... | @@ -39,8 +39,8 @@ pub const cpu_context = @import("debug/cpu_context.zig"); |
| 39 | /// pub const init: SelfInfo; | 39 | /// pub const init: SelfInfo; |
| 40 | /// pub fn deinit(si: *SelfInfo, io: Io) void; | 40 | /// pub fn deinit(si: *SelfInfo, io: Io) void; |
| 41 | /// | 41 | /// |
| 42 | /// /// Returns the symbol and source location of the instruction at `address`. | 42 | /// /// Returns an iterator over the symbols and source locations of the instruction at `address`. |
| 43 | /// pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) SelfInfoError!Symbol; | 43 | /// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfo.SymbolIterator; |
| 44 | /// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`. | 44 | /// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`. |
| 45 | /// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8; | 45 | /// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8; |
| 46 | /// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize; | 46 | /// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize; |
| ... | @@ -61,6 +61,11 @@ pub const cpu_context = @import("debug/cpu_context.zig"); | ... | @@ -61,6 +61,11 @@ pub const cpu_context = @import("debug/cpu_context.zig"); |
| 61 | /// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's | 61 | /// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's |
| 62 | /// /// return address, or 0 if the end of the stack has been reached. | 62 | /// /// return address, or 0 if the end of the stack has been reached. |
| 63 | /// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize; | 63 | /// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize; |
| 64 | /// /// Iterates symbols found at an address. | ||
| 65 | /// pub const SymbolIterator = struct { | ||
| 66 | /// pub fn deinit(Self: *SymbolIterator, io: Io) void; | ||
| 67 | /// pub fn next(self: *SymbolIterator) ?SelfInfoError!Symbol; | ||
| 68 | /// }; | ||
| 64 | /// ``` | 69 | /// ``` |
| 65 | pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo")) | 70 | pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo")) |
| 66 | root.debug.SelfInfo | 71 | root.debug.SelfInfo |
| ... | @@ -1107,33 +1112,37 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize { | ... | @@ -1107,33 +1112,37 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize { |
| 1107 | } | 1112 | } |
| 1108 | 1113 | ||
| 1109 | fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: usize) Writer.Error!void { | 1114 | fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: usize) Writer.Error!void { |
| 1110 | const symbol: Symbol = debug_info.getSymbol(io, address) catch |err| switch (err) { | 1115 | var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, address); |
| 1111 | error.MissingDebugInfo, | 1116 | defer symbols.deinit(io); |
| 1112 | error.UnsupportedDebugInfo, | 1117 | while (symbols.next()) |curr| { |
| 1113 | error.InvalidDebugInfo, | 1118 | const symbol: Symbol = curr catch |err| switch (err) { |
| 1114 | => .unknown, | 1119 | error.MissingDebugInfo, |
| 1115 | error.ReadFailed, error.Unexpected, error.Canceled => s: { | 1120 | error.UnsupportedDebugInfo, |
| 1116 | t.setColor(.dim) catch {}; | 1121 | error.InvalidDebugInfo, |
| 1117 | try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); | 1122 | => .unknown, |
| 1118 | t.setColor(.reset) catch {}; | 1123 | error.ReadFailed, error.Unexpected, error.Canceled => s: { |
| 1119 | break :s .unknown; | 1124 | t.setColor(.dim) catch {}; |
| 1120 | }, | 1125 | try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); |
| 1121 | error.OutOfMemory => s: { | 1126 | t.setColor(.reset) catch {}; |
| 1122 | t.setColor(.dim) catch {}; | 1127 | break :s .unknown; |
| 1123 | try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{}); | 1128 | }, |
| 1124 | t.setColor(.reset) catch {}; | 1129 | error.OutOfMemory => s: { |
| 1125 | break :s .unknown; | 1130 | t.setColor(.dim) catch {}; |
| 1126 | }, | 1131 | try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{}); |
| 1127 | }; | 1132 | t.setColor(.reset) catch {}; |
| 1128 | defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name); | 1133 | break :s .unknown; |
| 1129 | return printLineInfo( | 1134 | }, |
| 1130 | io, | 1135 | }; |
| 1131 | t, | 1136 | defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name); |
| 1132 | symbol.source_location, | 1137 | try printLineInfo( |
| 1133 | address, | 1138 | io, |
| 1134 | symbol.name orelse "???", | 1139 | t, |
| 1135 | symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???", | 1140 | symbol.source_location, |
| 1136 | ); | 1141 | address, |
| 1142 | symbol.name orelse "???", | ||
| 1143 | symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???", | ||
| 1144 | ); | ||
| 1145 | } | ||
| 1137 | } | 1146 | } |
| 1138 | fn printLineInfo( | 1147 | fn printLineInfo( |
| 1139 | io: Io, | 1148 | io: Io, |
lib/std/debug/Pdb.zig+453-14| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const File = std.Io.File; | 2 | const Io = std.Io; |
| 3 | const File = Io.File; | ||
| 3 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 4 | const pdb = std.pdb; | 5 | const pdb = std.pdb; |
| 5 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| ... | @@ -10,7 +11,7 @@ file_reader: *File.Reader, | ... | @@ -10,7 +11,7 @@ file_reader: *File.Reader, |
| 10 | msf: Msf, | 11 | msf: Msf, |
| 11 | allocator: Allocator, | 12 | allocator: Allocator, |
| 12 | string_table: ?*MsfStream, | 13 | string_table: ?*MsfStream, |
| 13 | dbi: ?*MsfStream, | 14 | ipi: ?[]u8, |
| 14 | modules: []Module, | 15 | modules: []Module, |
| 15 | sect_contribs: []pdb.SectionContribEntry, | 16 | sect_contribs: []pdb.SectionContribEntry, |
| 16 | guid: [16]u8, | 17 | guid: [16]u8, |
| ... | @@ -41,7 +42,7 @@ pub fn init(gpa: Allocator, file_reader: *File.Reader) !Pdb { | ... | @@ -41,7 +42,7 @@ pub fn init(gpa: Allocator, file_reader: *File.Reader) !Pdb { |
| 41 | .file_reader = file_reader, | 42 | .file_reader = file_reader, |
| 42 | .allocator = gpa, | 43 | .allocator = gpa, |
| 43 | .string_table = null, | 44 | .string_table = null, |
| 44 | .dbi = null, | 45 | .ipi = null, |
| 45 | .msf = try Msf.init(gpa, file_reader), | 46 | .msf = try Msf.init(gpa, file_reader), |
| 46 | .modules = &.{}, | 47 | .modules = &.{}, |
| 47 | .sect_contribs = &.{}, | 48 | .sect_contribs = &.{}, |
| ... | @@ -53,6 +54,7 @@ pub fn init(gpa: Allocator, file_reader: *File.Reader) !Pdb { | ... | @@ -53,6 +54,7 @@ pub fn init(gpa: Allocator, file_reader: *File.Reader) !Pdb { |
| 53 | pub fn deinit(self: *Pdb) void { | 54 | pub fn deinit(self: *Pdb) void { |
| 54 | const gpa = self.allocator; | 55 | const gpa = self.allocator; |
| 55 | self.msf.deinit(gpa); | 56 | self.msf.deinit(gpa); |
| 57 | if (self.ipi) |ipi| gpa.free(ipi); | ||
| 56 | for (self.modules) |*module| { | 58 | for (self.modules) |*module| { |
| 57 | module.deinit(gpa); | 59 | module.deinit(gpa); |
| 58 | } | 60 | } |
| ... | @@ -67,7 +69,7 @@ pub fn parseDbiStream(self: *Pdb) !void { | ... | @@ -67,7 +69,7 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 67 | const gpa = self.allocator; | 69 | const gpa = self.allocator; |
| 68 | const reader = &stream.interface; | 70 | const reader = &stream.interface; |
| 69 | 71 | ||
| 70 | const header = try reader.takeStruct(std.pdb.DbiStreamHeader, .little); | 72 | const header = try reader.takeStruct(pdb.DbiStreamHeader, .little); |
| 71 | if (header.version_header != 19990903) // V70, only value observed by LLVM team | 73 | if (header.version_header != 19990903) // V70, only value observed by LLVM team |
| 72 | return error.UnknownPDBVersion; | 74 | return error.UnknownPDBVersion; |
| 73 | // if (header.Age != age) | 75 | // if (header.Age != age) |
| ... | @@ -85,14 +87,14 @@ pub fn parseDbiStream(self: *Pdb) !void { | ... | @@ -85,14 +87,14 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 85 | const mod_info = try reader.takeStruct(pdb.ModInfo, .little); | 87 | const mod_info = try reader.takeStruct(pdb.ModInfo, .little); |
| 86 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | 88 | var this_record_len: usize = @sizeOf(pdb.ModInfo); |
| 87 | 89 | ||
| 88 | var module_name: std.Io.Writer.Allocating = .init(gpa); | 90 | var module_name: Io.Writer.Allocating = .init(gpa); |
| 89 | defer module_name.deinit(); | 91 | defer module_name.deinit(); |
| 90 | this_record_len += try reader.streamDelimiterLimit(&module_name.writer, 0, .limited(1024)); | 92 | this_record_len += try reader.streamDelimiterLimit(&module_name.writer, 0, .limited(1024)); |
| 91 | assert(reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API | 93 | assert(reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API |
| 92 | reader.toss(1); | 94 | reader.toss(1); |
| 93 | this_record_len += 1; | 95 | this_record_len += 1; |
| 94 | 96 | ||
| 95 | var obj_file_name: std.Io.Writer.Allocating = .init(gpa); | 97 | var obj_file_name: Io.Writer.Allocating = .init(gpa); |
| 96 | defer obj_file_name.deinit(); | 98 | defer obj_file_name.deinit(); |
| 97 | this_record_len += try reader.streamDelimiterLimit(&obj_file_name.writer, 0, .limited(1024)); | 99 | this_record_len += try reader.streamDelimiterLimit(&obj_file_name.writer, 0, .limited(1024)); |
| 98 | assert(reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API | 100 | assert(reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API |
| ... | @@ -128,7 +130,7 @@ pub fn parseDbiStream(self: *Pdb) !void { | ... | @@ -128,7 +130,7 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 128 | 130 | ||
| 129 | var sect_cont_offset: usize = 0; | 131 | var sect_cont_offset: usize = 0; |
| 130 | if (section_contrib_size != 0) { | 132 | if (section_contrib_size != 0) { |
| 131 | const version = reader.takeEnum(std.pdb.SectionContrSubstreamVersion, .little) catch |err| switch (err) { | 133 | const version = reader.takeEnum(pdb.SectionContrSubstreamVersion, .little) catch |err| switch (err) { |
| 132 | error.InvalidEnumTag, error.EndOfStream => return error.InvalidDebugInfo, | 134 | error.InvalidEnumTag, error.EndOfStream => return error.InvalidDebugInfo, |
| 133 | error.ReadFailed => return error.ReadFailed, | 135 | error.ReadFailed => return error.ReadFailed, |
| 134 | }; | 136 | }; |
| ... | @@ -148,6 +150,15 @@ pub fn parseDbiStream(self: *Pdb) !void { | ... | @@ -148,6 +150,15 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 148 | self.sect_contribs = try sect_contribs.toOwnedSlice(); | 150 | self.sect_contribs = try sect_contribs.toOwnedSlice(); |
| 149 | } | 151 | } |
| 150 | 152 | ||
| 153 | pub fn parseIpiStream(self: *Pdb) !void { | ||
| 154 | const gpa = self.allocator; | ||
| 155 | const stream = self.getStream(.ipi) orelse return; | ||
| 156 | const header = try stream.interface.peekStruct(pdb.IpiStreamHeader, .little); | ||
| 157 | if (header.version != .v80) // only value observed by LLVM team | ||
| 158 | return error.UnknownPDBVersion; | ||
| 159 | self.ipi = try stream.interface.readAlloc(gpa, @sizeOf(pdb.IpiStreamHeader) + header.type_record_bytes); | ||
| 160 | } | ||
| 161 | |||
| 151 | pub fn parseInfoStream(self: *Pdb) !void { | 162 | pub fn parseInfoStream(self: *Pdb) !void { |
| 152 | var stream = self.getStream(pdb.StreamType.pdb) orelse return error.InvalidDebugInfo; | 163 | var stream = self.getStream(pdb.StreamType.pdb) orelse return error.InvalidDebugInfo; |
| 153 | const reader = &stream.interface; | 164 | const reader = &stream.interface; |
| ... | @@ -212,8 +223,9 @@ pub fn parseInfoStream(self: *Pdb) !void { | ... | @@ -212,8 +223,9 @@ pub fn parseInfoStream(self: *Pdb) !void { |
| 212 | return error.MissingDebugInfo; | 223 | return error.MissingDebugInfo; |
| 213 | } | 224 | } |
| 214 | 225 | ||
| 215 | pub fn getSymbolName(self: *Pdb, module: *Module, address: u64) ?[]const u8 { | 226 | pub fn getProcSym(self: *Pdb, module: *Module, address: u64) ?*align(1) pdb.ProcSym { |
| 216 | _ = self; | 227 | _ = self; |
| 228 | |||
| 217 | std.debug.assert(module.populated); | 229 | std.debug.assert(module.populated); |
| 218 | 230 | ||
| 219 | var symbol_i: usize = 0; | 231 | var symbol_i: usize = 0; |
| ... | @@ -223,9 +235,9 @@ pub fn getSymbolName(self: *Pdb, module: *Module, address: u64) ?[]const u8 { | ... | @@ -223,9 +235,9 @@ pub fn getSymbolName(self: *Pdb, module: *Module, address: u64) ?[]const u8 { |
| 223 | return null; | 235 | return null; |
| 224 | switch (prefix.record_kind) { | 236 | switch (prefix.record_kind) { |
| 225 | .lproc32, .gproc32 => { | 237 | .lproc32, .gproc32 => { |
| 226 | const proc_sym: *align(1) pdb.ProcSym = @ptrCast(&module.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); | 238 | const proc_sym: *align(1) pdb.ProcSym = @ptrCast(prefix); |
| 227 | if (address >= proc_sym.code_offset and address < proc_sym.code_offset + proc_sym.code_size) { | 239 | if (address >= proc_sym.code_offset and address < proc_sym.code_offset + proc_sym.code_size) { |
| 228 | return std.mem.sliceTo(@as([*:0]u8, @ptrCast(&proc_sym.name[0])), 0); | 240 | return proc_sym; |
| 229 | } | 241 | } |
| 230 | }, | 242 | }, |
| 231 | else => {}, | 243 | else => {}, |
| ... | @@ -236,6 +248,433 @@ pub fn getSymbolName(self: *Pdb, module: *Module, address: u64) ?[]const u8 { | ... | @@ -236,6 +248,433 @@ pub fn getSymbolName(self: *Pdb, module: *Module, address: u64) ?[]const u8 { |
| 236 | return null; | 248 | return null; |
| 237 | } | 249 | } |
| 238 | 250 | ||
| 251 | pub const InlineSiteSymIterator = struct { | ||
| 252 | module_index: usize, | ||
| 253 | offset: usize, | ||
| 254 | end: usize, | ||
| 255 | |||
| 256 | pub const empty: InlineSiteSymIterator = .{ | ||
| 257 | .module_index = 0, | ||
| 258 | .offset = 0, | ||
| 259 | .end = 0, | ||
| 260 | }; | ||
| 261 | |||
| 262 | pub fn next(iter: *InlineSiteSymIterator, module: *Module) ?*align(1) pdb.InlineSiteSym { | ||
| 263 | while (iter.offset < iter.end) { | ||
| 264 | const inline_prefix: *align(1) pdb.RecordPrefix = @ptrCast(&module.symbols[iter.offset]); | ||
| 265 | if (inline_prefix.record_len < 2) | ||
| 266 | return null; | ||
| 267 | const end = iter.offset + inline_prefix.record_len + @sizeOf(u16); | ||
| 268 | defer iter.offset = end; | ||
| 269 | switch (inline_prefix.record_kind) { | ||
| 270 | // Skip nested procedures | ||
| 271 | .lproc32, | ||
| 272 | .lproc32_st, | ||
| 273 | .gproc32, | ||
| 274 | .gproc32_st, | ||
| 275 | .lproc32_id, | ||
| 276 | .gproc32_id, | ||
| 277 | .lproc32_dpc, | ||
| 278 | .lproc32_dpc_id, | ||
| 279 | => { | ||
| 280 | const skip: *align(1) pdb.ProcSym = @ptrCast(inline_prefix); | ||
| 281 | iter.offset = skip.end; | ||
| 282 | }, | ||
| 283 | .inlinesite, | ||
| 284 | .inlinesite2, | ||
| 285 | => return @ptrCast(inline_prefix), | ||
| 286 | else => {} | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | return null; | ||
| 291 | } | ||
| 292 | }; | ||
| 293 | |||
| 294 | pub const BinaryAnnotation = union(enum) { | ||
| 295 | code_offset: u32, | ||
| 296 | change_code_offset_base: u32, | ||
| 297 | change_code_offset: u32, | ||
| 298 | change_code_length: u32, | ||
| 299 | change_file: u32, | ||
| 300 | change_line_offset: i32, | ||
| 301 | change_line_end_delta: u32, | ||
| 302 | change_range_kind: RangeKind, | ||
| 303 | change_column_start: u32, | ||
| 304 | change_column_end_delta: i32, | ||
| 305 | change_code_offset_and_line_offset: struct { code_delta: u32, line_delta: i32 }, | ||
| 306 | change_code_length_and_code_offset: struct { length: u32, delta: u32 }, | ||
| 307 | change_column_end: u32, | ||
| 308 | |||
| 309 | pub const RangeKind = enum(u32) { expression = 0, statement = 1 }; | ||
| 310 | |||
| 311 | /// A virtual machine that processed binary annotations. | ||
| 312 | pub const RangeIterator = struct { | ||
| 313 | annotations: Iterator, | ||
| 314 | curr: PartialRange, | ||
| 315 | /// The previous range is tracked as the code length is sometimes implied by the subsequent | ||
| 316 | /// range. | ||
| 317 | prev: ?PartialRange, | ||
| 318 | |||
| 319 | const PartialRange = struct { | ||
| 320 | line_offset: u32, | ||
| 321 | file_offset: u32, | ||
| 322 | code_offset: u32, | ||
| 323 | code_length: ?u32, | ||
| 324 | }; | ||
| 325 | |||
| 326 | pub fn init(annotations: Iterator) RangeIterator { | ||
| 327 | return .{ | ||
| 328 | .annotations = annotations, | ||
| 329 | .curr = .{ | ||
| 330 | .line_offset = 0, | ||
| 331 | .file_offset = 0, | ||
| 332 | .code_offset = 0, | ||
| 333 | .code_length = null, | ||
| 334 | }, | ||
| 335 | .prev = null, | ||
| 336 | }; | ||
| 337 | } | ||
| 338 | |||
| 339 | pub const Range = struct { | ||
| 340 | line_offset: u32, | ||
| 341 | file_offset: u32, | ||
| 342 | code_offset: u32, | ||
| 343 | code_length: u32, | ||
| 344 | |||
| 345 | pub fn contains(self: Range, offset_in_func: usize) bool { | ||
| 346 | return self.code_offset <= offset_in_func and | ||
| 347 | offset_in_func < self.code_offset + self.code_length; | ||
| 348 | } | ||
| 349 | }; | ||
| 350 | |||
| 351 | pub fn next(self: *RangeIterator) error{InvalidDebugInfo}!?Range { | ||
| 352 | while (try self.annotations.next()) |annotation| { | ||
| 353 | switch (annotation) { | ||
| 354 | .change_code_offset => |delta| { | ||
| 355 | self.curr.code_offset += delta; | ||
| 356 | }, | ||
| 357 | .change_code_length => |length| { | ||
| 358 | if (self.prev) |*prev| prev.code_length = prev.code_length orelse length; | ||
| 359 | self.curr.code_offset += length; | ||
| 360 | }, | ||
| 361 | .change_file => @panic("unimplemented"), | ||
| 362 | // LLVM never emits this opcode, but it's clear enough how to interpret it so we may as | ||
| 363 | // well in case they use it in the future | ||
| 364 | .change_code_length_and_code_offset => |info| { | ||
| 365 | self.curr.code_length = info.length; | ||
| 366 | self.curr.code_offset += info.delta; | ||
| 367 | }, | ||
| 368 | .change_line_offset => |delta| { | ||
| 369 | self.curr.line_offset +%= @bitCast(delta); | ||
| 370 | }, | ||
| 371 | .change_code_offset_and_line_offset => |info| { | ||
| 372 | self.curr.code_offset += info.code_delta; | ||
| 373 | self.curr.line_offset +%= @bitCast(info.line_delta); | ||
| 374 | }, | ||
| 375 | |||
| 376 | // Not emitted by LLVM at the time of writing, but if we get it from elsewhere it should | ||
| 377 | // be safe to ignore since we don't use this info. Theoretically we could use column | ||
| 378 | // info if it was present, but it's not easy to test since LLVM doesn't output it. | ||
| 379 | .change_line_end_delta, | ||
| 380 | .change_column_start, | ||
| 381 | .change_column_end_delta, | ||
| 382 | .change_column_end, | ||
| 383 | => {}, | ||
| 384 | |||
| 385 | // Not emitted by LLVM at the time of writing. Various sources conflict on how these | ||
| 386 | // instructions should be interpreted, so we make no attempt to handle them. | ||
| 387 | .code_offset, | ||
| 388 | .change_code_offset_base, | ||
| 389 | .change_range_kind, | ||
| 390 | => @panic("unimplemented"), | ||
| 391 | } | ||
| 392 | |||
| 393 | switch (annotation) { | ||
| 394 | .change_code_offset, | ||
| 395 | .change_code_offset_and_line_offset, | ||
| 396 | .change_code_length_and_code_offset, | ||
| 397 | => {}, | ||
| 398 | else => continue, | ||
| 399 | } | ||
| 400 | |||
| 401 | if (self.prev) |*prev| { | ||
| 402 | if (prev.code_length == null) { | ||
| 403 | prev.code_length = self.curr.code_offset - prev.code_offset; | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | defer self.prev = .{ | ||
| 408 | .code_offset = self.curr.code_offset, | ||
| 409 | .code_length = self.curr.code_length, | ||
| 410 | .line_offset = self.curr.line_offset, | ||
| 411 | .file_offset = self.curr.file_offset, | ||
| 412 | }; | ||
| 413 | const prev = self.prev orelse continue; | ||
| 414 | const prev_code_length = prev.code_length orelse continue; | ||
| 415 | return .{ | ||
| 416 | .code_offset = prev.code_offset, | ||
| 417 | .code_length = prev_code_length, | ||
| 418 | .line_offset = prev.line_offset, | ||
| 419 | .file_offset = prev.file_offset, | ||
| 420 | }; | ||
| 421 | } | ||
| 422 | |||
| 423 | const prev = self.prev orelse return null; | ||
| 424 | defer self.prev = null; | ||
| 425 | const prev_code_length = prev.code_length orelse return null; | ||
| 426 | return .{ | ||
| 427 | .code_offset = prev.code_offset, | ||
| 428 | .code_length = prev_code_length, | ||
| 429 | .line_offset = prev.line_offset, | ||
| 430 | .file_offset = prev.file_offset, | ||
| 431 | }; | ||
| 432 | } | ||
| 433 | }; | ||
| 434 | |||
| 435 | pub const Iterator = struct { | ||
| 436 | reader: Io.Reader, | ||
| 437 | |||
| 438 | pub fn next(self: *Iterator) error{InvalidDebugInfo}!?BinaryAnnotation { | ||
| 439 | return take(&self.reader) catch |err| switch (err) { | ||
| 440 | error.ReadFailed => return error.InvalidDebugInfo, | ||
| 441 | error.EndOfStream => return null, | ||
| 442 | }; | ||
| 443 | } | ||
| 444 | }; | ||
| 445 | |||
| 446 | pub fn take(reader: *Io.Reader) Io.Reader.Error!BinaryAnnotation { | ||
| 447 | const op = std.enums.fromInt( | ||
| 448 | pdb.BinaryAnnotationOpcode, | ||
| 449 | try takePackedU32(reader), | ||
| 450 | ) orelse return error.ReadFailed; | ||
| 451 | switch (op) { | ||
| 452 | .invalid => return error.EndOfStream, | ||
| 453 | .code_offset => return .{ | ||
| 454 | .code_offset = try expect(takePackedU32(reader)), | ||
| 455 | }, | ||
| 456 | .change_code_offset_base => return .{ | ||
| 457 | .change_code_offset_base = try expect(takePackedU32(reader)), | ||
| 458 | }, | ||
| 459 | .change_code_offset => return .{ | ||
| 460 | .change_code_offset = try expect(takePackedU32(reader)), | ||
| 461 | }, | ||
| 462 | .change_code_length => return .{ | ||
| 463 | .change_code_length = try expect(takePackedU32(reader)), | ||
| 464 | }, | ||
| 465 | .change_file => return .{ | ||
| 466 | .change_file = try expect(takePackedU32(reader)), | ||
| 467 | }, | ||
| 468 | .change_line_offset => return .{ | ||
| 469 | .change_line_offset = try expect(takePackedI32(reader)), | ||
| 470 | }, | ||
| 471 | .change_line_end_delta => return .{ | ||
| 472 | .change_line_end_delta = try expect(takePackedU32(reader)), | ||
| 473 | }, | ||
| 474 | .change_range_kind => return .{ | ||
| 475 | .change_range_kind = std.enums.fromInt( | ||
| 476 | RangeKind, | ||
| 477 | try expect(takePackedU32(reader)), | ||
| 478 | ) orelse return error.ReadFailed, | ||
| 479 | }, | ||
| 480 | .change_column_start => return .{ | ||
| 481 | .change_column_start = try expect(takePackedU32(reader)), | ||
| 482 | }, | ||
| 483 | .change_column_end_delta => return .{ | ||
| 484 | .change_column_end_delta = try expect(takePackedI32(reader)), | ||
| 485 | }, | ||
| 486 | .change_code_offset_and_line_offset => { | ||
| 487 | const EncodedArgs = packed struct(u32) { | ||
| 488 | code_delta: u4, | ||
| 489 | encoded_line_delta: u28, | ||
| 490 | }; | ||
| 491 | const args: EncodedArgs = @bitCast(try expect(takePackedU32(reader))); | ||
| 492 | return .{ | ||
| 493 | .change_code_offset_and_line_offset = .{ | ||
| 494 | .code_delta = args.code_delta, | ||
| 495 | .line_delta = decodeI32(args.encoded_line_delta), | ||
| 496 | }, | ||
| 497 | }; | ||
| 498 | }, | ||
| 499 | .change_code_length_and_code_offset => return .{ | ||
| 500 | .change_code_length_and_code_offset = .{ | ||
| 501 | .length = try expect(takePackedU32(reader)), | ||
| 502 | .delta = try expect(takePackedU32(reader)), | ||
| 503 | }, | ||
| 504 | }, | ||
| 505 | .change_column_end => return .{ | ||
| 506 | .change_column_end = try expect(takePackedU32(reader)), | ||
| 507 | }, | ||
| 508 | } | ||
| 509 | } | ||
| 510 | |||
| 511 | // Adapated from: | ||
| 512 | // https://github.com/microsoft/microsoft-pdb/blob/805655a28bd8198004be2ac27e6e0290121a5e89/include/cvinfo.h#L4942 | ||
| 513 | pub fn takePackedU32(reader: *Io.Reader) Io.Reader.Error!u32 { | ||
| 514 | const b0: u32 = try reader.takeByte(); | ||
| 515 | if (b0 & 0x80 == 0x00) return b0; | ||
| 516 | |||
| 517 | const b1: u32 = try reader.takeByte(); | ||
| 518 | if (b0 & 0xC0 == 0x80) return ((b0 & 0x3F) << 8) | b1; | ||
| 519 | |||
| 520 | const b2: u32 = try reader.takeByte(); | ||
| 521 | const b3: u32 = try reader.takeByte(); | ||
| 522 | if (b0 & 0xE0 == 0xC0) return ((b0 & 0x1f) << 24) | (b1 << 16) | (b2 << 8) | b3; | ||
| 523 | |||
| 524 | return error.ReadFailed; | ||
| 525 | } | ||
| 526 | |||
| 527 | pub fn takePackedI32(reader: *Io.Reader) Io.Reader.Error!i32 { | ||
| 528 | return decodeI32(try takePackedU32(reader)); | ||
| 529 | } | ||
| 530 | |||
| 531 | pub fn decodeI32(u: u32) i32 { | ||
| 532 | const i: i32 = @bitCast(u); | ||
| 533 | if (i & 1 != 0) { | ||
| 534 | return -(i >> 1); | ||
| 535 | } else { | ||
| 536 | return i >> 1; | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | fn expect(value: anytype) error { ReadFailed }!@typeInfo(@TypeOf(value)).error_union.payload { | ||
| 541 | comptime assert(@typeInfo(@TypeOf(value)).error_union.error_set == Io.Reader.Error); | ||
| 542 | return value catch error.ReadFailed; | ||
| 543 | } | ||
| 544 | }; | ||
| 545 | |||
| 546 | pub fn findInlineeName(self: *const Pdb, inlinee: u32) ?[]const u8 { | ||
| 547 | var reader: Io.Reader = .fixed(self.ipi orelse return null); | ||
| 548 | const header = reader.takeStructPointer(pdb.IpiStreamHeader) catch return null; | ||
| 549 | for (header.type_index_begin..header.type_index_end) |type_index| { | ||
| 550 | const prefix = reader.takeStructPointer(pdb.LfRecordPrefix) catch return null; | ||
| 551 | reader.discardAll(prefix.len - @sizeOf(@FieldType(pdb.LfRecordPrefix, "len"))) catch return null; | ||
| 552 | |||
| 553 | if (type_index == inlinee) { | ||
| 554 | switch (prefix.kind) { | ||
| 555 | .func_id => { | ||
| 556 | const func: *align(1) pdb.LfFuncId = @ptrCast(prefix); | ||
| 557 | return std.mem.sliceTo(@as([*:0]const u8, @ptrCast(&func.name[0])), 0); | ||
| 558 | }, | ||
| 559 | .mfunc_id => { | ||
| 560 | const func: *align(1) pdb.LfMFuncId = @ptrCast(prefix); | ||
| 561 | return std.mem.sliceTo(@as([*:0]const u8, @ptrCast(&func.name[0])), 0); | ||
| 562 | }, | ||
| 563 | else => return null, | ||
| 564 | } | ||
| 565 | } | ||
| 566 | } | ||
| 567 | return null; | ||
| 568 | } | ||
| 569 | |||
| 570 | pub fn getInlinees(self: *Pdb, module: *Module, proc_sym: *align(1) const pdb.ProcSym) InlineSiteSymIterator { | ||
| 571 | const module_index = module - self.modules.ptr; | ||
| 572 | const offset = @intFromPtr(proc_sym) - | ||
| 573 | @intFromPtr(module.symbols.ptr) + | ||
| 574 | proc_sym.record_len + | ||
| 575 | @sizeOf(@FieldType(pdb.ProcSym, "record_len")); | ||
| 576 | return .{ | ||
| 577 | .module_index = module_index, | ||
| 578 | .offset = offset, | ||
| 579 | .end = proc_sym.end, | ||
| 580 | }; | ||
| 581 | } | ||
| 582 | |||
| 583 | pub fn getBinaryAnnotations(self: *Pdb, site: *align(1) const pdb.InlineSiteSym) BinaryAnnotation.Iterator { | ||
| 584 | _ = self; | ||
| 585 | var start: usize = @intFromPtr(site) + @sizeOf(pdb.InlineSiteSym); | ||
| 586 | var end = start + site.record_len + @sizeOf(@FieldType(pdb.InlineSiteSym, "record_len")) - @sizeOf(pdb.InlineSiteSym); | ||
| 587 | switch (site.record_kind) { | ||
| 588 | .inlinesite => {}, | ||
| 589 | .inlinesite2 => start += @sizeOf(pdb.InlineSiteSym2) - @sizeOf(pdb.InlineSiteSym), | ||
| 590 | else => end = start, | ||
| 591 | } | ||
| 592 | const ptr: [*]const u8 = @ptrFromInt(start); | ||
| 593 | const slice = ptr[0..end - start]; | ||
| 594 | return .{ .reader = Io.Reader.fixed(slice) }; | ||
| 595 | } | ||
| 596 | |||
| 597 | pub fn calculateOffset( | ||
| 598 | self: *Pdb, | ||
| 599 | site: *align(1) const pdb.InlineSiteSym, | ||
| 600 | loc: std.debug.SourceLocation, | ||
| 601 | offset_in_func: usize, | ||
| 602 | ) error{InvalidDebugInfo, MissingDebugInfo, ReadFailed}!?std.debug.SourceLocation { | ||
| 603 | var ranges: BinaryAnnotation.RangeIterator = .init(self.getBinaryAnnotations(site)); | ||
| 604 | while (try ranges.next()) |range| { | ||
| 605 | if (range.contains(offset_in_func)) { | ||
| 606 | var result: std.debug.SourceLocation = loc; | ||
| 607 | result.line += range.line_offset; | ||
| 608 | return result; | ||
| 609 | } | ||
| 610 | } | ||
| 611 | return null; | ||
| 612 | } | ||
| 613 | |||
| 614 | pub fn getSymbolName(self: *Pdb, proc_sym: *align(1) const pdb.ProcSym) []const u8 { | ||
| 615 | _ = self; | ||
| 616 | return std.mem.sliceTo(@as([*:0]const u8, @ptrCast(&proc_sym.name[0])), 0); | ||
| 617 | } | ||
| 618 | |||
| 619 | pub fn getInlineeInfo(self: *Pdb, mod: *Module, inlinee: u32) !std.debug.SourceLocation { | ||
| 620 | const gpa = self.allocator; | ||
| 621 | var sect_offset: usize = 0; | ||
| 622 | var skip_len: usize = undefined; | ||
| 623 | while (sect_offset < mod.subsect_info.len) : (sect_offset += skip_len) { | ||
| 624 | const subsect_hdr: *align(1) pdb.DebugSubsectionHeader = @ptrCast(&mod.subsect_info[sect_offset]); | ||
| 625 | skip_len = subsect_hdr.length; | ||
| 626 | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); | ||
| 627 | |||
| 628 | if (subsect_hdr.kind == .inlinee_lines) { | ||
| 629 | var offset = sect_offset; | ||
| 630 | const signature: *const align(1) pdb.InlineeSourceLineSignature = @ptrCast(&mod.subsect_info[offset]); | ||
| 631 | offset += @sizeOf(pdb.InlineeSourceLineSignature); | ||
| 632 | |||
| 633 | const has_extra_files = switch (signature.*) { | ||
| 634 | .normal => false, | ||
| 635 | .ex => true, | ||
| 636 | else => continue, | ||
| 637 | }; | ||
| 638 | |||
| 639 | while (offset < sect_offset + subsect_hdr.length) { | ||
| 640 | const entry: *const align(1) pdb.InlineeSourceLine = @ptrCast(&mod.subsect_info[offset]); | ||
| 641 | offset += @sizeOf(pdb.InlineeSourceLine); | ||
| 642 | |||
| 643 | if (has_extra_files) { | ||
| 644 | const file_count: *const align(1) u32 = @ptrCast(&mod.subsect_info[offset]); | ||
| 645 | offset += @sizeOf(u32); | ||
| 646 | offset += file_count.* * @sizeOf(u32); | ||
| 647 | } | ||
| 648 | |||
| 649 | if (entry.inlinee == inlinee) { | ||
| 650 | const source_file_name = s: { | ||
| 651 | const checksum_offset = mod.checksum_offset orelse return error.MissingDebugInfo; | ||
| 652 | const subsect_index = checksum_offset + entry.file_id; | ||
| 653 | const chksum_hdr: *align(1) pdb.FileChecksumEntryHeader = @ptrCast(&mod.subsect_info[subsect_index]); | ||
| 654 | const strtab_offset = @sizeOf(pdb.StringTableHeader) + chksum_hdr.file_name_offset; | ||
| 655 | try self.string_table.?.seekTo(strtab_offset); | ||
| 656 | const string_reader = &self.string_table.?.interface; | ||
| 657 | var source_file_name: Io.Writer.Allocating = .init(gpa); | ||
| 658 | defer source_file_name.deinit(); | ||
| 659 | _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024)); | ||
| 660 | assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API | ||
| 661 | string_reader.toss(1); | ||
| 662 | break :s try source_file_name.toOwnedSlice(); | ||
| 663 | }; | ||
| 664 | errdefer gpa.free(source_file_name); | ||
| 665 | |||
| 666 | return .{ | ||
| 667 | .line = entry.source_line_num, | ||
| 668 | .column = 0, | ||
| 669 | .file_name = source_file_name, | ||
| 670 | }; | ||
| 671 | } | ||
| 672 | } | ||
| 673 | } | ||
| 674 | } | ||
| 675 | return error.MissingDebugInfo; | ||
| 676 | } | ||
| 677 | |||
| 239 | pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.SourceLocation { | 678 | pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.SourceLocation { |
| 240 | std.debug.assert(module.populated); | 679 | std.debug.assert(module.populated); |
| 241 | const subsect_info = module.subsect_info; | 680 | const subsect_info = module.subsect_info; |
| ... | @@ -296,7 +735,7 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S | ... | @@ -296,7 +735,7 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S |
| 296 | try self.string_table.?.seekTo(strtab_offset); | 735 | try self.string_table.?.seekTo(strtab_offset); |
| 297 | const source_file_name = s: { | 736 | const source_file_name = s: { |
| 298 | const string_reader = &self.string_table.?.interface; | 737 | const string_reader = &self.string_table.?.interface; |
| 299 | var source_file_name: std.Io.Writer.Allocating = .init(gpa); | 738 | var source_file_name: Io.Writer.Allocating = .init(gpa); |
| 300 | defer source_file_name.deinit(); | 739 | defer source_file_name.deinit(); |
| 301 | _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024)); | 740 | _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024)); |
| 302 | assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API | 741 | assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API |
| ... | @@ -497,7 +936,7 @@ const MsfStream = struct { | ... | @@ -497,7 +936,7 @@ const MsfStream = struct { |
| 497 | next_read_pos: u64, | 936 | next_read_pos: u64, |
| 498 | blocks: []u32, | 937 | blocks: []u32, |
| 499 | block_size: u32, | 938 | block_size: u32, |
| 500 | interface: std.Io.Reader, | 939 | interface: Io.Reader, |
| 501 | err: ?Error, | 940 | err: ?Error, |
| 502 | 941 | ||
| 503 | const Error = File.Reader.SeekError; | 942 | const Error = File.Reader.SeekError; |
| ... | @@ -527,7 +966,7 @@ const MsfStream = struct { | ... | @@ -527,7 +966,7 @@ const MsfStream = struct { |
| 527 | }; | 966 | }; |
| 528 | } | 967 | } |
| 529 | 968 | ||
| 530 | fn stream(r: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize { | 969 | fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { |
| 531 | const ms: *MsfStream = @alignCast(@fieldParentPtr("interface", r)); | 970 | const ms: *MsfStream = @alignCast(@fieldParentPtr("interface", r)); |
| 532 | 971 | ||
| 533 | var block_id: usize = @intCast(ms.next_read_pos / ms.block_size); | 972 | var block_id: usize = @intCast(ms.next_read_pos / ms.block_size); |
| ... | @@ -595,7 +1034,7 @@ const MsfStream = struct { | ... | @@ -595,7 +1034,7 @@ const MsfStream = struct { |
| 595 | } | 1034 | } |
| 596 | }; | 1035 | }; |
| 597 | 1036 | ||
| 598 | fn readSparseBitVector(reader: *std.Io.Reader, allocator: Allocator) ![]u32 { | 1037 | fn readSparseBitVector(reader: *Io.Reader, allocator: Allocator) ![]u32 { |
| 599 | const num_words = try reader.takeInt(u32, .little); | 1038 | const num_words = try reader.takeInt(u32, .little); |
| 600 | var list = std.array_list.Managed(u32).init(allocator); | 1039 | var list = std.array_list.Managed(u32).init(allocator); |
| 601 | errdefer list.deinit(); | 1040 | errdefer list.deinit(); |
lib/std/debug/SelfInfo/Elf.zig+28-12| ... | @@ -30,51 +30,67 @@ pub fn deinit(si: *SelfInfo, io: Io) void { | ... | @@ -30,51 +30,67 @@ pub fn deinit(si: *SelfInfo, io: Io) void { |
| 30 | if (si.unwind_cache) |cache| gpa.free(cache); | 30 | if (si.unwind_cache) |cache| gpa.free(cache); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol { | 33 | pub const SymbolIterator = struct { |
| 34 | curr: ?Error!std.debug.Symbol, | ||
| 35 | |||
| 36 | pub fn deinit(self: *SymbolIterator, _: Io) void { | ||
| 37 | self.* = undefined; | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn next(self: *SymbolIterator) ?Error!std.debug.Symbol { | ||
| 41 | const result = self.curr; | ||
| 42 | self.curr = null; | ||
| 43 | return result; | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | |||
| 47 | pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator { | ||
| 34 | const gpa = std.debug.getDebugInfoAllocator(); | 48 | const gpa = std.debug.getDebugInfoAllocator(); |
| 35 | const module = try si.findModule(gpa, io, address, .exclusive); | 49 | const module = si.findModule(gpa, io, address, .exclusive) catch |err| return .{ .curr = err }; |
| 36 | defer si.rwlock.unlock(io); | 50 | defer si.rwlock.unlock(io); |
| 37 | 51 | ||
| 38 | const vaddr = address - module.load_offset; | 52 | const vaddr = address - module.load_offset; |
| 39 | 53 | ||
| 40 | const loaded_elf = try module.getLoadedElf(gpa, io); | 54 | const loaded_elf = module.getLoadedElf(gpa, io) catch |err| return .{ .curr = err }; |
| 41 | if (loaded_elf.file.dwarf) |*dwarf| { | 55 | if (loaded_elf.file.dwarf) |*dwarf| { |
| 42 | if (!loaded_elf.scanned_dwarf) { | 56 | if (!loaded_elf.scanned_dwarf) { |
| 43 | dwarf.open(gpa, native_endian) catch |err| switch (err) { | 57 | dwarf.open(gpa, native_endian) catch |err| switch (err) { |
| 44 | error.InvalidDebugInfo, | 58 | error.InvalidDebugInfo, |
| 45 | error.MissingDebugInfo, | 59 | error.MissingDebugInfo, |
| 46 | error.OutOfMemory, | 60 | error.OutOfMemory, |
| 47 | => |e| return e, | 61 | => |e| return .{ .curr = e }, |
| 48 | error.EndOfStream, | 62 | error.EndOfStream, |
| 49 | error.Overflow, | 63 | error.Overflow, |
| 50 | error.ReadFailed, | 64 | error.ReadFailed, |
| 51 | error.StreamTooLong, | 65 | error.StreamTooLong, |
| 52 | => return error.InvalidDebugInfo, | 66 | => return .{ .curr = error.InvalidDebugInfo }, |
| 53 | }; | 67 | }; |
| 54 | loaded_elf.scanned_dwarf = true; | 68 | loaded_elf.scanned_dwarf = true; |
| 55 | } | 69 | } |
| 56 | if (dwarf.getSymbol(gpa, native_endian, vaddr)) |sym| { | 70 | if (dwarf.getSymbol(gpa, native_endian, vaddr)) |sym| { |
| 57 | return sym; | 71 | return .{ .curr = sym }; |
| 58 | } else |err| switch (err) { | 72 | } else |err| switch (err) { |
| 59 | error.MissingDebugInfo => {}, | 73 | error.MissingDebugInfo => {}, |
| 60 | 74 | ||
| 61 | error.InvalidDebugInfo, | 75 | error.InvalidDebugInfo, |
| 62 | error.OutOfMemory, | 76 | error.OutOfMemory, |
| 63 | => |e| return e, | 77 | => |e| return .{ .curr = e }, |
| 64 | 78 | ||
| 65 | error.ReadFailed, | 79 | error.ReadFailed, |
| 66 | error.EndOfStream, | 80 | error.EndOfStream, |
| 67 | error.Overflow, | 81 | error.Overflow, |
| 68 | error.StreamTooLong, | 82 | error.StreamTooLong, |
| 69 | => return error.InvalidDebugInfo, | 83 | => return .{ .curr = error.InvalidDebugInfo }, |
| 70 | } | 84 | } |
| 71 | } | 85 | } |
| 72 | // When DWARF is unavailable, fall back to searching the symtab. | 86 | // When DWARF is unavailable, fall back to searching the symtab. |
| 73 | return loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) { | 87 | const symbol = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) { |
| 74 | error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo, | 88 | error.NoSymtab, error.NoStrtab => return .{ .curr = error.MissingDebugInfo }, |
| 75 | error.BadSymtab => return error.InvalidDebugInfo, | 89 | error.BadSymtab => return .{ .curr = error.InvalidDebugInfo }, |
| 76 | error.OutOfMemory => |e| return e, | 90 | error.OutOfMemory => |e| return .{ .curr = e }, |
| 77 | }; | 91 | }; |
| 92 | |||
| 93 | return .{ .curr = symbol }; | ||
| 78 | } | 94 | } |
| 79 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { | 95 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { |
| 80 | const gpa = std.debug.getDebugInfoAllocator(); | 96 | const gpa = std.debug.getDebugInfoAllocator(); |
lib/std/debug/SelfInfo/Windows.zig+158-39| ... | @@ -1,10 +1,10 @@ | ... | @@ -1,10 +1,10 @@ |
| 1 | mutex: Io.Mutex, | 1 | lock: Io.RwLock, |
| 2 | ntdll_handle: ?if (load_dll_notification_procs) *anyopaque else noreturn, | 2 | ntdll_handle: ?if (load_dll_notification_procs) *anyopaque else noreturn, |
| 3 | notification_cookie: ?LDR.DLL_NOTIFICATION.COOKIE, | 3 | notification_cookie: ?LDR.DLL_NOTIFICATION.COOKIE, |
| 4 | modules: std.ArrayList(Module), | 4 | modules: std.ArrayList(Module), |
| 5 | 5 | ||
| 6 | pub const init: SelfInfo = .{ | 6 | pub const init: SelfInfo = .{ |
| 7 | .mutex = .init, | 7 | .lock = .init, |
| 8 | .ntdll_handle = null, | 8 | .ntdll_handle = null, |
| 9 | .notification_cookie = null, | 9 | .notification_cookie = null, |
| 10 | .modules = .empty, | 10 | .modules = .empty, |
| ... | @@ -25,18 +25,117 @@ pub fn deinit(si: *SelfInfo, io: Io) void { | ... | @@ -25,18 +25,117 @@ pub fn deinit(si: *SelfInfo, io: Io) void { |
| 25 | si.modules.deinit(gpa); | 25 | si.modules.deinit(gpa); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol { | 28 | pub const SymbolIterator = struct { |
| 29 | err: Error!void = {}, | ||
| 30 | lock: ?*Io.RwLock, | ||
| 31 | module: *Module, | ||
| 32 | symbols: Module.DebugInfo.Symbols, | ||
| 33 | |||
| 34 | pub fn deinit(self: *SymbolIterator, io: Io) void { | ||
| 35 | if (self.lock) |lock| lock.unlockShared(io); | ||
| 36 | self.* = undefined; | ||
| 37 | } | ||
| 38 | |||
| 39 | fn failing(err: Error) SymbolIterator { | ||
| 40 | return .{ | ||
| 41 | .err = err, | ||
| 42 | .lock = null, | ||
| 43 | .module = undefined, | ||
| 44 | .symbols = .none, | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | |||
| 48 | pub fn next(self: *SymbolIterator) ?Error!std.debug.Symbol { | ||
| 49 | // Check for errors | ||
| 50 | self.err catch |err| { | ||
| 51 | self.err = {}; | ||
| 52 | self.symbols = .none; | ||
| 53 | return err; | ||
| 54 | }; | ||
| 55 | |||
| 56 | // Return the next symbol for the debug info type | ||
| 57 | switch (self.symbols) { | ||
| 58 | .pdb => |*info| { | ||
| 59 | // The failure cases are unreachable because we only set the pdb field if these are | ||
| 60 | // set | ||
| 61 | const di = if (self.module.di.?) |*di| di else |_| unreachable; | ||
| 62 | const pdb = if (di.pdb) |*pdb| pdb else unreachable; | ||
| 63 | |||
| 64 | // Get the next inlinee if it exists | ||
| 65 | if (info.inlinees.next(info.module)) |site| { | ||
| 66 | if (pdb.getInlineeInfo(info.module, site.inlinee) catch null) |loc| { | ||
| 67 | if (info.proc_sym) |proc_sym| { | ||
| 68 | const offset_in_func = info.addr - proc_sym.code_offset; | ||
| 69 | if (try pdb.calculateOffset(site, loc, offset_in_func)) |offset| { | ||
| 70 | return .{ | ||
| 71 | .name = pdb.findInlineeName(site.inlinee), | ||
| 72 | .compile_unit_name = null, | ||
| 73 | .source_location = offset, | ||
| 74 | }; | ||
| 75 | } | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | // Return the main symbol and end the iterator | ||
| 81 | defer self.symbols = .none; | ||
| 82 | return .{ | ||
| 83 | .name = if (info.proc_sym) |proc_sym| | ||
| 84 | pdb.getSymbolName(proc_sym) | ||
| 85 | else | ||
| 86 | null, | ||
| 87 | .compile_unit_name = fs.path.basename(info.module.obj_file_name), | ||
| 88 | .source_location = pdb.getLineNumberInfo( | ||
| 89 | info.module, | ||
| 90 | info.addr, | ||
| 91 | ) catch null, | ||
| 92 | }; | ||
| 93 | }, | ||
| 94 | .dwarf => |info| { | ||
| 95 | // The failure cases are unreachable because we only set the dwarf field if these | ||
| 96 | // are set | ||
| 97 | const di = if (self.module.di.?) |*di| di else |_| unreachable; | ||
| 98 | const dwarf = if (di.dwarf) |*dwarf| dwarf else unreachable; | ||
| 99 | |||
| 100 | // Return the main symbol and then return the iterator | ||
| 101 | defer self.symbols = .none; | ||
| 102 | const gpa = std.debug.getDebugInfoAllocator(); | ||
| 103 | return dwarf.getSymbol(gpa, native_endian, info.addr) catch |err| switch (err) { | ||
| 104 | error.MissingDebugInfo => return null, | ||
| 105 | |||
| 106 | error.InvalidDebugInfo, | ||
| 107 | error.OutOfMemory, | ||
| 108 | => |e| return e, | ||
| 109 | |||
| 110 | error.ReadFailed, | ||
| 111 | error.EndOfStream, | ||
| 112 | error.Overflow, | ||
| 113 | error.StreamTooLong, | ||
| 114 | => return error.InvalidDebugInfo, | ||
| 115 | }; | ||
| 116 | }, | ||
| 117 | .none => return null, | ||
| 118 | } | ||
| 119 | } | ||
| 120 | }; | ||
| 121 | |||
| 122 | pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SymbolIterator { | ||
| 29 | const gpa = std.debug.getDebugInfoAllocator(); | 123 | const gpa = std.debug.getDebugInfoAllocator(); |
| 30 | try si.mutex.lock(io); | 124 | si.lock.lockShared(io) catch |err| return .failing(err); |
| 31 | defer si.mutex.unlock(io); | 125 | errdefer si.lock.unlockShared(io); |
| 32 | const module = try si.findModule(gpa, address); | 126 | const module = si.findModule(gpa, address) catch |err| return .failing(err); |
| 33 | const di = try module.getDebugInfo(gpa, io); | 127 | const di = module.getDebugInfo(gpa, io) catch |err| return .failing(err); |
| 34 | return di.getSymbol(gpa, address - @intFromPtr(module.entry.DllBase)); | 128 | const symbols = di.getSymbols(address - @intFromPtr(module.entry.DllBase)) catch |err| return .failing(err); |
| 129 | return .{ | ||
| 130 | .lock = &si.lock, | ||
| 131 | .module = module, | ||
| 132 | .symbols = symbols, | ||
| 133 | }; | ||
| 35 | } | 134 | } |
| 36 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { | 135 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { |
| 37 | const gpa = std.debug.getDebugInfoAllocator(); | 136 | const gpa = std.debug.getDebugInfoAllocator(); |
| 38 | try si.mutex.lock(io); | 137 | try si.lock.lockShared(io); |
| 39 | defer si.mutex.unlock(io); | 138 | defer si.lock.unlockShared(io); |
| 40 | const module = try si.findModule(gpa, address); | 139 | const module = try si.findModule(gpa, address); |
| 41 | return module.name orelse { | 140 | return module.name orelse { |
| 42 | const name = try std.unicode.wtf16LeToWtf8Alloc(gpa, module.entry.BaseDllName.slice()); | 141 | const name = try std.unicode.wtf16LeToWtf8Alloc(gpa, module.entry.BaseDllName.slice()); |
| ... | @@ -46,8 +145,8 @@ pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { | ... | @@ -46,8 +145,8 @@ pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { |
| 46 | } | 145 | } |
| 47 | pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize { | 146 | pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize { |
| 48 | const gpa = std.debug.getDebugInfoAllocator(); | 147 | const gpa = std.debug.getDebugInfoAllocator(); |
| 49 | try si.mutex.lock(io); | 148 | try si.lock.lockShared(io); |
| 50 | defer si.mutex.unlock(io); | 149 | defer si.lock.unlockShared(io); |
| 51 | const module = try si.findModule(gpa, address); | 150 | const module = try si.findModule(gpa, address); |
| 52 | return module.base_address; | 151 | return module.base_address; |
| 53 | } | 152 | } |
| ... | @@ -240,7 +339,19 @@ const Module = struct { | ... | @@ -240,7 +339,19 @@ const Module = struct { |
| 240 | arena.deinit(); | 339 | arena.deinit(); |
| 241 | } | 340 | } |
| 242 | 341 | ||
| 243 | fn getSymbol(di: *DebugInfo, gpa: Allocator, vaddr: usize) Error!std.debug.Symbol { | 342 | pub const Symbols = union(enum) { |
| 343 | pdb: struct { | ||
| 344 | module: *Pdb.Module, | ||
| 345 | proc_sym: ?*align(1) const std.pdb.ProcSym, | ||
| 346 | addr: usize, | ||
| 347 | inlinees: Pdb.InlineSiteSymIterator, | ||
| 348 | }, | ||
| 349 | dwarf: struct { | ||
| 350 | addr: usize, | ||
| 351 | }, | ||
| 352 | none: void, | ||
| 353 | }; | ||
| 354 | fn getSymbols(di: *DebugInfo, vaddr: usize) Error!Symbols { | ||
| 244 | pdb: { | 355 | pdb: { |
| 245 | const pdb = &(di.pdb orelse break :pdb); | 356 | const pdb = &(di.pdb orelse break :pdb); |
| 246 | var coff_section: *align(1) const coff.SectionHeader = undefined; | 357 | var coff_section: *align(1) const coff.SectionHeader = undefined; |
| ... | @@ -270,32 +381,30 @@ const Module = struct { | ... | @@ -270,32 +381,30 @@ const Module = struct { |
| 270 | } orelse { | 381 | } orelse { |
| 271 | return error.InvalidDebugInfo; // bad module index | 382 | return error.InvalidDebugInfo; // bad module index |
| 272 | }; | 383 | }; |
| 273 | return .{ | ||
| 274 | .name = pdb.getSymbolName(module, vaddr - coff_section.virtual_address), | ||
| 275 | .compile_unit_name = fs.path.basename(module.obj_file_name), | ||
| 276 | .source_location = pdb.getLineNumberInfo( | ||
| 277 | module, | ||
| 278 | vaddr - coff_section.virtual_address, | ||
| 279 | ) catch null, | ||
| 280 | }; | ||
| 281 | } | ||
| 282 | dwarf: { | ||
| 283 | const dwarf = &(di.dwarf orelse break :dwarf); | ||
| 284 | const dwarf_address = vaddr + di.coff_image_base; | ||
| 285 | return dwarf.getSymbol(gpa, native_endian, dwarf_address) catch |err| switch (err) { | ||
| 286 | error.MissingDebugInfo => break :dwarf, | ||
| 287 | 384 | ||
| 288 | error.InvalidDebugInfo, | 385 | const addr = vaddr - coff_section.virtual_address; |
| 289 | error.OutOfMemory, | 386 | const proc_sym = pdb.getProcSym(module, addr); |
| 290 | => |e| return e, | 387 | const inlinees: Pdb.InlineSiteSymIterator = if (proc_sym) |sym| |
| 388 | pdb.getInlinees(module, sym) | ||
| 389 | else | ||
| 390 | .empty; | ||
| 391 | return .{ .pdb = .{ | ||
| 392 | .module = module, | ||
| 393 | .proc_sym = proc_sym, | ||
| 394 | .addr = addr, | ||
| 395 | .inlinees = inlinees, | ||
| 396 | } }; | ||
| 397 | } | ||
| 291 | 398 | ||
| 292 | error.ReadFailed, | 399 | // Dwarf |
| 293 | error.EndOfStream, | 400 | dwarf: { |
| 294 | error.Overflow, | 401 | if (di.dwarf == null) break :dwarf; |
| 295 | error.StreamTooLong, | 402 | const addr = vaddr + di.coff_image_base; |
| 296 | => return error.InvalidDebugInfo, | 403 | return .{ .dwarf = .{ |
| 297 | }; | 404 | .addr = addr, |
| 405 | } }; | ||
| 298 | } | 406 | } |
| 407 | |||
| 299 | return error.MissingDebugInfo; | 408 | return error.MissingDebugInfo; |
| 300 | } | 409 | } |
| 301 | }; | 410 | }; |
| ... | @@ -505,6 +614,16 @@ const Module = struct { | ... | @@ -505,6 +614,16 @@ const Module = struct { |
| 505 | error.ReadFailed, | 614 | error.ReadFailed, |
| 506 | => |e| return e, | 615 | => |e| return e, |
| 507 | }; | 616 | }; |
| 617 | pdb.parseIpiStream() catch |err| switch (err) { | ||
| 618 | error.UnknownPDBVersion => return error.UnsupportedDebugInfo, | ||
| 619 | |||
| 620 | error.EndOfStream, | ||
| 621 | => return error.InvalidDebugInfo, | ||
| 622 | |||
| 623 | error.OutOfMemory, | ||
| 624 | error.ReadFailed, | ||
| 625 | => |e| return e, | ||
| 626 | }; | ||
| 508 | 627 | ||
| 509 | if (!std.mem.eql(u8, &coff_obj.guid, &pdb.guid) or coff_obj.age != pdb.age) | 628 | if (!std.mem.eql(u8, &coff_obj.guid, &pdb.guid) or coff_obj.age != pdb.age) |
| 510 | return error.InvalidDebugInfo; | 629 | return error.InvalidDebugInfo; |
| ... | @@ -531,7 +650,7 @@ const Module = struct { | ... | @@ -531,7 +650,7 @@ const Module = struct { |
| 531 | } | 650 | } |
| 532 | }; | 651 | }; |
| 533 | 652 | ||
| 534 | /// Assumes we already hold `si.mutex`. | 653 | /// Assumes we already hold `si.lock`. |
| 535 | fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebugInfo, OutOfMemory, Unexpected }!*Module { | 654 | fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebugInfo, OutOfMemory, Unexpected }!*Module { |
| 536 | for (si.modules.items) |*mod| { | 655 | for (si.modules.items) |*mod| { |
| 537 | const base = @intFromPtr(mod.entry.DllBase); | 656 | const base = @intFromPtr(mod.entry.DllBase); |
| ... | @@ -601,8 +720,8 @@ fn dllNotification( | ... | @@ -601,8 +720,8 @@ fn dllNotification( |
| 601 | .LOADED => {}, | 720 | .LOADED => {}, |
| 602 | .UNLOADED => { | 721 | .UNLOADED => { |
| 603 | const io = std.Options.debug_io; | 722 | const io = std.Options.debug_io; |
| 604 | si.mutex.lockUncancelable(io); | 723 | si.lock.lockUncancelable(io); |
| 605 | defer si.mutex.unlock(io); | 724 | defer si.lock.unlock(io); |
| 606 | for (si.modules.items, 0..) |*mod, mod_index| { | 725 | for (si.modules.items, 0..) |*mod, mod_index| { |
| 607 | if (mod.entry.DllBase != data.Unloaded.DllBase) continue; | 726 | if (mod.entry.DllBase != data.Unloaded.DllBase) continue; |
| 608 | mod.deinit(std.debug.getDebugInfoAllocator(), io); | 727 | mod.deinit(std.debug.getDebugInfoAllocator(), io); |
lib/std/pdb.zig+148-4| ... | @@ -314,11 +314,9 @@ pub const SymbolKind = enum(u16) { | ... | @@ -314,11 +314,9 @@ pub const SymbolKind = enum(u16) { |
| 314 | 314 | ||
| 315 | pub const TypeIndex = u32; | 315 | pub const TypeIndex = u32; |
| 316 | 316 | ||
| 317 | // TODO According to this header: | ||
| 318 | // https://github.com/microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L3722 | ||
| 319 | // we should define RecordPrefix as part of the ProcSym structure. | ||
| 320 | // This might be important when we start generating PDB in self-hosted with our own PE linker. | ||
| 321 | pub const ProcSym = extern struct { | 317 | pub const ProcSym = extern struct { |
| 318 | record_len: u16, | ||
| 319 | record_kind: SymbolKind, | ||
| 322 | parent: u32, | 320 | parent: u32, |
| 323 | end: u32, | 321 | end: u32, |
| 324 | next: u32, | 322 | next: u32, |
| ... | @@ -508,3 +506,149 @@ pub const SuperBlock = extern struct { | ... | @@ -508,3 +506,149 @@ pub const SuperBlock = extern struct { |
| 508 | // implement it so we're kind of safe making this assumption for now. | 506 | // implement it so we're kind of safe making this assumption for now. |
| 509 | block_map_addr: u32, | 507 | block_map_addr: u32, |
| 510 | }; | 508 | }; |
| 509 | |||
| 510 | pub const IpiStreamVersion = enum(u32) { | ||
| 511 | v40 = 19950410, | ||
| 512 | v41 = 19951122, | ||
| 513 | v50 = 19961031, | ||
| 514 | v70 = 19990903, | ||
| 515 | v80 = 20040203, | ||
| 516 | _, | ||
| 517 | }; | ||
| 518 | |||
| 519 | pub const IpiStreamHeader = extern struct { | ||
| 520 | version: IpiStreamVersion, | ||
| 521 | header_size: u32, | ||
| 522 | type_index_begin: u32, | ||
| 523 | type_index_end: u32, | ||
| 524 | type_record_bytes: u32, | ||
| 525 | hash_stream_index: u16, | ||
| 526 | hash_aux_stream_index: u16, | ||
| 527 | hash_key_size: u32, | ||
| 528 | num_hash_buckets: u32, | ||
| 529 | hash_value_buffer_offset: i32, | ||
| 530 | hash_value_buffer_length: u32, | ||
| 531 | index_offset_buffer_offset: i32, | ||
| 532 | index_offset_buffer_length: u32, | ||
| 533 | hash_adj_buffer_offset: i32, | ||
| 534 | hash_adj_buffer_length: u32, | ||
| 535 | }; | ||
| 536 | |||
| 537 | pub const LfRecordPrefix = extern struct { | ||
| 538 | len: u16, | ||
| 539 | kind: LfRecordKind, | ||
| 540 | }; | ||
| 541 | |||
| 542 | pub const LfRecordKind = enum(u16) { | ||
| 543 | pointer = 0x1002, | ||
| 544 | modifier = 0x1001, | ||
| 545 | procedure = 0x1008, | ||
| 546 | mfunction = 0x1009, | ||
| 547 | label = 0x000e, | ||
| 548 | arglist = 0x1201, | ||
| 549 | fieldlist = 0x1203, | ||
| 550 | array = 0x1503, | ||
| 551 | class = 0x1504, | ||
| 552 | structure = 0x1505, | ||
| 553 | interface = 0x1519, | ||
| 554 | @"union" = 0x1506, | ||
| 555 | @"enum" = 0x1507, | ||
| 556 | typeserver2 = 0x1515, | ||
| 557 | vftable = 0x151d, | ||
| 558 | vtshape = 0x000a, | ||
| 559 | bitfield = 0x1205, | ||
| 560 | func_id = 0x1601, | ||
| 561 | mfunc_id = 0x1602, | ||
| 562 | buildinfo = 0x1603, | ||
| 563 | substr_list = 0x1604, | ||
| 564 | string_id = 0x1605, | ||
| 565 | udt_src_line = 0x1606, | ||
| 566 | udt_mod_src_line = 0x1607, | ||
| 567 | methodlist = 0x1206, | ||
| 568 | precomp = 0x1509, | ||
| 569 | endprecomp = 0x0014, | ||
| 570 | bclass = 0x1400, | ||
| 571 | binterface = 0x151a, | ||
| 572 | vbclass = 0x1401, | ||
| 573 | ivbclass = 0x1402, | ||
| 574 | vfunctab = 0x1409, | ||
| 575 | stmember = 0x150e, | ||
| 576 | method = 0x150f, | ||
| 577 | member = 0x150d, | ||
| 578 | nesttype = 0x1510, | ||
| 579 | onemethod = 0x1511, | ||
| 580 | enumerate = 0x1502, | ||
| 581 | index = 0x1404, | ||
| 582 | pad0 = 0xf0, | ||
| 583 | _, | ||
| 584 | }; | ||
| 585 | |||
| 586 | pub const LfFuncId = extern struct { | ||
| 587 | len: u16, | ||
| 588 | kind: LfRecordKind, | ||
| 589 | scope_id: u32, | ||
| 590 | type: u32, | ||
| 591 | name: [1]u8, // null-terminated | ||
| 592 | }; | ||
| 593 | |||
| 594 | pub const LfMFuncId = extern struct { | ||
| 595 | len: u16, | ||
| 596 | kind: LfRecordKind, | ||
| 597 | parent_type: u32, | ||
| 598 | type: u32, | ||
| 599 | name: [1]u8, // null-terminated | ||
| 600 | }; | ||
| 601 | |||
| 602 | pub const InlineSiteSym = extern struct { | ||
| 603 | record_len: u16, | ||
| 604 | record_kind: SymbolKind, | ||
| 605 | parent: u32, | ||
| 606 | end: u32, | ||
| 607 | inlinee: u32, | ||
| 608 | }; | ||
| 609 | |||
| 610 | pub const InlineSiteSym2 = extern struct { | ||
| 611 | record_len: u16, | ||
| 612 | record_kind: SymbolKind, | ||
| 613 | parent: u32, | ||
| 614 | end: u32, | ||
| 615 | inlinee: u32, | ||
| 616 | invocations: u32, | ||
| 617 | }; | ||
| 618 | |||
| 619 | pub const InlineeSourceLineSignature = enum(u32) { | ||
| 620 | normal = 0, | ||
| 621 | ex = 1, | ||
| 622 | _ | ||
| 623 | }; | ||
| 624 | |||
| 625 | pub const InlineeSourceLine = extern struct { | ||
| 626 | inlinee: u32, | ||
| 627 | file_id: u32, | ||
| 628 | source_line_num: u32, | ||
| 629 | }; | ||
| 630 | |||
| 631 | pub const InlineeSourceLineEx = extern struct { | ||
| 632 | inlinee: u32, | ||
| 633 | file_id: u32, | ||
| 634 | source_line_num: u32, | ||
| 635 | count_of_extra_files: u32, | ||
| 636 | }; | ||
| 637 | |||
| 638 | pub const BinaryAnnotationOpcode = enum(u8) { | ||
| 639 | invalid = 0, | ||
| 640 | code_offset = 1, | ||
| 641 | change_code_offset_base = 2, | ||
| 642 | change_code_offset = 3, | ||
| 643 | change_code_length = 4, | ||
| 644 | change_file = 5, | ||
| 645 | change_line_offset = 6, | ||
| 646 | change_line_end_delta = 7, | ||
| 647 | change_range_kind = 8, | ||
| 648 | change_column_start = 9, | ||
| 649 | change_column_end_delta = 10, | ||
| 650 | change_code_offset_and_line_offset = 11, | ||
| 651 | change_code_length_and_code_offset = 12, | ||
| 652 | change_column_end = 13, | ||
| 653 | }; | ||
| 654 |