X-Git-Url: http://git.jankratochvil.net/?p=debugger.git;a=blobdiff_plain;f=debugger.c;h=fd6e0f7025663e7789e765419bcae4413376e92f;hp=f880038bcc12bf14217a5e472dfb373fe08cb5cc;hb=HEAD;hpb=21acf600ec5492bfdb5451443e4f2e20b70d1971 diff --git a/debugger.c b/debugger.c index f880038..fd6e0f7 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 @@ -11,14 +14,31 @@ #include #include #include -#include -#include +#include +#include +#include +#include #include +#include +#include #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])) @@ -60,12 +80,15 @@ enum state { /* 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 }; @@ -73,21 +96,22 @@ 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"; + 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]; @@ -96,27 +120,20 @@ static enum state state (pid_t pid, unsigned expect_mask, const char *expect_mas 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"; @@ -134,42 +151,114 @@ static enum state state (pid_t pid, unsigned expect_mask, const char *expect_mas 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); @@ -182,9 +271,9 @@ static int attach_single (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 @@ -205,10 +294,10 @@ static int attach_single (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 (); @@ -219,33 +308,20 @@ static int attach_single (pid_t pid) 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) @@ -254,7 +330,7 @@ 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, @@ -265,12 +341,12 @@ static Dwfl *get_dwfl (struct ps_prochandle *proc_handle) 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; @@ -291,7 +367,48 @@ typedef enum 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, @@ -327,114 +444,170 @@ ps_err_e ps_lsetfpregs (struct ps_prochandle *proc_handle, lwpid_t lwp, /* 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 @@ -442,7 +615,7 @@ void detach (pid_t pid, int stopped) int main (int argc, char **argv) { pid_t pid; - int stopped; + struct attach_state_struct *attach_state; if (argc != 2) { @@ -452,9 +625,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; }