blob: 5d93b9397b42d6fd2f10000925ee17e1d932d11e [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
Alice Wang246108f2022-07-25 14:44:13 +000017#include <aidl/com/android/microdroid/testservice/BnBenchmarkService.h>
Alice Wang98e6e8a2022-08-08 15:31:58 +000018#include <android-base/logging.h>
19#include <android-base/parseint.h>
Alice Wang246108f2022-07-25 14:44:13 +000020#include <android-base/result.h>
Alice Wang98e6e8a2022-08-08 15:31:58 +000021#include <android-base/strings.h>
Alice Wang246108f2022-07-25 14:44:13 +000022#include <android-base/unique_fd.h>
23#include <fcntl.h>
24#include <linux/vm_sockets.h>
25#include <stdio.h>
Alice Wangc7a72312022-11-16 11:01:47 +000026#include <sys/stat.h>
27#include <sys/types.h>
Alice Wang98e6e8a2022-08-08 15:31:58 +000028#include <time.h>
Inseob Kim5d5476b2022-06-27 13:22:09 +090029#include <unistd.h>
Alan Stokes52d3c722022-10-04 17:27:13 +010030#include <vm_main.h>
Alice Wang4d370e62022-10-11 08:33:34 +000031#include <vm_payload.h>
Inseob Kim5d5476b2022-06-27 13:22:09 +090032
David Brazdila2129e02022-08-19 14:01:44 +010033#include <fstream>
Alice Wang246108f2022-07-25 14:44:13 +000034#include <random>
35#include <string>
36
Alice Wang98e6e8a2022-08-08 15:31:58 +000037#include "io_vsock.h"
Alice Wang246108f2022-07-25 14:44:13 +000038
Alice Wang246108f2022-07-25 14:44:13 +000039using android::base::ErrnoError;
40using android::base::Error;
41using android::base::Result;
42using android::base::unique_fd;
43
44namespace {
45constexpr uint64_t kBlockSizeBytes = 4096;
Alice Wangf2685f62022-09-06 07:26:53 +000046constexpr uint64_t kNumBytesPerMB = 1024 * 1024;
Alice Wang246108f2022-07-25 14:44:13 +000047
David Brazdila2129e02022-08-19 14:01:44 +010048template <typename T>
49static ndk::ScopedAStatus resultStatus(const T& result) {
50 if (!result.ok()) {
51 std::stringstream error;
52 error << result.error();
53 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
54 error.str().c_str());
55 }
56 return ndk::ScopedAStatus::ok();
57}
58
Alice Wang246108f2022-07-25 14:44:13 +000059class IOBenchmarkService : public aidl::com::android::microdroid::testservice::BnBenchmarkService {
60public:
Alice Wangc7a72312022-11-16 11:01:47 +000061 ndk::ScopedAStatus measureReadRate(const std::string& filename, bool isRand,
62 double* out) override {
63 auto res = measure_read_rate(filename, isRand);
David Brazdila2129e02022-08-19 14:01:44 +010064 if (res.ok()) {
Alice Wang246108f2022-07-25 14:44:13 +000065 *out = res.value();
Alice Wang246108f2022-07-25 14:44:13 +000066 }
David Brazdila2129e02022-08-19 14:01:44 +010067 return resultStatus(res);
68 }
69
70 ndk::ScopedAStatus getMemInfoEntry(const std::string& name, int64_t* out) override {
71 auto value = read_meminfo_entry(name);
72 if (!value.ok()) {
73 return resultStatus(value);
74 }
75
76 *out = (int64_t)value.value();
Alice Wang246108f2022-07-25 14:44:13 +000077 return ndk::ScopedAStatus::ok();
Inseob Kim5d5476b2022-06-27 13:22:09 +090078 }
Alice Wang246108f2022-07-25 14:44:13 +000079
Keir Fraser56a0f8c2022-12-02 09:10:13 +000080 ndk::ScopedAStatus allocAnonMemory(int64_t mb, int64_t* out) override {
81 *out = (int64_t)(long)alloc_anon_memory((long)mb);
Keir Fraser6ea1da92022-11-28 15:24:14 +000082 return ndk::ScopedAStatus::ok();
83 }
84
Alice Wang98e6e8a2022-08-08 15:31:58 +000085 ndk::ScopedAStatus initVsockServer(int32_t port, int32_t* out) override {
86 auto res = io_vsock::init_vsock_server(port);
87 if (res.ok()) {
88 *out = res.value();
89 }
90 return resultStatus(res);
91 }
92
93 ndk::ScopedAStatus runVsockServerAndReceiveData(int32_t server_fd,
94 int32_t num_bytes_to_receive) override {
95 auto res = io_vsock::run_vsock_server_and_receive_data(server_fd, num_bytes_to_receive);
96 return resultStatus(res);
97 }
98
Alice Wang246108f2022-07-25 14:44:13 +000099private:
Alice Wangc7a72312022-11-16 11:01:47 +0000100 /**
101 * Measures the read rate for reading the given file.
102 * @return The read rate in MB/s.
103 */
104 Result<double> measure_read_rate(const std::string& filename, bool is_rand) {
105 struct stat file_stats;
106 if (stat(filename.c_str(), &file_stats) == -1) {
107 return Error() << "failed to get file stats";
108 }
109 const int64_t file_size_bytes = file_stats.st_size;
110 const int64_t block_count = file_size_bytes / kBlockSizeBytes;
Alice Wang3e963dc2022-11-14 08:43:04 +0000111 std::vector<uint64_t> offsets(block_count);
112 for (auto i = 0; i < block_count; ++i) {
Alice Wang7067a702022-11-15 09:49:57 +0000113 offsets[i] = i * kBlockSizeBytes;
Alice Wang3e963dc2022-11-14 08:43:04 +0000114 }
Alice Wang246108f2022-07-25 14:44:13 +0000115 if (is_rand) {
116 std::mt19937 rd{std::random_device{}()};
Alice Wang246108f2022-07-25 14:44:13 +0000117 std::shuffle(offsets.begin(), offsets.end(), rd);
118 }
119 char buf[kBlockSizeBytes];
120
Inseob Kim8f3cf0e2023-01-04 14:17:08 +0900121 struct timespec start;
122 if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) {
123 return ErrnoError() << "failed to clock_gettime";
124 }
Alice Wangfc24b372022-07-28 16:45:42 +0000125 unique_fd fd(open(filename.c_str(), O_RDONLY | O_CLOEXEC));
Alice Wang246108f2022-07-25 14:44:13 +0000126 if (fd.get() == -1) {
127 return ErrnoError() << "Read: opening " << filename << " failed";
128 }
129 for (auto i = 0; i < block_count; ++i) {
Alice Wang3e963dc2022-11-14 08:43:04 +0000130 auto bytes = pread(fd, buf, kBlockSizeBytes, offsets[i]);
Alice Wang246108f2022-07-25 14:44:13 +0000131 if (bytes == 0) {
132 return Error() << "unexpected end of file";
133 } else if (bytes == -1) {
134 return ErrnoError() << "failed to read";
135 }
136 }
Inseob Kim8f3cf0e2023-01-04 14:17:08 +0900137 struct timespec finish;
138 if (clock_gettime(CLOCK_MONOTONIC, &finish) == -1) {
139 return ErrnoError() << "failed to clock_gettime";
140 }
141 double elapsed_seconds =
142 finish.tv_sec - start.tv_sec + (finish.tv_nsec - start.tv_nsec) / 1e9;
Alice Wangc7a72312022-11-16 11:01:47 +0000143 double file_size_mb = (double)file_size_bytes / kNumBytesPerMB;
144 return {file_size_mb / elapsed_seconds};
Alice Wang246108f2022-07-25 14:44:13 +0000145 }
David Brazdila2129e02022-08-19 14:01:44 +0100146
Keir Fraser6ea1da92022-11-28 15:24:14 +0000147 void* alloc_anon_memory(long mb) {
148 long bytes = mb << 20;
149 void* p = malloc(bytes);
150 /*
151 * Heap memory is demand allocated. Dirty all pages to ensure
152 * all are allocated.
153 */
154 memset(p, 0x55, bytes);
155 return p;
156 }
157
David Brazdila2129e02022-08-19 14:01:44 +0100158 Result<size_t> read_meminfo_entry(const std::string& stat) {
159 std::ifstream fs("/proc/meminfo");
160 if (!fs.is_open()) {
161 return Error() << "could not open /proc/meminfo";
162 }
163
164 std::string line;
165 while (std::getline(fs, line)) {
166 auto elems = android::base::Split(line, ":");
167 if (elems[0] != stat) continue;
168
169 std::string str = android::base::Trim(elems[1]);
170 if (android::base::EndsWith(str, " kB")) {
171 str = str.substr(0, str.length() - 3);
172 }
173
174 size_t value;
175 if (!android::base::ParseUint(str, &value)) {
176 return ErrnoError() << "failed to parse \"" << str << "\" as size_t";
177 }
178 return {value};
179 }
180
181 return Error() << "entry \"" << stat << "\" not found";
182 }
Alice Wang246108f2022-07-25 14:44:13 +0000183};
184
185Result<void> run_io_benchmark_tests() {
186 auto test_service = ndk::SharedRefBase::make<IOBenchmarkService>();
Alan Stokes65bbb912022-11-23 09:39:34 +0000187 auto callback = []([[maybe_unused]] void* param) { AVmPayload_notifyPayloadReady(); };
Devin Moorecaf7b952023-04-24 20:14:35 +0000188 AVmPayload_runVsockRpcServer(test_service->asBinder().get(), test_service->PORT, callback,
189 nullptr);
Alice Wang246108f2022-07-25 14:44:13 +0000190 return {};
191}
192} // Anonymous namespace
193
Alan Stokes52d3c722022-10-04 17:27:13 +0100194extern "C" int AVmPayload_main() {
Alan Stokes38d00f82022-10-03 17:43:45 +0100195 if (auto res = run_io_benchmark_tests(); !res.ok()) {
196 LOG(ERROR) << "IO benchmark test failed: " << res.error() << "\n";
197 return EXIT_FAILURE;
Alice Wang246108f2022-07-25 14:44:13 +0000198 }
Alice Wang98e6e8a2022-08-08 15:31:58 +0000199 return EXIT_SUCCESS;
Inseob Kim5d5476b2022-06-27 13:22:09 +0900200}