| #include "config.h" | 
 |  | 
 | #define	_GNU_SOURCE	1 | 
 | #include <stdlib.h> | 
 | #include <sys/types.h> | 
 | #include <sys/wait.h> | 
 | #include <errno.h> | 
 | #include <signal.h> | 
 | #include <string.h> | 
 | #include <sys/ptrace.h> | 
 | #include <assert.h> | 
 |  | 
 | #include "common.h" | 
 |  | 
 | static Event event; | 
 |  | 
 | /* A queue of events that we missed while enabling the | 
 |  * breakpoint in one of tasks.  */ | 
 | static Event * delayed_events = NULL; | 
 | static Event * end_delayed_events = NULL; | 
 |  | 
 | static enum pcb_status | 
 | first (Process * proc, void * data) | 
 | { | 
 | 	return pcb_stop; | 
 | } | 
 |  | 
 | void | 
 | enque_event(Event * event) | 
 | { | 
 | 	debug(DEBUG_FUNCTION, "%d: queuing event %d for later", | 
 | 	      event->proc->pid, event->type); | 
 | 	Event * ne = malloc(sizeof(*ne)); | 
 | 	if (ne == NULL) { | 
 | 		perror("event will be missed: malloc"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	*ne = *event; | 
 | 	ne->next = NULL; | 
 | 	if (end_delayed_events == NULL) { | 
 | 		assert(delayed_events == NULL); | 
 | 		end_delayed_events = delayed_events = ne; | 
 | 	} | 
 | 	else { | 
 | 		assert(delayed_events != NULL); | 
 | 		end_delayed_events = end_delayed_events->next = ne; | 
 | 	} | 
 | } | 
 |  | 
 | Event * | 
 | each_qd_event(enum ecb_status (*pred)(Event *, void *), void * data) | 
 | { | 
 | 	Event * prev = delayed_events; | 
 | 	Event * event; | 
 | 	for (event = prev; event != NULL; ) { | 
 | 		switch ((*pred)(event, data)) { | 
 | 		case ecb_cont: | 
 | 			prev = event; | 
 | 			event = event->next; | 
 | 			continue; | 
 |  | 
 | 		case ecb_deque: | 
 | 			debug(DEBUG_FUNCTION, "dequeuing event %d for %d", | 
 | 			      event->type, | 
 | 			      event->proc != NULL ? event->proc->pid : -1); | 
 | 			/* | 
 | 			printf("dequeuing event %d for %d\n", event->type, | 
 | 			       event->proc != NULL ? event->proc->pid : -1) ; | 
 | 			*/ | 
 | 			if (end_delayed_events == event) | 
 | 				end_delayed_events = prev; | 
 | 			if (delayed_events == event) | 
 | 				delayed_events = event->next; | 
 | 			else | 
 | 				prev->next = event->next; | 
 | 			if (delayed_events == NULL) | 
 | 				end_delayed_events = NULL; | 
 | 			/* fall-through */ | 
 |  | 
 | 		case ecb_yield: | 
 | 			return event; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static enum ecb_status | 
 | event_process_not_reenabling(Event * event, void * data) | 
 | { | 
 | 	if (event->proc == NULL | 
 | 	    || event->proc->leader == NULL | 
 | 	    || event->proc->leader->event_handler == NULL) | 
 | 		return ecb_deque; | 
 | 	else | 
 | 		return ecb_cont; | 
 | } | 
 |  | 
 | static Event * | 
 | next_qd_event(void) | 
 | { | 
 | 	return each_qd_event(&event_process_not_reenabling, NULL); | 
 | } | 
 |  | 
 | Event * | 
 | next_event(void) | 
 | { | 
 | 	pid_t pid; | 
 | 	int status; | 
 | 	int tmp; | 
 | 	int stop_signal; | 
 |  | 
 | 	debug(DEBUG_FUNCTION, "next_event()"); | 
 | 	Event * ev; | 
 | 	if ((ev = next_qd_event()) != NULL) { | 
 | 		event = *ev; | 
 | 		free(ev); | 
 | 		return &event; | 
 | 	} | 
 |  | 
 | 	if (!each_process(NULL, &first, NULL)) { | 
 | 		debug(DEBUG_EVENT, "event: No more traced programs: exiting"); | 
 | 		exit(0); | 
 | 	} | 
 | 	pid = waitpid(-1, &status, __WALL); | 
 | 	if (pid == -1) { | 
 | 		if (errno == ECHILD) { | 
 | 			debug(DEBUG_EVENT, "event: No more traced programs: exiting"); | 
 | 			exit(0); | 
 | 		} else if (errno == EINTR) { | 
 | 			debug(DEBUG_EVENT, "event: none (wait received EINTR?)"); | 
 | 			event.type = EVENT_NONE; | 
 | 			return &event; | 
 | 		} | 
 | 		perror("wait"); | 
 | 		exit(1); | 
 | 	} | 
 | 	event.proc = pid2proc(pid); | 
 | 	if (!event.proc || event.proc->state == STATE_BEING_CREATED) { | 
 | 		event.type = EVENT_NEW; | 
 | 		event.e_un.newpid = pid; | 
 | 		debug(DEBUG_EVENT, "event: NEW: pid=%d", pid); | 
 | 		return &event; | 
 | 	} | 
 | 	get_arch_dep(event.proc); | 
 | 	debug(3, "event from pid %u", pid); | 
 | 	if (event.proc->breakpoints_enabled == -1) | 
 | 		trace_set_options(event.proc, event.proc->pid); | 
 | 	Process *leader = event.proc->leader; | 
 | 	if (leader == event.proc) { | 
 | 		if (event.proc->breakpoints_enabled == -1) { | 
 | 			event.type = EVENT_NONE; | 
 | 			enable_all_breakpoints(event.proc); | 
 | 			continue_process(event.proc->pid); | 
 | 			debug(DEBUG_EVENT, | 
 | 			      "event: NONE: pid=%d (enabling breakpoints)", | 
 | 			      pid); | 
 | 			return &event; | 
 | 		} else if (!event.proc->libdl_hooked) { | 
 | 			/* debug struct may not have been written yet.. */ | 
 | 			if (linkmap_init(event.proc, &main_lte) == 0) { | 
 | 				event.proc->libdl_hooked = 1; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* The process should be stopped after the waitpid call.  But | 
 | 	 * when the whole thread group is terminated, we see | 
 | 	 * individual tasks spontaneously transitioning from 't' to | 
 | 	 * 'R' and 'Z'.  Calls to ptrace fail and /proc/pid/status may | 
 | 	 * not even be available anymore, so we can't check in | 
 | 	 * advance.  So we just drop the error checking around ptrace | 
 | 	 * calls.  We check for termination ex post when it fails, | 
 | 	 * suppress the event, and let the event loop collect the | 
 | 	 * termination in the next iteration.  */ | 
 | #define CHECK_PROCESS_TERMINATED					\ | 
 | 	do {								\ | 
 | 		int errno_save = errno;					\ | 
 | 		switch (process_stopped(pid))				\ | 
 | 		case 0:							\ | 
 | 		case -1: {						\ | 
 | 			debug(DEBUG_EVENT,				\ | 
 | 			      "process not stopped, is it terminating?"); \ | 
 | 			event.type = EVENT_NONE;			\ | 
 | 			continue_process(event.proc->pid);		\ | 
 | 			return &event;					\ | 
 | 		}							\ | 
 | 		errno = errno_save;					\ | 
 | 	} while (0) | 
 |  | 
 | 	event.proc->instruction_pointer = (void *)(uintptr_t)-1; | 
 |  | 
 | 	/* Check for task termination now, before we have a need to | 
 | 	 * call CHECK_PROCESS_TERMINATED later.  That would suppress | 
 | 	 * the event that we are processing.  */ | 
 | 	if (WIFSIGNALED(status)) { | 
 | 		event.type = EVENT_EXIT_SIGNAL; | 
 | 		event.e_un.signum = WTERMSIG(status); | 
 | 		debug(DEBUG_EVENT, "event: EXIT_SIGNAL: pid=%d, signum=%d", pid, event.e_un.signum); | 
 | 		return &event; | 
 | 	} | 
 | 	if (WIFEXITED(status)) { | 
 | 		event.type = EVENT_EXIT; | 
 | 		event.e_un.ret_val = WEXITSTATUS(status); | 
 | 		debug(DEBUG_EVENT, "event: EXIT: pid=%d, status=%d", pid, event.e_un.ret_val); | 
 | 		return &event; | 
 | 	} | 
 |  | 
 | 	event.proc->instruction_pointer = get_instruction_pointer(event.proc); | 
 | 	if (event.proc->instruction_pointer == (void *)(uintptr_t)-1) { | 
 | 		CHECK_PROCESS_TERMINATED; | 
 | 		if (errno != 0) | 
 | 			perror("get_instruction_pointer"); | 
 | 	} | 
 |  | 
 | 	switch (syscall_p(event.proc, status, &tmp)) { | 
 | 		case 1: | 
 | 			event.type = EVENT_SYSCALL; | 
 | 			event.e_un.sysnum = tmp; | 
 | 			debug(DEBUG_EVENT, "event: SYSCALL: pid=%d, sysnum=%d", pid, tmp); | 
 | 			return &event; | 
 | 		case 2: | 
 | 			event.type = EVENT_SYSRET; | 
 | 			event.e_un.sysnum = tmp; | 
 | 			debug(DEBUG_EVENT, "event: SYSRET: pid=%d, sysnum=%d", pid, tmp); | 
 | 			return &event; | 
 | 		case 3: | 
 | 			event.type = EVENT_ARCH_SYSCALL; | 
 | 			event.e_un.sysnum = tmp; | 
 | 			debug(DEBUG_EVENT, "event: ARCH_SYSCALL: pid=%d, sysnum=%d", pid, tmp); | 
 | 			return &event; | 
 | 		case 4: | 
 | 			event.type = EVENT_ARCH_SYSRET; | 
 | 			event.e_un.sysnum = tmp; | 
 | 			debug(DEBUG_EVENT, "event: ARCH_SYSRET: pid=%d, sysnum=%d", pid, tmp); | 
 | 			return &event; | 
 | 		case -1: | 
 | 			CHECK_PROCESS_TERMINATED; | 
 | 			if (errno != 0) | 
 | 				perror("syscall_p"); | 
 | 	} | 
 | 	if (WIFSTOPPED(status) && ((status>>16 == PTRACE_EVENT_FORK) || (status>>16 == PTRACE_EVENT_VFORK) || (status>>16 == PTRACE_EVENT_CLONE))) { | 
 | 		unsigned long data; | 
 | 		ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data); | 
 | 		event.type = EVENT_CLONE; | 
 | 		event.e_un.newpid = data; | 
 | 		debug(DEBUG_EVENT, "event: CLONE: pid=%d, newpid=%d", pid, (int)data); | 
 | 		return &event; | 
 | 	} | 
 | 	if (WIFSTOPPED(status) && (status>>16 == PTRACE_EVENT_EXEC)) { | 
 | 		event.type = EVENT_EXEC; | 
 | 		debug(DEBUG_EVENT, "event: EXEC: pid=%d", pid); | 
 | 		return &event; | 
 | 	} | 
 | 	if (!WIFSTOPPED(status)) { | 
 | 		/* should never happen */ | 
 | 		event.type = EVENT_NONE; | 
 | 		debug(DEBUG_EVENT, "event: NONE: pid=%d (wait error?)", pid); | 
 | 		return &event; | 
 | 	} | 
 |  | 
 | 	stop_signal = WSTOPSIG(status); | 
 |  | 
 | 	/* On some targets, breakpoints are signalled not using | 
 | 	   SIGTRAP, but also with SIGILL, SIGSEGV or SIGEMT.  SIGEMT | 
 | 	   is not defined on Linux, but check for the others. | 
 |  | 
 | 	   N.B. see comments in GDB's infrun.c for details.  I've | 
 | 	   actually seen this on an Itanium machine on RHEL 5, I don't | 
 | 	   remember the exact kernel version anymore.  ia64-sigill.s | 
 | 	   in the test suite tests this.  Petr Machata 2011-06-08.  */ | 
 | 	void * break_address | 
 | 		= event.proc->instruction_pointer - DECR_PC_AFTER_BREAK; | 
 | 	if ((stop_signal == SIGSEGV || stop_signal == SIGILL) | 
 | 	    && leader != NULL | 
 | 	    && address2bpstruct(leader, break_address)) | 
 | 			stop_signal = SIGTRAP; | 
 |  | 
 | 	if (stop_signal != (SIGTRAP | event.proc->tracesysgood) | 
 | 			&& stop_signal != SIGTRAP) { | 
 | 		event.type = EVENT_SIGNAL; | 
 | 		event.e_un.signum = stop_signal; | 
 | 		debug(DEBUG_EVENT, "event: SIGNAL: pid=%d, signum=%d", pid, stop_signal); | 
 | 		return &event; | 
 | 	} | 
 |  | 
 | 	/* last case [by exhaustion] */ | 
 | 	event.type = EVENT_BREAKPOINT; | 
 |  | 
 | 	event.e_un.brk_addr = break_address; | 
 | 	debug(DEBUG_EVENT, "event: BREAKPOINT: pid=%d, addr=%p", pid, event.e_un.brk_addr); | 
 |  | 
 | 	return &event; | 
 | } |