[llvm] Reduce llvm-gsymutil memory usage (PR #91023)

Kevin Frei via llvm-commits llvm-commits at lists.llvm.org
Fri May 3 15:24:47 PDT 2024


https://github.com/kevinfrei created https://github.com/llvm/llvm-project/pull/91023

llvm-gsymutil eats a lot of RAM. On some large binaries, it causes OOM's on smaller hardware, consuming well over 64G 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.

>From af5a901eff93aca32dce5e4cb1e2da067ececdb5 Mon Sep 17 00:00:00 2001
From: Kevin Frei <freik at meta.com>
Date: Tue, 30 Apr 2024 09:15:12 -0700
Subject: [PATCH 1/4] Release line tables when we're done with them

---
 llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
index 601686fdd3dd51..9376515e3712fc 100644
--- a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
+++ b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
@@ -587,6 +587,8 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
       DWARFDie Die = getDie(*CU);
       CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
       handleDie(Out, CUI, Die);
+      // Release the line table, once we're done.
+      DICtx.clearLineTableForUnit(CU.get());
     }
   } else {
     // LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up
@@ -612,7 +614,7 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
       DWARFDie Die = getDie(*CU);
       if (Die) {
         CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));
-        pool.async([this, CUI, &LogMutex, &Out, Die]() mutable {
+        pool.async([this, CUI, &CU, &LogMutex, &Out, Die]() mutable {
           std::string storage;
           raw_string_ostream StrStream(storage);
           OutputAggregator ThreadOut(Out.GetOS() ? &StrStream : nullptr);
@@ -623,6 +625,9 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
             StrStream.flush();
             Out << storage;
           }
+          // Release the line table, once we're done.
+          DICtx.clearLineTableForUnit(CU.get());
+
           Out.Merge(ThreadOut);
         });
       }

>From b5d81807a1fb5fa0133794bfa77d6cbbba74b3b3 Mon Sep 17 00:00:00 2001
From: Kevin Frei <freik at meta.com>
Date: Tue, 30 Apr 2024 14:54:53 -0700
Subject: [PATCH 2/4] Finished without crashing

---
 llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h | 2 ++
 llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp        | 5 +++++
 llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp  | 5 ++++-
 3 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h
index 80c27aea893123..52a8730a46c2cb 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h
@@ -566,6 +566,8 @@ class DWARFUnit {
 
   Error tryExtractDIEsIfNeeded(bool CUDieOnly);
 
+  void freeDIEs();
+
 private:
   /// Size in bytes of the .debug_info data associated with this compile unit.
   size_t getDebugInfoSize() const {
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
index bdd04b00f557bd..4786208c79fc2e 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
@@ -495,6 +495,11 @@ void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
     Context.getRecoverableErrorHandler()(std::move(e));
 }
 
+void DWARFUnit::freeDIEs() {
+  if (DieArray.capacity())
+    std::vector<DWARFDebugInfoEntry>().swap(DieArray);
+}
+
 Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) {
   if ((CUDieOnly && !DieArray.empty()) ||
       DieArray.size() > 1)
diff --git a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
index 9376515e3712fc..7204cefae1b844 100644
--- a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
+++ b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
@@ -625,8 +625,9 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
             StrStream.flush();
             Out << storage;
           }
-          // Release the line table, once we're done.
+          // Release the line table and DIEs once we're done.
           DICtx.clearLineTableForUnit(CU.get());
+          CU->freeDIEs();
 
           Out.Merge(ThreadOut);
         });
@@ -634,6 +635,8 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
     }
     pool.wait();
   }
+  for (const auto &CU : DICtx.compile_units())
+    CU->freeDIEs();
   size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore;
   Out << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n";
   return Error::success();

>From 83316aa8b43e2da3374bed4e0b410f3985ef5f48 Mon Sep 17 00:00:00 2001
From: Kevin Frei <freik at meta.com>
Date: Wed, 1 May 2024 14:58:26 -0700
Subject: [PATCH 3/4] Added locking

---
 llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h | 10 +++--
 llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp        | 41 ++++++++++++++-----
 llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp  | 13 +++---
 3 files changed, 44 insertions(+), 20 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h
