• 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 
28 #if !defined(ANGLE_PLATFORM_FUCHSIA)
29 #    include <sys/resource.h>
30 #endif
31 
32 namespace angle
33 {
34 namespace
35 {
36 struct ScopedPipe
37 {
~ScopedPipeangle::__anonc61450090111::ScopedPipe38     ~ScopedPipe()
39     {
40         closeEndPoint(0);
41         closeEndPoint(1);
42     }
43 
closeEndPointangle::__anonc61450090111::ScopedPipe44     void closeEndPoint(int index)
45     {
46         if (fds[index] >= 0)
47         {
48             close(fds[index]);
49             fds[index] = -1;
50         }
51     }
52 
validangle::__anonc61450090111::ScopedPipe53     bool valid() const { return fds[0] != -1 || fds[1] != -1; }
54 
55     int fds[2] = {
56         -1,
57         -1,
58     };
59 };
60 
ReadFromFile(int fd,std::string * out)61 bool ReadFromFile(int fd, std::string *out)
62 {
63     char buffer[256];
64     ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
65 
66     // If interrupted, retry.
67     if (bytesRead < 0 && errno == EINTR)
68     {
69         return true;
70     }
71 
72     // If failed, or nothing to read, we are done.
73     if (bytesRead <= 0)
74     {
75         return false;
76     }
77 
78     out->append(buffer, bytesRead);
79     return true;
80 }
81 
ReadEntireFile(int fd,std::string * out)82 void ReadEntireFile(int fd, std::string *out)
83 {
84     while (true)
85     {
86         if (!ReadFromFile(fd, out))
87             break;
88     }
89 }
90 
91 class PosixProcess : public Process
92 {
93   public:
PosixProcess(const std::vector<const char * > & commandLineArgs,bool captureStdOut,bool captureStdErr)94     PosixProcess(const std::vector<const char *> &commandLineArgs,
95                  bool captureStdOut,
96                  bool captureStdErr)
97     {
98         if (commandLineArgs.empty())
99         {
100             return;
101         }
102 
103         // Create pipes for stdout and stderr.
104         if (captureStdOut)
105         {
106             if (pipe(mStdoutPipe.fds) != 0)
107             {
108                 std::cerr << "Error calling pipe: " << errno << "\n";
109                 return;
110             }
111             if (fcntl(mStdoutPipe.fds[0], F_SETFL, O_NONBLOCK) == -1)
112             {
113                 std::cerr << "Error calling fcntl: " << errno << "\n";
114                 return;
115             }
116         }
117         if (captureStdErr)
118         {
119             if (pipe(mStderrPipe.fds) != 0)
120             {
121                 std::cerr << "Error calling pipe: " << errno << "\n";
122                 return;
123             }
124             if (fcntl(mStderrPipe.fds[0], F_SETFL, O_NONBLOCK) == -1)
125             {
126                 std::cerr << "Error calling fcntl: " << errno << "\n";
127                 return;
128             }
129         }
130 
131         mPID = fork();
132         if (mPID < 0)
133         {
134             return;
135         }
136 
137         mStarted = true;
138         mTimer.start();
139 
140         if (mPID == 0)
141         {
142             // Child.  Execute the application.
143 
144             // Redirect stdout and stderr to the pipe fds.
145             if (captureStdOut)
146             {
147                 if (dup2(mStdoutPipe.fds[1], STDOUT_FILENO) < 0)
148                 {
149                     _exit(errno);
150                 }
151                 mStdoutPipe.closeEndPoint(1);
152             }
153             if (captureStdErr)
154             {
155                 if (dup2(mStderrPipe.fds[1], STDERR_FILENO) < 0)
156                 {
157                     _exit(errno);
158                 }
159                 mStderrPipe.closeEndPoint(1);
160             }
161 
162             // Execute the application, which doesn't return unless failed.  Note: execv takes argv
163             // as `char * const *` for historical reasons.  It is safe to const_cast it:
164             //
165             // http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
166             //
167             // > The statement about argv[] and envp[] being constants is included to make explicit
168             // to future writers of language bindings that these objects are completely constant.
169             // Due to a limitation of the ISO C standard, it is not possible to state that idea in
170             // standard C. Specifying two levels of const- qualification for the argv[] and envp[]
171             // parameters for the exec functions may seem to be the natural choice, given that these
172             // functions do not modify either the array of pointers or the characters to which the
173             // function points, but this would disallow existing correct code. Instead, only the
174             // array of pointers is noted as constant.
175             std::vector<char *> args;
176             for (const char *arg : commandLineArgs)
177             {
178                 args.push_back(const_cast<char *>(arg));
179             }
180             args.push_back(nullptr);
181 
182             execv(commandLineArgs[0], args.data());
183             std::cerr << "Error calling evecv: " << errno;
184             _exit(errno);
185         }
186         // Parent continues execution.
187         mStdoutPipe.closeEndPoint(1);
188         mStderrPipe.closeEndPoint(1);
189     }
190 
~PosixProcess()191     ~PosixProcess() override {}
192 
started()193     bool started() override { return mStarted; }
194 
finish()195     bool finish() override
196     {
197         if (!mStarted)
198         {
199             return false;
200         }
201 
202         if (mFinished)
203         {
204             return true;
205         }
206 
207         while (!finished())
208         {
209             angle::Sleep(1);
210         }
211 
212         return true;
213     }
214 
finished()215     bool finished() override
216     {
217         if (!mStarted)
218         {
219             return false;
220         }
221 
222         if (mFinished)
223         {
224             return true;
225         }
226 
227         int status        = 0;
228         pid_t returnedPID = ::waitpid(mPID, &status, WNOHANG);
229 
230         if (returnedPID == -1 && errno != ECHILD)
231         {
232             std::cerr << "Error calling waitpid: " << ::strerror(errno) << "\n";
233             return true;
234         }
235 
236         if (returnedPID == mPID)
237         {
238             mFinished = true;
239             mTimer.stop();
240             readPipes();
241             mExitCode = WEXITSTATUS(status);
242             return true;
243         }
244 
245         if (mStdoutPipe.valid())
246         {
247             ReadFromFile(mStdoutPipe.fds[0], &mStdout);
248         }
249 
250         if (mStderrPipe.valid())
251         {
252             ReadFromFile(mStderrPipe.fds[0], &mStderr);
253         }
254 
255         return false;
256     }
257 
getExitCode()258     int getExitCode() override { return mExitCode; }
259 
kill()260     bool kill() override
261     {
262         if (!mStarted)
263         {
264             return false;
265         }
266 
267         if (finished())
268         {
269             return true;
270         }
271 
272         return (::kill(mPID, SIGTERM) == 0);
273     }
274 
275   private:
readPipes()276     void readPipes()
277     {
278         // Close the write end of the pipes, so EOF can be generated when child exits.
279         // Then read back the output of the child.
280         if (mStdoutPipe.valid())
281         {
282             ReadEntireFile(mStdoutPipe.fds[0], &mStdout);
283         }
284         if (mStderrPipe.valid())
285         {
286             ReadEntireFile(mStderrPipe.fds[0], &mStderr);
287         }
288     }
289 
290     bool mStarted  = false;
291     bool mFinished = false;
292     ScopedPipe mStdoutPipe;
293     ScopedPipe mStderrPipe;
294     int mExitCode = 0;
295     pid_t mPID    = -1;
296 };
297 
TempFileName()298 std::string TempFileName()
299 {
300     return std::string(".angle.XXXXXX");
301 }
302 }  // anonymous namespace
303 
Sleep(unsigned int milliseconds)304 void Sleep(unsigned int milliseconds)
305 {
306     // On Windows Sleep(0) yields while it isn't guaranteed by Posix's sleep
307     // so we replicate Windows' behavior with an explicit yield.
308     if (milliseconds == 0)
309     {
310         sched_yield();
311     }
312     else
313     {
314         timespec sleepTime = {
315             .tv_sec  = milliseconds / 1000,
316             .tv_nsec = (milliseconds % 1000) * 1000000,
317         };
318 
319         nanosleep(&sleepTime, nullptr);
320     }
321 }
322 
SetLowPriorityProcess()323 void SetLowPriorityProcess()
324 {
325 #if !defined(ANGLE_PLATFORM_FUCHSIA)
326     setpriority(PRIO_PROCESS, getpid(), 10);
327 #endif
328 }
329 
WriteDebugMessage(const char * format,...)330 void WriteDebugMessage(const char *format, ...)
331 {
332     va_list vararg;
333     va_start(vararg, format);
334     vfprintf(stderr, format, vararg);
335     va_end(vararg);
336 }
337 
StabilizeCPUForBenchmarking()338 bool StabilizeCPUForBenchmarking()
339 {
340 #if !defined(ANGLE_PLATFORM_FUCHSIA)
341     bool success = true;
342     errno        = 0;
343     setpriority(PRIO_PROCESS, getpid(), -20);
344     if (errno)
345     {
346         // A friendly warning in case the test was run without appropriate permission.
347         perror(
348             "Warning: setpriority failed in StabilizeCPUForBenchmarking. Process will retain "
349             "default priority");
350         success = false;
351     }
352 #    if ANGLE_PLATFORM_LINUX
353     cpu_set_t affinity;
354     CPU_SET(0, &affinity);
355     errno = 0;
356     if (sched_setaffinity(getpid(), sizeof(affinity), &affinity))
357     {
358         perror(
359             "Warning: sched_setaffinity failed in StabilizeCPUForBenchmarking. Process will retain "
360             "default affinity");
361         success = false;
362     }
363 #    else
364     // TODO(jmadill): Implement for non-linux. http://anglebug.com/2923
365 #    endif
366 
367     return success;
368 #else  // defined(ANGLE_PLATFORM_FUCHSIA)
369     return false;
370 #endif
371 }
372 
GetTempDir(char * tempDirOut,uint32_t maxDirNameLen)373 bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
374 {
375     const char *tmp = getenv("TMPDIR");
376     if (tmp)
377     {
378         strncpy(tempDirOut, tmp, maxDirNameLen);
379         return true;
380     }
381 
382 #if defined(ANGLE_PLATFORM_ANDROID)
383     // TODO(jmadill): Android support. http://anglebug.com/3162
384     // return PathService::Get(DIR_CACHE, path);
385     return false;
386 #else
387     strncpy(tempDirOut, "/tmp", maxDirNameLen);
388     return true;
389 #endif
390 }
391 
CreateTemporaryFileInDir(const char * dir,char * tempFileNameOut,uint32_t maxFileNameLen)392 bool CreateTemporaryFileInDir(const char *dir, char *tempFileNameOut, uint32_t maxFileNameLen)
393 {
394     std::string tempFile = TempFileName();
395     sprintf(tempFileNameOut, "%s/%s", dir, tempFile.c_str());
396     int fd = mkstemp(tempFileNameOut);
397     close(fd);
398     return fd != -1;
399 }
400 
DeleteFile(const char * path)401 bool DeleteFile(const char *path)
402 {
403     return unlink(path) == 0;
404 }
405 
LaunchProcess(const std::vector<const char * > & args,bool captureStdout,bool captureStderr)406 Process *LaunchProcess(const std::vector<const char *> &args,
407                        bool captureStdout,
408                        bool captureStderr)
409 {
410     return new PosixProcess(args, captureStdout, captureStderr);
411 }
412 
NumberOfProcessors()413 int NumberOfProcessors()
414 {
415     // sysconf returns the number of "logical" (not "physical") processors on both
416     // Mac and Linux.  So we get the number of max available "logical" processors.
417     //
418     // Note that the number of "currently online" processors may be fewer than the
419     // returned value of NumberOfProcessors(). On some platforms, the kernel may
420     // make some processors offline intermittently, to save power when system
421     // loading is low.
422     //
423     // One common use case that needs to know the processor count is to create
424     // optimal number of threads for optimization. It should make plan according
425     // to the number of "max available" processors instead of "currently online"
426     // ones. The kernel should be smart enough to make all processors online when
427     // it has sufficient number of threads waiting to run.
428     long res = sysconf(_SC_NPROCESSORS_CONF);
429     if (res == -1)
430     {
431         return 1;
432     }
433 
434     return static_cast<int>(res);
435 }
436 }  // namespace angle
437