~mmach/netext73/webkit2gtk

« back to all changes in this revision

Viewing changes to Source/ThirdParty/ANGLE/util/windows/test_utils_win.cpp

  • Committer: mmach
  • Date: 2023-06-16 17:21:37 UTC
  • Revision ID: netbit73@gmail.com-20230616172137-2rqx6yr96ga9g3kp
1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
{
 
28
    ~ScopedPipe()
 
29
    {
 
30
        closeReadHandle();
 
31
        closeWriteHandle();
 
32
    }
 
33
    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
    }
 
47
    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
 
 
62
    bool valid() const { return readHandle != nullptr || writeHandle != nullptr; }
 
63
 
 
64
    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.
 
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).
 
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.
 
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:
 
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
 
 
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
 
 
267
    bool started() override { return mStarted; }
 
268
 
 
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
 
 
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
 
 
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
 
 
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
 
 
376
void Sleep(unsigned int milliseconds)
 
377
{
 
378
    ::Sleep(static_cast<DWORD>(milliseconds));
 
379
}
 
380
 
 
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
 
 
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
 
 
403
bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
 
404
{
 
405
    DWORD pathLen = ::GetTempPathA(maxDirNameLen, tempDirOut);
 
406
    return (pathLen < MAX_PATH && pathLen > 0);
 
407
}
 
408
 
 
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
 
 
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