From 37ce89ebb295e6ed0fc0bf5a4eb2e718ed0fb2ee Mon Sep 17 00:00:00 2001 From: Stan Shebs Date: Thu, 28 Jun 2012 22:11:23 +0000 Subject: [PATCH] Make logging work for MI. * NEWS: Mention it. * interps.h (interp_set_logging_ftype): New typedef. (struct interp_procs): New field set_logging_proc. (current_interp_set_logging): Declare. * interps.c (current_interp_set_logging): New function. * cli/cli-logging.c: Include interps.h. (set_logging_redirect): Call current_interp_set_logging. (pop_output_files): Ditto. (handle_redirections): Ditto, plus skip ui-out redirect if MI. * mi/mi-console.h (mi_console_set_raw): Declare. * mi/mi-console.c (mi_console_set_raw): New function. * mi/mi-interp.c (saved_raw_stdout): New global. (mi_set_logging): New function. (_initialize_mi_interp): Add it to interp procs. * gdb.mi/mi-logging.exp: New file. --- gdb/ChangeLog | 18 ++++++ gdb/NEWS | 2 + gdb/cli/cli-logging.c | 64 +++++++++++++------- gdb/interps.c | 13 +++++ gdb/interps.h | 20 +++++++ gdb/mi/mi-console.c | 17 ++++++ gdb/mi/mi-console.h | 3 + gdb/mi/mi-interp.c | 51 +++++++++++++++- gdb/testsuite/ChangeLog | 4 ++ gdb/testsuite/gdb.mi/mi-logging.exp | 90 +++++++++++++++++++++++++++++ 10 files changed, 260 insertions(+), 22 deletions(-) create mode 100644 gdb/testsuite/gdb.mi/mi-logging.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index e17c44eee8e..ad9b710ef80 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2012-06-28 Stan Shebs + + Make logging work for MI. + * NEWS: Mention it. + * interps.h (interp_set_logging_ftype): New typedef. + (struct interp_procs): New field set_logging_proc. + (current_interp_set_logging): Declare. + * interps.c (current_interp_set_logging): New function. + * cli/cli-logging.c: Include interps.h. + (set_logging_redirect): Call current_interp_set_logging. + (pop_output_files): Ditto. + (handle_redirections): Ditto, plus skip ui-out redirect if MI. + * mi/mi-console.h (mi_console_set_raw): Declare. + * mi/mi-console.c (mi_console_set_raw): New function. + * mi/mi-interp.c (saved_raw_stdout): New global. + (mi_set_logging): New function. + (_initialize_mi_interp): Add it to interp procs. + 2012-06-28 Doug Evans * symtab.c (lookup_symbol_aux_objfile): Use diff --git a/gdb/NEWS b/gdb/NEWS index b1f67759fa7..5cfb48a1445 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -130,6 +130,8 @@ ** New command -info-os is the MI equivalent of "info os". + ** Output logs ("set logging" and related) now include MI output. + * New commands ** "catch load" and "catch unload" can be used to stop when a shared diff --git a/gdb/cli/cli-logging.c b/gdb/cli/cli-logging.c index 0c5bda87975..6ff064ad6f3 100644 --- a/gdb/cli/cli-logging.c +++ b/gdb/cli/cli-logging.c @@ -20,6 +20,7 @@ #include "defs.h" #include "gdbcmd.h" #include "ui-out.h" +#include "interps.h" #include "gdb_assert.h" #include "gdb_string.h" @@ -115,11 +116,17 @@ set_logging_redirect (char *args, int from_tty, struct cmd_list_element *c) logging_filename); } - gdb_stdout = output; - gdb_stderr = output; - gdb_stdlog = output; - gdb_stdtarg = output; - gdb_stdtargerr = output; + /* Give the current interpreter a chance to do anything special that + it might need for logging, such as updating other channels. */ + if (current_interp_set_logging (1, output, NULL) == 0) + { + gdb_stdout = output; + gdb_stdlog = output; + gdb_stderr = output; + gdb_stdtarg = output; + gdb_stdtargerr = output; + } + logging_no_redirect_file = new_logging_no_redirect_file; /* There is a former output pushed on the ui_out_redirect stack. We @@ -147,19 +154,25 @@ show_logging_redirect (struct ui_file *file, int from_tty, static void pop_output_files (void) { - /* Only delete one of the files -- they are all set to the same - value. */ - ui_file_delete (gdb_stdout); if (logging_no_redirect_file) { ui_file_delete (logging_no_redirect_file); logging_no_redirect_file = NULL; } - gdb_stdout = saved_output.out; - gdb_stderr = saved_output.err; - gdb_stdlog = saved_output.log; - gdb_stdtarg = saved_output.targ; - gdb_stdtargerr = saved_output.targ; + + if (current_interp_set_logging (0, NULL, NULL) == 0) + { + /* Only delete one of the files -- they are all set to the same + value. */ + ui_file_delete (gdb_stdout); + + gdb_stdout = saved_output.out; + gdb_stderr = saved_output.err; + gdb_stdlog = saved_output.log; + gdb_stdtarg = saved_output.targ; + gdb_stdtargerr = saved_output.targ; + } + saved_output.out = NULL; saved_output.err = NULL; saved_output.log = NULL; @@ -175,6 +188,7 @@ handle_redirections (int from_tty) { struct cleanup *cleanups; struct ui_file *output; + struct ui_file *no_redirect_file = NULL; if (saved_filename != NULL) { @@ -191,7 +205,7 @@ handle_redirections (int from_tty) /* Redirects everything to gdb_stdout while this is running. */ if (!logging_redirect) { - struct ui_file *no_redirect_file = output; + no_redirect_file = output; output = tee_file_new (gdb_stdout, 0, no_redirect_file, 0); if (output == NULL) @@ -220,14 +234,22 @@ handle_redirections (int from_tty) saved_output.targ = gdb_stdtarg; saved_output.targerr = gdb_stdtargerr; - gdb_stdout = output; - gdb_stderr = output; - gdb_stdlog = output; - gdb_stdtarg = output; - gdb_stdtargerr = output; + /* Let the interpreter do anything it needs. */ + if (current_interp_set_logging (1, output, no_redirect_file) == 0) + { + gdb_stdout = output; + gdb_stdlog = output; + gdb_stderr = output; + gdb_stdtarg = output; + gdb_stdtargerr = output; + } - if (ui_out_redirect (current_uiout, output) < 0) - warning (_("Current output protocol does not support redirection")); + /* Don't do the redirect for MI, it confuses MI's ui-out scheme. */ + if (!ui_out_is_mi_like_p (current_uiout)) + { + if (ui_out_redirect (current_uiout, output) < 0) + warning (_("Current output protocol does not support redirection")); + } } static void diff --git a/gdb/interps.c b/gdb/interps.c index 9b24c59eb32..698e26ee835 100644 --- a/gdb/interps.c +++ b/gdb/interps.c @@ -251,6 +251,19 @@ interp_ui_out (struct interp *interp) return current_interpreter->procs->ui_out_proc (current_interpreter); } +int +current_interp_set_logging (int start_log, struct ui_file *out, + struct ui_file *logfile) +{ + if (current_interpreter == NULL + || current_interpreter->procs->set_logging_proc == NULL) + return 0; + + return current_interpreter->procs->set_logging_proc (current_interpreter, + start_log, out, + logfile); +} + /* Temporarily overrides the current interpreter. */ struct interp * interp_set_temp (const char *name) diff --git a/gdb/interps.h b/gdb/interps.h index bbf083814a2..2102eca34e9 100644 --- a/gdb/interps.h +++ b/gdb/interps.h @@ -45,6 +45,10 @@ typedef struct gdb_exception (interp_exec_ftype) (void *data, typedef void (interp_command_loop_ftype) (void *data); typedef struct ui_out *(interp_ui_out_ftype) (struct interp *self); +typedef int (interp_set_logging_ftype) (struct interp *self, int start_log, + struct ui_file *out, + struct ui_file *logfile); + struct interp_procs { interp_init_ftype *init_proc; @@ -59,6 +63,11 @@ struct interp_procs formatter. */ interp_ui_out_ftype *ui_out_proc; + /* Provides a hook for interpreters to do any additional + setup/cleanup that they might need when logging is enabled or + disabled. */ + interp_set_logging_ftype *set_logging_proc; + interp_command_loop_ftype *command_loop_proc; }; @@ -74,6 +83,17 @@ extern struct interp *interp_set_temp (const char *name); extern int current_interp_named_p (const char *name); extern int current_interp_display_prompt_p (void); extern void current_interp_command_loop (void); + +/* Call this function to give the current interpreter an opportunity + to do any special handling of streams when logging is enabled or + disabled. START_LOG is 1 when logging is starting, 0 when it ends, + and OUT is the stream for the log file; it will be NULL when + logging is ending. LOGFILE is non-NULL if the output streams + are to be tees, with the log file as one of the outputs. */ + +extern int current_interp_set_logging (int start_log, struct ui_file *out, + struct ui_file *logfile); + /* Returns opaque data associated with the top-level interpreter. */ extern void *top_level_interpreter_data (void); extern struct interp *top_level_interpreter (void); diff --git a/gdb/mi/mi-console.c b/gdb/mi/mi-console.c index 102b8bee493..b04e65cc447 100644 --- a/gdb/mi/mi-console.c +++ b/gdb/mi/mi-console.c @@ -135,4 +135,21 @@ mi_console_file_flush (struct ui_file *file) ui_file_put (mi_console->buffer, mi_console_raw_packet, mi_console); ui_file_rewind (mi_console->buffer); + +} + +/* Change the underlying stream of the console directly; this is + useful as a minimum-impact way to reflect external changes like + logging enable/disable. */ + +void +mi_console_set_raw (struct ui_file *file, struct ui_file *raw) +{ + struct mi_console_file *mi_console = ui_file_data (file); + + if (mi_console->magic != &mi_console_file_magic) + internal_error (__FILE__, __LINE__, + _("mi_console_file_set_raw: bad magic number")); + + mi_console->raw = raw; } diff --git a/gdb/mi/mi-console.h b/gdb/mi/mi-console.h index 9727eb6c797..7e9009fb498 100644 --- a/gdb/mi/mi-console.h +++ b/gdb/mi/mi-console.h @@ -24,4 +24,7 @@ extern struct ui_file *mi_console_file_new (struct ui_file *raw, const char *prefix, char quote); +extern void mi_console_set_raw (struct ui_file *console, + struct ui_file *raw); + #endif diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index ac0af70b5a2..b48713676e1 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -755,6 +755,54 @@ mi_ui_out (struct interp *interp) return mi->uiout; } +/* Save the original value of raw_stdout here when logging, so we can + restore correctly when done. */ + +static struct ui_file *saved_raw_stdout; + +/* Do MI-specific logging actions; save raw_stdout, and change all + the consoles to use the supplied ui-file(s). */ + +static int +mi_set_logging (struct interp *interp, int start_log, + struct ui_file *out, struct ui_file *logfile) +{ + struct mi_interp *mi = interp_data (interp); + + if (!mi) + return 0; + + if (start_log) + { + /* The tee created already is based on gdb_stdout, which for MI + is a console and so we end up in an infinite loop of console + writing to ui_file writing to console etc. So discard the + existing tee (it hasn't been used yet, and MI won't ever use + it), and create one based on raw_stdout instead. */ + if (logfile) + { + ui_file_delete (out); + out = tee_file_new (raw_stdout, 0, logfile, 0); + } + + saved_raw_stdout = raw_stdout; + raw_stdout = out; + } + else + { + raw_stdout = saved_raw_stdout; + saved_raw_stdout = NULL; + } + + mi_console_set_raw (mi->out, raw_stdout); + mi_console_set_raw (mi->err, raw_stdout); + mi_console_set_raw (mi->log, raw_stdout); + mi_console_set_raw (mi->targ, raw_stdout); + mi_console_set_raw (mi->event_channel, raw_stdout); + + return 1; +} + extern initialize_file_ftype _initialize_mi_interp; /* -Wmissing-prototypes */ void @@ -767,7 +815,8 @@ _initialize_mi_interp (void) mi_interpreter_suspend, /* suspend_proc */ mi_interpreter_exec, /* exec_proc */ mi_interpreter_prompt_p, /* prompt_proc_p */ - mi_ui_out /* ui_out_proc */ + mi_ui_out, /* ui_out_proc */ + mi_set_logging /* set_logging_proc */ }; /* The various interpreter levels. */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 0e857df5299..44c72d8c77e 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2012-06-28 Stan Shebs + + * gdb.mi/mi-logging.exp: New file. + 2012-06-28 Jan Kratochvil Pedro Alves diff --git a/gdb/testsuite/gdb.mi/mi-logging.exp b/gdb/testsuite/gdb.mi/mi-logging.exp new file mode 100644 index 00000000000..d62bfe6bc1f --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-logging.exp @@ -0,0 +1,90 @@ +# Copyright 2012 Free Software Foundation, Inc. + +# 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 . + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +gdb_exit +if [mi_gdb_start] { + continue +} + +set testfile basics +set srcfile "$testfile.c" +set executable ${testfile} +set binfile $objdir/$subdir/$testfile +set opts {debug} + +if [build_executable $testfile.exp $executable $srcfile $opts] { + untested mi-logging.exp + return -1; +} + +if {[mi_run_to_main] < 0} { + return -1 +} + +set milogfile "milog.txt" + +mi_gdb_test "-gdb-set logging file $milogfile" ".*" + +mi_gdb_test "-gdb-set logging overwrite on" ".*" + +mi_gdb_test "-gdb-set logging on" ".*" "logging on" + +mi_step "logged step" + +mi_next "logged next" + +mi_gdb_test "-gdb-set logging off" ".*" "logging off" + +set chan [open $milogfile] +set logcontent [read $chan] +close $chan + +set mi_log_prompt "\[(\]gdb\[)\] \[\r\n\]+" + +if [regexp "\\^done\[\r\n\]+$mi_log_prompt\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt" $logcontent] { + pass "Log file contents" +} else { + fail "Log file contents" +} + +# Now try the redirect, which writes into the file only. + +mi_gdb_test "-gdb-set logging redirect on" ".*" "redirect logging on" + +# Since all output will be going into the file, just keep sending commands +# and don't expect anything to appear until logging is turned off. + +send_gdb "1001-gdb-set logging on\n" +send_gdb "1002-exec-step\n" +send_gdb "1003-exec-next\n" + +mi_gdb_test "1004-gdb-set logging off" ".*" "redirect logging off" + +set chan [open $milogfile] +set logcontent [read $chan] +close $chan + +if [regexp "1001\\^done\[\r\n\]+$mi_log_prompt.*1002\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt.*1003\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt" $logcontent] { + pass "Redirect log file contents" +} else { + fail "Redirect log file contents" +} + +mi_gdb_exit + +remote_file host delete $milogfile