Commit | Line | Data |
---|---|---|
722247f1 YQ |
1 | /* Remote notification in GDB protocol |
2 | ||
3666a048 | 3 | Copyright (C) 1988-2021 Free Software Foundation, Inc. |
722247f1 YQ |
4 | |
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | /* Remote async notification is sent from remote target over RSP. | |
21 | Each type of notification is represented by an object of | |
22 | 'struct notif', which has a field 'pending_reply'. It is not | |
23 | NULL when GDB receives a notification from GDBserver, but hasn't | |
24 | acknowledge yet. Before GDB acknowledges the notification, | |
25 | GDBserver shouldn't send notification again (see the header comments | |
26 | in gdbserver/notif.c). | |
27 | ||
28 | Notifications are processed in an almost-unified approach for both | |
29 | all-stop mode and non-stop mode, except the timing to process them. | |
30 | In non-stop mode, notifications are processed in | |
31 | remote_async_get_pending_events_handler, while in all-stop mode, | |
32 | they are processed in remote_resume. */ | |
33 | ||
34 | #include "defs.h" | |
35 | #include "remote.h" | |
36 | #include "remote-notif.h" | |
76727919 | 37 | #include "observable.h" |
400b5eca | 38 | #include "gdbsupport/event-loop.h" |
722247f1 YQ |
39 | #include "target.h" |
40 | #include "inferior.h" | |
45741a9c | 41 | #include "infrun.h" |
c9b6281a | 42 | #include "gdbcmd.h" |
93b54c8e | 43 | #include "async-event.h" |
722247f1 | 44 | |
491144b5 | 45 | bool notif_debug = false; |
722247f1 YQ |
46 | |
47 | /* Supported clients of notifications. */ | |
48 | ||
49 | static struct notif_client *notifs[] = | |
50 | { | |
51 | ¬if_client_stop, | |
52 | }; | |
53 | ||
f48ff2a7 YQ |
54 | gdb_static_assert (ARRAY_SIZE (notifs) == REMOTE_NOTIF_LAST); |
55 | ||
722247f1 YQ |
56 | /* Parse the BUF for the expected notification NC, and send packet to |
57 | acknowledge. */ | |
58 | ||
59 | void | |
6b8edb51 | 60 | remote_notif_ack (remote_target *remote, |
bb277751 | 61 | struct notif_client *nc, const char *buf) |
722247f1 | 62 | { |
32603266 | 63 | notif_event_up event = nc->alloc_event (); |
722247f1 YQ |
64 | |
65 | if (notif_debug) | |
66 | fprintf_unfiltered (gdb_stdlog, "notif: ack '%s'\n", | |
67 | nc->ack_command); | |
68 | ||
32603266 TT |
69 | nc->parse (remote, nc, buf, event.get ()); |
70 | nc->ack (remote, nc, buf, event.release ()); | |
722247f1 YQ |
71 | } |
72 | ||
73 | /* Parse the BUF for the expected notification NC. */ | |
74 | ||
75 | struct notif_event * | |
6b8edb51 | 76 | remote_notif_parse (remote_target *remote, |
bb277751 | 77 | struct notif_client *nc, const char *buf) |
722247f1 | 78 | { |
32603266 | 79 | notif_event_up event = nc->alloc_event (); |
722247f1 YQ |
80 | |
81 | if (notif_debug) | |
82 | fprintf_unfiltered (gdb_stdlog, "notif: parse '%s'\n", nc->name); | |
83 | ||
32603266 | 84 | nc->parse (remote, nc, buf, event.get ()); |
722247f1 | 85 | |
32603266 | 86 | return event.release (); |
722247f1 YQ |
87 | } |
88 | ||
5965e028 YQ |
89 | /* Process notifications in STATE's notification queue one by one. |
90 | EXCEPT is not expected in the queue. */ | |
722247f1 YQ |
91 | |
92 | void | |
5965e028 YQ |
93 | remote_notif_process (struct remote_notif_state *state, |
94 | struct notif_client *except) | |
722247f1 | 95 | { |
97dfbadd | 96 | while (!state->notif_queue.empty ()) |
722247f1 | 97 | { |
97dfbadd TT |
98 | struct notif_client *nc = state->notif_queue.front (); |
99 | state->notif_queue.pop_front (); | |
722247f1 YQ |
100 | |
101 | gdb_assert (nc != except); | |
102 | ||
6b8edb51 PA |
103 | if (nc->can_get_pending_events (state->remote, nc)) |
104 | remote_notif_get_pending_events (state->remote, nc); | |
722247f1 YQ |
105 | } |
106 | } | |
107 | ||
108 | static void | |
109 | remote_async_get_pending_events_handler (gdb_client_data data) | |
110 | { | |
6b36ddeb SM |
111 | remote_notif_state *notif_state = (remote_notif_state *) data; |
112 | clear_async_event_handler (notif_state->get_pending_events_token); | |
6efcd9a8 | 113 | gdb_assert (target_is_non_stop_p ()); |
6b36ddeb | 114 | remote_notif_process (notif_state, NULL); |
722247f1 YQ |
115 | } |
116 | ||
5965e028 YQ |
117 | /* Remote notification handler. Parse BUF, queue notification and |
118 | update STATE. */ | |
722247f1 YQ |
119 | |
120 | void | |
bb277751 | 121 | handle_notification (struct remote_notif_state *state, const char *buf) |
722247f1 | 122 | { |
62972e0b YQ |
123 | struct notif_client *nc; |
124 | size_t i; | |
722247f1 YQ |
125 | |
126 | for (i = 0; i < ARRAY_SIZE (notifs); i++) | |
127 | { | |
62972e0b YQ |
128 | const char *name = notifs[i]->name; |
129 | ||
61012eef | 130 | if (startswith (buf, name) |
62972e0b | 131 | && buf[strlen (name)] == ':') |
722247f1 YQ |
132 | break; |
133 | } | |
134 | ||
135 | /* We ignore notifications we don't recognize, for compatibility | |
136 | with newer stubs. */ | |
62972e0b | 137 | if (i == ARRAY_SIZE (notifs)) |
722247f1 YQ |
138 | return; |
139 | ||
62972e0b YQ |
140 | nc = notifs[i]; |
141 | ||
f48ff2a7 | 142 | if (state->pending_event[nc->id] != NULL) |
722247f1 YQ |
143 | { |
144 | /* We've already parsed the in-flight reply, but the stub for some | |
145 | reason thought we didn't, possibly due to timeout on its side. | |
146 | Just ignore it. */ | |
147 | if (notif_debug) | |
148 | fprintf_unfiltered (gdb_stdlog, | |
149 | "notif: ignoring resent notification\n"); | |
150 | } | |
151 | else | |
152 | { | |
153 | struct notif_event *event | |
6b8edb51 | 154 | = remote_notif_parse (state->remote, nc, buf + strlen (nc->name) + 1); |
722247f1 YQ |
155 | |
156 | /* Be careful to only set it after parsing, since an error | |
157 | may be thrown then. */ | |
f48ff2a7 | 158 | state->pending_event[nc->id] = event; |
722247f1 YQ |
159 | |
160 | /* Notify the event loop there's a stop reply to acknowledge | |
161 | and that there may be more events to fetch. */ | |
97dfbadd | 162 | state->notif_queue.push_back (nc); |
6efcd9a8 | 163 | if (target_is_non_stop_p ()) |
722247f1 YQ |
164 | { |
165 | /* In non-stop, We mark REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN | |
166 | in order to go on what we were doing and postpone | |
167 | querying notification events to some point safe to do so. | |
168 | See details in the function comment of | |
169 | remote.c:remote_notif_get_pending_events. | |
170 | ||
171 | In all-stop, GDB may be blocked to wait for the reply, we | |
172 | shouldn't return to event loop until the expected reply | |
173 | arrives. For example: | |
174 | ||
175 | 1.1) --> vCont;c | |
176 | GDB expects getting stop reply 'T05 thread:2'. | |
177 | 1.2) <-- %Notif | |
178 | <GDB marks the REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN> | |
179 | ||
180 | After step #1.2, we return to the event loop, which | |
181 | notices there is a new event on the | |
182 | REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN and calls the | |
183 | handler, which will send 'vNotif' packet. | |
184 | 1.3) --> vNotif | |
185 | It is not safe to start a new sequence, because target | |
186 | is still running and GDB is expecting the stop reply | |
187 | from stub. | |
188 | ||
189 | To solve this, whenever we parse a notification | |
190 | successfully, we don't mark the | |
191 | REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN and let GDB blocked | |
192 | there as before to get the sequence done. | |
193 | ||
194 | 2.1) --> vCont;c | |
195 | GDB expects getting stop reply 'T05 thread:2' | |
196 | 2.2) <-- %Notif | |
197 | <Don't mark the REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN> | |
198 | 2.3) <-- T05 thread:2 | |
199 | ||
200 | These pending notifications can be processed later. */ | |
5965e028 | 201 | mark_async_event_handler (state->get_pending_events_token); |
722247f1 YQ |
202 | } |
203 | ||
204 | if (notif_debug) | |
205 | fprintf_unfiltered (gdb_stdlog, | |
206 | "notif: Notification '%s' captured\n", | |
207 | nc->name); | |
208 | } | |
209 | } | |
210 | ||
5965e028 YQ |
211 | /* Return an allocated remote_notif_state. */ |
212 | ||
213 | struct remote_notif_state * | |
6b8edb51 | 214 | remote_notif_state_allocate (remote_target *remote) |
5965e028 | 215 | { |
97dfbadd | 216 | struct remote_notif_state *notif_state = new struct remote_notif_state; |
5965e028 | 217 | |
6b8edb51 PA |
218 | notif_state->remote = remote; |
219 | ||
5965e028 YQ |
220 | /* Register async_event_handler for notification. */ |
221 | ||
222 | notif_state->get_pending_events_token | |
223 | = create_async_event_handler (remote_async_get_pending_events_handler, | |
db20ebdf | 224 | notif_state, "remote-notif"); |
5965e028 YQ |
225 | |
226 | return notif_state; | |
227 | } | |
228 | ||
229 | /* Free STATE and its fields. */ | |
230 | ||
97dfbadd | 231 | remote_notif_state::~remote_notif_state () |
722247f1 | 232 | { |
f48ff2a7 YQ |
233 | int i; |
234 | ||
5965e028 | 235 | /* Unregister async_event_handler for notification. */ |
97dfbadd TT |
236 | if (get_pending_events_token != NULL) |
237 | delete_async_event_handler (&get_pending_events_token); | |
722247f1 | 238 | |
f48ff2a7 | 239 | for (i = 0; i < REMOTE_NOTIF_LAST; i++) |
97dfbadd | 240 | delete pending_event[i]; |
722247f1 YQ |
241 | } |
242 | ||
6c265988 | 243 | void _initialize_notif (); |
722247f1 | 244 | void |
6c265988 | 245 | _initialize_notif () |
722247f1 | 246 | { |
c9b6281a YQ |
247 | add_setshow_boolean_cmd ("notification", no_class, ¬if_debug, |
248 | _("\ | |
249 | Set debugging of async remote notification."), _("\ | |
250 | Show debugging of async remote notification."), _("\ | |
251 | When non-zero, debugging output about async remote notifications" | |
252 | " is enabled."), | |
253 | NULL, | |
254 | NULL, | |
255 | &setdebuglist, &showdebuglist); | |
722247f1 | 256 | } |