/* This module's target vector. */
static struct target_ops thread_db_ops;
-/* Handle from dlopen for libthread_db.so. Not NULL if we're using this
- module's target vector. */
-static void *thread_db_handle;
-
/* Non-zero if we have determined the signals used by the threads
library. */
static int thread_signals;
static sigset_t thread_stop_set;
static sigset_t thread_print_set;
-/* Structure that identifies the child process for the
- <proc_service.h> interface. */
-static struct ps_prochandle proc_handle;
+struct thread_db_info
+{
+ struct thread_db_info *next;
+
+ /* Process id this object refers to. */
+ int pid;
+
+ /* Handle from dlopen for libthread_db.so. */
+ void *handle;
+
+ /* Structure that identifies the child process for the
+ <proc_service.h> interface. */
+ struct ps_prochandle proc_handle;
+
+ /* Connection to the libthread_db library. */
+ td_thragent_t *thread_agent;
+
+ /* True if we need to apply the workaround for glibc/BZ5983. When
+ we catch a PTRACE_O_TRACEFORK, and go query the child's thread
+ list, nptl_db returns the parent's threads in addition to the new
+ (single) child thread. If this flag is set, we do extra work to
+ be able to ignore such stale entries. */
+ 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;
-/* Connection to the libthread_db library. */
-static td_thragent_t *thread_agent;
+ /* 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. */
-static td_err_e (*td_init_p) (void);
+ td_err_e (*td_init_p) (void);
-static td_err_e (*td_ta_new_p) (struct ps_prochandle * ps,
+ td_err_e (*td_ta_new_p) (struct ps_prochandle * ps,
td_thragent_t **ta);
-static td_err_e (*td_ta_map_id2thr_p) (const td_thragent_t *ta, thread_t pt,
- td_thrhandle_t *__th);
-static td_err_e (*td_ta_map_lwp2thr_p) (const td_thragent_t *ta,
- lwpid_t lwpid, td_thrhandle_t *th);
-static td_err_e (*td_ta_thr_iter_p) (const td_thragent_t *ta,
- td_thr_iter_f *callback, void *cbdata_p,
- td_thr_state_e state, int ti_pri,
- sigset_t *ti_sigmask_p,
- unsigned int ti_user_flags);
-static td_err_e (*td_ta_event_addr_p) (const td_thragent_t *ta,
- td_event_e event, td_notify_t *ptr);
-static td_err_e (*td_ta_set_event_p) (const td_thragent_t *ta,
- td_thr_events_t *event);
-static td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta,
- td_event_msg_t *msg);
-
-static td_err_e (*td_thr_validate_p) (const td_thrhandle_t *th);
-static td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th,
- td_thrinfo_t *infop);
-static td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th,
- int event);
-
-static td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th,
- void *map_address,
- size_t offset, void **address);
-
-/* 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. */
-static CORE_ADDR td_create_bp_addr;
-
-/* Location of the thread death event breakpoint. */
-static CORE_ADDR td_death_bp_addr;
+ td_err_e (*td_ta_map_id2thr_p) (const td_thragent_t *ta, thread_t pt,
+ td_thrhandle_t *__th);
+ td_err_e (*td_ta_map_lwp2thr_p) (const td_thragent_t *ta,
+ lwpid_t lwpid, td_thrhandle_t *th);
+ td_err_e (*td_ta_thr_iter_p) (const td_thragent_t *ta,
+ td_thr_iter_f *callback, void *cbdata_p,
+ td_thr_state_e state, int ti_pri,
+ sigset_t *ti_sigmask_p,
+ unsigned int ti_user_flags);
+ td_err_e (*td_ta_event_addr_p) (const td_thragent_t *ta,
+ td_event_e event, td_notify_t *ptr);
+ td_err_e (*td_ta_set_event_p) (const td_thragent_t *ta,
+ td_thr_events_t *event);
+ td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta,
+ td_event_msg_t *msg);
+
+ td_err_e (*td_thr_validate_p) (const td_thrhandle_t *th);
+ td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th,
+ td_thrinfo_t *infop);
+ td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th,
+ int event);
+
+ td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th,
+ void *map_address,
+ size_t offset, void **address);
+};
+
+/* List of known processes using thread_db, and the required
+ bookkeeping. */
+struct thread_db_info *thread_db_list;
+
+static void thread_db_find_new_threads_1 (ptid_t ptid);
+
+/* Add the current inferior to the list of processes using libpthread.
+ Return a pointer to the newly allocated object that was added to
+ THREAD_DB_LIST. HANDLE is the handle returned by dlopen'ing
+ LIBTHREAD_DB_SO. */
+
+static struct thread_db_info *
+add_thread_db_info (void *handle)
+{
+ int pid;
+ struct thread_db_info *info;
+
+ info = xcalloc (1, sizeof (*info));
+ info->pid = ptid_get_pid (inferior_ptid);
+ info->handle = handle;
+ info->need_stale_parent_threads_check = 1;
+
+ info->next = thread_db_list;
+ thread_db_list = info;
+
+ return info;
+}
+
+/* Return the thread_db_info object representing the bookkeeping
+ related to process PID, if any; NULL otherwise. */
+
+static struct thread_db_info *
+get_thread_db_info (int pid)
+{
+ struct thread_db_info *info;
+
+ for (info = thread_db_list; info; info = info->next)
+ if (pid == info->pid)
+ return info;
+
+ return NULL;
+}
+
+/* When PID has exited or has been detached, we no longer want to keep
+ track of it as using libpthread. Call this function to discard
+ thread_db related info related to PID. Note that this closes
+ LIBTHREAD_DB_SO's dlopen'ed handle. */
+
+static void
+delete_thread_db_info (int pid)
+{
+ struct thread_db_info *info, *info_prev;
+
+ info_prev = NULL;
+
+ for (info = thread_db_list; info; info_prev = info, info = info->next)
+ if (pid == info->pid)
+ break;
+
+ if (info == NULL)
+ return;
+
+ if (info->handle != NULL)
+ dlclose (info->handle);
+
+ if (info_prev)
+ info_prev->next = info->next;
+ else
+ thread_db_list = info->next;
+
+ xfree (info);
+}
/* Prototypes for local functions. */
-static void thread_db_find_new_threads_1 (void);
static void attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
const td_thrinfo_t *ti_p);
static void detach_thread (ptid_t ptid);
the threading library is not fully initialized yet. */
static int
-have_threads_callback (struct thread_info *thread, void *dummy)
+have_threads_callback (struct thread_info *thread, void *args)
{
+ int pid = * (int *) args;
+ if (ptid_get_pid (thread->ptid) != pid)
+ return 0;
+
return thread->private != NULL;
}
static int
-have_threads (void)
+have_threads (ptid_t ptid)
{
- return iterate_over_threads (have_threads_callback, NULL) != NULL;
+ int pid = ptid_get_pid (ptid);
+
+ return iterate_over_threads (have_threads_callback, &pid) != NULL;
}
+struct thread_get_info_inout
+{
+ struct thread_info *thread_info;
+ struct thread_db_info *thread_db_info;
+};
+
/* A callback function for td_ta_thr_iter, which we use to map all
threads to LWPs.
zero is returned to indicate success. */
static int
-thread_get_info_callback (const td_thrhandle_t *thp, void *infop)
+thread_get_info_callback (const td_thrhandle_t *thp, void *argp)
{
td_thrinfo_t ti;
td_err_e err;
- struct thread_info *thread_info;
ptid_t thread_ptid;
+ struct thread_get_info_inout *inout;
+ struct thread_db_info *info;
- err = td_thr_get_info_p (thp, &ti);
+ inout = argp;
+ info = inout->thread_db_info;
+
+ err = info->td_thr_get_info_p (thp, &ti);
if (err != TD_OK)
error (_("thread_get_info_callback: cannot get thread info: %s"),
thread_db_err_str (err));
/* Fill the cache. */
- thread_ptid = ptid_build (GET_PID (proc_handle.ptid), ti.ti_lid, 0);
- thread_info = find_thread_pid (thread_ptid);
+ thread_ptid = ptid_build (info->pid, ti.ti_lid, 0);
+ inout->thread_info = find_thread_ptid (thread_ptid);
/* In the case of a zombie thread, don't continue. We don't want to
attach to it thinking it is a new thread. */
if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
- {
- if (infop != NULL)
- *(struct thread_info **) infop = thread_info;
- return TD_THR_ZOMBIE;
- }
+ return TD_THR_ZOMBIE;
- if (thread_info == NULL)
+ if (inout->thread_info == NULL)
{
/* New thread. Attach to it now (why wait?). */
- if (!have_threads ())
- thread_db_find_new_threads_1 ();
+ if (!have_threads (thread_ptid))
+ thread_db_find_new_threads_1 (thread_ptid);
else
attach_thread (thread_ptid, thp, &ti);
- thread_info = find_thread_pid (thread_ptid);
- gdb_assert (thread_info != NULL);
+ inout->thread_info = find_thread_ptid (thread_ptid);
+ gdb_assert (inout->thread_info != NULL);
}
- if (infop != NULL)
- *(struct thread_info **) infop = thread_info;
-
return 0;
}
\f
{
td_thrhandle_t th;
td_err_e err;
- struct thread_info *thread_info;
ptid_t thread_ptid;
+ struct thread_db_info *info;
+ struct thread_get_info_inout io = {0};
/* This ptid comes from linux-nat.c, which should always fill in the
LWP. */
gdb_assert (GET_LWP (ptid) != 0);
+ info = get_thread_db_info (GET_PID (ptid));
+
/* Access an lwp we know is stopped. */
- proc_handle.ptid = ptid;
- err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th);
+ info->proc_handle.ptid = ptid;
+ err = info->td_ta_map_lwp2thr_p (info->thread_agent, GET_LWP (ptid), &th);
if (err != TD_OK)
error (_("Cannot find user-level thread for LWP %ld: %s"),
GET_LWP (ptid), thread_db_err_str (err));
- thread_info = NULL;
-
/* Fetch the thread info. If we get back TD_THR_ZOMBIE, then the
event thread has already died. If another gdb interface has called
thread_alive() previously, the thread won't be found on the thread list
discovered thread id that we should add to the list. Thus,
we return a -1 ptid which is also how the thread list marks a
dead thread. */
- if (thread_get_info_callback (&th, &thread_info) == TD_THR_ZOMBIE
- && thread_info == NULL)
- return pid_to_ptid (-1);
+ io.thread_db_info = info;
+ io.thread_info = NULL;
+ if (thread_get_info_callback (&th, &io) == TD_THR_ZOMBIE
+ && io.thread_info == NULL)
+ return minus_one_ptid;
gdb_assert (ptid_get_tid (ptid) == 0);
return ptid;
td_thrhandle_t th;
td_thrinfo_t ti;
td_err_e err;
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (ptid));
- if (thread_db_handle == NULL)
+ if (info == NULL)
return 0;
/* This ptid comes from linux-nat.c, which should always fill in the
gdb_assert (GET_LWP (ptid) != 0);
/* Access an lwp we know is stopped. */
- proc_handle.ptid = ptid;
+ 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 ())
- thread_db_find_new_threads_1 ();
+ if (!have_threads (ptid))
+ thread_db_find_new_threads_1 (ptid);
- err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th);
+ err = info->td_ta_map_lwp2thr_p (info->thread_agent, GET_LWP (ptid), &th);
if (err != TD_OK)
/* Cannot find user-level thread. */
return 0;
- err = td_thr_get_info_p (&th, &ti);
+ err = info->td_thr_get_info_p (&th, &ti);
if (err != TD_OK)
{
warning (_("Cannot get thread info: %s"), thread_db_err_str (err));
}
static td_err_e
-enable_thread_event (td_thragent_t *thread_agent, int event, CORE_ADDR *bp)
+enable_thread_event (int event, CORE_ADDR *bp)
{
td_notify_t notify;
td_err_e err;
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (inferior_ptid));
/* Access an lwp we know is stopped. */
- proc_handle.ptid = inferior_ptid;
+ info->proc_handle.ptid = inferior_ptid;
/* Get the breakpoint address for thread EVENT. */
- err = td_ta_event_addr_p (thread_agent, event, ¬ify);
+ 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
- (current_gdbarch,
+ (target_gdbarch,
/* Do proper sign extension for the target. */
(bfd_get_sign_extend_vma (exec_bfd) > 0
? (CORE_ADDR) (intptr_t) notify.u.bptaddr
const char *libc_version;
int libc_major, libc_minor;
#endif
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (inferior_ptid));
/* We cannot use the thread event reporting facility if these
functions aren't available. */
- if (td_ta_event_addr_p == NULL || td_ta_set_event_p == NULL
- || td_ta_event_getmsg_p == NULL || td_thr_event_enable_p == NULL)
+ 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. */
#endif
td_event_addset (&events, TD_DEATH);
- err = td_ta_set_event_p (thread_agent, &events);
+ err = info->td_ta_set_event_p (info->thread_agent, &events);
if (err != TD_OK)
{
warning (_("Unable to set global thread event mask: %s"),
/* Delete previous thread event breakpoints, if any. */
remove_thread_event_breakpoints ();
- td_create_bp_addr = 0;
- td_death_bp_addr = 0;
+ info->td_create_bp_addr = 0;
+ info->td_death_bp_addr = 0;
/* Set up the thread creation event. */
- err = enable_thread_event (thread_agent, TD_CREATE, &td_create_bp_addr);
+ 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"),
}
/* Set up the thread death event. */
- err = enable_thread_event (thread_agent, TD_DEATH, &td_death_bp_addr);
+ 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"),
}
}
+/* Lookup a library in which given symbol resides.
+ Note: this is looking in GDB process, not in the inferior.
+ Returns library name, or NULL. */
+
+static const char *
+dladdr_to_soname (const void *addr)
+{
+ Dl_info info;
+
+ if (dladdr (addr, &info) != 0)
+ return info.dli_fname;
+ return NULL;
+}
+
/* Attempt to initialize dlopen()ed libthread_db, described by HANDLE.
Return 1 on success.
Failure could happen if libthread_db does not have symbols we expect,
version mismatch between libthread_db and libpthread). */
static int
-try_thread_db_load_1 (void *handle)
+try_thread_db_load_1 (struct thread_db_info *info)
{
td_err_e err;
/* Initialize pointers to the dynamic library functions we will use.
Essential functions first. */
- td_init_p = verbose_dlsym (handle, "td_init");
- if (td_init_p == NULL)
+ info->td_init_p = verbose_dlsym (info->handle, "td_init");
+ if (info->td_init_p == NULL)
return 0;
- err = td_init_p ();
+ err = info->td_init_p ();
if (err != TD_OK)
{
warning (_("Cannot initialize libthread_db: %s"), thread_db_err_str (err));
return 0;
}
- td_ta_new_p = verbose_dlsym (handle, "td_ta_new");
- if (td_ta_new_p == NULL)
+ info->td_ta_new_p = verbose_dlsym (info->handle, "td_ta_new");
+ if (info->td_ta_new_p == NULL)
return 0;
/* Initialize the structure that identifies the child process. */
- proc_handle.ptid = inferior_ptid;
+ info->proc_handle.ptid = inferior_ptid;
/* Now attempt to open a connection to the thread library. */
- err = td_ta_new_p (&proc_handle, &thread_agent);
+ err = info->td_ta_new_p (&info->proc_handle, &info->thread_agent);
if (err != TD_OK)
{
- td_ta_new_p = NULL;
if (info_verbose)
printf_unfiltered (_("td_ta_new failed: %s\n"),
thread_db_err_str (err));
return 0;
}
- td_ta_map_id2thr_p = verbose_dlsym (handle, "td_ta_map_id2thr");
- if (td_ta_map_id2thr_p == NULL)
+ info->td_ta_map_id2thr_p = verbose_dlsym (info->handle, "td_ta_map_id2thr");
+ if (info->td_ta_map_id2thr_p == NULL)
return 0;
- td_ta_map_lwp2thr_p = verbose_dlsym (handle, "td_ta_map_lwp2thr");
- if (td_ta_map_lwp2thr_p == NULL)
+ info->td_ta_map_lwp2thr_p = verbose_dlsym (info->handle, "td_ta_map_lwp2thr");
+ if (info->td_ta_map_lwp2thr_p == NULL)
return 0;
- td_ta_thr_iter_p = verbose_dlsym (handle, "td_ta_thr_iter");
- if (td_ta_thr_iter_p == NULL)
+ info->td_ta_thr_iter_p = verbose_dlsym (info->handle, "td_ta_thr_iter");
+ if (info->td_ta_thr_iter_p == NULL)
return 0;
- td_thr_validate_p = verbose_dlsym (handle, "td_thr_validate");
- if (td_thr_validate_p == NULL)
+ info->td_thr_validate_p = verbose_dlsym (info->handle, "td_thr_validate");
+ if (info->td_thr_validate_p == NULL)
return 0;
- td_thr_get_info_p = verbose_dlsym (handle, "td_thr_get_info");
- if (td_thr_get_info_p == NULL)
+ info->td_thr_get_info_p = verbose_dlsym (info->handle, "td_thr_get_info");
+ if (info->td_thr_get_info_p == NULL)
return 0;
/* These are not essential. */
- td_ta_event_addr_p = dlsym (handle, "td_ta_event_addr");
- td_ta_set_event_p = dlsym (handle, "td_ta_set_event");
- td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg");
- td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable");
- td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr");
+ info->td_ta_event_addr_p = dlsym (info->handle, "td_ta_event_addr");
+ info->td_ta_set_event_p = dlsym (info->handle, "td_ta_set_event");
+ info->td_ta_event_getmsg_p = dlsym (info->handle, "td_ta_event_getmsg");
+ info->td_thr_event_enable_p = dlsym (info->handle, "td_thr_event_enable");
+ info->td_thr_tls_get_addr_p = dlsym (info->handle, "td_thr_tls_get_addr");
printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n"));
- /* The thread library was detected. Activate the thread_db target. */
- push_target (&thread_db_ops);
- thread_db_handle = handle;
+ if (info_verbose || *libthread_db_search_path)
+ {
+ const char *library;
- enable_thread_event_reporting ();
- thread_db_find_new_threads_1 ();
- return 1;
-}
+ library = dladdr_to_soname (*info->td_ta_new_p);
+ if (library == NULL)
+ library = LIBTHREAD_DB_SO;
-/* Lookup a library in which given symbol resides.
- Note: this is looking in GDB process, not in the inferior.
- Returns library name, or NULL. */
+ printf_unfiltered (_("Using host libthread_db library \"%s\".\n"),
+ library);
+ }
-static const char *
-dladdr_to_soname (const void *addr)
-{
- Dl_info info;
+ /* The thread library was detected. Activate the thread_db target
+ if this is the first process using it. */
+ if (thread_db_list->next == NULL)
+ push_target (&thread_db_ops);
- if (dladdr (addr, &info) != 0)
- return info.dli_fname;
- return NULL;
+ enable_thread_event_reporting ();
+ thread_db_find_new_threads_1 (inferior_ptid);
+ return 1;
}
/* Attempt to use LIBRARY as libthread_db. LIBRARY could be absolute,
try_thread_db_load (const char *library)
{
void *handle;
+ struct thread_db_info *info;
if (info_verbose)
printf_unfiltered (_("Trying host libthread_db library: %s.\n"),
}
}
- if (try_thread_db_load_1 (handle))
+ info = add_thread_db_info (handle);
+
+ if (try_thread_db_load_1 (info))
return 1;
/* This library "refused" to work on current inferior. */
- dlclose (handle);
+ delete_thread_db_info (GET_PID (inferior_ptid));
return 0;
}
thread_db_load (void)
{
struct objfile *obj;
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (inferior_ptid));
- if (thread_db_handle != NULL)
+ if (info != NULL)
return 1;
/* Don't attempt to use thread_db on targets which can not run
disable_thread_event_reporting (void)
{
td_thr_events_t events;
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (inferior_ptid));
/* Set the process wide mask saying we aren't interested in any
events anymore. */
td_event_emptyset (&events);
- td_ta_set_event_p (thread_agent, &events);
+ info->td_ta_set_event_p (info->thread_agent, &events);
/* Delete thread event breakpoints, if any. */
remove_thread_event_breakpoints ();
- td_create_bp_addr = 0;
- td_death_bp_addr = 0;
+ info->td_create_bp_addr = 0;
+ info->td_death_bp_addr = 0;
}
static void
/* Do nothing if we couldn't load libthread_db.so.1. */
if (!thread_db_load ())
return;
-
- /* First time through, report that libthread_db was successfuly
- loaded. Can't print this in in thread_db_load as, at that stage,
- the interpreter and it's console haven't started.
- We track td_ta_new_p because the user may switch executables,
- and as a result we may decide to use a different version of
- libthread_db. */
-
- if (last_loaded != td_ta_new_p)
- {
- last_loaded = td_ta_new_p;
-
- if (info_verbose || *libthread_db_search_path)
- {
- const char *library;
-
- library = dladdr_to_soname (*td_ta_new_p);
- if (library == NULL)
- library = LIBTHREAD_DB_SO;
-
- printf_unfiltered (_("Using host libthread_db library \"%s\".\n"),
- library);
- }
- }
}
static void
thread_db_new_objfile (struct objfile *objfile)
{
+ /* This observer must always be called with inferior_ptid set
+ correctly. */
+
if (objfile != NULL)
check_for_thread_db ();
}
struct private_thread_info *private;
struct thread_info *tp = NULL;
td_err_e err;
+ 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
thread and attach to the new one. */
if (in_thread_list (ptid))
{
- tp = find_thread_pid (ptid);
+ tp = find_thread_ptid (ptid);
gdb_assert (tp != NULL);
/* If tp->private is NULL, then GDB is already attached to this
else
tp->private = private;
+ info = get_thread_db_info (GET_PID (ptid));
+
/* Enable thread event reporting for this thread. */
- err = td_thr_event_enable_p (th_p, 1);
+ 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));
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_pid (ptid);
+ thread_info = find_thread_ptid (ptid);
gdb_assert (thread_info != NULL && thread_info->private != NULL);
thread_info->private->dying = 1;
}
thread_db_detach (struct target_ops *ops, char *args, int from_tty)
{
struct target_ops *target_beneath = find_target_beneath (ops);
+ struct thread_db_info *info;
- disable_thread_event_reporting ();
+ info = get_thread_db_info (GET_PID (inferior_ptid));
- /* Forget about the child's process ID. We shouldn't need it
- anymore. */
- proc_handle.ptid = null_ptid;
+ if (info)
+ {
+ disable_thread_event_reporting ();
- /* Detach thread_db target ops. */
- unpush_target (&thread_db_ops);
- if (thread_db_handle)
- dlclose (thread_db_handle);
- thread_db_handle = NULL;
+ /* 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 (GET_PID (inferior_ptid));
+ }
target_beneath->to_detach (target_beneath, args, from_tty);
+
+ /* NOTE: From this point on, inferior_ptid is null_ptid. */
+
+ /* If there are no more processes using libpthread, detach the
+ thread_db target ops. */
+ if (!thread_db_list)
+ unpush_target (&thread_db_ops);
}
/* Check if PID is currently stopped at the location of a thread event
td_err_e err;
CORE_ADDR stop_pc;
int loop = 0;
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (ptid));
/* Bail out early if we're not at a thread event breakpoint. */
stop_pc = regcache_read_pc (regcache)
- gdbarch_decr_pc_after_break (gdbarch);
- if (stop_pc != td_create_bp_addr && stop_pc != td_death_bp_addr)
+ if (stop_pc != info->td_create_bp_addr
+ && stop_pc != info->td_death_bp_addr)
return;
/* Access an lwp we know is stopped. */
- proc_handle.ptid = ptid;
+ 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 ())
- thread_db_find_new_threads_1 ();
+ 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.
do
{
- err = td_ta_event_getmsg_p (thread_agent, &msg);
+ err = info->td_ta_event_getmsg_p (info->thread_agent, &msg);
if (err != TD_OK)
{
if (err == TD_NOMSG)
thread_db_err_str (err));
}
- err = td_thr_get_info_p (msg.th_p, &ti);
+ 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));
static ptid_t
thread_db_wait (struct target_ops *ops,
- ptid_t ptid, struct target_waitstatus *ourstatus)
+ ptid_t ptid, struct target_waitstatus *ourstatus,
+ int options)
{
+ struct thread_db_info *info;
struct target_ops *beneath = find_target_beneath (ops);
- ptid = beneath->to_wait (beneath, ptid, ourstatus);
+ ptid = beneath->to_wait (beneath, ptid, ourstatus, options);
if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
return ptid;
|| ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
return ptid;
+ info = get_thread_db_info (GET_PID (ptid));
+
+ /* If this process isn't using thread_db, we're done. */
+ if (info == NULL)
+ return ptid;
+
if (ourstatus->kind == TARGET_WAITKIND_EXECD)
{
+ /* Breakpoints have already been marked non-inserted by the
+ layer below. We're safe in knowing that removing them will
+ not write the shadows of the old image into the new
+ image. */
remove_thread_event_breakpoints ();
- unpush_target (&thread_db_ops);
- if (thread_db_handle)
- dlclose (thread_db_handle);
- thread_db_handle = NULL;
+
+ /* New image, it may or may not end up using thread_db. Assume
+ not unless we find otherwise. */
+ delete_thread_db_info (GET_PID (ptid));
+ if (!thread_db_list)
+ unpush_target (&thread_db_ops);
return ptid;
}
/* If we do not know about the main thread yet, this would be a good time to
find it. */
- if (ourstatus->kind == TARGET_WAITKIND_STOPPED && !have_threads ())
- thread_db_find_new_threads_1 ();
+ if (ourstatus->kind == TARGET_WAITKIND_STOPPED && !have_threads (ptid))
+ thread_db_find_new_threads_1 (ptid);
if (ourstatus->kind == TARGET_WAITKIND_STOPPED
&& ourstatus->value.sig == TARGET_SIGNAL_TRAP)
/* Check for a thread event. */
check_event (ptid);
- if (have_threads ())
+ if (have_threads (ptid))
{
/* Change ptids back into the higher level PID + TID format. If
the thread is dead and no longer on the thread list, we will
{
struct target_ops *target_beneath = find_target_beneath (ops);
- /* Forget about the child's process ID. We shouldn't need it
- anymore. */
- proc_handle.ptid = null_ptid;
+ delete_thread_db_info (GET_PID (inferior_ptid));
- 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. */
+ /* Delete the old thread event breakpoints. Mark breakpoints out,
+ so that we don't try to un-insert them. */
+ mark_breakpoints_out ();
remove_thread_event_breakpoints ();
+ target_beneath->to_mourn_inferior (target_beneath);
+
/* Detach thread_db target ops. */
- unpush_target (ops);
- if (thread_db_handle)
- dlclose (thread_db_handle);
- thread_db_handle = NULL;
+ if (!thread_db_list)
+ unpush_target (ops);
}
static int
td_err_e err;
ptid_t ptid;
struct thread_info *tp;
+ struct thread_db_info *info = data;
- err = td_thr_get_info_p (th_p, &ti);
+ err = info->td_thr_get_info_p (th_p, &ti);
if (err != TD_OK)
error (_("find_new_threads_callback: cannot get thread info: %s"),
thread_db_err_str (err));
if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
return 0; /* A zombie -- ignore. */
- ptid = ptid_build (GET_PID (proc_handle.ptid), ti.ti_lid, 0);
-
if (ti.ti_tid == 0)
{
/* A thread ID of zero means that this is the main thread, but
be yet. Just enable event reporting and otherwise ignore
it. */
- err = td_thr_event_enable_p (th_p, 1);
+ /* In that case, we're not stopped in a fork syscall and don't
+ need this glibc bug workaround. */
+ info->need_stale_parent_threads_check = 0;
+
+ 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));
+ error (_("Cannot enable thread event reporting for LWP %d: %s"),
+ (int) ti.ti_lid, thread_db_err_str (err));
return 0;
}
- tp = find_thread_pid (ptid);
+ /* Ignore stale parent threads, caused by glibc/BZ5983. This is a
+ bit expensive, as it needs to open /proc/pid/status, so try to
+ avoid doing the work if we know we don't have to. */
+ if (info->need_stale_parent_threads_check)
+ {
+ int tgid = linux_proc_get_tgid (ti.ti_lid);
+ if (tgid != -1 && tgid != info->pid)
+ return 0;
+ }
+
+ ptid = ptid_build (info->pid, ti.ti_lid, 0);
+ tp = find_thread_ptid (ptid);
if (tp == NULL || tp->private == NULL)
attach_thread (ptid, th_p, &ti);
PTID. */
static void
-thread_db_find_new_threads_1 (void)
+thread_db_find_new_threads_1 (ptid_t ptid)
{
td_err_e err;
struct lwp_info *lp;
- ptid_t ptid;
+ struct thread_db_info *info;
+ int pid = ptid_get_pid (ptid);
/* In linux, we can only read memory through a stopped lwp. */
ALL_LWPS (lp, ptid)
- if (lp->stopped)
+ if (lp->stopped && ptid_get_pid (lp->ptid) == pid)
break;
if (!lp)
/* There is no stopped thread. Bail out. */
return;
+ info = get_thread_db_info (GET_PID (ptid));
+
/* Access an lwp we know is stopped. */
- proc_handle.ptid = ptid;
+ info->proc_handle.ptid = ptid;
/* Iterate over all user-space threads to discover new threads. */
- err = td_ta_thr_iter_p (thread_agent, find_new_threads_callback, NULL,
- TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
- TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+ err = info->td_ta_thr_iter_p (info->thread_agent, find_new_threads_callback,
+ info, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+ TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
if (err != TD_OK)
error (_("Cannot find new threads: %s"), thread_db_err_str (err));
}
static void
thread_db_find_new_threads (struct target_ops *ops)
{
- thread_db_find_new_threads_1 ();
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (inferior_ptid));
+
+ if (info == NULL)
+ return;
+
+ thread_db_find_new_threads_1 (inferior_ptid);
}
static char *
thread_db_pid_to_str (struct target_ops *ops, ptid_t ptid)
{
- struct thread_info *thread_info = find_thread_pid (ptid);
+ struct thread_info *thread_info = find_thread_ptid (ptid);
struct target_ops *beneath;
if (thread_info != NULL && thread_info->private != NULL)
struct target_ops *beneath;
/* If we have not discovered any threads yet, check now. */
- if (!have_threads ())
- thread_db_find_new_threads_1 ();
+ if (!have_threads (ptid))
+ thread_db_find_new_threads_1 (ptid);
/* Find the matching thread. */
- thread_info = find_thread_pid (ptid);
+ thread_info = find_thread_ptid (ptid);
if (thread_info != NULL && thread_info->private != NULL)
{
td_err_e err;
void *address;
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (ptid));
/* glibc doesn't provide the needed interface. */
- if (!td_thr_tls_get_addr_p)
+ if (!info->td_thr_tls_get_addr_p)
throw_error (TLS_NO_LIBRARY_SUPPORT_ERROR,
_("No TLS library support"));
gdb_assert (lm != 0);
/* Finally, get the address of the variable. */
- err = td_thr_tls_get_addr_p (&thread_info->private->th,
- (void *)(size_t) lm,
- offset, &address);
+ err = info->td_thr_tls_get_addr_p (&thread_info->private->th,
+ (void *)(size_t) lm,
+ offset, &address);
#ifdef THREAD_DB_HAS_TD_NOTALLOC
/* The memory hasn't been allocated, yet. */
{
struct thread_info *thread_info;
- thread_db_find_new_threads_1 ();
+ thread_db_find_new_threads_1 (inferior_ptid);
thread_info = iterate_over_threads (thread_db_find_thread_from_tid, &thread);
gdb_assert (thread_info != NULL);
return (thread_info->ptid);
}
+static void
+thread_db_resume (struct target_ops *ops,
+ ptid_t ptid, int step, enum target_signal signo)
+{
+ struct target_ops *beneath = find_target_beneath (ops);
+ struct thread_db_info *info;
+
+ if (ptid_equal (ptid, minus_one_ptid))
+ info = get_thread_db_info (GET_PID (inferior_ptid));
+ else
+ info = get_thread_db_info (GET_PID (ptid));
+
+ /* This workaround is only needed for child fork lwps stopped in a
+ PTRACE_O_TRACEFORK event. When the inferior is resumed, the
+ workaround can be disabled. */
+ if (info)
+ info->need_stale_parent_threads_check = 0;
+
+ beneath->to_resume (beneath, ptid, step, signo);
+}
+
static void
init_thread_db_ops (void)
{
thread_db_ops.to_doc = "Threads and pthreads support.";
thread_db_ops.to_detach = thread_db_detach;
thread_db_ops.to_wait = thread_db_wait;
+ thread_db_ops.to_resume = thread_db_resume;
thread_db_ops.to_mourn_inferior = thread_db_mourn_inferior;
thread_db_ops.to_find_new_threads = thread_db_find_new_threads;
thread_db_ops.to_pid_to_str = thread_db_pid_to_str;