diff --git a/README.md b/README.md index 5546637..a7ba858 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,8 @@ namespace cpptrace { std::vector frames; explicit stacktrace(); explicit stacktrace(std::vector&& frames); - static stacktrace current(std::uint32_t skip = 0); // here as a drop-in for std::stacktrace + static stacktrace current(std::uint_least32_t skip = 0); // here as a drop-in for std::stacktrace + static stacktrace current(std::uint_least32_t skip, std::uint_least32_t max_depth); void print() const; void print(std::ostream& stream) const; void print(std::ostream& stream, bool color) const; @@ -152,7 +153,8 @@ namespace cpptrace { /* operator<<(ostream, ..), std::format support, and iterators exist for this object */ }; - stacktrace generate_trace(std::uint32_t skip = 0); + stacktrace generate_trace(std::uint_least32_t skip = 0); + stacktrace generate_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); } ``` @@ -174,14 +176,16 @@ namespace cpptrace { struct object_trace { std::vector frames; explicit object_trace(std::vector&& frames); - static object_trace current(std::uint32_t skip = 0); + 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); stacktrace resolve() const; void clear(); bool empty() const noexcept; /* iterators exist for this object */ }; - object_trace generate_object_trace(std::uint32_t skip = 0); + object_trace generate_object_trace(std::uint_least32_t skip = 0); + object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); } ``` @@ -197,7 +201,8 @@ namespace cpptrace { struct raw_trace { std::vector frames; explicit raw_trace(std::vector&& frames); - static raw_trace current(std::uint32_t skip = 0); + 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; stacktrace resolve() const; void clear(); @@ -205,7 +210,8 @@ namespace cpptrace { /* iterators exist for this object */ }; - raw_trace generate_raw_trace(std::uint32_t skip = 0); + raw_trace generate_raw_trace(std::uint_least32_t skip = 0); + raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); } ``` @@ -236,7 +242,8 @@ namespace cpptrace { mutable raw_trace trace; mutable stacktrace resolved_trace; mutable std::string resolved_what; - explicit exception(uint32_t skip) noexcept; + explicit exception(std::uint_least32_t skip) noexcept; + explicit exception(std::uint_least32_t skip, std::uint_least32_t max_depth) noexcept; const stacktrace& get_resolved_trace() const noexcept; virtual const std::string& get_resolved_what() const noexcept; public: @@ -249,7 +256,8 @@ namespace cpptrace { class exception_with_message : public exception { mutable std::string message; - explicit exception_with_message(std::string&& message_arg, uint32_t skip) noexcept; + explicit exception_with_message(std::string&& message_arg, std::uint_least32_t skip) noexcept; + explicit exception_with_message(std::string&& message_arg, std::uint_least32_t skip, std::uint_least32_t max_depth) noexcept; const std::string& get_resolved_what() const noexcept override; public: explicit exception_with_message(std::string&& message_arg); diff --git a/include/cpptrace/cpptrace.hpp b/include/cpptrace/cpptrace.hpp index 5eec394..4b3db7f 100644 --- a/include/cpptrace/cpptrace.hpp +++ b/include/cpptrace/cpptrace.hpp @@ -30,7 +30,8 @@ namespace cpptrace { struct raw_trace { std::vector frames; explicit raw_trace(std::vector&& frames_) : frames(frames_) {} - CPPTRACE_API static raw_trace current(std::uint32_t skip = 0); + CPPTRACE_API static raw_trace current(std::uint_least32_t skip = 0); + CPPTRACE_API static raw_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth); CPPTRACE_API object_trace resolve_object_trace() const; CPPTRACE_API stacktrace resolve() const; CPPTRACE_API void clear(); @@ -54,7 +55,8 @@ namespace cpptrace { struct object_trace { std::vector frames; explicit object_trace(std::vector&& frames_) : frames(frames_) {} - CPPTRACE_API static object_trace current(std::uint32_t skip = 0); + CPPTRACE_API static object_trace current(std::uint_least32_t skip = 0); + CPPTRACE_API static object_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth); CPPTRACE_API stacktrace resolve() const; CPPTRACE_API void clear(); CPPTRACE_API bool empty() const noexcept; @@ -91,7 +93,8 @@ namespace cpptrace { std::vector frames; explicit stacktrace() {} explicit stacktrace(std::vector&& frames_) : frames(frames_) {} - CPPTRACE_API static stacktrace current(std::uint32_t skip = 0); + CPPTRACE_API static stacktrace current(std::uint_least32_t skip = 0); + CPPTRACE_API static stacktrace current(std::uint_least32_t skip, std::uint_least32_t max_depth); CPPTRACE_API void print() const; CPPTRACE_API void print(std::ostream& stream) const; CPPTRACE_API void print(std::ostream& stream, bool color) const; @@ -110,9 +113,12 @@ namespace cpptrace { CPPTRACE_API void print(std::ostream& stream, bool color, bool newline_at_end) const; }; - CPPTRACE_API raw_trace generate_raw_trace(std::uint32_t skip = 0); - CPPTRACE_API object_trace generate_object_trace(std::uint32_t skip = 0); - CPPTRACE_API stacktrace generate_trace(std::uint32_t skip = 0); + CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip = 0); + CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); + CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip = 0); + CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); + CPPTRACE_API stacktrace generate_trace(std::uint_least32_t skip = 0); + CPPTRACE_API stacktrace generate_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); // utilities: CPPTRACE_API std::string demangle(const std::string& name); @@ -127,9 +133,10 @@ namespace cpptrace { mutable raw_trace trace; mutable stacktrace resolved_trace; mutable std::string resolved_what; - explicit exception(uint32_t skip) noexcept : trace([skip] () noexcept { + explicit exception(std::uint_least32_t skip, std::uint_least32_t max_depth) noexcept + : trace([skip, max_depth] () noexcept { try { - return generate_raw_trace(skip + 2); + return generate_raw_trace(skip + 2, max_depth); } catch(const std::exception& e) { if(!detail::should_absorb_trace_exceptions()) { fprintf( @@ -141,6 +148,7 @@ namespace cpptrace { return raw_trace({}); } } ()) {} + explicit exception(std::uint_least32_t skip) noexcept : exception(skip + 1, UINT_LEAST32_MAX) {} const stacktrace& get_resolved_trace() const noexcept { // I think a non-empty raw trace can never resolve as empty, so this will accurately prevent resolving more // than once. Either way the raw trace is cleared. @@ -186,8 +194,15 @@ namespace cpptrace { class exception_with_message : public exception { protected: mutable std::string message; - explicit exception_with_message(std::string&& message_arg, uint32_t skip) noexcept - : exception(skip + 1), message(std::move(message_arg)) {} + explicit exception_with_message( + std::string&& message_arg, + uint32_t skip + ) noexcept : exception(skip + 1), message(std::move(message_arg)) {} + explicit exception_with_message( + std::string&& message_arg, + uint_least32_t skip, + uint_least32_t max_depth + ) noexcept : exception(skip + 1, max_depth), message(std::move(message_arg)) {} const std::string& get_resolved_what() const noexcept override { if(resolved_what.empty()) { resolved_what = message + "\n" + get_resolved_trace().to_string(); diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 3cd642b..756b9bb 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -31,10 +31,15 @@ namespace cpptrace { } CPPTRACE_FORCE_NO_INLINE CPPTRACE_API - raw_trace raw_trace::current(std::uint32_t skip) { + raw_trace raw_trace::current(std::uint_least32_t skip) { return generate_raw_trace(skip + 1); } + CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + raw_trace raw_trace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) { + return generate_raw_trace(skip + 1, max_depth); + } + CPPTRACE_API object_trace raw_trace::resolve_object_trace() const { return object_trace(detail::get_frames_object_info(frames)); @@ -60,10 +65,15 @@ namespace cpptrace { } CPPTRACE_FORCE_NO_INLINE CPPTRACE_API - object_trace object_trace::current(std::uint32_t skip) { + object_trace object_trace::current(std::uint_least32_t skip) { return generate_object_trace(skip + 1); } + CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + object_trace object_trace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) { + return generate_object_trace(skip + 1, max_depth); + } + CPPTRACE_API stacktrace object_trace::resolve() const { return stacktrace(detail::resolve_frames(frames)); @@ -114,6 +124,11 @@ namespace cpptrace { return generate_trace(skip + 1); } + CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + stacktrace stacktrace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) { + return generate_trace(skip + 1, max_depth); + } + CPPTRACE_API void stacktrace::print() const { print(std::cerr, true); @@ -206,18 +221,33 @@ namespace cpptrace { } CPPTRACE_FORCE_NO_INLINE CPPTRACE_API - raw_trace generate_raw_trace(std::uint32_t skip) { - return raw_trace(detail::capture_frames(skip + 1)); + raw_trace generate_raw_trace(std::uint_least32_t skip) { + return raw_trace(detail::capture_frames(skip + 1, UINT_LEAST32_MAX)); } CPPTRACE_FORCE_NO_INLINE CPPTRACE_API - object_trace generate_object_trace(std::uint32_t skip) { - return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1))); + 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)); } CPPTRACE_FORCE_NO_INLINE CPPTRACE_API - stacktrace generate_trace(std::uint32_t skip) { - std::vector frames = detail::capture_frames(skip + 1); + 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))); + } + + 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))); + } + + CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + stacktrace generate_trace(std::uint_least32_t skip) { + return generate_trace(skip + 1, UINT_LEAST32_MAX); + } + + CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + stacktrace generate_trace(std::uint32_t skip, std::uint_least32_t max_depth) { + std::vector frames = detail::capture_frames(skip + 1, max_depth); std::vector trace = detail::resolve_frames(frames); for(auto& frame : trace) { frame.symbol = detail::demangle(frame.symbol); diff --git a/src/unwind/unwind.hpp b/src/unwind/unwind.hpp index 4eab8ea..9eeb188 100644 --- a/src/unwind/unwind.hpp +++ b/src/unwind/unwind.hpp @@ -15,7 +15,7 @@ namespace detail { constexpr size_t hard_max_frames = 100; #endif CPPTRACE_FORCE_NO_INLINE - std::vector capture_frames(size_t skip); + std::vector capture_frames(size_t skip, size_t max_depth); } } diff --git a/src/unwind/unwind_with_execinfo.cpp b/src/unwind/unwind_with_execinfo.cpp index 3a3850b..836a712 100644 --- a/src/unwind/unwind_with_execinfo.cpp +++ b/src/unwind/unwind_with_execinfo.cpp @@ -13,15 +13,13 @@ namespace cpptrace { namespace detail { CPPTRACE_FORCE_NO_INLINE - std::vector capture_frames(size_t skip) { - std::vector addrs(hard_max_frames + skip, nullptr); - const int n_frames = backtrace(addrs.data(), int(hard_max_frames + skip)); // thread safe - addrs.resize(n_frames); - addrs.erase(addrs.begin(), addrs.begin() + ptrdiff_t(std::min(skip + 1, addrs.size()))); - addrs.shrink_to_fit(); - std::vector frames(addrs.size(), 0); - for(std::size_t i = 0; i < addrs.size(); i++) { - frames[i] = reinterpret_cast(addrs[i]); + std::vector capture_frames(size_t skip, size_t max_depth) { + skip += 2; // TODO: Not sure where the other 1 comes from + std::vector addrs(std::min(hard_max_frames, skip + max_depth), nullptr); + const int n_frames = backtrace(addrs.data(), static_cast(addrs.size())); // thread safe + std::vector frames(n_frames - skip, 0); + for(int i = skip; i < n_frames; i++) { + frames[i - skip] = reinterpret_cast(addrs[i]); } return frames; } diff --git a/src/unwind/unwind_with_nothing.cpp b/src/unwind/unwind_with_nothing.cpp index dfaf7fb..f1dbb5c 100644 --- a/src/unwind/unwind_with_nothing.cpp +++ b/src/unwind/unwind_with_nothing.cpp @@ -7,7 +7,7 @@ namespace cpptrace { namespace detail { - std::vector capture_frames(size_t) { + std::vector capture_frames(size_t skip, size_t max_depth) { return {}; } } diff --git a/src/unwind/unwind_with_unwind.cpp b/src/unwind/unwind_with_unwind.cpp index ba31344..0f67513 100644 --- a/src/unwind/unwind_with_unwind.cpp +++ b/src/unwind/unwind_with_unwind.cpp @@ -40,18 +40,22 @@ namespace detail { if(!is_before_instruction && ip != uintptr_t(0)) { ip--; } - if (ip == uintptr_t(0) || state.count == state.vec.size()) { + if (ip == uintptr_t(0)) { return _URC_END_OF_STACK; } else { // TODO: push_back?... state.vec[state.count++] = ip; - return _URC_NO_REASON; + if(state.count == state.vec.size()) { + return _URC_END_OF_STACK; + } else { + return _URC_NO_REASON; + } } } CPPTRACE_FORCE_NO_INLINE - std::vector capture_frames(size_t skip) { - std::vector frames(hard_max_frames, 0); + std::vector capture_frames(size_t skip, size_t max_depth) { + std::vector frames(std::min(hard_max_frames, max_depth), 0); unwind_state state{skip + 1, 0, frames}; _Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe frames.resize(state.count); diff --git a/src/unwind/unwind_with_winapi.cpp b/src/unwind/unwind_with_winapi.cpp index bf918aa..11e1bc3 100644 --- a/src/unwind/unwind_with_winapi.cpp +++ b/src/unwind/unwind_with_winapi.cpp @@ -5,20 +5,30 @@ #include "../platform/common.hpp" #include "../platform/utils.hpp" +#include #include #include #include +// Fucking windows headers +#ifdef min + #undef min +#endif + namespace cpptrace { namespace detail { CPPTRACE_FORCE_NO_INLINE - std::vector capture_frames(size_t skip) { - std::vector addrs(hard_max_frames, nullptr); - int n_frames = CaptureStackBackTrace(static_cast(skip + 1), hard_max_frames, addrs.data(), NULL); - addrs.resize(n_frames); - std::vector frames(addrs.size(), 0); - for(std::size_t i = 0; i < addrs.size(); i++) { + std::vector capture_frames(size_t skip, size_t max_depth) { + std::vector addrs(std::min(hard_max_frames, max_depth), nullptr); + int n_frames = CaptureStackBackTrace( + static_cast(skip + 1), + static_cast(addrs.size()), + addrs.data(), + NULL + ); + std::vector frames(n_frames, 0); + for(std::size_t i = 0; i < n_frames; i++) { frames[i] = reinterpret_cast(addrs[i]); } return frames;