/* Thread management interface, for the remote server for GDB.
- Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009
+ Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
Contributed by MontaVista Software.
static int thread_db_use_events;
-#ifdef HAVE_THREAD_DB_H
-#include <thread_db.h>
-#endif
-
#include "gdb_proc_service.h"
+#include "../gdb_thread_db.h"
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+#include <dlfcn.h>
+#endif
#include <stdint.h>
+#include <limits.h>
+#include <ctype.h>
+
+struct thread_db
+{
+ /* 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;
-/* Structure that identifies the child process for the
- <proc_service.h> interface. */
-static struct ps_prochandle proc_handle;
+ /* If this flag has been set, we've already asked GDB for all
+ symbols we might need; assume symbol cache misses are
+ failures. */
+ int all_symbols_looked_up;
-/* Connection to the libthread_db library. */
-static td_thragent_t *thread_agent;
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+ /* Handle of the libthread_db from dlopen. */
+ void *handle;
+#endif
-static int find_one_thread (int);
+ /* 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. Note that if the
+ running kernel supports tracing clones, then we don't need to use
+ (and in fact don't use) this magic thread event breakpoint to
+ learn about threads. */
+ struct breakpoint *td_create_bp;
+
+ /* Addresses of libthread_db functions. */
+ td_err_e (*td_ta_new_p) (struct ps_prochandle * ps, td_thragent_t **ta);
+ td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta,
+ td_event_msg_t *msg);
+ td_err_e (*td_ta_set_event_p) (const td_thragent_t *ta,
+ td_thr_events_t *event);
+ 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_map_lwp2thr_p) (const td_thragent_t *ta, lwpid_t lwpid,
+ 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_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_thr_tls_get_addr_p) (const td_thrhandle_t *th,
+ psaddr_t map_address,
+ size_t offset, psaddr_t *address);
+ const char ** (*td_symbol_list_p) (void);
+};
+
+static char *libthread_db_search_path;
+
+static int find_one_thread (ptid_t);
static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data);
static const char *
{
td_event_msg_t msg;
td_err_e err;
- struct process_info *process;
+ struct lwp_info *lwp;
+ struct thread_db *thread_db = current_process ()->private->thread_db;
+
+ if (thread_db->td_ta_event_getmsg_p == NULL)
+ fatal ("unexpected thread_db->td_ta_event_getmsg_p == NULL");
if (debug_threads)
fprintf (stderr, "Thread creation event.\n");
In the LinuxThreads implementation, this is safe,
because all events come from the manager thread
(except for its own creation, of course). */
- err = td_ta_event_getmsg (thread_agent, &msg);
+ err = thread_db->td_ta_event_getmsg_p (thread_db->thread_agent, &msg);
if (err != TD_OK)
fprintf (stderr, "thread getmsg err: %s\n",
thread_db_err_str (err));
/* If we do not know about the main thread yet, this would be a good time to
find it. We need to do this to pick up the main thread before any newly
created threads. */
- process = get_thread_process (current_inferior);
- if (process->thread_known == 0)
- find_one_thread (process->lwpid);
+ lwp = get_thread_lwp (current_inferior);
+ if (lwp->thread_known == 0)
+ find_one_thread (lwp->head.id);
/* msg.event == TD_EVENT_CREATE */
return 0;
}
-#if 0
-static int
-thread_db_death_event (CORE_ADDR where)
-{
- if (debug_threads)
- fprintf (stderr, "Thread death event.\n");
-
- return 0;
-}
-#endif
-
static int
-thread_db_enable_reporting ()
+thread_db_enable_reporting (void)
{
td_thr_events_t events;
td_notify_t notify;
td_err_e err;
+ struct thread_db *thread_db = current_process ()->private->thread_db;
+
+ if (thread_db->td_ta_set_event_p == NULL
+ || thread_db->td_ta_event_addr_p == NULL
+ || thread_db->td_ta_event_getmsg_p == NULL)
+ /* This libthread_db is missing required support. */
+ return 0;
/* Set the process wide mask saying which events we're interested in. */
td_event_emptyset (&events);
td_event_addset (&events, TD_CREATE);
-#if 0
- /* This is reported to be broken in glibc 2.1.3. A different approach
- will be necessary to support that. */
- td_event_addset (&events, TD_DEATH);
-#endif
-
- err = td_ta_set_event (thread_agent, &events);
+ err = thread_db->td_ta_set_event_p (thread_db->thread_agent, &events);
if (err != TD_OK)
{
warning ("Unable to set global thread event mask: %s",
- thread_db_err_str (err));
+ thread_db_err_str (err));
return 0;
}
/* Get address for thread creation breakpoint. */
- err = td_ta_event_addr (thread_agent, TD_CREATE, ¬ify);
+ err = thread_db->td_ta_event_addr_p (thread_db->thread_agent, TD_CREATE,
+ ¬ify);
if (err != TD_OK)
{
warning ("Unable to get location for thread creation breakpoint: %s",
thread_db_err_str (err));
return 0;
}
- set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
- thread_db_create_event);
-
-#if 0
- /* Don't concern ourselves with reported thread deaths, only
- with actual thread deaths (via wait). */
-
- /* Get address for thread death breakpoint. */
- err = td_ta_event_addr (thread_agent, TD_DEATH, ¬ify);
- if (err != TD_OK)
- {
- warning ("Unable to get location for thread death breakpoint: %s",
- thread_db_err_str (err));
- return;
- }
- set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
- thread_db_death_event);
-#endif
+ thread_db->td_create_bp
+ = set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
+ thread_db_create_event);
return 1;
}
static int
-find_one_thread (int lwpid)
+find_one_thread (ptid_t ptid)
{
td_thrhandle_t th;
td_thrinfo_t ti;
td_err_e err;
struct thread_info *inferior;
- struct process_info *process;
+ struct lwp_info *lwp;
+ struct thread_db *thread_db = current_process ()->private->thread_db;
+ int lwpid = ptid_get_lwp (ptid);
- inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid);
- process = get_thread_process (inferior);
- if (process->thread_known)
+ inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid);
+ lwp = get_thread_lwp (inferior);
+ if (lwp->thread_known)
return 1;
/* Get information about this thread. */
- err = td_ta_map_lwp2thr (thread_agent, process->lwpid, &th);
+ err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, &th);
if (err != TD_OK)
error ("Cannot get thread handle for LWP %d: %s",
lwpid, thread_db_err_str (err));
- err = td_thr_get_info (&th, &ti);
+ err = thread_db->td_thr_get_info_p (&th, &ti);
if (err != TD_OK)
error ("Cannot get thread info for LWP %d: %s",
lwpid, thread_db_err_str (err));
fprintf (stderr, "Found thread %ld (LWP %d)\n",
ti.ti_tid, ti.ti_lid);
- if (process->lwpid != ti.ti_lid)
+ if (lwpid != ti.ti_lid)
{
warning ("PID mismatch! Expected %ld, got %ld",
- (long) process->lwpid, (long) ti.ti_lid);
+ (long) lwpid, (long) ti.ti_lid);
return 0;
}
if (thread_db_use_events)
{
- err = td_thr_event_enable (&th, 1);
+ err = thread_db->td_thr_event_enable_p (&th, 1);
if (err != TD_OK)
error ("Cannot enable thread event reporting for %d: %s",
ti.ti_lid, thread_db_err_str (err));
if (ti.ti_tid == 0)
return 0;
- process->thread_known = 1;
- process->th = th;
+ lwp->thread_known = 1;
+ lwp->th = th;
return 1;
}
-static void
-maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
-{
- td_err_e err;
- struct thread_info *inferior;
- struct process_info *process;
+/* Attach a thread. Return true on success. */
- inferior = (struct thread_info *) find_inferior_id (&all_threads,
- ti_p->ti_lid);
- if (inferior != NULL)
- return;
+static int
+attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
+{
+ struct lwp_info *lwp;
if (debug_threads)
fprintf (stderr, "Attaching to thread %ld (LWP %d)\n",
ti_p->ti_tid, ti_p->ti_lid);
linux_attach_lwp (ti_p->ti_lid);
- inferior = (struct thread_info *) find_inferior_id (&all_threads,
- ti_p->ti_lid);
- if (inferior == NULL)
+ lwp = find_lwp_pid (pid_to_ptid (ti_p->ti_lid));
+ if (lwp == NULL)
{
warning ("Could not attach to thread %ld (LWP %d)\n",
ti_p->ti_tid, ti_p->ti_lid);
- return;
+ return 0;
}
- process = inferior_target_data (inferior);
-
- process->thread_known = 1;
- process->th = *th_p;
+ lwp->thread_known = 1;
+ lwp->th = *th_p;
if (thread_db_use_events)
{
- err = td_thr_event_enable (th_p, 1);
+ td_err_e err;
+ struct thread_db *thread_db = current_process ()->private->thread_db;
+
+ err = thread_db->td_thr_event_enable_p (th_p, 1);
if (err != TD_OK)
error ("Cannot enable thread event reporting for %d: %s",
ti_p->ti_lid, thread_db_err_str (err));
}
+
+ return 1;
+}
+
+/* Attach thread if we haven't seen it yet.
+ Increment *COUNTER if we have attached a new thread.
+ Return false on failure. */
+
+static int
+maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p,
+ int *counter)
+{
+ struct lwp_info *lwp;
+
+ lwp = find_lwp_pid (pid_to_ptid (ti_p->ti_lid));
+ if (lwp != NULL)
+ return 1;
+
+ if (!attach_thread (th_p, ti_p))
+ return 0;
+
+ if (counter != NULL)
+ *counter += 1;
+
+ return 1;
}
static int
{
td_thrinfo_t ti;
td_err_e err;
+ struct thread_db *thread_db = current_process ()->private->thread_db;
- err = td_thr_get_info (th_p, &ti);
+ err = thread_db->td_thr_get_info_p (th_p, &ti);
if (err != TD_OK)
error ("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;
- maybe_attach_thread (th_p, &ti);
+ if (!maybe_attach_thread (th_p, &ti, (int *) data))
+ {
+ /* Terminate iteration early: we might be looking at stale data in
+ the inferior. The thread_db_find_new_threads will retry. */
+ return 1;
+ }
return 0;
}
thread_db_find_new_threads (void)
{
td_err_e err;
+ ptid_t ptid = ((struct inferior_list_entry *) current_inferior)->id;
+ struct thread_db *thread_db = current_process ()->private->thread_db;
+ int loop, iteration;
/* This function is only called when we first initialize thread_db.
First locate the initial thread. If it is not ready for
debugging yet, then stop. */
- if (find_one_thread (all_threads.head->id) == 0)
+ if (find_one_thread (ptid) == 0)
return;
- /* Iterate over all user-space threads to discover new threads. */
- err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL,
- TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
- TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+ /* Require 4 successive iterations which do not find any new threads.
+ The 4 is a heuristic: there is an inherent race here, and I have
+ seen that 2 iterations in a row are not always sufficient to
+ "capture" all threads. */
+ for (loop = 0, iteration = 0; loop < 4; ++loop, ++iteration)
+ {
+ int new_thread_count = 0;
+
+ /* Iterate over all user-space threads to discover new threads. */
+ err = thread_db->td_ta_thr_iter_p (thread_db->thread_agent,
+ find_new_threads_callback,
+ &new_thread_count,
+ TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+ TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+ if (debug_threads)
+ fprintf (stderr, "Found %d threads in iteration %d.\n",
+ new_thread_count, iteration);
+
+ if (new_thread_count != 0)
+ {
+ /* Found new threads. Restart iteration from beginning. */
+ loop = -1;
+ }
+ }
if (err != TD_OK)
error ("Cannot find new threads: %s", thread_db_err_str (err));
}
static void
thread_db_look_up_symbols (void)
{
- const char **sym_list = td_symbol_list ();
+ struct thread_db *thread_db = current_process ()->private->thread_db;
+ const char **sym_list;
CORE_ADDR unused;
- for (sym_list = td_symbol_list (); *sym_list; sym_list++)
- look_up_one_symbol (*sym_list, &unused);
+ for (sym_list = thread_db->td_symbol_list_p (); *sym_list; sym_list++)
+ look_up_one_symbol (*sym_list, &unused, 1);
+
+ /* We're not interested in any other libraries loaded after this
+ point, only in symbols in libpthread.so. */
+ thread_db->all_symbols_looked_up = 1;
+}
+
+int
+thread_db_look_up_one_symbol (const char *name, CORE_ADDR *addrp)
+{
+ struct thread_db *thread_db = current_process ()->private->thread_db;
+ int may_ask_gdb = !thread_db->all_symbols_looked_up;
+
+ /* If we've passed the call to thread_db_look_up_symbols, then
+ anything not in the cache must not exist; we're not interested
+ in any libraries loaded after that point, only in symbols in
+ libpthread.so. It might not be an appropriate time to look
+ up a symbol, e.g. while we're trying to fetch registers. */
+ return look_up_one_symbol (name, addrp, may_ask_gdb);
}
int
thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
CORE_ADDR load_module, CORE_ADDR *address)
{
-#if HAVE_TD_THR_TLS_GET_ADDR
psaddr_t addr;
td_err_e err;
- struct process_info *process;
+ struct lwp_info *lwp;
+ struct thread_info *saved_inferior;
+ struct process_info *proc;
+ struct thread_db *thread_db;
+
+ proc = get_thread_process (thread);
+ thread_db = proc->private->thread_db;
+
+ /* If the thread layer is not (yet) initialized, fail. */
+ if (!thread_db->all_symbols_looked_up)
+ return TD_ERR;
+
+ if (thread_db->td_thr_tls_get_addr_p == NULL)
+ return -1;
- process = get_thread_process (thread);
- if (!process->thread_known)
- find_one_thread (process->lwpid);
- if (!process->thread_known)
+ lwp = get_thread_lwp (thread);
+ if (!lwp->thread_known)
+ find_one_thread (lwp->head.id);
+ if (!lwp->thread_known)
return TD_NOTHR;
+ saved_inferior = current_inferior;
+ current_inferior = thread;
/* Note the cast through uintptr_t: this interface only works if
a target address fits in a psaddr_t, which is a host pointer.
So a 32-bit debugger can not access 64-bit TLS through this. */
- err = td_thr_tls_get_addr (&process->th, (psaddr_t) (uintptr_t) load_module,
- offset, &addr);
+ err = thread_db->td_thr_tls_get_addr_p (&lwp->th,
+ (psaddr_t) (uintptr_t) load_module,
+ offset, &addr);
+ current_inferior = saved_inferior;
if (err == TD_OK)
{
*address = (CORE_ADDR) (uintptr_t) addr;
}
else
return err;
+}
+
+#ifdef USE_LIBTHREAD_DB_DIRECTLY
+
+static int
+thread_db_load_search (void)
+{
+ td_err_e err;
+ struct thread_db *tdb;
+ struct process_info *proc = current_process ();
+
+ if (proc->private->thread_db != NULL)
+ fatal ("unexpected: proc->private->thread_db != NULL");
+
+ tdb = xcalloc (1, sizeof (*tdb));
+ proc->private->thread_db = tdb;
+
+ tdb->td_ta_new_p = &td_ta_new;
+
+ /* Attempt to open a connection to the thread library. */
+ err = tdb->td_ta_new_p (&tdb->proc_handle, &tdb->thread_agent);
+ if (err != TD_OK)
+ {
+ if (debug_threads)
+ fprintf (stderr, "td_ta_new(): %s\n", thread_db_err_str (err));
+ free (tdb);
+ proc->private->thread_db = NULL;
+ return 0;
+ }
+
+ tdb->td_ta_map_lwp2thr_p = &td_ta_map_lwp2thr;
+ tdb->td_thr_get_info_p = &td_thr_get_info;
+ tdb->td_ta_thr_iter_p = &td_ta_thr_iter;
+ tdb->td_symbol_list_p = &td_symbol_list;
+
+ /* This is required only when thread_db_use_events is on. */
+ tdb->td_thr_event_enable_p = &td_thr_event_enable;
+
+ /* These are not essential. */
+ tdb->td_ta_event_addr_p = &td_ta_event_addr;
+ tdb->td_ta_set_event_p = &td_ta_set_event;
+ tdb->td_ta_event_getmsg_p = &td_ta_event_getmsg;
+ tdb->td_thr_tls_get_addr_p = &td_thr_tls_get_addr;
+
+ return 1;
+}
+
#else
- return -1;
+
+static int
+try_thread_db_load_1 (void *handle)
+{
+ td_err_e err;
+ struct thread_db *tdb;
+ struct process_info *proc = current_process ();
+
+ if (proc->private->thread_db != NULL)
+ fatal ("unexpected: proc->private->thread_db != NULL");
+
+ tdb = xcalloc (1, sizeof (*tdb));
+ proc->private->thread_db = tdb;
+
+ tdb->handle = handle;
+
+ /* Initialize pointers to the dynamic library functions we will use.
+ Essential functions first. */
+
+#define CHK(required, a) \
+ do \
+ { \
+ if ((a) == NULL) \
+ { \
+ if (debug_threads) \
+ fprintf (stderr, "dlsym: %s\n", dlerror ()); \
+ if (required) \
+ { \
+ free (tdb); \
+ proc->private->thread_db = NULL; \
+ return 0; \
+ } \
+ } \
+ } \
+ while (0)
+
+ CHK (1, tdb->td_ta_new_p = dlsym (handle, "td_ta_new"));
+
+ /* Attempt to open a connection to the thread library. */
+ err = tdb->td_ta_new_p (&tdb->proc_handle, &tdb->thread_agent);
+ if (err != TD_OK)
+ {
+ if (debug_threads)
+ fprintf (stderr, "td_ta_new(): %s\n", thread_db_err_str (err));
+ free (tdb);
+ proc->private->thread_db = NULL;
+ return 0;
+ }
+
+ CHK (1, tdb->td_ta_map_lwp2thr_p = dlsym (handle, "td_ta_map_lwp2thr"));
+ CHK (1, tdb->td_thr_get_info_p = dlsym (handle, "td_thr_get_info"));
+ CHK (1, tdb->td_ta_thr_iter_p = dlsym (handle, "td_ta_thr_iter"));
+ CHK (1, tdb->td_symbol_list_p = dlsym (handle, "td_symbol_list"));
+
+ /* This is required only when thread_db_use_events is on. */
+ CHK (thread_db_use_events,
+ tdb->td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable"));
+
+ /* These are not essential. */
+ CHK (0, tdb->td_ta_event_addr_p = dlsym (handle, "td_ta_event_addr"));
+ CHK (0, tdb->td_ta_set_event_p = dlsym (handle, "td_ta_set_event"));
+ CHK (0, tdb->td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg"));
+ CHK (0, tdb->td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr"));
+
+#undef CHK
+
+ return 1;
+}
+
+#ifdef HAVE_DLADDR
+
+/* Lookup a library in which given symbol resides.
+ Note: this is looking in the GDBSERVER 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;
+}
+
+#endif
+
+static int
+try_thread_db_load (const char *library)
+{
+ void *handle;
+
+ if (debug_threads)
+ fprintf (stderr, "Trying host libthread_db library: %s.\n",
+ library);
+ handle = dlopen (library, RTLD_NOW);
+ if (handle == NULL)
+ {
+ if (debug_threads)
+ fprintf (stderr, "dlopen failed: %s.\n", dlerror ());
+ return 0;
+ }
+
+#ifdef HAVE_DLADDR
+ if (debug_threads && 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)
+ fprintf (stderr, "Host %s resolved to: %s.\n",
+ library, libpath);
+ }
+ }
#endif
+
+ if (try_thread_db_load_1 (handle))
+ return 1;
+
+ /* This library "refused" to work on current inferior. */
+ dlclose (handle);
+ return 0;
}
+static int
+thread_db_load_search (void)
+{
+ char path[PATH_MAX];
+ const char *search_path;
+ int rc = 0;
+
+ if (libthread_db_search_path == NULL)
+ libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH);
+
+ search_path = libthread_db_search_path;
+ 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);
+ free (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 (debug_threads)
+ fprintf (stderr, "thread_db_load_search trying %s\n", path);
+ if (try_thread_db_load (path))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ if (rc == 0)
+ rc = try_thread_db_load (LIBTHREAD_DB_SO);
+
+ if (debug_threads)
+ fprintf (stderr, "thread_db_load_search returning %d\n", rc);
+ return rc;
+}
+
+#endif /* USE_LIBTHREAD_DB_DIRECTLY */
+
int
thread_db_init (int use_events)
{
- int err;
+ struct process_info *proc = current_process ();
/* FIXME drow/2004-10-16: This is the "overall process ID", which
GNU/Linux calls tgid, "thread group ID". When we support
This isn't the only place in gdbserver that assumes that the first
process in the list is the thread group leader. */
- proc_handle.pid = ((struct inferior_list_entry *)current_inferior)->id;
-
- /* Allow new symbol lookups. */
- all_symbols_looked_up = 0;
thread_db_use_events = use_events;
- err = td_ta_new (&proc_handle, &thread_agent);
- switch (err)
+ if (thread_db_load_search ())
{
- case TD_NOLIBTHREAD:
- /* No thread library was detected. */
- return 0;
-
- case TD_OK:
- /* The thread library was detected. */
-
if (use_events && thread_db_enable_reporting () == 0)
- return 0;
+ {
+ /* Keep trying; maybe event reporting will work later. */
+ thread_db_mourn (proc);
+ return 0;
+ }
thread_db_find_new_threads ();
thread_db_look_up_symbols ();
- all_symbols_looked_up = 1;
return 1;
+ }
- default:
- warning ("error initializing thread_db library: %s",
- thread_db_err_str (err));
+ return 0;
+}
+
+static int
+any_thread_of (struct inferior_list_entry *entry, void *args)
+{
+ int *pid_p = args;
+
+ if (ptid_get_pid (entry->id) == *pid_p)
+ return 1;
+
+ return 0;
+}
+
+static void
+switch_to_process (struct process_info *proc)
+{
+ int pid = pid_of (proc);
+
+ current_inferior =
+ (struct thread_info *) find_inferior (&all_threads,
+ any_thread_of, &pid);
+}
+
+/* Disconnect from libthread_db and free resources. */
+
+static void
+disable_thread_event_reporting (struct process_info *proc)
+{
+ struct thread_db *thread_db = proc->private->thread_db;
+ if (thread_db)
+ {
+ td_err_e (*td_ta_clear_event_p) (const td_thragent_t *ta,
+ td_thr_events_t *event);
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+ td_ta_clear_event_p = dlsym (thread_db->handle, "td_ta_clear_event");
+#else
+ td_ta_clear_event_p = &td_ta_clear_event;
+#endif
+
+ if (td_ta_clear_event_p != NULL)
+ {
+ struct thread_info *saved_inferior = current_inferior;
+ td_thr_events_t events;
+
+ switch_to_process (proc);
+
+ /* Set the process wide mask saying we aren't interested
+ in any events anymore. */
+ td_event_fillset (&events);
+ (*td_ta_clear_event_p) (thread_db->thread_agent, &events);
+
+ current_inferior = saved_inferior;
+ }
+ }
+}
+
+static void
+remove_thread_event_breakpoints (struct process_info *proc)
+{
+ struct thread_db *thread_db = proc->private->thread_db;
+
+ if (thread_db->td_create_bp != NULL)
+ {
+ struct thread_info *saved_inferior = current_inferior;
+
+ switch_to_process (proc);
+
+ delete_breakpoint (thread_db->td_create_bp);
+ thread_db->td_create_bp = NULL;
+
+ current_inferior = saved_inferior;
+ }
+}
+
+void
+thread_db_detach (struct process_info *proc)
+{
+ struct thread_db *thread_db = proc->private->thread_db;
+
+ if (thread_db)
+ {
+ disable_thread_event_reporting (proc);
+ remove_thread_event_breakpoints (proc);
+ }
+}
+
+/* Disconnect from libthread_db and free resources. */
+
+void
+thread_db_mourn (struct process_info *proc)
+{
+ struct thread_db *thread_db = proc->private->thread_db;
+ if (thread_db)
+ {
+ td_err_e (*td_ta_delete_p) (td_thragent_t *);
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+ td_ta_delete_p = dlsym (thread_db->handle, "td_ta_delete");
+#else
+ td_ta_delete_p = &td_ta_delete;
+#endif
+
+ if (td_ta_delete_p != NULL)
+ (*td_ta_delete_p) (thread_db->thread_agent);
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+ dlclose (thread_db->handle);
+#endif /* USE_LIBTHREAD_DB_DIRECTLY */
+
+ free (thread_db);
+ proc->private->thread_db = NULL;
+ }
+}
+
+/* Handle "set libthread-db-search-path" monitor command and return 1.
+ For any other command, return 0. */
+
+int
+thread_db_handle_monitor_command (char *mon)
+{
+ if (strncmp (mon, "set libthread-db-search-path ", 29) == 0)
+ {
+ const char *cp = mon + 29;
+
+ if (libthread_db_search_path != NULL)
+ free (libthread_db_search_path);
+
+ /* Skip leading space (if any). */
+ while (isspace (*cp))
+ ++cp;
+
+ libthread_db_search_path = xstrdup (cp);
+
+ monitor_output ("libthread-db-search-path set to `");
+ monitor_output (libthread_db_search_path);
+ monitor_output ("'\n");
+ return 1;
}
+ /* Tell server.c to perform default processing. */
return 0;
}