Skip to content

Commit d997b4f

Browse files
peremyachArslan Khabutdinov
and
Arslan Khabutdinov
authored
Reduce llvm-gsymutil memory usage (#140740)
Same as #139907 except there is now a special dovoidwork helper function. Previous approach with assert(f();return success;) failed tests for release builds, so I created a separate helper. Open to suggestions how to solve this more elegantly. Co-authored-by: Arslan Khabutdinov <[email protected]>
1 parent 584616c commit d997b4f

File tree

5 files changed

+143
-111
lines changed

5 files changed

+143
-111
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class DWARFContext : public DIContext {
102102
/// Parse a macro[.dwo] or macinfo[.dwo] section.
103103
std::unique_ptr<DWARFDebugMacro>
104104
parseMacroOrMacinfo(MacroSecType SectionType);
105+
106+
virtual Error doWorkThreadSafely(function_ref<Error()> Work) = 0;
105107
};
106108
friend class DWARFContextState;
107109

@@ -490,6 +492,10 @@ class DWARFContext : public DIContext {
490492
/// manually only for DWARF5.
491493
void setParseCUTUIndexManually(bool PCUTU) { ParseCUTUIndexManually = PCUTU; }
492494

495+
Error doWorkThreadSafely(function_ref<Error()> Work) {
496+
return State->doWorkThreadSafely(Work);
497+
}
498+
493499
private:
494500
void addLocalsForDie(DWARFCompileUnit *CU, DWARFDie Subprogram, DWARFDie Die,
495501
std::vector<DILocal> &Result);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,9 @@ class DWARFUnit {
566566

567567
Error tryExtractDIEsIfNeeded(bool CUDieOnly);
568568

569+
/// clearDIEs - Clear parsed DIEs to keep memory usage low.
570+
void clearDIEs(bool KeepCUDie, bool KeepDWODies = false);
571+
569572
private:
570573
/// Size in bytes of the .debug_info data associated with this compile unit.
571574
size_t getDebugInfoSize() const {
@@ -581,9 +584,6 @@ class DWARFUnit {
581584
void extractDIEsToVector(bool AppendCUDie, bool AppendNonCUDIEs,
582585
std::vector<DWARFDebugInfoEntry> &DIEs) const;
583586

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

llvm/lib/DebugInfo/DWARF/DWARFContext.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,10 @@ class ThreadUnsafeDWARFContextState : public DWARFContext::DWARFContextState {
621621
else
622622
return getNormalTypeUnitMap();
623623
}
624+
625+
Error doWorkThreadSafely(function_ref<Error()> Work) override {
626+
return Work();
627+
}
624628
};
625629

626630
class ThreadSafeState : public ThreadUnsafeDWARFContextState {
@@ -736,6 +740,11 @@ class ThreadSafeState : public ThreadUnsafeDWARFContextState {
736740
std::unique_lock<std::recursive_mutex> LockGuard(Mutex);
737741
return ThreadUnsafeDWARFContextState::getTypeUnitMap(IsDWO);
738742
}
743+
744+
Error doWorkThreadSafely(function_ref<Error()> Work) override {
745+
std::unique_lock<std::recursive_mutex> LockGuard(Mutex);
746+
return ThreadUnsafeDWARFContextState::doWorkThreadSafely(Work);
747+
}
739748
};
740749
} // namespace
741750

llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp

Lines changed: 112 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -496,107 +496,111 @@ void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
496496
}
497497

498498
Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) {
499-
if ((CUDieOnly && !DieArray.empty()) || DieArray.size() > 1)
500-
return Error::success(); // Already parsed.
501-
502-
bool HasCUDie = !DieArray.empty();
503-
extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray);
504-
505-
if (DieArray.empty())
506-
return Error::success();
499+
return Context.doWorkThreadSafely([&]() -> Error {
500+
if ((CUDieOnly && !DieArray.empty()) || DieArray.size() > 1)
501+
return Error::success(); // Already parsed.
502+
503+
bool HasCUDie = !DieArray.empty();
504+
extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray);
505+
506+
if (DieArray.empty())
507+
return Error::success();
508+
509+
// If CU DIE was just parsed, copy several attribute values from it.
510+
if (HasCUDie)
511+
return Error::success();
512+
513+
DWARFDie UnitDie(this, &DieArray[0]);
514+
if (std::optional<uint64_t> DWOId =
515+
toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id)))
516+
Header.setDWOId(*DWOId);
517+
if (!IsDWO) {
518+
assert(AddrOffsetSectionBase == std::nullopt);
519+
assert(RangeSectionBase == 0);
520+
assert(LocSectionBase == 0);
521+
AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_addr_base));
522+
if (!AddrOffsetSectionBase)
523+
AddrOffsetSectionBase =
524+
toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base));
525+
RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0);
526+
LocSectionBase = toSectionOffset(UnitDie.find(DW_AT_loclists_base), 0);
527+
}
507528

