diff options
Diffstat (limited to 'apct-tests')
5 files changed, 349 insertions, 0 deletions
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp new file mode 100644 index 000000000000..be5072ce3d9d --- /dev/null +++ b/apct-tests/perftests/blobstore/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test { + name: "BlobStorePerfTests", + srcs: ["src/**/*.java"], + static_libs: [ + "BlobStoreTestUtils", + "androidx.test.rules", + "androidx.annotation_annotation", + "apct-perftests-utils", + "ub-uiautomator", + ], + platform_apis: true, + test_suites: ["device-tests"], + certificate: "platform", +}
\ No newline at end of file diff --git a/apct-tests/perftests/blobstore/AndroidManifest.xml b/apct-tests/perftests/blobstore/AndroidManifest.xml new file mode 100644 index 000000000000..21d0726927af --- /dev/null +++ b/apct-tests/perftests/blobstore/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.perftests.blob"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.perftests.blob"/> + +</manifest>
\ No newline at end of file diff --git a/apct-tests/perftests/blobstore/AndroidTest.xml b/apct-tests/perftests/blobstore/AndroidTest.xml new file mode 100644 index 000000000000..19456c6d81d7 --- /dev/null +++ b/apct-tests/perftests/blobstore/AndroidTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs BlobStorePerfTests metric instrumentation."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-metric-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="BlobStorePerfTests.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.perftests.blob" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration>
\ No newline at end of file diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java new file mode 100644 index 000000000000..0208dab33746 --- /dev/null +++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.perftests.blob; + +import android.app.Instrumentation; +import android.app.UiAutomation; +import android.os.ParcelFileDescriptor; +import android.perftests.utils.TraceMarkParser; +import android.perftests.utils.TraceMarkParser.TraceMarkSlice; +import android.support.test.uiautomator.UiDevice; +import android.util.Log; + +import androidx.test.platform.app.InstrumentationRegistry; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; +import java.util.function.BiConsumer; + +// Copy of com.android.frameworks.perftests.am.util.AtraceUtils. TODO: avoid this duplication. +public class AtraceUtils { + private static final String TAG = "AtraceUtils"; + private static final boolean VERBOSE = true; + + private static final String ATRACE_START = "atrace --async_start -b %d -c %s"; + private static final String ATRACE_DUMP = "atrace --async_dump"; + private static final String ATRACE_STOP = "atrace --async_stop"; + private static final int DEFAULT_ATRACE_BUF_SIZE = 1024; + + private UiAutomation mAutomation; + private static AtraceUtils sUtils = null; + private boolean mStarted = false; + + private AtraceUtils(Instrumentation instrumentation) { + mAutomation = instrumentation.getUiAutomation(); + } + + public static AtraceUtils getInstance(Instrumentation instrumentation) { + if (sUtils == null) { + sUtils = new AtraceUtils(instrumentation); + } + return sUtils; + } + + /** + * @param categories The list of the categories to trace, separated with space. + */ + public void startTrace(String categories) { + synchronized (this) { + if (mStarted) { + throw new IllegalStateException("atrace already started"); + } + runShellCommand(String.format( + ATRACE_START, DEFAULT_ATRACE_BUF_SIZE, categories)); + mStarted = true; + } + } + + public void stopTrace() { + synchronized (this) { + mStarted = false; + runShellCommand(ATRACE_STOP); + } + } + + private String runShellCommand(String cmd) { + try { + return UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * @param parser The function that can accept the buffer of atrace dump and parse it. + * @param handler The parse result handler + */ + public void performDump(TraceMarkParser parser, + BiConsumer<String, List<TraceMarkSlice>> handler) { + parser.reset(); + try { + if (VERBOSE) { + Log.i(TAG, "Collecting atrace dump..."); + } + writeDataToBuf(mAutomation.executeShellCommand(ATRACE_DUMP), parser); + } catch (IOException e) { + Log.e(TAG, "Error in reading dump", e); + } + parser.forAllSlices(handler); + } + + // The given file descriptor here will be closed by this function + private void writeDataToBuf(ParcelFileDescriptor pfDescriptor, + TraceMarkParser parser) throws IOException { + InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfDescriptor); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while ((line = reader.readLine()) != null) { + parser.visit(line); + } + } + } +} diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java new file mode 100644 index 000000000000..a7b69c420c59 --- /dev/null +++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java @@ -0,0 +1,146 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.perftests.blob; + +import android.app.blob.BlobStoreManager; +import android.content.Context; +import android.perftests.utils.ManualBenchmarkState; +import android.perftests.utils.PerfManualStatusReporter; +import android.perftests.utils.TraceMarkParser; +import android.perftests.utils.TraceMarkParser.TraceMarkSlice; +import android.support.test.uiautomator.UiDevice; + +import androidx.test.filters.LargeTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.utils.blob.DummyBlobData; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@LargeTest +@RunWith(Parameterized.class) +public class BlobStorePerfTests { + // From frameworks/native/cmds/atrace/atrace.cpp + private static final String ATRACE_CATEGORY_SYSTEM_SERVER = "ss"; + // From f/b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java + private static final String ATRACE_COMPUTE_DIGEST_PREFIX = "computeBlobDigest-"; + + private Context mContext; + private BlobStoreManager mBlobStoreManager; + private AtraceUtils mAtraceUtils; + private ManualBenchmarkState mState; + + @Rule + public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter(); + + @Parameterized.Parameter(0) + public int fileSizeInMb; + + @Parameterized.Parameters(name = "{0}MB") + public static Collection<Object[]> getParameters() { + return Arrays.asList(new Object[][] { + { 25 }, + { 50 }, + { 100 }, + { 200 }, + }); + } + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + mBlobStoreManager = (BlobStoreManager) mContext.getSystemService( + Context.BLOB_STORE_SERVICE); + mAtraceUtils = AtraceUtils.getInstance(InstrumentationRegistry.getInstrumentation()); + mState = mPerfManualStatusReporter.getBenchmarkState(); + } + + @After + public void tearDown() { + // TODO: Add a blob_store shell command to trigger idle maintenance to avoid hardcoding + // job id like this. + // From BlobStoreConfig.IDLE_JOB_ID = 191934935. + runShellCommand("cmd jobscheduler run -f android 191934935"); + } + + @Test + public void testComputeDigest() throws Exception { + mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER); + try { + final List<Long> durations = new ArrayList<>(); + final DummyBlobData blobData = prepareDataBlob(fileSizeInMb); + final TraceMarkParser parser = new TraceMarkParser( + line -> line.name.startsWith(ATRACE_COMPUTE_DIGEST_PREFIX)); + while (mState.keepRunning(durations)) { + commitBlob(blobData); + + durations.clear(); + collectDigestDurationsFromTrace(parser, durations); + // get and delete blobId + } + } finally { + mAtraceUtils.stopTrace(); + } + } + + private void collectDigestDurationsFromTrace(TraceMarkParser parser, List<Long> durations) { + mAtraceUtils.performDump(parser, (key, slices) -> { + for (TraceMarkSlice slice : slices) { + durations.add(TimeUnit.MICROSECONDS.toNanos(slice.getDurationInMicroseconds())); + } + }); + } + + private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception { + final DummyBlobData blobData = new DummyBlobData(mContext, + fileSizeInMb * 1024 * 1024 /* bytes */); + blobData.prepare(); + return blobData; + } + + private void commitBlob(DummyBlobData blobData) throws Exception { + final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle()); + try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) { + blobData.writeToSession(session); + final CompletableFuture<Integer> callback = new CompletableFuture<>(); + session.commit(mContext.getMainExecutor(), callback::complete); + // Ignore commit callback result. + callback.get(); + } + } + + private String runShellCommand(String cmd) { + try { + return UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} |