Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Tom Tromey <tom@tromey.com>
To: gdb-patches@sourceware.org
Cc: Tom Tromey <tom@tromey.com>
Subject: [PATCH v3 15/21] Rewrite output redirection and logging
Date: Fri, 30 Jan 2026 06:17:29 -0700	[thread overview]
Message-ID: <20260130-pr-28948-logging-5-v3-15-3eec47ef3cba@tromey.com> (raw)
In-Reply-To: <20260130-pr-28948-logging-5-v3-0-3eec47ef3cba@tromey.com>

This patch changes how gdb output redirection is done.

Currently, output is done via the UI.  gdb_stdout, for example, is a
define the expands to an lvalue referencing a field in the current UI.
When redirecting, this field may temporarily be reset; and when
logging is enabled or disabled, this is also done.

This has lead to bugs where the combination of redirection and logging
results in use-after-free.  Crashes are readily observable; see the
new test cases.

This patch upends this.  Now, gdb_stdout is simply an rvalue, and
refers to the current interpreter.  The interpreter provides ui_files
that do whatever rewriting is needed (mostly for MI); then output is
forward to the current UI via an indirection (see the new
ui::passthrough_file).

The ui provides paging, logging, timestamps, and the final stream that
writes to an actual file descriptor.

Redirection is handled at the ui layer.  Rather than changing the
output pipeline, new ui_files are simply swapped in by rewriting
pointers, hopefully with a scoped_restore.

Redirecting at the ui layer means that interpreter rewriting is still
applied when capturing output.  This fixes one of the reported bugs.

Not changing the pipeline means that the problems with the combination
of redirect and logging simply vanish.  Logging just changes a flag
and doesn't involve object destruction.  This does mean, though, that
spots capturing output are responsible for also having a logging_file
in the mix, should logging be desired.  (IMO, it generally is, and
that's what I've implemented here.)

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=17697
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28620
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28798
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28948
---
 gdb/buffered-streams.c                   | 24 ++++----
 gdb/cli/cli-interp.c                     | 70 +----------------------
 gdb/cli/cli-interp.h                     | 22 --------
 gdb/cli/cli-logging.c                    | 27 +--------
 gdb/fork-child.c                         |  4 +-
 gdb/guile/scm-ports.c                    | 14 +++--
 gdb/interps.c                            | 25 +++-----
 gdb/interps.h                            | 66 ++++++++++++++--------
 gdb/main.c                               |  3 +-
 gdb/mi/mi-console.c                      | 10 ----
 gdb/mi/mi-console.h                      |  3 -
 gdb/mi/mi-interp.c                       | 64 ++-------------------
 gdb/mi/mi-interp.h                       | 18 +-----
 gdb/python/py-dap.c                      | 10 +---
 gdb/testsuite/gdb.base/early-logging.exp | 35 ++++++++++++
 gdb/testsuite/gdb.python/python.exp      | 16 ++++++
 gdb/top.c                                | 97 ++++++++++++++++++++++++++------
 gdb/tui/tui-interp.c                     | 15 -----
 gdb/tui/tui-io.c                         | 40 +++++++------
 gdb/ui.c                                 | 18 ++++--
 gdb/ui.h                                 | 70 ++++++++++++++++++-----
 gdb/utils.h                              | 57 +++++++++++++++----
 22 files changed, 358 insertions(+), 350 deletions(-)

diff --git a/gdb/buffered-streams.c b/gdb/buffered-streams.c
index 71122c7f237..16a202ce1bb 100644
--- a/gdb/buffered-streams.c
+++ b/gdb/buffered-streams.c
@@ -89,16 +89,16 @@ get_unbuffered (ui_file *stream)
 }
 
 buffered_streams::buffered_streams (buffer_group *group, ui_out *uiout)
-  : m_buffered_stdout (group, gdb_stdout),
-    m_buffered_stderr (group, gdb_stderr),
-    m_buffered_stdlog (group, gdb_stdlog),
-    m_buffered_stdtarg (group, gdb_stdtarg),
+  : m_buffered_stdout (group, *redirectable_stdout ()),
+    m_buffered_stderr (group, *redirectable_stderr ()),
+    m_buffered_stdlog (group, *redirectable_stdlog ()),
+    m_buffered_stdtarg (group, *redirectable_stdtarg ()),
     m_uiout (uiout)
 {
-  gdb_stdout = &m_buffered_stdout;
-  gdb_stderr = &m_buffered_stderr;
-  gdb_stdlog = &m_buffered_stdlog;
-  gdb_stdtarg = &m_buffered_stdtarg;
+  *redirectable_stdout () = &m_buffered_stdout;
+  *redirectable_stderr () = &m_buffered_stderr;
+  *redirectable_stdlog () = &m_buffered_stdlog;
+  *redirectable_stdtarg () = &m_buffered_stdtarg;
 
   ui_file *stream = current_uiout->current_stream ();
   if (stream != nullptr)
@@ -127,10 +127,10 @@ buffered_streams::remove_buffers ()
 
   m_buffers_in_place = false;
 
-  gdb_stdout = m_buffered_stdout.stream ();
-  gdb_stderr = m_buffered_stderr.stream ();
-  gdb_stdlog = m_buffered_stdlog.stream ();
-  gdb_stdtarg = m_buffered_stdtarg.stream ();
+  *redirectable_stdout () = m_buffered_stdout.stream ();
+  *redirectable_stderr () = m_buffered_stderr.stream ();
+  *redirectable_stdlog () = m_buffered_stdlog.stream ();
+  *redirectable_stdtarg () = m_buffered_stdtarg.stream ();
 
   if (m_buffered_current_uiout.has_value ())
     current_uiout->redirect (nullptr);
diff --git a/gdb/cli/cli-interp.c b/gdb/cli/cli-interp.c
index 91e9b164830..55892d2ce28 100644
--- a/gdb/cli/cli-interp.c
+++ b/gdb/cli/cli-interp.c
@@ -59,7 +59,7 @@ class cli_interp final : public cli_interp_base
 
 cli_interp::cli_interp (const char *name)
   : cli_interp_base (name),
-    m_cli_uiout (new cli_ui_out (gdb_stdout))
+    m_cli_uiout (new cli_ui_out (m_stdout.get ()))
 {
 }
 
@@ -183,27 +183,10 @@ void
 cli_interp::resume ()
 {
   struct ui *ui = current_ui;
-  struct ui_file *stream;
-
-  /*sync_execution = 1; */
-
-  /* gdb_setup_readline will change gdb_stdout.  If the CLI was
-     previously writing to gdb_stdout, then set it to the new
-     gdb_stdout afterwards.  */
-
-  stream = m_cli_uiout->set_stream (gdb_stdout);
-  if (stream != gdb_stdout)
-    {
-      m_cli_uiout->set_stream (stream);
-      stream = NULL;
-    }
 
   gdb_setup_readline (1);
 
   ui->input_handler = command_line_handler;
-
-  if (stream != NULL)
-    m_cli_uiout->set_stream (gdb_stdout);
 }
 
 void
@@ -252,57 +235,6 @@ cli_interp::interp_ui_out ()
   return m_cli_uiout.get ();
 }
 
