From: Tom Tromey Date: Sun, 7 Dec 2025 00:55:20 +0000 (-0600) Subject: Move buffered stream to new files X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=29d91b3c9036a7ff71c3d51bb5332d4be4f2ae13;p=thirdparty%2Fbinutils-gdb.git Move buffered stream to new files The buffered stream code is currently in ui-out.h and ui-out.c, which seems weird because it seems more like a low-level ui-file idea (for the most part, it does also affect some ui_out). This patch moves the declarations to a new header file, buffered-streams.h and the implementation to buffered-streams.c. This seems cleaner to me. Approved-By: Andrew Burgess --- diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 713c0920f6a..2aa95be968a 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1071,6 +1071,7 @@ COMMON_SFILES = \ breakpoint.c \ bt-utils.c \ btrace.c \ + buffered-streams.c \ build-id.c \ buildsym.c \ c-lang.c \ @@ -1347,6 +1348,7 @@ HFILES_NO_SRCDIR = \ bsd-uthread.h \ btrace.h \ bt-utils.h \ + buffered-streams.h \ build-id.h \ buildsym.h \ c-exp.h \ diff --git a/gdb/buffered-streams.c b/gdb/buffered-streams.c new file mode 100644 index 00000000000..a9d3fcf4f5d --- /dev/null +++ b/gdb/buffered-streams.c @@ -0,0 +1,155 @@ +/* Copyright (C) 2025, 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +#include "buffered-streams.h" +#include "ui-out.h" + +/* See buffered-streams.h. */ + +void +buffer_group::output_unit::flush () const +{ + if (!m_msg.empty ()) + m_stream->puts (m_msg.c_str ()); + + if (m_wrap_hint >= 0) + m_stream->wrap_here (m_wrap_hint); + + if (m_flush) + m_stream->flush (); +} + +/* See buffered-streams.h. */ + +void +buffer_group::write (const char *buf, long length_buf, ui_file *stream) +{ + /* Record each line separately. */ + for (size_t prev = 0, cur = 0; cur < length_buf; ++cur) + if (buf[cur] == '\n' || cur == length_buf - 1) + { + std::string msg (buf + prev, cur - prev + 1); + + if (m_buffered_output.size () > 0 + && m_buffered_output.back ().m_wrap_hint == -1 + && m_buffered_output.back ().m_stream == stream + && m_buffered_output.back ().m_msg.size () > 0 + && m_buffered_output.back ().m_msg.back () != '\n') + m_buffered_output.back ().m_msg.append (msg); + else + m_buffered_output.emplace_back (msg).m_stream = stream; + prev = cur + 1; + } +} + +/* See buffered-streams.h. */ + +void +buffer_group::wrap_here (int indent, ui_file *stream) +{ + m_buffered_output.emplace_back ("", indent).m_stream = stream; +} + +/* See buffered-streams.h. */ + +void +buffer_group::flush_here (ui_file *stream) +{ + m_buffered_output.emplace_back ("", -1, true).m_stream = stream; +} + +/* See buffered-streams.h. */ + +ui_file * +get_unbuffered (ui_file *stream) +{ + while (true) + { + buffering_file *buf = dynamic_cast (stream); + + if (buf == nullptr) + return stream; + + stream = buf->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_uiout (uiout) +{ + gdb_stdout = &m_buffered_stdout; + gdb_stderr = &m_buffered_stderr; + gdb_stdlog = &m_buffered_stdlog; + gdb_stdtarg = &m_buffered_stdtarg; + + ui_file *stream = current_uiout->current_stream (); + if (stream != nullptr) + { + m_buffered_current_uiout.emplace (group, stream); + current_uiout->redirect (&(*m_buffered_current_uiout)); + } + + stream = m_uiout->current_stream (); + if (stream != nullptr && current_uiout != m_uiout) + { + m_buffered_uiout.emplace (group, stream); + m_uiout->redirect (&(*m_buffered_uiout)); + } + + m_buffers_in_place = true; +} + +/* See buffered-streams.h. */ + +void +buffered_streams::remove_buffers () +{ + if (!m_buffers_in_place) + return; + + 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 (); + + if (m_buffered_current_uiout.has_value ()) + current_uiout->redirect (nullptr); + + if (m_buffered_uiout.has_value ()) + m_uiout->redirect (nullptr); +} + +buffer_group::buffer_group (ui_out *uiout) + : m_buffered_streams (new buffered_streams (this, uiout)) +{ /* Nothing. */ } + +/* See buffered-streams.h. */ + +void +buffer_group::flush () const +{ + m_buffered_streams->remove_buffers (); + + for (const output_unit &ou : m_buffered_output) + ou.flush (); +} diff --git a/gdb/buffered-streams.h b/gdb/buffered-streams.h new file mode 100644 index 00000000000..0da2d8a8f43 --- /dev/null +++ b/gdb/buffered-streams.h @@ -0,0 +1,205 @@ +/* Copyright (C) 2025, 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +#ifndef GDB_BUFFERED_STREAMS_H +#define GDB_BUFFERED_STREAMS_H + +#include +#include "ui-file.h" + +struct buffered_streams; +class ui_out; + +/* Organizes writes to a collection of buffered output streams + so that when flushed, output is written to all streams in + chronological order. */ + +struct buffer_group +{ + buffer_group (ui_out *uiout); + + /* Flush all buffered writes to the underlying output streams. */ + void flush () const; + + /* Record contents of BUF and associate it with STREAM. */ + void write (const char *buf, long length_buf, ui_file *stream); + + /* Record a wrap_here and associate it with STREAM. */ + void wrap_here (int indent, ui_file *stream); + + /* Record a call to flush and associate it with STREAM. */ + void flush_here (ui_file *stream); + +private: + + struct output_unit + { + output_unit (std::string msg, int wrap_hint = -1, bool flush = false) + : m_msg (msg), m_wrap_hint (wrap_hint), m_flush (flush) + {} + + /* Write contents of this output_unit to the underlying stream. */ + void flush () const; + + /* Underlying stream for which this output unit will be written to. */ + ui_file *m_stream; + + /* String to be written to underlying buffer. */ + std::string m_msg; + + /* Argument to wrap_here. -1 indicates no wrap. Used to call wrap_here + during buffer flush. */ + int m_wrap_hint; + + /* Indicate that the underlying output stream's flush should be called. */ + bool m_flush; + }; + + /* Output_units to be written to buffered output streams. */ + std::vector m_buffered_output; + + /* Buffered output streams. */ + std::unique_ptr m_buffered_streams; +}; + +/* If FILE is a buffering_file, return its underlying stream. */ + +extern ui_file *get_unbuffered (ui_file *file); + +/* Buffer output to gdb_stdout and gdb_stderr for the duration of FUNC. */ + +template +void +do_with_buffered_output (F func, ui_out *uiout, Arg... args) +{ + buffer_group g (uiout); + + try + { + func (uiout, std::forward (args)...); + } + catch (gdb_exception &ex) + { + /* Ideally flush would be called in the destructor of buffer_group, + however flushing might cause an exception to be thrown. Catch it + and ensure the first exception propagates. */ + try + { + g.flush (); + } + catch (const gdb_exception &) + { + } + + throw_exception (std::move (ex)); + } + + /* Try was successful. Let any further exceptions propagate. */ + g.flush (); +} + +/* Accumulate writes to an underlying ui_file. Output to the + underlying file is deferred until required. */ + +struct buffering_file : public ui_file +{ + buffering_file (buffer_group *group, ui_file *stream) + : m_group (group), + m_stream (stream) + { /* Nothing. */ } + + /* Return the underlying output stream. */ + ui_file *stream () const + { + return m_stream; + } + + /* Record the contents of BUF. */ + void write (const char *buf, long length_buf) override + { + m_group->write (buf, length_buf, m_stream); + } + + /* Record a wrap_here call with argument INDENT. */ + void wrap_here (int indent) override + { + m_group->wrap_here (indent, m_stream); + } + + /* Return true if the underlying stream is a tty. */ + bool isatty () override + { + return m_stream->isatty (); + } + + /* Return true if ANSI escapes can be used on the underlying stream. */ + bool can_emit_style_escape () override + { + return m_stream->can_emit_style_escape (); + } + + /* Flush the underlying output stream. */ + void flush () override + { + return m_group->flush_here (m_stream); + } + +private: + + /* Coordinates buffering across multiple buffering_files. */ + buffer_group *m_group; + + /* The underlying output stream. */ + ui_file *m_stream; +}; + +/* Attaches and detaches buffers for each of the gdb_std* streams. */ + +struct buffered_streams +{ + buffered_streams (buffer_group *group, ui_out *uiout); + + ~buffered_streams () + { + this->remove_buffers (); + } + + /* Remove buffering_files from all underlying streams. */ + void remove_buffers (); + +private: + + /* True if buffers are still attached to each underlying output stream. */ + bool m_buffers_in_place; + + /* Buffers for each gdb_std* output stream. */ + buffering_file m_buffered_stdout; + buffering_file m_buffered_stderr; + buffering_file m_buffered_stdlog; + buffering_file m_buffered_stdtarg; + + /* Buffer for current_uiout's output stream. */ + std::optional m_buffered_current_uiout; + + /* Additional ui_out being buffered. */ + ui_out *m_uiout; + + /* Buffer for m_uiout's output stream. */ + std::optional m_buffered_uiout; +}; + +#endif /* GDB_BUFFERED_STREAMS_H */ diff --git a/gdb/cli-out.c b/gdb/cli-out.c index 0f4dbc7768d..b1ea9560fcf 100644 --- a/gdb/cli-out.c +++ b/gdb/cli-out.c @@ -27,6 +27,7 @@ #include "cli/cli-style.h" #include "ui.h" #include "cli/cli-cmds.h" +#include "buffered-streams.h" /* These are the CLI output functions */ diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c index 16012f826ce..3da3edc432d 100644 --- a/gdb/debuginfod-support.c +++ b/gdb/debuginfod-support.c @@ -26,6 +26,7 @@ #include "cli/cli-style.h" #include "cli-out.h" #include "target.h" +#include "buffered-streams.h" /* Set/show debuginfod commands. */ static cmd_list_element *set_debuginfod_prefix_list; diff --git a/gdb/infrun.c b/gdb/infrun.c index 2abf35deeaf..428359c5580 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -76,6 +76,7 @@ #include "disasm.h" #include "interps.h" #include "finish-thread-state.h" +#include "buffered-streams.h" /* Prototypes for local functions */ diff --git a/gdb/stack.c b/gdb/stack.c index 3e8b6911ef7..aa0257902e1 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -50,6 +50,7 @@ #include "linespec.h" #include "cli/cli-utils.h" #include "objfiles.h" +#include "buffered-streams.h" #include "symfile.h" #include "extension.h" diff --git a/gdb/thread.c b/gdb/thread.c index 0788bea235a..96e3bb7b50f 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -50,6 +50,7 @@ #include "stack.h" #include "interps.h" #include "record-full.h" +#include "buffered-streams.h" /* Print notices when new threads are attached and detached. */ static bool print_thread_events = true; diff --git a/gdb/ui-out.c b/gdb/ui-out.c index 00c2055f492..8a41d9897fa 100644 --- a/gdb/ui-out.c +++ b/gdb/ui-out.c @@ -26,6 +26,7 @@ #include "gdbsupport/format.h" #include "cli/cli-style.h" #include "diagnostics.h" +#include "buffered-streams.h" #include #include @@ -847,139 +848,3 @@ ui_out::ui_out (ui_out_flags flags) ui_out::~ui_out () { } - -/* See ui-out.h. */ - -void -buffer_group::output_unit::flush () const -{ - if (!m_msg.empty ()) - m_stream->puts (m_msg.c_str ()); - - if (m_wrap_hint >= 0) - m_stream->wrap_here (m_wrap_hint); - - if (m_flush) - m_stream->flush (); -} - -/* See ui-out.h. */ - -void -buffer_group::write (const char *buf, long length_buf, ui_file *stream) -{ - /* Record each line separately. */ - for (size_t prev = 0, cur = 0; cur < length_buf; ++cur) - if (buf[cur] == '\n' || cur == length_buf - 1) - { - std::string msg (buf + prev, cur - prev + 1); - - if (m_buffered_output.size () > 0 - && m_buffered_output.back ().m_wrap_hint == -1 - && m_buffered_output.back ().m_stream == stream - && m_buffered_output.back ().m_msg.size () > 0 - && m_buffered_output.back ().m_msg.back () != '\n') - m_buffered_output.back ().m_msg.append (msg); - else - m_buffered_output.emplace_back (msg).m_stream = stream; - prev = cur + 1; - } -} - -/* See ui-out.h. */ - -void -buffer_group::wrap_here (int indent, ui_file *stream) -{ - m_buffered_output.emplace_back ("", indent).m_stream = stream; -} - -/* See ui-out.h. */ - -void -buffer_group::flush_here (ui_file *stream) -{ - m_buffered_output.emplace_back ("", -1, true).m_stream = stream; -} - -/* See ui-out.h. */ - -ui_file * -get_unbuffered (ui_file *stream) -{ - while (true) - { - buffering_file *buf = dynamic_cast (stream); - - if (buf == nullptr) - return stream; - - stream = buf->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_uiout (uiout) -{ - gdb_stdout = &m_buffered_stdout; - gdb_stderr = &m_buffered_stderr; - gdb_stdlog = &m_buffered_stdlog; - gdb_stdtarg = &m_buffered_stdtarg; - - ui_file *stream = current_uiout->current_stream (); - if (stream != nullptr) - { - m_buffered_current_uiout.emplace (group, stream); - current_uiout->redirect (&(*m_buffered_current_uiout)); - } - - stream = m_uiout->current_stream (); - if (stream != nullptr && current_uiout != m_uiout) - { - m_buffered_uiout.emplace (group, stream); - m_uiout->redirect (&(*m_buffered_uiout)); - } - - m_buffers_in_place = true; -} - -/* See ui-out.h. */ - -void -buffered_streams::remove_buffers () -{ - if (!m_buffers_in_place) - return; - - 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 (); - - if (m_buffered_current_uiout.has_value ()) - current_uiout->redirect (nullptr); - - if (m_buffered_uiout.has_value ()) - m_uiout->redirect (nullptr); -} - -buffer_group::buffer_group (ui_out *uiout) - : m_buffered_streams (new buffered_streams (this, uiout)) -{ /* Nothing. */ } - -/* See ui-out.h. */ - -void -buffer_group::flush () const -{ - m_buffered_streams->remove_buffers (); - - for (const output_unit &ou : m_buffered_output) - ou.flush (); -} diff --git a/gdb/ui-out.h b/gdb/ui-out.h index ee5e68fa233..69d9910443e 100644 --- a/gdb/ui-out.h +++ b/gdb/ui-out.h @@ -475,184 +475,4 @@ private: struct ui_out *m_uiout; }; -struct buffered_streams; - -/* Organizes writes to a collection of buffered output streams - so that when flushed, output is written to all streams in - chronological order. */ - -struct buffer_group -{ - buffer_group (ui_out *uiout); - - /* Flush all buffered writes to the underlying output streams. */ - void flush () const; - - /* Record contents of BUF and associate it with STREAM. */ - void write (const char *buf, long length_buf, ui_file *stream); - - /* Record a wrap_here and associate it with STREAM. */ - void wrap_here (int indent, ui_file *stream); - - /* Record a call to flush and associate it with STREAM. */ - void flush_here (ui_file *stream); - -private: - - struct output_unit - { - output_unit (std::string msg, int wrap_hint = -1, bool flush = false) - : m_msg (msg), m_wrap_hint (wrap_hint), m_flush (flush) - {} - - /* Write contents of this output_unit to the underlying stream. */ - void flush () const; - - /* Underlying stream for which this output unit will be written to. */ - ui_file *m_stream; - - /* String to be written to underlying buffer. */ - std::string m_msg; - - /* Argument to wrap_here. -1 indicates no wrap. Used to call wrap_here - during buffer flush. */ - int m_wrap_hint; - - /* Indicate that the underlying output stream's flush should be called. */ - bool m_flush; - }; - - /* Output_units to be written to buffered output streams. */ - std::vector m_buffered_output; - - /* Buffered output streams. */ - std::unique_ptr m_buffered_streams; -}; - -/* If FILE is a buffering_file, return its underlying stream. */ - -extern ui_file *get_unbuffered (ui_file *file); - -/* Buffer output to gdb_stdout and gdb_stderr for the duration of FUNC. */ - -template -void -do_with_buffered_output (F func, ui_out *uiout, Arg... args) -{ - buffer_group g (uiout); - - try - { - func (uiout, std::forward (args)...); - } - catch (gdb_exception &ex) - { - /* Ideally flush would be called in the destructor of buffer_group, - however flushing might cause an exception to be thrown. Catch it - and ensure the first exception propagates. */ - try - { - g.flush (); - } - catch (const gdb_exception &) - { - } - - throw_exception (std::move (ex)); - } - - /* Try was successful. Let any further exceptions propagate. */ - g.flush (); -} - -/* Accumulate writes to an underlying ui_file. Output to the - underlying file is deferred until required. */ - -struct buffering_file : public ui_file -{ - buffering_file (buffer_group *group, ui_file *stream) - : m_group (group), - m_stream (stream) - { /* Nothing. */ } - - /* Return the underlying output stream. */ - ui_file *stream () const - { - return m_stream; - } - - /* Record the contents of BUF. */ - void write (const char *buf, long length_buf) override - { - m_group->write (buf, length_buf, m_stream); - } - - /* Record a wrap_here call with argument INDENT. */ - void wrap_here (int indent) override - { - m_group->wrap_here (indent, m_stream); - } - - /* Return true if the underlying stream is a tty. */ - bool isatty () override - { - return m_stream->isatty (); - } - - /* Return true if ANSI escapes can be used on the underlying stream. */ - bool can_emit_style_escape () override - { - return m_stream->can_emit_style_escape (); - } - - /* Flush the underlying output stream. */ - void flush () override - { - return m_group->flush_here (m_stream); - } - -private: - - /* Coordinates buffering across multiple buffering_files. */ - buffer_group *m_group; - - /* The underlying output stream. */ - ui_file *m_stream; -}; - -/* Attaches and detaches buffers for each of the gdb_std* streams. */ - -struct buffered_streams -{ - buffered_streams (buffer_group *group, ui_out *uiout); - - ~buffered_streams () - { - this->remove_buffers (); - } - - /* Remove buffering_files from all underlying streams. */ - void remove_buffers (); - -private: - - /* True if buffers are still attached to each underlying output stream. */ - bool m_buffers_in_place; - - /* Buffers for each gdb_std* output stream. */ - buffering_file m_buffered_stdout; - buffering_file m_buffered_stderr; - buffering_file m_buffered_stdlog; - buffering_file m_buffered_stdtarg; - - /* Buffer for current_uiout's output stream. */ - std::optional m_buffered_current_uiout; - - /* Additional ui_out being buffered. */ - ui_out *m_uiout; - - /* Buffer for m_uiout's output stream. */ - std::optional m_buffered_uiout; -}; - #endif /* GDB_UI_OUT_H */