]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Centralize "[Thread ...exited]" notifications
authorPedro Alves <pedro@palves.net>
Tue, 21 Jun 2022 18:30:48 +0000 (19:30 +0100)
committerPedro Alves <pedro@palves.net>
Fri, 10 Mar 2023 19:14:20 +0000 (19:14 +0000)
Currently, each target backend is responsible for printing "[Thread
...exited]" before deleting a thread.  This leads to unnecessary
differences between targets, like e.g. with the remote target, we
never print such messages, even though we do print "[New Thread ...]".

E.g., debugging the gdb.threads/attach-many-short-lived-threads.exp
with gdbserver, letting it run for a bit, and then pressing Ctrl-C, we
currently see:

 (gdb) c
 Continuing.
 ^C[New Thread 3850398.3887449]
 [New Thread 3850398.3887500]
 [New Thread 3850398.3887551]
 [New Thread 3850398.3887602]
 [New Thread 3850398.3887653]
 ...

 Thread 1 "attach-many-sho" received signal SIGINT, Interrupt.
 0x00007ffff7e6a23f in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7fffffffda80, rem=rem@entry=0x7fffffffda80)
     at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
 78      in ../sysdeps/unix/sysv/linux/clock_nanosleep.c
 (gdb)

Above, we only see "New Thread" notifications, even though threads
were deleted.

After this patch, we'll see:

 (gdb) c
 Continuing.
 ^C[Thread 3558643.3577053 exited]
 [Thread 3558643.3577104 exited]
 [Thread 3558643.3577155 exited]
 [Thread 3558643.3579603 exited]
 ...
 [New Thread 3558643.3597415]
 [New Thread 3558643.3600015]
 [New Thread 3558643.3599965]
 ...

 Thread 1 "attach-many-sho" received signal SIGINT, Interrupt.
 0x00007ffff7e6a23f in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7fffffffda80, rem=rem@entry=0x7fffffffda80)
     at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
 78      in ../sysdeps/unix/sysv/linux/clock_nanosleep.c
 (gdb) q

This commit fixes this by moving the thread exit printing to common
code instead, triggered from within delete_thread (or rather,
set_thread_exited).

There's one wrinkle, though.  While most targest want to print:

 [Thread ... exited]

the Windows target wants to print:

 [Thread ... exited with code <exit_code>]

... and sometimes wants to suppress the notification for the main
thread.  To address that, this commits adds a delete_thread_with_code
function, only used by that target (so far).

The fact that remote is missing thread exited messages is PR
remote/30129, and kfailed in gdb.threads/thread-bp-deleted.exp and
gdb.mi/mi-thread-bp-deleted.exp.  This commit removes the now
unnecessary kfails (and kpasses) from those testcases, switching to
simpler gdb_assert instead.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30129
Change-Id: I06ec07b7c51527872a9713dd11cf7867b50fc5ff

16 files changed:
gdb/annotate.c
gdb/breakpoint.c
gdb/fbsd-nat.c
gdb/gdbthread.h
gdb/inferior.c
gdb/inferior.h
gdb/linux-nat.c
gdb/mi/mi-interp.c
gdb/netbsd-nat.c
gdb/observable.h
gdb/procfs.c
gdb/python/py-inferior.c
gdb/testsuite/gdb.mi/mi-thread-bp-deleted.exp
gdb/testsuite/gdb.threads/thread-bp-deleted.exp
gdb/thread.c
gdb/windows-nat.c

index 60fe6ccd5c27d6fc0c1515341a6b28c5ae2cc667..a24841f7c0d74758f840707bc9f1c3ec0ad5361e 100644 (file)
@@ -233,7 +233,9 @@ annotate_thread_changed (void)
 /* Emit notification on thread exit.  */
 
 static void
