Commit fe14b2e5 by Yuly Novikov Committed by Commit Bot

Revert "Add system util to execute app and retrieve its output"

This reverts commit c63d9552. Reason for revert: Test fails on Win7 Original change's description: > Add system util to execute app and retrieve its output > > This will be useful to run external applications, such as benchmarks, > and process their output. > > Bug: angleproject:3125 > Change-Id: Ic13c69f2e034f4b47498fb2f299c62423c355c4a > Reviewed-on: https://chromium-review.googlesource.com/c/1452534 > Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> > Reviewed-by: Jamie Madill <jmadill@google.com> TBR=jmadill@google.com,syoussefi@chromium.org,jmadill@chromium.org # Not skipping CQ checks because original CL landed > 1 day ago. Bug: angleproject:3125, angleproject:3168 Change-Id: I389c0ee639c7c0176c87abe92fc88f99d1b20cc4 Reviewed-on: https://chromium-review.googlesource.com/c/1487104Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org> Commit-Queue: Yuly Novikov <ynovikov@chromium.org>
parent 94171749
...@@ -25,15 +25,6 @@ std::string GetEnvironmentVar(const char *variableName); ...@@ -25,15 +25,6 @@ std::string GetEnvironmentVar(const char *variableName);
const char *GetPathSeparator(); const char *GetPathSeparator();
bool PrependPathToEnvironmentVar(const char *variableName, const char *path); bool PrependPathToEnvironmentVar(const char *variableName, const char *path);
// Run an application and get the output. Gets a nullptr-terminated set of args to execute the
// application with, and returns the stdout and stderr outputs as well as the exit code.
//
// Returns false if it fails to actually execute the application.
bool RunApp(const std::vector<const char *> &args,
std::string *stdoutOut,
std::string *stderrOut,
int *exitCodeOut);
class Library : angle::NonCopyable class Library : angle::NonCopyable
{ {
public: public:
......
...@@ -11,68 +11,10 @@ ...@@ -11,68 +11,10 @@
#include <array> #include <array>
#include <dlfcn.h> #include <dlfcn.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
// On mac, environ is not declared anywhere:
// https://stackoverflow.com/a/31347357/912144
#if defined(ANGLE_PLATFORM_APPLE)
extern char **environ;
#endif
namespace angle namespace angle
{ {
namespace
{
struct ScopedPipe
{
~ScopedPipe()
{
closeEndPoint(0);
closeEndPoint(1);
}
void closeEndPoint(int index)
{
if (fds[index] >= 0)
{
close(fds[index]);
fds[index] = -1;
}
}
int fds[2] = {
-1,
-1,
};
};
void ReadEntireFile(int fd, std::string *out)
{
out->clear();
while (true)
{
char buffer[256];
ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
// If interrupted, retry.
if (bytesRead < 0 && errno == EINTR)
{
continue;
}
// If failed, or nothing to read, we are done.
if (bytesRead <= 0)
{
break;
}
out->append(buffer, bytesRead);
}
}
} // anonymous namespace
Optional<std::string> GetCWD() Optional<std::string> GetCWD()
{ {
std::array<char, 4096> pathBuf; std::array<char, 4096> pathBuf;
...@@ -110,112 +52,6 @@ const char *GetPathSeparator() ...@@ -110,112 +52,6 @@ const char *GetPathSeparator()
return ":"; return ":";
} }
bool RunApp(const std::vector<const char *> &args,
std::string *stdoutOut,
std::string *stderrOut,
int *exitCodeOut)
{
if (args.size() == 0 || args.back() != nullptr)
{
return false;
}
ScopedPipe stdoutPipe;
ScopedPipe stderrPipe;
// Create pipes for stdout and stderr.
if (stdoutOut && pipe(stdoutPipe.fds) != 0)
{
return false;
}
if (stderrOut && pipe(stderrPipe.fds) != 0)
{
return false;
}
pid_t pid = fork();
if (pid < 0)
{
return false;
}
if (pid == 0)
{
// Child. Execute the application.
// Redirect stdout and stderr to the pipe fds.
if (stdoutOut)
{
if (dup2(stdoutPipe.fds[1], STDOUT_FILENO) < 0)
{
_exit(errno);
}
}
if (stderrOut)
{
if (dup2(stderrPipe.fds[1], STDERR_FILENO) < 0)
{
_exit(errno);
}
}
// Execute the application, which doesn't return unless failed. Note: execve takes argv as
// `char * const *` for historical reasons. It is safe to const_cast it:
//
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
//
// > The statement about argv[] and envp[] being constants is included to make explicit to
// future writers of language bindings that these objects are completely constant. Due to a
// limitation of the ISO C standard, it is not possible to state that idea in standard C.
// Specifying two levels of const- qualification for the argv[] and envp[] parameters for
// the exec functions may seem to be the natural choice, given that these functions do not
// modify either the array of pointers or the characters to which the function points, but
// this would disallow existing correct code. Instead, only the array of pointers is noted
// as constant.
execve(args[0], const_cast<char *const *>(args.data()), environ);
_exit(errno);
}
// Parent. Read child output from the pipes and clean it up.
// Close the write end of the pipes, so EOF can be generated when child exits.
stdoutPipe.closeEndPoint(1);
stderrPipe.closeEndPoint(1);
// Read back the output of the child.
if (stdoutOut)
{
ReadEntireFile(stdoutPipe.fds[0], stdoutOut);
}
if (stderrOut)
{
ReadEntireFile(stderrPipe.fds[0], stderrOut);
}
// Cleanup the child.
int status = 0;
do
{
pid_t changedPid = waitpid(pid, &status, 0);
if (changedPid < 0 && errno == EINTR)
{
continue;
}
if (changedPid < 0)
{
return false;
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
// Retrieve the error code.
if (exitCodeOut)
{
*exitCodeOut = WEXITSTATUS(status);
}
return true;
}
class PosixLibrary : public Library class PosixLibrary : public Library
{ {
public: public:
......
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// system_utils_unittest.cpp: Unit tests for ANGLE's system utility functions
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "common/system_utils.h"
using namespace angle;
namespace
{
constexpr char kRunAppTestEnvVarName[] = "RUN_APP_TEST_ENV";
constexpr char kRunAppTestEnvVarValue[] = "RunAppTest environment variable value\n";
constexpr char kRunAppTestStdout[] = "RunAppTest stdout test\n";
constexpr char kRunAppTestStderr[] = "RunAppTest stderr test\n .. that expands multiple lines\n";
// Transforms various line endings into C/Unix line endings:
//
// - A\nB -> A\nB
// - A\rB -> A\nB
// - A\r\nB -> A\nB
std::string NormalizeNewLines(const std::string &str)
{
std::string result;
for (size_t i = 0; i < str.size(); ++i)
{
if (str[i] == '\r')
{
if (i + 1 < str.size() && str[i + 1] == '\n')
{
++i;
}
result += '\n';
}
else
{
result += str[i];
}
}
return result;
}
// Test getting the executable path
TEST(SystemUtils, ExecutablePath)
{
#if defined(ANGLE_PLATFORM_FUCHSIA)
// TODO: fuchsia support. http://anglebug.com/3161
return;
#endif
std::string executablePath = GetExecutablePath();
EXPECT_NE("", executablePath);
}
// Test getting the executable directory
TEST(SystemUtils, ExecutableDir)
{
#if defined(ANGLE_PLATFORM_FUCHSIA)
// TODO: fuchsia support. http://anglebug.com/3161
return;
#endif
std::string executableDir = GetExecutableDirectory();
EXPECT_NE("", executableDir);
std::string executablePath = GetExecutablePath();
EXPECT_LT(executableDir.size(), executablePath.size());
EXPECT_EQ(0, strncmp(executableDir.c_str(), executablePath.c_str(), executableDir.size()));
}
// Test setting environment variables
TEST(SystemUtils, Environment)
{
constexpr char kEnvVarName[] = "UNITTEST_ENV_VARIABLE";
constexpr char kEnvVarValue[] = "The quick brown fox jumps over the lazy dog";
bool setEnvDone = SetEnvironmentVar(kEnvVarName, kEnvVarValue);
EXPECT_TRUE(setEnvDone);
std::string readback = GetEnvironmentVar(kEnvVarName);
EXPECT_EQ(kEnvVarValue, readback);
bool unsetEnvDone = UnsetEnvironmentVar(kEnvVarName);
EXPECT_TRUE(unsetEnvDone);
readback = GetEnvironmentVar(kEnvVarName);
EXPECT_EQ("", readback);
}
// Test running an external application and receiving its output
TEST(SystemUtils, RunApp)
{
#if defined(ANGLE_PLATFORM_ANDROID)
// TODO: android support. http://anglebug.com/3125
return;
#endif
#if defined(ANGLE_PLATFORM_FUCHSIA)
// TODO: fuchsia support. http://anglebug.com/3161
return;
#endif
std::string executablePath = GetExecutablePath();
EXPECT_NE(executablePath, "");
std::vector<const char *> args = {executablePath.c_str(),
"--gtest_filter=SystemUtils.RunAppTestTarget", nullptr};
std::string stdoutOutput;
std::string stderrOutput;
int exitCode = EXIT_FAILURE;
// Test that the application can be executed.
bool ranApp = RunApp(args, &stdoutOutput, &stderrOutput, &exitCode);
EXPECT_TRUE(ranApp);
// Note that stdout includes gtest output as well.
EXPECT_NE(std::string::npos, NormalizeNewLines(stdoutOutput).find(kRunAppTestStdout));
EXPECT_EQ(kRunAppTestStderr, NormalizeNewLines(stderrOutput));
EXPECT_EQ(EXIT_SUCCESS, exitCode);
// Test that environment variables reach the cild.
bool setEnvDone = SetEnvironmentVar(kRunAppTestEnvVarName, kRunAppTestEnvVarValue);
EXPECT_TRUE(setEnvDone);
ranApp = RunApp(args, &stdoutOutput, &stderrOutput, &exitCode);
EXPECT_TRUE(ranApp);
EXPECT_EQ(kRunAppTestEnvVarValue, NormalizeNewLines(stderrOutput));
EXPECT_EQ(EXIT_SUCCESS, exitCode);
}
// Not an actual test. Used as an application with controlled output by the RunApp test.
TEST(SystemUtils, RunAppTestTarget)
{
std::string env = GetEnvironmentVar(kRunAppTestEnvVarName);
if (env == "")
{
printf("%s", kRunAppTestStdout);
fprintf(stderr, "%s", kRunAppTestStderr);
}
else
{
fprintf(stderr, "%s", env.c_str());
}
}
} // anonymous namespace
...@@ -15,56 +15,6 @@ ...@@ -15,56 +15,6 @@
namespace angle namespace angle
{ {
namespace
{
struct ScopedPipe
{
~ScopedPipe()
{
closeReadHandle();
closeWriteHandle();
}
void closeReadHandle()
{
if (readHandle)
{
CloseHandle(readHandle);
readHandle = nullptr;
}
}
void closeWriteHandle()
{
if (writeHandle)
{
CloseHandle(writeHandle);
writeHandle = nullptr;
}
}
HANDLE readHandle = nullptr;
HANDLE writeHandle = nullptr;
};
void ReadEntireFile(HANDLE handle, std::string *out)
{
out->clear();
while (true)
{
char buffer[256];
DWORD bytesRead;
BOOL success = ReadFile(handle, buffer, sizeof(buffer), &bytesRead, nullptr);
if (!success || bytesRead == 0)
{
break;
}
out->append(buffer, bytesRead);
}
}
} // anonymous namespace
std::string GetExecutablePath() std::string GetExecutablePath()
{ {
std::array<char, MAX_PATH> executableFileBuf; std::array<char, MAX_PATH> executableFileBuf;
...@@ -131,113 +81,6 @@ const char *GetPathSeparator() ...@@ -131,113 +81,6 @@ const char *GetPathSeparator()
return ";"; return ";";
} }
bool RunApp(const std::vector<const char *> &args,
std::string *stdoutOut,
std::string *stderrOut,
int *exitCodeOut)
{
ScopedPipe stdoutPipe;
ScopedPipe stderrPipe;
SECURITY_ATTRIBUTES sa_attr;
// Set the bInheritHandle flag so pipe handles are inherited.
sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
sa_attr.bInheritHandle = TRUE;
sa_attr.lpSecurityDescriptor = nullptr;
// Create pipes for stdout and stderr. Ensure the read handles to the pipes are not inherited.
if (stdoutOut && !CreatePipe(&stdoutPipe.readHandle, &stdoutPipe.writeHandle, &sa_attr, 0) &&
!SetHandleInformation(stdoutPipe.readHandle, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
if (stderrOut && !CreatePipe(&stderrPipe.readHandle, &stderrPipe.writeHandle, &sa_attr, 0) &&
!SetHandleInformation(stderrPipe.readHandle, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
// Concat the nicely separated arguments into one string so the application has to reparse it.
// We don't support quotation and spaces in arguments currently.
std::vector<char> commandLineString;
for (const char *arg : args)
{
if (arg)
{
if (!commandLineString.empty())
{
commandLineString.push_back(' ');
}
commandLineString.insert(commandLineString.end(), arg, arg + strlen(arg));
}
}
commandLineString.push_back('\0');
STARTUPINFOA startInfo = {};
startInfo.cb = sizeof(STARTUPINFOA);
startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
if (stdoutOut)
{
startInfo.hStdOutput = stdoutPipe.writeHandle;
}
else
{
startInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
}
if (stderrOut)
{
startInfo.hStdError = stderrPipe.writeHandle;
}
else
{
startInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
startInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
PROCESS_INFORMATION processInfo = {};
if (!CreateProcessA(nullptr, commandLineString.data(), nullptr, nullptr,
TRUE, // Handles are inherited.
0, nullptr, nullptr, &startInfo, &processInfo))
{
return false;
}
// Close the write end of the pipes, so EOF can be generated when child exits.
stdoutPipe.closeWriteHandle();
stderrPipe.closeWriteHandle();
// Read back the output of the child.
if (stdoutOut)
{
ReadEntireFile(stdoutPipe.readHandle, stdoutOut);
}
if (stderrOut)
{
ReadEntireFile(stderrPipe.readHandle, stderrOut);
}
// Cleanup the child.
bool success = WaitForSingleObject(processInfo.hProcess, INFINITE) == WAIT_OBJECT_0;
if (success)
{
DWORD exitCode = 0;
success = GetExitCodeProcess(processInfo.hProcess, &exitCode);
if (success)
{
*exitCodeOut = static_cast<int>(exitCode);
}
}
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
return success;
}
class Win32Library : public Library class Win32Library : public Library
{ {
public: public:
......
...@@ -14,7 +14,6 @@ angle_unittests_sources = [ ...@@ -14,7 +14,6 @@ angle_unittests_sources = [
"../common/mathutil_unittest.cpp", "../common/mathutil_unittest.cpp",
"../common/matrix_utils_unittest.cpp", "../common/matrix_utils_unittest.cpp",
"../common/string_utils_unittest.cpp", "../common/string_utils_unittest.cpp",
"../common/system_utils_unittest.cpp",
"../common/utilities_unittest.cpp", "../common/utilities_unittest.cpp",
"../common/vector_utils_unittest.cpp", "../common/vector_utils_unittest.cpp",
"../feature_support_util/feature_support_util_unittest.cpp", "../feature_support_util/feature_support_util_unittest.cpp",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment