1 //
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.
5 //
6
7 // test_utils_win.cpp: Implementation of OS-specific functions for Windows
8
9 #include "util/test_utils.h"
10
11 #include <aclapi.h>
12 #include <stdarg.h>
13 #include <versionhelpers.h>
14 #include <windows.h>
15 #include <array>
16 #include <iostream>
17 #include <vector>
18
19 #include "anglebase/no_destructor.h"
20 #include "common/angleutils.h"
21
22 namespace angle
23 {
24 namespace
25 {
26 struct ScopedPipe
27 {
~ScopedPipeangle::__anon1d13e47c0111::ScopedPipe28 ~ScopedPipe()
29 {
30 closeReadHandle();
31 closeWriteHandle();
32 }
closeReadHandleangle::__anon1d13e47c0111::ScopedPipe33 bool closeReadHandle()
34 {
35 if (readHandle)
36 {
37 if (::CloseHandle(readHandle) == FALSE)
38 {
39 std::cerr << "Error closing write handle: " << GetLastError();
40 return false;
41 }
42 readHandle = nullptr;
43 }
44
45 return true;
46 }
closeWriteHandleangle::__anon1d13e47c0111::ScopedPipe47 bool closeWriteHandle()
48 {
49 if (writeHandle)
50 {
51 if (::CloseHandle(writeHandle) == FALSE)
52 {
53 std::cerr << "Error closing write handle: " << GetLastError();
54 return false;
55 }
56 writeHandle = nullptr;
57 }
58
59 return true;
60 }
61
validangle::__anon1d13e47c0111::ScopedPipe62 bool valid() const { return readHandle != nullptr || writeHandle != nullptr; }
63
initPipeangle::__anon1d13e47c0111::ScopedPipe64 bool initPipe(SECURITY_ATTRIBUTES *securityAttribs)
65 {
66 if (::CreatePipe(&readHandle, &writeHandle, securityAttribs, 0) == FALSE)
67 {
68 std::cerr << "Error creating pipe: " << GetLastError() << "\n";
69 return false;
70 }
71
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)
75 {
76 std::cerr << "Error setting handle info on pipe: " << GetLastError() << "\n";
77 return false;
78 }
79 #endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
80
81 return true;
82 }
83
84 HANDLE readHandle = nullptr;
85 HANDLE writeHandle = nullptr;
86 };
87
88 // Returns false on EOF or error.
ReadFromFile(bool blocking,HANDLE handle,std::string * out)89 void ReadFromFile(bool blocking, HANDLE handle, std::string *out)
90 {
91 char buffer[8192];
92 DWORD bytesRead = 0;
93
94 while (true)
95 {
96 if (!blocking)
97 {
98 BOOL success = ::PeekNamedPipe(handle, nullptr, 0, nullptr, &bytesRead, nullptr);
99 if (success == FALSE || bytesRead == 0)
100 return;
101 }
102
103 BOOL success = ::ReadFile(handle, buffer, sizeof(buffer), &bytesRead, nullptr);
104 if (success == FALSE || bytesRead == 0)
105 return;
106
107 out->append(buffer, bytesRead);
108 }
109
110 // unreachable.
111 }
112
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).
ReturnSuccessOnNotFound()117 bool ReturnSuccessOnNotFound()
118 {
119 const DWORD error_code = ::GetLastError();
120 return (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND);
121 }
122
123 // Job objects seems to have problems on the Chromium CI and Windows 7.
ShouldUseJobObjects()124 bool ShouldUseJobObjects()
125 {
126 #if defined(ANGLE_ENABLE_WINDOWS_UWP)
127 return false;
128 #else
129 return (::IsWindows10OrGreater());
130 #endif
131 }
132
133 class WindowsProcess : public Process
134 {
135 public:
WindowsProcess(const std::vector<const char * > & commandLineArgs,ProcessOutputCapture captureOutput)136 WindowsProcess(const std::vector<const char *> &commandLineArgs,
137 ProcessOutputCapture captureOutput)
138 {
139 mProcessInfo.hProcess = INVALID_HANDLE_VALUE;
140 mProcessInfo.hThread = INVALID_HANDLE_VALUE;
141
142 std::vector<char> commandLineString;
143 for (const char *arg : commandLineArgs)
144 {
145 if (arg)
146 {
147 if (!commandLineString.empty())
148 {
149 commandLineString.push_back(' ');
150 }
151 commandLineString.insert(commandLineString.end(), arg, arg + strlen(arg));
152 }
153 }
154 commandLineString.push_back('\0');
155
156 // Set the bInheritHandle flag so pipe handles are inherited.
157 SECURITY_ATTRIBUTES securityAttribs;
158 securityAttribs.nLength = sizeof(SECURITY_ATTRIBUTES);
159 securityAttribs.bInheritHandle = TRUE;
160 securityAttribs.lpSecurityDescriptor = nullptr;
161
162 STARTUPINFOA startInfo = {};
163
164 const bool captureStdout = captureOutput != ProcessOutputCapture::Nothing;
165 const bool captureStderr =
166 captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved ||
167 captureOutput == ProcessOutputCapture::StdoutAndStderrSeparately;
168 const bool pipeStderrToStdout =
169 captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved;
170
171 // Create pipes for stdout and stderr.
172 startInfo.cb = sizeof(STARTUPINFOA);
173 startInfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
174 if (captureStdout)
175 {
176 if (!mStdoutPipe.initPipe(&securityAttribs))
177 {
178 return;
179 }
180 startInfo.hStdOutput = mStdoutPipe.writeHandle;
181 }
182 else
183 {
184 startInfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
185 }
186
187 if (pipeStderrToStdout)
188 {
189 startInfo.hStdError = startInfo.hStdOutput;
190 }
191 else if (captureStderr)
192 {
193 if (!mStderrPipe.initPipe(&securityAttribs))
194 {
195 return;
196 }
197 startInfo.hStdError = mStderrPipe.writeHandle;
198 }
199 else
200 {
201 startInfo.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
202 }
203
204 #if !defined(ANGLE_ENABLE_WINDOWS_UWP)
205 if (captureStdout || captureStderr)
206 {
207 startInfo.dwFlags |= STARTF_USESTDHANDLES;
208 }
209
210 if (ShouldUseJobObjects())
211 {
212 // Create job object. Job objects allow us to automatically force child processes to
213 // exit if the parent process is unexpectedly killed. This should prevent ghost
214 // processes from hanging around.
215 mJobHandle = ::CreateJobObjectA(nullptr, nullptr);
216 if (mJobHandle == NULL)
217 {
218 std::cerr << "Error creating job object: " << GetLastError() << "\n";
219 return;
220 }
221
222 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limitInfo = {};
223 limitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
224 if (::SetInformationJobObject(mJobHandle, JobObjectExtendedLimitInformation, &limitInfo,
225 sizeof(limitInfo)) == FALSE)
226 {
227 std::cerr << "Error setting job information: " << GetLastError() << "\n";
228 return;
229 }
230 }
231 #endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
232
233 // Create the child process.
234 if (::CreateProcessA(nullptr, commandLineString.data(), nullptr, nullptr,
235 TRUE, // Handles are inherited.
236 0, nullptr, nullptr, &startInfo, &mProcessInfo) == FALSE)
237 {
238 std::cerr << "CreateProcessA Error code: " << GetLastError() << "\n";
239 return;
240 }
241
242 #if !defined(ANGLE_ENABLE_WINDOWS_UWP)
243 if (mJobHandle != nullptr)
244 {
245 if (::AssignProcessToJobObject(mJobHandle, mProcessInfo.hProcess) == FALSE)
246 {
247 std::cerr << "AssignProcessToJobObject failed: " << GetLastError() << "\n";
248 return;
249 }
250 }
251 #endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
252
253 // Close the write end of the pipes, so EOF can be generated when child exits.
254 if (!mStdoutPipe.closeWriteHandle() || !mStderrPipe.closeWriteHandle())
255 return;
256
257 mStarted = true;
258 mTimer.start();
259 }
260
~WindowsProcess()261 ~WindowsProcess() override
262 {
263 if (mProcessInfo.hProcess != INVALID_HANDLE_VALUE)
264 {
265 ::CloseHandle(mProcessInfo.hProcess);
266 }
267 if (mProcessInfo.hThread != INVALID_HANDLE_VALUE)
268 {
269 ::CloseHandle(mProcessInfo.hThread);
270 }
271 if (mJobHandle != nullptr)
272 {
273 ::CloseHandle(mJobHandle);
274 }
275 }
276
started()277 bool started() override { return mStarted; }
278
finish()279 bool finish() override
280 {
281 if (mStdoutPipe.valid())
282 {
283 ReadFromFile(true, mStdoutPipe.readHandle, &mStdout);
284 }
285
286 if (mStderrPipe.valid())
287 {
288 ReadFromFile(true, mStderrPipe.readHandle, &mStderr);
289 }
290
291 DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, INFINITE);
292 mTimer.stop();
293 return result == WAIT_OBJECT_0;
294 }
295
finished()296 bool finished() override
297 {
298 if (!mStarted)
299 return false;
300
301 // Pipe stdin and stdout.
302 if (mStdoutPipe.valid())
303 {
304 ReadFromFile(false, mStdoutPipe.readHandle, &mStdout);
305 }
306
307 if (mStderrPipe.valid())
308 {
309 ReadFromFile(false, mStderrPipe.readHandle, &mStderr);
310 }
311
312 DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, 0);
313 if (result == WAIT_OBJECT_0)
314 {
315 mTimer.stop();
316 return true;
317 }
318 if (result == WAIT_TIMEOUT)
319 return false;
320
321 mTimer.stop();
322 std::cerr << "Unexpected result from WaitForSingleObject: " << result
323 << ". Last error: " << ::GetLastError() << "\n";
324 return false;
325 }
326
getExitCode()327 int getExitCode() override
328 {
329 if (!mStarted)
330 return -1;
331
332 if (mProcessInfo.hProcess == INVALID_HANDLE_VALUE)
333 return -1;
334
335 DWORD exitCode = 0;
336 if (::GetExitCodeProcess(mProcessInfo.hProcess, &exitCode) == FALSE)
337 return -1;
338
339 return static_cast<int>(exitCode);
340 }
341
kill()342 bool kill() override
343 {
344 if (!mStarted)
345 return true;
346
347 HANDLE newHandle;
348 if (::DuplicateHandle(::GetCurrentProcess(), mProcessInfo.hProcess, ::GetCurrentProcess(),
349 &newHandle, PROCESS_ALL_ACCESS, false,
350 DUPLICATE_CLOSE_SOURCE) == FALSE)
351 {
352 std::cerr << "Error getting permission to terminate process: " << ::GetLastError()
353 << "\n";
354 return false;
355 }
356 mProcessInfo.hProcess = newHandle;
357
358 #if !defined(ANGLE_ENABLE_WINDOWS_UWP)
359 if (::TerminateThread(mProcessInfo.hThread, 1) == FALSE)
360 {
361 std::cerr << "TerminateThread failed: " << GetLastError() << "\n";
362 return false;
363 }
364 #endif // !defined(ANGLE_ENABLE_WINDOWS_UWP)
365
366 if (::TerminateProcess(mProcessInfo.hProcess, 1) == FALSE)
367 {
368 std::cerr << "TerminateProcess failed: " << GetLastError() << "\n";
369 return false;
370 }
371
372 mStarted = false;
373 mTimer.stop();
374 return true;
375 }
376
377 private:
378 bool mStarted = false;
379 ScopedPipe mStdoutPipe;
380 ScopedPipe mStderrPipe;
381 PROCESS_INFORMATION mProcessInfo = {};
382 HANDLE mJobHandle = nullptr;
383 };
384 } // namespace
385
Sleep(unsigned int milliseconds)386 void Sleep(unsigned int milliseconds)
387 {
388 ::Sleep(static_cast<DWORD>(milliseconds));
389 }
390
WriteDebugMessage(const char * format,...)391 void WriteDebugMessage(const char *format, ...)
392 {
393 va_list args;
394 va_start(args, format);
395 int size = vsnprintf(nullptr, 0, format, args);
396 va_end(args);
397
398 std::vector<char> buffer(size + 2);
399 va_start(args, format);
400 vsnprintf(buffer.data(), size + 1, format, args);
401 va_end(args);
402
403 OutputDebugStringA(buffer.data());
404 }
405
LaunchProcess(const std::vector<const char * > & args,ProcessOutputCapture captureOutput)406 Process *LaunchProcess(const std::vector<const char *> &args, ProcessOutputCapture captureOutput)
407 {
408 return new WindowsProcess(args, captureOutput);
409 }
410
GetTempDir(char * tempDirOut,uint32_t maxDirNameLen)411 bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
412 {
413 DWORD pathLen = ::GetTempPathA(maxDirNameLen, tempDirOut);
414 // Strip last path character if present.
415 if (pathLen > 0)
416 {
417 size_t lastChar = strlen(tempDirOut) - 1;
418 if (tempDirOut[lastChar] == '\\')
419 {
420 tempDirOut[lastChar] = 0;
421 }
422 }
423 return (pathLen < MAX_PATH && pathLen > 0);
424 }
425
CreateTemporaryFileInDir(const char * dir,char * tempFileNameOut,uint32_t maxFileNameLen)426 bool CreateTemporaryFileInDir(const char *dir, char *tempFileNameOut, uint32_t maxFileNameLen)
427 {
428 char fileName[MAX_PATH + 1];
429 if (::GetTempFileNameA(dir, "ANGLE", 0, fileName) == 0)
430 return false;
431
432 strncpy(tempFileNameOut, fileName, maxFileNameLen);
433 return true;
434 }
435
DeleteFile(const char * path)436 bool DeleteFile(const char *path)
437 {
438 if (strlen(path) >= MAX_PATH)
439 return false;
440
441 const DWORD attr = ::GetFileAttributesA(path);
442 // Report success if the file or path does not exist.
443 if (attr == INVALID_FILE_ATTRIBUTES)
444 {
445 return ReturnSuccessOnNotFound();
446 }
447
448 // Clear the read-only bit if it is set.
449 if ((attr & FILE_ATTRIBUTE_READONLY) &&
450 !::SetFileAttributesA(path, attr & ~FILE_ATTRIBUTE_READONLY))
451 {
452 // It's possible for |path| to be gone now under a race with other deleters.
453 return ReturnSuccessOnNotFound();
454 }
455
456 // We don't handle directories right now.
457 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
458 {
459 return false;
460 }
461
462 return !!::DeleteFileA(path) ? true : ReturnSuccessOnNotFound();
463 }
464
GetNativeEGLLibraryNameWithExtension()465 const char *GetNativeEGLLibraryNameWithExtension()
466 {
467 return "libEGL.dll";
468 }
469 } // namespace angle
470