/* * Check to see if the control thread was requested to stop when the victim * process reached a particular event (why) rather than continuing the victim. * If 'why' is set in the stop mask, we wait on dpr_cv for dt_proc_continue(). * If 'why' is not set, this function returns immediately and does nothing. */ static void dt_proc_stop(dt_proc_t *dpr, uint8_t why) { assert(DT_MUTEX_HELD(&dpr->dpr_lock)); assert(why != DT_PROC_STOP_IDLE); if (dpr->dpr_stop & why) { dpr->dpr_stop |= DT_PROC_STOP_IDLE; dpr->dpr_stop &= ~why; (void) pthread_cond_broadcast(&dpr->dpr_cv); /* * We disable breakpoints while stopped to preserve the * integrity of the program text for both our own disassembly * and that of the kernel. */ dt_proc_bpdisable(dpr); while (dpr->dpr_stop & DT_PROC_STOP_IDLE) (void) pthread_cond_wait(&dpr->dpr_cv, &dpr->dpr_lock); dt_proc_bpenable(dpr); } }
static void dt_proc_bpmatch(dtrace_hdl_t *dtp, dt_proc_t *dpr) { const lwpstatus_t *psp = &Pstatus(dpr->dpr_proc)->pr_lwp; dt_bkpt_t *dbp; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); for (dbp = dt_list_next(&dpr->dpr_bps); dbp != NULL; dbp = dt_list_next(dbp)) { if (psp->pr_reg[R_PC] == dbp->dbp_addr) break; } if (dbp == NULL) { dt_dprintf("pid %d: spurious breakpoint wakeup for %lx\n", (int)dpr->dpr_pid, (ulong_t)psp->pr_reg[R_PC]); return; } dt_dprintf("pid %d: hit breakpoint at %lx (%lu)\n", (int)dpr->dpr_pid, (ulong_t)dbp->dbp_addr, ++dbp->dbp_hits); dbp->dbp_func(dtp, dpr, dbp->dbp_data); (void) Pxecbkpt(dpr->dpr_proc, dbp->dbp_instr); }
static void dt_proc_bpdestroy(dt_proc_t *dpr, int delbkpts) { #if defined(sun) int state = Pstate(dpr->dpr_proc); #else int state = proc_state(dpr->dpr_proc); #endif dt_bkpt_t *dbp, *nbp; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); for (dbp = dt_list_next(&dpr->dpr_bps); dbp != NULL; dbp = nbp) { printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); #ifdef DOODAD if (delbkpts && dbp->dbp_active && state != PS_LOST && state != PS_UNDEAD) { (void) Pdelbkpt(dpr->dpr_proc, dbp->dbp_addr, dbp->dbp_instr); } #endif nbp = dt_list_next(dbp); dt_list_delete(&dpr->dpr_bps, dbp); dt_free(dpr->dpr_hdl, dbp); } }
/* * Wait for a stopped process to be set running again by some other debugger. * This is typically not required by /proc-based debuggers, since the usual * model is that one debugger controls one victim. But DTrace, as usual, has * its own needs: the stop() action assumes that prun(1) or some other tool * will be applied to resume the victim process. This could be solved by * adding a PCWRUN directive to /proc, but that seems like overkill unless * other debuggers end up needing this functionality, so we implement a cheap * equivalent to PCWRUN using the set of existing kernel mechanisms. * * Our intent is really not just to wait for the victim to run, but rather to * wait for it to run and then stop again for a reason other than the current * PR_REQUESTED stop. Since PCWSTOP/Pstopstatus() can be applied repeatedly * to a stopped process and will return the same result without affecting the * victim, we can just perform these operations repeatedly until Pstate() * changes, the representative LWP ID changes, or the stop timestamp advances. * dt_gproc_control() will then rediscover the new state and continue as usual. * When the process is still stopped in the same exact state, we sleep for a * brief interval before waiting again so as not to spin consuming CPU cycles. */ static void dt_proc_waitrun(dt_proc_t *dpr) { struct ps_prochandle *P = dpr->dpr_proc; const lwpstatus_t *psp = &Pstatus(P)->pr_lwp; int krflag = psp->pr_flags & (PR_KLC | PR_RLC); timestruc_t tstamp = psp->pr_tstamp; lwpid_t lwpid = psp->pr_lwpid; const long wstop = PCWSTOP; int pfd = Pctlfd(P); assert(DT_MUTEX_HELD(&dpr->dpr_lock)); assert(psp->pr_flags & PR_STOPPED); assert(Pstate(P) == PS_STOP); /* * 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); }
static void dt_proc_bpdisable(dt_proc_t *dpr) { dt_bkpt_t *dbp; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); for (dbp = dt_list_next(&dpr->dpr_bps); dbp != NULL; dbp = dt_list_next(dbp)) { if (dbp->dbp_active && Pdelbkpt(dpr->dpr_proc, dbp->dbp_addr, dbp->dbp_instr) == 0) dbp->dbp_active = B_FALSE; } dt_dprintf("breakpoints disabled\n"); }
static void dt_proc_bpdestroy(dt_proc_t *dpr, int delbkpts) { int state = Pstate(dpr->dpr_proc); dt_bkpt_t *dbp, *nbp; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); for (dbp = dt_list_next(&dpr->dpr_bps); dbp != NULL; dbp = nbp) { if (delbkpts && dbp->dbp_active && state != PS_LOST && state != PS_UNDEAD) { (void) Pdelbkpt(dpr->dpr_proc, dbp->dbp_addr, dbp->dbp_instr); } nbp = dt_list_next(dbp); dt_list_delete(&dpr->dpr_bps, dbp); dt_free(dpr->dpr_hdl, dbp); } }
static void dt_proc_bpdisable(dt_proc_t *dpr) { dt_bkpt_t *dbp; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); for (dbp = dt_list_next(&dpr->dpr_bps); dbp != NULL; dbp = dt_list_next(dbp)) { printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); #ifdef DOODAD if (dbp->dbp_active && Pdelbkpt(dpr->dpr_proc, dbp->dbp_addr, dbp->dbp_instr) == 0) dbp->dbp_active = B_FALSE; #endif } dt_dprintf("breakpoints disabled\n"); }
static int dt_pid_create_usdt_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp, dt_pcb_t *pcb, dt_proc_t *dpr) { struct ps_prochandle *P = dpr->dpr_proc; int ret = 0; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); #if defined(sun) (void) Pupdate_maps(P); if (Pobject_iter(P, dt_pid_usdt_mapping, P) != 0) { ret = -1; (void) dt_pid_error(dtp, pcb, dpr, NULL, D_PROC_USDT, "failed to instantiate probes for pid %d: %s", #if defined(sun) (int)Pstatus(P)->pr_pid, strerror(errno)); #else (int)proc_getpid(P), strerror(errno)); #endif }
static dt_bkpt_t * dt_proc_bpcreate(dt_proc_t *dpr, uintptr_t addr, dt_bkpt_f *func, void *data) { struct ps_prochandle *P = dpr->dpr_proc; dt_bkpt_t *dbp; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); if ((dbp = dt_zalloc(dpr->dpr_hdl, sizeof (dt_bkpt_t))) != NULL) { dbp->dbp_func = func; dbp->dbp_data = data; dbp->dbp_addr = addr; if (Psetbkpt(P, dbp->dbp_addr, &dbp->dbp_instr) == 0) dbp->dbp_active = B_TRUE; dt_list_append(&dpr->dpr_bps, dbp); } return (dbp); }
/* * Common code for enabling events associated with the run-time linker after * attaching to a process or after a victim process completes an exec(2). */ static void dt_proc_attach(dt_proc_t *dpr, int exec) { const pstatus_t *psp = Pstatus(dpr->dpr_proc); rd_err_e err; GElf_Sym sym; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); if (exec) { if (psp->pr_lwp.pr_errno != 0) return; /* exec failed: nothing needs to be done */ dt_proc_bpdestroy(dpr, B_FALSE); Preset_maps(dpr->dpr_proc); } if ((dpr->dpr_rtld = Prd_agent(dpr->dpr_proc)) != NULL && (err = rd_event_enable(dpr->dpr_rtld, B_TRUE)) == RD_OK) { dt_proc_rdwatch(dpr, RD_PREINIT, "RD_PREINIT"); dt_proc_rdwatch(dpr, RD_POSTINIT, "RD_POSTINIT"); dt_proc_rdwatch(dpr, RD_DLACTIVITY, "RD_DLACTIVITY"); } else { dt_dprintf("pid %d: failed to enable rtld events: %s\n", (int)dpr->dpr_pid, dpr->dpr_rtld ? rd_errstr(err) : "rtld_db agent initialization failed"); } Pupdate_maps(dpr->dpr_proc); if (Pxlookup_by_name(dpr->dpr_proc, LM_ID_BASE, "a.out", "main", &sym, NULL) == 0) { (void) dt_proc_bpcreate(dpr, (uintptr_t)sym.st_value, (dt_bkpt_f *)dt_proc_bpmain, "a.out`main"); } else { dt_dprintf("pid %d: failed to find a.out`main: %s\n", (int)dpr->dpr_pid, strerror(errno)); } }
static void dt_proc_bpmatch(dtrace_hdl_t *dtp, dt_proc_t *dpr) { #ifdef illumos const lwpstatus_t *psp = &Pstatus(dpr->dpr_proc)->pr_lwp; #else unsigned long pc; #endif dt_bkpt_t *dbp; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); #ifndef illumos proc_regget(dpr->dpr_proc, REG_PC, &pc); proc_bkptregadj(&pc); #endif for (dbp = dt_list_next(&dpr->dpr_bps); dbp != NULL; dbp = dt_list_next(dbp)) { #ifdef illumos if (psp->pr_reg[R_PC] == dbp->dbp_addr) break; #else if (pc == dbp->dbp_addr) break; #endif } if (dbp == NULL) { dt_dprintf("pid %d: spurious breakpoint wakeup for %lx\n", #ifdef illumos (int)dpr->dpr_pid, (ulong_t)psp->pr_reg[R_PC]); #else (int)dpr->dpr_pid, pc); #endif return; }