diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h --- a/llvm/include/llvm/DebugInfo/DIContext.h +++ b/llvm/include/llvm/DebugInfo/DIContext.h @@ -217,10 +217,7 @@ class DIContext { public: - enum DIContextKind { - CK_DWARF, - CK_PDB - }; + enum DIContextKind { CK_DWARF, CK_CACHED_DWARF, CK_PDB }; DIContext(DIContextKind K) : Kind(K) {} virtual ~DIContext() = default; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFCachedDIContext.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFCachedDIContext.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFCachedDIContext.h @@ -0,0 +1,102 @@ +//===- DWARFCachedDIContext.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===/ + +#ifndef LLVM_DEBUGINFO_DWARF_DWARFCACHEDDICONTEXT_H +#define LLVM_DEBUGINFO_DWARF_DWARFCACHEDDICONTEXT_H + +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" +#include +#include +#include +#include + +namespace llvm { + +class raw_ostream; + +/// DWARFUnitLRUCache +/// This class provides data structure for LRU caching DWARFUnit. +class DWARFUnitLRUCache { +public: + DWARFUnitLRUCache(int LRUSize) : UnitLRUSize(LRUSize) {} + + // Add a DWARFUnit to the cache if it doesn't already exists and evicts a + // least recently used (LRU) DWARFUnit in the cache if there's no more free + // space. Returns the pointer to the evicted DWARFUnit or nullptr if nothing's + // evicted. + DWARFUnit *addUnitAndEvictLRU(DWARFUnit *Unit); + +private: + using unit_queue_type = std::list; + using unit_queue_iterator = unit_queue_type::iterator; + using unit_map_type = std::unordered_map; + unit_queue_type UnitQueue; + unit_map_type UnitMap; + const int UnitLRUSize; +}; + +/// DWARFCachedDIContext +/// This class implements DIContext interface using DWARFContext with caching +/// internal data structure in order to keep memory usage from growing +/// without a bound. +class DWARFCachedDIContext : public DIContext { +public: + DWARFCachedDIContext(std::unique_ptr DCtx, + int LRUSize = DefaultLRUSize); + ~DWARFCachedDIContext(); + + DWARFCachedDIContext(DWARFCachedDIContext &) = delete; + DWARFCachedDIContext &operator=(DWARFCachedDIContext &) = delete; + + static bool classof(const DIContext *DICtx) { + return DICtx->getKind() == CK_CACHED_DWARF; + } + + void dump(raw_ostream &OS, DIDumpOptions DumpOpts) override; + + bool verify(raw_ostream &OS, DIDumpOptions DumpOpts = {}) override; + + DILineInfo getLineInfoForAddress( + object::SectionedAddress Address, + DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override; + DILineInfoTable getLineInfoForAddressRange( + object::SectionedAddress Address, uint64_t Size, + DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override; + DIInliningInfo getInliningInfoForAddress( + object::SectionedAddress Address, + DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override; + + std::vector + getLocalsForAddress(object::SectionedAddress Address) override; + + static std::unique_ptr + create(const object::ObjectFile &Obj, const LoadedObjectInfo *L = nullptr, + std::string DWPName = "", + std::function RecoverableErrorHandler = + WithColor::defaultErrorHandler, + std::function WarningHandler = + WithColor::defaultWarningHandler, + int LRUSize = DefaultLRUSize); + +private: + std::unique_ptr Context; + + // LRU mechanism of compile units to manage memory usage. + DWARFUnitLRUCache Cache; + static constexpr int DefaultLRUSize = 10; + + // Refer a compile unit so that it's pushed back in LRU. + void referCompileUnitForAddress(object::SectionedAddress Address); +}; + +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_DWARF_DWARFCACHEDDICONTEXT_H diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h @@ -316,6 +316,10 @@ getLineTableForUnit(DWARFUnit *U, function_ref RecoverableErrorHandler); + // Clear the line table object corresponding to a compile unit for memory + // management purpose. When it's referred to again, it'll be re-populated. + void clearLineTableForUnit(DWARFUnit *U); + DataExtractor getStringExtractor() const { return DataExtractor(DObj->getStrSection(), false, 0); } diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -305,6 +305,7 @@ getOrParseLineTable(DWARFDataExtractor &DebugLineData, uint64_t Offset, const DWARFContext &Ctx, const DWARFUnit *U, function_ref RecoverableErrorHandler); + void clearLineTable(uint64_t Offset); /// Helper to allow for parsing of an entire .debug_line section in sequence. class SectionParser { diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt --- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt @@ -2,6 +2,7 @@ DWARFAbbreviationDeclaration.cpp DWARFAddressRange.cpp DWARFAcceleratorTable.cpp + DWARFCachedDIContext.cpp DWARFCompileUnit.cpp DWARFContext.cpp DWARFDataExtractor.cpp diff --git a/llvm/lib/DebugInfo/DWARF/DWARFCachedDIContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFCachedDIContext.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFCachedDIContext.cpp @@ -0,0 +1,93 @@ +//===- DWARFCachedDIContext.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFCachedDIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include + +using namespace llvm; +using namespace object; + +DWARFCachedDIContext::DWARFCachedDIContext(std::unique_ptr DCtx, + int LRUSize) + : DIContext(CK_CACHED_DWARF), Context(std::move(DCtx)), Cache(LRUSize) {} + +DWARFCachedDIContext::~DWARFCachedDIContext() = default; + +void DWARFCachedDIContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { + Context->dump(OS, DumpOpts); +} + +bool DWARFCachedDIContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { + return Context->verify(OS, DumpOpts); +} + +DILineInfo +DWARFCachedDIContext::getLineInfoForAddress(object::SectionedAddress Address, + DILineInfoSpecifier Spec) { + referCompileUnitForAddress(Address); + return Context->getLineInfoForAddress(Address, Spec); +} + +DILineInfoTable DWARFCachedDIContext::getLineInfoForAddressRange( + object::SectionedAddress Address, uint64_t Size, DILineInfoSpecifier Spec) { + referCompileUnitForAddress(Address); + return Context->getLineInfoForAddressRange(Address, Size, Spec); +} + +DIInliningInfo DWARFCachedDIContext::getInliningInfoForAddress( + object::SectionedAddress Address, DILineInfoSpecifier Spec) { + referCompileUnitForAddress(Address); + return Context->getInliningInfoForAddress(Address, Spec); +} + +std::vector +DWARFCachedDIContext::getLocalsForAddress(object::SectionedAddress Address) { + referCompileUnitForAddress(Address); + return Context->getLocalsForAddress(Address); +} + +void DWARFCachedDIContext::referCompileUnitForAddress( + object::SectionedAddress Address) { + DWARFUnit *CU = Context->getCompileUnitForAddress(Address.Address); + DWARFUnit *LRU = Cache.addUnitAndEvictLRU(CU); + if (LRU != nullptr) { + Context->clearLineTableForUnit(LRU); + LRU->clear(); + } +} + +std::unique_ptr DWARFCachedDIContext::create( + const object::ObjectFile &Obj, const LoadedObjectInfo *L, + std::string DWPName, std::function RecoverableErrorHandler, + std::function WarningHandler, int LRUSize) { + auto DCtx = DWARFContext::create(Obj, L, DWPName, RecoverableErrorHandler, + WarningHandler); + return std::make_unique(std::move(DCtx), LRUSize); +} + +DWARFUnit *DWARFUnitLRUCache::addUnitAndEvictLRU(DWARFUnit *Unit) { + if (UnitLRUSize <= 0) + return nullptr; + if (Unit == nullptr) + return nullptr; + DWARFUnit *LRU = nullptr; + auto I = UnitMap.find(Unit); + if (I == UnitMap.end()) { + if (UnitQueue.size() == UnitLRUSize) { + LRU = UnitQueue.back(); + UnitQueue.pop_back(); + UnitMap.erase(LRU); + } + } else { + UnitQueue.erase(I->second); + } + UnitQueue.push_front(Unit); + UnitMap[Unit] = UnitQueue.begin(); + return LRU; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -937,6 +937,22 @@ RecoverableErrorHandler); } +void DWARFContext::clearLineTableForUnit(DWARFUnit *U) { + if (!Line) + return; + + auto UnitDIE = U->getUnitDIE(); + if (!UnitDIE) + return; + + auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); + if (!Offset) + return; + + uint64_t stmtOffset = *Offset + U->getLineTableOffset(); + Line->clearLineTable(stmtOffset); +} + void DWARFContext::parseNormalUnits() { if (!NormalUnits.empty()) return; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -571,6 +571,10 @@ return LT; } +void DWARFDebugLine::clearLineTable(uint64_t Offset) { + LineTableMap.erase(Offset); +} + static StringRef getOpcodeName(uint8_t Opcode, uint8_t OpcodeBase) { assert(Opcode != 0); if (Opcode < OpcodeBase) diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -367,6 +367,9 @@ LocSectionBase = 0; AddrOffsetSectionBase = None; clearDIEs(false); + AddrDieMap.clear(); + if (DWO) + DWO->clear(); DWO.reset(); } diff --git a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp --- a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp +++ b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp @@ -15,7 +15,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/COFF.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFCachedDIContext.h" #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ObjectFile.h" @@ -240,7 +240,7 @@ // probably using PEs and PDBs, and we shouldn't do the override. PE files // generally only contain the names of exported symbols. return FNKind == FunctionNameKind::LinkageName && UseSymbolTable && - isa(DebugInfoContext.get()); + isa(DebugInfoContext.get()); } DILineInfo diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp --- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -16,7 +16,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/BinaryFormat/COFF.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFCachedDIContext.h" #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/DebugInfo/PDB/PDBContext.h" #include "llvm/Demangle/Demangle.h" @@ -67,7 +67,7 @@ if (I != Modules.end()) return symbolizeCodeCommon(I->second.get(), ModuleOffset); - std::unique_ptr Context = DWARFContext::create(Obj); + std::unique_ptr Context = DWARFCachedDIContext::create(Obj); Expected InfoOrErr = createModuleInfo(&Obj, std::move(Context), ModuleName); if (!InfoOrErr) @@ -568,7 +568,8 @@ } } if (!Context) - Context = DWARFContext::create(*Objects.second, nullptr, Opts.DWPName); + Context = + DWARFCachedDIContext::create(*Objects.second, nullptr, Opts.DWPName); return createModuleInfo(Objects.first, std::move(Context), ModuleName); } diff --git a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt --- a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -12,6 +12,7 @@ add_llvm_unittest(DebugInfoDWARFTests DwarfGenerator.cpp DwarfUtils.cpp + DWARFCachedDIContextTest.cpp DWARFAcceleratorTableTest.cpp DWARFDataExtractorTest.cpp DWARFDebugArangeSetTest.cpp diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFCachedDIContextTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFCachedDIContextTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/DebugInfo/DWARF/DWARFCachedDIContextTest.cpp @@ -0,0 +1,109 @@ +//===- DWARFCachedDIContextTest.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFCachedDIContext.h" +#include "DwarfGenerator.h" +#include "DwarfUtils.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" +#include +#include + +using namespace llvm; +using namespace dwarf; +using namespace utils; + +namespace { + +TEST(DWARFUnitLRUCache, LRUSelection) { + Triple Triple = getNormalizedDefaultTargetTriple(); + if (!isConfigurationSupported(Triple)) + return; + + // Create 5 compile units. + auto ExpectedDG = dwarfgen::Generator::create(Triple, 4); + ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded()); + dwarfgen::Generator *DG = ExpectedDG.get().get(); + for (int i = 0; i < 5; i++) { + DG->addCompileUnit(); + } + StringRef FileBytes = DG->generate(); + MemoryBufferRef FileBuffer(FileBytes, "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + ASSERT_TRUE((bool)Obj); + std::unique_ptr Context = DWARFContext::create(**Obj); + std::vector CUs; + for (auto &CUPtr : Context->compile_units()) { + CUs.push_back(CUPtr.get()); + } + ASSERT_EQ(CUs.size(), 5); + + // Create a LRU cache of size 3. + DWARFUnitLRUCache Cache(3); + DWARFUnit *LRU; + + // Add 0. + // Before: [] + // After: [0] + LRU = Cache.addUnitAndEvictLRU(CUs[0]); + ASSERT_EQ(LRU, nullptr); + + // Add 1. + // Before: [0] + // After: [1, 0] + LRU = Cache.addUnitAndEvictLRU(CUs[1]); + ASSERT_EQ(LRU, nullptr); + + // Add 2. + // Before: [1, 0] + // After: [2, 1, 0] + LRU = Cache.addUnitAndEvictLRU(CUs[2]); + ASSERT_EQ(LRU, nullptr); + + // Add 3. + // Before: [2, 1, 0] + // After: [3, 2, 1] + LRU = Cache.addUnitAndEvictLRU(CUs[3]); + ASSERT_EQ(LRU, CUs[0]); + + // Refer to 1. + // Before: [3, 2, 1] + // After: [1, 3, 2] + LRU = Cache.addUnitAndEvictLRU(CUs[1]); + ASSERT_EQ(LRU, nullptr); + + // Add 4. + // Before: [1, 3, 2] + // After: [4, 1, 3] + LRU = Cache.addUnitAndEvictLRU(CUs[4]); + ASSERT_EQ(LRU, CUs[2]); + + // Refer to 3. + // Before: [4, 1, 3] + // After: [3, 4, 1] + LRU = Cache.addUnitAndEvictLRU(CUs[3]); + ASSERT_EQ(LRU, nullptr); + + // Add 0. + // Before: [3, 4, 1] + // After: [0, 3, 4] + LRU = Cache.addUnitAndEvictLRU(CUs[0]); + ASSERT_EQ(LRU, CUs[1]); + + // Add 1. + // Before: [0, 3, 4] + // After: [1, 0, 3] + LRU = Cache.addUnitAndEvictLRU(CUs[1]); + ASSERT_EQ(LRU, CUs[4]); +} + +} // end anonymous namespace