diff options
author | Pedro Alves <palves@redhat.com> | 2014-05-29 13:09:45 +0100 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2014-05-29 13:09:45 +0100 |
commit | fd664c91769bf7e31c3b4d64e41d05854eecd94a (patch) | |
tree | 1db5da8fcdfd99220446718632b9d1b499b2f38e /gdb/mi | |
parent | 8817a6f225766029787b5e2c1300a342b328909e (diff) | |
download | gdb-fd664c91769bf7e31c3b4d64e41d05854eecd94a.zip gdb-fd664c91769bf7e31c3b4d64e41d05854eecd94a.tar.gz gdb-fd664c91769bf7e31c3b4d64e41d05854eecd94a.tar.bz2 |
PR gdb/13860 - Make MI sync vs async output (closer to) the same.
Ignoring expected and desired differences like whether the prompt is
output after *stoppped records, GDB MI output is still different in
sync and async modes.
In sync mode, when a CLI execution command is entered, the "reason"
field is missing in the *stopped async record. And in async mode, for
some events, like program exits, the corresponding CLI output is
missing in the CLI channel.
Vis, diff between sync vs async modes:
run
^running
*running,thread-id="1"
(gdb)
...
- ~"[Inferior 1 (process 15882) exited normally]\n"
=thread-exited,id="1",group-id="i1"
=thread-group-exited,id="i1",exit-code="0"
- *stopped
+ *stopped,reason="exited-normally"
si
...
(gdb)
~"0x000000000045e033\t29\t memset (&args, 0, sizeof args);\n"
- *stopped,frame=...,thread-id="1",stopped-threads="all",core="0"
+ *stopped,reason="end-stepping-range",frame=...,thread-id="1",stopped-threads="all",core="0"
(gdb)
In addition, in both cases, when a MI execution command is entered,
and a breakpoint triggers, the event is sent to the console too. But
some events like program exits have the CLI output missing in the CLI
channel:
-exec-run
^running
*running,thread-id="1"
(gdb)
...
=thread-exited,id="1",group-id="i1"
=thread-group-exited,id="i1",exit-code="0"
- *stopped
+ *stopped,reason="exited-normally"
We'll want to make background commands always possible by default.
IOW, make target-async be the default. But, in order to do that,
we'll need to emulate MI sync on top of an async target. That means
we'll have yet another combination to care for in the testsuite.
Rather than making the testsuite cope with all these differences, I
thought it better to just fix GDB to always have the complete output,
no matter whether it's in sync or async mode.
This is all related to interpreter-exec, and the corresponding uiout
switching. (Typing a CLI command directly in MI is shorthand for
running it through -interpreter-exec console.)
In sync mode, when a CLI command is active, normal_stop is called when
the current interpreter and uiout are CLI's. So print_XXX_reason
prints the stop reason to CLI uiout (only), and we don't show it in
MI.
In async mode the stop event is processed when we're back in the MI
interpreter, so the stop reason is printed directly to the MI uiout.
Fix this by making run control event printing roughly independent of
whatever is the current interpreter or uiout. That is, move these
prints to interpreter observers, that know whether to print or be
quiet, and if printing, which uiout to print to. In the case of the
console/tui interpreters, only print if the top interpreter. For MI,
always print.
Breakpoint hits / normal stops are already handled similarly -- MI has
a normal_stop observer that prints the event to both MI and the CLI,
though that could be cleaned up further in the direction of this
patch.
This also makes all of:
(gdb) foo
and
(gdb) interpreter-exec MI "-exec-foo"
and
(gdb)
-exec-foo
and
(gdb)
-interpreter-exec console "foo"
print as expected.
Tested on x86_64 Fedora 20, sync and async modes.
gdb/
2014-05-29 Pedro Alves <palves@redhat.com>
PR gdb/13860
* cli/cli-interp.c: Include infrun.h and observer.h.
(cli_uiout, cli_interp): New globals.
(cli_on_signal_received, cli_on_end_stepping_range)
(cli_on_signal_exited, cli_on_exited, cli_on_no_history): New
functions.
(cli_interpreter_init): Install them as 'end_stepping_range',
'signal_received' 'signal_exited', 'exited' and 'no_history'
observers.
(_initialize_cli_interp): Remove cli_interp local.
* infrun.c (handle_inferior_event): Call the several stop reason
observers instead of printing the stop reason directly.
(end_stepping_range): New function.
(print_end_stepping_range_reason, print_signal_exited_reason)
(print_exited_reason, print_signal_received_reason)
(print_no_history_reason): Make static, and add an uiout
parameter. Print to that instead of to CURRENT_UIOUT.
* infrun.h (print_end_stepping_range_reason)
(print_signal_exited_reason, print_exited_reason)
(print_signal_received_reason print_no_history_reason): New
declarations.
* mi/mi-common.h (struct mi_interp): Rename 'uiout' field to
'mi_uiout'.
<cli_uiout>: New field.
* mi/mi-interp.c (mi_interpreter_init): Adjust. Create the new
uiout for CLI output. Install 'signal_received',
'end_stepping_range', 'signal_exited', 'exited' and 'no_history'
observers.
(find_mi_interpreter, mi_interp_data, mi_on_signal_received)
(mi_on_end_stepping_range, mi_on_signal_exited, mi_on_exited)
(mi_on_no_history): New functions.
(ui_out_free_cleanup): Delete function.
(mi_on_normal_stop): Don't allocate a new uiout for CLI output,
instead use the one already stored in the MI interpreter data.
(mi_ui_out): Adjust.
* tui/tui-interp.c: Include infrun.h and observer.h.
(tui_interp): New global.
(tui_on_signal_received, tui_on_end_stepping_range)
(tui_on_signal_exited, tui_on_exited)
(tui_on_no_history): New functions.
(tui_init): Install them as 'end_stepping_range',
'signal_received' 'signal_exited', 'exited' and 'no_history'
observers.
(_initialize_tui_interp): Delete tui_interp local.
gdb/doc/
2014-05-29 Pedro Alves <palves@redhat.com>
PR gdb/13860
* observer.texi (signal_received, end_stepping_range)
(signal_exited, exited, no_history): New observer subjects.
gdb/testsuite/
2014-05-29 Pedro Alves <palves@redhat.com>
PR gdb/13860
* gdb.mi/mi-cli.exp: Always expect "end-stepping-range" stop
reason, even in sync mode.
Diffstat (limited to 'gdb/mi')
-rw-r--r-- | gdb/mi/mi-common.h | 5 | ||||
-rw-r--r-- | gdb/mi/mi-interp.c | 136 |
2 files changed, 126 insertions, 15 deletions
diff --git a/gdb/mi/mi-common.h b/gdb/mi/mi-common.h index a6b0103..71e5d78 100644 --- a/gdb/mi/mi-common.h +++ b/gdb/mi/mi-common.h @@ -58,7 +58,10 @@ struct mi_interp struct ui_file *event_channel; /* MI's builder. */ - struct ui_out *uiout; + struct ui_out *mi_uiout; + + /* MI's CLI builder (wraps OUT). */ + struct ui_out *cli_uiout; /* This is the interpreter for the mi... */ struct interp *mi2_interp; diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index 99479cc..e8e30e2 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -56,7 +56,13 @@ static int mi_interp_query_hook (const char *ctlstr, va_list ap) static void mi_insert_notify_hooks (void); static void mi_remove_notify_hooks (void); + +static void mi_on_signal_received (enum gdb_signal siggnal); +static void mi_on_end_stepping_range (void); +static void mi_on_signal_exited (enum gdb_signal siggnal); +static void mi_on_exited (int exitstatus); static void mi_on_normal_stop (struct bpstats *bs, int print_frame); +static void mi_on_no_history (void); static void mi_new_thread (struct thread_info *t); static void mi_thread_exit (struct thread_info *t, int silent); @@ -118,7 +124,16 @@ mi_interpreter_init (struct interp *interp, int top_level) else gdb_assert_not_reached ("unhandled MI version"); - mi->uiout = mi_out_new (mi_version); + mi->mi_uiout = mi_out_new (mi_version); + mi->cli_uiout = cli_out_new (mi->out); + + /* There are installed even if MI is not the top level interpreter. + The callbacks themselves decide whether to be skipped. */ + observer_attach_signal_received (mi_on_signal_received); + observer_attach_end_stepping_range (mi_on_end_stepping_range); + observer_attach_signal_exited (mi_on_signal_exited); + observer_attach_exited (mi_on_exited); + observer_attach_no_history (mi_on_no_history); if (top_level) { @@ -431,14 +446,111 @@ restore_current_uiout_cleanup (void *arg) current_uiout = saved_uiout; } -/* Cleanup that destroys the a ui_out object. */ +/* Return the MI interpreter, if it is active -- either because it's + the top-level interpreter or the interpreter executing the current + command. Returns NULL if the MI interpreter is not being used. */ + +static struct interp * +find_mi_interpreter (void) +{ + struct interp *interp; + + interp = top_level_interpreter (); + if (ui_out_is_mi_like_p (interp_ui_out (interp))) + return interp; + + interp = command_interp (); + if (ui_out_is_mi_like_p (interp_ui_out (interp))) + return interp; + + return NULL; +} + +/* Return the MI_INTERP structure of the active MI interpreter. + Returns NULL if MI is not active. */ + +static struct mi_interp * +mi_interp_data (void) +{ + struct interp *interp = find_mi_interpreter (); + + if (interp != NULL) + return interp_data (interp); + return NULL; +} + +/* Observers for several run control events that print why the + inferior has stopped to both the the MI event channel and to the MI + console. If the MI interpreter is not active, print nothing. */ + +/* Observer for the signal_received notification. */ + +static void +mi_on_signal_received (enum gdb_signal siggnal) +{ + struct mi_interp *mi = mi_interp_data (); + + if (mi == NULL) + return; + + print_signal_received_reason (mi->mi_uiout, siggnal); + print_signal_received_reason (mi->cli_uiout, siggnal); +} + +/* Observer for the end_stepping_range notification. */ + +static void +mi_on_end_stepping_range (void) +{ + struct mi_interp *mi = mi_interp_data (); + + if (mi == NULL) + return; + + print_end_stepping_range_reason (mi->mi_uiout); + print_end_stepping_range_reason (mi->cli_uiout); +} + +/* Observer for the signal_exited notification. */ static void -ui_out_free_cleanup (void *arg) +mi_on_signal_exited (enum gdb_signal siggnal) { - struct ui_out *uiout = arg; + struct mi_interp *mi = mi_interp_data (); - ui_out_destroy (uiout); + if (mi == NULL) + return; + + print_signal_exited_reason (mi->mi_uiout, siggnal); + print_signal_exited_reason (mi->cli_uiout, siggnal); +} + +/* Observer for the exited notification. */ + +static void +mi_on_exited (int exitstatus) +{ + struct mi_interp *mi = mi_interp_data (); + + if (mi == NULL) + return; + + print_exited_reason (mi->mi_uiout, exitstatus); + print_exited_reason (mi->cli_uiout, exitstatus); +} + +/* Observer for the no_history notification. */ + +static void +mi_on_no_history (void) +{ + struct mi_interp *mi = mi_interp_data (); + + if (mi == NULL) + return; + + print_no_history_reason (mi->mi_uiout); + print_no_history_reason (mi->cli_uiout); } static void @@ -506,16 +618,12 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame) struct mi_interp *mi = top_level_interpreter_data (); struct target_waitstatus last; ptid_t last_ptid; - struct ui_out *cli_uiout; struct cleanup *old_chain; - /* Sets the current uiout to a new temporary CLI uiout - assigned to STREAM. */ - cli_uiout = cli_out_new (mi->out); - old_chain = make_cleanup (ui_out_free_cleanup, cli_uiout); - - make_cleanup (restore_current_uiout_cleanup, current_uiout); - current_uiout = cli_uiout; + /* Set the current uiout to CLI uiout temporarily. */ + old_chain = make_cleanup (restore_current_uiout_cleanup, + current_uiout); + current_uiout = mi->cli_uiout; get_last_target_status (&last_ptid, &last); print_stop_event (&last); @@ -972,7 +1080,7 @@ mi_ui_out (struct interp *interp) { struct mi_interp *mi = interp_data (interp); - return mi->uiout; + return mi->mi_uiout; } /* Save the original value of raw_stdout here when logging, so we can |