[Lldb-commits] [lldb] [lldb] Move DemangledNameInfo cache out of Mangled class (PR #225332)
Raphael Isemann via lldb-commits
lldb-commits at lists.llvm.org
Tue Sep 29 01:04:28 PDT 2026
https://github.com/Teemperor updated https://github.com/llvm/llvm-project/pull/225332
>From 51c2aebcd0c118ca2229ee7000f10efabf63d444 Mon Sep 17 00:00:00 2001
From: Raphael Isemann <rise at apple.com>
Date: Tue, 22 Sep 2026 09:44:30 +0100
Subject: [PATCH] [lldb] Move DemangledNameInfo cache out of Mangled class
This patch moves the per-Mangled DemangledInfoCache into a global
cache with a size limit. The motivation for this is:
1. It makes `Mangled` a smaller which saves memory as its a frequently
heap-allocated object.
2. The current Mangled class is sometimes accessed via the SB API from
multiple threads, and the stateful cache in Symbol causes crashes due
to the inherent race conditions of this approach.
3. It avoids potentially large memory usage in case many symbols have
their DemangledNameInfo computed and cached.
The size of the global cache can be changed via the new
'symbols.demangled-name-info-cache-size' setting. Setting this to 0
disables the cache.
For the caching strategy, the current implementation drops a random
entry whenever the cache is full. Note that we can't use an LRU/LFU
cache as this won't allow us to use a read-write lock (as we would
need to update the use counter on a read).
Also, this patch doesn't make Symbol or Mangled fully thread-safe. It
just avoid the racy allocations.
---
lldb/include/lldb/Core/DemangledNameInfo.h | 52 ++++++
lldb/include/lldb/Core/Mangled.h | 58 ++-----
lldb/include/lldb/Core/ModuleList.h | 1 +
lldb/include/lldb/Symbol/Symbol.h | 2 +-
lldb/source/Core/CoreProperties.td | 4 +
lldb/source/Core/DemangledNameInfo.cpp | 76 +++++++++
lldb/source/Core/Mangled.cpp | 48 +++---
lldb/source/Core/ModuleList.cpp | 6 +
.../Language/CPlusPlus/CPlusPlusLanguage.cpp | 11 +-
lldb/source/Symbol/Symbol.cpp | 2 +-
lldb/source/Symbol/Symtab.cpp | 2 +-
lldb/unittests/Core/MangledTest.cpp | 157 ++++++++++++++----
12 files changed, 314 insertions(+), 105 deletions(-)
diff --git a/lldb/include/lldb/Core/DemangledNameInfo.h b/lldb/include/lldb/Core/DemangledNameInfo.h
index ac08e091d902b0..3702aae5d151a1 100644
--- a/lldb/include/lldb/Core/DemangledNameInfo.h
+++ b/lldb/include/lldb/Core/DemangledNameInfo.h
@@ -9,14 +9,22 @@
#ifndef LLDB_CORE_DEMANGLEDNAMEINFO_H
#define LLDB_CORE_DEMANGLEDNAMEINFO_H
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Locked.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/Demangle/ItaniumDemangle.h"
#include "llvm/Demangle/Utility.h"
#include <cstddef>
+#include <optional>
+#include <random>
#include <utility>
+#include <vector>
namespace lldb_private {
+class Mangled;
+
/// Stores information about where certain portions of a demangled
/// function name begin and end.
struct DemangledNameInfo {
@@ -132,6 +140,50 @@ struct DemangledNameInfo {
bool hasSuffix() const { return SuffixRange.second >= SuffixRange.first; }
};
+/// A cache from mangled names to DemangledNameInfo.
+class DemangledNameInfoCache {
+public:
+ static constexpr size_t DefaultMaxEntries = 16 * 1024;
+
+ explicit DemangledNameInfoCache(size_t max_entries = DefaultMaxEntries)
+ : m_state(State(max_entries)) {}
+
+ /// Calculates the DemangledNameInfo and caches the result.
+ ///
+ /// \return std::nullopt if no info could be computed for \c mangled.
+ std::optional<DemangledNameInfo> Get(const Mangled &mangled);
+
+ void Clear();
+
+ size_t GetMaxEntries() const;
+
+ size_t GetNumEntries() const;
+
+ /// Changes the maximum number of entries. A limit of 0 disables the cache.
+ void SetMaxEntries(size_t max_entries);
+
+private:
+ struct State {
+ explicit State(size_t max_entries) : max_entries(max_entries) {}
+
+ /// Drops a random entry from the cache.
+ void EvictRandomEntry();
+
+ /// Maps a ConstString to its DemangledNameInfo.
+ /// Values can be empty if no name info could be computed for a name.
+ llvm::DenseMap<ConstString, std::optional<DemangledNameInfo>> infos;
+
+ /// The mangled names.
+ std::vector<ConstString> mangled_names;
+
+ std::minstd_rand rng;
+
+ size_t max_entries;
+ };
+
+ Guarded<State> m_state;
+};
+
/// An OutputBuffer which keeps a record of where certain parts of a
/// demangled name begin/end (e.g., basename, scope, argument list, etc.).
/// The tracking occurs during printing of the Itanium demangle tree.
diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index 1c7bbeb05a37d4..622c0f82f49f77 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -17,7 +17,7 @@
#include "llvm/ADT/StringRef.h"
#include <cstddef>
-#include <memory>
+#include <optional>
namespace lldb_private {
@@ -53,28 +53,6 @@ class Mangled {
/// Initialize with both mangled and demangled names empty.
Mangled() = default;
- Mangled(const Mangled &other)
- : m_mangled(other.m_mangled), m_demangled(other.m_demangled),
- m_demangled_info(
- other.m_demangled_info
- ? std::make_unique<DemangledNameInfo>(*other.m_demangled_info)
- : nullptr) {}
-
- Mangled &operator=(const Mangled &other) {
- if (this != &other) {
- m_mangled = other.m_mangled;
- m_demangled = other.m_demangled;
- m_demangled_info =
- other.m_demangled_info
- ? std::make_unique<DemangledNameInfo>(*other.m_demangled_info)
- : nullptr;
- }
- return *this;
- }
-
- Mangled(Mangled &&) = default;
- Mangled &operator=(Mangled &&) = default;
-
/// Construct with name.
///
/// Constructor with an optional string and auto-detect if \a name is
@@ -157,15 +135,9 @@ class Mangled {
/// A const reference to the display demangled name string object.
ConstString GetDisplayDemangledName() const;
- void SetDemangledName(ConstString name) {
- m_demangled = name;
- m_demangled_info.reset();
- }
+ void SetDemangledName(ConstString name) { m_demangled = name; }
- void SetMangledName(ConstString name) {
- m_mangled = name;
- m_demangled_info.reset();
- }
+ void SetMangledName(ConstString name) { m_mangled = name; }
/// Mangled name get accessor.
///
@@ -290,8 +262,15 @@ class Mangled {
/// table offsets in the cache data.
void Encode(DataEncoder &encoder, ConstStringTable &strtab) const;
- /// Retrieve \c DemangledNameInfo of the demangled name held by this object.
- const DemangledNameInfo *GetDemangledInfo() const;
+ /// Compute the \c DemangledNameInfo of the demangled name.
+ ///
+ /// Note that this always re-runs the demangler. \see DemangledNameInfoCache
+ /// for caching the result.
+ ///
+ /// \return
+ /// std::nullopt if no info could be computed (for example because the
+ /// name is mangled with a scheme that doesn't provide any info).
+ std::optional<DemangledNameInfo> ComputeDemangledInfo() const;
/// Compute the base name (without namespace/class qualifiers) from the
/// demangled name.
@@ -305,25 +284,14 @@ class Mangled {
ConstString GetBaseName() const;
private:
- /// If \c force is \c false, this function will re-use the previously
- /// demangled name (if any). If \c force is \c true (or the mangled name
- /// on this object was not previously demangled), demangle and cache the
- /// name.
- ConstString GetDemangledNameImpl(bool force) const;
-
/// The mangled version of the name.
ConstString m_mangled;
/// Mutable so we can get it on demand with
/// a const version of this object.
mutable ConstString m_demangled;
-
- /// If available, holds information about where in \c m_demangled certain
- /// parts of the name (e.g., basename, arguments, etc.) begin and end.
- mutable std::unique_ptr<DemangledNameInfo> m_demangled_info;
};
-static_assert(sizeof(Mangled) <= 2 * sizeof(ConstString) +
- sizeof(std::unique_ptr<DemangledNameInfo>),
+static_assert(sizeof(Mangled) <= 2 * sizeof(ConstString),
"High-volume object, size of object must be increased with care");
Stream &operator<<(Stream &s, const Mangled &obj);
diff --git a/lldb/include/lldb/Core/ModuleList.h b/lldb/include/lldb/Core/ModuleList.h
index aa0e7c674c1d28..4e815e8f32666d 100644
--- a/lldb/include/lldb/Core/ModuleList.h
+++ b/lldb/include/lldb/Core/ModuleList.h
@@ -113,6 +113,7 @@ class ModuleListProperties : public Properties {
FileSpec GetLLDBIndexCachePath() const;
bool SetLLDBIndexCachePath(const FileSpec &path);
bool GetLoadSymbolOnDemand() const;
+ uint64_t GetDemangledNameInfoCacheSize() const;
lldb::SymbolDownload GetSymbolAutoDownload() const;
PathMappingList GetSymlinkMappings() const;
};
diff --git a/lldb/include/lldb/Symbol/Symbol.h b/lldb/include/lldb/Symbol/Symbol.h
index 184e1ca9bdb63a..700b29b23a7472 100644
--- a/lldb/include/lldb/Symbol/Symbol.h
+++ b/lldb/include/lldb/Symbol/Symbol.h
@@ -458,7 +458,7 @@ class Symbol : public SymbolContextScope {
#if __SIZEOF_POINTER__ == 8
static_assert(
- sizeof(lldb_private::Symbol) == 80,
+ sizeof(lldb_private::Symbol) == 72,
"Symbol is a high volume data type, size must be increased with care");
#endif
diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td
index dee4cd40448bfc..e28d6c77fe957f 100644
--- a/lldb/source/Core/CoreProperties.td
+++ b/lldb/source/Core/CoreProperties.td
@@ -51,6 +51,10 @@ let Definition = "modulelist", Path = "symbols" in {
Global,
DefaultFalse,
Desc<"Enable on demand symbol loading in LLDB. LLDB will load debug info on demand for each module based on various conditions (e.g. matched breakpoint, resolved stack frame addresses and matched global variables/function symbols in symbol table) to improve performance. Please refer to docs/use/ondemand.rst for details.">;
+ def DemangledNameInfoCacheSize: Property<"demangled-name-info-cache-size", "UInt64">,
+ Global,
+ DefaultUnsignedValue<16384>,
+ Desc<"The maximum number of formatted function names to cache. A value of 0 disables the cache.">;
}
let Definition = "diagnostics", Path = "diagnostics" in {
diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp
index 16fbfda299b215..0f69947548d528 100644
--- a/lldb/source/Core/DemangledNameInfo.cpp
+++ b/lldb/source/Core/DemangledNameInfo.cpp
@@ -7,11 +7,87 @@
//===----------------------------------------------------------------------===//
#include "lldb/Core/DemangledNameInfo.h"
+#include "lldb/Core/Mangled.h"
using namespace llvm::itanium_demangle;
namespace lldb_private {
+std::optional<DemangledNameInfo>
+DemangledNameInfoCache::Get(const Mangled &mangled) {
+ ConstString mangled_name = mangled.GetMangledName();
+ if (!mangled_name)
+ return std::nullopt;
+
+ bool cache_enabled = false;
+ {
+ auto state = m_state.LockShared();
+ cache_enabled = state->max_entries != 0;
+ auto it = state->infos.find(mangled_name);
+ if (it != state->infos.end())
+ return it->second;
+ }
+
+ // Demangle outside of the lock. Two threads asking for the same name at the
+ // same time just compute the same info twice, which is cheaper than making
+ // every other thread wait for one demangle to finish.
+ std::optional<DemangledNameInfo> info = mangled.ComputeDemangledInfo();
+
+ // Caching is disabled.
+ if (!cache_enabled)
+ return info;
+
+ auto state = m_state.Lock();
+ // Another thread might have cached this name while we were demangling.
+ if (state->infos.contains(mangled_name))
+ return info;
+
+ // Make room for the new entry by dropping a random one.
+ if (state->infos.size() >= state->max_entries)
+ state->EvictRandomEntry();
+
+ state->infos.insert({mangled_name, info});
+ state->mangled_names.push_back(mangled_name);
+ return info;
+}
+
+void DemangledNameInfoCache::State::EvictRandomEntry() {
+ assert(!mangled_names.empty() && "evicting from an empty cache");
+ std::uniform_int_distribution<size_t> dist(0, mangled_names.size() - 1);
+ size_t index = dist(rng);
+ infos.erase(mangled_names[index]);
+ mangled_names[index] = mangled_names.back();
+ mangled_names.pop_back();
+}
+
+void DemangledNameInfoCache::Clear() {
+ auto state = m_state.Lock();
+ state->infos.clear();
+ state->mangled_names.clear();
+}
+
+size_t DemangledNameInfoCache::GetMaxEntries() const {
+ return m_state.LockShared()->max_entries;
+}
+
+size_t DemangledNameInfoCache::GetNumEntries() const {
+ return m_state.LockShared()->infos.size();
+}
+
+void DemangledNameInfoCache::SetMaxEntries(size_t max_entries) {
+ // This is called before every lookup, so avoid taking the exclusive lock
+ // when the limit didn't change.
+ if (GetMaxEntries() == max_entries)
+ return;
+
+ auto state = m_state.Lock();
+ state->max_entries = max_entries;
+ if (state->infos.size() > state->max_entries) {
+ state->infos.clear();
+ state->mangled_names.clear();
+ }
+}
+
bool TrackingOutputBuffer::shouldTrack() const {
if (!isPrintingTopLevelFunctionType())
return false;
diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp
index 3f278bfa40335f..06aa1a97309c1b 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -26,6 +26,7 @@
#include "llvm/Support/Compiler.h"
#include <mutex>
+#include <optional>
#include <string>
#include <string_view>
#include <utility>
@@ -112,7 +113,6 @@ Mangled::operator bool() const { return m_mangled || m_demangled; }
void Mangled::Clear() {
m_mangled.Clear();
m_demangled.Clear();
- m_demangled_info.reset();
}
// Compare the string values.
@@ -126,16 +126,13 @@ void Mangled::SetValue(ConstString name) {
if (IsMangledName(name.GetStringRef())) {
m_demangled.Clear();
m_mangled = name;
- m_demangled_info.reset();
} else {
m_demangled = name;
m_mangled.Clear();
- m_demangled_info.reset();
}
} else {
m_demangled.Clear();
m_mangled.Clear();
- m_demangled_info.reset();
}
}
@@ -281,30 +278,19 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context,
llvm_unreachable("Fully covered switch above!");
}
-ConstString Mangled::GetDemangledName() const {
- return GetDemangledNameImpl(/*force=*/false);
-}
-
-const DemangledNameInfo *Mangled::GetDemangledInfo() const {
- if (!m_demangled_info)
- GetDemangledNameImpl(/*force=*/true);
- return m_demangled_info.get();
-}
-
// Generate the demangled name on demand using this accessor. Code in this
// class will need to use this accessor if it wishes to decode the demangled
// name. The result is cached and will be kept until a new string value is
// supplied to this object, or until the end of the object's lifetime.
-ConstString Mangled::GetDemangledNameImpl(bool force) const {
+ConstString Mangled::GetDemangledName() const {
if (!m_mangled)
return m_demangled;
// Re-use previously demangled names.
- if (!force && !m_demangled.IsNull())
+ if (!m_demangled.IsNull())
return m_demangled;
- if (!force && m_mangled.GetMangledCounterpart(m_demangled) &&
- !m_demangled.IsNull())
+ if (m_mangled.GetMangledCounterpart(m_demangled) && !m_demangled.IsNull())
return m_demangled;
// We didn't already mangle this name, demangle it and if all goes well
@@ -314,14 +300,9 @@ ConstString Mangled::GetDemangledNameImpl(bool force) const {
case eManglingSchemeMSVC:
demangled_name = GetMSVCDemangledStr(m_mangled);
break;
- case eManglingSchemeItanium: {
- std::pair<char *, DemangledNameInfo> demangled =
- GetItaniumDemangledStr(m_mangled.GetCString());
- demangled_name = demangled.first;
- m_demangled_info =
- std::make_unique<DemangledNameInfo>(std::move(demangled.second));
+ case eManglingSchemeItanium:
+ demangled_name = GetItaniumDemangledStr(m_mangled.GetCString()).first;
break;
- }
case eManglingSchemeRustV0:
demangled_name = GetRustV0DemangledStr(m_mangled);
break;
@@ -351,6 +332,18 @@ ConstString Mangled::GetDemangledNameImpl(bool force) const {
return m_demangled;
}
+std::optional<DemangledNameInfo> Mangled::ComputeDemangledInfo() const {
+ // Only supported for itanium.
+ if (!m_mangled ||
+ GetManglingScheme(m_mangled.GetStringRef()) != eManglingSchemeItanium)
+ return std::nullopt;
+
+ std::pair<char *, DemangledNameInfo> demangled =
+ GetItaniumDemangledStr(m_mangled.GetCString());
+ free(demangled.first);
+ return std::move(demangled.second);
+}
+
ConstString Mangled::GetDisplayDemangledName() const {
if (Language *lang = Language::FindPlugin(GuessLanguage()))
return lang->GetDisplayDemangledName(*this);
@@ -469,7 +462,6 @@ bool Mangled::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr,
const StringTableReader &strtab) {
m_mangled.Clear();
m_demangled.Clear();
- m_demangled_info.reset();
MangledEncoding encoding = (MangledEncoding)data.GetU8(offset_ptr);
switch (encoding) {
case Empty:
@@ -549,8 +541,8 @@ void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const {
}
ConstString Mangled::GetBaseName() const {
- const auto *demangled_info = GetDemangledInfo();
- if (demangled_info == nullptr)
+ std::optional<DemangledNameInfo> demangled_info = ComputeDemangledInfo();
+ if (!demangled_info)
return {};
ConstString demangled_name = GetDemangledName();
diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp
index 5b7e15f71c26c1..c6a05757af6ace 100644
--- a/lldb/source/Core/ModuleList.cpp
+++ b/lldb/source/Core/ModuleList.cpp
@@ -198,6 +198,12 @@ bool ModuleListProperties::GetLoadSymbolOnDemand() const {
idx, g_modulelist_properties[idx].default_uint_value != 0);
}
+uint64_t ModuleListProperties::GetDemangledNameInfoCacheSize() const {
+ const uint32_t idx = ePropertyDemangledNameInfoCacheSize;
+ return GetPropertyAtIndexAs<uint64_t>(
+ idx, g_modulelist_properties[idx].default_uint_value);
+}
+
ModuleList::ModuleList() : m_modules(), m_modules_mutex() {}
ModuleList::ModuleList(const ModuleList &rhs) : m_modules(), m_modules_mutex() {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index d453f9c9c4ae08..ea8e94c116a111 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -23,6 +23,7 @@
#include "lldb/Core/DemangledNameInfo.h"
#include "lldb/Core/Mangled.h"
#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/UniqueCStringMap.h"
#include "lldb/DataFormatters/CXXFunctionPointer.h"
@@ -257,6 +258,14 @@ static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream,
return true;
}
+static DemangledNameInfoCache &GetDemangledInfoCache() {
+ static DemangledNameInfoCache g_cache;
+ // Pick up any changes to the size the user made since the last call.
+ g_cache.SetMaxEntries(ModuleList::GetGlobalModuleListProperties()
+ .GetDemangledNameInfoCacheSize());
+ return g_cache;
+}
+
static llvm::Expected<std::pair<llvm::StringRef, DemangledNameInfo>>
GetAndValidateInfo(const SymbolContext &sc) {
Mangled mangled = sc.GetPossiblyInlinedFunctionName();
@@ -269,7 +278,7 @@ GetAndValidateInfo(const SymbolContext &sc) {
"function '{0}' does not have a demangled name",
mangled.GetMangledName());
- const DemangledNameInfo *info = mangled.GetDemangledInfo();
+ std::optional<DemangledNameInfo> info = GetDemangledInfoCache().Get(mangled);
if (!info)
return llvm::createStringErrorV(
"function '{0}' does not have demangled info", demangled_name);
diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp
index 5fecd758fe84e1..35804b600a5902 100644
--- a/lldb/source/Symbol/Symbol.cpp
+++ b/lldb/source/Symbol/Symbol.cpp
@@ -772,7 +772,7 @@ bool Symbol::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr,
// the DataFileCache version number in Symtab::Encode
// will need to be incremented as well.
#if __SIZEOF_POINTER__ == 8
-static_assert(sizeof(lldb_private::Symbol) == 80,
+static_assert(sizeof(lldb_private::Symbol) == 72,
"Symbol size has changed, Symbol::Encode and Decode likely need "
"to be updated");
#endif
diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp
index 5bef28c58385d1..22f3b26ed88c78 100644
--- a/lldb/source/Symbol/Symtab.cpp
+++ b/lldb/source/Symbol/Symtab.cpp
@@ -1234,7 +1234,7 @@ constexpr uint32_t CURRENT_CACHE_VERSION = 2;
// CURRENT_CACHE_VERSION number will need to be incremented.
#if __SIZEOF_POINTER__ == 8
static_assert(
- sizeof(lldb_private::Symbol) == 80,
+ sizeof(lldb_private::Symbol) == 72,
"Symbol size has changed, DataFileCache version likely needs updating");
#endif
diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp
index dc0ecde94b1743..ffaba0b9f9c7e7 100644
--- a/lldb/unittests/Core/MangledTest.cpp
+++ b/lldb/unittests/Core/MangledTest.cpp
@@ -353,78 +353,179 @@ static bool NameInfoEquals(const DemangledNameInfo &lhs,
rhs.QualifiersRange);
}
-TEST(MangledTest, DemangledNameInfo_SetMangledResets) {
+TEST(MangledTest, DemangledNameInfo_SetMangled) {
Mangled mangled;
- EXPECT_EQ(mangled.GetDemangledInfo(), nullptr);
+ EXPECT_EQ(mangled.ComputeDemangledInfo(), std::nullopt);
mangled.SetMangledName(ConstString("_Z3foov"));
ASSERT_TRUE(mangled);
- ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
// Keep a copy of the original demangled info.
- DemangledNameInfo info1 = *mangled.GetDemangledInfo();
- EXPECT_TRUE(info1.hasBasename());
+ std::optional<DemangledNameInfo> info1 = mangled.ComputeDemangledInfo();
+ ASSERT_NE(info1, std::nullopt);
+ EXPECT_TRUE(info1->hasBasename());
mangled.SetMangledName(ConstString("_Z4funcv"));
// Should have re-calculated demangled-info since mangled name changed.
- ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
- DemangledNameInfo info2 = *mangled.GetDemangledInfo();
- EXPECT_TRUE(info2.hasBasename());
+ std::optional<DemangledNameInfo> info2 = mangled.ComputeDemangledInfo();
+ ASSERT_NE(info2, std::nullopt);
+ EXPECT_TRUE(info2->hasBasename());
- EXPECT_FALSE(NameInfoEquals(info1, info2));
+ EXPECT_FALSE(NameInfoEquals(*info1, *info2));
EXPECT_EQ(mangled.GetDemangledName(), "func()");
}
-TEST(MangledTest, DemangledNameInfo_SetDemangledResets) {
+TEST(MangledTest, DemangledNameInfo_SetDemangled) {
Mangled mangled("_Z3foov");
ASSERT_TRUE(mangled);
+ std::optional<DemangledNameInfo> info = mangled.ComputeDemangledInfo();
+ ASSERT_NE(info, std::nullopt);
+
mangled.SetDemangledName(ConstString(""));
- // Mangled name hasn't changed, so GetDemangledInfo causes re-demangling
- // of previously set mangled name.
- EXPECT_NE(mangled.GetDemangledInfo(), nullptr);
- EXPECT_EQ(mangled.GetDemangledName(), "foo()");
+ // The info is computed from the mangled name, which hasn't changed.
+ std::optional<DemangledNameInfo> new_info = mangled.ComputeDemangledInfo();
+ ASSERT_NE(new_info, std::nullopt);
+ EXPECT_TRUE(NameInfoEquals(*new_info, *info));
}
TEST(MangledTest, DemangledNameInfo_Clear) {
Mangled mangled("_Z3foov");
ASSERT_TRUE(mangled);
- EXPECT_NE(mangled.GetDemangledInfo(), nullptr);
+ EXPECT_NE(mangled.ComputeDemangledInfo(), std::nullopt);
mangled.Clear();
- EXPECT_EQ(mangled.GetDemangledInfo(), nullptr);
+ EXPECT_EQ(mangled.ComputeDemangledInfo(), std::nullopt);
}
TEST(MangledTest, DemangledNameInfo_SetValue) {
Mangled mangled("_Z4funcv");
ASSERT_TRUE(mangled);
- ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
// Keep a copy of the original demangled info.
- DemangledNameInfo demangled_func = *mangled.GetDemangledInfo();
+ std::optional<DemangledNameInfo> demangled_func =
+ mangled.ComputeDemangledInfo();
+ ASSERT_NE(demangled_func, std::nullopt);
- // SetValue(mangled) resets demangled-info
+ // SetValue(mangled) re-computes demangled-info
mangled.SetValue(ConstString("_Z3foov"));
- ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
- DemangledNameInfo demangled_foo = *mangled.GetDemangledInfo();
- EXPECT_FALSE(NameInfoEquals(demangled_foo, demangled_func));
+ std::optional<DemangledNameInfo> demangled_foo =
+ mangled.ComputeDemangledInfo();
+ ASSERT_NE(demangled_foo, std::nullopt);
+ EXPECT_FALSE(NameInfoEquals(*demangled_foo, *demangled_func));
- // SetValue(demangled) resets demangled-info
+ // SetValue(demangled) re-computes demangled-info
mangled.SetValue(ConstString("_Z4funcv"));
- EXPECT_TRUE(NameInfoEquals(*mangled.GetDemangledInfo(), demangled_func));
+ ASSERT_NE(mangled.ComputeDemangledInfo(), std::nullopt);
+ EXPECT_TRUE(NameInfoEquals(*mangled.ComputeDemangledInfo(), *demangled_func));
- // SetValue(empty) resets demangled-info
+ // SetValue(empty) leaves nothing to compute demangled-info from
mangled.SetValue(ConstString());
- EXPECT_EQ(mangled.GetDemangledInfo(), nullptr);
+ EXPECT_EQ(mangled.ComputeDemangledInfo(), std::nullopt);
// Demangling invalid mangled name will set demangled-info
// (without a valid basename).
mangled.SetValue(ConstString("_Zinvalid"));
- ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
- EXPECT_FALSE(mangled.GetDemangledInfo()->hasBasename());
+ ASSERT_NE(mangled.ComputeDemangledInfo(), std::nullopt);
+ EXPECT_FALSE(mangled.ComputeDemangledInfo()->hasBasename());
+}
+
+/// Returns the mangled name of the function 'void foo<i>()'.
+static Mangled MakeUniqueFunctionName(unsigned i) {
+ std::string basename = "foo" + std::to_string(i);
+ return Mangled("_Z" + std::to_string(basename.size()) + basename + "v");
+}
+
+TEST(MangledTest, DemangledNameInfoCache_Get) {
+ DemangledNameInfoCache cache;
+
+ Mangled func("_Z4funcv");
+ Mangled foo("_Z3foov");
+
+ std::optional<DemangledNameInfo> func_info = cache.Get(func);
+ ASSERT_NE(func_info, std::nullopt);
+ ASSERT_TRUE(func_info->hasBasename());
+
+ // Repeated lookups (of both the same and a copied object) hand out the same
+ // info.
+ EXPECT_TRUE(NameInfoEquals(*cache.Get(func), *func_info));
+ EXPECT_TRUE(NameInfoEquals(*cache.Get(Mangled("_Z4funcv")), *func_info));
+
+ // Different names get their own info.
+ std::optional<DemangledNameInfo> foo_info = cache.Get(foo);
+ ASSERT_NE(foo_info, std::nullopt);
+ EXPECT_FALSE(NameInfoEquals(*foo_info, *func_info));
+
+ // Names that aren't mangled don't return any name info.
+ Mangled not_mangled("not_mangled");
+ EXPECT_EQ(cache.Get(not_mangled), std::nullopt);
+ EXPECT_EQ(cache.Get(not_mangled), std::nullopt);
+
+ cache.Clear();
+
+ EXPECT_TRUE(NameInfoEquals(*cache.Get(func), *func_info));
+}
+
+TEST(MangledTest, DemangledNameInfoCache_MaxEntries) {
+ DemangledNameInfoCache cache(/*max_entries=*/4);
+
+ std::optional<DemangledNameInfo> expected = cache.Get(Mangled("_Z4funcv"));
+ ASSERT_NE(expected, std::nullopt);
+
+ // Overflow the cache a few times over. It drops entries to stay within its
+ // limit, but never hands out anything but the correct info.
+ for (unsigned i = 0; i < 4 * cache.GetMaxEntries(); ++i) {
+ cache.Get(MakeUniqueFunctionName(i));
+ EXPECT_TRUE(NameInfoEquals(*cache.Get(Mangled("_Z4funcv")), *expected));
+ EXPECT_LE(cache.GetNumEntries(), cache.GetMaxEntries());
+ }
+
+ // Only a single entry is dropped to make room, so a full cache stays full.
+ EXPECT_EQ(cache.GetNumEntries(), cache.GetMaxEntries());
+
+ // Further lookups keep the cache at its limit.
+ cache.Get(Mangled("_Z4funcv"));
+ EXPECT_EQ(cache.GetNumEntries(), cache.GetMaxEntries());
+
+ cache.Clear();
+ EXPECT_EQ(cache.GetNumEntries(), 0u);
+}
+
+TEST(MangledTest, DemangledNameInfoCache_SetMaxEntries) {
+ DemangledNameInfoCache cache(/*max_entries=*/4);
+
+ std::optional<DemangledNameInfo> expected = cache.Get(Mangled("_Z4funcv"));
+ ASSERT_NE(expected, std::nullopt);
+
+ cache.SetMaxEntries(8);
+ EXPECT_EQ(cache.GetMaxEntries(), 8u);
+ EXPECT_TRUE(NameInfoEquals(*cache.Get(Mangled("_Z4funcv")), *expected));
+
+ // Fill the cache with 8 unique functions.
+ for (unsigned i = 0; i < 8; ++i) {
+ cache.Get(MakeUniqueFunctionName(i));
+ }
+ EXPECT_EQ(cache.GetNumEntries(), 8u);
+
+ // Growing the limit keeps all entries.
+ cache.SetMaxEntries(16);
+ EXPECT_EQ(cache.GetNumEntries(), 8u);
+
+ // Shrinking the limit below the number of entries clears the cache.
+ cache.SetMaxEntries(2);
+ EXPECT_EQ(cache.GetNumEntries(), 0u);
+ EXPECT_TRUE(NameInfoEquals(*cache.Get(Mangled("_Z4funcv")), *expected));
+
+ // A limit of 0 disables the cache, but lookups still compute the info.
+ cache.SetMaxEntries(0);
+ EXPECT_EQ(cache.GetMaxEntries(), 0u);
+ EXPECT_EQ(cache.GetNumEntries(), 0u);
+ EXPECT_TRUE(NameInfoEquals(*cache.Get(Mangled("_Z4funcv")), *expected));
+ EXPECT_EQ(cache.Get(Mangled("not_mangled")), std::nullopt);
+ EXPECT_EQ(cache.GetNumEntries(), 0u);
}
struct DemanglingPartsTestCase {
More information about the lldb-commits
mailing list