Index: llvm/lib/Support/Windows/Signals.inc =================================================================== --- llvm/lib/Support/Windows/Signals.inc +++ llvm/lib/Support/Windows/Signals.inc @@ -731,6 +731,11 @@ /// otherwise. static std::error_code WINAPI WriteWindowsDumpFile(PMINIDUMP_EXCEPTION_INFORMATION ExceptionInfo) { + struct ScopedCriticalSection { + ScopedCriticalSection() { EnterCriticalSection(&CriticalSection); } + ~ScopedCriticalSection() { LeaveCriticalSection(&CriticalSection); } + } SCS; + using namespace llvm; using namespace llvm::sys; Index: llvm/unittests/Support/CMakeLists.txt =================================================================== --- llvm/unittests/Support/CMakeLists.txt +++ llvm/unittests/Support/CMakeLists.txt @@ -94,6 +94,7 @@ UnicodeTest.cpp VersionTupleTest.cpp VirtualFileSystemTest.cpp + WindowsDumpFileTest.cpp WithColorTest.cpp YAMLIOTest.cpp YAMLParserTest.cpp Index: llvm/unittests/Support/WindowsDumpFileTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Support/WindowsDumpFileTest.cpp @@ -0,0 +1,112 @@ +//===- llvm/unittests/Support/WindowsDumpFileTest.cpp ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Signals.h" + +#include "llvm/Config/config.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Threading.h" + +#include "gtest/gtest.h" + +#include +#include + +#if defined(_WIN32) && LLVM_ENABLE_THREADS && LLVM_ENABLE_CRASH_DUMPS + +using namespace llvm; + +// See llvm/utils/unittest/UnitTestMain/TestMain.cpp. +extern const char *TestMainArgv0; + +// Just a reachable symbol to ease resolving of the executable's path. +static cl::opt + WindowsDumpFileTestStringArg1("windows-dump-file-test-string-arg1"); + +TEST(WindowsDumpFileTest, MultiThread) { + unsigned NumThreads = hardware_concurrency().compute_thread_count(); + if (NumThreads == 1) + GTEST_SKIP(); + + // Use at least 4 threads. + NumThreads = std::max(NumThreads, 4U); + + if (getenv("LLVM_WINDOWS_DUMP_FILE_TEST_MULTI_THREAD")) { + ASSERT_FALSE(sys::Process::AreCoreFilesPrevented()); + sys::PrintStackTraceOnErrorSignal(StringRef(), false); + ASSERT_FALSE(sys::Process::AreCoreFilesPrevented()); + + std::vector Threads; + std::atomic ThreadsStarted(0); + std::promise Started; + std::mutex Mutex; + std::condition_variable Cond; + std::atomic Ready(false); + for (unsigned I = 0; I < NumThreads; ++I) + Threads.emplace_back([&, NumThreads] { + if (++ThreadsStarted == NumThreads) + Started.set_value(); + + std::unique_lock Lock(Mutex); + Cond.wait(Lock, [&] { return Ready.load(); }); + Lock.unlock(); + + LLVM_BUILTIN_TRAP; + }); + + Started.get_future().wait(); + + { + std::unique_lock Lock(Mutex); + Ready = true; + } + Cond.notify_all(); + + for (auto &T : Threads) + T.join(); + + LLVM_BUILTIN_TRAP; + } + + // Create a temporary directory to be used as the temporary directory for the + // test run. + SmallString<128> TmpDir; + std::error_code EC = + sys::fs::createUniqueDirectory("WindowsDumpFileTest", TmpDir); + ASSERT_FALSE(EC); + + std::string Executable = + sys::fs::getMainExecutable(TestMainArgv0, &WindowsDumpFileTestStringArg1); + StringRef Argv[] = {Executable, + "--gtest_filter=WindowsDumpFileTest.MultiThread"}; + std::vector Env; + Env.emplace_back("LLVM_WINDOWS_DUMP_FILE_TEST_MULTI_THREAD=1"); + + // Set temporary directory environment variables. + std::string TmpEnv = ("TMP=" + TmpDir).str(); + Env.emplace_back(TmpEnv); + std::string TempEnv = ("TEMP=" + TmpDir).str(); + Env.emplace_back(TempEnv); + + // The following is required to prevent disabling of dumps. + Env.emplace_back("LLVM_PROGRAM_TEST_NO_STACKTRACE_HANDLER=1"); + + unsigned TimeoutInSecs = NumThreads; + std::string Error; + int Ret = sys::ExecuteAndWait(Executable, Argv, ArrayRef(Env), {}, + TimeoutInSecs, 0, &Error); + ASSERT_NE(Ret, 0); + ASSERT_TRUE(Error.empty()); + + // Remove temporary directory. + sys::fs::remove_directories(TmpDir); +} + +#endif