index 52a8730a46c2cb..9614aab8bb9b50 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h
@@ -22,6 +22,7 @@
 #include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h"
 #include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
 #include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/RWMutex.h"
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
@@ -257,6 +258,9 @@ class DWARFUnit {
 
   std::shared_ptr<DWARFUnit> DWO;
 
+  mutable llvm::sys::RWMutex m_cu_die_array_mutex;
+  mutable llvm::sys::RWMutex m_all_die_array_mutex;
+
 protected:
   friend dwarf_linker::parallel::CompileUnit;
 
@@ -566,7 +570,8 @@ class DWARFUnit {
 
   Error tryExtractDIEsIfNeeded(bool CUDieOnly);
 
-  void freeDIEs();
+  /// clearDIEs - Clear parsed DIEs to keep memory usage low.
+  void clearDIEs(bool KeepCUDie);
 
 private:
   /// Size in bytes of the .debug_info data associated with this compile unit.
@@ -583,9 +588,6 @@ class DWARFUnit {
   void extractDIEsToVector(bool AppendCUDie, bool AppendNonCUDIEs,
                            std::vector<DWARFDebugInfoEntry> &DIEs) const;
 
-  /// clearDIEs - Clear parsed DIEs to keep memory usage low.
-  void clearDIEs(bool KeepCUDie);
-
   /// parseDWO - Parses .dwo file for current compile unit. Returns true if
   /// it was actually constructed.
   /// The \p AlternativeLocation specifies an alternative location to get
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
index 4786208c79fc2e..b3fdb4e017803b 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
@@ -495,26 +495,43 @@ void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
     Context.getRecoverableErrorHandler()(std::move(e));
 }
 
-void DWARFUnit::freeDIEs() {
-  if (DieArray.capacity())
-    std::vector<DWARFDebugInfoEntry>().swap(DieArray);
-}
-
 Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) {
-  if ((CUDieOnly && !DieArray.empty()) ||
-      DieArray.size() > 1)
-    return Error::success(); // Already parsed.
-
-  bool HasCUDie = !DieArray.empty();
-  extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray);
+  // read-lock:
+  {
+    llvm::sys::ScopedReader Lock(m_cu_die_array_mutex);
+    if ((CUDieOnly && !DieArray.empty()) ||
+        DieArray.size() > 1)
+      return Error::success(); // Already parsed.
+  }
+  bool HasCUDie = false;
+  // write-lock:
+  {
+    llvm::sys::ScopedWriter Lock(m_cu_die_array_mutex);
+    if ((CUDieOnly && !DieArray.empty()) ||
+        DieArray.size() > 1)
+      return Error::success(); // Already parsed.
+    HasCUDie = !DieArray.empty();
+    extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray);
+  }
 
+  // read-lock:
+{
+  llvm::sys::ScopedReader Lock(m_all_die_array_mutex);
   if (DieArray.empty())
     return Error::success();
 
   // If CU DIE was just parsed, copy several attribute values from it.
   if (HasCUDie)
     return Error::success();
+}
+  // write-lock:
+llvm::sys::ScopedWriter Lock(m_all_die_array_mutex);
+  if (DieArray.empty())
+    return Error::success();
 
+  // If CU DIE was just parsed, copy several attribute values from it.
+  if (HasCUDie)
+    return Error::success();
   DWARFDie UnitDie(this, &DieArray[0]);
   if (std::optional<uint64_t> DWOId =
           toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id)))
