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 15*0eae32dcSDimitry Andric #include "sanitizer_atomic.h" 1668d75effSDimitry Andric #include "sanitizer_common.h" 1768d75effSDimitry Andric #include "sanitizer_hash.h" 18*0eae32dcSDimitry 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 77*0eae32dcSDimitry Andric static void CompressStackStore() { 78*0eae32dcSDimitry Andric u64 start = MonotonicNanoTime(); 79*0eae32dcSDimitry Andric uptr diff = stackStore.Pack(static_cast<StackStore::Compression>( 80*0eae32dcSDimitry Andric Abs(common_flags()->compress_stack_depot))); 81*0eae32dcSDimitry Andric if (!diff) 82*0eae32dcSDimitry Andric return; 83*0eae32dcSDimitry Andric u64 finish = MonotonicNanoTime(); 84*0eae32dcSDimitry Andric uptr total_before = theDepot.GetStats().allocated + diff; 85*0eae32dcSDimitry Andric VPrintf(1, "%s: StackDepot released %zu KiB out of %zu KiB in %llu ms\n", 86*0eae32dcSDimitry Andric SanitizerToolName, diff >> 10, total_before >> 10, 87*0eae32dcSDimitry Andric (finish - start) / 1000000); 88*0eae32dcSDimitry Andric } 89*0eae32dcSDimitry Andric 90*0eae32dcSDimitry Andric namespace { 91*0eae32dcSDimitry Andric 92*0eae32dcSDimitry Andric class CompressThread { 93*0eae32dcSDimitry Andric public: 94*0eae32dcSDimitry Andric constexpr CompressThread() = default; 95*0eae32dcSDimitry Andric void NewWorkNotify(); 96*0eae32dcSDimitry Andric void Stop(); 97*0eae32dcSDimitry Andric void LockAndStop() NO_THREAD_SAFETY_ANALYSIS; 98*0eae32dcSDimitry Andric void Unlock() NO_THREAD_SAFETY_ANALYSIS; 99*0eae32dcSDimitry Andric 100*0eae32dcSDimitry Andric private: 101*0eae32dcSDimitry Andric enum class State { 102*0eae32dcSDimitry Andric NotStarted = 0, 103*0eae32dcSDimitry Andric Started, 104*0eae32dcSDimitry Andric Failed, 105*0eae32dcSDimitry Andric Stopped, 106*0eae32dcSDimitry Andric }; 107*0eae32dcSDimitry Andric 108*0eae32dcSDimitry Andric void Run(); 109*0eae32dcSDimitry Andric 110*0eae32dcSDimitry Andric bool WaitForWork() { 111*0eae32dcSDimitry Andric semaphore_.Wait(); 112*0eae32dcSDimitry Andric return atomic_load(&run_, memory_order_acquire); 113*0eae32dcSDimitry Andric } 114*0eae32dcSDimitry Andric 115*0eae32dcSDimitry Andric Semaphore semaphore_ = {}; 116*0eae32dcSDimitry Andric StaticSpinMutex mutex_ = {}; 117*0eae32dcSDimitry Andric State state_ GUARDED_BY(mutex_) = State::NotStarted; 118*0eae32dcSDimitry Andric void *thread_ GUARDED_BY(mutex_) = nullptr; 119*0eae32dcSDimitry Andric atomic_uint8_t run_ = {}; 120*0eae32dcSDimitry Andric }; 121*0eae32dcSDimitry Andric 122*0eae32dcSDimitry Andric static CompressThread compress_thread; 123*0eae32dcSDimitry Andric 124*0eae32dcSDimitry Andric void CompressThread::NewWorkNotify() { 125*0eae32dcSDimitry Andric int compress = common_flags()->compress_stack_depot; 126*0eae32dcSDimitry Andric if (!compress) 127*0eae32dcSDimitry Andric return; 128*0eae32dcSDimitry Andric if (compress > 0 /* for testing or debugging */) { 129*0eae32dcSDimitry Andric SpinMutexLock l(&mutex_); 130*0eae32dcSDimitry Andric if (state_ == State::NotStarted) { 131*0eae32dcSDimitry Andric atomic_store(&run_, 1, memory_order_release); 132*0eae32dcSDimitry Andric CHECK_EQ(nullptr, thread_); 133*0eae32dcSDimitry Andric thread_ = internal_start_thread( 134*0eae32dcSDimitry Andric [](void *arg) -> void * { 135*0eae32dcSDimitry Andric reinterpret_cast<CompressThread *>(arg)->Run(); 136*0eae32dcSDimitry Andric return nullptr; 137*0eae32dcSDimitry Andric }, 138*0eae32dcSDimitry Andric this); 139*0eae32dcSDimitry Andric state_ = thread_ ? State::Started : State::Failed; 140*0eae32dcSDimitry Andric } 141*0eae32dcSDimitry Andric if (state_ == State::Started) { 142*0eae32dcSDimitry Andric semaphore_.Post(); 143*0eae32dcSDimitry Andric return; 144*0eae32dcSDimitry Andric } 145*0eae32dcSDimitry Andric } 146*0eae32dcSDimitry Andric CompressStackStore(); 147*0eae32dcSDimitry Andric } 148*0eae32dcSDimitry Andric 149*0eae32dcSDimitry Andric void CompressThread::Run() { 150*0eae32dcSDimitry Andric VPrintf(1, "%s: StackDepot compression thread started\n", SanitizerToolName); 151*0eae32dcSDimitry Andric while (WaitForWork()) CompressStackStore(); 152*0eae32dcSDimitry Andric VPrintf(1, "%s: StackDepot compression thread stopped\n", SanitizerToolName); 153*0eae32dcSDimitry Andric } 154*0eae32dcSDimitry Andric 155*0eae32dcSDimitry Andric void CompressThread::Stop() { 156*0eae32dcSDimitry Andric void *t = nullptr; 157*0eae32dcSDimitry Andric { 158*0eae32dcSDimitry Andric SpinMutexLock l(&mutex_); 159*0eae32dcSDimitry Andric if (state_ != State::Started) 160*0eae32dcSDimitry Andric return; 161*0eae32dcSDimitry Andric state_ = State::Stopped; 162*0eae32dcSDimitry Andric CHECK_NE(nullptr, thread_); 163*0eae32dcSDimitry Andric t = thread_; 164*0eae32dcSDimitry Andric thread_ = nullptr; 165*0eae32dcSDimitry Andric } 166*0eae32dcSDimitry Andric atomic_store(&run_, 0, memory_order_release); 167*0eae32dcSDimitry Andric semaphore_.Post(); 168*0eae32dcSDimitry Andric internal_join_thread(t); 169*0eae32dcSDimitry Andric } 170*0eae32dcSDimitry Andric 171*0eae32dcSDimitry Andric void CompressThread::LockAndStop() { 172*0eae32dcSDimitry Andric mutex_.Lock(); 173*0eae32dcSDimitry Andric if (state_ != State::Started) 174*0eae32dcSDimitry Andric return; 175*0eae32dcSDimitry Andric CHECK_NE(nullptr, thread_); 176*0eae32dcSDimitry Andric 177*0eae32dcSDimitry Andric atomic_store(&run_, 0, memory_order_release); 178*0eae32dcSDimitry Andric semaphore_.Post(); 179*0eae32dcSDimitry Andric internal_join_thread(thread_); 180*0eae32dcSDimitry Andric // Allow to restart after Unlock() if needed. 181*0eae32dcSDimitry Andric state_ = State::NotStarted; 182*0eae32dcSDimitry Andric thread_ = nullptr; 183*0eae32dcSDimitry Andric } 184*0eae32dcSDimitry Andric 185*0eae32dcSDimitry Andric void CompressThread::Unlock() { mutex_.Unlock(); } 186*0eae32dcSDimitry Andric 187*0eae32dcSDimitry Andric } // namespace 188*0eae32dcSDimitry Andric 189349cc55cSDimitry Andric void StackDepotNode::store(u32 id, const args_type &args, hash_type hash) { 190349cc55cSDimitry Andric stack_hash = hash; 1914824e7fdSDimitry Andric uptr pack = 0; 1924824e7fdSDimitry Andric store_id = stackStore.Store(args, &pack); 193*0eae32dcSDimitry Andric if (LIKELY(!pack)) 194*0eae32dcSDimitry Andric return; 195*0eae32dcSDimitry Andric compress_thread.NewWorkNotify(); 196349cc55cSDimitry Andric } 197349cc55cSDimitry Andric 198349cc55cSDimitry Andric StackDepotNode::args_type StackDepotNode::load(u32 id) const { 199349cc55cSDimitry Andric if (!store_id) 200349cc55cSDimitry Andric return {}; 201349cc55cSDimitry Andric return stackStore.Load(store_id); 202349cc55cSDimitry Andric } 203349cc55cSDimitry Andric 204349cc55cSDimitry Andric StackDepotStats StackDepotGetStats() { return theDepot.GetStats(); } 205349cc55cSDimitry Andric 206349cc55cSDimitry Andric u32 StackDepotPut(StackTrace stack) { return theDepot.Put(stack); } 207349cc55cSDimitry Andric 20868d75effSDimitry Andric StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) { 209349cc55cSDimitry Andric return StackDepotNode::get_handle(theDepot.Put(stack)); 21068d75effSDimitry Andric } 21168d75effSDimitry Andric 21268d75effSDimitry Andric StackTrace StackDepotGet(u32 id) { 21368d75effSDimitry Andric return theDepot.Get(id); 21468d75effSDimitry Andric } 21568d75effSDimitry Andric 21668d75effSDimitry Andric void StackDepotLockAll() { 21768d75effSDimitry Andric theDepot.LockAll(); 218*0eae32dcSDimitry Andric compress_thread.LockAndStop(); 219*0eae32dcSDimitry Andric stackStore.LockAll(); 22068d75effSDimitry Andric } 22168d75effSDimitry Andric 22268d75effSDimitry Andric void StackDepotUnlockAll() { 223*0eae32dcSDimitry Andric stackStore.UnlockAll(); 224*0eae32dcSDimitry Andric compress_thread.Unlock(); 22568d75effSDimitry Andric theDepot.UnlockAll(); 22668d75effSDimitry Andric } 22768d75effSDimitry Andric 228e8d8bef9SDimitry Andric void StackDepotPrintAll() { 229e8d8bef9SDimitry Andric #if !SANITIZER_GO 230e8d8bef9SDimitry Andric theDepot.PrintAll(); 231e8d8bef9SDimitry Andric #endif 232e8d8bef9SDimitry Andric } 233e8d8bef9SDimitry Andric 234*0eae32dcSDimitry Andric void StackDepotStopBackgroundThread() { compress_thread.Stop(); } 235*0eae32dcSDimitry Andric 236349cc55cSDimitry Andric StackDepotHandle StackDepotNode::get_handle(u32 id) { 237349cc55cSDimitry Andric return StackDepotHandle(&theDepot.nodes[id], id); 23868d75effSDimitry Andric } 23968d75effSDimitry Andric 240349cc55cSDimitry Andric void StackDepotTestOnlyUnmap() { 241349cc55cSDimitry Andric theDepot.TestOnlyUnmap(); 242349cc55cSDimitry Andric stackStore.TestOnlyUnmap(); 24368d75effSDimitry Andric } 24468d75effSDimitry Andric 24568d75effSDimitry Andric } // namespace __sanitizer 246