forked from OSchip/llvm-project
Lock-based parallelization for updateDebugInfo
Summary: BOLT spends a decent amount of time creating patches to update debug information when -update-debug-sections is passed. In updateDebugInfo patches are created to update .debug_info and .debug_abbrev sections while .debug_loc and .debug_ranges contents are populated. This this diff uses a lock-based approach to parallelize updateDebugInfo functions and reduces the runtime of the function by around 30%. (cherry picked from FBD16352261)
This commit is contained in:
parent
86800abc81
commit
b50500893d
|
@ -12,6 +12,7 @@
|
|||
#include "DWARFRewriter.h"
|
||||
#include "BinaryContext.h"
|
||||
#include "BinaryFunction.h"
|
||||
#include "ParallelUtilities.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/BinaryFormat/Dwarf.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
|
@ -55,11 +56,18 @@ KeepARanges("keep-aranges",
|
|||
cl::Hidden,
|
||||
cl::cat(BoltCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
DeterministicDebugInfo("deterministic-debuginfo",
|
||||
cl::desc("disables parallel execution of tasks that may produce"
|
||||
"nondeterministic debug info"),
|
||||
cl::init(true),
|
||||
cl::cat(BoltCategory));
|
||||
|
||||
} // namespace opts
|
||||
|
||||
void DWARFRewriter::updateDebugInfo() {
|
||||
SectionPatchers[".debug_abbrev"] = llvm::make_unique<DebugAbbrevPatcher>();
|
||||
SectionPatchers[".debug_info"] = llvm::make_unique<SimpleBinaryPatcher>();
|
||||
SectionPatchers[".debug_info"] = llvm::make_unique<SimpleBinaryPatcher>();
|
||||
|
||||
DebugInfoPatcher =
|
||||
static_cast<SimpleBinaryPatcher *>(SectionPatchers[".debug_info"].get());
|
||||
|
@ -70,9 +78,23 @@ void DWARFRewriter::updateDebugInfo() {
|
|||
RangesSectionsWriter = llvm::make_unique<DebugRangesSectionsWriter>(&BC);
|
||||
LocationListWriter = llvm::make_unique<DebugLocWriter>(&BC);
|
||||
|
||||
for (auto &CU : BC.DwCtx->compile_units()) {
|
||||
updateUnitDebugInfo(CU->getUnitDIE(false),
|
||||
std::vector<const BinaryFunction *>{});
|
||||
auto processUnitDIE = [&](const DWARFDie DIE) {
|
||||
const BinaryFunction *CachedFunction = nullptr;
|
||||
std::map<DebugAddressRangesVector, uint64_t> CachedRanges{};
|
||||
updateUnitDebugInfo(DIE, std::vector<const BinaryFunction *>{},
|
||||
CachedFunction, CachedRanges);
|
||||
};
|
||||
|
||||
if (opts::NoThreads || opts::DeterministicDebugInfo) {
|
||||
for (auto &CU : BC.DwCtx->compile_units())
|
||||
processUnitDIE(CU->getUnitDIE(false));
|
||||
} else {
|
||||
// Update unit debug info in parallel
|
||||
auto &ThreadPool = ParallelUtilities::getThreadPool();
|
||||
for (auto &CU : BC.DwCtx->compile_units())
|
||||
ThreadPool.async(processUnitDIE, CU->getUnitDIE(false));
|
||||
|
||||
ThreadPool.wait();
|
||||
}
|
||||
|
||||
flushPendingRanges();
|
||||
|
@ -83,9 +105,9 @@ void DWARFRewriter::updateDebugInfo() {
|
|||
}
|
||||
|
||||
void DWARFRewriter::updateUnitDebugInfo(
|
||||
const DWARFDie DIE,
|
||||
std::vector<const BinaryFunction *> FunctionStack) {
|
||||
|
||||
const DWARFDie DIE, std::vector<const BinaryFunction *> FunctionStack,
|
||||
const BinaryFunction *&CachedFunction,
|
||||
std::map<DebugAddressRangesVector, uint64_t> &CachedRanges) {
|
||||
bool IsFunctionDef = false;
|
||||
switch (DIE.getTag()) {
|
||||
case dwarf::DW_TAG_compile_unit:
|
||||
|
@ -93,8 +115,8 @@ void DWARFRewriter::updateUnitDebugInfo(
|
|||
const auto ModuleRanges = DIE.getAddressRanges();
|
||||
auto OutputRanges = BC.translateModuleAddressRanges(ModuleRanges);
|
||||
const auto RangesSectionOffset =
|
||||
RangesSectionsWriter->addCURanges(DIE.getDwarfUnit()->getOffset(),
|
||||
std::move(OutputRanges));
|
||||
RangesSectionsWriter->addCURanges(DIE.getDwarfUnit()->getOffset(),
|
||||
std::move(OutputRanges));
|
||||
updateDWARFObjectAddressRanges(DIE, RangesSectionOffset);
|
||||
}
|
||||
break;
|
||||
|
@ -134,11 +156,19 @@ void DWARFRewriter::updateUnitDebugInfo(
|
|||
const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
|
||||
assert(Abbrev && "abbrev expected");
|
||||
|
||||
// Create a critical section.
|
||||
static std::shared_timed_mutex CriticalSectionMutex;
|
||||
std::unique_lock<std::shared_timed_mutex> Lock(CriticalSectionMutex);
|
||||
|
||||
if (FunctionRanges.size() > 1) {
|
||||
convertPending(Abbrev);
|
||||
// Exit critical section early.
|
||||
Lock.unlock();
|
||||
convertToRanges(DIE, FunctionRanges);
|
||||
} else if (ConvertedRangesAbbrevs.find(Abbrev) !=
|
||||
ConvertedRangesAbbrevs.end()) {
|
||||
// Exit critical section early.
|
||||
Lock.unlock();
|
||||
convertToRanges(DIE, FunctionRanges);
|
||||
} else {
|
||||
if (FunctionRanges.empty())
|
||||
|
@ -169,8 +199,8 @@ void DWARFRewriter::updateUnitDebugInfo(
|
|||
<< Twine::utohexstr(DIE.getDwarfUnit()->getOffset()) << '\n';
|
||||
}
|
||||
);
|
||||
RangesSectionOffset =
|
||||
RangesSectionsWriter->addRanges(Function, std::move(OutputRanges));
|
||||
RangesSectionOffset = RangesSectionsWriter->addRanges(
|
||||
Function, std::move(OutputRanges), CachedFunction, CachedRanges);
|
||||
}
|
||||
updateDWARFObjectAddressRanges(DIE, RangesSectionOffset);
|
||||
}
|
||||
|
@ -219,6 +249,7 @@ void DWARFRewriter::updateUnitDebugInfo(
|
|||
}
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
|
||||
DebugInfoPatcher->addLE32Patch(AttrOffset, LocListSectionOffset);
|
||||
} else {
|
||||
assert((Value.isFormClass(DWARFFormValue::FC_Exprloc) ||
|
||||
|
@ -238,6 +269,8 @@ void DWARFRewriter::updateUnitDebugInfo(
|
|||
<< " for DIE with tag " << DIE.getTag()
|
||||
<< " to 0x" << Twine::utohexstr(NewAddress) << '\n');
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
|
||||
DebugInfoPatcher->addLE64Patch(AttrOffset, NewAddress);
|
||||
} else if (opts::Verbosity >= 1) {
|
||||
errs() << "BOLT-WARNING: unexpected form value for attribute at 0x"
|
||||
|
@ -249,7 +282,7 @@ void DWARFRewriter::updateUnitDebugInfo(
|
|||
|
||||
// Recursively update each child.
|
||||
for (auto Child = DIE.getFirstChild(); Child; Child = Child.getSibling()) {
|
||||
updateUnitDebugInfo(Child, FunctionStack);
|
||||
updateUnitDebugInfo(Child, FunctionStack, CachedFunction, CachedRanges);
|
||||
}
|
||||
|
||||
if (IsFunctionDef)
|
||||
|
@ -286,6 +319,8 @@ void DWARFRewriter::updateDWARFObjectAddressRanges(
|
|||
uint32_t AttrOffset = -1U;
|
||||
DIE.find(dwarf::DW_AT_ranges, &AttrOffset);
|
||||
assert(AttrOffset != -1U && "failed to locate DWARF attribute");
|
||||
|
||||
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
|
||||
DebugInfoPatcher->addLE32Patch(AttrOffset, DebugRangesOffset);
|
||||
} else {
|
||||
// Case 2: The object has both DW_AT_low_pc and DW_AT_high_pc emitted back
|
||||
|
@ -578,6 +613,7 @@ void DWARFRewriter::updateGdbIndexSection() {
|
|||
|
||||
void
|
||||
DWARFRewriter::convertToRanges(const DWARFAbbreviationDeclaration *Abbrev) {
|
||||
std::lock_guard<std::mutex> Lock(AbbrevPatcherMutex);
|
||||
AbbrevPatcher->addAttributePatch(Abbrev,
|
||||
dwarf::DW_AT_low_pc,
|
||||
dwarf::DW_AT_ranges,
|
||||
|
@ -684,6 +720,8 @@ void DWARFRewriter::convertToRanges(DWARFDie DIE,
|
|||
} else {
|
||||
llvm_unreachable("unexpected form");
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
|
||||
DebugInfoPatcher->addLE32Patch(LowPCOffset, RangesSectionOffset);
|
||||
DebugInfoPatcher->addUDataPatch(LowPCOffset + 4, 0, LowPCSize);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "DebugData.h"
|
||||
#include "RewriteInstance.h"
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
|
@ -32,7 +33,12 @@ class DWARFRewriter {
|
|||
SectionPatchersType &SectionPatchers;
|
||||
|
||||
SimpleBinaryPatcher *DebugInfoPatcher{nullptr};
|
||||
|
||||
std::mutex DebugInfoPatcherMutex;
|
||||
|
||||
DebugAbbrevPatcher *AbbrevPatcher{nullptr};
|
||||
|
||||
std::mutex AbbrevPatcherMutex;
|
||||
|
||||
/// Stores and serializes information that will be put into the .debug_ranges
|
||||
/// and .debug_aranges DWARF sections.
|
||||
|
@ -43,8 +49,10 @@ class DWARFRewriter {
|
|||
/// Recursively update debug info for all DIEs in \p Unit.
|
||||
/// If \p Function is not empty, it points to a function corresponding
|
||||
/// to a parent DW_TAG_subprogram node of the current \p DIE.
|
||||
void updateUnitDebugInfo(const DWARFDie DIE,
|
||||
std::vector<const BinaryFunction *> FunctionStack);
|
||||
void updateUnitDebugInfo(
|
||||
const DWARFDie DIE, std::vector<const BinaryFunction *> FunctionStack,
|
||||
const BinaryFunction *&CachedFunction,
|
||||
std::map<DebugAddressRangesVector, uint64_t> &CachedRanges);
|
||||
|
||||
/// Patches the binary for an object's address ranges to be updated.
|
||||
/// The object can be a anything that has associated address ranges via either
|
||||
|
@ -68,7 +76,7 @@ class DWARFRewriter {
|
|||
|
||||
/// Abbreviations that were converted to use DW_AT_ranges.
|
||||
std::set<const DWARFAbbreviationDeclaration *> ConvertedRangesAbbrevs;
|
||||
|
||||
|
||||
/// DIEs with abbrevs that were not converted to DW_AT_ranges.
|
||||
/// We only update those when all DIEs have been processed to guarantee that
|
||||
/// the abbrev (which is shared) is intact.
|
||||
|
|
|
@ -65,23 +65,23 @@ DebugRangesSectionsWriter::DebugRangesSectionsWriter(BinaryContext *BC) {
|
|||
SectionOffset += writeAddressRanges(Writer.get(), DebugAddressRangesVector{});
|
||||
}
|
||||
|
||||
uint64_t DebugRangesSectionsWriter::addCURanges(
|
||||
uint64_t CUOffset,
|
||||
DebugAddressRangesVector &&Ranges) {
|
||||
uint64_t
|
||||
DebugRangesSectionsWriter::addCURanges(uint64_t CUOffset,
|
||||
DebugAddressRangesVector &&Ranges) {
|
||||
const auto RangesOffset = addRanges(Ranges);
|
||||
CUAddressRanges.emplace(CUOffset, std::move(Ranges));
|
||||
|
||||
std::lock_guard<std::mutex> Lock(CUAddressRangesMutex);
|
||||
CUAddressRanges.emplace(CUOffset, std::move(Ranges));
|
||||
return RangesOffset;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
DebugRangesSectionsWriter::addRanges(const BinaryFunction *Function,
|
||||
DebugAddressRangesVector &&Ranges) {
|
||||
uint64_t DebugRangesSectionsWriter::addRanges(
|
||||
const BinaryFunction *Function, DebugAddressRangesVector &&Ranges,
|
||||
const BinaryFunction *&CachedFunction,
|
||||
std::map<DebugAddressRangesVector, uint64_t> &CachedRanges) {
|
||||
if (Ranges.empty())
|
||||
return getEmptyRangesOffset();
|
||||
|
||||
static const BinaryFunction *CachedFunction;
|
||||
|
||||
if (Function == CachedFunction) {
|
||||
const auto RI = CachedRanges.find(Ranges);
|
||||
if (RI != CachedRanges.end())
|
||||
|
@ -102,6 +102,9 @@ DebugRangesSectionsWriter::addRanges(const DebugAddressRangesVector &Ranges) {
|
|||
if (Ranges.empty())
|
||||
return getEmptyRangesOffset();
|
||||
|
||||
// Reading the SectionOffset and updating it should be atomic to guarantee
|
||||
// unique and correct offsets in patches.
|
||||
std::lock_guard<std::mutex> Lock(WriterMutex);
|
||||
const auto EntryOffset = SectionOffset;
|
||||
SectionOffset += writeAddressRanges(Writer.get(), Ranges);
|
||||
|
||||
|
@ -165,14 +168,17 @@ uint64_t DebugLocWriter::addList(const DWARFDebugLoc::LocationList &LocList) {
|
|||
if (LocList.Entries.empty())
|
||||
return getEmptyListOffset();
|
||||
|
||||
// Reading the SectionOffset and updating it should be atomic to guarantee
|
||||
// unique and correct offsets in patches.
|
||||
std::lock_guard<std::mutex> Lock(WriterMutex);
|
||||
const auto EntryOffset = SectionOffset;
|
||||
|
||||
for (const auto &Entry : LocList.Entries) {
|
||||
Writer->writeLE64(Entry.Begin);
|
||||
Writer->writeLE64(Entry.End);
|
||||
Writer->writeLE16(Entry.Loc.size());
|
||||
Writer->writeBytes(
|
||||
StringRef(reinterpret_cast<const char *>(Entry.Loc.data()),
|
||||
Entry.Loc.size()));
|
||||
Writer->writeBytes(StringRef(
|
||||
reinterpret_cast<const char *>(Entry.Loc.data()), Entry.Loc.size()));
|
||||
SectionOffset += 2 * 8 + 2 + Entry.Loc.size();
|
||||
}
|
||||
Writer->writeLE64(0);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "llvm/Support/SMLoc.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
@ -102,8 +103,10 @@ public:
|
|||
uint64_t addCURanges(uint64_t CUOffset, DebugAddressRangesVector &&Ranges);
|
||||
|
||||
/// Add ranges with caching for \p Function.
|
||||
uint64_t addRanges(const BinaryFunction *Function,
|
||||
DebugAddressRangesVector &&Ranges);
|
||||
uint64_t
|
||||
addRanges(const BinaryFunction *Function, DebugAddressRangesVector &&Ranges,
|
||||
const BinaryFunction *&CachedFunction,
|
||||
std::map<DebugAddressRangesVector, uint64_t> &CachedRanges);
|
||||
|
||||
/// Add ranges and return offset into section.
|
||||
uint64_t addRanges(const DebugAddressRangesVector &Ranges);
|
||||
|
@ -139,6 +142,8 @@ private:
|
|||
|
||||
std::unique_ptr<MCObjectWriter> Writer;
|
||||
|
||||
std::mutex WriterMutex;
|
||||
|
||||
/// Current offset in the section (updated as new entries are written).
|
||||
/// Starts with 16 since the first 16 bytes are reserved for an empty range.
|
||||
uint32_t SectionOffset{0};
|
||||
|
@ -148,11 +153,10 @@ private:
|
|||
/// (first address, interval size).
|
||||
CUAddressRangesType CUAddressRanges;
|
||||
|
||||
std::mutex CUAddressRangesMutex;
|
||||
|
||||
/// Offset of an empty address ranges list.
|
||||
static constexpr uint64_t EmptyRangesOffset{0};
|
||||
|
||||
/// Cached used for de-duplicating entries for the same function.
|
||||
std::map<DebugAddressRangesVector, uint64_t> CachedRanges;
|
||||
};
|
||||
|
||||
/// Serializes the .debug_loc DWARF section with LocationLists.
|
||||
|
@ -175,6 +179,8 @@ private:
|
|||
|
||||
std::unique_ptr<MCObjectWriter> Writer;
|
||||
|
||||
std::mutex WriterMutex;
|
||||
|
||||
/// Offset of an empty location list.
|
||||
static uint64_t const EmptyListOffset = 0;
|
||||
|
||||
|
|
|
@ -1112,8 +1112,12 @@ void RewriteInstance::run() {
|
|||
executeRewritePass(LargeFunctions, false);
|
||||
}
|
||||
|
||||
if (opts::UpdateDebugSections)
|
||||
DebugInfoRewriter->updateDebugInfo();
|
||||
{
|
||||
NamedRegionTimer T("updateDebugInfo", "update debug info", TimerGroupName,
|
||||
TimerGroupDesc, opts::TimeRewrite);
|
||||
if (opts::UpdateDebugSections)
|
||||
DebugInfoRewriter->updateDebugInfo();
|
||||
}
|
||||
|
||||
addBoltInfoSection();
|
||||
|
||||
|
|
|
@ -295,8 +295,8 @@ public:
|
|||
static bool isDebugSection(StringRef SectionName);
|
||||
|
||||
using SectionPatchersType =
|
||||
std::map<std::string, std::unique_ptr<BinaryPatcher>>;
|
||||
|
||||
std::map<std::string, std::unique_ptr<BinaryPatcher>>;
|
||||
|
||||
private:
|
||||
/// Get the contents of the LSDA section for this binary.
|
||||
ArrayRef<uint8_t> getLSDAData();
|
||||
|
|
Loading…
Reference in New Issue