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 + -
显示快捷键?