• 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::__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