]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/fault_injector.h
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / common / fault_injector.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2020 Red Hat, Inc.
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #pragma once
16
17 #include <type_traits>
18 #include <boost/type_traits/has_equal_to.hpp>
19 #include <boost/type_traits/has_left_shift.hpp>
20 #include <variant>
21 #include "include/ceph_assert.h"
22 #include "common/dout.h"
23
24 /// @file
25
26 /// A failure type that aborts the process with a failed assertion.
27 struct InjectAbort {};
28
29 /// A failure type that injects an error code and optionally logs a message.
30 struct InjectError {
31 /// error code to inject
32 int error;
33 /// an optional log channel to print an error message
34 const DoutPrefixProvider* dpp = nullptr;
35 };
36
37 /** @class FaultInjector
38 * @brief Used to instrument a code path with deterministic fault injection
39 * by making one or more calls to check().
40 *
41 * A default-constructed FaultInjector contains no failure. It can also be
42 * constructed with a failure of type InjectAbort or InjectError, along with
43 * a location to inject that failure.
44 *
45 * The contained failure can be overwritten with a call to inject() or clear().
46 * This is not thread-safe with respect to other member functions on the same
47 * instance.
48 *
49 * @tparam Key The location can be represented by any Key type that is
50 * movable, default-constructible, inequality-comparable and stream-outputable.
51 * A string or string_view Key may be preferable when the location comes from
52 * user input, or to describe the steps like "before-foo" and "after-foo".
53 * An integer Key may be preferable for a code path with many steps, where you
54 * just want to check 1, 2, 3, etc. without inventing names for each.
55 */
56 template <typename Key>
57 class FaultInjector {
58 public:
59 /// Default-construct with no injected failure.
60 constexpr FaultInjector() noexcept : location() {}
61
62 /// Construct with an injected assertion failure at the given location.
63 constexpr FaultInjector(Key location, InjectAbort a)
64 : location(std::move(location)), failure(a) {}
65
66 /// Construct with an injected error code at the given location.
67 constexpr FaultInjector(Key location, InjectError e)
68 : location(std::move(location)), failure(e) {}
69
70 /// Inject an assertion failure at the given location.
71 void inject(Key location, InjectAbort a) {
72 this->location = std::move(location);
73 this->failure = a;
74 }
75
76 /// Inject an error at the given location.
77 void inject(Key location, InjectError e) {
78 this->location = std::move(location);
79 this->failure = e;
80 }
81
82 /// Clear any injected failure.
83 void clear() {
84 this->failure = Empty{};
85 }
86
87 /// Check for an injected failure at the given location. If the location
88 /// matches an InjectAbort failure, the process aborts here with an assertion
89 /// failure.
90 /// @returns 0 or InjectError::error if the location matches an InjectError
91 /// failure
92 [[nodiscard]] constexpr int check(const Key& location) const {
93 struct visitor {
94 const Key& check_location;
95 const Key& this_location;
96 constexpr int operator()(const std::monostate&) const {
97 return 0;
98 }
99 int operator()(const InjectAbort&) const {
100 if (check_location == this_location) {
101 ceph_assert_always(!"FaultInjector");
102 }
103 return 0;
104 }
105 int operator()(const InjectError& e) const {
106 if (check_location == this_location) {
107 ldpp_dout(e.dpp, -1) << "Injecting error=" << e.error
108 << " at location=" << this_location << dendl;
109 return e.error;
110 }
111 return 0;
112 }
113 };
114 return std::visit(visitor{location, this->location}, failure);
115 }
116
117 private:
118 // Key requirements:
119 static_assert(std::is_default_constructible_v<Key>,
120 "Key must be default-constrible");
121 static_assert(std::is_move_constructible_v<Key>,
122 "Key must be move-constructible");
123 static_assert(std::is_move_assignable_v<Key>,
124 "Key must be move-assignable");
125 static_assert(boost::has_equal_to<Key, Key, bool>::value,
126 "Key must be equality-comparable");
127 static_assert(boost::has_left_shift<std::ostream, Key, std::ostream&>::value,
128 "Key must have an ostream operator<<");
129
130 Key location; // location of the check that should fail
131
132 using Empty = std::monostate; // empty state for std::variant
133
134 std::variant<Empty, InjectAbort, InjectError> failure;
135 };