/* Copyright 2007, Red Hat Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 >> 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 \n", argv[0]); exit (EXIT_FAILURE); } pid = atoi (argv[1]); stopped = attach (pid); detach (pid, stopped); return EXIT_SUCCESS; } #endif /* !LIBRARY */