diff options
author | Paul Pluzhnikov <ppluzhnikov@google.com> | 2009-10-09 00:31:01 +0000 |
---|---|---|
committer | Paul Pluzhnikov <ppluzhnikov@google.com> | 2009-10-09 00:31:01 +0000 |
commit | cdbfd4198ec38a42766a578d4058bd752d25011c (patch) | |
tree | f0495b9a586fdff1e5205ec37e0c3748c9cb9c21 /gdb/gdbserver/thread-db.c | |
parent | 37e124c9c30152b6833198a8c283c533c7b1fcce (diff) | |
download | gdb-cdbfd4198ec38a42766a578d4058bd752d25011c.zip gdb-cdbfd4198ec38a42766a578d4058bd752d25011c.tar.gz gdb-cdbfd4198ec38a42766a578d4058bd752d25011c.tar.bz2 |
doc/
2009-10-08 Paul Pluzhnikov <ppluzhnikov@google.com>
* gdb.texinfo (Server): Document libthread-db-search-path.
gdbserver/
2009-10-08 Paul Pluzhnikov <ppluzhnikov@google.com>
* acinclude.m4: (SRV_CHECK_THREAD_DB, SRV_CHECK_TLS_GET_ADDR): Remove.
* configure.ac: Adjust.
* linux-low.h (struct process_info_private): Move members to struct
thread_db.
(thread_db_free, thread_db_handle_monitor_command): New prototype.
* linux-low.c (linux_remove_process): Adjust.
(linux_wait_for_event_1, linux_look_up_symbols): Likewise.
* server.c (handle_query): Move code ...
(handle_monitor_command): ... here. New function.
* target.h (struct target_ops): New member.
* thread-db.c (struct thread_db): New.
(libthread_db_search_path): New variable.
(thread_db_create_event, thread_db_enable_reporting)
(find_one_thread, maybe_attach_thread, find_new_threads_callback)
(thread_db_find_new_threads, (thread_db_get_tls_address): Adjust.
(try_thread_db_load_1, dladdr_to_soname): New functions.
(try_thread_db_load, thread_db_load_search): New functions.
(thread_db_init): Search for libthread_db.
(thread_db_free): New function.
(thread_db_handle_monitor_command): Likewise.
* config.in: Regenerate.
* configure: Regenerate.
Diffstat (limited to 'gdb/gdbserver/thread-db.c')
-rw-r--r-- | gdb/gdbserver/thread-db.c | 395 |
1 files changed, 317 insertions, 78 deletions
diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index 4a26be9..4d1008f 100644 --- a/gdb/gdbserver/thread-db.c +++ b/gdb/gdbserver/thread-db.c @@ -27,13 +27,51 @@ extern int debug_threads; 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" +#include <dlfcn.h> #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; + + /* Handle of the libthread_db from dlopen. */ + void *handle; + + /* 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, + void *map_address, + size_t offset, void **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); @@ -130,7 +168,10 @@ thread_db_create_event (CORE_ADDR where) td_event_msg_t msg; td_err_e err; struct lwp_info *lwp; - struct process_info_private *proc = current_process()->private; + 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"); @@ -139,7 +180,7 @@ thread_db_create_event (CORE_ADDR where) 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 (proc->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)); @@ -158,36 +199,25 @@ thread_db_create_event (CORE_ADDR where) 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 () { td_thr_events_t events; td_notify_t notify; td_err_e err; - struct process_info_private *proc = current_process()->private; + 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 (proc->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", @@ -196,7 +226,8 @@ thread_db_enable_reporting () } /* Get address for thread creation breakpoint. */ - err = td_ta_event_addr (proc->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", @@ -206,22 +237,6 @@ thread_db_enable_reporting () 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 (proc->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 - return 1; } @@ -233,7 +248,7 @@ find_one_thread (ptid_t ptid) td_err_e err; struct thread_info *inferior; struct lwp_info *lwp; - struct process_info_private *proc; + 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, ptid); @@ -242,13 +257,12 @@ find_one_thread (ptid_t ptid) return 1; /* Get information about this thread. */ - proc = get_thread_process (inferior)->private; - err = td_ta_map_lwp2thr (proc->thread_agent, 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)); @@ -266,7 +280,7 @@ find_one_thread (ptid_t ptid) 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)); @@ -310,7 +324,8 @@ maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) if (thread_db_use_events) { - err = td_thr_event_enable (th_p, 1); + 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)); @@ -322,8 +337,9 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data) { 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)); @@ -341,7 +357,7 @@ thread_db_find_new_threads (void) { td_err_e err; ptid_t ptid = ((struct inferior_list_entry *) current_inferior)->id; - struct process_info_private *proc = current_process()->private; + struct thread_db *thread_db = current_process ()->private->thread_db; /* This function is only called when we first initialize thread_db. First locate the initial thread. If it is not ready for @@ -350,10 +366,10 @@ thread_db_find_new_threads (void) return; /* Iterate over all user-space threads to discover new threads. */ - err = td_ta_thr_iter (proc->thread_agent, - find_new_threads_callback, NULL, - TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, - TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); + err = thread_db->td_ta_thr_iter_p (thread_db->thread_agent, + find_new_threads_callback, NULL, + 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)); } @@ -366,10 +382,11 @@ thread_db_find_new_threads (void) 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++) + for (sym_list = thread_db->td_symbol_list_p (); *sym_list; sym_list++) look_up_one_symbol (*sym_list, &unused); } @@ -377,16 +394,23 @@ 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 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 (!get_thread_process (thread)->all_symbols_looked_up) + if (!proc->all_symbols_looked_up) return TD_ERR; + if (thread_db->td_thr_tls_get_addr_p == NULL) + return -1; + lwp = get_thread_lwp (thread); if (!lwp->thread_known) find_one_thread (lwp->head.id); @@ -398,8 +422,9 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, /* 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 (&lwp->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) { @@ -408,17 +433,190 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, } else return err; -#else - return -1; -#endif +} + +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.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) \ + 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)); + 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 + + proc->private->thread_db = xmalloc (sizeof (tdb)); + memcpy (proc->private->thread_db, &tdb, sizeof (tdb)); + + return 1; +} + +/* 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; +} + +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; + } + + 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); + } + } + + 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; } int thread_db_init (int use_events) { - int err; struct process_info *proc = current_process (); - struct process_info_private *priv = proc->private; /* FIXME drow/2004-10-16: This is the "overall process ID", which GNU/Linux calls tgid, "thread group ID". When we support @@ -433,27 +631,68 @@ thread_db_init (int use_events) thread_db_use_events = use_events; - err = td_ta_new (&priv->proc_handle, &priv->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_free (proc); + return 0; + } thread_db_find_new_threads (); thread_db_look_up_symbols (); proc->all_symbols_looked_up = 1; return 1; + } - default: - warning ("error initializing thread_db library: %s", - thread_db_err_str (err)); + return 0; +} + +/* Disconnect from libthread_db and free resources. */ + +void +thread_db_free (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 *); + + td_ta_delete_p = dlsym (thread_db->handle, "td_ta_delete"); + if (td_ta_delete_p != NULL) + (*td_ta_delete_p) (thread_db->thread_agent); + + dlclose (thread_db->handle); + 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; } |