authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-13 17:58:01-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-13 18:04:23-07:00
log022bca9b0600da3dda8b30fefe8eb817647b0f08
tree9a855d043651aef5e599292ed461d49326d9739c
parenta9e7fb0e0189e01ebf67b144aca3fd8c318925c3

std.debug.Dwarf: better source location information

Two fixes here: Sort by addresses after generating the line table. Debug information in the wild is not sorted and the rest of the implementation requires this data to be sorted. Handle DW.LNE.end_sequence correctly. When I originally wrote this code, I misunderstood what this opcode was supposed to do. Now I understand that it marks the *end* of an address range, meaning the current address does *not* map to the current line information. This fixes source location information for a big chunk of ReleaseSafe code.

1 files changed, 45 insertions(+), 9 deletions(-)

lib/std/debug/Dwarf.zig+45-9
...@@ -26,7 +26,6 @@ const cast = std.math.cast;...@@ -26,7 +26,6 @@ const cast = std.math.cast;
26const maxInt = std.math.maxInt;26const maxInt = std.math.maxInt;
27const MemoryAccessor = std.debug.MemoryAccessor;27const MemoryAccessor = std.debug.MemoryAccessor;
28const Path = std.Build.Cache.Path;28const Path = std.Build.Cache.Path;
29
30const FixedBufferReader = std.debug.FixedBufferReader;29const FixedBufferReader = std.debug.FixedBufferReader;
3130
32const Dwarf = @This();31const Dwarf = @This();
...@@ -35,6 +34,9 @@ pub const expression = @import("Dwarf/expression.zig");...@@ -35,6 +34,9 @@ pub const expression = @import("Dwarf/expression.zig");
35pub const abi = @import("Dwarf/abi.zig");34pub const abi = @import("Dwarf/abi.zig");
36pub const call_frame = @import("Dwarf/call_frame.zig");35pub const call_frame = @import("Dwarf/call_frame.zig");
3736
37/// Useful to temporarily enable while working on this file.
38const debug_debug_mode = false;
39
38endian: std.builtin.Endian,40endian: std.builtin.Endian,
39sections: SectionArray = null_section_array,41sections: SectionArray = null_section_array,
40is_macho: bool,42is_macho: bool,
...@@ -165,6 +167,16 @@ pub const CompileUnit = struct {...@@ -165,6 +167,16 @@ pub const CompileUnit = struct {
165 column: u32,167 column: u32,
166 /// Offset by 1 depending on whether Dwarf version is >= 5.168 /// Offset by 1 depending on whether Dwarf version is >= 5.
167 file: u32,169 file: u32,
170
171 pub const invalid: LineEntry = .{
172 .line = undefined,
173 .column = undefined,
174 .file = std.math.maxInt(u32),
175 };
176
177 pub fn isInvalid(le: LineEntry) bool {
178 return le.file == invalid.file;
179 }
168 };180 };
169181
170 pub fn findSource(slc: *const SrcLocCache, address: u64) !LineEntry {182 pub fn findSource(slc: *const SrcLocCache, address: u64) !LineEntry {
...@@ -1400,6 +1412,7 @@ fn parseDie(...@@ -1400,6 +1412,7 @@ fn parseDie(
1400 };1412 };
1401}1413}
14021414
1415/// Ensures that addresses in the returned LineTable are monotonically increasing.
1403fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !CompileUnit.SrcLocCache {1416fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !CompileUnit.SrcLocCache {
1404 const compile_unit_cwd = try compile_unit.die.getAttrString(d, AT.comp_dir, d.section(.debug_line_str), compile_unit.*);1417 const compile_unit_cwd = try compile_unit.die.getAttrString(d, AT.comp_dir, d.section(.debug_line_str), compile_unit.*);
1405 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);1418 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
...@@ -1575,8 +1588,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1575,8 +1588,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1575 const sub_op = try fbr.readByte();1588 const sub_op = try fbr.readByte();
1576 switch (sub_op) {1589 switch (sub_op) {
1577 DW.LNE.end_sequence => {1590 DW.LNE.end_sequence => {
1578 prog.end_sequence = true;1591 // The row being added here is an "end" address, meaning
1579 try prog.addRow(gpa, &line_table);1592 // that it does not map to the source location here -
1593 // rather it marks the previous address as the last address
1594 // that maps to this source location.
1595
1596 // In this implementation we don't mark end of addresses.
1597 // This is a performance optimization based on the fact
1598 // that we don't need to know if an address is missing
1599 // source location info; we are only interested in being
1600 // able to look up source location info for addresses that
1601 // are known to have debug info.
1602 //if (debug_debug_mode) assert(!line_table.contains(prog.address));
1603 //try line_table.put(gpa, prog.address, CompileUnit.SrcLocCache.LineEntry.invalid);
1580 prog.reset();1604 prog.reset();
1581 },1605 },
1582 DW.LNE.set_address => {1606 DW.LNE.set_address => {
...@@ -1651,6 +1675,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1651,6 +1675,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1651 }1675 }
1652 }1676 }
16531677
1678 // Dwarf standard v5, 6.2.5 says
1679 // > Within a sequence, addresses and operation pointers may only increase.
1680 // However, this is empirically not the case in reality, so we sort here.
1681 line_table.sortUnstable(struct {
1682 keys: []const u64,
1683
1684 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
1685 return ctx.keys[a_index] < ctx.keys[b_index];
1686 }
1687 }{ .keys = line_table.keys() });
1688
1654 return .{1689 return .{
1655 .line_table = line_table,1690 .line_table = line_table,
1656 .directories = try directories.toOwnedSlice(gpa),1691 .directories = try directories.toOwnedSlice(gpa),
...@@ -1895,7 +1930,6 @@ const LineNumberProgram = struct {...@@ -1895,7 +1930,6 @@ const LineNumberProgram = struct {
1895 version: u16,1930 version: u16,
1896 is_stmt: bool,1931 is_stmt: bool,
1897 basic_block: bool,1932 basic_block: bool,
1898 end_sequence: bool,
18991933
1900 default_is_stmt: bool,1934 default_is_stmt: bool,
19011935
...@@ -1907,7 +1941,6 @@ const LineNumberProgram = struct {...@@ -1907,7 +1941,6 @@ const LineNumberProgram = struct {
1907 self.column = 0;1941 self.column = 0;
1908 self.is_stmt = self.default_is_stmt;1942 self.is_stmt = self.default_is_stmt;
1909 self.basic_block = false;1943 self.basic_block = false;
1910 self.end_sequence = false;
1911 }1944 }
19121945
1913 pub fn init(is_stmt: bool, version: u16) LineNumberProgram {1946 pub fn init(is_stmt: bool, version: u16) LineNumberProgram {
...@@ -1919,13 +1952,16 @@ const LineNumberProgram = struct {...@@ -1919,13 +1952,16 @@ const LineNumberProgram = struct {
1919 .version = version,1952 .version = version,
1920 .is_stmt = is_stmt,1953 .is_stmt = is_stmt,
1921 .basic_block = false,1954 .basic_block = false,
1922 .end_sequence = false,
1923 .default_is_stmt = is_stmt,1955 .default_is_stmt = is_stmt,
1924 };1956 };
1925 }1957 }
19261958
1927 pub fn addRow(prog: *LineNumberProgram, gpa: Allocator, table: *CompileUnit.SrcLocCache.LineTable) !void {1959 pub fn addRow(prog: *LineNumberProgram, gpa: Allocator, table: *CompileUnit.SrcLocCache.LineTable) !void {
1928 if (prog.line == 0) return; // garbage data1960 if (prog.line == 0) {
1961 //if (debug_debug_mode) @panic("garbage line data");
1962 return;
1963 }
1964 if (debug_debug_mode) assert(!table.contains(prog.address));
1929 try table.put(gpa, prog.address, .{1965 try table.put(gpa, prog.address, .{
1930 .line = cast(u32, prog.line) orelse maxInt(u32),1966 .line = cast(u32, prog.line) orelse maxInt(u32),
1931 .column = cast(u32, prog.column) orelse maxInt(u32),1967 .column = cast(u32, prog.column) orelse maxInt(u32),
...@@ -1972,12 +2008,12 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {...@@ -1972,12 +2008,12 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
1972/// This function is to make it handy to comment out the return and make it2008/// This function is to make it handy to comment out the return and make it
1973/// into a crash when working on this file.2009/// into a crash when working on this file.
1974pub fn bad() error{InvalidDebugInfo} {2010pub fn bad() error{InvalidDebugInfo} {
1975 //if (true) @panic("bad dwarf"); // can be handy to uncomment when working on this file2011 if (debug_debug_mode) @panic("bad dwarf");
1976 return error.InvalidDebugInfo;2012 return error.InvalidDebugInfo;
1977}2013}
19782014
1979fn missing() error{MissingDebugInfo} {2015fn missing() error{MissingDebugInfo} {
1980 //if (true) @panic("missing dwarf"); // can be handy to uncomment when working on this file2016 if (debug_debug_mode) @panic("missing dwarf");
1981 return error.MissingDebugInfo;2017 return error.MissingDebugInfo;
1982}2018}
19832019