[Lldb-commits] [lldb] a2154b1 - Cache the manual DWARF index out to the LLDB cache directory when the LLDB index cache is enabled.

Greg Clayton via lldb-commits lldb-commits at lists.llvm.org
Tue Dec 28 11:00:39 PST 2021


Author: Greg Clayton
Date: 2021-12-28T11:00:28-08:00
New Revision: a2154b19515304f42000160bed820630c3780db8

URL: https://github.com/llvm/llvm-project/commit/a2154b19515304f42000160bed820630c3780db8
DIFF: https://github.com/llvm/llvm-project/commit/a2154b19515304f42000160bed820630c3780db8.diff

LOG: Cache the manual DWARF index out to the LLDB cache directory when the LLDB index cache is enabled.

This patch add the ability to cache the manual DWARF indexing results to disk for faster subsequent debug sessions. Manual DWARF indexing is time consuming and causes all DWARF to be fully parsed and indexed each time you debug a binary that doesn't have an acceptable accelerator table. Acceptable accelerator tables include .debug_names in DWARF5 or Apple accelerator tables.

This patch breaks up testing by testing all of the encoding and decoding of required C++ objects in a gtest unit test, and then has a test to verify the debug info cache is generated correctly.

This patch also adds the ability to track when a symbol table or DWARF index is loaded or saved to the cache in the "statistics dump" command. This is essential to know in statistics as it can help explain why a debug session was slower or faster than expected.

Reviewed By: labath, wallace

Differential Revision: https://reviews.llvm.org/D115951

Added: 
    lldb/test/API/functionalities/module_cache/debug_index/TestDebugIndexCache.py
    lldb/test/API/functionalities/module_cache/debug_index/exe.yaml
    lldb/unittests/SymbolFile/DWARF/DWARFIndexCachingTest.cpp

