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