Remove support for thread events without PTRACE_EVENT_CLONE in GDB
Before, on systems that did not support PTRACE_EVENT_CLONE, both GDB and GDBServer coordinated with libthread_db.so to insert breakpoints at magic locations in libpthread.so, in order to break at thread creation and thread death. Support for thread events was removed from GDBServer as patch: https://sourceware.org/ml/gdb-patches/2015-11/msg00466.html This patch removes support for thread events in GDB. No regressions found on Ubuntu 14.04 x86_64. gdb/ChangeLog: * breakpoint.c (remove_thread_event_breakpoints): Remove. * breakpoint.h (remove_thread_event_breakpoints): Remove declaration. * linux-nat.c (in_pid_list_p): Remove. (lin_lwp_attach_lwp): Remove. * linux-nat.h (lin_lwp_attach_lwp): Remove declaration. * linux-thread-db.c (thread_db_use_events): Remove. (struct thread_db_info) <td_create_bp_addr>: Remove. <td_death_bp_addr>: Likewise. <td_ta_event_addr_p>: Likewise. <td_ta_set_event_p>: Likewise. <td_ta_clear_event_p>: Likewise. <td_ta_event_getmsg_p>: Likewise. <td_thr_event_enable_p>: Likewise. (attach_thread): Likewise. (detach_thread): Likewise. (have_threads_callback): Likewise. (have_threads): Likewise. (enable_thread_event): Likewise. (enable_thread_event_reporting): Likewise. (try_thread_db_load_1): Remove td_ta_event_addr, td_ta_set_event, td_ta_clear_event, td_ta_event_getmsg, td_thr_event_enable initializations. (try_thread_db_load_1): Remove enable_thread_event_reporting call. (disable_thread_event_reporting): Remove. (record_thread): Adapt to thread_db_use_event removal. (detach_thread): Remove. (thread_db_detach): Adapt to thread_db_use_event removal. (check_event): Remove. (thread_db_wait): Adapt to thread events support removal. (thread_db_mourn_inferior): Likewise. (find_new_threads_callback): Likewise. (find_new_threads_once): Likewise. (thread_db_update_thread_list): Likewise.
This commit is contained in:
parent
47f8114261
commit
c2c2a31fdb
6 changed files with 46 additions and 587 deletions
|
@ -1,3 +1,40 @@
|
||||||
|
2015-12-10 Antoine Tremblay <antoine.tremblay@ericsson.com>
|
||||||
|
|
||||||
|
* breakpoint.c (remove_thread_event_breakpoints): Remove.
|
||||||
|
* breakpoint.h (remove_thread_event_breakpoints): Remove
|
||||||
|
declaration.
|
||||||
|
* linux-nat.c (in_pid_list_p): Remove.
|
||||||
|
(lin_lwp_attach_lwp): Remove.
|
||||||
|
* linux-nat.h (lin_lwp_attach_lwp): Remove declaration.
|
||||||
|
* linux-thread-db.c (thread_db_use_events): Remove.
|
||||||
|
(struct thread_db_info) <td_create_bp_addr>: Remove.
|
||||||
|
<td_death_bp_addr>: Likewise.
|
||||||
|
<td_ta_event_addr_p>: Likewise.
|
||||||
|
<td_ta_set_event_p>: Likewise.
|
||||||
|
<td_ta_clear_event_p>: Likewise.
|
||||||
|
<td_ta_event_getmsg_p>: Likewise.
|
||||||
|
<td_thr_event_enable_p>: Likewise.
|
||||||
|
(attach_thread): Likewise.
|
||||||
|
(detach_thread): Likewise.
|
||||||
|
(have_threads_callback): Likewise.
|
||||||
|
(have_threads): Likewise.
|
||||||
|
(enable_thread_event): Likewise.
|
||||||
|
(enable_thread_event_reporting): Likewise.
|
||||||
|
(try_thread_db_load_1): Remove td_ta_event_addr, td_ta_set_event,
|
||||||
|
td_ta_clear_event, td_ta_event_getmsg, td_thr_event_enable
|
||||||
|
initializations.
|
||||||
|
(try_thread_db_load_1): Remove enable_thread_event_reporting call.
|
||||||
|
(disable_thread_event_reporting): Remove.
|
||||||
|
(record_thread): Adapt to thread_db_use_event removal.
|
||||||
|
(detach_thread): Remove.
|
||||||
|
(thread_db_detach): Adapt to thread_db_use_event removal.
|
||||||
|
(check_event): Remove.
|
||||||
|
(thread_db_wait): Adapt to thread events support removal.
|
||||||
|
(thread_db_mourn_inferior): Likewise.
|
||||||
|
(find_new_threads_callback): Likewise.
|
||||||
|
(find_new_threads_once): Likewise.
|
||||||
|
(thread_db_update_thread_list): Likewise.
|
||||||
|
|
||||||
2015-12-10 Andrew Burgess <andrew.burgess@embecosm.com>
|
2015-12-10 Andrew Burgess <andrew.burgess@embecosm.com>
|
||||||
|
|
||||||
* dwarf2read.c (dwarf2_ranges_read): Unify and fix base address
|
* dwarf2read.c (dwarf2_ranges_read): Unify and fix base address
|
||||||
|
|
|
@ -7765,17 +7765,6 @@ create_thread_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR address)
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
remove_thread_event_breakpoints (void)
|
|
||||||
{
|
|
||||||
struct breakpoint *b, *b_tmp;
|
|
||||||
|
|
||||||
ALL_BREAKPOINTS_SAFE (b, b_tmp)
|
|
||||||
if (b->type == bp_thread_event
|
|
||||||
&& b->loc->pspace == current_program_space)
|
|
||||||
delete_breakpoint (b);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct lang_and_radix
|
struct lang_and_radix
|
||||||
{
|
{
|
||||||
enum language lang;
|
enum language lang;
|
||||||
|
|
|
@ -1498,8 +1498,6 @@ extern void remove_solib_event_breakpoints (void);
|
||||||
delete at next stop disposition. */
|
delete at next stop disposition. */
|
||||||
extern void remove_solib_event_breakpoints_at_next_stop (void);
|
extern void remove_solib_event_breakpoints_at_next_stop (void);
|
||||||
|
|
||||||
extern void remove_thread_event_breakpoints (void);
|
|
||||||
|
|
||||||
extern void disable_breakpoints_in_shlibs (void);
|
extern void disable_breakpoints_in_shlibs (void);
|
||||||
|
|
||||||
/* This function returns TRUE if ep is a catchpoint. */
|
/* This function returns TRUE if ep is a catchpoint. */
|
||||||
|
|
144
gdb/linux-nat.c
144
gdb/linux-nat.c
|
@ -344,17 +344,6 @@ add_to_pid_list (struct simple_pid_list **listp, int pid, int status)
|
||||||
*listp = new_pid;
|
*listp = new_pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
in_pid_list_p (struct simple_pid_list *list, int pid)
|
|
||||||
{
|
|
||||||
struct simple_pid_list *p;
|
|
||||||
|
|
||||||
for (p = list; p != NULL; p = p->next)
|
|
||||||
if (p->pid == pid)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
|
pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
|
||||||
{
|
{
|
||||||
|
@ -1044,139 +1033,6 @@ linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attach to the LWP specified by PID. Return 0 if successful, -1 if
|
|
||||||
the new LWP could not be attached, or 1 if we're already auto
|
|
||||||
attached to this thread, but haven't processed the
|
|
||||||
PTRACE_EVENT_CLONE event of its parent thread, so we just ignore
|
|
||||||
its existance, without considering it an error. */
|
|
||||||
|
|
||||||
int
|
|
||||||
lin_lwp_attach_lwp (ptid_t ptid)
|
|
||||||
{
|
|
||||||
struct lwp_info *lp;
|
|
||||||
int lwpid;
|
|
||||||
|
|
||||||
gdb_assert (ptid_lwp_p (ptid));
|
|
||||||
|
|
||||||
lp = find_lwp_pid (ptid);
|
|
||||||
lwpid = ptid_get_lwp (ptid);
|
|
||||||
|
|
||||||
/* We assume that we're already attached to any LWP that is already
|
|
||||||
in our list of LWPs. If we're not seeing exit events from threads
|
|
||||||
and we've had PID wraparound since we last tried to stop all threads,
|
|
||||||
this assumption might be wrong; fortunately, this is very unlikely
|
|
||||||
to happen. */
|
|
||||||
if (lp == NULL)
|
|
||||||
{
|
|
||||||
int status, cloned = 0, signalled = 0;
|
|
||||||
|
|
||||||
if (ptrace (PTRACE_ATTACH, lwpid, 0, 0) < 0)
|
|
||||||
{
|
|
||||||
if (linux_supports_tracefork ())
|
|
||||||
{
|
|
||||||
/* If we haven't stopped all threads when we get here,
|
|
||||||
we may have seen a thread listed in thread_db's list,
|
|
||||||
but not processed the PTRACE_EVENT_CLONE yet. If
|
|
||||||
that's the case, ignore this new thread, and let
|
|
||||||
normal event handling discover it later. */
|
|
||||||
if (in_pid_list_p (stopped_pids, lwpid))
|
|
||||||
{
|
|
||||||
/* We've already seen this thread stop, but we
|
|
||||||
haven't seen the PTRACE_EVENT_CLONE extended
|
|
||||||
event yet. */
|
|
||||||
if (debug_linux_nat)
|
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
|
||||||
"LLAL: attach failed, but already seen "
|
|
||||||
"this thread %s stop\n",
|
|
||||||
target_pid_to_str (ptid));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int new_pid;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
if (debug_linux_nat)
|
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
|
||||||
"LLAL: attach failed, and haven't seen "
|
|
||||||
"this thread %s stop yet\n",
|
|
||||||
target_pid_to_str (ptid));
|
|
||||||
|
|
||||||
/* We may or may not be attached to the LWP already.
|
|
||||||
Try waitpid on it. If that errors, we're not
|
|
||||||
attached to the LWP yet. Otherwise, we're
|
|
||||||
already attached. */
|
|
||||||
gdb_assert (lwpid > 0);
|
|
||||||
new_pid = my_waitpid (lwpid, &status, WNOHANG);
|
|
||||||
if (new_pid == -1 && errno == ECHILD)
|
|
||||||
new_pid = my_waitpid (lwpid, &status, __WCLONE | WNOHANG);
|
|
||||||
if (new_pid != -1)
|
|
||||||
{
|
|
||||||
if (new_pid == 0)
|
|
||||||
{
|
|
||||||
/* The child hasn't stopped for its initial
|
|
||||||
SIGSTOP stop yet. */
|
|
||||||
if (debug_linux_nat)
|
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
|
||||||
"LLAL: child hasn't "
|
|
||||||
"stopped yet\n");
|
|
||||||
}
|
|
||||||
else if (WIFSTOPPED (status))
|
|
||||||
{
|
|
||||||
if (debug_linux_nat)
|
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
|
||||||
"LLAL: adding to stopped_pids\n");
|
|
||||||
add_to_pid_list (&stopped_pids, lwpid, status);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we fail to attach to the thread, issue a warning,
|
|
||||||
but continue. One way this can happen is if thread
|
|
||||||
creation is interrupted; as of Linux kernel 2.6.19, a
|
|
||||||
bug may place threads in the thread list and then fail
|
|
||||||
to create them. */
|
|
||||||
warning (_("Can't attach %s: %s"), target_pid_to_str (ptid),
|
|
||||||
safe_strerror (errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debug_linux_nat)
|
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
|
||||||
"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
|
|
||||||
target_pid_to_str (ptid));
|
|
||||||
|
|
||||||
status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
|
|
||||||
if (!WIFSTOPPED (status))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
lp = add_lwp (ptid);
|
|
||||||
lp->stopped = 1;
|
|
||||||
lp->last_resume_kind = resume_stop;
|
|
||||||
lp->cloned = cloned;
|
|
||||||
lp->signalled = signalled;
|
|
||||||
if (WSTOPSIG (status) != SIGSTOP)
|
|
||||||
{
|
|
||||||
lp->resumed = 1;
|
|
||||||
lp->status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
target_post_attach (ptid_get_lwp (lp->ptid));
|
|
||||||
|
|
||||||
if (debug_linux_nat)
|
|
||||||
{
|
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
|
||||||
"LLAL: waitpid %s received %s\n",
|
|
||||||
target_pid_to_str (ptid),
|
|
||||||
status_to_str (status));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
linux_nat_create_inferior (struct target_ops *ops,
|
linux_nat_create_inferior (struct target_ops *ops,
|
||||||
char *exec_file, char *allargs, char **env,
|
char *exec_file, char *allargs, char **env,
|
||||||
|
|
|
@ -141,8 +141,6 @@ extern void lin_thread_get_thread_signals (sigset_t *mask);
|
||||||
void linux_proc_pending_signals (int pid, sigset_t *pending,
|
void linux_proc_pending_signals (int pid, sigset_t *pending,
|
||||||
sigset_t *blocked, sigset_t *ignored);
|
sigset_t *blocked, sigset_t *ignored);
|
||||||
|
|
||||||
extern int lin_lwp_attach_lwp (ptid_t ptid);
|
|
||||||
|
|
||||||
/* For linux_stop_lwp see nat/linux-nat.h. */
|
/* For linux_stop_lwp see nat/linux-nat.h. */
|
||||||
|
|
||||||
/* Stop all LWPs, synchronously. (Any events that trigger while LWPs
|
/* Stop all LWPs, synchronously. (Any events that trigger while LWPs
|
||||||
|
|
|
@ -78,16 +78,6 @@ static char *libthread_db_search_path;
|
||||||
by the "set auto-load libthread-db" command. */
|
by the "set auto-load libthread-db" command. */
|
||||||
static int auto_load_thread_db = 1;
|
static int auto_load_thread_db = 1;
|
||||||
|
|
||||||
/* Returns true if we need to use thread_db thread create/death event
|
|
||||||
breakpoints to learn about threads. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
thread_db_use_events (void)
|
|
||||||
{
|
|
||||||
/* Not necessary if the kernel supports clone events. */
|
|
||||||
return !linux_supports_traceclone ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* "show" command for the auto_load_thread_db configuration variable. */
|
/* "show" command for the auto_load_thread_db configuration variable. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -161,30 +151,14 @@ struct thread_db_info
|
||||||
be able to ignore such stale entries. */
|
be able to ignore such stale entries. */
|
||||||
int need_stale_parent_threads_check;
|
int need_stale_parent_threads_check;
|
||||||
|
|
||||||
/* Location of the thread creation event breakpoint. The code at
|
|
||||||
this location in the child process will be called by the pthread
|
|
||||||
library whenever a new thread is created. By setting a special
|
|
||||||
breakpoint at this location, GDB can detect when a new thread is
|
|
||||||
created. We obtain this location via the td_ta_event_addr
|
|
||||||
call. */
|
|
||||||
CORE_ADDR td_create_bp_addr;
|
|
||||||
|
|
||||||
/* Location of the thread death event breakpoint. */
|
|
||||||
CORE_ADDR td_death_bp_addr;
|
|
||||||
|
|
||||||
/* Pointers to the libthread_db functions. */
|
/* Pointers to the libthread_db functions. */
|
||||||
|
|
||||||
td_init_ftype *td_init_p;
|
td_init_ftype *td_init_p;
|
||||||
td_ta_new_ftype *td_ta_new_p;
|
td_ta_new_ftype *td_ta_new_p;
|
||||||
td_ta_map_lwp2thr_ftype *td_ta_map_lwp2thr_p;
|
td_ta_map_lwp2thr_ftype *td_ta_map_lwp2thr_p;
|
||||||
td_ta_thr_iter_ftype *td_ta_thr_iter_p;
|
td_ta_thr_iter_ftype *td_ta_thr_iter_p;
|
||||||
td_ta_event_addr_ftype *td_ta_event_addr_p;
|
|
||||||
td_ta_set_event_ftype *td_ta_set_event_p;
|
|
||||||
td_ta_clear_event_ftype *td_ta_clear_event_p;
|
|
||||||
td_ta_event_getmsg_ftype * td_ta_event_getmsg_p;
|
|
||||||
td_thr_validate_ftype *td_thr_validate_p;
|
td_thr_validate_ftype *td_thr_validate_p;
|
||||||
td_thr_get_info_ftype *td_thr_get_info_p;
|
td_thr_get_info_ftype *td_thr_get_info_p;
|
||||||
td_thr_event_enable_ftype *td_thr_event_enable_p;
|
|
||||||
td_thr_tls_get_addr_ftype *td_thr_tls_get_addr_p;
|
td_thr_tls_get_addr_ftype *td_thr_tls_get_addr_p;
|
||||||
td_thr_tlsbase_ftype *td_thr_tlsbase_p;
|
td_thr_tlsbase_ftype *td_thr_tlsbase_p;
|
||||||
};
|
};
|
||||||
|
@ -273,12 +247,6 @@ delete_thread_db_info (int pid)
|
||||||
xfree (info);
|
xfree (info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prototypes for local functions. */
|
|
||||||
static int attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
|
|
||||||
const td_thrinfo_t *ti_p);
|
|
||||||
static void detach_thread (ptid_t ptid);
|
|
||||||
|
|
||||||
|
|
||||||
/* Use "struct private_thread_info" to cache thread state. This is
|
/* Use "struct private_thread_info" to cache thread state. This is
|
||||||
a substantial optimization. */
|
a substantial optimization. */
|
||||||
|
|
||||||
|
@ -359,30 +327,7 @@ thread_db_err_str (td_err_e err)
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return 1 if any threads have been registered. There may be none if
|
|
||||||
the threading library is not fully initialized yet. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
have_threads_callback (struct thread_info *thread, void *args)
|
|
||||||
{
|
|
||||||
int pid = * (int *) args;
|
|
||||||
|
|
||||||
if (ptid_get_pid (thread->ptid) != pid)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return thread->priv != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
have_threads (ptid_t ptid)
|
|
||||||
{
|
|
||||||
int pid = ptid_get_pid (ptid);
|
|
||||||
|
|
||||||
return iterate_over_threads (have_threads_callback, &pid) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Fetch the user-level thread id of PTID. */
|
/* Fetch the user-level thread id of PTID. */
|
||||||
|
|
||||||
static struct thread_info *
|
static struct thread_info *
|
||||||
|
@ -455,37 +400,6 @@ verbose_dlsym (void *handle, const char *name)
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
static td_err_e
|
|
||||||
enable_thread_event (td_event_e event, CORE_ADDR *bp)
|
|
||||||
{
|
|
||||||
td_notify_t notify;
|
|
||||||
td_err_e err;
|
|
||||||
struct thread_db_info *info;
|
|
||||||
|
|
||||||
info = get_thread_db_info (ptid_get_pid (inferior_ptid));
|
|
||||||
|
|
||||||
/* Access an lwp we know is stopped. */
|
|
||||||
info->proc_handle.ptid = inferior_ptid;
|
|
||||||
|
|
||||||
/* Get the breakpoint address for thread EVENT. */
|
|
||||||
err = info->td_ta_event_addr_p (info->thread_agent, event, ¬ify);
|
|
||||||
if (err != TD_OK)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/* Set up the breakpoint. */
|
|
||||||
gdb_assert (exec_bfd);
|
|
||||||
(*bp) = (gdbarch_convert_from_func_ptr_addr
|
|
||||||
(target_gdbarch (),
|
|
||||||
/* Do proper sign extension for the target. */
|
|
||||||
(bfd_get_sign_extend_vma (exec_bfd) > 0
|
|
||||||
? (CORE_ADDR) (intptr_t) notify.u.bptaddr
|
|
||||||
: (CORE_ADDR) (uintptr_t) notify.u.bptaddr),
|
|
||||||
¤t_target));
|
|
||||||
create_thread_event_breakpoint (target_gdbarch (), *bp);
|
|
||||||
|
|
||||||
return TD_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verify inferior's '\0'-terminated symbol VER_SYMBOL starts with "%d.%d" and
|
/* Verify inferior's '\0'-terminated symbol VER_SYMBOL starts with "%d.%d" and
|
||||||
return 1 if this version is lower (and not equal) to
|
return 1 if this version is lower (and not equal) to
|
||||||
VER_MAJOR_MIN.VER_MINOR_MIN. Return 0 in all other cases. */
|
VER_MAJOR_MIN.VER_MINOR_MIN. Return 0 in all other cases. */
|
||||||
|
@ -517,68 +431,6 @@ inferior_has_bug (const char *ver_symbol, int ver_major_min, int ver_minor_min)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
enable_thread_event_reporting (void)
|
|
||||||
{
|
|
||||||
td_thr_events_t events;
|
|
||||||
td_err_e err;
|
|
||||||
struct thread_db_info *info;
|
|
||||||
|
|
||||||
info = get_thread_db_info (ptid_get_pid (inferior_ptid));
|
|
||||||
|
|
||||||
/* We cannot use the thread event reporting facility if these
|
|
||||||
functions aren't available. */
|
|
||||||
if (info->td_ta_event_addr_p == NULL
|
|
||||||
|| info->td_ta_set_event_p == NULL
|
|
||||||
|| info->td_ta_event_getmsg_p == NULL
|
|
||||||
|| info->td_thr_event_enable_p == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Set the process wide mask saying which events we're interested in. */
|
|
||||||
td_event_emptyset (&events);
|
|
||||||
td_event_addset (&events, TD_CREATE);
|
|
||||||
|
|
||||||
/* There is a bug fixed between linuxthreads 2.1.3 and 2.2 by
|
|
||||||
commit 2e4581e4fba917f1779cd0a010a45698586c190a
|
|
||||||
* manager.c (pthread_exited): Correctly report event as TD_REAP
|
|
||||||
instead of TD_DEATH. Fix comments.
|
|
||||||
where event reporting facility is broken for TD_DEATH events,
|
|
||||||
so don't enable it if we have glibc but a lower version. */
|
|
||||||
if (!inferior_has_bug ("__linuxthreads_version", 2, 2))
|
|
||||||
td_event_addset (&events, TD_DEATH);
|
|
||||||
|
|
||||||
err = info->td_ta_set_event_p (info->thread_agent, &events);
|
|
||||||
if (err != TD_OK)
|
|
||||||
{
|
|
||||||
warning (_("Unable to set global thread event mask: %s"),
|
|
||||||
thread_db_err_str (err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Delete previous thread event breakpoints, if any. */
|
|
||||||
remove_thread_event_breakpoints ();
|
|
||||||
info->td_create_bp_addr = 0;
|
|
||||||
info->td_death_bp_addr = 0;
|
|
||||||
|
|
||||||
/* Set up the thread creation event. */
|
|
||||||
err = enable_thread_event (TD_CREATE, &info->td_create_bp_addr);
|
|
||||||
if (err != TD_OK)
|
|
||||||
{
|
|
||||||
warning (_("Unable to get location for thread creation breakpoint: %s"),
|
|
||||||
thread_db_err_str (err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set up the thread death event. */
|
|
||||||
err = enable_thread_event (TD_DEATH, &info->td_death_bp_addr);
|
|
||||||
if (err != TD_OK)
|
|
||||||
{
|
|
||||||
warning (_("Unable to get location for thread death breakpoint: %s"),
|
|
||||||
thread_db_err_str (err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Similar as thread_db_find_new_threads_1, but try to silently ignore errors
|
/* Similar as thread_db_find_new_threads_1, but try to silently ignore errors
|
||||||
if appropriate.
|
if appropriate.
|
||||||
|
|
||||||
|
@ -716,11 +568,6 @@ try_thread_db_load_1 (struct thread_db_info *info)
|
||||||
CHK (TDB_VERBOSE_DLSYM (info, td_thr_get_info));
|
CHK (TDB_VERBOSE_DLSYM (info, td_thr_get_info));
|
||||||
|
|
||||||
/* These are not essential. */
|
/* These are not essential. */
|
||||||
TDB_DLSYM (info, td_ta_event_addr);
|
|
||||||
TDB_DLSYM (info, td_ta_set_event);
|
|
||||||
TDB_DLSYM (info, td_ta_clear_event);
|
|
||||||
TDB_DLSYM (info, td_ta_event_getmsg);
|
|
||||||
TDB_DLSYM (info, td_thr_event_enable);
|
|
||||||
TDB_DLSYM (info, td_thr_tls_get_addr);
|
TDB_DLSYM (info, td_thr_tls_get_addr);
|
||||||
TDB_DLSYM (info, td_thr_tlsbase);
|
TDB_DLSYM (info, td_thr_tlsbase);
|
||||||
|
|
||||||
|
@ -784,10 +631,6 @@ try_thread_db_load_1 (struct thread_db_info *info)
|
||||||
if (thread_db_list->next == NULL)
|
if (thread_db_list->next == NULL)
|
||||||
push_target (&thread_db_ops);
|
push_target (&thread_db_ops);
|
||||||
|
|
||||||
/* Enable event reporting, but not when debugging a core file. */
|
|
||||||
if (target_has_execution && thread_db_use_events ())
|
|
||||||
enable_thread_event_reporting ();
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1095,23 +938,6 @@ thread_db_load (void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
disable_thread_event_reporting (struct thread_db_info *info)
|
|
||||||
{
|
|
||||||
if (info->td_ta_clear_event_p != NULL)
|
|
||||||
{
|
|
||||||
td_thr_events_t events;
|
|
||||||
|
|
||||||
/* Set the process wide mask saying we aren't interested in any
|
|
||||||
events anymore. */
|
|
||||||
td_event_fillset (&events);
|
|
||||||
info->td_ta_clear_event_p (info->thread_agent, &events);
|
|
||||||
}
|
|
||||||
|
|
||||||
info->td_create_bp_addr = 0;
|
|
||||||
info->td_death_bp_addr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
check_thread_signals (void)
|
check_thread_signals (void)
|
||||||
{
|
{
|
||||||
|
@ -1219,75 +1045,6 @@ update_thread_state (struct private_thread_info *priv,
|
||||||
|| ti_p->ti_state == TD_THR_ZOMBIE);
|
|| ti_p->ti_state == TD_THR_ZOMBIE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attach to a new thread. This function is called when we receive a
|
|
||||||
TD_CREATE event or when we iterate over all threads and find one
|
|
||||||
that wasn't already in our list. Returns true on success. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
|
|
||||||
const td_thrinfo_t *ti_p)
|
|
||||||
{
|
|
||||||
struct thread_info *tp;
|
|
||||||
struct thread_db_info *info;
|
|
||||||
|
|
||||||
/* If we're being called after a TD_CREATE event, we may already
|
|
||||||
know about this thread. There are two ways this can happen. We
|
|
||||||
may have iterated over all threads between the thread creation
|
|
||||||
and the TD_CREATE event, for instance when the user has issued
|
|
||||||
the `info threads' command before the SIGTRAP for hitting the
|
|
||||||
thread creation breakpoint was reported. Alternatively, the
|
|
||||||
thread may have exited and a new one been created with the same
|
|
||||||
thread ID. In the first case we don't need to do anything; in
|
|
||||||
the second case we should discard information about the dead
|
|
||||||
thread and attach to the new one. */
|
|
||||||
tp = find_thread_ptid (ptid);
|
|
||||||
if (tp != NULL)
|
|
||||||
{
|
|
||||||
/* If tp->priv is NULL, then GDB is already attached to this
|
|
||||||
thread, but we do not know anything about it. We can learn
|
|
||||||
about it here. This can only happen if we have some other
|
|
||||||
way besides libthread_db to notice new threads (i.e.
|
|
||||||
PTRACE_EVENT_CLONE); assume the same mechanism notices thread
|
|
||||||
exit, so this can not be a stale thread recreated with the
|
|
||||||
same ID. */
|
|
||||||
if (tp->priv != NULL)
|
|
||||||
{
|
|
||||||
if (!tp->priv->dying)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
delete_thread (ptid);
|
|
||||||
tp = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Under GNU/Linux, we have to attach to each and every thread. */
|
|
||||||
if (target_has_execution
|
|
||||||
&& tp == NULL)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
|
|
||||||
res = lin_lwp_attach_lwp (ptid_build (ptid_get_pid (ptid),
|
|
||||||
ti_p->ti_lid, 0));
|
|
||||||
if (res < 0)
|
|
||||||
{
|
|
||||||
/* Error, stop iterating. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if (res > 0)
|
|
||||||
{
|
|
||||||
/* Pretend this thread doesn't exist yet, and keep
|
|
||||||
iterating. */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, we sucessfully attached to the thread. */
|
|
||||||
}
|
|
||||||
|
|
||||||
info = get_thread_db_info (ptid_get_pid (ptid));
|
|
||||||
record_thread (info, tp, ptid, th_p, ti_p);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Record a new thread in GDB's thread list. Creates the thread's
|
/* Record a new thread in GDB's thread list. Creates the thread's
|
||||||
private info. If TP is NULL or TP is marked as having exited,
|
private info. If TP is NULL or TP is marked as having exited,
|
||||||
creates a new thread. Otherwise, uses TP. */
|
creates a new thread. Otherwise, uses TP. */
|
||||||
|
@ -1323,40 +1080,12 @@ record_thread (struct thread_db_info *info,
|
||||||
else
|
else
|
||||||
tp->priv = priv;
|
tp->priv = priv;
|
||||||
|
|
||||||
/* Enable thread event reporting for this thread, except when
|
|
||||||
debugging a core file. */
|
|
||||||
if (target_has_execution && thread_db_use_events () && new_thread)
|
|
||||||
{
|
|
||||||
err = info->td_thr_event_enable_p (th_p, 1);
|
|
||||||
if (err != TD_OK)
|
|
||||||
error (_("Cannot enable thread event reporting for %s: %s"),
|
|
||||||
target_pid_to_str (ptid), thread_db_err_str (err));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target_has_execution)
|
if (target_has_execution)
|
||||||
check_thread_signals ();
|
check_thread_signals ();
|
||||||
|
|
||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
detach_thread (ptid_t ptid)
|
|
||||||
{
|
|
||||||
struct thread_info *thread_info;
|
|
||||||
|
|
||||||
/* Don't delete the thread now, because it still reports as active
|
|
||||||
until it has executed a few instructions after the event
|
|
||||||
breakpoint - if we deleted it now, "info threads" would cause us
|
|
||||||
to re-attach to it. Just mark it as having had a TD_DEATH
|
|
||||||
event. This means that we won't delete it from our thread list
|
|
||||||
until we notice that it's dead (via prune_threads), or until
|
|
||||||
something re-uses its thread ID. We'll report the thread exit
|
|
||||||
when the underlying LWP dies. */
|
|
||||||
thread_info = find_thread_ptid (ptid);
|
|
||||||
gdb_assert (thread_info != NULL && thread_info->priv != NULL);
|
|
||||||
thread_info->priv->dying = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
thread_db_detach (struct target_ops *ops, const char *args, int from_tty)
|
thread_db_detach (struct target_ops *ops, const char *args, int from_tty)
|
||||||
{
|
{
|
||||||
|
@ -1366,21 +1095,7 @@ thread_db_detach (struct target_ops *ops, const char *args, int from_tty)
|
||||||
info = get_thread_db_info (ptid_get_pid (inferior_ptid));
|
info = get_thread_db_info (ptid_get_pid (inferior_ptid));
|
||||||
|
|
||||||
if (info)
|
if (info)
|
||||||
{
|
delete_thread_db_info (ptid_get_pid (inferior_ptid));
|
||||||
if (target_has_execution && thread_db_use_events ())
|
|
||||||
{
|
|
||||||
disable_thread_event_reporting (info);
|
|
||||||
|
|
||||||
/* Delete the old thread event breakpoints. Note that
|
|
||||||
unlike when mourning, we can remove them here because
|
|
||||||
there's still a live inferior to poke at. In any case,
|
|
||||||
GDB will not try to insert anything in the inferior when
|
|
||||||
removing a breakpoint. */
|
|
||||||
remove_thread_event_breakpoints ();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete_thread_db_info (ptid_get_pid (inferior_ptid));
|
|
||||||
}
|
|
||||||
|
|
||||||
target_beneath->to_detach (target_beneath, args, from_tty);
|
target_beneath->to_detach (target_beneath, args, from_tty);
|
||||||
|
|
||||||
|
@ -1392,101 +1107,6 @@ thread_db_detach (struct target_ops *ops, const char *args, int from_tty)
|
||||||
unpush_target (&thread_db_ops);
|
unpush_target (&thread_db_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if PID is currently stopped at the location of a thread event
|
|
||||||
breakpoint location. If it is, read the event message and act upon
|
|
||||||
the event. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
check_event (ptid_t ptid)
|
|
||||||
{
|
|
||||||
struct regcache *regcache = get_thread_regcache (ptid);
|
|
||||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
|
||||||
td_event_msg_t msg;
|
|
||||||
td_thrinfo_t ti;
|
|
||||||
td_err_e err;
|
|
||||||
CORE_ADDR stop_pc;
|
|
||||||
int loop = 0;
|
|
||||||
struct thread_db_info *info;
|
|
||||||
|
|
||||||
info = get_thread_db_info (ptid_get_pid (ptid));
|
|
||||||
|
|
||||||
/* Bail out early if we're not at a thread event breakpoint. */
|
|
||||||
stop_pc = regcache_read_pc (regcache);
|
|
||||||
if (!target_supports_stopped_by_sw_breakpoint ())
|
|
||||||
stop_pc -= gdbarch_decr_pc_after_break (gdbarch);
|
|
||||||
|
|
||||||
if (stop_pc != info->td_create_bp_addr
|
|
||||||
&& stop_pc != info->td_death_bp_addr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Access an lwp we know is stopped. */
|
|
||||||
info->proc_handle.ptid = ptid;
|
|
||||||
|
|
||||||
/* If we have only looked at the first thread before libpthread was
|
|
||||||
initialized, we may not know its thread ID yet. Make sure we do
|
|
||||||
before we add another thread to the list. */
|
|
||||||
if (!have_threads (ptid))
|
|
||||||
thread_db_find_new_threads_1 (ptid);
|
|
||||||
|
|
||||||
/* If we are at a create breakpoint, we do not know what new lwp
|
|
||||||
was created and cannot specifically locate the event message for it.
|
|
||||||
We have to call td_ta_event_getmsg() to get
|
|
||||||
the latest message. Since we have no way of correlating whether
|
|
||||||
the event message we get back corresponds to our breakpoint, we must
|
|
||||||
loop and read all event messages, processing them appropriately.
|
|
||||||
This guarantees we will process the correct message before continuing
|
|
||||||
from the breakpoint.
|
|
||||||
|
|
||||||
Currently, death events are not enabled. If they are enabled,
|
|
||||||
the death event can use the td_thr_event_getmsg() interface to
|
|
||||||
get the message specifically for that lwp and avoid looping
|
|
||||||
below. */
|
|
||||||
|
|
||||||
loop = 1;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
err = info->td_ta_event_getmsg_p (info->thread_agent, &msg);
|
|
||||||
if (err != TD_OK)
|
|
||||||
{
|
|
||||||
if (err == TD_NOMSG)
|
|
||||||
return;
|
|
||||||
|
|
||||||
error (_("Cannot get thread event message: %s"),
|
|
||||||
thread_db_err_str (err));
|
|
||||||
}
|
|
||||||
|
|
||||||
err = info->td_thr_get_info_p (msg.th_p, &ti);
|
|
||||||
if (err != TD_OK)
|
|
||||||
error (_("Cannot get thread info: %s"), thread_db_err_str (err));
|
|
||||||
|
|
||||||
ptid = ptid_build (ptid_get_pid (ptid), ti.ti_lid, 0);
|
|
||||||
|
|
||||||
switch (msg.event)
|
|
||||||
{
|
|
||||||
case TD_CREATE:
|
|
||||||
/* Call attach_thread whether or not we already know about a
|
|
||||||
thread with this thread ID. */
|
|
||||||
attach_thread (ptid, msg.th_p, &ti);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TD_DEATH:
|
|
||||||
|
|
||||||
if (!in_thread_list (ptid))
|
|
||||||
error (_("Spurious thread death event."));
|
|
||||||
|
|
||||||
detach_thread (ptid);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
error (_("Spurious thread event."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ptid_t
|
static ptid_t
|
||||||
thread_db_wait (struct target_ops *ops,
|
thread_db_wait (struct target_ops *ops,
|
||||||
ptid_t ptid, struct target_waitstatus *ourstatus,
|
ptid_t ptid, struct target_waitstatus *ourstatus,
|
||||||
|
@ -1518,17 +1138,9 @@ thread_db_wait (struct target_ops *ops,
|
||||||
if (!thread_db_list)
|
if (!thread_db_list)
|
||||||
unpush_target (&thread_db_ops);
|
unpush_target (&thread_db_ops);
|
||||||
|
|
||||||
/* Thread event breakpoints are deleted by
|
|
||||||
update_breakpoints_after_exec. */
|
|
||||||
|
|
||||||
return ptid;
|
return ptid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ourstatus->kind == TARGET_WAITKIND_STOPPED
|
|
||||||
&& ourstatus->value.sig == GDB_SIGNAL_TRAP)
|
|
||||||
/* Check for a thread event. */
|
|
||||||
check_event (ptid);
|
|
||||||
|
|
||||||
/* Fill in the thread's user-level thread id and status. */
|
/* Fill in the thread's user-level thread id and status. */
|
||||||
thread_from_lwp (ptid);
|
thread_from_lwp (ptid);
|
||||||
|
|
||||||
|
@ -1544,10 +1156,6 @@ thread_db_mourn_inferior (struct target_ops *ops)
|
||||||
|
|
||||||
target_beneath->to_mourn_inferior (target_beneath);
|
target_beneath->to_mourn_inferior (target_beneath);
|
||||||
|
|
||||||
/* Delete the old thread event breakpoints. Do this after mourning
|
|
||||||
the inferior, so that we don't try to uninsert them. */
|
|
||||||
remove_thread_event_breakpoints ();
|
|
||||||
|
|
||||||
/* Detach thread_db target ops. */
|
/* Detach thread_db target ops. */
|
||||||
if (!thread_db_list)
|
if (!thread_db_list)
|
||||||
unpush_target (ops);
|
unpush_target (ops);
|
||||||
|
@ -1595,21 +1203,12 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
|
||||||
/* A thread ID of zero means that this is the main thread, but
|
/* A thread ID of zero means that this is the main thread, but
|
||||||
glibc has not yet initialized thread-local storage and the
|
glibc has not yet initialized thread-local storage and the
|
||||||
pthread library. We do not know what the thread's TID will
|
pthread library. We do not know what the thread's TID will
|
||||||
be yet. Just enable event reporting and otherwise ignore
|
be yet. */
|
||||||
it. */
|
|
||||||
|
|
||||||
/* In that case, we're not stopped in a fork syscall and don't
|
/* In that case, we're not stopped in a fork syscall and don't
|
||||||
need this glibc bug workaround. */
|
need this glibc bug workaround. */
|
||||||
info->need_stale_parent_threads_check = 0;
|
info->need_stale_parent_threads_check = 0;
|
||||||
|
|
||||||
if (target_has_execution && thread_db_use_events ())
|
|
||||||
{
|
|
||||||
err = info->td_thr_event_enable_p (th_p, 1);
|
|
||||||
if (err != TD_OK)
|
|
||||||
error (_("Cannot enable thread event reporting for LWP %d: %s"),
|
|
||||||
(int) ti.ti_lid, thread_db_err_str (err));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1627,24 +1226,7 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
|
||||||
ptid = ptid_build (info->pid, ti.ti_lid, 0);
|
ptid = ptid_build (info->pid, ti.ti_lid, 0);
|
||||||
tp = find_thread_ptid (ptid);
|
tp = find_thread_ptid (ptid);
|
||||||
if (tp == NULL || tp->priv == NULL)
|
if (tp == NULL || tp->priv == NULL)
|
||||||
{
|
thread_from_lwp (ptid);
|
||||||
if (attach_thread (ptid, th_p, &ti))
|
|
||||||
cb_data->new_threads += 1;
|
|
||||||
else
|
|
||||||
/* Problem attaching this thread; perhaps it exited before we
|
|
||||||
could attach it?
|
|
||||||
This could mean that the thread list inside glibc itself is in
|
|
||||||
inconsistent state, and libthread_db could go on looping forever
|
|
||||||
(observed with glibc-2.3.6). To prevent that, terminate
|
|
||||||
iteration: thread_db_find_new_threads_2 will retry. */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if (target_has_execution && !thread_db_use_events ())
|
|
||||||
{
|
|
||||||
/* Need to update this if not using the libthread_db events
|
|
||||||
(particularly, the TD_DEATH event). */
|
|
||||||
update_thread_state (tp->priv, &ti);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1663,7 +1245,7 @@ find_new_threads_once (struct thread_db_info *info, int iteration,
|
||||||
data.new_threads = 0;
|
data.new_threads = 0;
|
||||||
|
|
||||||
/* See comment in thread_db_update_thread_list. */
|
/* See comment in thread_db_update_thread_list. */
|
||||||
gdb_assert (!target_has_execution || thread_db_use_events ());
|
gdb_assert (!target_has_execution);
|
||||||
|
|
||||||
TRY
|
TRY
|
||||||
{
|
{
|
||||||
|
@ -1789,12 +1371,11 @@ thread_db_update_thread_list (struct target_ops *ops)
|
||||||
it. In the latter case, it's possible that a thread exits just
|
it. In the latter case, it's possible that a thread exits just
|
||||||
at the exact time that causes GDB to get stuck in an infinite
|
at the exact time that causes GDB to get stuck in an infinite
|
||||||
loop. To avoid pausing all threads whenever the core wants to
|
loop. To avoid pausing all threads whenever the core wants to
|
||||||
refresh the thread list, if the kernel supports clone events
|
refresh the thread list, use thread_from_lwp immediately when we
|
||||||
(meaning we're always already attached to all LWPs), we use
|
see an LWP stop. That uses thread_db entry points that do not
|
||||||
thread_from_lwp immediately when we see an LWP stop. That uses
|
walk libpthread's thread list, so should be safe, as well as
|
||||||
thread_db entry points that do not walk libpthread's thread list,
|
more efficient. */
|
||||||
so should be safe, as well as more efficient. */
|
if (target_has_execution)
|
||||||
if (target_has_execution && !thread_db_use_events ())
|
|
||||||
ops->beneath->to_update_thread_list (ops->beneath);
|
ops->beneath->to_update_thread_list (ops->beneath);
|
||||||
else
|
else
|
||||||
thread_db_update_thread_list_td_ta_thr_iter (ops);
|
thread_db_update_thread_list_td_ta_thr_iter (ops);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue