forked from OSchip/llvm-project
[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:
parent
9f15b9f3c2
commit
53bd88c7fe
|
@ -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) {
|
||||
|
|
|
@ -58,8 +58,6 @@
|
|||
|
||||
namespace llvm {
|
||||
|
||||
class DWARFDebugInfoEntryMinimal;
|
||||
|
||||
using namespace object;
|
||||
|
||||
namespace bolt {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue