diff mbox series

[v2,34/38] gdbserver: better deal with threads initially stopped

Message ID 20220329154931.493851-35-npiggin@gmail.com
State New
Headers show
Series gdbserver multi-threaded debugging and POWER9/10 support | expand

Commit Message

Nicholas Piggin March 29, 2022, 3:49 p.m. UTC
Better deal with threads that are quiesced when gdbserver starts.
Record and clear SPATTN register to determine whether any had hit
attn, and prevent subsequent stop from getting a false positive on
SPATTN.

Switch target thread to the first one that had hit an attn or been
stopped when gdbserver starts up, if any.

Don't resume initially-stopped threads unless a client attaches and
directs them to.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 src/pdbgproxy.c | 90 +++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 80 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/src/pdbgproxy.c b/src/pdbgproxy.c
index 3ffa9e2..7dc0555 100644
--- a/src/pdbgproxy.c
+++ b/src/pdbgproxy.c
@@ -64,6 +64,7 @@  static enum client_state state = IDLE;
 struct gdb_thread {
 	uint64_t pir;
 	bool attn_set;
+	bool initial_stopped;
 	bool stop_attn;
 	bool stop_ctrlc;
 };
@@ -1058,11 +1059,15 @@  static int gdbserver_start(struct pdbg_target *adu, uint16_t port)
 
 static int gdbserver(uint16_t port)
 {
-	struct pdbg_target *target, *adu, *first_target = NULL;
+	struct pdbg_target *target, *adu;
+	struct pdbg_target *first_target = NULL;
+	struct pdbg_target *first_stopped_target = NULL;
+	struct pdbg_target *first_attn_target = NULL;
 
 	for_each_path_target_class("thread", target) {
 		struct thread *thread = target_to_thread(target);
 		struct gdb_thread *gdb_thread;
+		struct thread_state status;
 
 		if (pdbg_target_status(target) != PDBG_TARGET_ENABLED)
 			continue;
@@ -1080,6 +1085,13 @@  static int gdbserver(uint16_t port)
 
 		if (!first_target)
 			first_target = target;
+
+		status = thread_status(target);
+		if (status.quiesced) {
+			if (!first_stopped_target)
+				first_stopped_target = target;
+			gdb_thread->initial_stopped = true;
+		}
 	}
 
 	if (!first_target) {
@@ -1099,16 +1111,19 @@  static int gdbserver(uint16_t port)
 		PR_WARNING("GDBSERVER works best when targeting all threads (-a)\n");
 	}
 
-	thread_target = first_target;
-
 	for_each_path_target_class("thread", target) {
+		struct thread *thread = target_to_thread(target);
+		struct gdb_thread *gdb_thread = thread->gdbserver_priv;
+
 		if (pdbg_target_status(target) != PDBG_TARGET_ENABLED)
 			continue;
 
-		if (thread_stop(target)) {
-			PR_ERROR("Could not stop thread %s\n",
-				 pdbg_target_path(target));
-			return -1;
+		if (!gdb_thread->initial_stopped) {
+			if (thread_stop(target)) {
+				PR_ERROR("Could not stop thread %s\n",
+					 pdbg_target_path(target));
+				return -1;
+			}
 		}
 	}
 
@@ -1129,9 +1144,46 @@  static int gdbserver(uint16_t port)
 			PR_ERROR("PIR exceeds 16-bits.");
 			goto out;
 		}
+
+		if (thread_check_attn(target)) {
+			PR_INFO("thread pir=%llx hit attn\n", gdb_thread->pir);
+
+			if (!first_attn_target)
+				first_attn_target = target;
+			gdb_thread->stop_attn = true;
+			if (!gdb_thread->initial_stopped) {
+				PR_WARNING("thread pir=%llx hit attn but was not stopped\n", gdb_thread->pir);
+				gdb_thread->initial_stopped = true;
+			}
+		}
 	}
 
-	start_all();
+	/* Target attn as a priority, then any stopped, then first */
+	if (first_attn_target)
+		thread_target = first_target;
+	else if (first_stopped_target)
+		thread_target = first_stopped_target;
+	else
+		thread_target = first_target;
+
+	/*
+	 * Resume threads now, except those that were initially stopped,
+	 * leave them so the client can inspect them.
+	 */
+	for_each_path_target_class("thread", target) {
+		struct thread *thread = target_to_thread(target);
+		struct gdb_thread *gdb_thread = thread->gdbserver_priv;
+
+		if (pdbg_target_status(target) != PDBG_TARGET_ENABLED)
+			continue;
+
+		if (!gdb_thread->initial_stopped) {
+			if (thread_start(target)) {
+				PR_ERROR("Could not start thread %s\n",
+					 pdbg_target_path(target));
+			}
+		}
+	}
 
 	/* Select ADU target */
 	pdbg_for_each_class_target("mem", adu) {
@@ -1147,8 +1199,26 @@  static int gdbserver(uint16_t port)
 	gdbserver_start(adu, port);
 
 out:
-	if (all_stopped)
-		__start_all();
+	if (!all_stopped)
+		stop_all();
+
+	/*
+	 * Only resume those which were not initially stopped
+	 */
+	for_each_path_target_class("thread", target) {
+		struct thread *thread = target_to_thread(target);
+		struct gdb_thread *gdb_thread = thread->gdbserver_priv;
+
+		if (pdbg_target_status(target) != PDBG_TARGET_ENABLED)
+			continue;
+
+		if (!gdb_thread->initial_stopped) {
+			if (thread_start(target)) {
+				PR_ERROR("Could not start thread %s\n",
+					 pdbg_target_path(target));
+			}
+		}
+	}
 
 	return 0;
 }