Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > media > main-src > by-pkgid > b3bd92884018251b87f9099340c300c3 > files > 27

ltrace-0.5-13.45svn.el5_7.12.src.rpm

diff -urp ltrace-0.5-orig/sysdeps/linux-gnu/trace.c ltrace-0.5-pm/sysdeps/linux-gnu/trace.c
--- ltrace-0.5-orig/sysdeps/linux-gnu/trace.c	2011-08-20 23:03:58.093608714 +0200
+++ ltrace-0.5-pm/sysdeps/linux-gnu/trace.c	2011-08-20 23:06:03.348270689 +0200
@@ -126,7 +126,7 @@ struct pid_set {
 };
 
 /**
- * Breakpoint re-enablement.  When we hit a breakpoint, we must
+ * struct breakpoint re-enablement.  When we hit a breakpoint, we must
  * disable it, single-step, and re-enable it.  That single-step can be
  * done only by one task in a task group, while others are stopped,
  * otherwise the processes would race for who sees the breakpoint
@@ -153,8 +153,13 @@ struct process_stopping_handler
 		/* We are waiting for all the SIGSTOPs to arrive so
 		 * that we can sink them.  */
 		psh_sinking,
+
+		/* This is for tracking the ugly workaround.  */
+		psh_ugly_workaround,
 	} state;
 
+	int exiting;
+
 	struct pid_set pids;
 };
 
@@ -248,23 +253,94 @@ send_sigstop(struct process * task, void
 }
 
 static void
