diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -67,8 +67,7 @@ // 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 @@ /// 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 @@ llvm::Optional> 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 --- a/lldb/include/lldb/Symbol/Symtab.h +++ b/lldb/include/lldb/Symbol/Symtab.h @@ -212,6 +212,30 @@ /// 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 collection; typedef collection::iterator iterator; @@ -252,7 +276,8 @@ 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 & diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h --- a/lldb/include/lldb/Target/Statistics.h +++ b/lldb/include/lldb/Target/Statistics.h @@ -84,6 +84,10 @@ 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.h b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h --- a/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h @@ -54,6 +54,37 @@ 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 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/DIERef.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp --- 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::format(const DIERef &ref, raw_ostream &OS, StringRef Style) { if (ref.dwo_num()) @@ -16,3 +21,35 @@ 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::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/ManualDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h @@ -55,7 +55,7 @@ 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 @@ 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 different 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 m_units_to_avoid; IndexSet m_set; + bool m_indexed = false; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp --- 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; 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(&main_dwarf)); + LLDB_SCOPED_TIMERF("%p", static_cast(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 units_to_index; @@ -125,6 +130,8 @@ 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 @@ s.Printf("\nNamespaces:\n"); m_set.namespaces.Dump(&s); } + +constexpr llvm::StringLiteral kIdentifierManualDWARFIndex("DIDX"); +// Define IDs for the different 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 different 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 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/NameToDIE.h b/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h --- a/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h @@ -48,6 +48,44 @@ 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 m_map; }; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp b/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp --- 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 @@ 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 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/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp --- a/lldb/source/Symbol/Symtab.cpp +++ b/lldb/source/Symbol/Symtab.cpp @@ -34,7 +34,8 @@ 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())); m_name_to_symbol_indices.emplace(std::make_pair( @@ -1179,7 +1180,8 @@ // 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 @@ 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 --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -52,9 +52,15 @@ 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 @@ 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 @@ 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 @@ {"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 --- a/lldb/test/API/commands/statistics/basic/TestStats.py +++ b/lldb/test/API/commands/statistics/basic/TestStats.py @@ -164,8 +164,12 @@ '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 @@ '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 @@ '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 @@ 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 @@ '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 --- /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 --- /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 --- 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 @@ # 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 --- 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 --- /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 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 bytes = encoder.GetData(); + DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size); + + const_strtab.Encode(strtab_encoder); + llvm::ArrayRef 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 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); +}