author | Alberto Bertogli
<albertito@blitiri.com.ar> 2009-03-27 04:44:22 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2009-03-27 05:12:45 UTC |
parent | f63794c79e66586283adbfe68f01298e6dcd8849 |
.gitignore | +3 | -0 |
Makefile | +11 | -2 |
bindings/python3/fiu.py | +75 | -0 |
bindings/python3/fiu_ll.c | +201 | -0 |
bindings/python3/setup.py | +17 | -0 |
diff --git a/.gitignore b/.gitignore index e0c7c3d..7e4eb4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ bindings/python2/build bindings/python2/*.pyc bindings/python2/*.pyo +bindings/python3/build +bindings/python3/*.pyc +bindings/python3/*.pyo libfiu/*.o libfiu/libfiu.a libfiu/libfiu.pc diff --git a/Makefile b/Makefile index a05295c..31ae8e0 100644 --- a/Makefile +++ b/Makefile @@ -19,12 +19,21 @@ python2_install: python2_clean: cd bindings/python2 && rm -rf build/ +python3: + cd bindings/python3 && python3 setup.py build -clean: python2_clean +python3_install: + cd bindings/python3 && python3 setup.py install + +python3_clean: + cd bindings/python3 && rm -rf build/ + +clean: python2_clean python3_clean $(MAKE) -C libfiu clean .PHONY: default all clean libfiu utils \ - python2 python2_install python2_clean + python2 python2_install python2_clean \ + python3 python3_install python3_clean diff --git a/bindings/python3/fiu.py b/bindings/python3/fiu.py new file mode 100644 index 0000000..822ad9b --- /dev/null +++ b/bindings/python3/fiu.py @@ -0,0 +1,75 @@ + +""" +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() + + +# 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] = failnum + 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] = failnum + 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: + 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) + + diff --git a/bindings/python3/fiu_ll.c b/bindings/python3/fiu_ll.c new file mode 100644 index 0000000..cff30ed --- /dev/null +++ b/bindings/python3/fiu_ll.c @@ -0,0 +1,201 @@ + +/* + * Python bindings for libfiu + * Alberto Bertogli (albertito@blitiri.com.ar) + * + * This is the low-level module, used by the python one to construct + * friendlier objects. + */ + +#include <Python.h> + +/* Unconditionally enable fiu, otherwise we get fake headers */ +#define FIU_ENABLE 1 + +#include <fiu.h> +#include <fiu-control.h> + + +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_random", &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 *disable(PyObject *self, PyObject *args) +{ + char *name; + + if (!PyArg_ParseTuple(args, "s:fail", &name)) + return NULL; + + return PyLong_FromLong(fiu_disable(name)); +} + + +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 }, + { "disable", (PyCFunction) disable, METH_VARARGS, NULL }, + { NULL } +}; + +static PyModuleDef fiu_module = { + PyModuleDef_HEAD_INIT, + .m_name = "libfiu", + .m_size = -1, + .m_methods = fiu_methods, +}; + + +PyMODINIT_FUNC PyInit_fiu_ll(void) +{ + PyObject *m; + + m = PyModule_Create(&fiu_module); + + PyModule_AddIntConstant(m, "FIU_ONETIME", FIU_ONETIME); + + fiu_init(0); + + return m; +} + diff --git a/bindings/python3/setup.py b/bindings/python3/setup.py new file mode 100644 index 0000000..dbdc256 --- /dev/null +++ b/bindings/python3/setup.py @@ -0,0 +1,17 @@ + +from distutils.core import setup, Extension + +fiu_ll = Extension("fiu_ll", + libraries = ['fiu'], + sources = ['fiu_ll.c']) + +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'], + ext_modules = [fiu_ll] +) +