1 /*
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  */
16 
17 package com.android.virt.fs.benchmarks;
18 
19 import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
20 
21 
22 import static org.junit.Assume.assumeFalse;
23 import static org.junit.Assume.assumeTrue;
24 
25 import android.cts.host.utils.DeviceJUnit4ClassRunnerWithParameters;
26 import android.cts.host.utils.DeviceJUnit4Parameterized;
27 import android.platform.test.annotations.RootPermissionTest;
28 
29 import com.android.fs.common.AuthFsTestRule;
30 import com.android.microdroid.test.common.DeviceProperties;
31 import com.android.microdroid.test.common.MetricsProcessor;
32 import com.android.tradefed.device.DeviceNotAvailableException;
33 import com.android.tradefed.metrics.proto.MetricMeasurement.DataType;
34 import com.android.tradefed.metrics.proto.MetricMeasurement.Measurements;
35 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
36 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
37 
38 import org.junit.After;
39 import org.junit.AfterClass;
40 import org.junit.Before;
41 import org.junit.Rule;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 import org.junit.runners.Parameterized;
45 import org.junit.runners.Parameterized.UseParametersRunnerFactory;
46 
47 import java.util.ArrayList;
48 import java.util.Collection;
49 import java.util.List;
50 import java.util.Map;
51 
52 @RootPermissionTest
53 @RunWith(DeviceJUnit4Parameterized.class)
54 @UseParametersRunnerFactory(DeviceJUnit4ClassRunnerWithParameters.RunnerFactory.class)
55 public class AuthFsBenchmarks extends BaseHostJUnit4Test {
56     private static final int TRIAL_COUNT = 5;
57 
58     /** Path to measure_io on Microdroid. */
59     private static final String MEASURE_IO_BIN_PATH = "/mnt/apk/bin/measure_io";
60 
61     /** fs-verity digest (sha256) of testdata/input.4m */
62     private static final String DIGEST_4M =
63             "sha256-f18a268d565348fb4bbf11f10480b198f98f2922eb711de149857b3cecf98a8d";
64 
65     @Parameterized.Parameter(0)
66     public boolean mProtectedVm;
67 
68     @Rule public final AuthFsTestRule mAuthFsTestRule = new AuthFsTestRule();
69     @Rule public final TestMetrics mTestMetrics = new TestMetrics();
70     private MetricsProcessor mMetricsProcessor;
71 
72     @Parameterized.Parameters(name = "protectedVm={0}")
params()73     public static Collection<Object[]> params() {
74         return List.of(new Object[] {true}, new Object[] {false});
75     }
76 
77     @Before
setUp()78     public void setUp() throws Exception {
79         AuthFsTestRule.setUpAndroid(getTestInformation());
80         mAuthFsTestRule.setUpTest();
81         assumeTrue(AuthFsTestRule.getDevice().supportsMicrodroid(mProtectedVm));
82         DeviceProperties deviceProperties = DeviceProperties.create(getDevice()::getProperty);
83         assumeFalse(
84                 "Skip on CF; no need to collect metrics on CF", deviceProperties.isCuttlefish());
85         String metricsPrefix = MetricsProcessor.getMetricPrefix(deviceProperties.getMetricsTag());
86         mMetricsProcessor = new MetricsProcessor(metricsPrefix + "authfs/");
87         AuthFsTestRule.startMicrodroid(mProtectedVm);
88     }
89 
90     @After
tearDown()91     public void tearDown() throws DeviceNotAvailableException {
92         AuthFsTestRule.shutdownMicrodroid();
93     }
94 
95     @AfterClass
tearDownClass()96     public static void tearDownClass() {
97         AuthFsTestRule.tearDownAndroid();
98     }
99 
100     @Test
seqReadRemoteFile()101     public void seqReadRemoteFile() throws Exception {
102         readRemoteFile("seq");
103     }
104 
105     @Test
randReadRemoteFile()106     public void randReadRemoteFile() throws Exception {
107         readRemoteFile("rand");
108     }
109 
110     @Test
seqWriteRemoteFile()111     public void seqWriteRemoteFile() throws Exception {
112         writeRemoteFile("seq");
113     }
114 
115     @Test
randWriteRemoteFile()116     public void randWriteRemoteFile() throws Exception {
117         writeRemoteFile("rand");
118     }
119 
readRemoteFile(String mode)120     private void readRemoteFile(String mode) throws DeviceNotAvailableException {
121         // Cache the file in memory for the host.
122         mAuthFsTestRule
123                 .getAndroid()
124                 .run("cat " + mAuthFsTestRule.TEST_DIR + "/input.4m > /dev/null");
125 
126         String filePath = mAuthFsTestRule.MOUNT_DIR + "/3";
127         int fileSizeMb = 4;
128         String cmd = MEASURE_IO_BIN_PATH + " " + filePath + " " + fileSizeMb + " " + mode + " r";
129         List<Double> rates = new ArrayList<>(TRIAL_COUNT);
130         for (int i = 0; i < TRIAL_COUNT + 1; ++i) {
131             mAuthFsTestRule.runFdServerOnAndroid(
132                     "--open-ro 3:input.4m --open-ro 4:input.4m.fsv_meta", "--ro-fds 3:4");
133             mAuthFsTestRule.runAuthFsOnMicrodroid("--remote-ro-file 3:" + DIGEST_4M);
134 
135             String rate = mAuthFsTestRule.getMicrodroid().run(cmd);
136             rates.add(Double.parseDouble(rate));
137             mAuthFsTestRule.killFdServerOnAndroid();
138         }
139         reportMetrics(rates, mode + "_read", "mb_per_sec");
140     }
141 
writeRemoteFile(String mode)142     private void writeRemoteFile(String mode) throws DeviceNotAvailableException {
143         String filePath = mAuthFsTestRule.MOUNT_DIR + "/5";
144         int fileSizeMb = 8;
145         String cmd = MEASURE_IO_BIN_PATH + " " + filePath + " " + fileSizeMb + " " + mode + " w";
146         List<Double> rates = new ArrayList<>(TRIAL_COUNT);
147         for (int i = 0; i < TRIAL_COUNT + 1; ++i) {
148             mAuthFsTestRule.runFdServerOnAndroid(
149                     "--open-rw 5:" + AuthFsTestRule.TEST_OUTPUT_DIR + "/out.file", "--rw-fds 5");
150             mAuthFsTestRule.runAuthFsOnMicrodroid("--remote-new-rw-file 5");
151 
152             String rate = mAuthFsTestRule.getMicrodroid().run(cmd);
153             rates.add(Double.parseDouble(rate));
154             mAuthFsTestRule.killFdServerOnAndroid();
155             AuthFsTestRule.getAndroid()
156                     .runForResult("rm", "-rf", AuthFsTestRule.TEST_OUTPUT_DIR + "/out.file");
157         }
158         reportMetrics(rates, mode + "_write", "mb_per_sec");
159     }
160 
reportMetrics(List<Double> metrics, String name, String unit)161     private void reportMetrics(List<Double> metrics, String name, String unit) {
162         Map<String, Double> stats = mMetricsProcessor.computeStats(metrics, name, unit);
163         for (Map.Entry<String, Double> entry : stats.entrySet()) {
164             Metric metric =
165                     Metric.newBuilder()
166                             .setType(DataType.RAW)
167                             .setMeasurements(
168                                     Measurements.newBuilder().setSingleDouble(entry.getValue()))
169                             .build();
170             mTestMetrics.addTestMetric(entry.getKey(), metric);
171         }
172     }
173 }
174