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
Stats()28 Stats::Stats() {
29 check_and_warnings.Initialize(0);
30 TrackedLoads.Initialize(0);
31 }
32
~Stats()33 Stats::~Stats() { Printf("deleting nsan stats\n"); }
34
Key(CheckTypeT CheckType,u32 StackId)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>
UpdateEntry(CheckTypeT check_ty,uptr pc,uptr bp,MapT * map,VectorT * vector,Mutex * mutex,Fn F)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
AddCheck(CheckTypeT check_ty,uptr pc,uptr bp,double rel_err)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
AddWarning(CheckTypeT check_ty,uptr pc,uptr bp,double rel_err)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
AddInvalidLoadTrackingEvent(uptr pc,uptr bp)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
AddUnknownLoadTrackingEvent(uptr pc,uptr bp)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
CheckTypeDisplay(CheckTypeT CheckType)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
Print() const119 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
InitializeStats()157 void __nsan::InitializeStats() { nsan_stats = new (stats_placeholder) Stats(); }
158