blob: 808a7672fb4322c326c8f015c4373ba2e6a76046 [file] [log] [blame]
// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.compilerapi.startupprofile;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TextInputStream;
import com.android.tools.r8.compilerapi.CompilerApiTest;
import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.profile.art.ArtProfileClassRuleInfo;
import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfo;
import com.android.tools.r8.profile.art.ArtProfileRulePredicate;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.startup.StartupProfileBuilder;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import org.junit.Test;
public class StartupProfileApiTest extends CompilerApiTestRunner {
private static final int FIRST_API_LEVEL_WITH_NATIVE_MULTIDEX = 21;
public StartupProfileApiTest(TestParameters parameters) {
super(parameters);
}
@Override
public Class<? extends CompilerApiTest> binaryTestClass() {
return ApiTest.class;
}
@Test
public void testD8ArrayApi() throws Exception {
ApiTest test = new ApiTest(ApiTest.PARAMETERS);
runTest(
test,
programConsumer ->
test.runD8(ApiTest::addStartupProfileProviderUsingArrayApi, programConsumer));
}
@Test
public void testD8CollectionApi() throws Exception {
ApiTest test = new ApiTest(ApiTest.PARAMETERS);
runTest(
test,
programConsumer ->
test.runD8(ApiTest::addStartupProfileProviderUsingCollectionApi, programConsumer));
}
@Test
public void testR8ArrayApi() throws Exception {
ApiTest test = new ApiTest(ApiTest.PARAMETERS);
runTest(
test,
programConsumer ->
test.runR8(ApiTest::addStartupProfileProviderUsingArrayApi, programConsumer));
}
@Test
public void testR8CollectionApi() throws Exception {
ApiTest test = new ApiTest(ApiTest.PARAMETERS);
runTest(
test,
programConsumer ->
test.runR8(ApiTest::addStartupProfileProviderUsingCollectionApi, programConsumer));
}
private void runTest(ApiTest test, ThrowingConsumer<ProgramConsumer, Exception> testRunner)
throws Exception {
Path output = temp.newFolder().toPath();
testRunner.accept(new DexIndexedConsumer.DirectoryConsumer(output));
assertThat(
new CodeInspector(output.resolve("classes.dex")).clazz(test.getMockClass()), isPresent());
assertThat(
new CodeInspector(output.resolve("classes2.dex")).clazz(test.getPostStartupMockClass()),
isPresent());
}
public static class ApiTest extends CompilerApiTest {
public ApiTest(Object parameters) {
super(parameters);
}
private StartupProfileProvider getStartupProfileProvider() {
return new StartupProfileProvider() {
@Override
public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
// Create human-readable ART startup profile.
ClassReference mockClassReference = Reference.classFromClass(getMockClass());
ClosableByteArrayInputStream inputStream =
new ClosableByteArrayInputStream(mockClassReference.getDescriptor().getBytes());
// Create parser and parse ART profile.
List<ClassReference> seenClasses = new ArrayList<>();
startupProfileBuilder.addHumanReadableArtProfile(
new TextInputStream() {
@Override
public InputStream getInputStream() {
return inputStream;
}
@Override
public Charset getCharset() {
return StandardCharsets.UTF_8;
}
},
parserBuilder ->
parserBuilder.setRulePredicate(
new ArtProfileRulePredicate() {
@Override
public boolean testClassRule(
ClassReference reference, ArtProfileClassRuleInfo classRuleInfo) {
seenClasses.add(reference);
return true;
}
@Override
public boolean testMethodRule(
MethodReference reference, ArtProfileMethodRuleInfo methodRuleInfo) {
return true;
}
}));
// Verify rule predicate has been used and input stream is closed.
assertEquals(1, seenClasses.size());
assertEquals(mockClassReference, seenClasses.get(0));
assertTrue(inputStream.isClosed());
}
@Override
public Origin getOrigin() {
return Origin.unknown();
}
};
}
public void runD8(
BiConsumer<D8Command.Builder, StartupProfileProvider> startupProfileProviderInstaller,
ProgramConsumer programConsumer)
throws Exception {
D8Command.Builder commandBuilder =
D8Command.builder()
.addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
.addClassProgramData(getBytesForClass(getPostStartupMockClass()), Origin.unknown())
.addLibraryFiles(getJava8RuntimeJar())
.setMinApiLevel(FIRST_API_LEVEL_WITH_NATIVE_MULTIDEX)
.setProgramConsumer(programConsumer);
startupProfileProviderInstaller.accept(commandBuilder, getStartupProfileProvider());
D8.run(commandBuilder.build());
}
public void runR8(
BiConsumer<R8Command.Builder, StartupProfileProvider> startupProfileProviderInstaller,
ProgramConsumer programConsumer)
throws Exception {
R8Command.Builder commandBuilder =
R8Command.builder()
.addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
.addClassProgramData(getBytesForClass(getPostStartupMockClass()), Origin.unknown())
.addProguardConfiguration(
Collections.singletonList("-keep class * { *; }"), Origin.unknown())
.addLibraryFiles(getJava8RuntimeJar())
.setMinApiLevel(FIRST_API_LEVEL_WITH_NATIVE_MULTIDEX)
.setProgramConsumer(programConsumer);
startupProfileProviderInstaller.accept(commandBuilder, getStartupProfileProvider());
R8.run(commandBuilder.build());
}
@Test
public void testD8ArrayApi() throws Exception {
runD8(ApiTest::addStartupProfileProviderUsingArrayApi, DexIndexedConsumer.emptyConsumer());
}
private static void addStartupProfileProviderUsingArrayApi(
D8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
StartupProfileProvider[] startupProfileProviders =
new StartupProfileProvider[] {startupProfileProvider};
commandBuilder.addStartupProfileProviders(startupProfileProviders);
}
@Test
public void testD8CollectionApi() throws Exception {
runD8(
ApiTest::addStartupProfileProviderUsingCollectionApi, DexIndexedConsumer.emptyConsumer());
}
private static void addStartupProfileProviderUsingCollectionApi(
D8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
Collection<StartupProfileProvider> startupProfileProviders =
Collections.singleton(startupProfileProvider);
commandBuilder.addStartupProfileProviders(startupProfileProviders);
}
@Test
public void testR8ArrayApi() throws Exception {
runR8(ApiTest::addStartupProfileProviderUsingArrayApi, DexIndexedConsumer.emptyConsumer());
}
private static void addStartupProfileProviderUsingArrayApi(
R8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
StartupProfileProvider[] startupProfileProviders =
new StartupProfileProvider[] {startupProfileProvider};
commandBuilder.addStartupProfileProviders(startupProfileProviders);
}
@Test
public void testR8CollectionApi() throws Exception {
runR8(
ApiTest::addStartupProfileProviderUsingCollectionApi, DexIndexedConsumer.emptyConsumer());
}
private static void addStartupProfileProviderUsingCollectionApi(
R8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
Collection<StartupProfileProvider> startupProfileProviders =
Collections.singleton(startupProfileProvider);
commandBuilder.addStartupProfileProviders(startupProfileProviders);
}
private static class ClosableByteArrayInputStream extends ByteArrayInputStream {
private boolean closed;
public ClosableByteArrayInputStream(byte[] buf) {
super(buf);
}
@Override
public void close() throws IOException {
super.close();
closed = true;
}
public boolean isClosed() {
return closed;
}
}
}
}