#pragma ident "@(#)dt_proc.c 1.13 06/02/08 SMI"
#include <assert.h>
#include <errno.h>
#include "dt_impl.h"
#include <dt_proc.h>
#include <dt_pid.h>
extern void dt_proc_notify(dtrace_hdl_t *dtp, dt_proc_hash_t *dph, dt_proc_t *dpr, const char *msg);
extern void dt_proc_stop(dt_proc_t *dpr, uint8_t why);
extern void dt_proc_bpmain(dtrace_hdl_t *dtp, dt_proc_t *dpr, const char *fname);
extern dt_bkpt_t *dt_proc_bpcreate(dt_proc_t *dpr, uintptr_t addr, dt_bkpt_f *func, void *data);
extern void dt_proc_bpdestroy(dt_proc_t *, int);
extern dt_proc_rdwatch(dt_proc_t *dpr, rd_event_e event, const char *evname);
extern Phandler_func_t dt_proc_control_activity_handler;
extern psaddr_t rd_event_mock_addr(struct ps_prochandle *);
static void
dt_proc_bpmatch(dtrace_hdl_t *dtp, 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 (rd_event_mock_addr(dpr->dpr_proc) == dbp->dbp_addr)
break;
}
if (dbp == NULL) {
dt_dprintf("pid %d: spurious breakpoint wakeup for %lx\n",
(int)dpr->dpr_pid, rd_event_mock_addr(dpr->dpr_proc));
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_attach(dt_proc_t *dpr, int exec)
{
rd_err_e err;
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 0
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));
}
#else
#warning Need mechanism for stop at a.out`main
#endif
}
typedef struct dt_proc_control_data {
dtrace_hdl_t *dpcd_hdl;
dt_proc_t *dpcd_proc;
} dt_proc_control_data_t;
void *
dt_proc_control(void *arg)
{
dt_proc_control_data_t *datap = arg;
dtrace_hdl_t *dtp = datap->dpcd_hdl;
dt_proc_t *dpr = datap->dpcd_proc;
dt_proc_hash_t *dph = dpr->dpr_hdl->dt_procs;
struct ps_prochandle *P = dpr->dpr_proc;
int pid = dpr->dpr_pid;
int notify = B_FALSE;
(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
(void) pthread_mutex_lock(&dpr->dpr_lock);
(void) Punsetflags(P, PR_ASYNC);
(void) Psetflags(P, PR_BPTADJ);
(void) Punsetflags(P, PR_FORK);
dt_proc_attach(dpr, B_FALSE);
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);
while (!dpr->dpr_quit) {
void *activity = Pdequeue_proc_activity(P);
if (!activity)
continue;
(void) pthread_mutex_lock(&dpr->dpr_lock);
switch (Pstate(P)) {
case PS_STOP:
dt_dprintf("pid %d: proc stopped\n", pid);
dt_proc_bpmatch(dtp, dpr);
break;
case PS_RUN:
dt_proc_bpmatch(dtp, dpr);
break;
case PS_LOST:
dt_dprintf("pid %d: proc lost\n", pid);
dpr->dpr_quit = B_TRUE;
notify = B_TRUE;
break;
case PS_DEAD:
case PS_UNDEAD:
dt_dprintf("pid %d: proc died\n", pid);
dpr->dpr_quit = B_TRUE;
notify = B_TRUE;
break;
default:
assert(false);
dt_dprintf("pid %d: proc in unrecognized state, resuming\n", pid);
break;
}
(void) pthread_mutex_unlock(&dpr->dpr_lock);
Pdestroy_proc_activity(activity);
}
if (notify)
dt_proc_notify(dtp, dph, dpr, NULL);
(void) pthread_mutex_lock(&dpr->dpr_lock);
dt_proc_bpdestroy(dpr, B_TRUE);
dpr->dpr_done = B_TRUE;
dpr->dpr_tid = 0;
(void) pthread_cond_broadcast(&dpr->dpr_cv);
(void) pthread_mutex_unlock(&dpr->dpr_lock);
return (NULL);
}