Chris Apple 595e484c08
[rtsan] Add option to allow printing of duplicate stacks (suppress_equal_stacks) (#117069)
Following the example of tsan, where we took the name

This would allow users to determine if they want to see ALL output from
rtsan.

Additionally, remove the UNLIKELY hint, as it is now up to the flag whether or
not it is likely that we go through this conditional.
2024-11-21 06:32:05 -08:00

152 lines
4.3 KiB
C++

//===--- rtsan.cpp - Realtime Sanitizer -------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#include "rtsan/rtsan.h"
#include "rtsan/rtsan_assertions.h"
#include "rtsan/rtsan_diagnostics.h"
#include "rtsan/rtsan_flags.h"
#include "rtsan/rtsan_interceptors.h"
#include "rtsan/rtsan_stats.h"
#include "rtsan/rtsan_suppressions.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
using namespace __rtsan;
using namespace __sanitizer;
namespace {
enum class InitializationState : u8 {
Uninitialized,
Initializing,
Initialized,
};
} // namespace
static StaticSpinMutex rtsan_inited_mutex;
static atomic_uint8_t rtsan_initialized = {
static_cast<u8>(InitializationState::Uninitialized)};
static void SetInitializationState(InitializationState state) {
atomic_store(&rtsan_initialized, static_cast<u8>(state),
memory_order_release);
}
static InitializationState GetInitializationState() {
return static_cast<InitializationState>(
atomic_load(&rtsan_initialized, memory_order_acquire));
}
static void OnViolation(const BufferedStackTrace &stack,
const DiagnosticsInfo &info) {
IncrementTotalErrorCount();
// If in the future we interop with other sanitizers, we will
// need to make our own stackdepot
StackDepotHandle handle = StackDepotPut_WithHandle(stack);
const bool is_stack_novel = handle.use_count() == 0;
if (is_stack_novel || !flags().suppress_equal_stacks) {
IncrementUniqueErrorCount();
{
ScopedErrorReportLock l;
PrintDiagnostics(info);
stack.Print();
PrintErrorSummary(info, stack);
}
handle.inc_use_count_unsafe();
}
if (flags().halt_on_error) {
if (flags().print_stats_on_exit)
PrintStatisticsSummary();
Die();
}
}
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() {
CHECK(GetInitializationState() == InitializationState::Uninitialized);
SetInitializationState(InitializationState::Initializing);
SanitizerToolName = "RealtimeSanitizer";
InitializeFlags();
InitializeInterceptors();
InitializeSuppressions();
if (flags().print_stats_on_exit)
Atexit(PrintStatisticsSummary);
SetInitializationState(InitializationState::Initialized);
}
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_ensure_initialized() {
if (LIKELY(__rtsan_is_initialized()))
return;
SpinMutexLock lock(&rtsan_inited_mutex);
// Someone may have initialized us while we were waiting for the lock
if (__rtsan_is_initialized())
return;
__rtsan_init();
}
SANITIZER_INTERFACE_ATTRIBUTE bool __rtsan_is_initialized() {
return GetInitializationState() == InitializationState::Initialized;
}
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter() {
GetContextForThisThread().RealtimePush();
}
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit() {
GetContextForThisThread().RealtimePop();
}
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_disable() {
GetContextForThisThread().BypassPush();
}
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_enable() {
GetContextForThisThread().BypassPop();
}
SANITIZER_INTERFACE_ATTRIBUTE void
__rtsan_notify_intercepted_call(const char *func_name) {
// While initializing, we need all intercepted functions to behave normally
if (GetInitializationState() == InitializationState::Initializing)
return;
__rtsan_ensure_initialized();
GET_CALLER_PC_BP;
ExpectNotRealtime(GetContextForThisThread(),
{DiagnosticsInfoType::InterceptedCall, func_name, pc, bp},
OnViolation);
}
SANITIZER_INTERFACE_ATTRIBUTE void
__rtsan_notify_blocking_call(const char *func_name) {
__rtsan_ensure_initialized();
GET_CALLER_PC_BP;
ExpectNotRealtime(GetContextForThisThread(),
{DiagnosticsInfoType::BlockingCall, func_name, pc, bp},
OnViolation);
}
} // extern "C"