Skip to content

Commit 8d945c8

Browse files
author
Arslan Khabutdinov
committed
Reduce llvm-gsymutil memory usage
1 parent 86ba681 commit 8d945c8

File tree

3 files changed

+76
-53
lines changed

3 files changed

+76
-53
lines changed

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

Lines changed: 12 additions & 10 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>
@@ -125,7 +126,8 @@ bool isCompileUnit(const std::unique_ptr<DWARFUnit> &U);
125126

126127
/// Describe a collection of units. Intended to hold all units either from
127128
/// .debug_info and .debug_types, or from .debug_info.dwo and .debug_types.dwo.
128-
class DWARFUnitVector final : public SmallVector<std::unique_ptr<DWARFUnit>, 1> {
129+
class DWARFUnitVector final
130+
: public SmallVector<std::unique_ptr<DWARFUnit>, 1> {
129131
std::function<std::unique_ptr<DWARFUnit>(uint64_t, DWARFSectionKind,
130132
const DWARFSection *,
131133
const DWARFUnitIndex::Entry *)>
@@ -137,8 +139,8 @@ class DWARFUnitVector final : public SmallVector<std::unique_ptr<DWARFUnit>, 1>
137139
using iterator = typename UnitVector::iterator;
138140
using iterator_range = llvm::iterator_range<typename UnitVector::iterator>;
139141

140-
using compile_unit_range =
141-
decltype(make_filter_range(std::declval<iterator_range>(), isCompileUnit));
142+
using compile_unit_range = decltype(make_filter_range(
143+
std::declval<iterator_range>(), isCompileUnit));
142144

143145
DWARFUnit *getUnitForOffset(uint64_t Offset) const;
144146
DWARFUnit *getUnitForIndexEntry(const DWARFUnitIndex::Entry &E);
@@ -257,6 +259,8 @@ class DWARFUnit {
257259

258260
std::shared_ptr<DWARFUnit> DWO;
259261

262+
mutable std::recursive_mutex FreeDIEsMutex;
263+
260264
protected:
261265
friend dwarf_linker::parallel::CompileUnit;
262266

@@ -316,7 +320,7 @@ class DWARFUnit {
316320

317321
bool isLittleEndian() const { return IsLittleEndian; }
318322
bool isDWOUnit() const { return IsDWO; }
319-
DWARFContext& getContext() const { return Context; }
323+
DWARFContext &getContext() const { return Context; }
320324
const DWARFSection &getInfoSection() const { return InfoSection; }
321325
uint64_t getOffset() const { return Header.getOffset(); }
322326
const dwarf::FormParams &getFormParams() const {
@@ -377,9 +381,7 @@ class DWARFUnit {
377381
RangeSectionBase = Base;
378382
}
379383

380-
uint64_t getLocSectionBase() const {
381-
return LocSectionBase;
382-
}
384+
uint64_t getLocSectionBase() const { return LocSectionBase; }
383385

384386
std::optional<object::SectionedAddress>
385387
getAddrOffsetSectionItem(uint32_t Index) const;
@@ -566,6 +568,9 @@ class DWARFUnit {
566568

567569
Error tryExtractDIEsIfNeeded(bool CUDieOnly);
568570

571+
/// clearDIEs - Clear parsed DIEs to keep memory usage low.
572+
void clearDIEs(bool KeepCUDie, bool KeepDWODies = false);
573+
569574
private:
570575
/// Size in bytes of the .debug_info data associated with this compile unit.
571576
size_t getDebugInfoSize() const {
@@ -581,9 +586,6 @@ class DWARFUnit {
581586
void extractDIEsToVector(bool AppendCUDie, bool AppendNonCUDIEs,
582587
std::vector<DWARFDebugInfoEntry> &DIEs) const;
583588

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

llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,21 @@ void DWARFUnitVector::addUnitsForSection(DWARFContext &C,
4444
DWARFSectionKind SectionKind) {
4545
const DWARFObject &D = C.getDWARFObj();
4646
addUnitsImpl(C, D, Section, C.getDebugAbbrev(), &D.getRangesSection(),
47-
&D.getLocSection(), D.getStrSection(),
48-
D.getStrOffsetsSection(), &D.getAddrSection(),
49-
D.getLineSection(), D.isLittleEndian(), false, false,
50-
SectionKind);
47+
&D.getLocSection(), D.getStrSection(), D.getStrOffsetsSection(),
48+
&D.getAddrSection(), D.getLineSection(), D.isLittleEndian(),
49+
false, false, SectionKind);
5150
}
5251

5352
void DWARFUnitVector::addUnitsForDWOSection(DWARFContext &C,
5453
const DWARFSection &DWOSection,
5554
DWARFSectionKind SectionKind,
5655
bool Lazy) {
5756
const DWARFObject &D = C.getDWARFObj();
58-
addUnitsImpl(C, D, DWOSection, C.getDebugAbbrevDWO(), &D.getRangesDWOSection(),
59-
&D.getLocDWOSection(), D.getStrDWOSection(),
60-
D.getStrOffsetsDWOSection(), &D.getAddrSection(),
61-
D.getLineDWOSection(), C.isLittleEndian(), true, Lazy,
62-
SectionKind);
57+
addUnitsImpl(C, D, DWOSection, C.getDebugAbbrevDWO(),
58+
&D.getRangesDWOSection(), &D.getLocDWOSection(),
59+
D.getStrDWOSection(), D.getStrOffsetsDWOSection(),
60+
&D.getAddrSection(), D.getLineDWOSection(), C.isLittleEndian(),
61+
true, Lazy, SectionKind);
6362
}
6463

6564
void DWARFUnitVector::addUnitsImpl(
@@ -107,12 +106,12 @@ void DWARFUnitVector::addUnitsImpl(
107106
std::unique_ptr<DWARFUnit> U;
108107
if (Header.isTypeUnit())
109108
U = std::make_unique<DWARFTypeUnit>(Context, InfoSection, Header, DA,
110-
RS, LocSection, SS, SOS, AOS, LS,
111-
LE, IsDWO, *this);
109+
RS, LocSection, SS, SOS, AOS, LS,
110+
LE, IsDWO, *this);
112111
else
113-
U = std::make_unique<DWARFCompileUnit>(Context, InfoSection, Header,
114-
DA, RS, LocSection, SS, SOS,
115-
AOS, LS, LE, IsDWO, *this);
112+
U = std::make_unique<DWARFCompileUnit>(Context, InfoSection, Header, DA,
113+
RS, LocSection, SS, SOS, AOS, LS,
114+
LE, IsDWO, *this);
116115
return U;
117116
};
118117
}
@@ -496,8 +495,12 @@ void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
496495
}
497496

498497
Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) {
499-
if ((CUDieOnly && !DieArray.empty()) ||
500-
DieArray.size() > 1)
498+
// Acquire the FreeDIEsMutex recursive lock to prevent a different thread
499+
// from freeing the DIE arrays while they're being extracted. It needs to
500+
// be recursive, as there is a potentially recursive path through
501+
// determineStringOffsetsTableContribution.
502+
std::lock_guard<std::recursive_mutex> FreeLock(FreeDIEsMutex);
503+
if ((CUDieOnly && !DieArray.empty()) || DieArray.size() > 1)
501504
return Error::success(); // Already parsed.
502505

503506
bool HasCUDie = !DieArray.empty();
@@ -652,7 +655,13 @@ bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) {
652655
return true;
653656
}
654657

655-
void DWARFUnit::clearDIEs(bool KeepCUDie) {
658+
void DWARFUnit::clearDIEs(bool KeepCUDie, bool KeepDWODies) {
659+
// We need to acquire the FreeDIEsMutex lock because we are
660+
// going to free the DIEs, when other threads might be trying to create them.
661+
std::lock_guard<std::recursive_mutex> FreeLock(FreeDIEsMutex);
662+
if (!KeepDWODies && DWO) {
663+
DWO->clearDIEs(KeepCUDie, KeepDWODies);
664+
}
656665
// Do not use resize() + shrink_to_fit() to free memory occupied by dies.
657666
// shrink_to_fit() is a *non-binding* request to reduce capacity() to size().
658667
// It depends on the implementation whether the request is fulfilled.
@@ -868,9 +877,8 @@ DWARFDie DWARFUnit::getVariableForAddress(uint64_t Address) {
868877
return R->second.second;
869878
}
870879

871-
void
872-
DWARFUnit::getInlinedChainForAddress(uint64_t Address,
873-
SmallVectorImpl<DWARFDie> &InlinedChain) {
880+
void DWARFUnit::getInlinedChainForAddress(
881+
uint64_t Address, SmallVectorImpl<DWARFDie> &InlinedChain) {
874882
assert(InlinedChain.empty());
875883
// Try to look for subprogram DIEs in the DWO file.
876884
parseDWO();
@@ -886,7 +894,7 @@ DWARFUnit::getInlinedChainForAddress(uint64_t Address,
886894
}
887895
if (SubroutineDIE.getTag() == DW_TAG_inlined_subroutine)
888896
InlinedChain.push_back(SubroutineDIE);
889-
SubroutineDIE = SubroutineDIE.getParent();
897+
SubroutineDIE = SubroutineDIE.getParent();
890898
}
891899
}
892900

@@ -1087,18 +1095,22 @@ StrOffsetsContributionDescriptor::validateContributionSize(
10871095
if (ValidationSize >= Size)
10881096
if (DA.isValidOffsetForDataOfSize((uint32_t)Base, ValidationSize))
10891097
return *this;
1090-
return createStringError(errc::invalid_argument, "length exceeds section size");
1098+
return createStringError(errc::invalid_argument,
1099+
"length exceeds section size");
10911100
}
10921101

10931102
// Look for a DWARF64-formatted contribution to the string offsets table
10941103
// starting at a given offset and record it in a descriptor.
10951104
static Expected<StrOffsetsContributionDescriptor>
10961105
parseDWARF64StringOffsetsTableHeader(DWARFDataExtractor &DA, uint64_t Offset) {
10971106
if (!DA.isValidOffsetForDataOfSize(Offset, 16))
1098-
return createStringError(errc::invalid_argument, "section offset exceeds section size");
1107+
return createStringError(errc::invalid_argument,
1108+
"section offset exceeds section size");
10991109

11001110
if (DA.getU32(&Offset) != dwarf::DW_LENGTH_DWARF64)
1101-
return createStringError(errc::invalid_argument, "32 bit contribution referenced from a 64 bit unit");
1111+
return createStringError(
1112+
errc::invalid_argument,
1113+
"32 bit contribution referenced from a 64 bit unit");
11021114

11031115
uint64_t Size = DA.getU64(&Offset);
11041116
uint8_t Version = DA.getU16(&Offset);
@@ -1113,7 +1125,8 @@ parseDWARF64StringOffsetsTableHeader(DWARFDataExtractor &DA, uint64_t Offset) {
11131125
static Expected<StrOffsetsContributionDescriptor>
11141126
parseDWARF32StringOffsetsTableHeader(DWARFDataExtractor &DA, uint64_t Offset) {
11151127
if (!DA.isValidOffsetForDataOfSize(Offset, 8))
1116-
return createStringError(errc::invalid_argument, "section offset exceeds section size");
1128+
return createStringError(errc::invalid_argument,
1129+
"section offset exceeds section size");
11171130

11181131
uint32_t ContributionSize = DA.getU32(&Offset);
11191132
if (ContributionSize >= dwarf::DW_LENGTH_lo_reserved)
@@ -1135,7 +1148,8 @@ parseDWARFStringOffsetsTableHeader(DWARFDataExtractor &DA,
11351148
switch (Format) {
11361149
case dwarf::DwarfFormat::DWARF64: {
11371150
if (Offset < 16)
1138-
return createStringError(errc::invalid_argument, "insufficient space for 64 bit header prefix");
1151+
return createStringError(errc::invalid_argument,
1152+
"insufficient space for 64 bit header prefix");
11391153
auto DescOrError = parseDWARF64StringOffsetsTableHeader(DA, Offset - 16);
11401154
if (!DescOrError)
11411155
return DescOrError.takeError();
@@ -1144,7 +1158,8 @@ parseDWARFStringOffsetsTableHeader(DWARFDataExtractor &DA,
11441158
}
11451159
case dwarf::DwarfFormat::DWARF32: {
11461160
if (Offset < 8)
1147-
return createStringError(errc::invalid_argument, "insufficient space for 32 bit header prefix");
1161+
return createStringError(errc::invalid_argument,
1162+
"insufficient space for 32 bit header prefix");
11481163
auto DescOrError = parseDWARF32StringOffsetsTableHeader(DA, Offset - 8);
11491164
if (!DescOrError)
11501165
return DescOrError.takeError();
@@ -1182,7 +1197,8 @@ DWARFUnit::determineStringOffsetsTableContributionDWO(DWARFDataExtractor &DA) {
11821197
return std::nullopt;
11831198
Offset += Header.getFormat() == dwarf::DwarfFormat::DWARF32 ? 8 : 16;
11841199
// Look for a valid contribution at the given offset.
1185-
auto DescOrError = parseDWARFStringOffsetsTableHeader(DA, Header.getFormat(), Offset);
1200+
auto DescOrError =
1201+
parseDWARFStringOffsetsTableHeader(DA, Header.getFormat(), Offset);
11861202
if (!DescOrError)
11871203
return DescOrError.takeError();
11881204
return *DescOrError;

llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ struct llvm::gsym::CUInfo {
8282
}
8383
};
8484

85-
8685
static DWARFDie GetParentDeclContextDIE(DWARFDie &Die) {
8786
if (DWARFDie SpecDie =
8887
Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_specification)) {
@@ -170,7 +169,7 @@ getQualifiedNameIndex(DWARFDie &Die, uint64_t Language, GsymCreator &Gsym) {
170169
// templates
171170
if (ParentName.front() == '<' && ParentName.back() == '>')
172171
Name = "{" + ParentName.substr(1, ParentName.size() - 2).str() + "}" +
173-
"::" + Name;
172+
"::" + Name;
174173
else
175174
Name = ParentName.str() + "::" + Name;
176175
}
@@ -432,7 +431,7 @@ static void convertFunctionLineTable(OutputAggregator &Out, CUInfo &CUI,
432431
// Skip multiple line entries for the same file and line.
433432
auto LastLE = FI.OptLineTable->last();
434433
if (LastLE && LastLE->File == FileIdx && LastLE->Line == Row.Line)
435-
continue;
434+
continue;
436435
// Only push a row if it isn't an end sequence. End sequence markers are
437436
// included for the last address in a function or the last contiguous
438437
// address in a sequence.
@@ -656,6 +655,11 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
656655
DWARFDie Die = getDie(*CU);
657656
CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
658657
handleDie(Out, CUI, Die);
658+
// Release the line table, once we're done.
659+
DICtx.clearLineTableForUnit(CU.get());
660+
// Free any DIEs that were allocated by the DWARF parser.
661+
// If/when they're needed by other CU's, they'll be recreated.
662+
CU->clearDIEs(/*KeepCUDie=*/false);
659663
}
660664
} else {
661665
// LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up
@@ -668,24 +672,24 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
668672
for (const auto &CU : DICtx.compile_units())
669673
CU->getAbbreviations();
670674

671-
// Now parse all DIEs in case we have cross compile unit references in a
672-
// thread pool.
673675
DefaultThreadPool pool(hardware_concurrency(NumThreads));
674-
for (const auto &CU : DICtx.compile_units())
675-
pool.async([&CU]() { CU->getUnitDIE(false /*CUDieOnly*/); });
676-
pool.wait();
677676

678677
// Now convert all DWARF to GSYM in a thread pool.
679678
std::mutex LogMutex;
680679
for (const auto &CU : DICtx.compile_units()) {
681680
DWARFDie Die = getDie(*CU);
682681
if (Die) {
683682
CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
684-
pool.async([this, CUI, &LogMutex, &Out, Die]() mutable {
683+
pool.async([this, CUI, &CU, &LogMutex, &Out, Die]() mutable {
685684
std::string storage;
686685
raw_string_ostream StrStream(storage);
687686
OutputAggregator ThreadOut(Out.GetOS() ? &StrStream : nullptr);
688687
handleDie(ThreadOut, CUI, Die);
688+
// Release the line table once we're done.
689+
DICtx.clearLineTableForUnit(CU.get());
690+
// Free any DIEs that were allocated by the DWARF parser.
691+
// If/when they're needed by other CU's, they'll be recreated.
692+
CU->clearDIEs(/*KeepCUDie=*/false);
689693
// Print ThreadLogStorage lines into an actual stream under a lock
690694
std::lock_guard<std::mutex> guard(LogMutex);
691695
if (Out.GetOS()) {
@@ -697,6 +701,9 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
697701
}
698702
pool.wait();
699703
}
704+
// Now get rid of all the DIEs that may have been recreated
705+
for (const auto &CU : DICtx.compile_units())
706+
CU->clearDIEs(/*KeepCUDie=*/false);
700707
size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore;
701708
Out << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n";
702709
return Error::success();
@@ -718,8 +725,8 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath,
718725
for (uint32_t I = 0; I < NumAddrs; ++I) {
719726
auto FuncAddr = Gsym->getAddress(I);
720727
if (!FuncAddr)
721-
return createStringError(std::errc::invalid_argument,
722-
"failed to extract address[%i]", I);
728+
return createStringError(std::errc::invalid_argument,
729+
"failed to extract address[%i]", I);
723730

724731
auto FI = Gsym->getFunctionInfo(*FuncAddr);
725732
if (!FI)
@@ -734,8 +741,7 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath,
734741
if (!LR)
735742
return LR.takeError();
736743

737-
auto DwarfInlineInfos =
738-
DICtx.getInliningInfoForAddress(SectAddr, DLIS);
744+
auto DwarfInlineInfos = DICtx.getInliningInfoForAddress(SectAddr, DLIS);
739745
uint32_t NumDwarfInlineInfos = DwarfInlineInfos.getNumberOfFrames();
740746
if (NumDwarfInlineInfos == 0) {
741747
DwarfInlineInfos.addFrame(
@@ -773,8 +779,7 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath,
773779
continue;
774780
}
775781

776-
for (size_t Idx = 0, count = LR->Locations.size(); Idx < count;
777-
++Idx) {
782+
for (size_t Idx = 0, count = LR->Locations.size(); Idx < count; ++Idx) {
778783
const auto &gii = LR->Locations[Idx];
779784
if (Idx < NumDwarfInlineInfos) {
780785
const auto &dii = DwarfInlineInfos.getFrame(Idx);

0 commit comments

Comments
 (0)