+ugly_workaround(struct process * proc, int cont)
+{
+	void * ip = get_instruction_pointer(proc);
+	struct breakpoint * sbp = dict_find_entry(proc->leader->breakpoints, ip);
+	if (sbp != NULL)
+		enable_breakpoint(proc, sbp);
+	else
+		insert_breakpoint(proc, ip, NULL, 1);
+	if (cont)
+		ptrace(PTRACE_CONT, proc->pid, 0, 0);
+}
+
+static void
 process_stopping_done(struct process_stopping_handler * self,
 		      struct process * leader)
 {
 	size_t i;
-	for (i = 0; i < self->pids.count; ++i)
-		if (self->pids.tasks[i].pid != 0
-		    && self->pids.tasks[i].delivered)
-			continue_process(self->pids.tasks[i].pid);
-	continue_process(self->task_enabling_breakpoint->pid);
+	if (!self->exiting) {
+		for (i = 0; i < self->pids.count; ++i)
+			if (self->pids.tasks[i].pid != 0
+			    && self->pids.tasks[i].delivered)
+				continue_process(self->pids.tasks[i].pid);
+		continue_process(self->task_enabling_breakpoint->pid);
+		destroy_event_handler(leader);
+	} else {
+		self->state = psh_ugly_workaround;
+		ugly_workaround(self->task_enabling_breakpoint, 1);
+	}
+}
+
+/* Before we detach, we need to make sure that task's IP is on the
+ * edge of an instruction.  So for tasks that have a breakpoint event
+ * in the queue, we adjust the instruction pointer, just like
+ * continue_after_breakpoint does.  */
+static enum ecb_status
+undo_breakpoint(struct event * event, void * data)
+{
+	if (event != NULL
+	    && event->proc->leader == data
+	    && event->thing == LT_EV_BREAKPOINT)
+		set_instruction_pointer(event->proc, event->e_un.brk_addr);
+	return ecb_cont;
+}
+
+static enum pcb_status
+untrace_task(struct process * task, void * data)
+{
+	if (task != data)
+		untrace_pid(task->pid);
+	return pcb_cont;
+}
+
+static enum pcb_status
+remove_task(struct process * task, void * data)
+{
+	/* Don't untrace leader just yet.  */
+	if (task != data)
+		remove_process(task);
+	return pcb_cont;
+}
+
+static void
+detach_process(struct process * leader)
+{
+	each_qd_event(&undo_breakpoint, leader);
+	disable_all_breakpoints(leader);
+
+	/* Now untrace the process, if it was attached to by -p.  */
+	struct opt_p_t * it;
+	for (it = opt_p; it != NULL; it = it->next) {
+		struct process * proc = pid2proc(it->pid);
+		if (proc == NULL)
+			continue;
+		if (proc->leader == leader) {
+			each_task(leader, &untrace_task, NULL);
+			break;
+		}
+	}
+	each_task(leader, &remove_task, leader);
 	destroy_event_handler(leader);
+	remove_task(leader, NULL);
 }
 
 static void
 handle_stopping_event(struct pid_task * task_info, struct event ** eventp)
 {
 	/* Mark all events, so that we know whom to SIGCONT later.  */
-	if (task_info != NULL && task_info->sigstopped)
+	if (task_info != NULL)
 		task_info->got_event = 1;
 
 	struct event * event = *eventp;
@@ -354,6 +430,18 @@ await_sigstop_delivery(struct pid_set * 
 	return 0;
 }
 
+static int
+all_stops_accountable(struct pid_set * pids)
+{
+	size_t i;
+	for (i = 0; i < pids->count; ++i)
+		if (pids->tasks[i].pid != 0
+		    && !pids->tasks[i].got_event
+		    && !have_events_for(pids->tasks[i].pid))
+			return 0;
+	return 1;
+}
+
 /* This event handler is installed when we are in the process of
  * stopping the whole thread group to do the pointer re-enablement for
  * one of the threads.  We pump all events to the queue for later
@@ -401,8 +489,8 @@ process_stopping_on_event(Event_Handler 
 			/* Essentially we don't care what event caused
 			 * the thread to stop.  We can do the
 			 * re-enablement now.  */
-			enable_breakpoint(self->task_enabling_breakpoint,
-					  self->breakpoint_being_enabled);
+			if (sbp->enabled)
+				enable_breakpoint(teb, sbp);
 
 			continue_for_sigstop_delivery(&self->pids);
 
@@ -420,6 +508,22 @@ process_stopping_on_event(Event_Handler 
 	case psh_sinking:
 		if (await_sigstop_delivery(&self->pids, task_info, event))
 			process_stopping_done(self, leader);
+		break;
+
+	case psh_ugly_workaround:
+		if (event == NULL)
+			break;
+		if (event->thing == LT_EV_BREAKPOINT) {
+			undo_breakpoint(event, leader);
+			if (task == teb)
+				self->task_enabling_breakpoint = NULL;
+		}
+		if (self->task_enabling_breakpoint == NULL
+		    && all_stops_accountable(&self->pids)) {
+			undo_breakpoint(event, leader);
+			detach_process(leader);
+			event = NULL; // handled
+		}
 	}
 
 	if (event != NULL && event_to_queue) {
@@ -434,9 +538,6 @@ static void
 process_stopping_destroy(Event_Handler * super)
 {
 	struct process_stopping_handler * self = (void *)super;
-	if (self->breakpoint_being_enabled != NULL)
-		enable_breakpoint(self->task_enabling_breakpoint,
-				  self->breakpoint_being_enabled);
 	free(self->pids.tasks);
 }
 
@@ -444,8 +545,6 @@ void continue_after_breakpoint(struct pr
 {
 	set_instruction_pointer(proc, sbp->addr);
 	if (sbp->enabled == 0) {
-		if (sbp->enabled)
-			disable_breakpoint(proc, sbp);
 		continue_process(proc->pid);
 	} else {
 #if defined __sparc__  || defined __ia64___
@@ -532,22 +631,6 @@ struct ltrace_exiting_handler
 	struct pid_set pids;
 };
 
-static enum pcb_status
-remove_task(struct process * task, void * data)
-{
-	/* Don't untrace leader just yet.  */
-	if (task != data)
-		remove_process(task);
-	return pcb_cont;
-}
-
-static enum pcb_status
-untrace_task(struct process * task, void * data)
-{
-	untrace_pid(task->pid);
-	return pcb_cont;
-}
-
 static struct event *
 ltrace_exiting_on_event(Event_Handler * super, struct event * event)
 {
@@ -558,33 +641,19 @@ ltrace_exiting_on_event(Event_Handler * 
 	struct pid_task * task_info = get_task_info(&self->pids, task->pid);
 	handle_stopping_event(task_info, &event);
 
-	if (await_sigstop_delivery(&self->pids, task_info, event)) {
-		disable_all_breakpoints(leader);
-
-		/* Now untrace the process, if it was attached to by -p.  */
-		struct opt_p_t * it;
-		for (it = opt_p; it != NULL; it = it->next) {
-			struct process * proc = pid2proc(it->pid);
-			if (proc == NULL)
-				continue;
-			if (proc->leader == leader) {
-				each_task(leader, &untrace_task, NULL);
-				break;
-			}
-		}
+	if (event != NULL && event->thing == LT_EV_BREAKPOINT)
+		undo_breakpoint(event, leader);
 
-		each_task(leader, &remove_task, leader);
-		destroy_event_handler(leader);
-		remove_task(leader, NULL);
-		return NULL;
-	}
+	if (await_sigstop_delivery(&self->pids, task_info, event)
+	    && all_stops_accountable(&self->pids))
+		detach_process(leader);
 
 	/* Sink all non-exit events.  We are about to exit, so we
 	 * don't bother with queuing them. */
 	if (event_exit_or_none_p(event))
 		return event;
-	else
-		return NULL;
+
+	return NULL;
 }
 
 static void
@@ -607,6 +676,17 @@ ltrace_exiting_install_handler(struct pr
 	    && proc->event_handler->on_event == &ltrace_exiting_on_event)
 		return 0;
 
+	/* If stopping handler is already present, let it do the
+	 * work.  */
+	if (proc->event_handler != NULL) {
+		assert(proc->event_handler->on_event
+		       == &process_stopping_on_event);
+		struct process_stopping_handler * other
+			= (void *)proc->event_handler;
+		other->exiting = 1;
+		return 0;
+	}
+
 	struct ltrace_exiting_handler * handler
 		= calloc(sizeof(*handler), 1);
 	if (handler == NULL) {
@@ -616,34 +696,6 @@ ltrace_exiting_install_handler(struct pr
 		return -1;
 	}
 
-	/* If we are in the middle of breakpoint, extract the
-	 * pid-state information from that handler so that we can take
-	 * over the SIGSTOP handling.  */
-	if (proc->event_handler != NULL) {
-		assert(proc->event_handler->on_event
-		       == &process_stopping_on_event);
-		struct process_stopping_handler * other
-			= (void *)proc->event_handler;
-		size_t i;
-		for (i = 0; i < other->pids.count; ++i) {
-			struct pid_task * oti = &other->pids.tasks[i];
-			if (oti->pid == 0)
-				continue;
-			struct pid_task * task_info
-				= add_task_info(&handler->pids, oti->pid);
-			if (task_info == NULL) {
-				perror("ltrace_exiting_install_handler"
-				       ":add_task_info");
-				goto fatal;
-			}
-			/* Copy over the state.  */
-			*task_info = *oti;
-		}
-
-		/* And destroy the original handler.  */
-		destroy_event_handler(proc);
-	}
-
 	handler->super.on_event = ltrace_exiting_on_event;
 	handler->super.destroy = ltrace_exiting_destroy;
 	install_event_handler(proc->leader, &handler->super);
Only in ltrace-0.5-pm/sysdeps/linux-gnu: trace.c~