diff options
author | Simon Marchi <simon.marchi@efficios.com> | 2020-02-13 16:27:51 -0500 |
---|---|---|
committer | Simon Marchi <simon.marchi@efficios.com> | 2020-02-13 16:27:51 -0500 |
commit | feacfcacaac9f7e62f467a33c4ae54c56501ed18 (patch) | |
tree | 0e083366350e9fd279bec8c7b12413c90b9f255d /gdbserver/thread-db.cc | |
parent | 06b3c5bdb018262d7c09cda9d4637015c7ebe779 (diff) | |
download | gdb-feacfcacaac9f7e62f467a33c4ae54c56501ed18.zip gdb-feacfcacaac9f7e62f467a33c4ae54c56501ed18.tar.gz gdb-feacfcacaac9f7e62f467a33c4ae54c56501ed18.tar.bz2 |
gdbserver: rename source files to .cc
For the same reasons outlined in the previous patch, this patch renames
gdbserver source files to .cc.
I have moved the "-x c++" switch to only those rules that require it.
gdbserver/ChangeLog:
* Makefile.in: Rename source files from .c to .cc.
* %.c: Rename to %.cc.
* configure.ac: Rename server.c to server.cc.
* configure: Re-generate.
Diffstat (limited to 'gdbserver/thread-db.cc')
-rw-r--r-- | gdbserver/thread-db.cc | 910 |
1 files changed, 910 insertions, 0 deletions
diff --git a/gdbserver/thread-db.cc b/gdbserver/thread-db.cc new file mode 100644 index 0000000..2bb6d28 --- /dev/null +++ b/gdbserver/thread-db.cc @@ -0,0 +1,910 @@ +/* Thread management interface, for the remote server for GDB. + Copyright (C) 2002-2020 Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, see <http://www.gnu.org/licenses/>. */ + +#include "server.h" + +#include "linux-low.h" + +#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" + +#ifndef USE_LIBTHREAD_DB_DIRECTLY +#include <dlfcn.h> +#endif +#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; + + /* 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; + +#ifndef USE_LIBTHREAD_DB_DIRECTLY + /* Handle of the libthread_db from dlopen. */ + void *handle; +#endif + + /* 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; +}; + +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 const char * +thread_db_err_str (td_err_e err) +{ + static char buf[64]; + + switch (err) + { + case TD_OK: + return "generic 'call succeeded'"; + case TD_ERR: + return "generic error"; + case TD_NOTHR: + return "no thread to satisfy query"; + case TD_NOSV: + return "no sync handle to satisfy query"; + case TD_NOLWP: + return "no LWP to satisfy query"; + case TD_BADPH: + return "invalid process handle"; + case TD_BADTH: + return "invalid thread handle"; + case TD_BADSH: + return "invalid synchronization handle"; + case TD_BADTA: + return "invalid thread agent"; + case TD_BADKEY: + return "invalid key"; + case TD_NOMSG: + return "no event message for getmsg"; + case TD_NOFPREGS: + return "FPU register set not available"; + case TD_NOLIBTHREAD: + return "application not linked with libthread"; + case TD_NOEVENT: + return "requested event is not supported"; + case TD_NOCAPAB: + return "capability not available"; + case TD_DBERR: + return "debugger service failed"; + case TD_NOAPLIC: + return "operation not applicable to"; + case TD_NOTSD: + return "no thread-specific data for this thread"; + case TD_MALLOC: + return "malloc failed"; + case TD_PARTIALREG: + 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: + xsnprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err); + return buf; + } +} + +#if 0 +static char * +thread_db_state_str (td_thr_state_e state) +{ + static char buf[64]; + + 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: + xsnprintf (buf, sizeof (buf), "unknown thread_db state %d", state); + return buf; + } +} +#endif + +/* 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 lwp_info *lwp; + struct thread_db *thread_db = current_process ()->priv->thread_db; + int lwpid = ptid.lwp (); + + thread_info *thread = find_thread_ptid (ptid); + lwp = get_thread_lwp (thread); + if (lwp->thread_known) + return 1; + + /* 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)); + + err = thread_db->td_thr_get_info_p (&th, &ti); + if (err != TD_OK) + error ("Cannot get thread info for LWP %d: %s", + lwpid, thread_db_err_str (err)); + + 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; + + lwp->thread_known = 1; + lwp->th = th; + lwp->thread_handle = ti.ti_tid; + + return 1; +} + +/* 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) + 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; +} + +/* Attach thread if we haven't seen it yet. + Increment *COUNTER if we have attached a new thread. + Return false on failure. */ + +static int +maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p, + int *counter) +{ + struct lwp_info *lwp; + + lwp = find_lwp_pid (ptid_t (ti_p->ti_lid)); + if (lwp != NULL) + return 1; + + 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 = 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; + + 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) + { + *address = (CORE_ADDR) (uintptr_t) addr; + return 0; + } + else + 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 +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; + + 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) + { + if (debug_threads) + debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err)); + free (tdb); + proc->priv->thread_db = NULL; + return 0; + } + + 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; +} + +#else + +static int +try_thread_db_load_1 (void *handle) +{ + 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; + + 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; + } + + 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) + { + 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) + debug_printf ("Host %s resolved to: %s.\n", library, libpath); + } + } +#endif + + 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)) + { + 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; + } + + 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. */ + +static int +thread_db_load_search (void) +{ + int rc = 0; + + if (libthread_db_search_path == NULL) + libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH); + + std::vector<gdb::unique_xmalloc_ptr<char>> dir_vec + = dirnames_to_char_ptr_vec (libthread_db_search_path); + + 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; + + 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; +} + +#endif /* USE_LIBTHREAD_DB_DIRECTLY */ + +int +thread_db_init (void) +{ + struct process_info *proc = current_process (); + + /* 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. + + This isn't the only place in gdbserver that assumes that the first + process in the list is the thread group leader. */ + + 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 +switch_to_process (struct process_info *proc) +{ + int pid = pid_of (proc); + + 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->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 + + if (td_ta_clear_event_p != NULL) + { + struct thread_info *saved_thread = current_thread; + td_thr_events_t events; + + 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) + { + disable_thread_event_reporting (proc); + } +} + +/* Disconnect from libthread_db and free resources. */ + +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; + +#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."); +} |