Small edits
[c11tester.git] / snapshot.cc
1 #include <inttypes.h>
2 #include <sys/mman.h>
3 #include <unistd.h>
4 #include <signal.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/wait.h>
9
10 #include "hashtable.h"
11 #include "snapshot.h"
12 #include "mymemory.h"
13 #include "common.h"
14 #include "context.h"
15 #include "model.h"
16
17
18 #define SHARED_MEMORY_DEFAULT  (200 * ((size_t)1 << 20))        // 100mb for the shared memory
19 #define STACK_SIZE_DEFAULT      (((size_t)1 << 20) * 20)        // 20 mb out of the above 100 mb for my stack
20
21 struct fork_snapshotter {
22         /** @brief Pointer to the shared (non-snapshot) memory heap base
23          * (NOTE: this has size SHARED_MEMORY_DEFAULT - sizeof(*fork_snap)) */
24         void *mSharedMemoryBase;
25
26         /** @brief Pointer to the shared (non-snapshot) stack region */
27         void *mStackBase;
28
29         /** @brief Size of the shared stack */
30         size_t mStackSize;
31
32         /**
33          * @brief Stores the ID that we are attempting to roll back to
34          *
35          * Used in inter-process communication so that each process can
36          * determine whether or not to take over execution (w/ matching ID) or
37          * exit (we're rolling back even further). Dubiously marked 'volatile'
38          * to prevent compiler optimizations from messing with the
39          * inter-process behavior.
40          */
41         volatile snapshot_id mIDToRollback;
42
43
44
45         /** @brief Inter-process tracking of the next snapshot ID */
46         snapshot_id currSnapShotID;
47 };
48
49 static struct fork_snapshotter *fork_snap = NULL;
50 ucontext_t shared_ctxt;
51
52 /** @statics
53  *   These variables are necessary because the stack is shared region and
54  *   there exists a race between all processes executing the same function.
55  *   To avoid the problem above, we require variables allocated in 'safe' regions.
56  *   The bug was actually observed with the forkID, these variables below are
57  *   used to indicate the various contexts to which to switch to.
58  *
59  *   @private_ctxt: the context which is internal to the current process. Used
60  *   for running the internal snapshot/rollback loop.
61  *   @exit_ctxt: a special context used just for exiting from a process (so we
62  *   can use swapcontext() instead of setcontext() + hacks)
63  *   @snapshotid: it is a running counter for the various forked processes
64  *   snapshotid. it is incremented and set in a persistently shared record
65  */
66 static ucontext_t private_ctxt;
67 static snapshot_id snapshotid = 0;
68
69 /**
70  * @brief Create a new context, with a given stack and entry function
71  * @param ctxt The context structure to fill
72  * @param stack The stack to run the new context in
73  * @param stacksize The size of the stack
74  * @param func The entry point function for the context
75  */
76 static void create_context(ucontext_t *ctxt, void *stack, size_t stacksize,
77                                                                                                          void (*func)(void))
78 {
79         getcontext(ctxt);
80         ctxt->uc_stack.ss_sp = stack;
81         ctxt->uc_stack.ss_size = stacksize;
82         ctxt->uc_link = NULL;
83         makecontext(ctxt, func, 0);
84 }
85
86 /** @brief An empty function, used for an "empty" context which just exits a
87  *  process */
88 static void fork_exit()
89 {
90         _Exit(EXIT_SUCCESS);
91 }
92
93 static void createSharedMemory()
94 {
95         //step 1. create shared memory.
96         void *memMapBase = mmap(0, SHARED_MEMORY_DEFAULT + STACK_SIZE_DEFAULT, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
97         if (memMapBase == MAP_FAILED) {
98                 perror("mmap");
99                 exit(EXIT_FAILURE);
100         }
101
102         //Setup snapshot record at top of free region
103         fork_snap = (struct fork_snapshotter *)memMapBase;
104         fork_snap->mSharedMemoryBase = (void *)((uintptr_t)memMapBase + sizeof(*fork_snap));
105         fork_snap->mStackBase = (void *)((uintptr_t)memMapBase + SHARED_MEMORY_DEFAULT);
106         fork_snap->mStackSize = STACK_SIZE_DEFAULT;
107         fork_snap->mIDToRollback = -1;
108         fork_snap->currSnapShotID = 0;
109 }
110
111 /**
112  * Create a new mspace pointer for the non-snapshotting (i.e., inter-process
113  * shared) memory region. Only for fork-based snapshotting.
114  *
115  * @return The shared memory mspace
116  */
117 mspace create_shared_mspace()
118 {
119         if (!fork_snap)
120                 createSharedMemory();
121         return create_mspace_with_base((void *)(fork_snap->mSharedMemoryBase), SHARED_MEMORY_DEFAULT - sizeof(*fork_snap), 1);
122 }
123
124 static void fork_snapshot_init(unsigned int numbackingpages,
125                                                                                                                          unsigned int numsnapshots, unsigned int nummemoryregions,
126                                                                                                                          unsigned int numheappages)
127 {
128         if (!fork_snap)
129                 createSharedMemory();
130
131         model_snapshot_space = create_mspace(numheappages * PAGESIZE, 1);
132 }
133
134 volatile int modellock = 0;
135
136 static void fork_loop() {
137         /* switch back here when takesnapshot is called */
138         snapshotid = fork_snap->currSnapShotID;
139         if (model->params.nofork) {
140                 setcontext(&shared_ctxt);
141                 _Exit(EXIT_SUCCESS);
142         }
143
144         while (true) {
145                 pid_t forkedID;
146                 fork_snap->currSnapShotID = snapshotid + 1;
147
148                 modellock = 1;
149                 forkedID = fork();
150                 modellock = 0;
151
152                 if (0 == forkedID) {
153                         setcontext(&shared_ctxt);
154                 } else {
155                         DEBUG("parent PID: %d, child PID: %d, snapshot ID: %d\n",
156                                                 getpid(), forkedID, snapshotid);
157
158                         while (waitpid(forkedID, NULL, 0) < 0) {
159                                 /* waitpid() may be interrupted */
160                                 if (errno != EINTR) {
161                                         perror("waitpid");
162                                         exit(EXIT_FAILURE);
163                                 }
164                         }
165
166                         if (fork_snap->mIDToRollback != snapshotid)
167                                 _Exit(EXIT_SUCCESS);
168                 }
169         }
170 }
171
172 static void fork_startExecution() {
173         /* switch to a new entryPoint context, on a new stack */
174         create_context(&private_ctxt, snapshot_calloc(STACK_SIZE_DEFAULT, 1), STACK_SIZE_DEFAULT, fork_loop);
175 }
176
177 static snapshot_id fork_take_snapshot() {
178         model_swapcontext(&shared_ctxt, &private_ctxt);
179         DEBUG("TAKESNAPSHOT RETURN\n");
180         fork_snap->mIDToRollback = -1;
181         return snapshotid;
182 }
183
184 static void fork_roll_back(snapshot_id theID)
185 {
186         DEBUG("Rollback\n");
187         fork_snap->mIDToRollback = theID;
188         fork_exit();
189 }
190
191 /**
192  * @brief Initializes the snapshot system
193  * @param entryPoint the function that should run the program.
194  */
195 void snapshot_system_init(unsigned int numbackingpages,
196                                                                                                         unsigned int numsnapshots, unsigned int nummemoryregions,
197                                                                                                         unsigned int numheappages)
198 {
199         fork_snapshot_init(numbackingpages, numsnapshots, nummemoryregions, numheappages);
200 }
201
202 void startExecution() {
203         fork_startExecution();
204 }
205
206 /** Takes a snapshot of memory.
207  * @return The snapshot identifier.
208  */
209 snapshot_id take_snapshot()
210 {
211         return fork_take_snapshot();
212 }
213
214 /** Rolls the memory state back to the given snapshot identifier.
215  *  @param theID is the snapshot identifier to rollback to.
216  */
217 void snapshot_roll_back(snapshot_id theID)
218 {
219         fork_roll_back(theID);
220 }