| 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_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(initial_capacity: 0); |
| 30 | TrackedLoads.Initialize(initial_capacity: 0); |
| 31 | } |
| 32 | |
| 33 | Stats::~Stats() { Printf(format: "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, context: nullptr, request_fast: false); |
| 45 | u32 stack_id = StackDepotPut(stack: Stack); |
| 46 | typename MapT::Handle Handle(map, Key(CheckType: check_ty, StackId: 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, map: &CheckAndWarningsMap, vector: &check_and_warnings, |
| 62 | mutex: &check_and_warning_mutex, |
| 63 | F: [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, map: &CheckAndWarningsMap, vector: &check_and_warnings, |
| 73 | mutex: &check_and_warning_mutex, |
| 74 | F: [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(check_ty: CheckTypeT::kLoad, pc, bp, map: &LoadTrackingMap, vector: &TrackedLoads, |
| 84 | mutex: &TrackedLoadsMutex, |
| 85 | F: [](LoadTrackingValue &entry) { ++entry.num_invalid; }); |
| 86 | } |
| 87 | |
| 88 | void Stats::AddUnknownLoadTrackingEvent(uptr pc, uptr bp) { |
| 89 | UpdateEntry(check_ty: CheckTypeT::kLoad, pc, bp, map: &LoadTrackingMap, vector: &TrackedLoads, |
| 90 | mutex: &TrackedLoadsMutex, |
| 91 | F: [](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(format: "warned %llu times out of %llu %s checks " , entry.num_warnings, |
| 124 | entry.num_checks, CheckTypeDisplay(CheckType: entry.check_ty)); |
| 125 | if (entry.num_warnings > 0) { |
| 126 | char RelErrBuf[64]; |
| 127 | snprintf(s: RelErrBuf, maxlen: sizeof(RelErrBuf) - 1, format: "%f" , |
| 128 | entry.max_relative_err * 100.0); |
| 129 | Printf(format: "(max relative error: %s%%) " , RelErrBuf); |
| 130 | } |
| 131 | Printf(format: "at:\n" ); |
| 132 | StackDepotGet(id: 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(format: "invalid/unknown type for %llu/%llu loads at:\n" , |
| 144 | entry.num_invalid, entry.num_unknown); |
| 145 | StackDepotGet(id: entry.stack_id).Print(); |
| 146 | } |
| 147 | Printf( |
| 148 | format: "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 | |