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 {
~ScopedPipeangle::__anon5f3305900111::ScopedPipe49 ~ScopedPipe()
50 {
51 closeEndPoint(0);
52 closeEndPoint(1);
53 }
54
closeEndPointangle::__anon5f3305900111::ScopedPipe55 void closeEndPoint(int index)
56 {
57 if (fds[index] >= 0)
58 {
59 close(fds[index]);
60 fds[index] = -1;
61 }
62 }
63
validangle::__anon5f3305900111::ScopedPipe64 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
ReadFromFile(int fd,std::string * out)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
ReadEntireFile(int fd,std::string * out)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:
PosixProcess(const std::vector<const char * > & commandLineArgs,ProcessOutputCapture captureOutput)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
~PosixProcess()218 ~PosixProcess() override {}
219
started()220 bool started() override { return mStarted; }
221
finish()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
finished()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
getExitCode()285 int getExitCode() override { return mExitCode; }
286
kill()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:
readPipes()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
TempFileName()325 std::string TempFileName()
326 {
327 return std::string(".angle.XXXXXX");
328 }
329 } // anonymous namespace
330
Sleep(unsigned int milliseconds)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
SetLowPriorityProcess()351 void SetLowPriorityProcess()
352 {
353 #if !defined(ANGLE_PLATFORM_FUCHSIA)
354 setpriority(PRIO_PROCESS, getpid(), 10);
355 #endif
356 }
357
WriteDebugMessage(const char * format,...)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
StabilizeCPUForBenchmarking()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
GetTempDir(char * tempDirOut,uint32_t maxDirNameLen)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
CreateTemporaryFileInDir(const char * dir,char * tempFileNameOut,uint32_t maxFileNameLen)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
DeleteFile(const char * path)429 bool DeleteFile(const char *path)
430 {
431 return unlink(path) == 0;
432 }
433
LaunchProcess(const std::vector<const char * > & args,ProcessOutputCapture captureOutput)434 Process *LaunchProcess(const std::vector<const char *> &args, ProcessOutputCapture captureOutput)
435 {
436 return new PosixProcess(args, captureOutput);
437 }
438
NumberOfProcessors()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
GetNativeEGLLibraryNameWithExtension()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)
InitMetalFileAPIHooking(int argc,char ** argv)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