[BOLT] Refactor reading of debug line info

Summary:
Match BinaryFunction to a DWARFUnit based on the unit's address ranges
skipping the parsing of DIEs.

(cherry picked from FBD24269325)
This commit is contained in:
Maksim Panchenko 2020-10-12 21:04:42 -07:00
parent 9f15b9f3c2
commit 53bd88c7fe
5 changed files with 65 additions and 197 deletions

View File

@ -1263,40 +1263,6 @@ void BinaryContext::printGlobalSymbols(raw_ostream& OS) const {
}
}
namespace {
/// Recursively finds DWARF DW_TAG_subprogram DIEs and match them with
/// BinaryFunctions.
void findSubprograms(const DWARFDie DIE,
std::map<uint64_t, BinaryFunction> &BinaryFunctions) {
if (DIE.isSubprogramDIE()) {
uint64_t LowPC, HighPC, SectionIndex;
if (DIE.getLowAndHighPC(LowPC, HighPC, SectionIndex)) {
auto It = BinaryFunctions.find(LowPC);
if (It != BinaryFunctions.end()) {
It->second.addSubprogramDIE(DIE);
} else {
// The function must have been optimized away by GC.
}
} else {
const auto RangesVector = DIE.getAddressRanges();
for (const auto Range : DIE.getAddressRanges()) {
auto It = BinaryFunctions.find(Range.LowPC);
if (It != BinaryFunctions.end()) {
It->second.addSubprogramDIE(DIE);
}
}
}
}
for (auto ChildDIE = DIE.getFirstChild(); ChildDIE && !ChildDIE.isNULL();
ChildDIE = ChildDIE.getSibling()) {
findSubprograms(ChildDIE, BinaryFunctions);
}
}
} // namespace
unsigned BinaryContext::addDebugFilenameToUnit(const uint32_t DestCUID,
const uint32_t SrcCUID,
unsigned FileIndex) {
@ -1356,10 +1322,46 @@ std::vector<BinaryFunction *> BinaryContext::getAllBinaryFunctions() {
}
void BinaryContext::preprocessDebugInfo() {
// Populate MCContext with DWARF files.
struct CURange {
uint64_t LowPC;
uint64_t HighPC;
DWARFUnit *Unit;
bool operator<(const CURange &Other) const {
return LowPC < Other.LowPC;
}
};
// Building a map of address ranges to CUs similar to .debug_aranges and use
// it to assign CU to functions.
std::vector<CURange> AllRanges;
for (const auto &CU : DwCtx->compile_units()) {
const auto CUID = CU->getOffset();
auto *LineTable = DwCtx->getLineTableForUnit(CU.get());
for (auto &Range : CU->getUnitDIE().getAddressRanges()) {
// Parts of the debug info could be invalidated due to corresponding code
// being removed from the binary by the linker. Hence we check if the
// address is a valid one.
if (containsAddress(Range.LowPC))
AllRanges.emplace_back(CURange{Range.LowPC, Range.HighPC, CU.get()});
}
}
std::sort(AllRanges.begin(), AllRanges.end());
for (auto &KV : BinaryFunctions) {
const uint64_t FunctionAddress = KV.first;
BinaryFunction &Function = KV.second;
auto It = std::partition_point(AllRanges.begin(), AllRanges.end(),
[=](CURange R) { return R.HighPC <= FunctionAddress; });
if (It != AllRanges.end() && It->LowPC <= FunctionAddress) {
Function.setDWARFUnit(It->Unit);
}
}
// Populate MCContext with DWARF files from all units.
for (const auto &CU : DwCtx->compile_units()) {
const uint32_t CUID = CU->getOffset();
const DWARFDebugLine::LineTable *LineTable =
DwCtx->getLineTableForUnit(CU.get());
const auto &FileNames = LineTable->Prologue.FileNames;
// Make sure empty debug line tables are registered too.
if (FileNames.empty()) {
@ -1382,99 +1384,6 @@ void BinaryContext::preprocessDebugInfo() {
cantFail(Ctx->getDwarfFile(Dir, FileName, 0, nullptr, None, CUID));
}
}
// For each CU, iterate over its children DIEs and match subprogram DIEs to
// BinaryFunctions.
// Run findSubprograms on a range of compilation units
auto processBlock = [&](auto BlockBegin, auto BlockEnd) {
for (auto It = BlockBegin; It != BlockEnd; ++It) {
findSubprograms((*It)->getUnitDIE(false), BinaryFunctions);
}
};
if (opts::NoThreads) {
processBlock(DwCtx->compile_units().begin(), DwCtx->compile_units().end());
} else {
auto &ThreadPool = ParallelUtilities::getThreadPool();
// Divide compilation units uniformally into tasks.
unsigned BlockCost =
DwCtx->getNumCompileUnits() / (opts::TaskCount * opts::ThreadCount);
if (BlockCost == 0)
BlockCost = 1;
auto BlockBegin = DwCtx->compile_units().begin();
unsigned CurrentCost = 0;
for (auto It = DwCtx->compile_units().begin();
It != DwCtx->compile_units().end(); It++) {
CurrentCost++;
if (CurrentCost >= BlockCost) {
ThreadPool.async(processBlock, BlockBegin, std::next(It));
BlockBegin = std::next(It);
CurrentCost = 0;
}
}
ThreadPool.async(processBlock, BlockBegin, DwCtx->compile_units().end());
ThreadPool.wait();
}
for (auto &KV : BinaryFunctions) {
const auto FunctionAddress = KV.first;
auto &Function = KV.second;
// Sort associated CUs for deterministic update.
std::sort(Function.getSubprogramDIEs().begin(),
Function.getSubprogramDIEs().end(),
[](const DWARFDie &A, const DWARFDie &B) {
return A.getDwarfUnit()->getOffset() <
B.getDwarfUnit()->getOffset();
});
// Some functions may not have a corresponding subprogram DIE
// yet they will be included in some CU and will have line number
// information. Hence we need to associate them with the CU and include
// in CU ranges.
if (Function.getSubprogramDIEs().empty()) {
if (auto DebugAranges = DwCtx->getDebugAranges()) {
auto CUOffset = DebugAranges->findAddress(FunctionAddress);
if (CUOffset != -1U) {
Function.addSubprogramDIE(
DWARFDie(DwCtx->getCompileUnitForOffset(CUOffset), nullptr));
}
}
}
#ifdef DWARF_LOOKUP_ALL_RANGES
if (Function.getSubprogramDIEs().empty()) {
// Last resort - iterate over all compile units. This should not happen
// very often. If it does, we need to create a separate lookup table
// similar to .debug_aranges internally. This slows down processing
// considerably.
for (const auto &CU : DwCtx->compile_units()) {
const auto *CUDie = CU->getUnitDIE();
for (const auto &Range : CUDie->getAddressRanges(CU.get())) {
if (FunctionAddress >= Range.first &&
FunctionAddress < Range.second) {
Function.addSubprogramDIE(DWARFDie(CU.get(), nullptr));
break;
}
}
}
}
#endif
// Set line table for function to the first CU with such table.
for (const auto &DIE : Function.getSubprogramDIEs()) {
if (const auto *LineTable =
DwCtx->getLineTableForUnit(DIE.getDwarfUnit())) {
Function.setDWARFUnitLineTable(DIE.getDwarfUnit(), LineTable);
break;
}
}
}
}
bool BinaryContext::shouldEmit(const BinaryFunction &Function) const {
@ -1591,7 +1500,7 @@ void BinaryContext::printInstruction(raw_ostream &OS,
MIB->printAnnotations(Instruction, OS);
const DWARFDebugLine::LineTable *LineTable =
Function && opts::PrintDebugInfo ? Function->getDWARFUnitLineTable().second
Function && opts::PrintDebugInfo ? Function->getDWARFLineTable()
: nullptr;
if (LineTable) {

View File

@ -58,8 +58,6 @@
namespace llvm {
class DWARFDebugInfoEntryMinimal;
using namespace object;
namespace bolt {

View File

@ -446,8 +446,7 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, bool EmitColdPart,
Streamer.EmitNeverAlignCodeAtEnd(/*Alignment to avoid=*/64);
}
if (!EmitCodeOnly && opts::UpdateDebugSections &&
BF.getDWARFUnitLineTable().first) {
if (!EmitCodeOnly && opts::UpdateDebugSections && BF.getDWARFUnit()) {
LastLocSeen = emitLineInfo(BF, Instr.getLoc(), LastLocSeen, FirstInstr);
FirstInstr = false;
}
@ -604,11 +603,11 @@ void BinaryEmitter::emitConstantIslands(BinaryFunction &BF, bool EmitColdPart,
SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
SMLoc PrevLoc, bool FirstInstr) {
auto *FunctionCU = BF.getDWARFUnitLineTable().first;
const auto *FunctionLineTable = BF.getDWARFUnitLineTable().second;
DWARFUnit *FunctionCU = BF.getDWARFUnit();
const DWARFDebugLine::LineTable *FunctionLineTable = BF.getDWARFLineTable();
assert(FunctionCU && "cannot emit line info for function without CU");
auto RowReference = DebugLineTableRowRef::fromSMLoc(NewLoc);
DebugLineTableRowRef RowReference = DebugLineTableRowRef::fromSMLoc(NewLoc);
// Check if no new line info needs to be emitted.
if (RowReference == DebugLineTableRowRef::NULL_ROW ||
@ -616,7 +615,7 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
return PrevLoc;
unsigned CurrentFilenum = 0;
const auto *CurrentLineTable = FunctionLineTable;
const DWARFDebugLine::LineTable *CurrentLineTable = FunctionLineTable;
// If the CU id from the current instruction location does not
// match the CU id from the current function, it means that we
@ -919,9 +918,8 @@ void BinaryEmitter::emitDebugLineInfoForOriginalFunctions() {
if (Function.isEmitted())
continue;
auto ULT = Function.getDWARFUnitLineTable();
auto Unit = ULT.first;
auto LineTable = ULT.second;
DWARFUnit *Unit = Function.getDWARFUnit();
const DWARFDebugLine::LineTable *LineTable = Function.getDWARFLineTable();
if (!LineTable)
continue; // nothing to update for this function

View File

@ -179,9 +179,8 @@ bool emptyRange(const R &Range) {
/// to point to this information, which is represented by a
/// DebugLineTableRowRef. The returned pointer is null if no debug line
/// information for this instruction was found.
SMLoc findDebugLineInformationForInstructionAt(
uint64_t Address,
DWARFUnitLineTable &ULT) {
SMLoc findDebugLineInformationForInstructionAt(uint64_t Address,
DWARFUnit *Unit, const DWARFDebugLine::LineTable *LineTable) {
// We use the pointer in SMLoc to store an instance of DebugLineTableRowRef,
// which occupies 64 bits. Thus, we can only proceed if the struct fits into
// the pointer itself.
@ -190,11 +189,6 @@ SMLoc findDebugLineInformationForInstructionAt(
"Cannot fit instruction debug line information into SMLoc's pointer");
SMLoc NullResult = DebugLineTableRowRef::NULL_ROW.toSMLoc();
auto &LineTable = ULT.second;
if (!LineTable)
return NullResult;
uint32_t RowIndex = LineTable->lookupAddress(Address);
if (RowIndex == LineTable->UnknownRowIndex)
return NullResult;
@ -206,7 +200,7 @@ SMLoc findDebugLineInformationForInstructionAt(
DebugLineTableRowRef *InstructionLocation =
reinterpret_cast<DebugLineTableRowRef *>(&Ptr);
InstructionLocation->DwCompileUnitIndex = ULT.first->getOffset();
InstructionLocation->DwCompileUnitIndex = Unit->getOffset();
InstructionLocation->RowIndex = RowIndex + 1;
return SMLoc::getFromPointer(Ptr);
@ -968,8 +962,6 @@ bool BinaryFunction::disassemble() {
auto &Ctx = BC.Ctx;
auto &MIB = BC.MIB;
DWARFUnitLineTable ULT = getDWARFUnitLineTable();
// Insert a label at the beginning of the function. This will be our first
// basic block.
Labels[0] = Ctx->createTempSymbol("BB0", false);
@ -1355,9 +1347,11 @@ bool BinaryFunction::disassemble() {
}
add_instruction:
if (ULT.first && ULT.second) {
if (getDWARFLineTable()) {
Instruction.setLoc(
findDebugLineInformationForInstructionAt(AbsoluteInstrAddr, ULT));
findDebugLineInformationForInstructionAt(AbsoluteInstrAddr,
getDWARFUnit(),
getDWARFLineTable()));
}
// Record offset of the instruction for profile matching.

View File

@ -53,9 +53,6 @@ class DWARFDebugInfoEntryMinimal;
namespace bolt {
using DWARFUnitLineTable = std::pair<DWARFUnit *,
const DWARFDebugLine::LineTable *>;
/// Types of macro-fusion alignment corrections.
enum MacroFusionType {
MFT_NONE,
@ -364,15 +361,8 @@ private:
/// Original LSDA address for the function.
uint64_t LSDAAddress{0};
/// Associated DIEs in the .debug_info section with their respective CUs.
/// There can be multiple because of identical code folding.
std::vector<DWARFDie> SubprogramDIEs;
/// Line table for the function with containing compilation unit.
/// Because of identical code folding the function could have multiple
/// associated compilation units. The first of them with line number info
/// is referenced by UnitLineTable.
DWARFUnitLineTable UnitLineTable{nullptr, nullptr};
/// Containing compilation unit for the function.
DWARFUnit *DwarfUnit{nullptr};
/// Last computed hash value. Note that the value could be recomputed using
/// different parameters by every pass.
@ -1424,11 +1414,6 @@ public:
return HasEHRanges;
}
/// Return true if the function has associated debug info.
bool hasDebugInfo() const {
return !SubprogramDIEs.empty();
}
/// Return true if the function uses DW_CFA_GNU_args_size CFIs.
bool usesGnuArgsSize() const {
return UsesGnuArgsSize;
@ -2368,35 +2353,19 @@ public:
OperandHashFuncTy OperandHashFunc =
[](const MCOperand&) { return std::string(); }) const;
/// Sets the associated .debug_info entry.
void addSubprogramDIE(const DWARFDie DIE) {
static std::mutex CriticalSectionMutex;
std::lock_guard<std::mutex> Lock(CriticalSectionMutex);
SubprogramDIEs.emplace_back(DIE);
void setDWARFUnit(DWARFUnit *Unit) {
DwarfUnit = Unit;
}
void setDWARFUnitLineTable(DWARFUnit *Unit,
const DWARFDebugLine::LineTable *Table) {
UnitLineTable = std::make_pair(Unit, Table);
/// Return DWARF compile unit for this function.
DWARFUnit *getDWARFUnit() const {
return DwarfUnit;
}
/// Return all compilation units with entry for this function.
/// Because of identical code folding there could be multiple of these.
decltype(SubprogramDIEs) &getSubprogramDIEs() {
return SubprogramDIEs;
}
const decltype(SubprogramDIEs) &getSubprogramDIEs() const {
return SubprogramDIEs;
}
/// Return DWARF compile unit with line info for this function.
DWARFUnitLineTable &getDWARFUnitLineTable() {
return UnitLineTable;
}
const DWARFUnitLineTable &getDWARFUnitLineTable() const {
return UnitLineTable;
/// Return line info table for this function.
const DWARFDebugLine::LineTable *getDWARFLineTable() const {
return getDWARFUnit() ? BC.DwCtx->getLineTableForUnit(getDWARFUnit())
: nullptr;
}
/// Finalize profile for the function.