preload/posix/generate: Replace spaces with tab
[libfiu.git] / doc / guide.rst
blobb72cbc808b415fc62b3d7dc51f3aea559d20976d
2 libfiu - Fault injection in userspace
3 =====================================
5 Introduction
6 ------------
8 You, as a programmer, know many things can fail, and your software is often
9 expected to be able to handle those failures. But how do you test your failure
10 handling code, when it's not easy to make a failure appear in the first place?
11 One way to do it is to perform *fault injection*.
13 According to Wikipedia, "fault injection is a technique for improving the
14 coverage of a test by introducing faults in order to test code paths, in
15 particular error handling code paths, that might otherwise rarely be followed.
16 It is often used with stress testing and is widely considered to be an
17 important part of developing robust software".
19 libfiu is a library that you can use to add fault injection to your code. It
20 aims to be easy to use by means of a simple API, with minimal code impact and
21 little runtime overhead when enabled.
23 That means that the modifications you have to do to your code (and build
24 system) in order to support libfiu should be as little intrusive as possible.
27 Code overview
28 -------------
30 Let's take a look to a small (fictitious) code sample to see what's the
31 general idea behind libfiu.
33 Assume that you have this code that checks if there's enough free space to
34 store a given file::
36         size_t free_space() {
37                 [code to find out how much free space there is]
38                 return space;
39         }
41         bool file_fits(FILE *fd) {
42                 if (free_space() < file_size(fd)) {
43                         return false;
44                 }
45                 return true;
46         }
48 With current disk sizes, it's very unusual to run out of free space, which
49 makes the scenario where *free_space()* returns 0 hard to test. With libfiu,
50 you can do the following small addition::
52         size_t free_space() {
53                 fiu_return_on("no_free_space", 0);
55                 [code to find out how much free space there is]
56                 return space;
57         }
59         bool file_fits(FILE *fd) {
60                 if (free_space() < file_size(fd)) {
61                         return false;
62                 }
63                 return true;
64         }
66 The *fiu_return_on()* annotation is the only change you need to make to your
67 code to create a *point of failure*, which is identified by the name
68 **no_free_space**. When that point of failure is enabled, the function will
69 return 0.
71 In your testing code, you can now do this::
73         fiu_init();
74         fiu_enable("no_free_space", 1, NULL, 0);
75         assert(file_fits("tmpfile") == false);
77 The first line initializes the library, and the second *enables* the point of
78 failure. When the point of failure is enabled, *free_space()* will return 0,
79 so you can test how your code behaves under that condition, which was
80 otherwise hard to trigger.
82 libfiu's API has two "sides": a core API and a control API.  The core API is
83 used inside the code to be fault injected. The control API is used inside the
84 testing code, in order to control the injection of failures.
86 In the example above, *fiu_return_on()* is a part of the core API, and
87 *fiu_enable()* is a part of the control API.
90 Using libfiu in your project
91 ----------------------------
93 To use libfiu in your project, there are three things to consider: the build
94 system, the fault injection code, and the testing code.
97 The build system
98 ~~~~~~~~~~~~~~~~
100 The first thing to do is to enable your build system to use libfiu. Usually,
101 you do not want to make libfiu a runtime or build-time dependency, because
102 it's often only used for testing.
104 To that end, you should copy *fiu-local.h* into your source tree, and then
105 create an option to do a *fault injection build* that #defines the constant
106 *FIU_ENABLE* (usually done by adding ``-DFIU_ENABLE=1`` to your compiler
107 flags) and links against libfiu (usually done by adding ``-lfiu`` to your
108 linker flags).
110 That way, normal builds will not have a single trace of fault injection code,
111 but it will be easy to create a binary that does, for testing purposes.
114 The fault injection code
115 ~~~~~~~~~~~~~~~~~~~~~~~~
117 Adding fault injection to your code means inserting points of failure in it,
118 using the core API.
120 First, you should ``#include "fiu-local.h"`` in the files you want to add
121 points of failure to. That header allows you to avoid libfiu as a build-time
122 dependency, as mentioned in the last section.
124 Then, to insert points of failure, sprinkle your code with calls like
125 ``fiu_return_on("name", -1)``, ``fiu_exit_on("name")``, or more complex code
126 using ``fiu_fail("name")``. See the libfiu's manpage for the details on
127 the API.
129 It is recommended that you use meaningful names for your points of failure, to
130 be able to easily identify their purpose. You can also name them
131 hierarchically (for example, using names like *"io/write"*, *"io/read"*, and
132 so on), to be able to enable entire groups of points of failure (like
133 *"io/\*"*). To this end, any separator will do, the *'/'* is not special at
134 all.
137 The testing code
138 ~~~~~~~~~~~~~~~~
140 Testing can be done in too many ways, so I won't get into specific details
141 here. As a general approach, usually the idea with fault injection is to write
142 tests similar in spirit to the one shown above: initialize the library, enable
143 one or more failures using the control API, and then check if the code behaves
144 as expected.
146 Initially, all points of failure are disabled, which means your code should run
147 as usual, with a very small performance impact.
149 The points of failure can be enabled using different strategies:
151 Unconditional (*fiu_enable()*)
152   Enables the point of failure in an unconditional way, so it always fails.
154 Random (*fiu_enable_random()*)
155   Enables the point of failure in a non-deterministic way, which will fail with
156   the given probability.
158 External (*fiu_enable_external()*)
159   Enables the point of failure using an external function, which will be called
160   to determine whether the point of failure should fail or not.
162 You can also use an asterisk *at the end* of a name to enable all the points
163 of failure that begin with the given name (excluding the asterisk, of course).
165 Check libfiu's manpage for more details about the API.
167 If you prefer to avoid writing the test code in C, you can use the Python
168 bindings, and/or the *fiu-run* and *fiu-ctrl* utilities.