-/* See cli-interp.h.  */
-
-void
-cli_interp_base::set_logging (ui_file_up logfile, bool logging_redirect,
-			      bool debug_redirect)
-{
-  if (logfile != nullptr)
-    {
-      gdb_assert (m_saved_output == nullptr);
-      m_saved_output = std::make_unique<saved_output_files> ();
-      m_saved_output->out = gdb_stdout;
-      m_saved_output->err = gdb_stderr;
-      m_saved_output->log = gdb_stdlog;
-      m_saved_output->targ = gdb_stdtarg;
-
-      ui_file *logfile_p = logfile.get ();
-      m_saved_output->logfile_holder = std::move (logfile);
-
-      /* The new stdout and stderr only depend on whether logging
-	 redirection is being done.  */
-      ui_file *new_stdout = logfile_p;
-      ui_file *new_stderr = logfile_p;
-      if (!logging_redirect)
-	{
-	  m_saved_output->stdout_holder.reset
-	    (new tee_file (gdb_stdout, logfile_p));
-	  new_stdout = m_saved_output->stdout_holder.get ();
-	  m_saved_output->stderr_holder.reset
-	    (new tee_file (gdb_stderr, logfile_p));
-	  new_stderr = m_saved_output->stderr_holder.get ();
-	}
-
-      m_saved_output->stdlog_holder.reset
-	(new timestamped_file (debug_redirect ? logfile_p : new_stderr));
-
-      gdb_stdout = new_stdout;
-      gdb_stdlog = m_saved_output->stdlog_holder.get ();
-      gdb_stderr = new_stderr;
-      gdb_stdtarg = new_stderr;
-    }
-  else
-    {
-      gdb_stdout = m_saved_output->out;
-      gdb_stderr = m_saved_output->err;
-      gdb_stdlog = m_saved_output->log;
-      gdb_stdtarg = m_saved_output->targ;
-
-      m_saved_output.reset (nullptr);
-    }
-}
-
 /* Factory for CLI interpreters.  */
 
 static struct interp *
diff --git a/gdb/cli/cli-interp.h b/gdb/cli/cli-interp.h
index a766e38873d..c8428acfa10 100644
--- a/gdb/cli/cli-interp.h
+++ b/gdb/cli/cli-interp.h
@@ -28,8 +28,6 @@ class cli_interp_base : public interp
   explicit cli_interp_base (const char *name);
   virtual ~cli_interp_base () = 0;
 
-  void set_logging (ui_file_up logfile, bool logging_redirect,
-		    bool debug_redirect) override;
   void pre_command_loop () override;
   bool supports_command_editing () override;
 
@@ -41,26 +39,6 @@ class cli_interp_base : public interp
   void on_sync_execution_done () override;
   void on_command_error () override;
   void on_user_selected_context_changed (user_selected_what selection) override;
-
-private:
-  struct saved_output_files
-  {
-    /* Saved gdb_stdout, gdb_stderr, etc.  */
-    ui_file *out;
-    ui_file *err;
-    ui_file *log;
-    ui_file *targ;
-    /* When redirecting, some or all of these may be non-null
-       depending on the logging mode.  */
-    ui_file_up stdout_holder;
-    ui_file_up stderr_holder;
-    ui_file_up stdlog_holder;
-    ui_file_up logfile_holder;
-  };
-
-  /* These hold the pushed copies of the gdb output files.  If NULL
-     then nothing has yet been pushed.  */
-  std::unique_ptr<saved_output_files> m_saved_output;
 };
 
 /* Returns true if the current stop should be printed to
diff --git a/gdb/cli/cli-logging.c b/gdb/cli/cli-logging.c
index 6f2a6287700..c046e9131bc 100644
--- a/gdb/cli/cli-logging.c
+++ b/gdb/cli/cli-logging.c
@@ -213,17 +213,6 @@ logging_file<T>::puts_unfiltered (const char *str)
 template class logging_file<ui_file *>;
 template class logging_file<ui_file_up>;
 
-/* If we've pushed output files, close them and pop them.  */
-static void
-pop_output_files (void)
-{
-  current_interp_set_logging (NULL, false, false);
-
-  /* Stay consistent with handle_redirections.  */
-  if (!current_uiout->is_mi_like_p ())
-    current_uiout->redirect (NULL);
-}
-
 /* This is a helper for the `set logging' command.  */
 static void
 handle_redirections (int from_tty)
@@ -260,19 +249,7 @@ handle_redirections (int from_tty)
   saved_filename = logging_filename;
   logging_redirect_for_file = logging_redirect;
   debug_redirect_for_file = debug_redirect;
