| 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 | 39 | /// pub const init: SelfInfo; |
| 40 | 40 | /// pub fn deinit(si: *SelfInfo, io: Io) void; |
| 41 | 41 | /// |
| 42 | /// /// Returns the symbol and source location of the instruction at `address`. | |
| 43 | /// pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) SelfInfoError!Symbol; | |
| 42 | /// /// Returns an iterator over the symbols and source locations of the instruction at `address`. | |
| 43 | /// pub fn getSymbols(si: *SelfInfo, io: Io, address: usize) SelfInfo.SymbolIterator; | |
| 44 | 44 | /// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`. |
| 45 | 45 | /// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8; |
| 46 | 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 | 61 | /// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's |
| 62 | 62 | /// /// return address, or 0 if the end of the stack has been reached. |
| 63 | 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 | 70 | pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo")) |
| 66 | 71 | root.debug.SelfInfo |
| ... | ... | @@ -1107,33 +1112,37 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize { |
| 1107 | 1112 | } |
| 1108 | 1113 | |
| 1109 | 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) { | |
| 1111 | error.MissingDebugInfo, | |
| 1112 | error.UnsupportedDebugInfo, | |
| 1113 | error.InvalidDebugInfo, | |
| 1114 | => .unknown, | |
| 1115 | error.ReadFailed, error.Unexpected, error.Canceled => s: { | |
| 1116 | t.setColor(.dim) catch {}; | |
| 1117 | try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); | |
| 1118 | t.setColor(.reset) catch {}; | |
| 1119 | break :s .unknown; | |
| 1120 | }, | |
| 1121 | error.OutOfMemory => s: { | |
| 1122 | t.setColor(.dim) catch {}; | |
| 1123 | try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{}); | |
| 1124 | t.setColor(.reset) catch {}; | |
| 1125 | break :s .unknown; | |
| 1126 | }, | |
| 1127 | }; | |
| 1128 | defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name); | |
| 1129 | return printLineInfo( | |
| 1130 | io, | |
| 1131 | t, | |
| 1132 | symbol.source_location, | |
| 1133 | address, | |
| 1134 | symbol.name orelse "???", | |
| 1135 | symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???", | |
| 1136 | ); | |
| 1115 | var symbols: SelfInfo.SymbolIterator = debug_info.getSymbols(io, address); | |
| 1116 | defer symbols.deinit(io); | |
| 1117 | while (symbols.next()) |curr| { | |
| 1118 | const symbol: Symbol = curr catch |err| switch (err) { | |
| 1119 | error.MissingDebugInfo, | |
| 1120 | error.UnsupportedDebugInfo, | |
| 1121 | error.InvalidDebugInfo, | |
| 1122 | => .unknown, | |
| 1123 | error.ReadFailed, error.Unexpected, error.Canceled => s: { | |
| 1124 | t.setColor(.dim) catch {}; | |
| 1125 | try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); | |
| 1126 | t.setColor(.reset) catch {}; | |
| 1127 | break :s .unknown; | |
| 1128 | }, | |
| 1129 | error.OutOfMemory => s: { | |
| 1130 | t.setColor(.dim) catch {}; | |
| 1131 | try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{}); | |
| 1132 | t.setColor(.reset) catch {}; | |
| 1133 | break :s .unknown; | |
| 1134 | }, | |
| 1135 | }; | |
| 1136 | defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name); | |
| 1137 | try printLineInfo( | |
| 1138 | io, | |
| 1139 | t, | |
| 1140 | symbol.source_location, | |
| 1141 | address, | |
| 1142 | symbol.name orelse "???", | |
| 1143 | symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???", | |
| 1144 | ); | |
| 1145 | } | |
| 1137 | 1146 | } |
| 1138 | 1147 | fn printLineInfo( |
| 1139 | 1148 | io: Io, |
lib/std/debug/Pdb.zig+453-14| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | const File = std.Io.File; | |
| 2 | const Io = std.Io; | |
| 3 | const File = Io.File; | |
| 3 | 4 | const Allocator = std.mem.Allocator; |
| 4 | 5 | const pdb = std.pdb; |
| 5 | 6 | const assert = std.debug.assert; |
| ... | ... | @@ -10,7 +11,7 @@ file_reader: *File.Reader, |
| 10 | 11 | msf: Msf, |
| 11 | 12 | allocator: Allocator, |
| 12 | 13 | string_table: ?*MsfStream, |
| 13 | dbi: ?*MsfStream, | |
| 14 | ipi: ?[]u8, | |
| 14 | 15 | modules: []Module, |
| 15 | 16 | sect_contribs: []pdb.SectionContribEntry, |
| 16 | 17 | guid: [16]u8, |
| ... | ... | @@ -41,7 +42,7 @@ pub fn init(gpa: Allocator, file_reader: *File.Reader) !Pdb { |
| 41 | 42 | .file_reader = file_reader, |
| 42 | 43 | .allocator = gpa, |
| 43 | 44 | .string_table = null, |
| 44 | .dbi = null, | |
| 45 | .ipi = null, | |
| 45 | 46 | .msf = try Msf.init(gpa, file_reader), |
| 46 | 47 | .modules = &.{}, |
| 47 | 48 | .sect_contribs = &.{}, |
| ... | ... | @@ -53,6 +54,7 @@ pub fn init(gpa: Allocator, file_reader: *File.Reader) !Pdb { |
| 53 | 54 | pub fn deinit(self: *Pdb) void { |
| 54 | 55 | const gpa = self.allocator; |
| 55 | 56 | self.msf.deinit(gpa); |
| 57 | if (self.ipi) |ipi| gpa.free(ipi); | |
| 56 | 58 | for (self.modules) |*module| { |
| 57 | 59 | module.deinit(gpa); |
| 58 | 60 | } |
| ... | ... | @@ -67,7 +69,7 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 67 | 69 | const gpa = self.allocator; |
| 68 | 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 | 73 | if (header.version_header != 19990903) // V70, only value observed by LLVM team |
| 72 | 74 | return error.UnknownPDBVersion; |
| 73 | 75 | // if (header.Age != age) |
| ... | ... | @@ -85,14 +87,14 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 85 | 87 | const mod_info = try reader.takeStruct(pdb.ModInfo, .little); |
| 86 | 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 | 91 | defer module_name.deinit(); |
| 90 | 92 | this_record_len += try reader.streamDelimiterLimit(&module_name.writer, 0, .limited(1024)); |
| 91 | 93 | assert(reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API |
| 92 | 94 | reader.toss(1); |
| 93 | 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 | 98 | defer obj_file_name.deinit(); |
| 97 | 99 | this_record_len += try reader.streamDelimiterLimit(&obj_file_name.writer, 0, .limited(1024)); |
| 98 | 100 | assert(reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API |
| ... | ... | @@ -128,7 +130,7 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 128 | 130 | |
| 129 | 131 | var sect_cont_offset: usize = 0; |
| 130 | 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 | 134 | error.InvalidEnumTag, error.EndOfStream => return error.InvalidDebugInfo, |
| 133 | 135 | error.ReadFailed => return error.ReadFailed, |
| 134 | 136 | }; |
| ... | ... | @@ -148,6 +150,15 @@ pub fn parseDbiStream(self: *Pdb) !void { |
| 148 | 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 | 162 | pub fn parseInfoStream(self: *Pdb) !void { |
| 152 | 163 | var stream = self.getStream(pdb.StreamType.pdb) orelse return error.InvalidDebugInfo; |
| 153 | 164 | const reader = &stream.interface; |
| ... | ... | @@ -212,8 +223,9 @@ pub fn parseInfoStream(self: *Pdb) !void { |
| 212 | 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 | 227 | _ = self; |
| 228 | ||
| 217 | 229 | std.debug.assert(module.populated); |
| 218 | 230 | |
| 219 | 231 | var symbol_i: usize = 0; |
| ... | ... | @@ -223,9 +235,9 @@ pub fn getSymbolName(self: *Pdb, module: *Module, address: u64) ?[]const u8 { |
| 223 | 235 | return null; |
| 224 | 236 | switch (prefix.record_kind) { |
| 225 | 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 | 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 | 243 | else => {}, |
| ... | ... | @@ -236,6 +248,433 @@ pub fn getSymbolName(self: *Pdb, module: *Module, address: u64) ?[]const u8 { |
| 236 | 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 | 678 | pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.SourceLocation { |
| 240 | 679 | std.debug.assert(module.populated); |
| 241 | 680 | const subsect_info = module.subsect_info; |
| ... | ... | @@ -296,7 +735,7 @@ pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !std.debug.S |
| 296 | 735 | try self.string_table.?.seekTo(strtab_offset); |
| 297 | 736 | const source_file_name = s: { |
| 298 | 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 | 739 | defer source_file_name.deinit(); |
| 301 | 740 | _ = try string_reader.streamDelimiterLimit(&source_file_name.writer, 0, .limited(1024)); |
| 302 | 741 | assert(string_reader.buffered()[0] == 0); // TODO change streamDelimiterLimit API |
| ... | ... | @@ -497,7 +936,7 @@ const MsfStream = struct { |
| 497 | 936 | next_read_pos: u64, |
| 498 | 937 | blocks: []u32, |
| 499 | 938 | block_size: u32, |
| 500 | interface: std.Io.Reader, | |
| 939 | interface: Io.Reader, | |
| 501 | 940 | err: ?Error, |
| 502 | 941 | |
| 503 | 942 | const Error = File.Reader.SeekError; |
| ... | ... | @@ -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 | 970 | const ms: *MsfStream = @alignCast(@fieldParentPtr("interface", r)); |
| 532 | 971 | |
| 533 | 972 | var block_id: usize = @intCast(ms.next_read_pos / ms.block_size); |
| ... | ... | @@ -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 | 1038 | const num_words = try reader.takeInt(u32, .little); |
| 600 | 1039 | var list = std.array_list.Managed(u32).init(allocator); |
| 601 | 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 | 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 | 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 | 50 | defer si.rwlock.unlock(io); |
| 37 | 51 | |
| 38 | 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 | 55 | if (loaded_elf.file.dwarf) |*dwarf| { |
| 42 | 56 | if (!loaded_elf.scanned_dwarf) { |
| 43 | 57 | dwarf.open(gpa, native_endian) catch |err| switch (err) { |
| 44 | 58 | error.InvalidDebugInfo, |
| 45 | 59 | error.MissingDebugInfo, |
| 46 | 60 | error.OutOfMemory, |
| 47 | => |e| return e, | |
| 61 | => |e| return .{ .curr = e }, | |
| 48 | 62 | error.EndOfStream, |
| 49 | 63 | error.Overflow, |
| 50 | 64 | error.ReadFailed, |
| 51 | 65 | error.StreamTooLong, |
| 52 | => return error.InvalidDebugInfo, | |
| 66 | => return .{ .curr = error.InvalidDebugInfo }, | |
| 53 | 67 | }; |
| 54 | 68 | loaded_elf.scanned_dwarf = true; |
| 55 | 69 | } |
| 56 | 70 | if (dwarf.getSymbol(gpa, native_endian, vaddr)) |sym| { |
| 57 | return sym; | |
| 71 | return .{ .curr = sym }; | |
| 58 | 72 | } else |err| switch (err) { |
| 59 | 73 | error.MissingDebugInfo => {}, |
| 60 | 74 | |
| 61 | 75 | error.InvalidDebugInfo, |
| 62 | 76 | error.OutOfMemory, |
| 63 | => |e| return e, | |
| 77 | => |e| return .{ .curr = e }, | |
| 64 | 78 | |
| 65 | 79 | error.ReadFailed, |
| 66 | 80 | error.EndOfStream, |
| 67 | 81 | error.Overflow, |
| 68 | 82 | error.StreamTooLong, |
| 69 | => return error.InvalidDebugInfo, | |
| 83 | => return .{ .curr = error.InvalidDebugInfo }, | |
| 70 | 84 | } |
| 71 | 85 | } |
| 72 | 86 | // When DWARF is unavailable, fall back to searching the symtab. |
| 73 | return loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) { | |
| 74 | error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo, | |
| 75 | error.BadSymtab => return error.InvalidDebugInfo, | |
| 76 | error.OutOfMemory => |e| return e, | |
| 87 | const symbol = loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) { | |
| 88 | error.NoSymtab, error.NoStrtab => return .{ .curr = error.MissingDebugInfo }, | |
| 89 | error.BadSymtab => return .{ .curr = error.InvalidDebugInfo }, | |
| 90 | error.OutOfMemory => |e| return .{ .curr = e }, | |
| 77 | 91 | }; |
| 92 | ||
| 93 | return .{ .curr = symbol }; | |
| 78 | 94 | } |
| 79 | 95 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { |
| 80 | 96 | const gpa = std.debug.getDebugInfoAllocator(); |
lib/std/debug/SelfInfo/Windows.zig+158-39| ... | ... | @@ -1,10 +1,10 @@ |
| 1 | mutex: Io.Mutex, | |
| 1 | lock: Io.RwLock, | |
| 2 | 2 | ntdll_handle: ?if (load_dll_notification_procs) *anyopaque else noreturn, |
| 3 | 3 | notification_cookie: ?LDR.DLL_NOTIFICATION.COOKIE, |
| 4 | 4 | modules: std.ArrayList(Module), |
| 5 | 5 | |
| 6 | 6 | pub const init: SelfInfo = .{ |
| 7 | .mutex = .init, | |
| 7 | .lock = .init, | |
| 8 | 8 | .ntdll_handle = null, |
| 9 | 9 | .notification_cookie = null, |
| 10 | 10 | .modules = .empty, |
| ... | ... | @@ -25,18 +25,117 @@ pub fn deinit(si: *SelfInfo, io: Io) void { |
| 25 | 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 | 123 | const gpa = std.debug.getDebugInfoAllocator(); |
| 30 | try si.mutex.lock(io); | |
| 31 | defer si.mutex.unlock(io); | |
| 32 | const module = try si.findModule(gpa, address); | |
| 33 | const di = try module.getDebugInfo(gpa, io); | |
| 34 | return di.getSymbol(gpa, address - @intFromPtr(module.entry.DllBase)); | |
| 124 | si.lock.lockShared(io) catch |err| return .failing(err); | |
| 125 | errdefer si.lock.unlockShared(io); | |
| 126 | const module = si.findModule(gpa, address) catch |err| return .failing(err); | |
| 127 | const di = module.getDebugInfo(gpa, io) catch |err| return .failing(err); | |
| 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 | 135 | pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 { |
| 37 | 136 | const gpa = std.debug.getDebugInfoAllocator(); |
| 38 | try si.mutex.lock(io); | |
| 39 | defer si.mutex.unlock(io); | |
| 137 | try si.lock.lockShared(io); | |
| 138 | defer si.lock.unlockShared(io); | |
| 40 | 139 | const module = try si.findModule(gpa, address); |
| 41 | 140 | return module.name orelse { |
| 42 | 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 | 145 | } |
| 47 | 146 | pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize { |
| 48 | 147 | const gpa = std.debug.getDebugInfoAllocator(); |
| 49 | try si.mutex.lock(io); | |
| 50 | defer si.mutex.unlock(io); | |
| 148 | try si.lock.lockShared(io); | |
| 149 | defer si.lock.unlockShared(io); | |
| 51 | 150 | const module = try si.findModule(gpa, address); |
| 52 | 151 | return module.base_address; |
| 53 | 152 | } |
| ... | ... | @@ -240,7 +339,19 @@ const Module = struct { |
| 240 | 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 | 355 | pdb: { |
| 245 | 356 | const pdb = &(di.pdb orelse break :pdb); |
| 246 | 357 | var coff_section: *align(1) const coff.SectionHeader = undefined; |
| ... | ... | @@ -270,32 +381,30 @@ const Module = struct { |
| 270 | 381 | } orelse { |
| 271 | 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, | |
| 289 | error.OutOfMemory, | |
| 290 | => |e| return e, | |
| 385 | const addr = vaddr - coff_section.virtual_address; | |
| 386 | const proc_sym = pdb.getProcSym(module, addr); | |
| 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, | |
| 293 | error.EndOfStream, | |
| 294 | error.Overflow, | |
| 295 | error.StreamTooLong, | |
| 296 | => return error.InvalidDebugInfo, | |
| 297 | }; | |
| 399 | // Dwarf | |
| 400 | dwarf: { | |
| 401 | if (di.dwarf == null) break :dwarf; | |
| 402 | const addr = vaddr + di.coff_image_base; | |
| 403 | return .{ .dwarf = .{ | |
| 404 | .addr = addr, | |
| 405 | } }; | |
| 298 | 406 | } |
| 407 | ||
| 299 | 408 | return error.MissingDebugInfo; |
| 300 | 409 | } |
| 301 | 410 | }; |
| ... | ... | @@ -505,6 +614,16 @@ const Module = struct { |
| 505 | 614 | error.ReadFailed, |
| 506 | 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 | 628 | if (!std.mem.eql(u8, &coff_obj.guid, &pdb.guid) or coff_obj.age != pdb.age) |
| 510 | 629 | return error.InvalidDebugInfo; |
| ... | ... | @@ -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 | 654 | fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebugInfo, OutOfMemory, Unexpected }!*Module { |
| 536 | 655 | for (si.modules.items) |*mod| { |
| 537 | 656 | const base = @intFromPtr(mod.entry.DllBase); |
| ... | ... | @@ -601,8 +720,8 @@ fn dllNotification( |
| 601 | 720 | .LOADED => {}, |
| 602 | 721 | .UNLOADED => { |
| 603 | 722 | const io = std.Options.debug_io; |
| 604 | si.mutex.lockUncancelable(io); | |
| 605 | defer si.mutex.unlock(io); | |
| 723 | si.lock.lockUncancelable(io); | |
| 724 | defer si.lock.unlock(io); | |
| 606 | 725 | for (si.modules.items, 0..) |*mod, mod_index| { |
| 607 | 726 | if (mod.entry.DllBase != data.Unloaded.DllBase) continue; |
| 608 | 727 | mod.deinit(std.debug.getDebugInfoAllocator(), io); |
lib/std/pdb.zig+148-4| ... | ... | @@ -314,11 +314,9 @@ pub const SymbolKind = enum(u16) { |
| 314 | 314 | |
| 315 | 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 | 317 | pub const ProcSym = extern struct { |
| 318 | record_len: u16, | |
| 319 | record_kind: SymbolKind, | |
| 322 | 320 | parent: u32, |
| 323 | 321 | end: u32, |
| 324 | 322 | next: u32, |
| ... | ... | @@ -508,3 +506,149 @@ pub const SuperBlock = extern struct { |
| 508 | 506 | // implement it so we're kind of safe making this assumption for now. |
| 509 | 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 |