X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fthread-db.c;h=c6b43a06cca7adee05ac4bfd39d1443a32b60192;hb=cf950fd4dd4581849a445a76b57514d72074927d;hp=f3d57a54d622eb323061d34a023fcb81dca058a0;hpb=0d62e5e8077eecf77e9b7b5dc0d2689d051a3ab3;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index f3d57a54d6..c6b43a06cc 100644 --- a/gdb/gdbserver/thread-db.c +++ b/gdb/gdbserver/thread-db.c @@ -1,6 +1,5 @@ /* Thread management interface, for the remote server for GDB. - Copyright 2002 - Free Software Foundation, Inc. + Copyright (C) 2002-2019 Free Software Foundation, Inc. Contributed by MontaVista Software. @@ -8,7 +7,7 @@ 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, @@ -17,44 +16,60 @@ 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 . */ #include "server.h" #include "linux-low.h" -extern int debug_threads; +#include "debug.h" +#include "gdb_proc_service.h" +#include "nat/gdb_thread_db.h" +#include "gdbsupport/gdb_vecs.h" +#include "nat/linux-procfs.h" +#include "gdbsupport/scoped_restore.h" -#ifdef HAVE_THREAD_DB_H -#include +#ifndef USE_LIBTHREAD_DB_DIRECTLY +#include #endif +#include +#include -/* Correct for all GNU/Linux targets (for quite some time). */ -#define GDB_GREGSET_T elf_gregset_t -#define GDB_FPREGSET_T elf_fpregset_t +struct thread_db +{ + /* Structure that identifies the child process for the + interface. */ + struct ps_prochandle proc_handle; -#ifndef HAVE_ELF_FPREGSET_T -/* Make sure we have said types. Not all platforms bring in - via . */ -#ifdef HAVE_LINUX_ELF_H -#include -#endif -#endif + /* Connection to the libthread_db library. */ + td_thragent_t *thread_agent; + + /* If this flag has been set, we've already asked GDB for all + symbols we might need; assume symbol cache misses are + failures. */ + int all_symbols_looked_up; -#include "../gdb_proc_service.h" +#ifndef USE_LIBTHREAD_DB_DIRECTLY + /* Handle of the libthread_db from dlopen. */ + void *handle; +#endif -/* Structure that identifies the child process for the - interface. */ -static struct ps_prochandle proc_handle; + /* Addresses of libthread_db functions. */ + 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; +}; -/* Connection to the libthread_db library. */ -static td_thragent_t *thread_agent; +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); -static char * +static const char * thread_db_err_str (td_err_e err) { static char buf[64]; @@ -103,8 +118,12 @@ thread_db_err_str (td_err_e err) return "only part of register set was written/read"; case TD_NOXREGS: return "X register set not available for this thread"; +#ifdef HAVE_TD_VERSION + case TD_VERSION: + return "version mismatch between libthread_db and libpthread"; +#endif default: - snprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err); + xsnprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err); return buf; } } @@ -130,213 +149,762 @@ thread_db_state_str (td_thr_state_e state) case TD_THR_STOPPED_ASLEEP: return "stopped by debugger AND blocked"; default: - snprintf (buf, sizeof (buf), "unknown thread_db state %d", state); + xsnprintf (buf, sizeof (buf), "unknown thread_db state %d", state); return buf; } } #endif -static void -thread_db_create_event (CORE_ADDR where) +/* Get thread info about PTID, accessing memory via the current + thread. */ + +static int +find_one_thread (ptid_t ptid) { - td_event_msg_t msg; + td_thrhandle_t th; + td_thrinfo_t ti; td_err_e err; - struct inferior_linux_data *tdata; + struct lwp_info *lwp; + struct thread_db *thread_db = current_process ()->priv->thread_db; + int lwpid = ptid.lwp (); - if (debug_threads) - fprintf (stderr, "Thread creation event.\n"); + thread_info *thread = find_thread_ptid (ptid); + lwp = get_thread_lwp (thread); + if (lwp->thread_known) + return 1; - tdata = inferior_target_data (current_inferior); + /* Get information about this thread. */ + 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)); - /* 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 = td_ta_event_getmsg (thread_agent, &msg); + err = thread_db->td_thr_get_info_p (&th, &ti); if (err != TD_OK) - fprintf (stderr, "thread getmsg err: %s\n", - thread_db_err_str (err)); + error ("Cannot get thread info for LWP %d: %s", + lwpid, thread_db_err_str (err)); + + if (debug_threads) + debug_printf ("Found thread %ld (LWP %d)\n", + (unsigned long) ti.ti_tid, ti.ti_lid); + + if (lwpid != ti.ti_lid) + { + warning ("PID mismatch! Expected %ld, got %ld", + (long) lwpid, (long) ti.ti_lid); + return 0; + } + + /* 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) + return 0; - /* msg.event == TD_EVENT_CREATE */ + lwp->thread_known = 1; + lwp->th = th; + lwp->thread_handle = ti.ti_tid; - find_new_threads_callback (msg.th_p, NULL); + return 1; } -#if 0 -static void -thread_db_death_event (CORE_ADDR where) +/* Attach a thread. Return true on success. */ + +static int +attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) { + struct process_info *proc = current_process (); + int pid = pid_of (proc); + ptid_t ptid = ptid_t (pid, ti_p->ti_lid, 0); + struct lwp_info *lwp; + int err; + if (debug_threads) - fprintf (stderr, "Thread death event.\n"); + debug_printf ("Attaching to thread %ld (LWP %d)\n", + (unsigned long) ti_p->ti_tid, ti_p->ti_lid); + err = linux_attach_lwp (ptid); + if (err != 0) + { + 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; + } + + lwp = find_lwp_pid (ptid); + gdb_assert (lwp != NULL); + lwp->thread_known = 1; + lwp->th = *th_p; + lwp->thread_handle = ti_p->ti_tid; + + return 1; } -#endif + +/* Attach thread if we haven't seen it yet. + Increment *COUNTER if we have attached a new thread. + Return false on failure. */ static int -thread_db_enable_reporting () +maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p, + int *counter) { - td_thr_events_t events; - td_notify_t notify; - td_err_e err; + struct lwp_info *lwp; - /* Set the process wide mask saying which events we're interested in. */ - td_event_emptyset (&events); - td_event_addset (&events, TD_CREATE); + lwp = find_lwp_pid (ptid_t (ti_p->ti_lid)); + if (lwp != NULL) + return 1; -#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 + if (!attach_thread (th_p, ti_p)) + return 0; + + if (counter != NULL) + *counter += 1; + + return 1; +} + +static int +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 ()->priv->thread_db; - err = td_ta_set_event (thread_agent, &events); + 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) { - warning ("Unable to set global thread event mask: %s", - thread_db_err_str (err)); + /* 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; } - /* Get address for thread creation breakpoint. */ - err = td_ta_event_addr (thread_agent, TD_CREATE, ¬ify); + /* Check for zombies. */ + if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) + return 0; + + if (!maybe_attach_thread (th_p, &ti, (int *) data)) + { + /* Terminate iteration early: we might be looking at stale data in + the inferior. The thread_db_find_new_threads will retry. */ + return 1; + } + + return 0; +} + +static void +thread_db_find_new_threads (void) +{ + td_err_e err; + ptid_t ptid = current_ptid; + struct thread_db *thread_db = current_process ()->priv->thread_db; + int loop, iteration; + + /* This function is only called when we first initialize thread_db. + First locate the initial thread. If it is not ready for + debugging yet, then stop. */ + if (find_one_thread (ptid) == 0) + return; + + /* Require 4 successive iterations which do not find any new threads. + The 4 is a heuristic: there is an inherent race here, and I have + seen that 2 iterations in a row are not always sufficient to + "capture" all threads. */ + for (loop = 0, iteration = 0; loop < 4; ++loop, ++iteration) + { + int new_thread_count = 0; + + /* Iterate over all user-space threads to discover new threads. */ + err = thread_db->td_ta_thr_iter_p (thread_db->thread_agent, + find_new_threads_callback, + &new_thread_count, + TD_THR_ANY_STATE, + TD_THR_LOWEST_PRIORITY, + TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); + if (debug_threads) + debug_printf ("Found %d threads in iteration %d.\n", + new_thread_count, iteration); + + if (new_thread_count != 0) + { + /* Found new threads. Restart iteration from beginning. */ + loop = -1; + } + } if (err != TD_OK) + error ("Cannot find new threads: %s", thread_db_err_str (err)); +} + +/* Cache all future symbols that thread_db might request. We can not + request symbols at arbitrary states in the remote protocol, only + when the client tells us that new symbols are available. So when + we load the thread library, make sure to check the entire list. */ + +static void +thread_db_look_up_symbols (void) +{ + struct thread_db *thread_db = current_process ()->priv->thread_db; + const char **sym_list; + CORE_ADDR unused; + + for (sym_list = thread_db->td_symbol_list_p (); *sym_list; sym_list++) + look_up_one_symbol (*sym_list, &unused, 1); + + /* We're not interested in any other libraries loaded after this + point, only in symbols in libpthread.so. */ + thread_db->all_symbols_looked_up = 1; +} + +int +thread_db_look_up_one_symbol (const char *name, CORE_ADDR *addrp) +{ + 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 + anything not in the cache must not exist; we're not interested + in any libraries loaded after that point, only in symbols in + libpthread.so. It might not be an appropriate time to look + up a symbol, e.g. while we're trying to fetch registers. */ + return look_up_one_symbol (name, addrp, may_ask_gdb); +} + +int +thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, + CORE_ADDR load_module, CORE_ADDR *address) +{ + psaddr_t addr; + td_err_e err; + struct lwp_info *lwp; + struct thread_info *saved_thread; + struct process_info *proc; + struct thread_db *thread_db; + + proc = get_thread_process (thread); + 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) + return TD_ERR; + + /* If td_thr_tls_get_addr is missing rather do not expect td_thr_tlsbase + could work. */ + if (thread_db->td_thr_tls_get_addr_p == NULL + || (load_module == 0 && thread_db->td_thr_tlsbase_p == NULL)) + return -1; + + lwp = get_thread_lwp (thread); + if (!lwp->thread_known) + find_one_thread (thread->id); + if (!lwp->thread_known) + return TD_NOTHR; + + saved_thread = current_thread; + current_thread = thread; + + if (load_module != 0) + { + /* 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 = thread_db->td_thr_tls_get_addr_p (&lwp->th, + (psaddr_t) (uintptr_t) load_module, + offset, &addr); + } + else + { + /* This code path handles the case of -static -pthread executables: + https://sourceware.org/ml/libc-help/2014-03/msg00024.html + For older GNU libc r_debug.r_map is NULL. For GNU libc after + PR libc/16831 due to GDB PR threads/16954 LOAD_MODULE is also NULL. + The constant number 1 depends on GNU __libc_setup_tls + initialization of l_tls_modid to 1. */ + err = thread_db->td_thr_tlsbase_p (&lwp->th, 1, &addr); + addr = (char *) addr + offset; + } + + current_thread = saved_thread; + if (err == TD_OK) { - warning ("Unable to get location for thread creation breakpoint: %s", - thread_db_err_str (err)); + *address = (CORE_ADDR) (uintptr_t) addr; return 0; } - set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, - thread_db_create_event); + else + return err; +} -#if 0 - /* Don't concern ourselves with reported thread deaths, only - with actual thread deaths (via wait). */ +/* 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 +thread_db_load_search (void) +{ + td_err_e err; + struct thread_db *tdb; + struct process_info *proc = current_process (); + + gdb_assert (proc->priv->thread_db == NULL); + + tdb = XCNEW (struct thread_db); + proc->priv->thread_db = tdb; - /* Get address for thread death breakpoint. */ - err = td_ta_event_addr (thread_agent, TD_DEATH, ¬ify); + tdb->td_ta_new_p = &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) { - warning ("Unable to get location for thread death breakpoint: %s", - thread_db_err_str (err)); - return; + if (debug_threads) + debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err)); + free (tdb); + proc->priv->thread_db = NULL; + return 0; } - set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, - thread_db_death_event); -#endif + + tdb->td_ta_map_lwp2thr_p = &td_ta_map_lwp2thr; + tdb->td_thr_get_info_p = &td_thr_get_info; + tdb->td_ta_thr_iter_p = &td_ta_thr_iter; + tdb->td_symbol_list_p = &td_symbol_list; + + /* These are not essential. */ + tdb->td_thr_tls_get_addr_p = &td_thr_tls_get_addr; + tdb->td_thr_tlsbase_p = &td_thr_tlsbase; return 1; } -static void -maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) +#else + +static int +try_thread_db_load_1 (void *handle) { td_err_e err; - struct thread_info *inferior; - struct process_info *process; + struct thread_db *tdb; + struct process_info *proc = current_process (); + + gdb_assert (proc->priv->thread_db == NULL); + + tdb = XCNEW (struct thread_db); + proc->priv->thread_db = tdb; + + 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) \ + debug_printf ("dlsym: %s\n", dlerror ()); \ + if (required) \ + { \ + free (tdb); \ + proc->priv->thread_db = NULL; \ + return 0; \ + } \ + } \ + } \ + while (0) + +#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 (err != TD_OK) + { + if (debug_threads) + debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err)); + free (tdb); + proc->priv->thread_db = NULL; + return 0; + } - /* If we are attaching to our first thread, things are a little - different. */ - if (all_threads.head == all_threads.tail) + 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_DLSYM (tdb, td_thr_tls_get_addr)); + CHK (0, TDB_DLSYM (tdb, td_thr_tlsbase)); + +#undef CHK +#undef TDB_DLSYM + + return 1; +} + +#ifdef HAVE_DLADDR + +/* 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; +} + +#endif + +static int +try_thread_db_load (const char *library) +{ + void *handle; + + if (debug_threads) + debug_printf ("Trying host libthread_db library: %s.\n", + library); + handle = dlopen (library, RTLD_NOW); + if (handle == NULL) + { + if (debug_threads) + debug_printf ("dlopen failed: %s.\n", dlerror ()); + return 0; + } + +#ifdef HAVE_DLADDR + if (debug_threads && strchr (library, '/') == NULL) { - inferior = (struct thread_info *) all_threads.head; - process = get_thread_process (inferior); - if (process->thread_known == 0) + void *td_init; + + td_init = dlsym (handle, "td_init"); + if (td_init != NULL) { - /* Switch to indexing the threads list by TID. */ - change_inferior_id (&all_threads, ti_p->ti_tid); - goto found; + const char *const libpath = dladdr_to_soname (td_init); + + if (libpath != NULL) + debug_printf ("Host %s resolved to: %s.\n", library, libpath); } } - - inferior = (struct thread_info *) find_inferior_id (&all_threads, - ti_p->ti_tid); - if (inferior != NULL) - return; +#endif - if (debug_threads) - fprintf (stderr, "Attaching to thread %ld (LWP %d)\n", - ti_p->ti_tid, ti_p->ti_lid); - linux_attach_lwp (ti_p->ti_lid, ti_p->ti_tid); - inferior = (struct thread_info *) find_inferior_id (&all_threads, - ti_p->ti_tid); - if (inferior == NULL) + if (try_thread_db_load_1 (handle)) + return 1; + + /* This library "refused" to work on current inferior. */ + dlclose (handle); + return 0; +} + +/* Handle $sdir in libthread-db-search-path. + Look for libthread_db in the system dirs, or wherever a plain + dlopen(file_without_path) will look. + The result is true for success. */ + +static int +try_thread_db_load_from_sdir (void) +{ + return try_thread_db_load (LIBTHREAD_DB_SO); +} + +/* Try to load libthread_db from directory DIR of length DIR_LEN. + The result is true for success. */ + +static int +try_thread_db_load_from_dir (const char *dir, size_t dir_len) +{ + char path[PATH_MAX]; + + if (dir_len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path)) { - warning ("Could not attach to thread %ld (LWP %d)\n", - ti_p->ti_tid, ti_p->ti_lid); - return; + char *cp = (char *) xmalloc (dir_len + 1); + + memcpy (cp, dir, dir_len); + cp[dir_len] = '\0'; + warning (_("libthread-db-search-path component too long," + " ignored: %s."), cp); + free (cp); + return 0; } - process = inferior_target_data (inferior); + memcpy (path, dir, dir_len); + path[dir_len] = '/'; + strcpy (path + dir_len + 1, LIBTHREAD_DB_SO); + return try_thread_db_load (path); +} + +/* Search libthread_db_search_path for libthread_db which "agrees" + to work on current inferior. + The result is true for success. */ -found: - new_thread_notify (ti_p->ti_tid); +static int +thread_db_load_search (void) +{ + int rc = 0; - process->tid = ti_p->ti_tid; - process->lwpid = ti_p->ti_lid; + if (libthread_db_search_path == NULL) + libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH); - process->thread_known = 1; - err = td_thr_event_enable (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)); + std::vector> dir_vec + = dirnames_to_char_ptr_vec (libthread_db_search_path); + + for (const gdb::unique_xmalloc_ptr &this_dir_up : dir_vec) + { + char *this_dir = this_dir_up.get (); + const int pdir_len = sizeof ("$pdir") - 1; + size_t this_dir_len; + + this_dir_len = strlen (this_dir); + + if (strncmp (this_dir, "$pdir", pdir_len) == 0 + && (this_dir[pdir_len] == '\0' + || this_dir[pdir_len] == '/')) + { + /* We don't maintain a list of loaded libraries so we don't know + where libpthread lives. We *could* fetch the info, but we don't + do that yet. Ignore it. */ + } + else if (strcmp (this_dir, "$sdir") == 0) + { + if (try_thread_db_load_from_sdir ()) + { + rc = 1; + break; + } + } + else + { + if (try_thread_db_load_from_dir (this_dir, this_dir_len)) + { + rc = 1; + break; + } + } + } + + if (debug_threads) + debug_printf ("thread_db_load_search returning %d\n", rc); + return rc; } -static int -find_new_threads_callback (const td_thrhandle_t *th_p, void *data) +#endif /* USE_LIBTHREAD_DB_DIRECTLY */ + +int +thread_db_init (void) { - td_thrinfo_t ti; - td_err_e err; + struct process_info *proc = current_process (); - err = td_thr_get_info (th_p, &ti); - if (err != TD_OK) - error ("Cannot get thread info: %s", thread_db_err_str (err)); + /* FIXME drow/2004-10-16: This is the "overall process ID", which + 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. - /* Check for zombies. */ - if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) - return 0; + This isn't the only place in gdbserver that assumes that the first + process in the list is the thread group leader. */ - maybe_attach_thread (th_p, &ti); + if (thread_db_load_search ()) + { + /* 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 void -thread_db_find_new_threads (void) +switch_to_process (struct process_info *proc) { - td_err_e err; + int pid = pid_of (proc); - /* Iterate over all user-space threads to discover new threads. */ - err = td_ta_thr_iter (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)); + current_thread = find_any_thread_of_pid (pid); } -int -thread_db_init () +/* Disconnect from libthread_db and free resources. */ + +static void +disable_thread_event_reporting (struct process_info *proc) { - int err; + 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 + = (td_ta_clear_event_ftype *) dlsym (thread_db->handle, + "td_ta_clear_event"); +#else + td_ta_clear_event_p = &td_ta_clear_event; +#endif - proc_handle.pid = ((struct inferior_list_entry *)current_inferior)->id; + if (td_ta_clear_event_p != NULL) + { + struct thread_info *saved_thread = current_thread; + td_thr_events_t events; - err = td_ta_new (&proc_handle, &thread_agent); - switch (err) + switch_to_process (proc); + + /* Set the process wide mask saying we aren't interested + in any events anymore. */ + td_event_fillset (&events); + (*td_ta_clear_event_p) (thread_db->thread_agent, &events); + + current_thread = saved_thread; + } + } +} + +void +thread_db_detach (struct process_info *proc) +{ + struct thread_db *thread_db = proc->priv->thread_db; + + if (thread_db) { - case TD_NOLIBTHREAD: - /* No thread library was detected. */ - return 0; + disable_thread_event_reporting (proc); + } +} - case TD_OK: - /* The thread library was detected. */ +/* Disconnect from libthread_db and free resources. */ - if (thread_db_enable_reporting () == 0) - return 0; - thread_db_find_new_threads (); - return 1; +void +thread_db_mourn (struct process_info *proc) +{ + struct thread_db *thread_db = proc->priv->thread_db; + if (thread_db) + { + td_ta_delete_ftype *td_ta_delete_p; - default: - warning ("error initializing thread_db library."); +#ifndef USE_LIBTHREAD_DB_DIRECTLY + td_ta_delete_p = (td_ta_delete_ftype *) dlsym (thread_db->handle, "td_ta_delete"); +#else + td_ta_delete_p = &td_ta_delete; +#endif + + if (td_ta_delete_p != NULL) + (*td_ta_delete_p) (thread_db->thread_agent); + +#ifndef USE_LIBTHREAD_DB_DIRECTLY + dlclose (thread_db->handle); +#endif /* USE_LIBTHREAD_DB_DIRECTLY */ + + free (thread_db); + proc->priv->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) +{ + const char *cmd = "set libthread-db-search-path"; + size_t cmd_len = strlen (cmd); + + if (strncmp (mon, cmd, cmd_len) == 0 + && (mon[cmd_len] == '\0' + || mon[cmd_len] == ' ')) + { + const char *cp = mon + cmd_len; + + if (libthread_db_search_path != NULL) + free (libthread_db_search_path); + + /* Skip leading space (if any). */ + while (isspace (*cp)) + ++cp; + + if (*cp == '\0') + cp = LIBTHREAD_DB_SEARCH_PATH; + 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; } + +/* 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."); +}