11 #include "hashtable.h"
16 #define FAILURE(mesg) { model_print("failed in the API: %s with errno relative message: %s\n", mesg, strerror(errno)); exit(EXIT_FAILURE); }
18 #if USE_MPROTECT_SNAPSHOT
19 /* Each snapshotrecord lists the firstbackingpage that must be written to
20 * revert to that snapshot */
21 struct SnapShotRecord {
22 unsigned int firstBackingPage;
25 /** @brief Backing store page */
26 typedef unsigned char snapshot_page_t[PAGESIZE];
28 /* List the base address of the corresponding page in the backing store so we
29 * know where to copy it to */
30 struct BackingPageRecord {
34 /* Struct for each memory region */
36 void *basePtr; // base of memory region
37 int sizeInPages; // size of memory region in pages
40 /* Primary struct for snapshotting system */
42 struct MemoryRegion *regionsToSnapShot; //This pointer references an array of memory regions to snapshot
43 snapshot_page_t *backingStore; //This pointer references an array of snapshotpage's that form the backing store
44 void *backingStoreBasePtr; //This pointer references an array of snapshotpage's that form the backing store
45 struct BackingPageRecord *backingRecords; //This pointer references an array of backingpagerecord's (same number of elements as backingstore
46 struct SnapShotRecord *snapShots; //This pointer references the snapshot array
48 unsigned int lastSnapShot; //Stores the next snapshot record we should use
49 unsigned int lastBackingPage; //Stores the next backingpage we should use
50 unsigned int lastRegion; //Stores the next memory region to be used
52 unsigned int maxRegions; //Stores the max number of memory regions we support
53 unsigned int maxBackingPages; //Stores the total number of backing pages
54 unsigned int maxSnapShots; //Stores the total number of snapshots we allow
61 #define SHARED_MEMORY_DEFAULT (100 * ((size_t)1 << 20)) // 100mb for the shared memory
62 #define STACK_SIZE_DEFAULT (((size_t)1 << 20) * 20) // 20 mb out of the above 100 mb for my stack
65 void *mSharedMemoryBase;
68 volatile snapshot_id mIDToRollback;
69 ucontext_t mContextToRollback;
70 snapshot_id currSnapShotID;
74 static struct SnapShot *snapshotrecord = NULL;
76 /** PageAlignedAdressUpdate return a page aligned address for the
77 * address being added as a side effect the numBytes are also changed.
79 static void * PageAlignAddressUpward(void *addr)
81 return (void *)((((uintptr_t)addr) + PAGESIZE - 1) & ~(PAGESIZE - 1));
84 #if !USE_MPROTECT_SNAPSHOT
86 * These variables are necessary because the stack is shared region and
87 * there exists a race between all processes executing the same function.
88 * To avoid the problem above, we require variables allocated in 'safe' regions.
89 * The bug was actually observed with the forkID, these variables below are
90 * used to indicate the various contexts to which to switch to.
92 * @savedSnapshotContext: contains the point to which takesnapshot() call should switch to.
93 * @savedUserSnapshotContext: contains the point to which the process whose snapshotid is equal to the rollbackid should switch to
94 * @snapshotid: it is a running counter for the various forked processes snapshotid. it is incremented and set in a persistently shared record
96 static ucontext_t savedSnapshotContext;
97 static ucontext_t savedUserSnapshotContext;
98 static snapshot_id snapshotid = 0;
100 #else /* USE_MPROTECT_SNAPSHOT */
102 /** ReturnPageAlignedAddress returns a page aligned address for the
103 * address being added as a side effect the numBytes are also changed.
105 static void * ReturnPageAlignedAddress(void *addr)
107 return (void *)(((uintptr_t)addr) & ~(PAGESIZE - 1));
110 /** The initSnapShotRecord method initialized the snapshotting data
111 * structures for the mprotect based snapshot.
113 static void initSnapShotRecord(unsigned int numbackingpages, unsigned int numsnapshots, unsigned int nummemoryregions)
115 snapshotrecord = (struct SnapShot *)model_malloc(sizeof(struct SnapShot));
116 snapshotrecord->regionsToSnapShot = (struct MemoryRegion *)model_malloc(sizeof(struct MemoryRegion) * nummemoryregions);
117 snapshotrecord->backingStoreBasePtr = (void *)model_malloc(sizeof(snapshot_page_t) * (numbackingpages + 1));
118 //Page align the backingstorepages
119 snapshotrecord->backingStore = (snapshot_page_t *)PageAlignAddressUpward(snapshotrecord->backingStoreBasePtr);
120 snapshotrecord->backingRecords = (struct BackingPageRecord *)model_malloc(sizeof(struct BackingPageRecord) * numbackingpages);
121 snapshotrecord->snapShots = (struct SnapShotRecord *)model_malloc(sizeof(struct SnapShotRecord) * numsnapshots);
122 snapshotrecord->lastSnapShot = 0;
123 snapshotrecord->lastBackingPage = 0;
124 snapshotrecord->lastRegion = 0;
125 snapshotrecord->maxRegions = nummemoryregions;
126 snapshotrecord->maxBackingPages = numbackingpages;
127 snapshotrecord->maxSnapShots = numsnapshots;
130 /** HandlePF is the page fault handler for mprotect based snapshotting
133 static void HandlePF(int sig, siginfo_t *si, void *unused)
135 if (si->si_code == SEGV_MAPERR) {
136 model_print("Real Fault at %p\n", si->si_addr);
138 model_print("For debugging, place breakpoint at: %s:%d\n",
142 void* addr = ReturnPageAlignedAddress(si->si_addr);
144 unsigned int backingpage = snapshotrecord->lastBackingPage++; //Could run out of pages...
145 if (backingpage == snapshotrecord->maxBackingPages) {
146 model_print("Out of backing pages at %p\n", si->si_addr);
151 memcpy(&(snapshotrecord->backingStore[backingpage]), addr, sizeof(snapshot_page_t));
152 //remember where to copy page back to
153 snapshotrecord->backingRecords[backingpage].basePtrOfPage = addr;
154 //set protection to read/write
155 if (mprotect(addr, sizeof(snapshot_page_t), PROT_READ | PROT_WRITE)) {
157 // Handle error by quitting?
160 #endif /* USE_MPROTECT_SNAPSHOT */
162 #if !USE_MPROTECT_SNAPSHOT
163 static void createSharedMemory()
165 //step 1. create shared memory.
166 void *memMapBase = mmap(0, SHARED_MEMORY_DEFAULT + STACK_SIZE_DEFAULT, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
167 if (MAP_FAILED == memMapBase)
170 //Setup snapshot record at top of free region
171 snapshotrecord = (struct SnapShot *)memMapBase;
172 snapshotrecord->mSharedMemoryBase = (void *)((uintptr_t)memMapBase + sizeof(struct SnapShot));
173 snapshotrecord->mStackBase = (void *)((uintptr_t)memMapBase + SHARED_MEMORY_DEFAULT);
174 snapshotrecord->mStackSize = STACK_SIZE_DEFAULT;
175 snapshotrecord->mIDToRollback = -1;
176 snapshotrecord->currSnapShotID = 0;
180 * Create a new mspace pointer for the non-snapshotting (i.e., inter-process
181 * shared) memory region. Only for fork-based snapshotting.
183 * @return The shared memory mspace
185 mspace create_shared_mspace()
188 createSharedMemory();
189 return create_mspace_with_base((void *)(snapshotrecord->mSharedMemoryBase), SHARED_MEMORY_DEFAULT - sizeof(struct SnapShot), 1);
194 /** The initSnapshotLibrary function initializes the snapshot library.
195 * @param entryPoint the function that should run the program.
197 #if USE_MPROTECT_SNAPSHOT
199 void initSnapshotLibrary(unsigned int numbackingpages,
200 unsigned int numsnapshots, unsigned int nummemoryregions,
201 unsigned int numheappages, VoidFuncPtr entryPoint)
203 /* Setup a stack for our signal handler.... */
205 ss.ss_sp = PageAlignAddressUpward(model_malloc(SIGSTACKSIZE + PAGESIZE - 1));
206 ss.ss_size = SIGSTACKSIZE;
208 sigaltstack(&ss, NULL);
211 sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_RESTART | SA_ONSTACK;
212 sigemptyset(&sa.sa_mask);
213 sa.sa_sigaction = HandlePF;
215 if (sigaction(SIGBUS, &sa, NULL) == -1) {
216 model_print("SIGACTION CANNOT BE INSTALLED\n");
220 if (sigaction(SIGSEGV, &sa, NULL) == -1) {
221 model_print("SIGACTION CANNOT BE INSTALLED\n");
225 initSnapShotRecord(numbackingpages, numsnapshots, nummemoryregions);
227 // EVIL HACK: We need to make sure that calls into the HandlePF method don't cause dynamic links
228 // The problem is that we end up protecting state in the dynamic linker...
229 // Solution is to call our signal handler before we start protecting stuff...
232 memset(&si, 0, sizeof(si));
233 si.si_addr = ss.ss_sp;
234 HandlePF(SIGSEGV, &si, NULL);
235 snapshotrecord->lastBackingPage--; //remove the fake page we copied
237 void *basemySpace = model_malloc((numheappages + 1) * PAGESIZE);
238 void *pagealignedbase = PageAlignAddressUpward(basemySpace);
239 user_snapshot_space = create_mspace_with_base(pagealignedbase, numheappages * PAGESIZE, 1);
240 addMemoryRegionToSnapShot(pagealignedbase, numheappages);
242 void *base_model_snapshot_space = model_malloc((numheappages + 1) * PAGESIZE);
243 pagealignedbase = PageAlignAddressUpward(base_model_snapshot_space);
244 model_snapshot_space = create_mspace_with_base(pagealignedbase, numheappages * PAGESIZE, 1);
245 addMemoryRegionToSnapShot(pagealignedbase, numheappages);
250 void initSnapshotLibrary(unsigned int numbackingpages,
251 unsigned int numsnapshots, unsigned int nummemoryregions,
252 unsigned int numheappages, VoidFuncPtr entryPoint)
255 createSharedMemory();
257 void *base_model_snapshot_space = malloc((numheappages + 1) * PAGESIZE);
258 void *pagealignedbase = PageAlignAddressUpward(base_model_snapshot_space);
259 model_snapshot_space = create_mspace_with_base(pagealignedbase, numheappages * PAGESIZE, 1);
261 //step 2 setup the stack context.
262 ucontext_t newContext;
263 getcontext(&newContext);
264 newContext.uc_stack.ss_sp = snapshotrecord->mStackBase;
265 newContext.uc_stack.ss_size = STACK_SIZE_DEFAULT;
266 makecontext(&newContext, entryPoint, 0);
267 /* switch to a new entryPoint context, on a new stack */
268 swapcontext(&savedSnapshotContext, &newContext);
270 /* switch back here when takesnapshot is called */
272 snapshotid = snapshotrecord->currSnapShotID;
273 /* This bool indicates that the current process's snapshotid is same
274 as the id to which the rollback needs to occur */
276 bool rollback = false;
278 snapshotrecord->currSnapShotID = snapshotid + 1;
282 /* If the rollback bool is set, switch to the context we need to
283 return to during a rollback. */
285 setcontext(&(snapshotrecord->mContextToRollback));
287 /*Child process which is forked as a result of takesnapshot
288 call should switch back to the takesnapshot context*/
289 setcontext(&savedUserSnapshotContext);
295 DEBUG("The process id of child is %d and the process id of this process is %d and snapshot id is %d\n",
296 forkedID, getpid(), snapshotid);
299 retVal = waitpid(forkedID, &status, 0);
300 } while (-1 == retVal && errno == EINTR);
302 if (snapshotrecord->mIDToRollback != snapshotid) {
311 /** The addMemoryRegionToSnapShot function assumes that addr is page aligned.
313 void addMemoryRegionToSnapShot(void *addr, unsigned int numPages)
315 #if USE_MPROTECT_SNAPSHOT
316 unsigned int memoryregion = snapshotrecord->lastRegion++;
317 if (memoryregion == snapshotrecord->maxRegions) {
318 model_print("Exceeded supported number of memory regions!\n");
322 snapshotrecord->regionsToSnapShot[memoryregion].basePtr = addr;
323 snapshotrecord->regionsToSnapShot[memoryregion].sizeInPages = numPages;
324 #endif //NOT REQUIRED IN THE CASE OF FORK BASED SNAPSHOTS.
327 /** The takeSnapshot function takes a snapshot.
328 * @return The snapshot identifier.
330 snapshot_id takeSnapshot()
332 #if USE_MPROTECT_SNAPSHOT
333 for (unsigned int region = 0; region < snapshotrecord->lastRegion; region++) {
334 if (mprotect(snapshotrecord->regionsToSnapShot[region].basePtr, snapshotrecord->regionsToSnapShot[region].sizeInPages * sizeof(snapshot_page_t), PROT_READ) == -1) {
336 model_print("Failed to mprotect inside of takeSnapShot\n");
340 unsigned int snapshot = snapshotrecord->lastSnapShot++;
341 if (snapshot == snapshotrecord->maxSnapShots) {
342 model_print("Out of snapshots\n");
345 snapshotrecord->snapShots[snapshot].firstBackingPage = snapshotrecord->lastBackingPage;
349 swapcontext(&savedUserSnapshotContext, &savedSnapshotContext);
350 DEBUG("TAKESNAPSHOT RETURN\n");
355 /** The rollBack function rollback to the given snapshot identifier.
356 * @param theID is the snapshot identifier to rollback to.
358 void rollBack(snapshot_id theID)
360 #if USE_MPROTECT_SNAPSHOT == 2
361 if (snapshotrecord->lastSnapShot == (theID + 1)) {
362 for (unsigned int page = snapshotrecord->snapShots[theID].firstBackingPage; page < snapshotrecord->lastBackingPage; page++) {
363 memcpy(snapshotrecord->backingRecords[page].basePtrOfPage, &snapshotrecord->backingStore[page], sizeof(snapshot_page_t));
369 #if USE_MPROTECT_SNAPSHOT
370 HashTable< void *, bool, uintptr_t, 4, model_malloc, model_calloc, model_free> duplicateMap;
371 for (unsigned int region = 0; region < snapshotrecord->lastRegion; region++) {
372 if (mprotect(snapshotrecord->regionsToSnapShot[region].basePtr, snapshotrecord->regionsToSnapShot[region].sizeInPages * sizeof(snapshot_page_t), PROT_READ | PROT_WRITE) == -1) {
374 model_print("Failed to mprotect inside of takeSnapShot\n");
378 for (unsigned int page = snapshotrecord->snapShots[theID].firstBackingPage; page < snapshotrecord->lastBackingPage; page++) {
379 if (!duplicateMap.contains(snapshotrecord->backingRecords[page].basePtrOfPage)) {
380 duplicateMap.put(snapshotrecord->backingRecords[page].basePtrOfPage, true);
381 memcpy(snapshotrecord->backingRecords[page].basePtrOfPage, &snapshotrecord->backingStore[page], sizeof(snapshot_page_t));
384 snapshotrecord->lastSnapShot = theID;
385 snapshotrecord->lastBackingPage = snapshotrecord->snapShots[theID].firstBackingPage;
386 takeSnapshot(); //Make sure current snapshot is still good...All later ones are cleared
388 snapshotrecord->mIDToRollback = theID;
389 volatile int sTemp = 0;
390 getcontext(&snapshotrecord->mContextToRollback);
392 * This is used to quit the process on rollback, so that the process
393 * which needs to rollback can quit allowing the process whose
394 * snapshotid matches the rollbackid to switch to this context and
399 DEBUG("Invoked rollback\n");
403 * This fix obviates the need for a finalize call. hence less dependences for model-checker....
405 snapshotrecord->mIDToRollback = -1;