diff --git a/googletest/include/gtest/internal/gtest-port.h b/googletest/include/gtest/internal/gtest-port.h index b887e24e..bf6e3653 100644 --- a/googletest/include/gtest/internal/gtest-port.h +++ b/googletest/include/gtest/internal/gtest-port.h @@ -216,6 +216,7 @@ // specializations. Always defined to 0 or 1. // GTEST_USE_OWN_FLAGFILE_FLAG_ - Always defined to 0 or 1. // GTEST_HAS_CXXABI_H_ - Always defined to 0 or 1. +// GTEST_HAS_STD_STACKTRACE_ - Always defined to 0 or 1. // GTEST_CAN_STREAM_RESULTS_ - Always defined to 0 or 1. // GTEST_HAS_ALT_PATH_SEP_ - Always defined to 0 or 1. // GTEST_WIDE_STRING_USES_UTF16_ - Always defined to 0 or 1. @@ -873,6 +874,14 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; #endif #endif +#if !defined(GTEST_HAS_STD_STACKTRACE_) +#if defined(__cpp_lib_stacktrace) && __cpp_lib_stacktrace >= 202011L +#define GTEST_HAS_STD_STACKTRACE_ 1 +#else +#define GTEST_HAS_STD_STACKTRACE_ 0 +#endif +#endif + // A function level attribute to disable checking for use of uninitialized // memory when built with MemorySanitizer. #if GTEST_HAVE_ATTRIBUTE_(no_sanitize_memory) diff --git a/googletest/src/gtest-internal-inl.h b/googletest/src/gtest-internal-inl.h index 5b15e1a3..8cd647f2 100644 --- a/googletest/src/gtest-internal-inl.h +++ b/googletest/src/gtest-internal-inl.h @@ -59,6 +59,10 @@ #include // NOLINT #endif // GTEST_OS_WINDOWS +#if GTEST_HAS_STD_STACKTRACE_ +#include +#endif + #include "gtest/gtest-spi.h" #include "gtest/gtest.h" @@ -456,6 +460,29 @@ class AbslStackTraceGetter : public OsStackTraceGetterInterface { }; #endif // GTEST_HAS_ABSL +#if GTEST_HAS_STD_STACKTRACE_ +// A working implementation of the OsStackTraceGetterInterface interface. +class StdStackTraceGetter : public OsStackTraceGetterInterface { + public: + StdStackTraceGetter() = default; + + std::string CurrentStackTrace(int max_depth, int skip_count) override; + void UponLeavingGTest() override; + + private: + Mutex mutex_; // Protects all internal state. + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to the stack trace code from within the user code. + std::stacktrace_entry::native_handle_type caller_frame_ = {}; + + StdStackTraceGetter(const StdStackTraceGetter&) = delete; + StdStackTraceGetter& operator=(const StdStackTraceGetter&) = delete; +}; +#endif // GTEST_HAS_STD_STACKTRACE_ + // An implementation of OsStackTraceGetterInterface which returns no // stack trace. class NoopStackTraceGetter : public OsStackTraceGetterInterface { diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc index 1bfd95dc..8865b022 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -153,6 +153,10 @@ #include "absl/strings/strip.h" #endif // GTEST_HAS_ABSL +#if GTEST_HAS_STD_STACKTRACE_ +#include +#endif + // Checks builtin compiler feature |x| while avoiding an extra layer of #ifdefs // at the callsite. #if defined(__has_builtin) @@ -5025,6 +5029,64 @@ void AbslStackTraceGetter::UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_) { } #endif // GTEST_HAS_ABSL +#if GTEST_HAS_STD_STACKTRACE_ +std::string StdStackTraceGetter::CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_) { + if (max_depth <= 0) { + return ""; + } + + max_depth = std::min(max_depth, kMaxStackTraceDepth); + // Skips the frames requested by the caller, plus this function. + skip_count += 1; + + std::stacktrace stack = std::stacktrace::current(skip_count, max_depth); + + std::stacktrace_entry::native_handle_type caller_frame = {}; + { + MutexLock lock(&mutex_); + caller_frame = caller_frame_; + } + + std::ostringstream result; + for (const std::stacktrace_entry &entry : stack) { + if (entry.native_handle() == caller_frame && + !GTEST_FLAG_GET(show_internal_stack_frames)) { + // Add a marker to the trace and stop adding frames. + result << kElidedFramesMarker << '\n'; + break; + } + + result << " " << entry.native_handle() << ": "; + std::string description = entry.description(); + if (description.empty()) { + result << "(unknown)"; + } else { + result << description; + } + std::string source_file = entry.source_file(); + if (!source_file.empty()) { + result << ' ' << source_file << ':' << entry.source_line(); + } + result << '\n'; + } + return std::move(result).str(); +} +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_STD_STACKTRACE_ +void StdStackTraceGetter::UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_) { + std::stacktrace stack = std::stacktrace::current(/*skip=*/2, /*max_depth=*/1); + std::stacktrace_entry::native_handle_type caller_frame = {}; + if (!stack.empty()) { + caller_frame = stack[0].native_handle(); + } + + MutexLock lock(&mutex_); + caller_frame_ = caller_frame; +} +#endif // GTEST_HAS_ABSL + std::string NoopStackTraceGetter::CurrentStackTrace(int max_depth, int skip_count) { static_cast(max_depth); static_cast(skip_count); @@ -6244,6 +6306,8 @@ OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { if (os_stack_trace_getter_ == nullptr) { #ifdef GTEST_OS_STACK_TRACE_GETTER_ os_stack_trace_getter_ = new GTEST_OS_STACK_TRACE_GETTER_; +#elif GTEST_HAS_STD_STACKTRACE_ + os_stack_trace_getter_ = new StdStackTraceGetter; #elif defined(GTEST_HAS_ABSL) os_stack_trace_getter_ = new AbslStackTraceGetter; #else