Centralize "[Thread ...exited]" notifications

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).

Change-Id: I06ec07b7c51527872a9713dd11cf7867b50fc5ff
This commit is contained in:
Pedro Alves
2022-06-21 19:30:48 +01:00
parent 657a94e242
commit daf510bf56
14 changed files with 89 additions and 59 deletions

View File

@@ -233,7 +233,9 @@ annotate_thread_changed (void)
/* Emit notification on thread exit. */ /* Emit notification on thread exit. */
static void 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) if (annotation_level > 1)
{ {

View File

@@ -3237,7 +3237,9 @@ remove_breakpoints (void)
that thread. */ that thread. */
static void 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 ()) for (breakpoint *b : all_breakpoints_safe ())
{ {

View 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", fbsd_lwp_debug_printf ("deleting thread for LWP %u",
pl.pl_lwpid); pl.pl_lwpid);
if (print_thread_events)
gdb_printf (_("[%s exited]\n"),
target_pid_to_str (wptid).c_str ());
low_delete_thread (thr); low_delete_thread (thr);
delete_thread (thr); delete_thread (thr);
} }

View 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 /* Delete thread THREAD and notify of thread exit. If the thread is
currently not deletable, don't actually delete it but still tag it currently not deletable, don't actually delete it but still tag it
as exited and do the notification. */ as exited and do the notification. EXIT_CODE is the thread's exit
extern void delete_thread (struct thread_info *thread); 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 /* Like delete_thread, but be quiet about it. Used when the process
this thread belonged to has already exited, for example. */ this thread belonged to has already exited, for example. */
extern void delete_thread_silent (struct thread_info *thread); extern void delete_thread_silent (struct thread_info *thread);
/* Mark the thread exited, but don't delete it or remove it from the /* Mark the thread exited, but don't delete it or remove it from the
inferior thread list. */ inferior thread list. EXIT_CODE is the thread's exit code, if
extern void set_thread_exited (thread_info *tp, bool silent); 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. */ /* Delete a step_resume_breakpoint from the thread database. */
extern void delete_step_resume_breakpoint (struct thread_info *); extern void delete_step_resume_breakpoint (struct thread_info *);

View File

@@ -174,7 +174,7 @@ inferior::clear_thread_list ()
{ {
threads_debug_printf ("deleting thread %s", threads_debug_printf ("deleting thread %s",
thr->ptid.to_string ().c_str ()); thr->ptid.to_string ().c_str ());
set_thread_exited (thr, true); set_thread_exited (thr, {}, true);
if (thr->deletable ()) if (thr->deletable ())
delete thr; delete thr;
}); });

View File

@@ -628,6 +628,8 @@ extern void detach_inferior (inferior *inf);
extern void exit_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_silent (inferior *inf);
extern void exit_inferior_num_silent (int num); extern void exit_inferior_num_silent (int num);

View File

