xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
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;
eq__sanitizer::StackDepotNode33349cc55cSDimitry Andric   bool eq(hash_type hash, const args_type &args) const {
34349cc55cSDimitry Andric     return hash == stack_hash;
3568d75effSDimitry Andric   }
36349cc55cSDimitry Andric   static uptr allocated();
hash__sanitizer::StackDepotNode37349cc55cSDimitry 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   }
is_valid__sanitizer::StackDepotNode4368d75effSDimitry 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 
use_count() const65349cc55cSDimitry Andric int StackDepotHandle::use_count() const {
66349cc55cSDimitry Andric   return atomic_load_relaxed(&useCounts[id_]);
6768d75effSDimitry Andric }
6868d75effSDimitry Andric 
inc_use_count_unsafe()69349cc55cSDimitry Andric void StackDepotHandle::inc_use_count_unsafe() {
70349cc55cSDimitry Andric   atomic_fetch_add(&useCounts[id_], 1, memory_order_relaxed);
7168d75effSDimitry Andric }
7268d75effSDimitry Andric 
allocated()73349cc55cSDimitry Andric uptr StackDepotNode::allocated() {
744824e7fdSDimitry Andric   return stackStore.Allocated() + useCounts.MemoryUsage();
75349cc55cSDimitry Andric }
76349cc55cSDimitry Andric 
CompressStackStore()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 
WaitForWork()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 
NewWorkNotify()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 
Run()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 
Stop()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 
LockAndStop()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 
Unlock()1870eae32dcSDimitry Andric void CompressThread::Unlock() { mutex_.Unlock(); }
1880eae32dcSDimitry Andric 
1890eae32dcSDimitry Andric }  // namespace
1900eae32dcSDimitry Andric 
store(u32 id,const args_type & args,hash_type hash)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 
load(u32 id) const200349cc55cSDimitry 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 
StackDepotGetStats()206349cc55cSDimitry Andric StackDepotStats StackDepotGetStats() { return theDepot.GetStats(); }
207349cc55cSDimitry Andric 
StackDepotPut(StackTrace stack)208349cc55cSDimitry Andric u32 StackDepotPut(StackTrace stack) { return theDepot.Put(stack); }
209349cc55cSDimitry Andric 
StackDepotPut_WithHandle(StackTrace stack)21068d75effSDimitry Andric StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) {
211349cc55cSDimitry Andric   return StackDepotNode::get_handle(theDepot.Put(stack));
21268d75effSDimitry Andric }
21368d75effSDimitry Andric 
StackDepotGet(u32 id)21468d75effSDimitry Andric StackTrace StackDepotGet(u32 id) {
21568d75effSDimitry Andric   return theDepot.Get(id);
21668d75effSDimitry Andric }
21768d75effSDimitry Andric 
StackDepotLockBeforeFork()218*cb14a3feSDimitry Andric void StackDepotLockBeforeFork() {
219*cb14a3feSDimitry Andric   theDepot.LockBeforeFork();
2200eae32dcSDimitry Andric   compress_thread.LockAndStop();
2210eae32dcSDimitry Andric   stackStore.LockAll();
22268d75effSDimitry Andric }
22368d75effSDimitry Andric 
StackDepotUnlockAfterFork(bool fork_child)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 
StackDepotPrintAll()230e8d8bef9SDimitry Andric void StackDepotPrintAll() {
231e8d8bef9SDimitry Andric #if !SANITIZER_GO
232e8d8bef9SDimitry Andric   theDepot.PrintAll();
233e8d8bef9SDimitry Andric #endif
234e8d8bef9SDimitry Andric }
235e8d8bef9SDimitry Andric 
StackDepotStopBackgroundThread()2360eae32dcSDimitry Andric void StackDepotStopBackgroundThread() { compress_thread.Stop(); }
2370eae32dcSDimitry Andric 
get_handle(u32 id)238349cc55cSDimitry Andric StackDepotHandle StackDepotNode::get_handle(u32 id) {
239349cc55cSDimitry Andric   return StackDepotHandle(&theDepot.nodes[id], id);
24068d75effSDimitry Andric }
24168d75effSDimitry Andric 
StackDepotTestOnlyUnmap()242349cc55cSDimitry Andric void StackDepotTestOnlyUnmap() {
243349cc55cSDimitry Andric   theDepot.TestOnlyUnmap();
244349cc55cSDimitry Andric   stackStore.TestOnlyUnmap();
24568d75effSDimitry Andric }
24668d75effSDimitry Andric 
24768d75effSDimitry Andric } // namespace __sanitizer
248