diff --git a/common/pretty_stack_trace_function.h b/common/pretty_stack_trace_function.h index f4b68a61eb46..75228030a4e0 100644 --- a/common/pretty_stack_trace_function.h +++ b/common/pretty_stack_trace_function.h @@ -11,6 +11,8 @@ namespace Carbon { +// Calls `fn` as part of LLVM's pretty stack trace support. Implementations +// should typically have a terminating `\n`. class PrettyStackTraceFunction : public llvm::PrettyStackTraceEntry { public: explicit PrettyStackTraceFunction( diff --git a/testing/file_test/BUILD b/testing/file_test/BUILD index caa4f704ee87..49fbc7c2d302 100644 --- a/testing/file_test/BUILD +++ b/testing/file_test/BUILD @@ -48,6 +48,7 @@ cc_library( "//common:find", "//common:init_llvm", "//common:ostream", + "//common:pretty_stack_trace_function", "//common:raw_string_ostream", "//testing/base:file_helpers", "@abseil-cpp//absl/flags:flag", diff --git a/testing/file_test/run_test.cpp b/testing/file_test/run_test.cpp index 457b847e29ed..0149763fb1ee 100644 --- a/testing/file_test/run_test.cpp +++ b/testing/file_test/run_test.cpp @@ -9,6 +9,7 @@ #include #include +#include "common/pretty_stack_trace_function.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" @@ -89,6 +90,17 @@ static auto DoArgReplacements(llvm::SmallVector& test_args, return Success(); } +// Collects captured output when enabled. +static auto CollectOutputIfCapturing(TestFile& test_file) -> void { + if (!test_file.capture_console_output) { + return; + } + // No need to flush stderr. + llvm::outs().flush(); + test_file.actual_stdout += GetCapturedStdout(); + test_file.actual_stderr += GetCapturedStderr(); +} + auto RunTestFile(const FileTestBase& test_base, bool dump_output, TestFile& test_file) -> ErrorOr { llvm::SmallVector all_splits; @@ -164,6 +176,25 @@ auto RunTestFile(const FileTestBase& test_base, bool dump_output, llvm::raw_svector_ostream output_stream(test_file.actual_stdout); llvm::raw_svector_ostream error_stream(test_file.actual_stderr); + // Dump any available captured output if `Run` crashes. + PrettyStackTraceFunction stack_trace_streams([&](llvm::raw_ostream& out) { + CollectOutputIfCapturing(test_file); + + auto dump_stream = [&](llvm::SmallString<16> stream) { + if (stream.empty()) { + out << " (none)\n"; + } else { + out << "\n" << stream << "\n"; + } + }; + + out << "Test stdout:"; + dump_stream(test_file.actual_stdout); + + out << "\tTest stderr:"; + dump_stream(test_file.actual_stderr); + }); + Timer timer; ErrorOr run_result = dump_output ? test_base.Run(test_args_ref, fs, input_stream, llvm::outs(), @@ -172,13 +203,8 @@ auto RunTestFile(const FileTestBase& test_base, bool dump_output, output_stream, error_stream); test_file.run_elapsed_ms = timer.elapsed_ms(); - // Ensure stdout/stderr are always fetched, even when discarded on error. - if (test_file.capture_console_output) { - // No need to flush stderr. - llvm::outs().flush(); - test_file.actual_stdout += GetCapturedStdout(); - test_file.actual_stderr += GetCapturedStderr(); - } + // Collect captured stdout/stderr, even when discarded on error. + CollectOutputIfCapturing(test_file); if (!run_result.ok()) { return std::move(run_result).error();