168d75effSDimitry Andric //===-- sanitizer_stackdepot.cpp ------------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is shared between AddressSanitizer and ThreadSanitizer 1068d75effSDimitry Andric // run-time libraries. 1168d75effSDimitry Andric //===----------------------------------------------------------------------===// 1268d75effSDimitry Andric 1368d75effSDimitry Andric #include "sanitizer_stackdepot.h" 1468d75effSDimitry Andric 150eae32dcSDimitry Andric #include "sanitizer_atomic.h" 1668d75effSDimitry Andric #include "sanitizer_common.h" 1768d75effSDimitry Andric #include "sanitizer_hash.h" 180eae32dcSDimitry Andric #include "sanitizer_mutex.h" 19349cc55cSDimitry Andric #include "sanitizer_stack_store.h" 2068d75effSDimitry Andric #include "sanitizer_stackdepotbase.h" 2168d75effSDimitry Andric 2268d75effSDimitry Andric namespace __sanitizer { 2368d75effSDimitry Andric 2468d75effSDimitry Andric struct StackDepotNode { 25349cc55cSDimitry Andric using hash_type = u64; 26349cc55cSDimitry Andric hash_type stack_hash; 27349cc55cSDimitry Andric u32 link; 284824e7fdSDimitry Andric StackStore::Id store_id; 2968d75effSDimitry Andric 3068d75effSDimitry Andric static const u32 kTabSizeLog = SANITIZER_ANDROID ? 16 : 20; 3168d75effSDimitry Andric 3268d75effSDimitry Andric typedef StackTrace args_type; 33349cc55cSDimitry Andric bool eq(hash_type hash, const args_type &args) const { 34349cc55cSDimitry Andric return hash == stack_hash; 3568d75effSDimitry Andric } 36349cc55cSDimitry Andric static uptr allocated(); 37349cc55cSDimitry Andric static hash_type hash(const args_type &args) { 38349cc55cSDimitry Andric MurMur2Hash64Builder H(args.size * sizeof(uptr)); 3968d75effSDimitry Andric for (uptr i = 0; i < args.size; i++) H.add(args.trace[i]); 40349cc55cSDimitry Andric H.add(args.tag); 4168d75effSDimitry Andric return H.get(); 4268d75effSDimitry Andric } 4368d75effSDimitry Andric static bool is_valid(const args_type &args) { 4468d75effSDimitry Andric return args.size > 0 && args.trace; 4568d75effSDimitry Andric } 46349cc55cSDimitry Andric void store(u32 id, const args_type &args, hash_type hash); 47349cc55cSDimitry Andric args_type load(u32 id) const; 48349cc55cSDimitry Andric static StackDepotHandle get_handle(u32 id); 4968d75effSDimitry Andric 5068d75effSDimitry Andric typedef StackDepotHandle handle_type; 5168d75effSDimitry Andric }; 5268d75effSDimitry Andric 53349cc55cSDimitry Andric static StackStore stackStore; 5468d75effSDimitry Andric 5568d75effSDimitry Andric // FIXME(dvyukov): this single reserved bit is used in TSan. 5668d75effSDimitry Andric typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog> 5768d75effSDimitry Andric StackDepot; 5868d75effSDimitry Andric static StackDepot theDepot; 59349cc55cSDimitry Andric // Keep mutable data out of frequently access nodes to improve caching 60349cc55cSDimitry Andric // efficiency. 61349cc55cSDimitry Andric static TwoLevelMap<atomic_uint32_t, StackDepot::kNodesSize1, 62349cc55cSDimitry Andric StackDepot::kNodesSize2> 63349cc55cSDimitry Andric useCounts; 6468d75effSDimitry Andric 65349cc55cSDimitry Andric int StackDepotHandle::use_count() const { 66349cc55cSDimitry Andric return atomic_load_relaxed(&useCounts[id_]); 6768d75effSDimitry Andric } 6868d75effSDimitry Andric 69349cc55cSDimitry Andric void StackDepotHandle::inc_use_count_unsafe() { 70349cc55cSDimitry Andric atomic_fetch_add(&useCounts[id_], 1, memory_order_relaxed); 7168d75effSDimitry Andric } 7268d75effSDimitry Andric 73349cc55cSDimitry Andric uptr StackDepotNode::allocated() { 744824e7fdSDimitry Andric return stackStore.Allocated() + useCounts.MemoryUsage(); 75349cc55cSDimitry Andric } 76349cc55cSDimitry Andric 770eae32dcSDimitry Andric static void CompressStackStore() { 7881ad6265SDimitry Andric u64 start = Verbosity() >= 1 ? MonotonicNanoTime() : 0; 790eae32dcSDimitry Andric uptr diff = stackStore.Pack(static_cast<StackStore::Compression>( 800eae32dcSDimitry Andric Abs(common_flags()->compress_stack_depot))); 810eae32dcSDimitry Andric if (!diff) 820eae32dcSDimitry Andric return; 8381ad6265SDimitry Andric if (Verbosity() >= 1) { 840eae32dcSDimitry Andric u64 finish = MonotonicNanoTime(); 850eae32dcSDimitry Andric uptr total_before = theDepot.GetStats().allocated + diff; 860eae32dcSDimitry Andric VPrintf(1, "%s: StackDepot released %zu KiB out of %zu KiB in %llu ms\n", 870eae32dcSDimitry Andric SanitizerToolName, diff >> 10, total_before >> 10, 880eae32dcSDimitry Andric (finish - start) / 1000000); 890eae32dcSDimitry Andric } 9081ad6265SDimitry Andric } 910eae32dcSDimitry Andric 920eae32dcSDimitry Andric namespace { 930eae32dcSDimitry Andric 940eae32dcSDimitry Andric class CompressThread { 950eae32dcSDimitry Andric public: 960eae32dcSDimitry Andric constexpr CompressThread() = default; 970eae32dcSDimitry Andric void NewWorkNotify(); 980eae32dcSDimitry Andric void Stop(); 9904eeddc0SDimitry Andric void LockAndStop() SANITIZER_NO_THREAD_SAFETY_ANALYSIS; 10004eeddc0SDimitry Andric void Unlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS; 1010eae32dcSDimitry Andric 1020eae32dcSDimitry Andric private: 1030eae32dcSDimitry Andric enum class State { 1040eae32dcSDimitry Andric NotStarted = 0, 1050eae32dcSDimitry Andric Started, 1060eae32dcSDimitry Andric Failed, 1070eae32dcSDimitry Andric Stopped, 1080eae32dcSDimitry Andric }; 1090eae32dcSDimitry Andric 1100eae32dcSDimitry Andric void Run(); 1110eae32dcSDimitry Andric 1120eae32dcSDimitry Andric bool WaitForWork() { 1130eae32dcSDimitry Andric semaphore_.Wait(); 1140eae32dcSDimitry Andric return atomic_load(&run_, memory_order_acquire); 1150eae32dcSDimitry Andric } 1160eae32dcSDimitry Andric 1170eae32dcSDimitry Andric Semaphore semaphore_ = {}; 1180eae32dcSDimitry Andric StaticSpinMutex mutex_ = {}; 11904eeddc0SDimitry Andric State state_ SANITIZER_GUARDED_BY(mutex_) = State::NotStarted; 12004eeddc0SDimitry Andric void *thread_ SANITIZER_GUARDED_BY(mutex_) = nullptr; 1210eae32dcSDimitry Andric atomic_uint8_t run_ = {}; 1220eae32dcSDimitry Andric }; 1230eae32dcSDimitry Andric 1240eae32dcSDimitry Andric static CompressThread compress_thread; 1250eae32dcSDimitry Andric 1260eae32dcSDimitry Andric void CompressThread::NewWorkNotify() { 1270eae32dcSDimitry Andric int compress = common_flags()->compress_stack_depot; 1280eae32dcSDimitry Andric if (!compress) 1290eae32dcSDimitry Andric return; 1300eae32dcSDimitry Andric if (compress > 0 /* for testing or debugging */) { 1310eae32dcSDimitry Andric SpinMutexLock l(&mutex_); 1320eae32dcSDimitry Andric if (state_ == State::NotStarted) { 1330eae32dcSDimitry Andric atomic_store(&run_, 1, memory_order_release); 1340eae32dcSDimitry Andric CHECK_EQ(nullptr, thread_); 1350eae32dcSDimitry Andric thread_ = internal_start_thread( 1360eae32dcSDimitry Andric [](void *arg) -> void * { 1370eae32dcSDimitry Andric reinterpret_cast<CompressThread *>(arg)->Run(); 1380eae32dcSDimitry Andric return nullptr; 1390eae32dcSDimitry Andric }, 1400eae32dcSDimitry Andric this); 1410eae32dcSDimitry Andric state_ = thread_ ? State::Started : State::Failed; 1420eae32dcSDimitry Andric } 1430eae32dcSDimitry Andric if (state_ == State::Started) { 1440eae32dcSDimitry Andric semaphore_.Post(); 1450eae32dcSDimitry Andric return; 1460eae32dcSDimitry Andric } 1470eae32dcSDimitry Andric } 1480eae32dcSDimitry Andric CompressStackStore(); 1490eae32dcSDimitry Andric } 1500eae32dcSDimitry Andric 1510eae32dcSDimitry Andric void CompressThread::Run() { 1520eae32dcSDimitry Andric VPrintf(1, "%s: StackDepot compression thread started\n", SanitizerToolName); 1530eae32dcSDimitry Andric while (WaitForWork()) CompressStackStore(); 1540eae32dcSDimitry Andric VPrintf(1, "%s: StackDepot compression thread stopped\n", SanitizerToolName); 1550eae32dcSDimitry Andric } 1560eae32dcSDimitry Andric 1570eae32dcSDimitry Andric void CompressThread::Stop() { 1580eae32dcSDimitry Andric void *t = nullptr; 1590eae32dcSDimitry Andric { 1600eae32dcSDimitry Andric SpinMutexLock l(&mutex_); 1610eae32dcSDimitry Andric if (state_ != State::Started) 1620eae32dcSDimitry Andric return; 1630eae32dcSDimitry Andric state_ = State::Stopped; 1640eae32dcSDimitry Andric CHECK_NE(nullptr, thread_); 1650eae32dcSDimitry Andric t = thread_; 1660eae32dcSDimitry Andric thread_ = nullptr; 1670eae32dcSDimitry Andric } 1680eae32dcSDimitry Andric atomic_store(&run_, 0, memory_order_release); 1690eae32dcSDimitry Andric semaphore_.Post(); 1700eae32dcSDimitry Andric internal_join_thread(t); 1710eae32dcSDimitry Andric } 1720eae32dcSDimitry Andric 1730eae32dcSDimitry Andric void CompressThread::LockAndStop() { 1740eae32dcSDimitry Andric mutex_.Lock(); 1750eae32dcSDimitry Andric if (state_ != State::Started) 1760eae32dcSDimitry Andric return; 1770eae32dcSDimitry Andric CHECK_NE(nullptr, thread_); 1780eae32dcSDimitry Andric 1790eae32dcSDimitry Andric atomic_store(&run_, 0, memory_order_release); 1800eae32dcSDimitry Andric semaphore_.Post(); 1810eae32dcSDimitry Andric internal_join_thread(thread_); 1820eae32dcSDimitry Andric // Allow to restart after Unlock() if needed. 1830eae32dcSDimitry Andric state_ = State::NotStarted; 1840eae32dcSDimitry Andric thread_ = nullptr; 1850eae32dcSDimitry Andric } 1860eae32dcSDimitry Andric 1870eae32dcSDimitry Andric void CompressThread::Unlock() { mutex_.Unlock(); } 1880eae32dcSDimitry Andric 1890eae32dcSDimitry Andric } // namespace 1900eae32dcSDimitry Andric 191349cc55cSDimitry Andric void StackDepotNode::store(u32 id, const args_type &args, hash_type hash) { 192349cc55cSDimitry Andric stack_hash = hash; 1934824e7fdSDimitry Andric uptr pack = 0; 1944824e7fdSDimitry Andric store_id = stackStore.Store(args, &pack); 1950eae32dcSDimitry Andric if (LIKELY(!pack)) 1960eae32dcSDimitry Andric return; 1970eae32dcSDimitry Andric compress_thread.NewWorkNotify(); 198349cc55cSDimitry Andric } 199349cc55cSDimitry Andric 200349cc55cSDimitry Andric StackDepotNode::args_type StackDepotNode::load(u32 id) const { 201349cc55cSDimitry Andric if (!store_id) 202349cc55cSDimitry Andric return {}; 203349cc55cSDimitry Andric return stackStore.Load(store_id); 204349cc55cSDimitry Andric } 205349cc55cSDimitry Andric 206349cc55cSDimitry Andric StackDepotStats StackDepotGetStats() { return theDepot.GetStats(); } 207349cc55cSDimitry Andric 208349cc55cSDimitry Andric u32 StackDepotPut(StackTrace stack) { return theDepot.Put(stack); } 209349cc55cSDimitry Andric 21068d75effSDimitry Andric StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) { 211349cc55cSDimitry Andric return StackDepotNode::get_handle(theDepot.Put(stack)); 21268d75effSDimitry Andric } 21368d75effSDimitry Andric 21468d75effSDimitry Andric StackTrace StackDepotGet(u32 id) { 21568d75effSDimitry Andric return theDepot.Get(id); 21668d75effSDimitry Andric } 21768d75effSDimitry Andric 218*cb14a3feSDimitry Andric void StackDepotLockBeforeFork() { 219*cb14a3feSDimitry Andric theDepot.LockBeforeFork(); 2200eae32dcSDimitry Andric compress_thread.LockAndStop(); 2210eae32dcSDimitry Andric stackStore.LockAll(); 22268d75effSDimitry Andric } 22368d75effSDimitry Andric 224*cb14a3feSDimitry Andric void StackDepotUnlockAfterFork(bool fork_child) { 2250eae32dcSDimitry Andric stackStore.UnlockAll(); 2260eae32dcSDimitry Andric compress_thread.Unlock(); 227*cb14a3feSDimitry Andric theDepot.UnlockAfterFork(fork_child); 22868d75effSDimitry Andric } 22968d75effSDimitry Andric 230e8d8bef9SDimitry Andric void StackDepotPrintAll() { 231e8d8bef9SDimitry Andric #if !SANITIZER_GO 232e8d8bef9SDimitry Andric theDepot.PrintAll(); 233e8d8bef9SDimitry Andric #endif 234e8d8bef9SDimitry Andric } 235e8d8bef9SDimitry Andric 2360eae32dcSDimitry Andric void StackDepotStopBackgroundThread() { compress_thread.Stop(); } 2370eae32dcSDimitry Andric 238349cc55cSDimitry Andric StackDepotHandle StackDepotNode::get_handle(u32 id) { 239349cc55cSDimitry Andric return StackDepotHandle(&theDepot.nodes[id], id); 24068d75effSDimitry Andric } 24168d75effSDimitry Andric 242349cc55cSDimitry Andric void StackDepotTestOnlyUnmap() { 243349cc55cSDimitry Andric theDepot.TestOnlyUnmap(); 244349cc55cSDimitry Andric stackStore.TestOnlyUnmap(); 24568d75effSDimitry Andric } 24668d75effSDimitry Andric 24768d75effSDimitry Andric } // namespace __sanitizer 248