diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp @@ -12,6 +12,7 @@ #include "sanitizer_stackdepot.h" +#include "sanitizer_atomic.h" #include "sanitizer_common.h" #include "sanitizer_hash.h" #include "sanitizer_stack_store.h" @@ -72,25 +73,81 @@ return stackStore.Allocated() + useCounts.MemoryUsage(); } -static void CompressStackStore() { +// Control how long to wait before spawning a new thread. It can be lost on +// fork. +static constexpr u64 kMaxThreadNanoTimeout = 60ull * 1000 * 1000 * 1000; +static atomic_uint64_t thread_heartbeat; + +static bool CompressStackStore() { u64 start = MonotonicNanoTime(); uptr diff = stackStore.Pack(static_cast( - common_flags()->compress_stack_depot)); - if (!diff) - return; + Abs(common_flags()->compress_stack_depot))); u64 finish = MonotonicNanoTime(); + if (!diff) + return false; uptr total_before = stackStore.Allocated() + diff; VPrintf(1, "%s: StackDepot released %zu KiB out of %zu KiB in %llu ms\n", SanitizerToolName, diff >> 10, total_before >> 10, (finish - start) / 1000000); + return true; +} + +static void *CompressTracesThread(void *args) { + u64 hb = MonotonicNanoTime() + kMaxThreadNanoTimeout; + atomic_store(&thread_heartbeat, hb, memory_order_release); + for (int i = 0;;) { + if (CompressStackStore()) + i = 0; + if (++i > 4) + break; + u64 next_hb = MonotonicNanoTime() + kMaxThreadNanoTimeout; + if (!atomic_compare_exchange_strong(&thread_heartbeat, &hb, next_hb, + memory_order_release)) { + // Another thread? + return nullptr; + } + hb = next_hb; + SleepForSeconds(i * 10); + } + // Allow to spawn a new thread. + if (!atomic_compare_exchange_strong(&thread_heartbeat, &hb, 0, + memory_order_release)) { + // Another thread? + return nullptr; + } + // Do the work which was added before thread_heartbeat reset. + CompressStackStore(); + return nullptr; +} + +static void MaybeStartCompressThread() { + u64 heartbeat = atomic_load_relaxed(&thread_heartbeat); + u64 now = MonotonicNanoTime(); + if (heartbeat && now < heartbeat) + return; + + if (atomic_compare_exchange_strong(&thread_heartbeat, &heartbeat, + now + kMaxThreadNanoTimeout, + memory_order_release)) { + VPrintf(1, "%s: StackDepot starts compression thread\n", SanitizerToolName); + internal_start_thread(&CompressTracesThread, nullptr); + } } 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) + if (LIKELY(!pack)) + return; + int compress = common_flags()->compress_stack_depot; + if (!compress) + return; + if (compress < 0 /* for testing */) { CompressStackStore(); + return; + } + MaybeStartCompressThread(); } StackDepotNode::args_type StackDepotNode::load(u32 id) const { diff --git a/compiler-rt/test/sanitizer_common/TestCases/compress_stack_depot.cpp b/compiler-rt/test/sanitizer_common/TestCases/compress_stack_depot.cpp --- 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,7 @@ // 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 // Ubsan does not store stacks. // UNSUPPORTED: ubsan