Path: blob/main/contrib/llvm-project/lldb/source/Utility/ConstString.cpp
39587 views
//===-- ConstString.cpp ---------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Utility/ConstString.h"910#include "lldb/Utility/Stream.h"1112#include "llvm/ADT/StringMap.h"13#include "llvm/ADT/iterator.h"14#include "llvm/Support/Allocator.h"15#include "llvm/Support/DJB.h"16#include "llvm/Support/FormatProviders.h"17#include "llvm/Support/RWMutex.h"18#include "llvm/Support/Threading.h"1920#include <array>21#include <utility>2223#include <cinttypes>24#include <cstdint>25#include <cstring>2627using namespace lldb_private;2829class Pool {30public:31/// The default BumpPtrAllocatorImpl slab size.32static const size_t AllocatorSlabSize = 4096;33static const size_t SizeThreshold = AllocatorSlabSize;34/// Every Pool has its own allocator which receives an equal share of35/// the ConstString allocations. This means that when allocating many36/// ConstStrings, every allocator sees only its small share of allocations and37/// assumes LLDB only allocated a small amount of memory so far. In reality38/// LLDB allocated a total memory that is N times as large as what the39/// allocator sees (where N is the number of string pools). This causes that40/// the BumpPtrAllocator continues a long time to allocate memory in small41/// chunks which only makes sense when allocating a small amount of memory42/// (which is true from the perspective of a single allocator). On some43/// systems doing all these small memory allocations causes LLDB to spend44/// a lot of time in malloc, so we need to force all these allocators to45/// behave like one allocator in terms of scaling their memory allocations46/// with increased demand. To do this we set the growth delay for each single47/// allocator to a rate so that our pool of allocators scales their memory48/// allocations similar to a single BumpPtrAllocatorImpl.49///50/// Currently we have 256 string pools and the normal growth delay of the51/// BumpPtrAllocatorImpl is 128 (i.e., the memory allocation size increases52/// every 128 full chunks), so by changing the delay to 1 we get a53/// total growth delay in our allocator collection of 256/1 = 256. This is54/// still only half as fast as a normal allocator but we can't go any faster55/// without decreasing the number of string pools.56static const size_t AllocatorGrowthDelay = 1;57typedef llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, AllocatorSlabSize,58SizeThreshold, AllocatorGrowthDelay>59Allocator;60typedef const char *StringPoolValueType;61typedef llvm::StringMap<StringPoolValueType, Allocator> StringPool;62typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType;6364static StringPoolEntryType &65GetStringMapEntryFromKeyData(const char *keyData) {66return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData);67}6869static size_t GetConstCStringLength(const char *ccstr) {70if (ccstr != nullptr) {71// Since the entry is read only, and we derive the entry entirely from72// the pointer, we don't need the lock.73const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr);74return entry.getKey().size();75}76return 0;77}7879StringPoolValueType GetMangledCounterpart(const char *ccstr) {80if (ccstr != nullptr) {81const PoolEntry &pool = selectPool(llvm::StringRef(ccstr));82llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);83return GetStringMapEntryFromKeyData(ccstr).getValue();84}85return nullptr;86}8788const char *GetConstCString(const char *cstr) {89if (cstr != nullptr)90return GetConstCStringWithLength(cstr, strlen(cstr));91return nullptr;92}9394const char *GetConstCStringWithLength(const char *cstr, size_t cstr_len) {95if (cstr != nullptr)96return GetConstCStringWithStringRef(llvm::StringRef(cstr, cstr_len));97return nullptr;98}99100const char *GetConstCStringWithStringRef(llvm::StringRef string_ref) {101if (string_ref.data()) {102const uint32_t string_hash = StringPool::hash(string_ref);103PoolEntry &pool = selectPool(string_hash);104105{106llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);107auto it = pool.m_string_map.find(string_ref, string_hash);108if (it != pool.m_string_map.end())109return it->getKeyData();110}111112llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex);113StringPoolEntryType &entry =114*pool.m_string_map115.insert(std::make_pair(string_ref, nullptr), string_hash)116.first;117return entry.getKeyData();118}119return nullptr;120}121122const char *123GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled,124const char *mangled_ccstr) {125const char *demangled_ccstr = nullptr;126127{128const uint32_t demangled_hash = StringPool::hash(demangled);129PoolEntry &pool = selectPool(demangled_hash);130llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex);131132// Make or update string pool entry with the mangled counterpart133StringPool &map = pool.m_string_map;134StringPoolEntryType &entry =135*map.try_emplace_with_hash(demangled, demangled_hash).first;136137entry.second = mangled_ccstr;138139// Extract the const version of the demangled_cstr140demangled_ccstr = entry.getKeyData();141}142143{144// Now assign the demangled const string as the counterpart of the145// mangled const string...146PoolEntry &pool = selectPool(llvm::StringRef(mangled_ccstr));147llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex);148GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr);149}150151// Return the constant demangled C string152return demangled_ccstr;153}154155const char *GetConstTrimmedCStringWithLength(const char *cstr,156size_t cstr_len) {157if (cstr != nullptr) {158const size_t trimmed_len = strnlen(cstr, cstr_len);159return GetConstCStringWithLength(cstr, trimmed_len);160}161return nullptr;162}163164ConstString::MemoryStats GetMemoryStats() const {165ConstString::MemoryStats stats;166for (const auto &pool : m_string_pools) {167llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);168const Allocator &alloc = pool.m_string_map.getAllocator();169stats.bytes_total += alloc.getTotalMemory();170stats.bytes_used += alloc.getBytesAllocated();171}172return stats;173}174175protected:176struct PoolEntry {177mutable llvm::sys::SmartRWMutex<false> m_mutex;178StringPool m_string_map;179};180181std::array<PoolEntry, 256> m_string_pools;182183PoolEntry &selectPool(const llvm::StringRef &s) {184return selectPool(StringPool::hash(s));185}186187PoolEntry &selectPool(uint32_t h) {188return m_string_pools[((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff];189}190};191192// Frameworks and dylibs aren't supposed to have global C++ initializers so we193// hide the string pool in a static function so that it will get initialized on194// the first call to this static function.195//196// Note, for now we make the string pool a pointer to the pool, because we197// can't guarantee that some objects won't get destroyed after the global198// destructor chain is run, and trying to make sure no destructors touch199// ConstStrings is difficult. So we leak the pool instead.200static Pool &StringPool() {201static llvm::once_flag g_pool_initialization_flag;202static Pool *g_string_pool = nullptr;203204llvm::call_once(g_pool_initialization_flag,205[]() { g_string_pool = new Pool(); });206207return *g_string_pool;208}209210ConstString::ConstString(const char *cstr)211: m_string(StringPool().GetConstCString(cstr)) {}212213ConstString::ConstString(const char *cstr, size_t cstr_len)214: m_string(StringPool().GetConstCStringWithLength(cstr, cstr_len)) {}215216ConstString::ConstString(llvm::StringRef s)217: m_string(StringPool().GetConstCStringWithStringRef(s)) {}218219bool ConstString::operator<(ConstString rhs) const {220if (m_string == rhs.m_string)221return false;222223llvm::StringRef lhs_string_ref(GetStringRef());224llvm::StringRef rhs_string_ref(rhs.GetStringRef());225226// If both have valid C strings, then return the comparison227if (lhs_string_ref.data() && rhs_string_ref.data())228return lhs_string_ref < rhs_string_ref;229230// Else one of them was nullptr, so if LHS is nullptr then it is less than231return lhs_string_ref.data() == nullptr;232}233234Stream &lldb_private::operator<<(Stream &s, ConstString str) {235const char *cstr = str.GetCString();236if (cstr != nullptr)237s << cstr;238239return s;240}241242size_t ConstString::GetLength() const {243return Pool::GetConstCStringLength(m_string);244}245246bool ConstString::Equals(ConstString lhs, ConstString rhs,247const bool case_sensitive) {248if (lhs.m_string == rhs.m_string)249return true;250251// Since the pointers weren't equal, and identical ConstStrings always have252// identical pointers, the result must be false for case sensitive equality253// test.254if (case_sensitive)255return false;256257// perform case insensitive equality test258llvm::StringRef lhs_string_ref(lhs.GetStringRef());259llvm::StringRef rhs_string_ref(rhs.GetStringRef());260return lhs_string_ref.equals_insensitive(rhs_string_ref);261}262263int ConstString::Compare(ConstString lhs, ConstString rhs,264const bool case_sensitive) {265// If the iterators are the same, this is the same string266const char *lhs_cstr = lhs.m_string;267const char *rhs_cstr = rhs.m_string;268if (lhs_cstr == rhs_cstr)269return 0;270if (lhs_cstr && rhs_cstr) {271llvm::StringRef lhs_string_ref(lhs.GetStringRef());272llvm::StringRef rhs_string_ref(rhs.GetStringRef());273274if (case_sensitive) {275return lhs_string_ref.compare(rhs_string_ref);276} else {277return lhs_string_ref.compare_insensitive(rhs_string_ref);278}279}280281if (lhs_cstr)282return +1; // LHS isn't nullptr but RHS is283else284return -1; // LHS is nullptr but RHS isn't285}286287void ConstString::Dump(Stream *s, const char *fail_value) const {288if (s != nullptr) {289const char *cstr = AsCString(fail_value);290if (cstr != nullptr)291s->PutCString(cstr);292}293}294295void ConstString::DumpDebug(Stream *s) const {296const char *cstr = GetCString();297size_t cstr_len = GetLength();298// Only print the parens if we have a non-nullptr string299const char *parens = cstr ? "\"" : "";300s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64,301static_cast<int>(sizeof(void *) * 2),302static_cast<const void *>(this), parens, cstr, parens,303static_cast<uint64_t>(cstr_len));304}305306void ConstString::SetCString(const char *cstr) {307m_string = StringPool().GetConstCString(cstr);308}309310void ConstString::SetString(llvm::StringRef s) {311m_string = StringPool().GetConstCStringWithStringRef(s);312}313314void ConstString::SetStringWithMangledCounterpart(llvm::StringRef demangled,315ConstString mangled) {316m_string = StringPool().GetConstCStringAndSetMangledCounterPart(317demangled, mangled.m_string);318}319320bool ConstString::GetMangledCounterpart(ConstString &counterpart) const {321counterpart.m_string = StringPool().GetMangledCounterpart(m_string);322return (bool)counterpart;323}324325void ConstString::SetCStringWithLength(const char *cstr, size_t cstr_len) {326m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len);327}328329void ConstString::SetTrimmedCStringWithLength(const char *cstr,330size_t cstr_len) {331m_string = StringPool().GetConstTrimmedCStringWithLength(cstr, cstr_len);332}333334ConstString::MemoryStats ConstString::GetMemoryStats() {335return StringPool().GetMemoryStats();336}337338void llvm::format_provider<ConstString>::format(const ConstString &CS,339llvm::raw_ostream &OS,340llvm::StringRef Options) {341format_provider<StringRef>::format(CS.GetStringRef(), OS, Options);342}343344345