1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2020 Red Hat, Inc.
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.
17 #include <type_traits>
18 #include <boost/type_traits/has_equal_to.hpp>
19 #include <boost/type_traits/has_left_shift.hpp>
21 #include "include/ceph_assert.h"
22 #include "common/dout.h"
26 /// A failure type that aborts the process with a failed assertion.
27 struct InjectAbort
{};
29 /// A failure type that injects an error code and optionally logs a message.
31 /// error code to inject
33 /// an optional log channel to print an error message
34 const DoutPrefixProvider
* dpp
= nullptr;
37 /** @class FaultInjector
38 * @brief Used to instrument a code path with deterministic fault injection
39 * by making one or more calls to check().
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.
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
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.
56 template <typename Key
>
59 /// Default-construct with no injected failure.
60 constexpr FaultInjector() noexcept
: location() {}
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
) {}
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
) {}
70 /// Inject an assertion failure at the given location.
71 void inject(Key location
, InjectAbort a
) {
72 this->location
= std::move(location
);
76 /// Inject an error at the given location.
77 void inject(Key location
, InjectError e
) {
78 this->location
= std::move(location
);
82 /// Clear any injected failure.
84 this->failure
= Empty
{};
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
90 /// @returns 0 or InjectError::error if the location matches an InjectError
92 [[nodiscard
]] constexpr int check(const Key
& location
) const {
94 const Key
& check_location
;
95 const Key
& this_location
;
96 constexpr int operator()(const std::monostate
&) const {
99 int operator()(const InjectAbort
&) const {
100 if (check_location
== this_location
) {
101 ceph_assert_always(!"FaultInjector");
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
;
114 return std::visit(visitor
{location
, this->location
}, failure
);
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<<");
130 Key location
; // location of the check that should fail
132 using Empty
= std::monostate
; // empty state for std::variant
134 std::variant
<Empty
, InjectAbort
, InjectError
> failure
;