| // 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 <launchpad/launchpad.h> |
| #include <magenta/syscalls.h> |
| #include <magenta/syscalls/object.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| 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(), 0600), 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); |
| |
| CHECK_EQ(CopyFile(run_dir_name + "/" + benchmark_name, benchmark_name), 0) |
| << "Error copying binary, Error: " << 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) { |
| 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; |
| } |
| |
| // TODO: This is for fuchsia, need to write linux specific |
| #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 |