-annotate_thread_exited (struct thread_info *t, int silent)
+annotate_thread_exited (thread_info *t,
+                       gdb::optional<ULONGEST> exit_code,
+                       bool /* silent */)
 {
   if (annotation_level > 1)
     {
index a42d26fd25ab71f551070f2f0716d9d3fee95bb3..25c98919d94da095350787fde4015573ad8a2591 100644 (file)
@@ -3243,7 +3243,9 @@ remove_breakpoints (void)
    that thread.  */
 
 static void
-remove_threaded_breakpoints (struct thread_info *tp, int silent)
+remove_threaded_breakpoints (thread_info *tp,
+                            gdb::optional<ULONGEST> exit_code,
+                            bool /* silent */)
 {
   for (breakpoint *b : all_breakpoints_safe ())
     {
index 27d2fe45092904658dc1a0d306f93440b18d3e07..612ebcc924faf10af808055a2b6fd0fd39e51f93 100644 (file)
@@ -1300,9 +1300,6 @@ fbsd_nat_target::wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus,
                {
                  fbsd_lwp_debug_printf ("deleting thread for LWP %u",
                                         pl.pl_lwpid);
-                 if (print_thread_events)
-                   gdb_printf (_("[%s exited]\n"),
-                               target_pid_to_str (wptid).c_str ());
                  low_delete_thread (thr);
                  delete_thread (thr);
                }
index 79dedb23d4d140fade943142904a7b6e094aa380..905ed8bddd5004e96f3e7ce0175a85585b0e6a41 100644 (file)
@@ -636,16 +636,30 @@ extern struct thread_info *add_thread_with_info (process_stratum_target *targ,
 
 /* Delete thread THREAD and notify of thread exit.  If the thread is
    currently not deletable, don't actually delete it but still tag it
-   as exited and do the notification.  */
-extern void delete_thread (struct thread_info *thread);
+   as exited and do the notification.  EXIT_CODE is the thread's exit
+   code.  If SILENT, don't actually notify the CLI.  THREAD must not
+   be NULL or an assertion will fail.  */
+extern void delete_thread_with_exit_code (thread_info *thread,
+                                         ULONGEST exit_code,
+                                         bool silent = false);
+
+/* Delete thread THREAD and notify of thread exit.  If the thread is
+   currently not deletable, don't actually delete it but still tag it
+   as exited and do the notification.  THREAD must not be NULL or an
+   assertion will fail.  */
+extern void delete_thread (thread_info *thread);
 
 /* Like delete_thread, but be quiet about it.  Used when the process
    this thread belonged to has already exited, for example.  */
 extern void delete_thread_silent (struct thread_info *thread);
 
 /* Mark the thread exited, but don't delete it or remove it from the
-   inferior thread list.  */
-extern void set_thread_exited (thread_info *tp, bool silent);
+   inferior thread list.  EXIT_CODE is the thread's exit code, if
+   available.  If SILENT, then don't inform the CLI about the
+   exit.  */
+extern void set_thread_exited (thread_info *tp,
+                              gdb::optional<ULONGEST> exit_code = {},
+                              bool silent = false);
 
 /* Delete a step_resume_breakpoint from the thread database.  */
 extern void delete_step_resume_breakpoint (struct thread_info *);
index 80d53bb4e818fc1f4b332485712168f5207826d9..ee710964249303a50df8564d7a4172de92857e09 100644 (file)
@@ -223,7 +223,7 @@ inferior::clear_thread_list ()
     {
       threads_debug_printf ("deleting thread %s",
                            thr->ptid.to_string ().c_str ());
-      set_thread_exited (thr, true);
+      set_thread_exited (thr, {}, true);
       if (thr->deletable ())
        delete thr;
     });
index d64e7cc015c01e6c052a486e631c56f1d27e1dd7..4c2d0505a912f816f56e6f60d8b789d5b37b41a4 100644 (file)
@@ -697,6 +697,8 @@ extern void detach_inferior (inferior *inf);
 
 extern void exit_inferior (inferior *inf);
 
+/* Like exit_inferior, but be quiet -- don't announce the exit of the
+   inferior's threads to the CLI.  */
 extern void exit_inferior_silent (inferior *inf);
 
 extern void exit_inferior_num_silent (int num);
index 043c9a0489c5d5972d38262b51ac21851f0cdcda..dcd5d07728d0b995a8d6f7864e22e66f34a4a151 100644 (file)
@@ -916,15 +916,10 @@ linux_nat_switch_fork (ptid_t new_ptid)
 static void
 exit_lwp (struct lwp_info *lp, bool del_thread = true)
 {
-  struct thread_info *th = find_thread_ptid (linux_target, lp->ptid);
-
-  if (th)
+  if (del_thread)
     {
-      if (print_thread_events)
-       gdb_printf (_("[%s exited]\n"),
-                   target_pid_to_str (lp->ptid).c_str ());
-
-      if (del_thread)
+      thread_info *th = find_thread_ptid (linux_target, lp->ptid);
+      if (th != nullptr)
        delete_thread (th);
     }
 
index e1244f3df43871832430c34592c7af4793b3bcaf..dd1dc3126eb0bdb91eae5f44e411d71fd0cf76cc 100644 (file)
@@ -68,7 +68,9 @@ static void mi_on_normal_stop (struct bpstat *bs, int print_frame);
 static void mi_on_no_history (void);
 
 static void mi_new_thread (struct thread_info *t);
-static void mi_thread_exit (struct thread_info *t, int silent);
+static void mi_thread_exit (thread_info *t,
+                           gdb::optional<ULONGEST> exit_code,
+                           bool silent);
 static void mi_record_changed (struct inferior*, int, const char *,
                               const char *);
 static void mi_inferior_added (struct inferior *inf);
@@ -345,8 +347,10 @@ mi_new_thread (struct thread_info *t)
     }
 }
 
+/* Observer for the thread_exit notification.  */
+
 static void
-mi_thread_exit (struct thread_info *t, int silent)
+mi_thread_exit (thread_info *t, gdb::optional<ULONGEST> exit_code, bool silent)
 {
   SWITCH_THRU_ALL_UIS ()
     {
index 37f2fa49b0bad14e69333d87b8e6e78a793a0917..133a3cf788f35de8055aeaa359ecbef0cbe2ca1d 100644 (file)
@@ -625,10 +625,6 @@ nbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
        {
          /* NetBSD does not store an LWP exit status.  */
          ourstatus->set_thread_exited (0);
-
-         if (print_thread_events)
-           gdb_printf (_("[%s exited]\n"),
-                       target_pid_to_str (wptid).c_str ());
        }
 
       /* The GDB core expects that the rest of the threads are running.  */
index efd0446e1689e7a18b39f031f2a9391ef89296b5..d6be15348d557e53dcb7efc4a340f9d9cc521132 100644 (file)
@@ -126,10 +126,13 @@ extern observable<struct objfile */* objfile */> free_objfile;
 /* The thread specified by T has been created.  */
 extern observable<struct thread_info */* t */> new_thread;
 
-/* The thread specified by T has exited.  The SILENT argument
-   indicates that gdb is removing the thread from its tables without
-   wanting to notify the user about it.  */
-extern observable<struct thread_info */* t */, int /* silent */> thread_exit;
+/* The thread specified by T has exited.  EXIT_CODE is the thread's
+   exit code, if available.  The SILENT argument indicates that GDB is
+   removing the thread from its tables without wanting to notify the
+   CLI about it.  */
+extern observable<thread_info */* t */,
+                 gdb::optional<ULONGEST> /* exit_code */,
+                 bool /* silent */> thread_exit;
 
 /* An explicit stop request was issued to PTID.  If PTID equals
    minus_one_ptid, the request applied to all threads.  If
index 741e62a24026e5fb75e40931ba1760229f116ecf..a8a50c4ddc578b8822816e741d10ec5d404ac783 100644 (file)
@@ -2115,9 +2115,6 @@ wait_again:
              case PR_SYSENTRY:
                if (what == SYS_lwp_exit)
                  {
-                   if (print_thread_events)
-                     gdb_printf (_("[%s exited]\n"),
-                                 target_pid_to_str (retval).c_str ());
                    delete_thread (find_thread_ptid (this, retval));
                    target_continue_no_signal (ptid);
                    goto wait_again;
@@ -2222,9 +2219,6 @@ wait_again:
                  }
                else if (what == SYS_lwp_exit)
                  {
-                   if (print_thread_events)
-                     gdb_printf (_("[%s exited]\n"),
-                                 target_pid_to_str (retval).c_str ());
                    delete_thread (find_thread_ptid (this, retval));
                    status->set_spurious ();
                    return retval;
index 8b21f28afbe7f8f93736a922482092b9eb3d2914..5bb9d6a9fdc4da6b94f1a9cbafdb421607f5b8d1 100644 (file)
@@ -360,7 +360,9 @@ add_thread_object (struct thread_info *tp)
 }
 
 static void
-delete_thread_object (struct thread_info *tp, int ignore)
+delete_thread_object (thread_info *tp,
+                     gdb::optional<ULONGEST> /* exit_code */,
+                     bool /* silent */)
 {
   if (!gdb_python_initialized)
     return;
index 0ebca9248015be19ee42ea0cd9a7da5ba27e5641..6830991cc81ee2d6384fc228587cd02b9100c51b 100644 (file)
@@ -233,19 +233,11 @@ foreach_mi_ui_mode mode {
                exp_continue
            }
 
-           # The output has arrived!  Check how we did.  There are other bugs
-           # that come into play here which change what output we'll see.
-           if { $saw_mi_thread_exited && $saw_mi_bp_deleted \
-                    && $saw_cli_thread_exited \
-                    && $saw_cli_bp_deleted } {
-               kpass "gdb/30129" $gdb_test_name
-           } elseif { $saw_mi_thread_exited && $saw_mi_bp_deleted \
-                          && !$saw_cli_thread_exited \
-                          && $saw_cli_bp_deleted } {
-               kfail "gdb/30129" $gdb_test_name
-           } else {
-               fail "$gdb_test_name"
-           }
+           # The output has arrived!  Check how we did.
+           gdb_assert { $saw_mi_thread_exited && $saw_mi_bp_deleted \
+                            && $saw_cli_thread_exited \
+                            && $saw_cli_bp_deleted } \
+               $gdb_test_name
        }
     }
 
index 019bdddee818b1562ca82d46ff2b1469b56cb4eb..92178ff838b188783789f9701b6322ce212a927d 100644 (file)
@@ -155,17 +155,7 @@ if {$is_remote} {
                exp_continue
            }
 
-           # When PR gdb/30129 is fixed then this can all be collapsed down
-           # into a single gdb_assert call.  This is split out like this
-           # because the SAW_BP_DELETED part is working, and we want to
-           # spot if that stops working.
-           if { $saw_thread_exited && $saw_bp_deleted } {
-               kpass "gdb/30129" $gdb_test_name
-           } elseif {!$saw_thread_exited && $saw_bp_deleted} {
-               kfail "gdb/30129" $gdb_test_name
-           } else {
-               fail $gdb_test_name
-           }
+           gdb_assert { $saw_thread_exited && $saw_bp_deleted } $gdb_test_name
        }
     }
 } else {
index 0c5039530b0164753d65ed637ed9c74b2eeb7ee9..d6b3f074e789bbd5a3b0b2b835e3de0bb68160b0 100644 (file)
@@ -193,7 +193,8 @@ clear_thread_inferior_resources (struct thread_info *tp)
 /* See gdbthread.h.  */
 
 void
-set_thread_exited (thread_info *tp, bool silent)
+set_thread_exited (thread_info *tp, gdb::optional<ULONGEST> exit_code,
+                  bool silent)
 {
   /* Dead threads don't need to step-over.  Remove from chain.  */
   if (thread_is_in_step_over_chain (tp))
@@ -212,7 +213,22 @@ set_thread_exited (thread_info *tp, bool silent)
       if (proc_target != nullptr)
        proc_target->maybe_remove_resumed_with_pending_wait_status (tp);
 
-      gdb::observers::thread_exit.notify (tp, silent);
+      if (!silent && print_thread_events)
+       {
+         if (exit_code.has_value ())
+           {
+             gdb_printf (_("[%s exited with code %s]\n"),
+                         target_pid_to_str (tp->ptid).c_str (),
+                         pulongest (*exit_code));
+           }
+         else
+           {
+             gdb_printf (_("[%s exited]\n"),
+                         target_pid_to_str (tp->ptid).c_str ());
+           }
+       }
+
+      gdb::observers::thread_exit.notify (tp, exit_code, silent);
 
       /* Tag it as exited.  */
       tp->state = THREAD_EXITED;
@@ -469,20 +485,22 @@ global_thread_step_over_chain_remove (struct thread_info *tp)
   global_thread_step_over_list.erase (it);
 }
 
-/* Delete the thread referenced by THR.  If SILENT, don't notify
-   the observer of this exit.
-   
-   THR must not be NULL or a failed assertion will be raised.  */
+/* Helper for the different delete_thread variants.  */
 
 static void
-delete_thread_1 (thread_info *thr, bool silent)
+delete_thread_1 (thread_info *thr, gdb::optional<ULONGEST> exit_code,
+                bool silent)
 {
   gdb_assert (thr != nullptr);
 
-  threads_debug_printf ("deleting thread %s, silent = %d",
-                       thr->ptid.to_string ().c_str (), silent);
+  threads_debug_printf ("deleting thread %s, exit_code = %s, silent = %d",
+                       thr->ptid.to_string ().c_str (),
+                       (exit_code.has_value ()
+                        ? pulongest (*exit_code)
+                        : "<none>"),
+                       silent);
 
-  set_thread_exited (thr, silent);
+  set_thread_exited (thr, exit_code, silent);
 
   if (!thr->deletable ())
     {
@@ -498,16 +516,25 @@ delete_thread_1 (thread_info *thr, bool silent)
 
 /* See gdbthread.h.  */
 
+void
+delete_thread_with_exit_code (thread_info *thread, ULONGEST exit_code,
+                             bool silent)
+{
+  delete_thread_1 (thread, exit_code, false /* not silent */);
+}
+
+/* See gdbthread.h.  */
+
 void
 delete_thread (thread_info *thread)
 {
-  delete_thread_1 (thread, false /* not silent */);
+  delete_thread_1 (thread, {}, false /* not silent */);
 }
 
 void
 delete_thread_silent (thread_info *thread)
 {
-  delete_thread_1 (thread, true /* silent */);
+  delete_thread_1 (thread, {}, true /* not silent */);
 }
 
 struct thread_info *
index 26ad04b27be3415e9c7878cf2663a50eb883f509..03123e777eb1342a5bfb4d7765418d5a57b9b265 100644 (file)
@@ -612,21 +612,13 @@ windows_nat_target::delete_thread (ptid_t ptid, DWORD exit_code,
 
   id = ptid.lwp ();
 
-  /* Emit a notification about the thread being deleted.
-
-     Note that no notification was printed when the main thread
+  /* Note that no notification was printed when the main thread
      was created, and thus, unless in verbose mode, we should be
      symmetrical, and avoid that notification for the main thread
      here as well.  */
-
-  if (info_verbose)
-    gdb_printf ("[Deleting %s]\n", target_pid_to_str (ptid).c_str ());
-  else if (print_thread_events && !main_thread_p)
-    gdb_printf (_("[%s exited with code %u]\n"),
-               target_pid_to_str (ptid).c_str (),
-               (unsigned) exit_code);
-
-  ::delete_thread (find_thread_ptid (this, ptid));
+  bool silent = (main_thread_p && !info_verbose);
+  thread_info *todel = find_thread_ptid (this, ptid);
+  delete_thread_with_exit_code (todel, exit_code, silent);
 
   auto iter = std::find_if (windows_process.thread_list.begin (),
                            windows_process.thread_list.end (),