Implemented nptl_db testing.
authorlace <>
Wed, 16 May 2007 17:14:23 +0000 (17:14 +0000)
committerlace <>
Wed, 16 May 2007 17:14:23 +0000 (17:14 +0000)
Makefile
debugger.c
debugger.h
testsuite.c

index 43c9ea2..8065c65 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,17 +3,18 @@ ELFUTILS=../elfutils-0.127
 ifdef ELFUTILS
 CC+=-I$(ELFUTILS)/include -L$(ELFUTILS)/lib
 endif
+LIBS=-lthread_db -ldw -lelf -lebl -ldl
 
 all: debugger check
 
 debugger: debugger.c debugger.h
-       $(CC) -o $@ $<
+       $(CC) -o $@ $< $(LIBS)
 
 testsuite: testsuite.c debugger.c debugger.h
-       $(CC) -o $@ $< -pthread
+       $(CC) -o $@ $< $(LIBS)
 
 threadtest: threadtest.c
-       $(CC) -o $@ $< -lthread_db -ldw -lelf -lebl -ldl
+       $(CC) -o $@ $< $(LIBS)
 
 .PHONY: check
 check: testsuite
index 94d8a64..add07f9 100644 (file)
@@ -1,5 +1,8 @@
 /* Copyright 2007, Red Hat Inc.  */
 
+#define _GNU_SOURCE 1
+#define ps_prochandle attach_state_struct
+
 #include <unistd.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <string.h>
 #include <time.h>
+#include <sys/reg.h>
+#include <linux/ptrace.h>
+#include <asm/prctl.h>
+#include <elfutils/libdwfl.h>
+#include <thread_db.h>
+#include <fcntl.h>
 
 #include "debugger.h"
 
@@ -181,23 +190,64 @@ static enum state state (pid_t pid, unsigned expect_mask, const char *expect_mas
   crash ();
 }
 
-/* Debugging only: Number of signal needing redelivery on PTRACE_ATTACH.  */
-int attach_redelivered;
+struct attach_state_struct
+  {
+    pid_t pid;
+    Dwfl *dwfl;
+    int stopped;
+    td_thragent_t *thread_agent;
+    /* Debugging only: Number of signal needing redelivery on PTRACE_ATTACH.  */
+    int redelivered;
+    int threads_count;
+  };
+
+int attach_state_redelivered_get (struct attach_state_struct *attach_state)
+{
+  assert (attach_state != NULL);
+
+  return attach_state->redelivered;
+}
+
+int attach_state_threads_count_get (struct attach_state_struct *attach_state)
+{
+  assert (attach_state != NULL);
+
+  return attach_state->threads_count;
+}
+
+int attach_state_stopped_get (struct attach_state_struct *attach_state)
+{
+  assert (attach_state != NULL);
+
+  return attach_state->stopped;
+}
+
+pid_t attach_state_pid_get (struct attach_state_struct *attach_state)
+{
+  assert (attach_state != NULL);
+
+  return attach_state->pid;
+}
 
-int attach (pid_t pid)
+struct attach_state_struct *attach_single (pid_t pid)
 {
   int i;
   int status;
-  int stopped;
+  struct attach_state_struct *attach_state;
 
-  attach_redelivered = 0;
+  attach_state = malloc (sizeof (*attach_state));
+  assert (attach_state != NULL);
+  attach_state->pid = pid;
+  attach_state->dwfl = NULL;
+  attach_state->redelivered = 0;
+  attach_state->threads_count = -1;
 
   delay ();
 
-#if 1
-  stopped = (STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING)
-                        | (1 << STATE_STOPPED)) == STATE_STOPPED);
-#endif
+  attach_state->stopped = (STATE (pid, (1 << STATE_SLEEPING)
+                                      | (1 << STATE_RUNNING)
+                                      | (1 << STATE_STOPPED))
+                          == STATE_STOPPED);
 
   i = ptrace (PTRACE_ATTACH, pid, NULL, NULL);
   assert (i == 0);
@@ -210,9 +260,9 @@ int attach (pid_t pid)
   i = ptrace (PTRACE_CONT, pid, (void *) 1, (void *) SIGSTOP);
   /* `STOPPED == 1' may be false, even if the process was not stopped.  */
   if (i == 0)
