googletest icon indicating copy to clipboard operation
googletest copied to clipboard

Print stacktraces using std::stacktrace if available

Open strager opened this issue 1 year ago • 4 comments

Reviewers: See issue #4336 for some design considerations. This PR chooses approach B. The first commit changes the design, and the second commit adds the std::stacktrace feature. If this should be split into two separate pull requests (one commit per PR), let me know.

If std::stacktrace is available (currently only GCC (opt-in feature) and MSVC), use it for printing stack traces.

If both Abseil and std::stacktrace are available, prefer std::stacktrace.

Advantages of std::stacktrace over Abseil:

  • Includes file and line information
  • Includes DLL information
  • Better stack trace quality on Windows

Disadvantages of std::stacktrace compared to Abseil:

  • Runs more slowly on Windows
  • Includes information which might not be relevant such as byte offsets
  • Printed lines are longer thus harder to skim

Before (Windows using Abseil):

[ RUN      ] FooTest.Xyz
[snip]\googletest-filter-unittest_.cc(49): error: Failed
Expected failure.
Stack trace:
  00007FF69EF90E8D: testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test,void>
  00007FF69EF90AC3: testing::internal::HandleExceptionsInMethodIfSupported<testing::Test,void>
  00007FF69EF5C01B: testing::Test::Run
  00007FF69EF5CCC5: testing::TestInfo::Run
  00007FF69EF5D6EC: testing::TestSuite::Run
... Google Test internal frames ...

[  FAILED  ] FooTest.Xyz (21 ms)

After (Windows using MSVC STL's std::stacktrace):

[ RUN      ] FooTest.Xyz
[snip]\googletest-filter-unittest_.cc(49): error: Failed
Expected failure.
Stack trace:
  00007FF6770283A9: googletest_filter_unittest_!`anonymous namespace'::FooTest_Xyz_Test::TestBody+0x89 [snip]\googletest-filter-unittest_.cc:49
  00007FF677080E8D: googletest_filter_unittest_!testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test,void>+0x1D [snip]\gtest.cc:2609
  00007FF677080AC3: googletest_filter_unittest_!testing::internal::HandleExceptionsInMethodIfSupported<testing::Test,void>+0x73 [snip]\gtest.cc:2652
  00007FF67704C01B: googletest_filter_unittest_!testing::Test::Run+0xAB [snip]\gtest.cc:2698
... Google Test internal frames ...

[  FAILED  ] FooTest.Xyz (127 ms)

strager avatar Aug 10 '23 04:08 strager

Recently, we found a problem that: after gtest started taking dependency on abseil, the "::testing::InitGoogleTest" function will call SymInitialize Windows API indirectly. However, the API can only be called once per process. For example,

#include <iostream>
#include <Windows.h>
#include <DbgHelp.h>

int main()
{
    HANDLE process = GetCurrentProcess();
    SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
    if (!SymInitialize(process, nullptr, true)) {
        return -1;
    }
    if (!SymInitialize(process, nullptr, true)) {
        return -1;
    }
    return 0;
}

The second SymInitialize call would definitely fail. So, if the library we are testing also has its own backtrace implementation, the extra "SymInitialize" function call would cause trouble. I wonder how it is handled in VC++ runtime's "<stacktrace>" library.

snnn avatar Aug 15 '23 20:08 snnn

I think std::stacktrace has two benefits compared to the existing one:

  1. You don't need to worry about initialization/cleanup. The C++ runtime should handle it.
  2. It dynamically loads dbghelp.dll. So the dependency is optional.

snnn avatar Aug 22 '23 18:08 snnn

@derekmauro any plans to merge this?

zrno avatar Feb 26 '24 21:02 zrno

Recently, we found a problem that: after gtest started taking dependency on abseil, the "::testing::InitGoogleTest" function will call SymInitialize Windows API indirectly. However, the API can only be called once per process. For example,

#include <iostream>
#include <Windows.h>
#include <DbgHelp.h>

int main()
{
    HANDLE process = GetCurrentProcess();
    SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
    if (!SymInitialize(process, nullptr, true)) {
        return -1;
    }
    if (!SymInitialize(process, nullptr, true)) {
        return -1;
    }
    return 0;
}

The second SymInitialize call would definitely fail. So, if the library we are testing also has its own backtrace implementation, the extra "SymInitialize" function call would cause trouble. I wonder how it is handled in VC++ runtime's "" library.

Is this getting some attention? All that is need to be done is not to use GetCurrentProcess()

yuslepukhin avatar Mar 15 '24 20:03 yuslepukhin