/* 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"
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);
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
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 ();
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)
{
pid = atoi (argv[1]);
- stopped = attach (pid);
-
- detach (pid, stopped);
+ attach_state = attach (pid);
+ detach (attach_state);
return EXIT_SUCCESS;
}
/* Copyright 2007, Red Hat Inc. */
+#define _GNU_SOURCE 1
+
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#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)));
}
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). */
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). */
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. */
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);
{
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)
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);
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 ();