-    stopped = 1;
+    attach_state->stopped = 1;
   else if (errno == ESRCH)
-    stopped = 0;
+    attach_state->stopped = 0;
   else
     crash ();
 #endif
@@ -233,10 +283,10 @@ int attach (pid_t pid)
       if (WSTOPSIG (status) == SIGSTOP)
        break;
 
-      if (attach_redelivered == 0)
-       attach_redelivered = WSTOPSIG (status);
+      if (attach_state->redelivered == 0)
+       attach_state->redelivered = WSTOPSIG (status);
       else
-       attach_redelivered = -1;
+       attach_state->redelivered = -1;
 
       delay ();
 
@@ -247,28 +297,315 @@ int attach (pid_t pid)
       assert (i == 0);
     }
 
-  return stopped;
+  return attach_state;
 }
 
-void detach (pid_t pid, int stopped)
+void detach_single (struct attach_state_struct *attach_state)
 {
   int i;
 
   delay ();
 
-  i = ptrace (PTRACE_DETACH, pid, NULL,
-             (void *) (unsigned long) (stopped ? SIGSTOP : 0));
+  i = ptrace (PTRACE_DETACH, attach_state->pid, NULL,
+             (void *) (unsigned long) (attach_state->stopped ? SIGSTOP : 0));
   assert (i == 0);
 
   delay ();
 }
 