Modified: 
    lldb/include/lldb/Symbol/SymbolFile.h
    lldb/include/lldb/Symbol/Symtab.h
    lldb/include/lldb/Target/Statistics.h
    lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp
    lldb/source/Plugins/SymbolFile/DWARF/DIERef.h
    lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
    lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
    lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp
    lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h
    lldb/source/Symbol/Symtab.cpp
    lldb/source/Target/Statistics.cpp
    lldb/test/API/commands/statistics/basic/TestStats.py
    lldb/test/API/functionalities/module_cache/simple_exe/TestModuleCacheSimple.py
    lldb/unittests/SymbolFile/DWARF/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index 7c0365483c123..288576b978a78 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -67,8 +67,7 @@ class SymbolFile : public PluginInterface {
 
   // Constructors and Destructors
   SymbolFile(lldb::ObjectFileSP objfile_sp)
-      : m_objfile_sp(std::move(objfile_sp)), m_abilities(0),
-        m_calculated_abilities(false) {}
+      : m_objfile_sp(std::move(objfile_sp)) {}
 
   ~SymbolFile() override = default;
 
@@ -326,6 +325,29 @@ class SymbolFile : public PluginInterface {
   /// hasn't been indexed yet, or a valid duration if it has.
   virtual StatsDuration GetDebugInfoIndexTime() { return StatsDuration(0.0); }
 
+  /// Accessors for the bool that indicates if the debug info index was loaded
+  /// from, or saved to the module index cache.
+  ///
+  /// In statistics it is handy to know if a module's debug info was loaded from
+  /// or saved to the cache. When the debug info index is loaded from the cache
+  /// startup times can be faster. When the cache is enabled and the debug info
+  /// index is saved to the cache, debug sessions can be slower. These accessors
+  /// can be accessed by the statistics and emitted to help track these costs.
+  /// \{
+  bool GetDebugInfoIndexWasLoadedFromCache() const {
+    return m_index_was_loaded_from_cache;
+  }
+  void SetDebugInfoIndexWasLoadedFromCache() {
+    m_index_was_loaded_from_cache = true;
+  }
+  bool GetDebugInfoIndexWasSavedToCache() const {
+    return m_index_was_saved_to_cache;
+  }
+  void SetDebugInfoIndexWasSavedToCache() {
+    m_index_was_saved_to_cache = true;
+  }
+  /// \}
+
 protected:
   void AssertModuleLock();
   virtual uint32_t CalculateNumCompileUnits() = 0;
@@ -341,8 +363,10 @@ class SymbolFile : public PluginInterface {
   llvm::Optional<std::vector<lldb::CompUnitSP>> m_compile_units;
   TypeList m_type_list;
   Symtab *m_symtab = nullptr;
-  uint32_t m_abilities;
-  bool m_calculated_abilities;
+  uint32_t m_abilities = 0;
+  bool m_calculated_abilities = false;
+  bool m_index_was_loaded_from_cache = false;
+  bool m_index_was_saved_to_cache = false;
 
 private:
   SymbolFile(const SymbolFile &) = delete;

diff  --git a/lldb/include/lldb/Symbol/Symtab.h b/lldb/include/lldb/Symbol/Symtab.h
index fe0a82306c4f5..504b49c026742 100644
--- a/lldb/include/lldb/Symbol/Symtab.h
+++ b/lldb/include/lldb/Symbol/Symtab.h
@@ -212,6 +212,30 @@ class Symtab {
   ///   false if the symbol table wasn't cached or was out of date.
   bool LoadFromCache();
 
+
+  /// Accessors for the bool that indicates if the debug info index was loaded
+  /// from, or saved to the module index cache.
+  ///
+  /// In statistics it is handy to know if a module's debug info was loaded from
+  /// or saved to the cache. When the debug info index is loaded from the cache
+  /// startup times can be faster. When the cache is enabled and the debug info
+  /// index is saved to the cache, debug sessions can be slower. These accessors
+  /// can be accessed by the statistics and emitted to help track these costs.
+  /// \{
+  bool GetWasLoadedFromCache() const {
+    return m_loaded_from_cache;
+  }
+  void SetWasLoadedFromCache() {
+    m_loaded_from_cache = true;
+  }
+  bool GetWasSavedToCache() const {
+    return m_saved_to_cache;
+  }
+  void SetWasSavedToCache() {
+    m_saved_to_cache = true;
+  }
+  /// \}
+
 protected:
   typedef std::vector<Symbol> collection;
   typedef collection::iterator iterator;
@@ -252,7 +276,8 @@ class Symtab {
       m_name_to_symbol_indices;
   mutable std::recursive_mutex
       m_mutex; // Provide thread safety for this symbol table
-  bool m_file_addr_to_index_computed : 1, m_name_indexes_computed : 1;
+  bool m_file_addr_to_index_computed : 1, m_name_indexes_computed : 1,
+    m_loaded_from_cache : 1, m_saved_to_cache : 1;
 
 private:
   UniqueCStringMap<uint32_t> &

diff  --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h
index 087fbee263287..cf4fb83c816ea 100644
--- a/lldb/include/lldb/Target/Statistics.h
+++ b/lldb/include/lldb/Target/Statistics.h
@@ -84,6 +84,10 @@ struct ModuleStats {
   double debug_parse_time = 0.0;
   double debug_index_time = 0.0;
   uint64_t debug_info_size = 0;
+  bool symtab_loaded_from_cache = false;
+  bool symtab_saved_to_cache = false;
+  bool debug_info_index_loaded_from_cache = false;
+  bool debug_info_index_saved_to_cache = false;
 };
 
 /// A class that represents statistics for a since lldb_private::Target.

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp
index 7a8ab9c9bcfd1..25cb368763c16 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp
@@ -7,8 +7,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "DIERef.h"
+#include "lldb/Utility/DataEncoder.h"
+#include "lldb/Utility/DataExtractor.h"
 #include "llvm/Support/Format.h"
 
+using namespace lldb;
+using namespace lldb_private;
+
 void llvm::format_provider<DIERef>::format(const DIERef &ref, raw_ostream &OS,
                                            StringRef Style) {
   if (ref.dwo_num())
@@ -16,3 +21,35 @@ void llvm::format_provider<DIERef>::format(const DIERef &ref, raw_ostream &OS,
   OS << (ref.section() == DIERef::DebugInfo ? "INFO" : "TYPE");
   OS << "/" << format_hex_no_prefix(ref.die_offset(), 8);
 }
+
+constexpr uint32_t k_dwo_num_mask = 0x3FFFFFFF;
+constexpr uint32_t k_dwo_num_valid_bitmask = (1u << 30);
+constexpr uint32_t k_section_bitmask = (1u << 31);
+
+llvm::Optional<DIERef> DIERef::Decode(const DataExtractor &data,
+                                      lldb::offset_t *offset_ptr) {
+  const uint32_t bitfield_storage = data.GetU32(offset_ptr);
+  uint32_t dwo_num = bitfield_storage & k_dwo_num_mask;
+  bool dwo_num_valid = (bitfield_storage & (k_dwo_num_valid_bitmask)) != 0;
+  Section section = (Section)((bitfield_storage & (k_section_bitmask)) != 0);
+  // DIE offsets can't be zero and if we fail to decode something from data,
+  // it will return 0
+  dw_offset_t die_offset = data.GetU32(offset_ptr);
+  if (die_offset == 0)
+    return llvm::None;
+  if (dwo_num_valid)
+    return DIERef(dwo_num, section, die_offset);
+  else
+    return DIERef(llvm::None, section, die_offset);
+}
+
+void DIERef::Encode(DataEncoder &encoder) const {
+  uint32_t bitfield_storage = m_dwo_num;
+  if (m_dwo_num_valid)
+    bitfield_storage |= k_dwo_num_valid_bitmask;
+  if (m_section)
+    bitfield_storage |= k_section_bitmask;
+  encoder.AppendU32(bitfield_storage);
+  static_assert(sizeof(m_die_offset) == 4, "m_die_offset must be 4 bytes");
+  encoder.AppendU32(m_die_offset);
+}

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h
index f7e09ee17283e..23e1eec26ec3f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h
@@ -54,6 +54,37 @@ class DIERef {
     return m_die_offset < other.m_die_offset;
   }
 
+  bool operator==(const DIERef &rhs) const {
+    return dwo_num() == rhs.dwo_num() && m_section == rhs.m_section &&
+           m_die_offset == rhs.m_die_offset;
+  }
+
+  bool operator!=(const DIERef &rhs) const { return !(*this == rhs); }
+
+  /// Decode a serialized version of this object from data.
+  ///
+  /// \param data
+  ///   The decoder object that references the serialized data.
+  ///
+  /// \param offset_ptr
+  ///   A pointer that contains the offset from which the data will be decoded
+  ///   from that gets updated as data gets decoded.
+  ///
+  /// \return
+  ///   Returns a valid DIERef if decoding succeeded, llvm::None if there was
+  ///   unsufficient or invalid values that were decoded.
+  static llvm::Optional<DIERef> Decode(const lldb_private::DataExtractor &data,
+                                       lldb::offset_t *offset_ptr);
+
+  /// Encode this object into a data encoder object.
+  ///
+  /// This allows this object to be serialized to disk.
+  ///
+  /// \param encoder
+  ///   A data encoder object that serialized bytes will be encoded into.
+  ///
+  void Encode(lldb_private::DataEncoder &encoder) const;
+
 private:
   uint32_t m_dwo_num : 30;
   uint32_t m_dwo_num_valid : 1;

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
index ab10e9ca98f96..e15a22affcb25 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
@@ -12,9 +12,12 @@
 #include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
 #include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
+#include "lldb/Core/DataFileCache.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/Progress.h"
 #include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/DataEncoder.h"
+#include "lldb/Utility/DataExtractor.h"
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/Timer.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -24,17 +27,19 @@ using namespace lldb_private;
 using namespace lldb;
 
 void ManualDWARFIndex::Index() {
-  if (!m_dwarf)
+  if (m_indexed)
     return;
-
-  SymbolFileDWARF &main_dwarf = *m_dwarf;
-  m_dwarf = nullptr;
+  m_indexed = true;
 
   ElapsedTime elapsed(m_index_time);
-  LLDB_SCOPED_TIMERF("%p", static_cast<void *>(&main_dwarf));
+  LLDB_SCOPED_TIMERF("%p", static_cast<void *>(m_dwarf));
+  if (LoadFromCache()) {
+    m_dwarf->SetDebugInfoIndexWasLoadedFromCache();
+    return;
+  }
 
-  DWARFDebugInfo &main_info = main_dwarf.DebugInfo();
-  SymbolFileDWARFDwo *dwp_dwarf = main_dwarf.GetDwpSymbolFile().get();
+  DWARFDebugInfo &main_info = m_dwarf->DebugInfo();
+  SymbolFileDWARFDwo *dwp_dwarf = m_dwarf->GetDwpSymbolFile().get();
   DWARFDebugInfo *dwp_info = dwp_dwarf ? &dwp_dwarf->DebugInfo() : nullptr;
 
   std::vector<DWARFUnit *> units_to_index;
@@ -125,6 +130,8 @@ void ManualDWARFIndex::Index() {
   pool.async(finalize_fn, &IndexSet::types);
   pool.async(finalize_fn, &IndexSet::namespaces);
   pool.wait();
+
+  SaveToCache();
 }
 
 void ManualDWARFIndex::IndexUnit(DWARFUnit &unit, SymbolFileDWARFDwo *dwp,
@@ -480,3 +487,214 @@ void ManualDWARFIndex::Dump(Stream &s) {
   s.Printf("\nNamespaces:\n");
   m_set.namespaces.Dump(&s);
 }
+
+constexpr llvm::StringLiteral kIdentifierManualDWARFIndex("DIDX");
+// Define IDs for the 
diff erent tables when encoding and decoding the
+// ManualDWARFIndex NameToDIE objects so we can avoid saving any empty maps.
+enum DataID {
+  kDataIDFunctionBasenames = 1u,
+  kDataIDFunctionFullnames,
+  kDataIDFunctionMethods,
+  kDataIDFunctionSelectors,
+  kDataIDFunctionObjcClassSelectors,
+  kDataIDGlobals,
+  kDataIDTypes,
+  kDataIDNamespaces,
+  kDataIDEnd = 255u,
+
+};
+constexpr uint32_t CURRENT_CACHE_VERSION = 1;
+
+bool ManualDWARFIndex::IndexSet::Decode(const DataExtractor &data,
+                                        lldb::offset_t *offset_ptr) {
+  StringTableReader strtab;
+  // We now decode the string table for all strings in the data cache file.
+  if (!strtab.Decode(data, offset_ptr))
+    return false;
+
+  llvm::StringRef identifier((const char *)data.GetData(offset_ptr, 4), 4);
+  if (identifier != kIdentifierManualDWARFIndex)
+    return false;
+  const uint32_t version = data.GetU32(offset_ptr);
+  if (version != CURRENT_CACHE_VERSION)
+    return false;
+
+  bool done = false;
+  while (!done) {
+    switch (data.GetU8(offset_ptr)) {
+    default:
+      // If we got here, this is not expected, we expect the data IDs to match
+      // one of the values from the DataID enumeration.
+      return false;
+    case kDataIDFunctionBasenames:
+      if (!function_basenames.Decode(data, offset_ptr, strtab))
+        return false;
+      break;
+    case kDataIDFunctionFullnames:
+      if (!function_fullnames.Decode(data, offset_ptr, strtab))
+        return false;
+      break;
+    case kDataIDFunctionMethods:
+      if (!function_methods.Decode(data, offset_ptr, strtab))
+        return false;
+      break;
+    case kDataIDFunctionSelectors:
+      if (!function_selectors.Decode(data, offset_ptr, strtab))
+        return false;
+      break;
+    case kDataIDFunctionObjcClassSelectors:
+      if (!objc_class_selectors.Decode(data, offset_ptr, strtab))
+        return false;
+      break;
+    case kDataIDGlobals:
+      if (!globals.Decode(data, offset_ptr, strtab))
+        return false;
+      break;
+    case kDataIDTypes:
+      if (!types.Decode(data, offset_ptr, strtab))
+        return false;
+      break;
+    case kDataIDNamespaces:
+      if (!namespaces.Decode(data, offset_ptr, strtab))
+        return false;
+      break;
+    case kDataIDEnd:
+      // We got to the end of our NameToDIE encodings.
+      done = true;
+      break;
+    }
+  }
+  // Success!
+  return true;
+}
+
+void ManualDWARFIndex::IndexSet::Encode(DataEncoder &encoder) const {
+  ConstStringTable strtab;
+
+  // Encoder the DWARF index into a separate encoder first. This allows us
+  // gather all of the strings we willl need in "strtab" as we will need to
+  // write the string table out before the symbol table.
+  DataEncoder index_encoder(encoder.GetByteOrder(),
+                            encoder.GetAddressByteSize());
+
+  index_encoder.AppendData(kIdentifierManualDWARFIndex);
+  // Encode the data version.
+  index_encoder.AppendU32(CURRENT_CACHE_VERSION);
+
+  if (!function_basenames.IsEmpty()) {
+    index_encoder.AppendU8(kDataIDFunctionBasenames);
+    function_basenames.Encode(index_encoder, strtab);
+  }
+  if (!function_fullnames.IsEmpty()) {
+    index_encoder.AppendU8(kDataIDFunctionFullnames);
+    function_fullnames.Encode(index_encoder, strtab);
+  }
+  if (!function_methods.IsEmpty()) {
+    index_encoder.AppendU8(kDataIDFunctionMethods);
+    function_methods.Encode(index_encoder, strtab);
+  }
+  if (!function_selectors.IsEmpty()) {
+    index_encoder.AppendU8(kDataIDFunctionSelectors);
+    function_selectors.Encode(index_encoder, strtab);
+  }
+  if (!objc_class_selectors.IsEmpty()) {
+    index_encoder.AppendU8(kDataIDFunctionObjcClassSelectors);
+    objc_class_selectors.Encode(index_encoder, strtab);
+  }
+  if (!globals.IsEmpty()) {
+    index_encoder.AppendU8(kDataIDGlobals);
+    globals.Encode(index_encoder, strtab);
+  }
+  if (!types.IsEmpty()) {
+    index_encoder.AppendU8(kDataIDTypes);
+    types.Encode(index_encoder, strtab);
+  }
+  if (!namespaces.IsEmpty()) {
+    index_encoder.AppendU8(kDataIDNamespaces);
+    namespaces.Encode(index_encoder, strtab);
+  }
+  index_encoder.AppendU8(kDataIDEnd);
+
+  // Now that all strings have been gathered, we will emit the string table.
+  strtab.Encode(encoder);
+  // Followed the the symbol table data.
+  encoder.AppendData(index_encoder.GetData());
+}
+
+bool ManualDWARFIndex::Decode(const DataExtractor &data,
+                              lldb::offset_t *offset_ptr,
+                              bool &signature_mismatch) {
+  signature_mismatch = false;
+  CacheSignature signature;
+  if (!signature.Decode(data, offset_ptr))
+    return false;
+  if (CacheSignature(m_dwarf->GetObjectFile()) != signature) {
+    signature_mismatch = true;
+    return false;
+  }
+  IndexSet set;
+  if (!set.Decode(data, offset_ptr))
+    return false;
+  m_set = std::move(set);
+  return true;
+}
+
+bool ManualDWARFIndex::Encode(DataEncoder &encoder) const {
+  CacheSignature signature(m_dwarf->GetObjectFile());
+  if (!signature.Encode(encoder))
+    return false;
+  m_set.Encode(encoder);
+  return true;
+}
+
+std::string ManualDWARFIndex::GetCacheKey() {
+  std::string key;
+  llvm::raw_string_ostream strm(key);
+  // DWARF Index can come from 
diff erent object files for the same module. A
+  // module can have one object file as the main executable and might have
+  // another object file in a separate symbol file, or we might have a .dwo file
+  // that claims its module is the main executable.
+  ObjectFile *objfile = m_dwarf->GetObjectFile();
+  strm << objfile->GetModule()->GetCacheKey() << "-dwarf-index-"
+      << llvm::format_hex(objfile->GetCacheHash(), 10);
+  return strm.str();
+}
+
+bool ManualDWARFIndex::LoadFromCache() {
+  DataFileCache *cache = Module::GetIndexCache();
+  if (!cache)
+    return false;
+  ObjectFile *objfile = m_dwarf->GetObjectFile();
+  if (!objfile)
+    return false;
+  std::unique_ptr<llvm::MemoryBuffer> mem_buffer_up =
+      cache->GetCachedData(GetCacheKey());
+  if (!mem_buffer_up)
+    return false;
+  DataExtractor data(mem_buffer_up->getBufferStart(),
+                     mem_buffer_up->getBufferSize(),
+                     endian::InlHostByteOrder(),
+                     objfile->GetAddressByteSize());
+  bool signature_mismatch = false;
+  lldb::offset_t offset = 0;
+  const bool result = Decode(data, &offset, signature_mismatch);
+  if (signature_mismatch)
+    cache->RemoveCacheFile(GetCacheKey());
+  return result;
+}
+
+void ManualDWARFIndex::SaveToCache() {
+  DataFileCache *cache = Module::GetIndexCache();
+  if (!cache)
+    return; // Caching is not enabled.
+  ObjectFile *objfile = m_dwarf->GetObjectFile();
+  if (!objfile)
+    return;
+  DataEncoder file(endian::InlHostByteOrder(), objfile->GetAddressByteSize());
+  // Encode will return false if the object file doesn't have anything to make
+  // a signature from.
+  if (Encode(file)) {
+    if (cache->SetCachedData(GetCacheKey(), file.GetData()))
+      m_dwarf->SetDebugInfoIndexWasSavedToCache();
+  }
+}

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
index 36f371402b908..5c5e43de9ca65 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
@@ -55,7 +55,7 @@ class ManualDWARFIndex : public DWARFIndex {
 
   void Dump(Stream &s) override;
 
-private:
+  // Make IndexSet public so we can unit test the encoding and decoding logic.
   struct IndexSet {
     NameToDIE function_basenames;
     NameToDIE function_fullnames;
@@ -65,21 +65,113 @@ class ManualDWARFIndex : public DWARFIndex {
     NameToDIE globals;
     NameToDIE types;
     NameToDIE namespaces;
+    bool Decode(const DataExtractor &data, lldb::offset_t *offset_ptr);
+    void Encode(DataEncoder &encoder) const;
+    bool operator==(const IndexSet &rhs) const {
+      return function_basenames == rhs.function_basenames &&
+             function_fullnames == rhs.function_fullnames &&
+             function_methods == rhs.function_methods &&
+             function_selectors == rhs.function_selectors &&
+             objc_class_selectors == rhs.objc_class_selectors &&
+             globals == rhs.globals && types == rhs.types &&
+             namespaces == rhs.namespaces;
+    }
   };
+
+private:
   void Index();
+
+  /// Decode a serialized version of this object from data.
+  ///
+  /// \param data
+  ///   The decoder object that references the serialized data.
+  ///
+  /// \param offset_ptr
+  ///   A pointer that contains the offset from which the data will be decoded
+  ///   from that gets updated as data gets decoded.
+  ///
+  /// \param strtab
+  ///   All strings in cache files are put into string tables for efficiency
+  ///   and cache file size reduction. Strings are stored as uint32_t string
+  ///   table offsets in the cache data.
+  bool Decode(const DataExtractor &data, lldb::offset_t *offset_ptr,
+              bool &signature_mismatch);
+
+  /// Encode this object into a data encoder object.
+  ///
+  /// This allows this object to be serialized to disk.
+  ///
+  /// \param encoder
+  ///   A data encoder object that serialized bytes will be encoded into.
+  ///
+  /// \param strtab
+  ///   All strings in cache files are put into string tables for efficiency
+  ///   and cache file size reduction. Strings are stored as uint32_t string
+  ///   table offsets in the cache data.
+  ///
+  /// \return
+  ///   True if the symbol table's object file can generate a valid signature
+  ///   and all data for the symbol table was encoded, false otherwise.
+  bool Encode(DataEncoder &encoder) const;
+
+  /// Get the cache key string for this symbol table.
+  ///
+  /// The cache key must start with the module's cache key and is followed
+  /// by information that indicates this key is for caching the symbol table
+  /// contents and should also include the has of the object file. A module can
+  /// be represented by an ObjectFile object for the main executable, but can
+  /// also have a symbol file that is from the same or a 
diff erent object file.
+  /// This means we might have two symbol tables cached in the index cache, one
+  /// for the main executable and one for the symbol file.
+  ///
+  /// \return
+  ///   The unique cache key used to save and retrieve data from the index
+  ///   cache.
+  std::string GetCacheKey();
+
+  /// Save the symbol table data out into a cache.
+  ///
+  /// The symbol table will only be saved to a cache file if caching is enabled.
+  ///
+  /// We cache the contents of the symbol table since symbol tables in LLDB take
+  /// some time to initialize. This is due to the many sources for data that are
+  /// used to create a symbol table:
+  /// - standard symbol table
+  /// - dynamic symbol table (ELF)
+  /// - compressed debug info sections
+  /// - unwind information
+  /// - function pointers found in runtimes for global constructor/destructors
+  /// - other sources.
+  /// All of the above sources are combined and one symbol table results after
+  /// all sources have been considered.
+  void SaveToCache();
+
+  /// Load the symbol table from the index cache.
+  ///
+  /// Quickly load the finalized symbol table from the index cache. This saves
+  /// time when the debugger starts up. The index cache file for the symbol
+  /// table has the modification time set to the same time as the main module.
+  /// If the cache file exists and the modification times match, we will load
+  /// the symbol table from the serlized cache file.
+  ///
+  /// \return
+  ///   True if the symbol table was successfully loaded from the index cache,
+  ///   false if the symbol table wasn't cached or was out of date.
+  bool LoadFromCache();
+
   void IndexUnit(DWARFUnit &unit, SymbolFileDWARFDwo *dwp, IndexSet &set);
 
   static void IndexUnitImpl(DWARFUnit &unit,
                             const lldb::LanguageType cu_language,
                             IndexSet &set);
 
-  /// The DWARF file which we are indexing. Set to nullptr after the index is
-  /// built.
+  /// The DWARF file which we are indexing.
   SymbolFileDWARF *m_dwarf;
   /// Which dwarf units should we skip while building the index.
   llvm::DenseSet<dw_offset_t> m_units_to_avoid;
 
   IndexSet m_set;
+  bool m_indexed = false;
 };
 } // namespace lldb_private
 

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp b/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp
index 493d1b4a27023..33e2695f403a2 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp
@@ -8,8 +8,11 @@
 
 #include "NameToDIE.h"
 #include "DWARFUnit.h"
+#include "lldb/Core/DataFileCache.h"
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataEncoder.h"
+#include "lldb/Utility/DataExtractor.h"
 #include "lldb/Utility/RegularExpression.h"
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StreamString.h"
@@ -87,3 +90,50 @@ void NameToDIE::Append(const NameToDIE &other) {
                  other.m_map.GetValueAtIndexUnchecked(i));
   }
 }
+
+constexpr llvm::StringLiteral kIdentifierNameToDIE("N2DI");
+
+bool NameToDIE::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr,
+                       const StringTableReader &strtab) {
+  m_map.Clear();
+  llvm::StringRef identifier((const char *)data.GetData(offset_ptr, 4), 4);
+  if (identifier != kIdentifierNameToDIE)
+    return false;
+  const uint32_t count = data.GetU32(offset_ptr);
+  for (uint32_t i = 0; i < count; ++i) {
+    llvm::StringRef str(strtab.Get(data.GetU32(offset_ptr)));
+    // No empty strings allowed in the name to DIE maps.
+    if (str.empty())
+      return false;
+    if (llvm::Optional<DIERef> die_ref = DIERef::Decode(data, offset_ptr))
+      m_map.Append(ConstString(str), die_ref.getValue());
+    else
+      return false;
+  }
+  return true;
+}
+
+void NameToDIE::Encode(DataEncoder &encoder, ConstStringTable &strtab) const {
+  encoder.AppendData(kIdentifierNameToDIE);
+  encoder.AppendU32(m_map.GetSize());
+  for (const auto &entry : m_map) {
+    // Make sure there are no empty strings.
+    assert((bool)entry.cstring);
+    encoder.AppendU32(strtab.Add(entry.cstring));
+    entry.value.Encode(encoder);
+  }
+}
+
+bool NameToDIE::operator==(const NameToDIE &rhs) const {
+  const size_t size = m_map.GetSize();
+  if (size != rhs.m_map.GetSize())
+    return false;
+  for (size_t i = 0; i < size; ++i) {
+    if (m_map.GetCStringAtIndex(i) != rhs.m_map.GetCStringAtIndex(i))
+      return false;
+    if (m_map.GetValueRefAtIndexUnchecked(i) !=
+        rhs.m_map.GetValueRefAtIndexUnchecked(i))
+      return false;
+  }
+  return true;
+}

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h b/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h
index 994af07189f87..61df1a628ab59 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h
@@ -48,6 +48,44 @@ class NameToDIE {
                              const DIERef &die_ref)> const
               &callback) const;
 
+  /// Decode a serialized version of this object from data.
+  ///
+  /// \param data
+  ///   The decoder object that references the serialized data.
+  ///
+  /// \param offset_ptr
+  ///   A pointer that contains the offset from which the data will be decoded
+  ///   from that gets updated as data gets decoded.
+  ///
+  /// \param strtab
+  ///   All strings in cache files are put into string tables for efficiency
+  ///   and cache file size reduction. Strings are stored as uint32_t string
+  ///   table offsets in the cache data.
+  bool Decode(const lldb_private::DataExtractor &data,
+              lldb::offset_t *offset_ptr,
+              const lldb_private::StringTableReader &strtab);
+
+  /// Encode this object into a data encoder object.
+  ///
+  /// This allows this object to be serialized to disk.
+  ///
+  /// \param encoder
+  ///   A data encoder object that serialized bytes will be encoded into.
+  ///
+  /// \param strtab
+  ///   All strings in cache files are put into string tables for efficiency
+  ///   and cache file size reduction. Strings are stored as uint32_t string
+  ///   table offsets in the cache data.
+  void Encode(lldb_private::DataEncoder &encoder,
+              lldb_private::ConstStringTable &strtab) const;
+
+  /// Used for unit testing the encoding and decoding.
+  bool operator==(const NameToDIE &rhs) const;
+
+  bool IsEmpty() const { return m_map.IsEmpty(); }
+
+  void Clear() { m_map.Clear(); }
+
 protected:
   lldb_private::UniqueCStringMap<DIERef> m_map;
 };

diff  --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp
index 75450a156c28c..97dc31bc97660 100644
--- a/lldb/source/Symbol/Symtab.cpp
+++ b/lldb/source/Symbol/Symtab.cpp
@@ -34,7 +34,8 @@ using namespace lldb_private;
 Symtab::Symtab(ObjectFile *objfile)
     : m_objfile(objfile), m_symbols(), m_file_addr_to_index(*this),
       m_name_to_symbol_indices(), m_mutex(),
-      m_file_addr_to_index_computed(false), m_name_indexes_computed(false) {
+      m_file_addr_to_index_computed(false), m_name_indexes_computed(false),
+      m_loaded_from_cache(false), m_saved_to_cache(false) {
   m_name_to_symbol_indices.emplace(std::make_pair(
       lldb::eFunctionNameTypeNone, UniqueCStringMap<uint32_t>()));
   m_name_to_symbol_indices.emplace(std::make_pair(
@@ -1179,7 +1180,8 @@ void Symtab::SaveToCache() {
   // Encode will return false if the symbol table's object file doesn't have
   // anything to make a signature from.
   if (Encode(file))
-    cache->SetCachedData(GetCacheKey(), file.GetData());
+    if (cache->SetCachedData(GetCacheKey(), file.GetData()))
+      SetWasSavedToCache();
 }
 
 constexpr llvm::StringLiteral kIdentifierCStrMap("CMAP");
@@ -1343,5 +1345,7 @@ bool Symtab::LoadFromCache() {
   const bool result = Decode(data, &offset, signature_mismatch);
   if (signature_mismatch)
     cache->RemoveCacheFile(GetCacheKey());
+  if (result)
+    SetWasLoadedFromCache();
   return result;
 }

diff  --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index 1b205c533519e..d50343fb5a43c 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -52,9 +52,15 @@ json::Value ModuleStats::ToJSON() const {
   module.try_emplace("identifier", identifier);
   module.try_emplace("symbolTableParseTime", symtab_parse_time);
   module.try_emplace("symbolTableIndexTime", symtab_index_time);
+  module.try_emplace("symbolTableLoadedFromCache", symtab_loaded_from_cache);
+  module.try_emplace("symbolTableSavedToCache", symtab_saved_to_cache);
   module.try_emplace("debugInfoParseTime", debug_parse_time);
   module.try_emplace("debugInfoIndexTime", debug_index_time);
   module.try_emplace("debugInfoByteSize", (int64_t)debug_info_size);
+  module.try_emplace("debugInfoIndexLoadedFromCache",
+                     debug_info_index_loaded_from_cache);
+  module.try_emplace("debugInfoIndexSavedToCache",
+                     debug_info_index_saved_to_cache);
   return module;
 }
 
@@ -144,6 +150,10 @@ llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger,
   double symtab_index_time = 0.0;
   double debug_parse_time = 0.0;
   double debug_index_time = 0.0;
+  uint32_t symtabs_loaded = 0;
+  uint32_t symtabs_saved = 0;
+  uint32_t debug_index_loaded = 0;
+  uint32_t debug_index_saved = 0;
   uint64_t debug_info_size = 0;
   if (target) {
     json_targets.emplace_back(target->ReportStatistics());
@@ -169,11 +179,28 @@ llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger,
     module_stat.triple = module->GetArchitecture().GetTriple().str();
     module_stat.symtab_parse_time = module->GetSymtabParseTime().count();
     module_stat.symtab_index_time = module->GetSymtabIndexTime().count();
+    Symtab *symtab = module->GetSymtab();
+    if (symtab) {
+      module_stat.symtab_loaded_from_cache = symtab->GetWasLoadedFromCache();
+      if (module_stat.symtab_loaded_from_cache)
+        ++symtabs_loaded;
+      module_stat.symtab_saved_to_cache = symtab->GetWasSavedToCache();
+      if (module_stat.symtab_saved_to_cache)
+        ++symtabs_saved;
+    }
     SymbolFile *sym_file = module->GetSymbolFile();
     if (sym_file) {
       module_stat.debug_index_time = sym_file->GetDebugInfoIndexTime().count();
       module_stat.debug_parse_time = sym_file->GetDebugInfoParseTime().count();
       module_stat.debug_info_size = sym_file->GetDebugInfoSize();
+      module_stat.debug_info_index_loaded_from_cache =
+          sym_file->GetDebugInfoIndexWasLoadedFromCache();
+      if (module_stat.debug_info_index_loaded_from_cache)
+        ++debug_index_loaded;
+      module_stat.debug_info_index_saved_to_cache =
+          sym_file->GetDebugInfoIndexWasSavedToCache();
+      if (module_stat.debug_info_index_saved_to_cache)
+        ++debug_index_saved;
     }
     symtab_parse_time += module_stat.symtab_parse_time;
     symtab_index_time += module_stat.symtab_index_time;
@@ -188,8 +215,12 @@ llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger,
       {"modules", std::move(json_modules)},
       {"totalSymbolTableParseTime", symtab_parse_time},
       {"totalSymbolTableIndexTime", symtab_index_time},
+      {"totalSymbolTablesLoadedFromCache", symtabs_loaded},
+      {"totalSymbolTablesSavedToCache", symtabs_saved},
       {"totalDebugInfoParseTime", debug_parse_time},
       {"totalDebugInfoIndexTime", debug_index_time},
+      {"totalDebugInfoIndexLoadedFromCache", debug_index_loaded},
+      {"totalDebugInfoIndexSavedToCache", debug_index_saved},
       {"totalDebugInfoByteSize", debug_info_size},
   };
   return std::move(global_stats);

diff  --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index e2d62e181a525..f69fddc27fbaa 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -164,8 +164,12 @@ def test_default_no_run(self):
             'targets',
             'totalSymbolTableParseTime',
             'totalSymbolTableIndexTime',
+            'totalSymbolTablesLoadedFromCache',
+            'totalSymbolTablesSavedToCache',
             'totalDebugInfoByteSize',
             'totalDebugInfoIndexTime',
+            'totalDebugInfoIndexLoadedFromCache',
+            'totalDebugInfoIndexSavedToCache',
             'totalDebugInfoParseTime',
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
@@ -227,8 +231,12 @@ def test_default_with_run(self):
             'targets',
             'totalSymbolTableParseTime',
             'totalSymbolTableIndexTime',
+            'totalSymbolTablesLoadedFromCache',
+            'totalSymbolTablesSavedToCache',
             'totalDebugInfoByteSize',
             'totalDebugInfoIndexTime',
+            'totalDebugInfoIndexLoadedFromCache',
+            'totalDebugInfoIndexSavedToCache',
             'totalDebugInfoParseTime',
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
@@ -265,8 +273,12 @@ def test_modules(self):
             'targets',
             'totalSymbolTableParseTime',
             'totalSymbolTableIndexTime',
+            'totalSymbolTablesLoadedFromCache',
+            'totalSymbolTablesSavedToCache',
             'totalDebugInfoParseTime',
             'totalDebugInfoIndexTime',
+            'totalDebugInfoIndexLoadedFromCache',
+            'totalDebugInfoIndexSavedToCache',
             'totalDebugInfoByteSize'
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
@@ -278,12 +290,16 @@ def test_modules(self):
         exe_module = self.find_module_in_metrics(exe, debug_stats)
         module_keys = [
             'debugInfoByteSize',
+            'debugInfoIndexLoadedFromCache',
             'debugInfoIndexTime',
+            'debugInfoIndexSavedToCache',
             'debugInfoParseTime',
             'identifier',
             'path',
             'symbolTableIndexTime',
+            'symbolTableLoadedFromCache',
             'symbolTableParseTime',
+            'symbolTableSavedToCache',
             'triple',
             'uuid',
         ]
@@ -343,8 +359,12 @@ def test_breakpoints(self):
             'targets',
             'totalSymbolTableParseTime',
             'totalSymbolTableIndexTime',
+            'totalSymbolTablesLoadedFromCache',
+            'totalSymbolTablesSavedToCache',
             'totalDebugInfoParseTime',
             'totalDebugInfoIndexTime',
+            'totalDebugInfoIndexLoadedFromCache',
+            'totalDebugInfoIndexSavedToCache',
             'totalDebugInfoByteSize',
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)

diff  --git a/lldb/test/API/functionalities/module_cache/debug_index/TestDebugIndexCache.py b/lldb/test/API/functionalities/module_cache/debug_index/TestDebugIndexCache.py
new file mode 100644
index 0000000000000..9a77309d6e221
--- /dev/null
+++ b/lldb/test/API/functionalities/module_cache/debug_index/TestDebugIndexCache.py
@@ -0,0 +1,141 @@
+import glob
+import json
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import os
+import time
+
+
+class DebugIndexCacheTestcase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+        # Set the lldb module cache directory to a directory inside the build
+        # artifacts directory so no other tests are interfered with.
+        self.cache_dir = os.path.join(self.getBuildDir(), 'lldb-module-cache')
+
+    def get_module_cache_files(self, basename):
+        module_cache_glob = os.path.join(self.cache_dir,
+                                         "llvmcache-*%s*dwarf-index*" % (basename))
+        return glob.glob(module_cache_glob)
+
+    def get_stats(self, log_path=None):
+        """
+            Get the output of the "statistics dump" and return the JSON as a
+            python dictionary.
+        """
+        # If log_path is set, open the path and emit the output of the command
+        # for debugging purposes.
+        if log_path is not None:
+            f = open(log_path, 'w')
+        else:
+            f = None
+        return_obj = lldb.SBCommandReturnObject()
+        command = "statistics dump "
+        if f:
+            f.write('(lldb) %s\n' % (command))
+        self.ci.HandleCommand(command, return_obj, False)
+        metrics_json = return_obj.GetOutput()
+        if f:
+            f.write(metrics_json)
+        return json.loads(metrics_json)
+
+    def enable_lldb_index_cache(self):
+        self.runCmd('settings set symbols.lldb-index-cache-path "%s"' % (self.cache_dir))
+        self.runCmd('settings set symbols.enable-lldb-index-cache true')
+
+    @no_debug_info_test
+    def test_with_caching_enabled(self):
+        """
+            Test module cache functionality for debug info index caching.
+
+            We test that a debug info index file is created for the debug
+            information when caching is enabled with a file that contains
+            at least one of each kind of DIE in ManualDWARFIndex::IndexSet.
+
+            The input file has DWARF that will fill in every member of the
+            ManualDWARFIndex::IndexSet class to ensure we can encode all of the
+            required information.
+
+            With caching enabled, we also verify that the appropriate statistics
+            specify that the cache file was saved to the cache.
+        """
+        self.enable_lldb_index_cache()
+        src_dir = self.getSourceDir()
+        yaml_path = os.path.join(src_dir, "exe.yaml")
+        yaml_base, ext = os.path.splitext(yaml_path)
+        obj_path = self.getBuildArtifact("main.o")
+        self.yaml2obj(yaml_path, obj_path)
+
+        # Create a target with the object file we just created from YAML
+        target = self.dbg.CreateTarget(obj_path)
+        self.assertTrue(target, VALID_TARGET)
+
+        debug_index_cache_files = self.get_module_cache_files('main.o')
+        self.assertEqual(len(debug_index_cache_files), 1,
+                "make sure there is one file in the module cache directory (%s) for main.o that is a debug info cache" % (self.cache_dir))
+
+        # Verify that the module statistics have the information that specifies
+        # if we loaded or saved the debug index and symtab to the cache
+        stats = self.get_stats()
+        module_stats = stats['modules'][0]
+        self.assertFalse(module_stats['debugInfoIndexLoadedFromCache'])
+        self.assertTrue(module_stats['debugInfoIndexSavedToCache'])
+        self.assertFalse(module_stats['symbolTableLoadedFromCache'])
+        self.assertTrue(module_stats['symbolTableSavedToCache'])
+        # Verify the top level stats track how many things were loaded or saved
+        # to the cache.
+        self.assertEqual(stats["totalDebugInfoIndexLoadedFromCache"], 0)
+        self.assertEqual(stats["totalDebugInfoIndexSavedToCache"], 1)
+        self.assertEqual(stats["totalSymbolTablesLoadedFromCache"], 0)
+        self.assertEqual(stats["totalSymbolTablesSavedToCache"], 1)
+
+    @no_debug_info_test
+    def test_with_caching_disabled(self):
+        """
+            Test module cache functionality for debug info index caching.
+
+            We test that a debug info index file is not created for the debug
+            information when caching is disabled with a file that contains
+            at least one of each kind of DIE in ManualDWARFIndex::IndexSet.
+
+            The input file has DWARF that will fill in every member of the
+            ManualDWARFIndex::IndexSet class to ensure we can encode all of the
+            required information.
+
+            With caching disabled, we also verify that the appropriate
+            statistics specify that the cache file was not saved to the cache.
+        """
+        src_dir = self.getSourceDir()
+        yaml_path = os.path.join(src_dir, "exe.yaml")
+        yaml_base, ext = os.path.splitext(yaml_path)
+        obj_path = self.getBuildArtifact("main.o")
+        self.yaml2obj(yaml_path, obj_path)
+
+        # Create a target with the object file we just created from YAML
+        target = self.dbg.CreateTarget(obj_path)
+        self.assertTrue(target, VALID_TARGET)
+
+        debug_index_cache_files = self.get_module_cache_files('main.o')
+        self.assertEqual(len(debug_index_cache_files), 0,
+                "make sure there is no file in the module cache directory (%s) for main.o that is a debug info cache" % (self.cache_dir))
+
+        # Verify that the module statistics have the information that specifies
+        # if we loaded or saved the debug index and symtab to the cache
+        stats = self.get_stats()
+        module_stats = stats['modules'][0]
+        self.assertFalse(module_stats['debugInfoIndexLoadedFromCache'])
+        self.assertFalse(module_stats['debugInfoIndexSavedToCache'])
+        self.assertFalse(module_stats['symbolTableLoadedFromCache'])
+        self.assertFalse(module_stats['symbolTableSavedToCache'])
+        # Verify the top level stats track how many things were loaded or saved
+        # to the cache.
+        self.assertEqual(stats["totalDebugInfoIndexLoadedFromCache"], 0)
+        self.assertEqual(stats["totalDebugInfoIndexSavedToCache"], 0)
+        self.assertEqual(stats["totalSymbolTablesLoadedFromCache"], 0)
+        self.assertEqual(stats["totalSymbolTablesSavedToCache"], 0)

diff  --git a/lldb/test/API/functionalities/module_cache/debug_index/exe.yaml b/lldb/test/API/functionalities/module_cache/debug_index/exe.yaml
new file mode 100644
index 0000000000000..122095890e1e3
--- /dev/null
+++ b/lldb/test/API/functionalities/module_cache/debug_index/exe.yaml
@@ -0,0 +1,844 @@
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_386
+DWARF:
+  debug_str:
+    - 'Apple clang version 13.0.0 (clang-1300.0.29.3)'
+    - main.mm
+    - '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk'
+    - MacOSX.sdk
+    - '/tmp/test'
+    - g_global
+    - int
+    - SimpleClass
+    - NSObject
+    - isa
+    - Class
+    - objc_class
+    - foo
+    - _Z3fooi
+    - '-[SimpleClass sayHello]'
+    - sayHello
+    - main
+    - baz
+    - Bar
+    - x
+    - _ZNK3baz3Bar3getEv
+    - get
+    - _ZN3baz3BarC1Ei
+    - _ZN3baz3BarC2Ei
+    - self
+    - _cmd
+    - SEL
+    - objc_selector
+    - argc
+    - argv
+    - char
+    - b
+    - this
+    - i
+  debug_abbrev:
+    - ID:              0
+      Table:
+        - Code:            0x1
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_producer
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_language
+              Form:            DW_FORM_data2
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_LLVM_sysroot
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_APPLE_sdk
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_stmt_list
+              Form:            DW_FORM_sec_offset
+            - Attribute:       DW_AT_comp_dir
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_APPLE_major_runtime_vers
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+            - Attribute:       DW_AT_high_pc
+              Form:            DW_FORM_data4
+        - Code:            0x2
+          Tag:             DW_TAG_variable
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_external
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_location
+              Form:            DW_FORM_exprloc
+        - Code:            0x3
+          Tag:             DW_TAG_base_type
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_encoding
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_byte_size
+              Form:            DW_FORM_data1
+        - Code:            0x4
+          Tag:             DW_TAG_structure_type
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_APPLE_objc_complete_type
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_byte_size
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_APPLE_runtime_class
+              Form:            DW_FORM_data1
+        - Code:            0x5
+          Tag:             DW_TAG_inheritance
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_data_member_location
+              Form:            DW_FORM_data1
+        - Code:            0x6
+          Tag:             DW_TAG_structure_type
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_byte_size
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_APPLE_runtime_class
+              Form:            DW_FORM_data1
+        - Code:            0x7
+          Tag:             DW_TAG_member
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_data_member_location
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_accessibility
+              Form:            DW_FORM_data1
+        - Code:            0x8
+          Tag:             DW_TAG_typedef
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+        - Code:            0x9
+          Tag:             DW_TAG_pointer_type
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+        - Code:            0xA
+          Tag:             DW_TAG_structure_type
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_declaration
+              Form:            DW_FORM_flag_present
+        - Code:            0xB
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+            - Attribute:       DW_AT_high_pc
+              Form:            DW_FORM_data4
+            - Attribute:       DW_AT_APPLE_omit_frame_ptr
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_frame_base
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_linkage_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_external
+              Form:            DW_FORM_flag_present
+        - Code:            0xC
+          Tag:             DW_TAG_formal_parameter
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_location
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+        - Code:            0xD
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+            - Attribute:       DW_AT_high_pc
+              Form:            DW_FORM_data4
+            - Attribute:       DW_AT_APPLE_omit_frame_ptr
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_frame_base
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_object_pointer
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+        - Code:            0xE
+          Tag:             DW_TAG_formal_parameter
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_location
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_artificial
+              Form:            DW_FORM_flag_present
+        - Code:            0xF
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+            - Attribute:       DW_AT_high_pc
+              Form:            DW_FORM_data4
+            - Attribute:       DW_AT_frame_base
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_external
+              Form:            DW_FORM_flag_present
+        - Code:            0x10
+          Tag:             DW_TAG_variable
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_location
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+        - Code:            0x11
+          Tag:             DW_TAG_namespace
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+        - Code:            0x12
+          Tag:             DW_TAG_class_type
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_calling_convention
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_byte_size
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+        - Code:            0x13
+          Tag:             DW_TAG_member
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_data_member_location
+              Form:            DW_FORM_data1
+        - Code:            0x14
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_declaration
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_external
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_accessibility
+              Form:            DW_FORM_data1
+        - Code:            0x15
+          Tag:             DW_TAG_formal_parameter
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_artificial
+              Form:            DW_FORM_flag_present
+        - Code:            0x16
+          Tag:             DW_TAG_formal_parameter
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+        - Code:            0x17
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_linkage_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_declaration
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_external
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_accessibility
+              Form:            DW_FORM_data1
+        - Code:            0x18
+          Tag:             DW_TAG_const_type
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+        - Code:            0x19
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+            - Attribute:       DW_AT_high_pc
+              Form:            DW_FORM_data4
+            - Attribute:       DW_AT_frame_base
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_object_pointer
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_linkage_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_specification
+              Form:            DW_FORM_ref4
+        - Code:            0x1A
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+            - Attribute:       DW_AT_high_pc
+              Form:            DW_FORM_data4
+            - Attribute:       DW_AT_APPLE_omit_frame_ptr
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_frame_base
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_object_pointer
+              Form:            DW_FORM_ref4
+            - Attribute:       DW_AT_linkage_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_specification
+              Form:            DW_FORM_ref4
+  debug_info:
+    - Length:          0x21F
+      Version:         4
+      AbbrevTableID:   0
+      AbbrOffset:      0x0
+      AddrSize:        8
+      Entries:
+        - AbbrCode:        0x1
+          Values:
+            - Value:           0x0
+            - Value:           0x11
+            - Value:           0x2F
+            - Value:           0x37
+            - Value:           0x96
+            - Value:           0x0
+            - Value:           0xA1
+            - Value:           0x2
+            - Value:           0x0
+            - Value:           0xC4
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0xAB
+            - Value:           0x48
+            - Value:           0x1
+            - Value:           0x1
+            - Value:           0x3
+            - Value:           0x9
+              BlockData:       [ 0x3, 0xC4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                                 0x0 ]
+        - AbbrCode:        0x3
+          Values:
+            - Value:           0xB4
+            - Value:           0x5
+            - Value:           0x4
+        - AbbrCode:        0x4
+          Values:
+            - Value:           0x1
+            - Value:           0xB8
+            - Value:           0x8
+            - Value:           0x1
+            - Value:           0x13
+            - Value:           0x11
+        - AbbrCode:        0x5
+          Values:
+            - Value:           0x5F
+            - Value:           0x0
+        - AbbrCode:        0x0
+        - AbbrCode:        0x6
+          Values:
+            - Value:           0xC4
+            - Value:           0x8
+            - Value:           0x2
+            - Value:           0x35
+            - Value:           0x11
+        - AbbrCode:        0x7
+          Values:
+            - Value:           0xCD
+            - Value:           0x76
+            - Value:           0x2
+            - Value:           0x38
+            - Value:           0x0
+            - Value:           0x2
+        - AbbrCode:        0x0
+        - AbbrCode:        0x8
+          Values:
+            - Value:           0x81
+            - Value:           0xD1
+            - Value:           0x1
+            - Value:           0xD
+        - AbbrCode:        0x9
+          Values:
+            - Value:           0x86
+        - AbbrCode:        0xA
+          Values:
+            - Value:           0xD7
+            - Value:           0x1
+        - AbbrCode:        0xB
+          Values:
+            - Value:           0x0
+            - Value:           0x20
+            - Value:           0x1
+            - Value:           0x1
+              BlockData:       [ 0x6F ]
+            - Value:           0xE6
+            - Value:           0xE2
+            - Value:           0x1
+            - Value:           0x6
+            - Value:           0x48
+            - Value:           0x1
+        - AbbrCode:        0xC
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0xC ]
+            - Value:           0x11C
+            - Value:           0x1
+            - Value:           0x6
+            - Value:           0x48
+        - AbbrCode:        0x0
+        - AbbrCode:        0xD
+          Values:
+            - Value:           0x20
+            - Value:           0x14
+            - Value:           0x1
+            - Value:           0x1
+              BlockData:       [ 0x6F ]
+            - Value:           0xD0
+            - Value:           0xEE
+            - Value:           0x1
+            - Value:           0x18
+        - AbbrCode:        0xE
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x8 ]
+            - Value:           0x155
+            - Value:           0x1ED
+            - Value:           0x1
+        - AbbrCode:        0xE
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x0 ]
+            - Value:           0x15A
+            - Value:           0x1F2
+            - Value:           0x1
+        - AbbrCode:        0x0
+        - AbbrCode:        0xF
+          Values:
+            - Value:           0x34
+            - Value:           0x3C
+            - Value:           0x1
+              BlockData:       [ 0x6D ]
+            - Value:           0x10F
+            - Value:           0x1
+            - Value:           0x1B
+            - Value:           0x48
+            - Value:           0x1
+        - AbbrCode:        0xC
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x78 ]
+            - Value:           0x171
+            - Value:           0x1
+            - Value:           0x1B
+            - Value:           0x48
+        - AbbrCode:        0xC
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x8F, 0x10 ]
+            - Value:           0x176
+            - Value:           0x1
+            - Value:           0x1B
+            - Value:           0x207
+        - AbbrCode:        0x10
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x8F, 0xC ]
+            - Value:           0x180
+            - Value:           0x1
+            - Value:           0x1C
+            - Value:           0x132
+        - AbbrCode:        0x0
+        - AbbrCode:        0x11
+          Values:
+            - Value:           0x114
+        - AbbrCode:        0x12
+          Values:
+            - Value:           0x5
+            - Value:           0x118
+            - Value:           0x4
+            - Value:           0x1
+            - Value:           0xA
+        - AbbrCode:        0x13
+          Values:
+            - Value:           0x11C
+            - Value:           0x48
+            - Value:           0x1
+            - Value:           0xB
+            - Value:           0x0
+        - AbbrCode:        0x14
+          Values:
+            - Value:           0x118
+            - Value:           0x1
+            - Value:           0xD
+            - Value:           0x1
+            - Value:           0x1
+            - Value:           0x1
+        - AbbrCode:        0x15
+          Values:
+            - Value:           0x172
+            - Value:           0x1
+        - AbbrCode:        0x16
+          Values:
+            - Value:           0x48
+        - AbbrCode:        0x0
+        - AbbrCode:        0x17
+          Values:
+            - Value:           0x11E
+            - Value:           0x131
+            - Value:           0x1
+            - Value:           0xF
+            - Value:           0x48
+            - Value:           0x1
+            - Value:           0x1
+            - Value:           0x1
+        - AbbrCode:        0x15
+          Values:
+            - Value:           0x177
+            - Value:           0x1
+        - AbbrCode:        0x0
+        - AbbrCode:        0x0
+        - AbbrCode:        0x0
+        - AbbrCode:        0x9
+          Values:
+            - Value:           0x132
+        - AbbrCode:        0x9
+          Values:
+            - Value:           0x17C
+        - AbbrCode:        0x18
+          Values:
+            - Value:           0x132
+        - AbbrCode:        0x19
+          Values:
+            - Value:           0x70
+            - Value:           0x34
+            - Value:           0x1
+              BlockData:       [ 0x6D ]
+            - Value:           0x19C
+            - Value:           0x135
+            - Value:           0x147
+        - AbbrCode:        0xE
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x78 ]
+            - Value:           0x182
+            - Value:           0x21D
+            - Value:           0x1
+        - AbbrCode:        0xC
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x74 ]
+            - Value:           0x187
+            - Value:           0x1
+            - Value:           0xD
+            - Value:           0x48
+        - AbbrCode:        0x0
+        - AbbrCode:        0x1A
+          Values:
+            - Value:           0xA4
+            - Value:           0x20
+            - Value:           0x1
+            - Value:           0x1
+              BlockData:       [ 0x6F ]
+            - Value:           0x1D2
+            - Value:           0x145
+            - Value:           0x147
+        - AbbrCode:        0xE
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x8 ]
+            - Value:           0x182
+            - Value:           0x21D
+            - Value:           0x1
+        - AbbrCode:        0xC
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x4 ]
+            - Value:           0x187
+            - Value:           0x1
+            - Value:           0xD
+            - Value:           0x48
+        - AbbrCode:        0x0
+        - AbbrCode:        0x9
+          Values:
+            - Value:           0x4F
+        - AbbrCode:        0x8
+          Values:
+            - Value:           0x1FD
+            - Value:           0x15F
+            - Value:           0x1
+            - Value:           0x8
+        - AbbrCode:        0x9
+          Values:
+            - Value:           0x202
+        - AbbrCode:        0xA
+          Values:
+            - Value:           0x163
+            - Value:           0x1
+        - AbbrCode:        0x9
+          Values:
+            - Value:           0x20C
+        - AbbrCode:        0x9
+          Values:
+            - Value:           0x211
+        - AbbrCode:        0x18
+          Values:
+            - Value:           0x216
+        - AbbrCode:        0x3
+          Values:
+            - Value:           0x17B
+            - Value:           0x6
+            - Value:           0x1
+        - AbbrCode:        0x9
+          Values:
+            - Value:           0x132
+        - AbbrCode:        0x0
+  debug_line:
+    - Length:          250
+      Version:         4
+      PrologueLength:  157
+      MinInstLength:   1
+      MaxOpsPerInst:   1
+      DefaultIsStmt:   1
+      LineBase:        251
+      LineRange:       14
+      OpcodeBase:      13
+      StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
+      IncludeDirs:
+        - '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/objc'
+      Files:
+        - Name:            main.mm
+          DirIdx:          0
+          ModTime:         0
+          Length:          0
+        - Name:            NSObject.h
+          DirIdx:          1
+          ModTime:         0
+          Length:          0
+      Opcodes:
+        - Opcode:          DW_LNS_extended_op
+          ExtLen:          9
+          SubOpcode:       DW_LNE_set_address
+          Data:            0
+        - Opcode:          0x17
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            10
+        - Opcode:          DW_LNS_set_prologue_end
+          Data:            0
+        - Opcode:          0x83
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            0
+        - Opcode:          DW_LNS_negate_stmt
+          Data:            0
+        - Opcode:          DW_LNS_advance_line
+          SData:           -7
+          Data:            0
+        - Opcode:          0x4A
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            14
+        - Opcode:          0x51
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            12
+        - Opcode:          0x4A
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            3
+        - Opcode:          0x4A
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            0
+        - Opcode:          DW_LNS_negate_stmt
+          Data:            0
+        - Opcode:          DW_LNS_advance_line
+          SData:           17
+          Data:            0
+        - Opcode:          0x82
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            20
+        - Opcode:          DW_LNS_set_prologue_end
+          Data:            0
+        - Opcode:          0xBA
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            0
+        - Opcode:          0x85
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            12
+        - Opcode:          DW_LNS_set_prologue_end
+          Data:            0
+        - Opcode:          DW_LNS_advance_pc
+          Data:            40
+        - Opcode:          0x13
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            3
+        - Opcode:          0x83
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            0
+        - Opcode:          DW_LNS_advance_line
+          SData:           -16
+          Data:            0
+        - Opcode:          0xBA
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            21
+        - Opcode:          DW_LNS_set_prologue_end
+          Data:            0
+        - Opcode:          DW_LNS_const_add_pc
+          Data:            0
+        - Opcode:          0xAC
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            22
+        - Opcode:          DW_LNS_negate_stmt
+          Data:            0
+        - Opcode:          0xBA
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            0
+        - Opcode:          DW_LNS_negate_stmt
+          Data:            0
+        - Opcode:          0xBA
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            18
+        - Opcode:          DW_LNS_set_prologue_end
+          Data:            0
+        - Opcode:          0xF2
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            16
+        - Opcode:          DW_LNS_negate_stmt
+          Data:            0
+        - Opcode:          0x4A
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            22
+        - Opcode:          0x4A
+          Data:            0
+        - Opcode:          DW_LNS_advance_pc
+          Data:            8
+        - Opcode:          DW_LNS_extended_op
+          ExtLen:          1
+          SubOpcode:       DW_LNE_end_sequence
+          Data:            0
+...

