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