Support for logging calls to logcat
Change-Id: Id50c631949fb0ae9d459891a654bb9cc240eb19b
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 192178b..521d784 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
@@ -5,6 +5,7 @@
package com.android.tools.r8.profile.startup.instrumentation;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.utils.PredicateUtils.not;
import com.android.tools.r8.androidapi.ComputedApiLevel;
@@ -17,14 +18,16 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue.DexValueBoolean;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -44,6 +47,7 @@
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -67,7 +71,8 @@
public static void run(AppView<AppInfo> appView, ExecutorService executorService)
throws ExecutionException {
- if (appView.options().getStartupInstrumentationOptions().isStartupInstrumentationEnabled()) {
+ if (appView.options().getStartupInstrumentationOptions().isStartupInstrumentationEnabled()
+ && appView.options().isGeneratingDex()) {
StartupInstrumentation startupInstrumentation = new StartupInstrumentation(appView);
startupInstrumentation.instrumentAllClasses(executorService);
startupInstrumentation.injectStartupRuntimeLibrary(executorService);
@@ -126,6 +131,10 @@
.lookupUniqueStaticFieldWithName(dexItemFactory.createString("writeToLogcat"))
.setStaticValue(DexValueBoolean.create(true));
instrumentationServerImplClass
+ .lookupUniqueStaticFieldWithName(
+ dexItemFactory.createString("writeToLogcatIncludeDuplicates"))
+ .setStaticValue(DexValueBoolean.create(true));
+ instrumentationServerImplClass
.lookupUniqueStaticFieldWithName(dexItemFactory.createString("logcatTag"))
.setStaticValue(
new DexValueString(
@@ -137,11 +146,10 @@
InstrumentationServerFactory.createClass(dexItemFactory), instrumentationServerImplClass);
}
- @SuppressWarnings("ReferenceEquality")
private void instrumentClass(DexProgramClass clazz) {
// Do not instrument the instrumentation server if it is already in the app.
- if (clazz.getType() == references.instrumentationServerType
- || clazz.getType() == references.instrumentationServerImplType) {
+ if (clazz.getType().isIdenticalTo(references.instrumentationServerType)
+ || clazz.getType().isIdenticalTo(references.instrumentationServerImplType)) {
return;
}
@@ -178,7 +186,8 @@
// finalizing the code.
MutableMethodConversionOptions conversionOptions = MethodConversionOptions.forD8(appView);
IRCode code = method.buildIR(appView, conversionOptions);
- InstructionListIterator instructionIterator = code.entryBlock().listIterator(code);
+ BasicBlockIterator blocks = code.listIterator();
+ InstructionListIterator instructionIterator = blocks.next().listIterator(code);
instructionIterator.positionBeforeNextInstructionThatMatches(not(Instruction::isArgument));
// Insert invoke to record that the enclosing class is a startup class.
@@ -197,18 +206,59 @@
// Insert invoke to record the execution of the current method.
if (!skipMethodLogging) {
- DexReference referenceToPrint = method.getReference();
Value descriptorValue =
instructionIterator.insertConstStringInstruction(
- appView, code, dexItemFactory.createString(referenceToPrint.toSmaliString()));
+ appView, code, dexItemFactory.createString(method.getReference().toSmaliString()));
instructionIterator.add(
InvokeStatic.builder()
.setMethod(references.addMethod)
.setSingleArgument(descriptorValue)
.setPosition(Position.syntheticNone())
.build());
+
+ Set<DexMethod> callSitesToInstrument =
+ startupInstrumentationOptions.getCallSitesToInstrument();
+ if (!callSitesToInstrument.isEmpty()) {
+ do {
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ if (instruction.isInvokeMethod()) {
+ DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ if (callSitesToInstrument.contains(invokedMethod)) {
+ instructionIterator.previous();
+ ConstString calleeString =
+ ConstString.builder()
+ .setFreshOutValue(
+ code,
+ dexItemFactory.stringType.toTypeElement(appView, definitelyNotNull()))
+ .setPosition(instruction.getPosition())
+ .setValue(dexItemFactory.createString(invokedMethod.toSmaliString()))
+ .build();
+ instructionIterator =
+ instructionIterator.addPossiblyThrowingInstructionsToPossiblyThrowingBlock(
+ code,
+ blocks,
+ ImmutableList.of(
+ calleeString,
+ InvokeStatic.builder()
+ .setMethod(references.addCall)
+ .setArguments(descriptorValue, calleeString.outValue())
+ .setPosition(instruction.getPosition())
+ .build()),
+ options);
+ Instruction next = instructionIterator.next();
+ assert next == instruction;
+ }
+ }
+ }
+ if (blocks.hasNext()) {
+ instructionIterator = blocks.next().listIterator(code);
+ }
+ } while (instructionIterator.hasNext());
+ }
}
+ code.removeRedundantBlocks();
converter.deadCodeRemover.run(code, Timing.empty());
DexCode instrumentedCode =
diff --git a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentationOptions.java b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentationOptions.java
index ad94406..4a11409 100644
--- a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentationOptions.java
+++ b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentationOptions.java
@@ -7,8 +7,15 @@
import static com.android.tools.r8.utils.SystemPropertyUtils.getSystemPropertyForDevelopment;
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
+import com.android.tools.r8.graph.DexMethod;
+import java.util.Collections;
+import java.util.Set;
+
public class StartupInstrumentationOptions {
+ /** Set of method references where all calls to the exact method reference should print. */
+ private Set<DexMethod> callSitesToInstrument = Collections.emptySet();
+
/**
* When enabled, each method will be instrumented to notify the startup InstrumentationServer that
* it has been executed.
@@ -45,6 +52,14 @@
getSystemPropertyForDevelopment(
"com.android.tools.r8.startup.instrumentation.instrumentationtag");
+ public Set<DexMethod> getCallSitesToInstrument() {
+ return callSitesToInstrument;
+ }
+
+ public void setCallSitesToInstrument(Set<DexMethod> callSitesToInstrument) {
+ this.callSitesToInstrument = callSitesToInstrument;
+ }
+
public boolean hasStartupInstrumentationServerSyntheticContext() {
return startupInstrumentationServerSyntheticContext != null;
}
diff --git a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentationReferences.java b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentationReferences.java
index 78d55f0..7b180a8 100644
--- a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentationReferences.java
+++ b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentationReferences.java
@@ -12,6 +12,7 @@
final DexType instrumentationServerType;
final DexType instrumentationServerImplType;
+ final DexMethod addCall;
final DexMethod addMethod;
StartupInstrumentationReferences(DexItemFactory dexItemFactory) {
@@ -19,6 +20,12 @@
dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;");
instrumentationServerImplType =
dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;");
+ addCall =
+ dexItemFactory.createMethod(
+ instrumentationServerImplType,
+ dexItemFactory.createProto(
+ dexItemFactory.voidType, dexItemFactory.stringType, dexItemFactory.stringType),
+ "addCall");
addMethod =
dexItemFactory.createMethod(
instrumentationServerImplType,
diff --git a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
index e9d64ab..f6d1bf4 100644
--- a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
+++ b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
@@ -126,6 +126,16 @@
dexItemFactory.createField(
dexItemFactory.createType(
"Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ dexItemFactory.createType("Z"),
+ dexItemFactory.createString("writeToLogcatIncludeDuplicates")))
+ .setAccessFlags(FieldAccessFlags.fromCfAccessFlags(10))
+ .setApiLevel(ComputedApiLevel.unknown())
+ .build(),
+ DexEncodedField.syntheticBuilder()
+ .setField(
+ dexItemFactory.createField(
+ dexItemFactory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
dexItemFactory.createType("Ljava/lang/String;"),
dexItemFactory.createString("logcatTag")))
.setAccessFlags(FieldAccessFlags.fromCfAccessFlags(10))
@@ -162,7 +172,23 @@
dexItemFactory.createType(
"Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
dexItemFactory.createString("getInstance")))
- .setCode(method -> createCfCode4_getInstance(dexItemFactory, method))
+ .setCode(method -> createCfCode5_getInstance(dexItemFactory, method))
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
+ .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(9, false))
+ .setApiLevelForCode(ComputedApiLevel.unknown())
+ .setApiLevelForDefinition(ComputedApiLevel.unknown())
+ .setClassFileVersion(CfVersion.V1_8)
+ .setMethod(
+ dexItemFactory.createMethod(
+ dexItemFactory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ dexItemFactory.createProto(
+ dexItemFactory.createType("V"),
+ dexItemFactory.createType("Ljava/lang/String;"),
+ dexItemFactory.createType("Ljava/lang/String;")),
+ dexItemFactory.createString("addCall")))
+ .setCode(method -> createCfCode2_addCall(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(9, false))
@@ -177,7 +203,7 @@
dexItemFactory.createType("V"),
dexItemFactory.createType("Ljava/lang/String;")),
dexItemFactory.createString("addMethod")))
- .setCode(method -> createCfCode3_addMethod(dexItemFactory, method))
+ .setCode(method -> createCfCode4_addMethod(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(2, false))
@@ -192,10 +218,10 @@
dexItemFactory.createType("V"),
dexItemFactory.createType("Ljava/lang/String;")),
dexItemFactory.createString("addLine")))
- .setCode(method -> createCfCode2_addLine(dexItemFactory, method))
+ .setCode(method -> createCfCode3_addLine(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
- .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(2, false))
+ .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(10, false))
.setApiLevelForCode(ComputedApiLevel.unknown())
.setApiLevelForDefinition(ComputedApiLevel.unknown())
.setClassFileVersion(CfVersion.V1_8)
@@ -207,7 +233,7 @@
dexItemFactory.createType("V"),
dexItemFactory.createType("Ljava/lang/String;")),
dexItemFactory.createString("writeToLogcat")))
- .setCode(method -> createCfCode6_writeToLogcat(dexItemFactory, method))
+ .setCode(method -> createCfCode7_writeToLogcat(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(8, true))
@@ -239,7 +265,7 @@
dexItemFactory.createProto(
dexItemFactory.createType("V"), dexItemFactory.createType("Ljava/io/File;")),
dexItemFactory.createString("writeToFile")))
- .setCode(method -> createCfCode5_writeToFile(dexItemFactory, method))
+ .setCode(method -> createCfCode6_writeToFile(dexItemFactory, method))
.build()
};
}
@@ -314,7 +340,71 @@
ImmutableList.of());
}
- public static CfCode createCfCode2_addLine(DexItemFactory factory, DexMethod method) {
+ public static CfCode createCfCode2_addCall(DexItemFactory factory, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 2,
+ 2,
+ ImmutableList.of(
+ label0,
+ new CfNew(factory.stringBuilderType),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ 183,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.voidType),
+ factory.createString("<init>")),
+ false),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.stringBuilderType, factory.stringType),
+ factory.createString("append")),
+ false),
+ new CfConstString(factory.createString(" -> ")),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.stringBuilderType, factory.stringType),
+ factory.createString("append")),
+ false),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.stringBuilderType, factory.stringType),
+ factory.createString("append")),
+ false),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.stringType),
+ factory.createString("toString")),
+ false),
+ new CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ factory.createProto(factory.voidType, factory.stringType),
+ factory.createString("writeToLogcat")),
+ false),
+ label1,
+ new CfReturnVoid(),
+ label2),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode createCfCode3_addLine(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -327,12 +417,48 @@
CfLabel label9 = new CfLabel();
CfLabel label10 = new CfLabel();
CfLabel label11 = new CfLabel();
+ CfLabel label12 = new CfLabel();
+ CfLabel label13 = new CfLabel();
+ CfLabel label14 = new CfLabel();
return new CfCode(
method.holder,
2,
4,
ImmutableList.of(
label0,
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ factory.booleanType,
+ factory.createString("writeToLogcat"))),
+ new CfIf(IfType.EQ, ValueType.INT, label3),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ factory.booleanType,
+ factory.createString("writeToLogcatIncludeDuplicates"))),
+ new CfIf(IfType.EQ, ValueType.INT, label3),
+ label1,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ factory.createProto(factory.voidType, factory.stringType),
+ factory.createString("writeToLogcat")),
+ false),
+ label2,
+ new CfReturnVoid(),
+ label3,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ FrameType.initializedNonNullReference(factory.stringType)
+ })),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
@@ -342,7 +468,7 @@
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfStore(ValueType.OBJECT, 2),
new CfMonitor(MonitorType.ENTER),
- label1,
+ label4,
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
@@ -357,13 +483,13 @@
factory.createProto(factory.booleanType, factory.objectType),
factory.createString("add")),
false),
- new CfIf(IfType.NE, ValueType.INT, label4),
- label2,
+ new CfIf(IfType.NE, ValueType.INT, label7),
+ label5,
new CfLoad(ValueType.OBJECT, 2),
new CfMonitor(MonitorType.EXIT),
- label3,
+ label6,
new CfReturnVoid(),
- label4,
+ label7,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
@@ -376,9 +502,9 @@
})),
new CfLoad(ValueType.OBJECT, 2),
new CfMonitor(MonitorType.EXIT),
- label5,
- new CfGoto(label8),
- label6,
+ label8,
+ new CfGoto(label11),
+ label9,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
@@ -394,10 +520,10 @@
new CfStore(ValueType.OBJECT, 3),
new CfLoad(ValueType.OBJECT, 2),
new CfMonitor(MonitorType.EXIT),
- label7,
+ label10,
new CfLoad(ValueType.OBJECT, 3),
new CfThrow(),
- label8,
+ label11,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
@@ -412,18 +538,17 @@
factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
factory.booleanType,
factory.createString("writeToLogcat"))),
- new CfIf(IfType.EQ, ValueType.INT, label10),
- label9,
- new CfLoad(ValueType.OBJECT, 0),
+ new CfIf(IfType.EQ, ValueType.INT, label13),
+ label12,
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
- 183,
+ 184,
factory.createMethod(
factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
factory.createProto(factory.voidType, factory.stringType),
factory.createString("writeToLogcat")),
false),
- label10,
+ label13,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
@@ -434,18 +559,21 @@
FrameType.initializedNonNullReference(factory.stringType)
})),
new CfReturnVoid(),
- label11),
+ label14),
ImmutableList.of(
new CfTryCatch(
- label1, label3, ImmutableList.of(factory.throwableType), ImmutableList.of(label6)),
+ label4, label6, ImmutableList.of(factory.throwableType), ImmutableList.of(label9)),
new CfTryCatch(
- label4, label5, ImmutableList.of(factory.throwableType), ImmutableList.of(label6)),
+ label7, label8, ImmutableList.of(factory.throwableType), ImmutableList.of(label9)),
new CfTryCatch(
- label6, label7, ImmutableList.of(factory.throwableType), ImmutableList.of(label6))),
+ label9,
+ label10,
+ ImmutableList.of(factory.throwableType),
+ ImmutableList.of(label9))),
ImmutableList.of());
}
- public static CfCode createCfCode3_addMethod(DexItemFactory factory, DexMethod method) {
+ public static CfCode createCfCode4_addMethod(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -479,7 +607,7 @@
ImmutableList.of());
}
- public static CfCode createCfCode4_getInstance(DexItemFactory factory, DexMethod method) {
+ public static CfCode createCfCode5_getInstance(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
return new CfCode(
method.holder,
@@ -497,7 +625,7 @@
ImmutableList.of());
}
- public static CfCode createCfCode5_writeToFile(DexItemFactory factory, DexMethod method) {
+ public static CfCode createCfCode6_writeToFile(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -724,14 +852,14 @@
ImmutableList.of());
}
- public static CfCode createCfCode6_writeToLogcat(DexItemFactory factory, DexMethod method) {
+ public static CfCode createCfCode7_writeToLogcat(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
return new CfCode(
method.holder,
2,
- 2,
+ 1,
ImmutableList.of(
label0,
new CfStaticFieldRead(
@@ -739,7 +867,7 @@
factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
factory.stringType,
factory.createString("logcatTag"))),
- new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
184,
factory.createMethod(
diff --git a/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java b/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
index 1f19f3c..11e586a 100644
--- a/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
+++ b/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
@@ -15,6 +15,7 @@
// May be set to true by the instrumentation.
private static boolean writeToLogcat;
+ private static boolean writeToLogcatIncludeDuplicates;
private static String logcatTag;
private final LinkedHashSet<String> lines = new LinkedHashSet<>();
@@ -25,11 +26,19 @@
return InstrumentationServerImpl.INSTANCE;
}
+ public static void addCall(String callerDescriptor, String calleeDescriptor) {
+ writeToLogcat(callerDescriptor + " -> " + calleeDescriptor);
+ }
+
public static void addMethod(String descriptor) {
getInstance().addLine(descriptor);
}
private void addLine(String line) {
+ if (writeToLogcat && writeToLogcatIncludeDuplicates) {
+ writeToLogcat(line);
+ return;
+ }
synchronized (lines) {
if (!lines.add(line)) {
return;
@@ -54,7 +63,7 @@
}
}
- private void writeToLogcat(String line) {
+ private static void writeToLogcat(String line) {
android.util.Log.i(logcatTag, line);
}
}