-
-  /* Let the interpreter do anything it needs.  */
-  current_interp_set_logging (std::move (log), logging_redirect,
-			      debug_redirect);
-
-  /* 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);
+  log_file = std::move (log);
 }
 
 static void
@@ -292,7 +269,7 @@ set_logging_off (const char *args, int from_tty)
   if (saved_filename.empty ())
     return;
 
-  pop_output_files ();
+  log_file.reset ();
   if (from_tty)
     gdb_printf ("Done logging to %s.\n",
 		saved_filename.c_str ());
diff --git a/gdb/fork-child.c b/gdb/fork-child.c
index 318ca46e8cb..bd75a9593aa 100644
--- a/gdb/fork-child.c
+++ b/gdb/fork-child.c
@@ -47,8 +47,8 @@ get_exec_wrapper ()
 void
 gdb_flush_out_err ()
 {
-  gdb_flush (main_ui->m_gdb_stdout);
-  gdb_flush (main_ui->m_gdb_stderr);
+  gdb_flush (main_ui->m_ui_stdout);
+  gdb_flush (main_ui->m_ui_stderr);
 }
 
 /* The ui structure that will be saved on 'prefork_hook' and
diff --git a/gdb/guile/scm-ports.c b/gdb/guile/scm-ports.c
index 8841970cf1f..78be66ce2a2 100644
--- a/gdb/guile/scm-ports.c
+++ b/gdb/guile/scm-ports.c
@@ -25,6 +25,7 @@
 #include "ui.h"
 #include "target.h"
 #include "guile-internal.h"
+#include "logging-file.h"
 #include <optional>
 
 #ifdef HAVE_POLL
@@ -323,20 +324,23 @@ ioscm_with_output_to_port_worker (SCM port, SCM thunk, enum oport oport,
 
   scoped_restore restore_async = make_scoped_restore (&current_ui->async, 0);
 
-  ui_file_up port_file (new ioscm_file_port (port));
+  ui_file_up log_file
+    = (std::make_unique<logging_file<ui_file_up>>
+       (std::make_unique<ioscm_file_port> (port)));
 
   scoped_restore save_file = make_scoped_restore (oport == GDB_STDERR
-						  ? &gdb_stderr : &gdb_stdout);
+						  ? redirectable_stderr ()
+						  : redirectable_stdout ());
 
   {
     std::optional<ui_out_redirect_pop> redirect_popper;
     if (oport == GDB_STDERR)
-      gdb_stderr = port_file.get ();
+      *redirectable_stderr () = log_file.get ();
     else
       {
-	redirect_popper.emplace (current_uiout, port_file.get ());
+	redirect_popper.emplace (current_uiout, log_file.get ());
 
-	gdb_stdout = port_file.get ();
+	*redirectable_stderr () = log_file.get ();
       }
 
     result = gdbscm_safe_call_0 (thunk, NULL);
diff --git a/gdb/interps.c b/gdb/interps.c
index 903c52e7cf7..23663cb5ef5 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -45,9 +45,16 @@
 static struct interp *interp_lookup_existing (struct ui *ui,
 					      const char *name);
 
-interp::interp (const char *name)
+interp::interp (const char *name, bool make_outputs)
   : m_name (name)
 {
+  if (make_outputs)
+    {
+      m_stdout = std::make_unique<ui::ui_stdout_file> ();
+      m_stderr = std::make_unique<ui::ui_stderr_file> ();
+      m_stdlog = std::make_unique<ui::ui_stdlog_file> ();
+      m_stdtarg = std::make_unique<ui::ui_stdtarg_file> ();
+    }
 }
 
 interp::~interp () = default;
@@ -197,15 +204,6 @@ set_top_level_interpreter (const char *name, bool for_new_ui)
   interp_set (interp, true);
 }
 
-void
-current_interp_set_logging (ui_file_up logfile, bool logging_redirect,
-			    bool debug_redirect)
-{
-  struct interp *interp = current_ui->current_interpreter;
-
-  interp->set_logging (std::move (logfile), logging_redirect, debug_redirect);
-}
-
 /* Temporarily overrides the current interpreter.  */
 struct interp *
 scoped_restore_interp::set_interp (const char *name)
@@ -286,13 +284,6 @@ interpreter_exec_cmd (const char *args, int from_tty)
   unsigned int nrules;
   unsigned int i;
 
-  /* Interpreters may clobber stdout/stderr (e.g.  in mi_interp::resume at time
-     of writing), preserve their state here.  */
-  scoped_restore save_stdout = make_scoped_restore (&gdb_stdout);
-  scoped_restore save_stderr = make_scoped_restore (&gdb_stderr);
-  scoped_restore save_stdlog = make_scoped_restore (&gdb_stdlog);
-  scoped_restore save_stdtarg = make_scoped_restore (&gdb_stdtarg);
-
   if (args == NULL)
     error_no_arg (_("interpreter-exec command"));
 
diff --git a/gdb/interps.h b/gdb/interps.h
index 2178e6a454e..d92d6b977cf 100644
--- a/gdb/interps.h
+++ b/gdb/interps.h
@@ -51,7 +51,10 @@ extern void interp_exec (struct interp *interp, const char *command);
 class interp : public intrusive_list_node<interp>
 {
 public:
-  explicit interp (const char *name);
+  /* Construct a new interpreter with the given name.  If MAKE_OUTPUTS
+     is true, also initialize the standard output streams to the
+     instances of the appropriate ui::passthrough_file type.  */
+  explicit interp (const char *name, bool make_outputs = true);
   virtual ~interp () = 0;
 
   void init (bool top_level)
@@ -74,12 +77,6 @@ class interp : public intrusive_list_node<interp>
      formatter.  */
   virtual ui_out *interp_ui_out () = 0;
 
-  /* Provides a hook for interpreters to do any additional
-     setup/cleanup that they might need when logging is enabled or
-     disabled.  */
-  virtual void set_logging (ui_file_up logfile, bool logging_redirect,
-			    bool debug_redirect) = 0;
-
   /* Called before starting an event loop, to give the interpreter a
      chance to e.g., print a prompt.  */
   virtual void pre_command_loop ()
@@ -205,6 +202,46 @@ class interp : public intrusive_list_node<interp>
   virtual void on_memory_changed (inferior *inf, CORE_ADDR addr, ssize_t len,
 				  const bfd_byte *data) {}
 
+  /* Accessors that return the various standard output files.  */
+  ui_file *get_stdout ()
+  {
+    return m_stdout.get ();
+  }
+
+  ui_file *get_stderr ()
+  {
+    return m_stderr.get ();
+  }
+
+  ui_file *get_stdlog ()
+  {
+    return m_stdlog.get ();
+  }
+
+  ui_file *get_stdtarg ()
+  {
+    return m_stdtarg.get ();
+  }
+
+protected:
+
+  /* The standard output streams.
+
+     Each interpreter can manage these streams as it likes.  The only
+     general rule is that the final ui_file in each pipeline  should
+     be a ui::ui_*_file of the appropriate type.
+
+     The overall approach here is that output starts with the
+     interpreter -- that is, "globals" like gdb_stdout just route to
+     these fields in the current interpreter.
+
+     After any processing by the interpreter, output is then sent to
+     the UI's channels.  The UI handles paging, logging, etc.  */
+  ui_file_up m_stdout;
+  ui_file_up m_stderr;
+  ui_file_up m_stdlog;
+  ui_file_up m_stdtarg;
+
 private:
   /* Called to perform any needed initialization.  */
   virtual void do_init (bool top_level)
@@ -258,21 +295,6 @@ class scoped_restore_interp
 
 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.  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.  DEBUG_REDIRECT is same as
-   LOGGING_REDIRECT, but for the value of "set logging debugredirect"
-   instead.  */
-extern void current_interp_set_logging (ui_file_up logfile,
-					bool logging_redirect,
-					bool debug_redirect);
-
 /* Returns the top-level interpreter.  */
 extern struct interp *top_level_interpreter (void);
 
diff --git a/gdb/main.c b/gdb/main.c
index c9bd79a4f8b..a4e6cddef70 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -60,6 +60,7 @@
 #include "cli-out.h"
 #include "bt-utils.h"
 #include "terminal.h"
+#include "logging-file.h"
 
 /* The selected interpreter.  */
 std::string interpreter_p;
@@ -962,7 +963,7 @@ captured_main_1 (struct captured_main_args *context)
 	    break;
 	  case 'B':
 	    batch_flag = batch_silent = 1;
-	    gdb_stdout = new null_file ();
+	    current_ui->m_ui_stdout = new null_file ();
 	    break;
 	  case 'D':
 	    if (optarg[0] == '\0')
diff --git a/gdb/mi/mi-console.c b/gdb/mi/mi-console.c
index 444894b75f9..1a10bfa21fb 100644
--- a/gdb/mi/mi-console.c
+++ b/gdb/mi/mi-console.c
@@ -93,13 +93,3 @@ mi_console_file::flush ()
 
   m_buffer.clear ();
 }
