libckpt.so
. Given an arbitrary application
in C or C++, you will then run it with this library
with the following command-line:
LD_PRELOAD=/path/to/libckpt.so ./myprog
where /path/to/libckpt.so
must be an absolute pathname for the
libckpt.so
.
You must provide a Makefile for this project.
Here is some advice for writing the code for libckpt.so
. However,
if you prefer a different design, you are welcome to follow that.
Your code will only be tested on single-threaded targets. You are
not required to support multi-threaded applications. Similarly,
you are not required to support shared memory.
(Note that the memory layout of a process is stored in
/proc/PID/maps
.
The fields such as rwxp can be read as "read", "write", "execute"
and "private". Naturally, rwx refer to the permissions.
Private refers to private memory. If you see "s" instead
of "p", then it is shared memory.)
/proc/self/maps
(which is the memory
layout of the current process).
kill -12 PID
where PID
is the process id of the target process that you wish
to checkpoint. (Signal 12 is SIGUSR2
, reserved for end users.
signal()
to register the signal handler. We must write the call to
signal()
inside libckpt.so
. But it must be called before the
main()
routine of the end user. The solution is to define a constructor
function in C inside libckpt.so
. For the details of how to do this,
see, below: "Declaring constructor functions"
myckpt
.
gcc -static \
-Wl,-Ttext-segment=5000000 -Wl,-Tdata=5100000 -Wl,-Tbss=5200000 \
-o myrestart myrestart.c
Note: The gcc syntax -Wl,Ttext=...
means pass on the
parameter -Ttext=...
to the linker that will be called by gcc.
See man ld
for the Linux linker. ]
man ld
, you'll see that numbers like
5000000
should be interpreted as hexadecimal.
0x5300000
.
A good way to do that is to:
0x5300000 - 0x5301000
To create this memory, use mmap(). [ See: man mmap
]
$sp
or %sp
) from the initial stack to the
new stack. Then immediately make a function call to a new function.
The new function will then use a call frame in the new stack.
asm volatile ("mov %0,%%rsp" : : "g" (stack_ptr) : "memory");
restore_memory();
Note: you will see similar code near the beginning of the
function restart_fast_path():
https://github.com/dmtcp/dmtcp/blob/76ca86/src/mtcp/mtcp_restart.c#L398
/proc/self/maps
of the myrestart process;
[stack]
denoting
the current stack in use; and
munmap
on the stack region.
man mmap
)
setcontext()
to restore those as the working registers.
Note: See man setcontext
. Also, note that this will
restore your program counter (pc) and your stack
pointer (sp). So, you will automatically start using
the text segment and stack segment from your previously
checkpointed process.
man getcontext
and man setcontext
It will save register values to a struct of type
ucontext_t
.
You can copy the struct to your checkpoint header with code like
the following:
ucontext_t mycontext;
write_context_to_ckpt_header(&mycontext, sizeof mycontext);
signal()
on SIGUSR2 even before the end user's
main()
function is executed.
If this were C++, we would initialize some global variable to a
constructor, and we would call signal()
from inside the constructor.
A global constructor will always execute before main()
.
The equivalent trick in the C language is to use
__attribute__ ((constructor))
. This is a GNU extension that
is also supported by most other Linux compilers. The code is:
__attribute__ ((constructor))
void myconstructor() {
signal(SIGUSR2, ...);
}
The constructor attribute forces the myconstructor()
function to be
executed even before main()
.
Submission Instruction:
Your Makefile must include a target check
, which depends on
the files libckpt.so and hello.c. When you run make check
,
your program should do:
gcc [ADD YOUR FLAGS HERE] -o hello hello.c
(sleep 3 && kill -12 `pgrep -n hello` && \
sleep 2 && pkill -9 -n hello && make restart) &
./hello
Next, you must create a restart
target in Makefile. It should do:
./myrestart myckpt
Finally, you are responsible for writing the test program, hello.c.
Write code for hello.c that will go into an infinite loop that prints
to standard output the single character .
, and then sleeps for one second.
fflush(stdout)
to force the
output Normally, for efficiency, Linux will send the characters to the screen
only after it has seen the \n
character marking the end of a line
of output.
myrestart
using GDB, since
myrestart
resets the stack pointer, and descends into assembly language in order
to call setcontext, and change registers. Here are two advanced techniques
in GDB-based debugging that will help you.
(gdb) x/10i $pc-20
This means: examine the next 10 instructions, starting with the
value of the program counter ($pc) minus 20 bytes. Try this on
any GDB session, and it will quickly become intuitive.
(gdb) info registers
(gdb) apropos symbol
add-symbol-file -- Load symbols from FILE
...
The 'add-symbol-file' command requires a filename and an address:
(gdb) help add-symbol-file
...
Usage: add-symbol-file FILE ADDR [-s -s ...]
ADDR is the starting address of the file's text.
Clearly, the FILE for us is 'hello' (the executable compiled from hello.c).
ADDR should be the beginning of the text segment. You can guess
the address of the text segment:
For example, run "gdb ./hello", followed by
(gdb) break main
(gdb) run
(gdb) info proc mappings
If it's like my computer, you'll see the text begin at 0x400000. So:
(gdb) add-symbol-file ./hello 0x400000
Remember, your goal is to know about the text segment of ./hello,
prior to checkpointing.
Or else, you can use a general script. For this, copy:
util/gdb-add-symbol-file
from DMTCP to your local directory, or you can get it from the
developer's site:
https://raw.githubusercontent.com/dmtcp/dmtcp/master/util/gdb-add-symbol-file
Then run:
(gdb) shell ./gdb-add-symbol-file
and follow the instructions that it prints out.