Emit keep stats in metadata
Fixes: b/367558466
Change-Id: I14a805d955eeaefc7d4ec6d17b1eab3c89132dd2
diff --git a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
index 6d118ab..27f8792 100644
--- a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
+++ b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.metadata.impl.R8OptionsImpl;
import com.android.tools.r8.metadata.impl.R8ResourceOptimizationOptionsImpl;
import com.android.tools.r8.metadata.impl.R8StartupOptimizationOptionsImpl;
+import com.android.tools.r8.metadata.impl.R8StatsMetadataImpl;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import java.util.List;
@@ -34,6 +35,7 @@
deserializeTo(R8BaselineProfileRewritingOptionsImpl.class))
.registerTypeAdapter(R8CompilationInfo.class, deserializeTo(R8CompilationInfoImpl.class))
.registerTypeAdapter(R8DexFileMetadata.class, deserializeTo(R8DexFileMetadataImpl.class))
+ .registerTypeAdapter(R8StatsMetadata.class, deserializeTo(R8StatsMetadataImpl.class))
.registerTypeAdapter(
R8FeatureSplitMetadata.class, deserializeTo(R8FeatureSplitMetadataImpl.class))
.registerTypeAdapter(
@@ -85,6 +87,8 @@
*/
R8StartupOptimizationOptions getStartupOptizationOptions();
+ R8StatsMetadata getStatsMetadata();
+
String getVersion();
String toJson();
diff --git a/src/main/java/com/android/tools/r8/metadata/R8StatsMetadata.java b/src/main/java/com/android/tools/r8/metadata/R8StatsMetadata.java
new file mode 100644
index 0000000..6b446ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8StatsMetadata.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+
+@KeepForApi
+public interface R8StatsMetadata {
+
+ float getNoObfuscationPercentage();
+
+ float getNoOptimizationPercentage();
+
+ float getNoShrinkingPercentage();
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java b/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java
index 1bb4869..bcf50d4 100644
--- a/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java
+++ b/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java
@@ -47,7 +47,7 @@
return R8BuildMetadataImpl.builder()
.setOptions(new R8OptionsImpl(options))
.setBaselineProfileRewritingOptions(R8BaselineProfileRewritingOptionsImpl.create(options))
- .setCompilationInfo(R8CompilationInfoImpl.create(executorService, options))
+ .setCompilationInfo(R8CompilationInfoImpl.create(appView, executorService))
.applyIf(
options.isGeneratingDex(), builder -> builder.setDexFilesMetadata(baseVirtualFiles))
.applyIf(
@@ -58,6 +58,7 @@
.setResourceOptimizationOptions(R8ResourceOptimizationOptionsImpl.create(options))
.setStartupOptimizationOptions(
R8StartupOptimizationOptionsImpl.create(options, baseVirtualFiles))
+ .setStatsMetadata(R8StatsMetadataImpl.create(appView))
.setVersion(Version.LABEL)
.build();
}
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java b/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java
index edabf58..c24ba36 100644
--- a/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.metadata.R8Options;
import com.android.tools.r8.metadata.R8ResourceOptimizationOptions;
import com.android.tools.r8.metadata.R8StartupOptimizationOptions;
+import com.android.tools.r8.metadata.R8StatsMetadata;
import com.android.tools.r8.utils.ListUtils;
import com.google.gson.Gson;
import com.google.gson.annotations.Expose;
@@ -53,6 +54,10 @@
private final List<R8DexFileMetadata> dexFilesMetadata;
@Expose
+ @SerializedName("statsMetadata")
+ private final R8StatsMetadata statsMetadata;
+
+ @Expose
@SerializedName("featureSplitsMetadata")
private final R8FeatureSplitsMetadata featureSplitsMetadata;
@@ -73,6 +78,7 @@
R8BaselineProfileRewritingOptions baselineProfileRewritingOptions,
R8CompilationInfo compilationInfo,
List<R8DexFileMetadata> dexFilesMetadata,
+ R8StatsMetadata statsMetadata,
R8FeatureSplitsMetadata featureSplitsMetadata,
R8ResourceOptimizationOptions resourceOptimizationOptions,
R8StartupOptimizationOptions startupOptimizationOptions,
@@ -81,6 +87,7 @@
this.baselineProfileRewritingOptions = baselineProfileRewritingOptions;
this.compilationInfo = compilationInfo;
this.dexFilesMetadata = dexFilesMetadata;
+ this.statsMetadata = statsMetadata;
this.featureSplitsMetadata = featureSplitsMetadata;
this.resourceOptimizationOptions = resourceOptimizationOptions;
this.startupOptimizationOptions = startupOptimizationOptions;
@@ -127,6 +134,11 @@
}
@Override
+ public R8StatsMetadata getStatsMetadata() {
+ return statsMetadata;
+ }
+
+ @Override
public String getVersion() {
return version;
}
@@ -142,6 +154,7 @@
private R8BaselineProfileRewritingOptions baselineProfileRewritingOptions;
private R8CompilationInfo compilationInfo;
private List<R8DexFileMetadata> dexFilesMetadata;
+ private R8StatsMetadata statsMetadata;
private R8FeatureSplitsMetadata featureSplitsMetadata;
private R8ResourceOptimizationOptions resourceOptimizationOptions;
private R8StartupOptimizationOptions startupOptimizationOptions;
@@ -180,6 +193,11 @@
return this;
}
+ public Builder setStatsMetadata(R8StatsMetadata statsMetadata) {
+ this.statsMetadata = statsMetadata;
+ return this;
+ }
+
public Builder setFeatureSplitsMetadata(R8FeatureSplitsMetadata featureSplitsMetadata) {
this.featureSplitsMetadata = featureSplitsMetadata;
return this;
@@ -208,6 +226,7 @@
baselineProfileRewritingOptions,
compilationInfo,
dexFilesMetadata,
+ statsMetadata,
featureSplitsMetadata,
resourceOptimizationOptions,
startupOptimizationOptions,
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8CompilationInfoImpl.java b/src/main/java/com/android/tools/r8/metadata/impl/R8CompilationInfoImpl.java
index e68225d..f5f9094 100644
--- a/src/main/java/com/android/tools/r8/metadata/impl/R8CompilationInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8CompilationInfoImpl.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.metadata.impl;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
import com.android.tools.r8.keepanno.annotations.KeepConstraint;
@@ -38,7 +40,8 @@
}
public static R8CompilationInfoImpl create(
- ExecutorService executorService, InternalOptions options) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, ExecutorService executorService) {
+ InternalOptions options = appView.options();
assert options.created > 0;
long buildTime = System.nanoTime() - options.created;
return new R8CompilationInfoImpl(buildTime, ThreadUtils.getNumberOfThreads(executorService));
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8StatsMetadataImpl.java b/src/main/java/com/android/tools/r8/metadata/impl/R8StatsMetadataImpl.java
new file mode 100644
index 0000000..05b8e2f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8StatsMetadataImpl.java
@@ -0,0 +1,119 @@
+// Copyright (c) 2024, 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.metadata.impl;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
+import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.metadata.R8StatsMetadata;
+import com.android.tools.r8.shaking.KeepInfo;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+@UsedByReflection(
+ description = "Keep and preserve @SerializedName for correct (de)serialization",
+ constraints = {KeepConstraint.LOOKUP},
+ constrainAnnotations = @AnnotationPattern(constant = SerializedName.class),
+ kind = KeepItemKind.CLASS_AND_FIELDS,
+ fieldAccess = {FieldAccessFlags.PRIVATE},
+ fieldAnnotatedByClassConstant = SerializedName.class)
+public class R8StatsMetadataImpl implements R8StatsMetadata {
+
+ @Expose
+ @SerializedName("noObfuscationPercentage")
+ private final float noObfuscationPercentage;
+
+ @Expose
+ @SerializedName("noOptimizationPercentage")
+ private final float noOptimizationPercentage;
+
+ @Expose
+ @SerializedName("noShrinkingPercentage")
+ private final float noShrinkingPercentage;
+
+ private R8StatsMetadataImpl(
+ float noObfuscationPercentage, float noOptimizationPercentage, float noShrinkingPercentage) {
+ this.noObfuscationPercentage = noObfuscationPercentage;
+ this.noOptimizationPercentage = noOptimizationPercentage;
+ this.noShrinkingPercentage = noShrinkingPercentage;
+ }
+
+ public static R8StatsMetadataImpl create(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ Counters counters = Counters.create(appView);
+ return new R8StatsMetadataImpl(
+ counters.getNoObfuscationPercentage(),
+ counters.getNoOptimizationPercentage(),
+ counters.getNoShrinkingPercentage());
+ }
+
+ @Override
+ public float getNoObfuscationPercentage() {
+ return noObfuscationPercentage;
+ }
+
+ @Override
+ public float getNoOptimizationPercentage() {
+ return noOptimizationPercentage;
+ }
+
+ @Override
+ public float getNoShrinkingPercentage() {
+ return noShrinkingPercentage;
+ }
+
+ private static class Counters {
+
+ private int itemsCount = 0;
+ private int noObfuscationCount = 0;
+ private int noOptimizationCount = 0;
+ private int noShrinkingCount = 0;
+
+ private Counters() {}
+
+ static Counters create(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ Counters counters = new Counters();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ counters.add(appView, clazz);
+ clazz.forEachProgramMember(member -> counters.add(appView, member));
+ }
+ return counters;
+ }
+
+ private void add(
+ AppView<? extends AppInfoWithClassHierarchy> appView, ProgramDefinition definition) {
+ KeepInfo<?, ?> keepInfo = appView.getKeepInfo(definition);
+ InternalOptions options = appView.options();
+ itemsCount++;
+ noObfuscationCount += BooleanUtils.intValue(!keepInfo.isMinificationAllowed(options));
+ noOptimizationCount += BooleanUtils.intValue(!keepInfo.isOptimizationAllowed(options));
+ noShrinkingCount += BooleanUtils.intValue(!keepInfo.isShrinkingAllowed(options));
+ }
+
+ float getNoObfuscationPercentage() {
+ return toPercentageWithTwoDecimals(noObfuscationCount);
+ }
+
+ float getNoOptimizationPercentage() {
+ return toPercentageWithTwoDecimals(noOptimizationCount);
+ }
+
+ float getNoShrinkingPercentage() {
+ return toPercentageWithTwoDecimals(noShrinkingCount);
+ }
+
+ float toPercentageWithTwoDecimals(int count) {
+ // Multiply by 100 twice to get percentage with two decimals.
+ float number = (float) (count * 100 * 100) / itemsCount;
+ return (float) Math.round(number) / 100;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java b/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java
index 1365c88..719306b 100644
--- a/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java
@@ -89,6 +89,7 @@
private void inspectDeserializedBuildMetadata(R8BuildMetadata buildMetadata) {
assertNotNull(buildMetadata.getBaselineProfileRewritingOptions());
+ assertNotNull(buildMetadata.getCompilationInfo());
assertNotNull(buildMetadata.getOptions());
assertNotNull(buildMetadata.getOptions().getKeepAttributesOptions());
assertEquals(
@@ -113,6 +114,7 @@
} else {
assertNull(startupOptimizationOptions);
}
+ assertNotNull(buildMetadata.getStatsMetadata());
assertEquals(Version.LABEL, buildMetadata.getVersion());
}