Handle null lines / columns better
This commit is contained in:
parent
37a3ea7978
commit
aed456bc63
@ -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:
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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(),
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user