| //===-- nsan_stats.cc -----------------------------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file is a part of NumericalStabilitySanitizer. |
| // |
| // NumericalStabilitySanitizer statistics. |
| //===----------------------------------------------------------------------===// |
| |
| #include "nsan_stats.h" |
| |
| #include "sanitizer_common/sanitizer_common.h" |
| #include "sanitizer_common/sanitizer_placement_new.h" |
| #include "sanitizer_common/sanitizer_stackdepot.h" |
| #include "sanitizer_common/sanitizer_stacktrace.h" |
| #include "sanitizer_common/sanitizer_symbolizer.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| |
| using namespace __sanitizer; |
| using namespace __nsan; |
| |
| Stats::Stats() { |
| check_and_warnings.Initialize(0); |
| TrackedLoads.Initialize(0); |
| } |
| |
| Stats::~Stats() { Printf("deleting nsan stats\n"); } |
| |
| static uptr Key(CheckTypeT CheckType, u32 StackId) { |
| return static_cast<uptr>(CheckType) + |
| StackId * static_cast<uptr>(CheckTypeT::kMaxCheckType); |
| } |
| |
| template <typename MapT, typename VectorT, typename Fn> |
| static void UpdateEntry(CheckTypeT check_ty, uptr pc, uptr bp, MapT *map, |
| VectorT *vector, Mutex *mutex, Fn F) { |
| BufferedStackTrace Stack; |
| Stack.Unwind(pc, bp, nullptr, false); |
| u32 stack_id = StackDepotPut(Stack); |
| typename MapT::Handle Handle(map, Key(check_ty, stack_id)); |
| Lock L(mutex); |
| if (Handle.created()) { |
| typename VectorT::value_type entry; |
| entry.stack_id = stack_id; |
| entry.check_ty = check_ty; |
| F(entry); |
| vector->push_back(entry); |
| } else { |
| auto &entry = (*vector)[*Handle]; |
| F(entry); |
| } |
| } |
| |
| void Stats::AddCheck(CheckTypeT check_ty, uptr pc, uptr bp, double rel_err) { |
| UpdateEntry(check_ty, pc, bp, &CheckAndWarningsMap, &check_and_warnings, |
| &check_and_warning_mutex, |
| [rel_err](CheckAndWarningsValue &entry) { |
| ++entry.num_checks; |
| if (rel_err > entry.max_relative_err) { |
| entry.max_relative_err = rel_err; |
| } |
| }); |
| } |
| |
| void Stats::AddWarning(CheckTypeT check_ty, uptr pc, uptr bp, double rel_err) { |
| UpdateEntry(check_ty, pc, bp, &CheckAndWarningsMap, &check_and_warnings, |
| &check_and_warning_mutex, |
| [rel_err](CheckAndWarningsValue &entry) { |
| ++entry.num_warnings; |
| if (rel_err > entry.max_relative_err) { |
| entry.max_relative_err = rel_err; |
| } |
| }); |
| } |
| |
| void Stats::AddInvalidLoadTrackingEvent(uptr pc, uptr bp) { |
| UpdateEntry(CheckTypeT::kLoad, pc, bp, &LoadTrackingMap, &TrackedLoads, |
| &TrackedLoadsMutex, |
| [](LoadTrackingValue &entry) { ++entry.num_invalid; }); |
| } |
| |
| void Stats::AddUnknownLoadTrackingEvent(uptr pc, uptr bp) { |
| UpdateEntry(CheckTypeT::kLoad, pc, bp, &LoadTrackingMap, &TrackedLoads, |
| &TrackedLoadsMutex, |
| [](LoadTrackingValue &entry) { ++entry.num_unknown; }); |
| } |
| |
| static const char *CheckTypeDisplay(CheckTypeT CheckType) { |
| switch (CheckType) { |
| case CheckTypeT::kUnknown: |
| return "unknown"; |
| case CheckTypeT::kRet: |
| return "return"; |
| case CheckTypeT::kArg: |
| return "argument"; |
| case CheckTypeT::kLoad: |
| return "load"; |
| case CheckTypeT::kStore: |
| return "store"; |
| case CheckTypeT::kInsert: |
| return "vector insert"; |
| case CheckTypeT::kUser: |
| return "user-initiated"; |
| case CheckTypeT::kFcmp: |
| return "fcmp"; |
| case CheckTypeT::kMaxCheckType: |
| return "[max]"; |
| } |
| assert(false && "unknown CheckType case"); |
| return ""; |
| } |
| |
| void Stats::Print() const { |
| { |
| Lock L(&check_and_warning_mutex); |
| for (const auto &entry : check_and_warnings) { |
| Printf("warned %llu times out of %llu %s checks ", entry.num_warnings, |
| entry.num_checks, CheckTypeDisplay(entry.check_ty)); |
| if (entry.num_warnings > 0) { |
| char RelErrBuf[64]; |
| snprintf(RelErrBuf, sizeof(RelErrBuf) - 1, "%f", |
| entry.max_relative_err * 100.0); |
| Printf("(max relative error: %s%%) ", RelErrBuf); |
| } |
| Printf("at:\n"); |
| StackDepotGet(entry.stack_id).Print(); |
| } |
| } |
| |
| { |
| Lock L(&TrackedLoadsMutex); |
| u64 TotalInvalidLoadTracking = 0; |
| u64 TotalUnknownLoadTracking = 0; |
| for (const auto &entry : TrackedLoads) { |
| TotalInvalidLoadTracking += entry.num_invalid; |
| TotalUnknownLoadTracking += entry.num_unknown; |
| Printf("invalid/unknown type for %llu/%llu loads at:\n", |
| entry.num_invalid, entry.num_unknown); |
| StackDepotGet(entry.stack_id).Print(); |
| } |
| Printf( |
| "There were %llu/%llu floating-point loads where the shadow type was " |
| "invalid/unknown.\n", |
| TotalInvalidLoadTracking, TotalUnknownLoadTracking); |
| } |
| } |
| |
| alignas(64) static char stats_placeholder[sizeof(Stats)]; |
| Stats *__nsan::nsan_stats = nullptr; |
| |
| void __nsan::InitializeStats() { nsan_stats = new (stats_placeholder) Stats(); } |