pax_global_header00006660000000000000000000000064125634654150014525gustar00rootroot0000000000000052 comment=e0ba75f4d5fcc9fd6e1f416987f9b9ae4747e5cd libfiu-0.94/000077500000000000000000000000001256346541500127335ustar00rootroot00000000000000libfiu-0.94/.gitignore000066400000000000000000000014311256346541500147220ustar00rootroot00000000000000bindings/python/build bindings/python/*.pyc bindings/python/*.pyo bindings/python/fiu_ctrl.py libfiu/*.o libfiu/libfiu.a libfiu/libfiu.pc libfiu/libfiu.so libfiu/libfiu.so.* libfiu/build-flags libfiu/doxygen/doc.internal libfiu/doxygen/doc.public libfiu/doxygen/Doxyfile.base preload/posix/*.o preload/posix/*.so preload/posix/build-flags preload/posix/build-libcsoname preload/posix/build-needlibdl preload/posix/build-env.h preload/posix/function_list preload/posix/modules/*.o preload/posix/modules/*.mod.c preload/posix/modules/*.mod.fl preload/run/*.o preload/run/*.so preload/run/fiu-run preload/run/build-flags preload/run/build-needlibdl tests/*.o tests/build-flags tests/test-? tests/small-cat tests/libs/ tests/generated/build-flags tests/generated/tests/*.[oc] .*.swp *.gcno *.gcda libfiu-0.94/LICENSE000066400000000000000000000022461256346541500137440ustar00rootroot00000000000000 I don't like licenses, because I don't like having to worry about all this legal stuff just for a simple piece of software I don't really mind anyone using. But I also believe that it's important that people share and give back; so I'm placing this work under the following license. BOLA - Buena Onda License Agreement (v1.1) ------------------------------------------ This work is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this work. To all effects and purposes, this work is to be considered Public Domain. However, if you want to be "buena onda", you should: 1. Not take credit for it, and give proper recognition to the authors. 2. Share your modifications, so everybody benefits from them. 3. Do something nice for the authors. 4. Help someone who needs it: sign up for some volunteer work or help your neighbour paint the house. 5. Don't waste. Anything, but specially energy that comes from natural non-renewable resources. Extra points if you discover or invent something to replace them. 6. Be tolerant. Everything that's good in nature comes from cooperation. libfiu-0.94/Makefile000066400000000000000000000032411256346541500143730ustar00rootroot00000000000000 default: all install: all_install uninstall: all_uninstall all: libfiu preload utils all_install: libfiu_install preload_install utils_install all_uninstall: libfiu_uninstall preload_uninstall utils_uninstall libfiu: $(MAKE) -C libfiu libfiu_clean: $(MAKE) -C libfiu clean libfiu_install: $(MAKE) -C libfiu install libfiu_uninstall: $(MAKE) -C libfiu uninstall preload: libfiu $(MAKE) -C preload preload_clean: $(MAKE) -C preload clean preload_install: preload $(MAKE) -C preload install preload_uninstall: $(MAKE) -C preload uninstall utils: $(MAKE) -C utils utils_clean: $(MAKE) -C utils clean utils_install: utils $(MAKE) -C utils install utils_uninstall: $(MAKE) -C utils uninstall tests: test test: libfiu bindings preload $(MAKE) -C tests test_clean: $(MAKE) -C tests clean bindings: python2 python3 bindings_install: python2_install python3_install bindings_clean: python_clean python2: libfiu cd bindings/python && python setup.py build python2_install: python2 cd bindings/python && python setup.py install python3: libfiu cd bindings/python && python3 setup.py build python3_install: python3 cd bindings/python && python3 setup.py install python_clean: cd bindings/python && rm -rf build/ clean: python_clean preload_clean libfiu_clean utils_clean test_clean .PHONY: default all clean install all_install uninstall all_uninstall \ libfiu libfiu_clean libfiu_install libfiu_uninstall \ python2 python2_install python3 python3_install python_clean \ bindings bindings_install bindings_clean \ preload preload_clean preload_install preload_uninstall \ utils utils_clean utils_install utils_uninstall \ test tests test_clean libfiu-0.94/README000066400000000000000000000031001256346541500136050ustar00rootroot00000000000000 libfiu - Fault injection in userspace ------------------------------------- libfiu is a C library for fault injection. It provides functions to mark "points of failure" inside your code (the "core API"), and functions to enable/disable the failure of those points (the "control API"). It's in the public domain, see the LICENSE file for more information. The core API is used inside the code wanting to perform fault injection on. The control API is used inside the testing code, in order to control the injection of failures. Python bindings are available in the "bindings" directory. Documentation ------------- You can find the user guide in the "doc" directory, and a manpage in the "libfiu" directory. The manpage will be installed along the library. Python bindings have embedded documentation, although it's not as complete. Building and installing ----------------------- Running "make" (or "gmake") should be enough for building, and "make install" for installing. By default it installs into /usr/local, but you can provide an alternative prefix by running "make PREFIX=/my/prefix install". To build the Python bindings, use "make python2"; to install them you can run "make python2_install". For the Python 3 bindings, use "make python3" and "make python3_install". Solaris users must use "gmake INSTALL=ginstall" instead of "make". Where to report bugs -------------------- If you want to report bugs, or have any questions or comments, just let me know at albertito@blitiri.com.ar. For more information about the library, you can go to http://blitiri.com.ar/p/libfiu. libfiu-0.94/bindings/000077500000000000000000000000001256346541500145305ustar00rootroot00000000000000libfiu-0.94/bindings/python/000077500000000000000000000000001256346541500160515ustar00rootroot00000000000000libfiu-0.94/bindings/python/fiu.py000066400000000000000000000057471256346541500172230ustar00rootroot00000000000000 """ libfiu python wrapper This module is a wrapper for the libfiu, the fault injection C library. It provides an almost one-to-one mapping of the libfiu functions, although its primary use is to be able to test C code from within Python. For fault injection in Python, a native library would be more suitable. See libfiu's manpage for more detailed documentation. """ import fiu_ll as _ll def fail(name): "Returns the failure status of the given point of failure." return _ll.fail(name) def failinfo(name): """Returns the information associated with the last failure. Use with care, can be fatal if the point of failure was not enabled via Python.""" return _ll.failinfo() class Flags: """Contains the valid flag constants. ONETIME: This point of failure is disabled immediately after failing once. """ ONETIME = _ll.FIU_ONETIME # To be sure failinfo doesn't dissapear from under our feet, we keep a # name -> failinfo table. See fiu_ll's comments for more details. _fi_table = {} def enable(name, failnum = 1, failinfo = None, flags = 0): "Enables the given point of failure." _fi_table[name] = failinfo r = _ll.enable(name, failnum, failinfo, flags) if r != 0: del _fi_table[name] raise RuntimeError(r) def enable_random(name, probability, failnum = 1, failinfo = None, flags = 0): "Enables the given point of failure, with the given probability." _fi_table[name] = failinfo r = _ll.enable_random(name, failnum, failinfo, flags, probability) if r != 0: del _fi_table[name] raise RuntimeError(r) def enable_external(name, cb, failnum = 1, flags = 0): """Enables the given point of failure, leaving the decision whether to fail or not to the given external function, which should return 0 if it is not to fail, or 1 otherwise. The cb parameter is a Python function that takes three parameters, name, failnum and flags, with the same values that we receive. For technical limitations, enable_external() cannot take failinfo.""" # in this case, use the table to prevent the function from # dissapearing _fi_table[name] = cb r = _ll.enable_external(name, failnum, flags, cb) if r != 0: del _fi_table[name] raise RuntimeError(r) def enable_stack_by_name(name, func_name, failnum = 1, failinfo = None, flags = 0, pos_in_stack = -1): """Enables the given point of failure, but only if 'func_name' is in the stack. 'func_name' is be the name of the C function to look for. """ _fi_table[name] = failinfo r = _ll.enable_stack_by_name(name, failnum, failinfo, flags, func_name, pos_in_stack) if r != 0: del _fi_table[name] raise RuntimeError(r) def disable(name): """Disables the given point of failure, undoing the actions of the enable*() functions.""" if name in _fi_table: del _fi_table[name] r = _ll.disable(name) if r != 0: raise RuntimeError(r) def rc_fifo(basename): """Enables remote control over a named pipe that begins with the given basename. The final path will be "basename-$PID".""" r = _ll.rc_fifo(basename) if r != 0: raise RuntimeError(r) libfiu-0.94/bindings/python/fiu_ctrl.in.py000066400000000000000000000145431256346541500206460ustar00rootroot00000000000000""" libfiu python module for remote control This module provides an easy way to run a command with libfiu enabled, and controlling the failure points dynamically. It provides similar functionality to the fiu-ctrl and fiu-run shell tools, but is useful for programmed tests. Note it assumes the preloading libraries are installed in @@PLIBPATH@@. """ import os import tempfile import subprocess import shutil import time # Default path to look for preloader libraries. PLIBPATH = "@@PLIBPATH@@" class CommandError (RuntimeError): """There was an error running the command.""" pass class Flags: """Contains the valid flag constants. ONETIME: This point of failure is disabled immediately after failing once. """ ONETIME = "onetime" class _ControlBase (object): """Base class for remote control objects.""" def run_raw_cmd(self, cmd, args): """Runs a new raw command. To be implemented by subclasses""" raise NotImplementedError def _basic_args(self, name, failnum, failinfo, flags): """Converts the common arguments to an args list for run_raw_cmd().""" args = ["name=%s" % name] if failnum: args.append("failnum=%s" % failnum) if failinfo: args.append("failinfo=%s" % failinfo) if flags: args.extend(flags) return args def enable(self, name, failnum = 1, failinfo = None, flags = ()): """Enables the given point of failure.""" args = self._basic_args(name, failnum, failinfo, flags) self.run_raw_cmd("enable", args) def enable_random(self, name, probability, failnum = 1, failinfo = None, flags = ()): "Enables the given point of failure, with the given probability." args = self._basic_args(name, failnum, failinfo, flags) args.append("probability=%f" % probability) self.run_raw_cmd("enable_random", args) def enable_stack_by_name(self, name, func_name, failnum = 1, failinfo = None, flags = (), pos_in_stack = -1): """Enables the given point of failure, but only if 'func_name' is in the stack. 'func_name' is be the name of the C function to look for. """ args = self._basic_args(name, failnum, failinfo, flags) args.append("func_name=%s" % func_name) if pos_in_stack >= 0: args.append("pos_in_stack=%d" % pos_in_stack) self.run_raw_cmd("enable_stack_by_name", args) def disable(self, name): """Disables the given point of failure.""" self.run_raw_cmd("disable", ["name=%s" % name]) def _open_with_timeout(path, mode, timeout = 3): """Open a file, waiting if it doesn't exist yet.""" deadline = time.time() + timeout while not os.path.exists(path): time.sleep(0.01) if time.time() >= deadline: raise RuntimeError("Timeout waiting for file %r" % path) return open(path, mode) class PipeControl (_ControlBase): """Control pipe used to control a libfiu-instrumented process.""" def __init__(self, path_prefix): """Constructor. Args: path: Path to the control pipe. """ self.path_in = path_prefix + ".in" self.path_out = path_prefix + ".out" def _open_pipes(self): # Open the files, but wait if they are not there, as the child process # may not have created them yet. fd_in = _open_with_timeout(self.path_in, "a") fd_out = _open_with_timeout(self.path_out, "r") return fd_in, fd_out def run_raw_cmd(self, cmd, args): """Send a raw command over the pipe.""" # Note we open the pipe each time for simplicity, and also to simplify # external intervention that can be used for debugging. fd_in, fd_out = self._open_pipes() s = "%s %s\n" % (cmd, ','.join(args)) fd_in.write(s) fd_in.flush() r = int(fd_out.readline()) if r != 0: raise CommandError class EnvironmentControl (_ControlBase): """Pre-execution environment control.""" def __init__(self): self.env = "" def run_raw_cmd(self, cmd, args): """Add a raw command to the environment.""" self.env += "%s %s\n" % (cmd, ','.join(args)) class Subprocess (_ControlBase): """Wrapper for subprocess.Popen, but without immediate execution. This class provides a wrapper for subprocess.Popen, which can be used to run other processes under libfiu. However, the processes don't start straight away, allowing the user to pre-configure some failure points. The process can then be started with the start() method. After the process has been started, the failure points can be controlled remotely via the same functions. Processes can be started only once. Note that using shell=True is not recommended, as it makes the pid of the controlled process to be unknown. """ def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs # Note this removes fiu_enable_posix from kwargs if it's there, that # way kwargs remains "clean" for passing to Popen. self.fiu_enable_posix = kwargs.pop('fiu_enable_posix', False) self._proc = None self.tmpdir = None # Initially, this is an EnvironmentControl so we can do preparation; # once we start the command, we will change this to be PipeControl. self.ctrl = EnvironmentControl() def run_raw_cmd(self, cmd, args): self.ctrl.run_raw_cmd(cmd, args) def start(self): self.tmpdir = tempfile.mkdtemp(prefix = 'fiu_ctrl-') env = os.environ env['LD_PRELOAD'] = env.get('LD_PRELOAD', '') if self.fiu_enable_posix: env['LD_PRELOAD'] += ' ' + PLIBPATH + '/fiu_posix_preload.so' env['LD_PRELOAD'] += ' ' + PLIBPATH + '/fiu_run_preload.so ' env['FIU_CTRL_FIFO'] = self.tmpdir + '/ctrl-fifo' env['FIU_ENABLE'] = self.ctrl.env self._proc = subprocess.Popen(*self.args, **self.kwargs) fifo_path = "%s-%d" % (env['FIU_CTRL_FIFO'], self._proc.pid) self.ctrl = PipeControl(fifo_path) return self._proc def __del__(self): # Remove the temporary directory. # The "'fiu_ctrl-' in self.tmpdir" check is just a safeguard. if self.tmpdir and 'fiu_ctrl-' in self.tmpdir: shutil.rmtree(self.tmpdir) libfiu-0.94/bindings/python/fiu_ll.c000066400000000000000000000136671256346541500175040ustar00rootroot00000000000000 /* * Python 2/3 bindings for libfiu * Alberto Bertogli (albertito@blitiri.com.ar) * * This is the low-level module, used by the python one to construct * friendlier objects. It support both Python 2 and 3, assuming the constants * PYTHON2 and PYTHON3 are defined accordingly. */ #include /* Unconditionally enable fiu, otherwise we get fake headers */ #define FIU_ENABLE 1 #include #include static PyObject *fail(PyObject *self, PyObject *args) { char *name; PyObject *rv, *err; if (!PyArg_ParseTuple(args, "s:fail", &name)) return NULL; rv = PyLong_FromLong(fiu_fail(name)); err = PyErr_Occurred(); if (rv == NULL || err != NULL) { Py_XDECREF(rv); return NULL; } return rv; } static PyObject *failinfo(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, ":failinfo")) return NULL; /* We assume failinfo is a python object; but the caller must be * careful because if it's not, it can get into trouble. * Note that we DO NOT TOUCH THE RC OF THE OBJECT. It's entirely up to * the caller to make sure it's still alive. */ return (PyObject *) fiu_failinfo(); } static PyObject *enable(PyObject *self, PyObject *args) { char *name; int failnum; PyObject *failinfo; unsigned int flags; if (!PyArg_ParseTuple(args, "siOI:enable", &name, &failnum, &failinfo, &flags)) return NULL; /* See failinfo()'s comment regarding failinfo's RC */ return PyLong_FromLong(fiu_enable(name, failnum, failinfo, flags)); } static PyObject *enable_random(PyObject *self, PyObject *args) { char *name; int failnum; PyObject *failinfo; unsigned int flags; double probability; if (!PyArg_ParseTuple(args, "siOId:enable_random", &name, &failnum, &failinfo, &flags, &probability)) return NULL; /* See failinfo()'s comment regarding failinfo's RC */ return PyLong_FromLong(fiu_enable_random(name, failnum, failinfo, flags, probability)); } static int external_callback(const char *name, int *failnum, void **failinfo, unsigned int *flags) { int rv; PyObject *cbrv; PyObject *args; PyGILState_STATE gil_state; /* We need to protect ourselves from the following case: * - fiu.enable_callback('x', cb) (where cb is obviously a Python * function) * - Later on, call a function p1() inside a python C module, that * runs a C function c1() inside * Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS * - c1() calls fiu_fail("x") * - fiu_fail("x") calls external_callback(), and it should run cb() * - BUT! It can't run cb(), because it's inside * Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS so it's not safe to * execute any Python code! * * The solution is to ensure we're safe to run Python code using * PyGILState_Ensure()/PyGILState_Release(). */ gil_state = PyGILState_Ensure(); args = Py_BuildValue("(siI)", name, *failnum, *flags); if (args == NULL) { PyGILState_Release(gil_state); return 0; } cbrv = PyEval_CallObject(*failinfo, args); Py_DECREF(args); if (cbrv == NULL) { PyGILState_Release(gil_state); return 0; } /* If PyLong_AsLong() causes an error, it will be handled by the * PyErr_Occurred() check in fail(), so we don't need to worry about * it now. */ rv = PyLong_AsLong(cbrv); Py_DECREF(cbrv); PyGILState_Release(gil_state); return rv; } static PyObject *enable_external(PyObject *self, PyObject *args) { char *name; int failnum; unsigned int flags; PyObject *py_external_cb; if (!PyArg_ParseTuple(args, "siIO:enable_external", &name, &failnum, &flags, &py_external_cb)) return NULL; if (!PyCallable_Check(py_external_cb)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } /* We use failinfo to store Python's callback function. It'd be nice * if we could keep both, but it's not easy without keeping state * inside the C module. * * Similar to the way failinfo is handled, we DO NOT TOUCH THE RC OF * THE EXTERNAL CALLBACK, assuming the caller will take care of making * sure it doesn't dissapear from beneath us. */ return PyLong_FromLong(fiu_enable_external(name, failnum, py_external_cb, flags, external_callback)); } static PyObject *enable_stack_by_name(PyObject *self, PyObject *args) { char *name; int failnum; PyObject *failinfo; unsigned int flags; char *func_name; int pos_in_stack = -1; if (!PyArg_ParseTuple(args, "siOIs|i:enable_stack_by_name", &name, &failnum, &failinfo, &flags, &func_name, &pos_in_stack)) return NULL; return PyLong_FromLong(fiu_enable_stack_by_name(name, failnum, failinfo, flags, func_name, pos_in_stack)); } static PyObject *disable(PyObject *self, PyObject *args) { char *name; if (!PyArg_ParseTuple(args, "s:disable", &name)) return NULL; return PyLong_FromLong(fiu_disable(name)); } static PyObject *rc_fifo(PyObject *self, PyObject *args) { char *basename; if (!PyArg_ParseTuple(args, "s:rc_fifo", &basename)) return NULL; return PyLong_FromLong(fiu_rc_fifo(basename)); } static PyMethodDef fiu_methods[] = { { "fail", (PyCFunction) fail, METH_VARARGS, NULL }, { "failinfo", (PyCFunction) failinfo, METH_VARARGS, NULL }, { "enable", (PyCFunction) enable, METH_VARARGS, NULL }, { "enable_random", (PyCFunction) enable_random, METH_VARARGS, NULL }, { "enable_external", (PyCFunction) enable_external, METH_VARARGS, NULL }, { "enable_stack_by_name", (PyCFunction) enable_stack_by_name, METH_VARARGS, NULL }, { "disable", (PyCFunction) disable, METH_VARARGS, NULL }, { "rc_fifo", (PyCFunction) rc_fifo, METH_VARARGS, NULL }, { NULL } }; #ifdef PYTHON3 static PyModuleDef fiu_module = { PyModuleDef_HEAD_INIT, .m_name = "libfiu", .m_size = -1, .m_methods = fiu_methods, }; #endif #ifdef PYTHON2 PyMODINIT_FUNC initfiu_ll(void) #else PyMODINIT_FUNC PyInit_fiu_ll(void) #endif { PyObject *m; #ifdef PYTHON2 m = Py_InitModule("fiu_ll", fiu_methods); #else m = PyModule_Create(&fiu_module); #endif PyModule_AddIntConstant(m, "FIU_ONETIME", FIU_ONETIME); fiu_init(0); #ifdef PYTHON3 return m; #endif } libfiu-0.94/bindings/python/setup.py000066400000000000000000000026531256346541500175710ustar00rootroot00000000000000 import os import sys from distutils.core import setup, Extension from distutils.command.build_py import build_py if sys.version_info[0] == 2: ver_define = ('PYTHON2', '1') elif sys.version_info[0] == 3: ver_define = ('PYTHON3', '1') # We need to generate the fiu_ctrl.py file from fiu_ctrl.in.py, replacing some # environment variables in its contents. class generate_and_build_py (build_py): def run(self): if not self.dry_run: self.generate_fiu_ctrl() build_py.run(self) def generate_fiu_ctrl(self): prefix = os.environ.get('PREFIX', '/usr/local/') plibpath = os.environ.get('PLIBPATH', prefix + '/lib/') contents = open('fiu_ctrl.in.py', 'r').read() contents = contents.replace('@@PLIBPATH@@', plibpath) out = open('fiu_ctrl.py', 'w') out.write(contents) out.close() fiu_ll = Extension("fiu_ll", sources = ['fiu_ll.c'], define_macros = [ver_define], libraries = ['fiu'], # these two allow us to build without having libfiu installed, # assuming we're in the libfiu source tree include_dirs = ['../../libfiu/'], library_dirs=['../../libfiu/']) setup( name = 'fiu', description = "libfiu bindings", author = "Alberto Bertogli", author_email = "albertito@blitiri.com.ar", url = "http://blitiri.com.ar/p/libfiu", py_modules = ['fiu', 'fiu_ctrl'], ext_modules = [fiu_ll], cmdclass = {'build_py': generate_and_build_py}, ) libfiu-0.94/doc/000077500000000000000000000000001256346541500135005ustar00rootroot00000000000000libfiu-0.94/doc/guide.rst000066400000000000000000000142761256346541500153410ustar00rootroot00000000000000 libfiu - Fault injection in userspace ===================================== Introduction ------------ You, as a programmer, know many things can fail, and your software is often expected to be able to handle those failures. But how do you test your failure handling code, when it's not easy to make a failure appear in the first place? One way to do it is to perform *fault injection*. According to Wikipedia, "fault injection is a technique for improving the coverage of a test by introducing faults in order to test code paths, in particular error handling code paths, that might otherwise rarely be followed. It is often used with stress testing and is widely considered to be an important part of developing robust software". libfiu is a library that you can use to add fault injection to your code. It aims to be easy to use by means of a simple API, with minimal code impact and little runtime overhead when enabled. That means that the modifications you have to do to your code (and build system) in order to support libfiu should be as little intrusive as possible. Code overview ------------- Let's take a look to a small (fictitious) code sample to see what's the general idea behind libfiu. Assume that you have this code that checks if there's enough free space to store a given file:: size_t free_space() { [code to find out how much free space there is] return space; } bool file_fits(FILE *fd) { if (free_space() < file_size(fd)) { return false; } return true; } With current disk sizes, it's very unusual to ran out of free space, which makes the scenario where *free_space()* returns 0 hard to test. With libfiu, you can do the following small addition:: size_t free_space() { fiu_return_on("no_free_space", 0); [code to find out how much free space there is] return space; } bool file_fits(FILE *fd) { if (free_space() < file_size(fd)) { return false; } return true; } The *fiu_return_on()* annotation is the only change you need to make to your code to create a *point of failure*, which is identified by the name **no_free_space**. When that point of failure is enabled, the function will return 0. In your testing code, you can now do this:: fiu_init(); fiu_enable("no_free_space", 1, NULL, 0); assert(file_fits("tmpfile") == false); The first line initializes the library, and the second *enables* the point of failure. When the point of failure is enabled, *free_space()* will return 0, so you can test how your code behaves under that condition, which was otherwise hard to trigger. libfiu's API has two "sides": a core API and a control API. The core API is used inside the code to be fault injected. The control API is used inside the testing code, in order to control the injection of failures. In the example above, *fiu_return_on()* is a part of the core API, and *fiu_enable()* is a part of the control API. Using libfiu in your project ---------------------------- To use libfiu in your project, there are three things to consider: the build system, the fault injection code, and the testing code. The build system ~~~~~~~~~~~~~~~~ The first thing to do is to enable your build system to use libfiu. Usually, you do not want to make libfiu a runtime or build-time dependency, because it's often only used for testing. To that end, you should copy *fiu-local.h* into your source tree, and then create an option to do a *fault injection build* that #defines the constant *FIU_ENABLE* (usually done by adding ``-DFIU_ENABLE=1`` to your compiler flags) and links against libfiu (usually done by adding ``-lfiu`` to your linker flags). That way, normal builds will not have a single trace of fault injection code, but it will be easy to create a binary that does, for testing purposes. The fault injection code ~~~~~~~~~~~~~~~~~~~~~~~~ Adding fault injection to your code means inserting points of failure in it, using the core API. First, you should ``#include "fiu-local.h"`` in the files you want to add points of failure to. That header allows you to avoid libfiu as a build-time dependency, as mentioned in the last section. Then, to insert points of failure, sprinkle your code with calls like ``fiu_return_on("name", -1)``, ``fiu_exit_on("name")``, or more complex code using ``fiu_fail("name")``. See the libfiu's manpage for the details on the API. It is recommended that you use meaningful names for your points of failure, to be able to easily identify their purpose. You can also name them hierarchically (for example, using names like *"io/write"*, *"io/read"*, and so on), to be able to enable entire groups of points of failure (like *"io/\*"*). To this end, any separator will do, the *'/'* is not special at all. The testing code ~~~~~~~~~~~~~~~~ Testing can be done in too many ways, so I won't get into specific details here. As a general approach, usually the idea with fault injection is to write tests similar in spirit to the one shown above: initialize the library, enable one or more failures using the control API, and then check if the code behaves as expected. Initially, all points of failure are disabled, which means your code should run as usual, with a very small performance impact. The points of failure can be enabled using different strategies: Unconditional (*fiu_enable()*) Enables the point of failure in an unconditional way, so it always fails. Random (*fiu_enable_random()*) Enables the point of failure in a non-deterministic way, which will fail with the given probability. External (*fiu_enable_external()*) Enables the point of failure using an external function, which will be called to determine whether the point of failure should fail or not. You can also use an asterisk *at the end* of a name to enable all the points of failure that begin with the given name (excluding the asterisk, of course). Check libfiu's manpage for more details about the API. If you prefer to avoid writing the test code in C, you can use the Python bindings, and/or the *fiu-run* and *fiu-ctrl* utilities. libfiu-0.94/doc/posix.rst000066400000000000000000000074171256346541500154050ustar00rootroot00000000000000 Simulating failures in the POSIX API ==================================== When developing robust sofware, developers often consider the cases when the classic POSIX functions return failure. Testing that fault-handling code is a problem because under normal conditions it's hard to make the POSIX functions fail, and generating abnormal conditions is usually difficult. For example, getting *malloc()* to fail can represent using up all your memory, and at that point your test case might not even work. Or getting I/O operations to fail might involve filling up the disk which is very undesirable, or generating a very special environment which is difficult to reproduce. libfiu comes with some tools that can be used to perform fault injection in the POSIX API (which includes the C standard library functions) *without* having to modify the application's source code, that can help to simulate scenarios like the ones described above in an easy and reproducible way. fiu-run ------- The first of those tools is an application called *fiu-run*. Suppose you want to run the classic program "fortune" (which some would definitely consider mission critical) and see how it behaves on the presence of *read()* errors. With *fiu-run*, you can do it like this:: $ fiu-run -x -c "enable_random name=posix/io/rw/read,probability=0.05" fortune That enables the failure point with the name *posix/io/rw/read* with 5% probability to fail *on each call*, and then runs fortune. The *-x* parameter tells *fiu-run* to enable fault injection in the POSIX API. Run it several times and you can see that sometimes it works, but sometimes it doesn't, reporting an error reading, which means a *read()* failed as expected. When fortune is run, every *read()* has a 5% chance to fail, selecting an *errno* at random from the list of the ones that read() is allowed to return. If you want to select an specific *errno*, you can do it by passing its numerical value using the *-i* parameter. The name of the failure points are fixed, and there is at least one for each function that libfiu supports injecting failures to. Not all POSIX functions are included, but most of the important pieces are, and it can be easily extended. See below for details. To see the list of supported functions and names, see the (automatically generated) *preload/posix/function_list* file that comes in the libfiu tarball. fiu-ctrl -------- Sometimes it is more interesting to simulate failures at a given point in time instead of from the beginning, as *fiu-run* does. To that end, you can combine *fiu-run* with the second tool, called *fiu-ctrl*. Let's suppose we want to see what the "top" program does when it can't open files. First, we run it with *fiu-run*:: $ fiu-run -x top Everything should look normal. Then, in another terminal, we make *open()* fail unconditionally:: $ fiu-ctrl -c "enable name=posix/io/oc/open" `pidof top` After that moment, the top display will probably be empty, because it can't read process information. Now let's disable that failure point, so *open()* works again:: $ fiu-ctrl -c "disable name=posix/io/oc/open" `pidof top` And everything should have gone back to normal. How does it work ---------------- libfiu comes with two preload libraries: *fiu_run_preload* and *fiu_posix_preload*. The first one is loaded using *LD_PRELOAD* (see *ld.so(8)* for more information) by *fiu-run*, and can enable failure points and start libfiu's remote control capabilities before the program begins to run. The second one is also loaded using *LD_PRELOAD* by *fiu-run* when the *-x* parameter is given, and provides libfiu-enabled wrappers for the POSIX functions, allowing the user to inject failures in them. *fiu-ctrl* communicates with the applications launched by *fiu-run* via the libfiu remote control capabilities. libfiu-0.94/doc/remote_control.rst000066400000000000000000000031001256346541500172570ustar00rootroot00000000000000 Remote control ============== The library has remote controlling capabilities, so external, unrelated processes can enable and disable failure points. It has a very simple request/reply protocol that can be performed over different transports. At the moment, the only transport available is named pipes. Remote control must be enabled by the controlled process using *fiu_rc_fifo()* (for named pipes). A set of utilities are provided to enable remote control without having to alter the application's source code, which can be useful for performing fault injection in libraries, see *fiu-run* and *fiu-ctrl* for more information. Remote control protocol ----------------------- It is a line based request/reply protocol. Lines end with a newline character (no carriage return). A request is composed of a command and 0 or more parameters, separated with a single space. The following commands are supported at the moment: - ``enable [flags]`` - ``enable_random [flags]`` - ``disable `` Where: - *name* is the name of the point of failure (which, at the moment, cannot have spaces inside). - *failnum* the same as the *failnum* parameter of *fiu_enable()* (see the manpage for more details). - failinfo the same as the *failinfo* parameter of *fiu_enable()* (see the manpage for more details). - *flags* can be either absent or ``one``, which has the same meaning as passing ``FIU_ONETIME`` in the *flags* parameter to *fiu_enable()*. The reply is always a number: 0 on success, < 0 on errors. libfiu-0.94/libfiu/000077500000000000000000000000001256346541500142055ustar00rootroot00000000000000libfiu-0.94/libfiu/Makefile000066400000000000000000000072571256346541500156600ustar00rootroot00000000000000 CFLAGS += -std=c99 -pedantic -Wall ALL_CFLAGS = -D_XOPEN_SOURCE=600 -fPIC -DFIU_ENABLE=1 $(CFLAGS) # Optimization CFLAGS, only used when doing a non-debug build. OPT_CFLAGS = -O3 ifdef DEBUG ALL_CFLAGS += -g else ifdef PROFILE ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage else ALL_CFLAGS += $(OPT_CFLAGS) endif endif ifdef TRACE ALL_CFLAGS += -DFIU_TRACE=1 endif # prefix for installing the binaries PREFIX=/usr/local # prefix for eventual location of binaries DESTDIR=$(PREFIX) # install utility, we assume it's GNU/BSD compatible INSTALL=install OBJS = fiu.o fiu-rc.o backtrace.o wtable.o hash.o ifneq ($(V), 1) NICE_CC = @echo " CC $@"; $(CC) else NICE_CC = $(CC) endif LIB_VER=0.94 LIB_SO_VER=0 # We attempt to detect if we have the extensions we use in backtrace.c by just # attempting to compile it normally, and fall back to the dummy version if # that fails. Not very sophisticated but should be safe. USE_DUMMY_BACKTRACE := $(shell $(CC) $(ALL_CFLAGS) -c backtrace.c \ -o backtrace-check.o || echo -DDUMMY_BACKTRACE) ifndef USE_DUMMY_BACKTRACE # The real backtrace needs linking against libdl. USE_LIBDL = -ldl endif default: all all: libs libfiu.pc libfiu.pc: build-flags libfiu.pc.in @echo "generating libfiu.pc" @cat libfiu.pc.in | \ sed 's@++DESTDIR++@$(DESTDIR)@g' | \ sed 's@++LIB_VER++@$(LIB_VER)@g' \ > libfiu.pc libs: libfiu.so libfiu.a libfiu.so: build-flags symbols.map fiu.h $(OBJS) $(NICE_CC) $(ALL_CFLAGS) -shared -fPIC \ -Wl,-soname,libfiu.so.$(LIB_SO_VER) \ -Wl,--version-script=symbols.map \ $(OBJS) -lpthread $(USE_LIBDL) -o libfiu.so.$(LIB_VER) ln -fs libfiu.so.$(LIB_VER) libfiu.so ln -fs libfiu.so.$(LIB_VER) libfiu.so.$(LIB_SO_VER) libfiu.a: build-flags fiu.h $(OBJS) $(AR) cr libfiu.a $(OBJS) install-lib: libs libfiu.pc $(INSTALL) -d $(PREFIX)/lib $(INSTALL) -m 0755 libfiu.so.$(LIB_VER) $(PREFIX)/lib ln -fs libfiu.so.$(LIB_VER) $(PREFIX)/lib/libfiu.so ln -fs libfiu.so.$(LIB_VER) $(PREFIX)/lib/libfiu.so.$(LIB_SO_VER) $(INSTALL) -m 0755 libfiu.a $(PREFIX)/lib $(INSTALL) -d $(PREFIX)/include $(INSTALL) -m 0644 fiu.h $(PREFIX)/include $(INSTALL) -m 0644 fiu-control.h $(PREFIX)/include $(INSTALL) -m 0644 fiu-local.h $(PREFIX)/include $(INSTALL) -d $(PREFIX)/lib/pkgconfig $(INSTALL) -m 644 libfiu.pc $(PREFIX)/lib/pkgconfig @echo @echo "Please run ldconfig to update your library cache" @echo install-man: $(INSTALL) -d $(PREFIX)/share/man/man3 $(INSTALL) -m 0644 libfiu.3 $(PREFIX)/share/man/man3/ install: install-lib install-man uninstall-lib: $(RM) $(PREFIX)/lib/libfiu.so $(RM) $(PREFIX)/lib/libfiu.so.$(LIB_VER) $(RM) $(PREFIX)/lib/libfiu.so.$(LIB_SO_VER) $(RM) $(PREFIX)/lib/libfiu.a $(RM) $(PREFIX)/include/fiu.h $(RM) $(PREFIX)/include/fiu-control.h $(RM) $(PREFIX)/include/fiu-local.h $(RM) $(PREFIX)/lib/pkgconfig/libfiu.pc uninstall-man: $(RM) $(PREFIX)/share/man/man3/libfiu.3 uninstall: uninstall-lib uninstall-man BF = $(ALL_CFLAGS) ~ $(PREFIX) build-flags: .force-build-flags @if [ x"$(BF)" != x"`cat build-flags 2>/dev/null`" ]; then \ if [ -f build-flags ]; then \ echo "build flags changed, rebuilding"; \ fi; \ echo "$(BF)" > build-flags; \ fi $(OBJS): build-flags .c.o: $(NICE_CC) $(ALL_CFLAGS) -c $< -o $@ backtrace.o: ALL_CFLAGS += $(USE_DUMMY_BACKTRACE) doxygen: $(MAKE) LIB_VER=$(LIB_VER) -C doxygen clean: rm -f libfiu.pc $(OBJS) backtrace-check.o rm -f libfiu.so libfiu.so.$(LIB_VER) libfiu.so.$(LIB_SO_VER) libfiu.a rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out build-flags $(MAKE) -C doxygen $@ .PHONY: default all libs \ install-lib install-man install \ uninstall-lib uninstall-man uninstall \ doxygen clean \ .force-build-flags libfiu-0.94/libfiu/backtrace.c000066400000000000000000000053051256346541500162730ustar00rootroot00000000000000 /* Since our implementation relies on nonstandard functions and headers, we * provide a dummy one below for compatibility purposes. The dummy version can * be selected at build time using -DDUMMY_BACKTRACE. */ #ifndef DUMMY_BACKTRACE /* This is needed for some of the functions below. */ #define _GNU_SOURCE #include /* NULL */ #include #include #include #include int get_backtrace(void *buffer, int size) { return backtrace(buffer, size); } void *get_func_start(void *pc) { int r; Dl_info info; r = dladdr(pc, &info); if (r == 0) return NULL; return info.dli_saddr; } void *get_func_end(void *func) { int r; Dl_info dl_info; ElfW(Sym) *elf_info; r = dladdr1(func, &dl_info, (void **) &elf_info, RTLD_DL_SYMENT); if (r == 0) return NULL; if (elf_info == NULL) return NULL; if (dl_info.dli_saddr == NULL) return NULL; return ((unsigned char *) func) + elf_info->st_size; } void *get_func_addr(const char *func_name) { return dlsym(RTLD_DEFAULT, func_name); } #else /* Dummy versions */ #warning Using dummy versions of backtrace #include /* for NULL */ int get_backtrace(void *buffer, int size) { return 0; } void *get_func_end(void *pc) { return NULL; } void *get_func_start(void *pc) { return NULL; } void *get_func_addr(const char *func_name) { return NULL; } #endif // DUMMY_BACKTRACE /* Ugly but useful conversion from function pointer to void *. * This is not guaranteed by the standard, but has to work on all platforms * where we support backtrace(), because that function assumes it so. */ static void *fp_to_voidp(void (*funcp)()) { unsigned char **p; p = (unsigned char **) &funcp; return *p; } int backtrace_works(void (*caller)()) { /* We remember the result so we don't have to compute it over an over * again, we know it doesn't change. */ static int works = -1; void *start = NULL; void *end = NULL; void *bt_buffer[100]; void *pc; int nptrs, i; /* Return the result if we know it. */ if (works >= 0) return works; nptrs = get_backtrace(bt_buffer, 100); if (nptrs <= 0) { works = 0; return works; } /* We will detect if it works by looking for the caller in the * backtrace. */ start = get_func_start(fp_to_voidp(caller)); end = get_func_end(fp_to_voidp(caller)); if (start == NULL && end == NULL) { works = 0; return works; } for (i = 0; i < nptrs; i++) { pc = bt_buffer[i]; /* On some platforms, we have everything except * get_func_end(), and that's ok. */ if (end) { if (pc >= start && pc <= end) { works = 1; return works; } } else { if (get_func_start(pc) == start) { works = 1; return works; } } } works = 0; return works; } libfiu-0.94/libfiu/doxygen/000077500000000000000000000000001256346541500156625ustar00rootroot00000000000000libfiu-0.94/libfiu/doxygen/Doxyfile.base.in000066400000000000000000000132731256346541500207140ustar00rootroot00000000000000DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = libfiu PROJECT_NUMBER = ++VERSION++ OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES TYPEDEF_HIDES_STRUCT = NO SYMBOL_CACHE_SIZE = 0 EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = NO SORT_BRIEF_DOCS = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = INPUT = ../ INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c *.h *.doxy RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = NO USE_HTAGS = NO VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES HTML_DYNAMIC_SECTIONS = YES GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = YES GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NONE TREEVIEW_WIDTH = 250 FORMULA_FONTSIZE = 10 GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = FIU_ENABLE EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES DOT_FONTNAME = FreeSans DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = YES CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES SEARCHENGINE = NO libfiu-0.94/libfiu/doxygen/Doxyfile.internal000066400000000000000000000002301256346541500211760ustar00rootroot00000000000000@INCLUDE = Doxyfile.base PROJECT_NAME = "libfiu (internal)" OUTPUT_DIRECTORY = doc.internal EXTRACT_ALL = YES EXTRACT_STATIC = YES INTERNAL_DOCS = YES libfiu-0.94/libfiu/doxygen/Doxyfile.public000066400000000000000000000002641256346541500206470ustar00rootroot00000000000000@INCLUDE = Doxyfile.base PROJECT_NAME = "libfiu (public)" OUTPUT_DIRECTORY = doc.public EXTRACT_ALL = YES EXTRACT_STATIC = NO INTERNAL_DOCS = NO INPUT = ../fiu.h ../fiu-control.h libfiu-0.94/libfiu/doxygen/Makefile000066400000000000000000000013171256346541500173240ustar00rootroot00000000000000 ifneq ($(V), 1) NICE_DOXYGEN = @echo " DOXYGEN $@"; doxygen else NICE_DOXYGEN = doxygen endif default: all all: public internal # $(LIB_VER) must be defined externally if we want the generated docs to # specify a version number. Usually, this Makefile will be invoked by # libfiu's, which has that variable properly defined. Doxyfile.base: Doxyfile.base.in @echo "generating Doxyfile.base" @cat Doxyfile.base.in | \ sed 's@++VERSION++@$(LIB_VER)@g' \ > Doxyfile.base public: Doxyfile.base $(NICE_DOXYGEN) Doxyfile.public internal: Doxyfile.base $(NICE_DOXYGEN) Doxyfile.internal clean: rm -rf doc.internal doc.public Doxyfile.base .PHONY: all clean default doxygen internal public libfiu-0.94/libfiu/fiu-control.h000066400000000000000000000124101256346541500166150ustar00rootroot00000000000000 /** @file * * This header contains the control API. * It should be used for controlling the injection of failures, usually when * writing tests. */ #ifndef _FIU_CONTROL_H #define _FIU_CONTROL_H #ifdef __cplusplus extern "C" { #endif /* Flags for fiu_enable*() */ /** Only fail once; the point of failure will be automatically disabled * afterwards. */ #define FIU_ONETIME 1 /** Enables the given point of failure unconditionally. * * @param name Name of the point of failure to enable. * @param failnum What will fiu_fail() return, must be != 0. * @param failinfo What will fiu_failinfo() return. * @param flags Flags. * @returns 0 if success, < 0 otherwise. */ int fiu_enable(const char *name, int failnum, void *failinfo, unsigned int flags); /** Enables the given point of failure, with the given probability. That makes * it fail with the given probability. * * @param name Name of the point of failure to enable. * @param failnum What will fiu_fail() return, must be != 0. * @param failinfo What will fiu_failinfo() return. * @param flags Flags. * @param probability Probability a fiu_fail() call will return failnum, * between 0 (never fail) and 1 (always fail). As a special fast case, -1 * can also be used to always fail. * @returns 0 if success, < 0 otherwise. */ int fiu_enable_random(const char *name, int failnum, void *failinfo, unsigned int flags, float probability); /** Type of external callback functions. * They must return 0 to indicate not to fail, != 0 to indicate otherwise. Can * modify failnum, failinfo and flags, in order to alter the values of the * point of failure. */ typedef int external_cb_t(const char *name, int *failnum, void **failinfo, unsigned int *flags); /** Enables the given point of failure, leaving the decision whether to fail * or not to the given external function. * * @param name Name of the point of failure to enable. * @param failnum What will fiu_fail() return, must be != 0. * @param failinfo What will fiu_failinfo() return. * @param flags Flags. * @param external_cb Function to call to determine whether to fail or not. * @returns 0 if success, < 0 otherwise. */ int fiu_enable_external(const char *name, int failnum, void *failinfo, unsigned int flags, external_cb_t *external_cb); /* Enables the given point of failure, but only if the given function is in * the stack at the given position. * * This function relies on GNU extensions such as backtrace() and dladdr(), so * it may not be available on your platform. * * - name: point of failure name. * - failnum: what will fiu_fail() return, must be != 0. * - failinfo: what will fiu_failinfo() return. * - flags: flags. * - func: pointer to the function. * - func_pos: position where we expect the function to be; use -1 for "any". * Values other than -1 are not supported at the moment, but will be in * the future. */ int fiu_enable_stack(const char *name, int failnum, void *failinfo, unsigned int flags, void *func, int func_pos_in_stack); /** Enables the given point of failure, but only if 'func_name' is in * the stack at 'func_pos_in_stack'. * * This function relies on GNU extensions such as backtrace() and dladdr(), so * if your platform does not support them, it will always return failure. * * It is exactly like fiu_enable_stack() but takes a function name, it will * ask the dynamic linker to find the corresponding function pointer. * * @param name Name of the point of failure to enable. * @param failnum What will fiu_fail() return, must be != 0. * @param failinfo What will fiu_failinfo() return. * @param flags Flags. * @param func_name Name of the function. * @param func_pos_in_stack Position where we expect the function to be; use * -1 for "any". Values other than -1 are not supported at the * moment, but will be in the future. * @returns 0 if success, < 0 otherwise. */ int fiu_enable_stack_by_name(const char *name, int failnum, void *failinfo, unsigned int flags, const char *func_name, int func_pos_in_stack); /** Disables the given point of failure. That makes it NOT fail. * * @param name Name of the point of failure to disable. * @returns 0 if success, < 0 otherwise. */ int fiu_disable(const char *name); /** Enables remote control over a named pipe. * * The name pipe path will begin with the given basename. "-$PID" will be * appended to it to form the final path. After the process dies, the pipe * will be removed. If the process forks, a new pipe will be created. * * Once this function has been called, the fiu-ctrl utility can be used to * control the points of failure externally. * * @param basename Base path to use in the creation of the named pipes. * @returns 0 on success, -1 on errors. */ int fiu_rc_fifo(const char *basename); /** Applies a remote control command given via a string. * * The format of the string is not stable and is still subject to change. * At the moment, this function is exported for consumption by the libfiu * utilities. * * @param cmd: A zero-terminated string with the command to apply. * @param error: In case of an error, it will point to a human-readable error * message. * @returns 0 if success, < 0 otherwise. */ int fiu_rc_string(const char *cmd, char ** const error); #ifdef __cplusplus } #endif #endif // _FIU_CONTROL_H libfiu-0.94/libfiu/fiu-local.h000066400000000000000000000015571256346541500162410ustar00rootroot00000000000000 /* libfiu - Fault Injection in Userspace * * This header, part of libfiu, is meant to be included in your project to * avoid having libfiu as a mandatory build-time dependency. * * You can add it to your project, and #include it instead of fiu.h. * The real fiu.h will be used only when FIU_ENABLE is defined. * * This header, as the rest of libfiu, is in the public domain. * * You can find more information about libfiu at * http://blitiri.com.ar/p/libfiu. */ #ifndef _FIU_LOCAL_H #define _FIU_LOCAL_H /* Only define the stubs when fiu is disabled, otherwise use the real fiu.h * header */ #ifndef FIU_ENABLE #define fiu_init(flags) 0 #define fiu_fail(name) 0 #define fiu_failinfo() NULL #define fiu_do_on(name, action) #define fiu_exit_on(name) #define fiu_return_on(name, retval) #else #include #endif /* FIU_ENABLE */ #endif /* _FIU_LOCAL_H */ libfiu-0.94/libfiu/fiu-rc.c000066400000000000000000000164601256346541500155450ustar00rootroot00000000000000 /* * libfiu remote control API */ #include /* snprintf() */ #include /* strncpy() */ #include /* malloc()/free() */ #include /* PATH_MAX */ #include /* getpid(), mkfifo() */ #include /* getpid() */ #include /* mkfifo() */ #include /* open() and friends */ #include /* pthread_create() and friends */ #include /* errno and friends */ /* Enable us, so we get the real prototypes from the headers */ #define FIU_ENABLE 1 #include "fiu-control.h" #include "internal.h" /* Max length of a line containing a control directive */ #define MAX_LINE 512 /* * Generic remote control */ /* Reads a line from the given fd, assumes the buffer can hold MAX_LINE * characters. Returns the number of bytes read, or -1 on error. Inefficient, * but we don't really care. The final '\n' will not be included. */ static int read_line(int fd, char *buf) { int r; char c; unsigned int len; c = '\0'; len = 0; memset(buf, 0, MAX_LINE); do { r = read(fd, &c, 1); if (r < 0) return -1; if (r == 0) break; len += r; *buf = c; buf++; } while (c != '\n' && c != '\0' && len < MAX_LINE); if (len > 0 && c == '\n') { *(buf - 1) = '\0'; len--; } return len; } /* Remote control command processing. * * Supported commands: * - disable name=N * - enable name=N,failnum=F,failinfo=I * - enable_random ,probability=P * - enable_stack_by_name ,func_name=F,pos_in_stack=P * * All enable* commands can also take an additional "onetime" parameter, * indicating that this should only fail once (analogous to the FIU_ONETIME * flag). * * This function is ugly, but we aim for simplicity and ease to extend for * future commands. */ int fiu_rc_string(const char *cmd, char ** const error) { char m_cmd[MAX_LINE]; char command[MAX_LINE], parameters[MAX_LINE]; /* We need a version of cmd we can write to for parsing */ strncpy(m_cmd, cmd, MAX_LINE); /* Separate command and parameters */ { char *tok = NULL, *state = NULL; tok = strtok_r(m_cmd, " \t", &state); if (tok == NULL) { *error = "Cannot get command"; return -1; } strncpy(command, tok, MAX_LINE); tok = strtok_r(NULL, " \t", &state); if (tok == NULL) { *error = "Cannot get parameters"; return -1; } strncpy(parameters, tok, MAX_LINE); } /* Parsing of parameters. * * To simplify the code, we parse the command parameters here. Not all * commands use all the parameters, but since they're not ambiguous it * makes it easier to do it this way. */ char *fp_name = NULL; int failnum = 1; void *failinfo = NULL; unsigned int flags = 0; double probability = -1; char *func_name = NULL; int func_pos_in_stack = -1; { /* Different tokens that we accept as parameters */ enum { OPT_NAME = 0, OPT_FAILNUM, OPT_FAILINFO, OPT_PROBABILITY, OPT_FUNC_NAME, OPT_POS_IN_STACK, FLAG_ONETIME, }; char * const token[] = { [OPT_NAME] = "name", [OPT_FAILNUM] = "failnum", [OPT_FAILINFO] = "failinfo", [OPT_PROBABILITY] = "probability", [OPT_FUNC_NAME] = "func_name", [OPT_POS_IN_STACK] = "pos_in_stack", [FLAG_ONETIME] = "onetime", NULL }; char *value; char *opts = parameters; while (*opts != '\0') { switch (getsubopt(&opts, token, &value)) { case OPT_NAME: fp_name = value; break; case OPT_FAILNUM: failnum = atoi(value); break; case OPT_FAILINFO: failinfo = (void *) strtoul(value, NULL, 10); break; case OPT_PROBABILITY: probability = strtod(value, NULL); break; case OPT_FUNC_NAME: func_name = value; break; case OPT_POS_IN_STACK: func_pos_in_stack = atoi(value); break; case FLAG_ONETIME: flags |= FIU_ONETIME; break; default: *error = "Unknown parameter"; return -1; } } } /* Excecute the command */ if (strcmp(command, "disable") == 0) { return fiu_disable(fp_name); } else if (strcmp(command, "enable") == 0) { return fiu_enable(fp_name, failnum, failinfo, flags); } else if (strcmp(command, "enable_random") == 0) { return fiu_enable_random(fp_name, failnum, failinfo, flags, probability); } else if (strcmp(command, "enable_stack_by_name") == 0) { return fiu_enable_stack_by_name(fp_name, failnum, failinfo, flags, func_name, func_pos_in_stack); } else { *error = "Unknown command"; return -1; } } /* Read remote control directives from fdr and process them, writing the * results in fdw. Returns the length of the line read, 0 if EOF, or < 0 on * error. */ static int rc_do_command(int fdr, int fdw) { int len, r, reply_len; char buf[MAX_LINE], reply[MAX_LINE]; char *error; len = read_line(fdr, buf); if (len <= 0) return len; r = fiu_rc_string(buf, &error); reply_len = snprintf(reply, MAX_LINE, "%d\n", r); r = write(fdw, reply, reply_len); if (r <= 0) return r; return len; } /* * Remote control via named pipes * * Enables remote control over a named pipe that begins with the given * basename. "-$PID.in" will be appended to it to form the final path to read * commands from, and "-$PID.out" will be appended to it to form the final * path to write the replies to. After the process dies, the pipe will be * removed. If the process forks, a new pipe will be created. */ static char npipe_basename[PATH_MAX]; static char npipe_path_in[PATH_MAX]; static char npipe_path_out[PATH_MAX]; static void *rc_fifo_thread(void *unused) { int fdr, fdw, r, errcount; /* increment the recursion count so we're not affected by libfiu, * otherwise we could make the remote control useless by enabling all * failure points */ rec_count++; errcount = 0; reopen: if (errcount > 10) { fprintf(stderr, "libfiu: Too many errors in remote control " "thread, shutting down\n"); return NULL; } fdr = open(npipe_path_in, O_RDONLY); if (fdr < 0) return NULL; fdw = open(npipe_path_out, O_WRONLY); if (fdw < 0) { close(fdr); return NULL; } for (;;) { r = rc_do_command(fdr, fdw); if (r < 0 && errno != EPIPE) { perror("libfiu: Error reading from remote control"); errcount++; close(fdr); close(fdw); goto reopen; } else if (r == 0 || (r < 0 && errno == EPIPE)) { /* one of the ends of the pipe was closed */ close(fdr); close(fdw); goto reopen; } } /* we never get here */ } static void fifo_atexit(void) { unlink(npipe_path_in); unlink(npipe_path_out); } static int _fiu_rc_fifo(const char *basename) { pthread_t thread; /* see rc_fifo_thread() */ rec_count++; snprintf(npipe_path_in, PATH_MAX, "%s-%d.in", basename, getpid()); snprintf(npipe_path_out, PATH_MAX, "%s-%d.out", basename, getpid()); if (mkfifo(npipe_path_in, 0600) != 0 && errno != EEXIST) { rec_count--; return -1; } if (mkfifo(npipe_path_out, 0600) != 0 && errno != EEXIST) { unlink(npipe_path_in); rec_count--; return -1; } if (pthread_create(&thread, NULL, rc_fifo_thread, NULL) != 0) { unlink(npipe_path_in); unlink(npipe_path_out); rec_count--; return -1; } atexit(fifo_atexit); rec_count--; return 0; } static void fifo_atfork_child(void) { _fiu_rc_fifo(npipe_basename); } int fiu_rc_fifo(const char *basename) { int r; r = _fiu_rc_fifo(basename); if (r < 0) return r; strncpy(npipe_basename, basename, PATH_MAX); pthread_atfork(NULL, NULL, fifo_atfork_child); return r; } libfiu-0.94/libfiu/fiu.c000066400000000000000000000264631256346541500151470ustar00rootroot00000000000000 #include /* malloc() and friends */ #include /* strcmp() and friends */ #include /* mutexes */ #include /* gettimeofday() */ #include /* gettimeofday() */ #include /* ULONG_MAX */ /* Enable us, so we get the real prototypes from the headers */ #define FIU_ENABLE 1 #include "fiu.h" #include "fiu-control.h" #include "internal.h" #include "wtable.h" /* Tracing mode for debugging libfiu itself. */ #ifdef FIU_TRACE #include #define trace(...) \ do { fprintf(stderr, __VA_ARGS__); fflush(stderr); } while (0) #else #define trace(...) do { } while(0) #endif /* Different methods to decide when a point of failure fails */ enum pf_method { PF_ALWAYS = 1, PF_PROB, PF_EXTERNAL, PF_STACK, }; /* Point of failure information */ struct pf_info { char *name; unsigned int namelen; int failnum; void *failinfo; unsigned int flags; /* Lock and failed flag, used only when flags & FIU_ONETIME. */ pthread_mutex_t lock; bool failed_once; /* How to decide when this point of failure fails, and the information * needed to take the decision */ enum pf_method method; union { /* To use when method == PF_PROB */ float probability; /* To use when method == PF_EXTERNAL */ external_cb_t *external_cb; /* To use when method == PF_STACK */ struct stack { void *func_start; void *func_end; int func_pos_in_stack; } stack; } minfo; }; /* Creates a new pf_info. * Only the common fields are filled, the caller should take care of the * method-specific ones. For internal use only. */ static struct pf_info *pf_create(const char *name, int failnum, void *failinfo, unsigned int flags, enum pf_method method) { struct pf_info *pf; rec_count++; pf = malloc(sizeof(struct pf_info)); if (pf == NULL) goto exit; pf->name = strdup(name); if (pf->name == NULL) { free(pf); pf = NULL; goto exit; } pf->namelen = strlen(name); pf->failnum = failnum; pf->failinfo = failinfo; pf->flags = flags; pf->method = method; /* These two are only use when flags & FIU_ONETIME. */ pthread_mutex_init(&pf->lock, NULL); pf->failed_once = false; exit: rec_count--; return pf; } static void pf_free(struct pf_info *pf) { free(pf->name); pthread_mutex_destroy(&pf->lock); free(pf); } /* Table used to keep the information about the enabled points of failure. * It is protected by enabled_fails_lock. */ wtable_t *enabled_fails = NULL; static pthread_rwlock_t enabled_fails_lock = PTHREAD_RWLOCK_INITIALIZER; #define ef_rlock() do { pthread_rwlock_rdlock(&enabled_fails_lock); } while (0) #define ef_wlock() do { pthread_rwlock_wrlock(&enabled_fails_lock); } while (0) #define ef_runlock() do { pthread_rwlock_unlock(&enabled_fails_lock); } while (0) #define ef_wunlock() do { pthread_rwlock_unlock(&enabled_fails_lock); } while (0) /* To prevent unwanted recursive calls that would deadlock, we use a * thread-local recursion count. Unwanted recursive calls can result from * using functions that have been modified to call fiu_fail(), which can * happen when using the POSIX preloader library: fiu_enable() takes the lock * for writing, and can call malloc() (for example), which can in turn call * fiu_fail() which can take the lock for reading. * * It is also modified at fiu-rc.c, to prevent failing within the remote * control thread. * * Sadly, we have to use the GNU extension for TLS, so we do not resort to * pthread_[get|set]specific() which could be wrapped. Luckily it's available * almost everywhere. */ __thread int rec_count = 0; /* Used to keep the last failinfo via TLS */ static pthread_key_t last_failinfo_key; /* * Miscelaneous internal functions */ /* Determines if the given address is within the function code. */ static int pc_in_func(struct pf_info *pf, void *pc) { /* We don't know if the platform allows us to know func_end, * so we use different methods depending on its availability. */ if (pf->minfo.stack.func_end) { return (pc >= pf->minfo.stack.func_start && pc <= pf->minfo.stack.func_end); } else { return pf->minfo.stack.func_start == get_func_start(pc); } } /* Determines wether to fail or not the given failure point, which is of type * PF_STACK. Returns 1 if it should fail, or 0 if it should not. */ static int should_stack_fail(struct pf_info *pf) { // TODO: Find the right offset for pos_in_stack: we should look for // fiu_fail(), and start counting from there. int nptrs, i; void *buffer[100]; nptrs = get_backtrace(buffer, 100); for (i = 0; i < nptrs; i++) { if (pc_in_func(pf, buffer[i]) && (pf->minfo.stack.func_pos_in_stack == -1 || i == pf->minfo.stack.func_pos_in_stack)) { return 1; } } return 0; } /* Pseudorandom number generator. * * The performance of the PRNG is very sensitive to us, so we implement our * own instead of just use drand48() or similar. * * As we don't really need a very good, thread-safe or secure random source, * we use an algorithm similar to the one used in rand() and drand48() (a * linear congruential generator, see * http://en.wikipedia.org/wiki/Linear_congruential_generator for more * information). Coefficients are the ones used in rand(), so we assume * sizeof(int) >= 4. * * To seed it, we use the current microseconds. To prevent seed reuse, we * re-seed after each fork (see atfork_child()). */ static unsigned int randd_xn = 0xA673F42D; static void prng_seed(void) { struct timeval tv; gettimeofday(&tv, NULL); randd_xn = tv.tv_usec; } static double randd(void) { randd_xn = 1103515245 * randd_xn + 12345; return (double) randd_xn / UINT_MAX; } /* Function that runs after the process has been forked, at the child. It's * registered via pthread_atfork() in fiu_init(). */ static void atfork_child(void) { prng_seed(); } /* * Core API */ /* Initializes the library. It should be safe to call this more than once at * any time, to allow several independant libraries to use fiu at the same * time without clashes. */ int fiu_init(unsigned int flags) { /* Used to avoid re-initialization, protected by enabled_fails_lock */ static int initialized = 0; rec_count++; ef_wlock(); if (initialized) { ef_wunlock(); rec_count--; return 0; } pthread_key_create(&last_failinfo_key, NULL); enabled_fails = wtable_create((void (*)(void *)) pf_free); if (pthread_atfork(NULL, NULL, atfork_child) != 0) { ef_wunlock(); rec_count--; return -1; } prng_seed(); initialized = 1; ef_wunlock(); rec_count--; return 0; } /* Returns the failure status of the given name. Must work well even before * fiu_init() is called assuming no points of failure are enabled; although it * can (and does) assume fiu_init() will be called before enabling any. */ int fiu_fail(const char *name) { struct pf_info *pf; int failnum; rec_count++; /* We must do this before acquiring the lock and calling any * (potentially wrapped) functions. */ if (rec_count > 1) { rec_count--; return 0; } ef_rlock(); /* It can happen that someone calls fiu_fail() before fiu_init(); we * don't want to crash so we just exit. */ if (enabled_fails == NULL) { goto exit; } pf = wtable_get(enabled_fails, name); /* None found. */ if (pf == NULL) { goto exit; } if (pf->flags & FIU_ONETIME) { pthread_mutex_lock(&pf->lock); if (pf->failed_once) { pthread_mutex_unlock(&pf->lock); goto exit; } /* We leave it locked so we don't accidentally fail this * point twice. */ } switch (pf->method) { case PF_ALWAYS: goto exit_fail; break; case PF_PROB: if (pf->minfo.probability > randd()) goto exit_fail; break; case PF_EXTERNAL: if (pf->minfo.external_cb(pf->name, &(pf->failnum), &(pf->failinfo), &(pf->flags))) goto exit_fail; break; case PF_STACK: if (should_stack_fail(pf)) goto exit_fail; break; default: break; } if (pf->flags & FIU_ONETIME) { pthread_mutex_unlock(&pf->lock); } exit: trace("FIU Not failing %s\n", name); ef_runlock(); rec_count--; return 0; exit_fail: trace("FIU Failing %s on %s\n", name, pf->name); pthread_setspecific(last_failinfo_key, pf->failinfo); failnum = pf->failnum; if (pf->flags & FIU_ONETIME) { pf->failed_once = true; pthread_mutex_unlock(&pf->lock); } ef_runlock(); rec_count--; return failnum; } /* Returns the information associated with the last fail. */ void *fiu_failinfo(void) { return pthread_getspecific(last_failinfo_key); } /* * Control API */ /* Inserts a pf into in the enabled_fails table. * Returns 0 on success, -1 on failure. */ static int insert_pf(struct pf_info *pf) { bool success; rec_count++; ef_wlock(); success = wtable_set(enabled_fails, pf->name, pf); ef_wunlock(); rec_count--; return success ? 0 : -1; } /* Makes the given name fail. */ int fiu_enable(const char *name, int failnum, void *failinfo, unsigned int flags) { struct pf_info *pf; pf = pf_create(name, failnum, failinfo, flags, PF_ALWAYS); if (pf == NULL) return -1; return insert_pf(pf); } /* Makes the given name fail with the given probability. */ int fiu_enable_random(const char *name, int failnum, void *failinfo, unsigned int flags, float probability) { struct pf_info *pf; pf = pf_create(name, failnum, failinfo, flags, PF_PROB); if (pf == NULL) return -1; pf->minfo.probability = probability; return insert_pf(pf); } /* Makes the given name fail when the external function returns != 0. */ int fiu_enable_external(const char *name, int failnum, void *failinfo, unsigned int flags, external_cb_t *external_cb) { struct pf_info *pf; pf = pf_create(name, failnum, failinfo, flags, PF_EXTERNAL); if (pf == NULL) return -1; pf->minfo.external_cb = external_cb; return insert_pf(pf); } /* Makes the given name fail when func is in the stack at func_pos. * If func_pos is -1, then any position will match. */ int fiu_enable_stack(const char *name, int failnum, void *failinfo, unsigned int flags, void *func, int func_pos_in_stack) { struct pf_info *pf; /* Specifying the stack position is unsupported for now */ if (func_pos_in_stack != -1) return -1; if (backtrace_works((void (*)()) fiu_enable_stack) == 0) return -1; pf = pf_create(name, failnum, failinfo, flags, PF_STACK); if (pf == NULL) return -1; pf->minfo.stack.func_start = func; /* Note get_func_end(func) can return NULL and we would still be able * to make it work, see pc_in_func() above. */ pf->minfo.stack.func_end = get_func_end(func); pf->minfo.stack.func_pos_in_stack = func_pos_in_stack; return insert_pf(pf); } /* Same as fiu_enable_stack(), but takes a function name. */ int fiu_enable_stack_by_name(const char *name, int failnum, void *failinfo, unsigned int flags, const char *func_name, int func_pos_in_stack) { void *fp; /* We need to check this here instead of relying on the test within * fiu_enable_stack() in case it is inlined; that would fail the check * because fiu_enable_stack() would not be in the stack. */ if (backtrace_works((void (*)()) fiu_enable_stack_by_name) == 0) return -1; fp = get_func_addr(func_name); if (fp == NULL) return -1; return fiu_enable_stack(name, failnum, failinfo, flags, fp, func_pos_in_stack); } /* Makes the given name NOT fail. */ int fiu_disable(const char *name) { bool success; rec_count++; /* Just find the point of failure and remove it. */ ef_wlock(); success = wtable_del(enabled_fails, name); ef_wunlock(); rec_count--; return success ? 0 : -1; } libfiu-0.94/libfiu/fiu.h000066400000000000000000000042171256346541500151450ustar00rootroot00000000000000 /** @file * * This header contains the API that your project should use when defining * points of failure in your real (non-testing) code. * * If you want to avoid having libfiu as a mandatory build-time dependency, * you should add fiu-local.h to your project, and \#include that instead. */ #ifndef _FIU_H #define _FIU_H /* Controls whether the external code enables libfiu or not. */ #ifdef FIU_ENABLE #ifdef __cplusplus extern "C" { #endif /** Initializes the library. * * Must be called before any other library function. It is safe to invoke it * more than once. * * @param flags Unused. * @returns 0 if success, < 0 if error. */ int fiu_init(unsigned int flags); /** Returns the failure status of the given point of failure. * * @param name Point of failure name. * @returns The failure status (0 means it should not fail). */ int fiu_fail(const char *name); /** Returns the information associated with the last failure. * * Please note that this function is thread-safe and thread-local, so the * information returned will be for the last failure in the same thread. * * @returns The information associated with the last failure, or NULL if * there isn't one. */ void *fiu_failinfo(void); /** Performs the given action when the given point of failure fails. Mostly * used in the following macros. */ #define fiu_do_on(name, action) \ do { \ if (fiu_fail(name)) { \ action; \ } \ } while (0) /** Exits the program when the given point of failure fails. */ #define fiu_exit_on(name) fiu_do_on(name, exit(EXIT_FAILURE)) /** Makes the function return the given retval when the given point of failure * fails. */ #define fiu_return_on(name, retval) fiu_do_on(name, return retval) #ifdef __cplusplus } #endif #else /* These are used when fiu not enabled. They should match fiu-local.h but we * don't include it to avoid a circular dependency. */ #define fiu_init(flags) 0 #define fiu_fail(name) 0 #define fiu_failinfo() NULL #define fiu_do_on(name, action) #define fiu_exit_on(name) #define fiu_return_on(name, retval) #endif /* FIU_ENABLE */ #endif /* _FIU_H */ libfiu-0.94/libfiu/hash.c000066400000000000000000000210471256346541500153000ustar00rootroot00000000000000 /* * Generic, simple hash table. * * Takes \0-terminated strings as keys, and void * as values. * It is tuned for a small number of elements (< 1000). * * It is NOT thread-safe. */ #include /* for size_t */ #include /* for [u]int*_t */ #include /* for bool */ #include /* for malloc() */ #include /* for memcpy()/memcmp() */ #include /* snprintf() */ #include /* read-write locks */ #include "hash.h" /* MurmurHash2, by Austin Appleby. The one we use. * It has been modify to fit into the coding style, to work on uint32_t * instead of ints, and the seed was fixed to a random number because it's not * an issue for us. The author placed it in the public domain, so it's ok to * use it here. * http://sites.google.com/site/murmurhash/ */ static uint32_t murmurhash2(const char *key, size_t len) { const uint32_t m = 0x5bd1e995; const int r = 24; const uint32_t seed = 0x34a4b627; // Initialize the hash to a 'random' value uint32_t h = seed ^ len; // Mix 4 bytes at a time into the hash while (len >= 4) { uint32_t k = *(uint32_t *) key; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; key += 4; len -= 4; } // Handle the last few bytes of the input array switch (len) { case 3: h ^= key[2] << 16; case 2: h ^= key[1] << 8; case 1: h ^= key[0]; h *= m; } // Do a few final mixes of the hash to ensure the last few // bytes are well-incorporated. h ^= h >> 13; h *= m; h ^= h >> 15; return h; } enum used_as { NEVER = 0, IN_USE = 1, REMOVED = 2, }; struct entry { char *key; void *value; enum used_as in_use; }; struct hash { struct entry *entries; size_t table_size; size_t nentries; void (*destructor)(void *); }; /* Minimum table size. */ #define MIN_SIZE 10 /* Dumb destructor, used to simplify the code when no destructor is given. */ static void dumb_destructor(void *value) { return; } struct hash *hash_create(void (*destructor)(void *)) { struct hash *h = malloc(sizeof(struct hash)); if (h == NULL) return NULL; h->entries = malloc(sizeof(struct entry) * MIN_SIZE); if (h->entries == NULL) { free(h); return NULL; } memset(h->entries, 0, sizeof(struct entry) * MIN_SIZE); h->table_size = MIN_SIZE; h->nentries = 0; if (destructor == NULL) destructor = dumb_destructor; h->destructor = destructor; return h; } void hash_free(struct hash *h) { size_t i; struct entry *entry; for (i = 0; i < h->table_size; i++) { entry = h->entries + i; if (entry->in_use == IN_USE) { h->destructor(entry->value); free(entry->key); } } free(h->entries); free(h); } void *hash_get(struct hash *h, const char *key) { size_t pos; struct entry *entry; pos = murmurhash2(key, strlen(key)) % h->table_size; for (;;) { entry = h->entries + pos; if (entry->in_use == NEVER) { /* We got to a entry never used, no match. */ return NULL; } else if (entry->in_use == IN_USE && strcmp(key, entry->key) == 0) { /* The key matches. */ return entry->value; } else { /* We use linear probing for now */ pos = (pos + 1) % h->table_size; } } return NULL; } /* Internal version of hash_set. * It uses the key as-is (it won't copy it), and it won't resize the array * either. */ static bool _hash_set(struct hash *h, char *key, void *value) { size_t pos; struct entry *entry; pos = murmurhash2(key, strlen(key)) % h->table_size; for (;;) { entry = h->entries + pos; if (entry->in_use != IN_USE) { entry->in_use = IN_USE; entry->key = key; entry->value = value; h->nentries++; return true; } else if (strcmp(key, entry->key) == 0) { /* The key matches, override the value. */ h->destructor(entry->value); entry->value = value; return true; } else { /* The key doesn't match, linear probing. */ pos = (pos + 1) % h->table_size; } } return false; } static bool resize_table(struct hash *h, size_t new_size) { size_t i; struct entry *old_entries, *e; size_t old_size; if (new_size < MIN_SIZE) { /* Do not resize below minimum size */ return true; } old_entries = h->entries; old_size = h->table_size; h->entries = malloc(sizeof(struct entry) * new_size); if (h->entries == NULL) return false; memset(h->entries, 0, sizeof(struct entry) * new_size); h->table_size = new_size; h->nentries = 0; /* Insert the old entries into the new table. We use the internal * version _hash_set() to avoid copying the keys again. */ for (i = 0; i < old_size; i++) { e = old_entries + i; if (e->in_use == IN_USE) _hash_set(h, e->key, e->value); } free(old_entries); return true; } bool hash_set(struct hash *h, const char *key, void *value) { if ((float) h->nentries / h->table_size > 0.7) { /* If we're over 70% full, grow the table by 30% */ if (!resize_table(h, h->table_size * 1.3)) return false; } return _hash_set(h, strdup(key), value); } bool hash_del(struct hash *h, const char *key) { size_t pos; struct entry *entry; pos = murmurhash2(key, strlen(key)) % h->table_size; for (;;) { entry = h->entries + pos; if (entry->in_use == NEVER) { /* We got to a never used key, not found. */ return false; } else if (entry->in_use == IN_USE && strcmp(key, entry->key) == 0) { /* The key matches, remove it. */ free(entry->key); h->destructor(entry->value); entry->key = NULL; entry->value = NULL; entry->in_use = REMOVED; break; } else { /* The key doesn't match, linear probing. */ pos = (pos + 1) % h->table_size; } } if (h->table_size > MIN_SIZE && (float) h->nentries / h->table_size < 0.5) { /* If we're over 50% free, shrink. */ if (!resize_table(h, h->table_size * 0.8)) return false; } return true; } /* Generic, simple cache. * * It is implemented using a hash table and manipulating it directly when * needed. * * It favours reads over writes, and it's very basic. * It IS thread-safe. */ struct cache { struct hash *hash; size_t size; pthread_rwlock_t lock; }; struct cache *cache_create() { struct cache *c; c = malloc(sizeof(struct cache)); if (c == NULL) return NULL; c->hash = hash_create(NULL); if (c->hash == NULL) { free(c); return NULL; } pthread_rwlock_init(&c->lock, NULL); return c; } void cache_free(struct cache *c) { hash_free(c->hash); pthread_rwlock_destroy(&c->lock); free(c); } /* Internal non-locking version of cache_invalidate(). */ static void _cache_invalidate(struct cache *c) { size_t i; struct entry *entry; for (i = 0; i < c->hash->table_size; i++) { entry = c->hash->entries + i; if (entry->in_use == IN_USE) { free(entry->key); entry->key = NULL; entry->value = NULL; entry->in_use = NEVER; } } } bool cache_invalidate(struct cache *c) { pthread_rwlock_wrlock(&c->lock); _cache_invalidate(c); pthread_rwlock_unlock(&c->lock); return true; } bool cache_resize(struct cache *c, size_t new_size) { pthread_rwlock_wrlock(&c->lock); if (new_size > c->size) { if (resize_table(c->hash, new_size)) { c->size = new_size; goto success; } } else { /* TODO: Implement shrinking. We just invalidate everything * for now, and then resize. */ _cache_invalidate(c); if (resize_table(c->hash, new_size)) { c->size = new_size; goto success; } } pthread_rwlock_unlock(&c->lock); return false; success: pthread_rwlock_unlock(&c->lock); return true; } struct entry *entry_for_key(struct cache *c, const char *key) { size_t pos; struct entry *entry; pos = murmurhash2(key, strlen(key)) % c->hash->table_size; entry = c->hash->entries + pos; return entry; } bool cache_get(struct cache *c, const char *key, void **value) { pthread_rwlock_rdlock(&c->lock); struct entry *e = entry_for_key(c, key); *value = NULL; if (e->in_use != IN_USE) goto miss; if (strcmp(key, e->key) == 0) { *value = e->value; goto hit; } else { goto miss; } hit: pthread_rwlock_unlock(&c->lock); return true; miss: pthread_rwlock_unlock(&c->lock); return false; } bool cache_set(struct cache *c, const char *key, void *value) { pthread_rwlock_wrlock(&c->lock); struct entry *e = entry_for_key(c, key); if (e->in_use == IN_USE) free(e->key); e->in_use = IN_USE; e->key = strdup(key); e->value = value; pthread_rwlock_unlock(&c->lock); return true; } bool cache_del(struct cache *c, const char *key) { pthread_rwlock_wrlock(&c->lock); struct entry *e = entry_for_key(c, key); if (e->in_use == IN_USE && strcmp(e->key, key) == 0) { free(e->key); e->key = NULL; e->value = NULL; e->in_use = REMOVED; pthread_rwlock_unlock(&c->lock); return true; } pthread_rwlock_unlock(&c->lock); return false; } libfiu-0.94/libfiu/hash.h000066400000000000000000000014311256346541500153000ustar00rootroot00000000000000 #ifndef _HASH_H #define _HASH_H /* Generic hash table. See hash.c for more information. */ #include /* for size_t */ #include /* for int64_t */ typedef struct hash hash_t; hash_t *hash_create(void (*destructor)(void *)); void hash_free(hash_t *h); void *hash_get(hash_t *h, const char *key); bool hash_set(hash_t *h, const char *key, void *value); bool hash_del(hash_t *h, const char *key); /* Generic cache. */ typedef struct cache cache_t; cache_t *cache_create(); bool cache_resize(struct cache *c, size_t new_size); void cache_free(cache_t *c); bool cache_get(cache_t *c, const char *key, void **value); bool cache_set(cache_t *c, const char *key, void *value); bool cache_del(cache_t *c, const char *key); bool cache_invalidate(cache_t *c); #endif libfiu-0.94/libfiu/internal.h000066400000000000000000000016271256346541500162000ustar00rootroot00000000000000 /* Some libfiu's internal declarations */ #ifndef _INTERNAL_H #define _INTERNAL_H /* Recursion count, used both in fiu.c and fiu-rc.c */ extern __thread int rec_count; /* Gets a stack trace. The pointers are stored in the given buffer, which must * be of the given size. The number of entries is returned. * It's a wrapper around glibc's backtrace(). */ int get_backtrace(void *buffer, int size); /* Returns a pointer to the start of the function containing the given code * address, or NULL if it can't find any. */ void *get_func_end(void *pc); /* Returns a pointer to the end of the given function. */ void *get_func_start(void *func); /* Returns a pointer to the function given by name. */ void *get_func_addr(const char *func_name); /* Do the above backtrace-related functions work? * Takes a pointer to the caller so it can verify it's on the stack. */ int backtrace_works(void (*caller)()); #endif libfiu-0.94/libfiu/libfiu.3000066400000000000000000000170001256346541500155410ustar00rootroot00000000000000.TH libfiu 3 "17/Feb/2009" .SH NAME libfiu - Fault injection in userspace .SH SYNOPSYS .nf .B /* Core API */ .B #include .sp .BI "int fiu_init(unsigned int " flags ");" .BI "int fiu_fail(const char *" name ");" .BI "void *fiu_failinfo(void);" .BI "[void] fiu_do_on(char *" name ", " action "); [macro]" .BI "[void] fiu_exit_on(char *" name "); [macro]" .BI "[void] fiu_return_on(char *" name ", " retval "); [macro]" .sp .B /* Control API */ .B #include .sp .BI "int fiu_enable(const char *" name ", int " failnum "," .BI " void *" failinfo ", unsigned int " flags ");" .BI "int fiu_enable_random(const char *" name ", int " failnum "," .BI " void *" failinfo ", unsigned int " flags ", float " probability ");" .BI "typedef int external_cb_t(const char *" name ", int *" failnum "," .BI " void **" failinfo ", unsigned int *" flags ");" .BI "int fiu_enable_external(const char *" name ", int " failnum "," .BI " void *" failinfo ", unsigned int " flags "," .BI " external_cb_t *" external_cb ");" .BI "int fiu_enable_stack(const char *" name ", int " failnum "," .BI " void *" failinfo ", unsigned int " flags "," .BI " void *" func ", int " func_pos_in_stack ");" .BI "int fiu_enable_stack_by_name(const char *" name ", int " failnum "," .BI " void *" failinfo ", unsigned int " flags "," .BI " const char *" func_name ", int " func_pos_in_stack ");" .BI "int fiu_disable(const char *" name ");" .BI "int fiu_rc_fifo(const char *" basename ");" .sp .fi .SH DESCRIPTION libfiu is a library for fault injection. It provides functions to mark "points of failure" inside your code (the "core API"), and functions to enable/disable the failure of those points (the "control API"). The core API is used inside the code wanting to perform fault injection on. The control API is used inside the testing code, in order to control the injection of failures. This page is an API reference and not a complete manual, and as such does not go into detail about how to use the library. The library's manual can be found in the distribution. .SS CORE API To use the core API, you should .I "#include " (or .IR "" , see below). Because fault injection is usually a debugging/testing facility, unwanted at runtime, some special considerations were taken to minimize the impact of the core API. First of all, if .I FIU_ENABLE is not defined, then fiu.h will define empty stubs for all the core API, effectively disabling fault injection completely. Also, the special header .I fiu-local.h is shipped with libfiu. It is meant to be included in your project to avoid having libfiu as a mandatory build-time dependency. You can add it to your project, and #include it instead of .IR fiu.h . It will take care of including the real .I fiu.h only when .I FIU_ENABLE is defined. It is entirely optional, but recommended. See the library's manual for more details. .TP .BI "fiu_init(" flags ")" Initializes the library. Ideally, you should only call this once, although it can cope with multiple calls. The flags parameter is currently unused and must be set to 0. Returns 0 on success, < 0 on error. .TP .BI "fiu_fail(" name ")" Returns the failure status of the given point of failure. 0 means it should not fail. By default, all points of failure do not fail; they're enabled in runtime using the control API. .TP .BI "fiu_failinfo()" Returns the information associated with the last failure, or NULL if there isn't one. This function is thread-aware and will only return information about failures in the calling thread. .TP .BI "fiu_do_on(" name ", " action ") [macro]" This is a macro that uses .B fiu_fail() to perform the given action when the given point of failure fails. The action can be any valid C statement. .TP .BI "fiu_exit_on(" name ") [macro]" This is a macro that uses .B fiu_fail() to exit the process when the given point of failure fails. The process is exit using exit(3), which is given the status EXIT_FAILURE. .TP .BI "fiu_return_on(" name ", " retval ") [macro]" This is a macro that uses .B fiu_fail() to make the current function return the given value (whose type obviously depends on the return type of the function). .SS CONTROL API To use the control API, you should .IR "#include " . .TP .BI "fiu_enable(" name ", " failnum ", " failinfo ", " flags ")" Enables the given point of failure. .I failnum is what .B fiu_fail() will return, and must be != 0. .I failinfo is what .B fiu_failinfo() will return when called after the given point of failure has failed. .I flags can be either 0 or .IR FIU_ONETIME , which indicates that this point of failure should only fail once. Returns 0 if success, < 0 otherwise. If the point of failure was already enabled, this overwrites the previous values. Successive calls to .B fiu_fail() will return .I failnum until this point of failure is disabled. If .I FIU_ONETIME was passed in the flags, this point of failure is disabled immediately after failing once. If the name ends with an asterisk, then it this will match all points of failure that begin with the given name (excluding the asterisk, of course). .TP .BI "fiu_enable_random(" name ", " failnum ", " failinfo ", " flags ", " probability ")" Enables the given point of failure, with the given probability (between 0 and 1). The rest of the parameters, as well as the return value, are the same as the ones in .BR fiu_enable() . .TP .BI "fiu_enable_external(" name ", " failnum ", " failinfo ", " flags ", " external_cb ")" Enables the given point of failure, leaving the decision whether to fail or not to the given external function, which should return 0 if it is not to fail, or 1 otherwise. The rest of the parameters, as well as the return value, are the same as the ones in .BR fiu_enable() . .TP .BI "int fiu_enable_stack(" name ", " failnum ", " failinfo ", " flags ", " func ", " func_pos_in_stack ")" Enables the given point of failure, but only if .I func is in the stack at .IR func_pos_in_stack . .I func must be a function pointer, and .I func_pos_in_stack is the position where we expect the function to be, or -1 for "any" (values other than -1 are not yet supported). The rest of the parameters, as well as the return value, are the same as the ones in .BR fiu_enable() . This function relies on some GNU extensions, so it may be not available in all platforms. .TP .BI "int fiu_enable_stack_by_name(" name ", " failnum ", " failinfo ", " flags ", " func_name ", " func_pos_in_stack ")" Enables the given point of failure, but only if .I func_name is in the stack at .IR func_pos_in_stack . .I func must be the name of a function (resolved at runtime using dlsym()); the rest of the parameters, as well as the return value, are the same as the ones in .BR fiu_enable_stack . This function relies on some GNU extensions, so it may be not available in all platforms. .TP .BI "fiu_disable(" name ")" Disables the given point of failure, undoing the actions of the .B fiu_enable*() functions. .TP .BI "fiu_rc_fifo(" basename ")" Enables remote control over named pipes with the given basename. See the remote control documentation that comes with the library for more detail. .SS THREAD SAFETY The library is thread-safe. The list of enabled failure points is shared among all threads. Care should be taken in the user-provided functions given to .BR fiu_enable_external() , as they can be run in parallel. .SH SEE ALSO .BR fiu-run (1), .BR fiu-ctrl (1). .SH BUGS If you want to report bugs, or have any questions or comments, just let me know at albertito@blitiri.com.ar. For more information about libfiu, you can go to http://blitiri.com.ar/p/libfiu. libfiu-0.94/libfiu/libfiu.pc.in000066400000000000000000000003511256346541500164070ustar00rootroot00000000000000 prefix=++DESTDIR++ libdir=${prefix}/lib includedir=${prefix}/include Name: libfiu Description: Fault injection in userspace URL: http://blitiri.com.ar/p/libfiu/ Version: ++LIB_VER++ Libs: -L${libdir} -lfiu Cflags: -I${includedir} libfiu-0.94/libfiu/symbols.map000066400000000000000000000003771256346541500164030ustar00rootroot00000000000000# Map of publicly exported symbols. { global: fiu_disable; fiu_enable; fiu_enable_external; fiu_enable_random; fiu_enable_stack; fiu_enable_stack_by_name; fiu_fail; fiu_failinfo; fiu_init; fiu_rc_fifo; fiu_rc_string; local: *; }; libfiu-0.94/libfiu/wtable.c000066400000000000000000000174321256346541500156360ustar00rootroot00000000000000 /* * Wildcard table. * * This is a simple key-value table that associates a key (\0-terminated * string) with a value (void *). * * The keys can end in a wildcard ('*'), and then a lookup will match them as * expected. * * The current implementation is a very simple dynamic array, which is good * enough for a small number of elements (< 100), but we should optimize this * in the future, in particular to speed up lookups. * * If more than one entry matches, we do not guarantee any order, although * this may change in the future. */ #include /* for size_t */ #include /* for [u]int*_t */ #include /* for bool */ #include /* for malloc() */ #include /* for memcpy()/memcmp() */ #include /* snprintf() */ #include "hash.h" #include "wtable.h" /* Entry of the wildcard array. */ struct wentry { char *key; size_t key_len; void *value; bool in_use; }; struct wtable { /* Final (non-wildcard) entries are kept in this hash. */ hash_t *finals; /* Wildcarded entries are kept in this dynamic array. */ struct wentry *wildcards; size_t ws_size; size_t ws_used_count; /* And we keep a cache of lookups into the wildcards array. */ cache_t *wcache; void (*destructor)(void *); }; /* Minimum table size. */ #define MIN_SIZE 10 struct wtable *wtable_create(void (*destructor)(void *)) { struct wtable *t = malloc(sizeof(struct wtable)); if (t == NULL) return NULL; t->wildcards = NULL; t->wcache = NULL; t->finals = hash_create(destructor); if (t->finals == NULL) goto error; t->wildcards = malloc(sizeof(struct wentry) * MIN_SIZE); if (t->wildcards == NULL) goto error; memset(t->wildcards, 0, sizeof(struct wentry) * MIN_SIZE); t->wcache = cache_create(); if (t->wcache == NULL) goto error; t->ws_size = MIN_SIZE; t->ws_used_count = 0; t->destructor = destructor; return t; error: if (t->finals) hash_free(t->finals); if (t->wcache) cache_free(t->wcache); free(t->wildcards); free(t); return NULL; } void wtable_free(struct wtable *t) { int i; struct wentry *entry; hash_free(t->finals); cache_free(t->wcache); for (i = 0; i < t->ws_size; i++) { entry = t->wildcards + i; if (entry->in_use) { t->destructor(entry->value); free(entry->key); } } free(t->wildcards); free(t); } /* Return the last position where s1 and s2 match. */ static unsigned int strlast(const char *s1, const char *s2) { unsigned int i = 0; while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2) { i++; s1++; s2++; } return i; } /* True if s is a wildcarded string, False otherwise. */ static bool is_wildcard(const char *s, size_t len) { /* Note we only support wildcards at the end of the string for now. */ return s[len - 1] == '*'; } /* Checks if ws matches s. * * ws is a "wildcard string", which can end in a '*', in which case we compare * only up to that position, to do a wildcard matching. */ static bool ws_matches_s( const char *ws, size_t ws_len, const char *s, size_t s_len, bool exact) { if (ws == NULL || s == NULL) return false; if (exact || !is_wildcard(ws, ws_len)) { /* Exact match */ if (ws_len != s_len) return false; return strcmp(ws, s) == 0; } else { /* Inexact match */ return strlast(ws, s) >= ws_len - 1; } } /* Find the entry matching the given key. * * If exact == true, then the key must match exactly (no wildcard matching). * If first_free is not NULL, then it will be made to point to the first free * entry found during the lookup. * * Returns a pointer to the entry, or NULL if not found. */ static struct wentry *wildcards_find_entry(struct wtable *t, const char *key, bool exact, struct wentry **first_free) { size_t key_len; size_t pos; struct wentry *entry; bool found_free = false; key_len = strlen(key); for (pos = 0; pos < t->ws_size; pos++) { entry = t->wildcards + pos; if (!entry->in_use) { if (!found_free && first_free) { *first_free = entry; found_free = true; } continue; } else if (ws_matches_s(entry->key, entry->key_len, key, key_len, exact)) { /* The key matches. */ return entry; } } /* No match. */ return NULL; } void *wtable_get(struct wtable *t, const char *key) { void *value; struct wentry *entry; /* Do an exact lookup first. */ value = hash_get(t->finals, key); if (value) return value; /* Then see if we can find it in the wcache */ if (cache_get(t->wcache, key, &value)) return value; /* And then walk the wildcards array. */ entry = wildcards_find_entry(t, key, false, NULL); if (entry) { cache_set(t->wcache, key, entry->value); return entry->value; } /* Cache the negative result as well. */ cache_set(t->wcache, key, NULL); return NULL; } /* Set on our wildcards table. * For internal use only. * It uses the key as-is (it won't copy it), and it won't resize the array * either. */ static bool wildcards_set(struct wtable *t, char *key, void *value) { struct wentry *entry, *first_free; first_free = NULL; entry = wildcards_find_entry(t, key, true, &first_free); if (entry) { /* Found a match, override the value. */ free(entry->key); entry->key = key; t->destructor(entry->value); entry->value = value; return true; } else if (first_free) { /* We are inserting a new entry. */ first_free->key = key; first_free->key_len = strlen(key); first_free->value = value; first_free->in_use = true; t->ws_used_count++; return true; } /* This should never happen, as we should always have space by the * time this function is called. */ return false; } static bool resize_table(struct wtable *t, size_t new_size) { size_t i; struct wentry *old_wildcards, *e; size_t old_size; if (new_size < MIN_SIZE) { /* Do not resize below the minimum size. */ return true; } old_wildcards = t->wildcards; old_size = t->ws_size; t->wildcards = malloc(sizeof(struct wentry) * new_size); if (t->wildcards == NULL) return false; memset(t->wildcards, 0, sizeof(struct wentry) * new_size); t->ws_size = new_size; t->ws_used_count = 0; /* Insert the old entries into the new table. */ for (i = 0; i < old_size; i++) { e = old_wildcards + i; if (e->in_use) { wildcards_set(t, e->key, e->value); } } free(old_wildcards); /* Keep the cache the same size as our table, which works reasonably * well in practise */ cache_resize(t->wcache, new_size); return true; } bool wtable_set(struct wtable *t, const char *key, void *value) { if (is_wildcard(key, strlen(key))) { if ((t->ws_size - t->ws_used_count) <= 1) { /* If we only have one entry left, grow by 30%, plus * one to make sure we always increase even if the * percentage isn't enough. */ if (!resize_table(t, t->ws_size * 1.3 + 1)) return false; } /* Invalidate the cache. We could be smart and walk it, * removing only the negative hits, but it's also more * expensive */ cache_invalidate(t->wcache); return wildcards_set(t, strdup(key), value); } else { return hash_set(t->finals, key, value); } } bool wtable_del(struct wtable *t, const char *key) { struct wentry *entry; if (is_wildcard(key, strlen(key))) { entry = wildcards_find_entry(t, key, true, NULL); if (!entry) { /* Key not found. */ return false; } /* Mark the entry as free. */ free(entry->key); entry->key = NULL; entry->key_len = 0; t->destructor(entry->value); entry->value = NULL; entry->in_use = false; t->ws_used_count--; /* Shrink if the table is less than 60% occupied. */ if (t->ws_size > MIN_SIZE && (float) t->ws_used_count / t->ws_size < 0.6) { if (!resize_table(t, t->ws_used_count + 3)) return false; } /* Invalidate the cache. We could be smart and walk it, * removing only the positive hits, but it's also more * expensive */ cache_invalidate(t->wcache); return true; } else { return hash_del(t->finals, key); } } libfiu-0.94/libfiu/wtable.h000066400000000000000000000010241256346541500156310ustar00rootroot00000000000000 /* Wildcard table. * * This is a simple key-value table that associates a key (\0-terminated * string) with a value (void *). * * See wtable.c for more information. */ #ifndef _WTABLE_H #define _WTABLE_H #include /* for bool */ typedef struct wtable wtable_t; wtable_t *wtable_create(void (*destructor)(void *)); void wtable_free(wtable_t *t); void *wtable_get(wtable_t *t, const char *key); bool wtable_set(wtable_t *t, const char *key, void *value); bool wtable_del(wtable_t *t, const char *key); #endif libfiu-0.94/preload/000077500000000000000000000000001256346541500143615ustar00rootroot00000000000000libfiu-0.94/preload/Makefile000066400000000000000000000010731256346541500160220ustar00rootroot00000000000000 default: all all: posix run install: posix_install run_install uninstall: posix_uninstall run_uninstall clean: posix_clean run_clean posix: $(MAKE) -C posix/ posix_clean: $(MAKE) -C posix/ clean posix_install: $(MAKE) -C posix/ install posix_uninstall: $(MAKE) -C posix/ uninstall run: $(MAKE) -C run/ run_clean: $(MAKE) -C run/ clean run_install: $(MAKE) -C run/ install run_uninstall: $(MAKE) -C run/ uninstall .PHONY: default clean install uninstall\ posix posix_clean posix_install posix_uninstall \ run run_clean run_install run_uninstall libfiu-0.94/preload/posix/000077500000000000000000000000001256346541500155235ustar00rootroot00000000000000libfiu-0.94/preload/posix/Makefile000066400000000000000000000065411256346541500171710ustar00rootroot00000000000000 CFLAGS += -std=c99 -Wall -O3 ALL_CFLAGS = -D_XOPEN_SOURCE=600 -fPIC -DFIU_ENABLE=1 \ -D_LARGEFILE64_SOURCE=1 -I. -I../../libfiu/ \ $(CFLAGS) ifdef DEBUG ALL_CFLAGS += -g endif ifdef PROFILE ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage endif ifdef POSIX_TRACE ALL_CFLAGS += -DFIU_POSIX_TRACE=1 endif # prefix for installing the binaries PREFIX=/usr/local # install utility, we assume it's GNU/BSD compatible INSTALL=install MODS = $(wildcard modules/*.mod) GEN_C = $(addsuffix .c,$(MODS)) GEN_OBJS = $(addsuffix .o,$(MODS)) GEN_FL = $(addsuffix .fl,$(MODS)) CUSTOM_OBJS = $(patsubst %.c,%.o,$(wildcard modules/*.custom.c)) OBJS = codegen.o $(GEN_OBJS) $(CUSTOM_OBJS) ifneq ($(V), 1) NICE_CC = @echo " CC $@"; $(CC) NICE_GEN = @echo " GEN $@"; ./generate Q = @ else NICE_CC = $(CC) NICE_GEN = ./generate Q = endif default: all all: fiu_posix_preload.so function_list BF = $(ALL_CFLAGS) ~ $(PREFIX) build-flags: .force-build-flags @if [ x"$(BF)" != x"`cat build-flags 2>/dev/null`" ]; then \ if [ -f build-flags ]; then \ echo "build flags changed, rebuilding"; \ fi; \ echo "$(BF)" > build-flags; \ fi $(GEN_OBJS): $(GEN_C) $(OBJS): build-flags codegen.h %.mod.c: %.mod $(NICE_GEN) $< $@ $<.fl .c.o: $(NICE_CC) $(ALL_CFLAGS) -c $< -o $@ # We define _GNU_SOURCE to get RTLD_NEXT if available; on non-GNU # platforms it should be harmless. codegen.o: codegen.c build-flags build-env.h $(NICE_CC) $(ALL_CFLAGS) -D_GNU_SOURCE -c $< -o $@ # some platforms do not have libdl, we only use it if available build-needlibdl: @$(LD) -ldl -o dlcheck.so 2>/dev/null \ && echo -ldl > $@ || echo > $@ @rm -f dlcheck.so # libc's soname depends on the platform (most use libc.so.6, but for example # ia64 and alpha use libc.so.6.1), so find which one to use at build-time. # Please note that the argument to ldd *must* start with "./", otherwise some # "ldd"s won't work (for example, the one in FreeBSD 8.1). build-libcsoname: @$(CC) -x c /dev/null -lc -shared -o build-libccheck.so @ldd ./build-libccheck.so | grep libc.so | awk '{ print $$1 }' > $@ @rm build-libccheck.so @test "`cat $@`" != "" || \ (echo "Error finding soname, please report"; rm $@; exit 1) build-env.h: build-env.h.in build-libcsoname @echo " GEN $@" $(Q) sed "s+@@LIBC_SONAME@@+`cat build-libcsoname`+g" build-env.h.in \ > build-env.h fiu_posix_preload.so: build-flags build-env.h build-needlibdl $(OBJS) $(NICE_CC) $(ALL_CFLAGS) -shared -fPIC $(OBJS) \ -L../../libfiu/ \ -lfiu `cat build-needlibdl` \ -o fiu_posix_preload.so # this should only be needed when building the function list and not the # preload library %.mod.fl: %.mod $(NICE_GEN) $< $<.c $@ function_list: $(GEN_FL) function_list.in @echo " function_list" $(Q) cp function_list.in function_list $(Q) for i in $(GEN_FL); do cat $$i >> function_list; done install: fiu_posix_preload.so $(INSTALL) -d $(PREFIX)/lib $(INSTALL) -m 0755 fiu_posix_preload.so $(PREFIX)/lib uninstall: $(RM) $(PREFIX)/lib/fiu_posix_preload.so clean: rm -f $(OBJS) $(GEN_OBJS:.o=.c) $(GEN_FL) rm -f build-flags build-env.h build-libcsoname build-needlibdl rm -f function_list fiu_posix_preload.so rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out rm -f modules/*.bb modules/*.bbg modules/*.da rm -f modules/*.gcov modules/*.gcda modules/*.gcno modules/gmon.out .PHONY: default install uninstall clean .force-build-flags libfiu-0.94/preload/posix/build-env.h.in000066400000000000000000000003101256346541500201600ustar00rootroot00000000000000 #ifndef _BUILD_ENV_H #define _BUILD_ENV_H /* * Constants taken from the build environment */ /* libc's soname, used for dlopen()ing the C library */ #define LIBC_SONAME "@@LIBC_SONAME@@" #endif libfiu-0.94/preload/posix/codegen.c000066400000000000000000000025411256346541500172750ustar00rootroot00000000000000 #include #include #include #include #include "codegen.h" #include "build-env.h" /* Recursion counter, per-thread */ int __thread _fiu_called = 0; /* Let the user know if there is no constructor priorities support, just in * case there are bugs when building/running without them */ #ifdef NO_CONSTRUCTOR_PRIORITIES #warning "Building without using constructor priorities" #endif /* Get a symbol from libc. * This function is a wrapper around dlsym(libc, ...), that we use to abstract * away how we get the libc wrapper, because on some platforms there are * better shortcuts. */ void *libc_symbol(const char *symbol) { #ifdef RTLD_NEXT return dlsym(RTLD_NEXT, symbol); #else /* We don't want to get this over and over again, so we set it once * and reuse it afterwards. */ static void *_fiu_libc = NULL; if (_fiu_libc == NULL) { _fiu_libc = dlopen(LIBC_SONAME, RTLD_NOW); if (_fiu_libc == NULL) { fprintf(stderr, "Error loading libc: %s\n", dlerror()); exit(1); } } return dlsym(_fiu_libc, symbol); #endif } /* this runs after all function-specific constructors */ static void constructor_attr(250) _fiu_init_final(void) { struct timeval tv; rec_inc(); fiu_init(0); /* since we use random() in the wrappers, we need to seed it */ gettimeofday(&tv, NULL); srandom(tv.tv_usec); rec_dec(); } libfiu-0.94/preload/posix/codegen.h000066400000000000000000000120761256346541500173060ustar00rootroot00000000000000 #ifndef _FIU_CODEGEN #define _FIU_CODEGEN #include /* fiu_* */ #include /* NULL, random() */ /* Recursion counter, per-thread */ extern int __thread _fiu_called; /* Get a symbol from libc */ void *libc_symbol(const char *symbol); /* Some compilers support constructor priorities. Since we don't rely on them, * but use them for clarity purposes, use a macro so libfiu builds on systems * where they're not supported. * Compilers that are known to support constructor priorities: * - GCC >= 4.3 on Linux * - clang as of 2010-03-14 */ #define _GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__ * 10) #if \ ( (defined __GNUC__) && _GCC_VERSION >= 4030 ) \ || (defined __clang__) #define constructor_attr(prio) __attribute__((constructor(prio))) #else #define NO_CONSTRUCTOR_PRIORITIES 1 #define constructor_attr(prio) __attribute__((constructor)) #endif /* Useful macros for recursion and debugging */ #ifndef FIU_POSIX_TRACE #define rec_inc() do { _fiu_called++; } while(0) #define rec_dec() do { _fiu_called--; } while(0) #define printd(...) do { } while(0) #else /* debug variants */ #include #include #define rec_inc() \ do { \ _fiu_called++; \ fprintf(stderr, "I: %d %s\n", _fiu_called, \ __FUNCTION__); \ fflush(stderr); \ } while (0) #define rec_dec() \ do { \ _fiu_called--; \ fprintf(stderr, "D: %d %s\n", _fiu_called, \ __FUNCTION__); \ fflush(stderr); \ } while (0) #define printd(...) \ do { \ if (_fiu_called) \ fprintf(stderr, "\t"); \ _fiu_called++; \ fprintf(stderr, "%6.6d ", getpid()); \ fprintf(stderr, "%s(): ", __FUNCTION__ ); \ fprintf(stderr, __VA_ARGS__); \ fflush(stderr); \ _fiu_called--; \ } while(0) #endif /* * Wrapper generator macros */ /* Generates the init part of the wrapped function */ #define mkwrap_init(RTYPE, NAME, PARAMS, PARAMST) \ static RTYPE (*_fiu_orig_##NAME) PARAMS = NULL; \ \ static int _fiu_in_init_##NAME = 0; \ \ static void constructor_attr(201) _fiu_init_##NAME(void) \ { \ rec_inc(); \ _fiu_in_init_##NAME++; \ \ _fiu_orig_##NAME = (RTYPE (*) PARAMST) \ libc_symbol(#NAME); \ \ _fiu_in_init_##NAME--; \ rec_dec(); \ } /* Generates the definition part of the wrapped function. */ #define mkwrap_def(RTYPE, NAME, PARAMS, PARAMST) \ RTYPE NAME PARAMS \ { \ RTYPE r; \ int fstatus; /* Generate the first part of the body, which checks the recursion status */ #define mkwrap_body_called(NAME, PARAMSN, ON_ERR) \ if (_fiu_called) { \ if (_fiu_orig_##NAME == NULL) { \ if (_fiu_in_init_##NAME) { \ printd("fail on init\n"); \ return ON_ERR; \ } else { \ printd("get orig\n"); \ _fiu_init_##NAME(); \ } \ } \ printd("orig\n"); \ return (*_fiu_orig_##NAME) PARAMSN; \ } \ \ printd("fiu\n"); \ \ /* fiu_fail() may call anything */ \ rec_inc(); /* Generates the common top for most functions (init + def + body called) */ #define mkwrap_top(RTYPE, NAME, PARAMS, PARAMSN, PARAMST, ON_ERR) \ mkwrap_init(RTYPE, NAME, PARAMS, PARAMST) \ mkwrap_def(RTYPE, NAME, PARAMS, PARAMST) \ mkwrap_body_called(NAME, PARAMSN, ON_ERR) /* Generates the body of the function for normal, non-errno usage. The return * value is taken from failinfo. */ #define mkwrap_body_failinfo(FIU_NAME, RTYPE) \ \ fstatus = fiu_fail(FIU_NAME); \ if (fstatus != 0) { \ r = (RTYPE) fiu_failinfo(); \ printd("failing\n"); \ goto exit; \ } /* Generates the body of the function for normal, non-errno usage. The return * value is hardcoded. */ #define mkwrap_body_hardcoded(FIU_NAME, FAIL_RET) \ \ fstatus = fiu_fail(FIU_NAME); \ if (fstatus != 0) { \ r = FAIL_RET; \ printd("failing\n"); \ goto exit; \ } /* Generates the body of the function for functions that affect errno. The * return value is hardcoded. Assumes int valid_errnos[] exist was properly * defined. */ #define mkwrap_body_errno(FIU_NAME, FAIL_RET) \ \ fstatus = fiu_fail(FIU_NAME); \ if (fstatus != 0) { \ void *finfo = fiu_failinfo(); \ if (finfo == NULL) { \ errno = valid_errnos[random() % \ sizeof(valid_errnos) / sizeof(int)]; \ } else { \ errno = (long) finfo; \ } \ r = FAIL_RET; \ printd("failing\n"); \ goto exit; \ } /* Generates a body part that will reduce the CNT parameter in a random * amount when the given point of failure is enabled. Can be combined with the * other body generators. */ #define mkwrap_body_reduce(FIU_NAME, CNT) \ \ fstatus = fiu_fail(FIU_NAME); \ if (fstatus != 0) { \ printd("reducing\n"); \ CNT -= random() % CNT; \ } #define mkwrap_bottom(NAME, PARAMSN) \ \ if (_fiu_orig_##NAME == NULL) \ _fiu_init_##NAME(); \ \ printd("calling orig\n"); \ r = (*_fiu_orig_##NAME) PARAMSN; \ \ exit: \ rec_dec(); \ return r; \ } #endif /* _FIU_CODEGEN */ libfiu-0.94/preload/posix/function_list.in000066400000000000000000000004701256346541500207340ustar00rootroot00000000000000 What follows is a list with the POSIX functions and their corresponding failure point names, as implemented in the preload library. It is not set in stone, although it shouldn't change frequently. Manually written ---------------- open posix/io/oc/open Automatically generated ----------------------- libfiu-0.94/preload/posix/generate000077500000000000000000000200351256346541500172430ustar00rootroot00000000000000#!/usr/bin/env python """ Reads function information and generates code for the preloader library. The code is NOT nice. It just does the trick. """ import sys import re import copy # Function definition regular expression func_def_re = re.compile( r'(?P(?:[\w\*]+\s+\**)+)+(?P\w+).*\((?P.*)\).*;') # Regular expression to extract the types and names of the parameters from a # string containing the definition parameters (e.g. from # "int a, const char *b" extracts [('int ', 'a'), ('const char *', 'b')] params_info_re = \ re.compile(r"(?:(?P(?:[\w\*]+\s+\**)+)+(?P\w+),?\s*)+?") class Context: """Represents the current context information within a module definition file.""" def __init__(self): self.fiu_name_base = 'UNKNOWN' class Function: "Represents a function to be wrapped" def __init__(self, definition, ctx): "Constructor, takes the C definition as a string" self.definition = definition self.load_from_definition(definition) # fiu name, constructed by default from the context but can be # overriden by info self.fiu_name = ctx.fiu_name_base + '/' + self.name # what to return on error, by default set to None, which means # "take it from failinfo" self.on_error = None # whether to set errno or not, and the list of valid errnos; # in any case if failinfo is set we take the errno value from # there self.use_errno = False self.valid_errnos = [] # if the given parameter should be reduced by a random amount self.reduce = None # describes possible variations of function, for example # pread() and pread64(). self.variants = [] def load_from_definition(self, definition): m = func_def_re.match(definition) self.name = m.group("name") self.ret_type = m.group("ret_type") self.params = m.group("params") self.params_info = params_info_re.findall(self.params) def load_info(self, info): "Loads additional information from the given string" if ':' in info: s = info.split(':', 1) k, v = s[0].strip(), s[1].strip() if k == 'fiu name': self.fiu_name = v elif k == 'on error': self.on_error = v elif k == 'valid errnos': self.use_errno = True self.valid_errnos = v.split() elif k == 'reduce': self.reduce = v elif k == 'variants': self.variants = v.split(); else: raise SyntaxError, \ "Unknown information: " + k def __repr__(self): s = '' % \ { 'rt': self.ret_type, 'n': self.name, 'p': self.params, 'fn': self.fiu_name, 'oe': self.on_error, 've': str(self.valid_errnos), } return s def generate_to(self, f): """Generates code to the given file. Strongly related to codegen.h.""" f.write('/* Wrapper for %s() */\n' % self.name) # extract params names and types paramst = ', '.join([i[0] for i in self.params_info]) paramsn = ', '.join([i[1] for i in self.params_info]) f.write('mkwrap_top(%s, %s, (%s), (%s), (%s), (%s))\n' % \ (self.ret_type, self.name, self.params, paramsn, paramst, self.on_error) ) if self.reduce: f.write('mkwrap_body_reduce("%s/reduce", %s)\n' % \ (self.fiu_name, self.reduce) ) if self.use_errno: if self.on_error is None: desc = "%s uses errno but has no on_error" % \ self.name raise RuntimeError, desc # We can't put this as a macro parameter, so it has to # be explicit self.write_valid_errnos(f) f.write('mkwrap_body_errno("%s", %s)\n' % \ (self.fiu_name, self.on_error) ) elif self.on_error is not None: f.write('mkwrap_body_hardcoded("%s", %s)\n' % \ (self.fiu_name, self.on_error) ) else: f.write('mkwrap_body_failinfo("%s", %s)\n' % \ (self.fiu_name, self.ret_type) ) f.write('mkwrap_bottom(%s, (%s))\n' % (self.name, paramsn)) f.write('\n\n') def write_valid_errnos(self, f): "Generates the code for the static list of valid errnos." f.write("\tstatic const int valid_errnos[] = {\n") for e in self.valid_errnos: f.write("\t #ifdef %s\n" % e) f.write("\t\t%s,\n" % e) f.write("\t #endif\n") f.write("\t};\n"); def fiu_names(self): n = [self.fiu_name] if self.reduce: n.append(self.fiu_name + '/reduce') return n def apply_variant(self, v): if v != 'off64_t': raise SyntaxError, "Unknown function variant: " + v f = copy.copy(self) # NOTE: We don't modify fiu_name here to be able to enable # both and 64 versions of the function by # enabling just . f.name = f.name + "64" f.params = f.params.replace("off_t", "off64_t") f.params_info = [ (x, y) if x != "off_t " else ("off64_t ", y) for (x, y) in f.params_info] # This is glibc-specific, so surround it with #ifdefs. return [Verbatim("#ifdef __GLIBC__"), f, Verbatim("#endif")] def get_all_variants(self): """Returns all variants of the given function provided via 'variants:' function directive""" variants = [self] for v in self.variants: variants.extend(self.apply_variant(v)) return variants class Include: "Represents an include directive" def __init__(self, path): self.path = path def __repr__(self): return '' % self.path def generate_to(self, f): f.write("#include %s\n" % self.path) class Verbatim: "Represent a verbatim directive" def __init__(self, line): self.line = line def __repr__(self): return '' % self.line def generate_to(self, f): f.write(self.line + '\n') class EmptyLine: "Represents an empty line" def __repr__(self): return '' def generate_to(self, f): f.write('\n') class Comment: "Represents a full-line comment" def __init__(self, line): self.body = line.strip()[1:].strip() def __repr__(self): return '' % self.body def generate_to(self, f): f.write("// %s \n" % self.body) def parse_module(path): "Parses a module definition" f = open(path) directives = [] ctx = Context() current_func = None while True: l = f.readline() # handle EOF if not l: break # handle \ at the end of the line while l.endswith("\\\n"): nl = f.readline() l = l[:-2] + nl if not l.strip(): directives.append(EmptyLine()) continue if l.strip().startswith("#"): directives.append(Comment(l)) continue if not l.startswith(" ") and not l.startswith("\t"): # either a new function or a directive, but in either # case the current function is done if current_func: directives.extend(current_func.get_all_variants()) current_func = None l = l.strip() if ':' in l: # directive s = l.split(':', 1) k, v = s[0].strip(), s[1].strip() if k == 'fiu name base': v = v.strip().strip('/') ctx.fiu_name_base = v elif k == 'include': directives.append(Include(v)) elif k == 'v': directives.append(Verbatim(v)) else: raise SyntaxError, \ ("Unknown directive", l) else: current_func = Function(l, ctx) else: # function information current_func.load_info(l.strip()) if current_func: directives.extend(current_func.get_all_variants()) return directives # # Code generation # # Templates gen_header = """ /* * AUTOGENERATED FILE - DO NOT EDIT * * This file was automatically generated by libfiu, do not edit it directly, * but see libfiu's "preload" directory. */ #include "codegen.h" """ def generate_code(directives, path): """Generates code to the file in the given path""" f = open(path, 'w') f.write(gen_header) for directive in directives: directive.generate_to(f) def write_function_list(directives, path): "Writes the function list to the given path" f = open(path, 'a') for d in directives: if isinstance(d, Function): f.write("%-32s%s\n" % (d.name, \ ', '.join(d.fiu_names())) ) def usage(): print "Use: ./generate input.mod output.c file_list.fl" def main(): if len(sys.argv) < 4: usage() sys.exit(1) input_name = sys.argv[1] output_name = sys.argv[2] filelist_name = sys.argv[3] directives = parse_module(input_name) #import pprint #pprint.pprint(directives) generate_code(directives, output_name) write_function_list(directives, filelist_name) if __name__ == '__main__': main() libfiu-0.94/preload/posix/modules/000077500000000000000000000000001256346541500171735ustar00rootroot00000000000000libfiu-0.94/preload/posix/modules/libc.mm.mod000066400000000000000000000005401256346541500212140ustar00rootroot00000000000000 include: include: fiu name base: libc/mm/ void *malloc(size_t size); on error: NULL valid errnos: ENOMEM void *calloc(size_t nmemb, size_t size); on error: NULL valid errnos: ENOMEM void *realloc(void *ptr, size_t size); on error: NULL valid errnos: ENOMEM # Note we don't wrap free() as it does not return anything. libfiu-0.94/preload/posix/modules/libc.str.mod000066400000000000000000000006131256346541500214140ustar00rootroot00000000000000 include: include: include: fiu name base: libc/str/ # glibc, when _GNU_SOURCE is defined, can have macros for strdup/strndup, so # we need to avoid those. v: #ifndef strdup char *strdup(const char *s); on error: NULL valid errnos: ENOMEM v: #endif v: #ifndef strndup char *strndup(const char *s, size_t n); on error: NULL valid errnos: ENOMEM v: #endif libfiu-0.94/preload/posix/modules/linux.io.mod000066400000000000000000000004261256346541500214430ustar00rootroot00000000000000 include: include: fiu name base: linux/io/ # sync_file_range() is linux-only v: #ifdef __linux__ int sync_file_range(int fd, off64_t offset, off64_t nbytes, \ unsigned int flags); on error: -1 valid errnos: EBADF EINVAL EIO ENOMEM ENOSPC v: #endif libfiu-0.94/preload/posix/modules/posix.custom.c000066400000000000000000000067261256346541500220250ustar00rootroot00000000000000 /* * Custom-made wrappers for some special POSIX functions. */ #include "codegen.h" #include #include #include #include #include #include /* Wrapper for open(), we can't generate it because it has a variable number * of arguments */ mkwrap_init(int, open, (const char *pathname, int flags, ...), (const char *, int, ...)) int open(const char *pathname, int flags, ...) { int r; int fstatus; /* Differences from the generated code begin here */ mode_t mode; va_list l; if (flags & O_CREAT) { va_start(l, flags); /* va_arg() can only take fully promoted types, and mode_t * sometimes is smaller than an int, so we should always pass * int to it, and not mode_t. Not doing so would may result in * a compile-time warning and run-time error. We asume that it * is never bigger than an int, which holds in practise. */ mode = va_arg(l, int); va_end(l); } else { /* set it to 0, it's ignored anyway */ mode = 0; } /* Differences from the generated code end here */ /* Use the normal macros to complete the function, now that we have * set mode to something */ mkwrap_body_called(open, (pathname, flags, mode), -1) static const int valid_errnos[] = { #ifdef EACCESS EACCES, #endif #ifdef EFAULT EFAULT, #endif #ifdef EFBIG EFBIG, #endif #ifdef EOVERFLOW EOVERFLOW, #endif #ifdef ELOOP ELOOP, #endif #ifdef EMFILE EMFILE, #endif #ifdef ENAMETOOLONG ENAMETOOLONG, #endif #ifdef ENFILE ENFILE, #endif #ifdef ENOENT ENOENT, #endif #ifdef ENOMEM ENOMEM, #endif #ifdef ENOSPC ENOSPC, #endif #ifdef ENOTDIR ENOTDIR, #endif #ifdef EROFS EROFS #endif }; mkwrap_body_errno("posix/io/oc/open", -1) mkwrap_bottom(open, (pathname, flags, mode)) /* The 64-bit variant for glibc. * The code is identical to open(), just using open64() where appropriate. */ #ifdef __GLIBC__ mkwrap_init(int, open64, (const char *pathname, int flags, ...), (const char *, int, ...)) int open64(const char *pathname, int flags, ...) { int r; int fstatus; /* Differences from the generated code begin here */ mode_t mode; va_list l; if (flags & O_CREAT) { va_start(l, flags); /* va_arg() can only take fully promoted types, and mode_t * sometimes is smaller than an int, so we should always pass * int to it, and not mode_t. Not doing so would may result in * a compile-time warning and run-time error. We asume that it * is never bigger than an int, which holds in practise. */ mode = va_arg(l, int); va_end(l); } else { /* set it to 0, it's ignored anyway */ mode = 0; } /* Differences from the generated code end here */ /* Use the normal macros to complete the function, now that we have * set mode to something */ mkwrap_body_called(open, (pathname, flags, mode), -1) static const int valid_errnos[] = { #ifdef EACCESS EACCES, #endif #ifdef EFAULT EFAULT, #endif #ifdef EFBIG EFBIG, #endif #ifdef EOVERFLOW EOVERFLOW, #endif #ifdef ELOOP ELOOP, #endif #ifdef EMFILE EMFILE, #endif #ifdef ENAMETOOLONG ENAMETOOLONG, #endif #ifdef ENFILE ENFILE, #endif #ifdef ENOENT ENOENT, #endif #ifdef ENOMEM ENOMEM, #endif #ifdef ENOSPC ENOSPC, #endif #ifdef ENOTDIR ENOTDIR, #endif #ifdef EROFS EROFS #endif }; mkwrap_body_errno("posix/io/oc/open", -1) mkwrap_bottom(open64, (pathname, flags, mode)) #endif libfiu-0.94/preload/posix/modules/posix.io.mod000066400000000000000000000164141256346541500214520ustar00rootroot00000000000000 # Posix I/O include: include: include: include: include: include: include: include: include: fiu name base: posix/io/oc/ # open() has its own custom wrapper int close(int fd); on error: -1 valid errnos: EBADFD EINTR EIO fiu name base: posix/io/sync/ int fsync(int fd); on error: -1 valid errnos: EBADFD EIO EROFS EINVAL int fdatasync(int fd); on error: -1 valid errnos: EBADFD EIO EROFS EINVAL fiu name base: posix/io/rw/ ssize_t read(int fd, void *buf, size_t count); on error: -1 valid errnos: EBADFD EFAULT EINTR EINVAL EIO EISDIR reduce: count ssize_t pread(int fd, void *buf, size_t count, off_t offset); on error: -1 valid errnos: EBADFD EFAULT EINTR EINVAL EIO EISDIR EOVERFLOW ENXIO reduce: count variants: off64_t ssize_t readv(int fd, const struct iovec *iov, int iovcnt); on error: -1 valid errnos: EBADFD EFAULT EINTR EINVAL EIO EISDIR reduce: iovcnt ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset); on error: -1 valid errnos: EBADFD EFAULT EINTR EINVAL EIO EISDIR EOVERFLOW ENXIO reduce: iovcnt variants: off64_t ssize_t write(int fd, const void *buf, size_t count); on error: -1 valid errnos: EBADFD EDQUOT EFAULT EFBIG EINTR EINVAL EIO ENOSPC reduce: count ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); on error: -1 valid errnos: EBADFD EDQUOT EFAULT EFBIG EINTR EINVAL EIO ENOSPC \ EOVERFLOW ENXIO reduce: count variants: off64_t ssize_t writev(int fd, const struct iovec *iov, int iovcnt); on error: -1 valid errnos: EBADFD EDQUOT EFAULT EFBIG EINTR EINVAL EIO ENOSPC reduce: iovcnt ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset); on error: -1 valid errnos: EBADFD EDQUOT EFAULT EFBIG EINTR EINVAL EIO ENOSPC \ EOVERFLOW ENXIO reduce: iovcnt variants: off64_t int truncate(const char *path, off_t length); on error: -1 valid errnos: EACCES EFAULT EFBIG EINTR EINVAL EIO EISDIR ELOOP \ ENAMETOOLONG ENOENT ENOTDIR EPERM EROFS ETXTBSY variants: off64_t int ftruncate(int fd, off_t length); on error: -1 valid errnos: EACCES EBADF EFAULT EFBIG EINTR EINVAL EIO EISDIR ELOOP \ ENAMETOOLONG ENOENT ENOTDIR EPERM EROFS ETXTBSY variants: off64_t fiu name base: posix/io/dir/ include: DIR *opendir(const char *name); on error: NULL valid errnos: EACCES EBADF EMFILE ENFILE ENOENT ENOMEM ENOTDIR DIR *fdopendir(int fd); on error: NULL valid errnos: EACCES EBADF EMFILE ENFILE ENOENT ENOMEM ENOTDIR struct dirent *readdir(DIR *dirp); on error: NULL valid errnos: EBADF int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); on error: 1 int closedir(DIR *dirp); on error: -1 valid errnos: EBADF int unlink(const char *pathname); on error: -1 valid errnos: EACCES EBUSY EFAULT EIO EISDIR ELOOP ENAMETOOLONG ENOENT \ ENOMEM ENOTDIR EPERM EROFS int rename(const char *oldpath, const char *newpath); on error: -1 valid errnos: EACCES EBUSY EFAULT EINVAL EISDIR ELOOP EMLINK ENAMETOOLONG \ ENOENT ENOMEM ENOSPC ENOTDIR ENOTEMPTY EPERM EROFS EXDEV # NOTE: These are commented because stat() and friends are usually defined as # either macros or inline functions within one of the standard headers, which # makes overriding them this way much harder. # #fiu name base: posix/io/stat/ # #v: #ifndef stat #int stat(const char *path, struct stat *buf); # on error: -1 # valid errnos: EACCES EBADF EFAULT ELOOP ENAMETOOLONG ENOENT ENOMEM \ # ENOTDIR EOVERFLOW #v: #endif # #v: #ifndef fstat #int fstat(int fd, struct stat *buf); # on error: -1 # valid errnos: EACCES EBADF EFAULT ELOOP ENAMETOOLONG ENOENT ENOMEM \ # ENOTDIR EOVERFLOW #v: #endif # #v: #ifndef lstat #int lstat(const char *path, struct stat *buf); # on error: -1 # valid errnos: EACCES EBADF EFAULT ELOOP ENAMETOOLONG ENOENT ENOMEM \ # ENOTDIR EOVERFLOW #v: #endif fiu name base: posix/io/net/ int socket(int domain, int type, int protocol); on error: -1 valid errnos: EAFNOSUPPORT EMFILE ENFILE EPROTONOSUPPORT EPROTOTYPE \ EACCES ENOBUFS ENOMEM int bind(int socket, const struct sockaddr *address, socklen_t address_len); on error: -1 valid errnos: EADDRINUSE EADDRNOTAVAIL EAFNOSUPPORT EBADF EINVAL ENOTSOCK \ EOPNOTSUPP EACCES EDESTADDRREQ EIO ELOOP ENAMETOOLONG ENOENT \ ENOTDIR EROFS EACCES EINVAL EISCONN ELOOP ENAMETOOLONG \ ENOBUFS int listen(int socket, int backlog); on error: -1 valid errnos: EBADF EDESTADDRREQ EINVAL ENOTSOCK EOPNOTSUPP EACCES EINVAL \ ENOBUFS int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); on error: -1 valid errnos: EAGAIN EBADF ECONNABORTED EINTR EINVAL EMFILE ENFILE \ ENOTSOCK EOPNOTSUPP ENOBUFS ENOMEM EPROTO int connect(int socket, const struct sockaddr *address, socklen_t address_len); on error: -1 valid errnos: EADDRNOTAVAIL EAFNOSUPPORT EALREADY EBADF ECONNREFUSED \ EINPROGRESS EINTR EISCONN ENETUNREACH ENOTSOCK EPROTOTYPE \ ETIMEDOUT EIO ELOOP ENAMETOOLONG ENOENT ENOTDIR EACCES \ EADDRINUSE ECONNRESET EHOSTUNREACH EINVAL ELOOP ENAMETOOLONG \ ENETDOWN ENOBUFS EOPNOTSUPP ssize_t recv(int socket, void *buffer, size_t length, int flags); on error: -1 valid errnos: EAGAIN EBADF ECONNRESET EINTR EINVAL ENOTCONN ENOTSOCK \ EOPNOTSUPP ETIMEDOUT EIO ENOBUFS ENOMEM ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len); on error: -1 valid errnos: EAGAIN EBADF ECONNRESET EINTR EINVAL ENOTCONN ENOTSOCK \ EOPNOTSUPP ETIMEDOUT EIO ENOBUFS ENOMEM ssize_t recvmsg(int socket, struct msghdr *message, int flags); on error: -1 valid errnos: EAGAIN EBADF ECONNRESET EINTR EINVAL EMSGSIZE ENOTCONN \ ENOTSOCK EOPNOTSUPP ETIMEDOUT EIO ENOBUFS ENOMEM ssize_t send(int socket, const void *buffer, size_t length, int flags); on error: -1 valid errnos: EAGAIN EBADF ECONNRESET EDESTADDRREQ EINTR EMSGSIZE \ ENOTCONN ENOTSOCK EOPNOTSUPP EPIPE EACCES EIO ENETDOWN \ ENETUNREACH ENOBUFS ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len); on error: -1 valid errnos: EAFNOSUPPORT EAGAIN EBADF ECONNRESET EINTR EMSGSIZE \ ENOTCONN ENOTSOCK EOPNOTSUPP EPIPE EIO ELOOP ENAMETOOLONG \ ENOENT ENOTDIR EACCES EDESTADDRREQ EHOSTUNREACH EINVAL EIO \ EISCONN ENETDOWN ENETUNREACH ENOBUFS ENOMEM ELOOP \ ENAMETOOLONG ssize_t sendmsg(int socket, const struct msghdr *message, int flags); on error: -1 valid errnos: EAGAIN EAFNOSUPPORT EBADF ECONNRESET EINTR EINVAL EMSGSIZE \ ENOTCONN ENOTSOCK EOPNOTSUPP EPIPE EIO ELOOP ENAMETOOLONG \ ENOENT ENOTDIR EACCES EDESTADDRREQ EHOSTUNREACH EIO EISCONN \ ENETDOWN ENETUNREACH ENOBUFS ENOMEM ELOOP ENAMETOOLONG int shutdown(int socket, int how); on error: -1 valid errnos: EBADF EINVAL ENOTCONN ENOTSOCK ENOBUFS int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, \ struct timeval *timeout); on error: -1 valid errnos: EBADF EINTR EINVAL ENOMEM int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, \ const struct timespec *timeout, const sigset_t *sigmask); on error: -1 valid errnos: EBADF EINTR EINVAL ENOMEM int poll(struct pollfd *fds, nfds_t nfds, int timeout); on error: -1 valid errnos: EBADF EFAULT EINTR EINVAL ENOMEM libfiu-0.94/preload/posix/modules/posix.mm.mod000066400000000000000000000031621256346541500214500ustar00rootroot00000000000000 include: include: include: fiu name base: posix/mm/ v: #ifdef _POSIX_MAPPED_FILES void *mmap(void *addr, size_t length, int prot, int flags, int fd, \ off_t offset); on error: MAP_FAILED valid errnos: EACCES EAGAIN EBADF EINVAL ENFILE ENODEV ENOMEM EPERM \ ETXTBSY variants: off64_t int munmap(void *addr, size_t length); on error: 0 valid errnos: EACCES EAGAIN EBADF EINVAL ENFILE ENODEV ENOMEM EPERM \ ETXTBSY int msync(void *addr, size_t length, int flags); on error: -1 valid errnos: EBUSY EINVAL ENOMEM # glibc's mprotect() does not use const in the first argument, as the standard # says it should v: #ifdef __GLIBC__ int mprotect(void *addr, size_t len, int prot); on error: -1 valid errnos: EACCES EINVAL ENOMEM v: #else int mprotect(const void *addr, size_t len, int prot); on error: -1 valid errnos: EACCES EINVAL ENOMEM v: #endif int madvise(void *addr, size_t length, int advice); on error: -1 valid errnos: EAGAIN EBADF EINVAL EIO ENOMEM v: #else v: #warning "no mmap() (and friends) wrappers available" v: #endif v: #ifdef _POSIX_MEMLOCK_RANGE int mlock(const void *addr, size_t len); on error: -1 valid errnos: ENOMEM EPERM EAGAIN EINVAL int munlock(const void *addr, size_t len); on error: -1 valid errnos: ENOMEM EPERM EAGAIN EINVAL v: #else v: #warning "no mlock()/munlock() wrappers available" v: #endif v: #ifdef _POSIX_MEMLOCK int mlockall(int flags); on error: -1 valid errnos: ENOMEM EPERM EINVAL int munlockall(void); on error: -1 valid errnos: ENOMEM EPERM v: #else v: #warning "no mlockall()/munlockall() wrappers available" v: #endif libfiu-0.94/preload/posix/modules/posix.proc.mod000066400000000000000000000020001256346541500217700ustar00rootroot00000000000000 include: include: include: include: include: fiu name base: posix/proc/ pid_t fork(void); on error: -1 valid errnos: EAGAIN ENOMEM pid_t wait(int *status); on error: -1 valid errnos: ECHILD EINTR EINVAL pid_t waitpid(pid_t pid, int *status, int options); on error: -1 valid errnos: ECHILD EINTR EINVAL # FreeBSD does not have waitid() v: #ifndef __FreeBSD__ int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); on error: -1 valid errnos: ECHILD EINTR EINVAL v: #endif int kill(pid_t pid, int sig); on error: -1 valid errnos: EINVAL EPERM ESRCH # We need to do this typedef because our parser is not smart enough to handle # the function definition without it v: typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); on error: SIG_ERR valid errnos: EINVAL int sigaction(int signum, const struct sigaction *act, \ struct sigaction *oldact); on error: -1 valid errnos: EFAULT EINVAL libfiu-0.94/preload/posix/utils/000077500000000000000000000000001256346541500166635ustar00rootroot00000000000000libfiu-0.94/preload/posix/utils/extract_from_man000077500000000000000000000066221256346541500221470ustar00rootroot00000000000000#!/usr/bin/env python # encoding: utf8 """ Extracts information from a manpage (read from stdin) that can be useful to create modules for the code generator. Example usage: man 3posix chmod | extract_from_man Or, in a loop: rm -f gen.mod; for f in chmod chown chdir; do man 3posix $f | extract_from_man >> gen.mod; done """ import sys import re def wrap(s, cols, indent = 1): ns = '' line = '' for w in s.split(): if len(line + ' ' + w) > cols: ns += line + ' \\\n' + '\t' * indent line = w else: if line: line += ' ' + w else: line = w ns += line return ns.rstrip() def extract_sections(f): "Reads a manpage from the file, returns a dictionary of sections." sec_name = '' sec_data = '' sections = {} for l in f: if not l.strip(): continue if l.startswith((' ', '\t')): sec_data += l else: sections[sec_name] = sec_data sec_name = l.strip() sec_data = '' sections[sec_name] = sec_data return sections def get_ret_on_error(sections): "Tries to find out what the function returns on error." if 'RETURN VALUE' not in sections: return None # remove spaces and newlines to make it easier detect the patterns s = ' '.join(sections['RETURN VALUE'].split()) print s # Note: the '(-|‐)' regexp matches both the normal minus sign ('-') # and the UTF-8 hypen sign ('‐', or \xe2\x80\x90); sadly both usually # look the same regexps = [ r'On error,? (?P[-\w]+) is returned', r'On error,? .* returns? (?P[-\w]+).', r'some error occurs,? (?P[-\w]+) is returned', r'and (?P[-\w]+) if an error occurr(s|ed)', r'[Oo]ther((-|‐) )?wise, (?P[-\w]+) shall be returned', r'Other((-|‐) )?wise, the functions shall return (?P[-\w]+) and' ] regexps = map(re.compile, regexps) possible_errors = [] for regexp in regexps: m = regexp.search(s) if m: possible_errors.append(m.group('ev')) return possible_errors def get_possible_errnos(sections): """Tries to find out the possible valid errno values after the function has failed.""" if 'ERRORS' not in sections: return None errnos = [] for l in sections['ERRORS'].split('\n'): m = re.match(r'\s+(?P([A-Z]{3,},? *)+)\s*', l) if m: s = m.group('e').strip() if not s: continue s = [ x.strip() for x in s.split(',') ] errnos.extend(s) return errnos def get_defs(sections): "Tries to find out the includes and function definitions." if 'SYNOPSIS' not in sections: return None includes = [] funcs = [] fre = re.compile(r'\s+(?P[\w,\*\s]+\(?(\w|,|\*|\s|\.\.\.)*\)?[,;])$') for l in sections['SYNOPSIS'].split('\n'): sl = l.strip() if sl.startswith('#include'): includes.append(sl.split(' ', 1)[1]) m = fre.match(l.rstrip()) if m: f = m.group('f') # long functions are split in multiple lines, this # tries to detect that and append to the last seen # function if funcs and not funcs[-1].endswith(';'): funcs[-1] += ' ' + f else: funcs.append(f) return (includes, funcs) if __name__ == '__main__': if len(sys.argv) > 1: print __doc__ sys.exit(1) s = extract_sections(sys.stdin) on_error = get_ret_on_error(s) errnos = get_possible_errnos(s) incs, funcs = get_defs(s) print '\n'.join( 'include: ' + i for i in incs) print print '\n'.join(funcs) if on_error: print '\ton error:', ' || '.join(on_error) if errnos: print '\tvalid errnos:', wrap(' '.join(sorted(set(errnos))), 60, indent = 2) libfiu-0.94/preload/run/000077500000000000000000000000001256346541500151655ustar00rootroot00000000000000libfiu-0.94/preload/run/Makefile000066400000000000000000000036111256346541500166260ustar00rootroot00000000000000 CFLAGS += -std=c99 -Wall -O3 ALL_CFLAGS = -D_XOPEN_SOURCE=600 -fPIC -DFIU_ENABLE=1 \ -I. -I../../libfiu/ $(CFLAGS) ifdef DEBUG ALL_CFLAGS += -g endif ifdef PROFILE ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage endif # prefix for installing the binaries PREFIX=/usr/local # location of preload libraries PLIBPATH=$(PREFIX)/lib # install utility, we assume it's GNU/BSD compatible INSTALL=install OBJS = run.o ifneq ($(V), 1) NICE_CC = @echo " CC $@"; $(CC) else NICE_CC = $(CC) endif default: all all: fiu_run_preload.so fiu-run BF = $(ALL_CFLAGS) ~ $(PREFIX) build-flags: .force-build-flags @if [ x"$(BF)" != x"`cat build-flags 2>/dev/null`" ]; then \ if [ -f build-flags ]; then \ echo "build flags changed, rebuilding"; \ fi; \ echo "$(BF)" > build-flags; \ fi $(OBJS): build-flags .c.o: $(NICE_CC) $(ALL_CFLAGS) -c $< -o $@ # some platforms do not have libdl, we only use it if available build-needlibdl: @$(LD) -ldl -o dlcheck.so 2>/dev/null \ && echo -ldl > $@ || echo > $@ @rm -f dlcheck.so fiu_run_preload.so: build-flags build-needlibdl $(OBJS) $(NICE_CC) $(ALL_CFLAGS) -shared -fPIC $(OBJS) \ -L../../libfiu/ \ -lfiu `cat build-needlibdl` \ -o fiu_run_preload.so fiu-run: build-flags fiu-run.in cat fiu-run.in | sed "s+@@PLIBPATH@@+$(PLIBPATH)+g" > fiu-run chmod +x fiu-run install: fiu_run_preload.so fiu-run $(INSTALL) -d $(PREFIX)/lib $(INSTALL) -m 0755 fiu_run_preload.so $(PREFIX)/lib $(INSTALL) -d $(PREFIX)/bin $(INSTALL) -m 0755 fiu-run $(PREFIX)/bin $(INSTALL) -d $(PREFIX)/share/man/man1 $(INSTALL) -m 0644 fiu-run.1 $(PREFIX)/share/man/man1/ uninstall: $(RM) $(PREFIX)/lib/fiu_run_preload.so $(RM) $(PREFIX)/bin/fiu-run $(RM) $(PREFIX)/share/man/man1/fiu-run.1 clean: rm -f $(OBJS) fiu_run_preload.so fiu-run build-flags rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out .PHONY: default install uninstall clean .force-build-flags libfiu-0.94/preload/run/fiu-run.1000066400000000000000000000050641256346541500166410ustar00rootroot00000000000000.TH fiu-run 1 "16/Jun/2009" .SH NAME fiu-run - a script to launch programs using libfiu .SH SYNOPSIS fiu-run [options] program [program arguments] .SH DESCRIPTION fiu-run is a script to make it easier to launch programs using \fBlibfiu\fR(3). It can enable failure points and start libfiu's remote control capabilities before starting to execute the program, avoiding the need to write a special launcher to inject failures. It is specially useful when used to inject failures in the POSIX/libc functions, because it does not require any program modifications. After launching programs with fiu-run, \fBfiu-ctrl\fR(1) can be used to enable and disable their failure points at runtme. For additional documentation, go to the project's website at .IR http://blitiri.com.ar/p/libfiu . .SH OPTIONS .TP .B "-c command" Run the given libfiu remote control command before executing the program (see below for reference). .TP .B -x Use the POSIX libfiu preload library, allows simulate failures in the POSIX and C standard library functions. .TP .B "-f ctrlpath" Enable remote control over named pipes with the given path as base name, the process id will be appended (defaults to "$TMPDIR/fiu-ctrl", or "/tmp/fiu-ctrl" if "$TMPDIR" is not set). Set to "" to disable remote control over named pipes. .TP .B "-l path" Path where to find the libfiu preload libraries. Defaults to the path where they were installed, so it is usually correct. .P Remote control commands are of the form "\fIcommand param1=value1,param2=value2\fR". Valid commands are: .TP .B 'enable name=NAME' Enables the NAME failure point unconditionally. .TP .B 'enable_random name=NAME,probability=P' Enables the NAME failure point with a probability of P. .P All of the \fIenable*\fR commands can also optionally take \fIfailnum\fR and \fIfailinfo\fR parameters, analogous to the ones taken by the C functions. .SH EXAMPLES The following will run the \fBfortune\fR(1) program simulating faults in read() with 5% probability (note that the \fB-x\fR parameter is required in this case to enable failure points in the POSIX and libc functions): .RS .nf fiu\-run \-x \-c "enable_random name=posix/io/rw/read,probability=0.05" fortune .fi .RE By running it multiple times you will see that sometimes it works, but most of the time you get different errors, resulting from the simulated failures. .SH SEE ALSO .BR libfiu (3), .BR fiu-ctrl (1). .SH BUGS If you want to report bugs, or have any questions or comments, just let me know at albertito@blitiri.com.ar. For more information about libfiu, you can go to http://blitiri.com.ar/p/libfiu. libfiu-0.94/preload/run/fiu-run.in000066400000000000000000000073561256346541500171150ustar00rootroot00000000000000#!/usr/bin/env bash # This script aims to make the use of the fiu_run_preload library a little # easier by providing a more friendly user interface, and abstracting the # environment variables used to communicate with it. # default remote control over named pipes prefix FIFO_PREFIX="${TMPDIR:-/tmp}/fiu-ctrl" # default library path to look for preloader libraries PLIBPATH="@@PLIBPATH@@" # the enable string to pass to the preload library (via the FIU_ENABLE # environment variable) ENABLE="" # additional preloader libraries to use PRELOAD_LIBS="" # use the POSIX preload library? USE_POSIX_PRELOAD=0 # don't run, but show the command line instead DRY_RUN=0 HELP_MSG=" Usage: fiu-run [options] program [arguments] The following options are supported: -x Use POSIX libfiu preload library, allows simulate failures in the POSIX and C standard library functions. -c command Run the given libfiu remote control command before executing the program (see below for reference). -f ctrlpath Enable remote control over named pipes with the given path as base name, the process id will be appended (defaults to \"$FIFO_PREFIX\", set to \"\" to disable). -l path Path where to find the libfiu preload libraries, defaults to $PLIBPATH (which is usually correct). Remote control commands are of the form 'command param1=value1,param2=value2'. Valid commands are: - 'enable name=NAME' Enables the NAME failure point unconditionally. - 'enable_random name=NAME,probability=P' Enables the NAME failure point with a probability of P. All of them can also optionally take 'failnum' and 'failinfo' parameters, analogous to the ones taken by the C functions. The following options existed in the past but are deprecated and WILL BE REMOVED in future releases: -e, -p, -u and -i. Example: fiu-run -x -c 'enable_random name=posix/io/*,probability=0.25' \\ -c 'enable_random name=libc/mm/*,probability=0.05' ls -l Run \"ls -l\" enabling all failure points that begin with 'posix/io/' with a 25% of probability to fail, and the failure point libc/mm/malloc with a 5% of probability to fail. " # # Parse the options # if [ $# -lt 1 ]; then echo "$HELP_MSG" exit 1 fi function opts_reset() { # variables to store what we know so far; after a new name is found # the old one is added to $ENABLE DEP_NAME="" DEP_PROB=-1 DEP_FAILNUM=1 DEP_FAILINFO=0 } function add_deprecated_enable() { if [ "$NAME" == "" ]; then return fi; PARAMS="name=$DEP_NAME,failnum=$DEP_FAILNUM,failinfo=$DEP_FAILINFO" if [ $PROB -ge 0 ]; then C="enable_random $PARAMS,probability=$PROB" else C="enable $PARAMS" fi ENABLE="$ENABLE $C" } opts_reset; while getopts "+c:f:l:xne:p:u:i:h" opt; do case $opt in c) # Note we use the newline as a command separator. ENABLE="$ENABLE $OPTARG" ;; f) FIFO_PREFIX="$OPTARG" ;; l) PLIBPATH="$OPTARG" ;; x) USE_POSIX_PRELOAD=1 ;; n) DRY_RUN=1 ;; # Deprecated options e) add_deprecated_enable opts_reset DEP_NAME="$OPTARG" ;; p) DEP_PROB="$OPTARG" ;; u) DEP_FAILNUM="$OPTARG" ;; i) DEP_FAILINFO="$OPTARG" ;; h|*) echo "$HELP_MSG" exit 1 ;; esac; done # add leftovers add_deprecated_enable opts_reset; # eat the parameters we already processed shift $(( $OPTIND - 1 )) # Allow user to give -l and -x in any order. if [ $USE_POSIX_PRELOAD -eq 1 ] ; then PRELOAD_LIBS="$PRELOAD_LIBS $PLIBPATH/fiu_posix_preload.so" fi # # Run the application # export FIU_ENABLE="$ENABLE" export FIU_CTRL_FIFO="$FIFO_PREFIX" export LD_PRELOAD="$PLIBPATH/fiu_run_preload.so $PRELOAD_LIBS" if [ $DRY_RUN -eq 1 ] ; then echo "FIU_ENABLE=\"$ENABLE\"" \\ echo "FIU_CTRL_FIFO=\"$FIFO_PREFIX\"" \\ echo "LD_PRELOAD=\"$PLIBPATH/fiu_run_preload.so $PRELOAD_LIBS\"" \\ echo "$@" else exec "$@" fi libfiu-0.94/preload/run/run.c000066400000000000000000000023461256346541500161420ustar00rootroot00000000000000 #include /* printf() */ #include /* execve() */ #include /* strtok(), memset(), strncpy() */ #include /* atoi(), atol() */ #include /* getopt() */ #include #include static void __attribute__((constructor)) fiu_run_init(void) { char *fiu_fifo_env, *fiu_enable_env; fiu_init(0); fiu_fifo_env = getenv("FIU_CTRL_FIFO"); if (fiu_fifo_env && *fiu_fifo_env != '\0') { if (fiu_rc_fifo(fiu_fifo_env) < 0) { perror("fiu_run_preload: Error opening RC fifo"); } } fiu_enable_env = getenv("FIU_ENABLE"); if (fiu_enable_env && fiu_enable_env != '\0') { /* FIU_ENABLE can contain more than one command, separated by * a newline, so we split them and call fiu_rc_string() * accordingly. */ char *tok, *state; char *env_copy; char *rc_error; env_copy = strdup(fiu_enable_env); if (env_copy == NULL) { perror("fiu_run_preload: Error in strdup()"); return; } tok = strtok_r(env_copy, "\n", &state); while (tok) { if (fiu_rc_string(tok, &rc_error) != 0) { fprintf(stderr, "fiu_run_preload: Error applying " "FIU_ENABLE commands: %s\n", rc_error); return; } tok = strtok_r(NULL, "\n", &state); } } } libfiu-0.94/tests/000077500000000000000000000000001256346541500140755ustar00rootroot00000000000000libfiu-0.94/tests/Makefile000066400000000000000000000044701256346541500155420ustar00rootroot00000000000000 CFLAGS += -std=c99 -pedantic -Wall -rdynamic ALL_CFLAGS = -I../libfiu/ -L../libfiu/ \ -D_XOPEN_SOURCE=600 -D_GNU_SOURCE -fPIC -DFIU_ENABLE=1 $(CFLAGS) ifdef DEBUG ALL_CFLAGS += -g endif ifdef PROFILE ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage endif ifneq ($(V), 1) NICE_CC = @echo " CC $@"; $(CC) NICE_RUN = @echo " RUN $<"; LD_LIBRARY_PATH=../libfiu/ NICE_PY = @echo " PY $<"; ./wrap-python 2 NICE_LN = @echo " LN $@"; ln -f else NICE_CC = $(CC) NICE_RUN = LD_LIBRARY_PATH=../libfiu/ NICE_PY = ./wrap-python 2 NICE_LN = ln -f endif default: tests all: tests tests: c-tests py-tests gen-tests utils-tests collisions-tests # Link the libraries to a single place, some of the tests need this. libs: mkdir -p libs/ libs/fiu_posix_preload.so: ../preload/posix/fiu_posix_preload.so libs $(NICE_LN) $< libs/ libs/fiu_run_preload.so: ../preload/run/fiu_run_preload.so libs $(NICE_LN) $< libs/ lnlibs: libs/fiu_posix_preload.so libs/fiu_run_preload.so # # C tests # C_SRCS := $(wildcard test-*.c) C_OBJS := $(patsubst %.c,%.o,$(C_SRCS)) C_BINS := $(patsubst %.c,%,$(C_SRCS)) c-tests: $(patsubst %.c,c-run-%,$(C_SRCS)) test-%: test-%.o build-flags $(NICE_CC) $(ALL_CFLAGS) $< -lfiu -lpthread -o $@ c-run-%: % $(NICE_RUN) ./$< BF = $(ALL_CFLAGS) ~ $(PREFIX) build-flags: .force-build-flags @if [ x"$(BF)" != x"`cat build-flags 2>/dev/null`" ]; then \ if [ -f build-flags ]; then \ echo "build flags changed, rebuilding"; \ fi; \ echo "$(BF)" > build-flags; \ fi .c.o: $(NICE_CC) $(ALL_CFLAGS) -c $< -o $@ # # Python tests # PY_TESTS := $(wildcard test-*.py) py-tests: $(patsubst %.py,py-run-%,$(PY_TESTS)) py-run-%: %.py lnlibs small-cat $(NICE_PY) ./$< small-cat: small-cat.c $(NICE_CC) $(ALL_CFLAGS) $< -o $@ # # Sub-directory tests # gen-tests: $(MAKE) -C generated utils-tests: $(MAKE) -C utils collisions-tests: $(MAKE) -C collisions # # Cleanup # # Normally, $C_OBJS and $C_BINS are removed by make after building, # since here they're considered "intermediate files"; however we # also remove them when cleaning just in case. clean: rm -f $(C_OBJS) $(C_BINS) rm -rf libs/ small-cat rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out build-flags $(MAKE) -C generated clean FORCE: .PHONY: default all clean \ tests c-tests py-tests gen-tests utils-tests \ .force-build-flags libfiu-0.94/tests/collisions/000077500000000000000000000000001256346541500162535ustar00rootroot00000000000000libfiu-0.94/tests/collisions/.gitignore000066400000000000000000000000251256346541500202400ustar00rootroot00000000000000binary libcoltest.so libfiu-0.94/tests/collisions/Makefile000066400000000000000000000023421256346541500177140ustar00rootroot00000000000000 CFLAGS += -std=c99 -pedantic -Wall -rdynamic ALL_CFLAGS = -I../../libfiu/ -L../../libfiu/ -L./ \ -D_XOPEN_SOURCE=600 -D_GNU_SOURCE -fPIC -DFIU_ENABLE=1 $(CFLAGS) ifdef DEBUG ALL_CFLAGS += -g endif ifdef PROFILE ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage endif ifneq ($(V), 1) NICE_CC = @echo " CC $@"; $(CC) NICE_RUN = @echo " RUN $<"; \ LD_LIBRARY_PATH="./:../../libfiu/" NICE_LN = @echo " LN $@"; ln -f else NICE_CC = $(CC) NICE_RUN = LD_LIBRARY_PATH="./:../../libfiu/" NICE_LN = ln -f endif default: tests all: tests BF = $(ALL_CFLAGS) ~ $(PREFIX) build-flags: .force-build-flags @if [ x"$(BF)" != x"`cat build-flags 2>/dev/null`" ]; then \ if [ -f build-flags ]; then \ echo "build flags changed, rebuilding"; \ fi; \ echo "$(BF)" > build-flags; \ fi # # Test library and binaries. # libcoltest.so: libcoltest.c libcoltest.h $(NICE_CC) $(ALL_CFLAGS) -shared -fPIC $< -o $@ binary: binary.c libcoltest.so $(NICE_CC) $(ALL_CFLAGS) -L. $< -lfiu -lcoltest -o $@ tests: binary $(NICE_RUN) ./binary # # Cleanup # clean: rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out build-flags rm -f libcoltest.so binary FORCE: .PHONY: default all clean \ tests c-tests py-tests \ .force-build-flags libfiu-0.94/tests/collisions/binary.c000066400000000000000000000017211256346541500177040ustar00rootroot00000000000000// A binary that uses some of the same function names as libfiu. // We use this to test function name collissions. #include // printf() #include "libcoltest.h" #define ASSERT_CALLED(NAME, N) \ if (called_##NAME != N) { \ printf("Error: " #NAME "called %d != " #N "\n", \ called_##NAME); \ return 1; \ } #define CHECK(NAME) \ ASSERT_CALLED(NAME, 0) \ NAME(); \ ASSERT_CALLED(NAME, 1) int called_wtable_set = 0; void wtable_set(void) { called_wtable_set++; } int main(void) { // Defined in libcoltest. CHECK(wtable_get) // Defined here. CHECK(wtable_set) return 0; } libfiu-0.94/tests/collisions/libcoltest.c000066400000000000000000000003001256346541500205540ustar00rootroot00000000000000// A library that uses some of the same function names as libfiu. // We use this to test function name collissions. int called_wtable_get = 0; void wtable_get(void) { called_wtable_get++; } libfiu-0.94/tests/collisions/libcoltest.h000066400000000000000000000001211256346541500205620ustar00rootroot00000000000000// Headers for libcoltest. extern int called_wtable_get; void wtable_get(void); libfiu-0.94/tests/generated/000077500000000000000000000000001256346541500160335ustar00rootroot00000000000000libfiu-0.94/tests/generated/Makefile000066400000000000000000000032101256346541500174670ustar00rootroot00000000000000 CFLAGS += -std=c99 -pedantic -Wall -rdynamic ALL_CFLAGS = -I../../libfiu/ -L../../libfiu/ \ -D_XOPEN_SOURCE=600 -D_GNU_SOURCE -fPIC -DFIU_ENABLE=1 $(CFLAGS) ifdef DEBUG ALL_CFLAGS += -g endif ifdef PROFILE ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage endif ifneq ($(V), 1) NICE_CC = @echo " CC $@"; $(CC) NICE_RUN = @echo " RUN $<"; \ LD_LIBRARY_PATH=../../libfiu/ \ LD_PRELOAD="../../preload/run/fiu_run_preload.so \ ../../preload/posix/fiu_posix_preload.so" NICE_GEN = @echo " GEN $@"; ./generate-test else NICE_CC = $(CC) NICE_RUN = LD_LIBRARY_PATH=../../libfiu/ \ LD_PRELOAD="../../preload/run/fiu_run_preload.so \ ../../preload/posix/fiu_posix_preload.so" NICE_GEN = ./generate-test endif default: tests all: tests CONF := $(wildcard tests/*.conf) GEN_BIN := $(patsubst %.conf,%.bin,$(CONF)) tests: $(patsubst %,%-run,$(GEN_BIN)) %-run: % $(NICE_RUN) ./$< # .bin from .c %.bin: %.c build-flags $(NICE_CC) $(ALL_CFLAGS) $< -lfiu -o $@ # .c from .conf %.c: %.conf generate-test $(NICE_GEN) -c $< -o $@ # Useful for manually generating the C-files, that otherwise get deleted as # intermediates. c-files: $(patsubst %.conf,%.c,$(CONF)) BF = $(ALL_CFLAGS) ~ $(PREFIX) build-flags: .force-build-flags @if [ x"$(BF)" != x"`cat build-flags 2>/dev/null`" ]; then \ if [ -f build-flags ]; then \ echo "build flags changed, rebuilding"; \ fi; \ echo "$(BF)" > build-flags; \ fi # # Cleanup # clean: rm -f $(GEN_BIN) $(patsubst %.bin,%.c,$(GEN_BIN)) rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out build-flags FORCE: .PHONY: default all clean \ tests c-tests py-tests \ .force-build-flags libfiu-0.94/tests/generated/README000066400000000000000000000014641256346541500167200ustar00rootroot00000000000000 These are autogenerated tests for testing the posix preload, to make sure the functions are getting properly wrapped. Each test is configured by a .conf file in the conf/ directory. These are the allowed options ('-' means mandatory, '*' optional): - fp: failure point corresponding to the function. - call: how to call the function. * prep: preparation steps (usually declaring variables). * include: list of files to #include, space separated. * success_cond: condition we expect to be true if the call succeeds. * failure_cond: condition we expect to be true after we simulate a failure (if not set, we will only validate that we simulated the failure). * errno_on_fail: simulate this errno on failure. It is mandatory that a single section is defined; however, the name is only for description purposes. libfiu-0.94/tests/generated/generate-test000077500000000000000000000064741256346541500205430ustar00rootroot00000000000000#!/usr/bin/env python """ Generate C testcases based on the given configuration file. It can be useful for testing many C functions in the same way, like when testing a wrapper. """ import sys import ConfigParser import argparse from string import Template def listify(x): if isinstance(x, (str, unicode)): return [x] return x TEST_SUCCESS_TMPL = r""" int success(void) { $prep fiu_disable("$fp"); $call if (! ($success_cond) ) { printf("$fp - success condition is false\n"); return -1; } return 0; } """ TEST_FAILURE_TMPL = r""" static int external_cb_was_called = 0; int external_cb(const char *name, int *failnum, void **failinfo, unsigned int *flags) { external_cb_was_called++; *failinfo = (void *) $errno_on_fail; return *failnum; } int failure(void) { $prep fiu_enable_external("$fp", 1, NULL, 0, external_cb); $call fiu_disable("$fp"); if (external_cb_was_called != 1) { printf("$fp - external callback not invoked\n"); return -1; } if (! ($errno_cond) ) { printf("$fp - errno not set appropriately: "); printf("errno:%d, cond:$errno_cond\n", errno); return -1; } if (! ($failure_cond) ) { printf("$fp - failure condition is false\n"); return -1; } return 0; } """ TEST_MAIN_TMPL = r""" int main(void) { int s, f; s = success(); f = failure(); return s + f; } """ TEST_SKIPPED_TMPL = r""" int main(void) { printf("$fp: skipping test\n"); return 0; } """ def generate(options, outfile): outfile.write("/* AUTOGENERATED FILE - DO NOT EDIT */\n\n") outfile.write("#include \n") outfile.write("#include \n") outfile.write("#include \n") outfile.write("#include \n") includes = options.get("include", []) if isinstance(includes, (str, unicode)): includes = includes.split() for i in includes: outfile.write("#include <%s>\n" % i) else: outfile.write("\n\n") if options['if']: outfile.write("#if %s\n" % options['if']) if 'errno_on_fail' in options: options['errno_cond'] = \ 'errno == %s' % options['errno_on_fail'] else: # Default the cond to true, and set failinfo to 0 in case it's # used. options['errno_cond'] = '1' options['errno_on_fail'] = '0' outfile.write(Template(TEST_SUCCESS_TMPL).substitute(options)) outfile.write(Template(TEST_FAILURE_TMPL).substitute(options)) outfile.write(Template(TEST_MAIN_TMPL).substitute(options)) if options['if']: outfile.write("#else\n") outfile.write(Template(TEST_SKIPPED_TMPL).substitute(options)) outfile.write("#endif\n") def main(): parser = argparse.ArgumentParser( description ="Generate C testcases") parser.add_argument("-c", "--conf", metavar = "C", required = True, type = file, help = "configuration file") parser.add_argument("-o", "--out", metavar = "F", required = True, help = "generated file") args = parser.parse_args() # Defaults for the optional configuration parameters. conf_defaults = { 'include': (), 'prep': '', 'if': '', # These are C conditions that are always true. 'success_cond': '1', 'failure_cond': '1', # For errno_on_fail, we have a smarter logic so don't do # anything. } conf = ConfigParser.SafeConfigParser(conf_defaults) conf.readfp(args.conf) section = conf.sections()[0] outfile = open(args.out, 'w') generate(dict(conf.items(section)), outfile) if __name__ == "__main__": main() libfiu-0.94/tests/generated/tests/000077500000000000000000000000001256346541500171755ustar00rootroot00000000000000libfiu-0.94/tests/generated/tests/kill.conf000066400000000000000000000002371256346541500210010ustar00rootroot00000000000000 [kill] fp: posix/proc/kill include: sys/types.h signal.h prep: int r; call: r = kill(0, 0); success_cond: r != -1 failure_cond: r == -1 errno_on_fail: ESRCH libfiu-0.94/tests/generated/tests/malloc.conf000066400000000000000000000002161256346541500213120ustar00rootroot00000000000000 [malloc] fp: libc/mm/malloc include: stdlib.h prep: void *p = NULL; call: p = malloc(8000); success_cond: p != NULL failure_cond: p == NULL libfiu-0.94/tests/generated/tests/mmap.conf000066400000000000000000000004241256346541500207760ustar00rootroot00000000000000 [mmap] fp: posix/mm/mmap include: sys/mman.h unistd.h if: defined _POSIX_MAPPED_FILES prep: void *p; call: p = mmap(NULL, 4092, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); success_cond: p != MAP_FAILED failure_cond: p == MAP_FAILED errno_on_fail: ETXTBSY libfiu-0.94/tests/generated/tests/open.conf000066400000000000000000000003041256346541500210020ustar00rootroot00000000000000 [open] fp: posix/io/oc/open include: sys/types.h sys/stat.h fcntl.h prep: int fd = -1; call: fd = open("/dev/null", O_RDONLY); success_cond: fd != -1 failure_cond: fd == -1 errno_on_fail: ELOOP libfiu-0.94/tests/generated/tests/open64.conf000066400000000000000000000003361256346541500211610ustar00rootroot00000000000000 [open64] if: defined __GLIBC__ fp: posix/io/oc/open include: sys/types.h sys/stat.h fcntl.h prep: int fd = -1; call: fd = open64("/dev/null", O_RDONLY); success_cond: fd != -1 failure_cond: fd == -1 errno_on_fail: ELOOP libfiu-0.94/tests/generated/tests/pread.conf000066400000000000000000000004031256346541500211340ustar00rootroot00000000000000 [pread] fp: posix/io/rw/pread include: unistd.h sys/types.h sys/stat.h fcntl.h prep: unsigned char buf[1024]; ssize_t r; int fd = open("/dev/zero", O_RDONLY); call: r = pread(fd, buf, 1024, 0); success_cond: r != -1 failure_cond: r == -1 errno_on_fail: EIO libfiu-0.94/tests/generated/tests/pread64.conf000066400000000000000000000004351256346541500213130ustar00rootroot00000000000000 [pread64] if: defined __GLIBC__ fp: posix/io/rw/pread include: unistd.h sys/types.h sys/stat.h fcntl.h prep: unsigned char buf[1024]; ssize_t r; int fd = open("/dev/zero", O_RDONLY); call: r = pread64(fd, buf, 1024, 0); success_cond: r != -1 failure_cond: r == -1 errno_on_fail: EIO libfiu-0.94/tests/generated/tests/strdup.conf000066400000000000000000000003271256346541500213670ustar00rootroot00000000000000 [strdup] fp: libc/str/strdup include: string.h if: !(defined strdup) prep: char *dup, *s = "test string\n"; call: dup = strdup(s); success_cond: strcmp(s, dup) == 0 failure_cond: dup == NULL errno_on_fail: ENOMEM libfiu-0.94/tests/perf-fsck.py000066400000000000000000000105141256346541500163300ustar00rootroot00000000000000#!/usr/bin/env python """ Performance tests using fsck.ext2 on a test file. It can be tuned with the following environment variables: - TEST_FILE: The name of the file used for testing. By default, ".test_fs". - TEST_FILE_SIZE_MB: The size of the test file, in megabytes. Only used if the file doesn't exist. Default: 10. - VERBOSE: Show verbose output (from 0 to 2). Default: 0. - LD_LIBRARY_PATH: Library path to pass on to the subcommands. Default: /../libfiu/, which is usually the one containing the current build. """ import os import sys import subprocess import time test_file = os.environ.get('TEST_FILE', '.test_fs') test_file_size = int(os.environ.get('TEST_FILE_SIZE_MB', 10)) ld_library_path = os.environ.get('LD_LIBRARY_PATH', os.path.abspath( os.path.abspath(os.path.dirname(sys.argv[0])) + '/../libfiu/')) verbose = int(os.environ.get('VERBOSE', 0)) dev_null = open('/dev/null', 'w') def run_child(name, args, stdout = dev_null, stderr = dev_null): """Runs the subprocess, returns the Popen object.""" env = dict(os.environ) env['LD_LIBRARY_PATH'] = ld_library_path if verbose and stdout == stderr == dev_null: stdout = stderr = None child = subprocess.Popen(args, env = env, stdout = stdout, stderr = stderr) return child def run_and_time(name, args): """Run the given arguments, print the times.""" start = time.time() child = run_child(name, args) _, status, rusage = os.wait4(child.pid, 0) end = time.time() if verbose == 2: print 'Ran %s -> %d' % (args, status) if status != 0: print 'Error running %s: %s' % (args[0], status) raise RuntimeError print '%-10s u:%.3f s:%.3f r:%.3f' % ( name, rusage.ru_utime, rusage.ru_stime, end - start) def run_fsck(name, fiu_args): """Runs an fsck with the given fiu arguments.""" child = run_child(name, ["fiu-run", "-x"] + fiu_args + "fsck.ext2 -n -t -f".split() + [test_file], stdout = subprocess.PIPE, stderr = subprocess.PIPE) stdout, stderr = child.communicate() if child.returncode != 0: print 'Error running fsck: %s' % child.returncode raise RuntimeError # Find the times reported by fsck. # Not very robust, but useful as it measures the real program time run, # and not the startup overhead. # The line looks like: # Memory used: 560k/0k (387k/174k), time: 2.18/ 2.17/ 0.00 user_time = sys_time = real_time = -1 for l in stdout.split('\n'): if not l.startswith("Memory used"): continue times = l.split(':')[-1].split('/') times = [s.strip() for s in times] if len(times) != 3: continue real_time = float(times[0]) user_time = float(times[1]) sys_time = float(times[2]) break print '%-10s u:%.3f s:%.3f r:%.3f' % ( name, user_time, sys_time, real_time) def check_test_file(): if os.path.exists(test_file): return with open(test_file, 'w') as fd: fd.truncate(test_file_size * 1024 * 1024) retcode = subprocess.call( ["mkfs.ext2", "-F", test_file], stdout = open('/dev/null', 'w')) if retcode != 0: print 'Error running mkfs.ext2:', retcode return if __name__ == '__main__': check_test_file() run_and_time("base", "fsck.ext2 -n -f".split() + [test_file]) # 1 all-matching wildcard. run_fsck("w1", ["-c", "enable_random name=*,probability=0"]) # 1k final failure points, no matches. args = [] for i in range(1000): args += ["-c", "enable_random name=none/%d,probability=0" % i] run_fsck("f1k", args) # 1k wildcard failure points, no matches. args = [] for i in range(1000): args += ["-c", "enable_random name=none/%d/*,probability=0" % i] run_fsck("w1k", args) # 1k wildcarded failure points, and 1 match. args = [] for i in range(1000): args += ["-c", "enable_random name=none/%d/*,probability=0" % i] args += ["-c", "enable_random name=*,probability=0"] run_fsck("w1k+1", args) # 1k final failure points, *all* matches. args = [] for i in range(1000): args += ["-c", "enable_random name=*,probability=0"] run_fsck("m1k", args) libfiu-0.94/tests/small-cat.c000066400000000000000000000012671256346541500161240ustar00rootroot00000000000000// A small "cat" utility that copies stdin to stdout. // It does only one 4K read from stdin, and writes it to stdout in as few // write()s as possible. // This gives a controlled number of operations, which makes testing the // random operations more robust. #include // printf(), perror() #include // read(), write() const size_t BUFSIZE = 4092; int main(void) { char buf[BUFSIZE]; ssize_t r, w, pos; r = read(0, buf, BUFSIZE); if (r < 0) { perror("Read error in small-cat"); return 1; } pos = 0; while (r > 0) { w = write(1, buf + pos, r); if (w <= 0) { perror("Write error in small-cat"); return 2; } pos += w; r -= w; } return 0; } libfiu-0.94/tests/test-basic.py000066400000000000000000000011261256346541500165050ustar00rootroot00000000000000""" Basic tests for general functionality. """ import fiu # Test unknown failure point. assert not fiu.fail('unknown') # Test enable/disable. fiu.enable('p1') assert fiu.fail('p1') fiu.disable('p1') assert not fiu.fail('p1') # Test enable_random. fiu.enable_random('p1', probability = 0.5) result = { True: 0, False: 0 } for i in range(1000): result[fiu.fail('p1')] += 1 assert 400 < result[True] < 600, result assert 400 < result[False] < 600, result # Test repeated enabling/disabling. fiu.enable('p1') fiu.enable('p1') assert fiu.fail('p1') fiu.disable('p1') assert not fiu.fail('p1') libfiu-0.94/tests/test-cache_invalidation.py000066400000000000000000000007161256346541500212340ustar00rootroot00000000000000""" Tests to make sure cache invalidation works. """ import fiu # Unknown - add - fail - remove - not fail. # The initial unknown is relevant because it places a negative match in the # cache. assert not fiu.fail('p1') fiu.enable('p1') assert fiu.fail('p1') fiu.disable('p1') assert not fiu.fail('p1') # Same as above, but with wildcards. assert not fiu.fail('p2/x') fiu.enable('p2/*') assert fiu.fail('p2/x') fiu.disable('p2/*') assert not fiu.fail('p2/x') libfiu-0.94/tests/test-enable_stack.c000066400000000000000000000012321256346541500176270ustar00rootroot00000000000000 #include #include #include #include #include void func1(int should_fail) { int failed; /* int nptrs; void *buffer[100]; nptrs = backtrace(buffer, 100); backtrace_symbols_fd(buffer, nptrs, 1); */ failed = fiu_fail("fp-1") != 0; assert(failed == should_fail); } void func2(int should_fail) { func1(should_fail); } int main(void) { int r; fiu_init(0); r = fiu_enable_stack("fp-1", 1, NULL, 0, (void *) &func2, -1); if (r != 0) { printf("NOTE: fiu_enable_stack() failed, skipping test\n"); return 0; } func1(0); func2(1); fiu_disable("fp-1"); func1(0); func2(0); return 0; } libfiu-0.94/tests/test-enable_stack_by_name.c000066400000000000000000000012511256346541500213220ustar00rootroot00000000000000 #include #include #include #include #include void func1(int should_fail) { int failed; /* int nptrs; void *buffer[100]; nptrs = backtrace(buffer, 100); backtrace_symbols_fd(buffer, nptrs, 1); */ failed = fiu_fail("fp-1") != 0; assert(failed == should_fail); } void func2(int should_fail) { func1(should_fail); } int main(void) { int r; fiu_init(0); r = fiu_enable_stack_by_name("fp-1", 1, NULL, 0, "func2", -1); if (r != 0) { printf("NOTE: fiu_enable_stack_by_name() failed, " "skipping test\n"); return 0; } func1(0); func2(1); fiu_disable("fp-1"); func1(0); func2(0); return 0; } libfiu-0.94/tests/test-failinfo_refcount.py000066400000000000000000000005151256346541500211210ustar00rootroot00000000000000 """ Test that we keep references to failinfo as needed. """ import fiu # Object we'll use for failinfo finfo = [1, 2, 3] fiu.enable('p1', failinfo = finfo) assert fiu.fail('p1') assert fiu.failinfo('p1') is finfo finfo_id = id(finfo) del finfo assert fiu.failinfo('p1') == [1, 2, 3] assert id(fiu.failinfo('p1')) == finfo_id libfiu-0.94/tests/test-fiu_ctrl.py000066400000000000000000000034611256346541500172370ustar00rootroot00000000000000""" Tests for the fiu_ctrl.py module. Note the command line utility is covered by the utils/ tests, not from here, this is just for the Python module. """ import subprocess import fiu_ctrl import errno import time fiu_ctrl.PLIBPATH = "./libs/" def run_cat(**kwargs): return fiu_ctrl.Subprocess(["./small-cat"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, **kwargs) # Run without any failure point being enabled. cmd = run_cat() p = cmd.start() out, err = p.communicate('test\n') assert out == 'test\n', out assert err == '', err # Enable before starting. cmd = run_cat(fiu_enable_posix = True) cmd.enable('posix/io/rw/*', failinfo = errno.ENOSPC) p = cmd.start() out, err = p.communicate('test\n') assert out == '', out assert 'space' in err, err # Enable after starting. cmd = run_cat(fiu_enable_posix = True) p = cmd.start() cmd.enable('posix/io/rw/*', failinfo = errno.ENOSPC) out, err = p.communicate('test\n') assert out == '', out assert 'space' in err, err # Enable-disable. cmd = run_cat(fiu_enable_posix = True) p = cmd.start() cmd.enable('posix/io/rw/*', failinfo = errno.ENOSPC) cmd.disable('posix/io/rw/*') out, err = p.communicate('test\n') assert out == 'test\n', (out, err) # Enable random. # This relies on cat doing a reasonably small number of read and writes, which # our small-cat does. result = { True: 0, False: 0 } for i in range(50): cmd = run_cat(fiu_enable_posix = True) p = cmd.start() cmd.enable_random('posix/io/rw/*', failinfo = errno.ENOSPC, probability = 0.5) out, err = p.communicate('test\n') if 'space' in err: result[False] += 1 elif out == 'test\n': result[True] += 1 else: assert False, (out, err) assert 10 < result[True] < 40, result assert 10 < result[False] < 40, result libfiu-0.94/tests/test-manyfps.py000066400000000000000000000006331256346541500171030ustar00rootroot00000000000000 """ Test the creation of many failure points. """ import fiu N = 1000 for i in range(N): fiu.enable(str(i)) for i in range(N): assert fiu.fail(str(i)) # Remove only half and check again; this will stress the shrinking of our data # structures. for i in range(N / 2): fiu.disable(str(i)) for i in range(N / 2, N): assert fiu.fail(str(i)) for i in range(N / 2, N): fiu.disable(str(i)) libfiu-0.94/tests/test-onetime.py000066400000000000000000000003761256346541500170720ustar00rootroot00000000000000 """ Test that we fail ONETIME points only one time. """ import fiu fiu.enable('p1', flags = fiu.Flags.ONETIME) fiu.enable('p2') assert fiu.fail('p1') for i in range(100): assert not fiu.fail('p1') for i in range(100): assert fiu.fail('p2') libfiu-0.94/tests/test-parallel-wildcard.c000066400000000000000000000104641256346541500206060ustar00rootroot00000000000000 /* Test creation and removal of wildcarded failure points while checking them * on a different thread. * * This test will have a thread enabling and disabling failure points like * point:number:1:*, point:number:2:*, ... * * Then it has two threads checking: * point:number:1:1, point:number:1:2, ..., * point:number:2:1, point:number:2:2, ... * * Please note this is a non-deterministic test. */ #include #include #include #include #include #include #include #include /* Be careful with increasing these numbers, as the memory usage is directly * related to them. */ #define NHIGH 1000 #define NLOW 1000 /* Maximum number of a failure point's name */ #define MAX_FPNAME 32 /* How many seconds to run the test */ #define TEST_TIME 5 /* Signal for the threads to stop. */ bool stop_threads = false; /* The name of each final point. */ char final_point_name[NHIGH][NLOW][MAX_FPNAME]; /* The name of each wildcarded point. */ char wildcard_point_name[NHIGH][MAX_FPNAME]; char *make_final_point_name(char *name, int high, int low) { sprintf(name, "point/number/%d/%d", high, low); return name; } char *make_wildcard_point_name(char *name, int high) { sprintf(name, "point/number/%d/*", high); return name; } /* Calls all the final points all the time. */ unsigned long long no_check_caller_count = 0; void *no_check_caller(void *unused) { int high, low; for (;;) { for (high = 0; high < NHIGH; high++) { for (low = 0; low < NLOW; low++) { fiu_fail(final_point_name[high][low]); no_check_caller_count++; } if (stop_threads) return NULL; } } return NULL; } bool rand_bool(void) { return (rand() % 2) == 0; } /* Used too know if a point is enabled or not. */ bool enabled[NHIGH]; pthread_rwlock_t enabled_lock; /* Calls all the *enabled* points all the time. */ unsigned long long checking_caller_count = 0; void *checking_caller(void *unused) { int high, low; int failed; for (;;) { for (high = 0; high < NHIGH; high++) { pthread_rwlock_rdlock(&enabled_lock); if (!enabled[high]) { pthread_rwlock_unlock(&enabled_lock); continue; } for (low = 0; low < NLOW; low++) { failed = fiu_fail( final_point_name[high][low]) != 0; if (!failed) { printf("ERROR: %d:%d did not fail\n", high, low); assert(false); } checking_caller_count++; } pthread_rwlock_unlock(&enabled_lock); if (stop_threads) return NULL; } } return NULL; } /* Enable and disable wildcarded points all the time. */ unsigned long long enabler_count = 0; void *enabler(void *unused) { int high; for (;;) { for (high = 0; high < NHIGH; high++) { /* 50% chance of flipping this point. */ if (rand_bool()) continue; pthread_rwlock_wrlock(&enabled_lock); if (enabled[high]) { assert(fiu_disable(wildcard_point_name[high]) == 0); enabled[high] = false; } else { assert( fiu_enable( wildcard_point_name[high], 1, NULL, 0) == 0); enabled[high] = true; } pthread_rwlock_unlock(&enabled_lock); enabler_count++; if (stop_threads) return NULL; } } return NULL; } void disable_all() { int high; pthread_rwlock_wrlock(&enabled_lock); for (high = 0; high < NHIGH; high++) { /* Note this could fail as we don't check if they're active or * not, but here we don't care. */ fiu_disable(wildcard_point_name[high]); } pthread_rwlock_unlock(&enabled_lock); } int main(void) { int high, low; pthread_t t1, t2, t3; fiu_init(0); for (high = 0; high < NHIGH; high++) { make_wildcard_point_name(wildcard_point_name[high], high); for (low = 0; low < NLOW; low++) { make_final_point_name(final_point_name[high][low], high, low); } } pthread_rwlock_init(&enabled_lock, NULL); pthread_create(&t1, NULL, no_check_caller, NULL); pthread_create(&t2, NULL, checking_caller, NULL); pthread_create(&t3, NULL, enabler, NULL); sleep(TEST_TIME); stop_threads = 1; pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); disable_all(); pthread_rwlock_destroy(&enabled_lock); printf("wildcard nc: %-8llu c: %-8llu e: %-8llu t: %llu\n", no_check_caller_count, checking_caller_count, enabler_count, no_check_caller_count + checking_caller_count + enabler_count); return 0; } libfiu-0.94/tests/test-parallel.c000066400000000000000000000063671256346541500170260ustar00rootroot00000000000000 /* Test creation and removal of failure points while checking them on a * different thread. * * Please note this is a non-deterministic test. */ #include #include #include #include #include #include #include #include #define NPOINTS 10000 /* How many seconds to run the test */ #define TEST_TIME 5 /* Signal for the threads to stop. */ bool stop_threads = false; /* The name of each point */ char point_name[NPOINTS][16]; char *make_point_name(char *name, int i) { sprintf(name, "fp-%d", i); return name; } /* Calls all the points all the time. */ unsigned long long no_check_caller_count = 0; void *no_check_caller(void *unused) { int i = 0; while (!stop_threads) { fiu_fail(point_name[i]); i = (i + 1) % NPOINTS; if (i == 0) no_check_caller_count++; } no_check_caller_count = no_check_caller_count * NPOINTS + i; return NULL; } bool rand_bool(void) { return (rand() % 2) == 0; } /* Used too know if a point is enabled or not. */ bool enabled[NPOINTS]; pthread_rwlock_t enabled_lock; /* Calls all the *enabled* points all the time. */ unsigned long long checking_caller_count = 0; void *checking_caller(void *unused) { int i = 0; int failed; while (!stop_threads) { pthread_rwlock_rdlock(&enabled_lock); if (enabled[i]) { failed = fiu_fail(point_name[i]) != 0; pthread_rwlock_unlock(&enabled_lock); if (!failed) { printf("ERROR: fp %d did not fail\n", i); assert(false); } } else { pthread_rwlock_unlock(&enabled_lock); } i = (i + 1) % NPOINTS; if (i == 0) checking_caller_count++; } checking_caller_count = checking_caller_count * NPOINTS + i; return NULL; } /* Enable and disable points all the time. */ unsigned long long enabler_count = 0; void *enabler(void *unused) { int i = 0; while (!stop_threads) { if (rand_bool()) { pthread_rwlock_wrlock(&enabled_lock); if (enabled[i]) { assert(fiu_disable(point_name[i]) == 0); enabled[i] = false; } else { assert(fiu_enable(point_name[i], 1, NULL, 0) == 0); enabled[i] = true; } pthread_rwlock_unlock(&enabled_lock); } i = (i + 1) % NPOINTS; if (i == 0) enabler_count++; } enabler_count = enabler_count * NPOINTS + i; return NULL; } void disable_all() { int i = 0; pthread_rwlock_wrlock(&enabled_lock); for (i = 0; i < NPOINTS; i++) { /* Note this could fail as we don't check if they're active or * not, but here we don't care. */ fiu_disable(point_name[i]); } pthread_rwlock_unlock(&enabled_lock); } int main(void) { int i; pthread_t t1, t2, t3; fiu_init(0); for (i = 0; i < NPOINTS; i++) make_point_name(point_name[i], i); pthread_rwlock_init(&enabled_lock, NULL); pthread_create(&t1, NULL, no_check_caller, NULL); pthread_create(&t2, NULL, checking_caller, NULL); pthread_create(&t3, NULL, enabler, NULL); sleep(TEST_TIME); stop_threads = 1; pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); disable_all(); pthread_rwlock_destroy(&enabled_lock); printf("parallel nc: %-8llu c: %-8llu e: %-8llu t: %llu\n", no_check_caller_count, checking_caller_count, enabler_count, no_check_caller_count + checking_caller_count + enabler_count); return 0; } libfiu-0.94/tests/test-wildcards.py000066400000000000000000000014331256346541500174010ustar00rootroot00000000000000""" Test the behaviour of the wildcard failure points. """ import fiu fiu.enable("a:b:c") assert fiu.fail("a:b:c") fiu.enable("a:b:*") assert fiu.fail("a:b:c") assert fiu.fail("a:b:x") assert fiu.fail("a:b:c:d") fiu.enable("a:b:*") # Test repeated enabling of a wildcard. fiu.enable("a:b:c:d") assert fiu.fail("a:b:c:d") fiu.disable("a:b:c") assert fiu.fail("a:b:c") fiu.disable("a:b:*") assert not fiu.fail("a:b:c") assert not fiu.fail("a:b:x") assert fiu.fail("a:b:c:d") fiu.disable("a:b:c:d") assert not fiu.fail("a:b:c:d") s = "x" for i in range(200): fiu.enable(s + "/*") s += "/x" s = "x" for i in range(200): assert fiu.fail(s + '/asdf') fiu.disable(s + "/*") s += "/x" fiu.enable("*") assert fiu.fail("asdf") fiu.disable("*") assert not fiu.fail("asdf") libfiu-0.94/tests/utils/000077500000000000000000000000001256346541500152355ustar00rootroot00000000000000libfiu-0.94/tests/utils/.gitignore000066400000000000000000000000231256346541500172200ustar00rootroot00000000000000output-*.txt libs/ libfiu-0.94/tests/utils/Makefile000066400000000000000000000014171256346541500167000ustar00rootroot00000000000000 ifneq ($(V), 1) NICE_RUN = @echo " RUN $<"; LD_LIBRARY_PATH=../../libfiu/ NICE_LN = @echo " LN $@"; ln -f else NICE_RUN = LD_LIBRARY_PATH=../../libfiu/ NICE_LN = ln -f endif default: all all: lnlibs tests # Link the libraries to a single place, as the scripts expect. libs: mkdir -p libs/ libs/fiu_posix_preload.so: ../../preload/posix/fiu_posix_preload.so libs $(NICE_LN) $< libs/ libs/fiu_run_preload.so: ../../preload/run/fiu_run_preload.so libs $(NICE_LN) $< libs/ lnlibs: libs/fiu_posix_preload.so libs/fiu_run_preload.so # # Tests # SRCS := $(wildcard test-*) tests: $(patsubst %,run-%,$(SRCS)) run-%: % $(NICE_RUN) ./$< > output-$<.txt 2>&1 # # Cleanup # clean: rm -f libs/*.so output-*.txt rmdir libs/ .PHONY: default all clean \ tests lnlibs libfiu-0.94/tests/utils/test-basic_ctrl.py000077500000000000000000000034421256346541500206770ustar00rootroot00000000000000#!/usr/bin/env python import subprocess import time def fiu_ctrl(p, args): subprocess.check_call("./wrap fiu-ctrl".split() + args + [str(p.pid)]) def launch_sh(): # We use cat as a subprocess as it is reasonably ubiquitous, simple and # straightforward (which helps debugging and troubleshooting), but at the # same time it is interactive and we can make it do the operations we # want. p = subprocess.Popen("./wrap fiu-run -x cat".split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Give it a moment to initialize and create the control files. time.sleep(0.2) return p def send_cmd(p, cmd): p.stdin.write(cmd) p.stdin.close() # Give the control thread a moment to process the command. time.sleep(0.2) return p.stdout.read(), p.stderr.read() # Launch a subprocess and check that it shows up in fiu-ls. p = launch_sh() out = subprocess.check_output("./wrap fiu-ls".split()) assert ("%s: cat" % p.pid) in out, out # Send it a command and check that it works. # Nothing interesting here from libfiu's perspective, but it helps make sure # the test environment is sane. out, err = send_cmd(p, "test\n") assert out == 'test\n', out assert err == '', err # Launch and then make I/O fail at runtime. p = launch_sh() fiu_ctrl(p, ["-c", "enable name=posix/io/*"]) out, err = send_cmd(p, "test\n") assert out == '', out assert 'error' in err # Same, but with failinfo. p = launch_sh() fiu_ctrl(p, ["-c", "enable name=posix/io/*,failinfo=3"]) out, err = send_cmd(p, "test\n") assert out == '', out assert 'error' in err # Same, but with probability. p = launch_sh() fiu_ctrl(p, ["-c", "enable_random name=posix/io/*,probability=0.999"]) out, err = send_cmd(p, "test\n") assert out == '', out assert 'error' in err libfiu-0.94/tests/utils/test-basic_run.sh000077500000000000000000000005001256346541500205110ustar00rootroot00000000000000#!/bin/bash set -ex # Very basic test, nothing enabled. ./wrap fiu-run true # Same as above, but including the posix preloader. ./wrap fiu-run -x true # Now with an unused failure point. ./wrap fiu-run -c "enable name=p1" true # open() failure. ! ./wrap fiu-run -x -c "enable name=posix/io/oc/open" cat /dev/null libfiu-0.94/tests/utils/wrap000077500000000000000000000006421256346541500161360ustar00rootroot00000000000000#!/bin/bash # Wrap the utilities (fiu-ctrl, fiu-ls, fiu-run) so that we can run them # easily from within the development tree, agains the locally-built libraries. # # The first argument is the utility to run. case "$1" in "fiu-ctrl") BIN=../../utils/fiu-ctrl ARGS= ;; "fiu-ls") BIN=../../utils/fiu-ls ARGS= ;; "fiu-run") BIN=../../preload/run/fiu-run ARGS="-l ./libs" ;; esac shift exec $BIN $ARGS "$@" libfiu-0.94/tests/wrap-python000077500000000000000000000030001256346541500163040ustar00rootroot00000000000000#!/usr/bin/env python # Python wrapper, which makes it able to import the built (and not the # installed) version of libfiu. # # The first parameter must be the python version (2 or 3) import sys import os import glob if len(sys.argv) < 2 or sys.argv[1] not in ("2", "3"): sys.stderr.write("Error: the first argument must be the " + "version (2 or 3)\n") sys.exit(1) py_ver = sys.argv[1] # Find the path where the library was built and add it to the lookup paths, so # we run against it lib_bin = os.path.dirname(sys.argv[0]) + "/../libfiu/libfiu.so" if not os.path.exists(lib_bin): sys.stderr.write("Can't find library (run make)\n") sys.exit(1) lib_path = os.path.dirname(os.path.abspath(lib_bin)) os.environ["LD_LIBRARY_PATH"] = ":".join([lib_path, \ os.environ.get("LD_LIBRARY_PATH", "")]) # Find out the corresponding module path for the desired python version. The # path must be absolute mod_bins = glob.glob(os.path.dirname(sys.argv[0]) + "/../bindings/python/build/lib*-%s.*/fiu_ll.so" \ % py_ver) if not mod_bins: sys.stderr.write(("Can't find python%s bindings, run " + "make python%s\n") % (py_ver, py_ver)) sys.exit(1) if len(mod_bins) > 1: sys.stderr.write("Found too many matching python%s bindings" \ % py_ver) sys.exit(1) mod_path = os.path.dirname(os.path.abspath(mod_bins[0])) os.environ["PYTHONPATH"] = ":".join([mod_path, os.environ.get("PYTHONPATH", "")]) if py_ver == '2': py_bin = "python" else: py_bin = "python3" os.execvp(py_bin, [py_bin] + sys.argv[2:]) libfiu-0.94/utils/000077500000000000000000000000001256346541500140735ustar00rootroot00000000000000libfiu-0.94/utils/Makefile000066400000000000000000000011521256346541500155320ustar00rootroot00000000000000 # prefix for installing the binaries PREFIX=/usr/local # install utility, we assume it's GNU/BSD compatible INSTALL=install default: install: $(INSTALL) -d $(PREFIX)/bin $(INSTALL) -m 0755 fiu-ctrl $(PREFIX)/bin $(INSTALL) -m 0755 fiu-ls $(PREFIX)/bin $(INSTALL) -d $(PREFIX)/share/man/man1 $(INSTALL) -m 0644 fiu-ctrl.1 $(PREFIX)/share/man/man1/ $(INSTALL) -m 0644 fiu-ls.1 $(PREFIX)/share/man/man1/ uninstall: $(RM) $(PREFIX)/bin/fiu-ctrl $(RM) $(PREFIX)/bin/fiu-ls $(RM) $(PREFIX)/share/man/man1/fiu-ctrl.1 $(RM) $(PREFIX)/share/man/man1/fiu-ls.1 clean: .PHONY: default install uninstall clean libfiu-0.94/utils/fiu-ctrl000077500000000000000000000100761256346541500155520ustar00rootroot00000000000000#!/usr/bin/env bash # This scripts present a friendly interface to the fiu remote control # capabilities. Currently, it supports only the named pipe method (the only # one implemented). # default remote control over named pipes prefix; we use the same one as # fiu-run so it's easier to use FIFO_PREFIX="${TMPDIR:-/tmp}/fiu-ctrl" # commands to send, will be filled by options processing; must be in the # format supported by the fiu remote control (see fiu-rc.c for more details) declare -a CMDS HELP_MSG=" Usage: fiu-ctrl [options] PID [PID ...] The following options are supported: -c command Run the given libfiu remote control command before executing the program (see below for reference). -f ctrlpath Enable remote control over named pipes with the given path as base name, the process id will be appended (defaults to \"$FIFO_PREFIX\", set to \"\" to disable). Remote control commands are of the form 'command param1=value1,param2=value2'. Valid commands are: - 'enable name=NAME' Enables the NAME failure point unconditionally. - 'enable_random name=NAME,probability=P' Enables the NAME failure point with a probability of P. - 'disable name=NAME' Disables the NAME failure point. All of the enable\* can also optionally take 'failnum' and 'failinfo' parameters, analogous to the ones taken by the C functions. The following options existed in the past but are deprecated and WILL BE REMOVED in future releases: -e, -p, -u, -i, and -d. Examples: fiu-ctrl -c 'enable_random name=posix/io/*,probability=0.25' \\ -c 'enable_random name=libc/mm/*,probability=0.05' 12345 Tell the process with pid 12345 to enable the failure point 'posix/io/read' with a 25% of probability to fail, and the failure point 'libc/mm/malloc' with a 5% of probability to fail. fiu-ctrl -c 'disable name=posix/io/read' 12345 Tell the same process to disable the previously enabled failure point. You can control multiple processes at once by specifiying more than one process ID. " # # Parse the options # if [ $# -lt 1 ]; then echo "$HELP_MSG" exit 1 fi function opts_reset() { # variables to store what we know so far; after a new name is found # the old one is added to $CMD DEP_NAME="" DEP_PROB=-1 DEP_FAILNUM=1 DEP_FAILINFO=0 } function add_deprecated_enable() { if [ "$DEP_NAME" == "" ]; then return fi; PARAMS="name=$DEP_NAME,failnum=$DEP_FAILNUM,failinfo=$DEP_FAILINFO" if [ "$DEP_PROB" -ge 0 ]; then C="enable_random $PARAMS,probability=0.$DEP_PROB" else C="enable $PARAMS" fi CMDS[${#CMDS[*]}]="$C" } function deprecated_warning() { echo "Warning: this option is deprecated and will be removed." >&2 } opts_reset; while getopts "+c:e:p:u:i:d:f:h" opt; do case $opt in c) CMDS[${#CMDS[*]}]="$OPTARG" ;; e) # add the current one, if any deprecated_warning; add_deprecated_enable; opts_reset; DEP_NAME="$OPTARG" ;; p) deprecated_warning; DEP_PROB="$OPTARG" ;; u) deprecated_warning; DEP_FAILNUM="$OPTARG" ;; i) deprecated_warning; DEP_FAILINFO="$OPTARG" ;; f) FIFO_PREFIX="$OPTARG" ;; d) deprecated_warning; CMDS[${#CMDS[*]}]="disable name=$OPTARG" opts_reset; ;; h|*) echo "$HELP_MSG" exit 1 ;; esac; done # add leftovers if [ "$DEP_NAME" != "" ]; then add_deprecated_enable; fi # eat the parameters we already processed shift $(( $OPTIND - 1 )) PIDS="" PREFIXES="" for i in "$@"; do if test -p "$i.out"; then PREFIXES="$PREFIXES $i" elif kill -0 $i > /dev/null 2> /dev/null && \ test -p "$FIFO_PREFIX-$i.out"; then PIDS="$PIDS $i" else echo "Error: unknown pid or named pipe $i, skipping" echo "Note that options must come before the PID" fi done # # Send the commands # function send_cmd_fifo() { # $1 = complete fifo prefix # $2+ = command to send # echoes the reply P=$1 shift 1 echo "$@" > $P.in REPLY=$(cat "$P.out") if [ "$REPLY" == "-1" ]; then echo "$P: Command '$@' returned error ($REPLY)" fi #echo "$P: $@ -> $REPLY" } for c in "${CMDS[@]}"; do for i in $PIDS; do send_cmd_fifo $FIFO_PREFIX-$i "$c" done for i in $PREFIXES; do send_cmd_fifo $i "$c" done done libfiu-0.94/utils/fiu-ctrl.1000066400000000000000000000042371256346541500157100ustar00rootroot00000000000000.TH fiu-ctrl 1 "16/Jun/2009" .SH NAME fiu-ctrl - a script to remote control programs using libfiu .SH SYNOPSIS fiu-ctrl [options] PID [PID ...] .SH DESCRIPTION fiu-ctrl is a script to enable/disable failure points in running programs that are using \fBlibfiu\fR(3). Programs are usually launched using \fBfiu-run\fR(1), which enables libfiu's remote control capabilities without the need to modify the program's code. For additional documentation, go to the project's website at .IR http://blitiri.com.ar/p/libfiu . .SH OPTIONS .TP .B "-c command" Run the given libfiu remote control command before executing the program (see below for reference). .TP .B "-f ctrlpath" Set the default prefix for remote control over named pipes. Defaults to "$TMPDIR/fiu-ctrl", or "/tmp/fiu-ctrl" if "$TMPDIR" is not set, which is the usually correct for programs launched using \fBfiu-run\fR(1). .P Remote control commands are of the form "\fIcommand param1=value1,param2=value2\fR". Valid commands are: .TP .B 'enable name=NAME' Enables the NAME failure point unconditionally. .TP .B 'enable_random name=NAME,probability=P' Enables the NAME failure point with a probability of P. .TP .B 'disable name=NAME' Disables the NAME failure point. .P All of the \fIenable*\fR commands can also optionally take \fIfailnum\fR and \fIfailinfo\fR parameters, analogous to the ones taken by the C functions. .SH EXAMPLES The following command will tell the process running with PID 12345 to enable the failure point \fIposix/io/read\fR with a 25% of probability to fail, and the failure point \fIlibc/mm/malloc\fR with a 5% of probability to fail: .RS .nf fiu\-ctrl \-c 'enable_random name=posix/io/*,probability=0.25' \\ \-c 'enable_random name=libc/mm/*,probability=0.05' 12345 .fi .RE And the following will tell the same process to disable the previously enabled failure point \fIposix/io/read\fR: .RS .nf fiu\-ctrl \-c 'disable name=posix/io/read' 12345 .fi .RE .SH SEE ALSO .BR libfiu (3), .BR fiu-run (1). .SH BUGS If you want to report bugs, or have any questions or comments, just let me know at albertito@blitiri.com.ar. For more information about libfiu, you can go to http://blitiri.com.ar/p/libfiu. libfiu-0.94/utils/fiu-ls000077500000000000000000000020771256346541500152260ustar00rootroot00000000000000#!/usr/bin/env bash # Lists processes that can be controlled with fiu-ctrl. # default remote control over named pipes prefix; we use the same one as # fiu-run so it's easier to use FIFO_PREFIX="${TMPDIR:-/tmp}/fiu-ctrl" declare -a PIDS HELP_MSG=" Usage: fiu-ls [options] The following options are supported: -f ctrlpath Set the default prefix for remote control over named pipes. (defaults to \"$FIFO_PREFIX\", which is usually correct if the program was run using fiu-run(1)). " while getopts "f:h" opt; do case $opt in f) FIFO_PREFIX="$OPTARG" ;; h|*) echo "$HELP_MSG" exit 1 ;; esac; done for P in `ls -1 $FIFO_PREFIX-*.in 2>/dev/null` ; do if [ -n "$P" ] ; then OUT="`echo $P | cut -d. -f1`.out" if [ -p "$P" -a -w "$P" -a -p "$OUT" -a -r "$OUT" ] ; then PID="`echo $P | cut -d- -f3 | cut -d. -f1`" if [ -n "$PID" ] && \ kill -0 "$PID" >/dev/null 2>/dev/null ; then PIDS[${#PIDS[*]}]="$PID" fi fi fi done for P in "${PIDS[@]}"; do CMDLINE="`tr '\0' ' ' < /proc/$P/cmdline 2>/dev/null`" printf "%5d: %s\n" "$P" "$CMDLINE" done libfiu-0.94/utils/fiu-ls.1000066400000000000000000000016151256346541500153570ustar00rootroot00000000000000.TH fiu-ls 1 "7/Jul/2009" .SH NAME fiu-ls - list processes available for libfiu remote control .SH SYNOPSIS fiu-ls [options] .SH DESCRIPTION fiu-ls lists process that are available to be controlled using the libfiu \fBfiu-ctrl\fR(1) utility. Such processes have the remote control pipes available. For additional documentation, go to the project's website at .IR http://blitiri.com.ar/p/libfiu . .SH OPTIONS .TP .B "-f ctrlpath" Set the default prefix for remote control over named pipes. Defaults to "$TMPDIR/fiu-ctrl", or "/tmp/fiu-ctrl" if "$TMPDIR" is not set, which is the usually correct for programs launched using \fBfiu-run\fR(1). .SH SEE ALSO .BR libfiu (3), .BR fiu-ctrl (1), .BR fiu-run (1). .SH BUGS If you want to report bugs, or have any questions or comments, just let me know at albertito@blitiri.com.ar. For more information about libfiu, you can go to http://blitiri.com.ar/p/libfiu.