diff options
author | Pedro Alves <palves@redhat.com> | 2009-04-01 22:48:05 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2009-04-01 22:48:05 +0000 |
commit | bd99dc858385792aad304d42f4a47791fd8d3272 (patch) | |
tree | ee30ea92e8d9e1ddbfb5a2f5f2a26b0ed12364b6 /gdb/gdbserver/server.c | |
parent | 5b1c542ea1c4ff247db390bd24a9e0665d0c2e48 (diff) | |
download | gdb-bd99dc858385792aad304d42f4a47791fd8d3272.zip gdb-bd99dc858385792aad304d42f4a47791fd8d3272.tar.gz gdb-bd99dc858385792aad304d42f4a47791fd8d3272.tar.bz2 |
Non-stop mode support.
* server.h (non_stop): Declare.
(gdb_client_data, handler_func): Declare.
(delete_file_handler, add_file_handler, start_event_loop):
Declare.
(handle_serial_event, handle_target_event, push_event)
(putpkt_notif): Declare.
* target.h (enum resume_kind): New.
(struct thread_resume): Replace `step' field by `kind' field.
(TARGET_WNOHANG): Define.
(struct target_ops) <wait>: Add `options' argument.
<supports_non_stop, async, start_non_stop>: New fields.
(target_supports_non_stop, target_async): New.
(start_non_stop): Declare.
(mywait): Add `options' argument.
* target.c (mywait): Add `options' argument. Print child exit
notifications here.
(start_non_stop): New.
* server.c (non_stop, own_buf, mem_buf): New globals.
(struct vstop_notif): New.
(notif_queue): New global.
(queue_stop_reply, push_event, discard_queued_stop_replies)
(send_next_stop_reply): New.
(start_inferior): Adjust to use resume_kind. Adjust to mywait
interface changes.
(attach_inferior): In non-stop mode, don't wait for the target
here.
(handle_general_set): Handle QNonStop.
(handle_query): When handling qC, return the current general
thread, instead of the first thread of the list.
(handle_query): If the backend supports non-stop mode, include
QNonStop+ in the qSupported query response.
(handle_v_cont): Adjust to use resume_kind. Handle resume_stop
and non-stop mode.
(handle_v_attach, handle_v_run): Handle non-stop mode.
(handle_v_stopped): New.
(handle_v_requests): Report support for vCont;t. Handle vStopped.
(myresume): Adjust to use resume_kind. Handle non-stop.
(queue_stop_reply_callback): New.
(handle_status): Handle non-stop mode.
(main): Clear non_stop flag on reconnection. Use the event-loop.
Refactor serial protocol handling from here ...
(process_serial_event): ... to this new function. When GDB
selects any thread, select one here. In non-stop mode, wait until
GDB acks all pending events before exiting.
(handle_serial_event, handle_target_event): New.
* remote-utils.c (remote_open): Install remote_desc in the event
loop.
(remote_close): Remove remote_desc from the event loop.
(putpkt_binary): Rename to...
(putpkt_binary_1): ... this. Add `is_notic' argument. Handle it.
(putpkt_binary): New as wrapper around putpkt_binary_1.
(putpkt_notif): New.
(prepare_resume_reply): In non-stop mode, don't change the
general_thread.
* event-loop.c: New.
* Makefile.in (OBJ): Add event-loop.o.
(event-loop.o): New rule.
* linux-low.h (pid_of): Moved here.
(lwpid_of): New.
(get_lwp_thread): Use lwpid_of.
(struct lwp_info): Delete `lwpid' field. Add `suspended' field.
* linux-low.c (pid_of): Delete.
(inferior_pid): Use lwpid_of.
(linux_event_pipe): New.
(target_is_async_p): New.
(delete_lwp): New.
(handle_extended_wait): Use lwpid_of.
(add_lwp): Don't set lwpid field.
(linux_attach_lwp): Adjust debug output. Use lwpid_of.
(linux_kill_one_lwp): If killing a running lwp, stop it first.
Use lwpid_of. Adjust to linux_wait_for_event interface changes.
(linux_detach_one_lwp): If detaching from a running lwp, stop it
first. Adjust to linux_wait_for_event interface changes. Use
lwpid_of.
(linux_detach): Don't delete the main lwp here.
(linux_join): Use my_waitpid. Avoid signal_pid. Use lwpid_of.
(status_pending_p): Don't consider explicitly suspended lwps.
(linux_wait_for_lwp): Take an integer pid instead of a lwp_info
pointer. Add OPTIONS argument. Change return type to int. Use
my_waitpid instead of sleeping. Handle WNOHANG. Use lwpid_of.
(linux_wait_for_event): Take an integer pid instead of a lwp_info
pointer. Add status pointer argument. Return a pid instead of a
status. Use lwpid_of. Adjust to linux_wait_for_lwp interface
changes. In non-stop mode, don't switch to a random thread.
(linux_wait): Rename to...
(linux_wait_1): ... this. Add target_options argument, and handle
it. Adjust to use resume_kind. Use lwpid_of. In non-stop mode,
don't handle the continue thread. Handle TARGET_WNOHANG. Merge
clean exit and signal exit code. Don't stop all threads in
non-stop mode. In all-stop mode, only stop all threads when
reporting a stop to GDB. Handle explicit thread stop requests.
(async_file_flush, async_file_mark): New.
(linux_wait): New.
(send_sigstop): Use lwpid_of.
(wait_for_sigstop): Use lwpid_of. Adjust to linux_wait_for_event
interface changes. In non-stop mode, don't switch to a random
thread.
(linux_resume_one_lwp): Use lwpid_of.
(linux_continue_one_thread, linux_queue_one_thread): Merge into ...
(linux_resume_one_thread): ... this. Handle resume_stop. In
non-stop mode, don't look for pending flag in all threads.
(resume_status_pending_p): Don't consider explicitly suspended
threads.
(my_waitpid): Reimplement. Emulate __WALL.
(linux_request_interrupt, linux_read_offsets, linux_xfer_siginfo):
Use lwpid_of.
(sigchld_handler, linux_supports_non_stop, linux_async)
(linux_start_non_stop): New.
(linux_target_ops): Register linux_supports_non_stop, linux_async
and linux_start_non_stop.
(initialize_low): Install SIGCHLD handler.
* thread-db.c (thread_db_create_event, find_one_thread)
(thread_db_get_tls_address): Use lwpid_of.
* win32-low.c (win32_detach): Adjust to use resume_kind.
(win32_wait): Add `options' argument.
* spu-low.c (spu_resume): Adjust to use resume_kind.
(spu_wait): Add `options' argument.
Diffstat (limited to 'gdb/gdbserver/server.c')
-rw-r--r-- | gdb/gdbserver/server.c | 1090 |
1 files changed, 706 insertions, 384 deletions
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 1f61c0e..81efecc 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -43,6 +43,8 @@ static int attached; static int response_needed; static int exit_requested; +int non_stop; + static char **program_argv, **wrapper_argv; /* Enable miscellaneous debugging output. The name is historical - it @@ -90,6 +92,113 @@ int disable_packet_qfThreadInfo; static struct target_waitstatus last_status; static unsigned long last_ptid; +static char *own_buf; +static unsigned char *mem_buf; + +/* Structure holding information relative to a single stop reply. We + keep a queue of these (really a singly-linked list) to push to GDB + in non-stop mode. */ +struct vstop_notif +{ + /* Pointer to next in list. */ + struct vstop_notif *next; + + /* Thread or process that got the event. */ + unsigned long ptid; + + /* Event info. */ + struct target_waitstatus status; +}; + +/* The pending stop replies list head. */ +static struct vstop_notif *notif_queue = NULL; + +/* Put a stop reply to the stop reply queue. */ + +static void +queue_stop_reply (unsigned long ptid, struct target_waitstatus *status) +{ + struct vstop_notif *new_notif; + + new_notif = malloc (sizeof (*new_notif)); + new_notif->next = NULL; + new_notif->ptid = ptid; + new_notif->status = *status; + + if (notif_queue) + { + struct vstop_notif *tail; + for (tail = notif_queue; + tail && tail->next; + tail = tail->next) + ; + tail->next = new_notif; + } + else + notif_queue = new_notif; + + if (remote_debug) + { + int i = 0; + struct vstop_notif *n; + + for (n = notif_queue; n; n = n->next) + i++; + + fprintf (stderr, "pending stop replies: %d\n", i); + } +} + +/* Place an event in the stop reply queue, and push a notification if + we aren't sending one yet. */ + +void +push_event (unsigned long ptid, struct target_waitstatus *status) +{ + queue_stop_reply (ptid, status); + + /* If this is the first stop reply in the queue, then inform GDB + about it, by sending a Stop notification. */ + if (notif_queue->next == NULL) + { + char *p = own_buf; + strcpy (p, "Stop:"); + p += strlen (p); + prepare_resume_reply (p, + notif_queue->ptid, ¬if_queue->status); + putpkt_notif (own_buf); + } +} + +/* Get rid of the currently pending stop replies. */ + +static void +discard_queued_stop_replies (void) +{ + struct vstop_notif *next; + + while (notif_queue) + { + next = notif_queue->next; + notif_queue = next; + + free (next); + } +} + +/* If there are more stop replies to push, push one now. */ + +static void +send_next_stop_reply (char *own_buf) +{ + if (notif_queue) + prepare_resume_reply (own_buf, + notif_queue->ptid, + ¬if_queue->status); + else + write_ok (own_buf); +} + static int target_running (void) { @@ -147,10 +256,11 @@ start_inferior (char **argv) unsigned long ptid; resume_info.thread = -1; - resume_info.step = 0; + resume_info.kind = resume_continue; resume_info.sig = 0; - ptid = mywait (&last_status, 0); + ptid = mywait (&last_status, 0, 0); + if (last_status.kind != TARGET_WAITKIND_STOPPED) return signal_pid; @@ -158,7 +268,7 @@ start_inferior (char **argv) { (*the_target->resume) (&resume_info, 1); - mywait (&last_status, 0); + mywait (&last_status, 0, 0); if (last_status.kind != TARGET_WAITKIND_STOPPED) return signal_pid; } @@ -169,7 +279,7 @@ start_inferior (char **argv) /* Wait till we are at 1st instruction in program, return new pid (assuming success). */ - last_ptid = mywait (&last_status, 0); + last_ptid = mywait (&last_status, 0, 0); return signal_pid; } @@ -193,14 +303,17 @@ attach_inferior (int pid) whichever we were told to attach to. */ signal_pid = pid; - last_ptid = mywait (&last_status, 0); - - /* GDB knows to ignore the first SIGSTOP after attaching to a running - process using the "attach" command, but this is different; it's - just using "target remote". Pretend it's just starting up. */ - if (last_status.kind == TARGET_WAITKIND_STOPPED - && last_status.value.sig == TARGET_SIGNAL_STOP) - last_status.value.sig = TARGET_SIGNAL_TRAP; + if (!non_stop) + { + last_ptid = mywait (&last_status, 0, 0); + + /* GDB knows to ignore the first SIGSTOP after attaching to a running + process using the "attach" command, but this is different; it's + just using "target remote". Pretend it's just starting up. */ + if (last_status.kind == TARGET_WAITKIND_STOPPED + && last_status.value.sig == TARGET_SIGNAL_STOP) + last_status.value.sig = TARGET_SIGNAL_TRAP; + } return 0; } @@ -288,6 +401,43 @@ handle_general_set (char *own_buf) return; } + if (strncmp (own_buf, "QNonStop:", 9) == 0) + { + char *mode = own_buf + 9; + int req = -1; + char *req_str; + + if (strcmp (mode, "0") == 0) + req = 0; + else if (strcmp (mode, "1") == 0) + req = 1; + else + { + /* We don't know what this mode is, so complain to + GDB. */ + fprintf (stderr, "Unknown non-stop mode requested: %s\n", + own_buf); + write_enn (own_buf); + return; + } + + req_str = req ? "non-stop" : "all-stop"; + if (start_non_stop (req) != 0) + { + fprintf (stderr, "Setting %s mode failed\n", req_str); + write_enn (own_buf); + return; + } + + non_stop = req; + + if (remote_debug) + fprintf (stderr, "[%s mode enabled]\n", req_str); + + write_ok (own_buf); + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -506,10 +656,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) /* Reply the current thread id. */ if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC) { + unsigned long gdb_id; require_running (own_buf); - thread_ptr = all_threads.head; - sprintf (own_buf, "QC%x", - thread_to_gdb_id ((struct thread_info *)thread_ptr)); + + if (general_thread != 0 && general_thread != -1) + gdb_id = general_thread; + else + { + thread_ptr = all_threads.head; + gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); + } + + sprintf (own_buf, "QC%lx", gdb_id); return; } @@ -915,6 +1073,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (the_target->qxfer_osdata != NULL) strcat (own_buf, ";qXfer:osdata:read+"); + if (target_supports_non_stop ()) + strcat (own_buf, ";QNonStop+"); + return; } @@ -925,7 +1086,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) char *p = own_buf + 12; CORE_ADDR parts[2], address = 0; int i, err; - unsigned long ptid; + unsigned long ptid = 0; require_running (own_buf); @@ -1088,9 +1249,11 @@ handle_v_cont (char *own_buf) p++; if (p[0] == 's' || p[0] == 'S') - resume_info[i].step = 1; + resume_info[i].kind = resume_step; else if (p[0] == 'c' || p[0] == 'C') - resume_info[i].step = 0; + resume_info[i].kind = resume_continue; + else if (p[0] == 't') + resume_info[i].kind = resume_stop; else goto err; @@ -1145,20 +1308,29 @@ handle_v_cont (char *own_buf) resume_info[i] = default_action; /* Still used in occasional places in the backend. */ - if (n == 1 && resume_info[0].thread != -1) + if (n == 1 + && resume_info[0].thread != -1 + && resume_info[0].kind != resume_stop) cont_thread = resume_info[0].thread; else cont_thread = -1; set_desired_inferior (0); - enable_async_io (); + if (!non_stop) + enable_async_io (); + (*the_target->resume) (resume_info, n); free (resume_info); - last_ptid = mywait (&last_status, 1); - prepare_resume_reply (own_buf, last_ptid, &last_status); - disable_async_io (); + if (non_stop) + write_ok (own_buf); + else + { + last_ptid = mywait (&last_status, 0, 1); + prepare_resume_reply (own_buf, last_ptid, &last_status); + disable_async_io (); + } return; err: @@ -1181,7 +1353,17 @@ handle_v_attach (char *own_buf) library list. Avoids the "stopped by shared library event" notice on the GDB side. */ dlls_changed = 0; - prepare_resume_reply (own_buf, last_ptid, &last_status); + + if (non_stop) + { + /* In non-stop, we don't send a resume reply. Stop events + will follow up using the normal notification + mechanism. */ + write_ok (own_buf); + } + else + prepare_resume_reply (own_buf, last_ptid, &last_status); + return 1; } else @@ -1264,6 +1446,13 @@ handle_v_run (char *own_buf) if (last_status.kind == TARGET_WAITKIND_STOPPED) { prepare_resume_reply (own_buf, last_ptid, &last_status); + + /* In non-stop, sending a resume reply doesn't set the general + thread, but GDB assumes a vRun sets it (this is so GDB can + query which is the main thread of the new inferior. */ + if (non_stop) + general_thread = last_ptid; + return 1; } else @@ -1273,6 +1462,28 @@ handle_v_run (char *own_buf) } } +/* Handle a 'vStopped' packet. */ +static void +handle_v_stopped (char *own_buf) +{ + /* If we're waiting for GDB to acknowledge a pending stop reply, + consider that done. */ + if (notif_queue) + { + struct vstop_notif *head; + + if (remote_debug) + fprintf (stderr, "vStopped: acking %ld\n", notif_queue->ptid); + + head = notif_queue; + notif_queue = notif_queue->next; + free (head); + } + + /* Push another stop reply, or if there are no more left, an OK. */ + send_next_stop_reply (own_buf); +} + /* Handle all of the extended 'v' packets. */ void handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) @@ -1288,7 +1499,7 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) if (strncmp (own_buf, "vCont?", 6) == 0) { - strcpy (own_buf, "vCont;c;C;s;S"); + strcpy (own_buf, "vCont;c;C;s;S;t"); return; } } @@ -1321,12 +1532,21 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) return; } + if (strncmp (own_buf, "vStopped", 8) == 0) + { + handle_v_stopped (own_buf); + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; return; } +/* Resume inferior and wait for another event. In non-stop mode, + don't really wait here, but return immediatelly to the event + loop. */ void myresume (char *own_buf, int step, int sig) { @@ -1342,7 +1562,10 @@ myresume (char *own_buf, int step, int sig) { resume_info[0].thread = ((struct inferior_list_entry *) current_inferior)->id; - resume_info[0].step = step; + if (step) + resume_info[0].kind = resume_step; + else + resume_info[0].kind = resume_continue; resume_info[0].sig = sig; n++; } @@ -1350,16 +1573,39 @@ myresume (char *own_buf, int step, int sig) if (!valid_cont_thread) { resume_info[n].thread = -1; - resume_info[n].step = 0; + resume_info[n].kind = resume_continue; resume_info[n].sig = 0; n++; } - enable_async_io (); + if (!non_stop) + enable_async_io (); + (*the_target->resume) (resume_info, n); - last_ptid = mywait (&last_status, 1); - prepare_resume_reply (own_buf, last_ptid, &last_status); - disable_async_io (); + + if (non_stop) + write_ok (own_buf); + else + { + last_ptid = mywait (&last_status, 0, 1); + prepare_resume_reply (own_buf, last_ptid, &last_status); + disable_async_io (); + } +} + +/* Callback for for_each_inferior. Make a new stop reply for each + stopped thread. */ + +static void +queue_stop_reply_callback (struct inferior_list_entry *entry) +{ + struct target_waitstatus status; + + status.kind = TARGET_WAITKIND_STOPPED; + status.value.sig = TARGET_SIGNAL_TRAP; + + /* Pass the last stop reply back to GDB, but don't notify. */ + queue_stop_reply (entry->id, &status); } /* Status handler for the '?' packet. */ @@ -1367,11 +1613,32 @@ myresume (char *own_buf, int step, int sig) static void handle_status (char *own_buf) { - if (all_threads.head) - prepare_resume_reply (own_buf, - all_threads.head->id, &last_status); + struct target_waitstatus status; + status.kind = TARGET_WAITKIND_STOPPED; + status.value.sig = TARGET_SIGNAL_TRAP; + + /* In non-stop mode, we must send a stop reply for each stopped + thread. In all-stop mode, just send one for the first stopped + thread we find. */ + + if (non_stop) + { + discard_queued_stop_replies (); + for_each_inferior (&all_threads, queue_stop_reply_callback); + + /* The first is sent immediatly. OK is sent if there is no + stopped thread, which is the same handling of the vStopped + packet (by design). */ + send_next_stop_reply (own_buf); + } else - strcpy (own_buf, "W00"); + { + if (all_threads.head) + prepare_resume_reply (own_buf, + all_threads.head->id, &status); + else + strcpy (own_buf, "W00"); + } } static void @@ -1426,12 +1693,6 @@ gdbserver_show_disableable (FILE *stream) int main (int argc, char *argv[]) { - char ch, *own_buf; - unsigned char *mem_buf; - int i = 0; - int signal; - unsigned int len; - CORE_ADDR mem_addr; int bad_attach; int pid; char *arg_end, *port; @@ -1630,9 +1891,10 @@ main (int argc, char *argv[]) while (1) { noack_mode = 0; + non_stop = 0; + remote_open (port); - restart: if (setjmp (toplevel) != 0) { /* An error occurred. */ @@ -1643,373 +1905,433 @@ main (int argc, char *argv[]) } } - disable_async_io (); - while (!exit_requested) + /* Wait for events. This will return when all event sources are + removed from the event loop. */ + start_event_loop (); + + /* If an exit was requested (using the "monitor exit" command), + terminate now. The only other way to get here is for + getpkt to fail; close the connection and reopen it at the + top of the loop. */ + + if (exit_requested) { - unsigned char sig; - int packet_len; - int new_packet_len = -1; + if (attached) + detach_inferior (); + else + kill_inferior (); + exit (0); + } + else + fprintf (stderr, "Remote side has terminated connection. " + "GDBserver will reopen the connection.\n"); + } +} - response_needed = 0; - packet_len = getpkt (own_buf); - if (packet_len <= 0) - break; - response_needed = 1; +/* Event loop callback that handles a serial event. The first byte in + the serial buffer gets us here. We expect characters to arrive at + a brisk pace, so we read the rest of the packet with a blocking + getpkt call. */ - i = 0; - ch = own_buf[i++]; - switch (ch) +static void +process_serial_event (void) +{ + char ch; + int i = 0; + int signal; + unsigned int len; + CORE_ADDR mem_addr; + unsigned char sig; + int packet_len; + int new_packet_len = -1; + + /* Used to decide when gdbserver should exit in + multi-mode/remote. */ + static int have_ran = 0; + + if (!have_ran) + have_ran = target_running (); + + disable_async_io (); + + response_needed = 0; + packet_len = getpkt (own_buf); + if (packet_len <= 0) + { + target_async (0); + remote_close (); + return; + } + response_needed = 1; + + i = 0; + ch = own_buf[i++]; + switch (ch) + { + case 'q': + handle_query (own_buf, packet_len, &new_packet_len); + break; + case 'Q': + handle_general_set (own_buf); + break; + case 'D': + require_running (own_buf); + fprintf (stderr, "Detaching from inferior\n"); + if (detach_inferior () != 0) + write_enn (own_buf); + else + { + discard_queued_stop_replies (); + write_ok (own_buf); + + if (extended_protocol) { - case 'q': - handle_query (own_buf, packet_len, &new_packet_len); - break; - case 'Q': - handle_general_set (own_buf); - break; - case 'D': - require_running (own_buf); - fprintf (stderr, "Detaching from inferior\n"); - if (detach_inferior () != 0) - write_enn (own_buf); - else - { - write_ok (own_buf); - - if (extended_protocol) - { - /* Treat this like a normal program exit. */ - last_status.kind = TARGET_WAITKIND_EXITED; - last_status.value.integer = 0; - last_ptid = signal_pid; - } - else - { - putpkt (own_buf); - remote_close (); - - /* If we are attached, then we can exit. Otherwise, we - need to hang around doing nothing, until the child - is gone. */ - if (!attached) - join_inferior (); - - exit (0); - } - } - break; - case '!': - extended_protocol = 1; - write_ok (own_buf); - break; - case '?': - handle_status (own_buf); - break; - case 'H': - if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') - { - unsigned long gdb_id, thread_id; - - require_running (own_buf); - gdb_id = strtoul (&own_buf[2], NULL, 16); - if (gdb_id == 0 || gdb_id == -1) - thread_id = gdb_id; - else - { - thread_id = gdb_id_to_thread_id (gdb_id); - if (thread_id == 0) - { - write_enn (own_buf); - break; - } - } - - if (own_buf[1] == 'g') - { - general_thread = thread_id; - set_desired_inferior (1); - } - else if (own_buf[1] == 'c') - cont_thread = thread_id; - else if (own_buf[1] == 's') - step_thread = thread_id; - - write_ok (own_buf); - } - else - { - /* Silently ignore it so that gdb can extend the protocol - without compatibility headaches. */ - own_buf[0] = '\0'; - } - break; - case 'g': - require_running (own_buf); - set_desired_inferior (1); - registers_to_string (own_buf); - break; - case 'G': - require_running (own_buf); - set_desired_inferior (1); - registers_from_string (&own_buf[1]); - write_ok (own_buf); - break; - case 'm': - require_running (own_buf); - decode_m_packet (&own_buf[1], &mem_addr, &len); - if (read_inferior_memory (mem_addr, mem_buf, len) == 0) - convert_int_to_ascii (mem_buf, own_buf, len); - else - write_enn (own_buf); - break; - case 'M': - require_running (own_buf); - decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); - if (write_inferior_memory (mem_addr, mem_buf, len) == 0) - write_ok (own_buf); - else - write_enn (own_buf); - break; - case 'X': - require_running (own_buf); - if (decode_X_packet (&own_buf[1], packet_len - 1, - &mem_addr, &len, mem_buf) < 0 - || write_inferior_memory (mem_addr, mem_buf, len) != 0) - write_enn (own_buf); - else - write_ok (own_buf); - break; - case 'C': - require_running (own_buf); - convert_ascii_to_int (own_buf + 1, &sig, 1); - if (target_signal_to_host_p (sig)) - signal = target_signal_to_host (sig); - else - signal = 0; - myresume (own_buf, 0, signal); - break; - case 'S': - require_running (own_buf); - convert_ascii_to_int (own_buf + 1, &sig, 1); - if (target_signal_to_host_p (sig)) - signal = target_signal_to_host (sig); - else - signal = 0; - myresume (own_buf, 1, signal); - break; - case 'c': - require_running (own_buf); - signal = 0; - myresume (own_buf, 0, signal); - break; - case 's': - require_running (own_buf); - signal = 0; - myresume (own_buf, 1, signal); - break; - case 'Z': - { - char *lenptr; - char *dataptr; - CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); - int len = strtol (lenptr + 1, &dataptr, 16); - char type = own_buf[1]; - - if (the_target->insert_watchpoint == NULL - || (type < '2' || type > '4')) - { - /* No watchpoint support or not a watchpoint command; - unrecognized either way. */ - own_buf[0] = '\0'; - } - else - { - int res; - - require_running (own_buf); - res = (*the_target->insert_watchpoint) (type, addr, len); - if (res == 0) - write_ok (own_buf); - else if (res == 1) - /* Unsupported. */ - own_buf[0] = '\0'; - else - write_enn (own_buf); - } - break; - } - case 'z': - { - char *lenptr; - char *dataptr; - CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); - int len = strtol (lenptr + 1, &dataptr, 16); - char type = own_buf[1]; - - if (the_target->remove_watchpoint == NULL - || (type < '2' || type > '4')) - { - /* No watchpoint support or not a watchpoint command; - unrecognized either way. */ - own_buf[0] = '\0'; - } - else - { - int res; - - require_running (own_buf); - res = (*the_target->remove_watchpoint) (type, addr, len); - if (res == 0) - write_ok (own_buf); - else if (res == 1) - /* Unsupported. */ - own_buf[0] = '\0'; - else - write_enn (own_buf); - } - break; - } - case 'k': - response_needed = 0; - if (!target_running ()) - /* The packet we received doesn't make sense - but we - can't reply to it, either. */ - goto restart; - - fprintf (stderr, "Killing inferior\n"); - kill_inferior (); + /* Treat this like a normal program exit. */ + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.integer = 0; + last_ptid = signal_pid; - /* When using the extended protocol, we wait with no - program running. The traditional protocol will exit - instead. */ - if (extended_protocol) - { - last_status.kind = TARGET_WAITKIND_EXITED; - last_status.value.sig = TARGET_SIGNAL_KILL; - was_running = 0; - goto restart; - } - else + current_inferior = NULL; + } + else + { + putpkt (own_buf); + remote_close (); + + /* If we are attached, then we can exit. Otherwise, we + need to hang around doing nothing, until the child is + gone. */ + if (!attached) + join_inferior (); + + exit (0); + } + } + break; + case '!': + extended_protocol = 1; + write_ok (own_buf); + break; + case '?': + handle_status (own_buf); + break; + case 'H': + if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') + { + unsigned long gdb_id, thread_id; + + require_running (own_buf); + gdb_id = strtoul (&own_buf[2], NULL, 16); + if (gdb_id == 0 || gdb_id == -1) + thread_id = gdb_id; + else + { + thread_id = gdb_id_to_thread_id (gdb_id); + if (thread_id == 0) { - exit (0); - break; - } - case 'T': - { - unsigned long gdb_id, thread_id; - - require_running (own_buf); - gdb_id = strtoul (&own_buf[1], NULL, 16); - thread_id = gdb_id_to_thread_id (gdb_id); - if (thread_id == 0) - { - write_enn (own_buf); - break; - } - - if (mythread_alive (thread_id)) - write_ok (own_buf); - else write_enn (own_buf); - } - break; - case 'R': - response_needed = 0; - - /* Restarting the inferior is only supported in the - extended protocol. */ - if (extended_protocol) - { - if (target_running ()) - kill_inferior (); - fprintf (stderr, "GDBserver restarting\n"); - - /* Wait till we are at 1st instruction in prog. */ - if (program_argv != NULL) - start_inferior (program_argv); - else - { - last_status.kind = TARGET_WAITKIND_EXITED; - last_status.value.sig = TARGET_SIGNAL_KILL; - } - goto restart; - } - else - { - /* It is a request we don't understand. Respond with an - empty packet so that gdb knows that we don't support this - request. */ - own_buf[0] = '\0'; break; } - case 'v': - /* Extended (long) request. */ - handle_v_requests (own_buf, packet_len, &new_packet_len); - 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 - request. */ - own_buf[0] = '\0'; - break; } - if (new_packet_len != -1) - putpkt_binary (own_buf, new_packet_len); - else - putpkt (own_buf); - - response_needed = 0; - - if (was_running - && (last_status.kind == TARGET_WAITKIND_EXITED - || last_status.kind == TARGET_WAITKIND_SIGNALLED)) + if (own_buf[1] == 'g') { - was_running = 0; - - if (last_status.kind == TARGET_WAITKIND_EXITED) - fprintf (stderr, - "\nChild exited with status %d\n", - last_status.value.integer); - else if (last_status.kind == TARGET_WAITKIND_SIGNALLED) - fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", - target_signal_to_host (last_status.value.sig), - target_signal_to_name (last_status.value.sig)); - - if (extended_protocol) - goto restart; - else + if (thread_id == 0) { - fprintf (stderr, "GDBserver exiting\n"); - remote_close (); - exit (0); + /* GDB is telling us to choose any thread. Check if + the currently selected thread is still valid. If + it is not, select the first available. */ + struct thread_info *thread = + (struct thread_info *) find_inferior_id (&all_threads, + general_thread); + if (thread == NULL) + thread_id = all_threads.head->id; } + + general_thread = thread_id; + set_desired_inferior (1); } + else if (own_buf[1] == 'c') + cont_thread = thread_id; + else if (own_buf[1] == 's') + step_thread = thread_id; - if (last_status.kind != TARGET_WAITKIND_EXITED - && last_status.kind != TARGET_WAITKIND_SIGNALLED) - was_running = 1; + write_ok (own_buf); + } + else + { + /* Silently ignore it so that gdb can extend the protocol + without compatibility headaches. */ + own_buf[0] = '\0'; } + break; + case 'g': + require_running (own_buf); + set_desired_inferior (1); + registers_to_string (own_buf); + break; + case 'G': + require_running (own_buf); + set_desired_inferior (1); + registers_from_string (&own_buf[1]); + write_ok (own_buf); + break; + case 'm': + require_running (own_buf); + decode_m_packet (&own_buf[1], &mem_addr, &len); + if (read_inferior_memory (mem_addr, mem_buf, len) == 0) + convert_int_to_ascii (mem_buf, own_buf, len); + else + write_enn (own_buf); + break; + case 'M': + require_running (own_buf); + decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); + if (write_inferior_memory (mem_addr, mem_buf, len) == 0) + write_ok (own_buf); + else + write_enn (own_buf); + break; + case 'X': + require_running (own_buf); + if (decode_X_packet (&own_buf[1], packet_len - 1, + &mem_addr, &len, mem_buf) < 0 + || write_inferior_memory (mem_addr, mem_buf, len) != 0) + write_enn (own_buf); + else + write_ok (own_buf); + break; + case 'C': + require_running (own_buf); + convert_ascii_to_int (own_buf + 1, &sig, 1); + if (target_signal_to_host_p (sig)) + signal = target_signal_to_host (sig); + else + signal = 0; + myresume (own_buf, 0, signal); + break; + case 'S': + require_running (own_buf); + convert_ascii_to_int (own_buf + 1, &sig, 1); + if (target_signal_to_host_p (sig)) + signal = target_signal_to_host (sig); + else + signal = 0; + myresume (own_buf, 1, signal); + break; + case 'c': + require_running (own_buf); + signal = 0; + myresume (own_buf, 0, signal); + break; + case 's': + require_running (own_buf); + signal = 0; + myresume (own_buf, 1, signal); + break; + case 'Z': + { + char *lenptr; + char *dataptr; + CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); + int len = strtol (lenptr + 1, &dataptr, 16); + char type = own_buf[1]; + + if (the_target->insert_watchpoint == NULL + || (type < '2' || type > '4')) + { + /* No watchpoint support or not a watchpoint command; + unrecognized either way. */ + own_buf[0] = '\0'; + } + else + { + int res; + + require_running (own_buf); + res = (*the_target->insert_watchpoint) (type, addr, len); + if (res == 0) + write_ok (own_buf); + else if (res == 1) + /* Unsupported. */ + own_buf[0] = '\0'; + else + write_enn (own_buf); + } + break; + } + case 'z': + { + char *lenptr; + char *dataptr; + CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); + int len = strtol (lenptr + 1, &dataptr, 16); + char type = own_buf[1]; + + if (the_target->remove_watchpoint == NULL + || (type < '2' || type > '4')) + { + /* No watchpoint support or not a watchpoint command; + unrecognized either way. */ + own_buf[0] = '\0'; + } + else + { + int res; + + require_running (own_buf); + res = (*the_target->remove_watchpoint) (type, addr, len); + if (res == 0) + write_ok (own_buf); + else if (res == 1) + /* Unsupported. */ + own_buf[0] = '\0'; + else + write_enn (own_buf); + } + break; + } + case 'k': + response_needed = 0; + if (!target_running ()) + /* The packet we received doesn't make sense - but we + can't reply to it, either. */ + return; - /* If an exit was requested (using the "monitor exit" command), - terminate now. The only other way to get here is for - getpkt to fail; close the connection and reopen it at the - top of the loop. */ + fprintf (stderr, "Killing inferior\n"); + kill_inferior (); + discard_queued_stop_replies (); - if (exit_requested) + /* When using the extended protocol, we wait with no program + running. The traditional protocol will exit instead. */ + if (extended_protocol) + { + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.sig = TARGET_SIGNAL_KILL; + return; + } + else { - remote_close (); - if (attached && target_running ()) - detach_inferior (); - else if (target_running ()) - kill_inferior (); exit (0); + break; + } + case 'T': + { + unsigned long gdb_id, thread_id; + + require_running (own_buf); + gdb_id = strtoul (&own_buf[1], NULL, 16); + thread_id = gdb_id_to_thread_id (gdb_id); + if (thread_id == 0) + { + write_enn (own_buf); + break; + } + + if (mythread_alive (thread_id)) + write_ok (own_buf); + else + write_enn (own_buf); + } + break; + case 'R': + response_needed = 0; + + /* Restarting the inferior is only supported in the extended + protocol. */ + if (extended_protocol) + { + if (target_running ()) + { + kill_inferior (); + discard_queued_stop_replies (); + } + fprintf (stderr, "GDBserver restarting\n"); + + /* Wait till we are at 1st instruction in prog. */ + if (program_argv != NULL) + start_inferior (program_argv); + else + { + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.sig = TARGET_SIGNAL_KILL; + } + return; } else { - fprintf (stderr, "Remote side has terminated connection. " - "GDBserver will reopen the connection.\n"); + /* It is a request we don't understand. Respond with an + empty packet so that gdb knows that we don't support this + request. */ + own_buf[0] = '\0'; + break; + } + case 'v': + /* Extended (long) request. */ + handle_v_requests (own_buf, packet_len, &new_packet_len); + 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 + request. */ + own_buf[0] = '\0'; + break; + } + + if (new_packet_len != -1) + putpkt_binary (own_buf, new_packet_len); + else + putpkt (own_buf); + + response_needed = 0; + + if (!extended_protocol && have_ran && !target_running ()) + { + /* In non-stop, defer exiting until GDB had a chance to query + the whole vStopped list (until it gets an OK). */ + if (!notif_queue) + { + fprintf (stderr, "GDBserver exiting\n"); remote_close (); + exit (0); } } } + +/* Event-loop callback for serial events. */ + +void +handle_serial_event (int err, gdb_client_data client_data) +{ + if (debug_threads) + fprintf (stderr, "handling possible serial event\n"); + + /* Really handle it. */ + process_serial_event (); + + /* Be sure to not change the selected inferior behind GDB's back. + Important in the non-stop mode asynchronous protocol. */ + set_desired_inferior (1); +} + +/* Event-loop callback for target events. */ + +void +handle_target_event (int err, gdb_client_data client_data) +{ + if (debug_threads) + fprintf (stderr, "handling possible target event\n"); + + last_ptid = mywait (&last_status, TARGET_WNOHANG, 1); + + if (last_status.kind != TARGET_WAITKIND_IGNORE) + { + /* Something interesting. Tell GDB about it. */ + push_event (last_ptid, &last_status); + } + + /* Be sure to not change the selected inferior behind GDB's back. + Important in the non-stop mode asynchronous protocol. */ + set_desired_inferior (1); +} |