109467b48Spatrick //===- KillTheDoctor - Prevent Dr. Watson from stopping tests ---*- C++ -*-===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // This program provides an extremely hacky way to stop Dr. Watson from starting
1009467b48Spatrick // due to unhandled exceptions in child processes.
1109467b48Spatrick //
1209467b48Spatrick // This simply starts the program named in the first positional argument with
1309467b48Spatrick // the arguments following it under a debugger. All this debugger does is catch
1409467b48Spatrick // any unhandled exceptions thrown in the child process and close the program
1509467b48Spatrick // (and hopefully tells someone about it).
1609467b48Spatrick //
1709467b48Spatrick // This also provides another really hacky method to prevent assert dialog boxes
1809467b48Spatrick // from popping up. When --no-user32 is passed, if any process loads user32.dll,
1909467b48Spatrick // we assume it is trying to call MessageBoxEx and terminate it. The proper way
2009467b48Spatrick // to do this would be to actually set a break point, but there's quite a bit
2109467b48Spatrick // of code involved to get the address of MessageBoxEx in the remote process's
2209467b48Spatrick // address space due to Address space layout randomization (ASLR). This can be
2309467b48Spatrick // added if it's ever actually needed.
2409467b48Spatrick //
2509467b48Spatrick // If the subprocess exits for any reason other than successful termination, -1
2609467b48Spatrick // is returned. If the process exits normally the value it returned is returned.
2709467b48Spatrick //
2809467b48Spatrick // I hate Windows.
2909467b48Spatrick //
3009467b48Spatrick //===----------------------------------------------------------------------===//
3109467b48Spatrick
3209467b48Spatrick #include "llvm/ADT/STLExtras.h"
3309467b48Spatrick #include "llvm/ADT/SmallString.h"
3409467b48Spatrick #include "llvm/ADT/SmallVector.h"
3509467b48Spatrick #include "llvm/ADT/StringExtras.h"
3609467b48Spatrick #include "llvm/ADT/StringRef.h"
3709467b48Spatrick #include "llvm/ADT/Twine.h"
3809467b48Spatrick #include "llvm/Support/CommandLine.h"
3909467b48Spatrick #include "llvm/Support/ManagedStatic.h"
4009467b48Spatrick #include "llvm/Support/Path.h"
4109467b48Spatrick #include "llvm/Support/PrettyStackTrace.h"
4209467b48Spatrick #include "llvm/Support/Signals.h"
4309467b48Spatrick #include "llvm/Support/WindowsError.h"
4409467b48Spatrick #include "llvm/Support/raw_ostream.h"
4509467b48Spatrick #include "llvm/Support/type_traits.h"
4609467b48Spatrick #include <algorithm>
4709467b48Spatrick #include <cerrno>
4809467b48Spatrick #include <cstdlib>
4909467b48Spatrick #include <map>
5009467b48Spatrick #include <string>
5109467b48Spatrick #include <system_error>
5209467b48Spatrick
5309467b48Spatrick // These includes must be last.
5409467b48Spatrick #include <windows.h>
5509467b48Spatrick #include <winerror.h>
5609467b48Spatrick #include <dbghelp.h>
5709467b48Spatrick #include <psapi.h>
5809467b48Spatrick
5909467b48Spatrick using namespace llvm;
6009467b48Spatrick
6109467b48Spatrick #undef max
6209467b48Spatrick
6309467b48Spatrick namespace {
6409467b48Spatrick cl::opt<std::string> ProgramToRun(cl::Positional,
6509467b48Spatrick cl::desc("<program to run>"));
6609467b48Spatrick cl::list<std::string> Argv(cl::ConsumeAfter,
6709467b48Spatrick cl::desc("<program arguments>..."));
6809467b48Spatrick cl::opt<bool> TraceExecution("x",
6909467b48Spatrick cl::desc("Print detailed output about what is being run to stderr."));
7009467b48Spatrick cl::opt<unsigned> Timeout("t", cl::init(0),
7109467b48Spatrick cl::desc("Set maximum runtime in seconds. Defaults to infinite."));
7209467b48Spatrick cl::opt<bool> NoUser32("no-user32",
7309467b48Spatrick cl::desc("Terminate process if it loads user32.dll."));
7409467b48Spatrick
7509467b48Spatrick StringRef ToolName;
7609467b48Spatrick
7709467b48Spatrick template <typename HandleType>
7809467b48Spatrick class ScopedHandle {
7909467b48Spatrick typedef typename HandleType::handle_type handle_type;
8009467b48Spatrick
8109467b48Spatrick handle_type Handle;
8209467b48Spatrick
8309467b48Spatrick public:
ScopedHandle()8409467b48Spatrick ScopedHandle()
8509467b48Spatrick : Handle(HandleType::GetInvalidHandle()) {}
8609467b48Spatrick
ScopedHandle(handle_type handle)8709467b48Spatrick explicit ScopedHandle(handle_type handle)
8809467b48Spatrick : Handle(handle) {}
8909467b48Spatrick
~ScopedHandle()9009467b48Spatrick ~ScopedHandle() {
9109467b48Spatrick HandleType::Destruct(Handle);
9209467b48Spatrick }
9309467b48Spatrick
operator =(handle_type handle)9409467b48Spatrick ScopedHandle& operator=(handle_type handle) {
9509467b48Spatrick // Cleanup current handle.
9609467b48Spatrick if (!HandleType::isValid(Handle))
9709467b48Spatrick HandleType::Destruct(Handle);
9809467b48Spatrick Handle = handle;
9909467b48Spatrick return *this;
10009467b48Spatrick }
10109467b48Spatrick
operator bool() const10209467b48Spatrick operator bool() const {
10309467b48Spatrick return HandleType::isValid(Handle);
10409467b48Spatrick }
10509467b48Spatrick
operator handle_type()10609467b48Spatrick operator handle_type() {
10709467b48Spatrick return Handle;
10809467b48Spatrick }
10909467b48Spatrick };
11009467b48Spatrick
11109467b48Spatrick // This implements the most common handle in the Windows API.
11209467b48Spatrick struct CommonHandle {
11309467b48Spatrick typedef HANDLE handle_type;
11409467b48Spatrick
GetInvalidHandle__anon8e01e2600111::CommonHandle11509467b48Spatrick static handle_type GetInvalidHandle() {
11609467b48Spatrick return INVALID_HANDLE_VALUE;
11709467b48Spatrick }
11809467b48Spatrick
Destruct__anon8e01e2600111::CommonHandle11909467b48Spatrick static void Destruct(handle_type Handle) {
12009467b48Spatrick ::CloseHandle(Handle);
12109467b48Spatrick }
12209467b48Spatrick
isValid__anon8e01e2600111::CommonHandle12309467b48Spatrick static bool isValid(handle_type Handle) {
12409467b48Spatrick return Handle != GetInvalidHandle();
12509467b48Spatrick }
12609467b48Spatrick };
12709467b48Spatrick
12809467b48Spatrick struct FileMappingHandle {
12909467b48Spatrick typedef HANDLE handle_type;
13009467b48Spatrick
GetInvalidHandle__anon8e01e2600111::FileMappingHandle13109467b48Spatrick static handle_type GetInvalidHandle() {
13209467b48Spatrick return NULL;
13309467b48Spatrick }
13409467b48Spatrick
Destruct__anon8e01e2600111::FileMappingHandle13509467b48Spatrick static void Destruct(handle_type Handle) {
13609467b48Spatrick ::CloseHandle(Handle);
13709467b48Spatrick }
13809467b48Spatrick
isValid__anon8e01e2600111::FileMappingHandle13909467b48Spatrick static bool isValid(handle_type Handle) {
14009467b48Spatrick return Handle != GetInvalidHandle();
14109467b48Spatrick }
14209467b48Spatrick };
14309467b48Spatrick
14409467b48Spatrick struct MappedViewOfFileHandle {
14509467b48Spatrick typedef LPVOID handle_type;
14609467b48Spatrick
GetInvalidHandle__anon8e01e2600111::MappedViewOfFileHandle14709467b48Spatrick static handle_type GetInvalidHandle() {
14809467b48Spatrick return NULL;
14909467b48Spatrick }
15009467b48Spatrick
Destruct__anon8e01e2600111::MappedViewOfFileHandle15109467b48Spatrick static void Destruct(handle_type Handle) {
15209467b48Spatrick ::UnmapViewOfFile(Handle);
15309467b48Spatrick }
15409467b48Spatrick
isValid__anon8e01e2600111::MappedViewOfFileHandle15509467b48Spatrick static bool isValid(handle_type Handle) {
15609467b48Spatrick return Handle != GetInvalidHandle();
15709467b48Spatrick }
15809467b48Spatrick };
15909467b48Spatrick
16009467b48Spatrick struct ProcessHandle : CommonHandle {};
16109467b48Spatrick struct ThreadHandle : CommonHandle {};
16209467b48Spatrick struct TokenHandle : CommonHandle {};
16309467b48Spatrick struct FileHandle : CommonHandle {};
16409467b48Spatrick
16509467b48Spatrick typedef ScopedHandle<FileMappingHandle> FileMappingScopedHandle;
16609467b48Spatrick typedef ScopedHandle<MappedViewOfFileHandle> MappedViewOfFileScopedHandle;
16709467b48Spatrick typedef ScopedHandle<ProcessHandle> ProcessScopedHandle;
16809467b48Spatrick typedef ScopedHandle<ThreadHandle> ThreadScopedHandle;
16909467b48Spatrick typedef ScopedHandle<TokenHandle> TokenScopedHandle;
17009467b48Spatrick typedef ScopedHandle<FileHandle> FileScopedHandle;
17109467b48Spatrick }
17209467b48Spatrick
windows_error(DWORD E)17309467b48Spatrick static std::error_code windows_error(DWORD E) { return mapWindowsError(E); }
17409467b48Spatrick
GetFileNameFromHandle(HANDLE FileHandle,std::string & Name)17509467b48Spatrick static std::error_code GetFileNameFromHandle(HANDLE FileHandle,
17609467b48Spatrick std::string &Name) {
17709467b48Spatrick char Filename[MAX_PATH+1];
17809467b48Spatrick bool Success = false;
17909467b48Spatrick Name.clear();
18009467b48Spatrick
18109467b48Spatrick // Get the file size.
18209467b48Spatrick LARGE_INTEGER FileSize;
18309467b48Spatrick Success = ::GetFileSizeEx(FileHandle, &FileSize);
18409467b48Spatrick
18509467b48Spatrick if (!Success)
18609467b48Spatrick return windows_error(::GetLastError());
18709467b48Spatrick
18809467b48Spatrick // Create a file mapping object.
18909467b48Spatrick FileMappingScopedHandle FileMapping(
19009467b48Spatrick ::CreateFileMappingA(FileHandle,
19109467b48Spatrick NULL,
19209467b48Spatrick PAGE_READONLY,
19309467b48Spatrick 0,
19409467b48Spatrick 1,
19509467b48Spatrick NULL));
19609467b48Spatrick
19709467b48Spatrick if (!FileMapping)
19809467b48Spatrick return windows_error(::GetLastError());
19909467b48Spatrick
20009467b48Spatrick // Create a file mapping to get the file name.
20109467b48Spatrick MappedViewOfFileScopedHandle MappedFile(
20209467b48Spatrick ::MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 1));
20309467b48Spatrick
20409467b48Spatrick if (!MappedFile)
20509467b48Spatrick return windows_error(::GetLastError());
20609467b48Spatrick
207*d415bd75Srobert Success = ::GetMappedFileNameA(::GetCurrentProcess(), MappedFile, Filename,
208*d415bd75Srobert std::size(Filename) - 1);
20909467b48Spatrick
21009467b48Spatrick if (!Success)
21109467b48Spatrick return windows_error(::GetLastError());
21209467b48Spatrick else {
21309467b48Spatrick Name = Filename;
21409467b48Spatrick return std::error_code();
21509467b48Spatrick }
21609467b48Spatrick }
21709467b48Spatrick
21809467b48Spatrick /// Find program using shell lookup rules.
21909467b48Spatrick /// @param Program This is either an absolute path, relative path, or simple a
22009467b48Spatrick /// program name. Look in PATH for any programs that match. If no
22109467b48Spatrick /// extension is present, try all extensions in PATHEXT.
22209467b48Spatrick /// @return If ec == errc::success, The absolute path to the program. Otherwise
22309467b48Spatrick /// the return value is undefined.
FindProgram(const std::string & Program,std::error_code & ec)22409467b48Spatrick static std::string FindProgram(const std::string &Program,
22509467b48Spatrick std::error_code &ec) {
22609467b48Spatrick char PathName[MAX_PATH + 1];
22709467b48Spatrick typedef SmallVector<StringRef, 12> pathext_t;
22809467b48Spatrick pathext_t pathext;
22909467b48Spatrick // Check for the program without an extension (in case it already has one).
23009467b48Spatrick pathext.push_back("");
23109467b48Spatrick SplitString(std::getenv("PATHEXT"), pathext, ";");
23209467b48Spatrick
23309467b48Spatrick for (pathext_t::iterator i = pathext.begin(), e = pathext.end(); i != e; ++i){
23409467b48Spatrick SmallString<5> ext;
23509467b48Spatrick for (std::size_t ii = 0, e = i->size(); ii != e; ++ii)
23609467b48Spatrick ext.push_back(::tolower((*i)[ii]));
23709467b48Spatrick LPCSTR Extension = NULL;
23809467b48Spatrick if (ext.size() && ext[0] == '.')
23909467b48Spatrick Extension = ext.c_str();
240*d415bd75Srobert DWORD length = ::SearchPathA(NULL, Program.c_str(), Extension,
241*d415bd75Srobert std::size(PathName), PathName, NULL);
24209467b48Spatrick if (length == 0)
24309467b48Spatrick ec = windows_error(::GetLastError());
244*d415bd75Srobert else if (length > std::size(PathName)) {
24509467b48Spatrick // This may have been the file, return with error.
24609467b48Spatrick ec = windows_error(ERROR_BUFFER_OVERFLOW);
24709467b48Spatrick break;
24809467b48Spatrick } else {
24909467b48Spatrick // We found the path! Return it.
25009467b48Spatrick ec = std::error_code();
25109467b48Spatrick break;
25209467b48Spatrick }
25309467b48Spatrick }
25409467b48Spatrick
25509467b48Spatrick // Make sure PathName is valid.
25609467b48Spatrick PathName[MAX_PATH] = 0;
25709467b48Spatrick return PathName;
25809467b48Spatrick }
25909467b48Spatrick
ExceptionCodeToString(DWORD ExceptionCode)26009467b48Spatrick static StringRef ExceptionCodeToString(DWORD ExceptionCode) {
26109467b48Spatrick switch(ExceptionCode) {
26209467b48Spatrick case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION";
26309467b48Spatrick case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
26409467b48Spatrick return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
26509467b48Spatrick case EXCEPTION_BREAKPOINT: return "EXCEPTION_BREAKPOINT";
26609467b48Spatrick case EXCEPTION_DATATYPE_MISALIGNMENT:
26709467b48Spatrick return "EXCEPTION_DATATYPE_MISALIGNMENT";
26809467b48Spatrick case EXCEPTION_FLT_DENORMAL_OPERAND: return "EXCEPTION_FLT_DENORMAL_OPERAND";
26909467b48Spatrick case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
27009467b48Spatrick case EXCEPTION_FLT_INEXACT_RESULT: return "EXCEPTION_FLT_INEXACT_RESULT";
27109467b48Spatrick case EXCEPTION_FLT_INVALID_OPERATION:
27209467b48Spatrick return "EXCEPTION_FLT_INVALID_OPERATION";
27309467b48Spatrick case EXCEPTION_FLT_OVERFLOW: return "EXCEPTION_FLT_OVERFLOW";
27409467b48Spatrick case EXCEPTION_FLT_STACK_CHECK: return "EXCEPTION_FLT_STACK_CHECK";
27509467b48Spatrick case EXCEPTION_FLT_UNDERFLOW: return "EXCEPTION_FLT_UNDERFLOW";
27609467b48Spatrick case EXCEPTION_ILLEGAL_INSTRUCTION: return "EXCEPTION_ILLEGAL_INSTRUCTION";
27709467b48Spatrick case EXCEPTION_IN_PAGE_ERROR: return "EXCEPTION_IN_PAGE_ERROR";
27809467b48Spatrick case EXCEPTION_INT_DIVIDE_BY_ZERO: return "EXCEPTION_INT_DIVIDE_BY_ZERO";
27909467b48Spatrick case EXCEPTION_INT_OVERFLOW: return "EXCEPTION_INT_OVERFLOW";
28009467b48Spatrick case EXCEPTION_INVALID_DISPOSITION: return "EXCEPTION_INVALID_DISPOSITION";
28109467b48Spatrick case EXCEPTION_NONCONTINUABLE_EXCEPTION:
28209467b48Spatrick return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
28309467b48Spatrick case EXCEPTION_PRIV_INSTRUCTION: return "EXCEPTION_PRIV_INSTRUCTION";
28409467b48Spatrick case EXCEPTION_SINGLE_STEP: return "EXCEPTION_SINGLE_STEP";
28509467b48Spatrick case EXCEPTION_STACK_OVERFLOW: return "EXCEPTION_STACK_OVERFLOW";
28609467b48Spatrick default: return "<unknown>";
28709467b48Spatrick }
28809467b48Spatrick }
28909467b48Spatrick
main(int argc,char ** argv)29009467b48Spatrick int main(int argc, char **argv) {
29109467b48Spatrick // Print a stack trace if we signal out.
29209467b48Spatrick sys::PrintStackTraceOnErrorSignal(argv[0]);
29309467b48Spatrick PrettyStackTraceProgram X(argc, argv);
29409467b48Spatrick llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
29509467b48Spatrick
29609467b48Spatrick ToolName = argv[0];
29709467b48Spatrick
29809467b48Spatrick cl::ParseCommandLineOptions(argc, argv, "Dr. Watson Assassin.\n");
29909467b48Spatrick if (ProgramToRun.size() == 0) {
30009467b48Spatrick cl::PrintHelpMessage();
30109467b48Spatrick return -1;
30209467b48Spatrick }
30309467b48Spatrick
30409467b48Spatrick if (Timeout > std::numeric_limits<uint32_t>::max() / 1000) {
30509467b48Spatrick errs() << ToolName << ": Timeout value too large, must be less than: "
30609467b48Spatrick << std::numeric_limits<uint32_t>::max() / 1000
30709467b48Spatrick << '\n';
30809467b48Spatrick return -1;
30909467b48Spatrick }
31009467b48Spatrick
31109467b48Spatrick std::string CommandLine(ProgramToRun);
31209467b48Spatrick
31309467b48Spatrick std::error_code ec;
31409467b48Spatrick ProgramToRun = FindProgram(ProgramToRun, ec);
31509467b48Spatrick if (ec) {
31609467b48Spatrick errs() << ToolName << ": Failed to find program: '" << CommandLine
31709467b48Spatrick << "': " << ec.message() << '\n';
31809467b48Spatrick return -1;
31909467b48Spatrick }
32009467b48Spatrick
32109467b48Spatrick if (TraceExecution)
32209467b48Spatrick errs() << ToolName << ": Found Program: " << ProgramToRun << '\n';
32309467b48Spatrick
32409467b48Spatrick for (const std::string &Arg : Argv) {
32509467b48Spatrick CommandLine.push_back(' ');
32609467b48Spatrick CommandLine.append(Arg);
32709467b48Spatrick }
32809467b48Spatrick
32909467b48Spatrick if (TraceExecution)
33009467b48Spatrick errs() << ToolName << ": Program Image Path: " << ProgramToRun << '\n'
33109467b48Spatrick << ToolName << ": Command Line: " << CommandLine << '\n';
33209467b48Spatrick
33309467b48Spatrick STARTUPINFOA StartupInfo;
33409467b48Spatrick PROCESS_INFORMATION ProcessInfo;
33509467b48Spatrick std::memset(&StartupInfo, 0, sizeof(StartupInfo));
33609467b48Spatrick StartupInfo.cb = sizeof(StartupInfo);
33709467b48Spatrick std::memset(&ProcessInfo, 0, sizeof(ProcessInfo));
33809467b48Spatrick
33909467b48Spatrick // Set error mode to not display any message boxes. The child process inherits
34009467b48Spatrick // this.
34109467b48Spatrick ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
34209467b48Spatrick ::_set_error_mode(_OUT_TO_STDERR);
34309467b48Spatrick
34409467b48Spatrick BOOL success = ::CreateProcessA(ProgramToRun.c_str(),
34509467b48Spatrick const_cast<LPSTR>(CommandLine.c_str()),
34609467b48Spatrick NULL,
34709467b48Spatrick NULL,
34809467b48Spatrick FALSE,
34909467b48Spatrick DEBUG_PROCESS,
35009467b48Spatrick NULL,
35109467b48Spatrick NULL,
35209467b48Spatrick &StartupInfo,
35309467b48Spatrick &ProcessInfo);
35409467b48Spatrick if (!success) {
35509467b48Spatrick errs() << ToolName << ": Failed to run program: '" << ProgramToRun << "': "
35609467b48Spatrick << std::error_code(windows_error(::GetLastError())).message()
35709467b48Spatrick << '\n';
35809467b48Spatrick return -1;
35909467b48Spatrick }
36009467b48Spatrick
36109467b48Spatrick // Make sure ::CloseHandle is called on exit.
36209467b48Spatrick std::map<DWORD, HANDLE> ProcessIDToHandle;
36309467b48Spatrick
36409467b48Spatrick DEBUG_EVENT DebugEvent;
36509467b48Spatrick std::memset(&DebugEvent, 0, sizeof(DebugEvent));
36609467b48Spatrick DWORD dwContinueStatus = DBG_CONTINUE;
36709467b48Spatrick
36809467b48Spatrick // Run the program under the debugger until either it exits, or throws an
36909467b48Spatrick // exception.
37009467b48Spatrick if (TraceExecution)
37109467b48Spatrick errs() << ToolName << ": Debugging...\n";
37209467b48Spatrick
37309467b48Spatrick while(true) {
37409467b48Spatrick DWORD TimeLeft = INFINITE;
37509467b48Spatrick if (Timeout > 0) {
37609467b48Spatrick FILETIME CreationTime, ExitTime, KernelTime, UserTime;
37709467b48Spatrick ULARGE_INTEGER a, b;
37809467b48Spatrick success = ::GetProcessTimes(ProcessInfo.hProcess,
37909467b48Spatrick &CreationTime,
38009467b48Spatrick &ExitTime,
38109467b48Spatrick &KernelTime,
38209467b48Spatrick &UserTime);
38309467b48Spatrick if (!success) {
38409467b48Spatrick ec = windows_error(::GetLastError());
38509467b48Spatrick
38609467b48Spatrick errs() << ToolName << ": Failed to get process times: "
38709467b48Spatrick << ec.message() << '\n';
38809467b48Spatrick return -1;
38909467b48Spatrick }
39009467b48Spatrick a.LowPart = KernelTime.dwLowDateTime;
39109467b48Spatrick a.HighPart = KernelTime.dwHighDateTime;
39209467b48Spatrick b.LowPart = UserTime.dwLowDateTime;
39309467b48Spatrick b.HighPart = UserTime.dwHighDateTime;
39409467b48Spatrick // Convert 100-nanosecond units to milliseconds.
39509467b48Spatrick uint64_t TotalTimeMiliseconds = (a.QuadPart + b.QuadPart) / 10000;
39609467b48Spatrick // Handle the case where the process has been running for more than 49
39709467b48Spatrick // days.
39809467b48Spatrick if (TotalTimeMiliseconds > std::numeric_limits<uint32_t>::max()) {
39909467b48Spatrick errs() << ToolName << ": Timeout Failed: Process has been running for"
40009467b48Spatrick "more than 49 days.\n";
40109467b48Spatrick return -1;
40209467b48Spatrick }
40309467b48Spatrick
40409467b48Spatrick // We check with > instead of using Timeleft because if
40509467b48Spatrick // TotalTimeMiliseconds is greater than Timeout * 1000, TimeLeft would
40609467b48Spatrick // underflow.
40709467b48Spatrick if (TotalTimeMiliseconds > (Timeout * 1000)) {
40809467b48Spatrick errs() << ToolName << ": Process timed out.\n";
40909467b48Spatrick ::TerminateProcess(ProcessInfo.hProcess, -1);
41009467b48Spatrick // Otherwise other stuff starts failing...
41109467b48Spatrick return -1;
41209467b48Spatrick }
41309467b48Spatrick
41409467b48Spatrick TimeLeft = (Timeout * 1000) - static_cast<uint32_t>(TotalTimeMiliseconds);
41509467b48Spatrick }
41609467b48Spatrick success = WaitForDebugEvent(&DebugEvent, TimeLeft);
41709467b48Spatrick
41809467b48Spatrick if (!success) {
41909467b48Spatrick DWORD LastError = ::GetLastError();
42009467b48Spatrick ec = windows_error(LastError);
42109467b48Spatrick
42209467b48Spatrick if (LastError == ERROR_SEM_TIMEOUT || LastError == WSAETIMEDOUT) {
42309467b48Spatrick errs() << ToolName << ": Process timed out.\n";
42409467b48Spatrick ::TerminateProcess(ProcessInfo.hProcess, -1);
42509467b48Spatrick // Otherwise other stuff starts failing...
42609467b48Spatrick return -1;
42709467b48Spatrick }
42809467b48Spatrick
42909467b48Spatrick errs() << ToolName << ": Failed to wait for debug event in program: '"
43009467b48Spatrick << ProgramToRun << "': " << ec.message() << '\n';
43109467b48Spatrick return -1;
43209467b48Spatrick }
43309467b48Spatrick
43409467b48Spatrick switch(DebugEvent.dwDebugEventCode) {
43509467b48Spatrick case CREATE_PROCESS_DEBUG_EVENT:
43609467b48Spatrick // Make sure we remove the handle on exit.
43709467b48Spatrick if (TraceExecution)
43809467b48Spatrick errs() << ToolName << ": Debug Event: CREATE_PROCESS_DEBUG_EVENT\n";
43909467b48Spatrick ProcessIDToHandle[DebugEvent.dwProcessId] =
44009467b48Spatrick DebugEvent.u.CreateProcessInfo.hProcess;
44109467b48Spatrick ::CloseHandle(DebugEvent.u.CreateProcessInfo.hFile);
44209467b48Spatrick break;
44309467b48Spatrick case EXIT_PROCESS_DEBUG_EVENT: {
44409467b48Spatrick if (TraceExecution)
44509467b48Spatrick errs() << ToolName << ": Debug Event: EXIT_PROCESS_DEBUG_EVENT\n";
44609467b48Spatrick
44709467b48Spatrick // If this is the process we originally created, exit with its exit
44809467b48Spatrick // code.
44909467b48Spatrick if (DebugEvent.dwProcessId == ProcessInfo.dwProcessId)
45009467b48Spatrick return DebugEvent.u.ExitProcess.dwExitCode;
45109467b48Spatrick
45209467b48Spatrick // Otherwise cleanup any resources we have for it.
45309467b48Spatrick std::map<DWORD, HANDLE>::iterator ExitingProcess =
45409467b48Spatrick ProcessIDToHandle.find(DebugEvent.dwProcessId);
45509467b48Spatrick if (ExitingProcess == ProcessIDToHandle.end()) {
45609467b48Spatrick errs() << ToolName << ": Got unknown process id!\n";
45709467b48Spatrick return -1;
45809467b48Spatrick }
45909467b48Spatrick ::CloseHandle(ExitingProcess->second);
46009467b48Spatrick ProcessIDToHandle.erase(ExitingProcess);
46109467b48Spatrick }
46209467b48Spatrick break;
46309467b48Spatrick case CREATE_THREAD_DEBUG_EVENT:
46409467b48Spatrick ::CloseHandle(DebugEvent.u.CreateThread.hThread);
46509467b48Spatrick break;
46609467b48Spatrick case LOAD_DLL_DEBUG_EVENT: {
46709467b48Spatrick // Cleanup the file handle.
46809467b48Spatrick FileScopedHandle DLLFile(DebugEvent.u.LoadDll.hFile);
46909467b48Spatrick std::string DLLName;
47009467b48Spatrick ec = GetFileNameFromHandle(DLLFile, DLLName);
47109467b48Spatrick if (ec) {
47209467b48Spatrick DLLName = "<failed to get file name from file handle> : ";
47309467b48Spatrick DLLName += ec.message();
47409467b48Spatrick }
47509467b48Spatrick if (TraceExecution) {
47609467b48Spatrick errs() << ToolName << ": Debug Event: LOAD_DLL_DEBUG_EVENT\n";
47709467b48Spatrick errs().indent(ToolName.size()) << ": DLL Name : " << DLLName << '\n';
47809467b48Spatrick }
47909467b48Spatrick
48009467b48Spatrick if (NoUser32 && sys::path::stem(DLLName) == "user32") {
48109467b48Spatrick // Program is loading user32.dll, in the applications we are testing,
48209467b48Spatrick // this only happens if an assert has fired. By now the message has
48309467b48Spatrick // already been printed, so simply close the program.
48409467b48Spatrick errs() << ToolName << ": user32.dll loaded!\n";
48509467b48Spatrick errs().indent(ToolName.size())
48609467b48Spatrick << ": This probably means that assert was called. Closing "
48709467b48Spatrick "program to prevent message box from popping up.\n";
48809467b48Spatrick dwContinueStatus = DBG_CONTINUE;
48909467b48Spatrick ::TerminateProcess(ProcessIDToHandle[DebugEvent.dwProcessId], -1);
49009467b48Spatrick return -1;
49109467b48Spatrick }
49209467b48Spatrick }
49309467b48Spatrick break;
49409467b48Spatrick case EXCEPTION_DEBUG_EVENT: {
49509467b48Spatrick // Close the application if this exception will not be handled by the
49609467b48Spatrick // child application.
49709467b48Spatrick if (TraceExecution)
49809467b48Spatrick errs() << ToolName << ": Debug Event: EXCEPTION_DEBUG_EVENT\n";
49909467b48Spatrick
50009467b48Spatrick EXCEPTION_DEBUG_INFO &Exception = DebugEvent.u.Exception;
50109467b48Spatrick if (Exception.dwFirstChance > 0) {
50209467b48Spatrick if (TraceExecution) {
50309467b48Spatrick errs().indent(ToolName.size()) << ": Debug Info : ";
50409467b48Spatrick errs() << "First chance exception at "
50509467b48Spatrick << Exception.ExceptionRecord.ExceptionAddress
50609467b48Spatrick << ", exception code: "
50709467b48Spatrick << ExceptionCodeToString(
50809467b48Spatrick Exception.ExceptionRecord.ExceptionCode)
50909467b48Spatrick << " (" << Exception.ExceptionRecord.ExceptionCode << ")\n";
51009467b48Spatrick }
51109467b48Spatrick dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
51209467b48Spatrick } else {
51309467b48Spatrick errs() << ToolName << ": Unhandled exception in: " << ProgramToRun
51409467b48Spatrick << "!\n";
51509467b48Spatrick errs().indent(ToolName.size()) << ": location: ";
51609467b48Spatrick errs() << Exception.ExceptionRecord.ExceptionAddress
51709467b48Spatrick << ", exception code: "
51809467b48Spatrick << ExceptionCodeToString(
51909467b48Spatrick Exception.ExceptionRecord.ExceptionCode)
52009467b48Spatrick << " (" << Exception.ExceptionRecord.ExceptionCode
52109467b48Spatrick << ")\n";
52209467b48Spatrick dwContinueStatus = DBG_CONTINUE;
52309467b48Spatrick ::TerminateProcess(ProcessIDToHandle[DebugEvent.dwProcessId], -1);
52409467b48Spatrick return -1;
52509467b48Spatrick }
52609467b48Spatrick }
52709467b48Spatrick break;
52809467b48Spatrick default:
52909467b48Spatrick // Do nothing.
53009467b48Spatrick if (TraceExecution)
53109467b48Spatrick errs() << ToolName << ": Debug Event: <unknown>\n";
53209467b48Spatrick break;
53309467b48Spatrick }
53409467b48Spatrick
53509467b48Spatrick success = ContinueDebugEvent(DebugEvent.dwProcessId,
53609467b48Spatrick DebugEvent.dwThreadId,
53709467b48Spatrick dwContinueStatus);
53809467b48Spatrick if (!success) {
53909467b48Spatrick ec = windows_error(::GetLastError());
54009467b48Spatrick errs() << ToolName << ": Failed to continue debugging program: '"
54109467b48Spatrick << ProgramToRun << "': " << ec.message() << '\n';
54209467b48Spatrick return -1;
54309467b48Spatrick }
54409467b48Spatrick
54509467b48Spatrick dwContinueStatus = DBG_CONTINUE;
54609467b48Spatrick }
54709467b48Spatrick
54809467b48Spatrick assert(0 && "Fell out of debug loop. This shouldn't be possible!");
54909467b48Spatrick return -1;
55009467b48Spatrick }
551