blob: 2558a7dc7d946ba26c440b713e9e438cf0fc8044 [file] [log] [blame]
Inseob Kim5d5476b2022-06-27 13:22:09 +09001/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Alice Wang246108f2022-07-25 14:44:13 +000016
17#include <aidl/android/system/virtualmachineservice/IVirtualMachineService.h>
18#include <aidl/com/android/microdroid/testservice/BnBenchmarkService.h>
19#include <android-base/result.h>
20#include <android-base/unique_fd.h>
21#include <fcntl.h>
22#include <linux/vm_sockets.h>
23#include <stdio.h>
Inseob Kim5d5476b2022-06-27 13:22:09 +090024#include <unistd.h>
25
Alice Wang246108f2022-07-25 14:44:13 +000026#include <binder_rpc_unstable.hpp>
27#include <chrono>
David Brazdila2129e02022-08-19 14:01:44 +010028#include <fstream>
Alice Wang246108f2022-07-25 14:44:13 +000029#include <random>
30#include <string>
31
32#include "android-base/logging.h"
David Brazdila2129e02022-08-19 14:01:44 +010033#include "android-base/parseint.h"
34#include "android-base/strings.h"
Alice Wang246108f2022-07-25 14:44:13 +000035
36using aidl::android::system::virtualmachineservice::IVirtualMachineService;
37using android::base::ErrnoError;
38using android::base::Error;
39using android::base::Result;
40using android::base::unique_fd;
41
42namespace {
43constexpr uint64_t kBlockSizeBytes = 4096;
44
David Brazdila2129e02022-08-19 14:01:44 +010045template <typename T>
46static ndk::ScopedAStatus resultStatus(const T& result) {
47 if (!result.ok()) {
48 std::stringstream error;
49 error << result.error();
50 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
51 error.str().c_str());
52 }
53 return ndk::ScopedAStatus::ok();
54}
55
Alice Wang246108f2022-07-25 14:44:13 +000056class IOBenchmarkService : public aidl::com::android::microdroid::testservice::BnBenchmarkService {
57public:
58 ndk::ScopedAStatus readFile(const std::string& filename, int64_t fileSizeBytes, bool isRand,
59 double* out) override {
David Brazdila2129e02022-08-19 14:01:44 +010060 auto res = read_file(filename, fileSizeBytes, isRand);
61 if (res.ok()) {
Alice Wang246108f2022-07-25 14:44:13 +000062 *out = res.value();
Alice Wang246108f2022-07-25 14:44:13 +000063 }
David Brazdila2129e02022-08-19 14:01:44 +010064 return resultStatus(res);
65 }
66
67 ndk::ScopedAStatus getMemInfoEntry(const std::string& name, int64_t* out) override {
68 auto value = read_meminfo_entry(name);
69 if (!value.ok()) {
70 return resultStatus(value);
71 }
72
73 *out = (int64_t)value.value();
Alice Wang246108f2022-07-25 14:44:13 +000074 return ndk::ScopedAStatus::ok();
Inseob Kim5d5476b2022-06-27 13:22:09 +090075 }
Alice Wang246108f2022-07-25 14:44:13 +000076
77private:
78 /** Returns the elapsed seconds for reading the file. */
79 Result<double> read_file(const std::string& filename, int64_t fileSizeBytes, bool is_rand) {
80 const int64_t block_count = fileSizeBytes / kBlockSizeBytes;
81 std::vector<uint64_t> offsets;
82 if (is_rand) {
83 std::mt19937 rd{std::random_device{}()};
84 offsets.reserve(block_count);
85 for (auto i = 0; i < block_count; ++i) offsets.push_back(i * kBlockSizeBytes);
86 std::shuffle(offsets.begin(), offsets.end(), rd);
87 }
88 char buf[kBlockSizeBytes];
89
90 clock_t start = clock();
Alice Wangfc24b372022-07-28 16:45:42 +000091 unique_fd fd(open(filename.c_str(), O_RDONLY | O_CLOEXEC));
Alice Wang246108f2022-07-25 14:44:13 +000092 if (fd.get() == -1) {
93 return ErrnoError() << "Read: opening " << filename << " failed";
94 }
95 for (auto i = 0; i < block_count; ++i) {
96 if (is_rand) {
97 if (lseek(fd.get(), offsets[i], SEEK_SET) == -1) {
98 return ErrnoError() << "failed to lseek";
99 }
100 }
101 auto bytes = read(fd.get(), buf, kBlockSizeBytes);
102 if (bytes == 0) {
103 return Error() << "unexpected end of file";
104 } else if (bytes == -1) {
105 return ErrnoError() << "failed to read";
106 }
107 }
108 return {((double)clock() - start) / CLOCKS_PER_SEC};
109 }
David Brazdila2129e02022-08-19 14:01:44 +0100110
111 Result<size_t> read_meminfo_entry(const std::string& stat) {
112 std::ifstream fs("/proc/meminfo");
113 if (!fs.is_open()) {
114 return Error() << "could not open /proc/meminfo";
115 }
116
117 std::string line;
118 while (std::getline(fs, line)) {
119 auto elems = android::base::Split(line, ":");
120 if (elems[0] != stat) continue;
121
122 std::string str = android::base::Trim(elems[1]);
123 if (android::base::EndsWith(str, " kB")) {
124 str = str.substr(0, str.length() - 3);
125 }
126
127 size_t value;
128 if (!android::base::ParseUint(str, &value)) {
129 return ErrnoError() << "failed to parse \"" << str << "\" as size_t";
130 }
131 return {value};
132 }
133
134 return Error() << "entry \"" << stat << "\" not found";
135 }
Alice Wang246108f2022-07-25 14:44:13 +0000136};
137
138Result<void> run_io_benchmark_tests() {
139 auto test_service = ndk::SharedRefBase::make<IOBenchmarkService>();
140 auto callback = []([[maybe_unused]] void* param) {
141 // Tell microdroid_manager that we're ready.
142 // If we can't, abort in order to fail fast - the host won't proceed without
143 // receiving the onReady signal.
144 ndk::SpAIBinder binder(
145 RpcClient(VMADDR_CID_HOST, IVirtualMachineService::VM_BINDER_SERVICE_PORT));
146 auto vm_service = IVirtualMachineService::fromBinder(binder);
147 if (vm_service == nullptr) {
148 LOG(ERROR) << "failed to connect VirtualMachineService\n";
149 abort();
150 }
151 if (auto status = vm_service->notifyPayloadReady(); !status.isOk()) {
152 LOG(ERROR) << "failed to notify payload ready to virtualizationservice: "
153 << status.getDescription();
154 abort();
155 }
156 };
157
158 if (!RunRpcServerCallback(test_service->asBinder().get(), test_service->SERVICE_PORT, callback,
159 nullptr)) {
160 return Error() << "RPC Server failed to run";
161 }
162 return {};
163}
164} // Anonymous namespace
165
166extern "C" int android_native_main([[maybe_unused]] int argc, char* argv[]) {
167 if (strcmp(argv[1], "no_io") == 0) {
168 // do nothing for now; just leave it alive. good night.
169 for (;;) {
170 sleep(1000);
171 }
172 } else if (strcmp(argv[1], "io") == 0) {
173 if (auto res = run_io_benchmark_tests(); res.ok()) {
174 return 0;
175 } else {
176 LOG(ERROR) << "IO benchmark test failed: " << res.error() << "\n";
177 return 1;
178 }
179 }
180 return 0;
Inseob Kim5d5476b2022-06-27 13:22:09 +0900181}