1 //===-- nsan_stats.cc -----------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file is a part of NumericalStabilitySanitizer. 10 // 11 // NumericalStabilitySanitizer statistics. 12 //===----------------------------------------------------------------------===// 13 14 #include "nsan/nsan_stats.h" 15 16 #include "sanitizer_common/sanitizer_common.h" 17 #include "sanitizer_common/sanitizer_placement_new.h" 18 #include "sanitizer_common/sanitizer_stackdepot.h" 19 #include "sanitizer_common/sanitizer_stacktrace.h" 20 #include "sanitizer_common/sanitizer_symbolizer.h" 21 22 #include <assert.h> 23 #include <stdio.h> 24 25 using namespace __sanitizer; 26 using namespace __nsan; 27 28 Stats::Stats() { 29 check_and_warnings.Initialize(0); 30 TrackedLoads.Initialize(0); 31 } 32 33 Stats::~Stats() { Printf("deleting nsan stats\n"); } 34 35 static uptr Key(CheckTypeT CheckType, u32 StackId) { 36 return static_cast<uptr>(CheckType) + 37 StackId * static_cast<uptr>(CheckTypeT::kMaxCheckType); 38 } 39 40 template <typename MapT, typename VectorT, typename Fn> 41 static void UpdateEntry(CheckTypeT check_ty, uptr pc, uptr bp, MapT *map, 42 VectorT *vector, Mutex *mutex, Fn F) { 43 BufferedStackTrace Stack; 44 Stack.Unwind(pc, bp, nullptr, false); 45 u32 stack_id = StackDepotPut(Stack); 46 typename MapT::Handle Handle(map, Key(check_ty, stack_id)); 47 Lock L(mutex); 48 if (Handle.created()) { 49 typename VectorT::value_type entry; 50 entry.stack_id = stack_id; 51 entry.check_ty = check_ty; 52 F(entry); 53 vector->push_back(entry); 54 } else { 55 auto &entry = (*vector)[*Handle]; 56 F(entry); 57 } 58 } 59 60 void Stats::AddCheck(CheckTypeT check_ty, uptr pc, uptr bp, double rel_err) { 61 UpdateEntry(check_ty, pc, bp, &CheckAndWarningsMap, &check_and_warnings, 62 &check_and_warning_mutex, 63 [rel_err](CheckAndWarningsValue &entry) { 64 ++entry.num_checks; 65 if (rel_err > entry.max_relative_err) { 66 entry.max_relative_err = rel_err; 67 } 68 }); 69 } 70 71 void Stats::AddWarning(CheckTypeT check_ty, uptr pc, uptr bp, double rel_err) { 72 UpdateEntry(check_ty, pc, bp, &CheckAndWarningsMap, &check_and_warnings, 73 &check_and_warning_mutex, 74 [rel_err](CheckAndWarningsValue &entry) { 75 ++entry.num_warnings; 76 if (rel_err > entry.max_relative_err) { 77 entry.max_relative_err = rel_err; 78 } 79 }); 80 } 81 82 void Stats::AddInvalidLoadTrackingEvent(uptr pc, uptr bp) { 83 UpdateEntry(CheckTypeT::kLoad, pc, bp, &LoadTrackingMap, &TrackedLoads, 84 &TrackedLoadsMutex, 85 [](LoadTrackingValue &entry) { ++entry.num_invalid; }); 86 } 87 88 void Stats::AddUnknownLoadTrackingEvent(uptr pc, uptr bp) { 89 UpdateEntry(CheckTypeT::kLoad, pc, bp, &LoadTrackingMap, &TrackedLoads, 90 &TrackedLoadsMutex, 91 [](LoadTrackingValue &entry) { ++entry.num_unknown; }); 92 } 93 94 static const char *CheckTypeDisplay(CheckTypeT CheckType) { 95 switch (CheckType) { 96 case CheckTypeT::kUnknown: 97 return "unknown"; 98 case CheckTypeT::kRet: 99 return "return"; 100 case CheckTypeT::kArg: 101 return "argument"; 102 case CheckTypeT::kLoad: 103 return "load"; 104 case CheckTypeT::kStore: 105 return "store"; 106 case CheckTypeT::kInsert: 107 return "vector insert"; 108 case CheckTypeT::kUser: 109 return "user-initiated"; 110 case CheckTypeT::kFcmp: 111 return "fcmp"; 112 case CheckTypeT::kMaxCheckType: 113 return "[max]"; 114 } 115 assert(false && "unknown CheckType case"); 116 return ""; 117 } 118 119 void Stats::Print() const { 120 { 121 Lock L(&check_and_warning_mutex); 122 for (const auto &entry : check_and_warnings) { 123 Printf("warned %llu times out of %llu %s checks ", entry.num_warnings, 124 entry.num_checks, CheckTypeDisplay(entry.check_ty)); 125 if (entry.num_warnings > 0) { 126 char RelErrBuf[64]; 127 snprintf(RelErrBuf, sizeof(RelErrBuf) - 1, "%f", 128 entry.max_relative_err * 100.0); 129 Printf("(max relative error: %s%%) ", RelErrBuf); 130 } 131 Printf("at:\n"); 132 StackDepotGet(entry.stack_id).Print(); 133 } 134 } 135 136 { 137 Lock L(&TrackedLoadsMutex); 138 u64 TotalInvalidLoadTracking = 0; 139 u64 TotalUnknownLoadTracking = 0; 140 for (const auto &entry : TrackedLoads) { 141 TotalInvalidLoadTracking += entry.num_invalid; 142 TotalUnknownLoadTracking += entry.num_unknown; 143 Printf("invalid/unknown type for %llu/%llu loads at:\n", 144 entry.num_invalid, entry.num_unknown); 145 StackDepotGet(entry.stack_id).Print(); 146 } 147 Printf( 148 "There were %llu/%llu floating-point loads where the shadow type was " 149 "invalid/unknown.\n", 150 TotalInvalidLoadTracking, TotalUnknownLoadTracking); 151 } 152 } 153 154 alignas(64) static char stats_placeholder[sizeof(Stats)]; 155 Stats *__nsan::nsan_stats = nullptr; 156 157 void __nsan::InitializeStats() { nsan_stats = new (stats_placeholder) Stats(); } 158