From 722247f1cb9ab5678ad078ba0ba02070cb03a34b Mon Sep 17 00:00:00 2001 From: Yao Qi Date: Sat, 15 Dec 2012 03:50:22 +0000 Subject: [PATCH] gdb/ 2012-12-15 Yao Qi * 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) : Remove. : 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. --- gdb/ChangeLog | 49 +++++ gdb/Makefile.in | 9 +- gdb/remote-notif.c | 270 ++++++++++++++++++++++++++ gdb/remote-notif.h | 85 +++++++++ gdb/remote.c | 462 ++++++++++++++++++++++++--------------------- gdb/remote.h | 3 + 6 files changed, 657 insertions(+), 221 deletions(-) create mode 100644 gdb/remote-notif.c create mode 100644 gdb/remote-notif.h diff --git a/gdb/ChangeLog b/gdb/ChangeLog index cd28232223..e98564575f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,52 @@ +2012-12-15 Yao Qi + + * 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) : Remove. + : 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. + 2012-12-15 Yao Qi * remote.c (discard_pending_stop_replies): Update declaration. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 22f2acbaa9..8e7b589deb 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -507,7 +507,8 @@ SER_HARDWIRE = @SER_HARDWIRE@ # The `remote' debugging target is supported for most architectures, # but not all (e.g. 960) -REMOTE_OBS = remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o remote-fileio.o +REMOTE_OBS = remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o remote-fileio.o \ + remote-notif.o # This is remote-sim.o if a simulator is to be linked in. SIM_OBS = @SIM_OBS@ @@ -729,7 +730,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c printcmd.c \ proc-service.list progspace.c \ prologue-value.c psymtab.c \ - regcache.c reggroups.c remote.c remote-fileio.c reverse.c \ + regcache.c reggroups.c remote.c remote-fileio.c remote-notif.c reverse.c \ sentinel-frame.c \ serial.c ser-base.c ser-unix.c skip.c \ solib.c solib-target.c source.c \ @@ -774,7 +775,7 @@ c-lang.h d-lang.h go-lang.h frame.h event-loop.h block.h cli/cli-setshow.h \ cli/cli-decode.h cli/cli-cmds.h cli/cli-dump.h cli/cli-utils.h \ cli/cli-script.h macrotab.h symtab.h version.h \ gnulib/import/string.in.h gnulib/import/str-two-way.h \ -gnulib/import/stdint.in.h remote.h gdb.h sparc-nat.h \ +gnulib/import/stdint.in.h remote.h remote-notif.h gdb.h sparc-nat.h \ gdbthread.h dwarf2-frame.h dwarf2-frame-tailcall.h nbsd-nat.h dcache.h \ amd64-nat.h s390-tdep.h arm-linux-tdep.h exceptions.h macroscope.h \ gdbarch.h bsd-uthread.h common/gdb_stat.h memory-map.h memrange.h \ @@ -826,7 +827,7 @@ gnulib/import/extra/snippet/arg-nonnull.h gnulib/import/extra/snippet/c++defs.h gnulib/import/extra/snippet/warn-on-use.h \ gnulib/import/stddef.in.h gnulib/import/inttypes.in.h inline-frame.h skip.h \ common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \ -common/format.h common/host-defs.h utils.h \ +common/format.h common/host-defs.h utils.h common/queue.h \ common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h gdb_bfd.h # Header files that already have srcdir in them, or which are in objdir. 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 . */ + +/* 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 + +unsigned int notif_debug = 0; + +/* Supported clients of notifications. */ + +static struct notif_client *notifs[] = +{ + ¬if_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 + + + 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 + + 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); +} diff --git a/gdb/remote-notif.h b/gdb/remote-notif.h new file mode 100644 index 0000000000..92c4bb611b --- /dev/null +++ b/gdb/remote-notif.h @@ -0,0 +1,85 @@ +/* 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 . */ + +#ifndef REMOTE_NOTIF_H +#define REMOTE_NOTIF_H + +#include "queue.h" + +/* An event of a type of async remote notification. */ + +struct notif_event +{ + /* Destructor. Release everything from SELF, but not SELF + itself. */ + void (*dtr) (struct notif_event *self); +}; + +/* A client to a sort of async remote notification. */ + +typedef struct notif_client +{ + /* The name of notification packet. */ + const char *name; + + /* The packet to acknowledge a previous reply. */ + const char *ack_command; + + /* Parse BUF to get the expected event and update EVENT. This + function may throw exception if contents in BUF is not the + expected event. */ + void (*parse) (struct notif_client *self, char *buf, + struct notif_event *event); + + /* Send field to remote, and do some checking. If + something wrong, throw an exception. */ + void (*ack) (struct notif_client *self, char *buf, + struct notif_event *event); + + /* Check this notification client can get pending events in + 'remote_notif_process'. */ + int (*can_get_pending_events) (struct notif_client *self); + + /* Allocate an event. */ + struct notif_event *(*alloc_event) (void); + + /* One pending event. This is where we keep it until it is + acknowledged. When there is a notification packet, parse it, + and create an object of 'struct notif_event' to assign to + it. This field is unchanged until GDB starts to ack this + notification (which is done by + remote.c:remote_notif_pending_replies). */ + struct notif_event *pending_event; +} *notif_client_p; + +void remote_notif_ack (struct notif_client *nc, char *buf); +struct notif_event *remote_notif_parse (struct notif_client *nc, + char *buf); + +void handle_notification (char *buf); + +void remote_notif_register_async_event_handler (void); +void remote_notif_unregister_async_event_handler (void); + +void remote_notif_process (struct notif_client *except); +extern struct notif_client notif_client_stop; + +extern unsigned int notif_debug; + +#endif /* REMOTE_NOTIF_H */ diff --git a/gdb/remote.c b/gdb/remote.c index 3f4436728e..a6a6227c61 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -34,6 +34,7 @@ #include "gdb-stabs.h" #include "gdbthread.h" #include "remote.h" +#include "remote-notif.h" #include "regcache.h" #include "value.h" #include "gdb_assert.h" @@ -223,17 +224,13 @@ static void remote_check_symbols (struct objfile *objfile); void _initialize_remote (void); struct stop_reply; -static struct stop_reply *stop_reply_xmalloc (void); static void stop_reply_xfree (struct stop_reply *); -static void do_stop_reply_xfree (void *arg); -static void remote_parse_stop_reply (char *buf, struct stop_reply *); +static void remote_parse_stop_reply (char *, struct stop_reply *); static void push_stop_reply (struct stop_reply *); -static void remote_get_pending_stop_replies (void); static void discard_pending_stop_replies (struct inferior *); static int peek_stop_reply (ptid_t ptid); static void remote_async_inferior_event_handler (gdb_client_data); -static void remote_async_get_pending_events_handler (gdb_client_data); static void remote_terminal_ours (void); @@ -245,11 +242,6 @@ static int remote_supports_cond_breakpoints (void); static int remote_can_run_breakpoint_commands (void); -/* The non-stop remote protocol provisions for one pending stop reply. - This is where we keep it until it is acknowledged. */ - -static struct stop_reply *pending_stop_reply = NULL; - /* For "remote". */ static struct cmd_list_element *remote_cmdlist; @@ -1402,12 +1394,6 @@ static struct async_signal_handler *sigint_remote_token; static struct async_event_handler *remote_async_inferior_event_token; -/* Asynchronous signal handle registered as event loop source for when - the remote sent us a %Stop notification. The registered callback - will do a vStopped 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; static ptid_t magic_null_ptid; @@ -3030,8 +3016,8 @@ remote_close (int quitting) if (remote_async_inferior_event_token) delete_async_event_handler (&remote_async_inferior_event_token); - if (remote_async_get_pending_events_token) - delete_async_event_handler (&remote_async_get_pending_events_token); + + remote_notif_unregister_async_event_handler (); } /* Query the remote side for the text, data and bss offsets. */ @@ -3453,19 +3439,13 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p) mechanism. */ if (strcmp (rs->buf, "OK") != 0) { - struct stop_reply *stop_reply; - struct cleanup *old_chain; + struct notif_client *notif = ¬if_client_stop; - stop_reply = stop_reply_xmalloc (); - old_chain = make_cleanup (do_stop_reply_xfree, stop_reply); - - remote_parse_stop_reply (rs->buf, stop_reply); - discard_cleanups (old_chain); - - /* get_pending_stop_replies acks this one, and gets the rest - out. */ - pending_stop_reply = stop_reply; - remote_get_pending_stop_replies (); + /* remote_notif_get_pending_replies acks this one, and gets + the rest out. */ + notif_client_stop.pending_event + = remote_notif_parse (notif, rs->buf); + remote_notif_get_pending_events (notif); /* Make sure that threads that were stopped remain stopped. */ @@ -4225,9 +4205,7 @@ remote_open_1 (char *name, int from_tty, remote_async_inferior_event_token = create_async_event_handler (remote_async_inferior_event_handler, NULL); - remote_async_get_pending_events_token - = create_async_event_handler (remote_async_get_pending_events_handler, - NULL); + remote_notif_register_async_event_handler (); /* Reset the target state; these things will be queried either by remote_query_supported or as they are needed. */ @@ -4482,14 +4460,10 @@ extended_remote_attach_1 (struct target_ops *target, char *args, int from_tty) if (target_can_async_p ()) { - struct stop_reply *stop_reply; - struct cleanup *old_chain; + struct notif_event *reply + = remote_notif_parse (¬if_client_stop, wait_status); - stop_reply = stop_reply_xmalloc (); - old_chain = make_cleanup (do_stop_reply_xfree, stop_reply); - remote_parse_stop_reply (wait_status, stop_reply); - discard_cleanups (old_chain); - push_stop_reply (stop_reply); + push_stop_reply ((struct stop_reply *) reply); target_async (inferior_event_handler, 0); } @@ -4783,6 +4757,15 @@ remote_resume (struct target_ops *ops, struct remote_state *rs = get_remote_state (); char *buf; + /* In all-stop, we can't mark REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN + (explained in remote-notif.c:handle_notification) so + remote_notif_process is not called. We need find a place where + it is safe to start a 'vNotif' sequence. It is good to do it + before resuming inferior, because inferior was stopped and no RSP + traffic at that moment. */ + if (!non_stop) + remote_notif_process (¬if_client_stop); + last_sent_signal = siggnal; last_sent_step = step; @@ -5116,10 +5099,11 @@ typedef struct cached_reg DEF_VEC_O(cached_reg_t); -struct stop_reply +typedef struct stop_reply { - struct stop_reply *next; + struct notif_event base; + /* The identifier of the thread about this event */ ptid_t ptid; struct target_waitstatus ws; @@ -5137,19 +5121,18 @@ struct stop_reply int replay_event; int core; -}; - -/* The list of already fetched and acknowledged stop events. */ -static struct stop_reply *stop_reply_queue; - -static struct stop_reply * -stop_reply_xmalloc (void) -{ - struct stop_reply *r = XMALLOC (struct stop_reply); +} *stop_reply_p; - r->next = NULL; - return r; -} +DECLARE_QUEUE_P (stop_reply_p); +DEFINE_QUEUE_P (stop_reply_p); +/* The list of already fetched and acknowledged stop events. This + queue is used for notification Stop, and other notifications + don't need queue for their events, because the notification events + of Stop can't be consumed immediately, so that events should be + queued first, and be consumed by remote_wait_{ns,as} one per + time. Other notifications can consume their events immediately, + so queue is not needed for them. */ +static QUEUE (stop_reply_p) *stop_reply_queue; static void stop_reply_xfree (struct stop_reply *r) @@ -5161,51 +5144,169 @@ stop_reply_xfree (struct stop_reply *r) } } +static void +remote_notif_stop_parse (struct notif_client *self, char *buf, + struct notif_event *event) +{ + remote_parse_stop_reply (buf, (struct stop_reply *) event); +} + +static void +remote_notif_stop_ack (struct notif_client *self, char *buf, + struct notif_event *event) +{ + struct stop_reply *stop_reply = (struct stop_reply *) event; + + /* acknowledge */ + putpkt ((char *) self->ack_command); + + if (stop_reply->ws.kind == TARGET_WAITKIND_IGNORE) + /* We got an unknown stop reply. */ + error (_("Unknown stop reply")); + + push_stop_reply (stop_reply); +} + +static int +remote_notif_stop_can_get_pending_events (struct notif_client *self) +{ + /* We can't get pending events in remote_notif_process for + notification stop, and we have to do this in remote_wait_ns + instead. If we fetch all queued events from stub, remote stub + may exit and we have no chance to process them back in + remote_wait_ns. */ + mark_async_event_handler (remote_async_inferior_event_token); + return 0; +} + +static void +stop_reply_dtr (struct notif_event *event) +{ + struct stop_reply *r = (struct stop_reply *) event; + + VEC_free (cached_reg_t, r->regcache); +} + +static struct notif_event * +remote_notif_stop_alloc_reply (void) +{ + struct notif_event *r + = (struct notif_event *) XMALLOC (struct stop_reply); + + r->dtr = stop_reply_dtr; + + return r; +} + +/* A client of notification Stop. */ + +struct notif_client notif_client_stop = +{ + "Stop", + "vStopped", + remote_notif_stop_parse, + remote_notif_stop_ack, + remote_notif_stop_can_get_pending_events, + remote_notif_stop_alloc_reply, + NULL, +}; + +/* A parameter to pass data in and out. */ + +struct queue_iter_param +{ + void *input; + struct stop_reply *output; +}; + +/* Remove all queue elements meet the condition it checks. */ + +static int +remote_notif_remove_all (QUEUE (stop_reply_p) *q, + QUEUE_ITER (stop_reply_p) *iter, + stop_reply_p event, + void *data) +{ + struct queue_iter_param *param = data; + struct inferior *inf = param->input; + + if (inf == NULL || ptid_get_pid (event->ptid) == inf->pid) + { + stop_reply_xfree (event); + QUEUE_remove_elem (stop_reply_p, q, iter); + } + + return 1; +} + /* Discard all pending stop replies of inferior INF. If INF is NULL, discard everything. */ static void discard_pending_stop_replies (struct inferior *inf) { - struct stop_reply *prev = NULL, *reply, *next; + int i; + struct queue_iter_param param; + struct stop_reply *reply + = (struct stop_reply *) notif_client_stop.pending_event; /* Discard the in-flight notification. */ - if (pending_stop_reply != NULL + if (reply != NULL && (inf == NULL - || ptid_get_pid (pending_stop_reply->ptid) == inf->pid)) + || ptid_get_pid (reply->ptid) == inf->pid)) { - stop_reply_xfree (pending_stop_reply); - pending_stop_reply = NULL; + stop_reply_xfree (reply); + notif_client_stop.pending_event = NULL; } + param.input = inf; + param.output = NULL; /* Discard the stop replies we have already pulled with vStopped. */ - for (reply = stop_reply_queue; reply; reply = next) - { - next = reply->next; - if (inf == NULL - || ptid_get_pid (reply->ptid) == inf->pid) - { - if (reply == stop_reply_queue) - stop_reply_queue = reply->next; - else - prev->next = reply->next; + QUEUE_iterate (stop_reply_p, stop_reply_queue, + remote_notif_remove_all, ¶m); +} - stop_reply_xfree (reply); - } - else - prev = reply; +/* A parameter to pass data in and out. */ + +static int +remote_notif_remove_once_on_match (QUEUE (stop_reply_p) *q, + QUEUE_ITER (stop_reply_p) *iter, + stop_reply_p event, + void *data) +{ + struct queue_iter_param *param = data; + ptid_t *ptid = param->input; + + if (ptid_match (event->ptid, *ptid)) + { + param->output = event; + QUEUE_remove_elem (stop_reply_p, q, iter); + return 0; } + + return 1; } -/* Cleanup wrapper. */ +/* Remove the first reply in 'stop_reply_queue' which matches + PTID. */ -static void -do_stop_reply_xfree (void *arg) +static struct stop_reply * +remote_notif_remove_queued_reply (ptid_t ptid) { - struct stop_reply *r = arg; + struct queue_iter_param param; + + param.input = &ptid; + param.output = NULL; + + QUEUE_iterate (stop_reply_p, stop_reply_queue, + remote_notif_remove_once_on_match, ¶m); + if (notif_debug) + fprintf_unfiltered (gdb_stdlog, + "notif: discard queued event: 'Stop' in %s\n", + target_pid_to_str (ptid)); - stop_reply_xfree (r); + return param.output; } /* Look for a queued stop reply belonging to PTID. If one is found, @@ -5216,29 +5317,13 @@ do_stop_reply_xfree (void *arg) static struct stop_reply * queued_stop_reply (ptid_t ptid) { - struct stop_reply *it; - struct stop_reply **it_link; - - it = stop_reply_queue; - it_link = &stop_reply_queue; - while (it) - { - if (ptid_match (it->ptid, ptid)) - { - *it_link = it->next; - it->next = NULL; - break; - } - - it_link = &it->next; - it = *it_link; - } + struct stop_reply *r = remote_notif_remove_queued_reply (ptid); - if (stop_reply_queue) + if (!QUEUE_is_empty (stop_reply_p, stop_reply_queue)) /* There's still at least an event left. */ mark_async_event_handler (remote_async_inferior_event_token); - return it; + return r; } /* Push a fully parsed stop reply in the stop reply queue. Since we @@ -5248,38 +5333,37 @@ queued_stop_reply (ptid_t ptid) static void push_stop_reply (struct stop_reply *new_event) { - struct stop_reply *event; - - if (stop_reply_queue) - { - for (event = stop_reply_queue; - event && event->next; - event = event->next) - ; + QUEUE_enque (stop_reply_p, stop_reply_queue, new_event); - event->next = new_event; - } - else - stop_reply_queue = new_event; + if (notif_debug) + fprintf_unfiltered (gdb_stdlog, + "notif: push 'Stop' %s to queue %d\n", + target_pid_to_str (new_event->ptid), + QUEUE_length (stop_reply_p, + stop_reply_queue)); mark_async_event_handler (remote_async_inferior_event_token); } +static int +stop_reply_match_ptid_and_ws (QUEUE (stop_reply_p) *q, + QUEUE_ITER (stop_reply_p) *iter, + struct stop_reply *event, + void *data) +{ + ptid_t *ptid = data; + + return !(ptid_equal (*ptid, event->ptid) + && event->ws.kind == TARGET_WAITKIND_STOPPED); +} + /* Returns true if we have a stop reply for PTID. */ static int peek_stop_reply (ptid_t ptid) { - struct stop_reply *it; - - for (it = stop_reply_queue; it; it = it->next) - if (ptid_equal (ptid, it->ptid)) - { - if (it->ws.kind == TARGET_WAITKIND_STOPPED) - return 1; - } - - return 0; + return !QUEUE_iterate (stop_reply_p, stop_reply_queue, + stop_reply_match_ptid_and_ws, &ptid); } /* Parse the stop reply in BUF. Either the function succeeds, and the @@ -5495,13 +5579,14 @@ Packet: '%s'\n"), error (_("No process or thread specified in stop reply: %s"), buf); } -/* When the stub wants to tell GDB about a new stop reply, it sends a - stop notification (%Stop). Those can come it at any time, hence, - we have to make sure that any pending putpkt/getpkt sequence we're - making is finished, before querying the stub for more events with - vStopped. E.g., if we started a vStopped sequence immediatelly - upon receiving the %Stop notification, something like this could - happen: +/* When the stub wants to tell GDB about a new notification reply, it + sends a notification (%Stop, for example). Those can come it at + any time, hence, we have to make sure that any pending + putpkt/getpkt sequence we're making is finished, before querying + the stub for more events with the corresponding ack command + (vStopped, for example). E.g., if we started a vStopped sequence + immediately upon receiving the notification, something like this + could happen: 1.1) --> Hg 1 1.2) <-- OK @@ -5536,19 +5621,21 @@ Packet: '%s'\n"), 2.9) --> OK */ -static void -remote_get_pending_stop_replies (void) +void +remote_notif_get_pending_events (struct notif_client *nc) { struct remote_state *rs = get_remote_state (); - if (pending_stop_reply) + if (nc->pending_event) { - /* acknowledge */ - putpkt ("vStopped"); + if (notif_debug) + fprintf_unfiltered (gdb_stdlog, + "notif: process: '%s' ack pending event\n", + nc->name); - /* Now we can rely on it. */ - push_stop_reply (pending_stop_reply); - pending_stop_reply = NULL; + /* acknowledge */ + nc->ack (nc, rs->buf, nc->pending_event); + nc->pending_event = NULL; while (1) { @@ -5556,31 +5643,18 @@ remote_get_pending_stop_replies (void) if (strcmp (rs->buf, "OK") == 0) break; else - { - struct cleanup *old_chain; - struct stop_reply *stop_reply = stop_reply_xmalloc (); - - old_chain = make_cleanup (do_stop_reply_xfree, stop_reply); - remote_parse_stop_reply (rs->buf, stop_reply); - - /* acknowledge */ - putpkt ("vStopped"); - - if (stop_reply->ws.kind != TARGET_WAITKIND_IGNORE) - { - /* Now we can rely on it. */ - discard_cleanups (old_chain); - push_stop_reply (stop_reply); - } - else - /* We got an unknown stop reply. */ - do_cleanups (old_chain); - } + remote_notif_ack (nc, rs->buf); } } + else + { + if (notif_debug) + fprintf_unfiltered (gdb_stdlog, + "notif: process: '%s' no pending reply\n", + nc->name); + } } - /* Called when it is decided that STOP_REPLY holds the info of the event that is to be returned to the core. This function always destroys STOP_REPLY. */ @@ -5664,8 +5738,8 @@ remote_wait_ns (ptid_t ptid, struct target_waitstatus *status, int options) /* Acknowledge a pending stop reply that may have arrived in the mean time. */ - if (pending_stop_reply != NULL) - remote_get_pending_stop_replies (); + if (notif_client_stop.pending_event != NULL) + remote_notif_get_pending_events (¬if_client_stop); /* If indeed we noticed a stop reply, we're done. */ stop_reply = queued_stop_reply (ptid); @@ -5712,6 +5786,7 @@ remote_wait_as (ptid_t ptid, struct target_waitstatus *status, int options) else { int ret; + int is_notif; if (!target_is_async_p ()) { @@ -5729,7 +5804,14 @@ remote_wait_as (ptid_t ptid, struct target_waitstatus *status, int options) _never_ wait for ever -> test on target_is_async_p(). However, before we do that we need to ensure that the caller knows how to take the target into/out of async mode. */ - ret = getpkt_sane (&rs->buf, &rs->buf_size, wait_forever_enabled_p); + ret = getpkt_or_notif_sane (&rs->buf, &rs->buf_size, + wait_forever_enabled_p, &is_notif); + + /* GDB gets a notification. Return to core as this event is + not interesting. */ + if (ret != -1 && is_notif) + return minus_one_ptid; + if (!target_is_async_p ()) signal (SIGINT, ofunc); } @@ -5761,13 +5843,10 @@ remote_wait_as (ptid_t ptid, struct target_waitstatus *status, int options) break; case 'T': case 'S': case 'X': case 'W': { - struct stop_reply *stop_reply; - struct cleanup *old_chain; + struct stop_reply *stop_reply + = (struct stop_reply *) remote_notif_parse (¬if_client_stop, + rs->buf); - stop_reply = stop_reply_xmalloc (); - old_chain = make_cleanup (do_stop_reply_xfree, stop_reply); - remote_parse_stop_reply (buf, stop_reply); - discard_cleanups (old_chain); event_ptid = process_stop_reply (stop_reply, status); break; } @@ -5848,7 +5927,7 @@ remote_wait (struct target_ops *ops, { /* If there are are events left in the queue tell the event loop to return here. */ - if (stop_reply_queue) + if (!QUEUE_is_empty (stop_reply_p, stop_reply_queue)) mark_async_event_handler (remote_async_inferior_event_token); } @@ -6743,52 +6822,6 @@ remote_read_bytes (CORE_ADDR memaddr, gdb_byte *myaddr, int len) /* Return what we have. Let higher layers handle partial reads. */ return i; } - - -/* Remote notification handler. */ - -static void -handle_notification (char *buf) -{ - if (strncmp (buf, "Stop:", 5) == 0) - { - if (pending_stop_reply) - { - /* We've already parsed the in-flight stop-reply, but the - stub for some reason thought we didn't, possibly due to - timeout on its side. Just ignore it. */ - if (remote_debug) - fprintf_unfiltered (gdb_stdlog, "ignoring resent notification\n"); - } - else - { - struct cleanup *old_chain; - struct stop_reply *reply = stop_reply_xmalloc (); - - old_chain = make_cleanup (do_stop_reply_xfree, reply); - - remote_parse_stop_reply (buf + 5, reply); - - discard_cleanups (old_chain); - - /* Be careful to only set it after parsing, since an error - may be thrown then. */ - pending_stop_reply = reply; - - /* Notify the event loop there's a stop reply to acknowledge - and that there may be more events to fetch. */ - mark_async_event_handler (remote_async_get_pending_events_token); - - if (remote_debug) - fprintf_unfiltered (gdb_stdlog, "stop notification captured\n"); - } - } - else - { - /* We ignore notifications we don't recognize, for compatibility - with newer stubs. */ - } -} /* Read or write LEN bytes from inferior memory at MEMADDR, @@ -11196,12 +11229,6 @@ remote_async_inferior_event_handler (gdb_client_data data) inferior_event_handler (INF_REG_EVENT, NULL); } -static void -remote_async_get_pending_events_handler (gdb_client_data data) -{ - remote_get_pending_stop_replies (); -} - static void remote_async (void (*callback) (enum inferior_event_type event_type, void *context), void *context) @@ -11357,6 +11384,7 @@ _initialize_remote (void) init_remote_threadtests (); #endif + stop_reply_queue = QUEUE_alloc (stop_reply_p, stop_reply_xfree); /* set/show remote ... */ add_prefix_cmd ("remote", class_maintenance, set_remote_cmd, _("\ diff --git a/gdb/remote.h b/gdb/remote.h index 3adc54ee6c..4cd38f637d 100644 --- a/gdb/remote.h +++ b/gdb/remote.h @@ -19,6 +19,8 @@ #ifndef REMOTE_H #define REMOTE_H +#include "remote-notif.h" + struct target_desc; /* Read a packet from the remote machine, with error checking, and @@ -59,4 +61,5 @@ extern int remote_register_number_and_offset (struct gdbarch *gdbarch, int regnum, int *pnum, int *poffset); +extern void remote_notif_get_pending_events (struct notif_client *np); #endif -- 2.34.1