summaryrefslogtreecommitdiff
path: root/tools/traceinjection/test
diff options
context:
space:
mode:
author Michael Wright <michaelwr@google.com> 2022-02-11 22:16:19 +0000
committer Michael Wright <michaelwr@google.com> 2022-03-01 23:45:26 +0000
commit4fd5fae75fbfd44ddf6f978a9bd076372e0f0f6f (patch)
tree23265c4c724d9d1676b1adc123b6402c2f427b74 /tools/traceinjection/test
parentc95042682ff2bf76d499bf565ff0fec14236682f (diff)
Add tool for injecting tracing code into a method.
This tool rewrites the bytecode in the designated JAR files to produce tracing calls on enter and exit, while making sure to close the tracing span even on exceptions being thrown. The idea is mostly to reduce the amount of noise within methods when just trying to add some tracing. Test: atest --host TraceInjectionTests Change-Id: If6acb72f34cbb83d9b041a62ee3d8c2abf74b69e
Diffstat (limited to 'tools/traceinjection/test')
-rw-r--r--tools/traceinjection/test/com/android/traceinjection/InjectionTests.java246
-rw-r--r--tools/traceinjection/test/com/android/traceinjection/Trace.java22
2 files changed, 268 insertions, 0 deletions
diff --git a/tools/traceinjection/test/com/android/traceinjection/InjectionTests.java b/tools/traceinjection/test/com/android/traceinjection/InjectionTests.java
new file mode 100644
index 000000000000..81bf235fe0a6
--- /dev/null
+++ b/tools/traceinjection/test/com/android/traceinjection/InjectionTests.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.traceinjection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(JUnit4.class)
+public class InjectionTests {
+ public static final int TRACE_TAG = 42;
+ public static final String CUSTOM_TRACE_NAME = "Custom";
+
+ public static final TraceTracker TRACKER = new TraceTracker();
+
+ @After
+ public void tearDown() {
+ TRACKER.reset();
+ }
+
+ @Test
+ public void testDefaultLabel() {
+ assertTraces(this::tracedMethod, "tracedMethod");
+ tracedMethodThrowsAndCatches();
+ }
+
+ @Test
+ public void testCustomLabel() {
+ assertTraces(this::tracedMethodHasCustomName, CUSTOM_TRACE_NAME);
+ }
+
+ @Test
+ public void testTracedMethodsStillThrow() {
+ assertTraces(() -> assertThrows(IllegalArgumentException.class, this::tracedMethodThrows),
+ "tracedMethodThrows");
+ // Also test that we rethrow exceptions from method calls. This is slightly different from
+ // the previous case because the ATHROW instruction is not actually present at all in the
+ // bytecode of the instrumented method.
+ TRACKER.reset();
+ assertTraces(() -> assertThrows(NullPointerException.class,
+ this::tracedMethodCallsThrowingMethod),
+ "tracedMethodCallsThrowingMethod");
+ }
+
+ @Test
+ public void testNestedTracedMethods() {
+ assertTraces(this::outerTracedMethod, "outerTracedMethod", "innerTracedMethod");
+ }
+
+ @Test
+ public void testTracedMethodWithCatchBlock() {
+ assertTraces(this::tracedMethodThrowsAndCatches, "tracedMethodThrowsAndCatches");
+ }
+
+ @Test
+ public void testTracedMethodWithFinallyBlock() {
+ assertTraces(() -> assertThrows(IllegalArgumentException.class,
+ this::tracedMethodThrowWithFinally), "tracedMethodThrowWithFinally");
+ }
+
+ @Test
+ public void testNonVoidMethod() {
+ assertTraces(this::tracedNonVoidMethod, "tracedNonVoidMethod");
+ }
+
+ @Test
+ public void testNonVoidMethodReturnsWithinCatches() {
+ assertTraces(this::tracedNonVoidMethodReturnsWithinCatches,
+ "tracedNonVoidMethodReturnsWithinCatches");
+ }
+
+ @Test
+ public void testNonVoidMethodReturnsWithinFinally() {
+ assertTraces(this::tracedNonVoidMethodReturnsWithinFinally,
+ "tracedNonVoidMethodReturnsWithinFinally");
+ }
+
+ @Test
+ public void testTracedStaticMethod() {
+ assertTraces(InjectionTests::tracedStaticMethod, "tracedStaticMethod");
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethod() {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethodThrows() {
+ throw new IllegalArgumentException();
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethodCallsThrowingMethod() {
+ throwingMethod();
+ }
+
+ private void throwingMethod() {
+ throw new NullPointerException();
+ }
+
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethodThrowsAndCatches() {
+ try {
+ throw new IllegalArgumentException();
+ } catch (IllegalArgumentException ignored) {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethodThrowWithFinally() {
+ try {
+ throw new IllegalArgumentException();
+ } finally {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+ }
+
+ @Trace(tag = TRACE_TAG, label = CUSTOM_TRACE_NAME)
+ public void tracedMethodHasCustomName() {
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void outerTracedMethod() {
+ innerTracedMethod();
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void innerTracedMethod() {
+ assertEquals(2, TRACKER.getTraceCount(TRACE_TAG));
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public int tracedNonVoidMethod() {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ return 0;
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public int tracedNonVoidMethodReturnsWithinCatches() {
+ try {
+ throw new IllegalArgumentException();
+ } catch (IllegalArgumentException ignored) {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ return 0;
+ }
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public int tracedNonVoidMethodReturnsWithinFinally() {
+ try {
+ throw new IllegalArgumentException();
+ } finally {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ return 0;
+ }
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public static void tracedStaticMethod() {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+
+ public void assertTraces(Runnable r, String... traceLabels) {
+ r.run();
+ assertEquals(Arrays.asList(traceLabels), TRACKER.getTraceLabels(TRACE_TAG));
+ TRACKER.assertAllTracesClosed();
+ }
+
+ public static void traceStart(long tag, String name) {
+ TRACKER.onTraceStart(tag, name);
+ }
+
+ public static void traceEnd(long tag) {
+ TRACKER.onTraceEnd(tag);
+ }
+
+ static class TraceTracker {
+ private final Map<Long, List<String>> mTraceLabelsByTag = new HashMap<>();
+ private final Map<Long, Integer> mTraceCountsByTag = new HashMap<>();
+
+ public void onTraceStart(long tag, String name) {
+ getTraceLabels(tag).add(name);
+ mTraceCountsByTag.put(tag, mTraceCountsByTag.getOrDefault(tag, 0) + 1);
+ }
+
+ public void onTraceEnd(long tag) {
+ final int newCount = getTraceCount(tag) - 1;
+ if (newCount < 0) {
+ throw new IllegalStateException("Trace count has gone negative for tag " + tag);
+ }
+ mTraceCountsByTag.put(tag, newCount);
+ }
+
+ public void reset() {
+ mTraceLabelsByTag.clear();
+ mTraceCountsByTag.clear();
+ }
+
+ public List<String> getTraceLabels(long tag) {
+ if (!mTraceLabelsByTag.containsKey(tag)) {
+ mTraceLabelsByTag.put(tag, new ArrayList<>());
+ }
+ return mTraceLabelsByTag.get(tag);
+ }
+
+ public int getTraceCount(long tag) {
+ return mTraceCountsByTag.getOrDefault(tag, 0);
+ }
+
+ public void assertAllTracesClosed() {
+ for (Map.Entry<Long, Integer> count: mTraceCountsByTag.entrySet()) {
+ final String errorMsg = "Tag " + count.getKey() + " is not fully closed (count="
+ + count.getValue() + ")";
+ assertEquals(errorMsg, 0, (int) count.getValue());
+ }
+ }
+ }
+}
diff --git a/tools/traceinjection/test/com/android/traceinjection/Trace.java b/tools/traceinjection/test/com/android/traceinjection/Trace.java
new file mode 100644
index 000000000000..9e1c545673e8
--- /dev/null
+++ b/tools/traceinjection/test/com/android/traceinjection/Trace.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.traceinjection;
+
+public @interface Trace {
+ long tag();
+ String label() default "";
+}