Yume
errors.cpp
Go to the documentation of this file.
1#include "errors.hpp"
2
3#include "ast/ast.hpp"
4#include "token.hpp"
5#include "util.hpp"
6#include <algorithm>
7#include <array>
8#include <cstddef>
9#include <cstdint>
10#include <filesystem>
11#include <functional>
12#include <llvm/ADT/StringExtras.h>
13#include <llvm/Support/Process.h>
14#include <llvm/Support/Signals.h>
15#include <llvm/Support/raw_ostream.h>
16#include <string_view>
17#include <utility>
18
19namespace yume {
20ASTStackTrace::ASTStackTrace(string message) : message(move(message)) {}
21
22ASTStackTrace::ASTStackTrace(string message, const ast::AST& ast) : message(move(message)) {
23 this->message += " (" + ast.location().to_string() + ")";
24}
25
26ParserStackTrace::ParserStackTrace(string message) : message(move(message)) {}
27
28ParserStackTrace::ParserStackTrace(string message, const Token& token) : message(move(message)) {
29 this->message += " (" + token.loc.to_string() + ")";
30}
31
32namespace {
33enum Phase : uint8_t { Index, Address, Function, Source, Offset };
34
35struct ContainerLikeSimplify {
36 string_view front;
37 string_view middle;
38};
39
40struct SpanLikeSimplify {
41 string_view front;
42 string_view end;
43};
44
45struct DirectReplaceSimplify {
46 string_view from;
47 string_view to;
48};
49
50class stacktrace_ostream : public llvm::raw_ostream { // NOLINT(readability-identifier-naming): STL-like class
51 using enum llvm::raw_ostream::Colors;
52
53 Phase m_current_phase{};
54 unsigned char m_template_depth{};
55 bool m_unknown{};
56 bool m_skip{};
57 uint64_t m_total_size{};
58 string m_direct_buffer{};
59 llvm::raw_string_ostream m_buffer;
60
61 void write_impl(const char* ptr, size_t size) override;
62
63 void simplify(string_view msg);
64 auto simplify(string_view msg, ContainerLikeSimplify s) -> bool;
65 auto simplify(string_view msg, SpanLikeSimplify s) -> bool;
66 auto simplify(string_view msg, DirectReplaceSimplify s) -> bool;
67
68 void format_phase(string_view msg);
69 void set_color(llvm::raw_ostream::Colors color);
70
71 [[nodiscard]] auto current_pos() const -> uint64_t override { return m_total_size; }
72
73 static const std::size_t ALLOC_FOR_BUFFER = 128;
74 static const bool use_color;
75
76public:
77 explicit stacktrace_ostream() : m_buffer(m_direct_buffer) {
78 SetUnbuffered();
79 m_direct_buffer.reserve(ALLOC_FOR_BUFFER);
80 }
81
82 void clean_buffer() {
83 if (!m_skip)
84 errs() << m_direct_buffer;
85
86 m_skip = false;
87 m_direct_buffer.clear();
88 }
89};
90
91const bool stacktrace_ostream::use_color = errs().has_colors();
92
93// std::unique_ptr<$, std::default_delete<$> >
94constexpr ContainerLikeSimplify UPTR_SIMPLIFY = {"std::unique_ptr<", ", std::default_delete<"};
95// std::vector<$, std::allocator<$> >
96constexpr ContainerLikeSimplify VEC_SIMPLIFY = {"std::vector<", ", std::allocator<"};
97
98// std::span<$, 18446744073709551615ul>
99constexpr SpanLikeSimplify SPAN_SIMPLIFY = {"std::span<", ", 18446744073709551615ul>"};
100
101constexpr DirectReplaceSimplify STRING_SIMPLIFY = {
102 "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >", "std::string"};
103constexpr DirectReplaceSimplify STRINGSTREAM_SIMPLIFY = {
104 "std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >", "std::stringstream"};
105constexpr DirectReplaceSimplify ANONYMOUS_NS_SIMPLIFY = {"::(anonymous namespace)::", ":::"};
106
107auto stacktrace_ostream::simplify(string_view msg, ContainerLikeSimplify s) -> bool {
108 constexpr static const string_view MIDDLE_END = "> >";
109 constexpr static const string_view END = ">";
110
111 auto uptr_start = msg.find(s.front);
112 if (uptr_start == string::npos)
113 return false;
114
115 auto uptr_delete_start = msg.find(s.middle, uptr_start);
116 if (uptr_delete_start == string::npos)
117 return false;
118
119 auto contained_start = uptr_start + s.front.length();
120 auto contained_typename = msg.substr(contained_start, uptr_delete_start - contained_start);
121 auto from_deleter = msg.substr(uptr_delete_start + s.middle.length());
122
123 if (!from_deleter.starts_with(contained_typename))
124 return false;
125
126 if (!from_deleter.substr(contained_typename.length()).starts_with(MIDDLE_END))
127 return false;
128
129 auto uptr_end = uptr_delete_start + s.middle.length() + contained_typename.length() + MIDDLE_END.length();
130
131 simplify(msg.substr(0, uptr_start));
132 m_buffer << s.front << contained_typename << END;
133 simplify(msg.substr(uptr_end));
134
135 return true;
136}
137
138auto stacktrace_ostream::simplify(string_view msg, SpanLikeSimplify s) -> bool {
139 constexpr static const string_view END = ">";
140
141 auto span_start = msg.find(s.front);
142 if (span_start == string::npos)
143 return false;
144
145 auto span_bound_start = msg.find(s.end, span_start);
146 if (span_bound_start == string::npos)
147 return false;
148
149 auto contained_start = span_start + s.front.length();
150 auto contained_typename = msg.substr(contained_start, span_bound_start - contained_start);
151
152 auto span_end = span_bound_start + s.end.length();
153
154 simplify(msg.substr(0, span_start));
155 m_buffer << s.front << contained_typename << END;
156 simplify(msg.substr(span_end));
157
158 return true;
159}
160
161auto stacktrace_ostream::simplify(string_view msg, DirectReplaceSimplify s) -> bool {
162 auto start = msg.find(s.from);
163 if (start == string::npos)
164 return false;
165
166 simplify(msg.substr(0, start));
167 m_buffer << s.to;
168 simplify(msg.substr(start + s.from.size()));
169
170 return true;
171}
172
173void stacktrace_ostream::simplify(string_view msg) {
174 bool found_any = simplify(msg, UPTR_SIMPLIFY) || simplify(msg, VEC_SIMPLIFY) || simplify(msg, STRING_SIMPLIFY) ||
175 simplify(msg, STRINGSTREAM_SIMPLIFY) || simplify(msg, ANONYMOUS_NS_SIMPLIFY) ||
176 simplify(msg, SPAN_SIMPLIFY);
177
178 if (found_any)
179 return;
180
181 size_t prev_pos = 0;
182 size_t start_pos = 0;
183 while (true) {
184 start_pos = msg.find_first_of("<>()", prev_pos);
185 auto substr = msg.substr(prev_pos, start_pos == string::npos ? start_pos : start_pos - prev_pos);
186
187 if (m_template_depth == 0)
188 set_color(CYAN);
189
190 m_buffer << substr;
191
192 if (m_template_depth == 0 && use_color)
193 m_buffer << llvm::sys::Process::ResetColor();
194
195 if (start_pos == string::npos)
196 break;
197
198 auto angle = msg.at(start_pos);
199 m_template_depth += (angle == '<' || angle == '(') ? 1 : -1;
200 m_buffer << angle;
201 prev_pos = start_pos + 1;
202 }
203}
204
205void stacktrace_ostream::set_color(llvm::raw_ostream::Colors color) {
206 if (color != RESET && use_color)
207 m_buffer << llvm::sys::Process::OutputColor(static_cast<char>(color), false, false);
208}
209
210void stacktrace_ostream::format_phase(string_view msg) {
211 static array skip_lines = {"yume::CRTPWalker<"sv, "__libc_start_"sv, "std::__invoke_impl"sv, "std::__invoke"sv,
212 "::__visit_invoke"sv, "std::__do_visit"sv, "std::invoke_result<"sv};
213 static const string SOURCE_DIR = YUME_SRC_DIR;
214
215 switch (m_current_phase) {
216 case Address: m_buffer << msg; break;
217 case Index:
218 set_color(GREEN);
219 m_buffer << msg;
220 break;
221 case Offset:
222 set_color(RED);
223 m_buffer << msg;
224 break;
225 case Function:
226 set_color(CYAN);
227 if (std::ranges::any_of(skip_lines, [&](string_view s) noexcept { return msg.find(s) != string::npos; }))
228 m_skip = true;
229 else
230 simplify(msg);
231 break;
232 case Source:
233 set_color(YELLOW);
234 if (auto normal_path = std::filesystem::path(msg).lexically_normal().native();
235 normal_path.starts_with(SOURCE_DIR) && use_color) {
236 m_buffer << llvm::sys::Process::OutputBold(false);
237 m_buffer << static_cast<string_view>(normal_path).substr(SOURCE_DIR.size());
238 } else {
239 m_buffer << normal_path;
240 }
241 }
242
243 if (use_color)
244 m_buffer << llvm::sys::Process::ResetColor();
245}
246
247void stacktrace_ostream::write_impl(const char* ptr, size_t size) {
248 auto msg = string_view(ptr, size);
249 m_total_size += size;
250
251 if (size == 1 && llvm::isSpace(msg.at(0))) {
252 m_buffer << msg.at(0);
253 return;
254 }
255
256 if (m_unknown) {
257 m_unknown = msg.at(0) != ')';
258 } else if (msg.at(0) == '(') {
259 m_unknown = true;
260 m_skip = true;
261 m_current_phase = Offset;
262 } else if (msg.at(0) == '/' && m_current_phase != Source) {
263 m_current_phase = Source;
264 }
265
266 format_phase(msg);
267
268 int counter = static_cast<int>(m_current_phase);
269 if (m_current_phase != Offset) {
270 ++counter;
271 counter %= 4;
272 }
273
274 m_current_phase = static_cast<Phase>(counter);
275 if (m_current_phase == Offset && !m_unknown)
276 m_current_phase = Index;
277
278 if (m_current_phase == Index)
279 clean_buffer();
280}
281
282} // namespace
283
284void backtrace(void* /*unused*/) {
285 static stacktrace_ostream stream{};
286 llvm::sys::PrintStackTrace(stream);
287 stream.clean_buffer();
288}
289} // namespace yume
string_view front
Definition: errors.cpp:36
string_view middle
Definition: errors.cpp:37
string_view end
Definition: errors.cpp:42
string_view to
Definition: errors.cpp:47
string_view from
Definition: errors.cpp:46
Definition: ast.cpp:8
void backtrace(void *)
Definition: errors.cpp:284
ASTStackTrace(std::string message)