Error handling improvements (#46)
This commit is contained in:
parent
1c3e0e92f4
commit
183cdf5a1c
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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, "", ""};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include <string>
|
||||
|
||||
void trace() {
|
||||
cpptrace::absorb_trace_exceptions(false);
|
||||
cpptrace::generate_trace().print();
|
||||
}
|
||||
|
||||
|
||||
@ -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||
|
||||
@ -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||
|
||||
@ -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||
|
||||
@ -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||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -121,6 +121,7 @@ void function_one(int) {
|
||||
|
||||
int main() {
|
||||
x = 0;
|
||||
cpptrace::absorb_trace_exceptions(false);
|
||||
function_one(0);
|
||||
x = 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user