508-
// If CU DIE was just parsed, copy several attribute values from it.
509-
if (HasCUDie)
510-
return Error::success();
529+
// In general, in DWARF v5 and beyond we derive the start of the unit's
530+
// contribution to the string offsets table from the unit DIE's
531+
// DW_AT_str_offsets_base attribute. Split DWARF units do not use this
532+
// attribute, so we assume that there is a contribution to the string
533+
// offsets table starting at offset 0 of the debug_str_offsets.dwo section.
534+
// In both cases we need to determine the format of the contribution,
535+
// which may differ from the unit's format.
536+
DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection,
537+
IsLittleEndian, 0);
538+
if (IsDWO || getVersion() >= 5) {
539+
auto StringOffsetOrError =
540+
IsDWO ? determineStringOffsetsTableContributionDWO(DA)
541+
: determineStringOffsetsTableContribution(DA);
542+
if (!StringOffsetOrError) {
543+
return createStringError(errc::invalid_argument,
544+
"invalid reference to or invalid content in "
545+
".debug_str_offsets[.dwo]: " +
546+
toString(StringOffsetOrError.takeError()));
547+
}
511548

512-
DWARFDie UnitDie(this, &DieArray[0]);
513-
if (std::optional<uint64_t> DWOId =
514-
toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id)))
515-
Header.setDWOId(*DWOId);
516-
if (!IsDWO) {
517-
assert(AddrOffsetSectionBase == std::nullopt);
518-
assert(RangeSectionBase == 0);
519-
assert(LocSectionBase == 0);
520-
AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_addr_base));
521-
if (!AddrOffsetSectionBase)
522-
AddrOffsetSectionBase =
523-
toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base));
524-
RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0);
525-
LocSectionBase = toSectionOffset(UnitDie.find(DW_AT_loclists_base), 0);
526-
}
549+
StringOffsetsTableContribution = *StringOffsetOrError;
550+
}
527551

528-
// In general, in DWARF v5 and beyond we derive the start of the unit's
529-
// contribution to the string offsets table from the unit DIE's
530-
// DW_AT_str_offsets_base attribute. Split DWARF units do not use this
531-
// attribute, so we assume that there is a contribution to the string
532-
// offsets table starting at offset 0 of the debug_str_offsets.dwo section.
533-
// In both cases we need to determine the format of the contribution,
534-
// which may differ from the unit's format.
535-
DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection,
536-
IsLittleEndian, 0);
537-
if (IsDWO || getVersion() >= 5) {
538-
auto StringOffsetOrError =
539-
IsDWO ? determineStringOffsetsTableContributionDWO(DA)
540-
: determineStringOffsetsTableContribution(DA);
541-
if (!StringOffsetOrError)
542-
return createStringError(errc::invalid_argument,
543-
"invalid reference to or invalid content in "
544-
".debug_str_offsets[.dwo]: " +
545-
toString(StringOffsetOrError.takeError()));
546-
547-
StringOffsetsTableContribution = *StringOffsetOrError;
548-
}
552+
// DWARF v5 uses the .debug_rnglists and .debug_rnglists.dwo sections to
553+
// describe address ranges.
554+
if (getVersion() >= 5) {
555+
// In case of DWP, the base offset from the index has to be added.
556+
if (IsDWO) {
557+
uint64_t ContributionBaseOffset = 0;
558+
if (auto *IndexEntry = Header.getIndexEntry())
559+
if (auto *Contrib = IndexEntry->getContribution(DW_SECT_RNGLISTS))
560+
ContributionBaseOffset = Contrib->getOffset();
561+
setRangesSection(
562+
&Context.getDWARFObj().getRnglistsDWOSection(),
563+
ContributionBaseOffset +
564+
DWARFListTableHeader::getHeaderSize(Header.getFormat()));
565+
} else
566+
setRangesSection(&Context.getDWARFObj().getRnglistsSection(),
567+
toSectionOffset(UnitDie.find(DW_AT_rnglists_base),
568+
DWARFListTableHeader::getHeaderSize(
569+
Header.getFormat())));
570+
}
549571

