? testsuite/gdb.mi/crud ? testsuite/gdb.mi/foo Index: NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.528 diff -u -p -r1.528 NEWS --- NEWS 23 Jun 2012 22:23:46 -0000 1.528 +++ NEWS 25 Jun 2012 06:42:06 -0000 @@ -127,6 +127,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 Index: interps.c =================================================================== RCS file: /cvs/src/src/gdb/interps.c,v retrieving revision 1.46 diff -u -p -r1.46 interps.c --- interps.c 13 Jun 2012 15:47:14 -0000 1.46 +++ interps.c 25 Jun 2012 06:42:06 -0000 @@ -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) Index: interps.h =================================================================== RCS file: /cvs/src/src/gdb/interps.h,v retrieving revision 1.23 diff -u -p -r1.23 interps.h --- interps.h 8 May 2012 18:49:41 -0000 1.23 +++ interps.h 25 Jun 2012 06:42:06 -0000 @@ -45,6 +45,10 @@ typedef struct gdb_exception (interp_exe 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 (c 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); Index: cli/cli-logging.c =================================================================== RCS file: /cvs/src/src/gdb/cli/cli-logging.c,v retrieving revision 1.31 diff -u -p -r1.31 cli-logging.c --- cli/cli-logging.c 4 Jan 2012 08:17:17 -0000 1.31 +++ cli/cli-logging.c 25 Jun 2012 06:42:06 -0000 @@ -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 fr 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 *f 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 Index: mi/mi-console.c =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-console.c,v retrieving revision 1.21 diff -u -p -r1.21 mi-console.c --- mi/mi-console.c 6 Mar 2012 22:48:53 -0000 1.21 +++ mi/mi-console.c 25 Jun 2012 06:42:06 -0000 @@ -135,4 +135,21 @@ mi_console_file_flush (struct ui_file *f 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; } Index: mi/mi-console.h =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-console.h,v retrieving revision 1.13 diff -u -p -r1.13 mi-console.h --- mi/mi-console.h 4 Jan 2012 08:17:24 -0000 1.13 +++ mi/mi-console.h 25 Jun 2012 06:42:06 -0000 @@ -24,4 +24,7 @@ extern struct ui_file *mi_console_file_n const char *prefix, char quote); +extern void mi_console_set_raw (struct ui_file *console, + struct ui_file *raw); + #endif Index: mi/mi-interp.c =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-interp.c,v retrieving revision 1.74 diff -u -p -r1.74 mi-interp.c --- mi/mi-interp.c 6 Jun 2012 06:04:46 -0000 1.74 +++ mi/mi-interp.c 25 Jun 2012 06:42:06 -0000 @@ -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. */ Index: testsuite/gdb.mi/mi-logging.exp =================================================================== RCS file: testsuite/gdb.mi/mi-logging.exp diff -N testsuite/gdb.mi/mi-logging.exp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.mi/mi-logging.exp 25 Jun 2012 06:42:06 -0000 @@ -0,0 +1,93 @@ +# 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 + +pass "Log file has '$logcontent'" + +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 + +pass "Log file has '$logcontent'" + +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 +return 0