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.
This commit is contained in:
Paul Pluzhnikov
2009-10-09 00:31:01 +00:00
parent 37e124c9c3
commit cdbfd4198e
11 changed files with 432 additions and 343 deletions

View File

@ -14903,6 +14903,13 @@ Disable or enable general debugging messages.
Disable or enable specific debugging messages associated with the remote Disable or enable specific debugging messages associated with the remote
protocol (@pxref{Remote Protocol}). 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 @item monitor exit
Tell gdbserver to exit immediately. This command should be followed by Tell gdbserver to exit immediately. This command should be followed by
@code{disconnect} to close the debugging session. @code{gdbserver} will @code{disconnect} to close the debugging session. @code{gdbserver} will

View File

@ -1,3 +1,28 @@
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.
2009-09-27 Ulrich Weigand <uweigand@de.ibm.com> 2009-09-27 Ulrich Weigand <uweigand@de.ibm.com>
* spu-low.c (spu_kill): Wait for inferior to terminate. * spu-low.c (spu_kill): Wait for inferior to terminate.

View File

@ -7,63 +7,3 @@ sinclude(../../config/override.m4)
dnl For ACX_PKGVERSION and ACX_BUGURL. dnl For ACX_PKGVERSION and ACX_BUGURL.
sinclude(../../config/acx.m4) 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"
])])

View File

@ -130,9 +130,6 @@
/* Define to 1 if you have the <sys/wait.h> header file. */ /* Define to 1 if you have the <sys/wait.h> header file. */
#undef HAVE_SYS_WAIT_H #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. */ /* Define if TD_VERSION is available. */
#undef HAVE_TD_VERSION #undef HAVE_TD_VERSION

View File

@ -4210,125 +4210,7 @@ srv_libs=
USE_THREAD_DB= USE_THREAD_DB=
if test "$srv_linux_thread_db" = "yes"; then if test "$srv_linux_thread_db" = "yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libthread_db" >&5 srv_libs="-ldl"
$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
old_LDFLAGS="$LDFLAGS" old_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -rdynamic" LDFLAGS="$LDFLAGS -rdynamic"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext 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 conftest$ac_exeext conftest.$ac_ext
LDFLAGS="$old_LDFLAGS" LDFLAGS="$old_LDFLAGS"
fi
if test "$srv_linux_thread_db" = "yes"; then
srv_thread_depfiles="thread-db.o proc-service.o" srv_thread_depfiles="thread-db.o proc-service.o"
USE_THREAD_DB="-DUSE_THREAD_DB" USE_THREAD_DB="-DUSE_THREAD_DB"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for TD_VERSION" >&5 { $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 $as_echo "#define HAVE_TD_VERSION 1" >>confdefs.h
fi fi
if test "$srv_cv_tls_get_addr" = yes; then
$as_echo "#define HAVE_TD_THR_TLS_GET_ADDR 1" >>confdefs.h
fi
fi fi
if test "$srv_xmlfiles" != ""; then if test "$srv_xmlfiles" != ""; then

View File

