diff mbox series

[v2,31/38] gdbserver: track stop reason per thread

Message ID 20220329154931.493851-32-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
Maintain the reason why each thread has stopped, which allows in
particular distinguishing between threads that encountered a trap, one
that took an interrupt, from those that were stopped afterwards due to
all-stop semantics.

This allows sending TRAP/INT/other stop reasons as appropriate rather
than always sending TRAP. While here, switch to the more capable T
packet rather than S packet, which can includes the thread-id.

Switch target to the thread that hit a breakpoint when stopping.

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

Patch

diff --git a/src/pdbgproxy.c b/src/pdbgproxy.c
index db80b6a..fca728f 100644
--- a/src/pdbgproxy.c
+++ b/src/pdbgproxy.c
@@ -30,7 +30,16 @@ 
 
 #ifndef DISABLE_GDBSERVER
 
-/* Maximum packet size */
+/*
+ * If the client sents qSupported then it can handle unlimited length response.
+ * Shouldn't need more than 8k for now. If we had to handle old clients, 512
+ * should be a lower limit.
+ */
+#define MAX_RESP_LEN	8192
+
+/*
+ * Maximum packet size. We don't advertise this to the client (TODO).
+ */
 #define BUFFER_SIZE    	8192
 
 /* GDB packets */
@@ -38,7 +47,6 @@ 
 #define ACK "+"
 #define NACK "-"
 #define OK "OK"
-#define TRAP "S05"
 #define ERROR(e) "E"STR(e)
 
 #define TEST_SKIBOOT_ADDR 0x40000000
@@ -56,6 +64,8 @@  static enum client_state state = IDLE;
 struct gdb_thread {
 	uint64_t pir;
 	bool attn_set;
+	bool stop_attn;
+	bool stop_ctrlc;
 };
 
 static void destroy_client(int dead_fd);
@@ -132,9 +142,32 @@  static void set_thread(uint64_t *stack, void *priv)
 	send_response(fd, ERROR(EEXIST));
 }
 
+static void send_stop_for_thread(struct pdbg_target *target)
+{
+	struct thread *thread = target_to_thread(target);
+	struct gdb_thread *gdb_thread = thread->gdbserver_priv;
+	uint64_t pir = gdb_thread->pir;
+	char data[MAX_RESP_LEN];
+	size_t s = 0;
+	int sig;
+
+	if (gdb_thread->stop_attn)
+		sig = 5; /* TRAP */
+	else if (gdb_thread->stop_ctrlc)
+		sig = 2; /* INT */
+	else
+		sig = 0; /* default / initial stop reason */
+
+	s += snprintf(data + s, sizeof(data) - s, "T%02uthread:%04" PRIx64 ";%s", sig, pir, sig == 5 ? "swbreak:;" : "");
+
+	send_response(fd, data);
+}
+
 static void stop_reason(uint64_t *stack, void *priv)
 {
-	send_response(fd, TRAP);
+	PR_INFO("stop_reason\n");
+
+	send_stop_for_thread(thread_target);
 }
 
 static void detach(uint64_t *stack, void *priv)
@@ -539,8 +572,17 @@  out:
 
 static void v_conts(uint64_t *stack, void *priv)
 {
+	struct thread *thread = target_to_thread(thread_target);
+	struct gdb_thread *gdb_thread = thread->gdbserver_priv;
+
+	PR_INFO("thread_step\n");
+
 	thread_step(thread_target, 1);
-	send_response(fd, TRAP);
+
+	gdb_thread->stop_ctrlc = false;
+	gdb_thread->stop_attn = false;
+
+	send_stop_for_thread(thread_target);
 }
 
 static void __start_all(void)
@@ -568,6 +610,8 @@  static void start_all(void)
 	struct pdbg_target *target;
 
 	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)
@@ -577,6 +621,11 @@  static void start_all(void)
 		status = thread_status(target);
 		if (!status.quiesced)
 			PR_ERROR("starting thread not quiesced\n");
+
+		gdb_thread = thread->gdbserver_priv;
+
+		gdb_thread->stop_attn = false;
+		gdb_thread->stop_ctrlc = false;
 	}
 
 	__start_all();
@@ -691,6 +740,8 @@  static void stop_all(void)
 
 			PR_INFO("thread pir=%llx hit attn\n", gdb_thread->pir);
 
+			gdb_thread->stop_attn = true;
+
 			if (!(status.active))
 				PR_ERROR("Error thread inactive after trap\n");
 			/* Restore NIA to before break */
@@ -706,13 +757,18 @@  static void interrupt(uint64_t *stack, void *priv)
 {
 	PR_INFO("Interrupt from gdb client\n");
 	if (state != IDLE) {
+		struct thread *thread = target_to_thread(thread_target);
+		struct gdb_thread *gdb_thread = thread->gdbserver_priv;
+
 		stop_all();
+		if (!gdb_thread->stop_attn)
+			gdb_thread->stop_ctrlc = true;
 
 		state = IDLE;
 		poll_interval = VCONT_POLL_DELAY;
 	}
 
-	send_response(fd, TRAP);
+	send_stop_for_thread(thread_target);
 }
 
 static bool poll_threads(void)
@@ -735,6 +791,8 @@  static bool poll_threads(void)
 
 static void poll(void)
 {
+	struct pdbg_target *target;
+
 	if (state != SIGNAL_WAIT)
 		return;
 
@@ -745,12 +803,27 @@  static void poll(void)
 
 	stop_all();
 
+	for_each_path_target_class("thread", target) {
+		struct thread *thread = target_to_thread(target);
+		struct gdb_thread *gdb_thread;
+
+		if (pdbg_target_status(target) != PDBG_TARGET_ENABLED)
+			continue;
+
+		gdb_thread = thread->gdbserver_priv;
+
+		if (gdb_thread->stop_attn) {
+			thread_target = target;
+			break;
+		}
+	}
+
 	set_attn(false);
 
 	state = IDLE;
 	poll_interval = VCONT_POLL_DELAY;
 
-	send_response(fd, TRAP);
+	send_stop_for_thread(thread_target);
 }
 
 static void cmd_default(uint64_t *stack, void *priv)