-
-/* 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_file::set_raw (ui_file *raw)
-{
-  m_raw = raw;
-}
diff --git a/gdb/mi/mi-console.h b/gdb/mi/mi-console.h
index ac1cefe6885..8dea558fb9b 100644
--- a/gdb/mi/mi-console.h
+++ b/gdb/mi/mi-console.h
@@ -30,9 +30,6 @@ class mi_console_file : public ui_file
      string PREFIX and quoting it with QUOTE.  */
   mi_console_file (ui_file *raw, const char *prefix, char quote);
 
-  /* MI-specific API.  */
-  void set_raw (ui_file *raw);
-
   /* ui_file-specific methods.  */
 
   void flush () override;
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index a7cfb66f4af..40c9fcb350c 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -85,18 +85,18 @@ mi_interp::do_init (bool top_level)
   /* Store the current output channel, so that we can create a console
      channel that encapsulates and prefixes all gdb_output-type bits
      coming from the rest of the debugger.  */
-  mi->raw_stdout = gdb_stdout;
+  mi->raw_stdout = new ui::ui_stdout_file ();
 
   /* Create MI console channels, each with a different prefix so they
      can be distinguished.  */
-  mi->out = new mi_console_file (mi->raw_stdout, "~", '"');
-  mi->err = new mi_console_file (mi->raw_stdout, "&", '"');
-  mi->log = mi->err;
-  mi->targ = new mi_console_file (mi->raw_stdout, "@", '"');
+  m_stdout = std::make_unique<mi_console_file> (mi->raw_stdout, "~", '"');
+  m_stderr = std::make_unique<mi_console_file> (mi->raw_stdout, "&", '"');
+  m_stdlog = std::make_unique<mi_console_file> (mi->raw_stdout, "&", '"');
+  m_stdtarg = std::make_unique<mi_console_file> (mi->raw_stdout, "@", '"');
   mi->event_channel = new mi_console_file (mi->raw_stdout, "=", 0);
   mi->mi_uiout = mi_out_new (name ()).release ();
   gdb_assert (mi->mi_uiout != nullptr);
-  mi->cli_uiout = new cli_ui_out (mi->out);
+  mi->cli_uiout = new cli_ui_out (m_stdout.get ());
 
   if (top_level)
     {
@@ -116,23 +116,13 @@ mi_interp::do_init (bool top_level)
 void
 mi_interp::resume ()
 {
-  struct mi_interp *mi = this;
   struct ui *ui = current_ui;
 
-  /* As per hack note in mi_interpreter_init, swap in the output
-     channels... */
   gdb_setup_readline (0);
 
   ui->call_readline = gdb_readline_no_editing_callback;
   ui->input_handler = mi_execute_command_input_handler;
 
-  gdb_stdout = mi->out;
-  /* Route error and log output through the MI.  */
-  gdb_stderr = mi->err;
-  gdb_stdlog = mi->log;
-  /* Route target output through the MI.  */
-  gdb_stdtarg = mi->targ;
-
   deprecated_show_load_progress = mi_load_progress;
 }
 
@@ -885,48 +875,6 @@ mi_interp::interp_ui_out ()
   return this->mi_uiout;
 }
 
-/* Do MI-specific logging actions; save raw_stdout, and change all
-   the consoles to use the supplied ui-file(s).  */
-
-void
-mi_interp::set_logging (ui_file_up logfile, bool logging_redirect,
-			bool debug_redirect)
-{
-  struct mi_interp *mi = this;
-
-  if (logfile != NULL)
-    {
-      mi->saved_raw_stdout = mi->raw_stdout;
-
-      ui_file *logfile_p = logfile.get ();
-      mi->logfile_holder = std::move (logfile);
-
-      /* If something is not being redirected, then a tee containing both the
-	 logfile and stdout.  */
-      ui_file *tee = nullptr;
-      if (!logging_redirect || !debug_redirect)
-	{
-	  tee = new tee_file (mi->raw_stdout, logfile_p);
-	  mi->stdout_holder.reset (tee);
-	}
-
-      mi->raw_stdout = logging_redirect ? logfile_p : tee;
-    }
-  else
-    {
-      mi->logfile_holder.reset ();
-      mi->stdout_holder.reset ();
-      mi->raw_stdout = mi->saved_raw_stdout;
-      mi->saved_raw_stdout = nullptr;
-    }
-
-  mi->out->set_raw (mi->raw_stdout);
-  mi->err->set_raw (mi->raw_stdout);
-  mi->log->set_raw (mi->raw_stdout);
-  mi->targ->set_raw (mi->raw_stdout);
-  mi->event_channel->set_raw (mi->raw_stdout);
-}
-
 /* Factory for MI interpreters.  */
 
 static struct interp *