@ -140,23 +140,13 @@ srv_libs=
USE_THREAD_DB= USE_THREAD_DB=
if test "$srv_linux_thread_db" = "yes"; then if test "$srv_linux_thread_db" = "yes"; then
SRV_CHECK_THREAD_DB srv_libs="-ldl"
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
old_LDFLAGS="$LDFLAGS" old_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -rdynamic" LDFLAGS="$LDFLAGS -rdynamic"
AC_TRY_LINK([], [], [RDYNAMIC=-rdynamic], [RDYNAMIC=]) AC_TRY_LINK([], [], [RDYNAMIC=-rdynamic], [RDYNAMIC=])
AC_SUBST(RDYNAMIC) AC_SUBST(RDYNAMIC)
LDFLAGS="$old_LDFLAGS" LDFLAGS="$old_LDFLAGS"
fi
if test "$srv_linux_thread_db" = "yes"; then
srv_thread_depfiles="thread-db.o proc-service.o" srv_thread_depfiles="thread-db.o proc-service.o"
USE_THREAD_DB="-DUSE_THREAD_DB" USE_THREAD_DB="-DUSE_THREAD_DB"
AC_CACHE_CHECK([for TD_VERSION], gdbsrv_cv_have_td_version, 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 if test $gdbsrv_cv_have_td_version = yes; then
AC_DEFINE(HAVE_TD_VERSION, 1, [Define if TD_VERSION is available.]) AC_DEFINE(HAVE_TD_VERSION, 1, [Define if TD_VERSION is available.])
fi 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 fi
if test "$srv_xmlfiles" != ""; then if test "$srv_xmlfiles" != ""; then

View File

@ -261,8 +261,14 @@ linux_add_process (int pid, int attached)
static void static void
linux_remove_process (struct process_info *process) linux_remove_process (struct process_info *process)
{ {
free (process->private->arch_private); struct process_info_private *priv = process->private;
free (process->private);
#ifdef USE_THREAD_DB
thread_db_free (process);
#endif
free (priv->arch_private);
free (priv);
remove_process (process); remove_process (process);
} }
@ -1122,7 +1128,7 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
&& !event_child->stepping && !event_child->stepping
&& ( && (
#ifdef USE_THREAD_DB #ifdef USE_THREAD_DB
(current_process ()->private->thread_db_active (current_process ()->private->thread_db != NULL
&& (WSTOPSIG (*wstat) == __SIGRTMIN && (WSTOPSIG (*wstat) == __SIGRTMIN
|| WSTOPSIG (*wstat) == __SIGRTMIN + 1)) || WSTOPSIG (*wstat) == __SIGRTMIN + 1))
|| ||
@ -2642,11 +2648,10 @@ linux_look_up_symbols (void)
#ifdef USE_THREAD_DB #ifdef USE_THREAD_DB
struct process_info *proc = current_process (); struct process_info *proc = current_process ();
if (proc->private->thread_db_active) if (proc->private->thread_db != NULL)
return; return;
proc->private->thread_db_active thread_db_init (!linux_supports_tracefork_flag);
= thread_db_init (!linux_supports_tracefork_flag);
#endif #endif
} }
@ -3171,7 +3176,12 @@ static struct target_ops linux_target_ops = {
linux_supports_non_stop, linux_supports_non_stop,
linux_async, linux_async,
linux_start_non_stop, 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 static void

View File

@ -47,18 +47,12 @@ struct siginfo;
struct process_info_private 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
<proc_service.h> interface. */
struct ps_prochandle proc_handle;
/* Connection to the libthread_db library. */
td_thragent_t *thread_agent;
/* Arch-specific additions. */ /* Arch-specific additions. */
struct arch_process_info *arch_private; 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; struct lwp_info;
@ -203,9 +197,11 @@ char *linux_child_pid_to_exec_file (int pid);
int elf_64_file_p (const char *file); int elf_64_file_p (const char *file);
void linux_attach_lwp (unsigned long pid); 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); 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, int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
CORE_ADDR load_module, CORE_ADDR *address); CORE_ADDR load_module, CORE_ADDR *address);
struct lwp_info *find_lwp_pid (ptid_t ptid);

View File

@ -660,6 +660,53 @@ handle_search_memory (char *own_buf, int packet_len)
return; \ 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. */ /* Handle all of the extended 'q' packets. */
void void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p) 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); write_ok (own_buf);
if (strcmp (mon, "set debug 1") == 0) if (the_target->handle_monitor_command == NULL
{ || (*the_target->handle_monitor_command) (mon) == 0)
debug_threads = 1; /* Default processing. */
monitor_output ("Debug output enabled.\n"); handle_monitor_command (mon);
}
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);
}
free (mon); free (mon);
return; return;

View File

@ -279,6 +279,10 @@ struct target_ops
/* Returns true if the target supports multi-process debugging. */ /* Returns true if the target supports multi-process debugging. */
int (*supports_multi_process) (void); 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; extern struct target_ops *the_target;

View File

