diff options
Diffstat (limited to 'gdb/linux-nat.c')
-rw-r--r-- | gdb/linux-nat.c | 417 |
1 files changed, 122 insertions, 295 deletions
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 2ba5d7d..88e0d7d 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -88,6 +88,7 @@ /* The single-threaded native GNU/Linux target_ops. We save a pointer for the use of the multi-threaded target. */ static struct target_ops *linux_ops; +static struct target_ops linux_ops_saved; /* The saved to_xfer_partial method, inherited from inf-ptrace.c. Called by our to_xfer_partial. */ @@ -97,10 +98,6 @@ static LONGEST (*super_xfer_partial) (struct target_ops *, const gdb_byte *, ULONGEST, LONGEST); -/* The saved to_mourn_inferior method, inherited from inf-ptrace.c. - Called by our to_mourn_inferior. */ -static void (*super_mourn_inferior) (void); - static int debug_linux_nat; static void show_debug_linux_nat (struct ui_file *file, int from_tty, @@ -600,54 +597,6 @@ child_insert_exec_catchpoint (int pid) error (_("Your system does not support exec catchpoints.")); } -void -kill_inferior (void) -{ - int status; - int pid = PIDGET (inferior_ptid); - struct target_waitstatus last; - ptid_t last_ptid; - int ret; - - if (pid == 0) - return; - - /* First cut -- let's crudely do everything inline. */ - if (forks_exist_p ()) - { - linux_fork_killall (); - } - else - { - /* If we're stopped while forking and we haven't followed yet, - kill the other task. We need to do this first because the - parent will be sleeping if this is a vfork. */ - - get_last_target_status (&last_ptid, &last); - - if (last.kind == TARGET_WAITKIND_FORKED - || last.kind == TARGET_WAITKIND_VFORKED) - { - ptrace (PT_KILL, last.value.related_pid, 0, 0); - wait (&status); - } - - /* Kill the current process. */ - ptrace (PT_KILL, pid, 0, 0); - ret = wait (&status); - - /* We might get a SIGCHLD instead of an exit status. This is - aggravated by the first kill above - a child has just died. */ - - while (ret == pid && WIFSTOPPED (status)) - { - ptrace (PT_KILL, pid, 0, 0); - ret = wait (&status); - } - } - target_mourn_inferior (); -} - /* On GNU/Linux there are no real LWP's. The closest thing to LWP's are processes sharing the same VM space. A multi-threaded process is basically a group of such processes. However, such a grouping @@ -686,9 +635,6 @@ static struct lwp_info *lwp_list; /* Number of LWPs in the list. */ static int num_lwps; - -/* Non-zero if we're running in "threaded" mode. */ -static int threaded; #define GET_LWP(ptid) ptid_get_lwp (ptid) @@ -701,9 +647,6 @@ static int threaded; ptid_t trap_ptid; -/* This module's target-specific operations. */ -static struct target_ops linux_nat_ops; - /* Since we cannot wait (in linux_nat_wait) for the initial process and any cloned processes with a single call to waitpid, we have to use the WNOHANG flag and call waitpid in a loop. To optimize @@ -768,12 +711,10 @@ init_lwp_list (void) lwp_list = NULL; num_lwps = 0; - threaded = 0; } -/* Add the LWP specified by PID to the list. If this causes the - number of LWPs to become larger than one, go into "threaded" mode. - Return a pointer to the structure describing the new LWP. */ +/* Add the LWP specified by PID to the list. Return a pointer to the + structure describing the new LWP. */ static struct lwp_info * add_lwp (ptid_t ptid) @@ -792,8 +733,7 @@ add_lwp (ptid_t ptid) lp->next = lwp_list; lwp_list = lp; - if (++num_lwps > 1) - threaded = 1; + ++num_lwps; return lp; } @@ -814,8 +754,6 @@ delete_lwp (ptid_t ptid) if (!lp) return; - /* We don't go back to "non-threaded" mode if the number of threads - becomes less than two. */ num_lwps--; if (lpprev) @@ -867,6 +805,21 @@ iterate_over_lwps (int (*callback) (struct lwp_info *, void *), void *data) return NULL; } +/* Update our internal state when changing from one fork (checkpoint, + et cetera) to another indicated by NEW_PTID. We can only switch + single-threaded applications, so we only create one new LWP, and + the previous list is discarded. */ + +void +linux_nat_switch_fork (ptid_t new_ptid) +{ + struct lwp_info *lp; + + init_lwp_list (); + lp = add_lwp (new_ptid); + lp->stopped = 1; +} + /* Record a PTID for later deletion. */ struct saved_ptids @@ -1046,7 +999,8 @@ linux_nat_attach (char *args, int from_tty) linux_ops->to_attach (args, from_tty); /* Add the initial process as the first LWP to the list. */ - lp = add_lwp (BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid))); + inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid)); + lp = add_lwp (inferior_ptid); /* Make sure the initial process is stopped. The user-level threads layer might want to poke around in the inferior, and that won't @@ -1848,134 +1802,6 @@ resumed_callback (struct lwp_info *lp, void *data) return lp->resumed; } -/* Local mourn_inferior -- we need to override mourn_inferior - so that we can do something clever if one of several forks - has exited. */ - -static void -child_mourn_inferior (void) -{ - int status; - - if (! forks_exist_p ()) - { - /* Normal case, no other forks available. */ - super_mourn_inferior (); - return; - } - else - { - /* Multi-fork case. The current inferior_ptid has exited, but - there are other viable forks to debug. Delete the exiting - one and context-switch to the first available. */ - linux_fork_mourn_inferior (); - } -} - -/* We need to override child_wait to support attaching to cloned - processes, since a normal wait (as done by the default version) - ignores those processes. */ - -/* Wait for child PTID to do something. Return id of the child, - minus_one_ptid in case of error; store status into *OURSTATUS. */ - -ptid_t -child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) -{ - int save_errno; - int status; - pid_t pid; - - ourstatus->kind = TARGET_WAITKIND_IGNORE; - - do - { - set_sigint_trap (); /* Causes SIGINT to be passed on to the - attached process. */ - set_sigio_trap (); - - pid = my_waitpid (GET_PID (ptid), &status, 0); - if (pid == -1 && errno == ECHILD) - /* Try again with __WCLONE to check cloned processes. */ - pid = my_waitpid (GET_PID (ptid), &status, __WCLONE); - - if (debug_linux_nat) - { - fprintf_unfiltered (gdb_stdlog, - "CW: waitpid %ld received %s\n", - (long) pid, status_to_str (status)); - } - - save_errno = errno; - - /* Make sure we don't report an event for the exit of the - original program, if we've detached from it. */ - if (pid != -1 && !WIFSTOPPED (status) && pid != GET_PID (inferior_ptid)) - { - pid = -1; - save_errno = EINTR; - } - - /* Check for stop events reported by a process we didn't already - know about - in this case, anything other than inferior_ptid. - - If we're expecting to receive stopped processes after fork, - vfork, and clone events, then we'll just add the new one to - our list and go back to waiting for the event to be reported - - the stopped process might be returned from waitpid before - or after the event is. If we want to handle debugging of - CLONE_PTRACE processes we need to do more here, i.e. switch - to multi-threaded mode. */ - if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP - && pid != GET_PID (inferior_ptid)) - { - linux_record_stopped_pid (pid); - pid = -1; - save_errno = EINTR; - } - - /* Handle GNU/Linux's extended waitstatus for trace events. */ - if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP - && status >> 16 != 0) - { - linux_handle_extended_wait (pid, status, ourstatus); - - /* If we see a clone event, detach the child, and don't - report the event. It would be nice to offer some way to - switch into a non-thread-db based threaded mode at this - point. */ - if (ourstatus->kind == TARGET_WAITKIND_SPURIOUS) - { - ptrace (PTRACE_DETACH, ourstatus->value.related_pid, 0, 0); - ourstatus->kind = TARGET_WAITKIND_IGNORE; - ptrace (PTRACE_CONT, pid, 0, 0); - pid = -1; - save_errno = EINTR; - } - } - - clear_sigio_trap (); - clear_sigint_trap (); - } - while (pid == -1 && save_errno == EINTR); - - if (pid == -1) - { - warning (_("Child process unexpectedly missing: %s"), - safe_strerror (errno)); - - /* Claim it exited with unknown signal. */ - ourstatus->kind = TARGET_WAITKIND_SIGNALLED; - ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; - return minus_one_ptid; - } - - if (ourstatus->kind == TARGET_WAITKIND_IGNORE) - store_waitstatus (ourstatus, status); - - return pid_to_ptid (pid); -} - /* Stop an active thread, verify it still exists, then resume it. */ static int @@ -2007,6 +1833,19 @@ linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus) pid_t pid = PIDGET (ptid); sigset_t flush_mask; + /* The first time we get here after starting a new inferior, we may + not have added it to the LWP list yet - this is the earliest + moment at which we know its PID. */ + if (num_lwps == 0) + { + gdb_assert (!is_lwp (inferior_ptid)); + + inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), + GET_PID (inferior_ptid)); + lp = add_lwp (inferior_ptid); + lp->resumed = 1; + } + sigemptyset (&flush_mask); /* Make sure SIGCHLD is blocked. */ @@ -2018,9 +1857,8 @@ linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus) retry: - /* Make sure there is at least one LWP that has been resumed, at - least if there are any LWPs at all. */ - gdb_assert (num_lwps == 0 || iterate_over_lwps (resumed_callback, NULL)); + /* Make sure there is at least one LWP that has been resumed. */ + gdb_assert (iterate_over_lwps (resumed_callback, NULL)); /* First check if there is a LWP with a wait status pending. */ if (pid == -1) @@ -2159,23 +1997,20 @@ retry: if (options & __WCLONE) lp->cloned = 1; - if (threaded) - { - gdb_assert (WIFSTOPPED (status) - && WSTOPSIG (status) == SIGSTOP); - lp->signalled = 1; - - if (!in_thread_list (inferior_ptid)) - { - inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), - GET_PID (inferior_ptid)); - add_thread (inferior_ptid); - } + gdb_assert (WIFSTOPPED (status) + && WSTOPSIG (status) == SIGSTOP); + lp->signalled = 1; - add_thread (lp->ptid); - printf_unfiltered (_("[New %s]\n"), - target_pid_to_str (lp->ptid)); + if (!in_thread_list (inferior_ptid)) + { + inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), + GET_PID (inferior_ptid)); + add_thread (inferior_ptid); } + + add_thread (lp->ptid); + printf_unfiltered (_("[New %s]\n"), + target_pid_to_str (lp->ptid)); } /* Handle GNU/Linux's extended waitstatus for trace events. */ @@ -2377,12 +2212,9 @@ retry: the comment in cancel_breakpoints_callback to find out why. */ iterate_over_lwps (cancel_breakpoints_callback, lp); - /* If we're not running in "threaded" mode, we'll report the bare - process id. */ - if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP) { - trap_ptid = (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid))); + trap_ptid = lp->ptid; if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, "LLW: trap_ptid is %s.\n", @@ -2399,7 +2231,7 @@ retry: else store_waitstatus (ourstatus, status); - return (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid))); + return lp->ptid; } static int @@ -2464,20 +2296,35 @@ kill_wait_callback (struct lwp_info *lp, void *data) static void linux_nat_kill (void) { - /* Kill all LWP's ... */ - iterate_over_lwps (kill_callback, NULL); + struct target_waitstatus last; + ptid_t last_ptid; + int status; - /* ... and wait until we've flushed all events. */ - iterate_over_lwps (kill_wait_callback, NULL); + /* If we're stopped while forking and we haven't followed yet, + kill the other task. We need to do this first because the + parent will be sleeping if this is a vfork. */ - target_mourn_inferior (); -} + get_last_target_status (&last_ptid, &last); -static void -linux_nat_create_inferior (char *exec_file, char *allargs, char **env, - int from_tty) -{ - linux_ops->to_create_inferior (exec_file, allargs, env, from_tty); + if (last.kind == TARGET_WAITKIND_FORKED + || last.kind == TARGET_WAITKIND_VFORKED) + { + ptrace (PT_KILL, last.value.related_pid, 0, 0); + wait (&status); + } + + if (forks_exist_p ()) + linux_fork_killall (); + else + { + /* Kill all LWP's ... */ + iterate_over_lwps (kill_callback, NULL); + + /* ... and wait until we've flushed all events. */ + iterate_over_lwps (kill_wait_callback, NULL); + } + + target_mourn_inferior (); } static void @@ -2492,7 +2339,14 @@ linux_nat_mourn_inferior (void) sigprocmask (SIG_SETMASK, &normal_mask, NULL); sigemptyset (&blocked_mask); - linux_ops->to_mourn_inferior (); + if (! forks_exist_p ()) + /* Normal case, no other forks available. */ + linux_ops->to_mourn_inferior (); + else + /* Multi-fork case. The current inferior_ptid has exited, but + there are other viable forks to debug. Delete the exiting + one and context-switch to the first available. */ + linux_fork_mourn_inferior (); } static LONGEST @@ -2537,7 +2391,7 @@ linux_nat_pid_to_str (ptid_t ptid) { static char buf[64]; - if (is_lwp (ptid)) + if (lwp_list && lwp_list->next && is_lwp (ptid)) { snprintf (buf, sizeof (buf), "LWP %ld", GET_LWP (ptid)); return buf; @@ -2547,59 +2401,6 @@ linux_nat_pid_to_str (ptid_t ptid) } static void -linux_nat_fetch_registers (int regnum) -{ - /* to_fetch_registers will honor the LWP ID, so we can use it directly. */ - linux_ops->to_fetch_registers (regnum); -} - -static void -linux_nat_store_registers (int regnum) -{ - /* to_store_registers will honor the LWP ID, so we can use it directly. */ - linux_ops->to_store_registers (regnum); -} - -static void -linux_nat_child_post_startup_inferior (ptid_t ptid) -{ - linux_ops->to_post_startup_inferior (ptid); -} - -static void -init_linux_nat_ops (void) -{ -#if 0 - linux_nat_ops.to_open = linux_nat_open; -#endif - linux_nat_ops.to_shortname = "lwp-layer"; - linux_nat_ops.to_longname = "lwp-layer"; - linux_nat_ops.to_doc = "Low level threads support (LWP layer)"; - linux_nat_ops.to_attach = linux_nat_attach; - linux_nat_ops.to_detach = linux_nat_detach; - linux_nat_ops.to_resume = linux_nat_resume; - linux_nat_ops.to_wait = linux_nat_wait; - linux_nat_ops.to_fetch_registers = linux_nat_fetch_registers; - linux_nat_ops.to_store_registers = linux_nat_store_registers; - linux_nat_ops.to_xfer_partial = linux_nat_xfer_partial; - linux_nat_ops.to_kill = linux_nat_kill; - linux_nat_ops.to_create_inferior = linux_nat_create_inferior; - linux_nat_ops.to_mourn_inferior = linux_nat_mourn_inferior; - linux_nat_ops.to_thread_alive = linux_nat_thread_alive; - linux_nat_ops.to_pid_to_str = linux_nat_pid_to_str; - linux_nat_ops.to_post_startup_inferior - = linux_nat_child_post_startup_inferior; - linux_nat_ops.to_post_attach = child_post_attach; - linux_nat_ops.to_insert_fork_catchpoint = child_insert_fork_catchpoint; - linux_nat_ops.to_insert_vfork_catchpoint = child_insert_vfork_catchpoint; - linux_nat_ops.to_insert_exec_catchpoint = child_insert_exec_catchpoint; - - linux_nat_ops.to_stratum = thread_stratum; - linux_nat_ops.to_has_thread_control = tc_schedlock; - linux_nat_ops.to_magic = OPS_MAGIC; -} - -static void sigchld_handler (int signo) { /* Do nothing. The only reason for this handler is that it allows @@ -3310,8 +3111,6 @@ linux_target (void) #else t = inf_ptrace_trad_target (linux_register_u_offset); #endif - t->to_wait = child_wait; - t->to_kill = kill_inferior; 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; @@ -3325,18 +3124,50 @@ linux_target (void) super_xfer_partial = t->to_xfer_partial; t->to_xfer_partial = linux_xfer_partial; - super_mourn_inferior = t->to_mourn_inferior; - t->to_mourn_inferior = child_mourn_inferior; - - linux_ops = t; return t; } void +linux_nat_add_target (struct target_ops *t) +{ + extern void thread_db_init (struct target_ops *); + + /* Save the provided single-threaded target. We save this in a separate + variable because another target we've inherited from (e.g. inf-ptrace) + may have saved a pointer to T; we want to use it for the final + process stratum target. */ + linux_ops_saved = *t; + linux_ops = &linux_ops_saved; + + /* Override some methods for multithreading. */ + t->to_attach = linux_nat_attach; + t->to_detach = linux_nat_detach; + t->to_resume = linux_nat_resume; + t->to_wait = linux_nat_wait; + t->to_xfer_partial = linux_nat_xfer_partial; + t->to_kill = linux_nat_kill; + t->to_mourn_inferior = linux_nat_mourn_inferior; + t->to_thread_alive = linux_nat_thread_alive; + t->to_pid_to_str = linux_nat_pid_to_str; + t->to_has_thread_control = tc_schedlock; + + /* We don't change the stratum; this target will sit at + process_stratum and thread_db will set at thread_stratum. This + is a little strange, since this is a multi-threaded-capable + target, but we want to be on the stack below thread_db, and we + also want to be used for single-threaded processes. */ + + add_target (t); + + /* TODO: Eliminate this and have libthread_db use + find_target_beneath. */ + thread_db_init (t); +} + +void _initialize_linux_nat (void) { struct sigaction action; - extern void thread_db_init (struct target_ops *); add_info ("proc", linux_nat_info_proc_cmd, _("\ Show /proc process information about any running process.\n\ @@ -3347,10 +3178,6 @@ Specify any of the following keywords for detailed info:\n\ status -- list a different bunch of random process info.\n\ all -- list all available /proc info.")); - init_linux_nat_ops (); - add_target (&linux_nat_ops); - thread_db_init (&linux_nat_ops); - /* Save the original signal mask. */ sigprocmask (SIG_SETMASK, NULL, &normal_mask); |