xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp (revision 0eae32dcef82f6f06de6419a0d623d7def0cc8f6)
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