mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 12:03:41 +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>
|
2012-06-28 Doug Evans <dje@google.com>
|
||||||
|
|
||||||
* symtab.c (lookup_symbol_aux_objfile): Use
|
* 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".
|
** 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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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. */
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
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