diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 42dab168db..603c33f252 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,20 @@ +2003-10-13 Daniel Jacobowitz + + * linux-low.c (linux_resume): Take a struct thread_resume * + argument. + (linux_wait): Update call. + (resume_ptr): New static variable. + (linux_continue_one_thread): Renamed from + linux_continue_one_process. Use resume_ptr. + (linux_resume): Use linux_continue_one_thread. + * server.c (handle_v_cont, handle_v_requests): New functions. + (myresume): New function. + (main): Handle 'v' case. + * target.h (struct thread_resume): New type. + (struct target_ops): Change argument of "resume" to struct + thread_resume *. + (myresume): Delete macro. + 2003-08-08 H.J. Lu * Makefile.in (install-only): Create dest dir. Support DESTDIR. diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 55c187c1bd..68b3f65a33 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -52,7 +52,7 @@ int using_threads; static void linux_resume_one_process (struct inferior_list_entry *entry, int step, int signal); -static void linux_resume (int step, int signal); +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); @@ -652,7 +652,12 @@ retry: /* No stepping, no signal - unless one is pending already, of course. */ if (child == NULL) - linux_resume (0, 0); + { + struct thread_resume resume_info; + resume_info.thread = -1; + resume_info.step = resume_info.sig = resume_info.leave_stopped = 0; + linux_resume (&resume_info); + } } enable_async_io (); @@ -868,33 +873,48 @@ linux_resume_one_process (struct inferior_list_entry *entry, perror_with_name ("ptrace"); } -/* This function is called once per process other than the first - one. The first process we are told the signal to continue - with, and whether to step or continue; for all others, any - existing signals will be marked in status_pending_p to be - reported momentarily, and we preserve the stepping flag. */ +static struct thread_resume *resume_ptr; + +/* This function is called once per thread. We look up the thread + in RESUME_PTR, 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. If the thread has a status pending, it may not actually + be resumed. */ static void -linux_continue_one_process (struct inferior_list_entry *entry) +linux_continue_one_thread (struct inferior_list_entry *entry) { struct process_info *process; + struct thread_info *thread; + int ndx, step; - process = (struct process_info *) entry; - linux_resume_one_process (entry, process->stepping, 0); + thread = (struct thread_info *) entry; + process = get_thread_process (thread); + + ndx = 0; + while (resume_ptr[ndx].thread != -1 && resume_ptr[ndx].thread != entry->id) + ndx++; + + if (resume_ptr[ndx].leave_stopped) + return; + + if (resume_ptr[ndx].thread == -1) + step = process->stepping || resume_ptr[ndx].step; + else + step = resume_ptr[ndx].step; + + linux_resume_one_process (&process->head, step, resume_ptr[ndx].sig); } static void -linux_resume (int step, int signal) +linux_resume (struct thread_resume *resume_info) { - struct process_info *process; + /* Yes, this is quadratic. If it ever becomes a problem then it's + fairly easy to fix. Yes, the use of a global here is rather ugly. */ - process = get_thread_process (current_inferior); - - /* If the current process has a status pending, this signal will - be enqueued and sent later. */ - linux_resume_one_process (&process->head, step, signal); - - if (cont_thread == 0 || cont_thread == -1) - for_each_inferior (&all_processes, linux_continue_one_process); + resume_ptr = resume_info; + for_each_inferior (&all_threads, linux_continue_one_thread); } #ifdef HAVE_LINUX_USRREGS diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 81fde5b7f7..dffff2e1e6 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -125,6 +125,155 @@ handle_query (char *own_buf) own_buf[0] = 0; } +/* Parse vCont packets. */ +void +handle_v_cont (char *own_buf, char *status, unsigned char *signal) +{ + char *p, *q; + int n = 0, i = 0; + struct thread_resume *resume_info, default_action; + + /* Count the number of semicolons in the packet. There should be one + for every action. */ + p = &own_buf[5]; + while (p) + { + n++; + p++; + p = strchr (p, ';'); + } + /* Allocate room for one extra action, for the default remain-stopped + behavior; if no default action is in the list, we'll need the extra + slot. */ + resume_info = malloc ((n + 1) * sizeof (resume_info[0])); + + default_action.thread = -1; + default_action.leave_stopped = 1; + default_action.step = 0; + default_action.sig = 0; + + p = &own_buf[5]; + i = 0; + while (*p) + { + p++; + + resume_info[i].leave_stopped = 0; + + if (p[0] == 's' || p[0] == 'S') + resume_info[i].step = 1; + else if (p[0] == 'c' || p[0] == 'C') + resume_info[i].step = 0; + else + goto err; + + if (p[0] == 'S' || p[0] == 'C') + { + int sig; + sig = strtol (p + 1, &q, 16); + if (p == q) + goto err; + p = q; + + if (!target_signal_to_host_p (sig)) + goto err; + resume_info[i].sig = target_signal_to_host (sig); + } + else + { + resume_info[i].sig = 0; + p = p + 1; + } + + if (p[0] == 0) + { + resume_info[i].thread = -1; + default_action = resume_info[i]; + + /* Note: we don't increment i here, we'll overwrite this entry + the next time through. */ + } + else if (p[0] == ':') + { + resume_info[i].thread = strtol (p + 1, &q, 16); + if (p == q) + goto err; + p = q; + if (p[0] != ';' && p[0] != 0) + goto err; + + i++; + } + } + + resume_info[i] = default_action; + + /* Still used in occasional places in the backend. */ + if (n == 1 && resume_info[0].thread != -1) + cont_thread = resume_info[0].thread; + else + cont_thread = -1; + + (*the_target->resume) (resume_info); + + free (resume_info); + + *signal = mywait (status, 1); + prepare_resume_reply (own_buf, *status, *signal); + return; + +err: + /* No other way to report an error... */ + strcpy (own_buf, ""); + free (resume_info); + return; +} + +/* Handle all of the extended 'v' packets. */ +void +handle_v_requests (char *own_buf, char *status, unsigned char *signal) +{ + if (strncmp (own_buf, "vCont;", 6) == 0) + { + handle_v_cont (own_buf, status, signal); + return; + } + + if (strncmp (own_buf, "vCont?", 6) == 0) + { + strcpy (own_buf, "vCont;c;C;s;S"); + return; + } + + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + own_buf[0] = 0; + return; +} + +void +myresume (int step, int sig) +{ + struct thread_resume resume_info[2]; + int n = 0; + + if (step || sig || cont_thread > 0) + { + resume_info[0].thread + = ((struct inferior_list_entry *) current_inferior)->id; + resume_info[0].step = step; + resume_info[0].sig = sig; + resume_info[0].leave_stopped = 0; + n++; + } + resume_info[n].thread = -1; + resume_info[n].step = 0; + resume_info[n].sig = 0; + resume_info[n].leave_stopped = (cont_thread > 0); + + (*the_target->resume) (resume_info); +} + static int attached; static void @@ -383,6 +532,10 @@ main (int argc, char *argv[]) own_buf[0] = '\0'; break; } + case 'v': + /* Extended (long) request. */ + handle_v_requests (own_buf, &status, &signal); + break; default: /* It is a request we don't understand. Respond with an empty packet so that gdb knows that we don't support this diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index 1c47a3aedb..aa0a44afd6 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -24,6 +24,25 @@ #ifndef TARGET_H #define TARGET_H +/* This structure describes how to resume a particular thread (or + all threads) based on the client's request. If thread is -1, then + this entry applies to all threads. These are generally passed around + as an array, and terminated by a thread == -1 entry. */ + +struct thread_resume +{ + int thread; + + /* If non-zero, leave this thread stopped. */ + int leave_stopped; + + /* If non-zero, we want to single-step. */ + int step; + + /* If non-zero, send this signal when we resume. */ + int sig; +}; + struct target_ops { /* Start a new process. @@ -56,14 +75,9 @@ struct target_ops int (*thread_alive) (int pid); - /* Resume the inferior process. + /* Resume the inferior process. */ - If STEP is non-zero, we want to single-step. - - If SIGNAL is nonzero, send the process that signal as we resume it. - */ - - void (*resume) (int step, int signo); + void (*resume) (struct thread_resume *resume_info); /* Wait for the inferior process to change state. @@ -132,9 +146,6 @@ void set_target_ops (struct target_ops *); #define mythread_alive(pid) \ (*the_target->thread_alive) (pid) -#define myresume(step,signo) \ - (*the_target->resume) (step, signo) - #define fetch_inferior_registers(regno) \ (*the_target->fetch_registers) (regno)