]>
Commit | Line | Data |
---|---|---|
4a8f6654 AC |
1 | /* CLI Definitions for GDB, the GNU debugger. |
2 | ||
e2882c85 | 3 | Copyright (C) 2002-2018 Free Software Foundation, Inc. |
4a8f6654 AC |
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 | |
a9762ec7 | 9 | the Free Software Foundation; either version 3 of the License, or |
4a8f6654 AC |
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 | |
a9762ec7 | 18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
4a8f6654 AC |
19 | |
20 | #include "defs.h" | |
3c216924 | 21 | #include "cli-interp.h" |
4a8f6654 | 22 | #include "interps.h" |
4a8f6654 AC |
23 | #include "event-top.h" |
24 | #include "ui-out.h" | |
25 | #include "cli-out.h" | |
26 | #include "top.h" /* for "execute_command" */ | |
3c216924 | 27 | #include "event-top.h" |
fd664c91 | 28 | #include "infrun.h" |
76727919 | 29 | #include "observable.h" |
26cde2cc PA |
30 | #include "gdbthread.h" |
31 | #include "thread-fsm.h" | |
00431a78 | 32 | #include "inferior.h" |
4a8f6654 | 33 | |
d6f9b0fb PA |
34 | cli_interp_base::cli_interp_base (const char *name) |
35 | : interp (name) | |
36 | {} | |
37 | ||
38 | cli_interp_base::~cli_interp_base () | |
39 | {} | |
40 | ||
8322445e | 41 | /* The console interpreter. */ |
d6f9b0fb PA |
42 | |
43 | class cli_interp final : public cli_interp_base | |
8322445e | 44 | { |
d6f9b0fb PA |
45 | public: |
46 | explicit cli_interp (const char *name); | |
47 | ||
48 | void init (bool top_level) override; | |
49 | void resume () override; | |
50 | void suspend () override; | |
51 | gdb_exception exec (const char *command_str) override; | |
52 | ui_out *interp_ui_out () override; | |
53 | ||
8322445e | 54 | /* The ui_out for the console interpreter. */ |
112e8700 | 55 | cli_ui_out *cli_uiout; |
8322445e PA |
56 | }; |
57 | ||
d6f9b0fb PA |
58 | cli_interp::cli_interp (const char *name) |
59 | : cli_interp_base (name) | |
60 | { | |
61 | /* Create a default uiout builder for the CLI. */ | |
62 | this->cli_uiout = cli_out_new (gdb_stdout); | |
63 | } | |
64 | ||
4034d0ff AT |
65 | /* Suppress notification struct. */ |
66 | struct cli_suppress_notification cli_suppress_notification = | |
67 | { | |
68 | 0 /* user_selected_context_changed */ | |
69 | }; | |
70 | ||
73ab01a0 PA |
71 | /* Returns the INTERP's data cast as cli_interp if INTERP is a CLI, |
72 | and returns NULL otherwise. */ | |
73 | ||
74 | static struct cli_interp * | |
75 | as_cli_interp (struct interp *interp) | |
76 | { | |
716b8bc5 | 77 | return dynamic_cast<cli_interp *> (interp); |
73ab01a0 | 78 | } |
4a8f6654 | 79 | |
4791eb66 | 80 | /* Longjmp-safe wrapper for "execute_command". */ |
71fff37b | 81 | static struct gdb_exception safe_execute_command (struct ui_out *uiout, |
95a6b0a1 | 82 | const char *command, |
ebcd3b23 | 83 | int from_tty); |
fd664c91 | 84 | |
26cde2cc PA |
85 | /* See cli-interp.h. |
86 | ||
87 | Breakpoint hits should always be mirrored to a console. Deciding | |
88 | what to mirror to a console wrt to breakpoints and random stops | |
89 | gets messy real fast. E.g., say "s" trips on a breakpoint. We'd | |
90 | clearly want to mirror the event to the console in this case. But | |
91 | what about more complicated cases like "s&; thread n; s&", and one | |
92 | of those steps spawning a new thread, and that thread hitting a | |
93 | breakpoint? It's impossible in general to track whether the thread | |
94 | had any relation to the commands that had been executed. So we | |
95 | just simplify and always mirror breakpoints and random events to | |
96 | all consoles. | |
97 | ||
98 | OTOH, we should print the source line to the console when stepping | |
99 | or other similar commands, iff the step was started by that console | |
100 | (or in MI's case, by a console command), but not if it was started | |
101 | with MI's -exec-step or similar. */ | |
102 | ||
103 | int | |
104 | should_print_stop_to_console (struct interp *console_interp, | |
105 | struct thread_info *tp) | |
106 | { | |
107 | if ((bpstat_what (tp->control.stop_bpstat).main_action | |
108 | == BPSTAT_WHAT_STOP_NOISY) | |
8980e177 PA |
109 | || tp->thread_fsm == NULL |
110 | || tp->thread_fsm->command_interp == console_interp | |
111 | || !thread_fsm_finished_p (tp->thread_fsm)) | |
26cde2cc PA |
112 | return 1; |
113 | return 0; | |
114 | } | |
115 | ||
fd664c91 PA |
116 | /* Observers for several run control events. If the interpreter is |
117 | quiet (i.e., another interpreter is being run with | |
118 | interpreter-exec), print nothing. */ | |
119 | ||
243a9253 PA |
120 | /* Observer for the normal_stop notification. */ |
121 | ||
122 | static void | |
123 | cli_on_normal_stop (struct bpstats *bs, int print_frame) | |
124 | { | |
eaae60fd PA |
125 | if (!print_frame) |
126 | return; | |
127 | ||
0e454242 | 128 | SWITCH_THRU_ALL_UIS () |
243a9253 | 129 | { |
eaae60fd PA |
130 | struct interp *interp = top_level_interpreter (); |
131 | struct cli_interp *cli = as_cli_interp (interp); | |
132 | struct thread_info *thread; | |
73ab01a0 PA |
133 | |
134 | if (cli == NULL) | |
135 | continue; | |
136 | ||
eaae60fd PA |
137 | thread = inferior_thread (); |
138 | if (should_print_stop_to_console (interp, thread)) | |
73ab01a0 | 139 | print_stop_event (cli->cli_uiout); |
243a9253 PA |
140 | } |
141 | } | |
142 | ||
fd664c91 PA |
143 | /* Observer for the signal_received notification. */ |
144 | ||
145 | static void | |
146 | cli_on_signal_received (enum gdb_signal siggnal) | |
147 | { | |
0e454242 | 148 | SWITCH_THRU_ALL_UIS () |
73ab01a0 PA |
149 | { |
150 | struct cli_interp *cli = as_cli_interp (top_level_interpreter ()); | |
151 | ||
152 | if (cli == NULL) | |
153 | continue; | |
154 | ||
155 | print_signal_received_reason (cli->cli_uiout, siggnal); | |
156 | } | |
fd664c91 PA |
157 | } |
158 | ||
159 | /* Observer for the end_stepping_range notification. */ | |
160 | ||
161 | static void | |
162 | cli_on_end_stepping_range (void) | |
163 | { | |
0e454242 | 164 | SWITCH_THRU_ALL_UIS () |
73ab01a0 PA |
165 | { |
166 | struct cli_interp *cli = as_cli_interp (top_level_interpreter ()); | |
167 | ||
168 | if (cli == NULL) | |
169 | continue; | |
170 | ||
171 | print_end_stepping_range_reason (cli->cli_uiout); | |
172 | } | |
fd664c91 PA |
173 | } |
174 | ||
175 | /* Observer for the signalled notification. */ | |
176 | ||
177 | static void | |
178 | cli_on_signal_exited (enum gdb_signal siggnal) | |
179 | { | |
0e454242 | 180 | SWITCH_THRU_ALL_UIS () |
73ab01a0 PA |
181 | { |
182 | struct cli_interp *cli = as_cli_interp (top_level_interpreter ()); | |
183 | ||
184 | if (cli == NULL) | |
185 | continue; | |
186 | ||
187 | print_signal_exited_reason (cli->cli_uiout, siggnal); | |
188 | } | |
fd664c91 PA |
189 | } |
190 | ||
191 | /* Observer for the exited notification. */ | |
192 | ||
193 | static void | |
194 | cli_on_exited (int exitstatus) | |
195 | { | |
0e454242 | 196 | SWITCH_THRU_ALL_UIS () |
73ab01a0 PA |
197 | { |
198 | struct cli_interp *cli = as_cli_interp (top_level_interpreter ()); | |
199 | ||
200 | if (cli == NULL) | |
201 | continue; | |
202 | ||
203 | print_exited_reason (cli->cli_uiout, exitstatus); | |
204 | } | |
fd664c91 PA |
205 | } |
206 | ||
207 | /* Observer for the no_history notification. */ | |
208 | ||
209 | static void | |
210 | cli_on_no_history (void) | |
211 | { | |
0e454242 | 212 | SWITCH_THRU_ALL_UIS () |
73ab01a0 PA |
213 | { |
214 | struct cli_interp *cli = as_cli_interp (top_level_interpreter ()); | |
215 | ||
216 | if (cli == NULL) | |
217 | continue; | |
218 | ||
219 | print_no_history_reason (cli->cli_uiout); | |
220 | } | |
fd664c91 PA |
221 | } |
222 | ||
92bcb5f9 PA |
223 | /* Observer for the sync_execution_done notification. */ |
224 | ||
225 | static void | |
226 | cli_on_sync_execution_done (void) | |
227 | { | |
73ab01a0 PA |
228 | struct cli_interp *cli = as_cli_interp (top_level_interpreter ()); |
229 | ||
230 | if (cli == NULL) | |
231 | return; | |
232 | ||
233 | display_gdb_prompt (NULL); | |
92bcb5f9 PA |
234 | } |
235 | ||
236 | /* Observer for the command_error notification. */ | |
237 | ||
238 | static void | |
239 | cli_on_command_error (void) | |
240 | { | |
73ab01a0 PA |
241 | struct cli_interp *cli = as_cli_interp (top_level_interpreter ()); |
242 | ||
243 | if (cli == NULL) | |
244 | return; | |
245 | ||
246 | display_gdb_prompt (NULL); | |
92bcb5f9 PA |
247 | } |
248 | ||
4034d0ff AT |
249 | /* Observer for the user_selected_context_changed notification. */ |
250 | ||
251 | static void | |
252 | cli_on_user_selected_context_changed (user_selected_what selection) | |
253 | { | |
4034d0ff AT |
254 | struct thread_info *tp; |
255 | ||
256 | /* This event is suppressed. */ | |
257 | if (cli_suppress_notification.user_selected_context) | |
258 | return; | |
259 | ||
260 | tp = find_thread_ptid (inferior_ptid); | |
261 | ||
0e454242 | 262 | SWITCH_THRU_ALL_UIS () |
4034d0ff AT |
263 | { |
264 | struct cli_interp *cli = as_cli_interp (top_level_interpreter ()); | |
265 | ||
266 | if (cli == NULL) | |
267 | continue; | |
268 | ||
269 | if (selection & USER_SELECTED_INFERIOR) | |
270 | print_selected_inferior (cli->cli_uiout); | |
271 | ||
272 | if (tp != NULL | |
273 | && ((selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME)))) | |
274 | print_selected_thread_frame (cli->cli_uiout, selection); | |
275 | } | |
276 | } | |
277 | ||
b2d86570 PA |
278 | /* pre_command_loop implementation. */ |
279 | ||
280 | void | |
d6f9b0fb | 281 | cli_interp_base::pre_command_loop () |
b2d86570 PA |
282 | { |
283 | display_gdb_prompt (0); | |
284 | } | |
285 | ||
4a8f6654 AC |
286 | /* These implement the cli out interpreter: */ |
287 | ||
d6f9b0fb PA |
288 | void |
289 | cli_interp::init (bool top_level) | |
4a8f6654 | 290 | { |
4a8f6654 AC |
291 | } |
292 | ||
d6f9b0fb PA |
293 | void |
294 | cli_interp::resume () | |
4a8f6654 | 295 | { |
3c216924 | 296 | struct ui *ui = current_ui; |
d6f9b0fb | 297 | struct cli_interp *cli = this; |
38caaeec DJ |
298 | struct ui_file *stream; |
299 | ||
4a8f6654 | 300 | /*sync_execution = 1; */ |
38caaeec | 301 | |
ebcd3b23 MS |
302 | /* gdb_setup_readline will change gdb_stdout. If the CLI was |
303 | previously writing to gdb_stdout, then set it to the new | |
304 | gdb_stdout afterwards. */ | |
38caaeec | 305 | |
112e8700 | 306 | stream = cli->cli_uiout->set_stream (gdb_stdout); |
38caaeec DJ |
307 | if (stream != gdb_stdout) |
308 | { | |
112e8700 | 309 | cli->cli_uiout->set_stream (stream); |
38caaeec DJ |
310 | stream = NULL; |
311 | } | |
312 | ||
3c216924 PA |
313 | gdb_setup_readline (1); |
314 | ||
315 | ui->input_handler = command_line_handler; | |
38caaeec DJ |
316 | |
317 | if (stream != NULL) | |
112e8700 | 318 | cli->cli_uiout->set_stream (gdb_stdout); |
4a8f6654 AC |
319 | } |
320 | ||
d6f9b0fb PA |
321 | void |
322 | cli_interp::suspend () | |
4a8f6654 AC |
323 | { |
324 | gdb_disable_readline (); | |
4a8f6654 AC |
325 | } |
326 | ||
d6f9b0fb PA |
327 | gdb_exception |
328 | cli_interp::exec (const char *command_str) | |
4a8f6654 | 329 | { |
d6f9b0fb | 330 | struct cli_interp *cli = this; |
4a8f6654 | 331 | struct ui_file *old_stream; |
71fff37b | 332 | struct gdb_exception result; |
4a8f6654 | 333 | |
ebcd3b23 MS |
334 | /* gdb_stdout could change between the time cli_uiout was |
335 | initialized and now. Since we're probably using a different | |
336 | interpreter which has a new ui_file for gdb_stdout, use that one | |
337 | instead of the default. | |
4a8f6654 | 338 | |
ebcd3b23 MS |
339 | It is important that it gets reset everytime, since the user |
340 | could set gdb to use a different interpreter. */ | |
112e8700 | 341 | old_stream = cli->cli_uiout->set_stream (gdb_stdout); |
95a6b0a1 | 342 | result = safe_execute_command (cli->cli_uiout, command_str, 1); |
112e8700 | 343 | cli->cli_uiout->set_stream (old_stream); |
4a8f6654 AC |
344 | return result; |
345 | } | |
346 | ||
d6f9b0fb PA |
347 | bool |
348 | cli_interp_base::supports_command_editing () | |
3c216924 | 349 | { |
d6f9b0fb | 350 | return true; |
3c216924 PA |
351 | } |
352 | ||
71fff37b | 353 | static struct gdb_exception |
95a6b0a1 TT |
354 | safe_execute_command (struct ui_out *command_uiout, const char *command, |
355 | int from_tty) | |
4a8f6654 | 356 | { |
7556d4a4 | 357 | struct gdb_exception e = exception_none; |
f9679975 PA |
358 | |
359 | /* Save and override the global ``struct ui_out'' builder. */ | |
753ff9bd TT |
360 | scoped_restore saved_uiout = make_scoped_restore (¤t_uiout, |
361 | command_uiout); | |
cdb27c12 | 362 | |
492d29ea | 363 | TRY |
04bd08de TT |
364 | { |
365 | execute_command (command, from_tty); | |
366 | } | |
492d29ea | 367 | CATCH (exception, RETURN_MASK_ALL) |
7556d4a4 PA |
368 | { |
369 | e = exception; | |
370 | } | |
492d29ea | 371 | END_CATCH |
f9679975 | 372 | |
8a076db9 AC |
373 | /* FIXME: cagney/2005-01-13: This shouldn't be needed. Instead the |
374 | caller should print the exception. */ | |
9cbc821d | 375 | exception_print (gdb_stderr, e); |
8a076db9 | 376 | return e; |
4a8f6654 AC |
377 | } |
378 | ||
d6f9b0fb PA |
379 | ui_out * |
380 | cli_interp::interp_ui_out () | |
4801a9a3 | 381 | { |
d6f9b0fb | 382 | struct cli_interp *cli = (struct cli_interp *) this; |
8322445e PA |
383 | |
384 | return cli->cli_uiout; | |
385 | } | |
386 | ||
616268b6 PA |
387 | /* These hold the pushed copies of the gdb output files. |
388 | If NULL then nothing has yet been pushed. */ | |
389 | struct saved_output_files | |
390 | { | |
391 | ui_file *out; | |
392 | ui_file *err; | |
393 | ui_file *log; | |
394 | ui_file *targ; | |
395 | ui_file *targerr; | |
396 | }; | |
397 | static saved_output_files saved_output; | |
398 | ||
399 | /* See cli-interp.h. */ | |
400 | ||
401 | void | |
d6f9b0fb | 402 | cli_interp_base::set_logging (ui_file_up logfile, bool logging_redirect) |
616268b6 PA |
403 | { |
404 | if (logfile != NULL) | |
405 | { | |
406 | saved_output.out = gdb_stdout; | |
407 | saved_output.err = gdb_stderr; | |
408 | saved_output.log = gdb_stdlog; | |
409 | saved_output.targ = gdb_stdtarg; | |
410 | saved_output.targerr = gdb_stdtargerr; | |
411 | ||
412 | /* A raw pointer since ownership is transferred to | |
413 | gdb_stdout. */ | |
414 | ui_file *output = make_logging_output (gdb_stdout, | |
415 | std::move (logfile), | |
416 | logging_redirect); | |
417 | gdb_stdout = output; | |
418 | gdb_stdlog = output; | |
419 | gdb_stderr = output; | |
420 | gdb_stdtarg = output; | |
421 | gdb_stdtargerr = output; | |
422 | } | |
423 | else | |
424 | { | |
425 | /* Only delete one of the files -- they are all set to the same | |
426 | value. */ | |
427 | delete gdb_stdout; | |
428 | ||
429 | gdb_stdout = saved_output.out; | |
430 | gdb_stderr = saved_output.err; | |
431 | gdb_stdlog = saved_output.log; | |
432 | gdb_stdtarg = saved_output.targ; | |
433 | gdb_stdtargerr = saved_output.targerr; | |
434 | ||
435 | saved_output.out = NULL; | |
436 | saved_output.err = NULL; | |
437 | saved_output.log = NULL; | |
438 | saved_output.targ = NULL; | |
439 | saved_output.targerr = NULL; | |
440 | } | |
441 | } | |
442 | ||
8322445e PA |
443 | /* Factory for CLI interpreters. */ |
444 | ||
445 | static struct interp * | |
446 | cli_interp_factory (const char *name) | |
447 | { | |
d6f9b0fb | 448 | return new cli_interp (name); |
4801a9a3 | 449 | } |
4a8f6654 | 450 | |
4791eb66 | 451 | /* Standard gdb initialization hook. */ |
b9362cc7 | 452 | |
4a8f6654 AC |
453 | void |
454 | _initialize_cli_interp (void) | |
455 | { | |
8322445e | 456 | interp_factory_register (INTERP_CONSOLE, cli_interp_factory); |
73ab01a0 PA |
457 | |
458 | /* If changing this, remember to update tui-interp.c as well. */ | |
76727919 TT |
459 | gdb::observers::normal_stop.attach (cli_on_normal_stop); |
460 | gdb::observers::end_stepping_range.attach (cli_on_end_stepping_range); | |
461 | gdb::observers::signal_received.attach (cli_on_signal_received); | |
462 | gdb::observers::signal_exited.attach (cli_on_signal_exited); | |
463 | gdb::observers::exited.attach (cli_on_exited); | |
464 | gdb::observers::no_history.attach (cli_on_no_history); | |
465 | gdb::observers::sync_execution_done.attach (cli_on_sync_execution_done); | |
466 | gdb::observers::command_error.attach (cli_on_command_error); | |
467 | gdb::observers::user_selected_context_changed.attach | |
4034d0ff | 468 | (cli_on_user_selected_context_changed); |
4a8f6654 | 469 | } |