| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "spec_fixture.h" |
| #include "spec_utils.h" |
| |
| #include <benchmark/benchmark.h> |
| #include <fcntl.h> |
| #include <fstream> |
| #include <glog/logging.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #ifdef __unix__ |
| #include <spawn.h> |
| #include <sys/wait.h> |
| #endif |
| |
| #ifdef __Fuchsia__ |
| #include <launchpad/launchpad.h> |
| #include <magenta/syscalls.h> |
| #include <magenta/syscalls/object.h> |
| #endif |
| |
| extern std::string executableDir; |
| extern std::string tmpDir; |
| extern std::ofstream result; |
| |
| namespace { |
| class ScopedGuard { |
| public: |
| explicit ScopedGuard(std::function<void()> f) : f_(f) {} |
| ~ScopedGuard() noexcept { f_(); } |
| ScopedGuard(const ScopedGuard&) = delete; |
| ScopedGuard(ScopedGuard&&) = delete; |
| ScopedGuard& operator=(const ScopedGuard&) = delete; |
| ScopedGuard& operator=(ScopedGuard&&) = delete; |
| |
| private: |
| std::function<void()> f_; |
| }; |
| } // namespace |
| |
| void SpecFixture::SetUp(benchmark::State& st) { |
| DLOG(INFO) << "Preparing benchmark: " << benchmark_name; |
| run_dir_name = tmpDir + "/" + benchmark_name; |
| |
| DLOG(INFO) << "Changing dir to: " << executableDir.c_str(); |
| CHECK_EQ(chdir(executableDir.c_str()), 0) << "Not able to change dir to " |
| << executableDir; |
| CHECK_EQ(mkdir(run_dir_name.c_str(), 0700), 0) |
| << "Not able to create dir: " << run_dir_name |
| << ", Error: " << strerror(errno); |
| |
| CHECK_EQ(CopyDir(run_dir_name, "data/" + benchmark_name), 0) |
| << "Error copying dir: " << run_dir_name |
| << ", Error: " << strerror(errno); |
| |
| std::string binary = run_dir_name + "/" + benchmark_name; |
| CHECK_EQ(CopyFile(binary, benchmark_name), 0) |
| << "Error copying binary, Error: " << strerror(errno); |
| |
| CHECK_EQ(chmod(binary.c_str(), 0700), 0) |
| << "Error changing file permission for binary: " << strerror(errno); |
| } |
| |
| void SpecFixture::TearDown(benchmark::State& st) { |
| CHECK_EQ(DeleteDir(run_dir_name), 0) << "Error Deleting directory " |
| << run_dir_name |
| << ", Error: " << strerror(errno); |
| } |
| |
| int SpecFixture::RunSpec(const char* args[], int args_length) { |
| int page_size = getpagesize(); |
| char** argv = new char*[args_length + 1]; |
| argv[0] = (char*)(benchmark_name.c_str()); |
| if (args_length > 0) { |
| memcpy(argv + 1, args, args_length * sizeof(char*)); |
| } |
| |
| if (chdir(run_dir_name.c_str()) != 0) { |
| LOG(ERROR) << "Not able to change dir to " << run_dir_name |
| << ", Error: " << strerror(errno); |
| return errno; |
| } |
| |
| DLOG(INFO) << "Running benchmark"; |
| |
| // Capture output |
| char buffer[page_size]; |
| int out_p[2]; |
| int old_stdout = dup(STDOUT_FILENO); |
| if (pipe2(out_p, O_NONBLOCK) != 0) { |
| LOG(ERROR) << "Not able create pipe, Error: " << strerror(errno); |
| return errno; |
| } |
| ScopedGuard guard([&]() { close(out_p[0]); }); |
| dup2(out_p[1], STDOUT_FILENO); |
| close(out_p[1]); |
| |
| int status = LaunchProcess(argv, args_length + 1); |
| |
| fflush(stdout); |
| dup2(old_stdout, STDOUT_FILENO); |
| if (chdir(executableDir.c_str()) != 0) { |
| LOG(ERROR) << "Not able to change dir to " << executableDir |
| << ", Error: " << strerror(errno); |
| return errno; |
| } |
| delete[] argv; |
| if (status != 0) { |
| // Dump output |
| LOG(ERROR) << "Error while running benchmark"; |
| ssize_t len; |
| do { |
| len = read(out_p[0], buffer, page_size); |
| if (len == -1) { |
| LOG(ERROR) << "Error reading from pipe, Error: " << strerror(errno); |
| return errno; |
| } |
| fwrite(buffer, sizeof(char), len, stderr); |
| } while (len == page_size); |
| fprintf(stderr, "\n"); |
| return 1; |
| } |
| return 0; |
| } |
| |
| #ifdef __Fuchsia__ |
| int SpecFixture::LaunchProcess(char** args, int args_length) { |
| mx_handle_t handle = launchpad_launch_mxio(args[0], args_length, args); |
| if (handle < 0) { |
| LOG(ERROR) << "Failed to launch " << args[0] << ":" << handle; |
| return handle; |
| } |
| mx_status_t status = |
| mx_handle_wait_one(handle, MX_SIGNAL_SIGNALED, MX_TIME_INFINITE, NULL); |
| |
| if (status != NO_ERROR) { |
| LOG(ERROR) << "Failed to wait for process exiting " << args[0] << ":" |
| << status; |
| return status; |
| } |
| |
| mx_info_process_t proc_info; |
| ssize_t info_status = mx_object_get_info(handle, MX_INFO_PROCESS, &proc_info, |
| sizeof(proc_info), NULL, NULL); |
| mx_handle_close(handle); |
| |
| if (info_status < 0) { |
| LOG(ERROR) << "Failed to get process return code " << args[0] << ":" |
| << info_status; |
| return 1; |
| } |
| return proc_info.return_code; |
| } |
| #endif |
| |
| #ifdef __unix__ |
| int SpecFixture::LaunchProcess(char** args, int args_length) { |
| run_dir_name = tmpDir + "/" + benchmark_name + "/"; |
| std::string command = run_dir_name + args[0]; |
| char** argv = new char*[args_length + 1]; |
| ScopedGuard guard([&]() { delete[] argv; }); |
| for (int i = 0; i < args_length; i++) { |
| argv[i] = args[i]; |
| } |
| argv[args_length] = NULL; |
| pid_t pid; |
| int status = posix_spawn(&pid, command.c_str(), NULL, NULL, argv, environ); |
| if (status == 0) { |
| if (waitpid(pid, &status, 0) != -1) { |
| return status; |
| } else { |
| LOG(ERROR) << "waitpid error: " << strerror(errno); |
| } |
| } else { |
| LOG(ERROR) << "posix_spawn error: " << strerror(status); |
| } |
| return -1; |
| } |
| #endif |