Debuginfo and linker folks, we (AccessSoftek) would like to suggest a proposal for removing obsolete debug info. If you find it useful we will be happy to work on improving it. Thank you for any opinions and suggestions.
Alexey.
Currently when the linker does garbage collection a lot of abandoned debug info is left behind (see Appendix A for documentation). Besides inflated debug info size, we ended up with overlapping address ranges and no way to say valid vs garbage ranges. We propose removing debug info along with removing code. This would reduce debug info size and make sure debug info accuracy.
There are several approaches which could be used to solve that problem:
-
Require dwarf producers to generate fragmented debug data according to DWARF5 specification: “E.3.3 Single-function-per-DWARF-compilation-unit” page 388. That approach assumes fragmenting the whole debug info per function basis and glue fragmented sections at the link time using section groups.
-
Use an additional tool, which would optimize out unnecessary debug data, something similar to dwz (dwarf compressor tool), dsymutil (links the DWARF debug information). This approach assumes additional post-link binaries processing.
-
Teach the linker to parse debug data and let it remove unused debug data.
In this proposal, we focus on approach #3. We show that this approach is viable and discuss some preliminary results, leaving particular implementation out of the scope. We attach the Proof of Concept (PoC) implementation(https://reviews.llvm.org/D67469) for illustrative purposes. Please keep in mind that it is not final, and there is room for improvements (see Appendix B). However, the achieved results look quite promising and demonstrate up to 2 times size reduction and performance overhead is 30% of linking time (which is in the same ballpark as the already done section compressing (see table 2 point F)).
A straightforward implementation would fully parse DWARF, create an in-memory hierarchy of DWARF objects, optimize them, and generate new sections content. Thus, it would require too much memory and would take noticeable time to process. Instead, the proposed solution is a combination of “fragmented DWARF” (#1 above) and “Optimise parsed DWARF at link stage” (#3 above). However, there is no preliminary DWARF data fragmentation step. Instead, the data is parsed at the link time, and then pieces, that correspond to live debug data, are copied into resulting sections. Essentially, the patch skips the debug info (subprograms, address ranges, line sentences) that corresponds to the dead sections.
Two command-line options are added to lld:
-
–gc-debuginfo removes pieces of debug information related to the discarded sections.
-
–gc-debuginfo-types does alternative type deduplication while doing --gc-debuginfo.
For the purpose of simplicity, some shortcuts were used in this PoC implementation:
- Same types always use the same abbreviations. Full implementation should take different abbreviations into account.
- Split DWARF is not supported.
- Only .debug_abbrev, .debug_info, .debug_ranges, .debug_rnglists, .debug_lines tables are processed.
- DWARF64 is not supported.
We also note that the proposed approach is quite universal and could be used for other debug info optimization tasks. F.e. there exists an alternative solution for data types deduplication (other than using COMDAT sections to keep types (-fdebug-types-section)): parse DWARF, cut out duplicated types, patch type references to point to the single type definition. I.e., it uses the same approach as used for deleting unused debug info - cut out unneeded debug section content. This alternative implementation should not necessarily replace -fdebug-types-section, but it shows that this approach could be used for the type deduplication as well. This solution (combined with the global type table, which is not implemented by this patch) has some advantages though. It could reduce the number of references inside .debug_info section. It could reduce the size of the type information by deduplicating base and DW_FORM_ref_sig8 types.
There are several things which would have been approved by the DWARF standard will help this implementation to work better:
-
Minimize or entirely avoid references from subprograms into other parts of .debug_info section. That would simplify splitting and removing subprograms out in that sense that it would minimize the number of references that should be parsed and followed. (DW_FORM_ref_subroutine instead of DW_FORM_ref_*, ?)
-
Create additional section - global types table (.debug_types_table). That would significantly reduce the number of references inside .debug_info section. It also makes it possible to have a 4-byte reference in this section instead of 8-bytes reference into type unit (DW_FORM_ref_types instead of DW_FORM_ref_sig8). It also makes it possible to place base types into this section and avoid per-compile unit duplication of them. Additionally, there could be achieved size reduction by not generating type unit header. Note, that new section - .debug_types_table - differs from DWARF4 section .debug_types in that sense that: it contains unique type descriptors referenced by offsets instead of list of type units referenced by DW_FORM_ref_sig8; all table entries share the same abbreviations and do not have type unit headers.
-
Define the limited scope for line programs which could be removed independently. I.e. currently .debug_line section contains a program in byte-coded language for a state machine. That program actually represents a matrix [instruction][line information]. In general, it is hard to cut out part of that program and to keep the whole program correct. Thus it would be good to specify separate scopes (related to address ranges) which could be easily removed from the program body.
We evaluated the approach on LLVM and Clang codebases. The results obtained are summarized in the tables below:
Abbreviations:
LLVM bin size - size of llvm build/bin directory.
LLVM build time - compilation time for building llvm.
Clang size - size of clang binary.
link time - time for linking clang binary.
Errors - number of errors reported by llvm-dwarfdump --verify for clang binary.
gc-dbginfo - linker option added by this patch. Spelled as “-gc-debuginfo”.
gc-dbgtypes - linker option added by this patch. Spelled as “-gc-debuginfo-types”
Table 1. LLVM codebase.