Dump contexts of boxing/unboxing operations at runtime
Change-Id: Ia3cf42f2edc9e7e70ca7dc07d283a9e004c086a2
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
index 9e78975..2568376 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
@@ -57,7 +57,7 @@
return enableCompletenessCheckForTesting
&& !options.isDesugaredLibraryCompilation()
&& !options.getStartupOptions().isStartupCompletenessCheckForTestingEnabled()
- && !options.getStartupInstrumentationOptions().isStartupInstrumentationEnabled();
+ && !options.getInstrumentationOptions().isInstrumentationEnabled();
}
public boolean isNopCheckForTestingEnabled() {
diff --git a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/InstrumentationOptions.java b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/InstrumentationOptions.java
index 69bc2d2..c1d3fa5 100644
--- a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/InstrumentationOptions.java
+++ b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/InstrumentationOptions.java
@@ -7,7 +7,13 @@
import static com.android.tools.r8.utils.SystemPropertyUtils.getSystemPropertyForDevelopment;
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
import java.util.Collections;
import java.util.Set;
@@ -23,9 +29,9 @@
* <p>This will also inject the startup runtime library (i.e., the InstrumentationServer) into the
* app.
*/
- private boolean enableStartupInstrumentation =
+ private boolean enableExecutedClassesAndMethodsInstrumentation =
parseSystemPropertyForDevelopmentOrDefault(
- "com.android.tools.r8.startup.instrumentation.instrument", false);
+ "com.android.tools.r8.instrumentation.executedclassesandmethods", false);
/**
* Specifies the synthetic context of the startup runtime library. When this is set, the startup
@@ -35,11 +41,11 @@
*
* <p>Example synthetic context: "app.tivi.home.MainActivity".
*
- * <p>Note that this is only meaningful when {@link #enableStartupInstrumentation} is set to true.
+ * <p>Note that this is only meaningful when one or more instrumentations are enabled.
*/
- private String startupInstrumentationServerSyntheticContext =
+ private String syntheticServerContext =
getSystemPropertyForDevelopment(
- "com.android.tools.r8.startup.instrumentation.instrumentationserversyntheticcontext");
+ "com.android.tools.r8.instrumentation.syntheticservercontext");
/**
* Specifies the logcat tag that should be used by the InstrumentationServer when logging events.
@@ -48,9 +54,33 @@
* logcat. Instead, the startup events must be obtained by requesting the InstrumentationServer to
* write the events to a file.
*/
- private String startupInstrumentationTag =
- getSystemPropertyForDevelopment(
- "com.android.tools.r8.startup.instrumentation.instrumentationtag");
+ private String tag = getSystemPropertyForDevelopment("com.android.tools.r8.instrumentation.tag");
+
+ public InstrumentationOptions(InternalOptions options) {
+ String callSitesToInstrumentString =
+ getSystemPropertyForDevelopment("com.android.tools.r8.instrumentation.callsites");
+ if (callSitesToInstrumentString != null) {
+ setCallSitesToInstrument(
+ parseCallSitesToInstrument(callSitesToInstrumentString, options.dexItemFactory()));
+ }
+ }
+
+ private static Set<DexMethod> parseCallSitesToInstrument(
+ String smaliStrings, DexItemFactory factory) {
+ ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder();
+ for (String smaliString : Splitter.on(':').split(smaliStrings)) {
+ MethodReference methodReference = MethodReferenceUtils.parseSmaliString(smaliString);
+ if (methodReference == null) {
+ throw new IllegalArgumentException(smaliString);
+ }
+ builder.add(MethodReferenceUtils.toDexMethod(methodReference, factory));
+ }
+ return builder.build();
+ }
+
+ public boolean isInstrumentationEnabled() {
+ return enableExecutedClassesAndMethodsInstrumentation || !callSitesToInstrument.isEmpty();
+ }
public Set<DexMethod> getCallSitesToInstrument() {
return callSitesToInstrument;
@@ -60,40 +90,33 @@
this.callSitesToInstrument = callSitesToInstrument;
}
- public boolean hasStartupInstrumentationServerSyntheticContext() {
- return startupInstrumentationServerSyntheticContext != null;
+ public boolean isExecutedClassesAndMethodsInstrumentationEnabled() {
+ return enableExecutedClassesAndMethodsInstrumentation;
}
- public String getStartupInstrumentationServerSyntheticContext() {
- return startupInstrumentationServerSyntheticContext;
- }
-
- public InstrumentationOptions setStartupInstrumentationServerSyntheticContext(
- String startupInstrumentationServerSyntheticContext) {
- this.startupInstrumentationServerSyntheticContext =
- startupInstrumentationServerSyntheticContext;
+ public InstrumentationOptions setEnableExecutedClassesAndMethodsInstrumentation() {
+ enableExecutedClassesAndMethodsInstrumentation = true;
return this;
}
- public boolean hasStartupInstrumentationTag() {
- return startupInstrumentationTag != null;
+ public boolean hasSyntheticServerContext() {
+ return syntheticServerContext != null;
}
- public String getStartupInstrumentationTag() {
- return startupInstrumentationTag;
+ public String getSyntheticServerContext() {
+ return syntheticServerContext;
}
- public InstrumentationOptions setStartupInstrumentationTag(String startupInstrumentationTag) {
- this.startupInstrumentationTag = startupInstrumentationTag;
- return this;
+ public boolean hasTag() {
+ return tag != null;
}
- public boolean isStartupInstrumentationEnabled() {
- return enableStartupInstrumentation;
+ public String getTag() {
+ return tag;
}
- public InstrumentationOptions setEnableStartupInstrumentation() {
- enableStartupInstrumentation = true;
+ public InstrumentationOptions setTag(String tag) {
+ this.tag = tag;
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
index 96c4ca4..5c43c8b 100644
--- a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
@@ -58,7 +58,7 @@
private final DexItemFactory dexItemFactory;
private final InternalOptions options;
private final StartupInstrumentationReferences references;
- private final InstrumentationOptions startupInstrumentationOptions;
+ private final InstrumentationOptions instrumentationOptions;
private StartupInstrumentation(AppView<AppInfo> appView) {
this.appView = appView;
@@ -66,12 +66,12 @@
this.dexItemFactory = appView.dexItemFactory();
this.options = appView.options();
this.references = new StartupInstrumentationReferences(dexItemFactory);
- this.startupInstrumentationOptions = options.getStartupInstrumentationOptions();
+ this.instrumentationOptions = options.getInstrumentationOptions();
}
public static void run(AppView<AppInfo> appView, ExecutorService executorService)
throws ExecutionException {
- if (appView.options().getStartupInstrumentationOptions().isStartupInstrumentationEnabled()
+ if (appView.options().getInstrumentationOptions().isInstrumentationEnabled()
&& appView.options().isGeneratingDex()) {
StartupInstrumentation startupInstrumentation = new StartupInstrumentation(appView);
startupInstrumentation.instrumentAllClasses(executorService);
@@ -97,11 +97,11 @@
// If the startup options has a synthetic context for the startup instrumentation server, then
// only inject the runtime library if the synthetic context exists in program to avoid injecting
// the runtime library multiple times when there is separate compilation.
- if (startupInstrumentationOptions.hasStartupInstrumentationServerSyntheticContext()) {
+ if (instrumentationOptions.hasSyntheticServerContext()) {
DexType syntheticContext =
dexItemFactory.createType(
DescriptorUtils.javaTypeToDescriptor(
- startupInstrumentationOptions.getStartupInstrumentationServerSyntheticContext()));
+ instrumentationOptions.getSyntheticServerContext()));
if (asProgramClassOrNull(appView.definitionFor(syntheticContext)) == null) {
return;
}
@@ -126,7 +126,7 @@
private List<DexProgramClass> createStartupRuntimeLibraryClasses() {
DexProgramClass instrumentationServerImplClass =
InstrumentationServerImplFactory.createClass(dexItemFactory);
- if (startupInstrumentationOptions.hasStartupInstrumentationTag()) {
+ if (instrumentationOptions.hasTag()) {
instrumentationServerImplClass
.lookupUniqueStaticFieldWithName(dexItemFactory.createString("writeToLogcat"))
.setStaticValue(DexValueBoolean.create(true));
@@ -137,9 +137,7 @@
instrumentationServerImplClass
.lookupUniqueStaticFieldWithName(dexItemFactory.createString("logcatTag"))
.setStaticValue(
- new DexValueString(
- dexItemFactory.createString(
- startupInstrumentationOptions.getStartupInstrumentationTag())));
+ new DexValueString(dexItemFactory.createString(instrumentationOptions.getTag())));
}
return ImmutableList.of(
@@ -162,7 +160,8 @@
}
private boolean ensureClassInitializer(DexProgramClass clazz) {
- if (clazz.hasClassInitializer()) {
+ if (clazz.hasClassInitializer()
+ || !instrumentationOptions.isExecutedClassesAndMethodsInstrumentationEnabled()) {
return false;
}
ComputedApiLevel computedApiLevel =
@@ -191,7 +190,8 @@
instructionIterator.positionBeforeNextInstructionThatMatches(not(Instruction::isArgument));
// Insert invoke to record that the enclosing class is a startup class.
- if (method.getDefinition().isClassInitializer()) {
+ if (instrumentationOptions.isExecutedClassesAndMethodsInstrumentationEnabled()
+ && method.getDefinition().isClassInitializer()) {
DexType classToPrint = method.getHolderType();
Value descriptorValue =
instructionIterator.insertConstStringInstruction(
@@ -209,15 +209,16 @@
Value descriptorValue =
instructionIterator.insertConstStringInstruction(
appView, code, dexItemFactory.createString(method.getReference().toSmaliString()));
- instructionIterator.add(
- InvokeStatic.builder()
- .setMethod(references.addMethod)
- .setSingleArgument(descriptorValue)
- .setPosition(Position.syntheticNone())
- .build());
+ if (instrumentationOptions.isExecutedClassesAndMethodsInstrumentationEnabled()) {
+ instructionIterator.add(
+ InvokeStatic.builder()
+ .setMethod(references.addMethod)
+ .setSingleArgument(descriptorValue)
+ .setPosition(Position.syntheticNone())
+ .build());
+ }
- Set<DexMethod> callSitesToInstrument =
- startupInstrumentationOptions.getCallSitesToInstrument();
+ Set<DexMethod> callSitesToInstrument = instrumentationOptions.getCallSitesToInstrument();
if (!callSitesToInstrument.isEmpty()) {
do {
while (instructionIterator.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index bb72018..4644989 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -684,11 +684,7 @@
// actually equal.
GraphLens graphLens = appView.graphLens();
boolean includeContext =
- intermediate
- || appView
- .options()
- .getStartupInstrumentationOptions()
- .isStartupInstrumentationEnabled();
+ intermediate || appView.options().getInstrumentationOptions().isInstrumentationEnabled();
List<T> sortedPotentialMembers =
ListUtils.sort(
potentialEquivalence,
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 15701b3..f2492dc 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -263,6 +263,7 @@
proguardConfiguration = null;
enableTreeShaking = false;
enableMinification = false;
+ instrumentationOptions = new InstrumentationOptions(this);
}
// Constructor for D8, L8, Lint and other non-shrinkers.
@@ -275,6 +276,7 @@
enableTreeShaking = false;
enableMinification = false;
disableGlobalOptimizations();
+ instrumentationOptions = new InstrumentationOptions(this);
}
// Constructor for R8.
@@ -288,6 +290,7 @@
itemFactory = proguardConfiguration.getDexItemFactory();
enableTreeShaking = proguardConfiguration.isShrinking();
enableMinification = proguardConfiguration.isObfuscating();
+ instrumentationOptions = new InstrumentationOptions(this);
if (!proguardConfiguration.isOptimizing()) {
// TODO(b/171457102): Avoid the need for this.
@@ -946,7 +949,7 @@
private final MappingComposeOptions mappingComposeOptions = new MappingComposeOptions();
private final ArtProfileOptions artProfileOptions = new ArtProfileOptions(this);
private final StartupOptions startupOptions = new StartupOptions();
- private final InstrumentationOptions startupInstrumentationOptions = new InstrumentationOptions();
+ private final InstrumentationOptions instrumentationOptions;
public final TestingOptions testing = new TestingOptions();
public List<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
@@ -1042,8 +1045,8 @@
return startupOptions;
}
- public InstrumentationOptions getStartupInstrumentationOptions() {
- return startupInstrumentationOptions;
+ public InstrumentationOptions getInstrumentationOptions() {
+ return instrumentationOptions;
}
public TestingOptions getTestingOptions() {
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
index cd06774..c6cf0e3 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
@@ -91,9 +91,9 @@
.addOptionsModification(
options ->
options
- .getStartupInstrumentationOptions()
- .setEnableStartupInstrumentation()
- .setStartupInstrumentationTag("r8"))
+ .getInstrumentationOptions()
+ .setEnableExecutedClassesAndMethodsInstrumentation()
+ .setTag("r8"))
.enableCoreLibraryDesugaring(
LibraryDesugaringTestConfiguration.builder()
.addDesugaredLibraryConfiguration(
diff --git a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
index a8cf85a..742d9ce 100644
--- a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
@@ -78,9 +78,9 @@
.addOptionsModification(
options ->
options
- .getStartupInstrumentationOptions()
- .setEnableStartupInstrumentation()
- .setStartupInstrumentationTag("r8"))
+ .getInstrumentationOptions()
+ .setEnableExecutedClassesAndMethodsInstrumentation()
+ .setTag("r8"))
.setMinApi(apiLevel)
.release()
.compile()
@@ -98,9 +98,9 @@
.addOptionsModification(
options ->
options
- .getStartupInstrumentationOptions()
- .setEnableStartupInstrumentation()
- .setStartupInstrumentationTag("r8"))
+ .getInstrumentationOptions()
+ .setEnableExecutedClassesAndMethodsInstrumentation()
+ .setTag("r8"))
.setMinApi(apiLevel)
.release()
.compile()
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index 42d09b1..41c6730 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -101,10 +101,11 @@
.addOptionsModification(
options -> {
InstrumentationOptions startupInstrumentationOptions =
- options.getStartupInstrumentationOptions().setEnableStartupInstrumentation();
+ options
+ .getInstrumentationOptions()
+ .setEnableExecutedClassesAndMethodsInstrumentation();
if (logcat) {
- startupInstrumentationOptions.setStartupInstrumentationTag(
- startupInstrumentationTag);
+ startupInstrumentationOptions.setTag(startupInstrumentationTag);
}
})
.addLibraryFiles(parameters.getDefaultRuntimeLibrary())
diff --git a/tools/startup/instrument.py b/tools/startup/instrument.py
index 9752670..7e2870f 100755
--- a/tools/startup/instrument.py
+++ b/tools/startup/instrument.py
@@ -30,6 +30,14 @@
result.add_argument('--discard',
action='append',
help='Name of dex files to discard')
+ result.add_argument('--print-boxing-unboxing-callsites',
+ action='store_true',
+ default=False,
+ help='Print caller->callee edges for primitive boxing')
+ result.add_argument('--print-executed-classes-and-methods',
+ action='store_true',
+ default=False,
+ help='Print the classes and methods that are executed')
result.add_argument('--out',
help='Destination of resulting apk',
required=True)
@@ -52,14 +60,40 @@
tmp_dir):
d8_cmd = [
'java', '-cp', utils.R8_JAR,
- '-Dcom.android.tools.r8.startup.instrumentation.instrument=1',
- '-Dcom.android.tools.r8.startup.instrumentation.instrumentationtag=R8'
+ '-Dcom.android.tools.r8.instrumentation.tag=R8'
]
+ if options.print_boxing_unboxing_callsites:
+ callsites = ':'.join([
+ # Boxing
+ "Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;",
+ "Ljava/lang/Byte;->valueOf(B)Ljava/lang/Byte;",
+ "Ljava/lang/Character;->valueOf(C)Ljava/lang/Character;",
+ "Ljava/lang/Double;->valueOf(D)Ljava/lang/Double;",
+ "Ljava/lang/Float;->valueOf(F)Ljava/lang/Float;",
+ "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
+ "Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;",
+ "Ljava/lang/Short;->valueOf(S)Ljava/lang/Short;",
+ # Unboxing
+ "Ljava/lang/Boolean;->booleanValue()Z",
+ "Ljava/lang/Byte;->byteValue()B",
+ "Ljava/lang/Character;->charValue()C",
+ "Ljava/lang/Double;->doubleValue()D",
+ "Ljava/lang/Float;->floatValue()F",
+ "Ljava/lang/Integer;->intValue()I",
+ "Ljava/lang/Long;->longValue()J",
+ "Ljava/lang/Short;->shortValue()S"
+ ])
+ d8_cmd.append(
+ '-Dcom.android.tools.r8.instrumentation.callsites='
+ + callsites)
+ if options.print_executed_classes_and_methods:
+ d8_cmd.append(
+ '-Dcom.android.tools.r8.instrumentation.executedclassesandmethods=1')
if not include_instrumentation_server:
# We avoid injecting the InstrumentationServer by specifying it should only
# be added if foo.bar.Baz is in the program.
d8_cmd.append(
- '-Dcom.android.tools.r8.startup.instrumentation.instrumentationserversyntheticcontext=foo.bar.Baz'
+ '-Dcom.android.tools.r8.instrumentation.syntheticservercontext=foo.bar.Baz'
)
d8_cmd.extend([
'com.android.tools.r8.D8', '--min-api',