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-10-13 Daniel Jacobowitz <drow@mvista.com>
+
+ * 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 <hongjiu.lu@intel.com>
* Makefile.in (install-only): Create dest dir. Support DESTDIR.
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);
/* 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 ();
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;
+
+ 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;
- process = (struct process_info *) entry;
- linux_resume_one_process (entry, process->stepping, 0);
+ 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;
-
- 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);
+ /* 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. */
- 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
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
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
#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.
int (*thread_alive) (int pid);
- /* Resume the inferior process.
-
- If STEP is non-zero, we want to single-step.
+ /* Resume the inferior process. */
- 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.
#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)