|  | /* Remote notification in GDB protocol | 
|  |  | 
|  | Copyright (C) 1988-2022 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 "observable.h" | 
|  | #include "gdbsupport/event-loop.h" | 
|  | #include "target.h" | 
|  | #include "inferior.h" | 
|  | #include "infrun.h" | 
|  | #include "gdbcmd.h" | 
|  | #include "async-event.h" | 
|  |  | 
|  | bool notif_debug = false; | 
|  |  | 
|  | /* Supported clients of notifications.  */ | 
|  |  | 
|  | static struct notif_client *notifs[] = | 
|  | { | 
|  | ¬if_client_stop, | 
|  | }; | 
|  |  | 
|  | gdb_static_assert (ARRAY_SIZE (notifs) == REMOTE_NOTIF_LAST); | 
|  |  | 
|  | /* Parse the BUF for the expected notification NC, and send packet to | 
|  | acknowledge.  */ | 
|  |  | 
|  | void | 
|  | remote_notif_ack (remote_target *remote, | 
|  | struct notif_client *nc, const char *buf) | 
|  | { | 
|  | notif_event_up event = nc->alloc_event (); | 
|  |  | 
|  | if (notif_debug) | 
|  | fprintf_unfiltered (gdb_stdlog, "notif: ack '%s'\n", | 
|  | nc->ack_command); | 
|  |  | 
|  | nc->parse (remote, nc, buf, event.get ()); | 
|  | nc->ack (remote, nc, buf, event.release ()); | 
|  | } | 
|  |  | 
|  | /* Parse the BUF for the expected notification NC.  */ | 
|  |  | 
|  | struct notif_event * | 
|  | remote_notif_parse (remote_target *remote, | 
|  | struct notif_client *nc, const char *buf) | 
|  | { | 
|  | notif_event_up event = nc->alloc_event (); | 
|  |  | 
|  | if (notif_debug) | 
|  | fprintf_unfiltered (gdb_stdlog, "notif: parse '%s'\n", nc->name); | 
|  |  | 
|  | nc->parse (remote, nc, buf, event.get ()); | 
|  |  | 
|  | return event.release (); | 
|  | } | 
|  |  | 
|  | /* 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 (!state->notif_queue.empty ()) | 
|  | { | 
|  | struct notif_client *nc = state->notif_queue.front (); | 
|  | state->notif_queue.pop_front (); | 
|  |  | 
|  | gdb_assert (nc != except); | 
|  |  | 
|  | if (nc->can_get_pending_events (state->remote, nc)) | 
|  | remote_notif_get_pending_events (state->remote, nc); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | remote_async_get_pending_events_handler (gdb_client_data data) | 
|  | { | 
|  | remote_notif_state *notif_state = (remote_notif_state *) data; | 
|  | clear_async_event_handler (notif_state->get_pending_events_token); | 
|  | gdb_assert (remote_target_is_non_stop_p (notif_state->remote)); | 
|  | remote_notif_process (notif_state, NULL); | 
|  | } | 
|  |  | 
|  | /* Remote notification handler.  Parse BUF, queue notification and | 
|  | update STATE.  */ | 
|  |  | 
|  | void | 
|  | handle_notification (struct remote_notif_state *state, const char *buf) | 
|  | { | 
|  | struct notif_client *nc; | 
|  | size_t i; | 
|  |  | 
|  | for (i = 0; i < ARRAY_SIZE (notifs); i++) | 
|  | { | 
|  | const char *name = notifs[i]->name; | 
|  |  | 
|  | if (startswith (buf, name) | 
|  | && 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 (state->remote, 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.  */ | 
|  | state->notif_queue.push_back (nc); | 
|  | if (target_is_non_stop_p ()) | 
|  | { | 
|  | /* 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Return an allocated remote_notif_state.  */ | 
|  |  | 
|  | struct remote_notif_state * | 
|  | remote_notif_state_allocate (remote_target *remote) | 
|  | { | 
|  | struct remote_notif_state *notif_state = new struct remote_notif_state; | 
|  |  | 
|  | notif_state->remote = remote; | 
|  |  | 
|  | /* Register async_event_handler for notification.  */ | 
|  |  | 
|  | notif_state->get_pending_events_token | 
|  | = create_async_event_handler (remote_async_get_pending_events_handler, | 
|  | notif_state, "remote-notif"); | 
|  |  | 
|  | return notif_state; | 
|  | } | 
|  |  | 
|  | /* Free STATE and its fields.  */ | 
|  |  | 
|  | remote_notif_state::~remote_notif_state () | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* Unregister async_event_handler for notification.  */ | 
|  | if (get_pending_events_token != NULL) | 
|  | delete_async_event_handler (&get_pending_events_token); | 
|  |  | 
|  | for (i = 0; i < REMOTE_NOTIF_LAST; i++) | 
|  | delete pending_event[i]; | 
|  | } | 
|  |  | 
|  | void _initialize_notif (); | 
|  | void | 
|  | _initialize_notif () | 
|  | { | 
|  | 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); | 
|  | } |