Allow art profile method rule flags in any order
Bug: b/297923568
Change-Id: Id0c94e13d27a3396d8011a5c965e8563e467075a
diff --git a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
index e279600..5e90c59 100644
--- a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
+++ b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
@@ -15,6 +15,8 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.Reporter;
+import it.unimi.dsi.fastutil.chars.CharArraySet;
+import it.unimi.dsi.fastutil.chars.CharSet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -89,17 +91,28 @@
try {
ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
ArtProfileMethodRuleInfoImpl.builder();
- rule = parseFlag(rule, 'H', methodRuleInfoBuilder::setIsHot);
- rule = parseFlag(rule, 'S', methodRuleInfoBuilder::setIsStartup);
- rule = parseFlag(rule, 'P', methodRuleInfoBuilder::setIsPostStartup);
+ rule = parseFlags(rule, methodRuleInfoBuilder);
return parseClassOrMethodRule(rule, methodRuleInfoBuilder.build());
} catch (Throwable t) {
return false;
}
}
- private static String parseFlag(String rule, char c, Action action) {
- if (!rule.isEmpty() && rule.charAt(0) == c) {
+ private static String parseFlags(
+ String rule, ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder) {
+ CharSet seenFlags = new CharArraySet(3);
+ String previousRule;
+ do {
+ previousRule = rule;
+ rule = parseFlagIfNotSeen(rule, 'H', methodRuleInfoBuilder::setIsHot, seenFlags);
+ rule = parseFlagIfNotSeen(rule, 'S', methodRuleInfoBuilder::setIsStartup, seenFlags);
+ rule = parseFlagIfNotSeen(rule, 'P', methodRuleInfoBuilder::setIsPostStartup, seenFlags);
+ } while (!rule.equals(previousRule));
+ return rule;
+ }
+
+ private static String parseFlagIfNotSeen(String rule, char c, Action action, CharSet seenFlags) {
+ if (!rule.isEmpty() && rule.charAt(0) == c && seenFlags.add(c)) {
action.execute();
return rule.substring(1);
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/format/ArtProfileWithFlagsInAnyOrderTest.java b/src/test/java/com/android/tools/r8/profile/art/format/ArtProfileWithFlagsInAnyOrderTest.java
new file mode 100644
index 0000000..45ab398
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/format/ArtProfileWithFlagsInAnyOrderTest.java
@@ -0,0 +1,129 @@
+// Copyright (c) 2023, 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.profile.art.format;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.profile.art.ArtProfileBuilder;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
+import com.android.tools.r8.profile.art.diagnostic.HumanReadableArtProfileParserErrorDiagnostic;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.UTF8TextInputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ArtProfileWithFlagsInAnyOrderTest extends TestBase {
+
+ private static final MethodReference MAIN_METHOD_REFERENCE =
+ MethodReferenceUtils.mainMethod(Main.class);
+
+ private static Path profile;
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withMinimumApiLevel().build();
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ profile = getStaticTemp().newFile().toPath();
+ FileUtils.writeTextFile(
+ profile,
+ "PSH" + MethodReferenceUtils.toSmaliString(MAIN_METHOD_REFERENCE),
+ "HHH" + MethodReferenceUtils.toSmaliString(MAIN_METHOD_REFERENCE));
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8()
+ .addProgramClasses(Main.class)
+ .addArtProfileForRewriting(getArtProfileProvider())
+ .release()
+ .setMinApi(parameters)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics)
+ .inspectResidualArtProfile(this::inspectResidualArtProfile);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfileProvider())
+ .allowDiagnosticMessages()
+ .setMinApi(parameters)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics)
+ .inspectResidualArtProfile(this::inspectResidualArtProfile);
+ }
+
+ private ArtProfileProvider getArtProfileProvider() {
+ return new ArtProfileProvider() {
+
+ @Override
+ public void getArtProfile(ArtProfileBuilder profileBuilder) {
+ try {
+ profileBuilder.addHumanReadableArtProfile(
+ new UTF8TextInputStream(profile), parserBuilder -> {});
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return new PathOrigin(profile);
+ }
+ };
+ }
+
+ private void inspectDiagnostics(TestDiagnosticMessages diagnostics) {
+ diagnostics.assertInfosMatch(
+ allOf(
+ diagnosticType(HumanReadableArtProfileParserErrorDiagnostic.class),
+ diagnosticMessage(
+ equalTo(
+ "Unable to parse rule at line 2 from ART profile: HHH"
+ + MethodReferenceUtils.toSmaliString(MAIN_METHOD_REFERENCE)))));
+ }
+
+ private void inspectResidualArtProfile(ArtProfileInspector profileInspector) {
+ profileInspector
+ .inspectMethodRule(
+ MAIN_METHOD_REFERENCE,
+ ruleInspector -> ruleInspector.assertIsHot().assertIsStartup().assertIsPostStartup())
+ .assertContainsNoOtherRules();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}