• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::__anonf9fd97ae0111::ScopedPipe49     ~ScopedPipe()
50     {
51         closeEndPoint(0);
52         closeEndPoint(1);
53     }
54 
closeEndPointangle::__anonf9fd97ae0111::ScopedPipe55     void closeEndPoint(int index)
56     {
57         if (fds[index] >= 0)
58         {
59             close(fds[index]);
60             fds[index] = -1;
61         }
62     }
63 
validangle::__anonf9fd97ae0111::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 class PosixProcess : public Process
105 {
106   public:
PosixProcess(const std::vector<const char * > & commandLineArgs,ProcessOutputCapture captureOutput)107     PosixProcess(const std::vector<const char *> &commandLineArgs,
108                  ProcessOutputCapture captureOutput)
109     {
110         if (commandLineArgs.empty())
111         {
112             return;
113         }
114 
115         const bool captureStdout = captureOutput != ProcessOutputCapture::Nothing;
116         const bool captureStderr =
117             captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved ||
118             captureOutput == ProcessOutputCapture::StdoutAndStderrSeparately;
119         const bool pipeStderrToStdout =
120             captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved;
121 
122         // Create pipes for stdout and stderr.
123         if (captureStdout)
124         {
125             if (pipe(mStdoutPipe.fds) != 0)
126             {
127                 std::cerr << "Error calling pipe: " << errno << "\n";
128                 return;
129             }
130             if (fcntl(mStdoutPipe.fds[0], F_SETFL, O_NONBLOCK) == -1)
131             {
132                 std::cerr << "Error calling fcntl: " << errno << "\n";
133                 return;
134             }
135         }
136         if (captureStderr && !pipeStderrToStdout)
137         {
138             if (pipe(mStderrPipe.fds) != 0)
139             {
140                 std::cerr << "Error calling pipe: " << errno << "\n";
141                 return;
142             }
143             if (fcntl(mStderrPipe.fds[0], F_SETFL, O_NONBLOCK) == -1)
144             {
145                 std::cerr << "Error calling fcntl: " << errno << "\n";
146                 return;
147             }
148         }
149 
150         mPID = fork();
151         if (mPID < 0)
152         {
153             return;
154         }
155 
156         mStarted = true;
157         mTimer.start();
158 
159         if (mPID == 0)
160         {
161             // Child.  Execute the application.
162 
163             // Redirect stdout and stderr to the pipe fds.
164             if (captureStdout)
165             {
166                 if (dup2(mStdoutPipe.fds[1], STDOUT_FILENO) < 0)
167                 {
168                     _exit(errno);
169                 }
170                 mStdoutPipe.closeEndPoint(1);
171             }
172             if (pipeStderrToStdout)
173             {
174                 if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
175                 {
176                     _exit(errno);
177                 }
178             }
179             else if (captureStderr)
180             {
181                 if (dup2(mStderrPipe.fds[1], STDERR_FILENO) < 0)
182                 {
183                     _exit(errno);
184                 }
185                 mStderrPipe.closeEndPoint(1);
186             }
187 
188             // Execute the application, which doesn't return unless failed.  Note: execv takes argv
189             // as `char * const *` for historical reasons.  It is safe to const_cast it:
190             //
191             // http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
192             //
193             // > The statement about argv[] and envp[] being constants is included to make explicit
194             // to future writers of language bindings that these objects are completely constant.
195             // Due to a limitation of the ISO C standard, it is not possible to state that idea in
196             // standard C. Specifying two levels of const- qualification for the argv[] and envp[]
197             // parameters for the exec functions may seem to be the natural choice, given that these
198             // functions do not modify either the array of pointers or the characters to which the
199             // function points, but this would disallow existing correct code. Instead, only the
200             // array of pointers is noted as constant.
201             std::vector<char *> args;
202             for (const char *arg : commandLineArgs)
203             {
204                 args.push_back(const_cast<char *>(arg));
205             }
206             args.push_back(nullptr);
207 
208             execv(commandLineArgs[0], args.data());
209             std::cerr << "Error calling evecv: " << errno;
210             _exit(errno);
211         }
212         // Parent continues execution.
213         mStdoutPipe.closeEndPoint(1);
214         mStderrPipe.closeEndPoint(1);
215     }
216 
~PosixProcess()217     ~PosixProcess() override {}
218 
started()219     bool started() override { return mStarted; }
220 
finish()221     bool finish() override
222     {
223         if (!mStarted)
224         {
225             return false;
226         }
227 
228         if (mFinished)
229         {
230             return true;
231         }
232 
233         while (!finished())
234         {
235             angle::Sleep(1);
236         }
237 
238         return true;
239     }
240 
finished()241     bool finished() override
242     {
243         if (!mStarted)
244         {
245             return false;
246         }
247 
248         if (mFinished)
249         {
250             return true;
251         }
252 
253         int status        = 0;
254         pid_t returnedPID = ::waitpid(mPID, &status, WNOHANG);
255 
256         if (returnedPID == -1 && errno != ECHILD)
257         {
258             std::cerr << "Error calling waitpid: " << ::strerror(errno) << "\n";
259             return true;
260         }
261 
262         if (returnedPID == mPID)
263         {
264             mFinished = true;
265             mTimer.stop();
266             readPipes();
267             mExitCode = WEXITSTATUS(status);
268             return true;
269         }
270 
271         if (mStdoutPipe.valid())
272         {
273             ReadEntireFile(mStdoutPipe.fds[0], &mStdout);
274         }
275 
276         if (mStderrPipe.valid())
277         {
278             ReadEntireFile(mStderrPipe.fds[0], &mStderr);
279         }
280 
281         return false;
282     }
283 
getExitCode()284     int getExitCode() override { return mExitCode; }
285 
kill()286     bool kill() override
287     {
288         if (!mStarted)
289         {
290             return false;
291         }
292 
293         if (finished())
294         {
295             return true;
296         }
297 
298         return (::kill(mPID, SIGTERM) == 0);
299     }
300 
301   private:
readPipes()302     void readPipes()
303     {
304         // Close the write end of the pipes, so EOF can be generated when child exits.
305         // Then read back the output of the child.
306         if (mStdoutPipe.valid())
307         {
308             ReadEntireFile(mStdoutPipe.fds[0], &mStdout);
309         }
310         if (mStderrPipe.valid())
311         {
312             ReadEntireFile(mStderrPipe.fds[0], &mStderr);
313         }
314     }
315 
316     bool mStarted  = false;
317     bool mFinished = false;
318     ScopedPipe mStdoutPipe;
319     ScopedPipe mStderrPipe;
320     int mExitCode = 0;
321     pid_t mPID    = -1;
322 };
323 }  // anonymous namespace
324 
Sleep(unsigned int milliseconds)325 void Sleep(unsigned int milliseconds)
326 {
327     // On Windows Sleep(0) yields while it isn't guaranteed by Posix's sleep
328     // so we replicate Windows' behavior with an explicit yield.
329     if (milliseconds == 0)
330     {
331         sched_yield();
332     }
333     else
334     {
335         long milliseconds_long = milliseconds;
336         timespec sleepTime     = {
337                 .tv_sec  = milliseconds_long / 1000,
338                 .tv_nsec = (milliseconds_long % 1000) * 1000000,
339         };
340 
341         nanosleep(&sleepTime, nullptr);
342     }
343 }
344 
SetLowPriorityProcess()345 void SetLowPriorityProcess()
346 {
347 #if !defined(ANGLE_PLATFORM_FUCHSIA)
348     setpriority(PRIO_PROCESS, getpid(), 10);
349 #endif
350 }
351 
WriteDebugMessage(const char * format,...)352 void WriteDebugMessage(const char *format, ...)
353 {
354     va_list vararg;
355     va_start(vararg, format);
356     vfprintf(stderr, format, vararg);
357     va_end(vararg);
358 }
359 
StabilizeCPUForBenchmarking()360 bool StabilizeCPUForBenchmarking()
361 {
362 #if !defined(ANGLE_PLATFORM_FUCHSIA)
363     bool success = true;
364     errno        = 0;
365     setpriority(PRIO_PROCESS, getpid(), -20);
366     if (errno)
367     {
368         // A friendly warning in case the test was run without appropriate permission.
369         perror(
370             "Warning: setpriority failed in StabilizeCPUForBenchmarking. Process will retain "
371             "default priority");
372         success = false;
373     }
374 #    if ANGLE_PLATFORM_LINUX
375     cpu_set_t affinity;
376     CPU_SET(0, &affinity);
377     errno = 0;
378     if (sched_setaffinity(getpid(), sizeof(affinity), &affinity))
379     {
380         perror(
381             "Warning: sched_setaffinity failed in StabilizeCPUForBenchmarking. Process will retain "
382             "default affinity");
383         success = false;
384     }
385 #    else
386     // TODO(jmadill): Implement for non-linux. http://anglebug.com/2923
387 #    endif
388 
389     return success;
390 #else  // defined(ANGLE_PLATFORM_FUCHSIA)
391     return false;
392 #endif
393 }
394 
DeleteSystemFile(const char * path)395 bool DeleteSystemFile(const char *path)
396 {
397     return unlink(path) == 0;
398 }
399 
LaunchProcess(const std::vector<const char * > & args,ProcessOutputCapture captureOutput)400 Process *LaunchProcess(const std::vector<const char *> &args, ProcessOutputCapture captureOutput)
401 {
402     return new PosixProcess(args, captureOutput);
403 }
404 
NumberOfProcessors()405 int NumberOfProcessors()
406 {
407     // sysconf returns the number of "logical" (not "physical") processors on both
408     // Mac and Linux.  So we get the number of max available "logical" processors.
409     //
410     // Note that the number of "currently online" processors may be fewer than the
411     // returned value of NumberOfProcessors(). On some platforms, the kernel may
412     // make some processors offline intermittently, to save power when system
413     // loading is low.
414     //
415     // One common use case that needs to know the processor count is to create
416     // optimal number of threads for optimization. It should make plan according
417     // to the number of "max available" processors instead of "currently online"
418     // ones. The kernel should be smart enough to make all processors online when
419     // it has sufficient number of threads waiting to run.
420     long res = sysconf(_SC_NPROCESSORS_CONF);
421     if (res == -1)
422     {
423         return 1;
424     }
425 
426     return static_cast<int>(res);
427 }
428 
GetNativeEGLLibraryNameWithExtension()429 const char *GetNativeEGLLibraryNameWithExtension()
430 {
431 #if defined(ANGLE_PLATFORM_ANDROID)
432     return "libEGL.so";
433 #elif defined(ANGLE_PLATFORM_LINUX)
434     return "libEGL.so.1";
435 #else
436     return "unknown_libegl";
437 #endif
438 }
439 
440 #if defined(ANGLE_PLATFORM_MACOS)
InitMetalFileAPIHooking(int argc,char ** argv)441 void InitMetalFileAPIHooking(int argc, char **argv)
442 {
443     if (argc < 1)
444     {
445         return;
446     }
447 
448     for (int i = 0; i < argc; ++i)
449     {
450         if (strncmp(argv[i], kSkipFileHookingArg, strlen(kSkipFileHookingArg)) == 0)
451         {
452             return;
453         }
454     }
455 
456     constexpr char kInjectLibVarName[]    = "DYLD_INSERT_LIBRARIES";
457     constexpr size_t kInjectLibVarNameLen = sizeof(kInjectLibVarName) - 1;
458 
459     std::string exeDir = GetExecutableDirectory();
460     if (!exeDir.empty() && exeDir.back() != '/')
461     {
462         exeDir += "/";
463     }
464 
465     // Intercept Metal shader cache access and return as if the cache doesn't exist.
466     // This is to avoid slow shader cache mechanism that caused the test timeout in the past.
467     // In order to do that, we need to hook the file API functions by making sure
468     // libmetal_shader_cache_file_hooking.dylib library is loaded first before any other libraries.
469     std::string injectLibsVar =
470         std::string(kInjectLibVarName) + "=" + exeDir + "libmetal_shader_cache_file_hooking.dylib";
471 
472     char skipHookOption[sizeof(kSkipFileHookingArg)];
473     memcpy(skipHookOption, kSkipFileHookingArg, sizeof(kSkipFileHookingArg));
474 
475     // Construct environment variables
476     std::vector<char *> newEnv;
477     char **environ = *_NSGetEnviron();
478     for (int i = 0; environ[i]; ++i)
479     {
480         if (strncmp(environ[i], kInjectLibVarName, kInjectLibVarNameLen) == 0)
481         {
482             injectLibsVar += ':';
483             injectLibsVar += environ[i] + kInjectLibVarNameLen + 1;
484         }
485         else
486         {
487             newEnv.push_back(environ[i]);
488         }
489     }
490     newEnv.push_back(strdup(injectLibsVar.data()));
491     newEnv.push_back(nullptr);
492 
493     // Construct arguments with kSkipFileHookingArg flag to skip the hooking after re-launching.
494     std::vector<char *> newArgs;
495     newArgs.push_back(argv[0]);
496     newArgs.push_back(skipHookOption);
497     for (int i = 1; i < argc; ++i)
498     {
499         newArgs.push_back(argv[i]);
500     }
501     newArgs.push_back(nullptr);
502 
503     // Re-launch the app with file API hooked.
504     ASSERT(-1 != execve(argv[0], newArgs.data(), newEnv.data()));
505 }
506 #endif
507 
508 }  // namespace angle
509