From de113680248e96948b6bc68c68da96ec1409e7bf Mon Sep 17 00:00:00 2001 From: lace <> Date: Sun, 13 May 2007 17:47:21 +0000 Subject: [PATCH] Pilot nptl_db application. --- .cvsignore | 1 + Makefile | 3 + threadtest.c | 378 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 382 insertions(+) create mode 100644 threadtest.c diff --git a/.cvsignore b/.cvsignore index 530de92..7aabfb7 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1,3 +1,4 @@ debugger testsuite tags +threadtest diff --git a/Makefile b/Makefile index d8e6901..0b76bd4 100644 --- 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 index 0000000..c3afa7a --- /dev/null +++ b/threadtest.c @@ -0,0 +1,378 @@ +/* Copyright 2007, Red Hat Inc. */ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 >> 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 {|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; +} -- 1.8.3.1