+static Dwfl *get_dwfl (struct ps_prochandle *proc_handle)
+{
+  static char *debuginfo_path;
+
+  static const Dwfl_Callbacks proc_callbacks =
+   {
+     .find_debuginfo = dwfl_standard_find_debuginfo,
+     .debuginfo_path = &debuginfo_path,
+
+     .find_elf = dwfl_linux_proc_find_elf,
+   };
+
+  if (proc_handle->dwfl == NULL)
+    {
+      proc_handle->dwfl = dwfl_begin (&proc_callbacks);
+      assert (proc_handle->dwfl != NULL);
+
+      errno = 0;
+      if (dwfl_linux_proc_report (proc_handle->dwfl, proc_handle->pid) != 0
+        || dwfl_report_end (proc_handle->dwfl, NULL, NULL) != 0)
+       {
+         fprintf (stderr, "dwfl reporting: %m\n");
+         dwfl_end (proc_handle->dwfl);
+         proc_handle->dwfl = NULL;
+         abort ();
+       }
+    }
+  return proc_handle->dwfl;
+}
+
+/* Functions in this interface return one of these status codes.  */
+typedef enum
+{
+  PS_OK,               /* Generic "call succeeded".  */
+  PS_ERR,              /* Generic error. */
+  PS_BADPID,           /* Bad process handle.  */
+  PS_BADLID,           /* Bad LWP identifier.  */ 
+  PS_BADADDR,          /* Bad address.  */
+  PS_NOSYM,            /* Could not find given symbol.  */
+  PS_NOFREGS           /* FPU register set not available for given LWP.  */
+} ps_err_e;
+
+ps_err_e ps_pdread (struct ps_prochandle *proc_handle, psaddr_t addr,
+                   void *buffer, size_t length)
+{
+  pid_t pid = proc_handle->pid;
+  char filename[64];
+  int fd;
+
+  if (pid == getpid ())
+    {
+      memcpy (buffer, addr, length);
+      return PS_OK;
+    }
+
+/* Already under PTRACE_ATTACH.  */
+#if 0
+  if (ptrace (PTRACE_ATTACH, pid, NULL, NULL) != 0)
+    abort ();
+  if (waitpid (pid, NULL, 0) != pid)
+    abort ();
+#endif
+
+  snprintf (filename, sizeof (filename), "/proc/%ld/mem", (long) pid);
+  fd = open (filename, O_RDONLY);
+  assert (fd != -1);
+  if (lseek64 (fd, (off64_t) addr, SEEK_SET) != (off64_t) addr)
+    abort ();
+  if (read (fd, buffer, length) != length)
+    {
+      /* It occurs for singlethreaded processes.  */
+      if ((off64_t) addr == 0)
+        return PS_ERR;
+      fprintf (stderr, "read() error @0x%lx length %lu: %m\n",
+              (unsigned long) addr, (unsigned long) length);
+      abort ();
+    }
+  if (close (fd) != 0)
+    abort ();
+
+/* Already under PTRACE_ATTACH.  */
+#if 0
+  if (ptrace (PTRACE_DETACH, pid, NULL, NULL) != 0)
+    abort ();
+#endif
+
+  return PS_OK;
+}
+
+ps_err_e ps_pdwrite (struct ps_prochandle *proc_handle, psaddr_t addr,
+                    const void *buffer, size_t length)
+{
+  crash ();
+}
+
+ps_err_e ps_lgetregs (struct ps_prochandle *proc_handle, lwpid_t lwp,
+                     prgregset_t regs)
+{
+  crash ();
+}
+
+ps_err_e ps_lsetregs (struct ps_prochandle *proc_handle, lwpid_t lwp,
+                     const prgregset_t regs)
+{
+  crash ();
+}
+
+ps_err_e ps_lgetfpregs (struct ps_prochandle *proc_handle, lwpid_t lwp,
+                       prfpregset_t *fpregs)
+{
+  crash ();
+}
+
+ps_err_e ps_lsetfpregs (struct ps_prochandle *proc_handle, lwpid_t lwp,
+                       const prfpregset_t *fpregs)
+{
+  crash ();
+}
+
+/* Return the PID of the process.  */
+pid_t ps_getpid (struct ps_prochandle *proc_handle)
+{
+  return proc_handle->pid;
+}
+
+struct getmodules_callback_arg
+  {
+    const char *sym_name;
+    psaddr_t *sym_addr;
+    ps_err_e retval;
+  };
+
+static int getmodules_callback (Dwfl_Module *module,
+                               void **module_userdata_pointer,
+                               const char *module_name,
+                               Dwarf_Addr module_low_addr, void *arg)
+{
+  struct getmodules_callback_arg *getmodules_callback_arg = arg;
+  int sym_count, ndx;
+  GElf_Sym sym;
+
+  sym_count = dwfl_module_getsymtab (module);
+/* "/usr/lib/debug/lib64/ld-2.4.so.debug"?  */
+#if 0
+  assert (sym_count >= 0);
+#endif
+  for (ndx = 0; ndx < sym_count; ndx++)
+    {
+      const char *name_got;
+
+      name_got = dwfl_module_getsym (module, ndx, &sym,
+                                    NULL);
+      assert (name_got != NULL);
+      if (strcmp (name_got, getmodules_callback_arg->sym_name) == 0)
+        break;
+    }
+  if (ndx == sym_count)
+    return DWARF_CB_OK;
+
+  *getmodules_callback_arg->sym_addr = (psaddr_t) sym.st_value;
+  getmodules_callback_arg->retval = PS_OK;
+
+  return DWARF_CB_OK;
+}
+
+/* Look up the named symbol in the named DSO in the symbol tables
+   associated with the process being debugged, filling in *SYM_ADDR
+   with the corresponding run-time address.  */
+ps_err_e ps_pglobal_lookup (struct ps_prochandle *proc_handle,
+                           const char *object_name, const char *sym_name,
+                           psaddr_t *sym_addr)
+{
+  Dwfl *dwfl = get_dwfl (proc_handle);
+  struct getmodules_callback_arg getmodules_callback_arg;
+  ptrdiff_t err_ptrdiff;
+
+  /* FIXME: `object_name' ignored due to missing unresolving of shared
+     libraries symbolic links.  */
+  getmodules_callback_arg.sym_name = sym_name;
+  getmodules_callback_arg.sym_addr = sym_addr;
+  getmodules_callback_arg.retval = PS_NOSYM;
+  err_ptrdiff = dwfl_getmodules (dwfl, getmodules_callback,
+                                &getmodules_callback_arg, 0);
+  assert (err_ptrdiff == 0);
+  return getmodules_callback_arg.retval;
+}
+
+#ifdef __x86_64__
+ps_err_e ps_get_thread_area (const struct ps_prochandle *ph, lwpid_t lwpid,
+                            int idx, void **base)
+{
+  long val;
+
+  assert (idx == FS || idx == GS);
+
+  if (ptrace (PTRACE_ATTACH, lwpid, NULL, NULL) != 0)
+    abort ();
+  if (waitpid (lwpid, NULL, 0) != lwpid)
+    abort ();
+
+  val = ptrace (PTRACE_ARCH_PRCTL, lwpid, base, (idx == FS ? ARCH_GET_FS
+                                                          : ARCH_GET_GS));
+
+  if (ptrace (PTRACE_DETACH, lwpid, NULL, NULL) != 0)
+    abort ();
+
+  if (val != 0)
+    {
+      fprintf (stderr, "PTRACE_ARCH_PRCTL (%s): %m\n", (idx == FS ? "FS"
+                                                                 : "GS"));
+      return PS_ERR;
+    }
+
+  return PS_OK;
+}
+#else
+#error "Unsupported ps_get_thread_area ()!"
+#endif
+
+static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
+{
+  td_err_e err;
+  td_thrinfo_t info;
+  struct attach_state_struct *attach_state = data;
+
+  err = td_thr_get_info (th_p, &info);
+  assert (err == TD_OK);
+  /* printf ("LWP = %ld TID = 0x%lx\n", (long) info.ti_lid, info.ti_tid);  */
+
+  attach_state->threads_count++;
+
+  return 0;
+}
+
+static void attach_multi (struct attach_state_struct *attach_state)
+{
+  td_err_e err;
+
+  err = td_init ();
+  assert (err == TD_OK);
+
+  err = td_ta_new (attach_state, &attach_state->thread_agent);
+  assert (err == TD_OK || err == TD_NOLIBTHREAD);
+  attach_state->threads_count = 0;
+  if (err == TD_NOLIBTHREAD)
+    {
+      attach_state->thread_agent = NULL;
+      return;
+    }
+  assert (attach_state->thread_agent != NULL);
+  /* Multithreading test:  */
+  err = td_ta_thr_iter (attach_state->thread_agent, find_new_threads_callback,
+                       attach_state, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+                       TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+  if (err != TD_OK)
+    {
+      fprintf (stderr, "err = %d\n", err);
+      abort ();
+    }
+  assert (attach_state->threads_count > 0);
+}
+
+struct attach_state_struct *attach (pid_t pid)
+{
+  struct attach_state_struct *attach_state;
+
+  attach_state = attach_single (pid);
+  attach_multi (attach_state);
+
+  return attach_state;
+}
+
+void detach (struct attach_state_struct *attach_state)
+{
+  if (attach_state->thread_agent != NULL)
+    {
+      td_err_e err;
+
+      err = td_ta_delete (attach_state->thread_agent);
+      assert (err == TD_OK);
+    }
+  if (attach_state->dwfl != NULL);
+    dwfl_end (attach_state->dwfl);
+
+  detach_single (attach_state);
+}
+
 #ifndef LIBRARY
 
 int main (int argc, char **argv)
 {
   pid_t pid;
-  int stopped;
+  struct attach_state_struct *attach_state;
 
   if (argc != 2)
     {
@@ -278,9 +615,8 @@ int main (int argc, char **argv)
 
   pid = atoi (argv[1]);
 
-  stopped = attach (pid);
-
-  detach (pid, stopped);
+  attach_state = attach (pid);
+  detach (attach_state);
 
   return EXIT_SUCCESS;
 }
index e64f2c6..95cc1fb 100644 (file)
@@ -3,8 +3,14 @@
 #include <sys/types.h>
 
 
-extern int attach (pid_t pid);
-extern void detach (pid_t pid, int stopped);
+struct attach_state_struct;
+extern struct attach_state_struct *attach (pid_t pid);
+extern void detach_single (struct attach_state_struct *attach_state);
+
+extern int attach_state_redelivered_get (struct attach_state_struct *
+                                                                 attach_state);
+extern int attach_state_threads_count_get (struct attach_state_struct *attach_state);
+extern int attach_state_stopped_get (struct attach_state_struct *attach_state);
+extern pid_t attach_state_pid_get (struct attach_state_struct *attach_state);
 
-extern int attach_redelivered;
 extern void delay (void);
index ebd531b..c569777 100644 (file)
@@ -1,5 +1,7 @@
 /* Copyright 2007, Red Hat Inc.  */
 
+#define _GNU_SOURCE 1
+
 #include <unistd.h>
 #include <stdlib.h>
 #include <assert.h>
@@ -10,6 +12,7 @@
 #include <string.h>
 #include <pthread.h>
 #include <sys/syscall.h>
+#include <dlfcn.h>
 
 #include "debugger.h"
 
 #define tkill(tid, sig) syscall (SYS_tkill, (tid), (sig))
 
 
-static int attach_checked (pid_t pid, int redelivered_expect)
+static volatile int spawned_threads_count;     /* Set for the specific spawner.  */
+
+static struct attach_state_struct *attach_checked (pid_t pid, int redelivered_expect)
 {
-  int stopped;
+  struct attach_state_struct *attach_state;
 
   STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING) | (1 << STATE_STOPPED));
-  stopped = attach (pid);
-  if (attach_redelivered != redelivered_expect)
+  attach_state = attach (pid);
+  if (redelivered_expect != attach_state_redelivered_get (attach_state))
     {
       fprintf (stderr, "Expecting redelivery of %d but found %d\n",
-              redelivered_expect, attach_redelivered);
+              redelivered_expect, attach_state_redelivered_get (attach_state));
       abort ();
     }
