Handle null lines / columns better

This commit is contained in:
Jeremy 2023-11-15 11:52:24 -05:00
parent 37a3ea7978
commit aed456bc63
No known key found for this signature in database
GPG Key ID: B4C8300FEC395042
9 changed files with 120 additions and 70 deletions

View File

@ -3,8 +3,11 @@
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
#include <limits>
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <type_traits>
#include <utility>
#include <vector> #include <vector>
#include "cpptrace/cpptrace_export.hpp" #include "cpptrace/cpptrace_export.hpp"
@ -27,8 +30,8 @@ namespace cpptrace {
struct CPPTRACE_EXPORT raw_trace { struct CPPTRACE_EXPORT raw_trace {
std::vector<frame_ptr> frames; std::vector<frame_ptr> frames;
static raw_trace from_buffer(frame_ptr* buffer, std::size_t size); static raw_trace from_buffer(frame_ptr* buffer, std::size_t size);
static raw_trace current(std::uint_least32_t skip = 0); static raw_trace current(std::size_t skip = 0);
static raw_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth); static raw_trace current(std::size_t skip, std::size_t max_depth);
object_trace resolve_object_trace() const; object_trace resolve_object_trace() const;
stacktrace resolve() const; stacktrace resolve() const;
void clear(); void clear();
@ -53,8 +56,8 @@ namespace cpptrace {
struct CPPTRACE_EXPORT object_trace { struct CPPTRACE_EXPORT object_trace {
std::vector<object_frame> frames; std::vector<object_frame> frames;
static object_trace current(std::uint_least32_t skip = 0); static object_trace current(std::size_t skip = 0);
static object_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth); static object_trace current(std::size_t skip, std::size_t max_depth);
stacktrace resolve() const; stacktrace resolve() const;
void clear(); void clear();
bool empty() const noexcept; bool empty() const noexcept;
@ -69,10 +72,50 @@ namespace cpptrace {
inline const_iterator cend() const noexcept { return frames.cend(); } inline const_iterator cend() const noexcept { return frames.cend(); }
}; };
// This represents a nullable integer type
// The max value of the type is used as a sentinel
// This is used over std::optional because the library is C++11 and also std::optional is a bit heavy-duty for this
// use.
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
struct CPPTRACE_EXPORT nullable {
T raw_value;
nullable& operator=(T value) {
raw_value = value;
return *this;
}
bool has_value() const noexcept {
return raw_value != std::numeric_limits<T>::max();
}
T& value() noexcept {
return raw_value;
}
const T& value() const noexcept {
return raw_value;
}
T value_or(T alternative) const noexcept {
return has_value() ? raw_value : alternative;
}
void swap(nullable& other) noexcept {
std::swap(raw_value, other.raw_value);
}
void reset() noexcept {
raw_value = std::numeric_limits<T>::max();
}
bool operator==(const nullable& other) const noexcept {
return raw_value == other.raw_value;
}
bool operator!=(const nullable& other) const noexcept {
return raw_value != other.raw_value;
}
constexpr static nullable null() noexcept {
return { std::numeric_limits<T>::max() };
}
};
struct CPPTRACE_EXPORT stacktrace_frame { struct CPPTRACE_EXPORT stacktrace_frame {
frame_ptr address; frame_ptr address;
std::uint_least32_t line; // TODO: This should use UINT_LEAST32_MAX as a sentinel nullable<std::uint32_t> line;
std::uint_least32_t column; // UINT_LEAST32_MAX if not present nullable<std::uint32_t> column;
std::string filename; std::string filename;
std::string symbol; std::string symbol;
bool is_inline; bool is_inline;
@ -95,8 +138,8 @@ namespace cpptrace {
struct CPPTRACE_EXPORT stacktrace { struct CPPTRACE_EXPORT stacktrace {
std::vector<stacktrace_frame> frames; std::vector<stacktrace_frame> frames;
static stacktrace current(std::uint_least32_t skip = 0); static stacktrace current(std::size_t skip = 0);
static stacktrace current(std::uint_least32_t skip, std::uint_least32_t max_depth); static stacktrace current(std::size_t skip, std::size_t max_depth);
void print() const; void print() const;
void print(std::ostream& stream) const; void print(std::ostream& stream) const;
void print(std::ostream& stream, bool color) const; void print(std::ostream& stream, bool color) const;
@ -118,23 +161,23 @@ namespace cpptrace {
friend void print_terminate_trace(); friend void print_terminate_trace();
}; };
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::uint_least32_t skip = 0); CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip = 0);
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth);
CPPTRACE_EXPORT object_trace generate_object_trace(std::uint_least32_t skip = 0); CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip = 0);
CPPTRACE_EXPORT object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip, std::size_t max_depth);
CPPTRACE_EXPORT stacktrace generate_trace(std::uint_least32_t skip = 0); CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip = 0);
CPPTRACE_EXPORT stacktrace generate_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip, std::size_t max_depth);
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace( CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
frame_ptr* buffer, frame_ptr* buffer,
std::size_t size, std::size_t size,
std::uint_least32_t skip = 0 std::size_t skip = 0
); );
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace( CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
frame_ptr* buffer, frame_ptr* buffer,
std::size_t size, std::size_t size,
std::uint_least32_t skip, std::size_t skip,
std::uint_least32_t max_depth std::size_t max_depth
); );
// utilities: // utilities:
@ -172,8 +215,8 @@ namespace cpptrace {
mutable std::string what_string; mutable std::string what_string;
protected: protected:
explicit exception(std::uint_least32_t skip, std::uint_least32_t max_depth) noexcept; explicit exception(std::size_t skip, std::size_t max_depth) noexcept;
explicit exception(std::uint_least32_t skip) noexcept : exception(skip + 1, UINT_LEAST32_MAX) {} explicit exception(std::size_t skip) noexcept : exception(skip + 1, SIZE_MAX) {}
public: public:
explicit exception() noexcept : exception(1) {} explicit exception() noexcept : exception(1) {}
@ -200,8 +243,8 @@ namespace cpptrace {
explicit exception_with_message( explicit exception_with_message(
std::string&& message_arg, std::string&& message_arg,
std::uint_least32_t skip, std::size_t skip,
std::uint_least32_t max_depth std::size_t max_depth
) noexcept : exception(skip + 1, max_depth), message(std::move(message_arg)) {} ) noexcept : exception(skip + 1, max_depth), message(std::move(message_arg)) {}
public: public:

View File

@ -34,12 +34,12 @@ namespace cpptrace {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
raw_trace raw_trace::current(std::uint_least32_t skip) { raw_trace raw_trace::current(std::size_t skip) {
return generate_raw_trace(skip + 1); return generate_raw_trace(skip + 1);
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
raw_trace raw_trace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) { raw_trace raw_trace::current(std::size_t skip, std::size_t max_depth) {
return generate_raw_trace(skip + 1, max_depth); return generate_raw_trace(skip + 1, max_depth);
} }
@ -78,12 +78,12 @@ namespace cpptrace {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
object_trace object_trace::current(std::uint_least32_t skip) { object_trace object_trace::current(std::size_t skip) {
return generate_object_trace(skip + 1); return generate_object_trace(skip + 1);
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
object_trace object_trace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) { object_trace object_trace::current(std::size_t skip, std::size_t max_depth) {
return generate_object_trace(skip + 1, max_depth); return generate_object_trace(skip + 1, max_depth);
} }
@ -125,24 +125,24 @@ namespace cpptrace {
<< frame.symbol << frame.symbol
<< " at " << " at "
<< frame.filename; << frame.filename;
if(frame.line != 0) { if(frame.line.has_value()) {
stream stream
<< ":" << ":"
<< frame.line; << frame.line.value();
if(frame.column != UINT_LEAST32_MAX) { if(frame.column.has_value()) {
stream << frame.column; stream << frame.column.value();
} }
} }
return stream; return stream;
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
stacktrace stacktrace::current(std::uint32_t skip) { stacktrace stacktrace::current(std::size_t skip) {
return generate_trace(skip + 1); return generate_trace(skip + 1);
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
stacktrace stacktrace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) { stacktrace stacktrace::current(std::size_t skip, std::size_t max_depth) {
return generate_trace(skip + 1, max_depth); return generate_trace(skip + 1, max_depth);
} }
@ -206,16 +206,16 @@ namespace cpptrace {
<< green << green
<< frame.filename << frame.filename
<< reset; << reset;
if(frame.line != 0) { if(frame.line.has_value()) {
stream stream
<< ":" << ":"
<< blue << blue
<< frame.line << frame.line.value()
<< reset; << reset;
if(frame.column != UINT_LEAST32_MAX) { if(frame.column.has_value()) {
stream << ':' stream << ':'
<< blue << blue
<< std::to_string(frame.column) << std::to_string(frame.column.value())
<< reset; << reset;
} }
} }
@ -245,9 +245,9 @@ namespace cpptrace {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
raw_trace generate_raw_trace(std::uint_least32_t skip) { raw_trace generate_raw_trace(std::size_t skip) {
try { try {
return raw_trace{detail::capture_frames(skip + 1, UINT_LEAST32_MAX)}; return raw_trace{detail::capture_frames(skip + 1, SIZE_MAX)};
} catch(...) { // NOSONAR } catch(...) { // NOSONAR
if(!detail::should_absorb_trace_exceptions()) { if(!detail::should_absorb_trace_exceptions()) {
throw; throw;
@ -257,7 +257,7 @@ namespace cpptrace {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) { raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth) {
try { try {
return raw_trace{detail::capture_frames(skip + 1, max_depth)}; return raw_trace{detail::capture_frames(skip + 1, max_depth)};
} catch(...) { // NOSONAR } catch(...) { // NOSONAR
@ -269,24 +269,24 @@ namespace cpptrace {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::uint_least32_t skip) { std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip) {
return detail::safe_capture_frames(buffer, size, skip + 1, UINT_LEAST32_MAX); return detail::safe_capture_frames(buffer, size, skip + 1, SIZE_MAX);
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::size_t safe_generate_raw_trace( std::size_t safe_generate_raw_trace(
frame_ptr* buffer, frame_ptr* buffer,
std::size_t size, std::size_t size,
std::uint_least32_t skip, std::size_t skip,
std::uint_least32_t max_depth std::size_t max_depth
) { ) {
return detail::safe_capture_frames(buffer, size, skip + 1, max_depth); return detail::safe_capture_frames(buffer, size, skip + 1, max_depth);
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
object_trace generate_object_trace(std::uint_least32_t skip) { object_trace generate_object_trace(std::size_t skip) {
try { try {
return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, UINT_LEAST32_MAX))}; return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, SIZE_MAX))};
} catch(...) { // NOSONAR } catch(...) { // NOSONAR
if(!detail::should_absorb_trace_exceptions()) { if(!detail::should_absorb_trace_exceptions()) {
throw; throw;
@ -296,7 +296,7 @@ namespace cpptrace {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) { object_trace generate_object_trace(std::size_t skip, std::size_t max_depth) {
try { try {
return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth))}; return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth))};
} catch(...) { // NOSONAR } catch(...) { // NOSONAR
@ -308,12 +308,12 @@ namespace cpptrace {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
stacktrace generate_trace(std::uint_least32_t skip) { stacktrace generate_trace(std::size_t skip) {
return generate_trace(skip + 1, UINT_LEAST32_MAX); return generate_trace(skip + 1, SIZE_MAX);
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
stacktrace generate_trace(std::uint32_t skip, std::uint_least32_t max_depth) { stacktrace generate_trace(std::size_t skip, std::size_t max_depth) {
try { try {
std::vector<frame_ptr> frames = detail::capture_frames(skip + 1, max_depth); std::vector<frame_ptr> frames = detail::capture_frames(skip + 1, max_depth);
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames); std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
@ -412,7 +412,7 @@ namespace cpptrace {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
raw_trace get_raw_trace_and_absorb(std::uint_least32_t skip, std::uint_least32_t max_depth) noexcept { raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth) noexcept {
try { try {
return generate_raw_trace(skip + 1, max_depth); return generate_raw_trace(skip + 1, max_depth);
} catch(const std::exception& e) { } catch(const std::exception& e) {
@ -429,7 +429,7 @@ namespace cpptrace {
} }
} }
exception::exception(std::uint_least32_t skip, std::uint_least32_t max_depth) noexcept exception::exception(std::size_t skip, std::size_t max_depth) noexcept
: trace(detail::get_raw_trace_and_absorb(skip + 1, max_depth)) {} : trace(detail::get_raw_trace_and_absorb(skip + 1, max_depth)) {}
const char* exception::what() const noexcept { const char* exception::what() const noexcept {

View File

@ -61,7 +61,7 @@ namespace detail {
return str; return str;
} }
static const stacktrace_frame null_frame {0, 0, UINT_LEAST32_MAX, "", "", false}; static const stacktrace_frame null_frame {0, nullable<uint32_t>::null(), nullable<uint32_t>::null(), "", "", false};
} }
} }

View File

@ -63,10 +63,10 @@ namespace detail {
if(result[i].address == 0) { if(result[i].address == 0) {
result[i].address = trace[i].address; result[i].address = trace[i].address;
} }
if(result[i].line == 0) { if(!result[i].line.has_value()) {
result[i].line = trace[i].line; result[i].line = trace[i].line;
} }
if(result[i].column == UINT_LEAST32_MAX) { if(!result[i].column.has_value()) {
result[i].column = trace[i].column; result[i].column = trace[i].column;
} }
if(result[i].filename.empty()) { if(result[i].filename.empty()) {

View File

@ -346,8 +346,8 @@ namespace dbghelp {
std::fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n"); std::fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n");
return { return {
addr, addr,
static_cast<std::uint_least32_t>(line.LineNumber), static_cast<std::uint32_t>(line.LineNumber),
UINT_LEAST32_MAX, nullable<std::uint32_t>::null(),
line.FileName, line.FileName,
symbol->Name, symbol->Name,
false false
@ -378,17 +378,24 @@ namespace dbghelp {
signature = std::regex_replace(signature, comma_re, ", "); signature = std::regex_replace(signature, comma_re, ", ");
return { return {
addr, addr,
static_cast<std::uint_least32_t>(line.LineNumber), static_cast<std::int32_t>(line.LineNumber),
UINT_LEAST32_MAX, nullable<std::uint32_t>::null(),
line.FileName, line.FileName,
signature, signature,
false, false,
}; };
} else { } else {
return { addr, 0, UINT_LEAST32_MAX, "", symbol->Name, false }; return {
addr,
nullable<std::uint32_t>::null(),
nullable<std::uint32_t>::null(),
"",
symbol->Name,
false
};
} }
} else { } else {
return { addr, 0, UINT_LEAST32_MAX, "", "", false }; return { addr, nullable<std::uint32_t>::null(), nullable<std::uint32_t>::null(), "", "", false };
} }
} }

View File

@ -17,16 +17,16 @@ namespace libdl {
if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread-safe if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread-safe
return { return {
addr, addr,
0, nullable<std::uint32_t>::null(),
UINT_LEAST32_MAX, nullable<std::uint32_t>::null(),
info.dli_fname ? info.dli_fname : "", info.dli_fname ? info.dli_fname : "",
info.dli_sname ? info.dli_sname : "" info.dli_sname ? info.dli_sname : ""
}; };
} else { } else {
return { return {
addr, addr,
0, nullable<std::uint32_t>::null(),
UINT_LEAST32_MAX, nullable<std::uint32_t>::null(),
"", "",
"" ""
}; };

View File

@ -58,7 +58,7 @@ namespace libbacktrace {
stacktrace_frame resolve_frame(const frame_ptr addr) { stacktrace_frame resolve_frame(const frame_ptr addr) {
try { try {
stacktrace_frame frame; stacktrace_frame frame;
frame.column = UINT_LEAST32_MAX; frame.column = nullable<std::uint32_t>::null();
backtrace_pcinfo( backtrace_pcinfo(
get_backtrace_state(), get_backtrace_state(),
addr, addr,

View File

@ -606,7 +606,7 @@ namespace libdwarf {
// { // {
// Dwarf_Unsigned line_number = 0; // Dwarf_Unsigned line_number = 0;
// VERIFY(wrap(dwarf_lineno, line, &line_number) == DW_DLV_OK); // VERIFY(wrap(dwarf_lineno, line, &line_number) == DW_DLV_OK);
// frame.line = static_cast<std::uint_least32_t>(line_number); // frame.line = static_cast<std::uint32_t>(line_number);
// char* filename = nullptr; // char* filename = nullptr;
// VERIFY(wrap(dwarf_linesrc, line, &filename) == DW_DLV_OK); // VERIFY(wrap(dwarf_linesrc, line, &filename) == DW_DLV_OK);
// auto wrapper = raii_wrap( // auto wrapper = raii_wrap(
@ -660,7 +660,7 @@ namespace libdwarf {
if(!table_it->line_number) { if(!table_it->line_number) {
Dwarf_Unsigned line_number = 0; Dwarf_Unsigned line_number = 0;
VERIFY(wrap(dwarf_lineno, line, &line_number) == DW_DLV_OK); VERIFY(wrap(dwarf_lineno, line, &line_number) == DW_DLV_OK);
table_it->line_number = static_cast<std::uint_least32_t>(line_number); table_it->line_number = static_cast<std::uint32_t>(line_number);
} }
frame.line = table_it->line_number.unwrap(); frame.line = table_it->line_number.unwrap();
if(!table_it->path) { if(!table_it->path) {
@ -716,7 +716,7 @@ namespace libdwarf {
if(found_line) { if(found_line) {
Dwarf_Unsigned line_number = 0; Dwarf_Unsigned line_number = 0;
VERIFY(wrap(dwarf_lineno, found_line, &line_number) == DW_DLV_OK); VERIFY(wrap(dwarf_lineno, found_line, &line_number) == DW_DLV_OK);
frame.line = static_cast<std::uint_least32_t>(line_number); frame.line = static_cast<std::uint32_t>(line_number);
char* filename = nullptr; char* filename = nullptr;
VERIFY(wrap(dwarf_linesrc, found_line, &filename) == DW_DLV_OK); VERIFY(wrap(dwarf_linesrc, found_line, &filename) == DW_DLV_OK);
auto wrapper = raii_wrap( auto wrapper = raii_wrap(
@ -912,8 +912,8 @@ namespace libdwarf {
// inclusive range // inclusive range
auto begin = final_trace.end() - (1 + entry.inlines.size()); auto begin = final_trace.end() - (1 + entry.inlines.size());
auto end = final_trace.end() - 1; auto end = final_trace.end() - 1;
std::uint_least32_t carry_line = end->line; auto carry_line = end->line;
std::uint_least32_t carry_column = end->column; auto carry_column = end->column;
std::string carry_filename = std::move(end->filename); std::string carry_filename = std::move(end->filename);
for(auto it = end; it != begin; it--) { for(auto it = end; it != begin; it--) {
it->line = (it - 1)->line; it->line = (it - 1)->line;

View File

@ -131,7 +131,7 @@ void custom_print(const cpptrace::stacktrace& trace) {
std::cout std::cout
<< normalize_filename(frame.filename) << normalize_filename(frame.filename)
<< "||" << "||"
<< frame.line << frame.line.value()
<< "||" << "||"
<< frame.symbol << frame.symbol
<< std::endl; << std::endl;