From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id +TsuJaqwfGl6rx0AWB0awg (envelope-from ) for ; Fri, 30 Jan 2026 08:22:50 -0500 Authentication-Results: simark.ca; dkim=fail reason="signature verification failed" (768-bit key; unprotected) header.d=tromey.com header.i=@tromey.com header.a=rsa-sha256 header.s=default header.b=Xn3NpbXu; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 89A1A1E0DD; Fri, 30 Jan 2026 08:22:50 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id F07BB1E08D for ; Fri, 30 Jan 2026 08:22:48 -0500 (EST) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 68AC34BB3BCE for ; Fri, 30 Jan 2026 13:22:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 68AC34BB3BCE Authentication-Results: sourceware.org; dkim=fail reason="signature verification failed" (768-bit key, unprotected) header.d=tromey.com header.i=@tromey.com header.a=rsa-sha256 header.s=default header.b=Xn3NpbXu Received: from omta40.uswest2.a.cloudfilter.net (omta40.uswest2.a.cloudfilter.net [35.89.44.39]) by sourceware.org (Postfix) with ESMTPS id 800124BA9000 for ; Fri, 30 Jan 2026 13:17:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 800124BA9000 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=tromey.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=tromey.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 800124BA9000 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=35.89.44.39 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1769779044; cv=none; b=Ir3fvzhecdzd+NGlRhBSeIcO/+2PNkSsh6Z6prHijpBt6ggIzZm5rabGHBBBH3t0TMPoJFbosV/lIvodbbIwSWQ80nYyjIeEUeAht5yJGWwLvMpIl7Ht/BYGlLFbyj8lLUwZcFr6p/FMvqiH7hx25ZW4OQIB9EocPDmK1H0z0wk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1769779044; c=relaxed/simple; bh=Ei4R9KbCp8FNzgD4Qisdo+yaRSA4XlTAfUBzrJvWcm4=; h=DKIM-Signature:From:Date:Subject:MIME-Version:Message-Id:To; b=PXz4YeABvQYZZ3IdC4mMFVZqBqPdKqzc/3ynQRHhd5HZ3hTZ1Xz0AFd8WFE6OCFNGwwwaJU0q0YxACDh9rBuPf55d/b62pRrKAnW5mc4G8N84565Ybq+k0YB3CYgJqhsNphVQwOo8eLN1FagEJbs3M4fjjjWPmZTPoOMio9r/bg= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 800124BA9000 Received: from eig-obgw-5001b.ext.cloudfilter.net ([10.0.29.181]) by cmsmtp with ESMTPS id lkMGv3r9PaPqLloNHvgSv0; Fri, 30 Jan 2026 13:17:23 +0000 Received: from box5379.bluehost.com ([162.241.216.53]) by cmsmtp with ESMTPS id loNGvfeAlSqlVloNHvPsk0; Fri, 30 Jan 2026 13:17:23 +0000 X-Authority-Analysis: v=2.4 cv=I7FlRMgg c=1 sm=1 tr=0 ts=697caf63 a=ApxJNpeYhEAb1aAlGBBbmA==:117 a=ApxJNpeYhEAb1aAlGBBbmA==:17 a=IkcTkHD0fZMA:10 a=vUbySO9Y5rIA:10 a=ItBw4LHWJt0A:10 a=mDV3o1hIAAAA:8 a=20KFwNOVAAAA:8 a=fVeH9eLHcigc_CKGXLQA:9 a=QEXdDO2ut3YA:10 a=DCx65vhANUyCzuf5D8fC:22 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=tromey.com; s=default; h=Cc:To:In-Reply-To:References:Message-Id: Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date:From:Sender: Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender :Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=xUcvj5bR4+jpk7+mLiPlD1VnCTIV14C35+HvfsKi/dg=; b=Xn3NpbXuylXQ/IomKAWh9EgVml BU3reCSH0FuwNIUreSOml86jn4KZbauziwWq7WZIRZlowLhekKLuaGvDhcLaQ6O4fVnHcL6U5A/vX 5TwZTAu9fUCTGYwt9kINSy27P; Received: from 97-122-114-32.hlrn.qwest.net ([97.122.114.32]:33716 helo=[192.168.122.1]) by box5379.bluehost.com with esmtpsa (TLS1.3) tls TLS_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1vloNG-00000001JjQ-1n1B; Fri, 30 Jan 2026 06:17:22 -0700 From: Tom Tromey Date: Fri, 30 Jan 2026 06:17:19 -0700 Subject: [PATCH v3 05/21] Move buffered stream to new files MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260130-pr-28948-logging-5-v3-5-3eec47ef3cba@tromey.com> References: <20260130-pr-28948-logging-5-v3-0-3eec47ef3cba@tromey.com> In-Reply-To: <20260130-pr-28948-logging-5-v3-0-3eec47ef3cba@tromey.com> To: gdb-patches@sourceware.org Cc: Tom Tromey , Andrew Burgess X-Mailer: b4 0.14.3 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - box5379.bluehost.com X-AntiAbuse: Original Domain - sourceware.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - tromey.com X-BWhitelist: no X-Source-IP: 97.122.114.32 X-Source-L: No X-Exim-ID: 1vloNG-00000001JjQ-1n1B X-Source: X-Source-Args: X-Source-Dir: X-Source-Sender: 97-122-114-32.hlrn.qwest.net ([192.168.122.1]) [97.122.114.32]:33716 X-Source-Auth: tom+tromey.com X-Email-Count: 10 X-Org: HG=bhshared;ORG=bluehost; X-Source-Cap: ZWx5bnJvYmk7ZWx5bnJvYmk7Ym94NTM3OS5ibHVlaG9zdC5jb20= X-Local-Domain: yes X-CMAE-Envelope: MS4xfEFHf/7w7QQNfE3LBrr+mW8k1Wm0tNXWKeroZdZ1RXX474pA1+SUxifE2o7HdJSkNDk57WIDGL9HvZLcx9MLA8iYGhDXA7UTewESXvDr8F/N5j8NmuT9 j9nE+rj4taG19DBwqmazklsEXcgvzkM+z6itaIbbJyVtv5z+ZGStZ3mKqSLJt6MAEmn3zmhZAzwnbtQU5oiCEVDhvHgFcY90pm0= X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org 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 --- gdb/Makefile.in | 2 + gdb/buffered-streams.c | 155 +++++++++++++++++++++++++++++++++++ gdb/buffered-streams.h | 205 +++++++++++++++++++++++++++++++++++++++++++++++ gdb/cli-out.c | 1 + gdb/debuginfod-support.c | 1 + gdb/infrun.c | 1 + gdb/stack.c | 1 + gdb/thread.c | 1 + gdb/ui-out.c | 137 +------------------------------ gdb/ui-out.h | 180 ----------------------------------------- 10 files changed, 368 insertions(+), 316 deletions(-) diff --git a/gdb/Makefile.in b/gdb/Makefile.in index fc76925344f..9aa46f8b1fa 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 \ @@ -1346,6 +1347,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 6bcd8ec5cc0..422b4728477 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 732d525083e..919cbf73642 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 @@ class ui_out_redirect_pop 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 */ -- 2.49.0