diff  --git a/lldb/test/API/functionalities/module_cache/simple_exe/TestModuleCacheSimple.py b/lldb/test/API/functionalities/module_cache/simple_exe/TestModuleCacheSimple.py
index 4605c3b258648..35e96fb584ed1 100644
--- a/lldb/test/API/functionalities/module_cache/simple_exe/TestModuleCacheSimple.py
+++ b/lldb/test/API/functionalities/module_cache/simple_exe/TestModuleCacheSimple.py
@@ -31,7 +31,7 @@ def get_module_cache_files(self, basename):
 
     # Doesn't depend on any specific debug information.
     @no_debug_info_test
-    @skipIfWindows # Windows runs into trouble deleting the executable
+    @skipIfWindows
     def test(self):
         """
             Test module cache functionality for a simple object file.

diff  --git a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt
index 76215c31b2aab..16c38c4ab2190 100644
--- a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt
+++ b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_unittest(SymbolFileDWARFTests
   DWARFASTParserClangTests.cpp
   DWARFDIETest.cpp
+  DWARFIndexCachingTest.cpp
   DWARFUnitTest.cpp
   SymbolFileDWARFTests.cpp
   XcodeSDKModuleTests.cpp

diff  --git a/lldb/unittests/SymbolFile/DWARF/DWARFIndexCachingTest.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFIndexCachingTest.cpp
new file mode 100644
index 0000000000000..29514b5d1fcf5
--- /dev/null
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFIndexCachingTest.cpp
@@ -0,0 +1,198 @@
+//===-- DWARFIndexCachingTest.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 "Plugins/SymbolFile/DWARF/DIERef.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDIE.h"
+#include "Plugins/SymbolFile/DWARF/ManualDWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/NameToDIE.h"
+#include "TestingSupport/Symbol/YAMLModuleTester.h"
+#include "lldb/Core/DataFileCache.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Utility/DataEncoder.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "llvm/ADT/STLExtras.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void EncodeDecode(const DIERef &object, ByteOrder byte_order) {
+  const uint8_t addr_size = 8;
+  DataEncoder encoder(byte_order, addr_size);
+  object.Encode(encoder);
+  llvm::ArrayRef<uint8_t> bytes = encoder.GetData();
+  DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size);
+  offset_t data_offset = 0;
+  EXPECT_EQ(object, DIERef::Decode(data, &data_offset));
+}
+
+static void EncodeDecode(const DIERef &object) {
+  EncodeDecode(object, eByteOrderLittle);
+  EncodeDecode(object, eByteOrderBig);
+}
+
+TEST(DWARFIndexCachingTest, DIERefEncodeDecode) {
+  // Tests DIERef::Encode(...) and DIERef::Decode(...)
+  EncodeDecode(DIERef(llvm::None, DIERef::Section::DebugInfo, 0x11223344));
+  EncodeDecode(DIERef(llvm::None, DIERef::Section::DebugTypes, 0x11223344));
+  EncodeDecode(DIERef(100, DIERef::Section::DebugInfo, 0x11223344));
+  EncodeDecode(DIERef(200, DIERef::Section::DebugTypes, 0x11223344));
+}
+
+static void EncodeDecode(const NameToDIE &object, ByteOrder byte_order) {
+  const uint8_t addr_size = 8;
+  DataEncoder encoder(byte_order, addr_size);
+  DataEncoder strtab_encoder(byte_order, addr_size);
+  ConstStringTable const_strtab;
+
+  object.Encode(encoder, const_strtab);
+
+  llvm::ArrayRef<uint8_t> bytes = encoder.GetData();
+  DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size);
+
+  const_strtab.Encode(strtab_encoder);
+  llvm::ArrayRef<uint8_t> strtab_bytes = strtab_encoder.GetData();
+  DataExtractor strtab_data(strtab_bytes.data(), strtab_bytes.size(),
+                            byte_order, addr_size);
+  StringTableReader strtab_reader;
+  offset_t strtab_data_offset = 0;
+  ASSERT_EQ(strtab_reader.Decode(strtab_data, &strtab_data_offset), true);
+
+  NameToDIE decoded_object;
+  offset_t data_offset = 0;
+  decoded_object.Decode(data, &data_offset, strtab_reader);
+  EXPECT_TRUE(object == decoded_object);
+}
+
+static void EncodeDecode(const NameToDIE &object) {
+  EncodeDecode(object, eByteOrderLittle);
+  EncodeDecode(object, eByteOrderBig);
+}
+
+TEST(DWARFIndexCachingTest, NameToDIEEncodeDecode) {
+  NameToDIE map;
+  // Make sure an empty NameToDIE map encodes and decodes correctly.
+  EncodeDecode(map);
+  map.Insert(ConstString("hello"),
+             DIERef(llvm::None, DIERef::Section::DebugInfo, 0x11223344));
+  map.Insert(ConstString("workd"),
+             DIERef(100, DIERef::Section::DebugInfo, 0x11223344));
+  // Make sure a valid NameToDIE map encodes and decodes correctly.
+  EncodeDecode(map);
+}
+
+static void EncodeDecode(const ManualDWARFIndex::IndexSet &object,
+                         ByteOrder byte_order) {
+  const uint8_t addr_size = 8;
+  DataEncoder encoder(byte_order, addr_size);
+  DataEncoder strtab_encoder(byte_order, addr_size);
+  object.Encode(encoder);
+  llvm::ArrayRef<uint8_t> bytes = encoder.GetData();
+  DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size);
+  ManualDWARFIndex::IndexSet decoded_object;
+  offset_t data_offset = 0;
+  decoded_object.Decode(data, &data_offset);
+  EXPECT_TRUE(object == decoded_object);
+}
+
+static void EncodeDecode(const ManualDWARFIndex::IndexSet &object) {
+  EncodeDecode(object, eByteOrderLittle);
+  EncodeDecode(object, eByteOrderBig);
+}
+
+TEST(DWARFIndexCachingTest, ManualDWARFIndexIndexSetEncodeDecode) {
+  ManualDWARFIndex::IndexSet set;
+  // Make sure empty IndexSet can be encoded and decoded correctly
+  EncodeDecode(set);
+
+  dw_offset_t die_offset = 0;
+  // Make sure an IndexSet with only items in IndexSet::function_basenames can
+  // be encoded and decoded correctly.
+  set.function_basenames.Insert(
+      ConstString("a"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  EncodeDecode(set);
+  set.function_basenames.Clear();
+  // Make sure an IndexSet with only items in IndexSet::function_fullnames can
+  // be encoded and decoded correctly.
+  set.function_fullnames.Insert(
+      ConstString("a"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  EncodeDecode(set);
+  set.function_fullnames.Clear();
+  // Make sure an IndexSet with only items in IndexSet::function_methods can
+  // be encoded and decoded correctly.
+  set.function_methods.Insert(
+      ConstString("a"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  EncodeDecode(set);
+  set.function_methods.Clear();
+  // Make sure an IndexSet with only items in IndexSet::function_selectors can
+  // be encoded and decoded correctly.
+  set.function_selectors.Insert(
+      ConstString("a"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  EncodeDecode(set);
+  set.function_selectors.Clear();
+  // Make sure an IndexSet with only items in IndexSet::objc_class_selectors can
+  // be encoded and decoded correctly.
+  set.objc_class_selectors.Insert(
+      ConstString("a"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  EncodeDecode(set);
+  set.objc_class_selectors.Clear();
+  // Make sure an IndexSet with only items in IndexSet::globals can
+  // be encoded and decoded correctly.
+  set.globals.Insert(
+      ConstString("a"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  EncodeDecode(set);
+  set.globals.Clear();
+  // Make sure an IndexSet with only items in IndexSet::types can
+  // be encoded and decoded correctly.
+  set.types.Insert(
+      ConstString("a"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  EncodeDecode(set);
+  set.types.Clear();
+  // Make sure an IndexSet with only items in IndexSet::namespaces can
+  // be encoded and decoded correctly.
+  set.namespaces.Insert(
+      ConstString("a"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  EncodeDecode(set);
+  set.namespaces.Clear();
+  // Make sure that an IndexSet with item in all NameToDIE maps can be
+  // be encoded and decoded correctly.
+  set.function_basenames.Insert(
+      ConstString("a"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  set.function_fullnames.Insert(
+      ConstString("b"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  set.function_methods.Insert(
+      ConstString("c"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  set.function_selectors.Insert(
+      ConstString("d"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  set.objc_class_selectors.Insert(
+      ConstString("e"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  set.globals.Insert(
+      ConstString("f"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  set.types.Insert(
+      ConstString("g"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  set.namespaces.Insert(
+      ConstString("h"),
+      DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
+  EncodeDecode(set);
+}


        


More information about the lldb-commits mailing list