Move "tee" building down to interpreter::set_logging_proc
This patch gets rid of this hack in mi_set_logging: /* 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. */ By pushing down responsibility for the tee creation to the interpreter. I.e., pushing the CLI bits out of handle_redirections down to the CLI interpreter's set_logging_proc method. This fixes a few leaks that I spotted, and then confirmed with "valgrind --leak-check=full": [...] ==21429== 56 (32 direct, 24 indirect) bytes in 1 blocks are definitely lost in loss record 30,243 of 34,980 ==21429== at 0x4C29216: operator new(unsigned long) (vg_replace_malloc.c:334) ==21429== by 0x62D9A9: mi_set_logging(interp*, int, ui_file*, ui_file*) (mi-interp.c:1395) ==21429== by 0x810B8A: current_interp_set_logging(int, ui_file*, ui_file*) (interps.c:360) ==21429== by 0x61C537: handle_redirections(int) (cli-logging.c:162) ==21429== by 0x61C6EC: set_logging_on(char*, int) (cli-logging.c:190) ==21429== by 0x6163BE: do_cfunc(cmd_list_element*, char*, int) (cli-decode.c:105) ==21429== by 0x6193C1: cmd_func(cmd_list_element*, char*, int) (cli-decode.c:1913) ==21429== by 0x8DB790: execute_command(char*, int) (top.c:674) ==21429== by 0x632AE6: mi_execute_cli_command(char const*, int, char const*) (mi-main.c:2343) ==21429== by 0x6329BA: mi_cmd_execute(mi_parse*) (mi-main.c:2306) ==21429== by 0x631E19: captured_mi_execute_command(ui_out*, mi_parse*) (mi-main.c:1998) ==21429== by 0x632389: mi_execute_command(char const*, int) (mi-main.c:2163) ==21429== [...] ==26635== 24 bytes in 1 blocks are definitely lost in loss record 20,740 of 34,995 ==26635== at 0x4C29216: operator new(unsigned long) (vg_replace_malloc.c:334) ==26635== by 0x61C355: handle_redirections(int) (cli-logging.c:131) ==26635== by 0x61C6EC: set_logging_on(char*, int) (cli-logging.c:190) ==26635== by 0x6163BE: do_cfunc(cmd_list_element*, char*, int) (cli-decode.c:105) ==26635== by 0x6193C1: cmd_func(cmd_list_element*, char*, int) (cli-decode.c:1913) ==26635== by 0x8DB7BC: execute_command(char*, int) (top.c:674) ==26635== by 0x7B9132: command_handler(char*) (event-top.c:590) ==26635== by 0x7B94F7: command_line_handler(char*) (event-top.c:780) ==26635== by 0x7B8ABB: gdb_rl_callback_handler(char*) (event-top.c:213) ==26635== by 0x933CE9: rl_callback_read_char (callback.c:220) ==26635== by 0x7B89ED: gdb_rl_callback_read_char_wrapper_noexcept() (event-top.c:175) ==26635== by 0x7B8A49: gdb_rl_callback_read_char_wrapper(void*) (event-top.c:192) One is fixed by transfering ownership of the log file to the tee. In pseudo-patch, since the code was moved at the same time: - out = new tee_file (curr_output, false, logfile.get (), false); + out = new tee_file (curr_output, false, logfile.get (), true); The other is this bit in mi_set_logging: else { + delete mi->raw_stdout; I tried to split the leak fixes to a smaller preparatory patch, but that was difficult exactly because of the tee hack in handle_redirections -> mi_set_logging. gdb/ChangeLog: 2017-02-02 Pedro Alves <palves@redhat.com> * cli/cli-interp.c (struct saved_output_files, saved_output): Moved from cli/cli-logging.c. (cli_set_logging): New function. (cli_interp_procs): Install cli_set_logging. * cli/cli-interp.h (make_logging_output, cli_set_logging): Declare. * cli/cli-logging.c (struct saved_output_files, saved_output): Moved to cli/cli-interp.c. (pop_output_files): Don't save outputs here. (make_logging_output): New function. (handle_redirections): Don't build tee nor save previous outputs here. * interps.c (current_interp_set_logging): Change prototype. Assume there's always a set_logging_proc method installed. * interps.h (interp_set_logging_ftype): Change prototype. (current_interp_set_logging): Change prototype and adjust comment. * mi/mi-interp.c (mi_set_logging): Change protototype. Adjust to use make_logging_output. * tui/tui-interp.c (tui_interp_procs): Install cli_set_logging.
This commit is contained in:
parent
5be5dbf0ce
commit
616268b639
|
@ -1,5 +1,26 @@
|
|||
2017-02-02 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* cli/cli-interp.c (struct saved_output_files, saved_output):
|
||||
Moved from cli/cli-logging.c.
|
||||
(cli_set_logging): New function.
|
||||
(cli_interp_procs): Install cli_set_logging.
|
||||
* cli/cli-interp.h (make_logging_output, cli_set_logging):
|
||||
Declare.
|
||||
* cli/cli-logging.c (struct saved_output_files, saved_output):
|
||||
Moved to cli/cli-interp.c.
|
||||
(pop_output_files): Don't save outputs here.
|
||||
(make_logging_output): New function.
|
||||
(handle_redirections): Don't build tee nor save previous outputs
|
||||
here.
|
||||
* interps.c (current_interp_set_logging): Change prototype.
|
||||
Assume there's always a set_logging_proc method installed.
|
||||
* interps.h (interp_set_logging_ftype): Change prototype.
|
||||
(current_interp_set_logging): Change prototype and adjust comment.
|
||||
* mi/mi-interp.c (mi_set_logging): Change protototype. Adjust to
|
||||
use make_logging_output.
|
||||
* tui/tui-interp.c (tui_interp_procs): Install cli_set_logging.
|
||||
2017-02-02 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* cli/cli-logging.c (maybe_warn_already_logging): New factored out
|
||||
from ...
|
||||
(set_logging_overwrite): ... here.
|
||||
|
|
|
@ -373,6 +373,63 @@ cli_ui_out (struct interp *self)
|
|||
return cli->cli_uiout;
|
||||
}
|
||||
|
||||
/* These hold the pushed copies of the gdb output files.
|
||||
If NULL then nothing has yet been pushed. */
|
||||
struct saved_output_files
|
||||
{
|
||||
ui_file *out;
|
||||
ui_file *err;
|
||||
ui_file *log;
|
||||
ui_file *targ;
|
||||
ui_file *targerr;
|
||||
};
|
||||
static saved_output_files saved_output;
|
||||
|
||||
/* See cli-interp.h. */
|
||||
|
||||
void
|
||||
cli_set_logging (struct interp *interp,
|
||||
ui_file_up logfile, bool logging_redirect)
|
||||
{
|
||||
if (logfile != NULL)
|
||||
{
|
||||
saved_output.out = gdb_stdout;
|
||||
saved_output.err = gdb_stderr;
|
||||
saved_output.log = gdb_stdlog;
|
||||
saved_output.targ = gdb_stdtarg;
|
||||
saved_output.targerr = gdb_stdtargerr;
|
||||
|
||||
/* A raw pointer since ownership is transferred to
|
||||
gdb_stdout. */
|
||||
ui_file *output = make_logging_output (gdb_stdout,
|
||||
std::move (logfile),
|
||||
logging_redirect);
|
||||
gdb_stdout = output;
|
||||
gdb_stdlog = output;
|
||||
gdb_stderr = output;
|
||||
gdb_stdtarg = output;
|
||||
gdb_stdtargerr = output;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Only delete one of the files -- they are all set to the same
|
||||
value. */
|
||||
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.targerr;
|
||||
|
||||
saved_output.out = NULL;
|
||||
saved_output.err = NULL;
|
||||
saved_output.log = NULL;
|
||||
saved_output.targ = NULL;
|
||||
saved_output.targerr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* The CLI interpreter's vtable. */
|
||||
|
||||
static const struct interp_procs cli_interp_procs = {
|
||||
|
@ -381,7 +438,7 @@ static const struct interp_procs cli_interp_procs = {
|
|||
cli_interpreter_suspend, /* suspend_proc */
|
||||
cli_interpreter_exec, /* exec_proc */
|
||||
cli_ui_out, /* ui_out_proc */
|
||||
NULL, /* set_logging_proc */
|
||||
cli_set_logging, /* set_logging_proc */
|
||||
cli_interpreter_pre_command_loop, /* pre_command_loop_proc */
|
||||
cli_interpreter_supports_command_editing, /* supports_command_editing_proc */
|
||||
};
|
||||
|
|
|
@ -20,6 +20,24 @@
|
|||
|
||||
struct interp;
|
||||
|
||||
/* Make the output ui_file to use when logging is enabled.
|
||||
CURR_OUTPUT is the stream where output is currently being sent to
|
||||
(e.g., gdb_stdout for the CLI, raw output stream for the MI).
|
||||
LOGFILE is the log file already opened by the caller.
|
||||
LOGGING_REDIRECT is the value of the "set logging redirect"
|
||||
setting. If true, the resulting output is the logfile. If false,
|
||||
the output stream is a tee, with the log file as one of the
|
||||
outputs. Ownership of LOGFILE is transferred to the returned
|
||||
output file, which is an owning pointer. */
|
||||
extern ui_file *make_logging_output (ui_file *curr_output,
|
||||
ui_file_up logfile,
|
||||
bool logging_redirect);
|
||||
|
||||
/* The CLI interpreter's set_logging_proc method. Exported so other
|
||||
interpreters can reuse it. */
|
||||
extern void cli_set_logging (struct interp *interp,
|
||||
ui_file_up logfile, bool logging_redirect);
|
||||
|
||||
extern int cli_interpreter_supports_command_editing (struct interp *interp);
|
||||
|
||||
extern void cli_interpreter_pre_command_loop (struct interp *self);
|
||||
|
|
|
@ -22,17 +22,6 @@
|
|||
#include "ui-out.h"
|
||||
#include "interps.h"
|
||||
|
||||
/* These hold the pushed copies of the gdb output files.
|
||||
If NULL then nothing has yet been pushed. */
|
||||
struct saved_output_files
|
||||
{
|
||||
struct ui_file *out;
|
||||
struct ui_file *err;
|
||||
struct ui_file *log;
|
||||
struct ui_file *targ;
|
||||
struct ui_file *targerr;
|
||||
};
|
||||
static struct saved_output_files saved_output;
|
||||
static char *saved_filename;
|
||||
|
||||
static char *logging_filename;
|
||||
|
@ -90,37 +79,35 @@ show_logging_redirect (struct ui_file *file, int from_tty,
|
|||
static void
|
||||
pop_output_files (void)
|
||||
{
|
||||
if (current_interp_set_logging (0, NULL, NULL) == 0)
|
||||
{
|
||||
/* Only delete one of the files -- they are all set to the same
|
||||
value. */
|
||||
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.targerr;
|
||||
}
|
||||
|
||||
saved_output.out = NULL;
|
||||
saved_output.err = NULL;
|
||||
saved_output.log = NULL;
|
||||
saved_output.targ = NULL;
|
||||
saved_output.targerr = NULL;
|
||||
current_interp_set_logging (NULL, false);
|
||||
|
||||
/* Stay consistent with handle_redirections. */
|
||||
if (!current_uiout->is_mi_like_p ())
|
||||
current_uiout->redirect (NULL);
|
||||
}
|
||||
|
||||
/* See cli-interp.h. */
|
||||
|
||||
ui_file *
|
||||
make_logging_output (ui_file *curr_output, ui_file_up logfile,
|
||||
bool logging_redirect)
|
||||
{
|
||||
if (logging_redirect)
|
||||
return logfile.release ();
|
||||
else
|
||||
{
|
||||
/* Note that the "tee" takes ownership of the log file. */
|
||||
ui_file *out = new tee_file (curr_output, false,
|
||||
logfile.get (), true);
|
||||
logfile.release ();
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is a helper for the `set logging' command. */
|
||||
static void
|
||||
handle_redirections (int from_tty)
|
||||
{
|
||||
ui_file_up output;
|
||||
ui_file_up no_redirect_file;
|
||||
|
||||
if (saved_filename != NULL)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdout, "Already logging to %s.\n",
|
||||
|
@ -133,46 +120,27 @@ handle_redirections (int from_tty)
|
|||
perror_with_name (_("set logging"));
|
||||
|
||||
/* Redirects everything to gdb_stdout while this is running. */
|
||||
if (!logging_redirect)
|
||||
if (from_tty)
|
||||
{
|
||||
no_redirect_file = std::move (log);
|
||||
output.reset (new tee_file (gdb_stdout, 0, no_redirect_file.get (), 0));
|
||||
|
||||
if (from_tty)
|
||||
if (!logging_redirect)
|
||||
fprintf_unfiltered (gdb_stdout, "Copying output to %s.\n",
|
||||
logging_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
output = std::move (log);
|
||||
|
||||
if (from_tty)
|
||||
else
|
||||
fprintf_unfiltered (gdb_stdout, "Redirecting output to %s.\n",
|
||||
logging_filename);
|
||||
}
|
||||
|
||||
saved_filename = xstrdup (logging_filename);
|
||||
saved_output.out = gdb_stdout;
|
||||
saved_output.err = gdb_stderr;
|
||||
saved_output.log = gdb_stdlog;
|
||||
saved_output.targ = gdb_stdtarg;
|
||||
saved_output.targerr = gdb_stdtargerr;
|
||||
|
||||
/* Let the interpreter do anything it needs. */
|
||||
if (current_interp_set_logging (1, output.get (),
|
||||
no_redirect_file.get ()) == 0)
|
||||
{
|
||||
gdb_stdout = output.get ();
|
||||
gdb_stdlog = output.get ();
|
||||
gdb_stderr = output.get ();
|
||||
gdb_stdtarg = output.get ();
|
||||
gdb_stdtargerr = output.get ();
|
||||
}
|
||||
current_interp_set_logging (std::move (log), logging_redirect);
|
||||
|
||||
output.release ();
|
||||
no_redirect_file.release ();
|
||||
|
||||
/* Don't do the redirect for MI, it confuses MI's ui-out scheme. */
|
||||
/* Redirect the current ui-out object's output to the log. Use
|
||||
gdb_stdout, not log, since the interpreter may have created a tee
|
||||
that wraps the log. Don't do the redirect for MI, it confuses
|
||||
MI's ui-out scheme. Note that we may get here with MI as current
|
||||
interpreter, but with the current ui_out as a CLI ui_out, with
|
||||
'-interpreter-exec console "set logging on"'. */
|
||||
if (!current_uiout->is_mi_like_p ())
|
||||
current_uiout->redirect (gdb_stdout);
|
||||
}
|
||||
|
|
|
@ -346,18 +346,15 @@ interp_ui_out (struct interp *interp)
|
|||
return interp->procs->ui_out_proc (interp);
|
||||
}
|
||||
|
||||
int
|
||||
current_interp_set_logging (int start_log, struct ui_file *out,
|
||||
struct ui_file *logfile)
|
||||
void
|
||||
current_interp_set_logging (ui_file_up logfile,
|
||||
bool logging_redirect)
|
||||
{
|
||||
struct ui_interp_info *ui_interp = get_current_interp_info ();
|
||||
struct interp *interp = ui_interp->current_interpreter;
|
||||
|
||||
if (interp == NULL
|
||||
|| interp->procs->set_logging_proc == NULL)
|
||||
return 0;
|
||||
|
||||
return interp->procs->set_logging_proc (interp, start_log, out, logfile);
|
||||
return interp->procs->set_logging_proc (interp, std::move (logfile),
|
||||
logging_redirect);
|
||||
}
|
||||
|
||||
/* Temporarily overrides the current interpreter. */
|
||||
|
|
|
@ -49,9 +49,9 @@ typedef struct gdb_exception (interp_exec_ftype) (void *data,
|
|||
typedef void (interp_pre_command_loop_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);
|
||||
typedef void (interp_set_logging_ftype) (struct interp *self,
|
||||
ui_file_up logfile,
|
||||
bool logging_redirect);
|
||||
|
||||
typedef int (interp_supports_command_editing_ftype) (struct interp *self);
|
||||
|
||||
|
@ -109,13 +109,15 @@ extern int current_interp_named_p (const char *name);
|
|||
|
||||
/* 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);
|
||||
disabled. LOGFILE is the stream for the log file when logging is
|
||||
starting and is NULL when logging is ending. LOGGING_REDIRECT is
|
||||
the value of the "set logging redirect" setting. If true, the
|
||||
interpreter should configure the output streams to send output only
|
||||
to the logfile. If false, the interpreter should configure the
|
||||
output streams to send output to both the current output stream
|
||||
(i.e., the terminal) and the log file. */
|
||||
extern void current_interp_set_logging (ui_file_up logfile,
|
||||
bool logging_redirect);
|
||||
|
||||
/* Returns opaque data associated with the top-level interpreter. */
|
||||
extern void *top_level_interpreter_data (void);
|
||||
|
|
|
@ -1373,33 +1373,25 @@ mi_ui_out (struct interp *interp)
|
|||
/* 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)
|
||||
static void
|
||||
mi_set_logging (struct interp *interp,
|
||||
ui_file_up logfile, bool logging_redirect)
|
||||
{
|
||||
struct mi_interp *mi = (struct mi_interp *) interp_data (interp);
|
||||
|
||||
if (!mi)
|
||||
return 0;
|
||||
gdb_assert (mi != NULL);
|
||||
|
||||
if (start_log)
|
||||
if (logfile != NULL)
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
delete out;
|
||||
out = new tee_file (mi->raw_stdout, false, logfile, false);
|
||||
}
|
||||
|
||||
mi->saved_raw_stdout = mi->raw_stdout;
|
||||
mi->raw_stdout = out;
|
||||
mi->raw_stdout = make_logging_output (mi->raw_stdout,
|
||||
std::move (logfile),
|
||||
logging_redirect);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mi->raw_stdout;
|
||||
mi->raw_stdout = mi->saved_raw_stdout;
|
||||
mi->saved_raw_stdout = NULL;
|
||||
}
|
||||
|
@ -1409,8 +1401,6 @@ mi_set_logging (struct interp *interp, int start_log,
|
|||
mi->log->set_raw (mi->raw_stdout);
|
||||
mi->targ->set_raw (mi->raw_stdout);
|
||||
mi->event_channel->set_raw (mi->raw_stdout);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The MI interpreter's vtable. */
|
||||
|
|
|
@ -302,7 +302,7 @@ static const struct interp_procs tui_interp_procs = {
|
|||
tui_suspend,
|
||||
tui_exec,
|
||||
tui_ui_out,
|
||||
NULL,
|
||||
cli_set_logging,
|
||||
cli_interpreter_pre_command_loop,
|
||||
cli_interpreter_supports_command_editing,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue