/* libthread_db assisted debugging support, generic parts.
- Copyright 1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "gdb_thread_db.h"
#include "bfd.h"
+#include "exceptions.h"
#include "gdbthread.h"
#include "inferior.h"
#include "symfile.h"
#include "target.h"
#include "regcache.h"
#include "solib-svr4.h"
+#include "gdbcore.h"
+#include "observer.h"
+#include "linux-nat.h"
+
+#include <signal.h>
#ifdef HAVE_GNU_LIBC_VERSION_H
#include <gnu/libc-version.h>
/* If we're running on GNU/Linux, we must explicitly attach to any new
threads. */
-/* FIXME: There is certainly some room for improvements:
- - Cache LWP ids.
- - Bypass libthread_db when fetching or storing registers for
- threads bound to a LWP. */
-
/* This module's target vector. */
static struct target_ops thread_db_ops;
/* The target vector that we call for things this module can't handle. */
static struct target_ops *target_beneath;
-/* Pointer to the next function on the objfile event chain. */
-static void (*target_new_objfile_chain) (struct objfile * objfile);
-
/* Non-zero if we're using this module's target vector. */
static int using_thread_db;
static td_err_e (*td_thr_validate_p) (const td_thrhandle_t *th);
static td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th,
td_thrinfo_t *infop);
-static td_err_e (*td_thr_getfpregs_p) (const td_thrhandle_t *th,
- gdb_prfpregset_t *regset);
-static td_err_e (*td_thr_getgregs_p) (const td_thrhandle_t *th,
- prgregset_t gregs);
-static td_err_e (*td_thr_setfpregs_p) (const td_thrhandle_t *th,
- const gdb_prfpregset_t *fpregs);
-static td_err_e (*td_thr_setgregs_p) (const td_thrhandle_t *th,
- prgregset_t gregs);
static td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th,
int event);
#define is_thread(ptid) (GET_THREAD (ptid) != 0)
#define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0)
-#define BUILD_THREAD(tid, pid) ptid_build (pid, 0, tid)
\f
/* Use "struct private_thread_info" to cache thread state. This is
return "only part of register set was written/read";
case TD_NOXREGS:
return "X register set not available for this thread";
+#ifdef THREAD_DB_HAS_TD_NOTALLOC
+ case TD_NOTALLOC:
+ return "thread has not yet allocated TLS for given module";
+#endif
+#ifdef THREAD_DB_HAS_TD_VERSION
+ case TD_VERSION:
+ return "versions of libpthread and libthread_db do not match";
+#endif
+#ifdef THREAD_DB_HAS_TD_NOTLS
+ case TD_NOTLS:
+ return "there is no TLS segment in the given module";
+#endif
default:
snprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err);
return buf;
}
}
+\f
+/* Return 1 if any threads have been registered. There may be none if
+ the threading library is not fully initialized yet. */
-static char *
-thread_db_state_str (td_thr_state_e state)
+static int
+have_threads_callback (struct thread_info *thread, void *dummy)
{
- static char buf[64];
+ return 1;
+}
- switch (state)
- {
- case TD_THR_STOPPED:
- return "stopped by debugger";
- case TD_THR_RUN:
- return "runnable";
- case TD_THR_ACTIVE:
- return "active";
- case TD_THR_ZOMBIE:
- return "zombie";
- case TD_THR_SLEEP:
- return "sleeping";
- case TD_THR_STOPPED_ASLEEP:
- return "stopped by debugger AND blocked";
- default:
- snprintf (buf, sizeof (buf), "unknown thread_db state %d", state);
- return buf;
- }
+static int
+have_threads (void)
+{
+ return iterate_over_threads (have_threads_callback, NULL) != NULL;
}
-\f
+
/* A callback function for td_ta_thr_iter, which we use to map all
threads to LWPs.
err = td_thr_get_info_p (thp, &ti);
if (err != TD_OK)
- error ("thread_get_info_callback: cannot get thread info: %s",
+ error (_("thread_get_info_callback: cannot get thread info: %s"),
thread_db_err_str (err));
/* Fill the cache. */
- thread_ptid = BUILD_THREAD (ti.ti_tid, GET_PID (inferior_ptid));
+ thread_ptid = ptid_build (GET_PID (inferior_ptid), ti.ti_lid, ti.ti_tid);
thread_info = find_thread_pid (thread_ptid);
/* In the case of a zombie thread, don't continue. We don't want to
if (err != TD_OK)
{
if (fatal)
- error ("Cannot find thread %ld: %s",
+ error (_("Cannot find thread %ld: %s"),
(long) GET_THREAD (thread_info->ptid),
thread_db_err_str (err));
}
else
thread_info->private->th_valid = 1;
}
-
-static td_thrinfo_t *
-thread_db_get_info (struct thread_info *thread_info)
-{
- td_err_e err;
-
- if (thread_info->private->ti_valid)
- return &thread_info->private->ti;
-
- if (!thread_info->private->th_valid)
- thread_db_map_id2thr (thread_info, 1);
-
- err =
- td_thr_get_info_p (&thread_info->private->th, &thread_info->private->ti);
- if (err != TD_OK)
- error ("thread_db_get_info: cannot get thread info: %s",
- thread_db_err_str (err));
-
- thread_info->private->ti_valid = 1;
- return &thread_info->private->ti;
-}
\f
/* Convert between user-level thread ids and LWP ids. */
err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th);
if (err != TD_OK)
- error ("Cannot find user-level thread for LWP %ld: %s",
+ error (_("Cannot find user-level thread for LWP %ld: %s"),
GET_LWP (ptid), thread_db_err_str (err));
thread_info = NULL;
gdb_assert (thread_info && thread_info->private->ti_valid);
- return BUILD_THREAD (thread_info->private->ti.ti_tid, GET_PID (ptid));
+ return ptid_build (GET_PID (ptid), GET_LWP (ptid),
+ thread_info->private->ti.ti_tid);
}
static ptid_t
lwp_from_thread (ptid_t ptid)
{
- struct thread_info *thread_info;
- ptid_t thread_ptid;
-
- if (!is_thread (ptid))
- return ptid;
-
- thread_info = find_thread_pid (ptid);
- thread_db_get_info (thread_info);
-
- return BUILD_LWP (thread_info->private->ti.ti_lid, GET_PID (ptid));
+ return BUILD_LWP (GET_LWP (ptid), GET_PID (ptid));
}
\f
{
void *sym = dlsym (handle, name);
if (sym == NULL)
- warning ("Symbol \"%s\" not found in libthread_db: %s", name, dlerror ());
+ warning (_("Symbol \"%s\" not found in libthread_db: %s"), name, dlerror ());
return sym;
}
if (td_thr_get_info_p == NULL)
return 0;
- td_thr_getfpregs_p = verbose_dlsym (handle, "td_thr_getfpregs");
- if (td_thr_getfpregs_p == NULL)
- return 0;
-
- td_thr_getgregs_p = verbose_dlsym (handle, "td_thr_getgregs");
- if (td_thr_getgregs_p == NULL)
- return 0;
-
- td_thr_setfpregs_p = verbose_dlsym (handle, "td_thr_setfpregs");
- if (td_thr_setfpregs_p == NULL)
- return 0;
-
- td_thr_setgregs_p = verbose_dlsym (handle, "td_thr_setgregs");
- if (td_thr_setgregs_p == NULL)
- return 0;
-
/* Initialize the library. */
err = td_init_p ();
if (err != TD_OK)
{
- warning ("Cannot initialize libthread_db: %s", thread_db_err_str (err));
+ warning (_("Cannot initialize libthread_db: %s"), thread_db_err_str (err));
return 0;
}
return err;
/* Set up the breakpoint. */
- (*bp) = gdbarch_convert_from_func_ptr_addr (current_gdbarch,
- (CORE_ADDR) notify.u.bptaddr,
- ¤t_target);
+ gdb_assert (exec_bfd);
+ (*bp) = (gdbarch_convert_from_func_ptr_addr
+ (current_gdbarch,
+ /* Do proper sign extension for the target. */
+ (bfd_get_sign_extend_vma (exec_bfd) > 0
+ ? (CORE_ADDR) (intptr_t) notify.u.bptaddr
+ : (CORE_ADDR) (uintptr_t) notify.u.bptaddr),
+ ¤t_target));
create_thread_event_breakpoint ((*bp));
return TD_OK;
td_event_addset (&events, TD_CREATE);
#ifdef HAVE_GNU_LIBC_VERSION_H
- /* FIXME: kettenis/2000-04-23: The event reporting facility is
- broken for TD_DEATH events in glibc 2.1.3, so don't enable it for
- now. */
+ /* The event reporting facility is broken for TD_DEATH events in
+ glibc 2.1.3, so don't enable it if we have glibc but a lower
+ version. */
libc_version = gnu_get_libc_version ();
if (sscanf (libc_version, "%d.%d", &libc_major, &libc_minor) == 2
&& (libc_major > 2 || (libc_major == 2 && libc_minor > 1)))
err = td_ta_set_event_p (thread_agent, &events);
if (err != TD_OK)
{
- warning ("Unable to set global thread event mask: %s",
+ warning (_("Unable to set global thread event mask: %s"),
thread_db_err_str (err));
return;
}
err = enable_thread_event (thread_agent, TD_CREATE, &td_create_bp_addr);
if (err != TD_OK)
{
- warning ("Unable to get location for thread creation breakpoint: %s",
+ warning (_("Unable to get location for thread creation breakpoint: %s"),
thread_db_err_str (err));
return;
}
err = enable_thread_event (thread_agent, TD_DEATH, &td_death_bp_addr);
if (err != TD_OK)
{
- warning ("Unable to get location for thread death breakpoint: %s",
+ warning (_("Unable to get location for thread death breakpoint: %s"),
thread_db_err_str (err));
return;
}
#endif
}
-static void
-thread_db_new_objfile (struct objfile *objfile)
+/* Check whether thread_db is usable. This function is called when
+ an inferior is created (or otherwise acquired, e.g. attached to)
+ and when new shared libraries are loaded into a running process. */
+
+void
+check_for_thread_db (void)
{
td_err_e err;
+ static int already_loaded;
+
+ /* Do nothing if we couldn't load libthread_db.so.1. */
+ if (td_ta_new_p == NULL)
+ return;
/* First time through, report that libthread_db was successfuly
loaded. Can't print this in in thread_db_load as, at that stage,
- the interpreter and it's console haven't started. The real
- problem here is that libthread_db is loaded too early - it should
- only be loaded when there is a program to debug. */
- {
- static int dejavu;
- if (!dejavu)
- {
- Dl_info info;
- const char *library = NULL;
- /* Try dladdr. */
- if (dladdr ((*td_ta_new_p), &info) != 0)
- library = info.dli_fname;
- /* Try dlinfo? */
- if (library == NULL)
- /* Paranoid - don't let a NULL path slip through. */
- library = LIBTHREAD_DB_SO;
- printf_unfiltered ("Using host libthread_db library \"%s\".\n",
- library);
- dejavu = 1;
- }
- }
+ the interpreter and it's console haven't started. */
- /* Don't attempt to use thread_db on targets which can not run
- (core files). */
- if (objfile == NULL || !target_has_execution)
+ if (!already_loaded)
{
- /* All symbols have been discarded. If the thread_db target is
- active, deactivate it now. */
- if (using_thread_db)
- {
- gdb_assert (proc_handle.pid == 0);
- unpush_target (&thread_db_ops);
- using_thread_db = 0;
- }
+ Dl_info info;
+ const char *library = NULL;
+ if (dladdr ((*td_ta_new_p), &info) != 0)
+ library = info.dli_fname;
+
+ /* Try dlinfo? */
- goto quit;
+ if (library == NULL)
+ /* Paranoid - don't let a NULL path slip through. */
+ library = LIBTHREAD_DB_SO;
+
+ printf_unfiltered (_("Using host libthread_db library \"%s\".\n"),
+ library);
+ already_loaded = 1;
}
if (using_thread_db)
/* Nothing to do. The thread library was already detected and the
target vector was already activated. */
- goto quit;
+ return;
+
+ /* Don't attempt to use thread_db on targets which can not run
+ (executables not running yet, core files) for now. */
+ if (!target_has_execution)
+ return;
+
+ /* Don't attempt to use thread_db for remote targets. */
+ if (!target_can_run (¤t_target))
+ return;
- /* Initialize the structure that identifies the child process. Note
- that at this point there is no guarantee that we actually have a
- child process. */
+ /* Initialize the structure that identifies the child process. */
proc_handle.pid = GET_PID (inferior_ptid);
/* Now attempt to open a connection to the thread library. */
break;
case TD_OK:
- printf_unfiltered ("[Thread debugging using libthread_db enabled]\n");
+ printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n"));
/* The thread library was detected. Activate the thread_db target. */
push_target (&thread_db_ops);
break;
default:
- warning ("Cannot initialize thread debugging library: %s",
+ warning (_("Cannot initialize thread debugging library: %s"),
thread_db_err_str (err));
break;
}
+}
-quit:
- if (target_new_objfile_chain)
- target_new_objfile_chain (objfile);
+static void
+thread_db_new_objfile (struct objfile *objfile)
+{
+ if (objfile != NULL)
+ check_for_thread_db ();
}
/* Attach to a new thread. This function is called when we receive a
check_thread_signals ();
+ if (ti_p->ti_state == TD_THR_UNKNOWN || ti_p->ti_state == TD_THR_ZOMBIE)
+ return; /* A zombie thread -- do not attach. */
+
+ /* Under GNU/Linux, we have to attach to each and every thread. */
+ if (lin_lwp_attach_lwp (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid)), 0) < 0)
+ return;
+
/* Add the thread to GDB's thread list. */
tp = add_thread (ptid);
tp->private = xmalloc (sizeof (struct private_thread_info));
memset (tp->private, 0, sizeof (struct private_thread_info));
if (verbose)
- printf_unfiltered ("[New %s]\n", target_pid_to_str (ptid));
-
- if (ti_p->ti_state == TD_THR_UNKNOWN || ti_p->ti_state == TD_THR_ZOMBIE)
- return; /* A zombie thread -- do not attach. */
-
- /* Under GNU/Linux, we have to attach to each and every thread. */
-#ifdef ATTACH_LWP
- ATTACH_LWP (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid)), 0);
-#endif
+ printf_unfiltered (_("[New %s]\n"), target_pid_to_str (ptid));
/* Enable thread event reporting for this thread. */
err = td_thr_event_enable_p (th_p, 1);
if (err != TD_OK)
- error ("Cannot enable thread event reporting for %s: %s",
+ error (_("Cannot enable thread event reporting for %s: %s"),
target_pid_to_str (ptid), thread_db_err_str (err));
}
-static void
-thread_db_attach (char *args, int from_tty)
-{
- target_beneath->to_attach (args, from_tty);
-
- /* Destroy thread info; it's no longer valid. */
- init_thread_list ();
-
- /* The child process is now the actual multi-threaded
- program. Snatch its process ID... */
- proc_handle.pid = GET_PID (inferior_ptid);
-
- /* ...and perform the remaining initialization steps. */
- enable_thread_event_reporting ();
- thread_db_find_new_threads ();
-}
-
static void
detach_thread (ptid_t ptid, int verbose)
{
struct thread_info *thread_info;
if (verbose)
- printf_unfiltered ("[%s exited]\n", target_pid_to_str (ptid));
+ printf_unfiltered (_("[%s exited]\n"), target_pid_to_str (ptid));
/* Don't delete the thread now, because it still reports as active
until it has executed a few instructions after the event
disable_thread_event_reporting ();
/* There's no need to save & restore inferior_ptid here, since the
- inferior is supposed to be survive this function call. */
+ inferior is not supposed to survive this function call. */
inferior_ptid = lwp_from_thread (inferior_ptid);
- /* Forget about the child's process ID. We shouldn't need it
- anymore. */
- proc_handle.pid = 0;
-
target_beneath->to_detach (args, from_tty);
+
+ /* Should this be done by detach_command? */
+ target_mourn_inferior ();
}
static int
int loop = 0;
/* Bail out early if we're not at a thread event breakpoint. */
- stop_pc = read_pc_pid (ptid) - DECR_PC_AFTER_BREAK;
+ stop_pc = read_pc_pid (ptid) - gdbarch_decr_pc_after_break (current_gdbarch);
if (stop_pc != td_create_bp_addr && stop_pc != td_death_bp_addr)
return;
if (err == TD_NOMSG)
return;
- error ("Cannot get thread event message: %s",
+ error (_("Cannot get thread event message: %s"),
thread_db_err_str (err));
}
err = td_thr_get_info_p (msg.th_p, &ti);
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));
- ptid = BUILD_THREAD (ti.ti_tid, GET_PID (ptid));
+ ptid = ptid_build (GET_PID (ptid), ti.ti_lid, ti.ti_tid);
switch (msg.event)
{
case TD_DEATH:
if (!in_thread_list (ptid))
- error ("Spurious thread death event.");
+ error (_("Spurious thread death event."));
detach_thread (ptid, 1);
break;
default:
- error ("Spurious thread event.");
+ error (_("Spurious thread event."));
}
}
while (loop);
ptid = target_beneath->to_wait (ptid, ourstatus);
- if (proc_handle.pid == 0)
- /* The current child process isn't the actual multi-threaded
- program yet, so don't try to do any special thread-specific
- post-processing and bail out early. */
- return ptid;
-
- if (ourstatus->kind == TARGET_WAITKIND_EXITED)
+ if (ourstatus->kind == TARGET_WAITKIND_EXITED
+ || ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
return pid_to_ptid (-1);
- if (ourstatus->kind == TARGET_WAITKIND_STOPPED
- && ourstatus->value.sig == TARGET_SIGNAL_TRAP)
- /* Check for a thread event. */
- check_event (ptid);
-
- if (!ptid_equal (trap_ptid, null_ptid))
- trap_ptid = thread_from_lwp (trap_ptid);
-
- /* Change the ptid back into the higher level PID + TID format.
- If the thread is dead and no longer on the thread list, we will
- get back a dead ptid. This can occur if the thread death event
- gets postponed by other simultaneous events. In such a case,
- we want to just ignore the event and continue on. */
- ptid = thread_from_lwp (ptid);
- if (GET_PID (ptid) == -1)
- ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
-
- return ptid;
-}
-
-static int
-thread_db_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
- struct mem_attrib *attrib, struct target_ops *target)
-{
- struct cleanup *old_chain = save_inferior_ptid ();
- int xfer;
-
- if (is_thread (inferior_ptid))
+ if (ourstatus->kind == TARGET_WAITKIND_EXECD)
{
- /* FIXME: This seems to be necessary to make sure breakpoints
- are removed. */
- if (!target_thread_alive (inferior_ptid))
- inferior_ptid = pid_to_ptid (GET_PID (inferior_ptid));
- else
- inferior_ptid = lwp_from_thread (inferior_ptid);
- }
+ remove_thread_event_breakpoints ();
+ unpush_target (&thread_db_ops);
+ using_thread_db = 0;
- xfer =
- target_beneath->deprecated_xfer_memory (memaddr, myaddr, len, write,
- attrib, target);
-
- do_cleanups (old_chain);
- return xfer;
-}
-
-static void
-thread_db_fetch_registers (int regno)
-{
- struct thread_info *thread_info;
- prgregset_t gregset;
- gdb_prfpregset_t fpregset;
- td_err_e err;
-
- if (!is_thread (inferior_ptid))
- {
- /* Pass the request to the target beneath us. */
- target_beneath->to_fetch_registers (regno);
- return;
+ return pid_to_ptid (GET_PID (ptid));
}
- thread_info = find_thread_pid (inferior_ptid);
- thread_db_map_id2thr (thread_info, 1);
-
- err = td_thr_getgregs_p (&thread_info->private->th, gregset);
- if (err != TD_OK)
- error ("Cannot fetch general-purpose registers for thread %ld: %s",
- (long) GET_THREAD (inferior_ptid), thread_db_err_str (err));
+ /* If we do not know about the main thread yet, this would be a good time to
+ find it. */
+ if (ourstatus->kind == TARGET_WAITKIND_STOPPED && !have_threads ())
+ thread_db_find_new_threads ();
- err = td_thr_getfpregs_p (&thread_info->private->th, &fpregset);
- if (err != TD_OK)
- error ("Cannot get floating-point registers for thread %ld: %s",
- (long) GET_THREAD (inferior_ptid), thread_db_err_str (err));
-
- /* Note that we must call supply_gregset after calling the thread_db
- routines because the thread_db routines call ps_lgetgregs and
- friends which clobber GDB's register cache. */
- supply_gregset ((gdb_gregset_t *) gregset);
- supply_fpregset (&fpregset);
-}
-
-static void
-thread_db_store_registers (int regno)
-{
- prgregset_t gregset;
- gdb_prfpregset_t fpregset;
- td_err_e err;
- struct thread_info *thread_info;
-
- if (!is_thread (inferior_ptid))
- {
- /* Pass the request to the target beneath us. */
- target_beneath->to_store_registers (regno);
- return;
- }
-
- thread_info = find_thread_pid (inferior_ptid);
- thread_db_map_id2thr (thread_info, 1);
+ if (ourstatus->kind == TARGET_WAITKIND_STOPPED
+ && ourstatus->value.sig == TARGET_SIGNAL_TRAP)
+ /* Check for a thread event. */
+ check_event (ptid);
- if (regno != -1)
+ if (have_threads ())
{
- char raw[MAX_REGISTER_SIZE];
-
- deprecated_read_register_gen (regno, raw);
- thread_db_fetch_registers (-1);
- regcache_raw_supply (current_regcache, regno, raw);
+ /* Change ptids back into the higher level PID + TID format. If
+ the thread is dead and no longer on the thread list, we will
+ get back a dead ptid. This can occur if the thread death
+ event gets postponed by other simultaneous events. In such a
+ case, we want to just ignore the event and continue on. */
+
+ if (!ptid_equal (trap_ptid, null_ptid))
+ trap_ptid = thread_from_lwp (trap_ptid);
+
+ ptid = thread_from_lwp (ptid);
+ if (GET_PID (ptid) == -1)
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
}
- fill_gregset ((gdb_gregset_t *) gregset, -1);
- fill_fpregset (&fpregset, -1);
-
- err = td_thr_setgregs_p (&thread_info->private->th, gregset);
- if (err != TD_OK)
- error ("Cannot store general-purpose registers for thread %ld: %s",
- (long) GET_THREAD (inferior_ptid), thread_db_err_str (err));
- err = td_thr_setfpregs_p (&thread_info->private->th, &fpregset);
- if (err != TD_OK)
- error ("Cannot store floating-point registers for thread %ld: %s",
- (long) GET_THREAD (inferior_ptid), thread_db_err_str (err));
+ return ptid;
}
static void
target_beneath->to_kill ();
}
-static void
-thread_db_create_inferior (char *exec_file, char *allargs, char **env,
- int from_tty)
-{
- unpush_target (&thread_db_ops);
- using_thread_db = 0;
- target_beneath->to_create_inferior (exec_file, allargs, env, from_tty);
-}
-
-static void
-thread_db_post_startup_inferior (ptid_t ptid)
-{
- if (proc_handle.pid == 0)
- {
- /* The child process is now the actual multi-threaded
- program. Snatch its process ID... */
- proc_handle.pid = GET_PID (ptid);
-
- /* ...and perform the remaining initialization steps. */
- enable_thread_event_reporting ();
- thread_db_find_new_threads ();
- }
-}
-
static void
thread_db_mourn_inferior (void)
{
- remove_thread_event_breakpoints ();
-
/* Forget about the child's process ID. We shouldn't need it
anymore. */
proc_handle.pid = 0;
target_beneath->to_mourn_inferior ();
+ /* Delete the old thread event breakpoints. Do this after mourning
+ the inferior, so that we don't try to uninsert them. */
+ remove_thread_event_breakpoints ();
+
/* Detach thread_db target ops. */
unpush_target (&thread_db_ops);
using_thread_db = 0;
}
-static int
-thread_db_thread_alive (ptid_t ptid)
-{
- td_thrhandle_t th;
- td_err_e err;
-
- if (is_thread (ptid))
- {
- struct thread_info *thread_info;
- thread_info = find_thread_pid (ptid);
-
- thread_db_map_id2thr (thread_info, 0);
- if (!thread_info->private->th_valid)
- return 0;
-
- err = td_thr_validate_p (&thread_info->private->th);
- if (err != TD_OK)
- return 0;
-
- if (!thread_info->private->ti_valid)
- {
- err =
- td_thr_get_info_p (&thread_info->private->th,
- &thread_info->private->ti);
- if (err != TD_OK)
- return 0;
- thread_info->private->ti_valid = 1;
- }
-
- if (thread_info->private->ti.ti_state == TD_THR_UNKNOWN
- || thread_info->private->ti.ti_state == TD_THR_ZOMBIE)
- return 0; /* A zombie thread. */
-
- return 1;
- }
-
- if (target_beneath->to_thread_alive)
- return target_beneath->to_thread_alive (ptid);
-
- return 0;
-}
-
static int
find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
{
err = td_thr_get_info_p (th_p, &ti);
if (err != TD_OK)
- error ("find_new_threads_callback: cannot get thread info: %s",
+ error (_("find_new_threads_callback: cannot get thread info: %s"),
thread_db_err_str (err));
if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
return 0; /* A zombie -- ignore. */
- ptid = BUILD_THREAD (ti.ti_tid, GET_PID (inferior_ptid));
+ ptid = ptid_build (GET_PID (inferior_ptid), ti.ti_lid, ti.ti_tid);
+
+ if (ti.ti_tid == 0)
+ {
+ /* A thread ID of zero means that this is the main thread, but
+ glibc has not yet initialized thread-local storage and the
+ pthread library. We do not know what the thread's TID will
+ be yet. Just enable event reporting and otherwise ignore
+ it. */
+
+ err = td_thr_event_enable_p (th_p, 1);
+ if (err != TD_OK)
+ error (_("Cannot enable thread event reporting for %s: %s"),
+ target_pid_to_str (ptid), thread_db_err_str (err));
+
+ return 0;
+ }
if (!in_thread_list (ptid))
attach_thread (ptid, th_p, &ti, 1);
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));
+ error (_("Cannot find new threads: %s"), thread_db_err_str (err));
}
static char *
if (is_thread (ptid))
{
static char buf[64];
- td_thrinfo_t *ti_p;
- td_err_e err;
struct thread_info *thread_info;
thread_info = find_thread_pid (ptid);
- thread_db_map_id2thr (thread_info, 0);
- if (!thread_info->private->th_valid)
- {
- snprintf (buf, sizeof (buf), "Thread %ld (Missing)",
- GET_THREAD (ptid));
- return buf;
- }
-
- ti_p = thread_db_get_info (thread_info);
-
- if (ti_p->ti_state == TD_THR_ACTIVE && ti_p->ti_lid != 0)
- {
- snprintf (buf, sizeof (buf), "Thread %ld (LWP %d)",
- (long) ti_p->ti_tid, ti_p->ti_lid);
- }
+ if (thread_info == NULL)
+ snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld) (Missing)",
+ GET_THREAD (ptid), GET_LWP (ptid));
else
- {
- snprintf (buf, sizeof (buf), "Thread %ld (%s)",
- (long) ti_p->ti_tid,
- thread_db_state_str (ti_p->ti_state));
- }
+ snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld)",
+ GET_THREAD (ptid), GET_LWP (ptid));
return buf;
}
return normal_pid_to_str (ptid);
}
-/* Get the address of the thread local variable in OBJFILE which is
- stored at OFFSET within the thread local storage for thread PTID. */
+/* Return a string describing the state of the thread specified by
+ INFO. */
+
+static char *
+thread_db_extra_thread_info (struct thread_info *info)
+{
+ if (info->private->dying)
+ return "Exiting";
+
+ return NULL;
+}
+
+/* Return 1 if this thread has the same LWP as the passed PTID. */
+
+static int
+same_ptid_callback (struct thread_info *thread, void *arg)
+{
+ ptid_t *ptid_p = arg;
+
+ return GET_LWP (thread->ptid) == GET_LWP (*ptid_p);
+}
+
+/* Get the address of the thread local variable in load module LM which
+ is stored at OFFSET within the thread local storage for thread PTID. */
static CORE_ADDR
-thread_db_get_thread_local_address (ptid_t ptid, struct objfile *objfile,
+thread_db_get_thread_local_address (ptid_t ptid,
+ CORE_ADDR lm,
CORE_ADDR offset)
{
+ /* If we have not discovered any threads yet, check now. */
+ if (!is_thread (ptid) && !have_threads ())
+ thread_db_find_new_threads ();
+
+ /* Try to find a matching thread if we still have the LWP ID instead
+ of the thread ID. */
+ if (!is_thread (ptid))
+ {
+ struct thread_info *thread;
+
+ thread = iterate_over_threads (same_ptid_callback, &ptid);
+ if (thread != NULL)
+ ptid = thread->ptid;
+ }
+
if (is_thread (ptid))
{
- int objfile_is_library = (objfile->flags & OBJF_SHARED);
td_err_e err;
void *address;
- CORE_ADDR lm;
struct thread_info *thread_info;
/* glibc doesn't provide the needed interface. */
if (!td_thr_tls_get_addr_p)
- error ("Cannot find thread-local variables in this thread library.");
-
- /* Get the address of the link map for this objfile. */
- lm = svr4_fetch_objfile_link_map (objfile);
+ throw_error (TLS_NO_LIBRARY_SUPPORT_ERROR,
+ _("No TLS library support"));
- /* Whoops, we couldn't find one. Bail out. */
- if (!lm)
- {
- if (objfile_is_library)
- error ("Cannot find shared library `%s' link_map in dynamic"
- " linker's module list", objfile->name);
- else
- error ("Cannot find executable file `%s' link_map in dynamic"
- " linker's module list", objfile->name);
- }
+ /* Caller should have verified that lm != 0. */
+ gdb_assert (lm != 0);
/* Get info about the thread. */
thread_info = find_thread_pid (ptid);
+ gdb_assert (thread_info);
thread_db_map_id2thr (thread_info, 1);
/* Finally, get the address of the variable. */
- err = td_thr_tls_get_addr_p (&thread_info->private->th, (void *) lm,
+ err = td_thr_tls_get_addr_p (&thread_info->private->th,
+ (void *)(size_t) lm,
offset, &address);
#ifdef THREAD_DB_HAS_TD_NOTALLOC
/* The memory hasn't been allocated, yet. */
if (err == TD_NOTALLOC)
- {
/* Now, if libthread_db provided the initialization image's
address, we *could* try to build a non-lvalue value from
the initialization image. */
- if (objfile_is_library)
- error ("The inferior has not yet allocated storage for"
- " thread-local variables in\n"
- "the shared library `%s'\n"
- "for the thread %ld",
- objfile->name, (long) GET_THREAD (ptid));
- else
- error ("The inferior has not yet allocated storage for"
- " thread-local variables in\n"
- "the executable `%s'\n"
- "for the thread %ld",
- objfile->name, (long) GET_THREAD (ptid));
- }
+ throw_error (TLS_NOT_ALLOCATED_YET_ERROR,
+ _("TLS not allocated yet"));
#endif
/* Something else went wrong. */
if (err != TD_OK)
- {
- if (objfile_is_library)
- error ("Cannot find thread-local storage for thread %ld, "
- "shared library %s:\n%s",
- (long) GET_THREAD (ptid),
- objfile->name, thread_db_err_str (err));
- else
- error ("Cannot find thread-local storage for thread %ld, "
- "executable file %s:\n%s",
- (long) GET_THREAD (ptid),
- objfile->name, thread_db_err_str (err));
- }
+ throw_error (TLS_GENERIC_ERROR,
+ (("%s")), thread_db_err_str (err));
/* Cast assuming host == target. Joy. */
- return (CORE_ADDR) address;
+ /* Do proper sign extension for the target. */
+ gdb_assert (exec_bfd);
+ return (bfd_get_sign_extend_vma (exec_bfd) > 0
+ ? (CORE_ADDR) (intptr_t) address
+ : (CORE_ADDR) (uintptr_t) address);
}
if (target_beneath->to_get_thread_local_address)
- return target_beneath->to_get_thread_local_address (ptid, objfile,
- offset);
-
- error ("Cannot find thread-local values on this target.");
+ return target_beneath->to_get_thread_local_address (ptid, lm, offset);
+ else
+ throw_error (TLS_GENERIC_ERROR,
+ _("TLS not supported on this target"));
}
static void
thread_db_ops.to_shortname = "multi-thread";
thread_db_ops.to_longname = "multi-threaded child process.";
thread_db_ops.to_doc = "Threads and pthreads support.";
- thread_db_ops.to_attach = thread_db_attach;
thread_db_ops.to_detach = thread_db_detach;
thread_db_ops.to_resume = thread_db_resume;
thread_db_ops.to_wait = thread_db_wait;
- thread_db_ops.to_fetch_registers = thread_db_fetch_registers;
- thread_db_ops.to_store_registers = thread_db_store_registers;
- thread_db_ops.deprecated_xfer_memory = thread_db_xfer_memory;
thread_db_ops.to_kill = thread_db_kill;
- thread_db_ops.to_create_inferior = thread_db_create_inferior;
- thread_db_ops.to_post_startup_inferior = thread_db_post_startup_inferior;
thread_db_ops.to_mourn_inferior = thread_db_mourn_inferior;
- thread_db_ops.to_thread_alive = thread_db_thread_alive;
thread_db_ops.to_find_new_threads = thread_db_find_new_threads;
thread_db_ops.to_pid_to_str = thread_db_pid_to_str;
thread_db_ops.to_stratum = thread_stratum;
thread_db_ops.to_has_thread_control = tc_schedlock;
thread_db_ops.to_get_thread_local_address
= thread_db_get_thread_local_address;
+ thread_db_ops.to_extra_thread_info = thread_db_extra_thread_info;
thread_db_ops.to_magic = OPS_MAGIC;
}
add_target (&thread_db_ops);
/* Add ourselves to objfile event chain. */
- target_new_objfile_chain = deprecated_target_new_objfile_hook;
- deprecated_target_new_objfile_hook = thread_db_new_objfile;
+ observer_attach_new_objfile (thread_db_new_objfile);
}
}