dt_proc.c
来自「Sun Solaris 10 中的 DTrace 组件的源代码。请参看: htt」· C语言 代码 · 共 1,011 行 · 第 1/3 页
C
1,011 行
/* * While we are waiting for the victim to run, clear PR_KLC and PR_RLC * so that if the libdtrace client is killed, the victim stays stopped. * dt_proc_destroy() will also observe this and perform PRELEASE_HANG. */ (void) Punsetflags(P, krflag); Psync(P); (void) pthread_mutex_unlock(&dpr->dpr_lock); while (!dpr->dpr_quit) { if (write(pfd, &wstop, sizeof (wstop)) == -1 && errno == EINTR) continue; /* check dpr_quit and continue waiting */ (void) pthread_mutex_lock(&dpr->dpr_lock); (void) Pstopstatus(P, PCNULL, 0); psp = &Pstatus(P)->pr_lwp; /* * If we've reached a new state, found a new representative, or * the stop timestamp has changed, restore PR_KLC/PR_RLC to its * original setting and then return with dpr_lock held. */ if (Pstate(P) != PS_STOP || psp->pr_lwpid != lwpid || bcmp(&psp->pr_tstamp, &tstamp, sizeof (tstamp)) != 0) { (void) Psetflags(P, krflag); Psync(P); return; } (void) pthread_mutex_unlock(&dpr->dpr_lock); (void) poll(NULL, 0, MILLISEC / 2); } (void) pthread_mutex_lock(&dpr->dpr_lock);}/* * Main loop for all victim process control threads. We initialize all the * appropriate /proc control mechanisms, and then enter a loop waiting for * the process to stop on an event or die. We process any events by calling * appropriate subroutines, and exit when the victim dies or we lose control. * * The control thread synchronizes the use of dpr_proc with other libdtrace * threads using dpr_lock. We hold the lock for all of our operations except * waiting while the process is running: this is accomplished by writing a * PCWSTOP directive directly to the underlying /proc/<pid>/ctl file. If the * libdtrace client wishes to exit or abort our wait, SIGCANCEL can be used. */static void *dt_proc_control(void *arg){ dt_proc_t *dpr = arg; dt_proc_hash_t *dph = dpr->dpr_hdl->dt_procs; struct ps_prochandle *P = dpr->dpr_proc; int pfd = Pctlfd(P); int pid = dpr->dpr_pid; const long wstop = PCWSTOP; int notify = B_FALSE; /* * We disable the POSIX thread cancellation mechanism so that the * client program using libdtrace can't accidentally cancel our thread. * dt_proc_destroy() uses SIGCANCEL explicitly to simply poke us out * of PCWSTOP with EINTR, at which point we will see dpr_quit and exit. */ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); /* * Set up the corresponding process for tracing by libdtrace. We want * to be able to catch breakpoints and efficiently single-step over * them, and we need to enable librtld_db to watch libdl activity. */ (void) pthread_mutex_lock(&dpr->dpr_lock); (void) Punsetflags(P, PR_ASYNC); /* require synchronous mode */ (void) Psetflags(P, PR_BPTADJ); /* always adjust eip on x86 */ (void) Punsetflags(P, PR_FORK); /* do not inherit on fork */ (void) Pfault(P, FLTBPT, B_TRUE); /* always trace breakpoints */ (void) Pfault(P, FLTTRACE, B_TRUE); /* always trace single-step */ /* * We must trace exit from exec() system calls so that if the exec is * successful, we can reset our breakpoints and re-initialize libproc. */ (void) Psysexit(P, SYS_exec, B_TRUE); (void) Psysexit(P, SYS_execve, B_TRUE); /* * We must trace entry and exit for fork() system calls in order to * disable our breakpoints temporarily during the fork. We do not set * the PR_FORK flag, so if fork succeeds the child begins executing and * does not inherit any other tracing behaviors or a control thread. */ (void) Psysentry(P, SYS_vfork, B_TRUE); (void) Psysexit(P, SYS_vfork, B_TRUE); (void) Psysentry(P, SYS_fork1, B_TRUE); (void) Psysexit(P, SYS_fork1, B_TRUE); (void) Psysentry(P, SYS_forkall, B_TRUE); (void) Psysexit(P, SYS_forkall, B_TRUE); Psync(P); /* enable all /proc changes */ dt_proc_attach(dpr, B_FALSE); /* enable rtld breakpoints */ /* * If PR_KLC is set, we created the process; otherwise we grabbed it. * Check for an appropriate stop request and wait for dt_proc_continue. */ if (Pstatus(P)->pr_flags & PR_KLC) dt_proc_stop(dpr, DT_PROC_STOP_CREATE); else dt_proc_stop(dpr, DT_PROC_STOP_GRAB); if (Psetrun(P, 0, 0) == -1) { dt_dprintf("pid %d: failed to set running: %s\n", (int)dpr->dpr_pid, strerror(errno)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); /* * Wait for the process corresponding to this control thread to stop, * process the event, and then set it running again. We want to sleep * with dpr_lock *unheld* so that other parts of libdtrace can use the * ps_prochandle in the meantime (e.g. ustack()). To do this, we write * a PCWSTOP directive directly to the underlying /proc/<pid>/ctl file. * Once the process stops, we wake up, grab dpr_lock, and then call * Pwait() (which will return immediately) and do our processing. */ while (!dpr->dpr_quit) { const lwpstatus_t *psp; if (write(pfd, &wstop, sizeof (wstop)) == -1 && errno == EINTR) continue; /* check dpr_quit and continue waiting */ (void) pthread_mutex_lock(&dpr->dpr_lock);pwait_locked: if (Pstopstatus(P, PCNULL, 0) == -1 && errno == EINTR) { (void) pthread_mutex_unlock(&dpr->dpr_lock); continue; /* check dpr_quit and continue waiting */ } switch (Pstate(P)) { case PS_STOP: psp = &Pstatus(P)->pr_lwp; dt_dprintf("pid %d: proc stopped showing %d/%d\n", pid, psp->pr_why, psp->pr_what); /* * If the process stops showing PR_REQUESTED, then the * DTrace stop() action was applied to it or another * debugging utility (e.g. pstop(1)) asked it to stop. * In either case, the user's intention is for the * process to remain stopped until another external * mechanism (e.g. prun(1)) is applied. So instead of * setting the process running ourself, we wait for * someone else to do so. Once that happens, we return * to our normal loop waiting for an event of interest. */ if (psp->pr_why == PR_REQUESTED) { dt_proc_waitrun(dpr); (void) pthread_mutex_unlock(&dpr->dpr_lock); continue; } /* * If the process stops showing one of the events that * we are tracing, perform the appropriate response. * Note that we ignore PR_SUSPENDED, PR_CHECKPOINT, and * PR_JOBCONTROL by design: if one of these conditions * occurs, we will fall through to Psetrun() but the * process will remain stopped in the kernel by the * corresponding mechanism (e.g. job control stop). */ if (psp->pr_why == PR_FAULTED && psp->pr_what == FLTBPT) dt_proc_bpmatch(dpr); else if (psp->pr_why == PR_SYSENTRY && IS_SYS_FORK(psp->pr_what)) dt_proc_bpdisable(dpr); else if (psp->pr_why == PR_SYSEXIT && IS_SYS_FORK(psp->pr_what)) dt_proc_bpenable(dpr); else if (psp->pr_why == PR_SYSEXIT && IS_SYS_EXEC(psp->pr_what)) dt_proc_attach(dpr, B_TRUE); break; case PS_LOST: if (Preopen(P) == 0) goto pwait_locked; dt_dprintf("pid %d: proc lost: %s\n", pid, strerror(errno)); dpr->dpr_quit = B_TRUE; notify = B_TRUE; break; case PS_UNDEAD: dt_dprintf("pid %d: proc died\n", pid); dpr->dpr_quit = B_TRUE; notify = B_TRUE; break; } if (Pstate(P) != PS_UNDEAD && Psetrun(P, 0, 0) == -1) { dt_dprintf("pid %d: failed to set running: %s\n", (int)dpr->dpr_pid, strerror(errno)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); } /* * If the control thread detected PS_UNDEAD or PS_LOST, then enqueue * the dt_proc_t structure on the dt_proc_hash_t notification list. */ if (notify) { (void) pthread_mutex_lock(&dph->dph_lock); dpr->dpr_notify = dph->dph_notify; dph->dph_notify = dpr; (void) pthread_mutex_unlock(&dph->dph_lock); (void) pthread_cond_broadcast(&dph->dph_cv); } /* * Destroy and remove any remaining breakpoints, set dpr_done and clear * dpr_tid to indicate the control thread has exited, and notify any * waiting thread in dt_proc_destroy() that we have succesfully exited. */ (void) pthread_mutex_lock(&dpr->dpr_lock); dt_proc_bpdestroy(dpr, B_TRUE); dpr->dpr_done = B_TRUE; dpr->dpr_tid = 0; (void) pthread_mutex_unlock(&dpr->dpr_lock); (void) pthread_cond_broadcast(&dpr->dpr_cv); return (NULL);}/*PRINTFLIKE3*/static struct ps_prochandle *dt_proc_error(dtrace_hdl_t *dtp, dt_proc_t *dpr, const char *format, ...){ va_list ap; va_start(ap, format); dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap); va_end(ap); if (dpr->dpr_proc != NULL) Prelease(dpr->dpr_proc, 0); dt_free(dtp, dpr); (void) dt_set_errno(dtp, EDT_COMPILER); return (NULL);}dt_proc_t *dt_proc_lookup(dtrace_hdl_t *dtp, struct ps_prochandle *P, int remove){ dt_proc_hash_t *dph = dtp->dt_procs; pid_t pid = Pstatus(P)->pr_pid; dt_proc_t *dpr, **dpp = &dph->dph_hash[pid & (dph->dph_hashlen - 1)]; for (dpr = *dpp; dpr != NULL; dpr = dpr->dpr_hash) { if (dpr->dpr_pid == pid) break; else dpp = &dpr->dpr_hash; } assert(dpr != NULL); assert(dpr->dpr_proc == P); if (remove) *dpp = dpr->dpr_hash; /* remove from pid hash chain */ return (dpr);}static voiddt_proc_destroy(dtrace_hdl_t *dtp, struct ps_prochandle *P){ dt_proc_t *dpr = dt_proc_lookup(dtp, P, B_TRUE); dt_proc_hash_t *dph = dtp->dt_procs; dt_proc_t *npr, **npp; int rflag; assert(dpr != NULL); /* * If neither PR_KLC nor PR_RLC is set, then the process is stopped by * an external debugger and we were waiting in dt_proc_waitrun(). * Leave the process in this condition using PRELEASE_HANG. */ if (!(Pstatus(dpr->dpr_proc)->pr_flags & (PR_KLC | PR_RLC))) { dt_dprintf("abandoning pid %d\n", (int)dpr->dpr_pid); rflag = PRELEASE_HANG; } else { dt_dprintf("releasing pid %d\n", (int)dpr->dpr_pid); rflag = 0; /* apply kill or run-on-last-close */ } if (dpr->dpr_tid) { /* * Set the dpr_quit flag to tell the daemon thread to exit. We * send it a SIGCANCEL to poke it out of PCWSTOP or any other * long-term /proc system call. Our daemon threads have POSIX * cancellation disabled, so EINTR will be the only effect. We * then wait for dpr_done to indicate the thread has exited. */ (void) pthread_mutex_lock(&dpr->dpr_lock); dpr->dpr_quit = B_TRUE; (void) pthread_kill(dpr->dpr_tid, SIGCANCEL); while (!dpr->dpr_done) (void) pthread_cond_wait(&dpr->dpr_cv, &dpr->dpr_lock); (void) pthread_mutex_unlock(&dpr->dpr_lock); } /* * Before we free the process structure, walk the dt_proc_hash_t's * notification list and remove this dt_proc_t if it is enqueued. */ (void) pthread_mutex_lock(&dph->dph_lock); npp = &dph->dph_notify;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?