2013-08-21 Tristan Gingold <gingold@adacore.com>
[deliverable/binutils-gdb.git] / gdb / gdbserver / thread-db.c
index e169a50f3e5517bbe11d08e38ca271a448771ef1..e8d3481738f2941a89fcb9a4e5f8ef7f96fce2e5 100644 (file)
@@ -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-2013 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 <dlfcn.h>
@@ -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,
@@ -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;
     }
 }
@@ -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;
 }
@@ -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,7 +428,8 @@ 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",
@@ -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));
+      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.  */
@@ -557,42 +597,45 @@ try_thread_db_load_1 (void *handle)
          if (debug_threads)                                    \
            fprintf (stderr, "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));
+      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;
 }
 
@@ -655,64 +698,93 @@ 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);
   return rc;
@@ -743,47 +815,117 @@ 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, 0);
+         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, int detaching)
+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_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");
 #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_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);
@@ -803,9 +945,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 +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 `");
This page took 0.033328 seconds and 4 git commands to generate.