X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fthread-db.c;h=df06b9dbbb9d77afb98eac5cc7e467499248e836;hb=87ce2a04c53fa7bb4fff50a41e45c0b29af06dae;hp=06dfe3510a97a22c067e67c9c3f28f8954449201;hpb=96f1593750e940f6aac4fd5fe29968bbfe1e7f5b;p=binutils-gdb.git diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index 06dfe3510a9..df06b9dbbb9 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-2014 Free Software Foundation, Inc. Contributed by MontaVista Software. @@ -28,7 +27,8 @@ extern int debug_threads; static int thread_db_use_events; #include "gdb_proc_service.h" -#include "../gdb_thread_db.h" +#include "gdb_thread_db.h" +#include "gdb_vecs.h" #ifndef USE_LIBTHREAD_DB_DIRECTLY #include @@ -47,11 +47,26 @@ 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 + /* 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, @@ -71,8 +86,8 @@ struct thread_db sigset_t *ti_sigmask_p, unsigned int ti_user_flags); td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th, - void *map_address, - size_t offset, void **address); + psaddr_t map_address, + size_t offset, psaddr_t *address); const char ** (*td_symbol_list_p) (void); }; @@ -135,7 +150,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,7 +176,7 @@ 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; } } @@ -179,7 +194,7 @@ thread_db_create_event (CORE_ADDR where) fatal ("unexpected thread_db->td_ta_event_getmsg_p == NULL"); if (debug_threads) - fprintf (stderr, "Thread creation event.\n"); + debug_printf ("Thread creation event.\n"); /* FIXME: This assumes we don't get another event. In the LinuxThreads implementation, this is safe, @@ -205,7 +220,7 @@ thread_db_create_event (CORE_ADDR where) } static int -thread_db_enable_reporting () +thread_db_enable_reporting (void) { td_thr_events_t events; td_notify_t notify; @@ -239,8 +254,9 @@ thread_db_enable_reporting () thread_db_err_str (err)); return 0; } - set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, - thread_db_create_event); + thread_db->td_create_bp + = set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, + thread_db_create_event); return 1; } @@ -273,8 +289,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", + ti.ti_tid, ti.ti_lid); if (lwpid != ti.ti_lid) { @@ -310,8 +326,8 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) struct lwp_info *lwp; if (debug_threads) - fprintf (stderr, "Attaching to thread %ld (LWP %d)\n", - ti_p->ti_tid, ti_p->ti_lid); + debug_printf ("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) @@ -390,7 +406,7 @@ static void thread_db_find_new_threads (void) { td_err_e err; - ptid_t ptid = ((struct inferior_list_entry *) current_inferior)->id; + ptid_t ptid = current_ptid; struct thread_db *thread_db = current_process ()->private->thread_db; int loop, iteration; @@ -412,11 +428,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) { @@ -441,7 +458,25 @@ thread_db_look_up_symbols (void) 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 ()->private->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 @@ -459,7 +494,7 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, thread_db = proc->private->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) @@ -495,39 +530,41 @@ 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"); - tdb.td_ta_new_p = &td_ta_new; + tdb = xcalloc (1, sizeof (*tdb)); + proc->private->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); + 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->private->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; + 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_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; - - proc->private->thread_db = xmalloc (sizeof (tdb)); - memcpy (proc->private->thread_db, &tdb, sizeof (tdb)); + 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; return 1; } @@ -538,13 +575,16 @@ 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"); - tdb.handle = handle; + tdb = xcalloc (1, sizeof (*tdb)); + proc->private->thread_db = tdb; + + tdb->handle = handle; /* Initialize pointers to the dynamic library functions we will use. Essential functions first. */ @@ -555,47 +595,52 @@ 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->private->thread_db = NULL; \ + return 0; \ + } \ } \ } \ while (0) - CHK (1, tdb.td_ta_new_p = dlsym (handle, "td_ta_new")); + CHK (1, tdb->td_ta_new_p = dlsym (handle, "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->private->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")); + 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")); + tdb->td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable")); /* 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_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")); #undef CHK - proc->private->thread_db = xmalloc (sizeof (tdb)); - memcpy (proc->private->thread_db, &tdb, sizeof (tdb)); - 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. */ @@ -610,22 +655,25 @@ dladdr_to_soname (const void *addr) return NULL; } +#endif + static int 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; } +#ifdef HAVE_DLADDR if (debug_threads && strchr (library, '/') == NULL) { void *td_init; @@ -640,6 +688,7 @@ try_thread_db_load (const char *library) library, libpath); } } +#endif if (try_thread_db_load_1 (handle)) return 1; @@ -649,66 +698,95 @@ 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 = 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] == '/')) { - size_t len = end - search_path; - if (len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path)) + /* 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 ()) { - 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; } @@ -737,36 +815,123 @@ thread_db_init (int use_events) if (use_events && thread_db_enable_reporting () == 0) { /* Keep trying; maybe event reporting will work later. */ - thread_db_free (proc); + thread_db_mourn (proc); return 0; } 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 = 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_inferior = + (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) +static void +disable_thread_event_reporting (struct process_info *proc) { struct thread_db *thread_db = proc->private->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"); +#else + td_ta_clear_event_p = &td_ta_clear_event; +#endif + + if (td_ta_clear_event_p != NULL) + { + struct thread_info *saved_inferior = current_inferior; + 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_inferior = saved_inferior; + } + } +} + +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_inferior = current_inferior; + + switch_to_process (proc); + + delete_breakpoint (thread_db->td_create_bp); + thread_db->td_create_bp = NULL; + + current_inferior = saved_inferior; + } +} + +void +thread_db_detach (struct process_info *proc) +{ + struct thread_db *thread_db = proc->private->thread_db; + + if (thread_db) + { + disable_thread_event_reporting (proc); + remove_thread_event_breakpoints (proc); + } +} + +/* Disconnect from libthread_db and free resources. */ + +void +thread_db_mourn (struct process_info *proc) +{ + struct thread_db *thread_db = proc->private->thread_db; + if (thread_db) + { td_err_e (*td_ta_delete_p) (td_thragent_t *); +#ifndef USE_LIBTHREAD_DB_DIRECTLY td_ta_delete_p = 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); -#else - td_ta_delete (thread_db->thread_agent); #endif /* USE_LIBTHREAD_DB_DIRECTLY */ free (thread_db); @@ -780,9 +945,14 @@ thread_db_free (struct process_info *proc) 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); @@ -791,6 +961,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 `");