Skip to content

Reduce llvm-gsymutil memory usage (lambda-free, and less locking) #97640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <cstdint>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -257,6 +258,8 @@ class DWARFUnit {

std::shared_ptr<DWARFUnit> DWO;

mutable std::recursive_mutex FreeDIEsMutex;

protected:
friend dwarf_linker::parallel::CompileUnit;

Expand Down Expand Up @@ -566,6 +569,9 @@ class DWARFUnit {

Error tryExtractDIEsIfNeeded(bool CUDieOnly);

/// clearDIEs - Clear parsed DIEs to keep memory usage low.
void clearDIEs(bool KeepCUDie);

private:
/// Size in bytes of the .debug_info data associated with this compile unit.
size_t getDebugInfoSize() const {
Expand All @@ -581,9 +587,6 @@ class DWARFUnit {
void extractDIEsToVector(bool AppendCUDie, bool AppendNonCUDIEs,
std::vector<DWARFDebugInfoEntry> &DIEs) const;

/// clearDIEs - Clear parsed DIEs to keep memory usage low.
void clearDIEs(bool KeepCUDie);

/// parseDWO - Parses .dwo file for current compile unit. Returns true if
/// it was actually constructed.
/// The \p AlternativeLocation specifies an alternative location to get
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,12 @@ void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
}

Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) {
// Acquire the FreeDIEsMutex recursive lock to prevent a different thread
// from freeing the DIE arrays while they're being extracted. It needs to
// be recursive, as there is a potentially recursive path through
// determineStringOffsetsTableContribution.
std::lock_guard<std::recursive_mutex> FreeLock(FreeDIEsMutex);

if ((CUDieOnly && !DieArray.empty()) ||
DieArray.size() > 1)
return Error::success(); // Already parsed.
Expand Down Expand Up @@ -653,6 +659,10 @@ bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) {
}

void DWARFUnit::clearDIEs(bool KeepCUDie) {
// We need to acquire the FreeDIEsMutex lock in write-mode, because we are
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is out of date - the recursive_mutex isn't shared, so there's no "write-mode" at work here, I think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops: That slipped through. I'll update it.

// going to free the DIEs, when other threads might be trying to create them.
std::lock_guard<std::recursive_mutex> FreeLock(FreeDIEsMutex);

// Do not use resize() + shrink_to_fit() to free memory occupied by dies.
// shrink_to_fit() is a *non-binding* request to reduce capacity() to size().
// It depends on the implementation whether the request is fulfilled.
Expand Down
15 changes: 14 additions & 1 deletion llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,11 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
DWARFDie Die = getDie(*CU);
CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
handleDie(Out, CUI, Die);
// Release the line table, once we're done.
DICtx.clearLineTableForUnit(CU.get());
// Free any DIEs that were allocated by the DWARF parser.
// If/when they're needed by other CU's, they'll be recreated.
CU->clearDIEs(/*KeepCUDie=*/false);
}
} else {
// LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up
Expand All @@ -612,11 +617,16 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
DWARFDie Die = getDie(*CU);
if (Die) {
CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
pool.async([this, CUI, &LogMutex, &Out, Die]() mutable {
pool.async([this, CUI, &CU, &LogMutex, &Out, Die]() mutable {
std::string storage;
raw_string_ostream StrStream(storage);
OutputAggregator ThreadOut(Out.GetOS() ? &StrStream : nullptr);
handleDie(ThreadOut, CUI, Die);
// Release the line table once we're done.
DICtx.clearLineTableForUnit(CU.get());
// Free any DIEs that were allocated by the DWARF parser.
// If/when they're needed by other CU's, they'll be recreated.
CU->clearDIEs(/*KeepCUDie=*/false);
// Print ThreadLogStorage lines into an actual stream under a lock
std::lock_guard<std::mutex> guard(LogMutex);
if (Out.GetOS()) {
Expand All @@ -628,6 +638,9 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
}
pool.wait();
}
// Now get rid of all the DIEs that may have been recreated
for (const auto &CU : DICtx.compile_units())
CU->clearDIEs(/*KeepCUDie=*/false);
size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore;
Out << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n";
return Error::success();
Expand Down
Loading