2
// Copyright 2014 The ANGLE Project Authors. All rights reserved.
3
// Use of this source code is governed by a BSD-style license that can be
4
// found in the LICENSE file.
7
// test_utils_win.cpp: Implementation of OS-specific functions for Windows
9
#include "util/test_utils.h"
13
#include <versionhelpers.h>
19
#include "anglebase/no_destructor.h"
20
#include "common/angleutils.h"
33
bool closeReadHandle()
37
if (::CloseHandle(readHandle) == FALSE)
39
std::cerr << "Error closing write handle: " << GetLastError();
47
bool closeWriteHandle()
51
if (::CloseHandle(writeHandle) == FALSE)
53
std::cerr << "Error closing write handle: " << GetLastError();
56
writeHandle = nullptr;
62
bool valid() const { return readHandle != nullptr || writeHandle != nullptr; }
64
bool initPipe(SECURITY_ATTRIBUTES *securityAttribs)
66
if (::CreatePipe(&readHandle, &writeHandle, securityAttribs, 0) == FALSE)
68
std::cerr << "Error creating pipe: " << GetLastError() << "\n";
72
#if !defined(ANGLE_ENABLE_WINDOWS_UWP)
73
// Ensure the read handles to the pipes are not inherited.
74
if (::SetHandleInformation(readHandle, HANDLE_FLAG_INHERIT, 0) == FALSE)
76
std::cerr << "Error setting handle info on pipe: " << GetLastError() << "\n";
79
#endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
84
HANDLE readHandle = nullptr;
85
HANDLE writeHandle = nullptr;
88
// Returns false on EOF or error.
89
void ReadFromFile(bool blocking, HANDLE handle, std::string *out)
98
BOOL success = ::PeekNamedPipe(handle, nullptr, 0, nullptr, &bytesRead, nullptr);
99
if (success == FALSE || bytesRead == 0)
103
BOOL success = ::ReadFile(handle, buffer, sizeof(buffer), &bytesRead, nullptr);
104
if (success == FALSE || bytesRead == 0)
107
out->append(buffer, bytesRead);
113
// Returns the Win32 last error code or ERROR_SUCCESS if the last error code is
114
// ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND. This is useful in cases where
115
// the absence of a file or path is a success condition (e.g., when attempting
116
// to delete an item in the filesystem).
117
bool ReturnSuccessOnNotFound()
119
const DWORD error_code = ::GetLastError();
120
return (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND);
123
// Job objects seems to have problems on the Chromium CI and Windows 7.
124
bool ShouldUseJobObjects()
126
#if defined(ANGLE_ENABLE_WINDOWS_UWP)
129
return (::IsWindows10OrGreater());
133
class WindowsProcess : public Process
136
WindowsProcess(const std::vector<const char *> &commandLineArgs,
140
mProcessInfo.hProcess = INVALID_HANDLE_VALUE;
141
mProcessInfo.hThread = INVALID_HANDLE_VALUE;
143
std::vector<char> commandLineString;
144
for (const char *arg : commandLineArgs)
148
if (!commandLineString.empty())
150
commandLineString.push_back(' ');
152
commandLineString.insert(commandLineString.end(), arg, arg + strlen(arg));
155
commandLineString.push_back('\0');
157
// Set the bInheritHandle flag so pipe handles are inherited.
158
SECURITY_ATTRIBUTES securityAttribs;
159
securityAttribs.nLength = sizeof(SECURITY_ATTRIBUTES);
160
securityAttribs.bInheritHandle = TRUE;
161
securityAttribs.lpSecurityDescriptor = nullptr;
163
STARTUPINFOA startInfo = {};
165
// Create pipes for stdout and stderr.
166
startInfo.cb = sizeof(STARTUPINFOA);
167
startInfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
170
if (!mStdoutPipe.initPipe(&securityAttribs))
174
startInfo.hStdOutput = mStdoutPipe.writeHandle;
178
startInfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
183
if (!mStderrPipe.initPipe(&securityAttribs))
187
startInfo.hStdError = mStderrPipe.writeHandle;
191
startInfo.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
194
#if !defined(ANGLE_ENABLE_WINDOWS_UWP)
195
if (captureStdOut || captureStdErr)
197
startInfo.dwFlags |= STARTF_USESTDHANDLES;
200
if (ShouldUseJobObjects())
202
// Create job object. Job objects allow us to automatically force child processes to
203
// exit if the parent process is unexpectedly killed. This should prevent ghost
204
// processes from hanging around.
205
mJobHandle = ::CreateJobObjectA(nullptr, nullptr);
206
if (mJobHandle == NULL)
208
std::cerr << "Error creating job object: " << GetLastError() << "\n";
212
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limitInfo = {};
213
limitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
214
if (::SetInformationJobObject(mJobHandle, JobObjectExtendedLimitInformation, &limitInfo,
215
sizeof(limitInfo)) == FALSE)
217
std::cerr << "Error setting job information: " << GetLastError() << "\n";
221
#endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
223
// Create the child process.
224
if (::CreateProcessA(nullptr, commandLineString.data(), nullptr, nullptr,
225
TRUE, // Handles are inherited.
226
0, nullptr, nullptr, &startInfo, &mProcessInfo) == FALSE)
228
std::cerr << "CreateProcessA Error code: " << GetLastError() << "\n";
232
#if !defined(ANGLE_ENABLE_WINDOWS_UWP)
233
if (mJobHandle != nullptr)
235
if (::AssignProcessToJobObject(mJobHandle, mProcessInfo.hProcess) == FALSE)
237
std::cerr << "AssignProcessToJobObject failed: " << GetLastError() << "\n";
241
#endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
243
// Close the write end of the pipes, so EOF can be generated when child exits.
244
if (!mStdoutPipe.closeWriteHandle() || !mStderrPipe.closeWriteHandle())
251
~WindowsProcess() override
253
if (mProcessInfo.hProcess != INVALID_HANDLE_VALUE)
255
::CloseHandle(mProcessInfo.hProcess);
257
if (mProcessInfo.hThread != INVALID_HANDLE_VALUE)
259
::CloseHandle(mProcessInfo.hThread);
261
if (mJobHandle != nullptr)
263
::CloseHandle(mJobHandle);
267
bool started() override { return mStarted; }
269
bool finish() override
271
if (mStdoutPipe.valid())
273
ReadFromFile(true, mStdoutPipe.readHandle, &mStdout);
276
if (mStderrPipe.valid())
278
ReadFromFile(true, mStderrPipe.readHandle, &mStderr);
281
DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, INFINITE);
283
return result == WAIT_OBJECT_0;
286
bool finished() override
291
// Pipe stdin and stdout.
292
if (mStdoutPipe.valid())
294
ReadFromFile(false, mStdoutPipe.readHandle, &mStdout);
297
if (mStderrPipe.valid())
299
ReadFromFile(false, mStderrPipe.readHandle, &mStderr);
302
DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, 0);
303
if (result == WAIT_OBJECT_0)
308
if (result == WAIT_TIMEOUT)
312
std::cerr << "Unexpected result from WaitForSingleObject: " << result
313
<< ". Last error: " << ::GetLastError() << "\n";
317
int getExitCode() override
322
if (mProcessInfo.hProcess == INVALID_HANDLE_VALUE)
326
if (::GetExitCodeProcess(mProcessInfo.hProcess, &exitCode) == FALSE)
329
return static_cast<int>(exitCode);
338
if (::DuplicateHandle(::GetCurrentProcess(), mProcessInfo.hProcess, ::GetCurrentProcess(),
339
&newHandle, PROCESS_ALL_ACCESS, false,
340
DUPLICATE_CLOSE_SOURCE) == FALSE)
342
std::cerr << "Error getting permission to terminate process: " << ::GetLastError()
346
mProcessInfo.hProcess = newHandle;
348
#if !defined(ANGLE_ENABLE_WINDOWS_UWP)
349
if (::TerminateThread(mProcessInfo.hThread, 1) == FALSE)
351
std::cerr << "TerminateThread failed: " << GetLastError() << "\n";
354
#endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
356
if (::TerminateProcess(mProcessInfo.hProcess, 1) == FALSE)
358
std::cerr << "TerminateProcess failed: " << GetLastError() << "\n";
368
bool mStarted = false;
369
ScopedPipe mStdoutPipe;
370
ScopedPipe mStderrPipe;
371
PROCESS_INFORMATION mProcessInfo = {};
372
HANDLE mJobHandle = nullptr;
376
void Sleep(unsigned int milliseconds)
378
::Sleep(static_cast<DWORD>(milliseconds));
381
void WriteDebugMessage(const char *format, ...)
384
va_start(args, format);
385
int size = vsnprintf(nullptr, 0, format, args);
388
std::vector<char> buffer(size + 2);
389
va_start(args, format);
390
vsnprintf(buffer.data(), size + 1, format, args);
393
OutputDebugStringA(buffer.data());
396
Process *LaunchProcess(const std::vector<const char *> &args,
400
return new WindowsProcess(args, captureStdout, captureStderr);
403
bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
405
DWORD pathLen = ::GetTempPathA(maxDirNameLen, tempDirOut);
406
return (pathLen < MAX_PATH && pathLen > 0);
409
bool CreateTemporaryFileInDir(const char *dir, char *tempFileNameOut, uint32_t maxFileNameLen)
411
char fileName[MAX_PATH + 1];
412
if (::GetTempFileNameA(dir, "ANGLE", 0, fileName) == 0)
415
strncpy(tempFileNameOut, fileName, maxFileNameLen);
419
bool DeleteFile(const char *path)
421
if (strlen(path) >= MAX_PATH)
424
const DWORD attr = ::GetFileAttributesA(path);
425
// Report success if the file or path does not exist.
426
if (attr == INVALID_FILE_ATTRIBUTES)
428
return ReturnSuccessOnNotFound();
431
// Clear the read-only bit if it is set.
432
if ((attr & FILE_ATTRIBUTE_READONLY) &&
433
!::SetFileAttributesA(path, attr & ~FILE_ATTRIBUTE_READONLY))
435
// It's possible for |path| to be gone now under a race with other deleters.
436
return ReturnSuccessOnNotFound();
439
// We don't handle directories right now.
440
if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
445
return !!::DeleteFileA(path) ? true : ReturnSuccessOnNotFound();