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:
laith sakka 2019-07-17 14:58:17 -07:00 committed by Maksim Panchenko
parent 86800abc81
commit b50500893d
6 changed files with 98 additions and 36 deletions

View File

@ -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);
}

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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();