Commit 4c417877 by Derek Mauro

Adds stacktrace support from Abseil to Google Test

This change adds the ability to generate stacktraces in Google Test on both failures of assertions/expectations and on crashes. The stacktrace support is conditionally available only when using Abseil with Google Test. To use this support, run the test under Bazel with a command like this: bazel test --define absl=1 --test_env=GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1 //path/to/your:test The "--define absl=1" part enables stacktraces on assertion/expectation failures. The "--test_env=GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" part enables the signal handler that logs a stacktrace in the event of a crash (this also requires the "--define absl=1" part). This is not the default since it may interfere with existing tests.
parent ba96d0b1
...@@ -38,7 +38,7 @@ licenses(["notice"]) ...@@ -38,7 +38,7 @@ licenses(["notice"])
config_setting( config_setting(
name = "windows", name = "windows",
values = { "cpu": "x64_windows" }, values = {"cpu": "x64_windows"},
) )
config_setting( config_setting(
...@@ -51,7 +51,6 @@ config_setting( ...@@ -51,7 +51,6 @@ config_setting(
values = {"define": "absl=1"}, values = {"define": "absl=1"},
) )
# Google Test including Google Mock # Google Test including Google Mock
cc_library( cc_library(
name = "gtest", name = "gtest",
...@@ -70,7 +69,7 @@ cc_library( ...@@ -70,7 +69,7 @@ cc_library(
"googlemock/src/gmock_main.cc", "googlemock/src/gmock_main.cc",
], ],
), ),
hdrs =glob([ hdrs = glob([
"googletest/include/gtest/*.h", "googletest/include/gtest/*.h",
"googlemock/include/gmock/*.h", "googlemock/include/gmock/*.h",
]), ]),
...@@ -81,6 +80,14 @@ cc_library( ...@@ -81,6 +80,14 @@ cc_library(
"//conditions:default": ["-pthread"], "//conditions:default": ["-pthread"],
}, },
), ),
defines = select(
{
":has_absl": [
"GTEST_HAS_ABSL=1",
],
"//conditions:default": [],
},
),
includes = [ includes = [
"googlemock", "googlemock",
"googlemock/include", "googlemock/include",
...@@ -94,21 +101,18 @@ cc_library( ...@@ -94,21 +101,18 @@ cc_library(
"-pthread", "-pthread",
], ],
}), }),
defines = select ({ deps = select(
":has_absl": [ {
"GTEST_HAS_ABSL=1",
],
"//conditions:default": [],
}
),
deps = select ({
":has_absl": [ ":has_absl": [
"@com_google_absl//absl/debugging:failure_signal_handler",
"@com_google_absl//absl/debugging:stacktrace",
"@com_google_absl//absl/debugging:symbolize",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:optional",
"@com_google_absl//absl/strings"
], ],
"//conditions:default": [], "//conditions:default": [],
} },
) ),
) )
cc_library( cc_library(
......
...@@ -446,6 +446,16 @@ class OsStackTraceGetter : public OsStackTraceGetterInterface { ...@@ -446,6 +446,16 @@ class OsStackTraceGetter : public OsStackTraceGetterInterface {
virtual void UponLeavingGTest(); virtual void UponLeavingGTest();
private: private:
#if GTEST_HAS_ABSL
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.
void* caller_frame_ = nullptr;
#endif // GTEST_HAS_ABSL
GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter);
}; };
......
...@@ -139,6 +139,13 @@ ...@@ -139,6 +139,13 @@
# define vsnprintf _vsnprintf # define vsnprintf _vsnprintf
#endif // GTEST_OS_WINDOWS #endif // GTEST_OS_WINDOWS
#if GTEST_HAS_ABSL
#include "absl/debugging/failure_signal_handler.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
#include "absl/strings/str_cat.h"
#endif // GTEST_HAS_ABSL
namespace testing { namespace testing {
using internal::CountIf; using internal::CountIf;
...@@ -228,6 +235,13 @@ GTEST_DEFINE_string_( ...@@ -228,6 +235,13 @@ GTEST_DEFINE_string_(
"exclude). A test is run if it matches one of the positive " "exclude). A test is run if it matches one of the positive "
"patterns and does not match any of the negative patterns."); "patterns and does not match any of the negative patterns.");
GTEST_DEFINE_bool_(
install_failure_signal_handler,
internal::BoolFromGTestEnv("install_failure_signal_handler", false),
"If true and supported on the current platform, " GTEST_NAME_ " should "
"install a signal handler that dumps debugging information when fatal "
"signals are raised.");
GTEST_DEFINE_bool_(list_tests, false, GTEST_DEFINE_bool_(list_tests, false,
"List all tests without running them."); "List all tests without running them.");
...@@ -4243,12 +4257,67 @@ void StreamingListener::SocketWriter::MakeConnection() { ...@@ -4243,12 +4257,67 @@ void StreamingListener::SocketWriter::MakeConnection() {
const char* const OsStackTraceGetterInterface::kElidedFramesMarker = const char* const OsStackTraceGetterInterface::kElidedFramesMarker =
"... " GTEST_NAME_ " internal frames ..."; "... " GTEST_NAME_ " internal frames ...";
std::string OsStackTraceGetter::CurrentStackTrace(int /*max_depth*/, std::string OsStackTraceGetter::CurrentStackTrace(int max_depth, int skip_count)
int /*skip_count*/) { GTEST_LOCK_EXCLUDED_(mutex_) {
#if GTEST_HAS_ABSL
std::string result;
if (max_depth <= 0) {
return result;
}
max_depth = std::min(max_depth, kMaxStackTraceDepth);
std::vector<void*> raw_stack(max_depth);
// Skips the frames requested by the caller, plus this function.
const int raw_stack_size =
absl::GetStackTrace(&raw_stack[0], max_depth, skip_count + 1);
void* caller_frame = nullptr;
{
MutexLock lock(&mutex_);
caller_frame = caller_frame_;
}
for (int i = 0; i < raw_stack_size; ++i) {
if (raw_stack[i] == caller_frame &&
!GTEST_FLAG(show_internal_stack_frames)) {
// Add a marker to the trace and stop adding frames.
absl::StrAppend(&result, kElidedFramesMarker, "\n");
break;
}
char tmp[1024];
const char* symbol = "(unknown)";
if (absl::Symbolize(raw_stack[i], tmp, sizeof(tmp))) {
symbol = tmp;
}
char line[1024];
snprintf(line, sizeof(line), " %p: %s\n", raw_stack[i], symbol);
result += line;
}
return result;
#else // !GTEST_HAS_ABSL
static_cast<void>(max_depth);
static_cast<void>(skip_count);
return ""; return "";
#endif // GTEST_HAS_ABSL
} }
void OsStackTraceGetter::UponLeavingGTest() {} void OsStackTraceGetter::UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_) {
#if GTEST_HAS_ABSL
void* caller_frame = nullptr;
if (absl::GetStackTrace(&caller_frame, 1, 3) <= 0) {
caller_frame = nullptr;
}
MutexLock lock(&mutex_);
caller_frame_ = caller_frame;
#endif // GTEST_HAS_ABSL
}
// A helper class that creates the premature-exit file in its // A helper class that creates the premature-exit file in its
// constructor and deletes the file in its destructor. // constructor and deletes the file in its destructor.
...@@ -4865,6 +4934,13 @@ void UnitTestImpl::PostFlagParsingInit() { ...@@ -4865,6 +4934,13 @@ void UnitTestImpl::PostFlagParsingInit() {
// Configures listeners for streaming test results to the specified server. // Configures listeners for streaming test results to the specified server.
ConfigureStreamingOutput(); ConfigureStreamingOutput();
#endif // GTEST_CAN_STREAM_RESULTS_ #endif // GTEST_CAN_STREAM_RESULTS_
#if GTEST_HAS_ABSL
if (GTEST_FLAG(install_failure_signal_handler)) {
absl::FailureSignalHandlerOptions options;
absl::InstallFailureSignalHandler(options);
}
#endif // GTEST_HAS_ABSL
} }
} }
...@@ -5769,6 +5845,10 @@ void InitGoogleTestImpl(int* argc, CharType** argv) { ...@@ -5769,6 +5845,10 @@ void InitGoogleTestImpl(int* argc, CharType** argv) {
g_argvs.push_back(StreamableToString(argv[i])); g_argvs.push_back(StreamableToString(argv[i]));
} }
#if GTEST_HAS_ABSL
absl::InitializeSymbolizer(g_argvs[0].c_str());
#endif // GTEST_HAS_ABSL
ParseGoogleTestFlagsOnly(argc, argv); ParseGoogleTestFlagsOnly(argc, argv);
GetUnitTestImpl()->PostFlagParsingInit(); GetUnitTestImpl()->PostFlagParsingInit();
} }
......
...@@ -34,7 +34,20 @@ ...@@ -34,7 +34,20 @@
licenses(["notice"]) licenses(["notice"])
""" gtest own tests """ config_setting(
name = "windows",
values = {"cpu": "x64_windows"},
)
config_setting(
name = "windows_msvc",
values = {"cpu": "x64_windows_msvc"},
)
config_setting(
name = "has_absl",
values = {"define": "absl=1"},
)
#on windows exclude gtest-tuple.h and gtest-tuple_test.cc #on windows exclude gtest-tuple.h and gtest-tuple_test.cc
cc_test( cc_test(
...@@ -135,7 +148,6 @@ py_library( ...@@ -135,7 +148,6 @@ py_library(
name = "gtest_test_utils", name = "gtest_test_utils",
testonly = 1, testonly = 1,
srcs = ["gtest_test_utils.py"], srcs = ["gtest_test_utils.py"],
) )
cc_binary( cc_binary(
...@@ -144,6 +156,7 @@ cc_binary( ...@@ -144,6 +156,7 @@ cc_binary(
srcs = ["gtest_help_test_.cc"], srcs = ["gtest_help_test_.cc"],
deps = ["//:gtest_main"], deps = ["//:gtest_main"],
) )
py_test( py_test(
name = "gtest_help_test", name = "gtest_help_test",
size = "small", size = "small",
...@@ -163,6 +176,10 @@ py_test( ...@@ -163,6 +176,10 @@ py_test(
name = "gtest_output_test", name = "gtest_output_test",
size = "small", size = "small",
srcs = ["gtest_output_test.py"], srcs = ["gtest_output_test.py"],
args = select({
":has_absl": [],
"//conditions:default": ["--no_stacktrace_support"],
}),
data = [ data = [
"gtest_output_test_golden_lin.txt", "gtest_output_test_golden_lin.txt",
":gtest_output_test_", ":gtest_output_test_",
...@@ -176,6 +193,7 @@ cc_binary( ...@@ -176,6 +193,7 @@ cc_binary(
srcs = ["gtest_color_test_.cc"], srcs = ["gtest_color_test_.cc"],
deps = ["//:gtest"], deps = ["//:gtest"],
) )
py_test( py_test(
name = "gtest_color_test", name = "gtest_color_test",
size = "small", size = "small",
...@@ -327,6 +345,10 @@ py_test( ...@@ -327,6 +345,10 @@ py_test(
"gtest_xml_output_unittest.py", "gtest_xml_output_unittest.py",
"gtest_xml_test_utils.py", "gtest_xml_test_utils.py",
], ],
args = select({
":has_absl": [],
"//conditions:default": ["--no_stacktrace_support"],
}),
data = [ data = [
# We invoke gtest_no_test_unittest to verify the XML output # We invoke gtest_no_test_unittest to verify the XML output
# when the test program contains no test definition. # when the test program contains no test definition.
......
...@@ -52,6 +52,9 @@ import gtest_test_utils ...@@ -52,6 +52,9 @@ import gtest_test_utils
GENGOLDEN_FLAG = '--gengolden' GENGOLDEN_FLAG = '--gengolden'
CATCH_EXCEPTIONS_ENV_VAR_NAME = 'GTEST_CATCH_EXCEPTIONS' CATCH_EXCEPTIONS_ENV_VAR_NAME = 'GTEST_CATCH_EXCEPTIONS'
# The flag indicating stacktraces are not supported
NO_STACKTRACE_SUPPORT_FLAG = '--no_stacktrace_support'
IS_LINUX = os.name == 'posix' and os.uname()[0] == 'Linux' IS_LINUX = os.name == 'posix' and os.uname()[0] == 'Linux'
IS_WINDOWS = os.name == 'nt' IS_WINDOWS = os.name == 'nt'
...@@ -252,13 +255,12 @@ test_list = GetShellCommandOutput(COMMAND_LIST_TESTS) ...@@ -252,13 +255,12 @@ test_list = GetShellCommandOutput(COMMAND_LIST_TESTS)
SUPPORTS_DEATH_TESTS = 'DeathTest' in test_list SUPPORTS_DEATH_TESTS = 'DeathTest' in test_list
SUPPORTS_TYPED_TESTS = 'TypedTest' in test_list SUPPORTS_TYPED_TESTS = 'TypedTest' in test_list
SUPPORTS_THREADS = 'ExpectFailureWithThreadsTest' in test_list SUPPORTS_THREADS = 'ExpectFailureWithThreadsTest' in test_list
SUPPORTS_STACK_TRACES = IS_LINUX SUPPORTS_STACK_TRACES = NO_STACKTRACE_SUPPORT_FLAG not in sys.argv
CAN_GENERATE_GOLDEN_FILE = (SUPPORTS_DEATH_TESTS and CAN_GENERATE_GOLDEN_FILE = (SUPPORTS_DEATH_TESTS and
SUPPORTS_TYPED_TESTS and SUPPORTS_TYPED_TESTS and
SUPPORTS_THREADS and SUPPORTS_THREADS and
SUPPORTS_STACK_TRACES and SUPPORTS_STACK_TRACES)
not IS_WINDOWS)
class GTestOutputTest(gtest_test_utils.TestCase): class GTestOutputTest(gtest_test_utils.TestCase):
def RemoveUnsupportedTests(self, test_output): def RemoveUnsupportedTests(self, test_output):
...@@ -325,7 +327,11 @@ class GTestOutputTest(gtest_test_utils.TestCase): ...@@ -325,7 +327,11 @@ class GTestOutputTest(gtest_test_utils.TestCase):
if __name__ == '__main__': if __name__ == '__main__':
if sys.argv[1:] == [GENGOLDEN_FLAG]: if NO_STACKTRACE_SUPPORT_FLAG in sys.argv:
# unittest.main() can't handle unknown flags
sys.argv.remove(NO_STACKTRACE_SUPPORT_FLAG)
if GENGOLDEN_FLAG in sys.argv:
if CAN_GENERATE_GOLDEN_FILE: if CAN_GENERATE_GOLDEN_FILE:
output = GetOutputOfAllCommands() output = GetOutputOfAllCommands()
golden_file = open(GOLDEN_PATH, 'wb') golden_file = open(GOLDEN_PATH, 'wb')
......
...@@ -47,17 +47,22 @@ GTEST_OUTPUT_FLAG = '--gtest_output' ...@@ -47,17 +47,22 @@ GTEST_OUTPUT_FLAG = '--gtest_output'
GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml' GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml'
GTEST_PROGRAM_NAME = 'gtest_xml_output_unittest_' GTEST_PROGRAM_NAME = 'gtest_xml_output_unittest_'
# The flag indicating stacktraces are not supported
NO_STACKTRACE_SUPPORT_FLAG = '--no_stacktrace_support'
# The environment variables for test sharding. # The environment variables for test sharding.
TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS' TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS'
SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX' SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX'
SHARD_STATUS_FILE_ENV_VAR = 'GTEST_SHARD_STATUS_FILE' SHARD_STATUS_FILE_ENV_VAR = 'GTEST_SHARD_STATUS_FILE'
SUPPORTS_STACK_TRACES = False SUPPORTS_STACK_TRACES = NO_STACKTRACE_SUPPORT_FLAG not in sys.argv
if SUPPORTS_STACK_TRACES: if SUPPORTS_STACK_TRACES:
STACK_TRACE_TEMPLATE = '\nStack trace:\n*' STACK_TRACE_TEMPLATE = '\nStack trace:\n*'
else: else:
STACK_TRACE_TEMPLATE = '' STACK_TRACE_TEMPLATE = ''
# unittest.main() can't handle unknown flags
sys.argv.remove(NO_STACKTRACE_SUPPORT_FLAG)
EXPECTED_NON_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?> EXPECTED_NON_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="23" failures="4" disabled="2" errors="0" time="*" timestamp="*" name="AllTests" ad_hoc_property="42"> <testsuites tests="23" failures="4" disabled="2" errors="0" time="*" timestamp="*" name="AllTests" ad_hoc_property="42">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment