Add safe tracing interface

This commit is contained in:
Jeremy 2023-11-12 23:31:41 -05:00
parent 44ba826f67
commit e80afd460b
No known key found for this signature in database
GPG Key ID: BE03111EB7ED6E2E
9 changed files with 101 additions and 0 deletions

View File

@ -26,6 +26,7 @@ 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);
object_trace resolve_object_trace() const;
@ -124,6 +125,18 @@ namespace cpptrace {
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 std::size_t safe_generate_raw_trace(
frame_ptr* buffer,
std::size_t size,
std::uint_least32_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
);
// utilities:
CPPTRACE_EXPORT std::string demangle(const std::string& name);
CPPTRACE_EXPORT void absorb_trace_exceptions(bool absorb);

View File

@ -28,6 +28,11 @@
#define CYAN ESC "36m"
namespace cpptrace {
CPPTRACE_FORCE_NO_INLINE
raw_trace raw_trace::from_buffer(frame_ptr* buffer, std::size_t size) {
return raw_trace{std::vector<cpptrace::frame_ptr>(buffer, buffer + size)};
}
CPPTRACE_FORCE_NO_INLINE
raw_trace raw_trace::current(std::uint_least32_t skip) {
return generate_raw_trace(skip + 1);
@ -263,6 +268,21 @@ 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);
}
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
) {
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) {
try {

View File

@ -14,8 +14,12 @@ namespace detail {
#else
constexpr std::size_t hard_max_frames = 100;
#endif
CPPTRACE_FORCE_NO_INLINE
std::vector<frame_ptr> capture_frames(std::size_t skip, std::size_t max_depth);
CPPTRACE_FORCE_NO_INLINE
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth);
}
}

View File

@ -152,6 +152,12 @@ namespace detail {
}
return trace;
}
CPPTRACE_FORCE_NO_INLINE
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth) {
// Can't safe trace with dbghelp
return 0;
}
#if IS_MSVC
#pragma warning(pop)
#endif

View File

@ -27,6 +27,12 @@ namespace detail {
}
return frames;
}
CPPTRACE_FORCE_NO_INLINE
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth) {
// Can't safe trace with execinfo
return 0;
}
}
}

View File

@ -36,6 +36,41 @@ namespace detail {
} while(unw_step(&cursor) > 0 && frames.size() < max_depth);
return frames;
}
CPPTRACE_FORCE_NO_INLINE
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth) {
// some code duplication, but whatever
skip++;
std::vector<frame_ptr> frames;
unw_context_t context;
unw_cursor_t cursor;
// thread and signal-safe https://www.nongnu.org/libunwind/man/unw_getcontext(3).html
unw_getcontext(&context);
// thread and signal-safe https://www.nongnu.org/libunwind/man/unw_init_local(3).html
unw_init_local(&cursor, &context);
size_t i = 0;
while(i < size && i < max_depth) {
unw_word_t pc;
unw_word_t sp;
// thread and signal-safe https://www.nongnu.org/libunwind/man/unw_get_reg(3).html
unw_get_reg(&cursor, UNW_REG_IP, &pc);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
if(skip) {
skip--;
} else {
// pc is the instruction after the `call`, adjust back to the previous instruction
// just a cast, signal safe
buffer[i] = to_frame_ptr(pc) - 1;
i++;
}
// thread and signal-safe as long as the cursor is in the local address space, which it is
// https://www.nongnu.org/libunwind/man/unw_step(3).html
if(unw_step(&cursor) <= 0) {
break;
}
}
return i;
}
}
}

View File

@ -10,6 +10,11 @@ namespace detail {
std::vector<frame_ptr> capture_frames(std::size_t, std::size_t) {
return {};
}
CPPTRACE_FORCE_NO_INLINE
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth) {
return 0;
}
}
}

View File

@ -62,6 +62,12 @@ namespace detail {
frames.shrink_to_fit();
return frames;
}
CPPTRACE_FORCE_NO_INLINE
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth) {
// Can't safe trace with _Unwind
return 0;
}
}
}

View File

@ -37,6 +37,12 @@ namespace detail {
}
return frames;
}
CPPTRACE_FORCE_NO_INLINE
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth) {
// Can't safe trace with winapi
return 0;
}
}
}