Index: gdb/remote-notif.c |
diff --git a/gdb/remote-notif.c b/gdb/remote-notif.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..163979d211108687f0c779c6889c2ac9b57e8009 |
--- /dev/null |
+++ b/gdb/remote-notif.c |
@@ -0,0 +1,289 @@ |
+/* Remote notification in GDB protocol |
+ |
+ Copyright (C) 1988-2013 Free Software Foundation, Inc. |
+ |
+ This file is part of GDB. |
+ |
+ This program is free software; you can redistribute it and/or modify |
+ it under the terms of the GNU General Public License as published by |
+ the Free Software Foundation; either version 3 of the License, or |
+ (at your option) any later version. |
+ |
+ This program is distributed in the hope that it will be useful, |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ GNU General Public License for more details. |
+ |
+ You should have received a copy of the GNU General Public License |
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
+ |
+/* Remote async notification is sent from remote target over RSP. |
+ Each type of notification is represented by an object of |
+ 'struct notif', which has a field 'pending_reply'. It is not |
+ NULL when GDB receives a notification from GDBserver, but hasn't |
+ acknowledge yet. Before GDB acknowledges the notification, |
+ GDBserver shouldn't send notification again (see the header comments |
+ in gdbserver/notif.c). |
+ |
+ Notifications are processed in an almost-unified approach for both |
+ all-stop mode and non-stop mode, except the timing to process them. |
+ In non-stop mode, notifications are processed in |
+ remote_async_get_pending_events_handler, while in all-stop mode, |
+ they are processed in remote_resume. */ |
+ |
+#include "defs.h" |
+#include "remote.h" |
+#include "remote-notif.h" |
+#include "observer.h" |
+#include "event-loop.h" |
+#include "target.h" |
+#include "inferior.h" |
+#include "gdbcmd.h" |
+ |
+#include <string.h> |
+ |
+int notif_debug = 0; |
+ |
+/* Supported clients of notifications. */ |
+ |
+static struct notif_client *notifs[] = |
+{ |
+ ¬if_client_stop, |
+}; |
+ |
+gdb_static_assert (ARRAY_SIZE (notifs) == REMOTE_NOTIF_LAST); |
+ |
+static void do_notif_event_xfree (void *arg); |
+ |
+/* Parse the BUF for the expected notification NC, and send packet to |
+ acknowledge. */ |
+ |
+void |
+remote_notif_ack (struct notif_client *nc, char *buf) |
+{ |
+ struct notif_event *event = nc->alloc_event (); |
+ struct cleanup *old_chain |
+ = make_cleanup (do_notif_event_xfree, event); |
+ |
+ if (notif_debug) |
+ fprintf_unfiltered (gdb_stdlog, "notif: ack '%s'\n", |
+ nc->ack_command); |
+ |
+ nc->parse (nc, buf, event); |
+ nc->ack (nc, buf, event); |
+ |
+ discard_cleanups (old_chain); |
+} |
+ |
+/* Parse the BUF for the expected notification NC. */ |
+ |
+struct notif_event * |
+remote_notif_parse (struct notif_client *nc, char *buf) |
+{ |
+ struct notif_event *event = nc->alloc_event (); |
+ struct cleanup *old_chain |
+ = make_cleanup (do_notif_event_xfree, event); |
+ |
+ if (notif_debug) |
+ fprintf_unfiltered (gdb_stdlog, "notif: parse '%s'\n", nc->name); |
+ |
+ nc->parse (nc, buf, event); |
+ |
+ discard_cleanups (old_chain); |
+ return event; |
+} |
+ |
+DEFINE_QUEUE_P (notif_client_p); |
+ |
+/* Process notifications in STATE's notification queue one by one. |
+ EXCEPT is not expected in the queue. */ |
+ |
+void |
+remote_notif_process (struct remote_notif_state *state, |
+ struct notif_client *except) |
+{ |
+ while (!QUEUE_is_empty (notif_client_p, state->notif_queue)) |
+ { |
+ struct notif_client *nc = QUEUE_deque (notif_client_p, |
+ state->notif_queue); |
+ |
+ gdb_assert (nc != except); |
+ |
+ if (nc->can_get_pending_events (nc)) |
+ remote_notif_get_pending_events (nc); |
+ } |
+} |
+ |
+static void |
+remote_async_get_pending_events_handler (gdb_client_data data) |
+{ |
+ gdb_assert (non_stop); |
+ remote_notif_process (data, NULL); |
+} |
+ |
+/* Remote notification handler. Parse BUF, queue notification and |
+ update STATE. */ |
+ |
+void |
+handle_notification (struct remote_notif_state *state, char *buf) |
+{ |
+ struct notif_client *nc; |
+ size_t i; |
+ |
+ for (i = 0; i < ARRAY_SIZE (notifs); i++) |
+ { |
+ const char *name = notifs[i]->name; |
+ |
+ if (strncmp (buf, name, strlen (name)) == 0 |
+ && buf[strlen (name)] == ':') |
+ break; |
+ } |
+ |
+ /* We ignore notifications we don't recognize, for compatibility |
+ with newer stubs. */ |
+ if (i == ARRAY_SIZE (notifs)) |
+ return; |
+ |
+ nc = notifs[i]; |
+ |
+ if (state->pending_event[nc->id] != NULL) |
+ { |
+ /* We've already parsed the in-flight reply, but the stub for some |
+ reason thought we didn't, possibly due to timeout on its side. |
+ Just ignore it. */ |
+ if (notif_debug) |
+ fprintf_unfiltered (gdb_stdlog, |
+ "notif: ignoring resent notification\n"); |
+ } |
+ else |
+ { |
+ struct notif_event *event |
+ = remote_notif_parse (nc, buf + strlen (nc->name) + 1); |
+ |
+ /* Be careful to only set it after parsing, since an error |
+ may be thrown then. */ |
+ state->pending_event[nc->id] = event; |
+ |
+ /* Notify the event loop there's a stop reply to acknowledge |
+ and that there may be more events to fetch. */ |
+ QUEUE_enque (notif_client_p, state->notif_queue, nc); |
+ if (non_stop) |
+ { |
+ /* In non-stop, We mark REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN |
+ in order to go on what we were doing and postpone |
+ querying notification events to some point safe to do so. |
+ See details in the function comment of |
+ remote.c:remote_notif_get_pending_events. |
+ |
+ In all-stop, GDB may be blocked to wait for the reply, we |
+ shouldn't return to event loop until the expected reply |
+ arrives. For example: |
+ |
+ 1.1) --> vCont;c |
+ GDB expects getting stop reply 'T05 thread:2'. |
+ 1.2) <-- %Notif |
+ <GDB marks the REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN> |
+ |
+ After step #1.2, we return to the event loop, which |
+ notices there is a new event on the |
+ REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN and calls the |
+ handler, which will send 'vNotif' packet. |
+ 1.3) --> vNotif |
+ It is not safe to start a new sequence, because target |
+ is still running and GDB is expecting the stop reply |
+ from stub. |
+ |
+ To solve this, whenever we parse a notification |
+ successfully, we don't mark the |
+ REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN and let GDB blocked |
+ there as before to get the sequence done. |
+ |
+ 2.1) --> vCont;c |
+ GDB expects getting stop reply 'T05 thread:2' |
+ 2.2) <-- %Notif |
+ <Don't mark the REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN> |
+ 2.3) <-- T05 thread:2 |
+ |
+ These pending notifications can be processed later. */ |
+ mark_async_event_handler (state->get_pending_events_token); |
+ } |
+ |
+ if (notif_debug) |
+ fprintf_unfiltered (gdb_stdlog, |
+ "notif: Notification '%s' captured\n", |
+ nc->name); |
+ } |
+} |
+ |
+/* Invoke destructor of EVENT and xfree it. */ |
+ |
+void |
+notif_event_xfree (struct notif_event *event) |
+{ |
+ if (event != NULL && event->dtr != NULL) |
+ event->dtr (event); |
+ |
+ xfree (event); |
+} |
+ |
+/* Cleanup wrapper. */ |
+ |
+static void |
+do_notif_event_xfree (void *arg) |
+{ |
+ notif_event_xfree (arg); |
+} |
+ |
+/* Return an allocated remote_notif_state. */ |
+ |
+struct remote_notif_state * |
+remote_notif_state_allocate (void) |
+{ |
+ struct remote_notif_state *notif_state = xzalloc (sizeof (*notif_state)); |
+ |
+ notif_state->notif_queue = QUEUE_alloc (notif_client_p, NULL); |
+ |
+ /* Register async_event_handler for notification. */ |
+ |
+ notif_state->get_pending_events_token |
+ = create_async_event_handler (remote_async_get_pending_events_handler, |
+ notif_state); |
+ |
+ return notif_state; |
+} |
+ |
+/* Free STATE and its fields. */ |
+ |
+void |
+remote_notif_state_xfree (struct remote_notif_state *state) |
+{ |
+ int i; |
+ |
+ QUEUE_free (notif_client_p, state->notif_queue); |
+ |
+ /* Unregister async_event_handler for notification. */ |
+ if (state->get_pending_events_token != NULL) |
+ delete_async_event_handler (&state->get_pending_events_token); |
+ |
+ for (i = 0; i < REMOTE_NOTIF_LAST; i++) |
+ notif_event_xfree (state->pending_event[i]); |
+ |
+ xfree (state); |
+} |
+ |
+/* -Wmissing-prototypes */ |
+extern initialize_file_ftype _initialize_notif; |
+ |
+void |
+_initialize_notif (void) |
+{ |
+ add_setshow_boolean_cmd ("notification", no_class, ¬if_debug, |
+ _("\ |
+Set debugging of async remote notification."), _("\ |
+Show debugging of async remote notification."), _("\ |
+When non-zero, debugging output about async remote notifications" |
+" is enabled."), |
+ NULL, |
+ NULL, |
+ &setdebuglist, &showdebuglist); |
+} |