1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 #pragma once
6 
7 #include <assert.h>
8 #include <functional>
9 #include <mutex>
10 #include <string>
11 #include <thread>
12 #include <vector>
13 
14 #include "rocksdb/rocksdb_namespace.h"
15 
16 #ifdef NDEBUG
17 // empty in release build
18 #define TEST_KILL_RANDOM_WITH_WEIGHT(kill_point, rocksdb_kill_odds_weight)
19 #define TEST_KILL_RANDOM(kill_point)
20 #else
21 
22 namespace ROCKSDB_NAMESPACE {
23 
24 // To avoid crashing always at some frequently executed codepaths (during
25 // kill random test), use this factor to reduce odds
26 #define REDUCE_ODDS 2
27 #define REDUCE_ODDS2 4
28 
29 // A class used to pass when a kill point is reached.
30 struct KillPoint {
31  public:
32   // This is only set from db_stress.cc and for testing only.
33   // If non-zero, kill at various points in source code with probability 1/this
34   int rocksdb_kill_odds = 0;
35   // If kill point has a prefix on this list, will skip killing.
36   std::vector<std::string> rocksdb_kill_exclude_prefixes;
37   // Kill the process with probability 1/odds for testing.
38   void TestKillRandom(std::string kill_point, int odds,
39                       const std::string& srcfile, int srcline);
40 
41   static KillPoint* GetInstance();
42 };
43 
44 #define TEST_KILL_RANDOM_WITH_WEIGHT(kill_point, rocksdb_kill_odds_weight) \
45   {                                                                        \
46     KillPoint::GetInstance()->TestKillRandom(                              \
47         kill_point, rocksdb_kill_odds_weight, __FILE__, __LINE__);         \
48   }
49 #define TEST_KILL_RANDOM(kill_point) TEST_KILL_RANDOM_WITH_WEIGHT(kill_point, 1)
50 }  // namespace ROCKSDB_NAMESPACE
51 
52 #endif
53 
54 #ifdef NDEBUG
55 #define TEST_SYNC_POINT(x)
56 #define TEST_IDX_SYNC_POINT(x, index)
57 #define TEST_SYNC_POINT_CALLBACK(x, y)
58 #define INIT_SYNC_POINT_SINGLETONS()
59 #else
60 
61 namespace ROCKSDB_NAMESPACE {
62 
63 // This class provides facility to reproduce race conditions deterministically
64 // in unit tests.
65 // Developer could specify sync points in the codebase via TEST_SYNC_POINT.
66 // Each sync point represents a position in the execution stream of a thread.
67 // In the unit test, 'Happens After' relationship among sync points could be
68 // setup via SyncPoint::LoadDependency, to reproduce a desired interleave of
69 // threads execution.
70 // Refer to (DBTest,TransactionLogIteratorRace), for an example use case.
71 
72 class SyncPoint {
73  public:
74   static SyncPoint* GetInstance();
75 
76   SyncPoint(const SyncPoint&) = delete;
77   SyncPoint& operator=(const SyncPoint&) = delete;
78   ~SyncPoint();
79 
80   struct SyncPointPair {
81     std::string predecessor;
82     std::string successor;
83   };
84 
85   // call once at the beginning of a test to setup the dependency between
86   // sync points
87   void LoadDependency(const std::vector<SyncPointPair>& dependencies);
88 
89   // call once at the beginning of a test to setup the dependency between
90   // sync points and setup markers indicating the successor is only enabled
91   // when it is processed on the same thread as the predecessor.
92   // When adding a marker, it implicitly adds a dependency for the marker pair.
93   void LoadDependencyAndMarkers(const std::vector<SyncPointPair>& dependencies,
94                                 const std::vector<SyncPointPair>& markers);
95 
96   // The argument to the callback is passed through from
97   // TEST_SYNC_POINT_CALLBACK(); nullptr if TEST_SYNC_POINT or
98   // TEST_IDX_SYNC_POINT was used.
99   void SetCallBack(const std::string& point,
100                    const std::function<void(void*)>& callback);
101 
102   // Clear callback function by point
103   void ClearCallBack(const std::string& point);
104 
105   // Clear all call back functions.
106   void ClearAllCallBacks();
107 
108   // enable sync point processing (disabled on startup)
109   void EnableProcessing();
110 
111   // disable sync point processing
112   void DisableProcessing();
113 
114   // remove the execution trace of all sync points
115   void ClearTrace();
116 
117   // triggered by TEST_SYNC_POINT, blocking execution until all predecessors
118   // are executed.
119   // And/or call registered callback function, with argument `cb_arg`
120   void Process(const std::string& point, void* cb_arg = nullptr);
121 
122   // TODO: it might be useful to provide a function that blocks until all
123   // sync points are cleared.
124 
125   // We want this to be public so we can
126   // subclass the implementation
127   struct Data;
128 
129  private:
130    // Singleton
131   SyncPoint();
132   Data*  impl_;
133 };
134 
135 // Sets up sync points to mock direct IO instead of actually issuing direct IO
136 // to the file system.
137 void SetupSyncPointsToMockDirectIO();
138 }  // namespace ROCKSDB_NAMESPACE
139 
140 // Use TEST_SYNC_POINT to specify sync points inside code base.
141 // Sync points can have happens-after dependency on other sync points,
142 // configured at runtime via SyncPoint::LoadDependency. This could be
143 // utilized to re-produce race conditions between threads.
144 // See TransactionLogIteratorRace in db_test.cc for an example use case.
145 // TEST_SYNC_POINT is no op in release build.
146 #define TEST_SYNC_POINT(x) \
147   ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->Process(x)
148 #define TEST_IDX_SYNC_POINT(x, index)                      \
149   ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->Process(x + \
150                                                        std::to_string(index))
151 #define TEST_SYNC_POINT_CALLBACK(x, y) \
152   ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->Process(x, y)
153 #define INIT_SYNC_POINT_SINGLETONS() \
154   (void)ROCKSDB_NAMESPACE::SyncPoint::GetInstance();
155 #endif  // NDEBUG
156 
157 // Callback sync point for any read IO errors that should be ignored by
158 // the fault injection framework
159 // Disable in release mode
160 #ifdef NDEBUG
161 #define IGNORE_STATUS_IF_ERROR(_status_)
162 #else
163 #define IGNORE_STATUS_IF_ERROR(_status_)            \
164   {                                                 \
165     if (!_status_.ok()) {                           \
166       TEST_SYNC_POINT("FaultInjectionIgnoreError"); \
167     }                                               \
168   }
169 #endif  // NDEBUG
170