X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Flinux-nat.c;h=695dbb0bfc0d9bdf664f3ee862eed139cc033ef6;hb=1143fffb920d9d9995854541dae2c8f6add8387a;hp=c0e6dc1a51c82af81509ce2ce6dd88013b61c740;hpb=9ee57c332efe6e35ee95d8be28f89c320590e0a3;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index c0e6dc1a51..695dbb0bfc 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -1,6 +1,6 @@ /* GNU/Linux native-dependent code common to multiple platforms. - Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GDB. @@ -113,6 +113,7 @@ static int linux_parent_pid; struct simple_pid_list { int pid; + int status; struct simple_pid_list *next; }; struct simple_pid_list *stopped_pids; @@ -131,16 +132,17 @@ static int linux_supports_tracevforkdone_flag = -1; /* Trivial list manipulation functions to keep track of a list of new stopped processes. */ static void -add_to_pid_list (struct simple_pid_list **listp, int pid) +add_to_pid_list (struct simple_pid_list **listp, int pid, int status) { struct simple_pid_list *new_pid = xmalloc (sizeof (struct simple_pid_list)); new_pid->pid = pid; + new_pid->status = status; new_pid->next = *listp; *listp = new_pid; } static int -pull_pid_from_list (struct simple_pid_list **listp, int pid) +pull_pid_from_list (struct simple_pid_list **listp, int pid, int *status) { struct simple_pid_list **p; @@ -148,6 +150,7 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid) if ((*p)->pid == pid) { struct simple_pid_list *next = (*p)->next; + *status = (*p)->status; xfree (*p); *p = next; return 1; @@ -155,10 +158,10 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid) return 0; } -void -linux_record_stopped_pid (int pid) +static void +linux_record_stopped_pid (int pid, int status) { - add_to_pid_list (&stopped_pids, pid); + add_to_pid_list (&stopped_pids, pid, status); } @@ -332,8 +335,8 @@ linux_enable_event_reporting (ptid_t ptid) ptrace (PTRACE_SETOPTIONS, pid, 0, options); } -void -child_post_attach (int pid) +static void +linux_child_post_attach (int pid) { linux_enable_event_reporting (pid_to_ptid (pid)); check_for_thread_db (); @@ -346,8 +349,8 @@ linux_child_post_startup_inferior (ptid_t ptid) check_for_thread_db (); } -int -child_follow_fork (struct target_ops *ops, int follow_child) +static int +linux_child_follow_fork (struct target_ops *ops, int follow_child) { ptid_t last_ptid; struct target_waitstatus last_status; @@ -516,86 +519,23 @@ child_follow_fork (struct target_ops *ops, int follow_child) return 0; } -ptid_t -linux_handle_extended_wait (int pid, int status, - struct target_waitstatus *ourstatus) -{ - int event = status >> 16; - - if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK - || event == PTRACE_EVENT_CLONE) - { - unsigned long new_pid; - int ret; - - ptrace (PTRACE_GETEVENTMSG, pid, 0, &new_pid); - - /* If we haven't already seen the new PID stop, wait for it now. */ - if (! pull_pid_from_list (&stopped_pids, new_pid)) - { - /* The new child has a pending SIGSTOP. We can't affect it until it - hits the SIGSTOP, but we're already attached. */ - ret = my_waitpid (new_pid, &status, - (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0); - if (ret == -1) - perror_with_name (_("waiting for new child")); - else if (ret != new_pid) - internal_error (__FILE__, __LINE__, - _("wait returned unexpected PID %d"), ret); - else if (!WIFSTOPPED (status) || WSTOPSIG (status) != SIGSTOP) - internal_error (__FILE__, __LINE__, - _("wait returned unexpected status 0x%x"), status); - } - - if (event == PTRACE_EVENT_FORK) - ourstatus->kind = TARGET_WAITKIND_FORKED; - else if (event == PTRACE_EVENT_VFORK) - ourstatus->kind = TARGET_WAITKIND_VFORKED; - else - ourstatus->kind = TARGET_WAITKIND_SPURIOUS; - - ourstatus->value.related_pid = new_pid; - return inferior_ptid; - } - - if (event == PTRACE_EVENT_EXEC) - { - ourstatus->kind = TARGET_WAITKIND_EXECD; - ourstatus->value.execd_pathname - = xstrdup (child_pid_to_exec_file (pid)); - - if (linux_parent_pid) - { - detach_breakpoints (linux_parent_pid); - ptrace (PTRACE_DETACH, linux_parent_pid, 0, 0); - - linux_parent_pid = 0; - } - - return inferior_ptid; - } - - internal_error (__FILE__, __LINE__, - _("unknown ptrace event %d"), event); -} - -void -child_insert_fork_catchpoint (int pid) +static void +linux_child_insert_fork_catchpoint (int pid) { if (! linux_supports_tracefork (pid)) error (_("Your system does not support fork catchpoints.")); } -void -child_insert_vfork_catchpoint (int pid) +static void +linux_child_insert_vfork_catchpoint (int pid) { if (!linux_supports_tracefork (pid)) error (_("Your system does not support vfork catchpoints.")); } -void -child_insert_exec_catchpoint (int pid) +static void +linux_child_insert_exec_catchpoint (int pid) { if (!linux_supports_tracefork (pid)) error (_("Your system does not support exec catchpoints.")); @@ -676,6 +616,7 @@ static sigset_t blocked_mask; /* Prototypes for local functions. */ static int stop_wait_callback (struct lwp_info *lp, void *data); static int linux_nat_thread_alive (ptid_t ptid); +static char *linux_child_pid_to_exec_file (int pid); /* Convert wait status STATUS to a string. Used for printing debug messages only. */ @@ -1293,44 +1234,113 @@ kill_lwp (int lwpid, int signo) return kill (lwpid, signo); } -/* Handle a GNU/Linux extended wait response. Most of the work we - just pass off to linux_handle_extended_wait, but if it reports a - clone event we need to add the new LWP to our list (and not report - the trap to higher layers). This function returns non-zero if - the event should be ignored and we should wait again. If STOPPING - is true, the new LWP remains stopped, otherwise it is continued. */ +/* Handle a GNU/Linux extended wait response. If we see a clone + event, we need to add the new LWP to our list (and not report the + trap to higher layers). This function returns non-zero if the + event should be ignored and we should wait again. If STOPPING is + true, the new LWP remains stopped, otherwise it is continued. */ static int -linux_nat_handle_extended (struct lwp_info *lp, int status, int stopping) +linux_handle_extended_wait (struct lwp_info *lp, int status, + int stopping) { - linux_handle_extended_wait (GET_LWP (lp->ptid), status, - &lp->waitstatus); + int pid = GET_LWP (lp->ptid); + struct target_waitstatus *ourstatus = &lp->waitstatus; + struct lwp_info *new_lp = NULL; + int event = status >> 16; - /* TARGET_WAITKIND_SPURIOUS is used to indicate clone events. */ - if (lp->waitstatus.kind == TARGET_WAITKIND_SPURIOUS) + if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK + || event == PTRACE_EVENT_CLONE) { - struct lwp_info *new_lp; - new_lp = add_lwp (BUILD_LWP (lp->waitstatus.value.related_pid, - GET_PID (inferior_ptid))); - new_lp->cloned = 1; + unsigned long new_pid; + int ret; - if (stopping) - new_lp->stopped = 1; + ptrace (PTRACE_GETEVENTMSG, pid, 0, &new_pid); + + /* If we haven't already seen the new PID stop, wait for it now. */ + if (! pull_pid_from_list (&stopped_pids, new_pid, &status)) + { + /* The new child has a pending SIGSTOP. We can't affect it until it + hits the SIGSTOP, but we're already attached. */ + ret = my_waitpid (new_pid, &status, + (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0); + if (ret == -1) + perror_with_name (_("waiting for new child")); + else if (ret != new_pid) + internal_error (__FILE__, __LINE__, + _("wait returned unexpected PID %d"), ret); + else if (!WIFSTOPPED (status)) + internal_error (__FILE__, __LINE__, + _("wait returned unexpected status 0x%x"), status); + } + + ourstatus->value.related_pid = new_pid; + + if (event == PTRACE_EVENT_FORK) + ourstatus->kind = TARGET_WAITKIND_FORKED; + else if (event == PTRACE_EVENT_VFORK) + ourstatus->kind = TARGET_WAITKIND_VFORKED; else - ptrace (PTRACE_CONT, lp->waitstatus.value.related_pid, 0, 0); + { + ourstatus->kind = TARGET_WAITKIND_IGNORE; + new_lp = add_lwp (BUILD_LWP (new_pid, GET_PID (inferior_ptid))); + new_lp->cloned = 1; - lp->waitstatus.kind = TARGET_WAITKIND_IGNORE; + if (WSTOPSIG (status) != SIGSTOP) + { + /* This can happen if someone starts sending signals to + the new thread before it gets a chance to run, which + have a lower number than SIGSTOP (e.g. SIGUSR1). + This is an unlikely case, and harder to handle for + fork / vfork than for clone, so we do not try - but + we handle it for clone events here. We'll send + the other signal on to the thread below. */ + + new_lp->signalled = 1; + } + else + status = 0; - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "LLHE: Got clone event from LWP %ld, resuming\n", - GET_LWP (lp->ptid)); - ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); + if (stopping) + new_lp->stopped = 1; + else + { + new_lp->resumed = 1; + ptrace (PTRACE_CONT, lp->waitstatus.value.related_pid, 0, + status ? WSTOPSIG (status) : 0); + } - return 1; + if (debug_linux_nat) + fprintf_unfiltered (gdb_stdlog, + "LHEW: Got clone event from LWP %ld, resuming\n", + GET_LWP (lp->ptid)); + ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); + + return 1; + } + + return 0; } - return 0; + if (event == PTRACE_EVENT_EXEC) + { + ourstatus->kind = TARGET_WAITKIND_EXECD; + ourstatus->value.execd_pathname + = xstrdup (linux_child_pid_to_exec_file (pid)); + + if (linux_parent_pid) + { + detach_breakpoints (linux_parent_pid); + ptrace (PTRACE_DETACH, linux_parent_pid, 0, 0); + + linux_parent_pid = 0; + } + + return 0; + } + + internal_error (__FILE__, __LINE__, + _("unknown ptrace event %d"), event); } /* Wait for LP to stop. Returns the wait status, or 0 if the LWP has @@ -1401,7 +1411,7 @@ wait_lwp (struct lwp_info *lp) fprintf_unfiltered (gdb_stdlog, "WL: Handling extended status 0x%06x\n", status); - if (linux_nat_handle_extended (lp, status, 1)) + if (linux_handle_extended_wait (lp, status, 1)) return wait_lwp (lp); } @@ -1641,7 +1651,15 @@ flush_callback (struct lwp_info *lp, void *data) lp->status = 0; } - while (linux_nat_has_pending (GET_LWP (lp->ptid), &pending, flush_mask)) + /* While there is a pending signal we would like to flush, continue + the inferior and collect another signal. But if there's already + a saved status that we don't want to flush, we can't resume the + inferior - if it stopped for some other reason we wouldn't have + anywhere to save the new status. In that case, we must leave the + signal unflushed (and possibly generate an extra SIGINT stop). + That's much less bad than losing a signal. */ + while (lp->status == 0 + && linux_nat_has_pending (GET_LWP (lp->ptid), &pending, flush_mask)) { int ret; @@ -1749,7 +1767,8 @@ cancel_breakpoints_callback (struct lwp_info *lp, void *data) if (lp->status != 0 && WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP && breakpoint_inserted_here_p (read_pc_pid (lp->ptid) - - DECR_PC_AFTER_BREAK)) + gdbarch_decr_pc_after_break + (current_gdbarch))) { if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, @@ -1757,8 +1776,10 @@ cancel_breakpoints_callback (struct lwp_info *lp, void *data) target_pid_to_str (lp->ptid)); /* Back up the PC if necessary. */ - if (DECR_PC_AFTER_BREAK) - write_pc_pid (read_pc_pid (lp->ptid) - DECR_PC_AFTER_BREAK, lp->ptid); + if (gdbarch_decr_pc_after_break (current_gdbarch)) + write_pc_pid (read_pc_pid (lp->ptid) - gdbarch_decr_pc_after_break + (current_gdbarch), + lp->ptid); /* Throw away the SIGTRAP. */ lp->status = 0; @@ -1995,7 +2016,7 @@ retry: from waitpid before or after the event is. */ if (WIFSTOPPED (status) && !lp) { - linux_record_stopped_pid (lwpid); + linux_record_stopped_pid (lwpid, status); status = 0; continue; } @@ -2046,7 +2067,7 @@ retry: fprintf_unfiltered (gdb_stdlog, "LLW: Handling extended status 0x%06x\n", status); - if (linux_nat_handle_extended (lp, status, 0)) + if (linux_handle_extended_wait (lp, status, 0)) { status = 0; continue; @@ -2446,8 +2467,8 @@ sigchld_handler (int signo) /* Accepts an integer PID; Returns a string representing a file that can be opened to get the symbols for the child process. */ -char * -child_pid_to_exec_file (int pid) +static char * +linux_child_pid_to_exec_file (int pid) { char *name1, *name2; @@ -2536,7 +2557,7 @@ linux_nat_find_memory_regions (int (*func) (CORE_ADDR, size, paddr_nz (addr), read ? 'r' : ' ', write ? 'w' : ' ', exec ? 'x' : ' '); - if (filename && filename[0]) + if (filename[0]) fprintf_filtered (gdb_stdout, " for %s", filename); fprintf_filtered (gdb_stdout, "\n"); } @@ -2574,7 +2595,7 @@ linux_nat_do_thread_registers (bfd *obfd, ptid_t ptid, regset->collect_regset (regset, current_regcache, -1, &gregs, sizeof (gregs)); else - fill_gregset (&gregs, -1); + fill_gregset (current_regcache, &gregs, -1); note_data = (char *) elfcore_write_prstatus (obfd, note_data, @@ -2589,7 +2610,7 @@ linux_nat_do_thread_registers (bfd *obfd, ptid_t ptid, regset->collect_regset (regset, current_regcache, -1, &fpregs, sizeof (fpregs)); else - fill_fpregset (&fpregs, -1); + fill_fpregset (current_regcache, &fpregs, -1); note_data = (char *) elfcore_write_prfpreg (obfd, note_data, @@ -2604,7 +2625,7 @@ linux_nat_do_thread_registers (bfd *obfd, ptid_t ptid, regset->collect_regset (regset, current_regcache, -1, &fpxregs, sizeof (fpxregs)); else - fill_fpxregset (&fpxregs, -1); + fill_fpxregset (current_regcache, &fpxregs, -1); note_data = (char *) elfcore_write_prxfpreg (obfd, note_data, @@ -2633,8 +2654,8 @@ linux_nat_corefile_thread_callback (struct lwp_info *ti, void *data) inferior_ptid = ti->ptid; registers_changed (); - target_fetch_registers (-1); /* FIXME should not be necessary; - fill_gregset should do it automatically. */ + /* FIXME should not be necessary; fill_gregset should do it automatically. */ + target_fetch_registers (current_regcache, -1); args->note_data = linux_nat_do_thread_registers (args->obfd, ti->ptid, args->note_data, @@ -2642,8 +2663,9 @@ linux_nat_corefile_thread_callback (struct lwp_info *ti, void *data) args->num_notes++; inferior_ptid = saved_ptid; registers_changed (); - target_fetch_registers (-1); /* FIXME should not be necessary; - fill_gregset should do it automatically. */ + /* FIXME should not be necessary; fill_gregset should do it automatically. */ + target_fetch_registers (current_regcache, -1); + return 0; } @@ -2654,8 +2676,8 @@ linux_nat_do_registers (bfd *obfd, ptid_t ptid, char *note_data, int *note_size) { registers_changed (); - target_fetch_registers (-1); /* FIXME should not be necessary; - fill_gregset should do it automatically. */ + /* FIXME should not be necessary; fill_gregset should do it automatically. */ + target_fetch_registers (current_regcache, -1); return linux_nat_do_thread_registers (obfd, ptid_build (ptid_get_pid (inferior_ptid), ptid_get_pid (inferior_ptid), @@ -2804,7 +2826,7 @@ linux_nat_info_proc_cmd (char *args, int from_tty) if (cmdline_f || all) { sprintf (fname1, "/proc/%lld/cmdline", pid); - if ((procfile = fopen (fname1, "r")) > 0) + if ((procfile = fopen (fname1, "r")) != NULL) { fgets (buffer, sizeof (buffer), procfile); printf_filtered ("cmdline = '%s'\n", buffer); @@ -2834,7 +2856,7 @@ linux_nat_info_proc_cmd (char *args, int from_tty) if (mappings_f || all) { sprintf (fname1, "/proc/%lld/maps", pid); - if ((procfile = fopen (fname1, "r")) > 0) + if ((procfile = fopen (fname1, "r")) != NULL) { long long addr, endaddr, size, offset, inode; char permissions[8], device[8], filename[MAXPATHLEN]; @@ -2894,7 +2916,7 @@ linux_nat_info_proc_cmd (char *args, int from_tty) if (status_f || all) { sprintf (fname1, "/proc/%lld/status", pid); - if ((procfile = fopen (fname1, "r")) > 0) + if ((procfile = fopen (fname1, "r")) != NULL) { while (fgets (buffer, sizeof (buffer), procfile) != NULL) puts_filtered (buffer); @@ -2906,7 +2928,7 @@ linux_nat_info_proc_cmd (char *args, int from_tty) if (stat_f || all) { sprintf (fname1, "/proc/%lld/stat", pid); - if ((procfile = fopen (fname1, "r")) > 0) + if ((procfile = fopen (fname1, "r")) != NULL) { int itmp; char ctmp; @@ -3147,47 +3169,44 @@ linux_xfer_partial (struct target_ops *ops, enum target_object object, offset, len); } -#ifndef FETCH_INFERIOR_REGISTERS - -/* Return the address in the core dump or inferior of register - REGNO. */ +/* Create a prototype generic Linux target. The client can override + it with local methods. */ -static CORE_ADDR -linux_register_u_offset (int regno) +static void +linux_target_install_ops (struct target_ops *t) { - /* FIXME drow/2005-09-04: The hardcoded use of register_addr should go - away. This requires disentangling the various definitions of it - (particularly alpha-nat.c's). */ - return register_addr (regno, 0); -} - -#endif + t->to_insert_fork_catchpoint = linux_child_insert_fork_catchpoint; + t->to_insert_vfork_catchpoint = linux_child_insert_vfork_catchpoint; + t->to_insert_exec_catchpoint = linux_child_insert_exec_catchpoint; + t->to_pid_to_exec_file = linux_child_pid_to_exec_file; + t->to_post_startup_inferior = linux_child_post_startup_inferior; + t->to_post_attach = linux_child_post_attach; + t->to_follow_fork = linux_child_follow_fork; + t->to_find_memory_regions = linux_nat_find_memory_regions; + t->to_make_corefile_notes = linux_nat_make_corefile_notes; -/* Create a prototype generic Linux target. The client can override - it with local methods. */ + super_xfer_partial = t->to_xfer_partial; + t->to_xfer_partial = linux_xfer_partial; +} struct target_ops * linux_target (void) { struct target_ops *t; -#ifdef FETCH_INFERIOR_REGISTERS t = inf_ptrace_target (); -#else - t = inf_ptrace_trad_target (linux_register_u_offset); -#endif - t->to_insert_fork_catchpoint = child_insert_fork_catchpoint; - t->to_insert_vfork_catchpoint = child_insert_vfork_catchpoint; - t->to_insert_exec_catchpoint = child_insert_exec_catchpoint; - t->to_pid_to_exec_file = child_pid_to_exec_file; - t->to_post_startup_inferior = linux_child_post_startup_inferior; - t->to_post_attach = child_post_attach; - t->to_follow_fork = child_follow_fork; - t->to_find_memory_regions = linux_nat_find_memory_regions; - t->to_make_corefile_notes = linux_nat_make_corefile_notes; + linux_target_install_ops (t); - super_xfer_partial = t->to_xfer_partial; - t->to_xfer_partial = linux_xfer_partial; + return t; +} + +struct target_ops * +linux_trad_target (CORE_ADDR (*register_u_offset)(struct gdbarch *, int, int)) +{ + struct target_ops *t; + + t = inf_ptrace_trad_target (register_u_offset); + linux_target_install_ops (t); return t; }