diff --git a/gdb/mi/mi-interp.h b/gdb/mi/mi-interp.h
index 39b39779de7..2ffd3a151fb 100644
--- a/gdb/mi/mi-interp.h
+++ b/gdb/mi/mi-interp.h
@@ -30,7 +30,7 @@ class mi_interp final : public interp
 {
 public:
   mi_interp (const char *name)
-    : interp (name)
+    : interp (name, false)
   {}
 
   void do_init (bool top_level) override;
@@ -38,8 +38,6 @@ class mi_interp final : public interp
   void suspend () override;
   void exec (const char *command_str) override;
   ui_out *interp_ui_out () override;
-  void set_logging (ui_file_up logfile, bool logging_redirect,
-		    bool debug_redirect) override;
   void pre_command_loop () override;
 
   void on_signal_received (gdb_signal sig) override;
@@ -74,22 +72,10 @@ class mi_interp final : public interp
   void on_memory_changed (inferior *inf, CORE_ADDR addr, ssize_t len,
 			  const bfd_byte *data) override;
 
-  /* MI's output channels */
-  mi_console_file *out;
-  mi_console_file *err;
-  mi_console_file *log;
-  mi_console_file *targ;
   mi_console_file *event_channel;
 
   /* Raw console output.  */
-  struct ui_file *raw_stdout;
-
-  /* Save the original value of raw_stdout here when logging, and the
-     file which we need to delete, so we can restore correctly when
-     done.  */
-  struct ui_file *saved_raw_stdout;
-  ui_file_up logfile_holder;
-  ui_file_up stdout_holder;
+  ui_file *raw_stdout;
 
   /* MI's builder.  */
   struct ui_out *mi_uiout;
diff --git a/gdb/python/py-dap.c b/gdb/python/py-dap.c
index 47d90e51938..07cd47bdc0e 100644
--- a/gdb/python/py-dap.c
+++ b/gdb/python/py-dap.c
@@ -27,9 +27,9 @@ class dap_interp final : public interp
 public:
 
   explicit dap_interp (const char *name)
-    : interp (name),
-      m_ui_out (new cli_ui_out (gdb_stdout))
+    : interp (name)
   {
+    m_ui_out = std::make_unique<cli_ui_out> (m_stdout.get ());
   }
 
   ~dap_interp () override = default;
@@ -49,12 +49,6 @@ class dap_interp final : public interp
     /* Just ignore it.  */
   }
 
