X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fthread-db.c;h=1ffb79d5c108ac0aa539e31937602f06c6ffc601;hb=61baf725eca99af2569262d10aca03dcde2698f6;hp=e169a50f3e5517bbe11d08e38ca271a448771ef1;hpb=10e86dd77848e00024b5de420c18e13db9a103c9;p=external%2Fbinutils.git diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index e169a50..1ffb79d 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 (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009 - Free Software Foundation, Inc. + Copyright (C) 2002-2017 Free Software Foundation, Inc. Contributed by MontaVista Software. @@ -25,16 +24,14 @@ extern int debug_threads; -static int thread_db_use_events; - #include "gdb_proc_service.h" -#include "../gdb_thread_db.h" +#include "nat/gdb_thread_db.h" +#include "gdb_vecs.h" +#include "nat/linux-procfs.h" #ifndef USE_LIBTHREAD_DB_DIRECTLY #include #endif - -#include #include #include @@ -47,33 +44,24 @@ struct thread_db /* 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_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); - 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; @@ -135,7 +123,7 @@ thread_db_err_str (td_err_e err) 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; } } @@ -161,91 +149,13 @@ 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 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; - - if (thread_db->td_ta_event_getmsg_p == NULL) - fatal ("unexpected thread_db->td_ta_event_getmsg_p == NULL"); - - if (debug_threads) - fprintf (stderr, "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_inferior); - if (lwp->thread_known == 0) - find_one_thread (lwp->head.id); - - /* msg.event == TD_EVENT_CREATE */ - - find_new_threads_callback (msg.th_p, NULL); - - return 0; -} - -static int -thread_db_enable_reporting () -{ - 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; - } - set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, - thread_db_create_event); - - return 1; -} - -static int find_one_thread (ptid_t ptid) { td_thrhandle_t th; @@ -253,7 +163,7 @@ find_one_thread (ptid_t ptid) td_err_e err; struct thread_info *inferior; struct lwp_info *lwp; - struct thread_db *thread_db = current_process ()->private->thread_db; + struct thread_db *thread_db = current_process ()->priv->thread_db; int lwpid = ptid_get_lwp (ptid); inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid); @@ -273,8 +183,8 @@ find_one_thread (ptid_t ptid) lwpid, thread_db_err_str (err)); if (debug_threads) - fprintf (stderr, "Found thread %ld (LWP %d)\n", - ti.ti_tid, ti.ti_lid); + debug_printf ("Found thread %ld (LWP %d)\n", + (unsigned long) ti.ti_tid, ti.ti_lid); if (lwpid != ti.ti_lid) { @@ -283,14 +193,6 @@ find_one_thread (ptid_t ptid) 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) @@ -307,34 +209,29 @@ find_one_thread (ptid_t ptid) 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_build (pid, ti_p->ti_lid, 0); struct lwp_info *lwp; + int err; 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); - lwp = find_lwp_pid (pid_to_ptid (ti_p->ti_lid)); - if (lwp == NULL) + 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) { - warning ("Could not attach to thread %ld (LWP %d)\n", - ti_p->ti_tid, ti_p->ti_lid); + warning ("Could not attach to thread %ld (LWP %d): %s\n", + (unsigned long) ti_p->ti_tid, ti_p->ti_lid, + linux_ptrace_attach_fail_reason_string (ptid, err)); return 0; } + lwp = find_lwp_pid (ptid); + 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 = current_process ()->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)); - } - return 1; } @@ -366,12 +263,27 @@ 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 ()->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; @@ -390,8 +302,8 @@ static void thread_db_find_new_threads (void) { td_err_e err; - ptid_t ptid = ((struct inferior_list_entry *) current_inferior)->id; - struct thread_db *thread_db = current_process ()->private->thread_db; + 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. @@ -412,11 +324,12 @@ thread_db_find_new_threads (void) 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_THR_ANY_STATE, + TD_THR_LOWEST_PRIORITY, TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); if (debug_threads) - fprintf (stderr, "Found %d threads in iteration %d.\n", - new_thread_count, iteration); + debug_printf ("Found %d threads in iteration %d.\n", + new_thread_count, iteration); if (new_thread_count != 0) { @@ -436,12 +349,30 @@ thread_db_find_new_threads (void) 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; for (sym_list = thread_db->td_symbol_list_p (); *sym_list; sym_list++) - look_up_one_symbol (*sym_list, &unused); + look_up_one_symbol (*sym_list, &unused, 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 @@ -451,35 +382,54 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, psaddr_t addr; td_err_e err; struct lwp_info *lwp; - struct thread_info *saved_inferior; + struct thread_info *saved_thread; struct process_info *proc; 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 (!proc->all_symbols_looked_up) + if (thread_db == NULL || !thread_db->all_symbols_looked_up) return TD_ERR; - if (thread_db->td_thr_tls_get_addr_p == NULL) + /* 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 (lwp->head.id); + find_one_thread (thread->entry.id); if (!lwp->thread_known) return TD_NOTHR; - saved_inferior = current_inferior; - current_inferior = thread; - /* 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); - current_inferior = saved_inferior; + 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; @@ -495,39 +445,35 @@ static int thread_db_load_search (void) { td_err_e err; - struct thread_db tdb; + struct thread_db *tdb; struct process_info *proc = current_process (); - if (proc->private->thread_db != NULL) - fatal ("unexpected: proc->private->thread_db != NULL"); + 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; + 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); + err = tdb->td_ta_new_p (&tdb->proc_handle, &tdb->thread_agent); if (err != TD_OK) { if (debug_threads) - fprintf (stderr, "td_ta_new(): %s\n", thread_db_err_str (err)); + 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; - - /* This is required only when thread_db_use_events is on. */ - tdb.td_thr_event_enable_p = &td_thr_event_enable; + 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_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; - - proc->private->thread_db = xmalloc (sizeof (tdb)); - memcpy (proc->private->thread_db, &tdb, sizeof (tdb)); + tdb->td_thr_tls_get_addr_p = &td_thr_tls_get_addr; + tdb->td_thr_tlsbase_p = &td_thr_tlsbase; return 1; } @@ -538,13 +484,15 @@ static int try_thread_db_load_1 (void *handle) { td_err_e err; - struct thread_db tdb; + struct thread_db *tdb; struct process_info *proc = current_process (); - if (proc->private->thread_db != NULL) - fatal ("unexpected: proc->private->thread_db != NULL"); + gdb_assert (proc->priv->thread_db == NULL); + + tdb = XCNEW (struct thread_db); + proc->priv->thread_db = tdb; - tdb.handle = handle; + tdb->handle = handle; /* Initialize pointers to the dynamic library functions we will use. Essential functions first. */ @@ -555,43 +503,44 @@ try_thread_db_load_1 (void *handle) if ((a) == NULL) \ { \ if (debug_threads) \ - fprintf (stderr, "dlsym: %s\n", dlerror ()); \ + debug_printf ("dlsym: %s\n", dlerror ()); \ if (required) \ - return 0; \ + { \ + free (tdb); \ + 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); + err = tdb->td_ta_new_p (&tdb->proc_handle, &tdb->thread_agent); if (err != TD_OK) { if (debug_threads) - fprintf (stderr, "td_ta_new(): %s\n", thread_db_err_str (err)); + debug_printf ("td_ta_new(): %s\n", thread_db_err_str (err)); + free (tdb); + 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_DLSYM (tdb, td_thr_tls_get_addr)); + CHK (0, TDB_DLSYM (tdb, td_thr_tlsbase)); #undef CHK - - proc->private->thread_db = xmalloc (sizeof (tdb)); - memcpy (proc->private->thread_db, &tdb, sizeof (tdb)); +#undef TDB_DLSYM return 1; } @@ -620,13 +569,13 @@ try_thread_db_load (const char *library) void *handle; if (debug_threads) - fprintf (stderr, "Trying host libthread_db library: %s.\n", - library); + debug_printf ("Trying host libthread_db library: %s.\n", + library); handle = dlopen (library, RTLD_NOW); if (handle == NULL) { if (debug_threads) - fprintf (stderr, "dlopen failed: %s.\n", dlerror ()); + debug_printf ("dlopen failed: %s.\n", dlerror ()); return 0; } @@ -641,8 +590,7 @@ try_thread_db_load (const char *library) 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 @@ -655,73 +603,102 @@ try_thread_db_load (const char *library) 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 -thread_db_load_search (void) +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]; - const char *search_path; - int rc = 0; + + 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) +{ + VEC (char_ptr) *dir_vec; + char *this_dir; + int i, rc = 0; if (libthread_db_search_path == NULL) libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH); - search_path = libthread_db_search_path; - while (*search_path) + dir_vec = dirnames_to_char_ptr_vec (libthread_db_search_path); + + for (i = 0; VEC_iterate (char_ptr, dir_vec, i, this_dir); ++i) { - const char *end = strchr (search_path, ':'); - if (end) + 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) { - size_t len = end - search_path; - if (len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path)) + if (try_thread_db_load_from_sdir ()) { - char *cp = xmalloc (len + 1); - memcpy (cp, search_path, len); - cp[len] = '\0'; - warning ("libthread_db_search_path component too long, " - "ignored: %s.", cp); - free (cp); - search_path += len + 1; - continue; + rc = 1; + break; } - memcpy (path, search_path, len); - path[len] = '\0'; - search_path += len + 1; } else { - size_t len = strlen (search_path); - - if (len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path)) + if (try_thread_db_load_from_dir (this_dir, this_dir_len)) { - warning ("libthread_db_search_path component too long," - " ignored: %s.", search_path); + rc = 1; break; } - memcpy (path, search_path, len + 1); - search_path += len; - } - strcat (path, "/"); - strcat (path, LIBTHREAD_DB_SO); - if (debug_threads) - fprintf (stderr, "thread_db_load_search trying %s\n", path); - if (try_thread_db_load (path)) - { - rc = 1; - break; } } - if (rc == 0) - rc = try_thread_db_load (LIBTHREAD_DB_SO); + free_char_ptr_vec (dir_vec); if (debug_threads) - fprintf (stderr, "thread_db_load_search returning %d\n", rc); + 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 (); @@ -729,61 +706,116 @@ thread_db_init (int use_events) 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_free (proc, 0); - 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 (); - proc->all_symbols_looked_up = 1; return 1; } return 0; } +static int +any_thread_of (struct inferior_list_entry *entry, void *args) +{ + int *pid_p = (int *) 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); +} + /* Disconnect from libthread_db and free resources. */ -void -thread_db_free (struct process_info *proc, int detaching) +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_delete_p) (td_thragent_t *); 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_delete_p = dlsym (thread_db->handle, "td_ta_delete"); + td_ta_clear_event_p + = (td_ta_clear_event_ftype *) dlsym (thread_db->handle, + "td_ta_clear_event"); #else - td_ta_delete_p = &td_ta_delete; td_ta_clear_event_p = &td_ta_clear_event; #endif - if (detaching && td_ta_clear_event_p != NULL) + 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); @@ -793,7 +825,7 @@ thread_db_free (struct process_info *proc, int detaching) #endif /* USE_LIBTHREAD_DB_DIRECTLY */ free (thread_db); - proc->private->thread_db = NULL; + proc->priv->thread_db = NULL; } } @@ -803,9 +835,14 @@ thread_db_free (struct process_info *proc, int detaching) int thread_db_handle_monitor_command (char *mon) { - if (strncmp (mon, "set libthread-db-search-path ", 29) == 0) + 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 + 29; + const char *cp = mon + cmd_len; if (libthread_db_search_path != NULL) free (libthread_db_search_path); @@ -814,6 +851,8 @@ thread_db_handle_monitor_command (char *mon) 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 `");