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.
This commit is contained in:
Stan Shebs 2012-06-28 22:11:23 +00:00
parent fe54041627
commit 37ce89ebb2
10 changed files with 260 additions and 22 deletions

View File

@ -1,3 +1,21 @@
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> 2012-06-28 Doug Evans <dje@google.com>
* symtab.c (lookup_symbol_aux_objfile): Use * symtab.c (lookup_symbol_aux_objfile): Use

View File

@ -130,6 +130,8 @@
** New command -info-os is the MI equivalent of "info os". ** New command -info-os is the MI equivalent of "info os".
** Output logs ("set logging" and related) now include MI output.
* New commands * New commands
** "catch load" and "catch unload" can be used to stop when a shared ** "catch load" and "catch unload" can be used to stop when a shared

View File

@ -20,6 +20,7 @@
#include "defs.h" #include "defs.h"
#include "gdbcmd.h" #include "gdbcmd.h"
#include "ui-out.h" #include "ui-out.h"
#include "interps.h"
#include "gdb_assert.h" #include "gdb_assert.h"
#include "gdb_string.h" #include "gdb_string.h"
@ -115,11 +116,17 @@ set_logging_redirect (char *args, int from_tty, struct cmd_list_element *c)
logging_filename); logging_filename);
} }
gdb_stdout = output; /* Give the current interpreter a chance to do anything special that
gdb_stderr = output; it might need for logging, such as updating other channels. */
gdb_stdlog = output; if (current_interp_set_logging (1, output, NULL) == 0)
gdb_stdtarg = output; {
gdb_stdtargerr = output; gdb_stdout = output;
gdb_stdlog = output;
gdb_stderr = output;
gdb_stdtarg = output;
gdb_stdtargerr = output;
}
logging_no_redirect_file = new_logging_no_redirect_file; logging_no_redirect_file = new_logging_no_redirect_file;
/* There is a former output pushed on the ui_out_redirect stack. We /* 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 static void
pop_output_files (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) if (logging_no_redirect_file)
{ {
ui_file_delete (logging_no_redirect_file); ui_file_delete (logging_no_redirect_file);
logging_no_redirect_file = NULL; logging_no_redirect_file = NULL;
} }
gdb_stdout = saved_output.out;
gdb_stderr = saved_output.err; if (current_interp_set_logging (0, NULL, NULL) == 0)
gdb_stdlog = saved_output.log; {
gdb_stdtarg = saved_output.targ; /* Only delete one of the files -- they are all set to the same
gdb_stdtargerr = saved_output.targ; 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.out = NULL;
saved_output.err = NULL; saved_output.err = NULL;
saved_output.log = NULL; saved_output.log = NULL;
@ -175,6 +188,7 @@ handle_redirections (int from_tty)
{ {
struct cleanup *cleanups; struct cleanup *cleanups;
struct ui_file *output; struct ui_file *output;
struct ui_file *no_redirect_file = NULL;
if (saved_filename != NULL) if (saved_filename != NULL)
{ {
@ -191,7 +205,7 @@ handle_redirections (int from_tty)
/* Redirects everything to gdb_stdout while this is running. */ /* Redirects everything to gdb_stdout while this is running. */
if (!logging_redirect) 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); output = tee_file_new (gdb_stdout, 0, no_redirect_file, 0);
if (output == NULL) if (output == NULL)
@ -220,14 +234,22 @@ handle_redirections (int from_tty)
saved_output.targ = gdb_stdtarg; saved_output.targ = gdb_stdtarg;
saved_output.targerr = gdb_stdtargerr; saved_output.targerr = gdb_stdtargerr;
gdb_stdout = output; /* Let the interpreter do anything it needs. */
gdb_stderr = output; if (current_interp_set_logging (1, output, no_redirect_file) == 0)
gdb_stdlog = output; {
gdb_stdtarg = output; gdb_stdout = output;
gdb_stdtargerr = output; gdb_stdlog = output;
gdb_stderr = output;
gdb_stdtarg = output;
gdb_stdtargerr = output;
}
if (ui_out_redirect (current_uiout, output) < 0) /* Don't do the redirect for MI, it confuses MI's ui-out scheme. */
warning (_("Current output protocol does not support redirection")); 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 static void

View File

@ -251,6 +251,19 @@ interp_ui_out (struct interp *interp)
return current_interpreter->procs->ui_out_proc (current_interpreter); 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. */ /* Temporarily overrides the current interpreter. */
struct interp * struct interp *
interp_set_temp (const char *name) interp_set_temp (const char *name)

View File

@ -45,6 +45,10 @@ typedef struct gdb_exception (interp_exec_ftype) (void *data,
typedef void (interp_command_loop_ftype) (void *data); typedef void (interp_command_loop_ftype) (void *data);
typedef struct ui_out *(interp_ui_out_ftype) (struct interp *self); 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 struct interp_procs
{ {
interp_init_ftype *init_proc; interp_init_ftype *init_proc;
@ -59,6 +63,11 @@ struct interp_procs
formatter. */ formatter. */
interp_ui_out_ftype *ui_out_proc; 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; 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_named_p (const char *name);
extern int current_interp_display_prompt_p (void); extern int current_interp_display_prompt_p (void);
extern void current_interp_command_loop (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. */ /* Returns opaque data associated with the top-level interpreter. */
extern void *top_level_interpreter_data (void); extern void *top_level_interpreter_data (void);
extern struct interp *top_level_interpreter (void); extern struct interp *top_level_interpreter (void);

View File

@ -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_put (mi_console->buffer, mi_console_raw_packet, mi_console);
ui_file_rewind (mi_console->buffer); 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;
} }

View File

@ -24,4 +24,7 @@ extern struct ui_file *mi_console_file_new (struct ui_file *raw,
const char *prefix, const char *prefix,
char quote); char quote);
extern void mi_console_set_raw (struct ui_file *console,
struct ui_file *raw);
#endif #endif

View File

@ -755,6 +755,54 @@ mi_ui_out (struct interp *interp)
return mi->uiout; 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 */ extern initialize_file_ftype _initialize_mi_interp; /* -Wmissing-prototypes */
void void
@ -767,7 +815,8 @@ _initialize_mi_interp (void)
mi_interpreter_suspend, /* suspend_proc */ mi_interpreter_suspend, /* suspend_proc */
mi_interpreter_exec, /* exec_proc */ mi_interpreter_exec, /* exec_proc */
mi_interpreter_prompt_p, /* prompt_proc_p */ 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. */ /* The various interpreter levels. */

View File

@ -1,3 +1,7 @@
2012-06-28 Stan Shebs <stan@codesourcery.com>
* gdb.mi/mi-logging.exp: New file.
2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com> 2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com>
Pedro Alves <palves@redhat.com> Pedro Alves <palves@redhat.com>

View File

@ -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 <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