/* 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 <sys/wait.h>
#include <limits.h>
#include <string.h>
-#include <execinfo.h>
-#include <thread_db.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"
+/* Insert constant delay for before each critical check? */
+#if 0
#define USLEEP (1000000 / 2)
+#endif
+
+#if 1 /* Standard run. */
+#define TIMEOUT_SECS 4
+#define LOOPS_MIN 100000
+#else /* QEMU? */
+/* LOOPS_MIN is a safety as QEMU clock time sucks.
+ 100000 is 4s natively and 53s in QEMU. */
+#define TIMEOUT_SECS 20
+#define LOOPS_MIN 500000
+#endif /* QEMU? */
#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
{
/* Separate the valid `1 << enum state' and `enum state' ranges. */
STATE_FIRST = 4,
+ STATE_INSTABLE,
STATE_ENOENT,
STATE_SLEEPING,
STATE_RUNNING,
STATE_STOPPED,
STATE_PTRACED,
STATE_ZOMBIE,
+ STATE_DEAD,
+ STATE_DISK_SLEEP,
STATE_LAST
};
{
switch (state)
{
- case STATE_ENOENT: return "STATE_ENOENT";
- case STATE_SLEEPING: return "STATE_SLEEPING";
- case STATE_RUNNING: return "STATE_RUNNING";
- case STATE_STOPPED: return "STATE_STOPPED";
- case STATE_PTRACED: return "STATE_PTRACED";
- case STATE_ZOMBIE: return "STATE_ZOMBIE";
+ case STATE_INSTABLE: return "STATE_INSTABLE";
+ case STATE_ENOENT: return "STATE_ENOENT";
+ case STATE_SLEEPING: return "STATE_SLEEPING";
+ case STATE_RUNNING: return "STATE_RUNNING";
+ case STATE_STOPPED: return "STATE_STOPPED";
+ case STATE_PTRACED: return "STATE_PTRACED";
+ case STATE_ZOMBIE: return "STATE_ZOMBIE";
+ case STATE_DEAD: return "STATE_DEAD";
+ case STATE_DISK_SLEEP: return "STATE_DISK_SLEEP";
default: crash ();
}
/* NOTREACHED */
crash ();
}
-#define STATE(pid, expect_mask) state ((pid), (expect_mask), #expect_mask )
-
-static enum state state (pid_t pid, unsigned expect_mask, const char *expect_mask_string)
+static enum state state_get (pid_t pid)
{
char status_name[32];
char line[LINE_MAX];
delay ();
- /* Sanity check `1 << enum state' was not misplaced with `enum state'. */
- assert (1 << (STATE_FIRST + 1) >= STATE_LAST);
- assert (expect_mask != 0);
-#define MASK_UNDER_EXCLUSIVE(bit) ((1 << (bit)) - 1)
-#define MASK_ABOVE_INCLUSIVE(bit) (~MASK_UNDER_EXCLUSIVE (bit))
- assert ((expect_mask & MASK_UNDER_EXCLUSIVE (STATE_FIRST + 1)) == 0);
- assert ((expect_mask & MASK_ABOVE_INCLUSIVE (STATE_LAST)) == 0);
-#undef MASK_ABOVE_INCLUSIVE
-#undef MASK_UNDER_EXCLUSIVE
-
snprintf (status_name, sizeof (status_name), "/proc/%d/status", (int) pid);
f = fopen (status_name, "r");
if (f == NULL && errno == ENOENT)
found = STATE_ENOENT;
else if (f == NULL)
- crash ();
+ {
+ fprintf (stderr, "errno = %d\n", errno);
+ crash ();
+ }
else
{
int i;
- found = STATE_ENOENT;
+ found = STATE_INSTABLE;
while (errno = 0, fgets (line, sizeof (line), f) != NULL)
{
const char *const string = "State:\t";
found = STATE_PTRACED;
else if (strcmp (line + length, "Z (zombie)\n") == 0)
found = STATE_ZOMBIE;
+ /* FIXME: What does it mean? */
+ else if (strcmp (line + length, "X (dead)\n") == 0)
+ found = STATE_DEAD;
+ else if (strcmp (line + length, "D (disk sleep)\n") == 0)
+ found = STATE_DISK_SLEEP;
else
{
fprintf (stderr, "Found an unknown state: %s", line + length);
crash ();
}
}
- assert (found != STATE_ENOENT);
+ assert (found != STATE_INSTABLE || errno == ESRCH);
i = fclose (f);
assert (i == 0);
}
- if (((1 << found) & expect_mask) == 0)
+ return found;
+}
+
+#define STATE(pid, expect_mask) state ((pid), (expect_mask), #expect_mask )
+
+static enum state state (pid_t pid, unsigned expect_mask, const char *expect_mask_string)
+{
+ enum state found;
+ time_t timeout = time (NULL) + TIMEOUT_SECS;
+ unsigned loops = 0;
+
+ /* Sanity check `1 << enum state' was not misplaced with `enum state'. */
+ assert (1 << (STATE_FIRST + 1) >= STATE_LAST);
+ assert (expect_mask != 0);
+#define MASK_UNDER_EXCLUSIVE(bit) ((1 << (bit)) - 1)
+#define MASK_ABOVE_INCLUSIVE(bit) (~MASK_UNDER_EXCLUSIVE (bit))
+ assert ((expect_mask & MASK_UNDER_EXCLUSIVE (STATE_FIRST + 1)) == 0);
+ assert ((expect_mask & MASK_ABOVE_INCLUSIVE (STATE_LAST)) == 0);
+#undef MASK_ABOVE_INCLUSIVE
+#undef MASK_UNDER_EXCLUSIVE
+
+ do
{
- fprintf (stderr, "Found for PID %d state %s but expecting (%s)\n",
- (int) pid, state_to_name (found), expect_mask_string);
- crash ();
+ found = state_get (pid);
+
+ if (((1 << found) & expect_mask) != 0)
+ return found;
}
- return found;
+ while (loops++ < LOOPS_MIN || time (NULL) < timeout);
+
+ fprintf (stderr, "Found for PID %d state %s but expecting (%s)\n",
+ (int) pid, state_to_name (found), expect_mask_string);
+ crash ();
+}
+
+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;
}
-/* Debugging only: Number of signal needing redelivery on PTRACE_ATTACH. */
-int attach_redelivered;
+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);
-static int attach_single (pid_t pid)
+ return attach_state->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;
}
-struct ps_prochandle_module
- {
- struct ps_prochandle_module *next;
- Dwfl_Module *module;
- };
+void detach_single (struct attach_state_struct *attach_state)
+{
+ int i;
-struct ps_prochandle
- {
- pid_t pid;
- Dwfl *dwfl;
- struct ps_prochandle_module *module_list;
- };
+ delay ();
-static int get_dwfl_find_debuginfo (Dwfl_Module *mod, void **userdata,
- const char *modname, GElf_Addr base,
- const char *file_name,
- const char *debuglink_file,
- GElf_Word debuglink_crc,
- char **debuginfo_file_name)
-{
- printf("modname=<%s>,file_name=<%s>\n",modname,file_name);
- return dwfl_standard_find_debuginfo (mod, userdata, modname, base, file_name,
- debuglink_file, debuglink_crc,
- debuginfo_file_name);
+ 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 const Dwfl_Callbacks proc_callbacks =
{
- .find_debuginfo = get_dwfl_find_debuginfo,
+ .find_debuginfo = dwfl_standard_find_debuginfo,
.debuginfo_path = &debuginfo_path,
.find_elf = dwfl_linux_proc_find_elf,
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)
{
- dwfl_end (proc_handle->dwfl);
- proc_handle->dwfl = NULL;
- crash ();
+ fprintf (stderr, "dwfl reporting: %m\n");
+ abort ();
}
}
return proc_handle->dwfl;
ps_err_e ps_pdread (struct ps_prochandle *proc_handle, psaddr_t addr,
void *buffer, size_t length)
{
- crash ();
+ 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,
/* Return the PID of the process. */
pid_t ps_getpid (struct ps_prochandle *proc_handle)
{
- crash ();
+ return proc_handle->pid;
}
-/* 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);
- dwfl = dwfl;
- dwfl_addrmodule (dwfl, 0x3571d00000);
+struct getmodules_callback_arg
+ {
+ const char *sym_name;
+ psaddr_t *sym_addr;
+ ps_err_e retval;
+ };
-#if 0
- Elf *elf = FIXME;
- Elf_Scn *scn = NULL;
+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 (Dwfl_Module *mod);
+ sym_count = dwfl_module_getsymtab (module);
+ if (sym_count == -1)
+ return DWARF_CB_OK;
assert (sym_count >= 0);
+
for (ndx = 0; ndx < sym_count; ndx++)
{
- GElf_Sym sym;
const char *name_got;
- name_got = dwfl_module_getsym (Dwfl_Module *mod, ndx, &sym, NULL);
+ name_got = dwfl_module_getsym (module, ndx, &sym,
+ NULL);
assert (name_got != NULL);
- if (strcmp (name_got, sym_name) == 0)
+ if (strcmp (name_got, getmodules_callback_arg->sym_name) == 0)
break;
}
- assert (ndx < sym_count);
+ if (ndx == sym_count)
+ return DWARF_CB_OK;
-extern Elf *dwarf_getelf (Dwarf *dwarf);
+ *getmodules_callback_arg->sym_addr = (psaddr_t) sym.st_value;
+ getmodules_callback_arg->retval = PS_OK;
+ return DWARF_CB_OK;
+}
- while ((scn = elf_nextscn (elf, scn)) != NULL)
- {
- /* Handle the section if it is a symbol table. */
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+/* 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;
+}
- if (shdr != NULL && shdr->sh_type == SHT_SYMTAB)
- handle_symtab (ebl, scn, shdr);
- }
+#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);
- for (modulei = 0; modulei < dwfl->nmodules; modulei++)
- {
- Dwfl_Module *m = dwfl->modules[modulei];
+ if (ptrace (PTRACE_ATTACH, lwpid, NULL, NULL) != 0)
+ abort ();
+ if (waitpid (lwpid, NULL, 0) != lwpid)
+ abort ();
- puts (m->name);
- }
-#if 0
- if (strcmp (sym_name, "nptl_version") == 0)
+ 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)
{
- extern const char nptl_version[];
- *sym_addr = (psaddr_t) nptl_version;
- return PS_OK;
+ 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
-#endif
- crash ();
+
+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;
}
-int attach (pid_t pid)
+static void attach_multi (struct attach_state_struct *attach_state)
{
- int stopped;
td_err_e err;
- td_thragent_t *thread_agent;
- struct ps_prochandle proc_handle_local;
- static int thread_db_init = 0;
- stopped = attach_single (pid);
+ err = td_init ();
+ assert (err == TD_OK);
- if (thread_db_init == 0)
+ 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)
{
- err = td_init ();
- assert (err == TD_OK);
- thread_db_init = 1;
+ attach_state->thread_agent = NULL;
+ return;
}
-
- proc_handle_local.pid = pid;
- proc_handle_local.dwfl = NULL;
- err = td_ta_new (&proc_handle_local, &thread_agent);
- if (proc_handle_local.dwfl != NULL)
- dwfl_end (proc_handle_local.dwfl);
- if (err == TD_NOLIBTHREAD)
+ 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)
{
- delay ();
- return stopped;
+ fprintf (stderr, "err = %d\n", err);
+ abort ();
}
- assert (err == TD_OK);
-
- assert (0);
+ /* Do not: assert (attach_state->threads_count > 0);
+ as during early initialization we really may get 0. */
}
-void detach (pid_t pid, int stopped)
+struct attach_state_struct *attach (pid_t pid)
{
- int i;
+ struct attach_state_struct *attach_state;
- delay ();
+ attach_state = attach_single (pid);
+ attach_multi (attach_state);
- i = ptrace (PTRACE_DETACH, pid, NULL,
- (void *) (unsigned long) (stopped ? SIGSTOP : 0));
- assert (i == 0);
+ return attach_state;
+}
- delay ();
+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;
}