extern "C" int setpgid(pid_t pid, pid_t pgid) { DMTCP_PLUGIN_DISABLE_CKPT(); pid_t currPid = VIRTUAL_TO_REAL_PID (pid); pid_t currPgid = VIRTUAL_TO_REAL_PID (pgid); int retVal = _real_setpgid (currPid, currPgid); DMTCP_PLUGIN_ENABLE_CKPT(); return retVal; }
LIB_PRIVATE int tgkill(int tgid, int tid, int sig) { // FIXME: Check the comments in kill() // DMTCP_PLUGIN_DISABLE_CKPT(); int realTgid = VIRTUAL_TO_REAL_PID ( tgid ); int realTid = VIRTUAL_TO_REAL_PID ( tid ); int retVal = _real_tgkill ( realTgid, realTid, sig ); // DMTCP_PLUGIN_ENABLE_CKPT(); return retVal; }
extern "C" int fcntl(int fd, int cmd, ...) { va_list ap; // Handling the variable number of arguments void *arg_in = NULL; void *arg = NULL; va_start( ap, cmd ); arg_in = va_arg(ap, void *); va_end(ap); arg = arg_in; DMTCP_PLUGIN_DISABLE_CKPT(); if (cmd == F_SETOWN) { pid_t virtualPid = VIRTUAL_TO_REAL_PID((pid_t) (unsigned long) arg_in); arg = (void*) (unsigned long) virtualPid; } int result = _real_fcntl(fd, cmd, arg); int retval = result; if (cmd == F_GETOWN) { retval = REAL_TO_VIRTUAL_PID(result); } DMTCP_PLUGIN_ENABLE_CKPT(); return retval; }
extern "C" int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options) { int retval = 0; struct timespec sleepTime = {0, 1000}; siginfo_t siginfop; memset(&siginfop, 0, sizeof(siginfop)); /* waitid returns 0 in case of success as well as when WNOHANG is specified * and we need to distinguish those two cases.man page for waitid says: * If WNOHANG was specified in options and there were no children in a * waitable state, then waitid() returns 0 immediately and the state of * the siginfo_t structure pointed to by infop is unspecified. To * distinguish this case from that where a child was in a waitable state, * zero out the si_pid field before the call and check for a nonzero value * in this field after the call returns. * * See comments above wait4() */ while (retval == 0) { DMTCP_PLUGIN_DISABLE_CKPT(); pid_t currPid = VIRTUAL_TO_REAL_PID (id); retval = _real_waitid (idtype, currPid, &siginfop, options | WNOHANG); if (retval != -1) { pid_t virtualPid = REAL_TO_VIRTUAL_PID ( siginfop.si_pid ); siginfop.si_pid = virtualPid; if ( siginfop.si_code == CLD_EXITED || siginfop.si_code == CLD_KILLED ) dmtcp::VirtualPidTable::instance().erase(virtualPid); } DMTCP_PLUGIN_ENABLE_CKPT(); if ((options & WNOHANG) || retval == -1 || siginfop.si_pid != 0) { break; } else { if (sleepTime.tv_sec == 0) { sleepTime.tv_nsec *= 2; if (sleepTime.tv_nsec >= 1000 * 1000 * 1000) { sleepTime.tv_sec++; sleepTime.tv_nsec = 0; } } nanosleep(&sleepTime, NULL); } } if (retval == 0 && infop != NULL) { *infop = siginfop; } return retval; }
extern "C" pid_t getpgid(pid_t pid) { DMTCP_PLUGIN_DISABLE_CKPT(); pid_t realPid = VIRTUAL_TO_REAL_PID (pid); pid_t res = _real_getpgid (realPid); pid_t origPgid = REAL_TO_VIRTUAL_PID (res); DMTCP_PLUGIN_ENABLE_CKPT(); return origPgid; }
// TODO: Add check for the below two functions in configure int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags) { DMTCP_PLUGIN_DISABLE_CKPT(); int result = -1; pid_t real_pid = 0; if (pid != 0) { real_pid = VIRTUAL_TO_REAL_PID(pid); } result = _real_sched_setattr(real_pid, attr, flags); DMTCP_PLUGIN_ENABLE_CKPT(); return result; }
int sched_getparam(pid_t pid, struct sched_param *param) { DMTCP_PLUGIN_DISABLE_CKPT(); int result = -1; pid_t real_pid = 0; if (pid != 0) { real_pid = VIRTUAL_TO_REAL_PID(pid); } result = _real_sched_getparam(real_pid, param); DMTCP_PLUGIN_ENABLE_CKPT(); return result; }
int sched_getscheduler(pid_t pid) { DMTCP_PLUGIN_DISABLE_CKPT(); int result = -1; pid_t real_pid = 0; if (pid != 0) { real_pid = VIRTUAL_TO_REAL_PID(pid); } result = _real_sched_getscheduler(real_pid); DMTCP_PLUGIN_ENABLE_CKPT(); return result; }
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param) { DMTCP_PLUGIN_DISABLE_CKPT(); int result = -1; pid_t real_pid = 0; if (pid != 0) { real_pid = VIRTUAL_TO_REAL_PID(pid); } result = _real_sched_setscheduler(real_pid, policy, param); DMTCP_PLUGIN_ENABLE_CKPT(); return result; }
int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask) { DMTCP_PLUGIN_DISABLE_CKPT(); int result = -1; pid_t real_pid = 0; if (pid != 0) { real_pid = VIRTUAL_TO_REAL_PID(pid); } result = _real_sched_getaffinity(real_pid, cpusetsize, mask); DMTCP_PLUGIN_ENABLE_CKPT(); return result; }
extern "C" pid_t tcsetpgrp(int fd, pid_t pgrp) { DMTCP_PLUGIN_DISABLE_CKPT(); pid_t currPgrp = VIRTUAL_TO_REAL_PID( pgrp ); // JTRACE( "Inside tcsetpgrp wrapper" ) (fd) (pgrp) (currPgrp); pid_t realPid = _real_tcsetpgrp(fd, currPgrp); pid_t virtualPid = REAL_TO_VIRTUAL_PID(realPid); //JTRACE( "tcsetpgrp return value" ) (fd) (pgrp) (currPgrp) (retval); DMTCP_PLUGIN_ENABLE_CKPT(); return virtualPid; }
// FIXME: This function needs third argument newpathsize, or assume PATH_MAX static void updateProcPathVirtualToReal(const char *path, char **newpath) { if (dmtcp::Util::strStartsWith(path, PROC_PREFIX)) { int index = strlen(PROC_PREFIX); char *rest; pid_t virtualPid = strtol(&path[index], &rest, 0); if (virtualPid > 0 && *rest == '/') { pid_t realPid = VIRTUAL_TO_REAL_PID(virtualPid); sprintf(*newpath, "/proc/%d%s", realPid, rest); return; } } *newpath = (char *)path; }
extern "C" long ptrace (int request, pid_t pid, void* addr, void* data) #endif { #ifndef ANDROID va_list ap; #endif pid_t virtualPid; pid_t realPid; #ifndef ANDROID void *addr; void *data; va_start(ap, request); virtualPid = va_arg(ap, pid_t); addr = va_arg(ap, void *); data = va_arg(ap, void *); va_end(ap); #else virtualPid = pid; #endif realPid = VIRTUAL_TO_REAL_PID(virtualPid); long ptrace_ret = _real_ptrace(request, realPid, addr, data); /* * PTRACE_GETEVENTMSG (since Linux 2.5.46) * Retrieve a message (as an unsigned long) about the ptrace event * that just happened, placing it in the location data in the * parent. For PTRACE_EVENT_EXIT this is the child's exit status. * For PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK and PTRACE_EVENT_CLONE * this is the PID of the new process. Since Linux 2.6.18, the PID * of the new process is also available for PTRACE_EVENT_VFORK_DONE. * (addr is ignored.) */ if (ptrace_ret == 0 && request == PTRACE_GETEVENTMSG) { unsigned long *ldata = (unsigned long*) data; pid_t newRealPid = (pid_t) *ldata; *ldata = (unsigned long) REAL_TO_VIRTUAL_PID(newRealPid); } return ptrace_ret; }
extern "C" pid_t getsid(pid_t pid) { DMTCP_PLUGIN_DISABLE_CKPT(); pid_t currPid; // If !pid then we ask SID of this process if( pid ) currPid = VIRTUAL_TO_REAL_PID (pid); else currPid = _real_getpid(); pid_t res = _real_getsid (currPid); pid_t origSid = REAL_TO_VIRTUAL_PID (res); DMTCP_PLUGIN_ENABLE_CKPT(); return origSid; }
pid_t wait4(pid_t pid, __WAIT_STATUS status, int options, struct rusage *rusage) { int stat; int saved_errno = errno; pid_t currPid; pid_t virtualPid; pid_t retval = 0; struct timespec sleepTime = {0, 10*000}; if (status == NULL) status = (__WAIT_STATUS) &stat; while (retval == 0) { DMTCP_PLUGIN_DISABLE_CKPT(); currPid = VIRTUAL_TO_REAL_PID(pid); retval = _real_wait4(currPid, status, options | WNOHANG, rusage); saved_errno = errno; virtualPid = REAL_TO_VIRTUAL_PID(retval); if (retval > 0 && (WIFEXITED(*(int*)status) || WIFSIGNALED(*(int*)status))) { dmtcp::VirtualPidTable::instance().erase(virtualPid); } DMTCP_PLUGIN_ENABLE_CKPT(); if ((options & WNOHANG) || retval != 0) { break; } else { if (sleepTime.tv_sec == 0) { sleepTime.tv_nsec *= 2; if (sleepTime.tv_nsec >= 1000 * 1000 * 1000) { sleepTime.tv_sec++; sleepTime.tv_nsec = 0; } } nanosleep(&sleepTime, NULL); } } errno = saved_errno; return virtualPid; }
// FIXME: This function needs third argument newpathsize, or assume PATH_MAX // FIXME: This does a lot of copying even if "/proc" doesn't appear. static void updateProcPathVirtualToReal(const char *path, char *newpath) { if (path == NULL || strlen(path) == 0) { strcpy(newpath, ""); return; } if (dmtcp::Util::strStartsWith(path, "/proc/")) { int index = 6; char *rest; pid_t virtualPid = strtol(&path[index], &rest, 0); if (virtualPid > 0 && *rest == '/') { pid_t realPid = VIRTUAL_TO_REAL_PID(virtualPid); sprintf(newpath, "/proc/%d%s", realPid, rest); } else { strcpy(newpath, path); } } else { strcpy(newpath, path); } return; }
//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) { /* * struct MtcpRestartThreadArg * * DMTCP requires the virtualTids of the threads being created during * the RESTARTING phase. We use an MtcpRestartThreadArg structure to pass * the virtualTid of the thread being created from MTCP to DMTCP. * * actual clone call: clone (fn, child_stack, flags, void *, ... ) * new clone call : clone (fn, child_stack, flags, * (struct MtcpRestartThreadArg *), ...) * * DMTCP automatically extracts arg from this structure and passes that * to the _real_clone call. * * IMPORTANT NOTE: While updating, this struct must be kept in sync * with the struct of the same name in mtcp.c */ struct MtcpRestartThreadArg { void * arg; pid_t virtualTid; } *mtcpRestartThreadArg; pid_t virtualTid = -1; if (!dmtcp_is_running_state()) { mtcpRestartThreadArg = (struct MtcpRestartThreadArg *) arg; arg = mtcpRestartThreadArg -> arg; virtualTid = mtcpRestartThreadArg -> virtualTid; if (virtualTid != VIRTUAL_TO_REAL_PID(virtualTid)) { dmtcp::VirtualPidTable::instance().postRestart(); } } else { virtualTid = dmtcp::VirtualPidTable::instance().getNewVirtualTid(); dmtcp::VirtualPidTable::instance().writeVirtualTidToFileForPtrace(virtualTid); } // We have to use DMTCP-specific memory allocator because using glibc:malloc // can interfere with user threads. // We use JALLOC_HELPER_FREE to free this memory in two places: // 1. later in this function in case of failure on call to __clone; and // 2. near the beginnging of clone_start (wrapper for start_routine). struct ThreadArg *threadArg = (struct ThreadArg *) JALLOC_HELPER_MALLOC(sizeof (struct ThreadArg)); threadArg->fn = fn; threadArg->arg = arg; threadArg->virtualTid = virtualTid; JTRACE("Calling libc:__clone"); pid_t tid = _real_clone(clone_start, child_stack, flags, threadArg, parent_tidptr, newtls, child_tidptr); if (dmtcp_is_running_state() && dmtcp::Util::isPtraced()) { dmtcp::VirtualPidTable::instance().readVirtualTidFromFileForPtrace(); } if (tid > 0) { JTRACE("New thread created") (tid); dmtcp::VirtualPidTable::instance().updateMapping(virtualTid, tid); } else { // Free memory previously allocated by calling JALLOC_HELPER_MALLOC JALLOC_HELPER_FREE(threadArg); virtualTid = tid; } return virtualTid; }
extern "C" int kill(pid_t pid, int sig) { /* FIXME: When bash receives a SIGINT signal, the signal handler is * called to process the signal. Once the processing is done, bash * performs a longjmp to a much higher call frame. As a result, this * call frame never gets a chance to return and hence we fail to * perform DMTCP_PLUGIN_ENABLE_CKPT() WHICH RESULTS in the lock * being held but never released. Thus later on, when the ckpt-thread * tries to acquire this lock, it results in a deadlock. * * To avoid the deadlock, FOR NOW, we shouldn't call WRAPPER_...() * calls in this function or any kill() family of wrappers. * * Potential Solution: If the signal sending process is among the * potential signal receivers, it should MASK/BLOCK signal delivery * right before sending the signal (before calling _real_kill()). Once * the system call returns, it should then call * DMTCP_PLUGIN_ENABLE_CKPT() AND THEN RESTore the signal mask to * as it was prior to calling this wrapper. So this function will as * follows: * DMTCP_PLUGIN_DISABLE_CKPT(); * // if this process is a potential receiver of this signal * if (pid == getpid() || pid == 0 || pid == -1 || * getpgrp() == abs(pid)) { * <SAVE_SIGNAL_MASK>; * <BLOCK SIGNAL 'sig'>; * sigmaskAltered = true; * } * pid_t currPid = VIRTUAL_TO_REAL_PID(pid); * int retVal = _real_kill(currPid, sig); * DMTCP_PLUGIN_ENABLE_CKPT(); * if (sigmaskAltered) { * <RESTORE_SIGNAL_MASK> * } * return retVal; * * * This longjmp trouble can happen with any wrapper, whose execution my * end up in a call to user-code i.e. if the call frame looks sth like: * ... * user_func1(...) * ... * DMTCP_WRAPPER(...) * ... * user_func2(...) * ... * * Another potential way would be to put a wrapper around longjmp() in * which the calling thread should release all the DMTCP-locks being * held at the moment. This would require us to keep a count of lock() * calls without a corresponding unlock() call. After the longjmp() * call, one need to make sure that an unlock() call is requested only * if there is a corresponding lock, because it might happen that * longjmp() was harmless in the sense that, it didn't cause a * callframe like the one mentioned above. * */ // DMTCP_PLUGIN_DISABLE_CKPT(); pid_t currPid = VIRTUAL_TO_REAL_PID (pid); int retVal = _real_kill (currPid, sig); // DMTCP_PLUGIN_ENABLE_CKPT(); return retVal; }