Branch data Line data Source code
1 : : /* Support for buffering diagnostics before flushing them to output sinks.
2 : : Copyright (C) 2024-2025 Free Software Foundation, Inc.
3 : : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it under
8 : : the terms of the GNU General Public License as published by the Free
9 : : Software Foundation; either version 3, or (at your option) any later
10 : : version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #define INCLUDE_VECTOR
22 : : #include "config.h"
23 : : #include "system.h"
24 : : #include "coretypes.h"
25 : : #include "diagnostics/buffering.h"
26 : : #include "diagnostics/sink.h"
27 : : #include "diagnostics/dumping.h"
28 : :
29 : : namespace diagnostics {
30 : :
31 : : /* Methods fns of diagnostics::context relating to buffering. */
32 : :
33 : : /* If BUFFER_ is non-null, use BUFFER as the active diagnostics::buffer on
34 : : this context. BUFFER is borrowed.
35 : :
36 : : If BUFFER_ is null, stop any buffering on this context until the next call
37 : : to this function. */
38 : :
39 : : void
40 : 2696463 : context::set_diagnostic_buffer (buffer *buffer_)
41 : : {
42 : : /* Early reject of no-op (to avoid recursively crashing when handling an
43 : : ICE when inside a nested diagnostics; see PR diagnostics/121876). */
44 : 2696463 : if (buffer_ == m_diagnostic_buffer)
45 : : return;
46 : :
47 : : /* We don't allow changing buffering within a diagnostic group
48 : : (to simplify handling of buffered diagnostics within the
49 : : diagnostic_format implementations). */
50 : 2388290 : gcc_assert (m_diagnostic_groups.m_group_nesting_depth == 0);
51 : :
52 : : /* Likewise, for simplicity, we only allow changing buffers
53 : : at nesting level 0. */
54 : 2388290 : gcc_assert (m_diagnostic_groups.m_diagnostic_nesting_level == 0);
55 : :
56 : 2388290 : m_diagnostic_buffer = buffer_;
57 : :
58 : 2388290 : if (buffer_)
59 : : {
60 : 1194149 : buffer_->ensure_per_sink_buffers ();
61 : 1194149 : gcc_assert (buffer_->m_per_sink_buffers);
62 : 3582447 : gcc_assert (buffer_->m_per_sink_buffers->length ()
63 : : == m_sinks.length ());
64 : 2388298 : for (unsigned idx = 0; idx < m_sinks.length (); ++idx)
65 : : {
66 : 1194149 : auto sink_ = m_sinks[idx];
67 : 1194149 : auto per_sink_buffer = (*buffer_->m_per_sink_buffers)[idx];
68 : 1194149 : sink_->set_buffer (per_sink_buffer);
69 : : }
70 : : }
71 : : else
72 : 4776564 : for (auto sink_ : m_sinks)
73 : 1194141 : sink_->set_buffer (nullptr);
74 : : }
75 : :
76 : : /* Clear BUFFER_ without flushing it. */
77 : :
78 : : void
79 : 22877713 : context::clear_diagnostic_buffer (buffer &buffer_)
80 : : {
81 : 22877713 : if (buffer_.m_per_sink_buffers)
82 : 19924040 : for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
83 : 4981010 : per_sink_buffer_->clear ();
84 : :
85 : 22877713 : buffer_.m_diagnostic_counters.clear ();
86 : :
87 : : /* We need to reset last_location, otherwise we may skip caret lines
88 : : when we actually give a diagnostic. */
89 : 22877713 : m_last_location = UNKNOWN_LOCATION;
90 : 22877713 : }
91 : :
92 : : /* Flush the diagnostics in BUFFER_ to this context, clearing BUFFER_. */
93 : :
94 : : void
95 : 7230 : context::flush_diagnostic_buffer (buffer &buffer_)
96 : : {
97 : 7230 : bool had_errors
98 : 7230 : = (buffer_.diagnostic_count (kind::error) > 0
99 : 7230 : || buffer_.diagnostic_count (kind::werror) > 0);
100 : 7230 : if (buffer_.m_per_sink_buffers)
101 : 28920 : for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
102 : 7230 : per_sink_buffer_->flush ();
103 : 7230 : buffer_.m_diagnostic_counters.move_to (m_diagnostic_counters);
104 : :
105 : 11286 : action_after_output (had_errors ? kind::error : kind::warning);
106 : 7230 : check_max_errors (true);
107 : 7227 : }
108 : :
109 : : /* class diagnostics::buffer. */
110 : :
111 : 8405472 : buffer::buffer (context &ctxt)
112 : 8405472 : : m_ctxt (ctxt),
113 : 8405472 : m_per_sink_buffers (nullptr)
114 : : {
115 : 8405472 : }
116 : :
117 : 8374585 : buffer::~buffer ()
118 : : {
119 : 8374585 : if (m_per_sink_buffers)
120 : : {
121 : 377656 : for (auto iter : *m_per_sink_buffers)
122 : 94414 : delete iter;
123 : 188828 : delete m_per_sink_buffers;
124 : : }
125 : 8374585 : }
126 : :
127 : : void
128 : 0 : buffer::dump (FILE *out, int indent) const
129 : : {
130 : 0 : m_diagnostic_counters.dump (out, indent + 2);
131 : 0 : dumping::emit_heading (out, indent, "m_per_sink_buffers");
132 : 0 : if (m_per_sink_buffers)
133 : 0 : for (auto per_sink_buffer_ : *m_per_sink_buffers)
134 : 0 : per_sink_buffer_->dump (out, indent + 2);
135 : : else
136 : 0 : dumping::emit_none (out, indent + 2);
137 : 0 : }
138 : :
139 : : bool
140 : 6006097 : buffer::empty_p () const
141 : : {
142 : 6006097 : if (m_per_sink_buffers)
143 : 9577887 : for (auto per_sink_buffer_ : *m_per_sink_buffers)
144 : : /* Query initial buffer. */
145 : 3192629 : return per_sink_buffer_->empty_p ();
146 : : return true;
147 : : }
148 : :
149 : : void
150 : 103600 : buffer::move_to (buffer &dest)
151 : : {
152 : : /* Bail if there's nothing to move. */
153 : 103600 : if (!m_per_sink_buffers)
154 : : return;
155 : :
156 : 103600 : m_diagnostic_counters.move_to (dest.m_diagnostic_counters);
157 : :
158 : 103600 : if (!dest.m_per_sink_buffers)
159 : : {
160 : : /* Optimization for the "move to empty" case:
161 : : simply move the vec to the dest. */
162 : 81115 : dest.m_per_sink_buffers = m_per_sink_buffers;
163 : 81115 : m_per_sink_buffers = nullptr;
164 : 81115 : return;
165 : : }
166 : :
167 : 22485 : dest.ensure_per_sink_buffers ();
168 : 22485 : gcc_assert (m_per_sink_buffers);
169 : 67455 : gcc_assert (m_per_sink_buffers->length ()
170 : : == m_ctxt.m_sinks.length ());
171 : 22485 : gcc_assert (dest.m_per_sink_buffers);
172 : 44970 : gcc_assert (dest.m_per_sink_buffers->length ()
173 : : == m_ctxt.m_sinks.length ());
174 : 44970 : for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
175 : : {
176 : 22485 : auto per_sink_buffer_src = (*m_per_sink_buffers)[idx];
177 : 22485 : auto per_sink_buffer_dest = (*dest.m_per_sink_buffers)[idx];
178 : 22485 : per_sink_buffer_src->move_to (*per_sink_buffer_dest);
179 : : }
180 : : }
181 : :
182 : : /* Lazily get the output formats to create their own kind of buffers.
183 : : We can't change the output sinks on a context once this has been called
184 : : on any diagnostics::buffer instances for that context, since there's no
185 : : way to update all diagnostics::buffer instances for that context. */
186 : :
187 : : void
188 : 1216634 : buffer::ensure_per_sink_buffers ()
189 : : {
190 : 1216634 : if (!m_per_sink_buffers)
191 : : {
192 : 96383 : m_per_sink_buffers = new auto_vec<per_sink_buffer *> ();
193 : 192766 : for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
194 : : {
195 : 96383 : auto sink_ = m_ctxt.m_sinks[idx];
196 : 96383 : auto per_sink_buffer = sink_->make_per_sink_buffer ();
197 : 96383 : m_per_sink_buffers->safe_push (per_sink_buffer.release ());
198 : 96383 : }
199 : : }
200 : 1216634 : gcc_assert (m_per_sink_buffers);
201 : 3649902 : gcc_assert (m_per_sink_buffers->length ()
202 : : == m_ctxt.m_sinks.length ());
203 : 1216634 : }
204 : :
205 : : } // namespace diagnostics
|