static int _open_open64_work(int(*fn) (const char *path, int flags, ...), const char *path, int flags, mode_t mode) { const char *newpath = path; WRAPPER_EXECUTION_DISABLE_CKPT(); if (dmtcp::Util::strStartsWith(path, UNIQUE_PTS_PREFIX_STR)) { dmtcp::string currPtsDevName = dmtcp::UniquePtsNameToPtmxConId::instance().retrieveCurrentPtsDeviceName(path); newpath = currPtsDevName.c_str(); } int fd =(*fn) (newpath, flags, mode); if (fd >= 0 && strcmp(path, "/dev/ptmx") == 0) { processDevPtmxConnection(fd); } else if (fd >= 0 && dmtcp::Util::strStartsWith(path, UNIQUE_PTS_PREFIX_STR)) { processDevPtsConnection(fd, path, newpath); } WRAPPER_EXECUTION_ENABLE_CKPT(); return fd; }
extern "C" int pthread_tryjoin_np(pthread_t thread, void **retval) { int ret; if (!dmtcp::ProcessInfo::instance().beginPthreadJoin(thread)) { return EINVAL; } WRAPPER_EXECUTION_DISABLE_CKPT(); ret = _real_pthread_tryjoin_np(thread, retval); WRAPPER_EXECUTION_ENABLE_CKPT(); #ifdef PTRACE /* Wrap the call to pthread_join() to make sure we call * delete_thread_on_pthread_join(). * FIXME: MTCP:process_pthread_join(thread) is calling threadisdead() THIS * SHOULDN'T BE NECESSARY. */ if (ret == 0) { mtcpFuncPtrs.process_pthread_join(thread); } #endif dmtcp::ProcessInfo::instance().endPthreadJoin(thread); return ret; }
//need to forward user clone extern "C" int __clone(int (*fn) (void *arg), void *child_stack, int flags, void *arg, int *parent_tidptr, struct user_desc *newtls, int *child_tidptr) { WRAPPER_EXECUTION_DISABLE_CKPT(); dmtcp::ThreadSync::incrementUninitializedThreadCount(); void *mtcpArg = mtcpFuncPtrs.prepare_for_clone(fn, child_stack, &flags, arg, parent_tidptr, newtls, &child_tidptr); struct ThreadArg *threadArg = (struct ThreadArg *) JALLOC_HELPER_MALLOC (sizeof (struct ThreadArg)); threadArg->fn = fn; threadArg->arg = arg; threadArg->mtcpArg = mtcpArg; pid_t tid = _real_clone(clone_start, child_stack, flags, threadArg, parent_tidptr, newtls, child_tidptr); if (tid == -1) { JTRACE("Clone call failed")(JASSERT_ERRNO); dmtcp::ThreadSync::decrementUninitializedThreadCount(); } else { dmtcp_process_event(DMTCP_EVENT_THREAD_CREATED, (void*) (unsigned long) tid); } WRAPPER_EXECUTION_ENABLE_CKPT(); return tid; }
/* * pthread_join() is a blocking call that waits for the given thread to exit. * It examines the value of 'tid' field in 'struct pthread' of the given * thread. The kernel will write '0' to this field when the thread exits. * * In pthread_join(), the thread makes a futex call in the following fashion: * _tid = pd->tid; * while !succeeded * futex(&pd->tid, FUTEX_WAIT, 0, _tid, ...) * As we can see, if the checkpoint is issued during pthread_join(), on * restart, the tid would have changed, but the call to futex would still used * the previously cached tid. This causes the caller to spin with 100% cpu * usage. * * The fix is to use the non blocking pthread_tryjoin_np function. To maintain * the semantics of pthread_join(), we need to ensure that only one thread is * allowed to wait on the given thread. This is done by keeping track of * threads that are being waited on by some other thread. * * Similar measures are taken for pthread_timedjoin_np(). */ extern "C" int pthread_join(pthread_t thread, void **retval) { int ret; if (!dmtcp::ProcessInfo::instance().beginPthreadJoin(thread)) { return EINVAL; } while (1) { WRAPPER_EXECUTION_DISABLE_CKPT(); ret = _real_pthread_tryjoin_np(thread, retval); WRAPPER_EXECUTION_ENABLE_CKPT(); if (ret != EBUSY) { break; } const struct timespec timeout = {(time_t) 0, (long)100 * 1000 * 1000}; nanosleep(&timeout, NULL); } #ifdef PTRACE /* Wrap the call to pthread_join() to make sure we call * delete_thread_on_pthread_join(). * FIXME: MTCP:process_pthread_join(thread) is calling threadisdead() THIS * SHOULDN'T BE NECESSARY. */ if (ret == 0) { mtcpFuncPtrs.process_pthread_join(thread); } #endif dmtcp::ProcessInfo::instance().endPthreadJoin(thread); return ret; }
// Invoked via pthread_create as start_routine // On return, it calls mtcp_threadiszombie() static void *pthread_start(void *arg) { struct ThreadArg *threadArg = (struct ThreadArg*) arg; void *thread_arg = threadArg->arg; void * (*pthread_fn) (void *) = threadArg->pthread_fn; pid_t virtualTid = threadArg->virtualTid; JASSERT(pthread_fn != 0x0); JALLOC_HELPER_FREE(arg); // Was allocated in calling thread in pthread_create mtcpFuncPtrs.fill_in_pthread_id(_real_gettid(), pthread_self()); dmtcp::ThreadSync::threadFinishedInitialization(); void *result = (*pthread_fn)(thread_arg); JTRACE("Thread returned") (virtualTid); WRAPPER_EXECUTION_DISABLE_CKPT(); mtcpFuncPtrs.threadiszombie(); /* * This thread has finished its execution, do some cleanup on our part. * erasing the virtualTid entry from virtualpidtable * FIXME: What if the process gets checkpointed after erase() but before the * thread actually exits? */ dmtcp::ProcessInfo::instance().eraseTid(virtualTid); dmtcp_process_event(DMTCP_EVENT_PTHREAD_RETURN, NULL); WRAPPER_EXECUTION_ENABLE_CKPT(); dmtcp::ThreadSync::unsetOkToGrabLock(); return result; }
extern "C" int ptsname_r(int fd, char * buf, size_t buflen) { WRAPPER_EXECUTION_DISABLE_CKPT(); int retVal = ptsname_r_work(fd, buf, buflen); WRAPPER_EXECUTION_ENABLE_CKPT(); return retVal; }
extern "C" void pthread_exit(void * retval) { WRAPPER_EXECUTION_DISABLE_CKPT(); mtcpFuncPtrs.threadiszombie(); dmtcp::ProcessInfo::instance().eraseTid(gettid()); dmtcp_process_event(DMTCP_EVENT_PTHREAD_EXIT, NULL); WRAPPER_EXECUTION_ENABLE_CKPT(); dmtcp::ThreadSync::unsetOkToGrabLock(); _real_pthread_exit(retval); for(;;); // To hide compiler warning about "noreturn" function }
extern "C" int __ptsname_r_chk(int fd, char * buf, size_t buflen, size_t nreal) { WRAPPER_EXECUTION_DISABLE_CKPT(); JASSERT(buflen <= nreal) (buflen) (nreal) .Text("Buffer Overflow detected!"); int retVal = ptsname_r_work(fd, buf, buflen); WRAPPER_EXECUTION_ENABLE_CKPT(); return retVal; }
extern "C" int pthread_timedjoin_np(pthread_t thread, void **retval, const struct timespec *abstime) { int ret; if (!dmtcp::ProcessInfo::instance().beginPthreadJoin(thread)) { return EINVAL; } /* * We continue to call pthread_tryjoin_np (and sleep) until we have gone past * the abstime provided by the caller */ while (1) { struct timeval tv; struct timespec ts; JASSERT(gettimeofday(&tv, NULL) == 0); TIMEVAL_TO_TIMESPEC(&tv, &ts); WRAPPER_EXECUTION_DISABLE_CKPT(); ret = _real_pthread_tryjoin_np(thread, retval); WRAPPER_EXECUTION_ENABLE_CKPT(); if (ret == 0) { break; } if (ts.tv_sec > abstime->tv_sec || (ts.tv_sec == abstime->tv_sec && ts.tv_nsec > abstime->tv_nsec)) { ret = ETIMEDOUT; break; } const struct timespec timeout = {(time_t) 0, (long)100 * 1000 * 1000}; nanosleep(&timeout, NULL); } #ifdef PTRACE /* Wrap the call to pthread_join() to make sure we call * delete_thread_on_pthread_join(). * FIXME: MTCP:process_pthread_join(thread) is calling threadisdead() THIS * SHOULDN'T BE NECESSARY. */ if (ret == 0) { mtcpFuncPtrs.process_pthread_join(thread); } #endif dmtcp::ProcessInfo::instance().endPthreadJoin(thread); return ret; }
extern "C" int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { WRAPPER_EXECUTION_DISABLE_CKPT(); WRAPPER_HEADER_CKPT_DISABLED(int, getpeername, _real_getpeername, sockfd, addr, addrlen); if (SYNC_IS_REPLAY) { WRAPPER_REPLAY_START(getpeername); if (retval != -1) { *addr = GET_FIELD(my_entry, getpeername, ret_addr); *addrlen = GET_FIELD(my_entry, getpeername, ret_addrlen); } WRAPPER_REPLAY_END(getpeername); } else if (SYNC_IS_RECORD) { retval = _real_getpeername(sockfd, addr, addrlen); if (retval != -1) { SET_FIELD2(my_entry, getpeername, ret_addr, *addr); SET_FIELD2(my_entry, getpeername, ret_addrlen, *addrlen); } WRAPPER_LOG_WRITE_ENTRY(my_entry); } WRAPPER_EXECUTION_ENABLE_CKPT(); return retval; }