Fluxcapacitor is a tool for making your program run without blocking on timeouts, on functions like
select, by spoofing POSIX time functions.
It is somewhat similar to:
While these tools patch time libraries in Ruby and Python,
fluxcapacitor works on a lower layer by "patching" low-level
syscalls. That way, it can lie about time to any program in any
programming language, as long as it runs on Linux.
This approach has a significant advantage: it is possible to lie about time to many processes at the same time. It is especially useful for running network applications where server and client run in different processes which rely on time. It will also work with multithreaded applications.
Another comparable project is:
Fluxcapacitor is fundamentally different from
can fake the time functions, but doesn't affect the runtime of the
fluxcapacitor will make your program run faster
and be 100% CPU constrained. It does that by "speeding up" blocking
syscalls. Faking time is a necessary side effect.
ptrace on syscalls and
which is why it's Linux specific.
When you run
sleep bash command, well, it will block the console for
a given time. For example:
$ sleep 12
will halt terminal for 12 seconds. When you run it with
$ ./fluxcapacitor -- sleep 12
it will finish instantly. Cool, huh? To illustrate this:
$ time sleep 12 real 0m12.003s
$ time ./fluxcapacitor -- sleep 12 real 0m0.057s
Another example, take a look at this session:
$ date Thu Feb 14 23:49:55 GMT 2013 $ ./fluxcapacitor -- bash -c "date; sleep 120; date" Thu Feb 14 23:49:57 GMT 2013 Thu Feb 14 23:51:57 GMT 2013 $ date Thu Feb 14 23:49:58 GMT 2013
You should see a program thinks time had passed, although it did not in reality.
Ever heard of the year 2038 problem? Here's how it's going to look like in action (this works on 32 bit systems):
$ ./fluxcapacitor -- bash -c "sleep 700000000; date" Thu Apr 26 17:44:25 BST 2035 $ ./fluxcapacitor -- bash -c "sleep 800000000; date" Wed May 21 20:04:03 GMT 1902
fluxcapacitor works with any programming language:
$ ./fluxcapacitor -- python2 -c "import time; time.sleep(1000)"
fluxcapacitor usage info:
$ ./fluxcapacitor --help Usage: fluxcapacitor [options] [ -- command [ arguments ... ] ... ] Options: --libpath=PATH Load fluxcapacitor_preload.so from selected PATH directory. --signal=SIGNAL Use specified signal to interrupt blocking syscall instead of SIGURG. --verbose,-v Print more stuff. --help Print this message.
How does it work
Fluxcapacitor internally does two things:
fluxcapacitor_preload.soto be preloaded using the
LD_PRELOADlinux facility. This library is responsible for two things:
- It makes sure that
clock_gettime()will use the standard syscall, not the ultra-fast VDSO mechanism. That gives us the opportunity to replace the return value of the system call later.
- It replaces various time-related libc functions:
clock_nanosleep()with variants using modified
clock_gettime(). That simplifies syscall semantics thus making some parts of the server code less involved.
- It makes sure that
It runs then given command and its forked children in a
ptrace()sandbox, capturing all syscalls. Some syscalls - notably
clock_gettime, have their original results returned from the kernel overwritten by faked values. Other syscalls, like
epoll_wait(), can be interrupted (by a signal) and the result will be set to look like a timeout has expired. Full list of recognized syscalls that can be sped up:
Fluxcapacitor monitors all syscalls run by the child processes. All
syscalls are relayed to the kernel, as normal. This operation
continues until fluxcapacitor notices that all the child processes are
waiting on recognised time-related syscalls, like
select. When that happens, fluxcapacitor decides to speed up the
time. It advances the internal timer and sends a signal (SIGURG by
default) to the process that is blocked with the smallest timeout
value. Fluxcapacitor is then woken up by the kernel to give it a
chance to pass the signal to the child. It swallows the signal and
sets the return value of the syscall to look like a timeout had
expired. See diagram:
child fluxcapacitor kernel ----- ------------- ------ | +--- select(1s) -->+ | +------------------------> kill(child, SIGURG) +<---- signal received --- | (pretend it was a timeout) | +<--- timeout -----+ |
When it won't work
Fluxcapacitor won't work in a number of cases:
If your code is statically compiled and
fluxcapacitor_preload.sold-preloaded library can't play its role.
If your code uses unpopular blocking functions in the event loop, like
sigwait(), or if your program relies heavily on signals and things like
If your code uses file access or modification timestamps.
Fluxcapacitordoes not mock that.
Basically, for Fluxcapacitor to work all the time, queries need to be
clock_gettime(), and all the waiting
for timeouts must rely on
epoll_wait(). Fortunately, that's the case in most programming
Fluxcapacitor's main application is speeding up tests.
Say you have a "delayed echo" server and you want to test it. It echos messages, just delayed by a few seconds. You don't want your tests to take too long. For example the code:
Normally you could run the server, run the tests in a separate console
and wait for some time. With
fluxcapacitor you write a
#!/usr/bin/env python2 import os import time import signal server_pid = os.fork() if server_pid == 0: os.execv("/usr/bin/python2", ["python2", "server.py"]) os._exit(0) else: time.sleep(1) os.system("python2 tests.py") os.kill(server_pid, signal.SIGINT)
This script just runs the tests in an automated manner. Normally the tests take 1 second each:
$ time python2 run_test.py real 0m5.112s
fluxcapacitor it's much faster:
$ ./fluxcapacitor -- python2 run_test.py real 0m0.355s
To compile the things you need are
$ sudo yum git gcc make
$ sudo apt-get install git gcc make
fluxcapacitor you need a reasonably recent linux
Fluxcapacitor comes with a number of python tests. See
subdirectory for details. To test
You can also run specific tests, but that's a bit more complex. For
example to run
FCPATH="$PWD/fluxcapacitor --libpath=$PWD" \ python2 tests/tests_basic.py SingleProcess.test_bash_sleep