diff mbox series

[v2,35/39] gdbserver: better deal with threads initially stopped

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

Commit Message

Nicholas Piggin April 20, 2022, 6:50 a.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(-)

Comments

Joel Stanley May 3, 2022, 7:40 a.m. UTC | #1
On Wed, 20 Apr 2022 at 06:51, Nicholas Piggin <npiggin@gmail.com> wrote:
>
> 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>

Reviewed-by: Joel Stanley <joel@jms.id.au>

> ---
>  src/pdbgproxy.c | 90 +++++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 80 insertions(+), 10 deletions(-)
>
> diff --git a/src/pdbgproxy.c b/src/pdbgproxy.c
> index 642cbe61..6b9bdf90 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=%"PRIx64" 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=%"PRIx64" 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;
>  }
> --
> 2.35.1
>
> --
> Pdbg mailing list
> Pdbg@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/pdbg
diff mbox series

Patch

diff --git a/src/pdbgproxy.c b/src/pdbgproxy.c
index 642cbe61..6b9bdf90 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=%"PRIx64" 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=%"PRIx64" 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;
 }