Path: blob/main_old/util/posix/test_utils_posix.cpp
1693 views
//1// Copyright 2015 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//56// test_utils_posix.cpp: Implementation of OS-specific functions for Posix systems78#include "util/test_utils.h"910#include <dlfcn.h>11#include <errno.h>12#include <fcntl.h>13#include <sched.h>14#include <signal.h>15#include <sys/stat.h>16#include <sys/types.h>17#include <sys/wait.h>18#include <time.h>19#include <unistd.h>20#include <cstdarg>21#include <cstring>22#include <iostream>2324#include "common/debug.h"25#include "common/platform.h"26#include "common/system_utils.h"2728#if !defined(ANGLE_PLATFORM_FUCHSIA)29# include <sys/resource.h>30#endif3132#if defined(ANGLE_PLATFORM_MACOS)33# include <crt_externs.h>34#endif3536namespace angle37{38namespace39{4041#if defined(ANGLE_PLATFORM_MACOS)42// Argument to skip the file hooking step. Might be automatically added by InitMetalFileAPIHooking()43constexpr char kSkipFileHookingArg[] = "--skip-file-hooking";44#endif4546struct ScopedPipe47{48~ScopedPipe()49{50closeEndPoint(0);51closeEndPoint(1);52}5354void closeEndPoint(int index)55{56if (fds[index] >= 0)57{58close(fds[index]);59fds[index] = -1;60}61}6263bool valid() const { return fds[0] != -1 || fds[1] != -1; }6465int fds[2] = {66-1,67-1,68};69};7071enum class ReadResult72{73NoData,74GotData,75};7677ReadResult ReadFromFile(int fd, std::string *out)78{79constexpr size_t kBufSize = 2048;80char buffer[kBufSize];81ssize_t bytesRead = read(fd, buffer, sizeof(buffer));8283if (bytesRead < 0 && errno == EINTR)84{85return ReadResult::GotData;86}8788if (bytesRead <= 0)89{90return ReadResult::NoData;91}9293out->append(buffer, bytesRead);94return ReadResult::GotData;95}9697void ReadEntireFile(int fd, std::string *out)98{99while (ReadFromFile(fd, out) == ReadResult::GotData)100{101}102}103104class PosixProcess : public Process105{106public:107PosixProcess(const std::vector<const char *> &commandLineArgs,108ProcessOutputCapture captureOutput)109{110if (commandLineArgs.empty())111{112return;113}114115const bool captureStdout = captureOutput != ProcessOutputCapture::Nothing;116const bool captureStderr =117captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved ||118captureOutput == ProcessOutputCapture::StdoutAndStderrSeparately;119const bool pipeStderrToStdout =120captureOutput == ProcessOutputCapture::StdoutAndStderrInterleaved;121122// Create pipes for stdout and stderr.123if (captureStdout)124{125if (pipe(mStdoutPipe.fds) != 0)126{127std::cerr << "Error calling pipe: " << errno << "\n";128return;129}130if (fcntl(mStdoutPipe.fds[0], F_SETFL, O_NONBLOCK) == -1)131{132std::cerr << "Error calling fcntl: " << errno << "\n";133return;134}135}136if (captureStderr && !pipeStderrToStdout)137{138if (pipe(mStderrPipe.fds) != 0)139{140std::cerr << "Error calling pipe: " << errno << "\n";141return;142}143if (fcntl(mStderrPipe.fds[0], F_SETFL, O_NONBLOCK) == -1)144{145std::cerr << "Error calling fcntl: " << errno << "\n";146return;147}148}149150mPID = fork();151if (mPID < 0)152{153return;154}155156mStarted = true;157mTimer.start();158159if (mPID == 0)160{161// Child. Execute the application.162163// Redirect stdout and stderr to the pipe fds.164if (captureStdout)165{166if (dup2(mStdoutPipe.fds[1], STDOUT_FILENO) < 0)167{168_exit(errno);169}170mStdoutPipe.closeEndPoint(1);171}172if (pipeStderrToStdout)173{174if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)175{176_exit(errno);177}178}179else if (captureStderr)180{181if (dup2(mStderrPipe.fds[1], STDERR_FILENO) < 0)182{183_exit(errno);184}185mStderrPipe.closeEndPoint(1);186}187188// Execute the application, which doesn't return unless failed. Note: execv takes argv189// as `char * const *` for historical reasons. It is safe to const_cast it:190//191// http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html192//193// > The statement about argv[] and envp[] being constants is included to make explicit194// 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 in196// 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 these198// functions do not modify either the array of pointers or the characters to which the199// function points, but this would disallow existing correct code. Instead, only the200// array of pointers is noted as constant.201std::vector<char *> args;202for (const char *arg : commandLineArgs)203{204args.push_back(const_cast<char *>(arg));205}206args.push_back(nullptr);207208execv(commandLineArgs[0], args.data());209std::cerr << "Error calling evecv: " << errno;210_exit(errno);211}212// Parent continues execution.213mStdoutPipe.closeEndPoint(1);214mStderrPipe.closeEndPoint(1);215}216217~PosixProcess() override {}218219bool started() override { return mStarted; }220221bool finish() override222{223if (!mStarted)224{225return false;226}227228if (mFinished)229{230return true;231}232233while (!finished())234{235angle::Sleep(1);236}237238return true;239}240241bool finished() override242{243if (!mStarted)244{245return false;246}247248if (mFinished)249{250return true;251}252253int status = 0;254pid_t returnedPID = ::waitpid(mPID, &status, WNOHANG);255256if (returnedPID == -1 && errno != ECHILD)257{258std::cerr << "Error calling waitpid: " << ::strerror(errno) << "\n";259return true;260}261262if (returnedPID == mPID)263{264mFinished = true;265mTimer.stop();266readPipes();267mExitCode = WEXITSTATUS(status);268return true;269}270271if (mStdoutPipe.valid())272{273ReadEntireFile(mStdoutPipe.fds[0], &mStdout);274}275276if (mStderrPipe.valid())277{278ReadEntireFile(mStderrPipe.fds[0], &mStderr);279}280281return false;282}283284int getExitCode() override { return mExitCode; }285286bool kill() override287{288if (!mStarted)289{290return false;291}292293if (finished())294{295return true;296}297298return (::kill(mPID, SIGTERM) == 0);299}300301private:302void 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.306if (mStdoutPipe.valid())307{308ReadEntireFile(mStdoutPipe.fds[0], &mStdout);309}310if (mStderrPipe.valid())311{312ReadEntireFile(mStderrPipe.fds[0], &mStderr);313}314}315316bool mStarted = false;317bool mFinished = false;318ScopedPipe mStdoutPipe;319ScopedPipe mStderrPipe;320int mExitCode = 0;321pid_t mPID = -1;322};323324std::string TempFileName()325{326return std::string(".angle.XXXXXX");327}328} // anonymous namespace329330void Sleep(unsigned int milliseconds)331{332// On Windows Sleep(0) yields while it isn't guaranteed by Posix's sleep333// so we replicate Windows' behavior with an explicit yield.334if (milliseconds == 0)335{336sched_yield();337}338else339{340long milliseconds_long = milliseconds;341timespec sleepTime = {342.tv_sec = milliseconds_long / 1000,343.tv_nsec = (milliseconds_long % 1000) * 1000000,344};345346nanosleep(&sleepTime, nullptr);347}348}349350void SetLowPriorityProcess()351{352#if !defined(ANGLE_PLATFORM_FUCHSIA)353setpriority(PRIO_PROCESS, getpid(), 10);354#endif355}356357void WriteDebugMessage(const char *format, ...)358{359va_list vararg;360va_start(vararg, format);361vfprintf(stderr, format, vararg);362va_end(vararg);363}364365bool StabilizeCPUForBenchmarking()366{367#if !defined(ANGLE_PLATFORM_FUCHSIA)368bool success = true;369errno = 0;370setpriority(PRIO_PROCESS, getpid(), -20);371if (errno)372{373// A friendly warning in case the test was run without appropriate permission.374perror(375"Warning: setpriority failed in StabilizeCPUForBenchmarking. Process will retain "376"default priority");377success = false;378}379# if ANGLE_PLATFORM_LINUX380cpu_set_t affinity;381CPU_SET(0, &affinity);382errno = 0;383if (sched_setaffinity(getpid(), sizeof(affinity), &affinity))384{385perror(386"Warning: sched_setaffinity failed in StabilizeCPUForBenchmarking. Process will retain "387"default affinity");388success = false;389}390# else391// TODO(jmadill): Implement for non-linux. http://anglebug.com/2923392# endif393394return success;395#else // defined(ANGLE_PLATFORM_FUCHSIA)396return false;397#endif398}399400bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)401{402const char *tmp = getenv("TMPDIR");403if (tmp)404{405strncpy(tempDirOut, tmp, maxDirNameLen);406return true;407}408409#if defined(ANGLE_PLATFORM_ANDROID)410// Not used right now in the ANGLE test runner.411// return PathService::Get(DIR_CACHE, path);412return false;413#else414strncpy(tempDirOut, "/tmp", maxDirNameLen);415return true;416#endif417}418419bool CreateTemporaryFileInDir(const char *dir, char *tempFileNameOut, uint32_t maxFileNameLen)420{421std::string tempFile = TempFileName();422sprintf(tempFileNameOut, "%s/%s", dir, tempFile.c_str());423int fd = mkstemp(tempFileNameOut);424close(fd);425return fd != -1;426}427428bool DeleteFile(const char *path)429{430return unlink(path) == 0;431}432433Process *LaunchProcess(const std::vector<const char *> &args, ProcessOutputCapture captureOutput)434{435return new PosixProcess(args, captureOutput);436}437438int NumberOfProcessors()439{440// sysconf returns the number of "logical" (not "physical") processors on both441// Mac and Linux. So we get the number of max available "logical" processors.442//443// Note that the number of "currently online" processors may be fewer than the444// returned value of NumberOfProcessors(). On some platforms, the kernel may445// make some processors offline intermittently, to save power when system446// loading is low.447//448// One common use case that needs to know the processor count is to create449// optimal number of threads for optimization. It should make plan according450// to the number of "max available" processors instead of "currently online"451// ones. The kernel should be smart enough to make all processors online when452// it has sufficient number of threads waiting to run.453long res = sysconf(_SC_NPROCESSORS_CONF);454if (res == -1)455{456return 1;457}458459return static_cast<int>(res);460}461462const char *GetNativeEGLLibraryNameWithExtension()463{464#if defined(ANGLE_PLATFORM_ANDROID)465return "libEGL.so";466#elif defined(ANGLE_PLATFORM_LINUX)467return "libEGL.so.1";468#else469return "unknown_libegl";470#endif471}472473#if defined(ANGLE_PLATFORM_MACOS)474void InitMetalFileAPIHooking(int argc, char **argv)475{476if (argc < 1)477{478return;479}480481for (int i = 0; i < argc; ++i)482{483if (strncmp(argv[i], kSkipFileHookingArg, strlen(kSkipFileHookingArg)) == 0)484{485return;486}487}488489constexpr char kInjectLibVarName[] = "DYLD_INSERT_LIBRARIES";490constexpr size_t kInjectLibVarNameLen = sizeof(kInjectLibVarName) - 1;491492std::string exeDir = GetExecutableDirectory();493if (!exeDir.empty() && exeDir.back() != '/')494{495exeDir += "/";496}497498// Intercept Metal shader cache access and return as if the cache doesn't exist.499// This is to avoid slow shader cache mechanism that caused the test timeout in the past.500// In order to do that, we need to hook the file API functions by making sure501// libmetal_shader_cache_file_hooking.dylib library is loaded first before any other libraries.502std::string injectLibsVar =503std::string(kInjectLibVarName) + "=" + exeDir + "libmetal_shader_cache_file_hooking.dylib";504505char skipHookOption[sizeof(kSkipFileHookingArg)];506memcpy(skipHookOption, kSkipFileHookingArg, sizeof(kSkipFileHookingArg));507508// Construct environment variables509std::vector<char *> newEnv;510char **environ = *_NSGetEnviron();511for (int i = 0; environ[i]; ++i)512{513if (strncmp(environ[i], kInjectLibVarName, kInjectLibVarNameLen) == 0)514{515injectLibsVar += ':';516injectLibsVar += environ[i] + kInjectLibVarNameLen + 1;517}518else519{520newEnv.push_back(environ[i]);521}522}523newEnv.push_back(strdup(injectLibsVar.data()));524newEnv.push_back(nullptr);525526// Construct arguments with kSkipFileHookingArg flag to skip the hooking after re-launching.527std::vector<char *> newArgs;528newArgs.push_back(argv[0]);529newArgs.push_back(skipHookOption);530for (int i = 1; i < argc; ++i)531{532newArgs.push_back(argv[i]);533}534newArgs.push_back(nullptr);535536// Re-launch the app with file API hooked.537ASSERT(-1 != execve(argv[0], newArgs.data(), newEnv.data()));538}539#endif540541} // namespace angle542543544