/* libthread_db assisted debugging support, generic parts.
- Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008
+ Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
This file is part of GDB.
#include "gdb_thread_db.h"
#include "bfd.h"
+#include "command.h"
#include "exceptions.h"
+#include "gdbcmd.h"
#include "gdbthread.h"
#include "inferior.h"
#include "symfile.h"
#include "objfiles.h"
#include "target.h"
#include "regcache.h"
+#include "solib.h"
#include "solib-svr4.h"
#include "gdbcore.h"
#include "observer.h"
#include <gnu/libc-version.h>
#endif
-#ifndef LIBTHREAD_DB_SO
-#define LIBTHREAD_DB_SO "libthread_db.so.1"
-#endif
+/* GNU/Linux libthread_db support.
+
+ libthread_db is a library, provided along with libpthread.so, which
+ exposes the internals of the thread library to a debugger. It
+ allows GDB to find existing threads, new threads as they are
+ created, thread IDs (usually, the result of pthread_self), and
+ thread-local variables.
+
+ The libthread_db interface originates on Solaris, where it is
+ both more powerful and more complicated. This implementation
+ only works for LinuxThreads and NPTL, the two glibc threading
+ libraries. It assumes that each thread is permanently assigned
+ to a single light-weight process (LWP).
+
+ libthread_db-specific information is stored in the "private" field
+ of struct thread_info. When the field is NULL we do not yet have
+ information about the new thread; this could be temporary (created,
+ but the thread library's data structures do not reflect it yet)
+ or permanent (created using clone instead of pthread_create).
+
+ Process IDs managed by linux-thread-db.c match those used by
+ linux-nat.c: a common PID for all processes, an LWP ID for each
+ thread, and no TID. We save the TID in private. Keeping it out
+ of the ptid_t prevents thread IDs changing when libpthread is
+ loaded or unloaded. */
+
+static char *libthread_db_search_path;
/* If we're running on GNU/Linux, we must explicitly attach to any new
threads. */
/* This module's target vector. */
static struct target_ops thread_db_ops;
-/* The target vector that we call for things this module can't handle. */
-static struct target_ops *target_beneath;
-
-/* Non-zero if we're using this module's target vector. */
-static int using_thread_db;
-
/* 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. */
-static td_thragent_t *thread_agent;
+ /* Connection to the libthread_db library. */
+ td_thragent_t *thread_agent;
-/* Pointers to the libthread_db functions. */
+ /* 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;
-static td_err_e (*td_init_p) (void);
+ /* 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;
-static td_err_e (*td_ta_new_p) (struct ps_prochandle * ps,
+ /* Location of the thread death event breakpoint. */
+ CORE_ADDR td_death_bp_addr;
+
+ /* Pointers to the libthread_db functions. */
+
+ td_err_e (*td_init_p) (void);
+
+ 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);
+};
-/* Prototypes for local functions. */
-static void thread_db_find_new_threads (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, int verbose);
-\f
+/* 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. */
-/* Building process ids. */
+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;
-#define GET_PID(ptid) ptid_get_pid (ptid)
-#define GET_LWP(ptid) ptid_get_lwp (ptid)
-#define GET_THREAD(ptid) ptid_get_tid (ptid)
+ info->next = thread_db_list;
+ thread_db_list = info;
-#define is_lwp(ptid) (GET_LWP (ptid) != 0)
-#define is_thread(ptid) (GET_THREAD (ptid) != 0)
+ return info;
+}
-#define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0)
+/* 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 attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
+ const td_thrinfo_t *ti_p);
+static void detach_thread (ptid_t ptid);
\f
/* Use "struct private_thread_info" to cache thread state. This is
unsigned int dying:1;
/* Cached thread state. */
- unsigned int th_valid:1;
- unsigned int ti_valid:1;
-
td_thrhandle_t th;
- td_thrinfo_t ti;
+ thread_t tid;
};
\f
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)
{
- return 1;
+ 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 (inferior_ptid), ti.ti_lid, ti.ti_tid);
- 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;
- if (thread_info != NULL)
- {
- memcpy (&thread_info->private->th, thp, sizeof (*thp));
- thread_info->private->th_valid = 1;
- memcpy (&thread_info->private->ti, &ti, sizeof (ti));
- thread_info->private->ti_valid = 1;
- }
- 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?). */
- attach_thread (thread_ptid, thp, &ti);
- thread_info = find_thread_pid (thread_ptid);
- gdb_assert (thread_info != NULL);
+ if (!have_threads (thread_ptid))
+ thread_db_find_new_threads_1 (thread_ptid);
+ else
+ attach_thread (thread_ptid, thp, &ti);
+ inout->thread_info = find_thread_ptid (thread_ptid);
+ gdb_assert (inout->thread_info != NULL);
}
- memcpy (&thread_info->private->th, thp, sizeof (*thp));
- thread_info->private->th_valid = 1;
- memcpy (&thread_info->private->ti, &ti, sizeof (ti));
- thread_info->private->ti_valid = 1;
-
- if (infop != NULL)
- *(struct thread_info **) infop = thread_info;
-
return 0;
}
-
-/* Accessor functions for the thread_db information, with caching. */
-
-static void
-thread_db_map_id2thr (struct thread_info *thread_info, int fatal)
-{
- td_err_e err;
-
- if (thread_info->private->th_valid)
- return;
-
- err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (thread_info->ptid),
- &thread_info->private->th);
- if (err != TD_OK)
- {
- if (fatal)
- error (_("Cannot find thread %ld: %s"),
- (long) GET_THREAD (thread_info->ptid),
- thread_db_err_str (err));
- }
- else
- thread_info->private->th_valid = 1;
-}
\f
/* Convert between user-level thread ids and LWP ids. */
{
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};
- if (GET_LWP (ptid) == 0)
- ptid = BUILD_LWP (GET_PID (ptid), GET_PID (ptid));
+ /* This ptid comes from linux-nat.c, which should always fill in the
+ LWP. */
+ gdb_assert (GET_LWP (ptid) != 0);
- gdb_assert (is_lwp (ptid));
+ info = get_thread_db_info (GET_PID (ptid));
- err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th);
+ /* Access an lwp we know is stopped. */
+ 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);
-
- gdb_assert (thread_info && thread_info->private->ti_valid);
+ 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;
- return ptid_build (GET_PID (ptid), GET_LWP (ptid),
- thread_info->private->ti.ti_tid);
-}
-
-static ptid_t
-lwp_from_thread (ptid_t ptid)
-{
- return BUILD_LWP (GET_LWP (ptid), GET_PID (ptid));
+ gdb_assert (ptid_get_tid (ptid) == 0);
+ return ptid;
}
\f
-void
-thread_db_init (struct target_ops *target)
-{
- target_beneath = target;
-}
-
-static void *
-verbose_dlsym (void *handle, const char *name)
+/* Attach to lwp PTID, doing whatever else is required to have this
+ LWP under the debugger's control --- e.g., enabling event
+ reporting. Returns true on success. */
+int
+thread_db_attach_lwp (ptid_t ptid)
{
- void *sym = dlsym (handle, name);
- if (sym == NULL)
- warning (_("Symbol \"%s\" not found in libthread_db: %s"), name, dlerror ());
- return sym;
-}
-
-static int
-thread_db_load (void)
-{
- void *handle;
+ td_thrhandle_t th;
+ td_thrinfo_t ti;
td_err_e err;
+ struct thread_db_info *info;
- handle = dlopen (LIBTHREAD_DB_SO, RTLD_NOW);
- if (handle == NULL)
- {
- fprintf_filtered (gdb_stderr, "\n\ndlopen failed on '%s' - %s\n",
- LIBTHREAD_DB_SO, dlerror ());
- fprintf_filtered (gdb_stderr,
- "GDB will not be able to debug pthreads.\n\n");
- return 0;
- }
+ info = get_thread_db_info (GET_PID (ptid));
- /* 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)
- return 0;
-
- td_ta_new_p = verbose_dlsym (handle, "td_ta_new");
- if (td_ta_new_p == NULL)
- return 0;
-
- td_ta_map_id2thr_p = verbose_dlsym (handle, "td_ta_map_id2thr");
- if (td_ta_map_id2thr_p == NULL)
+ if (info == NULL)
return 0;
- td_ta_map_lwp2thr_p = verbose_dlsym (handle, "td_ta_map_lwp2thr");
- if (td_ta_map_lwp2thr_p == NULL)
- return 0;
+ /* This ptid comes from linux-nat.c, which should always fill in the
+ LWP. */
+ gdb_assert (GET_LWP (ptid) != 0);
- td_ta_thr_iter_p = verbose_dlsym (handle, "td_ta_thr_iter");
- if (td_ta_thr_iter_p == NULL)
- return 0;
+ /* Access an lwp we know is stopped. */
+ info->proc_handle.ptid = ptid;
- td_thr_validate_p = verbose_dlsym (handle, "td_thr_validate");
- if (td_thr_validate_p == NULL)
- return 0;
+ /* 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);
- td_thr_get_info_p = verbose_dlsym (handle, "td_thr_get_info");
- if (td_thr_get_info_p == NULL)
+ 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;
- /* Initialize the library. */
- err = td_init_p ();
+ err = info->td_thr_get_info_p (&th, &ti);
if (err != TD_OK)
{
- warning (_("Cannot initialize libthread_db: %s"), thread_db_err_str (err));
+ warning (_("Cannot get thread info: %s"), thread_db_err_str (err));
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");
-
+ attach_thread (ptid, &th, &ti);
return 1;
}
+static void *
+verbose_dlsym (void *handle, const char *name)
+{
+ void *sym = dlsym (handle, name);
+ if (sym == NULL)
+ warning (_("Symbol \"%s\" not found in libthread_db: %s"), name, dlerror ());
+ return sym;
+}
+
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. */
+ 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,
+ or when it refuses to work with the current inferior (e.g. due to
+ version mismatch between libthread_db and libpthread). */
+
+static int
+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. */
+
+ info->td_init_p = verbose_dlsym (info->handle, "td_init");
+ if (info->td_init_p == NULL)
+ return 0;
+
+ err = info->td_init_p ();
+ if (err != TD_OK)
+ {
+ warning (_("Cannot initialize libthread_db: %s"), thread_db_err_str (err));
+ return 0;
+ }
+
+ 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. */
+ info->proc_handle.ptid = inferior_ptid;
+
+ /* Now attempt to open a connection to the thread library. */
+ err = info->td_ta_new_p (&info->proc_handle, &info->thread_agent);
+ if (err != TD_OK)
+ {
+ if (info_verbose)
+ printf_unfiltered (_("td_ta_new failed: %s\n"),
+ thread_db_err_str (err));
+ else
+ switch (err)
+ {
+ case TD_NOLIBTHREAD:
+#ifdef THREAD_DB_HAS_TD_VERSION
+ case TD_VERSION:
+#endif
+ /* The errors above are not unexpected and silently ignored:
+ they just mean we haven't found correct version of
+ libthread_db yet. */
+ break;
+ default:
+ warning (_("td_ta_new failed: %s"), thread_db_err_str (err));
+ }
+ return 0;
+ }
+
+ info->td_ta_map_id2thr_p = verbose_dlsym (info->handle, "td_ta_map_id2thr");
+ if (info->td_ta_map_id2thr_p == NULL)
+ return 0;
+
+ info->td_ta_map_lwp2thr_p = verbose_dlsym (info->handle, "td_ta_map_lwp2thr");
+ if (info->td_ta_map_lwp2thr_p == NULL)
+ return 0;
+
+ info->td_ta_thr_iter_p = verbose_dlsym (info->handle, "td_ta_thr_iter");
+ if (info->td_ta_thr_iter_p == NULL)
+ return 0;
+
+ info->td_thr_validate_p = verbose_dlsym (info->handle, "td_thr_validate");
+ if (info->td_thr_validate_p == NULL)
+ return 0;
+
+ 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. */
+ 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"));
+
+ if (info_verbose || *libthread_db_search_path)
+ {
+ const char *library;
+
+ library = dladdr_to_soname (*info->td_ta_new_p);
+ if (library == NULL)
+ library = LIBTHREAD_DB_SO;
+
+ printf_unfiltered (_("Using host libthread_db library \"%s\".\n"),
+ library);
+ }
+
+ /* 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);
+
+ 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,
+ relative, or just LIBTHREAD_DB. */
+
+static int
+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"),
+ library);
+ handle = dlopen (library, RTLD_NOW);
+ if (handle == NULL)
+ {
+ if (info_verbose)
+ printf_unfiltered (_("dlopen failed: %s.\n"), dlerror ());
+ return 0;
+ }
+
+ if (info_verbose && strchr (library, '/') == NULL)
+ {
+ void *td_init;
+
+ td_init = dlsym (handle, "td_init");
+ if (td_init != NULL)
+ {
+ const char *const libpath = dladdr_to_soname (td_init);
+
+ if (libpath != NULL)
+ printf_unfiltered (_("Host %s resolved to: %s.\n"),
+ library, libpath);
+ }
+ }
+
+ info = add_thread_db_info (handle);
+
+ if (try_thread_db_load_1 (info))
+ return 1;
+
+ /* This library "refused" to work on current inferior. */
+ delete_thread_db_info (GET_PID (inferior_ptid));
+ return 0;
+}
+
+
+/* Search libthread_db_search_path for libthread_db which "agrees"
+ to work on current inferior. */
+
+static int
+thread_db_load_search (void)
+{
+ char path[PATH_MAX];
+ const char *search_path = libthread_db_search_path;
+ int rc = 0;
+
+ while (*search_path)
+ {
+ const char *end = strchr (search_path, ':');
+ if (end)
+ {
+ size_t len = end - search_path;
+ if (len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path))
+ {
+ char *cp = xmalloc (len + 1);
+ memcpy (cp, search_path, len);
+ cp[len] = '\0';
+ warning (_("libthread_db_search_path component too long,"
+ " ignored: %s."), cp);
+ xfree (cp);
+ search_path += len + 1;
+ continue;
+ }
+ memcpy (path, search_path, len);
+ path[len] = '\0';
+ search_path += len + 1;
+ }
+ else
+ {
+ size_t len = strlen (search_path);
+
+ if (len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path))
+ {
+ warning (_("libthread_db_search_path component too long,"
+ " ignored: %s."), search_path);
+ break;
+ }
+ memcpy (path, search_path, len + 1);
+ search_path += len;
+ }
+ strcat (path, "/");
+ strcat (path, LIBTHREAD_DB_SO);
+ if (try_thread_db_load (path))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ if (rc == 0)
+ rc = try_thread_db_load (LIBTHREAD_DB_SO);
+ return rc;
+}
+
+/* Attempt to load and initialize libthread_db.
+ Return 1 on success.
+ */
+
+static int
+thread_db_load (void)
+{
+ struct objfile *obj;
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (inferior_ptid));
+
+ if (info != NULL)
+ return 1;
+
+ /* Don't attempt to use thread_db on targets which can not run
+ (executables not running yet, core files) for now. */
+ if (!target_has_execution)
+ return 0;
+
+ /* Don't attempt to use thread_db for remote targets. */
+ if (!target_can_run (¤t_target))
+ return 0;
+
+ if (thread_db_load_search ())
+ return 1;
+
+ /* None of the libthread_db's on our search path, not the system default
+ ones worked. If the executable is dynamically linked against
+ libpthread, try loading libthread_db from the same directory. */
+
+ ALL_OBJFILES (obj)
+ if (libpthread_name_p (obj->name))
+ {
+ char path[PATH_MAX], *cp;
+
+ gdb_assert (strlen (obj->name) < sizeof (path));
+ strcpy (path, obj->name);
+ cp = strrchr (path, '/');
+
+ if (cp == NULL)
+ {
+ warning (_("Expected absolute pathname for libpthread in the"
+ " inferior, but got %s."), path);
+ }
+ else if (cp + 1 + strlen (LIBTHREAD_DB_SO) + 1 > path + sizeof (path))
+ {
+ warning (_("Unexpected: path to libpthread in the inferior is"
+ " too long: %s"), path);
+ }
+ else
+ {
+ strcpy (cp + 1, LIBTHREAD_DB_SO);
+ if (try_thread_db_load (path))
+ return 1;
+ }
+ warning (_("Unable to find libthread_db matching inferior's thread"
+ " library, thread debugging will not be available."));
+ return 0;
+ }
+ /* Either this executable isn't using libpthread at all, or it is
+ statically linked. Since we can't easily distinguish these two cases,
+ no warning is issued. */
+ return 0;
+}
+
static void
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
check_for_thread_db (void)
{
td_err_e err;
- static int already_loaded;
+ static void *last_loaded;
/* Do nothing if we couldn't load libthread_db.so.1. */
- if (td_ta_new_p == NULL)
- 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. */
-
- if (!already_loaded)
- {
- Dl_info info;
- const char *library = NULL;
- if (dladdr ((*td_ta_new_p), &info) != 0)
- library = info.dli_fname;
-
- /* Try dlinfo? */
-
- if (library == NULL)
- /* Paranoid - don't let a NULL path slip through. */
- library = LIBTHREAD_DB_SO;
-
- if (info_verbose)
- printf_unfiltered (_("Using host libthread_db library \"%s\".\n"),
- library);
- already_loaded = 1;
- }
-
- if (using_thread_db)
- /* Nothing to do. The thread library was already detected and the
- target vector was already activated. */
- return;
-
- /* Don't attempt to use thread_db on targets which can not run
- (executables not running yet, core files) for now. */
- if (!target_has_execution)
- return;
-
- /* Don't attempt to use thread_db for remote targets. */
- if (!target_can_run (¤t_target))
+ if (!thread_db_load ())
return;
-
- /* Initialize the structure that identifies the child process. */
- proc_handle.pid = GET_PID (inferior_ptid);
-
- /* Now attempt to open a connection to the thread library. */
- err = td_ta_new_p (&proc_handle, &thread_agent);
- switch (err)
- {
- case TD_NOLIBTHREAD:
- /* No thread library was detected. */
- break;
-
- case TD_OK:
- printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n"));
-
- /* The thread library was detected. Activate the thread_db target. */
- push_target (&thread_db_ops);
- using_thread_db = 1;
-
- enable_thread_event_reporting ();
- thread_db_find_new_threads ();
- break;
-
- default:
- warning (_("Cannot initialize thread debugging library: %s"),
- thread_db_err_str (err));
- break;
- }
}
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 ();
}
attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
const td_thrinfo_t *ti_p)
{
- struct thread_info *tp;
+ 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->dying)
- return;
+ /* If tp->private 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->private != NULL)
+ {
+ if (!tp->private->dying)
+ return;
- delete_thread (ptid);
+ delete_thread (ptid);
+ tp = NULL;
+ }
}
check_thread_signals ();
return; /* A zombie thread -- do not attach. */
/* Under GNU/Linux, we have to attach to each and every thread. */
- if (lin_lwp_attach_lwp (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid))) < 0)
+ if (tp == NULL
+ && lin_lwp_attach_lwp (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid))) < 0)
return;
+ /* Construct the thread's private data. */
+ private = xmalloc (sizeof (struct private_thread_info));
+ memset (private, 0, sizeof (struct private_thread_info));
+
+ /* A thread ID of zero may mean the thread library has not initialized
+ yet. But we shouldn't even get here if that's the case. FIXME:
+ if we change GDB to always have at least one thread in the thread
+ list this will have to go somewhere else; maybe private == NULL
+ until the thread_db target claims it. */
+ gdb_assert (ti_p->ti_tid != 0);
+ private->th = *th_p;
+ private->tid = ti_p->ti_tid;
+
/* Add the thread to GDB's thread list. */
- tp = add_thread (ptid);
- tp->private = xmalloc (sizeof (struct private_thread_info));
- memset (tp->private, 0, sizeof (struct private_thread_info));
+ if (tp == NULL)
+ tp = add_thread_with_info (ptid, private);
+ 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));
}
static void
-detach_thread (ptid_t ptid, int verbose)
+detach_thread (ptid_t ptid)
{
struct thread_info *thread_info;
- if (verbose)
- printf_unfiltered (_("[%s exited]\n"), target_pid_to_str (ptid));
-
/* 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. */
- thread_info = find_thread_pid (ptid);
- gdb_assert (thread_info != NULL);
+ 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->private != NULL);
thread_info->private->dying = 1;
}
static void
-thread_db_detach (char *args, int from_tty)
-{
- disable_thread_event_reporting ();
-
- /* There's no need to save & restore inferior_ptid here, since the
- inferior is not supposed to survive this function call. */
- inferior_ptid = lwp_from_thread (inferior_ptid);
-
- target_beneath->to_detach (args, from_tty);
-
- /* Should this be done by detach_command? */
- target_mourn_inferior ();
-}
-
-static int
-clear_lwpid_callback (struct thread_info *thread, void *dummy)
+thread_db_detach (struct target_ops *ops, char *args, int from_tty)
{
- /* If we know that our thread implementation is 1-to-1, we could save
- a certain amount of information; it's not clear how much, so we
- are always conservative. */
+ struct target_ops *target_beneath = find_target_beneath (ops);
+ struct thread_db_info *info;
- thread->private->th_valid = 0;
- thread->private->ti_valid = 0;
+ info = get_thread_db_info (GET_PID (inferior_ptid));
- return 0;
-}
+ if (info)
+ {
+ disable_thread_event_reporting ();
-static void
-thread_db_resume (ptid_t ptid, int step, enum target_signal signo)
-{
- struct cleanup *old_chain = save_inferior_ptid ();
+ /* 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 ();
- if (GET_PID (ptid) == -1)
- inferior_ptid = lwp_from_thread (inferior_ptid);
- else if (is_thread (ptid))
- ptid = lwp_from_thread (ptid);
+ delete_thread_db_info (GET_PID (inferior_ptid));
+ }
- /* Clear cached data which may not be valid after the resume. */
- iterate_over_threads (clear_lwpid_callback, NULL);
+ target_beneath->to_detach (target_beneath, args, from_tty);
- target_beneath->to_resume (ptid, step, signo);
+ /* NOTE: From this point on, inferior_ptid is null_ptid. */
- do_cleanups (old_chain);
+ /* 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
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 (GET_PID (ptid));
/* Bail out early if we're not at a thread event breakpoint. */
- stop_pc = read_pc_pid (ptid) - gdbarch_decr_pc_after_break (current_gdbarch);
- if (stop_pc != td_create_bp_addr && stop_pc != td_death_bp_addr)
+ stop_pc = regcache_read_pc (regcache)
+ - 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
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));
- ptid = ptid_build (GET_PID (ptid), ti.ti_lid, ti.ti_tid);
+ ptid = ptid_build (GET_PID (ptid), ti.ti_lid, 0);
switch (msg.event)
{
if (!in_thread_list (ptid))
error (_("Spurious thread death event."));
- detach_thread (ptid, print_thread_events);
+ detach_thread (ptid);
break;
}
static ptid_t
-thread_db_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
+thread_db_wait (struct target_ops *ops,
+ ptid_t ptid, struct target_waitstatus *ourstatus,
+ int options)
{
- extern ptid_t trap_ptid;
+ struct thread_db_info *info;
+ struct target_ops *beneath = find_target_beneath (ops);
- if (GET_PID (ptid) != -1 && is_thread (ptid))
- ptid = lwp_from_thread (ptid);
+ ptid = beneath->to_wait (beneath, ptid, ourstatus, options);
- ptid = target_beneath->to_wait (ptid, ourstatus);
+ if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
+ return ptid;
if (ourstatus->kind == TARGET_WAITKIND_EXITED
- || ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
- return pid_to_ptid (-1);
+ || 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);
- using_thread_db = 0;
- return pid_to_ptid (GET_PID (ptid));
+ /* 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 ();
+ 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
event gets postponed by other simultaneous events. In such a
case, we want to just ignore the event and continue on. */
- if (!ptid_equal (trap_ptid, null_ptid))
- trap_ptid = thread_from_lwp (trap_ptid);
-
ptid = thread_from_lwp (ptid);
if (GET_PID (ptid) == -1)
ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
}
static void
-thread_db_kill (void)
+thread_db_mourn_inferior (struct target_ops *ops)
{
- /* There's no need to save & restore inferior_ptid here, since the
- inferior isn't supposed to survive this function call. */
- inferior_ptid = lwp_from_thread (inferior_ptid);
- target_beneath->to_kill ();
-}
+ struct target_ops *target_beneath = find_target_beneath (ops);
-static void
-thread_db_mourn_inferior (void)
-{
- /* Forget about the child's process ID. We shouldn't need it
- anymore. */
- proc_handle.pid = 0;
-
- target_beneath->to_mourn_inferior ();
+ delete_thread_db_info (GET_PID (inferior_ptid));
- /* 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 (&thread_db_ops);
- using_thread_db = 0;
+ if (!thread_db_list)
+ unpush_target (ops);
}
static int
td_thrinfo_t ti;
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 (inferior_ptid), ti.ti_lid, ti.ti_tid);
-
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;
}
- if (!in_thread_list (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);
return 0;
}
+/* Search for new threads, accessing memory through stopped thread
+ PTID. */
+
static void
-thread_db_find_new_threads (void)
+thread_db_find_new_threads_1 (ptid_t ptid)
{
td_err_e err;
+ struct lwp_info *lp;
+ 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 && 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. */
+ 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)
+{
+ 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 (ptid_t ptid)
+thread_db_pid_to_str (struct target_ops *ops, ptid_t ptid)
{
- if (is_thread (ptid))
+ struct thread_info *thread_info = find_thread_ptid (ptid);
+ struct target_ops *beneath;
+
+ if (thread_info != NULL && thread_info->private != NULL)
{
static char buf[64];
- struct thread_info *thread_info;
+ thread_t tid;
- thread_info = find_thread_pid (ptid);
- if (thread_info == NULL)
- snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld) (Missing)",
- GET_THREAD (ptid), GET_LWP (ptid));
- else
- snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld)",
- GET_THREAD (ptid), GET_LWP (ptid));
+ tid = thread_info->private->tid;
+ snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld)",
+ tid, GET_LWP (ptid));
return buf;
}
- if (target_beneath->to_pid_to_str (ptid))
- return target_beneath->to_pid_to_str (ptid);
+ beneath = find_target_beneath (ops);
+ if (beneath->to_pid_to_str (beneath, ptid))
+ return beneath->to_pid_to_str (beneath, ptid);
return normal_pid_to_str (ptid);
}
static char *
thread_db_extra_thread_info (struct thread_info *info)
{
+ if (info->private == NULL)
+ return NULL;
+
if (info->private->dying)
return "Exiting";
return NULL;
}
-/* Return 1 if this thread has the same LWP as the passed PTID. */
-
-static int
-same_ptid_callback (struct thread_info *thread, void *arg)
-{
- ptid_t *ptid_p = arg;
-
- return GET_LWP (thread->ptid) == GET_LWP (*ptid_p);
-}
-
/* Get the address of the thread local variable in load module LM which
is stored at OFFSET within the thread local storage for thread PTID. */
static CORE_ADDR
-thread_db_get_thread_local_address (ptid_t ptid,
+thread_db_get_thread_local_address (struct target_ops *ops,
+ ptid_t ptid,
CORE_ADDR lm,
CORE_ADDR offset)
{
- /* If we have not discovered any threads yet, check now. */
- if (!is_thread (ptid) && !have_threads ())
- thread_db_find_new_threads ();
+ struct thread_info *thread_info;
+ struct target_ops *beneath;
- /* Try to find a matching thread if we still have the LWP ID instead
- of the thread ID. */
- if (!is_thread (ptid))
- {
- struct thread_info *thread;
+ /* If we have not discovered any threads yet, check now. */
+ if (!have_threads (ptid))
+ thread_db_find_new_threads_1 (ptid);
- thread = iterate_over_threads (same_ptid_callback, &ptid);
- if (thread != NULL)
- ptid = thread->ptid;
- }
+ /* Find the matching thread. */
+ thread_info = find_thread_ptid (ptid);
- if (is_thread (ptid))
+ if (thread_info != NULL && thread_info->private != NULL)
{
td_err_e err;
void *address;
- struct thread_info *thread_info;
+ 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"));
/* Caller should have verified that lm != 0. */
gdb_assert (lm != 0);
- /* Get info about the thread. */
- thread_info = find_thread_pid (ptid);
- gdb_assert (thread_info);
- thread_db_map_id2thr (thread_info, 1);
-
/* 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. */
: (CORE_ADDR) (uintptr_t) address);
}
- if (target_beneath->to_get_thread_local_address)
- return target_beneath->to_get_thread_local_address (ptid, lm, offset);
+ beneath = find_target_beneath (ops);
+ if (beneath->to_get_thread_local_address)
+ return beneath->to_get_thread_local_address (beneath, ptid, lm, offset);
else
throw_error (TLS_GENERIC_ERROR,
_("TLS not supported on this target"));
}
+/* Callback routine used to find a thread based on the TID part of
+ its PTID. */
+
+static int
+thread_db_find_thread_from_tid (struct thread_info *thread, void *data)
+{
+ long *tid = (long *) data;
+
+ if (thread->private->tid == *tid)
+ return 1;
+
+ return 0;
+}
+
+/* Implement the to_get_ada_task_ptid target method for this target. */
+
+static ptid_t
+thread_db_get_ada_task_ptid (long lwp, long thread)
+{
+ struct thread_info *thread_info;
+
+ 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_longname = "multi-threaded child process.";
thread_db_ops.to_doc = "Threads and pthreads support.";
thread_db_ops.to_detach = thread_db_detach;
- thread_db_ops.to_resume = thread_db_resume;
thread_db_ops.to_wait = thread_db_wait;
- thread_db_ops.to_kill = thread_db_kill;
+ 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;
thread_db_ops.to_get_thread_local_address
= thread_db_get_thread_local_address;
thread_db_ops.to_extra_thread_info = thread_db_extra_thread_info;
+ thread_db_ops.to_get_ada_task_ptid = thread_db_get_ada_task_ptid;
thread_db_ops.to_magic = OPS_MAGIC;
}
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_thread_db;
+
void
_initialize_thread_db (void)
{
- /* Only initialize the module if we can load libthread_db. */
- if (thread_db_load ())
- {
- init_thread_db_ops ();
- add_target (&thread_db_ops);
-
- /* Add ourselves to objfile event chain. */
- observer_attach_new_objfile (thread_db_new_objfile);
- }
+ init_thread_db_ops ();
+ add_target (&thread_db_ops);
+
+ /* Defer loading of libthread_db.so until inferior is running.
+ This allows gdb to load correct libthread_db for a given
+ executable -- there could be mutiple versions of glibc,
+ compiled with LinuxThreads or NPTL, and until there is
+ a running inferior, we can't tell which libthread_db is
+ the correct one to load. */
+
+ libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH);
+
+ add_setshow_optional_filename_cmd ("libthread-db-search-path",
+ class_support,
+ &libthread_db_search_path, _("\
+Set search path for libthread_db."), _("\
+Show the current search path or libthread_db."), _("\
+This path is used to search for libthread_db to be loaded into \
+gdb itself."),
+ NULL,
+ NULL,
+ &setlist, &showlist);
+ /* Add ourselves to objfile event chain. */
+ observer_attach_new_objfile (thread_db_new_objfile);
}