mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2026-08-27 00:26:02 -04:00
update thread list, delete exited threads
On GNU/Linux, if the running kernel supports clone events, then
linux-thread-db.c defers thread listing to the target beneath:
static void
thread_db_update_thread_list (struct target_ops *ops)
{
...
if (target_has_execution && !thread_db_use_events ())
ops->beneath->to_update_thread_list (ops->beneath);
else
thread_db_update_thread_list_td_ta_thr_iter (ops);
...
}
However, when live debugging, the target beneath, linux-nat.c, does
not implement the to_update_thread_list method. The result is that if
a thread is marked exited (because it can't be deleted right now,
e.g., it was the selected thread), then it won't ever be deleted,
until the process exits or is killed/detached.
A similar thing happens with the remote.c target. Because its
target_update_thread_list implementation skips exited threads when it
walks the current thread list looking for threads that no longer exits
on the target side, using ALL_NON_EXITED_THREADS_SAFE, stale exited
threads are never deleted.
This is not a big deal -- I can't think of any way this might be user
visible, other than gdb's memory growing a tiny bit whenever a thread
gets stuck in exited state. Still, might as well clean things up
properly.
All other targets use prune_threads, so are unaffected.
The fix adds a ALL_THREADS_SAFE macro, that like
ALL_NON_EXITED_THREADS_SAFE, walks the thread list and allows deleting
the iterated thread, and uses that in places that are walking the
thread list in order to delete threads. Actually, after converting
linux-nat.c and remote.c to use this, we find the only other user of
ALL_NON_EXITED_THREADS_SAFE is also walking the list to delete
threads. So we convert that too, and end up deleting
ALL_NON_EXITED_THREADS_SAFE.
Tested on x86_64 Fedora 20, native and gdbserver.
gdb/ChangeLog
2015-04-07 Pedro Alves <palves@redhat.com>
* gdbthread.h (ALL_NON_EXITED_THREADS_SAFE): Rename to ...
(ALL_THREADS_SAFE): ... this, and don't skip exited threads.
(delete_exited_threads): New declaration.
* infrun.c (follow_exec): Use ALL_THREADS_SAFE.
* linux-nat.c (linux_nat_update_thread_list): New function.
(linux_nat_add_target): Install it.
* remote.c (remote_update_thread_list): Use ALL_THREADS_SAFE.
* thread.c (prune_threads): Use ALL_THREADS_SAFE.
(delete_exited_threads): New function.
This commit is contained in:
parent
87070c082f
commit
8a06aea71e
6 changed files with 57 additions and 10 deletions
|
|
@ -1,3 +1,15 @@
|
|||
2015-04-07 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* gdbthread.h (ALL_NON_EXITED_THREADS_SAFE): Rename to ...
|
||||
(ALL_THREADS_SAFE): ... this, and don't skip exited threads.
|
||||
(delete_exited_threads): New declaration.
|
||||
* infrun.c (follow_exec): Use ALL_THREADS_SAFE.
|
||||
* linux-nat.c (linux_nat_update_thread_list): New function.
|
||||
(linux_nat_add_target): Install it.
|
||||
* remote.c (remote_update_thread_list): Use ALL_THREADS_SAFE.
|
||||
* thread.c (prune_threads): Use ALL_THREADS_SAFE.
|
||||
(delete_exited_threads): New function.
|
||||
|
||||
2015-04-07 Pedro Alves <pedro@codesourcery.com>
|
||||
|
||||
* infrun.c (resume) <displaced stepping debug output>: Get the
|
||||
|
|
|
|||
|
|
@ -380,13 +380,12 @@ extern struct thread_info *iterate_over_threads (thread_callback_func, void *);
|
|||
for (T = thread_list; T; T = T->next) \
|
||||
if ((T)->state != THREAD_EXITED)
|
||||
|
||||
/* Like ALL_NON_EXITED_THREADS, but allows deleting the currently
|
||||
iterated thread. */
|
||||
#define ALL_NON_EXITED_THREADS_SAFE(T, TMP) \
|
||||
/* Traverse all threads, including those that have THREAD_EXITED
|
||||
state. Allows deleting the currently iterated thread. */
|
||||
#define ALL_THREADS_SAFE(T, TMP) \
|
||||
for ((T) = thread_list; \
|
||||
(T) != NULL ? ((TMP) = (T)->next, 1): 0; \
|
||||
(T) = (TMP)) \
|
||||
if ((T)->state != THREAD_EXITED)
|
||||
(T) = (TMP))
|
||||
|
||||
extern int thread_count (void);
|
||||
|
||||
|
|
@ -484,6 +483,11 @@ extern void update_thread_list (void);
|
|||
|
||||
extern void prune_threads (void);
|
||||
|
||||
/* Delete threads marked THREAD_EXITED. Unlike prune_threads, this
|
||||
does not consult the target about whether the thread is alive right
|
||||
now. */
|
||||
extern void delete_exited_threads (void);
|
||||
|
||||
/* Return true if PC is in the stepping range of THREAD. */
|
||||
|
||||
int pc_in_thread_step_range (CORE_ADDR pc, struct thread_info *thread);
|
||||
|
|
|
|||
|
|
@ -1099,7 +1099,7 @@ follow_exec (ptid_t ptid, char *execd_pathname)
|
|||
them. Deleting them now rather than at the next user-visible
|
||||
stop provides a nicer sequence of events for user and MI
|
||||
notifications. */
|
||||
ALL_NON_EXITED_THREADS_SAFE (th, tmp)
|
||||
ALL_THREADS_SAFE (th, tmp)
|
||||
if (ptid_get_pid (th->ptid) == pid && !ptid_equal (th->ptid, ptid))
|
||||
delete_thread (th->ptid);
|
||||
|
||||
|
|
|
|||
|
|
@ -4030,6 +4030,23 @@ linux_nat_thread_alive (struct target_ops *ops, ptid_t ptid)
|
|||
return linux_thread_alive (ptid);
|
||||
}
|
||||
|
||||
/* Implement the to_update_thread_list target method for this
|
||||
target. */
|
||||
|
||||
static void
|
||||
linux_nat_update_thread_list (struct target_ops *ops)
|
||||
{
|
||||
if (linux_supports_traceclone ())
|
||||
{
|
||||
/* With support for clone events, we add/delete threads from the
|
||||
list as clone/exit events are processed, so just try deleting
|
||||
exited threads still in the thread list. */
|
||||
delete_exited_threads ();
|
||||
}
|
||||
else
|
||||
prune_threads ();
|
||||
}
|
||||
|
||||
static char *
|
||||
linux_nat_pid_to_str (struct target_ops *ops, ptid_t ptid)
|
||||
{
|
||||
|
|
@ -4854,6 +4871,7 @@ linux_nat_add_target (struct target_ops *t)
|
|||
t->to_kill = linux_nat_kill;
|
||||
t->to_mourn_inferior = linux_nat_mourn_inferior;
|
||||
t->to_thread_alive = linux_nat_thread_alive;
|
||||
t->to_update_thread_list = linux_nat_update_thread_list;
|
||||
t->to_pid_to_str = linux_nat_pid_to_str;
|
||||
t->to_thread_name = linux_nat_thread_name;
|
||||
t->to_has_thread_control = tc_schedlock;
|
||||
|
|
|
|||
|
|
@ -2835,7 +2835,7 @@ remote_update_thread_list (struct target_ops *ops)
|
|||
/* CONTEXT now holds the current thread list on the remote
|
||||
target end. Delete GDB-side threads no longer found on the
|
||||
target. */
|
||||
ALL_NON_EXITED_THREADS_SAFE (tp, tmp)
|
||||
ALL_THREADS_SAFE (tp, tmp)
|
||||
{
|
||||
for (i = 0;
|
||||
VEC_iterate (thread_item_t, context.items, i, item);
|
||||
|
|
|
|||
19
gdb/thread.c
19
gdb/thread.c
|
|
@ -625,16 +625,29 @@ thread_alive (struct thread_info *tp)
|
|||
void
|
||||
prune_threads (void)
|
||||
{
|
||||
struct thread_info *tp, *next;
|
||||
struct thread_info *tp, *tmp;
|
||||
|
||||
for (tp = thread_list; tp; tp = next)
|
||||
ALL_THREADS_SAFE (tp, tmp)
|
||||
{
|
||||
next = tp->next;
|
||||
if (!thread_alive (tp))
|
||||
delete_thread (tp->ptid);
|
||||
}
|
||||
}
|
||||
|
||||
/* See gdbthreads.h. */
|
||||
|
||||
void
|
||||
delete_exited_threads (void)
|
||||
{
|
||||
struct thread_info *tp, *tmp;
|
||||
|
||||
ALL_THREADS_SAFE (tp, tmp)
|
||||
{
|
||||
if (tp->state == THREAD_EXITED)
|
||||
delete_thread (tp->ptid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable storing stack temporaries for the thread whose id is
|
||||
stored in DATA. */
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue