180814287SRaphael Isemann //===-- ConstString.cpp ---------------------------------------------------===// 2bf9a7730SZachary Turner // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bf9a7730SZachary Turner // 7bf9a7730SZachary Turner //===----------------------------------------------------------------------===// 8bf9a7730SZachary Turner 9bf9a7730SZachary Turner #include "lldb/Utility/ConstString.h" 10bf9a7730SZachary Turner 114479ac15SZachary Turner #include "lldb/Utility/Stream.h" 12bf9a7730SZachary Turner 13bf9a7730SZachary Turner #include "llvm/ADT/StringMap.h" 14672d2c12SJonas Devlieghere #include "llvm/ADT/iterator.h" 15672d2c12SJonas Devlieghere #include "llvm/Support/Allocator.h" 16672d2c12SJonas Devlieghere #include "llvm/Support/DJB.h" 17672d2c12SJonas Devlieghere #include "llvm/Support/FormatProviders.h" 18bf9a7730SZachary Turner #include "llvm/Support/RWMutex.h" 19c5f28e2aSKamil Rytarowski #include "llvm/Support/Threading.h" 204479ac15SZachary Turner 214479ac15SZachary Turner #include <array> 22672d2c12SJonas Devlieghere #include <utility> 234479ac15SZachary Turner 24672d2c12SJonas Devlieghere #include <inttypes.h> 25672d2c12SJonas Devlieghere #include <stdint.h> 26672d2c12SJonas Devlieghere #include <string.h> 27bf9a7730SZachary Turner 28bf9a7730SZachary Turner using namespace lldb_private; 29bf9a7730SZachary Turner 30bf9a7730SZachary Turner class Pool { 31bf9a7730SZachary Turner public: 32fad012bcSRaphael Isemann /// The default BumpPtrAllocatorImpl slab size. 33fad012bcSRaphael Isemann static const size_t AllocatorSlabSize = 4096; 34fad012bcSRaphael Isemann static const size_t SizeThreshold = AllocatorSlabSize; 35fad012bcSRaphael Isemann /// Every Pool has its own allocator which receives an equal share of 36fad012bcSRaphael Isemann /// the ConstString allocations. This means that when allocating many 37fad012bcSRaphael Isemann /// ConstStrings, every allocator sees only its small share of allocations and 38fad012bcSRaphael Isemann /// assumes LLDB only allocated a small amount of memory so far. In reality 39fad012bcSRaphael Isemann /// LLDB allocated a total memory that is N times as large as what the 40fad012bcSRaphael Isemann /// allocator sees (where N is the number of string pools). This causes that 41fad012bcSRaphael Isemann /// the BumpPtrAllocator continues a long time to allocate memory in small 42fad012bcSRaphael Isemann /// chunks which only makes sense when allocating a small amount of memory 43fad012bcSRaphael Isemann /// (which is true from the perspective of a single allocator). On some 44fad012bcSRaphael Isemann /// systems doing all these small memory allocations causes LLDB to spend 45fad012bcSRaphael Isemann /// a lot of time in malloc, so we need to force all these allocators to 46fad012bcSRaphael Isemann /// behave like one allocator in terms of scaling their memory allocations 47fad012bcSRaphael Isemann /// with increased demand. To do this we set the growth delay for each single 48fad012bcSRaphael Isemann /// allocator to a rate so that our pool of allocators scales their memory 49fad012bcSRaphael Isemann /// allocations similar to a single BumpPtrAllocatorImpl. 50fad012bcSRaphael Isemann /// 51fad012bcSRaphael Isemann /// Currently we have 256 string pools and the normal growth delay of the 52fad012bcSRaphael Isemann /// BumpPtrAllocatorImpl is 128 (i.e., the memory allocation size increases 53fad012bcSRaphael Isemann /// every 128 full chunks), so by changing the delay to 1 we get a 54fad012bcSRaphael Isemann /// total growth delay in our allocator collection of 256/1 = 256. This is 55fad012bcSRaphael Isemann /// still only half as fast as a normal allocator but we can't go any faster 56fad012bcSRaphael Isemann /// without decreasing the number of string pools. 57fad012bcSRaphael Isemann static const size_t AllocatorGrowthDelay = 1; 58fad012bcSRaphael Isemann typedef llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, AllocatorSlabSize, 59fad012bcSRaphael Isemann SizeThreshold, AllocatorGrowthDelay> 60fad012bcSRaphael Isemann Allocator; 61bf9a7730SZachary Turner typedef const char *StringPoolValueType; 62fad012bcSRaphael Isemann typedef llvm::StringMap<StringPoolValueType, Allocator> StringPool; 63bf9a7730SZachary Turner typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType; 64bf9a7730SZachary Turner 65bf9a7730SZachary Turner static StringPoolEntryType & 66bf9a7730SZachary Turner GetStringMapEntryFromKeyData(const char *keyData) { 678070bf0aSPavel Labath return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData); 68bf9a7730SZachary Turner } 69bf9a7730SZachary Turner 708070bf0aSPavel Labath static size_t GetConstCStringLength(const char *ccstr) { 71bf9a7730SZachary Turner if (ccstr != nullptr) { 7205097246SAdrian Prantl // Since the entry is read only, and we derive the entry entirely from 7305097246SAdrian Prantl // the pointer, we don't need the lock. 74bf9a7730SZachary Turner const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr); 75bf9a7730SZachary Turner return entry.getKey().size(); 76bf9a7730SZachary Turner } 77bf9a7730SZachary Turner return 0; 78bf9a7730SZachary Turner } 79bf9a7730SZachary Turner 80bf9a7730SZachary Turner StringPoolValueType GetMangledCounterpart(const char *ccstr) const { 81bf9a7730SZachary Turner if (ccstr != nullptr) { 82bf9a7730SZachary Turner const uint8_t h = hash(llvm::StringRef(ccstr)); 83bf9a7730SZachary Turner llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex); 84bf9a7730SZachary Turner return GetStringMapEntryFromKeyData(ccstr).getValue(); 85bf9a7730SZachary Turner } 86bf9a7730SZachary Turner return nullptr; 87bf9a7730SZachary Turner } 88bf9a7730SZachary Turner 89bf9a7730SZachary Turner const char *GetConstCString(const char *cstr) { 90bf9a7730SZachary Turner if (cstr != nullptr) 91bf9a7730SZachary Turner return GetConstCStringWithLength(cstr, strlen(cstr)); 92bf9a7730SZachary Turner return nullptr; 93bf9a7730SZachary Turner } 94bf9a7730SZachary Turner 95bf9a7730SZachary Turner const char *GetConstCStringWithLength(const char *cstr, size_t cstr_len) { 96bf9a7730SZachary Turner if (cstr != nullptr) 97bf9a7730SZachary Turner return GetConstCStringWithStringRef(llvm::StringRef(cstr, cstr_len)); 98bf9a7730SZachary Turner return nullptr; 99bf9a7730SZachary Turner } 100bf9a7730SZachary Turner 101bf9a7730SZachary Turner const char *GetConstCStringWithStringRef(const llvm::StringRef &string_ref) { 102bf9a7730SZachary Turner if (string_ref.data()) { 103bf9a7730SZachary Turner const uint8_t h = hash(string_ref); 104bf9a7730SZachary Turner 105bf9a7730SZachary Turner { 106bf9a7730SZachary Turner llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex); 107bf9a7730SZachary Turner auto it = m_string_pools[h].m_string_map.find(string_ref); 108bf9a7730SZachary Turner if (it != m_string_pools[h].m_string_map.end()) 109bf9a7730SZachary Turner return it->getKeyData(); 110bf9a7730SZachary Turner } 111bf9a7730SZachary Turner 112bf9a7730SZachary Turner llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex); 113bf9a7730SZachary Turner StringPoolEntryType &entry = 114bf9a7730SZachary Turner *m_string_pools[h] 115bf9a7730SZachary Turner .m_string_map.insert(std::make_pair(string_ref, nullptr)) 116bf9a7730SZachary Turner .first; 117bf9a7730SZachary Turner return entry.getKeyData(); 118bf9a7730SZachary Turner } 119bf9a7730SZachary Turner return nullptr; 120bf9a7730SZachary Turner } 121bf9a7730SZachary Turner 122bf9a7730SZachary Turner const char * 12319a357adSPavel Labath GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled, 124bf9a7730SZachary Turner const char *mangled_ccstr) { 125bf9a7730SZachary Turner const char *demangled_ccstr = nullptr; 126bf9a7730SZachary Turner 127bf9a7730SZachary Turner { 12819a357adSPavel Labath const uint8_t h = hash(demangled); 129bf9a7730SZachary Turner llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex); 130bf9a7730SZachary Turner 1312397a2b6SStefan Granitz // Make or update string pool entry with the mangled counterpart 1322397a2b6SStefan Granitz StringPool &map = m_string_pools[h].m_string_map; 1332397a2b6SStefan Granitz StringPoolEntryType &entry = *map.try_emplace(demangled).first; 1342397a2b6SStefan Granitz 1352397a2b6SStefan Granitz entry.second = mangled_ccstr; 136bf9a7730SZachary Turner 137bf9a7730SZachary Turner // Extract the const version of the demangled_cstr 138bf9a7730SZachary Turner demangled_ccstr = entry.getKeyData(); 139bf9a7730SZachary Turner } 140bf9a7730SZachary Turner 141bf9a7730SZachary Turner { 142bf9a7730SZachary Turner // Now assign the demangled const string as the counterpart of the 143bf9a7730SZachary Turner // mangled const string... 144bf9a7730SZachary Turner const uint8_t h = hash(llvm::StringRef(mangled_ccstr)); 145bf9a7730SZachary Turner llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex); 146bf9a7730SZachary Turner GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr); 147bf9a7730SZachary Turner } 148bf9a7730SZachary Turner 149bf9a7730SZachary Turner // Return the constant demangled C string 150bf9a7730SZachary Turner return demangled_ccstr; 151bf9a7730SZachary Turner } 152bf9a7730SZachary Turner 153bf9a7730SZachary Turner const char *GetConstTrimmedCStringWithLength(const char *cstr, 154bf9a7730SZachary Turner size_t cstr_len) { 155bf9a7730SZachary Turner if (cstr != nullptr) { 156bb3609e4SJan Kratochvil const size_t trimmed_len = strnlen(cstr, cstr_len); 157bf9a7730SZachary Turner return GetConstCStringWithLength(cstr, trimmed_len); 158bf9a7730SZachary Turner } 159bf9a7730SZachary Turner return nullptr; 160bf9a7730SZachary Turner } 161bf9a7730SZachary Turner 16205097246SAdrian Prantl // Return the size in bytes that this object and any items in its collection 16305097246SAdrian Prantl // of uniqued strings + data count values takes in memory. 164bf9a7730SZachary Turner size_t MemorySize() const { 165bf9a7730SZachary Turner size_t mem_size = sizeof(Pool); 166bf9a7730SZachary Turner for (const auto &pool : m_string_pools) { 167bf9a7730SZachary Turner llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex); 168bf9a7730SZachary Turner for (const auto &entry : pool.m_string_map) 169bf9a7730SZachary Turner mem_size += sizeof(StringPoolEntryType) + entry.getKey().size(); 170bf9a7730SZachary Turner } 171bf9a7730SZachary Turner return mem_size; 172bf9a7730SZachary Turner } 173bf9a7730SZachary Turner 174bf9a7730SZachary Turner protected: 175bf9a7730SZachary Turner uint8_t hash(const llvm::StringRef &s) const { 176560ce2c7SJonas Devlieghere uint32_t h = llvm::djbHash(s); 177bf9a7730SZachary Turner return ((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff; 178bf9a7730SZachary Turner } 179bf9a7730SZachary Turner 180bf9a7730SZachary Turner struct PoolEntry { 181bf9a7730SZachary Turner mutable llvm::sys::SmartRWMutex<false> m_mutex; 182755420c0SRaphael Isemann StringPool m_string_map; 183bf9a7730SZachary Turner }; 184bf9a7730SZachary Turner 185bf9a7730SZachary Turner std::array<PoolEntry, 256> m_string_pools; 186bf9a7730SZachary Turner }; 187bf9a7730SZachary Turner 18805097246SAdrian Prantl // Frameworks and dylibs aren't supposed to have global C++ initializers so we 18905097246SAdrian Prantl // hide the string pool in a static function so that it will get initialized on 19005097246SAdrian Prantl // the first call to this static function. 191bf9a7730SZachary Turner // 19205097246SAdrian Prantl // Note, for now we make the string pool a pointer to the pool, because we 19305097246SAdrian Prantl // can't guarantee that some objects won't get destroyed after the global 19405097246SAdrian Prantl // destructor chain is run, and trying to make sure no destructors touch 19505097246SAdrian Prantl // ConstStrings is difficult. So we leak the pool instead. 196bf9a7730SZachary Turner static Pool &StringPool() { 197c5f28e2aSKamil Rytarowski static llvm::once_flag g_pool_initialization_flag; 198bf9a7730SZachary Turner static Pool *g_string_pool = nullptr; 199bf9a7730SZachary Turner 200c5f28e2aSKamil Rytarowski llvm::call_once(g_pool_initialization_flag, 201bf9a7730SZachary Turner []() { g_string_pool = new Pool(); }); 202bf9a7730SZachary Turner 203bf9a7730SZachary Turner return *g_string_pool; 204bf9a7730SZachary Turner } 205bf9a7730SZachary Turner 206bf9a7730SZachary Turner ConstString::ConstString(const char *cstr) 207bf9a7730SZachary Turner : m_string(StringPool().GetConstCString(cstr)) {} 208bf9a7730SZachary Turner 209bf9a7730SZachary Turner ConstString::ConstString(const char *cstr, size_t cstr_len) 210bf9a7730SZachary Turner : m_string(StringPool().GetConstCStringWithLength(cstr, cstr_len)) {} 211bf9a7730SZachary Turner 212bf9a7730SZachary Turner ConstString::ConstString(const llvm::StringRef &s) 21312886f04SRaphael Isemann : m_string(StringPool().GetConstCStringWithStringRef(s)) {} 214bf9a7730SZachary Turner 215*ccdb5b4bSJonas Devlieghere bool ConstString::operator<(ConstString rhs) const { 216bf9a7730SZachary Turner if (m_string == rhs.m_string) 217bf9a7730SZachary Turner return false; 218bf9a7730SZachary Turner 2198070bf0aSPavel Labath llvm::StringRef lhs_string_ref(GetStringRef()); 2208070bf0aSPavel Labath llvm::StringRef rhs_string_ref(rhs.GetStringRef()); 221bf9a7730SZachary Turner 222bf9a7730SZachary Turner // If both have valid C strings, then return the comparison 223bf9a7730SZachary Turner if (lhs_string_ref.data() && rhs_string_ref.data()) 224bf9a7730SZachary Turner return lhs_string_ref < rhs_string_ref; 225bf9a7730SZachary Turner 226bf9a7730SZachary Turner // Else one of them was nullptr, so if LHS is nullptr then it is less than 227bf9a7730SZachary Turner return lhs_string_ref.data() == nullptr; 228bf9a7730SZachary Turner } 229bf9a7730SZachary Turner 230*ccdb5b4bSJonas Devlieghere Stream &lldb_private::operator<<(Stream &s, ConstString str) { 231bf9a7730SZachary Turner const char *cstr = str.GetCString(); 232bf9a7730SZachary Turner if (cstr != nullptr) 233bf9a7730SZachary Turner s << cstr; 234bf9a7730SZachary Turner 235bf9a7730SZachary Turner return s; 236bf9a7730SZachary Turner } 237bf9a7730SZachary Turner 238bf9a7730SZachary Turner size_t ConstString::GetLength() const { 2398070bf0aSPavel Labath return Pool::GetConstCStringLength(m_string); 240bf9a7730SZachary Turner } 241bf9a7730SZachary Turner 242*ccdb5b4bSJonas Devlieghere bool ConstString::Equals(ConstString lhs, ConstString rhs, 243bf9a7730SZachary Turner const bool case_sensitive) { 244bf9a7730SZachary Turner if (lhs.m_string == rhs.m_string) 245bf9a7730SZachary Turner return true; 246bf9a7730SZachary Turner 247bf9a7730SZachary Turner // Since the pointers weren't equal, and identical ConstStrings always have 24805097246SAdrian Prantl // identical pointers, the result must be false for case sensitive equality 24905097246SAdrian Prantl // test. 250bf9a7730SZachary Turner if (case_sensitive) 251bf9a7730SZachary Turner return false; 252bf9a7730SZachary Turner 253bf9a7730SZachary Turner // perform case insensitive equality test 2548070bf0aSPavel Labath llvm::StringRef lhs_string_ref(lhs.GetStringRef()); 2558070bf0aSPavel Labath llvm::StringRef rhs_string_ref(rhs.GetStringRef()); 256bf9a7730SZachary Turner return lhs_string_ref.equals_lower(rhs_string_ref); 257bf9a7730SZachary Turner } 258bf9a7730SZachary Turner 259*ccdb5b4bSJonas Devlieghere int ConstString::Compare(ConstString lhs, ConstString rhs, 260bf9a7730SZachary Turner const bool case_sensitive) { 261bf9a7730SZachary Turner // If the iterators are the same, this is the same string 262bf9a7730SZachary Turner const char *lhs_cstr = lhs.m_string; 263bf9a7730SZachary Turner const char *rhs_cstr = rhs.m_string; 264bf9a7730SZachary Turner if (lhs_cstr == rhs_cstr) 265bf9a7730SZachary Turner return 0; 266bf9a7730SZachary Turner if (lhs_cstr && rhs_cstr) { 2678070bf0aSPavel Labath llvm::StringRef lhs_string_ref(lhs.GetStringRef()); 2688070bf0aSPavel Labath llvm::StringRef rhs_string_ref(rhs.GetStringRef()); 269bf9a7730SZachary Turner 270bf9a7730SZachary Turner if (case_sensitive) { 271bf9a7730SZachary Turner return lhs_string_ref.compare(rhs_string_ref); 272bf9a7730SZachary Turner } else { 273bf9a7730SZachary Turner return lhs_string_ref.compare_lower(rhs_string_ref); 274bf9a7730SZachary Turner } 275bf9a7730SZachary Turner } 276bf9a7730SZachary Turner 277bf9a7730SZachary Turner if (lhs_cstr) 278bf9a7730SZachary Turner return +1; // LHS isn't nullptr but RHS is 279bf9a7730SZachary Turner else 280bf9a7730SZachary Turner return -1; // LHS is nullptr but RHS isn't 281bf9a7730SZachary Turner } 282bf9a7730SZachary Turner 283bf9a7730SZachary Turner void ConstString::Dump(Stream *s, const char *fail_value) const { 284bf9a7730SZachary Turner if (s != nullptr) { 285bf9a7730SZachary Turner const char *cstr = AsCString(fail_value); 286bf9a7730SZachary Turner if (cstr != nullptr) 287bf9a7730SZachary Turner s->PutCString(cstr); 288bf9a7730SZachary Turner } 289bf9a7730SZachary Turner } 290bf9a7730SZachary Turner 291bf9a7730SZachary Turner void ConstString::DumpDebug(Stream *s) const { 292bf9a7730SZachary Turner const char *cstr = GetCString(); 293bf9a7730SZachary Turner size_t cstr_len = GetLength(); 294bf9a7730SZachary Turner // Only print the parens if we have a non-nullptr string 295bf9a7730SZachary Turner const char *parens = cstr ? "\"" : ""; 296bf9a7730SZachary Turner s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64, 297bf9a7730SZachary Turner static_cast<int>(sizeof(void *) * 2), 298bf9a7730SZachary Turner static_cast<const void *>(this), parens, cstr, parens, 299bf9a7730SZachary Turner static_cast<uint64_t>(cstr_len)); 300bf9a7730SZachary Turner } 301bf9a7730SZachary Turner 302bf9a7730SZachary Turner void ConstString::SetCString(const char *cstr) { 303bf9a7730SZachary Turner m_string = StringPool().GetConstCString(cstr); 304bf9a7730SZachary Turner } 305bf9a7730SZachary Turner 306bf9a7730SZachary Turner void ConstString::SetString(const llvm::StringRef &s) { 307bf9a7730SZachary Turner m_string = StringPool().GetConstCStringWithLength(s.data(), s.size()); 308bf9a7730SZachary Turner } 309bf9a7730SZachary Turner 31019a357adSPavel Labath void ConstString::SetStringWithMangledCounterpart(llvm::StringRef demangled, 311*ccdb5b4bSJonas Devlieghere ConstString mangled) { 312bf9a7730SZachary Turner m_string = StringPool().GetConstCStringAndSetMangledCounterPart( 313bf9a7730SZachary Turner demangled, mangled.m_string); 314bf9a7730SZachary Turner } 315bf9a7730SZachary Turner 316bf9a7730SZachary Turner bool ConstString::GetMangledCounterpart(ConstString &counterpart) const { 317bf9a7730SZachary Turner counterpart.m_string = StringPool().GetMangledCounterpart(m_string); 318bf9a7730SZachary Turner return (bool)counterpart; 319bf9a7730SZachary Turner } 320bf9a7730SZachary Turner 321bf9a7730SZachary Turner void ConstString::SetCStringWithLength(const char *cstr, size_t cstr_len) { 322bf9a7730SZachary Turner m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len); 323bf9a7730SZachary Turner } 324bf9a7730SZachary Turner 325bf9a7730SZachary Turner void ConstString::SetTrimmedCStringWithLength(const char *cstr, 326bf9a7730SZachary Turner size_t cstr_len) { 327bf9a7730SZachary Turner m_string = StringPool().GetConstTrimmedCStringWithLength(cstr, cstr_len); 328bf9a7730SZachary Turner } 329bf9a7730SZachary Turner 330bf9a7730SZachary Turner size_t ConstString::StaticMemorySize() { 331bf9a7730SZachary Turner // Get the size of the static string pool 332bf9a7730SZachary Turner return StringPool().MemorySize(); 333bf9a7730SZachary Turner } 3343b7e1981SPavel Labath 3353b7e1981SPavel Labath void llvm::format_provider<ConstString>::format(const ConstString &CS, 3363b7e1981SPavel Labath llvm::raw_ostream &OS, 3373b7e1981SPavel Labath llvm::StringRef Options) { 338642bc15dSRaphael Isemann format_provider<StringRef>::format(CS.GetStringRef(), OS, Options); 3393b7e1981SPavel Labath } 340bc9b6b33SJonas Devlieghere 341bc9b6b33SJonas Devlieghere void llvm::yaml::ScalarTraits<ConstString>::output(const ConstString &Val, 342bc9b6b33SJonas Devlieghere void *, raw_ostream &Out) { 343bc9b6b33SJonas Devlieghere Out << Val.GetStringRef(); 344bc9b6b33SJonas Devlieghere } 345bc9b6b33SJonas Devlieghere 346bc9b6b33SJonas Devlieghere llvm::StringRef 347bc9b6b33SJonas Devlieghere llvm::yaml::ScalarTraits<ConstString>::input(llvm::StringRef Scalar, void *, 348bc9b6b33SJonas Devlieghere ConstString &Val) { 349bc9b6b33SJonas Devlieghere Val = ConstString(Scalar); 350bc9b6b33SJonas Devlieghere return {}; 351bc9b6b33SJonas Devlieghere } 352