Skip to content

Commit 7d33c14

Browse files
kevinfreiKevin Frei
authored andcommitted
Reduce llvm-gsymutil memory usage (llvm#91023)
llvm-gsymutil eats a lot of RAM. On some large binaries, it causes OOM's on smaller hardware, consuming well over 64GB of RAM. This change frees line tables once we're done with them, and frees DWARFUnits's DIE's when we finish processing each DU, though they may get reconstituted if there are references from other DU's during processing. Once the conversion is complete, all DIE's are freed. The reduction in peak memory usage from these changes showed between 7-12% in my tests. There is a recursive mutex necessary to prevent accidental freeing of the DIE arrays while they're being extraced. It needs to be recursive as there's a recursive path through the final section of the code (determineStringOffsetsTableContribution) that may result in a call to this function.
1 parent 0f52193 commit 7d33c14

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <cstdint>
2828
#include <map>
2929
#include <memory>
30+
#include <mutex>
3031
#include <set>
3132
#include <utility>
3233
#include <vector>
@@ -257,6 +258,8 @@ class DWARFUnit {
257258

258259
std::shared_ptr<DWARFUnit> DWO;
259260

261+
mutable std::recursive_mutex FreeDIEsMutex;
262+
260263
protected:
261264
friend dwarf_linker::parallel::CompileUnit;
262265

@@ -566,6 +569,9 @@ class DWARFUnit {
566569

567570
Error tryExtractDIEsIfNeeded(bool CUDieOnly);
568571

572+
/// clearDIEs - Clear parsed DIEs to keep memory usage low.
573+
void clearDIEs(bool KeepCUDie);
574+
569575
private:
570576
/// Size in bytes of the .debug_info data associated with this compile unit.
571577
size_t getDebugInfoSize() const {
@@ -581,9 +587,6 @@ class DWARFUnit {
581587
void extractDIEsToVector(bool AppendCUDie, bool AppendNonCUDIEs,
582588
std::vector<DWARFDebugInfoEntry> &DIEs) const;
583589

584-
/// clearDIEs - Clear parsed DIEs to keep memory usage low.
585-
void clearDIEs(bool KeepCUDie);
586-
587590
/// parseDWO - Parses .dwo file for current compile unit. Returns true if
588591
/// it was actually constructed.
589592
/// The \p AlternativeLocation specifies an alternative location to get

llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,12 @@ void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
496496
}
497497

498498
Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) {
499+
// Acquire the FreeDIEsMutex recursive lock to prevent a different thread
500+
// from freeing the DIE arrays while they're being extracted. It needs to
501+
// be recursive, as there is a potentially recursive path through
502+
// determineStringOffsetsTableContribution.
503+
std::lock_guard<std::recursive_mutex> FreeLock(FreeDIEsMutex);
504+
499505
if ((CUDieOnly && !DieArray.empty()) ||
500506
DieArray.size() > 1)
501507
return Error::success(); // Already parsed.
@@ -653,6 +659,10 @@ bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) {
653659
}
654660

655661
void DWARFUnit::clearDIEs(bool KeepCUDie) {
662+
// We need to acquire the FreeDIEsMutex lock in write-mode, because we are
663+
// going to free the DIEs, when other threads might be trying to create them.
664+
std::lock_guard<std::recursive_mutex> FreeLock(FreeDIEsMutex);
665+
656666
// Do not use resize() + shrink_to_fit() to free memory occupied by dies.
657667
// shrink_to_fit() is a *non-binding* request to reduce capacity() to size().
658668
// It depends on the implementation whether the request is fulfilled.

llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,11 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
587587
DWARFDie Die = getDie(*CU);
588588
CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
589589
handleDie(Out, CUI, Die);
590+
// Release the line table, once we're done.
591+
DICtx.clearLineTableForUnit(CU.get());
592+
// Free any DIEs that were allocated by the DWARF parser.
593+
// If/when they're needed by other CU's, they'll be recreated.
594+
CU->clearDIEs(/*KeepCUDie=*/false);
590595
}
591596
} else {
592597
// LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up
@@ -612,11 +617,16 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
612617
DWARFDie Die = getDie(*CU);
613618
if (Die) {
614619
CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
615-
pool.async([this, CUI, &LogMutex, &Out, Die]() mutable {
620+
pool.async([this, CUI, &CU, &LogMutex, &Out, Die]() mutable {
616621
std::string storage;
617622
raw_string_ostream StrStream(storage);
618623
OutputAggregator ThreadOut(Out.GetOS() ? &StrStream : nullptr);
619624
handleDie(ThreadOut, CUI, Die);
625+
// Release the line table once we're done.
626+
DICtx.clearLineTableForUnit(CU.get());
627+
// Free any DIEs that were allocated by the DWARF parser.
628+
// If/when they're needed by other CU's, they'll be recreated.
629+
CU->clearDIEs(/*KeepCUDie=*/false);
620630
// Print ThreadLogStorage lines into an actual stream under a lock
621631
std::lock_guard<std::mutex> guard(LogMutex);
622632
if (Out.GetOS()) {
@@ -628,6 +638,9 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
628638
}
629639
pool.wait();
630640
}
641+
// Now get rid of all the DIEs that may have been recreated
642+
for (const auto &CU : DICtx.compile_units())
643+
CU->clearDIEs(/*KeepCUDie=*/false);
631644
size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore;
632645
Out << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n";
633646
return Error::success();

0 commit comments

Comments
 (0)