Pilot nptl_db application.
authorlace <>
Sun, 13 May 2007 17:47:21 +0000 (17:47 +0000)
committerlace <>
Sun, 13 May 2007 17:47:21 +0000 (17:47 +0000)
.cvsignore
Makefile
threadtest.c [new file with mode: 0644]

index 530de92..7aabfb7 100644 (file)
@@ -1,3 +1,4 @@
 debugger
 testsuite
 tags
+threadtest
index d8e6901..0b76bd4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,9 @@ debugger: debugger.c debugger.h
 testsuite: testsuite.c debugger.c debugger.h
        $(CC) -o $@ $< -pthread
 
+threadtest: threadtest.c
+       $(CC) -o $@ $< -lthread_db -ldw
+
 .PHONY: check
 check: testsuite
        ./testsuite
diff --git a/threadtest.c b/threadtest.c
new file mode 100644 (file)
index 0000000..c3afa7a
--- /dev/null
@@ -0,0 +1,378 @@
+/* Copyright 2007, Red Hat Inc.  */
+
+#define _GNU_SOURCE 1
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <string.h>
+#include <execinfo.h>
+#include <thread_db.h>
+#include <link.h>
+#include <elfutils/libdwfl.h>
+#include <fcntl.h>
+#include <sys/reg.h>
+#include <linux/ptrace.h>
+#include <asm/prctl.h>
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+static __attribute__((__noreturn__)) void crash (void)
+{
+#if 0
+  void *array[0x100];
+  int count;
+#endif
+  char command[256];
+
+  fputs (">>> CRASH START\n", stderr);
+#if 0
+  count = backtrace (array, ARRAY_SIZE (array));
+  backtrace_symbols_fd (array, count, STDERR_FILENO);
+#endif
+  snprintf (command, sizeof (command), "echo -e \"bt\\nquit\""
+           " >/tmp/debugger.%d; gdb --batch -nx --command=/tmp/debugger.%d"
+           " /proc/%d/exe %d </dev/null;rm -f /tmp/debugger.%d",
+           (int) getpid(), (int) getpid(), (int) getpid(), (int) getpid(),
+           (int) getpid());
+  system (command);
+  fputs (">>> CRASH FINISH\n", stderr);
+  abort ();
+}
+
+struct ps_prochandle
+  {
+    pid_t pid;
+    Dwfl *dwfl;
+  };
+
+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;
+    }
+
+  if (ptrace (PTRACE_ATTACH, pid, NULL, NULL) != 0)
+    abort ();
+  if (waitpid (pid, NULL, 0) != pid)
+    abort ();
+
+  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)
+    {
+      fprintf (stderr, "read() error @0x%lx length %lu: %m\n",
+              (unsigned long) addr, (unsigned long) length);
+      abort ();
+    }
+  if (close (fd) != 0)
+    abort ();
+
+  if (ptrace (PTRACE_DETACH, pid, NULL, NULL) != 0)
+    abort ();
+
+  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 *object_name;
+    Dwfl_Module *module_main;  /* first */
+    Dwfl_Module *module_libpthread;
+  };
+
+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;
+  const char *basename;
+
+  if (getmodules_callback_arg->module_main == NULL)
+    getmodules_callback_arg->module_main = module;
+
+  basename = strrchr (module_name, '/');
+  if (basename != NULL)
+    basename++;
+  else
+    basename = module_name;
+
+  /* FIXME */
+  if (strcmp (basename, "libpthread-2.5.so") == 0)
+    basename = "libpthread.so.0";
+
+  if (strcmp (basename, getmodules_callback_arg->object_name) != 0)
+    return DWARF_CB_OK;
+
+  getmodules_callback_arg->module_libpthread = module;
+  return DWARF_CB_ABORT;
+}
+
+/* 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;
+  int sym_count, ndx;
+  GElf_Sym sym;
+  Dwfl_Module *module;
+
+  getmodules_callback_arg.object_name = object_name;
+  getmodules_callback_arg.module_main = NULL;
+  getmodules_callback_arg.module_libpthread = NULL;
+  dwfl_getmodules (dwfl, getmodules_callback, &getmodules_callback_arg, 0);
+  if (getmodules_callback_arg.module_libpthread != NULL)
+    module = getmodules_callback_arg.module_libpthread;
+  else if (getmodules_callback_arg.module_main != NULL)
+    module = getmodules_callback_arg.module_main;
+  else
+    return PS_NOSYM;
+
+  sym_count = dwfl_module_getsymtab (module);
+  assert (sym_count >= 0);
+  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, sym_name) == 0)
+        break;
+    }
+  if (ndx == sym_count)
+    return PS_NOSYM;
+
+  *sym_addr = (psaddr_t) sym.st_value;
+  return PS_OK;
+}
+
+#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;
+
+  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);
+
+  return 0;
+}
+
+static void thread_test (td_thragent_t *thread_agent)
+{
+  td_err_e err;
+
+  err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL,
+                       TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+                       TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+  assert (err == TD_OK);
+}
+
+static void attach (pid_t pid)
+{
+  td_err_e err;
+  td_thragent_t *thread_agent;
+  struct ps_prochandle proc_handle_local;
+
+  err = td_init ();
+  assert (err == TD_OK);
+
+  proc_handle_local.pid = pid;
+  proc_handle_local.dwfl = NULL;
+  err = td_ta_new (&proc_handle_local, &thread_agent);
+  assert (err == TD_OK || err == TD_NOLIBTHREAD);
+  if (err == TD_OK)
+    puts ("multithreaded");
+  else
+    puts ("singlethreaded");
+  thread_test (thread_agent);
+}
+
+static void *start (void *arg)
+{
+  sleep (1);
+
+  return arg;
+}
+
+static __attribute__((__noreturn__)) void syntax (void)
+{
+  puts ("threadtest {<pid>|multi|single}");
+  exit (EXIT_FAILURE);
+}
+
+int main (int argc, char **argv)
+{
+  if (argc != 2)
+    syntax ();
+  if (strcmp (argv[1], "single") == 0)
+    attach (getpid ());
+  else if (strcmp (argv[1], "multi") == 0)
+    {
+      void *handle;
+      int (*pthread_create_pointer) (pthread_t *thread,
+                                    const pthread_attr_t *attr,
+                                    void *(*start_routine) (void *),
+                                    void *arg);
+      pthread_t thread;
+      int i;
+
+      /* Do not access pthread_create () directly as we would need to link
+         `-lpthread' and we could never provide the `single' option.  */
+      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, start, NULL);
+      assert (i == 0);
+      i = (*pthread_create_pointer) (&thread, NULL, start, NULL);
+      assert (i == 0);
+      
+      attach (getpid ());
+    }
+  else
+    {
+      char *endptr;
+      long pidl = strtol (argv[1], &endptr, 0);
+
+      if (pidl < 0 || (pid_t) pidl != pidl || (endptr != NULL && *endptr != 0))
+        syntax ();
+      attach (pidl);
+    }
+
+  return EXIT_SUCCESS;
+}