@ -27,13 +27,51 @@ extern int debug_threads;
static int thread_db_use_events; static int thread_db_use_events;
#ifdef HAVE_THREAD_DB_H
#include <thread_db.h>
#endif
#include "gdb_proc_service.h" #include "gdb_proc_service.h"
#include "../gdb_thread_db.h"
#include <dlfcn.h>
#include <stdint.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_one_thread (ptid_t);
static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data); 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_event_msg_t msg;
td_err_e err; td_err_e err;
struct lwp_info *lwp; 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) if (debug_threads)
fprintf (stderr, "Thread creation event.\n"); fprintf (stderr, "Thread creation event.\n");
@ -139,7 +180,7 @@ thread_db_create_event (CORE_ADDR where)
In the LinuxThreads implementation, this is safe, In the LinuxThreads implementation, this is safe,
because all events come from the manager thread because all events come from the manager thread
(except for its own creation, of course). */ (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) if (err != TD_OK)
fprintf (stderr, "thread getmsg err: %s\n", fprintf (stderr, "thread getmsg err: %s\n",
thread_db_err_str (err)); thread_db_err_str (err));
@ -158,36 +199,25 @@ thread_db_create_event (CORE_ADDR where)
return 0; 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 static int
thread_db_enable_reporting () thread_db_enable_reporting ()
{ {
td_thr_events_t events; td_thr_events_t events;
td_notify_t notify; td_notify_t notify;
td_err_e err; 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. */ /* Set the process wide mask saying which events we're interested in. */
td_event_emptyset (&events); td_event_emptyset (&events);
td_event_addset (&events, TD_CREATE); td_event_addset (&events, TD_CREATE);
#if 0 err = thread_db->td_ta_set_event_p (thread_db->thread_agent, &events);
/* 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);
if (err != TD_OK) if (err != TD_OK)
{ {
warning ("Unable to set global thread event mask: %s", warning ("Unable to set global thread event mask: %s",
@ -196,7 +226,8 @@ thread_db_enable_reporting ()
} }
/* Get address for thread creation breakpoint. */ /* Get address for thread creation breakpoint. */
err = td_ta_event_addr (proc->thread_agent, TD_CREATE, &notify); err = thread_db->td_ta_event_addr_p (thread_db->thread_agent, TD_CREATE,
&notify);
if (err != TD_OK) if (err != TD_OK)
{ {
warning ("Unable to get location for thread creation breakpoint: %s", 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, set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
thread_db_create_event); 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, &notify);
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; return 1;
} }
@ -233,7 +248,7 @@ find_one_thread (ptid_t ptid)
td_err_e err; td_err_e err;
struct thread_info *inferior; struct thread_info *inferior;
struct lwp_info *lwp; 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); int lwpid = ptid_get_lwp (ptid);
inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid); inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid);
@ -242,13 +257,12 @@ find_one_thread (ptid_t ptid)
return 1; return 1;
/* Get information about this thread. */ /* Get information about this thread. */
proc = get_thread_process (inferior)->private; err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, &th);
err = td_ta_map_lwp2thr (proc->thread_agent, lwpid, &th);
if (err != TD_OK) if (err != TD_OK)
error ("Cannot get thread handle for LWP %d: %s", error ("Cannot get thread handle for LWP %d: %s",
lwpid, thread_db_err_str (err)); 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) if (err != TD_OK)
error ("Cannot get thread info for LWP %d: %s", error ("Cannot get thread info for LWP %d: %s",
lwpid, thread_db_err_str (err)); lwpid, thread_db_err_str (err));
@ -266,7 +280,7 @@ find_one_thread (ptid_t ptid)
if (thread_db_use_events) 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) if (err != TD_OK)
error ("Cannot enable thread event reporting for %d: %s", error ("Cannot enable thread event reporting for %d: %s",
ti.ti_lid, thread_db_err_str (err)); 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) 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) if (err != TD_OK)
error ("Cannot enable thread event reporting for %d: %s", error ("Cannot enable thread event reporting for %d: %s",
ti_p->ti_lid, thread_db_err_str (err)); 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_thrinfo_t ti;
td_err_e err; 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) if (err != TD_OK)
error ("Cannot get thread info: %s", thread_db_err_str (err)); 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; td_err_e err;
ptid_t ptid = ((struct inferior_list_entry *) current_inferior)->id; 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. /* This function is only called when we first initialize thread_db.
First locate the initial thread. If it is not ready for First locate the initial thread. If it is not ready for
@ -350,10 +366,10 @@ thread_db_find_new_threads (void)
return; return;
/* Iterate over all user-space threads to discover new threads. */ /* Iterate over all user-space threads to discover new threads. */
err = td_ta_thr_iter (proc->thread_agent, err = thread_db->td_ta_thr_iter_p (thread_db->thread_agent,
find_new_threads_callback, NULL, find_new_threads_callback, NULL,
TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
if (err != TD_OK) if (err != TD_OK)
error ("Cannot find new threads: %s", thread_db_err_str (err)); error ("Cannot find new threads: %s", thread_db_err_str (err));
} }
@ -366,10 +382,11 @@ thread_db_find_new_threads (void)
static void static void
thread_db_look_up_symbols (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; 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); look_up_one_symbol (*sym_list, &unused);
} }
@ -377,16 +394,23 @@ int
thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
CORE_ADDR load_module, CORE_ADDR *address) CORE_ADDR load_module, CORE_ADDR *address)
{ {
#if HAVE_TD_THR_TLS_GET_ADDR
psaddr_t addr; psaddr_t addr;
td_err_e err; td_err_e err;
struct lwp_info *lwp; struct lwp_info *lwp;
struct thread_info *saved_inferior; 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 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; return TD_ERR;
if (thread_db->td_thr_tls_get_addr_p == NULL)
return -1;
lwp = get_thread_lwp (thread); lwp = get_thread_lwp (thread);
if (!lwp->thread_known) if (!lwp->thread_known)
find_one_thread (lwp->head.id); 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 /* Note the cast through uintptr_t: this interface only works if
a target address fits in a psaddr_t, which is a host pointer. 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. */ 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, err = thread_db->td_thr_tls_get_addr_p (&lwp->th,
offset, &addr); (psaddr_t) (uintptr_t) load_module,
offset, &addr);
current_inferior = saved_inferior; current_inferior = saved_inferior;
if (err == TD_OK) if (err == TD_OK)
{ {
@ -408,17 +433,190 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
} }
else else
return err; 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 int
thread_db_init (int use_events) thread_db_init (int use_events)
{ {
int err;
struct process_info *proc = current_process (); 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 /* FIXME drow/2004-10-16: This is the "overall process ID", which
GNU/Linux calls tgid, "thread group ID". When we support 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; thread_db_use_events = use_events;
err = td_ta_new (&priv->proc_handle, &priv->thread_agent); if (thread_db_load_search ())
switch (err)
{ {
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) 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_find_new_threads ();
thread_db_look_up_symbols (); thread_db_look_up_symbols ();
proc->all_symbols_looked_up = 1; proc->all_symbols_looked_up = 1;
return 1; return 1;
default:
warning ("error initializing thread_db library: %s",
thread_db_err_str (err));
} }
return 0; 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;
}