From cdbfd4198ec38a42766a578d4058bd752d25011c Mon Sep 17 00:00:00 2001 From: Paul Pluzhnikov Date: Fri, 9 Oct 2009 00:31:01 +0000 Subject: [PATCH] doc/ 2009-10-08 Paul Pluzhnikov * gdb.texinfo (Server): Document libthread-db-search-path. gdbserver/ 2009-10-08 Paul Pluzhnikov * 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. --- gdb/doc/gdb.texinfo | 7 + gdb/gdbserver/ChangeLog | 25 +++ gdb/gdbserver/acinclude.m4 | 60 ------ gdb/gdbserver/config.in | 3 - gdb/gdbserver/configure | 128 +----------- gdb/gdbserver/configure.ac | 16 +- gdb/gdbserver/linux-low.c | 24 ++- gdb/gdbserver/linux-low.h | 20 +- gdb/gdbserver/server.c | 91 +++++---- gdb/gdbserver/target.h | 4 + gdb/gdbserver/thread-db.c | 395 +++++++++++++++++++++++++++++-------- 11 files changed, 431 insertions(+), 342 deletions(-) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 80e9901e717..3f9fd06fa9f 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -14903,6 +14903,13 @@ Disable or enable general debugging messages. Disable or enable specific debugging messages associated with the remote protocol (@pxref{Remote Protocol}). +@item monitor set libthread-db-search-path [PATH] +@cindex gdbserver, search path for @code{libthread_db} +When this command is issued, @var{path} is a colon-separated list of +directories to search for @code{libthread_db} (@pxref{Threads,,set +libthread-db-search-path}). If you omit @var{path}, +@samp{libthread-db-search-path} will be reset to an empty list. + @item monitor exit Tell gdbserver to exit immediately. This command should be followed by @code{disconnect} to close the debugging session. @code{gdbserver} will diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index a85f26b659a..9333c2524e6 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,28 @@ +2009-10-08 Paul Pluzhnikov + + * 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. + 2009-09-27 Ulrich Weigand * spu-low.c (spu_kill): Wait for inferior to terminate. diff --git a/gdb/gdbserver/acinclude.m4 b/gdb/gdbserver/acinclude.m4 index 8770ad07bcd..87d0db5f02e 100644 --- a/gdb/gdbserver/acinclude.m4 +++ b/gdb/gdbserver/acinclude.m4 @@ -7,63 +7,3 @@ sinclude(../../config/override.m4) dnl For ACX_PKGVERSION and ACX_BUGURL. sinclude(../../config/acx.m4) -AC_DEFUN([SRV_CHECK_THREAD_DB], -[AC_CACHE_CHECK([for libthread_db],[srv_cv_thread_db], - [old_LIBS="$LIBS" - LIBS="$LIBS -lthread_db" - AC_TRY_LINK( - [void ps_pglobal_lookup() {} - void ps_pdread() {} - void ps_pdwrite() {} - void ps_lgetregs() {} - void ps_lsetregs() {} - void ps_lgetfpregs() {} - void ps_lsetfpregs() {} - void ps_get_thread_area() {} - void ps_getpid() {}], - [td_ta_new();], - [srv_cv_thread_db="-lthread_db"], - [srv_cv_thread_db=no - - if test "$prefix" = "/usr" || test "$prefix" = "NONE"; then - thread_db="/lib/libthread_db.so.1" - else - thread_db='$prefix/lib/libthread_db.so.1' - fi - LIBS="$old_LIBS `eval echo "$thread_db"`" - AC_TRY_LINK( - [void ps_pglobal_lookup() {} - void ps_pdread() {} - void ps_pdwrite() {} - void ps_lgetregs() {} - void ps_lsetregs() {} - void ps_lgetfpregs() {} - void ps_lsetfpregs() {} - void ps_get_thread_area() {} - void ps_getpid() {}], - [td_ta_new();], - [srv_cv_thread_db="$thread_db"], - [srv_cv_thread_db=no]) - ]) - LIBS="$old_LIBS" -])]) - -AC_DEFUN([SRV_CHECK_TLS_GET_ADDR], -[AC_CACHE_CHECK([for thread_db_tls_get_addr],[srv_cv_tls_get_addr], - [old_LIBS="$LIBS" - LIBS="$LIBS $srv_cv_thread_db" - AC_TRY_LINK( - [void ps_pglobal_lookup() {} - void ps_pdread() {} - void ps_pdwrite() {} - void ps_lgetregs() {} - void ps_lsetregs() {} - void ps_lgetfpregs() {} - void ps_lsetfpregs() {} - void ps_get_thread_area() {} - void ps_getpid() {}], - [td_thr_tls_get_addr();], - [srv_cv_tls_get_addr=yes], - [srv_cv_tls_get_addr=no]) - LIBS="$old_LIBS" -])]) diff --git a/gdb/gdbserver/config.in b/gdb/gdbserver/config.in index dc976dc1eb3..7eb11b9941a 100644 --- a/gdb/gdbserver/config.in +++ b/gdb/gdbserver/config.in @@ -130,9 +130,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_WAIT_H -/* Define if td_thr_tls_get_addr is available. */ -#undef HAVE_TD_THR_TLS_GET_ADDR - /* Define if TD_VERSION is available. */ #undef HAVE_TD_VERSION diff --git a/gdb/gdbserver/configure b/gdb/gdbserver/configure index b777c1b08ea..b88ed5b605d 100755 --- a/gdb/gdbserver/configure +++ b/gdb/gdbserver/configure @@ -4210,125 +4210,7 @@ srv_libs= USE_THREAD_DB= if test "$srv_linux_thread_db" = "yes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libthread_db" >&5 -$as_echo_n "checking for libthread_db... " >&6; } -if test "${srv_cv_thread_db+set}" = set; then : - $as_echo_n "(cached) " >&6 -else - old_LIBS="$LIBS" - LIBS="$LIBS -lthread_db" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -void ps_pglobal_lookup() {} - void ps_pdread() {} - void ps_pdwrite() {} - void ps_lgetregs() {} - void ps_lsetregs() {} - void ps_lgetfpregs() {} - void ps_lsetfpregs() {} - void ps_get_thread_area() {} - void ps_getpid() {} -int -main () -{ -td_ta_new(); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - srv_cv_thread_db="-lthread_db" -else - srv_cv_thread_db=no - - if test "$prefix" = "/usr" || test "$prefix" = "NONE"; then - thread_db="/lib/libthread_db.so.1" - else - thread_db='$prefix/lib/libthread_db.so.1' - fi - LIBS="$old_LIBS `eval echo "$thread_db"`" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -void ps_pglobal_lookup() {} - void ps_pdread() {} - void ps_pdwrite() {} - void ps_lgetregs() {} - void ps_lsetregs() {} - void ps_lgetfpregs() {} - void ps_lsetfpregs() {} - void ps_get_thread_area() {} - void ps_getpid() {} -int -main () -{ -td_ta_new(); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - srv_cv_thread_db="$thread_db" -else - srv_cv_thread_db=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - LIBS="$old_LIBS" - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $srv_cv_thread_db" >&5 -$as_echo "$srv_cv_thread_db" >&6; } - if test "$srv_cv_thread_db" = no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not find libthread_db." >&5 -$as_echo "$as_me: WARNING: Could not find libthread_db." >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Disabling thread support in gdbserver." >&5 -$as_echo "$as_me: WARNING: Disabling thread support in gdbserver." >&2;} - srv_linux_thread_db=no - else - srv_libs="$srv_cv_thread_db" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for thread_db_tls_get_addr" >&5 -$as_echo_n "checking for thread_db_tls_get_addr... " >&6; } -if test "${srv_cv_tls_get_addr+set}" = set; then : - $as_echo_n "(cached) " >&6 -else - old_LIBS="$LIBS" - LIBS="$LIBS $srv_cv_thread_db" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -void ps_pglobal_lookup() {} - void ps_pdread() {} - void ps_pdwrite() {} - void ps_lgetregs() {} - void ps_lsetregs() {} - void ps_lgetfpregs() {} - void ps_lsetfpregs() {} - void ps_get_thread_area() {} - void ps_getpid() {} -int -main () -{ -td_thr_tls_get_addr(); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - srv_cv_tls_get_addr=yes -else - srv_cv_tls_get_addr=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - LIBS="$old_LIBS" - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $srv_cv_tls_get_addr" >&5 -$as_echo "$srv_cv_tls_get_addr" >&6; } - fi + srv_libs="-ldl" old_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -rdynamic" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4351,9 +4233,7 @@ rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$old_LDFLAGS" -fi -if test "$srv_linux_thread_db" = "yes"; then srv_thread_depfiles="thread-db.o proc-service.o" USE_THREAD_DB="-DUSE_THREAD_DB" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for TD_VERSION" >&5 @@ -4386,12 +4266,6 @@ $as_echo "$gdbsrv_cv_have_td_version" >&6; } $as_echo "#define HAVE_TD_VERSION 1" >>confdefs.h fi - - if test "$srv_cv_tls_get_addr" = yes; then - -$as_echo "#define HAVE_TD_THR_TLS_GET_ADDR 1" >>confdefs.h - - fi fi if test "$srv_xmlfiles" != ""; then diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac index 0bba8244dd7..a631c7eb0fa 100644 --- a/gdb/gdbserver/configure.ac +++ b/gdb/gdbserver/configure.ac @@ -140,23 +140,13 @@ srv_libs= USE_THREAD_DB= if test "$srv_linux_thread_db" = "yes"; then - SRV_CHECK_THREAD_DB - if test "$srv_cv_thread_db" = no; then - AC_WARN([Could not find libthread_db.]) - AC_WARN([Disabling thread support in gdbserver.]) - srv_linux_thread_db=no - else - srv_libs="$srv_cv_thread_db" - SRV_CHECK_TLS_GET_ADDR - fi + srv_libs="-ldl" old_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -rdynamic" AC_TRY_LINK([], [], [RDYNAMIC=-rdynamic], [RDYNAMIC=]) AC_SUBST(RDYNAMIC) LDFLAGS="$old_LDFLAGS" -fi -if test "$srv_linux_thread_db" = "yes"; then srv_thread_depfiles="thread-db.o proc-service.o" USE_THREAD_DB="-DUSE_THREAD_DB" AC_CACHE_CHECK([for TD_VERSION], gdbsrv_cv_have_td_version, @@ -166,10 +156,6 @@ if test "$srv_linux_thread_db" = "yes"; then if test $gdbsrv_cv_have_td_version = yes; then AC_DEFINE(HAVE_TD_VERSION, 1, [Define if TD_VERSION is available.]) fi - - if test "$srv_cv_tls_get_addr" = yes; then - AC_DEFINE(HAVE_TD_THR_TLS_GET_ADDR, 1, [Define if td_thr_tls_get_addr is available.]) - fi fi if test "$srv_xmlfiles" != ""; then diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index a88f4d1f02e..8a882376e69 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -261,8 +261,14 @@ linux_add_process (int pid, int attached) static void linux_remove_process (struct process_info *process) { - free (process->private->arch_private); - free (process->private); + struct process_info_private *priv = process->private; + +#ifdef USE_THREAD_DB + thread_db_free (process); +#endif + + free (priv->arch_private); + free (priv); remove_process (process); } @@ -1122,7 +1128,7 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options) && !event_child->stepping && ( #ifdef USE_THREAD_DB - (current_process ()->private->thread_db_active + (current_process ()->private->thread_db != NULL && (WSTOPSIG (*wstat) == __SIGRTMIN || WSTOPSIG (*wstat) == __SIGRTMIN + 1)) || @@ -2642,11 +2648,10 @@ linux_look_up_symbols (void) #ifdef USE_THREAD_DB struct process_info *proc = current_process (); - if (proc->private->thread_db_active) + if (proc->private->thread_db != NULL) return; - proc->private->thread_db_active - = thread_db_init (!linux_supports_tracefork_flag); + thread_db_init (!linux_supports_tracefork_flag); #endif } @@ -3171,7 +3176,12 @@ static struct target_ops linux_target_ops = { linux_supports_non_stop, linux_async, linux_start_non_stop, - linux_supports_multi_process + linux_supports_multi_process, +#ifdef USE_THREAD_DB + thread_db_handle_monitor_command +#else + NULL +#endif }; static void diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 603963e9b08..9a3815c995a 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -47,18 +47,12 @@ struct siginfo; struct process_info_private { - /* True if this process has loaded thread_db, and it is active. */ - int thread_db_active; - - /* Structure that identifies the child process for the - interface. */ - struct ps_prochandle proc_handle; - - /* Connection to the libthread_db library. */ - td_thragent_t *thread_agent; - /* Arch-specific additions. */ struct arch_process_info *arch_private; + + /* libthread_db-specific additions. Not NULL if this process has loaded + thread_db, and it is active. */ + struct thread_db *thread_db; }; struct lwp_info; @@ -203,9 +197,11 @@ char *linux_child_pid_to_exec_file (int pid); int elf_64_file_p (const char *file); void linux_attach_lwp (unsigned long pid); +struct lwp_info *find_lwp_pid (ptid_t ptid); +/* From thread-db.c */ int thread_db_init (int use_events); +void thread_db_free (struct process_info *); +int thread_db_handle_monitor_command (char *); int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, CORE_ADDR load_module, CORE_ADDR *address); - -struct lwp_info *find_lwp_pid (ptid_t ptid); diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index d704180a539..9bf4f3dbd00 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -660,6 +660,53 @@ handle_search_memory (char *own_buf, int packet_len) return; \ } +/* Handle monitor commands not handled by target-specific handlers. */ + +static void +handle_monitor_command (char *mon) +{ + if (strcmp (mon, "set debug 1") == 0) + { + debug_threads = 1; + monitor_output ("Debug output enabled.\n"); + } + else if (strcmp (mon, "set debug 0") == 0) + { + debug_threads = 0; + monitor_output ("Debug output disabled.\n"); + } + else if (strcmp (mon, "set debug-hw-points 1") == 0) + { + debug_hw_points = 1; + monitor_output ("H/W point debugging output enabled.\n"); + } + else if (strcmp (mon, "set debug-hw-points 0") == 0) + { + debug_hw_points = 0; + monitor_output ("H/W point debugging output disabled.\n"); + } + else if (strcmp (mon, "set remote-debug 1") == 0) + { + remote_debug = 1; + monitor_output ("Protocol debug output enabled.\n"); + } + else if (strcmp (mon, "set remote-debug 0") == 0) + { + remote_debug = 0; + monitor_output ("Protocol debug output disabled.\n"); + } + else if (strcmp (mon, "help") == 0) + monitor_show_help (); + else if (strcmp (mon, "exit") == 0) + exit_requested = 1; + else + { + monitor_output ("Unknown monitor command.\n\n"); + monitor_show_help (); + write_enn (own_buf); + } +} + /* Handle all of the extended 'q' packets. */ void handle_query (char *own_buf, int packet_len, int *new_packet_len_p) @@ -1211,46 +1258,10 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) write_ok (own_buf); - if (strcmp (mon, "set debug 1") == 0) - { - debug_threads = 1; - monitor_output ("Debug output enabled.\n"); - } - else if (strcmp (mon, "set debug 0") == 0) - { - debug_threads = 0; - monitor_output ("Debug output disabled.\n"); - } - else if (strcmp (mon, "set debug-hw-points 1") == 0) - { - debug_hw_points = 1; - monitor_output ("H/W point debugging output enabled.\n"); - } - else if (strcmp (mon, "set debug-hw-points 0") == 0) - { - debug_hw_points = 0; - monitor_output ("H/W point debugging output disabled.\n"); - } - else if (strcmp (mon, "set remote-debug 1") == 0) - { - remote_debug = 1; - monitor_output ("Protocol debug output enabled.\n"); - } - else if (strcmp (mon, "set remote-debug 0") == 0) - { - remote_debug = 0; - monitor_output ("Protocol debug output disabled.\n"); - } - else if (strcmp (mon, "help") == 0) - monitor_show_help (); - else if (strcmp (mon, "exit") == 0) - exit_requested = 1; - else - { - monitor_output ("Unknown monitor command.\n\n"); - monitor_show_help (); - write_enn (own_buf); - } + if (the_target->handle_monitor_command == NULL + || (*the_target->handle_monitor_command) (mon) == 0) + /* Default processing. */ + handle_monitor_command (mon); free (mon); return; diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index 5fdfee312a8..0e4b1277472 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -279,6 +279,10 @@ struct target_ops /* Returns true if the target supports multi-process debugging. */ int (*supports_multi_process) (void); + + /* If not NULL, target-specific routine to process monitor command. + Returns 1 if handled, or 0 to perform default processing. */ + int (*handle_monitor_command) (char *); }; extern struct target_ops *the_target; diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index 4a26be951b2..4d1008fce29 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 -#endif - #include "gdb_proc_service.h" +#include "../gdb_thread_db.h" +#include #include +#include +#include + +struct thread_db +{ + /* Structure that identifies the child process for the + 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; } -- 2.39.2