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
25using namespace __sanitizer;
26using namespace __nsan;
27
28Stats::Stats() {
29 check_and_warnings.Initialize(initial_capacity: 0);
30 TrackedLoads.Initialize(initial_capacity: 0);
31}
32
33Stats::~Stats() { Printf(format: "deleting nsan stats\n"); }
34
35static uptr Key(CheckTypeT CheckType, u32 StackId) {
36 return static_cast<uptr>(CheckType) +
37 StackId * static_cast<uptr>(CheckTypeT::kMaxCheckType);
38}
39
40template <typename MapT, typename VectorT, typename Fn>
41static 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
60void 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
71void 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
82void 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
88void 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
94static 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
119void 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
154alignas(64) static char stats_placeholder[sizeof(Stats)];
155Stats *__nsan::nsan_stats = nullptr;
156
157void __nsan::InitializeStats() { nsan_stats = new (stats_placeholder) Stats(); }
158