diff options
Diffstat (limited to 'gdb/gdbserver/linux-low.c')
-rw-r--r-- | gdb/gdbserver/linux-low.c | 2002 |
1 files changed, 1506 insertions, 496 deletions
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 4766cc91e9e..c303e444c3a 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -82,16 +82,19 @@ #endif #endif -/* ``all_threads'' is keyed by the LWP ID, which we use as the GDB protocol - representation of the thread ID. +/* ``all_processes'' is keyed by the "overall process ID", which + GNU/Linux calls tgid, "thread group ID". - ``all_processes'' is keyed by the process ID - which on Linux is (presently) - the same as the LWP ID. */ + ``all_threads'' is keyed by the LWP ID, which we use as the GDB + protocol representation of the thread ID. -struct inferior_list all_processes; + ``all_lwps'' is keyed by the LWP ID - on Linux the LWP IDs are + global (presently). */ -/* A list of all unknown processes which receive stop signals. Some other - process will presumably claim each of these as forked children +struct inferior_list all_lwps; + +/* A list of all unknown lwps which receive stop signals. Some other + lwp will presumably claim each of these as forked children momentarily. */ struct inferior_list stopped_pids; @@ -101,22 +104,25 @@ int stopping_threads; /* FIXME make into a target method? */ int using_threads = 1; -static int thread_db_active; static int must_set_ptrace_flags; -/* This flag is true iff we've just created or attached to a new inferior - but it has not stopped yet. As soon as it does, we need to call the - low target's arch_setup callback. */ +/* This flag is true iff we've just created or attached to our first + inferior but it has not stopped yet. As soon as it does, we need + to call the low target's arch_setup callback. Doing this only on + the first inferior avoids reinializing the architecture on every + inferior, and avoids messing with the register caches of the + already running inferiors. NOTE: this assumes all inferiors under + control of gdbserver have the same architecture. */ static int new_inferior; -static void linux_resume_one_process (struct inferior_list_entry *entry, - int step, int signal, siginfo_t *info); -static void linux_resume (struct thread_resume *resume_info); -static void stop_all_processes (void); -static int linux_wait_for_event (struct thread_info *child); -static int check_removed_breakpoint (struct process_info *event_child); -static void *add_process (unsigned long pid); +static void linux_resume_one_lwp (struct inferior_list_entry *entry, + int step, int signal, siginfo_t *info); +static void linux_resume (struct thread_resume *resume_info, size_t n); +static void stop_all_lwps (void); +static int linux_wait_for_event (ptid_t ptid, int *wstat, int options); +static int check_removed_breakpoint (struct lwp_info *event_child); +static void *add_lwp (ptid_t ptid); struct pending_signals { @@ -133,23 +139,124 @@ static char *disabled_regsets; static int num_regsets; #endif -#define pid_of(proc) ((proc)->head.id) +#define pid_of(proc) ptid_get_pid ((proc)->head.id) +#define lwpid_of(proc) ptid_get_lwp ((proc)->head.id) + +#define is_lwpid(ptid) (ptid_get_lwp (ptid) != 0) /* FIXME: Delete eventually. */ -#define inferior_pid (pid_of (get_thread_process (current_inferior))) +#define inferior_pid (pid_of (get_thread_lwp (current_inferior))) +#define inferior_lwpid (lwpid_of (get_thread_lwp (current_inferior))) + +/* The read/write ends of the pipe registered as waitable file in the + event loop. */ +static int linux_event_pipe[2] = { -1, -1 }; + +/* True if we're currently in async mode. */ +#define target_is_async_p() (linux_event_pipe[0] != -1) + +static int my_waitpid (int pid, int *status, int flags); +static void send_sigstop (struct inferior_list_entry *entry); +static void wait_for_sigstop (struct inferior_list_entry *entry); +static int cancel_breakpoints_callback (struct inferior_list_entry *entry, + void *data); + +/* Add a process to the common process list, and set its private + data. */ + +static struct process_info * +linux_add_process (int pid, int attached) +{ + struct process_info *proc; + + proc = add_process (pid, attached); + proc->private = calloc (1, sizeof (*proc->private)); + + return proc; +} + +/* Read the tgid of an lwp from `/proc/PID/status'. */ + +static int +tgid_of_pid (int pid) +{ + FILE *status_file; + char buf[100]; + int tgid = 0; + + snprintf (buf, sizeof (buf), "/proc/%d/status", pid); + status_file = fopen (buf, "r"); + if (status_file != NULL) + { + while (fgets (buf, sizeof (buf), status_file)) + { + if (strncmp (buf, "Tgid:", 5) == 0) + { + tgid = strtol (buf + 5, NULL, 0); + break; + } + } + fclose (status_file); + } + + return tgid; +} + +static char * +linux_pid_to_exec_file (int pid) +{ + static char path[MAXPATHLEN]; + char procexe[MAXPATHLEN]; + + sprintf (procexe, "/proc/%d/exe", pid); + if (readlink (procexe, path, MAXPATHLEN) > 0) + return path; + else + return NULL; +} + +static int linux_supports_tracefork_flag; static void -handle_extended_wait (struct process_info *event_child, int wstat) +linux_enable_event_reporting (int pid) +{ + int options; + + options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE; + +#if 0 + /* TODO: We need to model this in the remote protocol. */ + if (linux_supports_tracevforkdone (pid)) + options |= PTRACE_O_TRACEVFORKDONE; +#endif + + /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support + read-only process state. */ + ptrace (PTRACE_SETOPTIONS, pid, 0, options); +} + +/* 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. */ + +static int +handle_extended_wait (struct lwp_info *event_child, int wstat) { int event = wstat >> 16; - struct process_info *new_process; + struct lwp_info *new_lwp; + struct target_waitstatus *ourstatus = &event_child->waitstatus; - if (event == PTRACE_EVENT_CLONE) + if (event == PTRACE_EVENT_FORK + || event == PTRACE_EVENT_VFORK + || event == PTRACE_EVENT_CLONE) { + ptid_t ptid; unsigned long new_pid; int ret, status = W_STOPCODE (SIGSTOP); - ptrace (PTRACE_GETEVENTMSG, inferior_pid, 0, &new_pid); + ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_child), 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)) @@ -157,9 +264,7 @@ handle_extended_wait (struct process_info *event_child, int wstat) /* The new child has a pending SIGSTOP. We can't affect it until it hits the SIGSTOP, but we're already attached. */ - do { - ret = waitpid (new_pid, &status, __WALL); - } while (ret == -1 && errno == EINTR); + ret = my_waitpid (new_pid, &status, __WALL); if (ret == -1) perror_with_name ("waiting for new child"); @@ -169,54 +274,103 @@ handle_extended_wait (struct process_info *event_child, int wstat) warning ("wait returned unexpected status 0x%x", status); } - ptrace (PTRACE_SETOPTIONS, new_pid, 0, PTRACE_O_TRACECLONE); + ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0); - new_process = (struct process_info *) add_process (new_pid); - add_thread (new_pid, new_process, new_pid); - new_thread_notify (thread_id_to_gdb_id (new_process->lwpid)); + linux_enable_event_reporting (new_pid); - /* Normally we will get the pending SIGSTOP. But in some cases - we might get another signal delivered to the group first. - If we do, be sure not to lose it. */ - if (WSTOPSIG (status) == SIGSTOP) + if (event == PTRACE_EVENT_FORK + || event == PTRACE_EVENT_VFORK) { - if (stopping_threads) - new_process->stopped = 1; + int is_fork = (event == PTRACE_EVENT_FORK); + + if (is_fork) + ourstatus->kind = TARGET_WAITKIND_FORKED; else - ptrace (PTRACE_CONT, new_pid, 0, 0); + ourstatus->kind = TARGET_WAITKIND_VFORKED; + + if (debug_threads) + { + if (is_fork) + fprintf (stderr, "PTRACE_EVENT_FORK pid=(%ld), newpid=(%ld)", + lwpid_of (event_child), new_pid); + else + fprintf (stderr, "PTRACE_EVENT_VFORK pid=(%ld), newpid=(%ld)", + lwpid_of (event_child), new_pid); + } + + linux_add_process (new_pid, 1); + ptid = ptid_build (new_pid, new_pid, 0); + new_lwp = (struct lwp_info *) add_lwp (ptid); + new_lwp->stopped = 1; + add_thread (ptid, new_lwp); + + event_child->stopped = 1; + return 0; } else { - new_process->stop_expected = 1; - if (stopping_threads) + ptid = ptid_build (pid_of (event_child), new_pid, 0); + new_lwp = (struct lwp_info *) add_lwp (ptid); + add_thread (ptid, new_lwp); +#if 0 + new_thread_notify (thread_id_to_gdb_id (new_lwp->lwpid)); +#endif + + /* Normally we will get the pending SIGSTOP. But in some + cases we might get another signal delivered to the group + first. If we do, be sure not to lose it. */ + if (WSTOPSIG (status) == SIGSTOP) { - new_process->stopped = 1; - new_process->status_pending_p = 1; - new_process->status_pending = status; + if (stopping_threads) + new_lwp->stopped = 1; + else + ptrace (PTRACE_CONT, new_pid, 0, 0); } else - /* Pass the signal on. This is what GDB does - except - shouldn't we really report it instead? */ - ptrace (PTRACE_CONT, new_pid, 0, WSTOPSIG (status)); - } + { + new_lwp->stop_expected = 1; + if (stopping_threads) + { + new_lwp->stopped = 1; + new_lwp->status_pending_p = 1; + new_lwp->status_pending = status; + } + else + /* Pass the signal on. This is what GDB does - except + shouldn't we really report it instead? */ + ptrace (PTRACE_CONT, new_pid, 0, WSTOPSIG (status)); + } - /* Always resume the current thread. If we are stopping - threads, it will have a pending SIGSTOP; we may as well - collect it now. */ - linux_resume_one_process (&event_child->head, + /* Always resume the current thread. If we are stopping + threads, it will have a pending SIGSTOP; we may as well + collect it now. */ + linux_resume_one_lwp (&event_child->head, event_child->stepping, 0, NULL); + return 1; + } } + else if (event == PTRACE_EVENT_EXEC) + { + fprintf (stderr, "got PTRACE_EVENT_EXEC\n"); + + ourstatus->kind = TARGET_WAITKIND_EXECD; + ourstatus->value.execd_pathname + = strdup (linux_pid_to_exec_file (lwpid_of (event_child))); + return 0; + } + + error ("unknown ptrace event %d", event); } -/* This function should only be called if the process got a SIGTRAP. +/* This function should only be called if the lwp got a SIGTRAP. The SIGTRAP could mean several things. On i386, where decr_pc_after_break is non-zero: - If we were single-stepping this process using PTRACE_SINGLESTEP, + If we were single-stepping this lwp using PTRACE_SINGLESTEP, we will get only the one SIGTRAP (even if the instruction we stepped over was a breakpoint). The value of $eip will be the next instruction. - If we continue the process using PTRACE_CONT, we will get a + If we continue the lwp using PTRACE_CONT, we will get a SIGTRAP when we hit a breakpoint. The value of $eip will be the instruction after the breakpoint (i.e. needs to be decremented). If we report the SIGTRAP to GDB, we must also @@ -235,26 +389,27 @@ get_stop_pc (void) { CORE_ADDR stop_pc = (*the_low_target.get_pc) (); - if (get_thread_process (current_inferior)->stepping) + if (get_thread_lwp (current_inferior)->stepping) return stop_pc; else return stop_pc - the_low_target.decr_pc_after_break; } static void * -add_process (unsigned long pid) +add_lwp (ptid_t ptid) { - struct process_info *process; + struct lwp_info *lwp; + + lwp = (struct lwp_info *) malloc (sizeof (*lwp)); + memset (lwp, 0, sizeof (*lwp)); - process = (struct process_info *) malloc (sizeof (*process)); - memset (process, 0, sizeof (*process)); + lwp->head.id = ptid; - process->head.id = pid; - process->lwpid = pid; + lwp->waitstatus.kind = TARGET_WAITKIND_IGNORE; - add_inferior_to_list (&all_processes, &process->head); + add_inferior_to_list (&all_lwps, &lwp->head); - return process; + return lwp; } /* Start an inferior process and returns its pid. @@ -263,8 +418,9 @@ add_process (unsigned long pid) static int linux_create_inferior (char *program, char **allargs) { - void *new_process; + void *new_lwp; int pid; + ptid_t ptid; #if defined(__UCLIBC__) && defined(HAS_NOMMU) pid = vfork (); @@ -292,10 +448,16 @@ linux_create_inferior (char *program, char **allargs) _exit (0177); } - new_process = add_process (pid); - add_thread (pid, new_process, pid); + /* If this the first process? If so, then set the arch. */ + if (all_processes.head == NULL) + new_inferior = 1; + + linux_add_process (pid, 0); + + ptid = ptid_build (pid, pid, 0); + new_lwp = add_lwp (ptid); + add_thread (ptid, new_lwp); must_set_ptrace_flags = 1; - new_inferior = 1; return pid; } @@ -303,165 +465,314 @@ linux_create_inferior (char *program, char **allargs) /* Attach to an inferior process. */ void -linux_attach_lwp (unsigned long pid) +linux_attach_lwp (unsigned long pid, int initial) { - struct process_info *new_process; + ptid_t ptid; + struct lwp_info *new_lwp; if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0) { - if (all_threads.head != NULL) + if (!initial) { /* If we fail to attach to an LWP, just warn. */ - fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid, + fprintf (stderr, "Cannot attach to lwp %ld: %s (%d)\n", pid, strerror (errno), errno); fflush (stderr); return; } else - /* If we fail to attach to a process, report an error. */ - error ("Cannot attach to process %ld: %s (%d)\n", pid, - strerror (errno), errno); + { + new_inferior = 0; + + /* If we fail to attach to a process, report an error. */ + error ("Cannot attach to lwp %ld: %s (%d)\n", pid, + strerror (errno), errno); + } } - ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE); + linux_enable_event_reporting (pid); - new_process = (struct process_info *) add_process (pid); - add_thread (pid, new_process, pid); - new_thread_notify (thread_id_to_gdb_id (new_process->lwpid)); + if (initial) + /* NOTE/FIXME: This lwp might have not been the tgid. */ + ptid = ptid_build (pid, pid, 0); + else + ptid = ptid_build (inferior_pid, pid, 0); + + new_lwp = (struct lwp_info *) add_lwp (ptid); + add_thread (ptid, new_lwp); + +#if 0 + new_thread_notify (thread_id_to_gdb_id (new_lwp->lwpid)); +#endif /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH - brings it to a halt. We should ignore that SIGSTOP and resume the process + brings it to a halt. We should ignore that SIGSTOP and resume the lwp (unless this is the first process, in which case the flag will be cleared in linux_attach). On the other hand, if we are currently trying to stop all threads, we should treat the new thread as if we had sent it a SIGSTOP. This works - because we are guaranteed that add_process added us to the end of the + because we are guaranteed that add_lwp added us to the end of the list, and so the new thread has not yet reached wait_for_sigstop (but will). */ if (! stopping_threads) - new_process->stop_expected = 1; + new_lwp->stop_expected = 1; } int linux_attach (unsigned long pid) { - struct process_info *process; + struct lwp_info *lwp; + int tgid = tgid_of_pid (pid); + + /* Maybe we could be smarter about this. */ + if (tgid != 0 && tgid != pid) + warning ("%s is a cloned process", + target_pid_to_str (pid_to_ptid (pid))); - linux_attach_lwp (pid); + linux_attach_lwp (pid, 1); + + /* If this the first process? If so, then set the arch. */ + if (all_processes.head == NULL) + new_inferior = 1; + + linux_add_process (pid, 1); /* Don't ignore the initial SIGSTOP if we just attached to this process. It will be collected by wait shortly. */ - process = (struct process_info *) find_inferior_id (&all_processes, pid); - process->stop_expected = 0; + lwp = (struct lwp_info *) find_inferior_id (&all_lwps, + ptid_build (pid, pid, 0)); + lwp->stop_expected = 0; + + return 0; +} + +struct counter +{ + int pid; + int count; +}; + +static int +check_if_last_thread_of_pid (struct inferior_list_entry *entry, void *args) +{ + struct counter *counter = args; - new_inferior = 1; + if (ptid_get_pid (entry->id) == counter->pid) + { + counter->count++; + + if (counter->count > 1) + /* OK, we know it's not the last, we can stop counting. */ + return 1; + } return 0; } -/* Kill the inferior process. Make us have no inferior. */ +static int +is_last_thread_of_process (struct thread_info *thread) +{ + ptid_t ptid = ((struct inferior_list_entry *)thread)->id; + int pid = ptid_get_pid (ptid); + struct counter counter = { pid , 0 }; -static void -linux_kill_one_process (struct inferior_list_entry *entry) + return (find_inferior (&all_threads, + check_if_last_thread_of_pid, &counter) == NULL); +} + +/* Kill the inferior lwp. */ + +static int +linux_kill_one_lwp (struct inferior_list_entry *entry, void *args) { struct thread_info *thread = (struct thread_info *) entry; - struct process_info *process = get_thread_process (thread); + struct lwp_info *lwp = get_thread_lwp (thread); int wstat; + int pid = * (int *) args; + + if (ptid_get_pid (entry->id) != pid) + return 0; /* We avoid killing the first thread here, because of a Linux kernel (at least 2.6.0-test7 through 2.6.8-rc4) bug; if we kill the parent before the children get a chance to be reaped, it will remain a zombie forever. */ - if (entry == all_threads.head) - return; + + if (pid_of (lwp) == lwpid_of (lwp)) + { + if (debug_threads) + fprintf (stderr, "lkop: is last of process %s\n", + target_pid_to_str (entry->id)); + return 0; + } + + /* If we're killing a running inferior, make sure it is stopped + first, as PTRACE_KILL will not work otherwise. */ + if (!lwp->stopped) + send_sigstop (&lwp->head); do { - ptrace (PTRACE_KILL, pid_of (process), 0, 0); + ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0); /* Make sure it died. The loop is most likely unnecessary. */ - wstat = linux_wait_for_event (thread); - } while (WIFSTOPPED (wstat)); + pid = my_waitpid (lwpid_of (lwp), &wstat, __WALL); + } while (pid == lwpid_of (lwp)); + + remove_inferior (&all_lwps, &lwp->head); + remove_thread (thread); + + return 0; } -static void -linux_kill (void) +static int +linux_kill (int pid) { - struct thread_info *thread = (struct thread_info *) all_threads.head; struct process_info *process; + struct lwp_info *lwp; + struct thread_info *thread; int wstat; + int lwpid; - if (thread == NULL) - return; + process = + (struct process_info *) find_inferior_id (&all_processes, pid_to_ptid (pid)); - for_each_inferior (&all_threads, linux_kill_one_process); + if (process == NULL) + return -1; + + find_inferior (&all_threads, linux_kill_one_lwp, &pid); - /* See the comment in linux_kill_one_process. We did not kill the first + /* See the comment in linux_kill_one_lwp. We did not kill the first thread in the list, so do so now. */ - process = get_thread_process (thread); + lwp = find_lwp_pid (pid_to_ptid (pid)); + thread = get_lwp_thread (lwp); + + if (debug_threads) + fprintf (stderr, "lk_1: killing lwp %s, for pid: %d\n", + target_pid_to_str (lwp->head.id), pid); + + /* If we're killing a running inferior, make sure it is stopped + first, as PTRACE_KILL will not work otherwise. */ + if (!lwp->stopped) + send_sigstop (&lwp->head); + do { - ptrace (PTRACE_KILL, pid_of (process), 0, 0); + ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0); /* Make sure it died. The loop is most likely unnecessary. */ - wstat = linux_wait_for_event (thread); - } while (WIFSTOPPED (wstat)); + lwpid = my_waitpid (lwpid_of (lwp), &wstat, __WALL); + } while (lwpid == lwpid_of (lwp)); - clear_inferiors (); - free (all_processes.head); - all_processes.head = all_processes.tail = NULL; + remove_inferior (&all_lwps, &lwp->head); + remove_thread (thread); + remove_inferior (&all_processes, &process->head); + + return 0; } -static void -linux_detach_one_process (struct inferior_list_entry *entry) +static int +linux_detach_one_lwp (struct inferior_list_entry *entry, void *args) { struct thread_info *thread = (struct thread_info *) entry; - struct process_info *process = get_thread_process (thread); + struct lwp_info *lp = get_thread_lwp (thread); + int pid = * (int *) args; + + if (ptid_get_pid (entry->id) != pid) + return 0; - /* Make sure the process isn't stopped at a breakpoint that's + /* If we're detaching from a running inferior, make sure it is + stopped first, as PTRACE_DETACH will not work otherwise. */ + if (!lp->stopped) + { + send_sigstop (&lp->head); + wait_for_sigstop (&lp->head); + } + + /* Make sure the LWP isn't stopped at a breakpoint that's no longer there. */ - check_removed_breakpoint (process); + check_removed_breakpoint (lp); - /* If this process is stopped but is expecting a SIGSTOP, then make + /* If this lwp is stopped but is expecting a SIGSTOP, then make sure we take care of that now. This isn't absolutely guaranteed to collect the SIGSTOP, but is fairly likely to. */ - if (process->stop_expected) + if (lp->stop_expected) { + int wstat; /* Clear stop_expected, so that the SIGSTOP will be reported. */ - process->stop_expected = 0; - if (process->stopped) - linux_resume_one_process (&process->head, 0, 0, NULL); - linux_wait_for_event (thread); + lp->stop_expected = 0; + if (lp->stopped) + linux_resume_one_lwp (&lp->head, 0, 0, NULL); + linux_wait_for_event (lp->head.id, &wstat, __WALL); } - /* Flush any pending changes to the process's registers. */ + /* Flush any pending changes to the lwp's registers. */ regcache_invalidate_one ((struct inferior_list_entry *) - get_process_thread (process)); + get_lwp_thread (lp)); /* Finally, let it resume. */ - ptrace (PTRACE_DETACH, pid_of (process), 0, 0); + ptrace (PTRACE_DETACH, lwpid_of (lp), 0, 0); + + /* Get rid of it. */ + remove_inferior (&all_lwps, &lp->head); + remove_thread (thread); + + return 0; +} + +static int +any_thread_of (struct inferior_list_entry *entry, void *args) +{ + int *pid_p = args; + + if (ptid_get_pid (entry->id) == *pid_p) + return 1; + + return 0; } static int -linux_detach (void) +linux_detach (int pid) { + struct process_info *process; + + process = + (struct process_info *) find_inferior_id (&all_processes, + pid_to_ptid (pid)); + + if (process == NULL) + return -1; + + current_inferior = + (struct thread_info *) find_inferior (&all_threads, any_thread_of, &pid); + delete_all_breakpoints (); - for_each_inferior (&all_threads, linux_detach_one_process); - clear_inferiors (); - free (all_processes.head); - all_processes.head = all_processes.tail = NULL; + find_inferior (&all_threads, linux_detach_one_lwp, &pid); + + remove_inferior (&all_processes, &process->head); return 0; } static void -linux_join (void) +linux_join (int pid) { - extern unsigned long signal_pid; int status, ret; + struct process_info *process; + process = (struct process_info *) find_inferior_id (&all_processes, + pid_to_ptid (pid)); + + if (!process) + return; /* weird */ + + /* If we are attached, then we can exit. Otherwise, we need to hang + around doing nothing, until the child is gone. */ + if (process->attached) + return; + do { - ret = waitpid (signal_pid, &status, 0); + ret = my_waitpid (pid, &status, 0); if (WIFEXITED (status) || WIFSIGNALED (status)) break; } while (ret != -1 || errno != ECHILD); @@ -469,19 +780,24 @@ linux_join (void) /* Return nonzero if the given thread is still alive. */ static int -linux_thread_alive (unsigned long lwpid) +linux_thread_alive (ptid_t ptid) { - if (find_inferior_id (&all_threads, lwpid) != NULL) - return 1; + struct lwp_info *lwp = find_lwp_pid (ptid); + + /* We assume we always know if a thread exits. If a whole process + exited but we still haven't been able to report it to GDB, we'll + hold on to the last lwp of the dead process. */ + if (lwp != NULL) + return !lwp->dead; else return 0; } -/* Return nonzero if this process stopped at a breakpoint which +/* Return nonzero if this lwp stopped at a breakpoint which no longer appears to be inserted. Also adjust the PC appropriately to resume where the breakpoint used to be. */ static int -check_removed_breakpoint (struct process_info *event_child) +check_removed_breakpoint (struct lwp_info *event_child) { CORE_ADDR stop_pc; struct thread_info *saved_inferior; @@ -490,11 +806,11 @@ check_removed_breakpoint (struct process_info *event_child) return 0; if (debug_threads) - fprintf (stderr, "Checking for breakpoint in process %ld.\n", - event_child->lwpid); + fprintf (stderr, "Checking for breakpoint in lwp %ld.\n", + lwpid_of (event_child)); saved_inferior = current_inferior; - current_inferior = get_process_thread (event_child); + current_inferior = get_lwp_thread (event_child); stop_pc = get_stop_pc (); @@ -539,89 +855,213 @@ check_removed_breakpoint (struct process_info *event_child) return 1; } -/* Return 1 if this process has an interesting status pending. This function - may silently resume an inferior process. */ +/* Return 1 if this lwp has an interesting status pending. This function + may silently resume an inferior lwp. */ static int -status_pending_p (struct inferior_list_entry *entry, void *dummy) +status_pending_p (struct inferior_list_entry *entry, void *arg) { - struct process_info *process = (struct process_info *) entry; + struct lwp_info *lwp = (struct lwp_info *) entry; + ptid_t ptid = * (ptid_t *) arg; - if (process->status_pending_p) - if (check_removed_breakpoint (process)) + /* Check if we're only interested in events from a specific process + or its lwps. */ + if (!ptid_equal (minus_one_ptid, ptid) + && ptid_get_pid (ptid) != ptid_get_pid (lwp->head.id)) + return 0; + + if (lwp->status_pending_p && !lwp->suspended) + if (check_removed_breakpoint (lwp)) { /* This thread was stopped at a breakpoint, and the breakpoint is now gone. We were told to continue (or step...) all threads, so GDB isn't trying to single-step past this breakpoint. So instead of reporting the old SIGTRAP, pretend we got to the breakpoint just after it was removed instead of just - before; resume the process. */ - linux_resume_one_process (&process->head, 0, 0, NULL); + before; resume the lwp. */ + linux_resume_one_lwp (&lwp->head, 0, 0, NULL); return 0; } - return process->status_pending_p; + return (lwp->status_pending_p && !lwp->suspended); } -static void -linux_wait_for_process (struct process_info **childp, int *wstatp) +static int +same_lwp (struct inferior_list_entry *entry, void *data) { - int ret; - int to_wait_for = -1; + ptid_t ptid = *(ptid_t *) data; + int lwp; - if (*childp != NULL) - to_wait_for = (*childp)->lwpid; + if (ptid_get_lwp (ptid) != 0) + lwp = ptid_get_lwp (ptid); + else + lwp = ptid_get_pid (ptid); -retry: - while (1) + if (ptid_get_lwp (entry->id) == lwp) + return 1; + + return 0; +} + +struct lwp_info * +find_lwp_pid (ptid_t ptid) +{ + return (struct lwp_info*) find_inferior (&all_lwps, same_lwp, &ptid); +} + +/* Wrapper function for waitpid which handles EINTR, and emulates + __WALL for systems where that is not available. */ + +static int +my_waitpid (int pid, int *status, int flags) +{ + int ret, out_errno; + + if (debug_threads) + fprintf (stderr, "my_waitpid (%d, 0x%x)\n", pid, flags); + + if (flags & __WALL) { - ret = waitpid (to_wait_for, wstatp, WNOHANG); + sigset_t mask; + sigset_t org_mask; + sigset_t suspend_mask; - if (ret == -1) - { - if (errno != ECHILD) - perror_with_name ("waitpid"); - } - else if (ret > 0) - break; + int org_flags = flags; + int wnohang = (flags & WNOHANG) != 0; + flags &= ~__WALL; + flags |= WNOHANG; + flags &= ~__WCLONE; + + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); - ret = waitpid (to_wait_for, wstatp, WNOHANG | __WCLONE); + sigprocmask (SIG_BLOCK, &mask, &org_mask); - if (ret == -1) + /* Any signal unblocks the sigsuspend below. */ + sigemptyset (&suspend_mask); + + while (1) { - if (errno != ECHILD) - perror_with_name ("waitpid (WCLONE)"); + do + { + errno = 0; + ret = waitpid (pid, status, flags); + } + while (ret == -1 && errno == EINTR); + out_errno = errno; + + if (ret == -1) + { + if (out_errno != ECHILD) + break; + + /* We know there's no child of this flavour, recurse + with the right type. */ + org_flags &= ~__WALL; + org_flags |= (flags & __WCLONE) ? 0 : __WCLONE; + ret = my_waitpid (pid, status, org_flags); + out_errno = errno; + break; + } + else if (ret > 0) + break; + + if (flags & __WCLONE) + { + if (wnohang) + break; + else + { + if (debug_threads) + fprintf (stderr, "blocking\n"); + + /* Block waiting for any signal. Ideally, we'd wait for + SIGCHLD and LinuxThreads' signals, but this avoids + knowing which signals those are. */ + sigsuspend (&suspend_mask); + } + } + + flags ^= __WCLONE; } - else if (ret > 0) - break; - usleep (1000); + sigprocmask (SIG_SETMASK, &org_mask, NULL); + } + else + { + do + ret = waitpid (pid, status, flags); + while (ret == -1 && errno == EINTR); + out_errno = errno; } + if (debug_threads) + fprintf (stderr, "my_waitpid (%d, 0x%x): status(%x), %d\n", + pid, flags, status ? *status : -1, ret); + + errno = out_errno; + return ret; +} + +static struct lwp_info * +linux_wait_for_lwp (ptid_t ptid, int *wstatp, int options) +{ + int ret; + int to_wait_for = -1; + struct lwp_info *child = NULL; + + if (debug_threads) + fprintf (stderr, "linux_wait_for_lwp: %s\n", target_pid_to_str (ptid)); + + if (ptid_equal (ptid, minus_one_ptid)) + to_wait_for = -1; /* any child */ + else if (is_lwpid (ptid)) + to_wait_for = ptid_get_lwp (ptid); /* this lwp only */ + else + to_wait_for = -1; /* a specific tgid, but this + is not possible. We wait + for everything, and cache + what we don't need now. */ + + options |= __WALL; + +retry: + + ret = my_waitpid (to_wait_for, wstatp, options); + if (ret == 0 || (ret == -1 && errno == ECHILD && (options & WNOHANG))) + return NULL; + else if (ret == -1) + perror_with_name ("waitpid"); + if (debug_threads && (!WIFSTOPPED (*wstatp) || (WSTOPSIG (*wstatp) != 32 && WSTOPSIG (*wstatp) != 33))) fprintf (stderr, "Got an event from %d (%x)\n", ret, *wstatp); - if (to_wait_for == -1) - *childp = (struct process_info *) find_inferior_id (&all_processes, ret); + child = find_lwp_pid (pid_to_ptid (ret)); - /* If we didn't find a process, one of two things presumably happened: - - A process we started and then detached from has exited. Ignore it. - - A process we are controlling has forked and the new child's stop + /* If we didn't find a lwp, one of two things presumably happened: + - A lwp we started and then detached from has exited. Ignore it. + - A lwp we are controlling has forked and the new child's stop was reported to us by the kernel. Save its PID. */ - if (*childp == NULL && WIFSTOPPED (*wstatp)) + if (child == NULL && WIFSTOPPED (*wstatp)) { add_pid_to_list (&stopped_pids, ret); + if (options & WNOHANG) + return NULL; + goto retry; + } + else if (child == NULL) + { + if (options & WNOHANG) + return NULL; goto retry; } - else if (*childp == NULL) - goto retry; - (*childp)->stopped = 1; - (*childp)->pending_is_breakpoint = 0; + child->stopped = 1; + child->pending_is_breakpoint = 0; - (*childp)->last_status = *wstatp; + child->last_status = *wstatp; /* Architecture-specific setup after inferior is running. This needs to happen after we have attached to the inferior @@ -640,117 +1080,184 @@ retry: && WIFSTOPPED (*wstatp)) { current_inferior = (struct thread_info *) - find_inferior_id (&all_threads, (*childp)->lwpid); + find_inferior_id (&all_threads, child->head.id); /* For testing only; i386_stop_pc prints out a diagnostic. */ if (the_low_target.get_pc != NULL) get_stop_pc (); } + + if (ptid_is_pid (ptid)) + { + if (pid_of (child) != ptid_get_pid (ptid)) + { + if (debug_threads) + fprintf (stderr, "LWP %ld got an event %06x, leaving pending.\n", + lwpid_of (child), *wstatp); + child->status_pending_p = 1; + child->status_pending = *wstatp; + if ((options & WNOHANG) == 0) + goto retry; + + child = NULL; + } + } + + return child; } static int -linux_wait_for_event (struct thread_info *child) +resume_stopped_lwps (struct inferior_list_entry *entry, void *arg) +{ + ptid_t ptid = * (ptid_t *) arg; + struct lwp_info *lwp = (struct lwp_info *) entry; + + if ((ptid_equal (ptid, minus_one_ptid) + || (ptid_is_pid (ptid) + && ptid_get_pid (ptid) == pid_of (lwp))) + && lwp->stopped + && !lwp->suspended) + { + if (debug_threads) + fprintf (stderr, "resuming stopped LWP %ld\n", lwpid_of (lwp)); + linux_resume_one_lwp (&lwp->head, 0, 0, NULL); + } + + return 0; +} + + +static int +linux_wait_for_event (ptid_t ptid, int *wstat, int options) { CORE_ADDR stop_pc; - struct process_info *event_child; - int wstat; + struct lwp_info *event_child = NULL; int bp_status; + struct lwp_info *requested_child = NULL; - /* Check for a process with a pending status. */ + /* Check for a lwp with a pending status. */ /* It is possible that the user changed the pending task's registers since it stopped. We correctly handle the change of PC if we hit a breakpoint (in check_removed_breakpoint); signals should be reported anyway. */ - if (child == NULL) + + if (ptid_equal (ptid, minus_one_ptid) + || ptid_equal (pid_to_ptid (ptid_get_pid (ptid)), ptid)) { - event_child = (struct process_info *) - find_inferior (&all_processes, status_pending_p, NULL); + event_child = (struct lwp_info *) + find_inferior (&all_lwps, status_pending_p, &ptid); if (debug_threads && event_child) - fprintf (stderr, "Got a pending child %ld\n", event_child->lwpid); + fprintf (stderr, "Got a pending child %ld\n", lwpid_of (event_child)); + + /* If we just handled a pending status, and the event was not + interesting to report to GDB, we will reach here again. This + time, we may find that there are no more interesting pending + statuses to handle, but, the previous iteration left all + threads stopped. Resume them now. + + Don't do this in non-stop mode, as that would resume threads + silently behind GDB's back --- e.g., just after starting a + new inferior, we could get here due to a spurious + target_wait(..., TARGET_WNOHANG) call. In that case, we + don't want to resume all stopped threads. In all-stop, there + should be no wnohang calls, so we always want to resume + threads. */ + if (!non_stop && !event_child) + find_inferior (&all_lwps, resume_stopped_lwps, &ptid); } else { - event_child = get_thread_process (child); - if (event_child->status_pending_p - && check_removed_breakpoint (event_child)) - event_child = NULL; + requested_child = find_lwp_pid (ptid); + if (requested_child->status_pending_p + && !check_removed_breakpoint (requested_child)) + event_child = requested_child; } if (event_child != NULL) { - if (event_child->status_pending_p) - { - if (debug_threads) - fprintf (stderr, "Got an event from pending child %ld (%04x)\n", - event_child->lwpid, event_child->status_pending); - wstat = event_child->status_pending; - event_child->status_pending_p = 0; - event_child->status_pending = 0; - current_inferior = get_process_thread (event_child); - return wstat; - } + if (debug_threads) + fprintf (stderr, "Got an event from pending child %ld (%04x)\n", + ptid_get_lwp (event_child->head.id), event_child->status_pending); + *wstat = event_child->status_pending; + event_child->status_pending_p = 0; + event_child->status_pending = 0; } - /* We only enter this loop if no process has a pending wait status. Thus - any action taken in response to a wait status inside this loop is - responding as soon as we detect the status, not after any pending - events. */ - while (1) - { - if (child == NULL) - event_child = NULL; - else - event_child = get_thread_process (child); + /* If no lwp of interested had a pending wait status, wait for + one. */ + if (!event_child) + event_child = linux_wait_for_lwp (ptid, wstat, options); - linux_wait_for_process (&event_child, &wstat); + { + if ((options & WNOHANG) && event_child == NULL) + return -1; if (event_child == NULL) error ("event from unknown child"); - current_inferior = (struct thread_info *) - find_inferior_id (&all_threads, event_child->lwpid); + current_inferior = get_lwp_thread (event_child); /* Check for thread exit. */ - if (! WIFSTOPPED (wstat)) + if (! WIFSTOPPED (*wstat)) { if (debug_threads) - fprintf (stderr, "LWP %ld exiting\n", event_child->head.id); + fprintf (stderr, "LWP %ld exiting\n", + ptid_get_lwp (event_child->head.id)); /* If the last thread is exiting, just return. */ - if (all_threads.head == all_threads.tail) - return wstat; - + if (is_last_thread_of_process (current_inferior)) + { + if (debug_threads) + fprintf (stderr, "LWP %ld is last lwp of process\n", + ptid_get_lwp (event_child->head.id)); + return 0; + } +#if 0 dead_thread_notify (thread_id_to_gdb_id (event_child->lwpid)); +#endif - remove_inferior (&all_processes, &event_child->head); + remove_inferior (&all_lwps, &event_child->head); free (event_child); remove_thread (current_inferior); - current_inferior = (struct thread_info *) all_threads.head; + + if (!non_stop) + { + current_inferior = (struct thread_info *) all_threads.head; + if (debug_threads) + fprintf (stderr, "Current inferior is now %ld\n", + lwpid_of (get_thread_lwp (current_inferior))); + } + else + { + current_inferior = NULL; + if (debug_threads) + fprintf (stderr, "Current inferior is now <NULL>\n"); + } /* If we were waiting for this particular child to do something... well, it did something. */ - if (child != NULL) - return wstat; + if (requested_child != NULL) + return 0; /* Wait for a more interesting event. */ - continue; + return -1; } - if (WIFSTOPPED (wstat) - && WSTOPSIG (wstat) == SIGSTOP + if (WIFSTOPPED (*wstat) + && WSTOPSIG (*wstat) == SIGSTOP && event_child->stop_expected) { if (debug_threads) fprintf (stderr, "Expected stop.\n"); event_child->stop_expected = 0; - linux_resume_one_process (&event_child->head, - event_child->stepping, 0, NULL); - continue; + linux_resume_one_lwp (&event_child->head, + event_child->stepping, 0, NULL); + return -1; } - if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP - && wstat >> 16 != 0) + if (WIFSTOPPED (*wstat) && WSTOPSIG (*wstat) == SIGTRAP + && *wstat >> 16 != 0) { - handle_extended_wait (event_child, wstat); - continue; + if (handle_extended_wait (event_child, *wstat)) + return -1; } /* If GDB is not interested in this signal, don't stop other @@ -762,47 +1269,49 @@ linux_wait_for_event (struct thread_info *child) special handling to skip the signal handler. */ /* FIXME drow/2002-06-09: Get signal numbers from the inferior's thread library? */ - if (WIFSTOPPED (wstat) + if (WIFSTOPPED (*wstat) && !event_child->stepping && ( #ifdef USE_THREAD_DB - (thread_db_active && (WSTOPSIG (wstat) == __SIGRTMIN - || WSTOPSIG (wstat) == __SIGRTMIN + 1)) + (current_process ()->private->thread_db_active + && (WSTOPSIG (*wstat) == __SIGRTMIN + || WSTOPSIG (*wstat) == __SIGRTMIN + 1)) || #endif - (pass_signals[target_signal_from_host (WSTOPSIG (wstat))] - && (WSTOPSIG (wstat) != SIGSTOP || !stopping_threads)))) + (pass_signals[target_signal_from_host (WSTOPSIG (*wstat))] + && (WSTOPSIG (*wstat) != SIGSTOP || !stopping_threads)))) { siginfo_t info, *info_p; if (debug_threads) fprintf (stderr, "Ignored signal %d for LWP %ld.\n", - WSTOPSIG (wstat), event_child->head.id); + WSTOPSIG (*wstat), ptid_get_lwp (event_child->head.id)); - if (ptrace (PTRACE_GETSIGINFO, event_child->lwpid, 0, &info) == 0) + if (ptrace (PTRACE_GETSIGINFO, ptid_get_lwp (event_child->head.id), 0, &info) == 0) info_p = &info; else info_p = NULL; - linux_resume_one_process (&event_child->head, - event_child->stepping, - WSTOPSIG (wstat), info_p); - continue; + + linux_resume_one_lwp (&event_child->head, + event_child->stepping, + WSTOPSIG (*wstat), info_p); + return -1; } /* If this event was not handled above, and is not a SIGTRAP, report it. */ - if (!WIFSTOPPED (wstat) || WSTOPSIG (wstat) != SIGTRAP) - return wstat; + if (!WIFSTOPPED (*wstat) || WSTOPSIG (*wstat) != SIGTRAP) + return 0; /* If this target does not support breakpoints, we simply report the SIGTRAP; it's of no concern to us. */ if (the_low_target.get_pc == NULL) - return wstat; + return 0; stop_pc = get_stop_pc (); /* bp_reinsert will only be set if we were single-stepping. - Notice that we will resume the process after hitting + Notice that we will resume the lwp after hitting a gdbserver breakpoint; single-stepping to/over one is not supported (yet). */ if (event_child->bp_reinsert != 0) @@ -813,8 +1322,8 @@ linux_wait_for_event (struct thread_info *child) event_child->bp_reinsert = 0; /* Clear the single-stepping flag and SIGTRAP as we resume. */ - linux_resume_one_process (&event_child->head, 0, 0, NULL); - continue; + linux_resume_one_lwp (&event_child->head, 0, 0, NULL); + return -1; } bp_status = check_breakpoints (stop_pc); @@ -847,24 +1356,29 @@ linux_wait_for_event (struct thread_info *child) Otherwise, call the target function to figure out where we need our temporary breakpoint, create it, and continue executing this - process. */ + lwp. */ + + /* NOTE: we're lifting breakpoints in non-stop mode. This + is currently only used for thread event breakpoints, so + it isn't that bad as long as we have PTRACE_EVENT_CLONE + events. */ if (bp_status == 2) /* No need to reinsert. */ - linux_resume_one_process (&event_child->head, 0, 0, NULL); + linux_resume_one_lwp (&event_child->head, 0, 0, NULL); else if (the_low_target.breakpoint_reinsert_addr == NULL) { event_child->bp_reinsert = stop_pc; uninsert_breakpoint (stop_pc); - linux_resume_one_process (&event_child->head, 1, 0, NULL); + linux_resume_one_lwp (&event_child->head, 1, 0, NULL); } else { reinsert_breakpoint_by_bp (stop_pc, (*the_low_target.breakpoint_reinsert_addr) ()); - linux_resume_one_process (&event_child->head, 0, 0, NULL); + linux_resume_one_lwp (&event_child->head, 0, 0, NULL); } - continue; + return -1; } if (debug_threads) @@ -882,11 +1396,11 @@ linux_wait_for_event (struct thread_info *child) if (event_child->stepping) { event_child->stepping = 0; - return wstat; + return 0; } /* A SIGTRAP that we can't explain. It may have been a breakpoint. - Check if it is a breakpoint, and if so mark the process information + Check if it is a breakpoint, and if so mark the lwp information accordingly. This will handle both the necessary fiddling with the PC on decr_pc_after_break targets and suppressing extra threads hitting a breakpoint if two hit it at once and then GDB removes it @@ -899,7 +1413,7 @@ linux_wait_for_event (struct thread_info *child) event_child->pending_stop_pc = stop_pc; } - return wstat; + return 0; } /* NOTREACHED */ @@ -908,40 +1422,59 @@ linux_wait_for_event (struct thread_info *child) /* Wait for process, returns status. */ -static unsigned char -linux_wait (char *status) +static ptid_t +linux_wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus, int target_options) { int w; - struct thread_info *child = NULL; + struct thread_info *thread = NULL; + struct lwp_info *lwp = NULL; + int options; + + /* Translate generic target options into linux options. */ + options = __WALL; + if (target_options & TARGET_WNOHANG) + options |= WNOHANG; + + ourstatus->kind = TARGET_WAITKIND_IGNORE; -retry: /* If we were only supposed to resume one thread, only wait for that thread - if it's still alive. If it died, however - which can happen if we're coming from the thread death case below - then we need to make sure we restart the other threads. We could pick a thread at random or restart all; restarting all is less arbitrary. */ - if (cont_thread != 0 && cont_thread != -1) + /* TODO: Check if the incoming PTID is requesting an event for an + lwp of another process? */ + /* Don't do this in non-stop. */ + if (!non_stop + && !ptid_equal (cont_thread, null_ptid) + && !ptid_equal (cont_thread, minus_one_ptid)) { - child = (struct thread_info *) find_inferior_id (&all_threads, - cont_thread); + thread = (struct thread_info *) find_inferior_id (&all_threads, + cont_thread); /* No stepping, no signal - unless one is pending already, of course. */ - if (child == NULL) + if (thread == NULL) { struct thread_resume resume_info; - resume_info.thread = -1; - resume_info.step = resume_info.sig = resume_info.leave_stopped = 0; - linux_resume (&resume_info); + resume_info.thread = minus_one_ptid; + resume_info.kind = rk_continue; + resume_info.sig = 0; + linux_resume (&resume_info, 1); } + else + ptid = cont_thread; } - w = linux_wait_for_event (child); - stop_all_processes (); + if (linux_wait_for_event (ptid, &w, options) != 0) + return minus_one_ptid; + + if (!non_stop) + stop_all_lwps (); if (must_set_ptrace_flags) { - ptrace (PTRACE_SETOPTIONS, inferior_pid, 0, PTRACE_O_TRACECLONE); + linux_enable_event_reporting (inferior_lwpid); must_set_ptrace_flags = 0; } @@ -957,35 +1490,137 @@ retry: Report the exit status of the last thread to exit. This matches LinuxThreads' behavior. */ - if (all_threads.head == all_threads.tail) + lwp = get_thread_lwp (current_inferior); + + /* Now that we've selected our final event LWP, cancel any + breakpoints in other LWPs that have hit a GDB breakpoint. See + the comment in cancel_breakpoints_callback to find out why. */ + if (!non_stop) + find_inferior (&all_lwps, cancel_breakpoints_callback, lwp); + + if (is_last_thread_of_process (current_inferior)) { - if (WIFEXITED (w)) - { - fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); - *status = 'W'; - clear_inferiors (); - free (all_processes.head); - all_processes.head = all_processes.tail = NULL; - return WEXITSTATUS (w); - } - else if (!WIFSTOPPED (w)) + if (WIFEXITED (w) || WIFSIGNALED (w)) { - fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); - *status = 'X'; - clear_inferiors (); - free (all_processes.head); - all_processes.head = all_processes.tail = NULL; - return target_signal_from_host (WTERMSIG (w)); + struct process_info *process; + int pid; + + pid = pid_of (lwp); + + process = (struct process_info *) + find_inferior_id (&all_processes, pid_to_ptid (pid)); + + remove_inferior (&all_lwps, &lwp->head); + remove_thread (current_inferior); + remove_inferior (&all_processes, &process->head); + + current_inferior = NULL; + + if (WIFEXITED (w)) + { + ourstatus->kind = TARGET_WAITKIND_EXITED; + ourstatus->value.integer = WEXITSTATUS (w); + + if (debug_threads) + fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); + + return pid_to_ptid (pid); + } + else + { + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = target_signal_from_host (WTERMSIG (w)); + + if (debug_threads) + fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); + + return pid_to_ptid (pid); + } } } else { if (!WIFSTOPPED (w)) - goto retry; + return lwp->head.id; + } + + if (lwp->waitstatus.kind != TARGET_WAITKIND_IGNORE) + { + *ourstatus = lwp->waitstatus; + lwp->waitstatus.kind = TARGET_WAITKIND_IGNORE; + } + else + { + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = target_signal_from_host (WSTOPSIG (w)); + } + + if (debug_threads) + fprintf (stderr, "linux_wait ret = %s, %d, %d\n", + target_pid_to_str (lwp->head.id), + ourstatus->kind, + ourstatus->value.sig); + + return lwp->head.id; +} + +/* Get rid of any pending event in the pipe. */ +static void +async_file_flush (void) +{ + int ret; + char buf; + + do + ret = read (linux_event_pipe[0], &buf, 1); + while (ret == 0 || (ret == -1 && errno == EINTR)); +} + +/* Put something in the pipe, so the event loop wakes up. */ +static void +async_file_mark (void) +{ + int ret; + + async_file_flush (); + + do + ret = write (linux_event_pipe[1], "+", 1); + while (ret == 0 || (ret == -1 && errno == EINTR)); +} + +/* */ +static ptid_t +linux_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int target_options) +{ + ptid_t event_ptid; + + if (debug_threads) + fprintf (stderr, "linux_wait: [%s]\n", target_pid_to_str (ptid)); + + /* Flush the async file first. We can't do it below, because that + would be racy. A SIGCHLD may arrive any time after we get out of + my_waitpid, which marks the async file. */ + if (target_is_async_p ()) + async_file_flush (); + + do + { + event_ptid = linux_wait_1 (ptid, ourstatus, target_options); + } while ((target_options & TARGET_WNOHANG) == 0 + && ourstatus->kind == TARGET_WAITKIND_IGNORE); + + /* If we requested any event, and something came out, assume there + may be more. If we requested a specific lwp or process, also + assume there may be more. */ + if (target_is_async_p ()) + { + if (!ptid_equal (ptid, minus_one_ptid) + || ourstatus->kind != TARGET_WAITKIND_IGNORE) + async_file_mark (); } - *status = 'T'; - return target_signal_from_host (WSTOPSIG (w)); + return event_ptid; } /* Send a signal to an LWP. For LinuxThreads, kill is enough; however, if @@ -1015,126 +1650,159 @@ kill_lwp (unsigned long lwpid, int signo) static void send_sigstop (struct inferior_list_entry *entry) { - struct process_info *process = (struct process_info *) entry; + int lwpid; + struct lwp_info *lwp; + + lwp = (struct lwp_info *) entry; - if (process->stopped) + if (lwp->stopped) return; - /* If we already have a pending stop signal for this process, don't + lwpid = lwpid_of (lwp); + + /* If we already have a pending stop signal for this lwp, don't send another. */ - if (process->stop_expected) + if (lwp->stop_expected) { if (debug_threads) - fprintf (stderr, "Have pending sigstop for process %ld\n", - process->lwpid); + fprintf (stderr, "Have pending sigstop for lwp %d\n", + lwpid); /* We clear the stop_expected flag so that wait_for_sigstop will receive the SIGSTOP event (instead of silently resuming and waiting again). It'll be reset below. */ - process->stop_expected = 0; + lwp->stop_expected = 0; return; } if (debug_threads) - fprintf (stderr, "Sending sigstop to process %ld\n", process->head.id); + fprintf (stderr, "Sending sigstop to lwp %d\n", lwpid); - kill_lwp (process->head.id, SIGSTOP); + kill_lwp (lwpid, SIGSTOP); } static void wait_for_sigstop (struct inferior_list_entry *entry) { - struct process_info *process = (struct process_info *) entry; - struct thread_info *saved_inferior, *thread; + struct lwp_info *lwp = (struct lwp_info *) entry; + struct thread_info *saved_inferior; int wstat; - unsigned long saved_tid; + ptid_t saved_tid; + ptid_t ptid; - if (process->stopped) + if (lwp->stopped) return; saved_inferior = current_inferior; saved_tid = ((struct inferior_list_entry *) saved_inferior)->id; - thread = (struct thread_info *) find_inferior_id (&all_threads, - process->lwpid); - wstat = linux_wait_for_event (thread); + + ptid = lwp->head.id; + + stopping_threads = 1; + linux_wait_for_lwp (ptid, &wstat, __WALL); + stopping_threads = 0; /* If we stopped with a non-SIGSTOP signal, save it for later - and record the pending SIGSTOP. If the process exited, just + and record the pending SIGSTOP. If the lwp exited, just return. */ if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) != SIGSTOP) { if (debug_threads) fprintf (stderr, "LWP %ld stopped with non-sigstop status %06x\n", - process->lwpid, wstat); - process->status_pending_p = 1; - process->status_pending = wstat; - process->stop_expected = 1; + lwpid_of (lwp), wstat); + lwp->status_pending_p = 1; + lwp->status_pending = wstat; + lwp->stop_expected = 1; + } + else if (!WIFSTOPPED (wstat)) + { + if (debug_threads) + fprintf (stderr, "Process %ld exited while stopping LWPs\n", + ptid_get_lwp (lwp->head.id)); + + /* Leave this status pending for the next time we're able to + report it. In the mean time, we'll report this lwp as + dead to GDB, so GDB doesn't try to read registers and + memory from it. */ + lwp->dead = 1; + lwp->status_pending_p = 1; + lwp->status_pending = wstat; + lwp->stopped = 1; /* prevent trying to stop it again. */ + lwp->stop_expected = 0; /* No stop is expected, the lwp is + dead. */ } - if (linux_thread_alive (saved_tid)) + if (non_stop) + /* We can't change the current inferior behind GDB's back, + otherwise, a subsequent command may apply to the wrong process. + Actually, we have to take care of this in all-stop + + multi-process too. FIXME. */ current_inferior = saved_inferior; else { - if (debug_threads) - fprintf (stderr, "Previously current thread died.\n"); + if (linux_thread_alive (saved_tid)) + current_inferior = saved_inferior; + else + { + if (debug_threads) + fprintf (stderr, "Previously current thread died.\n"); - /* Set a valid thread as current. */ - set_desired_inferior (0); + /* Set a valid thread as current. */ + set_desired_inferior (0); + } } } static void -stop_all_processes (void) +stop_all_lwps (void) { - stopping_threads = 1; - for_each_inferior (&all_processes, send_sigstop); - for_each_inferior (&all_processes, wait_for_sigstop); - stopping_threads = 0; + for_each_inferior (&all_lwps, send_sigstop); + for_each_inferior (&all_lwps, wait_for_sigstop); } -/* Resume execution of the inferior process. +/* Resume execution of the inferior lwp. If STEP is nonzero, single-step it. If SIGNAL is nonzero, give it that signal. */ static void -linux_resume_one_process (struct inferior_list_entry *entry, - int step, int signal, siginfo_t *info) +linux_resume_one_lwp (struct inferior_list_entry *entry, + int step, int signal, siginfo_t *info) { - struct process_info *process = (struct process_info *) entry; + struct lwp_info *lwp = (struct lwp_info *) entry; struct thread_info *saved_inferior; - if (process->stopped == 0) + if (lwp->stopped == 0) return; /* If we have pending signals or status, and a new signal, enqueue the signal. Also enqueue the signal if we are waiting to reinsert a breakpoint; it will be picked up again below. */ if (signal != 0 - && (process->status_pending_p || process->pending_signals != NULL - || process->bp_reinsert != 0)) + && (lwp->status_pending_p || lwp->pending_signals != NULL + || lwp->bp_reinsert != 0)) { struct pending_signals *p_sig; p_sig = malloc (sizeof (*p_sig)); - p_sig->prev = process->pending_signals; + p_sig->prev = lwp->pending_signals; p_sig->signal = signal; if (info == NULL) memset (&p_sig->info, 0, sizeof (siginfo_t)); else memcpy (&p_sig->info, info, sizeof (siginfo_t)); - process->pending_signals = p_sig; + lwp->pending_signals = p_sig; } - if (process->status_pending_p && !check_removed_breakpoint (process)) + if (lwp->status_pending_p && !check_removed_breakpoint (lwp)) return; saved_inferior = current_inferior; - current_inferior = get_process_thread (process); + current_inferior = get_lwp_thread (lwp); if (debug_threads) - fprintf (stderr, "Resuming process %ld (%s, signal %d, stop %s)\n", inferior_pid, + fprintf (stderr, "Resuming lwp %ld (%s, signal %d, stop %s)\n", inferior_lwpid, step ? "step" : "continue", signal, - process->stop_expected ? "expected" : "not expected"); + lwp->stop_expected ? "expected" : "not expected"); /* This bit needs some thinking about. If we get a signal that we must report while a single-step reinsert is still pending, @@ -1143,13 +1811,13 @@ linux_resume_one_process (struct inferior_list_entry *entry, the reinsert happened right away and not lose any signals. Making this stack would also shrink the window in which breakpoints are - uninserted (see comment in linux_wait_for_process) but not enough for + uninserted (see comment in linux_wait_for_lwp) but not enough for complete correctness, so it won't solve that problem. It may be worthwhile just to solve this one, however. */ - if (process->bp_reinsert != 0) + if (lwp->bp_reinsert != 0) { if (debug_threads) - fprintf (stderr, " pending reinsert at %08lx", (long)process->bp_reinsert); + fprintf (stderr, " pending reinsert at %08lx", (long)lwp->bp_reinsert); if (step == 0) fprintf (stderr, "BAD - reinserting but not stepping.\n"); step = 1; @@ -1158,7 +1826,7 @@ linux_resume_one_process (struct inferior_list_entry *entry, signal = 0; } - check_removed_breakpoint (process); + check_removed_breakpoint (lwp); if (debug_threads && the_low_target.get_pc != NULL) { @@ -1168,28 +1836,28 @@ linux_resume_one_process (struct inferior_list_entry *entry, /* If we have pending signals, consume one unless we are trying to reinsert a breakpoint. */ - if (process->pending_signals != NULL && process->bp_reinsert == 0) + if (lwp->pending_signals != NULL && lwp->bp_reinsert == 0) { struct pending_signals **p_sig; - p_sig = &process->pending_signals; + p_sig = &lwp->pending_signals; while ((*p_sig)->prev != NULL) p_sig = &(*p_sig)->prev; signal = (*p_sig)->signal; if ((*p_sig)->info.si_signo != 0) - ptrace (PTRACE_SETSIGINFO, process->lwpid, 0, &(*p_sig)->info); + ptrace (PTRACE_SETSIGINFO, ptid_get_lwp (lwp->head.id), 0, &(*p_sig)->info); free (*p_sig); *p_sig = NULL; } regcache_invalidate_one ((struct inferior_list_entry *) - get_process_thread (process)); + get_lwp_thread (lwp)); errno = 0; - process->stopped = 0; - process->stepping = step; - ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, process->lwpid, 0, signal); + lwp->stopped = 0; + lwp->stepping = step; + ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwpid_of (lwp), 0, signal); current_inferior = saved_inferior; if (errno) @@ -1208,147 +1876,412 @@ linux_resume_one_process (struct inferior_list_entry *entry, } } -static struct thread_resume *resume_ptr; +struct resume_info +{ + struct thread_resume *request; + size_t n; +}; -/* This function is called once per thread. We look up the thread - in RESUME_PTR, and mark the thread with a pointer to the appropriate +/* This function is called once per thread. We look up the thread in + RESUME_INFO, and mark the thread with a pointer to the appropriate resume request. This algorithm is O(threads * resume elements), but resume elements is small (and will remain small at least until GDB supports thread suspension). */ -static void -linux_set_resume_request (struct inferior_list_entry *entry) +static int +linux_set_resume_request (struct inferior_list_entry *entry, void *arg) { - struct process_info *process; + struct lwp_info *lwp; struct thread_info *thread; int ndx; + struct resume_info *resume_info; thread = (struct thread_info *) entry; - process = get_thread_process (thread); + lwp = get_thread_lwp (thread); + resume_info = arg; - ndx = 0; - while (resume_ptr[ndx].thread != -1 && resume_ptr[ndx].thread != entry->id) - ndx++; + for (ndx = 0; ndx < resume_info->n; ndx++) + { + ptid_t ptid = resume_info->request[ndx].thread; + if (ptid_equal (ptid, minus_one_ptid) + || ptid_equal (ptid, entry->id) + || (ptid_is_pid (ptid) + && (ptid_get_pid (ptid) == pid_of (lwp))) + || (ptid_get_lwp (ptid) == -1 + && (ptid_get_pid (ptid) == pid_of (lwp)))) + { + lwp->resume = &resume_info->request[ndx]; + return 0; + } + } - process->resume = &resume_ptr[ndx]; + /* Don't touch. */ + lwp->resume = NULL; + + return 0; } -/* This function is called once per thread. We check the thread's resume - request, which will tell us whether to resume, step, or leave the thread - stopped; and what signal, if any, it should be sent. For threads which - we aren't explicitly told otherwise, we preserve the stepping flag; this - is used for stepping over gdbserver-placed breakpoints. */ +/* Set *FLAG_P if this lwp has an interesting status pending. */ +static int +resume_status_pending_p (struct inferior_list_entry *entry, void *flag_p) +{ + struct lwp_info *lwp = (struct lwp_info *) entry; -static void -linux_continue_one_thread (struct inferior_list_entry *entry) + /* LWPs which will not be resumed are not interesting, because + we might not wait for them next time through linux_wait. */ + if (lwp->resume == NULL) + return 0; + + if (lwp->dead) + { + /* If this thread is known to be dead, then the whole process it + belonged to has exited. Report it. */ + + /* TODO: See if the dead flag is really needed, or the + pending_status is enough. */ + if (!lwp->status_pending_p) + warning ("dead thread without a pending status?"); + + * (int *) flag_p = 1; + return 0; + } + + /* If this thread has a removed breakpoint, we won't have any + events to report later, so check now. check_removed_breakpoint + may clear status_pending_p. We avoid calling check_removed_breakpoint + for any thread that we are not otherwise going to resume - this + lets us preserve stopped status when two threads hit a breakpoint. + GDB removes the breakpoint to single-step a particular thread + past it, then re-inserts it and resumes all threads. We want + to report the second thread without resuming it in the interim. */ + if (lwp->status_pending_p && !lwp->suspended) + check_removed_breakpoint (lwp); + + if (lwp->status_pending_p) + * (int *) flag_p = 1; + + return 0; +} + +static int +cancel_breakpoint (struct lwp_info *lwp) { - struct process_info *process; - struct thread_info *thread; - int step; + /* Arrange for a breakpoint to be hit again later. We don't keep + the SIGTRAP status and don't forward the SIGTRAP signal to the + thread. Eventually we will resume this thread, and this + breakpoint will trap again. + + If we do not do this, then we run the risk that the user will + delete or disable the breakpoint, but the thread will have + already tripped on it. */ + + if (lwp->stopped + && !lwp->dead + && !lwp->stepping + && ((lwp->status_pending_p + && WIFSTOPPED (lwp->status_pending) + && WSTOPSIG (lwp->status_pending) == SIGTRAP) + || (!lwp->status_pending_p + && WIFSTOPPED (lwp->last_status) + && WSTOPSIG (lwp->last_status) == SIGTRAP))) + { + CORE_ADDR stop_pc; + struct thread_info *saved_inferior; - thread = (struct thread_info *) entry; - process = get_thread_process (thread); + /* If there's a breakpoint here, adjust the PC, so the + breakpoint is hit again when the thread is resumed. */ - if (process->resume->leave_stopped) - return; + if (debug_threads) + fprintf (stderr, "Checking for breakpoint in lwp %ld.\n", + lwpid_of (lwp)); - if (process->resume->thread == -1) - step = process->stepping || process->resume->step; - else - step = process->resume->step; + saved_inferior = current_inferior; + current_inferior = get_lwp_thread (lwp); + + stop_pc = (*the_low_target.get_pc) (); + stop_pc -= the_low_target.decr_pc_after_break; + + /* If there's a breakpoint there, back off the PC. */ + if (breakpoint_at (stop_pc) + || (*the_low_target.breakpoint_at) (stop_pc)) + { + if (the_low_target.set_pc != NULL) + { + if (debug_threads) + fprintf (stderr, "CB: breakpoint present, backing off PC.\n"); + (*the_low_target.set_pc) (stop_pc); + } + else + { + if (debug_threads) + fprintf (stderr, "CB: breakpoint present ignoring SIGTRAP.\n"); + } + + lwp->pending_is_breakpoint = 0; + lwp->status_pending_p = 0; + lwp->status_pending = 0; + lwp->last_status = W_STOPCODE (0); + + current_inferior = saved_inferior; + return 1; + } + + current_inferior = saved_inferior; + } - linux_resume_one_process (&process->head, step, process->resume->sig, NULL); + return 0; +} + +static int +cancel_breakpoints_callback (struct inferior_list_entry *entry, void *data) +{ + struct lwp_info *lp = (struct lwp_info *) entry; + struct lwp_info *event_lp = data; + + /* Leave the LWP that has been elected to receive a SIGTRAP alone. */ + if (lp == event_lp) + return 0; + + /* If a LWP other than the LWP that we're reporting an event for has + hit a GDB breakpoint (as opposed to some random trap signal), + then just arrange for it to hit it again later. We don't keep + the SIGTRAP status and don't forward the SIGTRAP signal to the + LWP. We will handle the current event, eventually we will resume + all LWPs, and this one will get its breakpoint trap again. + + If we do not do this, then we run the risk that the user will + delete or disable the breakpoint, but the LWP will have already + tripped on it. */ - process->resume = NULL; + cancel_breakpoint (lp); + return 0; } /* This function is called once per thread. We check the thread's resume request, which will tell us whether to resume, step, or leave the thread - stopped; and what signal, if any, it should be sent. We queue any needed - signals, since we won't actually resume. We already have a pending event - to report, so we don't need to preserve any step requests; they should - be re-issued if necessary. */ + stopped; and what signal, if any, it should be sent. -static void -linux_queue_one_thread (struct inferior_list_entry *entry) + For threads which we aren't explicitly told otherwise, we preserve + the stepping flag; this is used for stepping over gdbserver-placed + breakpoints. + + If pending_flags was set in any thread, we queue any needed + signals, since we won't actually resume. We already have a pending + event to report, so we don't need to preserve any step requests; + they should be re-issued if necessary. */ + +static int +linux_resume_one_thread (struct inferior_list_entry *entry, void *arg) { - struct process_info *process; + struct lwp_info *lwp; struct thread_info *thread; + int step; + int pending_flag = * (int *) arg; thread = (struct thread_info *) entry; - process = get_thread_process (thread); + lwp = get_thread_lwp (thread); - if (process->resume->leave_stopped) - return; + if (lwp->resume == NULL) + return 0; - /* If we have a new signal, enqueue the signal. */ - if (process->resume->sig != 0) + if (lwp->resume->kind == rk_stop) { - struct pending_signals *p_sig; - p_sig = malloc (sizeof (*p_sig)); - p_sig->prev = process->pending_signals; - p_sig->signal = process->resume->sig; - memset (&p_sig->info, 0, sizeof (siginfo_t)); + if (debug_threads) + fprintf (stderr, "suspending thread %s\n", + target_pid_to_str (entry->id)); + + /* Do this before waiting for sigstop, as the thread may + die. */ + /* TODO: how do we report that happening to GDB? GDB and the + user can get very confused if a thread exits without GDB + being aware of it. E.g, user tries to interrupts thread, but + GDB doesn't reply back, The simplest seem to be to implement + a thread exit stop reply. Note that returning a generic + error is not an option if we're handling a vCont. The reply + has already been sent at this point, and, the same vCont can + hold multiple resumptions -- to which would the error + apply? */ + lwp->resume = NULL; + + /* To simplify things, we're waiting for the lwp to stop here, + and pushing the stop reply to gdbserver's common code. An + alternative, would be to defer to linux_wait to do the wait, + and notice that an lwp with the "suspended" flag but + "!stopped", should leave any status pending, and report a + sig0 stop status. */ + + /* Do we have to distinguish internal stops from external stops, + due to the support for gdbserver breakpoints? If so, then + thread_info needs to gain a "running" property. */ + if (!lwp->stopped) + { + ptid_t ptid = entry->id; - /* If this is the same signal we were previously stopped by, - make sure to queue its siginfo. We can ignore the return - value of ptrace; if it fails, we'll skip - PTRACE_SETSIGINFO. */ - if (WIFSTOPPED (process->last_status) - && WSTOPSIG (process->last_status) == process->resume->sig) - ptrace (PTRACE_GETSIGINFO, process->lwpid, 0, &p_sig->info); + if (debug_threads) + fprintf (stderr, "running -> suspending %s\n", + target_pid_to_str (entry->id)); + + lwp->suspended = 1; + send_sigstop (&lwp->head); + wait_for_sigstop (&lwp->head); + if (!lwp->dead) + { + struct target_waitstatus status; + status.kind = TARGET_WAITKIND_STOPPED; + + /* Cancel internal breakpoints, otherwise, the user will + see a possibly invalid PC on decr_pc_after_break + archs, because GDB can't tell there's a breakpoint + there. As long as we do it, might as well cancel GDB + breakpoints too, although GDB will also adjust the PC + if we don't in that case. */ + + /* Cancel breakpoints, but leave out finished steps and + watchpoint hits. */ + if (cancel_breakpoint (lwp)) + /* The LWP hit a breakpoint while we tried to stop it, + and we backed off the PC. Report a SIG0. */ + status.value.sig = 0; + else if (lwp->stopped + && lwp->stepping + && ((!lwp->status_pending_p + && WIFSTOPPED (lwp->last_status) + && WSTOPSIG (lwp->last_status) == SIGTRAP) + || (lwp->status_pending_p + && WIFSTOPPED (lwp->status_pending) + && WSTOPSIG (lwp->status_pending) == SIGTRAP))) + { + /* The LWP finished a hardware single-step; report + the SIGTRAP to GDB. */ + lwp->pending_is_breakpoint = 0; + lwp->status_pending_p = 0; + lwp->status_pending = 0; + lwp->last_status = W_STOPCODE (SIGTRAP); + /* Report the finished single-step. When using + displaced stepping, GDB needs this to be able to + fixup the PC. */ + status.value.sig = TARGET_SIGNAL_TRAP; + } + else + /* Leave other signals pending. */ + status.value.sig = 0; + + /* Pass the stop reply back to GDB. */ + push_event (ptid, &status); + } + else + ; + } + else + { + if (debug_threads) + { + if (lwp->suspended) + fprintf (stderr, "already stopped/suspended %s\n", + target_pid_to_str (entry->id)); + else + fprintf (stderr, "already stopped/not suspended %s\n", + target_pid_to_str (entry->id)); + } - process->pending_signals = p_sig; + /* Make sure we leave the LWP suspended, so we don't try to + resume it without GDB telling us to. FIXME: The LWP may + have been stopped in an internal event that was not meant + to be notified back to GDB (e.g., gdbserver breakpoint), + so we should be reporting a stop event in that case + too. */ + lwp->suspended = 1; + } + return 0; } + else + lwp->suspended = 0; - process->resume = NULL; -} + /* If this thread which is about to be resumed has a pending status, + then don't resume any threads - we can just report the pending + status. Make sure to queue any signals that would otherwise be + sent. In all-stop mode, we do this decision based on if *any* + thread has a pending status. */ + if (non_stop) + resume_status_pending_p (&lwp->head, &pending_flag); -/* Set DUMMY if this process has an interesting status pending. */ -static int -resume_status_pending_p (struct inferior_list_entry *entry, void *flag_p) -{ - struct process_info *process = (struct process_info *) entry; + if (!pending_flag) + { + if (debug_threads) + fprintf (stderr, "resuming thread %s\n", + target_pid_to_str (entry->id)); - /* Processes which will not be resumed are not interesting, because - we might not wait for them next time through linux_wait. */ - if (process->resume->leave_stopped) - return 0; + if (ptid_equal (lwp->resume->thread, minus_one_ptid)) + step = lwp->stepping || (lwp->resume->kind == rk_step); + else + step = (lwp->resume->kind == rk_step); - /* If this thread has a removed breakpoint, we won't have any - events to report later, so check now. check_removed_breakpoint - may clear status_pending_p. We avoid calling check_removed_breakpoint - for any thread that we are not otherwise going to resume - this - lets us preserve stopped status when two threads hit a breakpoint. - GDB removes the breakpoint to single-step a particular thread - past it, then re-inserts it and resumes all threads. We want - to report the second thread without resuming it in the interim. */ - if (process->status_pending_p) - check_removed_breakpoint (process); + linux_resume_one_lwp (&lwp->head, step, lwp->resume->sig, NULL); + } + else + { + if (debug_threads) + fprintf (stderr, "leaving thread %s stopped\n", + target_pid_to_str (entry->id)); - if (process->status_pending_p) - * (int *) flag_p = 1; + /* If we have a new signal, enqueue the signal. */ + if (lwp->resume->sig != 0) + { + struct pending_signals *p_sig; + p_sig = malloc (sizeof (*p_sig)); + p_sig->prev = lwp->pending_signals; + p_sig->signal = lwp->resume->sig; + memset (&p_sig->info, 0, sizeof (siginfo_t)); + + /* If this is the same signal we were previously stopped by, + make sure to queue its siginfo. We can ignore the return + value of ptrace; if it fails, we'll skip + PTRACE_SETSIGINFO. */ + if (WIFSTOPPED (lwp->last_status) + && WSTOPSIG (lwp->last_status) == lwp->resume->sig) + ptrace (PTRACE_GETSIGINFO, ptid_get_lwp (lwp->head.id), 0, &p_sig->info); + + lwp->pending_signals = p_sig; + } + } + lwp->resume = NULL; return 0; } static void -linux_resume (struct thread_resume *resume_info) +linux_resume (struct thread_resume *resume_info, size_t n) { int pending_flag; + struct resume_info info = { resume_info, n }; - /* Yes, the use of a global here is rather ugly. */ - resume_ptr = resume_info; + if (debug_threads) + { + int idx; + fprintf (stderr, "linux_resume\n"); + for (idx = 0; idx < n; idx++) + { + fprintf (stderr, " thread(%s), kind(%d), sig(%d)\n", + target_pid_to_str (resume_info[idx].thread), + resume_info[idx].kind, + resume_info[idx].sig); + idx++; + } + } - for_each_inferior (&all_threads, linux_set_resume_request); + find_inferior (&all_threads, linux_set_resume_request, &info); /* If there is a thread which would otherwise be resumed, which has a pending status, then don't resume any threads - we can just report the pending status. Make sure to queue any signals that would otherwise be sent. */ + /* This is only used in all-stop mode however. In non-stop mode, + decide to leave pending based on the thread we're resuming itself + having a pending status already or not. */ pending_flag = 0; - find_inferior (&all_processes, resume_status_pending_p, &pending_flag); + if (!non_stop) + find_inferior (&all_lwps, resume_status_pending_p, &pending_flag); if (debug_threads) { @@ -1358,10 +2291,7 @@ linux_resume (struct thread_resume *resume_info) fprintf (stderr, "Resuming, no pending status\n"); } - if (pending_flag) - for_each_inferior (&all_threads, linux_queue_one_thread); - else - for_each_inferior (&all_threads, linux_continue_one_thread); + find_inferior (&all_threads, linux_resume_one_thread, &pending_flag); } #ifdef HAVE_LINUX_USRREGS @@ -1402,7 +2332,7 @@ fetch_register (int regno) { errno = 0; *(PTRACE_XFER_TYPE *) (buf + i) = - ptrace (PTRACE_PEEKUSER, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, 0); + ptrace (PTRACE_PEEKUSER, inferior_lwpid, (PTRACE_ARG3_TYPE) regaddr, 0); regaddr += sizeof (PTRACE_XFER_TYPE); if (errno != 0) { @@ -1424,7 +2354,7 @@ fetch_register (int regno) error_exit:; } -/* Fetch all registers, or just one, from the child process. */ +/* Fetch all registers, or just one, from the child lwp. */ static void usr_fetch_inferior_registers (int regno) { @@ -1470,13 +2400,13 @@ usr_store_inferior_registers (int regno) for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE)) { errno = 0; - ptrace (PTRACE_POKEUSER, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, + ptrace (PTRACE_POKEUSER, inferior_lwpid, (PTRACE_ARG3_TYPE) regaddr, *(PTRACE_XFER_TYPE *) (buf + i)); if (errno != 0) { - /* At this point, ESRCH should mean the process is already gone, + /* At this point, ESRCH should mean the lwp is already gone, in which case we simply ignore attempts to change its registers. - See also the related comment in linux_resume_one_process. */ + See also the related comment in linux_resume_one_lwp. */ if (errno == ESRCH) return; @@ -1523,17 +2453,13 @@ regsets_fetch_inferior_registers () } buf = malloc (regset->size); -#ifndef __sparc__ - res = ptrace (regset->get_request, inferior_pid, 0, buf); -#else - res = ptrace (regset->get_request, inferior_pid, buf, 0); -#endif + res = ptrace (regset->get_request, inferior_lwpid, 0, buf); if (res < 0) { if (errno == EIO) { /* If we get EIO on a regset, do not try it again for - this process. */ + this lwp. */ disabled_regsets[regset - target_regsets] = 1; continue; } @@ -1541,7 +2467,7 @@ regsets_fetch_inferior_registers () { char s[256]; sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%ld", - inferior_pid); + inferior_lwpid); perror (s); } } @@ -1580,11 +2506,7 @@ regsets_store_inferior_registers () /* First fill the buffer with the current register set contents, in case there are any items in the kernel's regset that are not in gdbserver's regcache. */ -#ifndef __sparc__ - res = ptrace (regset->get_request, inferior_pid, 0, buf); -#else - res = ptrace (regset->get_request, inferior_pid, buf, 0); -#endif + res = ptrace (regset->get_request, inferior_lwpid, 0, buf); if (res == 0) { @@ -1592,11 +2514,7 @@ regsets_store_inferior_registers () regset->fill_function (buf); /* Only now do we write the register set. */ -#ifndef __sparc__ - res = ptrace (regset->set_request, inferior_pid, 0, buf); -#else - res = ptrace (regset->set_request, inferior_pid, buf, 0); -#endif + res = ptrace (regset->set_request, inferior_lwpid, 0, buf); } if (res < 0) @@ -1604,15 +2522,16 @@ regsets_store_inferior_registers () if (errno == EIO) { /* If we get EIO on a regset, do not try it again for - this process. */ + this lwp. */ disabled_regsets[regset - target_regsets] = 1; continue; } else if (errno == ESRCH) { - /* At this point, ESRCH should mean the process is already gone, + /* At this point, ESRCH should mean the lwp is already gone, in which case we simply ignore attempts to change its registers. - See also the related comment in linux_resume_one_process. */ + See also the related comment in linux_resume_one_lwp. */ + free (buf); return 0; } else @@ -1684,7 +2603,7 @@ linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) { /* We could keep this file open and cache it - possibly one per thread. That requires some juggling, but is even faster. */ - sprintf (filename, "/proc/%ld/mem", inferior_pid); + sprintf (filename, "/proc/%ld/mem", inferior_lwpid); fd = open (filename, O_RDONLY | O_LARGEFILE); if (fd == -1) goto no_proc; @@ -1712,7 +2631,7 @@ linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) { errno = 0; - buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0); + buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_lwpid, (PTRACE_ARG3_TYPE) addr, 0); if (errno) return errno; } @@ -1747,13 +2666,13 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) /* Fill start and end extra bytes of buffer with existing memory data. */ - buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_pid, + buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_lwpid, (PTRACE_ARG3_TYPE) addr, 0); if (count > 1) { buffer[count - 1] - = ptrace (PTRACE_PEEKTEXT, inferior_pid, + = ptrace (PTRACE_PEEKTEXT, inferior_lwpid, (PTRACE_ARG3_TYPE) (addr + (count - 1) * sizeof (PTRACE_XFER_TYPE)), 0); @@ -1768,7 +2687,7 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) { errno = 0; - ptrace (PTRACE_POKETEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, buffer[i]); + ptrace (PTRACE_POKETEXT, inferior_lwpid, (PTRACE_ARG3_TYPE) addr, buffer[i]); if (errno) return errno; } @@ -1776,8 +2695,6 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) return 0; } -static int linux_supports_tracefork_flag; - /* Helper functions for linux_test_for_tracefork, called via clone (). */ static int @@ -1803,21 +2720,6 @@ linux_tracefork_child (void *arg) _exit (0); } -/* Wrapper function for waitpid which handles EINTR. */ - -static int -my_waitpid (int pid, int *status, int flags) -{ - int ret; - do - { - ret = waitpid (pid, status, flags); - } - while (ret == -1 && errno == EINTR); - - return ret; -} - /* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. Make sure that we can enable the option, and that it had the desired effect. */ @@ -1914,10 +2816,15 @@ static void linux_look_up_symbols (void) { #ifdef USE_THREAD_DB - if (thread_db_active) + /* It would probably be cleaner now to move this whole function to + thread-db.c. */ + + struct process_info *proc = current_process (); + if (proc->private->thread_db_active) return; - thread_db_active = thread_db_init (!linux_supports_tracefork_flag); + proc->private->thread_db_active + = thread_db_init (!linux_supports_tracefork_flag); #endif } @@ -1926,12 +2833,15 @@ linux_request_interrupt (void) { extern unsigned long signal_pid; - if (cont_thread != 0 && cont_thread != -1) + if (!ptid_equal (cont_thread, null_ptid) + && !ptid_equal (cont_thread, minus_one_ptid)) { - struct process_info *process; + struct lwp_info *lwp; + int lwpid; - process = get_thread_process (current_inferior); - kill_lwp (process->lwpid, SIGINT); + lwp = get_thread_lwp (current_inferior); + lwpid = ptid_get_lwp (lwp->head.id); + kill_lwp (lwpid, SIGINT); } else kill_lwp (signal_pid, SIGINT); @@ -1946,7 +2856,7 @@ linux_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len) char filename[PATH_MAX]; int fd, n; - snprintf (filename, sizeof filename, "/proc/%ld/auxv", inferior_pid); + snprintf (filename, sizeof filename, "/proc/%ld/auxv", inferior_lwpid); fd = open (filename, O_RDONLY); if (fd < 0) @@ -2012,6 +2922,12 @@ linux_stopped_data_address (void) #define PT_TEXT_END_ADDR 51*4 #endif +#if defined(__arm__) +#ifndef PTRACE_GETPROCMAP +#define PTRACE_GETPROCMAP 27 +#endif +#endif + /* Under uClinux, programs are loaded at non-zero offsets, which we need to tell gdb about. */ @@ -2020,7 +2936,7 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p) { #if defined(PT_TEXT_ADDR) && defined(PT_DATA_ADDR) && defined(PT_TEXT_END_ADDR) unsigned long text, text_end, data; - int pid = get_thread_process (current_inferior)->head.id; + int pid = pid_of (get_thread_lwp (current_inferior)); errno = 0; @@ -2045,10 +2961,95 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p) return 1; } #endif +#if defined(PTRACE_GETPROCMAP) + unsigned long data[6]; + int pid = pid_of (get_thread_lwp (current_inferior)); + + if (ptrace (PTRACE_GETPROCMAP, pid, NULL, data) == 0) + { + /* GETPROCMAP returns text_start, text_end, data_start. See above. */ + *text_p = data[0]; + *data_p = data[2] - (data[1] - data[0]); + return 1; + } +#endif return 0; } #endif +/* SIGCHLD handler that serves two purposes: In non-stop/async mode, + so we notice when any child changes state; as the handler for the + sigsuspend in my_waitpid. */ + +static void +sigchld_handler (int signo) +{ + int old_errno = errno; + + if (debug_threads) + /* fprintf is not an async-signal-safe function, so call write + directly. */ + write (2, "sigchld_handler\n", + strlen ("sigchld_handler\n")); + + if (target_is_async_p ()) + async_file_mark (); /* trigger a linux_wait */ + + errno = old_errno; +} + +static int +linux_async (int enable) +{ + int previous = (linux_event_pipe[0] != -1); + + if (previous != enable) + { + sigset_t mask; + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); + + sigprocmask (SIG_BLOCK, &mask, NULL); + + if (enable) + { + if (pipe (linux_event_pipe) == -1) + internal_error ("creating event pipe failed."); + + fcntl (linux_event_pipe[0], F_SETFL, O_NONBLOCK); + fcntl (linux_event_pipe[1], F_SETFL, O_NONBLOCK); + + /* Register the event loop handler. */ + add_file_handler (linux_event_pipe[0], + handle_target_event, NULL); + + /* Always trigger a linux_wait. */ + async_file_mark (); + } + else + { + delete_file_handler (linux_event_pipe[0]); + + close (linux_event_pipe[0]); + close (linux_event_pipe[1]); + linux_event_pipe[0] = -1; + linux_event_pipe[1] = -1; + } + + sigprocmask (SIG_UNBLOCK, &mask, NULL); + } + + return previous; +} + +static int +linux_start_non_stop (int nonstop) +{ + /* Register or unregister from event-loop accordingly. */ + linux_async (nonstop); + return 0; +} + static struct target_ops linux_target_ops = { linux_create_inferior, linux_attach, @@ -2081,6 +3082,9 @@ static struct target_ops linux_target_ops = { #endif NULL, hostio_last_error_from_errno, + linux_pid_to_exec_file, + linux_async, + linux_start_non_stop, }; static void @@ -2094,7 +3098,8 @@ linux_init_signals () void initialize_low (void) { - thread_db_active = 0; + struct sigaction sigchld_action; + set_target_ops (&linux_target_ops); set_breakpoint_data (the_low_target.breakpoint, the_low_target.breakpoint_len); @@ -2105,4 +3110,9 @@ initialize_low (void) ; disabled_regsets = malloc (num_regsets); #endif + + sigchld_action.sa_handler = sigchld_handler; + sigemptyset (&sigchld_action.sa_mask); + sigchld_action.sa_flags = SA_RESTART; + sigaction (SIGCHLD, &sigchld_action, NULL); } |