-  void set_logging (ui_file_up logfile, bool logging_redirect,
-		    bool debug_redirect) override
-  {
-    /* Just ignore it.  */
-  }
-
   ui_out *interp_ui_out () override
   {
     return m_ui_out.get ();
diff --git a/gdb/testsuite/gdb.base/early-logging.exp b/gdb/testsuite/gdb.base/early-logging.exp
new file mode 100644
index 00000000000..2a9eef499d6
--- /dev/null
+++ b/gdb/testsuite/gdb.base/early-logging.exp
@@ -0,0 +1,35 @@
+# Copyright 2021-2025 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/>.
+
+# Test GDB's early init file mechanism.
+
+require {!is_remote host}
+
+save_vars { INTERNAL_GDBFLAGS } {
+    set logfile [standard_output_file gdb.txt]
+    append INTERNAL_GDBFLAGS " " \
+	[join [list \
+		   -eiex \
+		   "\"set logging file $logfile\"" \
+		   -eiex \
+		   "\"set logging enabled on\""]]
+
+    # The bug was that gdb would crash during startup if logging was
+    # enabled early.
+    clean_restart
+
+    # Just check that it's still alive.
+    gdb_test "print 23" " = 23" "it lives"
+}
diff --git a/gdb/testsuite/gdb.python/python.exp b/gdb/testsuite/gdb.python/python.exp
index ca3a4d23753..f05f3ac1419 100644
--- a/gdb/testsuite/gdb.python/python.exp
+++ b/gdb/testsuite/gdb.python/python.exp
@@ -120,6 +120,13 @@ gdb_test "python gdb.execute('echo 2\\necho 3\\\\n\\n')" "23" \
     "multi-line execute"
 gdb_test " " "23" "gdb.execute does not affect repeat history"
 
+gdb_test_no_output \
+    "python x = gdb.execute(\"interpreter-exec mi '-data-evaluate-expression 23'\", to_string=True)" \
+    "execute MI command from python"
+gdb_test "python print(x)" \
+    "\\^done,value=\"23\"\r\n" \
+    "output of MI command"
+
 # Test post_event.
 gdb_test_multiline "post event insertion" \
   "python" "" \
@@ -562,3 +569,12 @@ foreach type {Instruction LazyString Membuf Record RecordFunctionSegment \
     gdb_test "python print(type(gdb.$type))" "<class 'type'>" \
 	"gdb.$type is registered"
 }
+
+set logfile [host_standard_output_file gdb.txt]
+gdb_test_no_output "set logging file $logfile" \
+    "set logging file [file tail $logfile]"
+gdb_test_no_output "python gdb.execute('set logging enabled on')" \
+    "enable logging via python"
+gdb_test "set logging enabled off" \
+    "Done logging to .*gdb.txt." \
+    "disable logging"
diff --git a/gdb/top.c b/gdb/top.c
index 5819dfa7897..c3dc9abe60d 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -57,6 +57,7 @@
 #include "gdbsupport/pathstuff.h"
 #include "cli/cli-style.h"
 #include "pager.h"
+#include "logging-file.h"
 
 /* readline include files.  */
 #include "readline/readline.h"
@@ -91,34 +92,96 @@ extern void initialize_all_files (void);
 #define DEFAULT_PROMPT	"(gdb) "
 #endif
 
-struct ui_file **
-current_ui_gdb_stdout_ptr ()
+/* See utils.h.  */
+
+struct ui_file *
+current_gdb_stdout ()
 {
-  return &current_ui->m_gdb_stdout;
+  /* In early startup, there's no interpreter, but we'd still like to
+     be able to print.  */
+  interp *curr = current_interpreter ();
+  if (curr == nullptr)
+    return current_ui->m_ui_stdout;
+  return curr->get_stdout ();
 }
 
+/* See utils.h.  */
+
 struct ui_file *
-current_ui_gdb_stdin ()
+current_gdb_stdin ()
 {
   return current_ui->m_gdb_stdin;
 }
 
+/* See utils.h.  */
+
+struct ui_file *
+current_gdb_stderr ()
+{
+  /* In early startup, there's no interpreter, but we'd still like to
+     be able to print.  */
+  interp *curr = current_interpreter ();
+  if (curr == nullptr)
+    return current_ui->m_ui_stderr;
+  return curr->get_stderr ();
+}
+
+/* See utils.h.  */
+
+struct ui_file *
+current_gdb_stdlog ()
+{
+  /* In early startup, there's no interpreter, but we'd still like to
+     be able to print.  */
+  interp *curr = current_interpreter ();
+  if (curr == nullptr)
+    return current_ui->m_ui_stdlog;
+  return curr->get_stdlog ();
+}
+
+/* See utils.h.  */
+
+struct ui_file *
+current_gdb_stdtarg ()
+{
+  /* In early startup, there's no interpreter, but we'd still like to
+     be able to print.  */
+  interp *curr = current_interpreter ();
+  if (curr == nullptr)
+    return current_ui->m_ui_stdtarg;
+  return curr->get_stdtarg ();
+}
+
+/* See utils.h.  */
+
 struct ui_file **
-current_ui_gdb_stderr_ptr ()
+redirectable_stdout ()
 {
-  return &current_ui->m_gdb_stderr;
+  return &current_ui->m_ui_stdout;
 }
 
+/* See utils.h.  */
+
 struct ui_file **
-current_ui_gdb_stdlog_ptr ()
+redirectable_stderr ()
 {
-  return &current_ui->m_gdb_stdlog;
+  return &current_ui->m_ui_stderr;
 }
 
+/* See utils.h.  */
+
+struct ui_file **
+redirectable_stdlog ()
+{
+  return &current_ui->m_ui_stdlog;
+}
+
+/* See utils.h.  */
+
 struct ui_file **
-current_ui_gdb_stdtarg_ptr ()
+redirectable_stdtarg ()
 {
-  return &current_ui->m_gdb_stdtarg;
+  return &current_ui->m_ui_stdtarg;
 }
 
 struct ui_out **
@@ -603,22 +666,22 @@ execute_command (const char *p, int from_tty)
 static void
 execute_fn_to_ui_file (struct ui_file *file, std::function<void(void)> fn)
 {
-  /* GDB_STDOUT should be better already restored during these
-     restoration callbacks.  */
   set_batch_flag_and_restore_page_info save_page_info;
 
   scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
 
-  ui_out_redirect_pop redirect_popper (current_uiout, file);
+  logging_file<ui_file *> logger (file);
+  ui_out_redirect_pop redirect_popper (current_uiout, &logger);
 
   scoped_restore save_stdout
-    = make_scoped_restore (&gdb_stdout, file);
+    = make_scoped_restore (redirectable_stdout (), &logger);
   scoped_restore save_stderr
-    = make_scoped_restore (&gdb_stderr, file);
+    = make_scoped_restore (redirectable_stderr (), &logger);
+  logging_file<ui_file *> log_logger (file, true);
   scoped_restore save_stdlog
-    = make_scoped_restore (&gdb_stdlog, file);
+    = make_scoped_restore (redirectable_stdlog (), &log_logger);
   scoped_restore save_stdtarg
-    = make_scoped_restore (&gdb_stdtarg, file);
+    = make_scoped_restore (redirectable_stdtarg (), &logger);
 
   fn ();
 }
diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c
index 1316c8bedd6..e171ae6263e 100644
--- a/gdb/tui/tui-interp.c
+++ b/gdb/tui/tui-interp.c
@@ -105,26 +105,11 @@ void
 tui_interp::resume ()
 {
   struct ui *ui = current_ui;
-  struct ui_file *stream;
-
-  /* gdb_setup_readline will change gdb_stdout.  If the TUI was
-     previously writing to gdb_stdout, then set it to the new
-     gdb_stdout afterwards.  */
-
-  stream = tui_old_uiout->set_stream (gdb_stdout);
-  if (stream != gdb_stdout)
-    {
-      tui_old_uiout->set_stream (stream);
-      stream = NULL;
-    }
 
   gdb_setup_readline (1);
 
   ui->input_handler = tui_command_line_handler;
 
-  if (stream != NULL)
-    tui_old_uiout->set_stream (gdb_stdout);
-
   if (tui_start_enabled)
     tui_enable ();
 }
diff --git a/gdb/tui/tui-io.c b/gdb/tui/tui-io.c
index 7abe4fa3e81..41704fc44d1 100644
--- a/gdb/tui/tui-io.c
+++ b/gdb/tui/tui-io.c
@@ -45,6 +45,7 @@
 #include "gdbsupport/unordered_map.h"
 #include "pager.h"
 #include "gdbsupport/gdb-checked-static-cast.h"
+#include "logging-file.h"
 
 /* This redefines CTRL if it is not already defined, so it must come
    after terminal state related include files like <term.h> and
@@ -73,7 +74,7 @@ key_is_start_sequence (int ch)
    mode.
 
    In curses mode, the gdb outputs are made in a curses command
-   window.  For this, the gdb_stdout and gdb_stderr are redirected to
+   window.  For this, the redirectable stdout and stderr are set to
    the specific ui_file implemented by TUI.  The output is handled by
    tui_puts().  The input is also controlled by curses with
    tui_getc().  The readline library uses this function to get its
@@ -861,16 +862,16 @@ tui_setup_io (int mode)
       rl_already_prompted = 0;
 
       /* Keep track of previous gdb output.  */
-      tui_old_stdout = gdb_stdout;
-      tui_old_stderr = gdb_stderr;
-      tui_old_stdlog = gdb_stdlog;
+      tui_old_stdout = *redirectable_stdout ();
+      tui_old_stderr = *redirectable_stderr ();
+      tui_old_stdlog = *redirectable_stdlog ();
       tui_old_uiout = gdb::checked_static_cast<cli_ui_out *> (current_uiout);
 
       /* Reconfigure gdb output.  */
-      gdb_stdout = tui_stdout;
-      gdb_stderr = tui_stderr;
-      gdb_stdlog = tui_stdlog;
-      gdb_stdtarg = gdb_stderr;
+      *redirectable_stdout () = tui_stdout;
+      *redirectable_stderr () = tui_stderr;
+      *redirectable_stdlog () = tui_stdlog;
+      *redirectable_stdtarg () = tui_stderr;
       current_uiout = tui_out;
 
       /* Save tty for SIGCONT.  */
@@ -879,10 +880,10 @@ tui_setup_io (int mode)
   else
     {
       /* Restore gdb output.  */
-      gdb_stdout = tui_old_stdout;
-      gdb_stderr = tui_old_stderr;
-      gdb_stdlog = tui_old_stdlog;
-      gdb_stdtarg = gdb_stderr;
+      *redirectable_stdout () = tui_old_stdout;
+      *redirectable_stderr () = tui_old_stderr;
+      *redirectable_stdlog () = tui_old_stdlog;
+      *redirectable_stdtarg () = tui_old_stderr;
       current_uiout = tui_old_uiout;
 
       /* Restore readline.  */
@@ -933,13 +934,18 @@ tui_initialize_io (void)
 #endif
 
   /* Create tui output streams.  */
-  tui_stdout = new pager_file (std::make_unique<tui_file> (stdout, true));
-  tui_stderr = new tui_file (stderr, false);
-  tui_stdlog = new timestamped_file (tui_stderr);
+  tui_stdout = new pager_file (std::make_unique<logging_file<ui_file_up>>
+			       (std::make_unique<tui_file> (stdout, true)));
+  ui_file *err_out = new tui_file (stderr, false);
+  /* Let tui_stderr own ERR_OUT.  */
+  tui_stderr = new logging_file<ui_file_up> (ui_file_up (err_out));
+  tui_stdlog = (new timestamped_file
+		(new logging_file<ui_file *> (err_out, true)));
   tui_out = new cli_ui_out (tui_stdout, 0);
 
-  /* Create the default UI.  */
-  tui_old_uiout = new cli_ui_out (gdb_stdout);
+  /* Using redirectable_stdout here is a hack.  This should probably
+     be done when constructing the interpreter instead.  */
+  tui_old_uiout = new cli_ui_out (*redirectable_stdout ());
 
 #ifdef TUI_USE_PIPE_FOR_READLINE
   /* Temporary solution for readline writing to stdout: redirect
diff --git a/gdb/ui.c b/gdb/ui.c
index 647e63d1bde..9d71135bc36 100644
--- a/gdb/ui.c
+++ b/gdb/ui.c
@@ -27,6 +27,7 @@
 #include "pager.h"
 #include "main.h"
 #include "top.h"
+#include "logging-file.h"
 
 /* See top.h.  */
 
@@ -48,11 +49,16 @@ ui::ui (FILE *instream_, FILE *outstream_, FILE *errstream_)
     errstream (errstream_),
     input_fd (fileno (instream)),
     m_input_interactive_p (ISATTY (instream)),
-    m_gdb_stdout (new pager_file (std::make_unique<stdio_file> (outstream))),
+    m_ui_stdout (new pager_file
+		 (std::make_unique<logging_file<ui_file_up>>
+		  (std::make_unique<stdio_file> (outstream)))),
     m_gdb_stdin (new stdio_file (instream)),
-    m_gdb_stderr (new stderr_file (errstream)),
-    m_gdb_stdlog (new timestamped_file (m_gdb_stderr)),
-    m_gdb_stdtarg (m_gdb_stderr)
+    m_ui_stderr (new logging_file<ui_file_up>
+		 (std::make_unique<stderr_file> (errstream))),
+    m_ui_stdlog (new timestamped_file
+		 (new logging_file<ui_file_up>
+		  (std::make_unique<stderr_file> (errstream), true))),
+    m_ui_stdtarg (m_ui_stderr)
 {
   unbuffer_stream (instream_);
 
@@ -86,8 +92,8 @@ ui::~ui ()
     ui_list = next;
 
   delete m_gdb_stdin;
-  delete m_gdb_stdout;
-  delete m_gdb_stderr;
+  delete m_ui_stdout;
+  delete m_ui_stderr;
 }
 
 
diff --git a/gdb/ui.h b/gdb/ui.h
index ba98d2500af..c2b1bca2121 100644
--- a/gdb/ui.h
+++ b/gdb/ui.h
@@ -22,8 +22,10 @@
 #include "gdbsupport/intrusive_list.h"
 #include "gdbsupport/next-iterator.h"
 #include "gdbsupport/scoped_restore.h"
+#include "ui-file.h"
 
 struct interp;
+struct ui;
 
 /* Prompt state.  */
 
@@ -43,6 +45,17 @@ enum prompt_state
   PROMPTED,
 };
 
+/* The main UI.  This is the UI that is bound to stdin/stdout/stderr.
+   It always exists and is created automatically when GDB starts
+   up.  */
+extern struct ui *main_ui;
+
+/* The current UI.  */
+extern struct ui *current_ui;
+
+/* The list of all UIs.  */
+extern struct ui *ui_list;
+
 /* All about a user interface instance.  Each user interface has its
    own I/O files/streams, readline state, its own top level
    interpreter (for the main UI, this is the interpreter specified
@@ -141,22 +154,60 @@ struct ui
      execution.  */
   bool keep_prompt_blocked = false;
 
+  /* A "smart pointer" that references a particular member of the
+     current UI.  */
+  template<ui_file *ui::* F>
+  struct ui_file_ptr
+  {
+    ui_file *operator-> () const
+    {
+      return current_ui->*F;
+    }
+  };
+
+  /* A ui_file that simply forwards.  */
+  template<ui_file *ui::* F>
+  class passthrough_file : public wrapped_file<ui_file_ptr<F>>
+  {
+  public:
+    passthrough_file () : wrapped_file<ui_file_ptr<F>> ({})
+    {
+    }
+
+    void write (const char *buf, long len) override
+    {
+      this->m_stream->write (buf, len);
+    }
+  };
+
   /* The fields below that start with "m_" are "private".  They're
      meant to be accessed through wrapper macros that make them look
      like globals.  */
 
   /* The ui_file streams.  */
   /* Normal results */
-  struct ui_file *m_gdb_stdout;
+  struct ui_file *m_ui_stdout;
   /* Input stream */
   struct ui_file *m_gdb_stdin;
   /* Serious error notifications */
-  struct ui_file *m_gdb_stderr;
+  struct ui_file *m_ui_stderr;
   /* Log/debug/trace messages that should bypass normal stdout/stderr
      filtering.  */
-  struct ui_file *m_gdb_stdlog;
+  struct ui_file *m_ui_stdlog;
   /* Target output.  */
-  struct ui_file *m_gdb_stdtarg;
+  struct ui_file *m_ui_stdtarg;
+
+  /* Types for directing output to the various UI-managed streams.  An
+     indirection solves all problems.  Here the problem being solved
+     is that an interpreter could be active with any UI; and that
+     redirections (like capturing command output to a string) is done
+     by manipulating the pointers in the current UI.  To this end, we
+     export some ui_file types that can be used to implement the
+     various kinds of redirections.  */
+  using ui_stdout_file = passthrough_file<&ui::m_ui_stdout>;
+  using ui_stderr_file = passthrough_file<&ui::m_ui_stderr>;
+  using ui_stdlog_file = passthrough_file<&ui::m_ui_stdlog>;
+  using ui_stdtarg_file = passthrough_file<&ui::m_ui_stdtarg>;
 
   /* The current ui_out.  */
   struct ui_out *m_current_uiout = nullptr;
@@ -171,17 +222,6 @@ struct ui
   bool input_interactive_p () const;
 };
 
