mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-23 18:14:13 +08:00
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:
parent
fe54041627
commit
37ce89ebb2
@ -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>
|
||||
|
||||
* symtab.c (lookup_symbol_aux_objfile): Use
|
||||
|
2
gdb/NEWS
2
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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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>
|
||||
Pedro Alves <palves@redhat.com>
|
||||
|
||||
|
90
gdb/testsuite/gdb.mi/mi-logging.exp
Normal file
90
gdb/testsuite/gdb.mi/mi-logging.exp
Normal 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
|
Loading…
Reference in New Issue
Block a user