Error handling improvements (#46)

This commit is contained in:
Jeremy Rifkin 2023-09-23 17:46:26 -04:00 committed by GitHub
parent 1c3e0e92f4
commit 183cdf5a1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 174 additions and 94 deletions

View File

@ -218,9 +218,11 @@ namespace cpptrace {
### Utilities
`cpptrace::demangle` provides a helper function for name demangling, since it has to implement that helper internally
anyways. It also provides a function to control whether any exceptions that are throw internally are caught and ignored
before returning to user code. The library makes an attempt to fail silently during trace generation if any errors are
encountered.
anyways.
The library makes an attempt to fail silently and continue during trace generation if any errors are encountered.
`cpptrace::absorb_trace_exceptions` can be used to configure whether these exceptions are absorbed silently internally
or wether they're rethrown to the caller.
```cpp
namespace cpptrace {

View File

@ -42,16 +42,30 @@ namespace cpptrace {
CPPTRACE_API
object_trace raw_trace::resolve_object_trace() const {
return object_trace(detail::get_frames_object_info(frames));
try {
return object_trace(detail::get_frames_object_info(frames));
} catch(...) {
if(!detail::should_absorb_trace_exceptions()) {
throw;
}
return object_trace({});
}
}
CPPTRACE_API
stacktrace raw_trace::resolve() const {
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
for(auto& frame : trace) {
frame.symbol = detail::demangle(frame.symbol);
try {
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
for(auto& frame : trace) {
frame.symbol = detail::demangle(frame.symbol);
}
return stacktrace(std::move(trace));
} catch(...) {
if(!detail::should_absorb_trace_exceptions()) {
throw;
}
return stacktrace();
}
return stacktrace(std::move(trace));
}
CPPTRACE_API
@ -76,7 +90,14 @@ namespace cpptrace {
CPPTRACE_API
stacktrace object_trace::resolve() const {
return stacktrace(detail::resolve_frames(frames));
try {
return stacktrace(detail::resolve_frames(frames));
} catch(...) {
if(!detail::should_absorb_trace_exceptions()) {
throw;
}
return stacktrace();
}
}
CPPTRACE_API
@ -222,22 +243,50 @@ namespace cpptrace {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
raw_trace generate_raw_trace(std::uint_least32_t skip) {
return raw_trace(detail::capture_frames(skip + 1, UINT_LEAST32_MAX));
try {
return raw_trace(detail::capture_frames(skip + 1, UINT_LEAST32_MAX));
} catch(...) {
if(!detail::should_absorb_trace_exceptions()) {
throw;
}
return raw_trace({});
}
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) {
return raw_trace(detail::capture_frames(skip + 1, max_depth));
try {
return raw_trace(detail::capture_frames(skip + 1, max_depth));
} catch(...) {
if(!detail::should_absorb_trace_exceptions()) {
throw;
}
return raw_trace({});
}
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
object_trace generate_object_trace(std::uint_least32_t skip) {
return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1, UINT_LEAST32_MAX)));
try {
return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1, UINT_LEAST32_MAX)));
} catch(...) {
if(!detail::should_absorb_trace_exceptions()) {
throw;
}
return object_trace({});
}
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) {
return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth)));
try {
return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth)));
} catch(...) {
if(!detail::should_absorb_trace_exceptions()) {
throw;
}
return object_trace({});
}
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
@ -247,12 +296,19 @@ namespace cpptrace {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
stacktrace generate_trace(std::uint32_t skip, std::uint_least32_t max_depth) {
std::vector<uintptr_t> frames = detail::capture_frames(skip + 1, max_depth);
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
for(auto& frame : trace) {
frame.symbol = detail::demangle(frame.symbol);
try {
std::vector<uintptr_t> frames = detail::capture_frames(skip + 1, max_depth);
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
for(auto& frame : trace) {
frame.symbol = detail::demangle(frame.symbol);
}
return stacktrace(std::move(trace));
} catch(...) {
if(!detail::should_absorb_trace_exceptions()) {
throw;
}
return stacktrace();
}
return stacktrace(std::move(trace));
}
CPPTRACE_API

View File

@ -60,6 +60,8 @@ namespace detail {
snprintf(&str[0], length + 1, args...);
return str;
}
static const stacktrace_frame null_frame {0, 0, UINT_LEAST32_MAX, "", ""};
}
}

View File

@ -126,7 +126,7 @@ namespace detail {
return byte_swapper<T, sizeof(T)>{}(value);
}
inline void enable_virtual_terminal_processing_if_needed() {
inline void enable_virtual_terminal_processing_if_needed() noexcept {
// enable colors / ansi processing if necessary
#if IS_WINDOWS
// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
@ -142,7 +142,7 @@ namespace detail {
#endif
}
inline constexpr unsigned n_digits(unsigned value) {
inline constexpr unsigned n_digits(unsigned value) noexcept {
return value < 10 ? 1 : 1 + n_digits(value / 10);
}
static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result");

View File

@ -267,7 +267,7 @@ namespace addr2line {
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
// TODO: Refactor better
std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, UINT_LEAST32_MAX, "", "" });
std::vector<stacktrace_frame> trace(frames.size(), null_frame);
for(size_t i = 0; i < frames.size(); i++) {
trace[i].address = frames[i].raw_address;
// Set what is known for now, and resolutions from addr2line should overwrite
@ -277,28 +277,34 @@ namespace addr2line {
if(has_addr2line()) {
const auto entries = collate_frames(frames, trace);
for(const auto& entry : entries) {
const auto& object_name = entry.first;
const auto& entries_vec = entry.second;
// You may ask why it'd ever happen that there could be an empty entries_vec array, if there're
// no addresses why would get_addr2line_targets do anything? The reason is because if things in
// get_addr2line_targets fail it will silently skip. This is partly an optimization but also an
// assertion below will fail if addr2line is given an empty input.
if(entries_vec.empty()) {
continue;
}
std::string address_input;
for(const auto& pair : entries_vec) {
address_input += to_hex(pair.first.get().obj_address);
#if !IS_WINDOWS
address_input += '\n';
#else
address_input += ' ';
#endif
}
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
VERIFY(output.size() == entries_vec.size());
for(size_t i = 0; i < output.size(); i++) {
update_trace(output[i], i, entries_vec);
try {
const auto& object_name = entry.first;
const auto& entries_vec = entry.second;
// You may ask why it'd ever happen that there could be an empty entries_vec array, if there're
// no addresses why would get_addr2line_targets do anything? The reason is because if things in
// get_addr2line_targets fail it will silently skip. This is partly an optimization but also an
// assertion below will fail if addr2line is given an empty input.
if(entries_vec.empty()) {
continue;
}
std::string address_input;
for(const auto& pair : entries_vec) {
address_input += to_hex(pair.first.get().obj_address);
#if !IS_WINDOWS
address_input += '\n';
#else
address_input += ' ';
#endif
}
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
VERIFY(output.size() == entries_vec.size());
for(size_t i = 0; i < output.size(); i++) {
update_trace(output[i], i, entries_vec);
}
} catch(...) {
if(!should_absorb_trace_exceptions()) {
throw;
}
}
}
}

View File

@ -382,22 +382,10 @@ namespace dbghelp {
signature
};
} else {
return {
addr,
0,
UINT_LEAST32_MAX,
"",
symbol->Name
};
return { addr, 0, UINT_LEAST32_MAX, "", symbol->Name };
}
} else {
return {
addr,
0,
UINT_LEAST32_MAX,
"",
""
};
return { addr, 0, UINT_LEAST32_MAX, "", "" };
}
}
@ -414,7 +402,14 @@ namespace dbghelp {
throw std::logic_error("SymInitialize failed");
}
for(const auto frame : frames) {
trace.push_back(resolve_frame(proc, frame));
try {
trace.push_back(resolve_frame(proc, frame));
} catch(...) {
if(!detail::should_absorb_trace_exceptions()) {
throw;
}
trace.push_back(null_frame);
}
}
if(!SymCleanup(proc)) {
//throw std::logic_error("SymCleanup failed");

View File

@ -8,6 +8,7 @@
#include <cstdio>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <vector>
#ifdef CPPTRACE_BACKTRACE_PATH
@ -21,9 +22,6 @@ namespace detail {
namespace libbacktrace {
int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) {
stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data);
if(line == 0) {
///fprintf(stderr, "Getting bad data for some reason\n"); // TODO: Eliminate
}
frame.address = address;
frame.line = line;
frame.filename = file ? file : "";
@ -40,7 +38,7 @@ namespace libbacktrace {
}
void error_callback(void*, const char* msg, int errnum) {
fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum);
throw std::runtime_error(stringf("Libbacktrace error: %s, code %d\n", msg, errnum));
}
backtrace_state* get_backtrace_state() {
@ -58,26 +56,33 @@ namespace libbacktrace {
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
stacktrace_frame resolve_frame(const uintptr_t addr) {
stacktrace_frame frame;
frame.column = UINT_LEAST32_MAX;
backtrace_pcinfo(
get_backtrace_state(),
addr,
full_callback,
error_callback,
&frame
);
if(frame.symbol.empty()) {
// fallback, try to at least recover the symbol name with backtrace_syminfo
backtrace_syminfo(
try {
stacktrace_frame frame;
frame.column = UINT_LEAST32_MAX;
backtrace_pcinfo(
get_backtrace_state(),
addr,
syminfo_callback,
full_callback,
error_callback,
&frame
);
if(frame.symbol.empty()) {
// fallback, try to at least recover the symbol name with backtrace_syminfo
backtrace_syminfo(
get_backtrace_state(),
addr,
syminfo_callback,
error_callback,
&frame
);
}
return frame;
} catch(...) {
if(!should_absorb_trace_exceptions()) {
throw;
}
return null_frame;
}
return frame;
}
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) {

View File

@ -1089,7 +1089,7 @@ namespace libdwarf {
CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
stacktrace_frame resolve_frame(const object_frame& frame_info) {
stacktrace_frame frame { 0, 0, UINT_LEAST32_MAX, "", "" };
stacktrace_frame frame = null_frame;
frame.filename = frame_info.obj_path;
frame.symbol = frame_info.symbol;
frame.address = frame_info.raw_address;
@ -1112,19 +1112,31 @@ namespace libdwarf {
CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, UINT_LEAST32_MAX, "", "" });
std::vector<stacktrace_frame> trace(frames.size(), null_frame);
static std::mutex mutex;
// Locking around all libdwarf interaction per https://github.com/davea42/libdwarf-code/discussions/184
const std::lock_guard<std::mutex> lock(mutex);
for(const auto& obj_entry : collate_frames(frames, trace)) {
const auto& obj_name = obj_entry.first;
dwarf_resolver resolver(obj_name);
// If there's no debug information it'll mark itself as not ok
if(resolver.ok) {
for(const auto& entry : obj_entry.second) {
const auto& dlframe = entry.first.get();
auto& frame = entry.second.get();
frame = resolver.resolve_frame(dlframe);
try {
const auto& obj_name = obj_entry.first;
dwarf_resolver resolver(obj_name);
// If there's no debug information it'll mark itself as not ok
if(resolver.ok) {
for(const auto& entry : obj_entry.second) {
try {
const auto& dlframe = entry.first.get();
auto& frame = entry.second.get();
frame = resolver.resolve_frame(dlframe);
} catch(...) {
if(!should_absorb_trace_exceptions()) {
throw;
}
}
}
}
} catch(...) {
if(!should_absorb_trace_exceptions()) {
throw;
}
}
}

View File

@ -9,11 +9,11 @@ namespace cpptrace {
namespace detail {
namespace nothing {
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) {
return std::vector<stacktrace_frame>(frames.size(), stacktrace_frame { 0, 0, UINT_LEAST32_MAX, "", "" });
return std::vector<stacktrace_frame>(frames.size(), null_frame);
}
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
return std::vector<stacktrace_frame>(frames.size(), stacktrace_frame { 0, 0, UINT_LEAST32_MAX, "", "" });
return std::vector<stacktrace_frame>(frames.size(), null_frame);
}
}
}

View File

@ -6,6 +6,7 @@
#include <string>
void trace() {
cpptrace::absorb_trace_exceptions(false);
cpptrace::generate_trace().print();
}

View File

@ -32,7 +32,7 @@ test/test.cpp||106||void foo<int, int, int, int, int, int, int, int>(int, int, i
test/test.cpp||106||void foo<int, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int)
test/test.cpp||112||function_two(int, float)
test/test.cpp||118||function_one(int)
test/test.cpp||124||main
test/test.cpp||125||main
./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main
./csu/../csu/libc-start.c||392||__libc_start_main_impl
./test||0||

View File

@ -32,7 +32,7 @@ test/test.cpp||106||void foo<int, int, int, int, int, int, int, int>(int, int, i
test/test.cpp||106||void foo<int, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int)
test/test.cpp||112||function_two(int, float)
test/test.cpp||118||function_one(int)
test/test.cpp||124||main
test/test.cpp||125||main
./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main
./csu/../csu/libc-start.c||392||__libc_start_main_impl
./test||0||

View File

@ -32,7 +32,7 @@ test/test.cpp||106||void foo<int, int, int, int, int, int, int, int>(int, int, i
test/test.cpp||106||void foo<int, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int)
test/test.cpp||112||function_two(int, float)
test/test.cpp||118||function_one(int)
test/test.cpp||124||main
test/test.cpp||125||main
./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main
./csu/../csu/libc-start.c||392||__libc_start_main_impl
./test||0||

View File

@ -32,7 +32,7 @@ test/test.cpp||106||void foo<int, int, int, int, int, int, int, int>(int, int, i
test/test.cpp||106||void foo<int, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int)
test/test.cpp||112||function_two(int, float)
test/test.cpp||118||function_one(int)
test/test.cpp||124||main
test/test.cpp||125||main
./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main
./csu/../csu/libc-start.c||392||__libc_start_main_impl
./test||0||

View File

@ -32,7 +32,7 @@ test/test.cpp||106||void foo<int, int, int, int, int, int, int, int>(int, int, i
test/test.cpp||106||void foo<int, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int)
test/test.cpp||112||function_two(int, float)
test/test.cpp||118||function_one(int)
test/test.cpp||124||main
test/test.cpp||125||main
C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c||272||__tmainCRTStartup
C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c||193||mainCRTStartup
||0||BaseThreadInitThunk

View File

@ -32,7 +32,7 @@ test\test.cpp||106||foo<int, int, int, int, int, int, int, int>(int, int, int, i
test\test.cpp||106||foo<int, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int)
test\test.cpp||112||function_two(int, float)
test\test.cpp||118||function_one(int)
test\test.cpp||124||main()
test\test.cpp||125||main()
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||79||invoke_main()
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||288||__scrt_common_main_seh()
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||331||__scrt_common_main()

View File

@ -121,6 +121,7 @@ void function_one(int) {
int main() {
x = 0;
cpptrace::absorb_trace_exceptions(false);
function_one(0);
x = 0;
}