@@ -916,15 +916,10 @@ linux_nat_switch_fork (ptid_t new_ptid)
static void static void
exit_lwp (struct lwp_info *lp, bool del_thread = true) exit_lwp (struct lwp_info *lp, bool del_thread = true)
{ {
struct thread_info *th = find_thread_ptid (linux_target, lp->ptid); if (del_thread)
if (th)
{ {
if (print_thread_events) thread_info *th = find_thread_ptid (linux_target, lp->ptid);
gdb_printf (_("[%s exited]\n"), if (th != nullptr)
target_pid_to_str (lp->ptid).c_str ());
if (del_thread)
delete_thread (th); delete_thread (th);
} }

View 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_on_no_history (void);
static void mi_new_thread (struct thread_info *t); 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 *, static void mi_record_changed (struct inferior*, int, const char *,
const char *); const char *);
static void mi_inferior_added (struct inferior *inf); static void mi_inferior_added (struct inferior *inf);
@@ -351,8 +353,10 @@ mi_new_thread (struct thread_info *t)
} }
} }
/* Observer for the thread_exit notification. */
static void 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 () SWITCH_THRU_ALL_UIS ()
{ {

View File

@@ -625,10 +625,6 @@ nbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
{ {
/* NetBSD does not store an LWP exit status. */ /* NetBSD does not store an LWP exit status. */
ourstatus->set_thread_exited (0); 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. */ /* The GDB core expects that the rest of the threads are running. */

View File

@@ -126,10 +126,13 @@ extern observable<struct objfile */* objfile */> free_objfile;
/* The thread specified by T has been created. */ /* The thread specified by T has been created. */
extern observable<struct thread_info */* t */> new_thread; extern observable<struct thread_info */* t */> new_thread;
/* The thread specified by T has exited. The SILENT argument /* The thread specified by T has exited. EXIT_CODE is the thread's
indicates that gdb is removing the thread from its tables without exit code, if available. The SILENT argument indicates that GDB is
wanting to notify the user about it. */ removing the thread from its tables without wanting to notify the
extern observable<struct thread_info */* t */, int /* silent */> thread_exit; 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 /* An explicit stop request was issued to PTID. If PTID equals
minus_one_ptid, the request applied to all threads. If minus_one_ptid, the request applied to all threads. If

View File

@@ -2115,9 +2115,6 @@ wait_again:
case PR_SYSENTRY: case PR_SYSENTRY:
if (what == SYS_lwp_exit) 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)); delete_thread (find_thread_ptid (this, retval));
target_continue_no_signal (ptid); target_continue_no_signal (ptid);
goto wait_again; goto wait_again;
@@ -2222,9 +2219,6 @@ wait_again:
} }
else if (what == SYS_lwp_exit) 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)); delete_thread (find_thread_ptid (this, retval));
status->set_spurious (); status->set_spurious ();
return retval; return retval;

View File

@@ -360,7 +360,9 @@ add_thread_object (struct thread_info *tp)
} }
static void 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) if (!gdb_python_initialized)
return; return;

View File

@@ -192,7 +192,8 @@ clear_thread_inferior_resources (struct thread_info *tp)
/* See gdbthread.h. */ /* See gdbthread.h. */
void 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. */ /* Dead threads don't need to step-over. Remove from chain. */
if (thread_is_in_step_over_chain (tp)) if (thread_is_in_step_over_chain (tp))
@@ -211,7 +212,22 @@ set_thread_exited (thread_info *tp, bool silent)
if (proc_target != nullptr) if (proc_target != nullptr)
proc_target->maybe_remove_resumed_with_pending_wait_status (tp); 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. */ /* Tag it as exited. */
tp->state = THREAD_EXITED; tp->state = THREAD_EXITED;
@@ -468,20 +484,22 @@ global_thread_step_over_chain_remove (struct thread_info *tp)
global_thread_step_over_list.erase (it); global_thread_step_over_list.erase (it);
} }
/* Delete the thread referenced by THR. If SILENT, don't notify /* Helper for the different delete_thread variants. */
the observer of this exit.
THR must not be NULL or a failed assertion will be raised. */
static void 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); gdb_assert (thr != nullptr);
threads_debug_printf ("deleting thread %s, silent = %d", threads_debug_printf ("deleting thread %s, exit_code = %s, silent = %d",
thr->ptid.to_string ().c_str (), silent); 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 ()) if (!thr->deletable ())
{ {
@@ -497,16 +515,25 @@ delete_thread_1 (thread_info *thr, bool silent)
/* See gdbthread.h. */ /* 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 void
delete_thread (thread_info *thread) delete_thread (thread_info *thread)
{ {
delete_thread_1 (thread, false /* not silent */); delete_thread_1 (thread, {}, false /* not silent */);
} }
void void
delete_thread_silent (thread_info *thread) delete_thread_silent (thread_info *thread)
{ {
delete_thread_1 (thread, true /* silent */); delete_thread_1 (thread, {}, true /* not silent */);
} }
struct thread_info * struct thread_info *

View File

@@ -611,21 +611,13 @@ windows_nat_target::delete_thread (ptid_t ptid, DWORD exit_code,
id = ptid.lwp (); 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 was created, and thus, unless in verbose mode, we should be
symmetrical, and avoid that notification for the main thread symmetrical, and avoid that notification for the main thread
here as well. */ here as well. */
bool silent = (main_thread_p && !info_verbose);
if (info_verbose) thread_info *todel = find_thread_ptid (this, ptid);
gdb_printf ("[Deleting %s]\n", target_pid_to_str (ptid).c_str ()); delete_thread_with_exit_code (todel, exit_code, silent);
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));
auto iter = std::find_if (windows_process.thread_list.begin (), auto iter = std::find_if (windows_process.thread_list.begin (),
windows_process.thread_list.end (), windows_process.thread_list.end (),