diff options
author | Chandler Carruth <chandlerc@gmail.com> | 2017-12-22 06:41:23 +0000 |
---|---|---|
committer | Chandler Carruth <chandlerc@gmail.com> | 2017-12-22 06:41:23 +0000 |
commit | 45383c8c8551c1fed32d006c9528a64293e1f936 (patch) | |
tree | 511f46a4c35009714da1c2549dd089fbe45d0abc /include | |
parent | 30ada0c3f3188ddd5ac70ff28519ee7f81b33342 (diff) |
Rewrite the cached map used for locating the most precise DIE among
inlined subroutines for a given address.
This is essentially the hot path of llvm-symbolizer when extracting
inlined frames during symbolization. Previously, we would read every
subprogram and every inlined subroutine, building a std::map across the
entire PC space to the best DIE, and then do only a handful of queries
as we symbolized a backtrace. A huge fraction of the time was spent
building the map itself.
This patch changes it two a two-level system. First, we just build a map
from PC-interval to DWARF subprograms. These are required to be disjoint
and so constructing this is pretty easy. Second, we build a map *just*
for the inlined subroutines within the subprogram containing the query
address. This allows us to look at far fewer DIEs and build a *much*
smaller set of cached maps in the llvm-symbolizer case where only a few
address get symbolized during the entire run.
It also builds both interval maps in a very different way. It constructs
a single flat vector of pairs that maps from offset -> index. The
indices point into collections of DIE objects, but can also be
"tombstones" (-1) to mark gaps. In the case of subprograms, this mostly
just simplifies the data structure a bit. For inlined subroutines,
because we carefully split them as we build the map, we end up in many
cases having no holes and not having to store both start and stop
offsets.
Finally, the PC ranges for the inlined subroutines are compressed into
32-bits by making them relative to the base PC of the outer subprogram.
This means that if you have a single function body with over 2gb of
executable code in it, we will stop mapping address past the first 2gb
of that function into inlined subroutines and just give you the
subprogram. This doesn't seem like a problem. ;]
All of this combines to make llvm-symbolizer *well* over 2x faster for
symbolizing backtraces out of LLVM's unittests. Death-test heavy unit
tests are running >2x faster. I'm still going to look at completely
disabling symbolization there, but figured while I had a good benchmark
we should make symbolization a bit better.
Sadly, the logic to build the flat interval map for the inlined
subroutines is fairly complex. I'm not super happy about this and
welcome any simplifying suggestions.
Huge thanks to Dave Blaikie who helped walk me through what the various
things I needed to do in DWARF to make this work.
Differential Revision: https://reviews.llvm.org/D40987
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@321345 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'include')
-rw-r--r-- | include/llvm/DebugInfo/DWARF/DWARFUnit.h | 44 |
1 files changed, 37 insertions, 7 deletions
diff --git a/include/llvm/DebugInfo/DWARF/DWARFUnit.h b/include/llvm/DebugInfo/DWARF/DWARFUnit.h index 09356ade8ef0..3cec58383f87 100644 --- a/include/llvm/DebugInfo/DWARF/DWARFUnit.h +++ b/include/llvm/DebugInfo/DWARF/DWARFUnit.h @@ -220,10 +220,40 @@ class DWARFUnit { /// The compile unit debug information entry items. std::vector<DWARFDebugInfoEntry> DieArray; - /// Map from range's start address to end address and corresponding DIE. - /// IntervalMap does not support range removal, as a result, we use the - /// std::map::upper_bound for address range lookup. - std::map<uint64_t, std::pair<uint64_t, DWARFDie>> AddrDieMap; + /// The vector of inlined subroutine DIEs that we can map directly to from + /// their subprogram below. + std::vector<DWARFDie> InlinedSubroutineDIEs; + + /// A type representing a subprogram DIE and a map (built using a sorted + /// vector) into that subprogram's inlined subroutine DIEs. + struct SubprogramDIEAddrInfo { + DWARFDie SubprogramDIE; + + uint64_t SubprogramBasePC; + + /// A vector sorted to allow mapping from a relative PC to the inlined + /// subroutine DIE with the most specific address range covering that PC. + /// + /// The PCs are relative to the `SubprogramBasePC`. + /// + /// The vector is sorted in ascending order of the first int which + /// represents the relative PC for an interval in the map. The second int + /// represents the index into the `InlinedSubroutineDIEs` vector of the DIE + /// that interval maps to. An index of '-1` indicates an empty mapping. The + /// interval covered is from the `.first` relative PC to the next entry's + /// `.first` relative PC. + std::vector<std::pair<uint32_t, int32_t>> InlinedSubroutineDIEAddrMap; + }; + + /// Vector of the subprogram DIEs and their subroutine address maps. + std::vector<SubprogramDIEAddrInfo> SubprogramDIEAddrInfos; + + /// A vector sorted to allow mapping from a PC to the subprogram DIE (and + /// associated addr map) index. Subprograms with overlapping PC ranges aren't + /// supported here. Nothing will crash, but the mapping may be inaccurate. + /// This vector may also contain "empty" ranges marked by an address with + /// a DIE index of '-1'. + std::vector<std::pair<uint64_t, int64_t>> SubprogramDIEAddrMap; using die_iterator_range = iterator_range<std::vector<DWARFDebugInfoEntry>::iterator>; @@ -282,9 +312,6 @@ public: AddrOffsetSectionBase = Base; } - /// Recursively update address to Die map. - void updateAddressDieMap(DWARFDie Die); - void setRangesSection(const DWARFSection *RS, uint32_t Base) { RangeSection = RS; RangeSectionBase = Base; @@ -480,6 +507,9 @@ private: /// parseDWO - Parses .dwo file for current compile unit. Returns true if /// it was actually constructed. bool parseDWO(); + + void buildSubprogramDIEAddrMap(); + void buildInlinedSubroutineDIEAddrMap(SubprogramDIEAddrInfo &SPInfo); }; } // end namespace llvm |