--- /dev/null
+/* 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;
+}