summaryrefslogtreecommitdiff
path: root/gdb/remote-notif.c
diff options
context:
space:
mode:
authorYao Qi <yao@codesourcery.com>2012-12-15 03:50:22 +0000
committerYao Qi <yao@codesourcery.com>2012-12-15 03:50:22 +0000
commit722247f1cb9ab5678ad078ba0ba02070cb03a34b (patch)
treea2f4c7ea5d329c76827c2b97ff93953609b8785e /gdb/remote-notif.c
parent5f4cf0bb27e5c49c5581c104e24770bde830af85 (diff)
gdb/
2012-12-15 Yao Qi <yao@codesourcery.com> * Makefile.in (REMOTE_OBS): Add "remote-notif.o". (SFILES): Add "remote-notif.c". (HFILES_NO_SRCDIR): Add "remote-notif.h" and "common/queue.h". * remote-notif.c: New. Factored out from remote.c. * remote-notif.h: New. * remote.c: Include "remote-notif.h". (stop_reply_xmalloc, do_stop_reply_xfree): (remote_parse_stop_reply, remote_get_pending_stop_replies): (remote_async_get_pending_events_handler): Remove declarations. (remote_parse_stop_reply): Declare. (pending_stop_reply): Remove. (remote_async_get_pending_events_token): Move to remote-notif.c. (remote_close): Replace 'delete_async_event_handler' with remote_notif_unregister_async_event_handler. Don't call discard_pending_stop_replies. (remote_start_remote): Replace code with remote_notif_parse and remote_notif_get_pending_replies. (remote_open_1): Replace 'create_async_event_handler' with remote_notif_register_async_event_handler. (extended_remote_attach_1): Call remote_notif_parse and notif_stop_reply_push. (struct stop_reply) <next>: Remove. <base>: New field. Callers update. (stop_reply_queue): Change its type. (stop_reply_xmalloc, do_stop_reply_xfree): Remove. (remote_notif_remove_all): New. (discard_pending_stop_replies): Update. (remote_notif_stop_ack, stop_reply_dtr): New. (remote_notif_stop_alloc_event): New. (notif_client_stop): New variable. (stop_reply_match_ptid, stop_reply_match_ptid_and_ws: New. (queued_stop_reply, peek_stop_reply): Adjust. (remote_get_pending_stop_replies): Rename to remote_notif_get_pending_events. (handle_notification): Move to remote-notif.c. (remote_async_get_pending_events_handler): Likewise. (remote_wait_as): Adjust to call remote_notif_parse. Call 'getpkt_or_notif_sane' instead of 'getpkt_sane'. Return minus_one_ptid early if gets a notification. (remote_wait): Call QUEUE_is_empty (notif_reply_p). (_initialize_remote): Call QUEUE_alloc. Update caller. (remote_resume): Call 'remote_notif_process' in all-stop mode. * remote.h: Include "remote-notif.h". (remote_notif_get_pending_replies): Declare.
Diffstat (limited to 'gdb/remote-notif.c')
-rw-r--r--gdb/remote-notif.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/gdb/remote-notif.c b/gdb/remote-notif.c
new file mode 100644
index 0000000000..5a72f40813
--- /dev/null
+++ b/gdb/remote-notif.c
@@ -0,0 +1,270 @@
+/* Remote notification in GDB protocol
+
+ Copyright (C) 1988-2012 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 <string.h>
+
+unsigned int notif_debug = 0;
+
+/* Supported clients of notifications. */
+
+static struct notif_client *notifs[] =
+{
+ &notif_client_stop,
+};
+
+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;
+}
+
+DECLARE_QUEUE_P (notif_client_p);
+DEFINE_QUEUE_P (notif_client_p);
+
+static QUEUE(notif_client_p) *notif_queue;
+
+/* Process notifications one by one. EXCEPT is not expected in
+ the queue. */
+
+void
+remote_notif_process (struct notif_client *except)
+{
+ while (!QUEUE_is_empty (notif_client_p, notif_queue))
+ {
+ struct notif_client *nc = QUEUE_deque (notif_client_p,
+ 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 (NULL);
+}
+
+/* Asynchronous signal handle registered as event loop source for when
+ the remote sent us a notification. The registered callback
+ will do a ACK sequence to pull the rest of the events out of
+ the remote side into our event queue. */
+
+static struct async_event_handler *remote_async_get_pending_events_token;
+
+/* Register async_event_handler for notification. */
+
+void
+remote_notif_register_async_event_handler (void)
+{
+ remote_async_get_pending_events_token
+ = create_async_event_handler (remote_async_get_pending_events_handler,
+ NULL);
+}
+
+/* Unregister async_event_handler for notification. */
+
+void
+remote_notif_unregister_async_event_handler (void)
+{
+ if (remote_async_get_pending_events_token)
+ delete_async_event_handler (&remote_async_get_pending_events_token);
+}
+
+/* Remote notification handler. */
+
+void
+handle_notification (char *buf)
+{
+ struct notif_client *nc = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE (notifs); i++)
+ {
+ nc = notifs[i];
+ if (strncmp (buf, nc->name, strlen (nc->name)) == 0
+ && buf[strlen (nc->name)] == ':')
+ break;
+ }
+
+ /* We ignore notifications we don't recognize, for compatibility
+ with newer stubs. */
+ if (nc == NULL)
+ return;
+
+ if (nc->pending_event)
+ {
+ /* 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. */
+ nc->pending_event = 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, 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 (remote_async_get_pending_events_token);
+ }
+
+ if (notif_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "notif: Notification '%s' captured\n",
+ nc->name);
+ }
+}
+
+/* Cleanup wrapper. */
+
+static void
+do_notif_event_xfree (void *arg)
+{
+ struct notif_event *event = arg;
+
+ if (event && event->dtr)
+ event->dtr (event);
+
+ xfree (event);
+}
+
+static void
+notif_xfree (struct notif_client *notif)
+{
+ if (notif->pending_event != NULL
+ && notif->pending_event->dtr != NULL)
+ notif->pending_event->dtr (notif->pending_event);
+
+ xfree (notif->pending_event);
+ xfree (notif);
+}
+
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_notif;
+
+void
+_initialize_notif (void)
+{
+ notif_queue = QUEUE_alloc (notif_client_p, notif_xfree);
+}