authorgravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-03-20 17:51:34-07:00
committergravatar for mason@gamesbymason.comMason Remaley <mason@gamesbymason.com> 2026-04-12 04:01:29-07:00
log22f9592dc7db869b2a618a72560fd0cf25b62d42
tree88e34ab198a4cf85f321a4b18bcbd230a232b802
parentedfc4727e24b1c75f49aa42f1b84c7fc59176a32

First pass at reading inline info from PDBs


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's61/// /// 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/// ```
65pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo"))70pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo"))
66 root.debug.SelfInfo71 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}
11081113
1109fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: usize) Writer.Error!void {1114fn 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}
1138fn printLineInfo(1147fn printLineInfo(
1139 io: Io,1148 io: Io,
lib/std/debug/Pdb.zig+453-14
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const File = std.Io.File;2const Io = std.Io;
3const File = Io.File;
3const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
4const pdb = std.pdb;5const pdb = std.pdb;
5const assert = std.debug.assert;6const assert = std.debug.assert;
...@@ -10,7 +11,7 @@ file_reader: *File.Reader,...@@ -10,7 +11,7 @@ file_reader: *File.Reader,
10msf: Msf,11msf: Msf,
11allocator: Allocator,12allocator: Allocator,
12string_table: ?*MsfStream,13string_table: ?*MsfStream,
13dbi: ?*MsfStream,14ipi: ?[]u8,
14modules: []Module,15modules: []Module,
15sect_contribs: []pdb.SectionContribEntry,16sect_contribs: []pdb.SectionContribEntry,
16guid: [16]u8,17guid: [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 {
53pub fn deinit(self: *Pdb) void {54pub 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;
6971
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 team73 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);
8789
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 API93 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;
9496
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 API100 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 {
128130
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}
150152
153pub 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
151pub fn parseInfoStream(self: *Pdb) !void {162pub 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}
214225
215pub fn getSymbolName(self: *Pdb, module: *Module, address: u64) ?[]const u8 {226pub 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);
218230
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}
238250
251pub 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
294pub 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
546pub 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
570pub 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
583pub 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
597pub 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
614pub 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
619pub 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
239pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.SourceLocation {678pub 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 API741 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,
502941
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 }
529968
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));
532971
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};
5971036
598fn readSparseBitVector(reader: *std.Io.Reader, allocator: Allocator) ![]u32 {1037fn 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}
3232
33pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol {33pub 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
47pub 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);
3751
38 const vaddr = address - module.load_offset;52 const vaddr = address - module.load_offset;
3953
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 => {},
6074
61 error.InvalidDebugInfo,75 error.InvalidDebugInfo,
62 error.OutOfMemory,76 error.OutOfMemory,
63 => |e| return e,77 => |e| return .{ .curr = e },
6478
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}
79pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {95pub 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 @@
1mutex: Io.Mutex,1lock: Io.RwLock,
2ntdll_handle: ?if (load_dll_notification_procs) *anyopaque else noreturn,2ntdll_handle: ?if (load_dll_notification_procs) *anyopaque else noreturn,
3notification_cookie: ?LDR.DLL_NOTIFICATION.COOKIE,3notification_cookie: ?LDR.DLL_NOTIFICATION.COOKIE,
4modules: std.ArrayList(Module),4modules: std.ArrayList(Module),
55
6pub const init: SelfInfo = .{6pub 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}
2727
28pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol {28pub 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
122pub 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}
36pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {135pub 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}
47pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {146pub 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 }
242341
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 index382 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,
287384
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 }
291398
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 };
508627
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};
533652
534/// Assumes we already hold `si.mutex`.653/// Assumes we already hold `si.lock`.
535fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebugInfo, OutOfMemory, Unexpected }!*Module {654fn 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) {
314314
315pub const TypeIndex = u32;315pub const TypeIndex = u32;
316316
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.
321pub const ProcSym = extern struct {317pub 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
510pub const IpiStreamVersion = enum(u32) {
511 v40 = 19950410,
512 v41 = 19951122,
513 v50 = 19961031,
514 v70 = 19990903,
515 v80 = 20040203,
516 _,
517};
518
519pub 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
537pub const LfRecordPrefix = extern struct {
538 len: u16,
539 kind: LfRecordKind,
540};
541
542pub 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
586pub 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
594pub 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
602pub const InlineSiteSym = extern struct {
603 record_len: u16,
604 record_kind: SymbolKind,
605 parent: u32,
606 end: u32,
607 inlinee: u32,
608};
609
610pub 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
619pub const InlineeSourceLineSignature = enum(u32) {
620 normal = 0,
621 ex = 1,
622 _
623};
624
625pub const InlineeSourceLine = extern struct {
626 inlinee: u32,
627 file_id: u32,
628 source_line_num: u32,
629};
630
631pub const InlineeSourceLineEx = extern struct {
632 inlinee: u32,
633 file_id: u32,
634 source_line_num: u32,
635 count_of_extra_files: u32,
636};
637
638pub 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