1 #include "hphp/tools/debug-parser/dwarf-context-manager.h"
6 #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h"
7 #include "llvm/DebugInfo/DWARF/DWARFUnit.h"
8 #include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h"
10 #include "hphp/util/trace.h"
12 namespace debug_parser
{
18 DWARFContextManager::DWARFContextManager(std::string filename
) {
19 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>> dwarfBufferErrorOr
=
20 llvm::MemoryBuffer::getFileOrSTDIN(filename
);
22 if (dwarfBufferErrorOr
.getError()) {
23 auto const msg
= dwarfBufferErrorOr
.getError().message();
24 llvm::errs() << fmt::format("Error reading file ({}): {}\n", filename
,
29 buffer_
= std::move(dwarfBufferErrorOr
.get());
30 llvm::Expected
<std::unique_ptr
<llvm::object::ObjectFile
>> objFile
=
31 llvm::object::ObjectFile::createObjectFile(buffer_
->getMemBufferRef());
34 llvm::errs() << fmt::format("Failed to create object file:{}\n",
35 toString(objFile
.takeError()));
40 objFile_
= std::move(objFile
.get());
42 // LLVM has a threadsafe dwarf context but we only modify the context on the
43 // main thread and then access individual units in the worker threads.
44 dwarfContext_
= llvm::DWARFContext::create(*objFile_
);
46 // TODO: see if we can use the main dwarf context instead of handling .dwp in
48 if (std::filesystem::exists(filename
+ ".dwp")) {
49 dwoContext_
= dwarfContext_
->getDWOContext(filename
+ ".dwp");
52 // Load units into supplementary datastructures
56 void DWARFContextManager::loadUnits() {
57 // Only create the type signature index if the TU index doesn't already exist
58 const auto createIndex
= !(dwoContext_
&& dwoContext_
->getTUIndex());
60 const auto processUnit
= [&](const std::unique_ptr
<llvm::DWARFUnit
>& dwarfUnit
, bool isInfo
) {
61 // Pre-load type units to speed up lookups
62 if (dwarfUnit
->isTypeUnit() || dwoContext_
== nullptr) {
63 dwarfUnit
->getNonSkeletonUnitDIE(false);
66 // Store type unit signatures in a lookup table when TU index doesn't exist
67 if (dwarfUnit
->isTypeUnit() && createIndex
) {
68 const uint64_t typeSignature
= cast_or_null
<llvm::DWARFTypeUnit
>(
69 dwarfUnit
.get())->getTypeHash();
70 const uint64_t typeOffset
= cast_or_null
<llvm::DWARFTypeUnit
>(
71 dwarfUnit
.get())->getTypeOffset();
74 getGlobalOffset(dwarfUnit
->getOffset() + typeOffset
, isInfo
)
78 (isInfo
? infoUnits_
: typeUnits_
).push_back(dwarfUnit
.get());
83 forEachSectionUnit(processUnit
, true);
84 forEachSectionUnit(processUnit
, false);
87 llvm::DWARFUnit
* DWARFContextManager::findUnitForGlobalOffset(GlobalOff globalOff
) const {
88 auto& units
= globalOff
.isInfo() ? infoUnits_
: typeUnits_
;
90 auto it
= std::upper_bound(units
.begin(), units
.end(), globalOff
.offset(),
91 [](uint64_t lhs
, const llvm::DWARFUnit
* rhs
) {
92 return lhs
< rhs
->getNextUnitOffset();
96 // We always expect to find the unit for a given offset
97 always_assert(it
!= units
.end() && (*it
)->getOffset() <= globalOff
.offset());
101 DieContext
DWARFContextManager::getDieContextAtGlobalOffset(GlobalOff globalOff
) const {
102 auto dwarfUnit
= findUnitForGlobalOffset(globalOff
);
105 .die
= dwarfUnit
->getDIEForOffset(globalOff
.offset()),
106 .isInfo
= globalOff
.isInfo()
110 // Specifically do not resolve DW_AT_specification or DW_AT_abstract_origin to
111 // maintain backwards compatibility with old dwarf parser.
112 std::string
DWARFContextManager::getDIEName(llvm::DWARFDie die
) const {
113 for (const auto& attr
: die
.attributes()) {
114 if (attr
.Attr
== llvm::dwarf::DW_AT_name
) {
115 if (auto val
= llvm::dwarf::toString(attr
.Value
)) {
124 GlobalOff
DWARFContextManager::getGlobalOffset(uint64_t offset
, bool isInfo
) const {
125 // Treat all offsets as info units, this can be cleaned up as we only need
126 // offsets in one file at a time (main binary _or_ dwp)
127 const auto hasDwp
= dwoContext_
!= nullptr;
128 return GlobalOff
{offset
, isInfo
, hasDwp
};
131 GlobalOff
DWARFContextManager::getTypeUnitOffset(uint64_t sig8
) const {
132 // Use the TU index to retrieve type information, if it exists
133 if (dwoContext_
&& dwoContext_
->getTUIndex()) {
134 llvm::DWARFTypeUnit
* tu
= dwoContext_
->getTypeUnitForHash(5, sig8
, true);
136 return getGlobalOffset(tu
->getOffset() + tu
->getTypeOffset(), true);
140 // Otherwise, it will have been populated when units were loaded
141 always_assert(sig8Map_
.contains(sig8
));
142 return sig8Map_
.at(sig8
);