Rewrite the whole detach logic to use stopping handler
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index d66ead3..8e4288f 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -212,6 +212,8 @@
 		psh_ugly_workaround,
 	} state;
 
+	int exiting;
+
 	struct pid_set pids;
 };
 
@@ -306,17 +308,88 @@
 }
 
 static void
+ugly_workaround(Process * proc, int cont)
+{
+	void * ip = get_instruction_pointer(proc);
+	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, Process * leader)
 {
 	debug(DEBUG_PROCESS, "process stopping done %d",
 	      self->task_enabling_breakpoint->pid);
 	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(Event * event, void * data)
+{
+	if (event != NULL
+	    && event->proc->leader == data
+	    && event->type == EVENT_BREAKPOINT)
+		set_instruction_pointer(event->proc, event->e_un.brk_addr);
+	return ecb_cont;
+}
+
+static enum pcb_status
+untrace_task(Process * task, void * data)
+{
+	if (task != data)
+		untrace_pid(task->pid);
+	return pcb_cont;
+}
+
+static enum pcb_status
+remove_task(Process * task, void * data)
+{
+	/* Don't untrace leader just yet.  */
+	if (task != data)
+		remove_process(task);
+	return pcb_cont;
+}
+
+static void
+detach_process(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) {
+		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
@@ -417,6 +490,18 @@
 	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
@@ -469,7 +554,8 @@
 			/* Essentially we don't care what event caused
 			 * the thread to stop.  We can do the
 			 * re-enablement now.  */
-			enable_breakpoint(teb, sbp);
+			if (sbp->enabled)
+				enable_breakpoint(teb, sbp);
 
 			continue_for_sigstop_delivery(&self->pids);
 
@@ -487,6 +573,22 @@
 	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->type == EVENT_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) {
@@ -501,9 +603,6 @@
 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);
 }
 
@@ -512,8 +611,6 @@
 {
 	set_instruction_pointer(proc, sbp->addr);
 	if (sbp->enabled == 0) {
-		if (sbp->enabled)
-			disable_breakpoint(proc, sbp);
 		continue_process(proc->pid);
 	} else {
 		debug(DEBUG_PROCESS,
@@ -565,69 +662,8 @@
 {
 	Event_Handler super;
 	struct pid_set pids;
-	int state;
-	Process * task_enabling_breakpoint;
 };
 
-static enum pcb_status
-remove_task(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(Process * task, void * data)
-{
-	untrace_pid(task->pid);
-	return pcb_cont;
-}
-
-/* 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(Event * event, void * data)
-{
-	if (event != NULL
-	    && event->proc->leader == data
-	    && event->type == EVENT_BREAKPOINT) {
-		fprintf(stderr, " + %p ", get_instruction_pointer(event->proc));
-		set_instruction_pointer(event->proc, event->e_un.brk_addr);
-		fprintf(stderr, "-> %p\n", get_instruction_pointer(event->proc));
-	}
-	return ecb_cont;
-}
-
-static void
-ugly_workaround(Process * proc, int cont)
-{
-	void * ip = get_instruction_pointer(proc);
-	fprintf(stderr, "activate workaround %d %p\n", proc->pid, ip);
-	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 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;
-}
-
 static Event *
 ltrace_exiting_on_event(Event_Handler * super, Event * event)
 {
@@ -640,47 +676,12 @@
 	struct pid_task * task_info = get_task_info(&self->pids, task->pid);
 	handle_stopping_event(task_info, &event);
 
-	if (event != NULL && event->type == EVENT_BREAKPOINT) {
-		if (self->state == psh_singlestep
-		    && self->task_enabling_breakpoint == event->proc) {
-			ugly_workaround(event->proc, 1);
-			self->state = psh_ugly_workaround;
-		} else {
-			undo_breakpoint(event, leader);
-			if (self->state == psh_ugly_workaround
-			    && self->task_enabling_breakpoint == event->proc) {
-				self->task_enabling_breakpoint = NULL;
-				self->state = psh_sinking;
-			}
-		}
-	}
+	if (event != NULL && event->type == EVENT_BREAKPOINT)
+		undo_breakpoint(event, leader);
 
 	if (await_sigstop_delivery(&self->pids, task_info, event)
-	    && (self->task_enabling_breakpoint == NULL
-		/* NB: psh_ugly_workaround > psh_singlestep  */
-		|| self->state < psh_singlestep)
-	    && all_stops_accountable(&self->pids)) {
-		debug(DEBUG_PROCESS, "all SIGSTOPs delivered %d", leader->pid);
-		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) {
-			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);
-		return NULL;
-	}
+	    && 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. */
@@ -710,6 +711,17 @@
 	    && 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) {
@@ -719,51 +731,6 @@
 		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) {
-		debug(DEBUG_PROCESS, "taking over breakpoint handling");
-		assert(proc->event_handler->on_event
-		       == &process_stopping_on_event);
-		struct process_stopping_handler * other
-			= (void *)proc->event_handler;
-
-		handler->task_enabling_breakpoint
-			= other->task_enabling_breakpoint;
-		if (other->state == psh_sinking) {
-			ugly_workaround(handler->task_enabling_breakpoint, 1);
-			handler->state = psh_ugly_workaround;
-		} else {
-			handler->state = other->state;
-		}
-
-		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;
-		}
-
-		/* The re-enablement handler sets this to NULL when
-		 * it calls continue_for_sigstop_delivery.  */
-		if (other->breakpoint_being_enabled != NULL)
-			continue_for_sigstop_delivery(&handler->pids);
-
-		/* 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);