* 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.
+2012-06-28 Stan Shebs <stan@codesourcery.com>
+
+ 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 <dje@google.com>
* symtab.c (lookup_symbol_aux_objfile): Use
** 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
#include "defs.h"
#include "gdbcmd.h"
#include "ui-out.h"
+#include "interps.h"
#include "gdb_assert.h"
#include "gdb_string.h"
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
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;
{
struct cleanup *cleanups;
struct ui_file *output;
+ struct ui_file *no_redirect_file = NULL;
if (saved_filename != NULL)
{
/* 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)
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
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)
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;
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;
};
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);
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;
}
const char *prefix,
char quote);
+extern void mi_console_set_raw (struct ui_file *console,
+ struct ui_file *raw);
+
#endif
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
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. */
+2012-06-28 Stan Shebs <stan@codesourcery.com>
+
+ * gdb.mi/mi-logging.exp: New file.
+
2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com>
Pedro Alves <palves@redhat.com>
--- /dev/null
+# 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 <http://www.gnu.org/licenses/>.
+
+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