@@ -663,6 +680,8 @@ void DWARFUnit::clearDIEs(bool KeepCUDie) {
   // It depends on the implementation whether the request is fulfilled.
   // Create a new vector with a small capacity and assign it to the DieArray to
   // have previous contents freed.
+  llvm::sys::ScopedWriter CULock(m_cu_die_array_mutex);
+  llvm::sys::ScopedWriter AllLock(m_all_die_array_mutex);
   DieArray = (KeepCUDie && !DieArray.empty())
                  ? std::vector<DWARFDebugInfoEntry>({DieArray[0]})
                  : std::vector<DWARFDebugInfoEntry>();
diff --git a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
index 7204cefae1b844..bd4c24f3ee28e1 100644
--- a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
+++ b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
@@ -619,24 +619,27 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
           raw_string_ostream StrStream(storage);
           OutputAggregator ThreadOut(Out.GetOS() ? &StrStream : nullptr);
           handleDie(ThreadOut, CUI, Die);
+          // Release the line table once we're done.
+          DICtx.clearLineTableForUnit(CU.get());
+          // Free any DIEs that were allocated by the DWARF parser.
+          // If/when they're needed by other CU's, they'll be recreated.
+          CU->clearDIEs(false);
           // Print ThreadLogStorage lines into an actual stream under a lock
           std::lock_guard<std::mutex> guard(LogMutex);
           if (Out.GetOS()) {
             StrStream.flush();
             Out << storage;
           }
-          // Release the line table and DIEs once we're done.
-          DICtx.clearLineTableForUnit(CU.get());
-          CU->freeDIEs();
-
           Out.Merge(ThreadOut);
+
         });
       }
     }
     pool.wait();
   }
+  // Now get rid of all the DIEs that may have been recreated
   for (const auto &CU : DICtx.compile_units())
-    CU->freeDIEs();
+    CU->clearDIEs(false);
   size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore;
   Out << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n";
   return Error::success();

>From 3479f13a6b787ae34b9f6e0c0b16365bdbf4af3a Mon Sep 17 00:00:00 2001
From: Kevin Frei <freik at meta.com>
Date: Fri, 3 May 2024 09:26:24 -0700
Subject: [PATCH 4/4] Code formatting

---
 llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp       | 28 ++++++++------------
 llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp |  4 ++-
 2 files changed, 14 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
index b3fdb4e017803b..cc79d9ec7130c9 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
@@ -496,42 +496,36 @@ void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
 }
 
 Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) {
-  // read-lock:
   {
     llvm::sys::ScopedReader Lock(m_cu_die_array_mutex);
-    if ((CUDieOnly && !DieArray.empty()) ||
-        DieArray.size() > 1)
+    if ((CUDieOnly && !DieArray.empty()) || DieArray.size() > 1)
       return Error::success(); // Already parsed.
   }
   bool HasCUDie = false;
-  // write-lock:
   {
     llvm::sys::ScopedWriter Lock(m_cu_die_array_mutex);
-    if ((CUDieOnly && !DieArray.empty()) ||
-        DieArray.size() > 1)
+    if ((CUDieOnly && !DieArray.empty()) || DieArray.size() > 1)
       return Error::success(); // Already parsed.
     HasCUDie = !DieArray.empty();
     extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray);
   }
+  {
+    llvm::sys::ScopedReader Lock(m_all_die_array_mutex);
+    if (DieArray.empty())
+      return Error::success();
 
-  // read-lock:
-{
-  llvm::sys::ScopedReader Lock(m_all_die_array_mutex);
+    // If CU DIE was just parsed, copy several attribute values from it.
+    if (HasCUDie)
+      return Error::success();
+  }
+  llvm::sys::ScopedWriter Lock(m_all_die_array_mutex);
   if (DieArray.empty())
     return Error::success();
 
   // If CU DIE was just parsed, copy several attribute values from it.
   if (HasCUDie)
     return Error::success();
-}
-  // write-lock:
-llvm::sys::ScopedWriter Lock(m_all_die_array_mutex);
-  if (DieArray.empty())
-    return Error::success();
 
-  // If CU DIE was just parsed, copy several attribute values from it.
-  if (HasCUDie)
-    return Error::success();
   DWARFDie UnitDie(this, &DieArray[0]);
   if (std::optional<uint64_t> DWOId =
           toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id)))
diff --git a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
index bd4c24f3ee28e1..4a1ed91a7244f2 100644
--- a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
+++ b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
@@ -589,6 +589,9 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
       handleDie(Out, CUI, Die);
       // Release the line table, once we're done.
       DICtx.clearLineTableForUnit(CU.get());
+      // Free any DIEs that were allocated by the DWARF parser.
+      // If/when they're needed by other CU's, they'll be recreated.
+      CU->clearDIEs(false);
     }
   } else {
     // LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up
@@ -631,7 +634,6 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
             Out << storage;
           }
           Out.Merge(ThreadOut);
-
         });
       }
     }



More information about the llvm-commits mailing list