LLDB mainline
StringPool.cpp
Go to the documentation of this file.
1//===-- StringPool.cpp ----------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
11#include "llvm/ADT/StringMap.h"
12#include "llvm/Support/Allocator.h"
13#include "llvm/Support/Threading.h"
14
15#include <array>
16#include <mutex>
17#include <shared_mutex>
18#include <utility>
19
20#include <cstdint>
21#include <cstring>
22
23using namespace lldb_private;
24
25#if !defined(__APPLE__)
26using PoolMutex = std::shared_mutex;
27#else
28#include <os/lock.h>
29
30namespace {
31/// On Apple platforms os_unfair_lock is significantly faster than
32/// pthread_rwlock for concurrent writes, and roughly on par for concurrent
33/// reads.
34///
35/// The class satisfies both Lockable and SharedLockable so it composes with
36/// std::lock_guard and std::shared_lock.
37class PoolMutex {
38public:
39 void lock() { os_unfair_lock_lock(&m_lock); }
40 void unlock() { os_unfair_lock_unlock(&m_lock); }
41 void lock_shared() { os_unfair_lock_lock(&m_lock); }
42 void unlock_shared() { os_unfair_lock_unlock(&m_lock); }
43
44private:
45 os_unfair_lock m_lock = OS_UNFAIR_LOCK_INIT;
46};
47} // namespace
48#endif
49
50namespace {
51/// The default BumpPtrAllocatorImpl slab size.
52constexpr size_t AllocatorSlabSize = 4096;
53constexpr size_t SizeThreshold = AllocatorSlabSize;
54constexpr size_t NumPools = 256;
55/// Every pool shard has its own allocator which receives an equal share of
56/// the string allocations. This means that when allocating many strings, every
57/// allocator sees only its small share of allocations and assumes LLDB only
58/// allocated a small amount of memory so far. In reality LLDB allocated a total
59/// memory that is N times as large as what the allocator sees (where N is the
60/// number of string pools). This causes that the BumpPtrAllocator continues a
61/// long time to allocate memory in small chunks which only makes sense when
62/// allocating a small amount of memory (which is true from the perspective of a
63/// single allocator). On some systems doing all these small memory allocations
64/// causes LLDB to spend a lot of time in malloc, so we need to force all these
65/// allocators to behave like one allocator in terms of scaling their memory
66/// allocations with increased demand. To do this we set the growth delay for
67/// each single allocator to a rate so that our pool of allocators scales their
68/// memory allocations similar to a single BumpPtrAllocatorImpl.
69///
70/// Currently we have 256 string pools and the normal growth delay of the
71/// BumpPtrAllocatorImpl is 128 (i.e., the memory allocation size increases
72/// every 128 full chunks), so by changing the delay to 1 we get a
73/// total growth delay in our allocator collection of 256/1 = 256. This is
74/// still only half as fast as a normal allocator but we can't go any faster
75/// without decreasing the number of string pools.
76constexpr size_t AllocatorGrowthDelay = 1;
77using Allocator =
78 llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, AllocatorSlabSize,
79 SizeThreshold, AllocatorGrowthDelay>;
80using StringPoolValueType = const char *;
81using StringMapType = llvm::StringMap<StringPoolValueType, Allocator>;
82using StringPoolEntryType = llvm::StringMapEntry<StringPoolValueType>;
83
84StringPoolEntryType &GetStringMapEntryFromKeyData(const char *keyData) {
85 return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData);
86}
87} // namespace
88
91 StringMapType m_string_map;
92 /// The exact number of bytes used by this pool.
93 /// This excludes alignment, padding and redzones.
94 std::size_t used_bytes = 0;
95};
96
98
99StringPool::~StringPool() = default;
100
101// Frameworks and dylibs aren't supposed to have global C++ initializers so we
102// hide the string pool in a static function so that it will get initialized on
103// the first call to this static function.
104//
105// Note, for now we make the string pool a pointer to the pool, because we
106// can't guarantee that some objects won't get destroyed after the global
107// destructor chain is run, and trying to make sure no destructors touch
108// ConstStrings is difficult. So we leak the pool instead.
110 static llvm::once_flag g_pool_initialization_flag;
111 static StringPool *g_string_pool = nullptr;
112
113 llvm::call_once(g_pool_initialization_flag,
114 []() { g_string_pool = new StringPool(); });
115
116 return *g_string_pool;
117}
118
119static StringPool *g_system_pool = nullptr;
120
122 assert(!g_system_pool && "system pool already initialized");
124}
125
127
129 assert(g_system_pool && "system pool not initialized");
131}
132
134 return m_string_pools[((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff];
135}
136
138 return selectPool(StringMapType::hash(s));
139}
140
141size_t StringPool::GetConstCStringLength(const char *ccstr) {
142 if (ccstr != nullptr) {
143 // Since the entry is read only, and we derive the entry entirely from
144 // the pointer, we don't need the lock.
145 const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr);
146 return entry.getKeyLength();
147 }
148 return 0;
149}
150
151const char *StringPool::GetMangledCounterpart(llvm::StringRef str) {
152 const char *const ccstr = str.data();
153 if (ccstr != nullptr) {
154 const PoolEntry &pool = selectPool(str);
155 std::shared_lock<PoolMutex> lock(pool.m_mutex);
156 return GetStringMapEntryFromKeyData(ccstr).getValue();
157 }
158 return nullptr;
159}
160
161const char *StringPool::GetConstCString(const char *cstr) {
162 if (cstr != nullptr)
163 return GetConstCStringWithLength(cstr, strlen(cstr));
164 return nullptr;
165}
166
167const char *StringPool::GetConstCStringWithLength(const char *cstr,
168 size_t cstr_len) {
169 if (cstr != nullptr)
170 return Intern(llvm::StringRef(cstr, cstr_len));
171 return nullptr;
172}
173
174const char *StringPool::Intern(llvm::StringRef string_ref) {
175 if (string_ref.data()) {
176 const uint32_t string_hash = StringMapType::hash(string_ref);
177 PoolEntry &pool = selectPool(string_hash);
178
179 {
180 std::shared_lock<PoolMutex> lock(pool.m_mutex);
181 auto it = pool.m_string_map.find(string_ref, string_hash);
182 if (it != pool.m_string_map.end())
183 return it->getKeyData();
184 }
185
186 std::lock_guard<PoolMutex> lock(pool.m_mutex);
187 pool.used_bytes += string_ref.size();
188 StringPoolEntryType &entry =
189 *pool.m_string_map
190 .insert(std::make_pair(string_ref, nullptr), string_hash)
191 .first;
192 return entry.getKeyData();
193 }
194 return nullptr;
195}
196
197const char *
199 llvm::StringRef mangled) {
200 const char *demangled_ccstr = nullptr;
201 const char *const mangled_ccstr = mangled.data();
202
203 {
204 const uint32_t demangled_hash = StringMapType::hash(demangled);
205 PoolEntry &pool = selectPool(demangled_hash);
206 std::lock_guard<PoolMutex> lock(pool.m_mutex);
207
208 // Make or update string pool entry with the mangled counterpart
209 StringMapType &map = pool.m_string_map;
210 auto [entry, inserted] =
211 map.try_emplace_with_hash(demangled, demangled_hash);
212 if (inserted)
213 pool.used_bytes += demangled.size();
214
215 entry->second = mangled_ccstr;
216
217 // Extract the const version of the demangled_cstr
218 demangled_ccstr = entry->getKeyData();
219 }
220
221 {
222 // Now assign the demangled const string as the counterpart of the
223 // mangled const string...
224 PoolEntry &pool = selectPool(mangled);
225 std::lock_guard<PoolMutex> lock(pool.m_mutex);
226 GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr);
227 }
228
229 // Return the constant demangled C string
230 return demangled_ccstr;
231}
232
234 size_t cstr_len) {
235 if (cstr != nullptr) {
236 const size_t trimmed_len = strnlen(cstr, cstr_len);
237 return GetConstCStringWithLength(cstr, trimmed_len);
238 }
239 return nullptr;
240}
241
244 for (size_t i = 0; i < NumPools; ++i) {
245 const PoolEntry &pool = m_string_pools[i];
246 std::shared_lock<PoolMutex> lock(pool.m_mutex);
247 const Allocator &alloc = pool.m_string_map.getAllocator();
248 stats.bytes_total += alloc.getTotalMemory();
249 stats.bytes_used += pool.used_bytes;
250 }
251 return stats;
252}
std::shared_mutex PoolMutex
static StringPool * g_system_pool
A handle to a StringPool that does not own it.
Definition StringPool.h:72
A thread-safe pool of interned, null-terminated strings.
Definition StringPool.h:25
std::unique_ptr< PoolEntry[]> m_string_pools
Definition StringPool.h:68
const char * GetConstCString(const char *cstr)
PoolEntry & selectPool(uint32_t hash)
const char * Intern(llvm::StringRef str)
Returns the pooled copy of str, or nullptr if str has no data.
const char * GetMangledCounterpart(llvm::StringRef str)
static StringPoolRef GetSystemPool()
The system pool. Only valid between Initialize and Terminate.
ConstString::MemoryStats GetMemoryStats() const
const char * GetConstCStringWithLength(const char *cstr, size_t cstr_len)
static StringPool & GetGlobal()
The pool backing ConstString. It is never destroyed.
static size_t GetConstCStringLength(const char *ccstr)
Length of a string returned by any pool.
static void Initialize()
Set up and tear down the system pool, for strings that are not owned by a Debugger.
const char * GetConstTrimmedCStringWithLength(const char *cstr, size_t cstr_len)
const char * GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled, llvm::StringRef mangled)
Interns demangled and links it with the already interned mangled.
A class that represents a running process on the host machine.
std::size_t used_bytes
The exact number of bytes used by this pool.