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