From fa24c4a1c07ad17c9709e086d86ca6f792fe79f2 Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Mon, 22 Nov 2021 21:24:10 -0800 Subject: [PATCH] [sanitizer] Run Stack compression in background thread Depends on D114495. Reviewed By: dvyukov Differential Revision: https://reviews.llvm.org/D114498 --- .../sanitizer_common_libcdep.cpp | 6 +- .../sanitizer_common/sanitizer_fuchsia.cpp | 3 + .../sanitizer_common/sanitizer_stackdepot.cpp | 112 +++++++++++++++++- .../sanitizer_common/sanitizer_stackdepot.h | 1 + .../TestCases/compress_stack_depot.cpp | 14 ++- 5 files changed, 130 insertions(+), 6 deletions(-) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cpp index 13624a83865d..50564e62645d 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cpp @@ -15,13 +15,14 @@ #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_procmaps.h" - +#include "sanitizer_stackdepot.h" namespace __sanitizer { #if (SANITIZER_LINUX || SANITIZER_NETBSD) && !SANITIZER_GO // Weak default implementation for when sanitizer_stackdepot is not linked in. SANITIZER_WEAK_ATTRIBUTE StackDepotStats StackDepotGetStats() { return {}; } +SANITIZER_WEAK_ATTRIBUTE void StackDepotStopBackgroundThread() {} void *BackgroundThread(void *arg) { VPrintf(1, "%s: Started BackgroundThread\n", SanitizerToolName); @@ -201,6 +202,9 @@ void ProtectGap(uptr addr, uptr size, uptr zero_base_shadow_start, SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify, __sanitizer_sandbox_arguments *args) { +#if (SANITIZER_LINUX || SANITIZER_NETBSD) && !SANITIZER_GO + __sanitizer::StackDepotStopBackgroundThread(); +#endif __sanitizer::PlatformPrepareForSandboxing(args); if (__sanitizer::sandboxing_callback) __sanitizer::sandboxing_callback(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp index 6fdf2c424ba5..66a0fd64a05a 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp @@ -484,6 +484,9 @@ u32 GetNumberOfCPUs() { return zx_system_get_num_cpus(); } uptr GetRSS() { UNIMPLEMENTED(); } +void *internal_start_thread(void *(*func)(void *arg), void *arg) { return 0; } +void internal_join_thread(void *th) {} + void InitializePlatformCommonFlags(CommonFlags *cf) {} } // namespace __sanitizer diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp index 1d3ac5cc778a..c755b1829d2a 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp @@ -12,8 +12,10 @@ #include "sanitizer_stackdepot.h" +#include "sanitizer_atomic.h" #include "sanitizer_common.h" #include "sanitizer_hash.h" +#include "sanitizer_mutex.h" #include "sanitizer_stack_store.h" #include "sanitizer_stackdepotbase.h" @@ -75,7 +77,7 @@ uptr StackDepotNode::allocated() { static void CompressStackStore() { u64 start = MonotonicNanoTime(); uptr diff = stackStore.Pack(static_cast( - common_flags()->compress_stack_depot)); + Abs(common_flags()->compress_stack_depot))); if (!diff) return; u64 finish = MonotonicNanoTime(); @@ -85,12 +87,112 @@ static void CompressStackStore() { (finish - start) / 1000000); } +namespace { + +class CompressThread { + public: + constexpr CompressThread() = default; + void NewWorkNotify(); + void Stop(); + void LockAndStop() NO_THREAD_SAFETY_ANALYSIS; + void Unlock() NO_THREAD_SAFETY_ANALYSIS; + + private: + enum class State { + NotStarted = 0, + Started, + Failed, + Stopped, + }; + + void Run(); + + bool WaitForWork() { + semaphore_.Wait(); + return atomic_load(&run_, memory_order_acquire); + } + + Semaphore semaphore_ = {}; + StaticSpinMutex mutex_ = {}; + State state_ GUARDED_BY(mutex_) = State::NotStarted; + void *thread_ GUARDED_BY(mutex_) = nullptr; + atomic_uint8_t run_ = {}; +}; + +static CompressThread compress_thread; + +void CompressThread::NewWorkNotify() { + int compress = common_flags()->compress_stack_depot; + if (!compress) + return; + if (compress > 0 /* for testing or debugging */) { + SpinMutexLock l(&mutex_); + if (state_ == State::NotStarted) { + atomic_store(&run_, 1, memory_order_release); + CHECK_EQ(nullptr, thread_); + thread_ = internal_start_thread( + [](void *arg) -> void * { + reinterpret_cast(arg)->Run(); + return nullptr; + }, + this); + state_ = thread_ ? State::Started : State::Failed; + } + if (state_ == State::Started) { + semaphore_.Post(); + return; + } + } + CompressStackStore(); +} + +void CompressThread::Run() { + VPrintf(1, "%s: StackDepot compression thread started\n", SanitizerToolName); + while (WaitForWork()) CompressStackStore(); + VPrintf(1, "%s: StackDepot compression thread stopped\n", SanitizerToolName); +} + +void CompressThread::Stop() { + void *t = nullptr; + { + SpinMutexLock l(&mutex_); + if (state_ != State::Started) + return; + state_ = State::Stopped; + CHECK_NE(nullptr, thread_); + t = thread_; + thread_ = nullptr; + } + atomic_store(&run_, 0, memory_order_release); + semaphore_.Post(); + internal_join_thread(t); +} + +void CompressThread::LockAndStop() { + mutex_.Lock(); + if (state_ != State::Started) + return; + CHECK_NE(nullptr, thread_); + + atomic_store(&run_, 0, memory_order_release); + semaphore_.Post(); + internal_join_thread(thread_); + // Allow to restart after Unlock() if needed. + state_ = State::NotStarted; + thread_ = nullptr; +} + +void CompressThread::Unlock() { mutex_.Unlock(); } + +} // namespace + void StackDepotNode::store(u32 id, const args_type &args, hash_type hash) { stack_hash = hash; uptr pack = 0; store_id = stackStore.Store(args, &pack); - if (pack && common_flags()->compress_stack_depot) - CompressStackStore(); + if (LIKELY(!pack)) + return; + compress_thread.NewWorkNotify(); } StackDepotNode::args_type StackDepotNode::load(u32 id) const { @@ -113,11 +215,13 @@ StackTrace StackDepotGet(u32 id) { void StackDepotLockAll() { theDepot.LockAll(); + compress_thread.LockAndStop(); stackStore.LockAll(); } void StackDepotUnlockAll() { stackStore.UnlockAll(); + compress_thread.Unlock(); theDepot.UnlockAll(); } @@ -127,6 +231,8 @@ void StackDepotPrintAll() { #endif } +void StackDepotStopBackgroundThread() { compress_thread.Stop(); } + StackDepotHandle StackDepotNode::get_handle(u32 id) { return StackDepotHandle(&theDepot.nodes[id], id); } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h index 56d655d9404c..cca6fd534688 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h @@ -42,6 +42,7 @@ StackTrace StackDepotGet(u32 id); void StackDepotLockAll(); void StackDepotUnlockAll(); void StackDepotPrintAll(); +void StackDepotStopBackgroundThread(); void StackDepotTestOnlyUnmap(); diff --git a/compiler-rt/test/sanitizer_common/TestCases/compress_stack_depot.cpp b/compiler-rt/test/sanitizer_common/TestCases/compress_stack_depot.cpp index c64c9392a107..6bf067618f95 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/compress_stack_depot.cpp +++ b/compiler-rt/test/sanitizer_common/TestCases/compress_stack_depot.cpp @@ -1,7 +1,9 @@ // RUN: %clangxx %s -fsanitize-memory-track-origins=1 -o %t // RUN: %env_tool_opts="compress_stack_depot=0:malloc_context_size=128:verbosity=1" %run %t 2>&1 | FileCheck %s --implicit-check-not="StackDepot released" -// RUN: %env_tool_opts="compress_stack_depot=1:malloc_context_size=128:verbosity=1" %run %t 2>&1 | FileCheck %s --check-prefixes=COMPRESS -// RUN: %env_tool_opts="compress_stack_depot=2:malloc_context_size=128:verbosity=1" %run %t 2>&1 | FileCheck %s --check-prefixes=COMPRESS +// RUN: %env_tool_opts="compress_stack_depot=-1:malloc_context_size=128:verbosity=1" %run %t 2>&1 | FileCheck %s --check-prefixes=COMPRESS +// RUN: %env_tool_opts="compress_stack_depot=-2:malloc_context_size=128:verbosity=1" %run %t 2>&1 | FileCheck %s --check-prefixes=COMPRESS +// RUN: %env_tool_opts="compress_stack_depot=1:malloc_context_size=128:verbosity=1" %run %t 2>&1 | FileCheck %s --check-prefixes=COMPRESS,THREAD +// RUN: %env_tool_opts="compress_stack_depot=2:malloc_context_size=128:verbosity=1" %run %t 2>&1 | FileCheck %s --check-prefixes=COMPRESS,THREAD // Ubsan does not store stacks. // UNSUPPORTED: ubsan @@ -9,6 +11,8 @@ // FIXME: Fails for unknown reason. // UNSUPPORTED: s390x +#include + #include __attribute__((noinline)) void a(unsigned v); @@ -35,7 +39,13 @@ __attribute__((noinline)) void b(unsigned v) { return a(v); } int main(int argc, char *argv[]) { for (unsigned i = 0; i < 100000; ++i) a(i + (i << 16)); + + __sanitizer_sandbox_arguments args = {0}; + __sanitizer_sandbox_on_notify(&args); + return 0; } +// THREAD: StackDepot compression thread started // COMPRESS: StackDepot released {{[0-9]+}} +// THREAD: StackDepot compression thread stopped