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>
22ASTStackTrace::ASTStackTrace(
string message,
const ast::AST& ast) : message(move(message)) {
23 this->message +=
" (" + ast.location().to_string() +
")";
26ParserStackTrace::ParserStackTrace(
string message) : message(move(message)) {}
28ParserStackTrace::ParserStackTrace(
string message,
const Token& token) : message(move(message)) {
29 this->message +=
" (" + token.loc.to_string() +
")";
33enum Phase : uint8_t { Index, Address, Function, Source, Offset };
35struct ContainerLikeSimplify {
40struct SpanLikeSimplify {
45struct DirectReplaceSimplify {
50class stacktrace_ostream :
public llvm::raw_ostream {
51 using enum llvm::raw_ostream::Colors;
53 Phase m_current_phase{};
54 unsigned char m_template_depth{};
57 uint64_t m_total_size{};
58 string m_direct_buffer{};
59 llvm::raw_string_ostream m_buffer;
61 void write_impl(
const char* ptr,
size_t size)
override;
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;
68 void format_phase(string_view msg);
69 void set_color(llvm::raw_ostream::Colors color);
71 [[nodiscard]]
auto current_pos() const -> uint64_t
override {
return m_total_size; }
73 static const std::size_t ALLOC_FOR_BUFFER = 128;
74 static const bool use_color;
77 explicit stacktrace_ostream() : m_buffer(m_direct_buffer) {
79 m_direct_buffer.reserve(ALLOC_FOR_BUFFER);
84 errs() << m_direct_buffer;
87 m_direct_buffer.clear();
91const bool stacktrace_ostream::use_color = errs().has_colors();
94constexpr ContainerLikeSimplify UPTR_SIMPLIFY = {
"std::unique_ptr<",
", std::default_delete<"};
96constexpr ContainerLikeSimplify VEC_SIMPLIFY = {
"std::vector<",
", std::allocator<"};
99constexpr SpanLikeSimplify SPAN_SIMPLIFY = {
"std::span<",
", 18446744073709551615ul>"};
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)::",
":::"};
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 =
">";
111 auto uptr_start = msg.find(s.front);
112 if (uptr_start == string::npos)
115 auto uptr_delete_start = msg.find(s.middle, uptr_start);
116 if (uptr_delete_start == string::npos)
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());
123 if (!from_deleter.starts_with(contained_typename))
126 if (!from_deleter.substr(contained_typename.length()).starts_with(MIDDLE_END))
129 auto uptr_end = uptr_delete_start + s.middle.length() + contained_typename.length() + MIDDLE_END.length();
131 simplify(msg.substr(0, uptr_start));
132 m_buffer << s.front << contained_typename << END;
133 simplify(msg.substr(uptr_end));
138auto stacktrace_ostream::simplify(string_view msg, SpanLikeSimplify s) ->
bool {
139 constexpr static const string_view END =
">";
141 auto span_start = msg.find(s.front);
142 if (span_start == string::npos)
145 auto span_bound_start = msg.find(s.end, span_start);
146 if (span_bound_start == string::npos)
149 auto contained_start = span_start + s.front.length();
150 auto contained_typename = msg.substr(contained_start, span_bound_start - contained_start);
152 auto span_end = span_bound_start + s.end.length();
154 simplify(msg.substr(0, span_start));
155 m_buffer << s.front << contained_typename << END;
156 simplify(msg.substr(span_end));
161auto stacktrace_ostream::simplify(string_view msg, DirectReplaceSimplify s) ->
bool {
162 auto start = msg.find(s.from);
163 if (start == string::npos)
166 simplify(msg.substr(0, start));
168 simplify(msg.substr(start + s.from.size()));
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);
182 size_t start_pos = 0;
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);
187 if (m_template_depth == 0)
192 if (m_template_depth == 0 && use_color)
193 m_buffer << llvm::sys::Process::ResetColor();
195 if (start_pos == string::npos)
198 auto angle = msg.at(start_pos);
199 m_template_depth += (angle ==
'<' || angle ==
'(') ? 1 : -1;
201 prev_pos = start_pos + 1;
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);
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;
215 switch (m_current_phase) {
216 case Address: m_buffer << msg;
break;
227 if (std::ranges::any_of(skip_lines, [&](string_view s)
noexcept {
return msg.find(s) != string::npos; }))
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());
239 m_buffer << normal_path;
244 m_buffer << llvm::sys::Process::ResetColor();
247void stacktrace_ostream::write_impl(
const char* ptr,
size_t size) {
248 auto msg = string_view(ptr, size);
249 m_total_size += size;
251 if (size == 1 && llvm::isSpace(msg.at(0))) {
252 m_buffer << msg.at(0);
257 m_unknown = msg.at(0) !=
')';
258 }
else if (msg.at(0) ==
'(') {
261 m_current_phase = Offset;
262 }
else if (msg.at(0) ==
'/' && m_current_phase != Source) {
263 m_current_phase = Source;
268 int counter =
static_cast<int>(m_current_phase);
269 if (m_current_phase != Offset) {
274 m_current_phase =
static_cast<Phase
>(counter);
275 if (m_current_phase == Offset && !m_unknown)
276 m_current_phase = Index;
278 if (m_current_phase == Index)
285 static stacktrace_ostream stream{};
286 llvm::sys::PrintStackTrace(stream);
287 stream.clean_buffer();
ASTStackTrace(std::string message)