From c4d096db0ca0a93738cb3d3faa3db51d977c93be Mon Sep 17 00:00:00 2001 From: lace <> Date: Wed, 16 May 2007 17:14:23 +0000 Subject: [PATCH] Implemented nptl_db testing. --- Makefile | 7 +- debugger.c | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- debugger.h | 12 +- testsuite.c | 108 ++++++++++++----- 4 files changed, 451 insertions(+), 56 deletions(-) diff --git a/Makefile b/Makefile index 43c9ea2..8065c65 100644 --- 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 diff --git a/debugger.c b/debugger.c index 94d8a64..add07f9 100644 --- a/debugger.c +++ b/debugger.c @@ -1,5 +1,8 @@ /* Copyright 2007, Red Hat Inc. */ +#define _GNU_SOURCE 1 +#define ps_prochandle attach_state_struct + #include #include #include @@ -12,6 +15,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #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; } diff --git a/debugger.h b/debugger.h index e64f2c6..95cc1fb 100644 --- a/debugger.h +++ b/debugger.h @@ -3,8 +3,14 @@ #include -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); diff --git a/testsuite.c b/testsuite.c index ebd531b..c569777 100644 --- a/testsuite.c +++ b/testsuite.c @@ -1,5 +1,7 @@ /* Copyright 2007, Red Hat Inc. */ +#define _GNU_SOURCE 1 + #include #include #include @@ -10,6 +12,7 @@ #include #include #include +#include #include "debugger.h" @@ -20,28 +23,34 @@ #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 (); -- 1.8.3.1