author | Alberto Bertogli
<albertito@blitiri.com.ar> 2018-05-13 14:48:42 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2018-05-13 14:48:42 UTC |
parent | 527317f4a90045c061cada3adb40417efd489aa4 |
bindings/python/fiu.py | +5 | -0 |
bindings/python/fiu_ll.c | +12 | -0 |
libfiu/fiu.c | +13 | -0 |
libfiu/fiu.h | +16 | -0 |
libfiu/symbols.map | +1 | -0 |
tests/test-set_prng_seed.py | +23 | -0 |
diff --git a/bindings/python/fiu.py b/bindings/python/fiu.py index 0b0a308..20f4a13 100644 --- a/bindings/python/fiu.py +++ b/bindings/python/fiu.py @@ -95,6 +95,11 @@ def disable(name): if r != 0: raise RuntimeError(r) +def set_prng_seed(seed): + """Sets the PRNG seed. Don't use this unless you know what you're + doing.""" + return _ll.set_prng_seed(seed) + def rc_fifo(basename): """Enables remote control over a named pipe that begins with the given basename. The final path will be "basename-$PID".""" diff --git a/bindings/python/fiu_ll.c b/bindings/python/fiu_ll.c index e49874b..85ca93b 100644 --- a/bindings/python/fiu_ll.c +++ b/bindings/python/fiu_ll.c @@ -187,6 +187,17 @@ static PyObject *disable(PyObject *self, PyObject *args) return PyLong_FromLong(fiu_disable(name)); } +static PyObject *set_prng_seed(PyObject *self, PyObject *args) +{ + int seed; + + if (!PyArg_ParseTuple(args, "i:set_prng_seed", &seed)) + return NULL; + + fiu_set_prng_seed(seed); + Py_RETURN_NONE; +} + static PyObject *rc_fifo(PyObject *self, PyObject *args) { char *basename; @@ -207,6 +218,7 @@ static PyMethodDef fiu_methods[] = { { "enable_stack_by_name", (PyCFunction) enable_stack_by_name, METH_VARARGS, NULL }, { "disable", (PyCFunction) disable, METH_VARARGS, NULL }, + { "set_prng_seed", (PyCFunction) set_prng_seed, METH_VARARGS, NULL }, { "rc_fifo", (PyCFunction) rc_fifo, METH_VARARGS, NULL }, { NULL } }; diff --git a/libfiu/fiu.c b/libfiu/fiu.c index 4f9de64..e49bfc2 100644 --- a/libfiu/fiu.c +++ b/libfiu/fiu.c @@ -191,11 +191,17 @@ static int should_stack_fail(struct pf_info *pf) * 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 bool randd_xn_manual = false; static void prng_seed(void) { struct timeval tv; + /* If the seed is being handled manually, don't interfere. */ + if (randd_xn_manual) { + return; + } + gettimeofday(&tv, NULL); randd_xn = tv.tv_usec; @@ -255,6 +261,13 @@ int fiu_init(unsigned int flags) return 0; } +/* Sets the PRNG seed. */ +void fiu_set_prng_seed(unsigned int seed) +{ + randd_xn = seed; + randd_xn_manual = true; +} + /* 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. */ diff --git a/libfiu/fiu.h b/libfiu/fiu.h index 7659b7f..8dd6bb7 100644 --- a/libfiu/fiu.h +++ b/libfiu/fiu.h @@ -30,6 +30,21 @@ extern "C" { */ int fiu_init(unsigned int flags); +/** Sets the PRNG seed. + * + * This function is called if you want to manually set the seed used to + * generate random numbers. This allows more control over randomized tests. + * + * Must be called before fiu_init(), or at fork. Otherwise, races might occur + * as this function is not threadsafe wrt. other fiu functions. + * + * There's no need to call this function for normal operations. Don't use it + * unless you know what you're doing. + * + * @param seed PRNG seed to use. + */ +void fiu_set_prng_seed(unsigned int seed); + /** Returns the failure status of the given point of failure. * * @param name Point of failure name. @@ -73,6 +88,7 @@ void *fiu_failinfo(void); * don't include it to avoid a circular dependency. */ #define fiu_init(flags) 0 +#define fiu_set_prng_seed(seed) #define fiu_fail(name) 0 #define fiu_failinfo() NULL #define fiu_do_on(name, action) diff --git a/libfiu/symbols.map b/libfiu/symbols.map index 204acc1..bae73d5 100644 --- a/libfiu/symbols.map +++ b/libfiu/symbols.map @@ -10,6 +10,7 @@ fiu_fail; fiu_failinfo; fiu_init; + fiu_set_prng_seed; fiu_rc_fifo; fiu_rc_string; diff --git a/tests/test-set_prng_seed.py b/tests/test-set_prng_seed.py new file mode 100644 index 0000000..f1f3f5c --- /dev/null +++ b/tests/test-set_prng_seed.py @@ -0,0 +1,23 @@ +""" +Test that we get reproducible results with manually set PRNG seeds. +""" + +import fiu + + +fiu.set_prng_seed(1234) +fiu.enable_random('p1', probability = 0.5) +result = { True: 0, False: 0 } +for i in range(1000): + result[fiu.fail('p1')] += 1 + +assert result == {False: 516, True: 484}, result + + +fiu.set_prng_seed(4321) +fiu.enable_random('p1', probability = 0.5) +result = { True: 0, False: 0 } +for i in range(1000): + result[fiu.fail('p1')] += 1 + +assert result == {False: 495, True: 505}, result