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 <exception>
#include <limits>
#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "cpptrace/cpptrace_export.hpp"
@ -27,8 +30,8 @@ namespace cpptrace {
struct CPPTRACE_EXPORT raw_trace {
std::vector<frame_ptr> frames;
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::uint_least32_t skip, std::uint_least32_t max_depth);
static raw_trace current(std::size_t skip = 0);
static raw_trace current(std::size_t skip, std::size_t max_depth);
object_trace resolve_object_trace() const;
stacktrace resolve() const;
void clear();
@ -53,8 +56,8 @@ namespace cpptrace {
struct CPPTRACE_EXPORT object_trace {
std::vector<object_frame> frames;
static object_trace current(std::uint_least32_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 = 0);
static object_trace current(std::size_t skip, std::size_t max_depth);
stacktrace resolve() const;
void clear();
bool empty() const noexcept;
@ -69,10 +72,50 @@ namespace cpptrace {
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 {
frame_ptr address;
std::uint_least32_t line; // TODO: This should use UINT_LEAST32_MAX as a sentinel
std::uint_least32_t column; // UINT_LEAST32_MAX if not present
nullable<std::uint32_t> line;
nullable<std::uint32_t> column;
std::string filename;
std::string symbol;
bool is_inline;
@ -95,8 +138,8 @@ namespace cpptrace {
struct CPPTRACE_EXPORT stacktrace {
std::vector<stacktrace_frame> frames;
static stacktrace current(std::uint_least32_t skip = 0);
static stacktrace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
static stacktrace current(std::size_t skip = 0);
static stacktrace current(std::size_t skip, std::size_t max_depth);
void print() const;
void print(std::ostream& stream) const;
void print(std::ostream& stream, bool color) const;
@ -118,23 +161,23 @@ namespace cpptrace {
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::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_EXPORT object_trace generate_object_trace(std::uint_least32_t skip = 0);
CPPTRACE_EXPORT object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_EXPORT stacktrace generate_trace(std::uint_least32_t skip = 0);
CPPTRACE_EXPORT stacktrace generate_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip = 0);
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth);
CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip = 0);
CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip, std::size_t max_depth);
CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip = 0);
CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip, std::size_t max_depth);
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
frame_ptr* buffer,
std::size_t size,
std::uint_least32_t skip = 0
std::size_t skip = 0
);
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
frame_ptr* buffer,
std::size_t size,
std::uint_least32_t skip,
std::uint_least32_t max_depth
std::size_t skip,
std::size_t max_depth
);
// utilities:
@ -172,8 +215,8 @@ namespace cpptrace {
mutable std::string what_string;
protected:
explicit exception(std::uint_least32_t skip, std::uint_least32_t max_depth) noexcept;
explicit exception(std::uint_least32_t skip) noexcept : exception(skip + 1, UINT_LEAST32_MAX) {}
explicit exception(std::size_t skip, std::size_t max_depth) noexcept;
explicit exception(std::size_t skip) noexcept : exception(skip + 1, SIZE_MAX) {}
public:
explicit exception() noexcept : exception(1) {}
@ -200,8 +243,8 @@ namespace cpptrace {
explicit exception_with_message(
std::string&& message_arg,
std::uint_least32_t skip,
std::uint_least32_t max_depth
std::size_t skip,
std::size_t max_depth
) noexcept : exception(skip + 1, max_depth), message(std::move(message_arg)) {}
public:

View File

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

View File

@ -61,7 +61,7 @@ namespace detail {
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) {
result[i].address = trace[i].address;
}
if(result[i].line == 0) {
if(!result[i].line.has_value()) {
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;
}
if(result[i].filename.empty()) {

View File

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

View File

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

View File

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

View File

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