action: refactor ATOMIC_READ printing
[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 #include <ucontext.h>
10
11 #include "hashtable.h"
12 #include "snapshot.h"
13 #include "snapshotimp.h"
14 #include "mymemory.h"
15 #include "common.h"
16
17 #define FAILURE(mesg) { model_print("failed in the API: %s with errno relative message: %s\n", mesg, strerror( errno ) ); exit(EXIT_FAILURE); }
18
19 #ifdef CONFIG_SSDEBUG
20 #define SSDEBUG         model_print
21 #else
22 #define SSDEBUG(...)    do { } while (0)
23 #endif
24
25 /* extern declaration definition */
26 struct SnapShot * snapshotrecord = NULL;
27
28 /** PageAlignedAdressUpdate return a page aligned address for the
29  * address being added as a side effect the numBytes are also changed.
30  */
31 static void * PageAlignAddressUpward(void * addr) {
32         return (void *)((((uintptr_t)addr)+PAGESIZE-1)&~(PAGESIZE-1));
33 }
34
35 #if !USE_MPROTECT_SNAPSHOT
36 /** @statics
37 *   These variables are necessary because the stack is shared region and
38 *   there exists a race between all processes executing the same function.
39 *   To avoid the problem above, we require variables allocated in 'safe' regions.
40 *   The bug was actually observed with the forkID, these variables below are
41 *   used to indicate the various contexts to which to switch to.
42 *
43 *   @savedSnapshotContext: contains the point to which takesnapshot() call should switch to.
44 *   @savedUserSnapshotContext: contains the point to which the process whose snapshotid is equal to the rollbackid should switch to
45 *   @snapshotid: it is a running counter for the various forked processes snapshotid. it is incremented and set in a persistently shared record
46 */
47 static ucontext_t savedSnapshotContext;
48 static ucontext_t savedUserSnapshotContext;
49 static snapshot_id snapshotid = 0;
50
51 #else /* USE_MPROTECT_SNAPSHOT */
52
53 /** ReturnPageAlignedAddress returns a page aligned address for the
54  * address being added as a side effect the numBytes are also changed.
55  */
56 static void * ReturnPageAlignedAddress(void * addr) {
57         return (void *)(((uintptr_t)addr)&~(PAGESIZE-1));
58 }
59
60 /** The initSnapShotRecord method initialized the snapshotting data
61  *  structures for the mprotect based snapshot.
62  */
63 static void initSnapShotRecord(unsigned int numbackingpages, unsigned int numsnapshots, unsigned int nummemoryregions) {
64         snapshotrecord=( struct SnapShot * )model_malloc(sizeof(struct SnapShot));
65         snapshotrecord->regionsToSnapShot=( struct MemoryRegion * )model_malloc(sizeof(struct MemoryRegion)*nummemoryregions);
66         snapshotrecord->backingStoreBasePtr= ( struct SnapShotPage * )model_malloc( sizeof( struct SnapShotPage ) * (numbackingpages + 1) );
67         //Page align the backingstorepages
68         snapshotrecord->backingStore=( struct SnapShotPage * )PageAlignAddressUpward(snapshotrecord->backingStoreBasePtr);
69         snapshotrecord->backingRecords=( struct BackingPageRecord * )model_malloc(sizeof(struct BackingPageRecord)*numbackingpages);
70         snapshotrecord->snapShots= ( struct SnapShotRecord * )model_malloc(sizeof(struct SnapShotRecord)*numsnapshots);
71         snapshotrecord->lastSnapShot=0;
72         snapshotrecord->lastBackingPage=0;
73         snapshotrecord->lastRegion=0;
74         snapshotrecord->maxRegions=nummemoryregions;
75         snapshotrecord->maxBackingPages=numbackingpages;
76         snapshotrecord->maxSnapShots=numsnapshots;
77 }
78
79 /** HandlePF is the page fault handler for mprotect based snapshotting
80  * algorithm.
81  */
82 static void HandlePF( int sig, siginfo_t *si, void * unused){
83         if( si->si_code == SEGV_MAPERR ){
84                 model_print("Real Fault at %p\n", si->si_addr);
85                 print_trace();
86                 model_print("For debugging, place breakpoint at: %s:%d\n",
87                                 __FILE__, __LINE__);
88                 exit( EXIT_FAILURE );
89         }
90         void* addr = ReturnPageAlignedAddress(si->si_addr);
91
92         unsigned int backingpage=snapshotrecord->lastBackingPage++; //Could run out of pages...
93         if (backingpage==snapshotrecord->maxBackingPages) {
94                 model_print("Out of backing pages at %p\n", si->si_addr);
95                 exit( EXIT_FAILURE );
96         }
97
98         //copy page
99         memcpy(&(snapshotrecord->backingStore[backingpage]), addr, sizeof(struct SnapShotPage));
100         //remember where to copy page back to
101         snapshotrecord->backingRecords[backingpage].basePtrOfPage=addr;
102         //set protection to read/write
103         if (mprotect( addr, sizeof(struct SnapShotPage), PROT_READ | PROT_WRITE )) {
104                 perror("mprotect");
105                 // Handle error by quitting?
106         }
107 }
108 #endif /* USE_MPROTECT_SNAPSHOT */
109
110 #if !USE_MPROTECT_SNAPSHOT
111 void createSharedMemory(){
112         //step 1. create shared memory.
113         void * memMapBase = mmap( 0, SHARED_MEMORY_DEFAULT + STACK_SIZE_DEFAULT, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0 );
114         if( MAP_FAILED == memMapBase )
115                 FAILURE("mmap");
116
117         //Setup snapshot record at top of free region
118         snapshotrecord = ( struct SnapShot * )memMapBase;
119         snapshotrecord->mSharedMemoryBase = (void *)((uintptr_t)memMapBase + sizeof(struct SnapShot));
120         snapshotrecord->mStackBase = (void *)((uintptr_t)memMapBase + SHARED_MEMORY_DEFAULT);
121         snapshotrecord->mStackSize = STACK_SIZE_DEFAULT;
122         snapshotrecord->mIDToRollback = -1;
123         snapshotrecord->currSnapShotID = 0;
124 }
125 #endif
126
127
128 /** The initSnapshotLibrary function initializes the snapshot library.
129  *  @param entryPoint the function that should run the program.
130  */
131 #if USE_MPROTECT_SNAPSHOT
132
133 void initSnapshotLibrary(unsigned int numbackingpages,
134                 unsigned int numsnapshots, unsigned int nummemoryregions,
135                 unsigned int numheappages, VoidFuncPtr entryPoint) {
136         /* Setup a stack for our signal handler....  */
137         stack_t ss;
138         ss.ss_sp = PageAlignAddressUpward(model_malloc(SIGSTACKSIZE+PAGESIZE-1));
139         ss.ss_size = SIGSTACKSIZE;
140         ss.ss_flags = 0;
141         sigaltstack(&ss, NULL);
142
143         struct sigaction sa;
144         sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_RESTART | SA_ONSTACK;
145         sigemptyset( &sa.sa_mask );
146         sa.sa_sigaction = HandlePF;
147 #ifdef MAC
148         if( sigaction( SIGBUS, &sa, NULL ) == -1 ){
149                 model_print("SIGACTION CANNOT BE INSTALLED\n");
150                 exit(EXIT_FAILURE);
151         }
152 #endif
153         if( sigaction( SIGSEGV, &sa, NULL ) == -1 ){
154                 model_print("SIGACTION CANNOT BE INSTALLED\n");
155                 exit(EXIT_FAILURE);
156         }
157
158         initSnapShotRecord(numbackingpages, numsnapshots, nummemoryregions);
159
160         // EVIL HACK: We need to make sure that calls into the HandlePF method don't cause dynamic links
161         // The problem is that we end up protecting state in the dynamic linker...
162         // Solution is to call our signal handler before we start protecting stuff...
163
164         siginfo_t si;
165         memset(&si, 0, sizeof(si));
166         si.si_addr=ss.ss_sp;
167         HandlePF(SIGSEGV, &si, NULL);
168         snapshotrecord->lastBackingPage--; //remove the fake page we copied
169
170         void *basemySpace = model_malloc((numheappages+1)*PAGESIZE);
171         void * pagealignedbase=PageAlignAddressUpward(basemySpace);
172         user_snapshot_space = create_mspace_with_base(pagealignedbase, numheappages * PAGESIZE, 1);
173         addMemoryRegionToSnapShot(pagealignedbase, numheappages);
174
175         void *base_model_snapshot_space = model_malloc((numheappages + 1) * PAGESIZE);
176         pagealignedbase = PageAlignAddressUpward(base_model_snapshot_space);
177         model_snapshot_space = create_mspace_with_base(pagealignedbase, numheappages * PAGESIZE, 1);
178         addMemoryRegionToSnapShot(pagealignedbase, numheappages);
179
180         entryPoint();
181 }
182 #else
183 void initSnapshotLibrary(unsigned int numbackingpages,
184                 unsigned int numsnapshots, unsigned int nummemoryregions,
185                 unsigned int numheappages, VoidFuncPtr entryPoint) {
186         if (!snapshotrecord)
187                 createSharedMemory();
188
189         void *base_model_snapshot_space = malloc((numheappages + 1) * PAGESIZE);
190         void *pagealignedbase = PageAlignAddressUpward(base_model_snapshot_space);
191         model_snapshot_space = create_mspace_with_base(pagealignedbase, numheappages * PAGESIZE, 1);
192
193         //step 2 setup the stack context.
194         ucontext_t newContext;
195         getcontext( &newContext );
196         newContext.uc_stack.ss_sp = snapshotrecord->mStackBase;
197         newContext.uc_stack.ss_size = STACK_SIZE_DEFAULT;
198         makecontext( &newContext, entryPoint, 0 );
199         /* switch to a new entryPoint context, on a new stack */
200         swapcontext(&savedSnapshotContext, &newContext);
201
202         /* switch back here when takesnapshot is called */
203         pid_t forkedID = 0;
204         snapshotid = snapshotrecord->currSnapShotID;
205         /* This bool indicates that the current process's snapshotid is same
206                  as the id to which the rollback needs to occur */
207
208         bool rollback = false;
209         while( true ){
210                 snapshotrecord->currSnapShotID=snapshotid+1;
211                 forkedID = fork();
212
213                 if( 0 == forkedID ){
214                         /* If the rollback bool is set, switch to the context we need to
215                                  return to during a rollback. */
216                         if( rollback) {
217                                 setcontext( &( snapshotrecord->mContextToRollback ) );
218                         } else {
219                                 /*Child process which is forked as a result of takesnapshot
220                                         call should switch back to the takesnapshot context*/
221                                 setcontext( &savedUserSnapshotContext );
222                         }
223                 } else {
224                         int status;
225                         int retVal;
226
227                         SSDEBUG("The process id of child is %d and the process id of this process is %d and snapshot id is %d\n",
228                                 forkedID, getpid(), snapshotid );
229
230                         do {
231                                 retVal=waitpid( forkedID, &status, 0 );
232                         } while( -1 == retVal && errno == EINTR );
233
234                         if( snapshotrecord->mIDToRollback != snapshotid ) {
235                                 exit(EXIT_SUCCESS);
236                         }
237                         rollback = true;
238                 }
239         }
240 }
241 #endif
242
243 /** The addMemoryRegionToSnapShot function assumes that addr is page aligned.
244  */
245 void addMemoryRegionToSnapShot( void * addr, unsigned int numPages) {
246 #if USE_MPROTECT_SNAPSHOT
247         unsigned int memoryregion=snapshotrecord->lastRegion++;
248         if (memoryregion==snapshotrecord->maxRegions) {
249                 model_print("Exceeded supported number of memory regions!\n");
250                 exit(EXIT_FAILURE);
251         }
252
253         snapshotrecord->regionsToSnapShot[ memoryregion ].basePtr=addr;
254         snapshotrecord->regionsToSnapShot[ memoryregion ].sizeInPages=numPages;
255 #endif //NOT REQUIRED IN THE CASE OF FORK BASED SNAPSHOTS.
256 }
257
258 /** The takeSnapshot function takes a snapshot.
259  * @return The snapshot identifier.
260  */
261 snapshot_id takeSnapshot( ){
262 #if USE_MPROTECT_SNAPSHOT
263         for(unsigned int region=0; region<snapshotrecord->lastRegion;region++) {
264                 if( mprotect(snapshotrecord->regionsToSnapShot[region].basePtr, snapshotrecord->regionsToSnapShot[region].sizeInPages*sizeof(struct SnapShotPage), PROT_READ ) == -1 ){
265                         perror("mprotect");
266                         model_print("Failed to mprotect inside of takeSnapShot\n");
267                         exit(EXIT_FAILURE);
268                 }
269         }
270         unsigned int snapshot=snapshotrecord->lastSnapShot++;
271         if (snapshot==snapshotrecord->maxSnapShots) {
272                 model_print("Out of snapshots\n");
273                 exit(EXIT_FAILURE);
274         }
275         snapshotrecord->snapShots[snapshot].firstBackingPage=snapshotrecord->lastBackingPage;
276
277         return snapshot;
278 #else
279         swapcontext( &savedUserSnapshotContext, &savedSnapshotContext );
280         SSDEBUG("TAKESNAPSHOT RETURN\n");
281         return snapshotid;
282 #endif
283 }
284
285 /** The rollBack function rollback to the given snapshot identifier.
286  *  @param theID is the snapshot identifier to rollback to.
287  */
288 void rollBack( snapshot_id theID ){
289 #if USE_MPROTECT_SNAPSHOT==2
290         if (snapshotrecord->lastSnapShot==(theID+1)) {
291                 for(unsigned int page=snapshotrecord->snapShots[theID].firstBackingPage; page<snapshotrecord->lastBackingPage; page++) {
292                         memcpy(snapshotrecord->backingRecords[page].basePtrOfPage, &snapshotrecord->backingStore[page], sizeof(struct SnapShotPage));
293                 }
294                 return;
295         }
296 #endif
297
298 #if USE_MPROTECT_SNAPSHOT
299         HashTable< void *, bool, uintptr_t, 4, model_malloc, model_calloc, model_free> duplicateMap;
300         for(unsigned int region=0; region<snapshotrecord->lastRegion;region++) {
301                 if( mprotect(snapshotrecord->regionsToSnapShot[region].basePtr, snapshotrecord->regionsToSnapShot[region].sizeInPages*sizeof(struct SnapShotPage), PROT_READ | PROT_WRITE ) == -1 ){
302                         perror("mprotect");
303                         model_print("Failed to mprotect inside of takeSnapShot\n");
304                         exit(EXIT_FAILURE);
305                 }
306         }
307         for(unsigned int page=snapshotrecord->snapShots[theID].firstBackingPage; page<snapshotrecord->lastBackingPage; page++) {
308                 if( !duplicateMap.contains(snapshotrecord->backingRecords[page].basePtrOfPage )) {
309                         duplicateMap.put(snapshotrecord->backingRecords[page].basePtrOfPage, true);
310                         memcpy(snapshotrecord->backingRecords[page].basePtrOfPage, &snapshotrecord->backingStore[page], sizeof(struct SnapShotPage));
311                 }
312         }
313         snapshotrecord->lastSnapShot=theID;
314         snapshotrecord->lastBackingPage=snapshotrecord->snapShots[theID].firstBackingPage;
315         takeSnapshot(); //Make sure current snapshot is still good...All later ones are cleared
316 #else
317         snapshotrecord->mIDToRollback = theID;
318         volatile int sTemp = 0;
319         getcontext( &snapshotrecord->mContextToRollback );
320         /*
321          * This is used to quit the process on rollback, so that the process
322          * which needs to rollback can quit allowing the process whose
323          * snapshotid matches the rollbackid to switch to this context and
324          * continue....
325          */
326         if( !sTemp ){
327                 sTemp = 1;
328                 SSDEBUG("Invoked rollback\n");
329                 exit(EXIT_SUCCESS);
330         }
331         /*
332          * This fix obviates the need for a finalize call. hence less dependences for model-checker....
333          *
334          */
335         snapshotrecord->mIDToRollback = -1;
336 #endif
337 }
338