+  assert (spawned_threads_count == attach_state_threads_count_get (attach_state));
   /* FIXME: Why also STATE_STOPPED?  */
   STATE (pid, (1 << STATE_PTRACED) | (1 << STATE_STOPPED));
-  return stopped;
+  return attach_state;
 }
 
-static void detach_checked (pid_t pid, int stopped)
+static void detach_checked (struct attach_state_struct *attach_state)
 {
+  pid_t pid = attach_state_pid_get (attach_state);
+  int stopped = attach_state_stopped_get (attach_state);
+
   /* FIXME: Why STATE_STOPPED?  */
   STATE (pid, (stopped ? 1 << STATE_STOPPED : 1 << STATE_PTRACED));
-  detach (pid, stopped);
+  detach (attach_state);
   STATE (pid, (stopped ? 1 << STATE_STOPPED : (1 << STATE_SLEEPING) | (1 << STATE_RUNNING)));
 }
 
@@ -300,16 +309,16 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
                          void *input)
 {
   pid_t inferior;
-  int stopped;
+  struct attach_state_struct *attach_state;
   int i;
 
   assert (input == NULL);
 
   /* Plain attach/detach.  */
   inferior = (unsigned long) (*child) (data, child_pause);
-  stopped = attach_checked (inferior, 0);
-  assert (stopped == 0);
-  detach_checked (inferior, stopped);
+  attach_state = attach_checked (inferior, 0);
+  assert (attach_state_stopped_get (attach_state) == 0);
+  detach_checked (attach_state);
   murder (inferior);
 
   /* Attach to a process stopped by standard kill(2).  */
@@ -318,9 +327,9 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
   i = kill (inferior, SIGSTOP);
   assert (i == 0);
   STATE (inferior, 1 << STATE_STOPPED);
-  stopped = attach_checked (inferior, 0);
-  assert (stopped == 1);
-  detach_checked (inferior, stopped);
+  attach_state = attach_checked (inferior, 0);
+  assert (attach_state_stopped_get (attach_state) == 1);
+  detach_checked (attach_state);
   murder (inferior);
 
   /* Attach to a process stopped by Linux specific tkill(2).  */
@@ -329,9 +338,9 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
   i = tkill (inferior, SIGSTOP);
   assert (i == 0);
   STATE (inferior, 1 << STATE_STOPPED);
-  stopped = attach_checked (inferior, 0);
-  assert (stopped == 1);
-  detach_checked (inferior, stopped);
+  attach_state = attach_checked (inferior, 0);
+  assert (attach_state_stopped_get (attach_state) == 1);
+  detach_checked (attach_state);
   murder (inferior);
 
   /* Attach to a stopped process with already pending SIGALRM.  */
@@ -348,12 +357,12 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
   STATE (inferior, 1 << STATE_STOPPED);
   /* FIXME: SIGALRM did not get redelivered?  */
 #if 0
-  stopped = attach_checked (inferior, SIGALRM);
+  attach_state = attach_checked (inferior, SIGALRM);
 #else
-  stopped = attach_checked (inferior, 0);
+  attach_state = attach_checked (inferior, 0);
 #endif
-  assert (stopped == 1);
-  detach_checked (inferior, stopped);
+  assert (attach_state_stopped_get (attach_state) == 1);
+  detach_checked (attach_state);
   STATE (inferior, 1 << STATE_STOPPED);
   delay ();
   i = tkill (inferior, SIGCONT);
@@ -409,24 +418,48 @@ static void *spawn_threaded_parent (void *data, void *input)
 {
   pthread_t thread;
   int i;
+  void *handle;
+  void *retval;
+  int (*pthread_create_pointer) (pthread_t *thread,
+                                const pthread_attr_t *attr,
+                                void *(*start_routine) (void *),
+                                void *arg);
+
+  handle = dlopen ("libpthread.so.0", RTLD_LAZY);
+  assert (handle != NULL);
+  pthread_create_pointer = dlsym (handle, "pthread_create");
+  assert (pthread_create_pointer != NULL);
+
+  i = (*pthread_create_pointer) (&thread, NULL, spawn_threaded_parent_start,
+                                NULL);
+  assert (i == 0);
+  retval = pass (data, input);
 
-  i = pthread_create (&thread, NULL, spawn_threaded_parent_start, NULL);
+  i = dlclose (handle);
   assert (i == 0);
-  return pass (data, input);
+
+  return retval;
 }
 
 struct spawn_threaded_child_start
   {
     void *data;
     void *input;
+    void *lib_handle;
   };
 
 static void *spawn_threaded_child_start (void *arg_voidpointer)
 {
   struct spawn_threaded_child_start *arg = arg_voidpointer;
+  void *retval;
+  int i;
 
-  return pass (arg->data, arg->input);
-  /* NOTREACHED */
+  retval = pass (arg->data, arg->input);
+
+  i = dlclose (arg->lib_handle);
+  assert (i == 0);
+
+  return retval;
 }
 
 static void *spawn_threaded_child (void *data, void *input)
@@ -434,12 +467,25 @@ static void *spawn_threaded_child (void *data, void *input)
   pthread_t thread;
   int i;
   struct spawn_threaded_child_start arg_local;
+  int (*pthread_create_pointer) (pthread_t *thread,
+                                const pthread_attr_t *attr,
+                                void *(*start_routine) (void *),
+                                void *arg);
+  int (*pthread_join_pointer) (pthread_t thread, void **value_ptr);
+
+  arg_local.lib_handle = dlopen ("libpthread.so.0", RTLD_LAZY);
+  assert (arg_local.lib_handle != NULL);
+  pthread_create_pointer = dlsym (arg_local.lib_handle, "pthread_create");
+  assert (pthread_create_pointer != NULL);
+  pthread_join_pointer = dlsym (arg_local.lib_handle, "pthread_join");
+  assert (pthread_join_pointer != NULL);
 
   arg_local.data = data;
   arg_local.input = input;
-  i = pthread_create (&thread, NULL, spawn_threaded_child_start, &arg_local);
+  i = (*pthread_create_pointer) (&thread, NULL, spawn_threaded_child_start,
+                                &arg_local);
   assert (i == 0);
-  i = pthread_join (thread, NULL);
+  i = (*pthread_join_pointer) (thread, NULL);
   assert (i == 0);
 
   _exit (EXIT_SUCCESS);
@@ -499,8 +545,14 @@ int main (int argc, char **argv)
          printf ("%lu\n", loops);
          loops_print--;
        }
+
+      spawned_threads_count = 0;
       body_maywaiter (spawn_singlethreaded, NULL, NULL);
+
+      spawned_threads_count = 2;
       body_maywaiter (spawn_threaded_parent, NULL, NULL);
+
+      spawned_threads_count = 2;
       body_maywaiter (spawn_threaded_child, NULL, NULL);
 
       registry_cleanup ();