550-
// DWARF v5 uses the .debug_rnglists and .debug_rnglists.dwo sections to
551-
// describe address ranges.
552-
if (getVersion() >= 5) {
553-
// In case of DWP, the base offset from the index has to be added.
554572
if (IsDWO) {
555-
uint64_t ContributionBaseOffset = 0;
573+
// If we are reading a package file, we need to adjust the location list
574+
// data based on the index entries.
575+
StringRef Data = Header.getVersion() >= 5
576+
? Context.getDWARFObj().getLoclistsDWOSection().Data
577+
: Context.getDWARFObj().getLocDWOSection().Data;
556578
if (auto *IndexEntry = Header.getIndexEntry())
557-
if (auto *Contrib = IndexEntry->getContribution(DW_SECT_RNGLISTS))
558-
ContributionBaseOffset = Contrib->getOffset();
559-
setRangesSection(
560-
&Context.getDWARFObj().getRnglistsDWOSection(),
561-
ContributionBaseOffset +
562-
DWARFListTableHeader::getHeaderSize(Header.getFormat()));
563-
} else
564-
setRangesSection(&Context.getDWARFObj().getRnglistsSection(),
565-
toSectionOffset(UnitDie.find(DW_AT_rnglists_base),
566-
DWARFListTableHeader::getHeaderSize(
567-
Header.getFormat())));
568-
}
579+
if (const auto *C = IndexEntry->getContribution(
580+
Header.getVersion() >= 5 ? DW_SECT_LOCLISTS : DW_SECT_EXT_LOC))
581+
Data = Data.substr(C->getOffset(), C->getLength());
582+
583+
DWARFDataExtractor DWARFData(Data, IsLittleEndian, getAddressByteSize());
584+
LocTable =
585+
std::make_unique<DWARFDebugLoclists>(DWARFData, Header.getVersion());
586+
LocSectionBase = DWARFListTableHeader::getHeaderSize(Header.getFormat());
587+
} else if (getVersion() >= 5) {
588+
LocTable = std::make_unique<DWARFDebugLoclists>(
589+
DWARFDataExtractor(Context.getDWARFObj(),
590+
Context.getDWARFObj().getLoclistsSection(),
591+
IsLittleEndian, getAddressByteSize()),
592+
getVersion());
593+
} else {
594+
LocTable = std::make_unique<DWARFDebugLoc>(DWARFDataExtractor(
595+
Context.getDWARFObj(), Context.getDWARFObj().getLocSection(),
596+
IsLittleEndian, getAddressByteSize()));
597+
}
569598

570-
if (IsDWO) {
571-
// If we are reading a package file, we need to adjust the location list
572-
// data based on the index entries.
573-
StringRef Data = Header.getVersion() >= 5
574-
? Context.getDWARFObj().getLoclistsDWOSection().Data
575-
: Context.getDWARFObj().getLocDWOSection().Data;
576-
if (auto *IndexEntry = Header.getIndexEntry())
577-
if (const auto *C = IndexEntry->getContribution(
578-
Header.getVersion() >= 5 ? DW_SECT_LOCLISTS : DW_SECT_EXT_LOC))
579-
Data = Data.substr(C->getOffset(), C->getLength());
580-
581-
DWARFDataExtractor DWARFData(Data, IsLittleEndian, getAddressByteSize());
582-
LocTable =
583-
std::make_unique<DWARFDebugLoclists>(DWARFData, Header.getVersion());
584-
LocSectionBase = DWARFListTableHeader::getHeaderSize(Header.getFormat());
585-
} else if (getVersion() >= 5) {
586-
LocTable = std::make_unique<DWARFDebugLoclists>(
587-
DWARFDataExtractor(Context.getDWARFObj(),
588-
Context.getDWARFObj().getLoclistsSection(),
589-
IsLittleEndian, getAddressByteSize()),
590-
getVersion());
591-
} else {
592-
LocTable = std::make_unique<DWARFDebugLoc>(DWARFDataExtractor(
593-
Context.getDWARFObj(), Context.getDWARFObj().getLocSection(),
594-
IsLittleEndian, getAddressByteSize()));
595-
}
599+
// Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for
600+
// skeleton CU DIE, so that DWARF users not aware of it are not broken.
596601

