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 ### Utilities
`cpptrace::demangle` provides a helper function for name demangling, since it has to implement that helper internally `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 anyways.
before returning to user code. The library makes an attempt to fail silently during trace generation if any errors are
encountered. 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 ```cpp
namespace cpptrace { namespace cpptrace {

View File

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

View File

@ -60,6 +60,8 @@ namespace detail {
snprintf(&str[0], length + 1, args...); snprintf(&str[0], length + 1, args...);
return str; 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); 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 // enable colors / ansi processing if necessary
#if IS_WINDOWS #if IS_WINDOWS
// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing // 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 #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); return value < 10 ? 1 : 1 + n_digits(value / 10);
} }
static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result"); 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) { std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
// TODO: Refactor better // 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++) { for(size_t i = 0; i < frames.size(); i++) {
trace[i].address = frames[i].raw_address; trace[i].address = frames[i].raw_address;
// Set what is known for now, and resolutions from addr2line should overwrite // Set what is known for now, and resolutions from addr2line should overwrite
@ -277,6 +277,7 @@ namespace addr2line {
if(has_addr2line()) { if(has_addr2line()) {
const auto entries = collate_frames(frames, trace); const auto entries = collate_frames(frames, trace);
for(const auto& entry : entries) { for(const auto& entry : entries) {
try {
const auto& object_name = entry.first; const auto& object_name = entry.first;
const auto& entries_vec = entry.second; 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 // You may ask why it'd ever happen that there could be an empty entries_vec array, if there're
@ -300,6 +301,11 @@ namespace addr2line {
for(size_t i = 0; i < output.size(); i++) { for(size_t i = 0; i < output.size(); i++) {
update_trace(output[i], i, entries_vec); update_trace(output[i], i, entries_vec);
} }
} catch(...) {
if(!should_absorb_trace_exceptions()) {
throw;
}
}
} }
} }
return trace; return trace;

View File

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

View File

@ -8,6 +8,7 @@
#include <cstdio> #include <cstdio>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <stdexcept>
#include <vector> #include <vector>
#ifdef CPPTRACE_BACKTRACE_PATH #ifdef CPPTRACE_BACKTRACE_PATH
@ -21,9 +22,6 @@ namespace detail {
namespace libbacktrace { namespace libbacktrace {
int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) { int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) {
stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data); 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.address = address;
frame.line = line; frame.line = line;
frame.filename = file ? file : ""; frame.filename = file ? file : "";
@ -40,7 +38,7 @@ namespace libbacktrace {
} }
void error_callback(void*, const char* msg, int errnum) { 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() { backtrace_state* get_backtrace_state() {
@ -58,6 +56,7 @@ namespace libbacktrace {
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
stacktrace_frame resolve_frame(const uintptr_t addr) { stacktrace_frame resolve_frame(const uintptr_t addr) {
try {
stacktrace_frame frame; stacktrace_frame frame;
frame.column = UINT_LEAST32_MAX; frame.column = UINT_LEAST32_MAX;
backtrace_pcinfo( backtrace_pcinfo(
@ -78,6 +77,12 @@ namespace libbacktrace {
); );
} }
return frame; return frame;
} catch(...) {
if(!should_absorb_trace_exceptions()) {
throw;
}
return null_frame;
}
} }
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) { 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 CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
stacktrace_frame resolve_frame(const object_frame& frame_info) { 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.filename = frame_info.obj_path;
frame.symbol = frame_info.symbol; frame.symbol = frame_info.symbol;
frame.address = frame_info.raw_address; frame.address = frame_info.raw_address;
@ -1112,19 +1112,31 @@ namespace libdwarf {
CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) { 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; static std::mutex mutex;
// Locking around all libdwarf interaction per https://github.com/davea42/libdwarf-code/discussions/184 // Locking around all libdwarf interaction per https://github.com/davea42/libdwarf-code/discussions/184
const std::lock_guard<std::mutex> lock(mutex); const std::lock_guard<std::mutex> lock(mutex);
for(const auto& obj_entry : collate_frames(frames, trace)) { for(const auto& obj_entry : collate_frames(frames, trace)) {
try {
const auto& obj_name = obj_entry.first; const auto& obj_name = obj_entry.first;
dwarf_resolver resolver(obj_name); dwarf_resolver resolver(obj_name);
// If there's no debug information it'll mark itself as not ok // If there's no debug information it'll mark itself as not ok
if(resolver.ok) { if(resolver.ok) {
for(const auto& entry : obj_entry.second) { for(const auto& entry : obj_entry.second) {
try {
const auto& dlframe = entry.first.get(); const auto& dlframe = entry.first.get();
auto& frame = entry.second.get(); auto& frame = entry.second.get();
frame = resolver.resolve_frame(dlframe); 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 detail {
namespace nothing { namespace nothing {
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) { 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) { 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> #include <string>
void trace() { void trace() {
cpptrace::absorb_trace_exceptions(false);
cpptrace::generate_trace().print(); 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||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||112||function_two(int, float)
test/test.cpp||118||function_one(int) 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/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main
./csu/../csu/libc-start.c||392||__libc_start_main_impl ./csu/../csu/libc-start.c||392||__libc_start_main_impl
./test||0|| ./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||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||112||function_two(int, float)
test/test.cpp||118||function_one(int) 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/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main
./csu/../csu/libc-start.c||392||__libc_start_main_impl ./csu/../csu/libc-start.c||392||__libc_start_main_impl
./test||0|| ./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||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||112||function_two(int, float)
test/test.cpp||118||function_one(int) 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/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main
./csu/../csu/libc-start.c||392||__libc_start_main_impl ./csu/../csu/libc-start.c||392||__libc_start_main_impl
./test||0|| ./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||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||112||function_two(int, float)
test/test.cpp||118||function_one(int) 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/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main
./csu/../csu/libc-start.c||392||__libc_start_main_impl ./csu/../csu/libc-start.c||392||__libc_start_main_impl
./test||0|| ./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||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||112||function_two(int, float)
test/test.cpp||118||function_one(int) 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||272||__tmainCRTStartup
C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c||193||mainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c||193||mainCRTStartup
||0||BaseThreadInitThunk ||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||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||112||function_two(int, float)
test\test.cpp||118||function_one(int) 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||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||288||__scrt_common_main_seh()
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||331||__scrt_common_main() 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() { int main() {
x = 0; x = 0;
cpptrace::absorb_trace_exceptions(false);
function_one(0); function_one(0);
x = 0; x = 0;
} }