Skip to content

Commit 7f18052

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

File tree

5 files changed

+140
-113
lines changed

5 files changed

+140
-113
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class DWARFContext : public DIContext {
103103
std::unique_ptr<DWARFDebugMacro>
104104
parseMacroOrMacinfo(MacroSecType SectionType);
105105

106+
virtual Error doWorkThreadSafely(function_ref<Error()> Work) = 0;
106107
};
107108
friend class DWARFContextState;
108109

@@ -491,6 +492,10 @@ class DWARFContext : public DIContext {
491492
/// manually only for DWARF5.
492493
void setParseCUTUIndexManually(bool PCUTU) { ParseCUTUIndexManually = PCUTU; }
493494

495+
Error doWorkThreadSafely(function_ref<Error()> Work) {
496+
return State->doWorkThreadSafely(Work);
497+
}
498+
494499
private:
495500
void addLocalsForDie(DWARFCompileUnit *CU, DWARFDie Subprogram, DWARFDie Die,
496501
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: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ class ThreadUnsafeDWARFContextState : public DWARFContext::DWARFContextState {
622622
return getNormalTypeUnitMap();
623623
}
624624

625-
625+
Error doWorkThreadSafely(function_ref<Error()> Work) override { return Work(); }
626626
};
627627

628628
class ThreadSafeState : public ThreadUnsafeDWARFContextState {
@@ -738,6 +738,11 @@ class ThreadSafeState : public ThreadUnsafeDWARFContextState {
738738
std::unique_lock<std::recursive_mutex> LockGuard(Mutex);
739739
return ThreadUnsafeDWARFContextState::getTypeUnitMap(IsDWO);
740740
}
741+
742+
Error doWorkThreadSafely(function_ref<Error()> Work) override {
743+
std::unique_lock<std::recursive_mutex> LockGuard(Mutex);
744+
return ThreadUnsafeDWARFContextState::doWorkThreadSafely(Work);
745+
}
741746
};
742747
} // namespace
743748

llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp

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

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

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

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-
}
550+
StringOffsetsTableContribution = *StringOffsetOrError;
551+
}
528552

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-
548-
StringOffsetsTableContribution = *StringOffsetOrError;
549-
}
553+
// DWARF v5 uses the .debug_rnglists and .debug_rnglists.dwo sections to
554+
// describe address ranges.
555+
if (getVersion() >= 5) {
556+
// In case of DWP, the base offset from the index has to be added.
557+
if (IsDWO) {
558+
uint64_t ContributionBaseOffset = 0;
559+
if (auto *IndexEntry = Header.getIndexEntry())
560+
if (auto *Contrib = IndexEntry->getContribution(DW_SECT_RNGLISTS))
561+
ContributionBaseOffset = Contrib->getOffset();
562+
setRangesSection(
563+
&Context.getDWARFObj().getRnglistsDWOSection(),
564+
ContributionBaseOffset +
565+
DWARFListTableHeader::getHeaderSize(Header.getFormat()));
566+
} else
567+
setRangesSection(&Context.getDWARFObj().getRnglistsSection(),
568+
toSectionOffset(UnitDie.find(DW_AT_rnglists_base),
569+
DWARFListTableHeader::getHeaderSize(
570+
Header.getFormat())));
571+
}
550572

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

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

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

603607
bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) {
@@ -652,15 +656,21 @@ bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) {
652656
return true;
653657
}
654658

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

666676
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)