/* Thread management interface, for the remote server for GDB.
- Copyright (C) 2002-2015 Free Software Foundation, Inc.
+ Copyright (C) 2002-2020 Free Software Foundation, Inc.
Contributed by MontaVista Software.
#include "linux-low.h"
-extern int debug_threads;
-
-static int thread_db_use_events;
-
+#include "debug.h"
#include "gdb_proc_service.h"
#include "nat/gdb_thread_db.h"
-#include "gdb_vecs.h"
+#include "gdbsupport/gdb_vecs.h"
+#include "nat/linux-procfs.h"
+#include "gdbsupport/scoped_restore.h"
#ifndef USE_LIBTHREAD_DB_DIRECTLY
#include <dlfcn.h>
#endif
-
-#include <stdint.h>
#include <limits.h>
#include <ctype.h>
void *handle;
#endif
- /* Thread creation event breakpoint. The code at this location in
- the child process will be called by the pthread library whenever
- a new thread is created. By setting a special breakpoint at this
- location, GDB can detect when a new thread is created. We obtain
- this location via the td_ta_event_addr call. Note that if the
- running kernel supports tracing clones, then we don't need to use
- (and in fact don't use) this magic thread event breakpoint to
- learn about threads. */
- struct breakpoint *td_create_bp;
-
/* Addresses of libthread_db functions. */
- td_err_e (*td_ta_new_p) (struct ps_prochandle * ps, td_thragent_t **ta);
- td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta,
- td_event_msg_t *msg);
- td_err_e (*td_ta_set_event_p) (const td_thragent_t *ta,
- td_thr_events_t *event);
- td_err_e (*td_ta_event_addr_p) (const td_thragent_t *ta,
- td_event_e event, td_notify_t *ptr);
- td_err_e (*td_ta_map_lwp2thr_p) (const td_thragent_t *ta, lwpid_t lwpid,
- td_thrhandle_t *th);
- td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th,
- td_thrinfo_t *infop);
- td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th, int event);
- td_err_e (*td_ta_thr_iter_p) (const td_thragent_t *ta,
- td_thr_iter_f *callback, void *cbdata_p,
- td_thr_state_e state, int ti_pri,
- sigset_t *ti_sigmask_p,
- unsigned int ti_user_flags);
- td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th,
- psaddr_t map_address,
- size_t offset, psaddr_t *address);
- td_err_e (*td_thr_tlsbase_p) (const td_thrhandle_t *th,
- unsigned long int modid,
- psaddr_t *base);
- const char ** (*td_symbol_list_p) (void);
+ td_ta_new_ftype *td_ta_new_p;
+ td_ta_map_lwp2thr_ftype *td_ta_map_lwp2thr_p;
+ td_thr_get_info_ftype *td_thr_get_info_p;
+ td_ta_thr_iter_ftype *td_ta_thr_iter_p;
+ td_thr_tls_get_addr_ftype *td_thr_tls_get_addr_p;
+ td_thr_tlsbase_ftype *td_thr_tlsbase_p;
+ td_symbol_list_ftype *td_symbol_list_p;
};
static char *libthread_db_search_path;
}
#endif
-static int
-thread_db_create_event (CORE_ADDR where)
-{
- td_event_msg_t msg;
- td_err_e err;
- struct lwp_info *lwp;
- struct thread_db *thread_db = current_process ()->private->thread_db;
-
- gdb_assert (thread_db->td_ta_event_getmsg_p != NULL);
-
- if (debug_threads)
- debug_printf ("Thread creation event.\n");
-
- /* FIXME: This assumes we don't get another event.
- In the LinuxThreads implementation, this is safe,
- because all events come from the manager thread
- (except for its own creation, of course). */
- err = thread_db->td_ta_event_getmsg_p (thread_db->thread_agent, &msg);
- if (err != TD_OK)
- fprintf (stderr, "thread getmsg err: %s\n",
- thread_db_err_str (err));
-
- /* If we do not know about the main thread yet, this would be a good time to
- find it. We need to do this to pick up the main thread before any newly
- created threads. */
- lwp = get_thread_lwp (current_thread);
- if (lwp->thread_known == 0)
- find_one_thread (current_thread->entry.id);
-
- /* msg.event == TD_EVENT_CREATE */
-
- find_new_threads_callback (msg.th_p, NULL);
-
- return 0;
-}
-
-static int
-thread_db_enable_reporting (void)
-{
- td_thr_events_t events;
- td_notify_t notify;
- td_err_e err;
- struct thread_db *thread_db = current_process ()->private->thread_db;
-
- if (thread_db->td_ta_set_event_p == NULL
- || thread_db->td_ta_event_addr_p == NULL
- || thread_db->td_ta_event_getmsg_p == NULL)
- /* This libthread_db is missing required support. */
- return 0;
-
- /* Set the process wide mask saying which events we're interested in. */
- td_event_emptyset (&events);
- td_event_addset (&events, TD_CREATE);
-
- err = thread_db->td_ta_set_event_p (thread_db->thread_agent, &events);
- if (err != TD_OK)
- {
- warning ("Unable to set global thread event mask: %s",
- thread_db_err_str (err));
- return 0;
- }
-
- /* Get address for thread creation breakpoint. */
- err = thread_db->td_ta_event_addr_p (thread_db->thread_agent, TD_CREATE,
- ¬ify);
- if (err != TD_OK)
- {
- warning ("Unable to get location for thread creation breakpoint: %s",
- thread_db_err_str (err));
- return 0;
- }
- thread_db->td_create_bp
- = set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
- thread_db_create_event);
-
- return 1;
-}
+/* Get thread info about PTID, accessing memory via the current
+ thread. */
static int
find_one_thread (ptid_t ptid)
td_thrhandle_t th;
td_thrinfo_t ti;
td_err_e err;
- struct thread_info *inferior;
struct lwp_info *lwp;
- struct thread_db *thread_db = current_process ()->private->thread_db;
- int lwpid = ptid_get_lwp (ptid);
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
+ int lwpid = ptid.lwp ();
- inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid);
- lwp = get_thread_lwp (inferior);
+ thread_info *thread = find_thread_ptid (ptid);
+ lwp = get_thread_lwp (thread);
if (lwp->thread_known)
return 1;
if (debug_threads)
debug_printf ("Found thread %ld (LWP %d)\n",
- ti.ti_tid, ti.ti_lid);
+ (unsigned long) ti.ti_tid, ti.ti_lid);
if (lwpid != ti.ti_lid)
{
return 0;
}
- if (thread_db_use_events)
- {
- err = thread_db->td_thr_event_enable_p (&th, 1);
- if (err != TD_OK)
- error ("Cannot enable thread event reporting for %d: %s",
- ti.ti_lid, thread_db_err_str (err));
- }
-
/* If the new thread ID is zero, a final thread ID will be available
later. Do not enable thread debugging yet. */
if (ti.ti_tid == 0)
lwp->thread_known = 1;
lwp->th = th;
+ lwp->thread_handle = ti.ti_tid;
return 1;
}
{
struct process_info *proc = current_process ();
int pid = pid_of (proc);
- ptid_t ptid = ptid_build (pid, ti_p->ti_lid, 0);
+ ptid_t ptid = ptid_t (pid, ti_p->ti_lid, 0);
struct lwp_info *lwp;
int err;
if (debug_threads)
debug_printf ("Attaching to thread %ld (LWP %d)\n",
- ti_p->ti_tid, ti_p->ti_lid);
+ (unsigned long) ti_p->ti_tid, ti_p->ti_lid);
err = linux_attach_lwp (ptid);
if (err != 0)
{
- warning ("Could not attach to thread %ld (LWP %d): %s\n",
- ti_p->ti_tid, ti_p->ti_lid,
- linux_ptrace_attach_fail_reason_string (ptid, err));
+ std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+
+ warning ("Could not attach to thread %ld (LWP %d): %s",
+ (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
+
return 0;
}
gdb_assert (lwp != NULL);
lwp->thread_known = 1;
lwp->th = *th_p;
-
- if (thread_db_use_events)
- {
- td_err_e err;
- struct thread_db *thread_db = proc->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));
- }
+ lwp->thread_handle = ti_p->ti_tid;
return 1;
}
{
struct lwp_info *lwp;
- lwp = find_lwp_pid (pid_to_ptid (ti_p->ti_lid));
+ lwp = find_lwp_pid (ptid_t (ti_p->ti_lid));
if (lwp != NULL)
return 1;
{
td_thrinfo_t ti;
td_err_e err;
- struct thread_db *thread_db = current_process ()->private->thread_db;
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
err = thread_db->td_thr_get_info_p (th_p, &ti);
if (err != TD_OK)
error ("Cannot get thread info: %s", thread_db_err_str (err));
+ if (ti.ti_lid == -1)
+ {
+ /* A thread with kernel thread ID -1 is either a thread that
+ exited and was joined, or a thread that is being created but
+ hasn't started yet, and that is reusing the tcb/stack of a
+ thread that previously exited and was joined. (glibc marks
+ terminated and joined threads with kernel thread ID -1. See
+ glibc PR17707. */
+ if (debug_threads)
+ debug_printf ("thread_db: skipping exited and "
+ "joined thread (0x%lx)\n",
+ (unsigned long) ti.ti_tid);
+ return 0;
+ }
+
/* Check for zombies. */
if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
return 0;
{
td_err_e err;
ptid_t ptid = current_ptid;
- struct thread_db *thread_db = current_process ()->private->thread_db;
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
int loop, iteration;
/* This function is only called when we first initialize thread_db.
static void
thread_db_look_up_symbols (void)
{
- struct thread_db *thread_db = current_process ()->private->thread_db;
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
const char **sym_list;
CORE_ADDR unused;
int
thread_db_look_up_one_symbol (const char *name, CORE_ADDR *addrp)
{
- struct thread_db *thread_db = current_process ()->private->thread_db;
+ struct thread_db *thread_db = current_process ()->priv->thread_db;
int may_ask_gdb = !thread_db->all_symbols_looked_up;
/* If we've passed the call to thread_db_look_up_symbols, then
struct thread_db *thread_db;
proc = get_thread_process (thread);
- thread_db = proc->private->thread_db;
+ thread_db = proc->priv->thread_db;
/* If the thread layer is not (yet) initialized, fail. */
if (thread_db == NULL || !thread_db->all_symbols_looked_up)
lwp = get_thread_lwp (thread);
if (!lwp->thread_known)
- find_one_thread (thread->entry.id);
+ find_one_thread (thread->id);
if (!lwp->thread_known)
return TD_NOTHR;
return err;
}
+/* See linux-low.h. */
+
+bool
+thread_db_thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len)
+{
+ struct thread_db *thread_db;
+ struct lwp_info *lwp;
+ thread_info *thread = find_thread_ptid (ptid);
+
+ if (thread == NULL)
+ return false;
+
+ thread_db = get_thread_process (thread)->priv->thread_db;
+
+ if (thread_db == NULL)
+ return false;
+
+ lwp = get_thread_lwp (thread);
+
+ if (!lwp->thread_known && !find_one_thread (thread->id))
+ return false;
+
+ gdb_assert (lwp->thread_known);
+
+ *handle = (gdb_byte *) &lwp->thread_handle;
+ *handle_len = sizeof (lwp->thread_handle);
+ return true;
+}
+
#ifdef USE_LIBTHREAD_DB_DIRECTLY
static int
struct thread_db *tdb;
struct process_info *proc = current_process ();
- gdb_assert (proc->private->thread_db == NULL);
+ gdb_assert (proc->priv->thread_db == NULL);
- tdb = xcalloc (1, sizeof (*tdb));
- proc->private->thread_db = tdb;
+ tdb = XCNEW (struct thread_db);
+ proc->priv->thread_db = tdb;
tdb->td_ta_new_p = &td_ta_new;
if (debug_threads)
debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err));
free (tdb);
- proc->private->thread_db = NULL;
+ proc->priv->thread_db = NULL;
return 0;
}
tdb->td_ta_thr_iter_p = &td_ta_thr_iter;
tdb->td_symbol_list_p = &td_symbol_list;
- /* This is required only when thread_db_use_events is on. */
- tdb->td_thr_event_enable_p = &td_thr_event_enable;
-
/* These are not essential. */
- tdb->td_ta_event_addr_p = &td_ta_event_addr;
- tdb->td_ta_set_event_p = &td_ta_set_event;
- tdb->td_ta_event_getmsg_p = &td_ta_event_getmsg;
tdb->td_thr_tls_get_addr_p = &td_thr_tls_get_addr;
tdb->td_thr_tlsbase_p = &td_thr_tlsbase;
struct thread_db *tdb;
struct process_info *proc = current_process ();
- gdb_assert (proc->private->thread_db == NULL);
+ gdb_assert (proc->priv->thread_db == NULL);
- tdb = xcalloc (1, sizeof (*tdb));
- proc->private->thread_db = tdb;
+ tdb = XCNEW (struct thread_db);
+ proc->priv->thread_db = tdb;
tdb->handle = handle;
if (required) \
{ \
free (tdb); \
- proc->private->thread_db = NULL; \
+ proc->priv->thread_db = NULL; \
return 0; \
} \
} \
} \
while (0)
- CHK (1, tdb->td_ta_new_p = dlsym (handle, "td_ta_new"));
+#define TDB_DLSYM(tdb, func) \
+ tdb->func ## _p = (func ## _ftype *) dlsym (tdb->handle, #func)
+
+ CHK (1, TDB_DLSYM (tdb, 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 (debug_threads)
debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err));
free (tdb);
- proc->private->thread_db = NULL;
+ proc->priv->thread_db = NULL;
return 0;
}
- CHK (1, tdb->td_ta_map_lwp2thr_p = dlsym (handle, "td_ta_map_lwp2thr"));
- CHK (1, tdb->td_thr_get_info_p = dlsym (handle, "td_thr_get_info"));
- CHK (1, tdb->td_ta_thr_iter_p = dlsym (handle, "td_ta_thr_iter"));
- CHK (1, tdb->td_symbol_list_p = dlsym (handle, "td_symbol_list"));
-
- /* This is required only when thread_db_use_events is on. */
- CHK (thread_db_use_events,
- tdb->td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable"));
+ CHK (1, TDB_DLSYM (tdb, td_ta_map_lwp2thr));
+ CHK (1, TDB_DLSYM (tdb, td_thr_get_info));
+ CHK (1, TDB_DLSYM (tdb, td_ta_thr_iter));
+ CHK (1, TDB_DLSYM (tdb, td_symbol_list));
/* 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"));
- CHK (0, tdb->td_thr_tlsbase_p = dlsym (handle, "td_thr_tlsbase"));
+ CHK (0, TDB_DLSYM (tdb, td_thr_tls_get_addr));
+ CHK (0, TDB_DLSYM (tdb, td_thr_tlsbase));
#undef CHK
+#undef TDB_DLSYM
return 1;
}
const char *const libpath = dladdr_to_soname (td_init);
if (libpath != NULL)
- fprintf (stderr, "Host %s resolved to: %s.\n",
- library, libpath);
+ debug_printf ("Host %s resolved to: %s.\n", library, libpath);
}
}
#endif
if (dir_len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path))
{
- char *cp = xmalloc (dir_len + 1);
+ char *cp = (char *) xmalloc (dir_len + 1);
memcpy (cp, dir, dir_len);
cp[dir_len] = '\0';
static int
thread_db_load_search (void)
{
- VEC (char_ptr) *dir_vec;
- char *this_dir;
- int i, rc = 0;
+ int rc = 0;
if (libthread_db_search_path == NULL)
libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH);
- dir_vec = dirnames_to_char_ptr_vec (libthread_db_search_path);
+ std::vector<gdb::unique_xmalloc_ptr<char>> dir_vec
+ = dirnames_to_char_ptr_vec (libthread_db_search_path);
- for (i = 0; VEC_iterate (char_ptr, dir_vec, i, this_dir); ++i)
+ for (const gdb::unique_xmalloc_ptr<char> &this_dir_up : dir_vec)
{
+ char *this_dir = this_dir_up.get ();
const int pdir_len = sizeof ("$pdir") - 1;
size_t this_dir_len;
}
}
- free_char_ptr_vec (dir_vec);
if (debug_threads)
debug_printf ("thread_db_load_search returning %d\n", rc);
return rc;
#endif /* USE_LIBTHREAD_DB_DIRECTLY */
int
-thread_db_init (int use_events)
+thread_db_init (void)
{
struct process_info *proc = current_process ();
GNU/Linux calls tgid, "thread group ID". When we support
attaching to threads, the original thread may not be the correct
thread. We would have to get the process ID from /proc for NPTL.
- For LinuxThreads we could do something similar: follow the chain
- of parent processes until we find the highest one we're attached
- to, and use its tgid.
This isn't the only place in gdbserver that assumes that the first
process in the list is the thread group leader. */
- thread_db_use_events = use_events;
-
if (thread_db_load_search ())
{
- if (use_events && thread_db_enable_reporting () == 0)
- {
- /* Keep trying; maybe event reporting will work later. */
- thread_db_mourn (proc);
- return 0;
- }
- thread_db_find_new_threads ();
+ /* It's best to avoid td_ta_thr_iter if possible. That walks
+ data structures in the inferior's address space that may be
+ corrupted, or, if the target is running, the list may change
+ while we walk it. In the latter case, it's possible that a
+ thread exits just at the exact time that causes GDBserver to
+ get stuck in an infinite loop. As the kernel supports clone
+ events and /proc/PID/task/ exists, then we already know about
+ all threads in the process. When we need info out of
+ thread_db on a given thread (e.g., for TLS), we'll use
+ find_one_thread then. That uses thread_db entry points that
+ do not walk libpthread's thread list, so should be safe, as
+ well as more efficient. */
+ if (!linux_proc_task_list_dir_exists (pid_of (proc)))
+ thread_db_find_new_threads ();
thread_db_look_up_symbols ();
return 1;
}
return 0;
}
-static int
-any_thread_of (struct inferior_list_entry *entry, void *args)
-{
- int *pid_p = args;
-
- if (ptid_get_pid (entry->id) == *pid_p)
- return 1;
-
- return 0;
-}
-
static void
switch_to_process (struct process_info *proc)
{
int pid = pid_of (proc);
- current_thread =
- (struct thread_info *) find_inferior (&all_threads,
- any_thread_of, &pid);
+ current_thread = find_any_thread_of_pid (pid);
}
/* Disconnect from libthread_db and free resources. */
static void
disable_thread_event_reporting (struct process_info *proc)
{
- struct thread_db *thread_db = proc->private->thread_db;
+ struct thread_db *thread_db = proc->priv->thread_db;
if (thread_db)
{
td_err_e (*td_ta_clear_event_p) (const td_thragent_t *ta,
td_thr_events_t *event);
#ifndef USE_LIBTHREAD_DB_DIRECTLY
- td_ta_clear_event_p = dlsym (thread_db->handle, "td_ta_clear_event");
+ td_ta_clear_event_p
+ = (td_ta_clear_event_ftype *) dlsym (thread_db->handle,
+ "td_ta_clear_event");
#else
td_ta_clear_event_p = &td_ta_clear_event;
#endif
}
}
-static void
-remove_thread_event_breakpoints (struct process_info *proc)
-{
- struct thread_db *thread_db = proc->private->thread_db;
-
- if (thread_db->td_create_bp != NULL)
- {
- struct thread_info *saved_thread = current_thread;
-
- switch_to_process (proc);
-
- delete_breakpoint (thread_db->td_create_bp);
- thread_db->td_create_bp = NULL;
-
- current_thread = saved_thread;
- }
-}
-
void
thread_db_detach (struct process_info *proc)
{
- struct thread_db *thread_db = proc->private->thread_db;
+ struct thread_db *thread_db = proc->priv->thread_db;
if (thread_db)
{
disable_thread_event_reporting (proc);
- remove_thread_event_breakpoints (proc);
}
}
void
thread_db_mourn (struct process_info *proc)
{
- struct thread_db *thread_db = proc->private->thread_db;
+ struct thread_db *thread_db = proc->priv->thread_db;
if (thread_db)
{
- td_err_e (*td_ta_delete_p) (td_thragent_t *);
+ td_ta_delete_ftype *td_ta_delete_p;
#ifndef USE_LIBTHREAD_DB_DIRECTLY
- td_ta_delete_p = dlsym (thread_db->handle, "td_ta_delete");
+ td_ta_delete_p = (td_ta_delete_ftype *) dlsym (thread_db->handle, "td_ta_delete");
#else
td_ta_delete_p = &td_ta_delete;
#endif
#endif /* USE_LIBTHREAD_DB_DIRECTLY */
free (thread_db);
- proc->private->thread_db = NULL;
+ proc->priv->thread_db = NULL;
}
}
/* Tell server.c to perform default processing. */
return 0;
}
+
+/* See linux-low.h. */
+
+void
+thread_db_notice_clone (struct thread_info *parent_thr, ptid_t child_ptid)
+{
+ process_info *parent_proc = get_thread_process (parent_thr);
+ struct thread_db *thread_db = parent_proc->priv->thread_db;
+
+ /* If the thread layer isn't initialized, return. It may just
+ be that the program uses clone, but does not use libthread_db. */
+ if (thread_db == NULL || !thread_db->all_symbols_looked_up)
+ return;
+
+ /* find_one_thread calls into libthread_db which accesses memory via
+ the current thread. Temporarily switch to a thread we know is
+ stopped. */
+ scoped_restore restore_current_thread
+ = make_scoped_restore (¤t_thread, parent_thr);
+
+ if (!find_one_thread (child_ptid))
+ warning ("Cannot find thread after clone.");
+}