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(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 | |