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