597-
// Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for
598-
// skeleton CU DIE, so that DWARF users not aware of it are not broken.
599-
return Error::success();
602+
return Error::success();
603+
});
600604
}
601605

602606
bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) {
@@ -651,15 +655,21 @@ bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) {
651655
return true;
652656
}
653657

654-
void DWARFUnit::clearDIEs(bool KeepCUDie) {
655-
// Do not use resize() + shrink_to_fit() to free memory occupied by dies.
656-
// shrink_to_fit() is a *non-binding* request to reduce capacity() to size().
657-
// It depends on the implementation whether the request is fulfilled.
658-
// Create a new vector with a small capacity and assign it to the DieArray to
659-
// have previous contents freed.
660-
DieArray = (KeepCUDie && !DieArray.empty())
661-
? std::vector<DWARFDebugInfoEntry>({DieArray[0]})
662-
: std::vector<DWARFDebugInfoEntry>();
658+
void DWARFUnit::clearDIEs(bool KeepCUDie, bool KeepDWODies) {
659+
cantFail(Context.doWorkThreadSafely([&] {
660+
if (!KeepDWODies && DWO) {
661+
DWO->clearDIEs(KeepCUDie, KeepDWODies);
662+
}
663+
// Do not use resize() + shrink_to_fit() to free memory occupied by dies.
664+
// shrink_to_fit() is a *non-binding* request to reduce capacity() to
665+
// size(). It depends on the implementation whether the request is
666+
// fulfilled. Create a new vector with a small capacity and assign it to the
667+
// DieArray to have previous contents freed.
668+
DieArray = (KeepCUDie && !DieArray.empty())
669+
? std::vector<DWARFDebugInfoEntry>({DieArray[0]})
670+
: std::vector<DWARFDebugInfoEntry>();
671+
return Error::success();
672+
}));
663673
}
664674

665675
Expected<DWARFAddressRangesVector>

llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,11 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
656656
DWARFDie Die = getDie(*CU);
657657
CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
658658
handleDie(Out, CUI, Die);
659+
// Release the line table, once we're done.
660+
DICtx.clearLineTableForUnit(CU.get());
661+
// Free any DIEs that were allocated by the DWARF parser.
662+
// If/when they're needed by other CU's, they'll be recreated.
663+
CU->clearDIEs(/*KeepCUDie=*/false, /*KeepDWODIEs=*/false);
659664
}
660665
} else {
661666
// LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up
@@ -668,24 +673,23 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
668673
for (const auto &CU : DICtx.compile_units())
669674
CU->getAbbreviations();
670675

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

678678
// Now convert all DWARF to GSYM in a thread pool.
679679
std::mutex LogMutex;
680680
for (const auto &CU : DICtx.compile_units()) {
681681
DWARFDie Die = getDie(*CU);
682682
if (Die) {
683683
CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
684-
pool.async([this, CUI, &LogMutex, &Out, Die]() mutable {
684+
pool.async([this, CUI, &CU, &LogMutex, &Out, Die]() mutable {
685685
std::string storage;
686686
raw_string_ostream StrStream(storage);
687687
OutputAggregator ThreadOut(Out.GetOS() ? &StrStream : nullptr);
688688
handleDie(ThreadOut, CUI, Die);
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, /*KeepDWODIEs=*/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, /*KeepDWODIEs=*/false);
700707
size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore;
701708
Out << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n";
702709
return Error::success();

0 commit comments

Comments
 (0)