diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp index 87c3611935ee5..6fcff5e326a52 100644 --- a/compiler-rt/lib/rtsan/rtsan.cpp +++ b/compiler-rt/lib/rtsan/rtsan.cpp @@ -18,6 +18,7 @@ #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" using namespace __rtsan; @@ -49,7 +50,30 @@ static auto OnViolationAction(DiagnosticsInfo info) { return [info]() { IncrementTotalErrorCount(); - PrintDiagnostics(info); + BufferedStackTrace stack; + + // We use the unwind_on_fatal flag here because of precedent with other + // sanitizers, this action is not necessarily fatal if halt_on_error=false + stack.Unwind(info.pc, info.bp, nullptr, + common_flags()->fast_unwind_on_fatal); + + // 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; + + // Marked UNLIKELY as if user is runing with halt_on_error=false + // we expect a high number of duplicate stacks. We are willing + // To pay for the first insertion. + if (UNLIKELY(is_stack_novel)) { + IncrementUniqueErrorCount(); + + PrintDiagnostics(info); + stack.Print(); + + handle.inc_use_count_unsafe(); + } if (flags().halt_on_error) Die(); diff --git a/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp b/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp index f82001f5b2057..cfe71481d3dc7 100644 --- a/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp +++ b/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp @@ -39,13 +39,6 @@ class Decorator : public __sanitizer::SanitizerCommonDecorator { }; } // namespace -static void PrintStackTrace(uptr pc, uptr bp) { - BufferedStackTrace stack{}; - - stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal); - stack.Print(); -} - static void PrintError(const Decorator &decorator, const DiagnosticsInfo &info) { const auto ErrorTypeStr = [&info]() -> const char * { @@ -91,5 +84,4 @@ void __rtsan::PrintDiagnostics(const DiagnosticsInfo &info) { PrintError(d, info); PrintReason(d, info); Printf("%s", d.Default()); - PrintStackTrace(info.pc, info.bp); } diff --git a/compiler-rt/lib/rtsan/rtsan_stats.cpp b/compiler-rt/lib/rtsan/rtsan_stats.cpp index 7c1ccf2876f08..dac7b23c3ef52 100644 --- a/compiler-rt/lib/rtsan/rtsan_stats.cpp +++ b/compiler-rt/lib/rtsan/rtsan_stats.cpp @@ -19,17 +19,27 @@ using namespace __sanitizer; using namespace __rtsan; static atomic_uint32_t rtsan_total_error_count{0}; +static atomic_uint32_t rtsan_unique_error_count{0}; void __rtsan::IncrementTotalErrorCount() { atomic_fetch_add(&rtsan_total_error_count, 1, memory_order_relaxed); } +void __rtsan::IncrementUniqueErrorCount() { + atomic_fetch_add(&rtsan_unique_error_count, 1, memory_order_relaxed); +} + static u32 GetTotalErrorCount() { return atomic_load(&rtsan_total_error_count, memory_order_relaxed); } +static u32 GetUniqueErrorCount() { + return atomic_load(&rtsan_unique_error_count, memory_order_relaxed); +} + void __rtsan::PrintStatisticsSummary() { ScopedErrorReportLock l; Printf("RealtimeSanitizer exit stats:\n"); Printf(" Total error count: %u\n", GetTotalErrorCount()); + Printf(" Unique error count: %u\n", GetUniqueErrorCount()); } diff --git a/compiler-rt/lib/rtsan/rtsan_stats.h b/compiler-rt/lib/rtsan/rtsan_stats.h index 3aa30f6a5db76..a72098792c89c 100644 --- a/compiler-rt/lib/rtsan/rtsan_stats.h +++ b/compiler-rt/lib/rtsan/rtsan_stats.h @@ -15,6 +15,7 @@ namespace __rtsan { void IncrementTotalErrorCount(); +void IncrementUniqueErrorCount(); void PrintStatisticsSummary(); diff --git a/compiler-rt/test/rtsan/deduplicate_errors.cpp b/compiler-rt/test/rtsan/deduplicate_errors.cpp new file mode 100644 index 0000000000000..7d60d4d7da7dd --- /dev/null +++ b/compiler-rt/test/rtsan/deduplicate_errors.cpp @@ -0,0 +1,39 @@ +// RUN: %clangxx -fsanitize=realtime %s -o %t +// RUN: env RTSAN_OPTIONS="halt_on_error=false,print_stats_on_exit=true" %run %t 2>&1 | FileCheck %s + +// UNSUPPORTED: ios + +// Intent: Ensure all errors are deduplicated. + +#include + +const int kNumViolations = 10; + +void violation() [[clang::nonblocking]] { + for (int i = 0; i < kNumViolations; i++) + usleep(1); +} + +void violation2() [[clang::nonblocking]] { + for (int i = 0; i < kNumViolations; i++) + violation(); +} + +void double_violation() [[clang::nonblocking]] { + violation(); + violation2(); +} + +int main() { + violation(); // 1 unique errors here, but 10 total + violation2(); // 1 unique errors here, but 100 total + double_violation(); // 2 unique errors here, but 110 total + return 0; +} + +// CHECK-COUNT-4: ==ERROR: +// CHECK-NOT: ==ERROR: + +// CHECK: RealtimeSanitizer exit stats: +// CHECK-NEXT: Total error count: 220 +// CHECK-NEXT: Unique error count: 4 diff --git a/compiler-rt/test/rtsan/exit_stats.cpp b/compiler-rt/test/rtsan/exit_stats.cpp index b46a0fd62bac1..4341fbb0f9cf2 100644 --- a/compiler-rt/test/rtsan/exit_stats.cpp +++ b/compiler-rt/test/rtsan/exit_stats.cpp @@ -21,3 +21,4 @@ int main() { // CHECK: RealtimeSanitizer exit stats: // CHECK-NEXT: Total error count: 10 +// CHECK-NEXT: Unique error count: 1