Branch data Line data Source code
1 : : // Copyright (c) 2026-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <sync.h>
6 : : #include <test/fuzz/util/reachability.h>
7 : : #include <util/check.h>
8 : :
9 : : #include <compare>
10 : : #include <cstdint>
11 : : #include <cstdio>
12 : : #include <cstdlib>
13 : : #include <iostream>
14 : : #include <map>
15 : : #include <string>
16 : : #include <string_view>
17 : :
18 : : namespace {
19 : : struct GoalId {
20 : : std::string_view file_name;
21 : 96660 : std::uint_least32_t line;
22 : 84728 : std::uint_least32_t column;
23 : :
24 : 96660 : auto operator<=>(const GoalId&) const = default;
[ - + + +
+ + + + -
+ - - -
+ ]
25 : : };
26 : :
27 : 17 : struct GoalState {
28 : : std::string message;
29 : : bool reached{false};
30 : :
31 : 17 : explicit GoalState(std::string_view message) : message{message} {}
32 : : };
33 : :
34 : : class ReachabilityTracker
35 : : {
36 : : public:
37 : 238 : ReachabilityTracker()
38 : 238 : {
39 : 238 : const char* enforce{std::getenv("FUZZ_ENFORCE_REACHABILITY")};
40 [ - + - - ]: 238 : if (!enforce || std::string_view{enforce} == "0") return;
41 [ # # ]: 0 : if (std::string_view{enforce} == "1") {
42 : 0 : m_enforce = true;
43 : 0 : return;
44 : : }
45 [ # # ]: 0 : std::fputs("FUZZ_ENFORCE_REACHABILITY must be 0 or 1.\n", stderr);
46 : 0 : std::abort();
47 : 0 : }
48 : :
49 : 238 : ~ReachabilityTracker()
50 : : {
51 : 238 : LOCK(m_mutex);
52 : 238 : bool all_reached{true};
53 [ - + + + ]: 255 : for (const auto& [id, goal] : m_goals) {
54 : 17 : std::cerr << (goal.reached ? "PASS" : "FAIL") << " reachability goal \""
55 [ - + - + ]: 34 : << goal.message << "\" at " << id.file_name << ':' << id.line << ':' << id.column << '\n';
56 : 17 : all_reached &= goal.reached;
57 : : }
58 [ - + - - ]: 238 : if (m_enforce && !all_reached) {
59 : 0 : std::cerr.flush();
60 : 0 : std::_Exit(EXIT_FAILURE);
61 : : }
62 : 238 : }
63 : :
64 : 17 : void Register(const ReachabilityGoal& goal) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
65 : : {
66 : 17 : const GoalId id{
67 : : .file_name = goal.location.file_name(),
68 [ + - ]: 17 : .line = goal.location.line(),
69 : 34 : .column = goal.location.column(),
70 [ + - + - : 51 : };
+ - ]
71 : 17 : LOCK(m_mutex);
72 [ + - ]: 17 : const bool inserted{m_goals.try_emplace(id, goal.message).second};
73 [ + - ]: 17 : Assert(inserted);
74 : 17 : }
75 : :
76 : 42364 : void Observe(bool reached, const ReachabilityGoal& goal) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
77 : : {
78 : 42364 : const GoalId id{
79 : : .file_name = goal.location.file_name(),
80 [ + - ]: 42364 : .line = goal.location.line(),
81 : 84728 : .column = goal.location.column(),
82 [ + - + - : 127092 : };
+ - ]
83 : 42364 : LOCK(m_mutex);
84 : 42364 : const auto it{m_goals.find(id)};
85 [ + - ]: 42364 : Assert(it != m_goals.end());
86 [ + - ]: 42364 : it->second.reached |= reached;
87 : 42364 : }
88 : :
89 : : private:
90 : : Mutex m_mutex;
91 : : std::map<GoalId, GoalState> m_goals GUARDED_BY(m_mutex);
92 : : bool m_enforce{false};
93 : : };
94 : :
95 : : ReachabilityTracker g_reachability_tracker;
96 : : } // namespace
97 : :
98 : 17 : void RegisterReachabilityGoal(const ReachabilityGoal& goal)
99 : : {
100 : 17 : g_reachability_tracker.Register(goal);
101 : 17 : }
102 : :
103 : 42364 : void ObserveReachabilityGoal(bool reached, const ReachabilityGoal& goal)
104 : : {
105 : 42364 : g_reachability_tracker.Observe(reached, goal);
106 : 42364 : }
|