Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/util/posix/test_utils_posix.cpp
1693 views
1
//
2
// Copyright 2015 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_posix.cpp: Implementation of OS-specific functions for Posix systems
8
9
#include "util/test_utils.h"
10
11
#include <dlfcn.h>
12
#include <errno.h>
13
#include <fcntl.h>
14
#include <sched.h>
15
#include <signal.h>
16
#include <sys/stat.h>
17
#include <sys/types.h>
18
#include <sys/wait.h>
19
#include <time.h>
20
#include <unistd.h>
21
#include <cstdarg>
22
#include <cstring>
23
#include <iostream>
24
25
#include "common/debug.h"
26
#include "common/platform.h"
27
#include "common/system_utils.h"
28
29
#if !defined(ANGLE_PLATFORM_FUCHSIA)
30
# include <sys/resource.h>
31
#endif
32
33
#if defined(ANGLE_PLATFORM_MACOS)
34
# include <crt_externs.h>
35
#endif
36
37
namespace angle
38
{
39
namespace
40
{
41
42
#if defined(ANGLE_PLATFORM_MACOS)
43
// Argument to skip the file hooking step. Might be automatically added by InitMetalFileAPIHooking()
44
constexpr char kSkipFileHookingArg[] = "--skip-file-hooking";
45
#endif
46
47
struct ScopedPipe
48
{
49
~ScopedPipe()
50
{
51
closeEndPoint(0);
52
closeEndPoint(1);
53
}
54
55
void closeEndPoint(int index)
56
{
57
if (fds[index] >= 0)
58
{
59
close(fds[index]);
60
fds[index] = -1;
61
}
62
}
63
64
bool valid() const { return fds[0] != -1 || fds[1] != -1; }
65
66
int fds[2] = {
67
-1,
68
-1,
69
};
70
};
71
72
enum class ReadResult
73
{
74
NoData,
75
GotData,
76
};
77
78
ReadResult ReadFromFile(int fd, std::string *out)
79
{
80
constexpr size_t kBufSize = 2048;
81
char buffer[kBufSize];
82
ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
83
84
if (bytesRead < 0 && errno == EINTR)
85
{
86
return ReadResult::GotData;
87
}
88
89
if (bytesRead <= 0)
90
{
91
return ReadResult::NoData;
92
}
93
94
out->append(buffer, bytesRead);
95
return ReadResult::GotData;
96
}
97
98
void ReadEntireFile(int fd, std::string *out)
99
{
100
while (ReadFromFile(fd, out) == ReadResult::GotData)
101
{
102
}
103
}
104
105
class PosixProcess : public Process
106
{
107
public:
108
PosixProcess(const std::vector<const char *> &commandLineArgs,
109
ProcessOutputCapture captureOutput)
110
{
111
if (commandLineArgs.empty())
112
{
113
return;
114
}
115
116
const bool captureStdout = captureOutput != ProcessOutputCapture::Nothing;
117
const bool captureStderr =
118
captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved ||
119
captureOutput == ProcessOutputCapture::StdoutAndStderrSeparately;
120
const bool pipeStderrToStdout =
121
captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved;
122
123
// Create pipes for stdout and stderr.
124
if (captureStdout)
125
{
126
if (pipe(mStdoutPipe.fds) != 0)
127
{
128
std::cerr << "Error calling pipe: " << errno << "\n";
129
return;
130
}
131
if (fcntl(mStdoutPipe.fds[0], F_SETFL, O_NONBLOCK) == -1)
132
{
133
std::cerr << "Error calling fcntl: " << errno << "\n";
134
return;
135
}
136
}
137
if (captureStderr && !pipeStderrToStdout)
138
{
139
if (pipe(mStderrPipe.fds) != 0)
140
{
141
std::cerr << "Error calling pipe: " << errno << "\n";
142
return;
143
}
144
if (fcntl(mStderrPipe.fds[0], F_SETFL, O_NONBLOCK) == -1)
145
{
146
std::cerr << "Error calling fcntl: " << errno << "\n";
147
return;
148
}
149
}
150
151
mPID = fork();
152
if (mPID < 0)
153
{
154
return;
155
}
156
157
mStarted = true;
158
mTimer.start();
159
160
if (mPID == 0)
161
{
162
// Child. Execute the application.
163
164
// Redirect stdout and stderr to the pipe fds.
165
if (captureStdout)
166
{
167
if (dup2(mStdoutPipe.fds[1], STDOUT_FILENO) < 0)
168
{
169
_exit(errno);
170
}
171
mStdoutPipe.closeEndPoint(1);
172
}
173
if (pipeStderrToStdout)
174
{
175
if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
176
{
177
_exit(errno);
178
}
179
}
180
else if (captureStderr)
181
{
182
if (dup2(mStderrPipe.fds[1], STDERR_FILENO) < 0)
183
{
184
_exit(errno);
185
}
186
mStderrPipe.closeEndPoint(1);
187
}
188
189
// Execute the application, which doesn't return unless failed. Note: execv takes argv
190
// as `char * const *` for historical reasons. It is safe to const_cast it:
191
//
192
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
193
//
194
// > The statement about argv[] and envp[] being constants is included to make explicit
195
// to future writers of language bindings that these objects are completely constant.
196
// Due to a limitation of the ISO C standard, it is not possible to state that idea in
197
// standard C. Specifying two levels of const- qualification for the argv[] and envp[]
198
// parameters for the exec functions may seem to be the natural choice, given that these
199
// functions do not modify either the array of pointers or the characters to which the
200
// function points, but this would disallow existing correct code. Instead, only the
201
// array of pointers is noted as constant.
202
std::vector<char *> args;
203
for (const char *arg : commandLineArgs)
204
{
205
args.push_back(const_cast<char *>(arg));
206
}
207
args.push_back(nullptr);
208
209
execv(commandLineArgs[0], args.data());
210
std::cerr << "Error calling evecv: " << errno;
211
_exit(errno);
212
}
213
// Parent continues execution.
214
mStdoutPipe.closeEndPoint(1);
215
mStderrPipe.closeEndPoint(1);
216
}
217
218
~PosixProcess() override {}
219
220
bool started() override { return mStarted; }
221
222
bool finish() override
223
{
224
if (!mStarted)
225
{
226
return false;
227
}
228
229
if (mFinished)
230
{
231
return true;
232
}
233
234
while (!finished())
235
{
236
angle::Sleep(1);
237
}
238
239
return true;
240
}
241
242
bool finished() override
243
{
244
if (!mStarted)
245
{
246
return false;
247
}
248
249
if (mFinished)
250
{
251
return true;
252
}
253
254
int status = 0;
255
pid_t returnedPID = ::waitpid(mPID, &status, WNOHANG);
256
257
if (returnedPID == -1 && errno != ECHILD)
258
{
259
std::cerr << "Error calling waitpid: " << ::strerror(errno) << "\n";
260
return true;
261
}
262
263
if (returnedPID == mPID)
264
{
265
mFinished = true;
266
mTimer.stop();
267
readPipes();
268
mExitCode = WEXITSTATUS(status);
269
return true;
270
}
271
272
if (mStdoutPipe.valid())
273
{
274
ReadEntireFile(mStdoutPipe.fds[0], &mStdout);
275
}
276
277
if (mStderrPipe.valid())
278
{
279
ReadEntireFile(mStderrPipe.fds[0], &mStderr);
280
}
281
282
return false;
283
}
284
285
int getExitCode() override { return mExitCode; }
286
287
bool kill() override
288
{
289
if (!mStarted)
290
{
291
return false;
292
}
293
294
if (finished())
295
{
296
return true;
297
}
298
299
return (::kill(mPID, SIGTERM) == 0);
300
}
301
302
private:
303
void readPipes()
304
{
305
// Close the write end of the pipes, so EOF can be generated when child exits.
306
// Then read back the output of the child.
307
if (mStdoutPipe.valid())
308
{
309
ReadEntireFile(mStdoutPipe.fds[0], &mStdout);
310
}
311
if (mStderrPipe.valid())
312
{
313
ReadEntireFile(mStderrPipe.fds[0], &mStderr);
314
}
315
}
316
317
bool mStarted = false;
318
bool mFinished = false;
319
ScopedPipe mStdoutPipe;
320
ScopedPipe mStderrPipe;
321
int mExitCode = 0;
322
pid_t mPID = -1;
323
};
324
325
std::string TempFileName()
326
{
327
return std::string(".angle.XXXXXX");
328
}
329
} // anonymous namespace
330
331
void Sleep(unsigned int milliseconds)
332
{
333
// On Windows Sleep(0) yields while it isn't guaranteed by Posix's sleep
334
// so we replicate Windows' behavior with an explicit yield.
335
if (milliseconds == 0)
336
{
337
sched_yield();
338
}
339
else
340
{
341
long milliseconds_long = milliseconds;
342
timespec sleepTime = {
343
.tv_sec = milliseconds_long / 1000,
344
.tv_nsec = (milliseconds_long % 1000) * 1000000,
345
};
346
347
nanosleep(&sleepTime, nullptr);
348
}
349
}
350
351
void SetLowPriorityProcess()
352
{
353
#if !defined(ANGLE_PLATFORM_FUCHSIA)
354
setpriority(PRIO_PROCESS, getpid(), 10);
355
#endif
356
}
357
358
void WriteDebugMessage(const char *format, ...)
359
{
360
va_list vararg;
361
va_start(vararg, format);
362
vfprintf(stderr, format, vararg);
363
va_end(vararg);
364
}
365
366
bool StabilizeCPUForBenchmarking()
367
{
368
#if !defined(ANGLE_PLATFORM_FUCHSIA)
369
bool success = true;
370
errno = 0;
371
setpriority(PRIO_PROCESS, getpid(), -20);
372
if (errno)
373
{
374
// A friendly warning in case the test was run without appropriate permission.
375
perror(
376
"Warning: setpriority failed in StabilizeCPUForBenchmarking. Process will retain "
377
"default priority");
378
success = false;
379
}
380
# if ANGLE_PLATFORM_LINUX
381
cpu_set_t affinity;
382
CPU_SET(0, &affinity);
383
errno = 0;
384
if (sched_setaffinity(getpid(), sizeof(affinity), &affinity))
385
{
386
perror(
387
"Warning: sched_setaffinity failed in StabilizeCPUForBenchmarking. Process will retain "
388
"default affinity");
389
success = false;
390
}
391
# else
392
// TODO(jmadill): Implement for non-linux. http://anglebug.com/2923
393
# endif
394
395
return success;
396
#else // defined(ANGLE_PLATFORM_FUCHSIA)
397
return false;
398
#endif
399
}
400
401
bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
402
{
403
const char *tmp = getenv("TMPDIR");
404
if (tmp)
405
{
406
strncpy(tempDirOut, tmp, maxDirNameLen);
407
return true;
408
}
409
410
#if defined(ANGLE_PLATFORM_ANDROID)
411
// Not used right now in the ANGLE test runner.
412
// return PathService::Get(DIR_CACHE, path);
413
return false;
414
#else
415
strncpy(tempDirOut, "/tmp", maxDirNameLen);
416
return true;
417
#endif
418
}
419
420
bool CreateTemporaryFileInDir(const char *dir, char *tempFileNameOut, uint32_t maxFileNameLen)
421
{
422
std::string tempFile = TempFileName();
423
sprintf(tempFileNameOut, "%s/%s", dir, tempFile.c_str());
424
int fd = mkstemp(tempFileNameOut);
425
close(fd);
426
return fd != -1;
427
}
428
429
bool DeleteFile(const char *path)
430
{
431
return unlink(path) == 0;
432
}
433
434
Process *LaunchProcess(const std::vector<const char *> &args, ProcessOutputCapture captureOutput)
435
{
436
return new PosixProcess(args, captureOutput);
437
}
438
439
int NumberOfProcessors()
440
{
441
// sysconf returns the number of "logical" (not "physical") processors on both
442
// Mac and Linux. So we get the number of max available "logical" processors.
443
//
444
// Note that the number of "currently online" processors may be fewer than the
445
// returned value of NumberOfProcessors(). On some platforms, the kernel may
446
// make some processors offline intermittently, to save power when system
447
// loading is low.
448
//
449
// One common use case that needs to know the processor count is to create
450
// optimal number of threads for optimization. It should make plan according
451
// to the number of "max available" processors instead of "currently online"
452
// ones. The kernel should be smart enough to make all processors online when
453
// it has sufficient number of threads waiting to run.
454
long res = sysconf(_SC_NPROCESSORS_CONF);
455
if (res == -1)
456
{
457
return 1;
458
}
459
460
return static_cast<int>(res);
461
}
462
463
const char *GetNativeEGLLibraryNameWithExtension()
464
{
465
#if defined(ANGLE_PLATFORM_ANDROID)
466
return "libEGL.so";
467
#elif defined(ANGLE_PLATFORM_LINUX)
468
return "libEGL.so.1";
469
#else
470
return "unknown_libegl";
471
#endif
472
}
473
474
#if defined(ANGLE_PLATFORM_MACOS)
475
void InitMetalFileAPIHooking(int argc, char **argv)
476
{
477
if (argc < 1)
478
{
479
return;
480
}
481
482
for (int i = 0; i < argc; ++i)
483
{
484
if (strncmp(argv[i], kSkipFileHookingArg, strlen(kSkipFileHookingArg)) == 0)
485
{
486
return;
487
}
488
}
489
490
constexpr char kInjectLibVarName[] = "DYLD_INSERT_LIBRARIES";
491
constexpr size_t kInjectLibVarNameLen = sizeof(kInjectLibVarName) - 1;
492
493
std::string exeDir = GetExecutableDirectory();
494
if (!exeDir.empty() && exeDir.back() != '/')
495
{
496
exeDir += "/";
497
}
498
499
// Intercept Metal shader cache access and return as if the cache doesn't exist.
500
// This is to avoid slow shader cache mechanism that caused the test timeout in the past.
501
// In order to do that, we need to hook the file API functions by making sure
502
// libmetal_shader_cache_file_hooking.dylib library is loaded first before any other libraries.
503
std::string injectLibsVar =
504
std::string(kInjectLibVarName) + "=" + exeDir + "libmetal_shader_cache_file_hooking.dylib";
505
506
char skipHookOption[sizeof(kSkipFileHookingArg)];
507
memcpy(skipHookOption, kSkipFileHookingArg, sizeof(kSkipFileHookingArg));
508
509
// Construct environment variables
510
std::vector<char *> newEnv;
511
char **environ = *_NSGetEnviron();
512
for (int i = 0; environ[i]; ++i)
513
{
514
if (strncmp(environ[i], kInjectLibVarName, kInjectLibVarNameLen) == 0)
515
{
516
injectLibsVar += ':';
517
injectLibsVar += environ[i] + kInjectLibVarNameLen + 1;
518
}
519
else
520
{
521
newEnv.push_back(environ[i]);
522
}
523
}
524
newEnv.push_back(strdup(injectLibsVar.data()));
525
newEnv.push_back(nullptr);
526
527
// Construct arguments with kSkipFileHookingArg flag to skip the hooking after re-launching.
528
std::vector<char *> newArgs;
529
newArgs.push_back(argv[0]);
530
newArgs.push_back(skipHookOption);
531
for (int i = 1; i < argc; ++i)
532
{
533
newArgs.push_back(argv[i]);
534
}
535
newArgs.push_back(nullptr);
536
537
// Re-launch the app with file API hooked.
538
ASSERT(-1 != execve(argv[0], newArgs.data(), newEnv.data()));
539
}
540
#endif
541
542
} // namespace angle
543
544