+/* Copyright 2007, Red Hat Inc. */
+
+#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 <elfutils/libdwfl.h>
+
+#include "debugger.h"
+
+
+#define USLEEP (1000000 / 2)
+
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+void delay (void)
+{
+#ifdef USLEEP
+ int i;
+
+ i = usleep (USLEEP);
+ assert (i == 0);
+#endif
+}
+
+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 ();
+}
+
+enum state
+ {
+ /* Separate the valid `1 << enum state' and `enum state' ranges. */
+ STATE_FIRST = 4,
+ STATE_ENOENT,
+ STATE_SLEEPING,
+ STATE_RUNNING,
+ STATE_STOPPED,
+ STATE_PTRACED,
+ STATE_ZOMBIE,
+ STATE_LAST
+ };
+
+static const char *state_to_name (enum state state)
+{
+ 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";
+ 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)
+{
+ char status_name[32];
+ char line[LINE_MAX];
+ FILE *f;
+ enum state found;
+
+ 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 ();
+ else
+ {
+ int i;
+
+ found = STATE_ENOENT;
+ while (errno = 0, fgets (line, sizeof (line), f) != NULL)
+ {
+ const char *const string = "State:\t";
+ const size_t length = sizeof "State:\t" - 1;
+
+ if (strncmp (line, string, length) != 0)
+ continue;
+ if (strcmp (line + length, "S (sleeping)\n") == 0)
+ found = STATE_SLEEPING;
+ else if (strcmp (line + length, "R (running)\n") == 0)
+ found = STATE_RUNNING;
+ else if (strcmp (line + length, "T (stopped)\n") == 0)
+ found = STATE_STOPPED;
+ else if (strcmp (line + length, "T (tracing stop)\n") == 0)
+ found = STATE_PTRACED;
+ else if (strcmp (line + length, "Z (zombie)\n") == 0)
+ found = STATE_ZOMBIE;
+ else
+ {
+ fprintf (stderr, "Found an unknown state: %s", line + length);
+ crash ();
+ }
+ }
+ assert (found != STATE_ENOENT);
+ i = fclose (f);
+ assert (i == 0);
+ }
+ if (((1 << found) & expect_mask) == 0)
+ {
+ fprintf (stderr, "Found for PID %d state %s but expecting (%s)\n",
+ (int) pid, state_to_name (found), expect_mask_string);
+ crash ();
+ }
+ return found;
+}
+
+/* Debugging only: Number of signal needing redelivery on PTRACE_ATTACH. */
+int attach_redelivered;
+
+static int attach_single (pid_t pid)
+{
+ int i;
+ int status;
+ int stopped;
+
+ attach_redelivered = 0;
+
+ delay ();
+
+#if 1
+ stopped = (STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING)
+ | (1 << STATE_STOPPED)) == STATE_STOPPED);
+#endif
+
+ i = ptrace (PTRACE_ATTACH, pid, NULL, NULL);
+ assert (i == 0);
+
+ /* FIXME: Why it does not work?
+ Disable also the STATE () call above. */
+#if 0
+ delay();
+
+ 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;
+ else if (errno == ESRCH)
+ stopped = 0;
+ else
+ crash ();
+#endif
+
+ for (;;)
+ {
+ delay ();
+
+ i = waitpid (pid, &status, 0);
+ assert (i == pid);
+ if (!WIFSTOPPED (status))
+ {
+ /* Process may have exited. */
+ fprintf (stderr, "PID %d waitpid(2) status %d\n", (int) pid,
+ status);
+ exit (EXIT_FAILURE);
+ }
+ if (WSTOPSIG (status) == SIGSTOP)
+ break;
+
+ if (attach_redelivered == 0)
+ attach_redelivered = WSTOPSIG (status);
+ else
+ attach_redelivered = -1;
+
+ delay ();
+
+ /* Re-deliver the signal received before SIGSTOP.
+ It happens with about only 1:200000 probability. */
+ i = ptrace (PTRACE_CONT, pid, (void *) 1,
+ (void *) (unsigned long) WSTOPSIG (status));
+ assert (i == 0);
+ }
+
+ return stopped;
+}
+
+struct ps_prochandle_module
+ {
+ struct ps_prochandle_module *next;
+ Dwfl_Module *module;
+ };
+
+struct ps_prochandle
+ {
+ pid_t pid;
+ Dwfl *dwfl;
+ struct ps_prochandle_module *module_list;
+ };
+
+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);
+}
+
+static Dwfl *get_dwfl (struct ps_prochandle *proc_handle)
+{
+ static char *debuginfo_path;
+
+ static const Dwfl_Callbacks proc_callbacks =
+ {
+ .find_debuginfo = get_dwfl_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);
+
+ 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 ();
+ }
+ }
+ 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)
+{
+ crash ();
+}
+
+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)
+{
+ crash ();
+}
+
+/* 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);
+
+#if 0
+ Elf *elf = FIXME;
+ Elf_Scn *scn = NULL;
+ int sym_count, ndx;
+
+ sym_count = dwfl_module_getsymtab (Dwfl_Module *mod);
+ 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);
+ assert (name_got != NULL);
+ if (strcmp (name_got, sym_name) == 0)
+ break;
+ }
+ assert (ndx < sym_count);
+
+extern Elf *dwarf_getelf (Dwarf *dwarf);
+
+
+ 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);
+
+ if (shdr != NULL && shdr->sh_type == SHT_SYMTAB)
+ handle_symtab (ebl, scn, shdr);
+ }
+
+
+ for (modulei = 0; modulei < dwfl->nmodules; modulei++)
+ {
+ Dwfl_Module *m = dwfl->modules[modulei];
+
+ puts (m->name);
+ }
+#if 0
+ if (strcmp (sym_name, "nptl_version") == 0)
+ {
+ extern const char nptl_version[];
+ *sym_addr = (psaddr_t) nptl_version;
+ return PS_OK;
+ }
+#endif
+#endif
+ crash ();
+}
+
+int attach (pid_t pid)
+{
+ 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);
+
+ if (thread_db_init == 0)
+ {
+ err = td_init ();
+ assert (err == TD_OK);
+ thread_db_init = 1;
+ }
+
+ 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)
+ {
+ delay ();
+ return stopped;
+ }
+ assert (err == TD_OK);
+
+ assert (0);
+}
+
+void detach (pid_t pid, int stopped)
+{
+ int i;
+
+ delay ();
+
+ i = ptrace (PTRACE_DETACH, pid, NULL,
+ (void *) (unsigned long) (stopped ? SIGSTOP : 0));
+ assert (i == 0);
+
+ delay ();
+}
+
+#ifndef LIBRARY
+
+int main (int argc, char **argv)
+{
+ pid_t pid;
+ int stopped;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Usage: %s <PID>\n", argv[0]);
+ exit (EXIT_FAILURE);
+ }
+
+ pid = atoi (argv[1]);
+
+ stopped = attach (pid);
+
+ detach (pid, stopped);
+
+ return EXIT_SUCCESS;
+}
+
+#endif /* !LIBRARY */