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::__anon4f0b30e40111::ScopedPipe28     ~ScopedPipe()
29     {
30         closeReadHandle();
31         closeWriteHandle();
32     }
closeReadHandleangle::__anon4f0b30e40111::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::__anon4f0b30e40111::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::__anon4f0b30e40111::ScopedPipe62     bool valid() const { return readHandle != nullptr || writeHandle != nullptr; }
63 
initPipeangle::__anon4f0b30e40111::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,bool captureStdOut,bool captureStdErr)136     WindowsProcess(const std::vector<const char *> &commandLineArgs,
137                    bool captureStdOut,
138                    bool captureStdErr)
139     {
140         mProcessInfo.hProcess = INVALID_HANDLE_VALUE;
141         mProcessInfo.hThread  = INVALID_HANDLE_VALUE;
142 
143         std::vector<char> commandLineString;
144         for (const char *arg : commandLineArgs)
145         {
146             if (arg)
147             {
148                 if (!commandLineString.empty())
149                 {
150                     commandLineString.push_back(' ');
151                 }
152                 commandLineString.insert(commandLineString.end(), arg, arg + strlen(arg));
153             }
154         }
155         commandLineString.push_back('\0');
156 
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;
162 
163         STARTUPINFOA startInfo = {};
164 
165         // Create pipes for stdout and stderr.
166         startInfo.cb        = sizeof(STARTUPINFOA);
167         startInfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
168         if (captureStdOut)
169         {
170             if (!mStdoutPipe.initPipe(&securityAttribs))
171             {
172                 return;
173             }
174             startInfo.hStdOutput = mStdoutPipe.writeHandle;
175         }
176         else
177         {
178             startInfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
179         }
180 
181         if (captureStdErr)
182         {
183             if (!mStderrPipe.initPipe(&securityAttribs))
184             {
185                 return;
186             }
187             startInfo.hStdError = mStderrPipe.writeHandle;
188         }
189         else
190         {
191             startInfo.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
192         }
193 
194 #if !defined(ANGLE_ENABLE_WINDOWS_UWP)
195         if (captureStdOut || captureStdErr)
196         {
197             startInfo.dwFlags |= STARTF_USESTDHANDLES;
198         }
199 
200         if (ShouldUseJobObjects())
201         {
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)
207             {
208                 std::cerr << "Error creating job object: " << GetLastError() << "\n";
209                 return;
210             }
211 
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)
216             {
217                 std::cerr << "Error setting job information: " << GetLastError() << "\n";
218                 return;
219             }
220         }
221 #endif  // !defined(ANGLE_ENABLE_WINDOWS_UWP)
222 
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)
227         {
228             std::cerr << "CreateProcessA Error code: " << GetLastError() << "\n";
229             return;
230         }
231 
232 #if !defined(ANGLE_ENABLE_WINDOWS_UWP)
233         if (mJobHandle != nullptr)
234         {
235             if (::AssignProcessToJobObject(mJobHandle, mProcessInfo.hProcess) == FALSE)
236             {
237                 std::cerr << "AssignProcessToJobObject failed: " << GetLastError() << "\n";
238                 return;
239             }
240         }
241 #endif  // !defined(ANGLE_ENABLE_WINDOWS_UWP)
242 
243         // Close the write end of the pipes, so EOF can be generated when child exits.
244         if (!mStdoutPipe.closeWriteHandle() || !mStderrPipe.closeWriteHandle())
245             return;
246 
247         mStarted = true;
248         mTimer.start();
249     }
250 
~WindowsProcess()251     ~WindowsProcess() override
252     {
253         if (mProcessInfo.hProcess != INVALID_HANDLE_VALUE)
254         {
255             ::CloseHandle(mProcessInfo.hProcess);
256         }
257         if (mProcessInfo.hThread != INVALID_HANDLE_VALUE)
258         {
259             ::CloseHandle(mProcessInfo.hThread);
260         }
261         if (mJobHandle != nullptr)
262         {
263             ::CloseHandle(mJobHandle);
264         }
265     }
266 
started()267     bool started() override { return mStarted; }
268 
finish()269     bool finish() override
270     {
271         if (mStdoutPipe.valid())
272         {
273             ReadFromFile(true, mStdoutPipe.readHandle, &mStdout);
274         }
275 
276         if (mStderrPipe.valid())
277         {
278             ReadFromFile(true, mStderrPipe.readHandle, &mStderr);
279         }
280 
281         DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, INFINITE);
282         mTimer.stop();
283         return result == WAIT_OBJECT_0;
284     }
285 
finished()286     bool finished() override
287     {
288         if (!mStarted)
289             return false;
290 
291         // Pipe stdin and stdout.
292         if (mStdoutPipe.valid())
293         {
294             ReadFromFile(false, mStdoutPipe.readHandle, &mStdout);
295         }
296 
297         if (mStderrPipe.valid())
298         {
299             ReadFromFile(false, mStderrPipe.readHandle, &mStderr);
300         }
301 
302         DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, 0);
303         if (result == WAIT_OBJECT_0)
304         {
305             mTimer.stop();
306             return true;
307         }
308         if (result == WAIT_TIMEOUT)
309             return false;
310 
311         mTimer.stop();
312         std::cerr << "Unexpected result from WaitForSingleObject: " << result
313                   << ". Last error: " << ::GetLastError() << "\n";
314         return false;
315     }
316 
getExitCode()317     int getExitCode() override
318     {
319         if (!mStarted)
320             return -1;
321 
322         if (mProcessInfo.hProcess == INVALID_HANDLE_VALUE)
323             return -1;
324 
325         DWORD exitCode = 0;
326         if (::GetExitCodeProcess(mProcessInfo.hProcess, &exitCode) == FALSE)
327             return -1;
328 
329         return static_cast<int>(exitCode);
330     }
331 
kill()332     bool kill() override
333     {
334         if (!mStarted)
335             return true;
336 
337         HANDLE newHandle;
338         if (::DuplicateHandle(::GetCurrentProcess(), mProcessInfo.hProcess, ::GetCurrentProcess(),
339                               &newHandle, PROCESS_ALL_ACCESS, false,
340                               DUPLICATE_CLOSE_SOURCE) == FALSE)
341         {
342             std::cerr << "Error getting permission to terminate process: " << ::GetLastError()
343                       << "\n";
344             return false;
345         }
346         mProcessInfo.hProcess = newHandle;
347 
348 #if !defined(ANGLE_ENABLE_WINDOWS_UWP)
349         if (::TerminateThread(mProcessInfo.hThread, 1) == FALSE)
350         {
351             std::cerr << "TerminateThread failed: " << GetLastError() << "\n";
352             return false;
353         }
354 #endif  // !defined(ANGLE_ENABLE_WINDOWS_UWP)
355 
356         if (::TerminateProcess(mProcessInfo.hProcess, 1) == FALSE)
357         {
358             std::cerr << "TerminateProcess failed: " << GetLastError() << "\n";
359             return false;
360         }
361 
362         mStarted = false;
363         mTimer.stop();
364         return true;
365     }
366 
367   private:
368     bool mStarted = false;
369     ScopedPipe mStdoutPipe;
370     ScopedPipe mStderrPipe;
371     PROCESS_INFORMATION mProcessInfo = {};
372     HANDLE mJobHandle                = nullptr;
373 };
374 }  // namespace
375 
Sleep(unsigned int milliseconds)376 void Sleep(unsigned int milliseconds)
377 {
378     ::Sleep(static_cast<DWORD>(milliseconds));
379 }
380 
WriteDebugMessage(const char * format,...)381 void WriteDebugMessage(const char *format, ...)
382 {
383     va_list args;
384     va_start(args, format);
385     int size = vsnprintf(nullptr, 0, format, args);
386     va_end(args);
387 
388     std::vector<char> buffer(size + 2);
389     va_start(args, format);
390     vsnprintf(buffer.data(), size + 1, format, args);
391     va_end(args);
392 
393     OutputDebugStringA(buffer.data());
394 }
395 
LaunchProcess(const std::vector<const char * > & args,bool captureStdout,bool captureStderr)396 Process *LaunchProcess(const std::vector<const char *> &args,
397                        bool captureStdout,
398                        bool captureStderr)
399 {
400     return new WindowsProcess(args, captureStdout, captureStderr);
401 }
402 
GetTempDir(char * tempDirOut,uint32_t maxDirNameLen)403 bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
404 {
405     DWORD pathLen = ::GetTempPathA(maxDirNameLen, tempDirOut);
406     return (pathLen < MAX_PATH && pathLen > 0);
407 }
408 
CreateTemporaryFileInDir(const char * dir,char * tempFileNameOut,uint32_t maxFileNameLen)409 bool CreateTemporaryFileInDir(const char *dir, char *tempFileNameOut, uint32_t maxFileNameLen)
410 {
411     char fileName[MAX_PATH + 1];
412     if (::GetTempFileNameA(dir, "ANGLE", 0, fileName) == 0)
413         return false;
414 
415     strncpy(tempFileNameOut, fileName, maxFileNameLen);
416     return true;
417 }
418 
DeleteFile(const char * path)419 bool DeleteFile(const char *path)
420 {
421     if (strlen(path) >= MAX_PATH)
422         return false;
423 
424     const DWORD attr = ::GetFileAttributesA(path);
425     // Report success if the file or path does not exist.
426     if (attr == INVALID_FILE_ATTRIBUTES)
427     {
428         return ReturnSuccessOnNotFound();
429     }
430 
431     // Clear the read-only bit if it is set.
432     if ((attr & FILE_ATTRIBUTE_READONLY) &&
433         !::SetFileAttributesA(path, attr & ~FILE_ATTRIBUTE_READONLY))
434     {
435         // It's possible for |path| to be gone now under a race with other deleters.
436         return ReturnSuccessOnNotFound();
437     }
438 
439     // We don't handle directories right now.
440     if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
441     {
442         return false;
443     }
444 
445     return !!::DeleteFileA(path) ? true : ReturnSuccessOnNotFound();
446 }
447 }  // namespace angle
448