-/* The main UI.  This is the UI that is bound to stdin/stdout/stderr.
-   It always exists and is created automatically when GDB starts
-   up.  */
-extern struct ui *main_ui;
-
-/* The current UI.  */
-extern struct ui *current_ui;
-
-/* The list of all UIs.  */
-extern struct ui *ui_list;
-
 /* State for SWITCH_THRU_ALL_UIS.  */
 class switch_thru_all_uis
 {
diff --git a/gdb/utils.h b/gdb/utils.h
index 4d888b80274..7cab021e7a9 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -165,11 +165,48 @@ extern bool pagination_enabled;
 /* A flag indicating whether to timestamp debugging messages.  */
 extern bool debug_timestamp;
 
-extern struct ui_file **current_ui_gdb_stdout_ptr (void);
-extern struct ui_file *current_ui_gdb_stdin ();
-extern struct ui_file **current_ui_gdb_stderr_ptr (void);
-extern struct ui_file **current_ui_gdb_stdlog_ptr (void);
-extern struct ui_file **current_ui_gdb_stdtarg_ptr ();
+/* Return the current ui_file for a given output stream.
+
+   Output in gdb is somewhat complicated.  Responsibility for it is
+   split between the interpreter and the UI, and has to allow for
+   output manipulation (like MI quoting), paging, logging, timestamps,
+   etc.
+
+   The outermost ui_file comes from the current interpreter.  (That is
+   what these functions return.)
+
+   The interpreter supplies anything that it needs.  E.g., for the
+   CLI, this is nothing, but for MI, each stream applies its own
+   quoting rule to the output.
+
+   The interpreter's pipeline ends with a ui::passthrough_file,
+   causing output to then proceed via the UI's pipeline.
+
+   The UI provides whatever is needed by that UI.  For instance, the
+   pager is handled here.  Logging is also handled here, and the
+   stdlog file also has a timestamp filter on it.
+
+   The UI is also where redirection happens.  That is, when
+   temporarily redirecting output to a different stream (like a
+   string_file), the redirectable_* streams are used -- these just
+   return pointers to fields in the current UI.  This way,
+   interpreter-specific changes are still applied before output.
+
+   When redirecting, if logging is still desired (normally it is),
+   then it is the redirector's responsibility to put a logging file
+   into the pipeline.  */
+
+extern struct ui_file *current_gdb_stdout ();
+extern struct ui_file *current_gdb_stderr ();
+extern struct ui_file *current_gdb_stdlog ();
+extern struct ui_file *current_gdb_stdtarg ();
+
+extern struct ui_file **redirectable_stdout ();
+extern struct ui_file **redirectable_stderr ();
+extern struct ui_file **redirectable_stdlog ();
+extern struct ui_file **redirectable_stdtarg ();
+
+extern struct ui_file *current_gdb_stdin ();
 
 /* Flush STREAM.  */
 extern void gdb_flush (struct ui_file *stream);
@@ -177,17 +214,17 @@ extern void gdb_flush (struct ui_file *stream);
 /* The current top level's ui_file streams.  */
 
 /* Normal results */
-#define gdb_stdout (*current_ui_gdb_stdout_ptr ())
+#define gdb_stdout (current_gdb_stdout ())
 /* Input stream.  */
-#define gdb_stdin (current_ui_gdb_stdin ())
+#define gdb_stdin (current_gdb_stdin ())
 /* Serious error notifications.  This bypasses the pager, if one is in
    use.  */
-#define gdb_stderr (*current_ui_gdb_stderr_ptr ())
+#define gdb_stderr (current_gdb_stderr ())
 /* Log/debug/trace messages that bypasses the pager, if one is in
    use.  */
-#define gdb_stdlog (*current_ui_gdb_stdlog_ptr ())
+#define gdb_stdlog (current_gdb_stdlog ())
 /* Target output.  */
-#define gdb_stdtarg (*current_ui_gdb_stdtarg_ptr ())
+#define gdb_stdtarg (current_gdb_stdtarg ())
 
 /* Set the screen dimensions to WIDTH and HEIGHT.  */
 

-- 
2.49.0


  parent reply	other threads:[~2026-01-30 13:24 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-30 13:17 [PATCH v3 00/21] Rework gdb logging and output redirection Tom Tromey
2026-01-30 13:17 ` [PATCH v3 01/21] Remove unnecessary override of write_async_safe Tom Tromey
2026-01-30 13:17 ` [PATCH v3 02/21] Move stdtarg to ui Tom Tromey
2026-01-30 13:17 ` [PATCH v3 03/21] Turn wrapped_file into a template Tom Tromey
2026-01-30 13:17 ` [PATCH v3 04/21] Small rewrite of get_unbuffered Tom Tromey
2026-01-30 13:17 ` [PATCH v3 05/21] Move buffered stream to new files Tom Tromey
2026-01-30 13:17 ` [PATCH v3 06/21] Remove TYPE_FN_FIELD_STUB and associated code Tom Tromey
2026-01-30 13:17 ` [PATCH v3 07/21] Change how stdin is handled in the UI Tom Tromey
2026-01-30 13:17 ` [PATCH v3 08/21] Remove gdb_stdtargin Tom Tromey
2026-01-30 13:17 ` [PATCH v3 09/21] Improve fputs_highlighted by using ui_file::write Tom Tromey
2026-01-30 13:17 ` [PATCH v3 10/21] Add stream to buffer_group::output_unit constructor Tom Tromey
2026-01-30 13:17 ` [PATCH v3 11/21] Restore ui_file::can_page Tom Tromey
2026-01-30 13:17 ` [PATCH v3 12/21] Rewrite cli-style.c:do_show Tom Tromey
2026-01-30 13:17 ` [PATCH v3 13/21] Remove m_applied_style from ui_file Tom Tromey
2026-01-30 13:17 ` [PATCH v3 14/21] Add a new logging_file implementation Tom Tromey
2026-01-30 13:17 ` Tom Tromey [this message]
2026-01-30 13:17 ` [PATCH v3 16/21] Remove tee_file Tom Tromey
2026-01-30 13:17 ` [PATCH v3 17/21] Warn if log file changed while logging Tom Tromey
2026-01-30 13:17 ` [PATCH v3 18/21] Fix leaks with timestamped_file Tom Tromey
2026-01-30 13:17 ` [PATCH v3 19/21] Use std::make_unique with ui_files Tom Tromey
2026-01-30 13:17 ` [PATCH v3 20/21] Style filenames in cli-logging.c Tom Tromey
2026-01-30 13:17 ` [PATCH v3 21/21] Update gdb.execute documentation Tom Tromey
2026-01-30 13:35   ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260130-pr-28948-logging-5-v3-15-3eec47ef3cba@tromey.com \
    --to=tom@tromey.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox