Add tests for field access and modify JVMTI callbacks
Test: ./test.py --host -j40
Change-Id: Id0af65ef217f49dbef0ff09846d69113b1c786c5
diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt
index d3d9249..30ad532 100644
--- a/test/988-method-trace/expected.txt
+++ b/test/988-method-trace/expected.txt
@@ -1,4 +1,5 @@
-<= public static native void art.Trace.enableMethodTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
+.<= public static native void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
+<= public static void art.Trace.enableMethodTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
=> art.Test988$IterOp()
.=> public java.lang.Object()
.<= public java.lang.Object() -> <null: null>
@@ -142,10 +143,10 @@
......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- at art.Test988.iter_fibonacci(Test988.java:207)
- at art.Test988$IterOp.applyAsInt(Test988.java:202)
- at art.Test988.doFibTest(Test988.java:295)
- at art.Test988.run(Test988.java:265)
+ at art.Test988.iter_fibonacci(Test988.java:209)
+ at art.Test988$IterOp.applyAsInt(Test988.java:204)
+ at art.Test988.doFibTest(Test988.java:297)
+ at art.Test988.run(Test988.java:267)
at Main.main(Main.java:19)
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -162,10 +163,10 @@
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- at art.Test988.iter_fibonacci(Test988.java:207)
- at art.Test988$IterOp.applyAsInt(Test988.java:202)
- at art.Test988.doFibTest(Test988.java:295)
- at art.Test988.run(Test988.java:265)
+ at art.Test988.iter_fibonacci(Test988.java:209)
+ at art.Test988$IterOp.applyAsInt(Test988.java:204)
+ at art.Test988.doFibTest(Test988.java:297)
+ at art.Test988.run(Test988.java:267)
at Main.main(Main.java:19)
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
@@ -243,10 +244,10 @@
......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- at art.Test988.fibonacci(Test988.java:229)
- at art.Test988$RecurOp.applyAsInt(Test988.java:224)
- at art.Test988.doFibTest(Test988.java:295)
- at art.Test988.run(Test988.java:266)
+ at art.Test988.fibonacci(Test988.java:231)
+ at art.Test988$RecurOp.applyAsInt(Test988.java:226)
+ at art.Test988.doFibTest(Test988.java:297)
+ at art.Test988.run(Test988.java:268)
at Main.main(Main.java:19)
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -263,14 +264,14 @@
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- at art.Test988.fibonacci(Test988.java:229)
- at art.Test988$RecurOp.applyAsInt(Test988.java:224)
- at art.Test988.doFibTest(Test988.java:295)
- at art.Test988.run(Test988.java:266)
+ at art.Test988.fibonacci(Test988.java:231)
+ at art.Test988$RecurOp.applyAsInt(Test988.java:226)
+ at art.Test988.doFibTest(Test988.java:297)
+ at art.Test988.run(Test988.java:268)
at Main.main(Main.java:19)
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> public static native java.lang.Thread java.lang.Thread.currentThread()
<= public static native java.lang.Thread java.lang.Thread.currentThread() -> <class java.lang.Thread: <non-deterministic>>
-=> public static native void art.Trace.disableMethodTracing(java.lang.Thread)
+=> public static native void art.Trace.disableTracing(java.lang.Thread)
diff --git a/test/988-method-trace/src/art/Test988.java b/test/988-method-trace/src/art/Test988.java
index 37ff136..6a45c0e 100644
--- a/test/988-method-trace/src/art/Test988.java
+++ b/test/988-method-trace/src/art/Test988.java
@@ -194,7 +194,9 @@
}
private static List<Printable> results = new ArrayList<>();
- private static int cnt = 1;
+ // Starts with => enableMethodTracing
+ // .=> enableTracing
+ private static int cnt = 2;
// Iterative version
static final class IterOp implements IntUnaryOperator {
@@ -253,7 +255,7 @@
public static void run() throws Exception {
// call this here so it is linked. It doesn't actually do anything here.
loadAllClasses();
- Trace.disableMethodTracing(Thread.currentThread());
+ Trace.disableTracing(Thread.currentThread());
Trace.enableMethodTracing(
Test988.class,
Test988.class.getDeclaredMethod("notifyMethodEntry", Object.class),
@@ -265,7 +267,7 @@
doFibTest(-19, new IterOp());
doFibTest(-19, new RecurOp());
// Turn off method tracing so we don't have to deal with print internals.
- Trace.disableMethodTracing(Thread.currentThread());
+ Trace.disableTracing(Thread.currentThread());
printResults();
}
diff --git a/test/988-method-trace/src/art/Trace.java b/test/988-method-trace/src/art/Trace.java
index 3370996..9c27c9f 100644
--- a/test/988-method-trace/src/art/Trace.java
+++ b/test/988-method-trace/src/art/Trace.java
@@ -16,10 +16,34 @@
package art;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Trace {
- public static native void enableMethodTracing(
- Class<?> methodClass, Method entryMethod, Method exitMethod, Thread thr);
- public static native void disableMethodTracing(Thread thr);
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
}
diff --git a/test/989-method-trace-throw/src/art/Test989.java b/test/989-method-trace-throw/src/art/Test989.java
index 18421bd..4feb29c 100644
--- a/test/989-method-trace-throw/src/art/Test989.java
+++ b/test/989-method-trace-throw/src/art/Test989.java
@@ -56,7 +56,7 @@
// to an infinite loop on the RI.
private static void disableTraceForRI() {
if (!System.getProperty("java.vm.name").equals("Dalvik")) {
- Trace.disableMethodTracing(Thread.currentThread());
+ Trace.disableTracing(Thread.currentThread());
}
}
@@ -158,7 +158,7 @@
private static void maybeDisableTracing() throws Exception {
if (DISABLE_TRACING) {
- Trace.disableMethodTracing(Thread.currentThread());
+ Trace.disableTracing(Thread.currentThread());
}
}
@@ -179,7 +179,7 @@
}
private static void setEntry(MethodTracer type) throws Exception {
if (DISABLE_TRACING || !System.getProperty("java.vm.name").equals("Dalvik")) {
- Trace.disableMethodTracing(Thread.currentThread());
+ Trace.disableTracing(Thread.currentThread());
setupTracing();
}
currentTracer = type;
@@ -274,7 +274,7 @@
maybeDisableTracing();
System.out.println("Finished!");
- Trace.disableMethodTracing(Thread.currentThread());
+ Trace.disableTracing(Thread.currentThread());
}
private static final class throwAClass implements MyRunnable {
diff --git a/test/989-method-trace-throw/src/art/Trace.java b/test/989-method-trace-throw/src/art/Trace.java
index 3370996..9c27c9f 100644
--- a/test/989-method-trace-throw/src/art/Trace.java
+++ b/test/989-method-trace-throw/src/art/Trace.java
@@ -16,10 +16,34 @@
package art;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Trace {
- public static native void enableMethodTracing(
- Class<?> methodClass, Method entryMethod, Method exitMethod, Thread thr);
- public static native void disableMethodTracing(Thread thr);
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
}
diff --git a/test/990-field-trace/expected.txt b/test/990-field-trace/expected.txt
new file mode 100644
index 0000000..cceb008
--- /dev/null
+++ b/test/990-field-trace/expected.txt
@@ -0,0 +1,52 @@
+MODIFY of int art.Test990$TestClass1.xyz on object of type: class art.Test990$TestClass1 in method public art.Test990$TestClass1(int,java.lang.Object). New value: 1 (type: class java.lang.Integer)
+MODIFY of java.lang.Object art.Test990$TestClass1.abc on object of type: class art.Test990$TestClass1 in method public art.Test990$TestClass1(int,java.lang.Object). New value: tc1 (type: class java.lang.String)
+MODIFY of static long art.Test990$TestClass2.TOTAL on object of type: null in method art.Test990$TestClass2(). New value: 0 (type: class java.lang.Long)
+MODIFY of int art.Test990$TestClass1.xyz on object of type: class art.Test990$TestClass2 in method public art.Test990$TestClass1(int,java.lang.Object). New value: 1337 (type: class java.lang.Integer)
+MODIFY of java.lang.Object art.Test990$TestClass1.abc on object of type: class art.Test990$TestClass2 in method public art.Test990$TestClass1(int,java.lang.Object). New value: TESTING (type: class java.lang.String)
+MODIFY of long art.Test990$TestClass2.baz on object of type: class art.Test990$TestClass2 in method public art.Test990$TestClass2(long). New value: 2 (type: class java.lang.Long)
+MODIFY of int art.Test990$TestClass1.xyz on object of type: class art.Test990$TestClass1 in method public art.Test990$TestClass1(int,java.lang.Object). New value: 3 (type: class java.lang.Integer)
+MODIFY of java.lang.Object art.Test990$TestClass1.abc on object of type: class art.Test990$TestClass1 in method public art.Test990$TestClass1(int,java.lang.Object). New value: TestClass1 { abc: "tc1", xyz: 1, foobar: 0 } (type: class art.Test990$TestClass1)
+MODIFY of int art.Test990$TestClass1.xyz on object of type: class art.Test990$TestClass1 in method public art.Test990$TestClass1(int,java.lang.Object). New value: 4 (type: class java.lang.Integer)
+MODIFY of java.lang.Object art.Test990$TestClass1.abc on object of type: class art.Test990$TestClass1 in method public art.Test990$TestClass1(int,java.lang.Object). New value: TestClass1 { abc: "TESTING", xyz: 1337, foobar: 0 } (type: class art.Test990$TestClass2)
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of int art.Test990$TestClass1.foobar on object of type class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int)
+MODIFY of int art.Test990$TestClass1.foobar on object of type: class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int). New value: 1 (type: class java.lang.Integer)
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of int art.Test990$TestClass1.foobar on object of type class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int)
+MODIFY of int art.Test990$TestClass1.foobar on object of type: class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int). New value: 2 (type: class java.lang.Integer)
+ACCESS of static long art.Test990$TestClass2.TOTAL on object of type null in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of static long art.Test990$TestClass2.TOTAL on object of type: null in method public void art.Test990$TestClass2.tweak(int). New value: 1 (type: class java.lang.Long)
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of long art.Test990$TestClass2.baz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of long art.Test990$TestClass2.baz on object of type: class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int). New value: 3 (type: class java.lang.Long)
+ACCESS of static long art.Test990$TestClass2.TOTAL on object of type null in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of static long art.Test990$TestClass2.TOTAL on object of type: null in method public void art.Test990$TestClass2.tweak(int). New value: 2 (type: class java.lang.Long)
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of int art.Test990$TestClass1.foobar on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass1.tweak(int)
+MODIFY of int art.Test990$TestClass1.foobar on object of type: class art.Test990$TestClass2 in method public void art.Test990$TestClass1.tweak(int). New value: 1 (type: class java.lang.Integer)
+ACCESS of long art.Test990$TestClass2.baz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of long art.Test990$TestClass2.baz on object of type: class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int). New value: 4 (type: class java.lang.Long)
+ACCESS of static long art.Test990$TestClass2.TOTAL on object of type null in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of static long art.Test990$TestClass2.TOTAL on object of type: null in method public void art.Test990$TestClass2.tweak(int). New value: 3 (type: class java.lang.Long)
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of long art.Test990$TestClass2.baz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of long art.Test990$TestClass2.baz on object of type: class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int). New value: 5 (type: class java.lang.Long)
+ACCESS of static long art.Test990$TestClass2.TOTAL on object of type null in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of static long art.Test990$TestClass2.TOTAL on object of type: null in method public void art.Test990$TestClass2.tweak(int). New value: 4 (type: class java.lang.Long)
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of long art.Test990$TestClass2.baz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of long art.Test990$TestClass2.baz on object of type: class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int). New value: 6 (type: class java.lang.Long)
+ACCESS of int art.Test990$TestClass1.foobar on object of type class art.Test990$TestClass1 in method public static void art.Test990.run() throws java.lang.Exception
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of long art.Test990$TestClass2.baz on object of type class art.Test990$TestClass2 in method public static void art.Test990.run() throws java.lang.Exception
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of static long art.Test990$TestClass2.TOTAL on object of type null in method public static void art.Test990.run() throws java.lang.Exception
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of int art.Test990$TestClass1.foobar on object of type class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int)
+MODIFY of int art.Test990$TestClass1.foobar on object of type: class art.Test990$TestClass1 in method public void art.Test990$TestClass1.tweak(int). New value: 1 (type: class java.lang.Integer)
+ACCESS of static long art.Test990$TestClass2.TOTAL on object of type null in method public static void art.Test990.run() throws java.lang.Exception
+ACCESS of static long art.Test990$TestClass2.TOTAL on object of type null in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of static long art.Test990$TestClass2.TOTAL on object of type: null in method public void art.Test990$TestClass2.tweak(int). New value: 5 (type: class java.lang.Long)
+ACCESS of int art.Test990$TestClass1.xyz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass1.tweak(int)
+ACCESS of long art.Test990$TestClass2.baz on object of type class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int)
+MODIFY of long art.Test990$TestClass2.baz on object of type: class art.Test990$TestClass2 in method public void art.Test990$TestClass2.tweak(int). New value: 7 (type: class java.lang.Long)
diff --git a/test/990-field-trace/info.txt b/test/990-field-trace/info.txt
new file mode 100644
index 0000000..67d164e
--- /dev/null
+++ b/test/990-field-trace/info.txt
@@ -0,0 +1 @@
+Tests field access and modification watches in JVMTI
diff --git a/test/990-field-trace/run b/test/990-field-trace/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/990-field-trace/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/990-field-trace/src/Main.java b/test/990-field-trace/src/Main.java
new file mode 100644
index 0000000..cb14f5d
--- /dev/null
+++ b/test/990-field-trace/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test990.run();
+ }
+}
diff --git a/test/990-field-trace/src/art/Test990.java b/test/990-field-trace/src/art/Test990.java
new file mode 100644
index 0000000..d766876
--- /dev/null
+++ b/test/990-field-trace/src/art/Test990.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017 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 art;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Vector;
+import java.util.function.Function;
+
+public class Test990 {
+
+ // Fields of these classes are accessed/modified differently in the RI and ART so we ignore them.
+ static Collection<Class<?>> IGNORED_CLASSES = Arrays.asList(new Class<?>[] {
+ ClassLoader.class,
+ Vector.class,
+ });
+
+ static interface Printable { public void Print(); }
+
+ static final class FieldWrite implements Printable {
+ private Executable method;
+ private Object target;
+ private Field f;
+ private String initialValue;
+ private Class<?> initialValueType;
+
+ public FieldWrite(Executable method, Object target, Field f, Object v) {
+ this.method = method;
+ this.target = target;
+ this.f = f;
+ this.initialValue = genericToString(v);
+ this.initialValueType = v != null ? v.getClass() : null;
+ }
+
+ @Override
+ public void Print() {
+ System.out.println("MODIFY of " + f + " on object of" +
+ " type: " + (target == null ? null : target.getClass()) +
+ " in method " + method +
+ ". New value: " + initialValue + " (type: " + initialValueType + ")");
+ }
+ }
+
+ static final class FieldRead implements Printable {
+ private Executable method;
+ private Object target;
+ private Field f;
+
+ public FieldRead(Executable method, Object target, Field f) {
+ this.method = method;
+ this.target = target;
+ this.f = f;
+ }
+
+ @Override
+ public void Print() {
+ System.out.println("ACCESS of " + f + " on object of" +
+ " type " + (target == null ? null : target.getClass()) +
+ " in method " + method);
+ }
+ }
+
+ private static String genericToString(Object val) {
+ if (val == null) {
+ return "null";
+ } else if (val.getClass().isArray()) {
+ return arrayToString(val);
+ } else if (val instanceof Throwable) {
+ StringWriter w = new StringWriter();
+ ((Throwable) val).printStackTrace(new PrintWriter(w));
+ return w.toString();
+ } else {
+ return val.toString();
+ }
+ }
+
+ private static String charArrayToString(char[] src) {
+ String[] res = new String[src.length];
+ for (int i = 0; i < src.length; i++) {
+ if (Character.isISOControl(src[i])) {
+ res[i] = Character.getName(src[i]);
+ } else {
+ res[i] = Character.toString(src[i]);
+ }
+ }
+ return Arrays.toString(res);
+ }
+
+ private static String arrayToString(Object val) {
+ Class<?> klass = val.getClass();
+ if ((new Object[0]).getClass().isAssignableFrom(klass)) {
+ return Arrays.toString(
+ Arrays.stream((Object[])val).map(new Function<Object, String>() {
+ public String apply(Object o) {
+ return genericToString(o);
+ }
+ }).toArray());
+ } else if ((new byte[0]).getClass().isAssignableFrom(klass)) {
+ return Arrays.toString((byte[])val);
+ } else if ((new char[0]).getClass().isAssignableFrom(klass)) {
+ return charArrayToString((char[])val);
+ } else if ((new short[0]).getClass().isAssignableFrom(klass)) {
+ return Arrays.toString((short[])val);
+ } else if ((new int[0]).getClass().isAssignableFrom(klass)) {
+ return Arrays.toString((int[])val);
+ } else if ((new long[0]).getClass().isAssignableFrom(klass)) {
+ return Arrays.toString((long[])val);
+ } else if ((new float[0]).getClass().isAssignableFrom(klass)) {
+ return Arrays.toString((float[])val);
+ } else if ((new double[0]).getClass().isAssignableFrom(klass)) {
+ return Arrays.toString((double[])val);
+ } else {
+ throw new Error("Unknown type " + klass);
+ }
+ }
+
+ private static List<Printable> results = new ArrayList<>();
+
+ public static void notifyFieldModify(
+ Executable m, long location, Class<?> f_klass, Object target, Field f, Object value) {
+ if (IGNORED_CLASSES.contains(f_klass)) {
+ return;
+ }
+ results.add(new FieldWrite(m, target, f, value));
+ }
+
+ public static void notifyFieldAccess(
+ Executable m, long location, Class<?> f_klass, Object target, Field f) {
+ if (IGNORED_CLASSES.contains(f_klass)) {
+ return;
+ }
+ results.add(new FieldRead(m, target, f));
+ }
+
+ static class TestClass1 {
+ Object abc;
+ int xyz;
+ int foobar;
+ public TestClass1(int xyz, Object abc) {
+ this.xyz = xyz;
+ this.abc = abc;
+ }
+
+ public void tweak(int def) {
+ if (def == xyz) {
+ foobar++;
+ }
+ }
+ public String toString() {
+ return "TestClass1 { abc: \"" + genericToString(abc) + "\", xyz: " + xyz
+ + ", foobar: " + foobar + " }";
+ }
+ }
+
+ static class TestClass2 extends TestClass1 {
+ static long TOTAL = 0;
+ long baz;
+ public TestClass2(long baz) {
+ super(1337, "TESTING");
+ this.baz = baz;
+ }
+
+ public void tweak(int def) {
+ TOTAL++;
+ super.tweak(def);
+ baz++;
+ }
+
+ public String toString() {
+ return "TestClass2 { super: \"%s\", TOTAL: %d, baz: %d }".format(
+ super.toString(), TOTAL, baz);
+ }
+ }
+
+
+ public static void run() throws Exception {
+ Trace.disableTracing(Thread.currentThread());
+ Trace.enableFieldTracing(
+ Test990.class,
+ Test990.class.getDeclaredMethod("notifyFieldAccess",
+ Executable.class, Long.TYPE, Class.class, Object.class, Field.class),
+ Test990.class.getDeclaredMethod("notifyFieldModify",
+ Executable.class, Long.TYPE, Class.class, Object.class, Field.class, Object.class),
+ Thread.currentThread());
+ Trace.watchAllFieldAccesses();
+ Trace.watchAllFieldModifications();
+ TestClass1 t1 = new TestClass1(1, "tc1");
+ TestClass1 t2 = new TestClass2(2);
+ TestClass1 t3 = new TestClass1(3, t1);
+ TestClass1 t4 = new TestClass1(4, t2);
+ t1.tweak(1);
+ t1.tweak(1);
+ t2.tweak(12);
+ t2.tweak(1337);
+ t2.tweak(12);
+ t2.tweak(1338);
+ t1.tweak(t3.foobar);
+ t4.tweak((int)((TestClass2)t2).baz);
+ t4.tweak((int)TestClass2.TOTAL);
+ t2.tweak((int)TestClass2.TOTAL);
+
+ // Turn off tracing so we don't have to deal with print internals.
+ Trace.disableTracing(Thread.currentThread());
+ printResults();
+ }
+
+ public static void printResults() {
+ for (Printable p : results) {
+ p.Print();
+ }
+ }
+}
diff --git a/test/990-field-trace/src/art/Trace.java b/test/990-field-trace/src/art/Trace.java
new file mode 100644
index 0000000..9c27c9f
--- /dev/null
+++ b/test/990-field-trace/src/art/Trace.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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 art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+}
diff --git a/test/991-field-trace-2/expected.txt b/test/991-field-trace-2/expected.txt
new file mode 100644
index 0000000..8da8ffd
--- /dev/null
+++ b/test/991-field-trace-2/expected.txt
@@ -0,0 +1,118 @@
+Test is class art.Test991$DoNothingFieldTracer & class art.Test991$JavaReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$DoNothingFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1)
+normal read: xyz = 0
+FieldTracer: class art.Test991$DoNothingFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1). New value: 1 (type: class java.lang.Integer)
+Final state: xyz = 1
+Test is class art.Test991$ThrowReadFieldTracer & class art.Test991$JavaReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ThrowReadFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1)
+Caught error. art.Test991$TestError: Throwing error during access
+Final state: xyz = 0
+Test is class art.Test991$ThrowWriteFieldTracer & class art.Test991$JavaReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ThrowWriteFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1)
+normal read: xyz = 0
+FieldTracer: class art.Test991$ThrowWriteFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1). New value: 1 (type: class java.lang.Integer)
+Caught error. art.Test991$TestError: Throwing error during modify
+Final state: xyz = 0
+Test is class art.Test991$ModifyDuringReadFieldTracer & class art.Test991$JavaReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ModifyDuringReadFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1)
+normal read: xyz = 20
+FieldTracer: class art.Test991$ModifyDuringReadFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1). New value: 21 (type: class java.lang.Integer)
+Final state: xyz = 21
+Test is class art.Test991$ModifyDuringWriteFieldTracer & class art.Test991$JavaReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ModifyDuringWriteFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1)
+normal read: xyz = 0
+FieldTracer: class art.Test991$ModifyDuringWriteFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1). New value: 1 (type: class java.lang.Integer)
+Final state: xyz = 1
+Test is class art.Test991$ModifyDuringReadAndWriteFieldTracer & class art.Test991$JavaReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ModifyDuringReadAndWriteFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1)
+normal read: xyz = 10
+FieldTracer: class art.Test991$ModifyDuringReadAndWriteFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public void art.Test991$JavaReadWrite.accept(art.Test991$TestClass1). New value: 11 (type: class java.lang.Integer)
+Final state: xyz = 11
+Test is class art.Test991$DoNothingFieldTracer & class art.Test991$ReflectiveReadWrite
+Initial state: xyz = 0
+reflective read: xyz = 0
+Final state: xyz = 1
+Test is class art.Test991$ThrowReadFieldTracer & class art.Test991$ReflectiveReadWrite
+Initial state: xyz = 0
+reflective read: xyz = 0
+Final state: xyz = 1
+Test is class art.Test991$ThrowWriteFieldTracer & class art.Test991$ReflectiveReadWrite
+Initial state: xyz = 0
+reflective read: xyz = 0
+Final state: xyz = 1
+Test is class art.Test991$ModifyDuringReadFieldTracer & class art.Test991$ReflectiveReadWrite
+Initial state: xyz = 0
+reflective read: xyz = 0
+Final state: xyz = 1
+Test is class art.Test991$ModifyDuringWriteFieldTracer & class art.Test991$ReflectiveReadWrite
+Initial state: xyz = 0
+reflective read: xyz = 0
+Final state: xyz = 1
+Test is class art.Test991$ModifyDuringReadAndWriteFieldTracer & class art.Test991$ReflectiveReadWrite
+Initial state: xyz = 0
+reflective read: xyz = 0
+Final state: xyz = 1
+Test is class art.Test991$DoNothingFieldTracer & class art.Test991$NativeReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$DoNothingFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1)
+native read: xyz = 0
+FieldTracer: class art.Test991$DoNothingFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1). New value: 1 (type: class java.lang.Integer)
+Final state: xyz = 1
+Test is class art.Test991$ThrowReadFieldTracer & class art.Test991$NativeReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ThrowReadFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1)
+Caught error. art.Test991$TestError: Throwing error during access
+Final state: xyz = 0
+Test is class art.Test991$ThrowWriteFieldTracer & class art.Test991$NativeReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ThrowWriteFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1)
+native read: xyz = 0
+FieldTracer: class art.Test991$ThrowWriteFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1). New value: 1 (type: class java.lang.Integer)
+Caught error. art.Test991$TestError: Throwing error during modify
+Final state: xyz = 1
+Test is class art.Test991$ModifyDuringReadFieldTracer & class art.Test991$NativeReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ModifyDuringReadFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1)
+native read: xyz = 20
+FieldTracer: class art.Test991$ModifyDuringReadFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1). New value: 21 (type: class java.lang.Integer)
+Final state: xyz = 21
+Test is class art.Test991$ModifyDuringWriteFieldTracer & class art.Test991$NativeReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ModifyDuringWriteFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1)
+native read: xyz = 0
+FieldTracer: class art.Test991$ModifyDuringWriteFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1). New value: 1 (type: class java.lang.Integer)
+Final state: xyz = 1
+Test is class art.Test991$ModifyDuringReadAndWriteFieldTracer & class art.Test991$NativeReadWrite
+Initial state: xyz = 0
+FieldTracer: class art.Test991$ModifyDuringReadAndWriteFieldTracer
+ ACCESS of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1)
+native read: xyz = 10
+FieldTracer: class art.Test991$ModifyDuringReadAndWriteFieldTracer
+ MODIFY of public int art.Test991$TestClass1.xyz on object of type: class art.Test991$TestClass1 in method public static native void art.Test991.doNativeReadWrite(art.Test991$TestClass1). New value: 11 (type: class java.lang.Integer)
+Final state: xyz = 11
diff --git a/test/991-field-trace-2/field_trace.cc b/test/991-field-trace-2/field_trace.cc
new file mode 100644
index 0000000..823f9fd
--- /dev/null
+++ b/test/991-field-trace-2/field_trace.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <stdio.h>
+
+#include "android-base/macros.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test991FieldTrace {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test991_doNativeReadWrite(
+ JNIEnv* env, jclass klass, jobject testclass) {
+ CHECK(testclass != nullptr);
+ ScopedLocalRef<jclass> testclass_klass(env, env->GetObjectClass(testclass));
+ jmethodID notifyMethod = env->GetStaticMethodID(klass, "doPrintNativeNotification", "(I)V");
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ jfieldID xyz_field = env->GetFieldID(testclass_klass.get(), "xyz", "I");
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ jint val = env->GetIntField(testclass, xyz_field);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ env->CallStaticVoidMethod(klass, notifyMethod, val);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ val += 1;
+ env->SetIntField(testclass, xyz_field, val);
+}
+
+} // namespace Test991FieldTrace
+} // namespace art
+
diff --git a/test/991-field-trace-2/info.txt b/test/991-field-trace-2/info.txt
new file mode 100644
index 0000000..c2a1b68
--- /dev/null
+++ b/test/991-field-trace-2/info.txt
@@ -0,0 +1,5 @@
+Tests field access and modification watches in JVMTI.
+
+This test specifically examines how the runtime responds to exceptions occurring
+while handling these events. It also verifies the situations in which these
+events are sent.
diff --git a/test/991-field-trace-2/run b/test/991-field-trace-2/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/991-field-trace-2/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/991-field-trace-2/src/Main.java b/test/991-field-trace-2/src/Main.java
new file mode 100644
index 0000000..d945a5c
--- /dev/null
+++ b/test/991-field-trace-2/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test991.run();
+ }
+}
diff --git a/test/991-field-trace-2/src/art/Test991.java b/test/991-field-trace-2/src/art/Test991.java
new file mode 100644
index 0000000..644f4e1
--- /dev/null
+++ b/test/991-field-trace-2/src/art/Test991.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2017 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 art;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class Test991 {
+ static List<Field> WATCH_FIELDS = Arrays.asList(TestClass1.class.getDeclaredFields());
+
+ static FieldTracer TRACE = null;
+
+ static abstract class FieldTracer {
+ public final void notifyFieldAccess(
+ Executable method, long location, Class<?> f_klass, Object target, Field f) {
+ System.out.println("FieldTracer: " + this.getClass());
+ System.out.println("\tACCESS of " + f + " on object of" +
+ " type: " + (target == null ? null : target.getClass()) +
+ " in method " + method);
+ handleFieldAccess(method, location, f_klass, target, f);
+ }
+
+ public final void notifyFieldModify(
+ Executable method, long location, Class<?> f_klass, Object target, Field f, Object value) {
+ System.out.println("FieldTracer: " + this.getClass());
+ System.out.println("\tMODIFY of " + f + " on object of" +
+ " type: " + (target == null ? null : target.getClass()) +
+ " in method " + method +
+ ". New value: " + value + " (type: " + value.getClass() + ")");
+ handleFieldModify(method, location, f_klass, target, f, value);
+ }
+
+ public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {}
+ public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) {}
+ }
+
+ private static class TestError extends Error {
+ private static final long serialVersionUID = 0;
+ public TestError(String s) { super(s); }
+ }
+ static class DoNothingFieldTracer extends FieldTracer {}
+ static class ThrowReadFieldTracer extends FieldTracer {
+ @Override
+ public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {
+ throw new TestError("Throwing error during access");
+ }
+ }
+ static class ThrowWriteFieldTracer extends FieldTracer {
+ @Override
+ public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) {
+ throw new TestError("Throwing error during modify");
+ }
+ }
+ static class ModifyDuringReadAndWriteFieldTracer extends FieldTracer {
+ @Override
+ public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) {
+ // NB This is only safe because the agent doesn't send recursive access/modification events up
+ // to the java layer here.
+ ((TestClass1)t).xyz += 100;
+ }
+ @Override
+ public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {
+ // NB This is only safe because the agent doesn't send recursive access/modification events up
+ // to the java layer here.
+ ((TestClass1)t).xyz += 10;
+ }
+ }
+
+ static class ModifyDuringWriteFieldTracer extends FieldTracer {
+ @Override
+ public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) {
+ // NB This is only safe because the agent doesn't send recursive access/modification events up
+ // to the java layer here.
+ ((TestClass1)t).xyz += 200;
+ }
+ }
+
+ static class ModifyDuringReadFieldTracer extends FieldTracer {
+ @Override
+ public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {
+ // NB This is only safe because the agent doesn't send recursive access/modification events up
+ // to the java layer here.
+ ((TestClass1)t).xyz += 20;
+ }
+ }
+
+ public static void notifyFieldModify(
+ Executable m, long location, Class<?> f_klass, Object target, Field f, Object value) {
+ if (TRACE != null) {
+ TRACE.notifyFieldModify(m, location, f_klass, target, f, value);
+ }
+ }
+
+ public static void notifyFieldAccess(
+ Executable m, long location, Class<?> f_klass, Object target, Field f) {
+ if (TRACE != null) {
+ TRACE.notifyFieldAccess(m, location, f_klass, target, f);
+ }
+ }
+
+ public static class TestClass1 {
+ public int xyz;
+ public TestClass1(int xyz) {
+ this.xyz = xyz;
+ }
+ }
+
+ public static int readFieldUntraced(TestClass1 target) {
+ FieldTracer tmp = TRACE;
+ TRACE = null;
+ int res = target.xyz;
+ TRACE = tmp;
+ return res;
+ }
+
+ public static class JavaReadWrite implements Consumer<TestClass1> {
+ public void accept(TestClass1 t1) {
+ int val = t1.xyz;
+ System.out.println("normal read: xyz = " + val);
+ t1.xyz = val + 1;
+ }
+ }
+
+ public static class ReflectiveReadWrite implements Consumer<TestClass1> {
+ public void accept(TestClass1 t1) {
+ try {
+ Field f = t1.getClass().getDeclaredField("xyz");
+ int val = f.getInt(t1);
+ System.out.println("reflective read: xyz = " + val);
+ f.setInt(t1, val + 1);
+ } catch (IllegalAccessException iae) {
+ throw new InternalError("Could not set field xyz", iae);
+ } catch (NoSuchFieldException nsfe) {
+ throw new InternalError("Could not find field xyz", nsfe);
+ }
+ }
+ }
+
+ public static class NativeReadWrite implements Consumer<TestClass1> {
+ public void accept(TestClass1 t1) {
+ doNativeReadWrite(t1);
+ }
+ }
+
+ public static TestClass1 createTestClassNonTraced() {
+ FieldTracer tmp = TRACE;
+ TRACE = null;
+ TestClass1 n = new TestClass1(0);
+ TRACE = tmp;
+ return n;
+ }
+
+ public static void run() throws Exception {
+ Trace.disableTracing(Thread.currentThread());
+ Trace.enableFieldTracing(
+ Test991.class,
+ Test991.class.getDeclaredMethod("notifyFieldAccess",
+ Executable.class, Long.TYPE, Class.class, Object.class, Field.class),
+ Test991.class.getDeclaredMethod("notifyFieldModify",
+ Executable.class, Long.TYPE, Class.class, Object.class, Field.class, Object.class),
+ Thread.currentThread());
+ for (Field f : WATCH_FIELDS) {
+ Trace.watchFieldAccess(f);
+ Trace.watchFieldModification(f);
+ }
+ FieldTracer[] tracers = new FieldTracer[] {
+ new DoNothingFieldTracer(),
+ new ThrowReadFieldTracer(),
+ new ThrowWriteFieldTracer(),
+ new ModifyDuringReadFieldTracer(),
+ new ModifyDuringWriteFieldTracer(),
+ new ModifyDuringReadAndWriteFieldTracer(),
+ };
+ Consumer<TestClass1>[] field_modification = new Consumer[] {
+ new JavaReadWrite(),
+ new ReflectiveReadWrite(),
+ new NativeReadWrite(),
+ };
+ for (Consumer<TestClass1> c : field_modification) {
+ for (FieldTracer trace : tracers) {
+ System.out.println("Test is " + trace.getClass() + " & " + c.getClass());
+ TestClass1 t1 = createTestClassNonTraced();
+ TRACE = trace;
+ System.out.println("Initial state: xyz = " + readFieldUntraced(t1));
+ try {
+ c.accept(t1);
+ } catch (TestError e) {
+ System.out.println("Caught error. " + e);
+ } finally {
+ System.out.println("Final state: xyz = " + readFieldUntraced(t1));
+ }
+ }
+ }
+ Trace.disableTracing(Thread.currentThread());
+ }
+
+ public static native void doNativeReadWrite(TestClass1 t1);
+
+ public static void doPrintNativeNotification(int val) {
+ System.out.println("native read: xyz = " + val);
+ }
+}
diff --git a/test/991-field-trace-2/src/art/Trace.java b/test/991-field-trace-2/src/art/Trace.java
new file mode 100644
index 0000000..9c27c9f
--- /dev/null
+++ b/test/991-field-trace-2/src/art/Trace.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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 art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+}
diff --git a/test/Android.bp b/test/Android.bp
index 0937c62..8da415f 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -279,6 +279,7 @@
"986-native-method-bind/native_bind.cc",
"987-agent-bind/agent_bind.cc",
"989-method-trace-throw/method_trace.cc",
+ "991-field-trace-2/field_trace.cc",
],
shared_libs: [
"libbase",
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 6eaa5c3..4fe58db 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -78,9 +78,23 @@
jclass test_klass;
jmethodID enter_method;
jmethodID exit_method;
+ jmethodID field_access;
+ jmethodID field_modify;
bool in_callback;
+ bool access_watch_on_load;
+ bool modify_watch_on_load;
};
+static jobject GetJavaField(jvmtiEnv* jvmti, JNIEnv* env, jclass field_klass, jfieldID f) {
+ jint mods = 0;
+ if (JvmtiErrorToException(env, jvmti, jvmti->GetFieldModifiers(field_klass, f, &mods))) {
+ return nullptr;
+ }
+
+ bool is_static = (mods & kAccStatic) != 0;
+ return env->ToReflectedField(field_klass, f, is_static);
+}
+
static jobject GetJavaMethod(jvmtiEnv* jvmti, JNIEnv* env, jmethodID m) {
jint mods = 0;
if (JvmtiErrorToException(env, jvmti, jvmti->GetMethodModifiers(m, &mods))) {
@@ -97,21 +111,9 @@
return res;
}
-static jobject GetJavaValue(jvmtiEnv* jvmtienv,
- JNIEnv* env,
- jmethodID m,
- jvalue value) {
- char *fname, *fsig, *fgen;
- if (JvmtiErrorToException(env, jvmtienv, jvmtienv->GetMethodName(m, &fname, &fsig, &fgen))) {
- return nullptr;
- }
- std::string type(fsig);
- type = type.substr(type.find(")") + 1);
- jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig));
- jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname));
- jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen));
+static jobject GetJavaValueByType(JNIEnv* env, char type, jvalue value) {
std::string name;
- switch (type[0]) {
+ switch (type) {
case 'V':
return nullptr;
case '[':
@@ -146,7 +148,7 @@
return nullptr;
}
std::ostringstream oss;
- oss << "(" << type[0] << ")L" << name << ";";
+ oss << "(" << type << ")L" << name << ";";
std::string args = oss.str();
jclass target = env->FindClass(name.c_str());
jmethodID valueOfMethod = env->GetStaticMethodID(target, "valueOf", args.c_str());
@@ -157,6 +159,98 @@
return res;
}
+static jobject GetJavaValue(jvmtiEnv* jvmtienv,
+ JNIEnv* env,
+ jmethodID m,
+ jvalue value) {
+ char *fname, *fsig, *fgen;
+ if (JvmtiErrorToException(env, jvmtienv, jvmtienv->GetMethodName(m, &fname, &fsig, &fgen))) {
+ return nullptr;
+ }
+ std::string type(fsig);
+ type = type.substr(type.find(")") + 1);
+ jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig));
+ jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname));
+ jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen));
+ return GetJavaValueByType(env, type[0], value);
+}
+
+static void fieldAccessCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thr ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback) {
+ // Don't do callback for either of these to prevent an infinite loop.
+ return;
+ }
+ CHECK(data->field_access != nullptr);
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->field_access,
+ method_arg,
+ static_cast<jlong>(location),
+ field_klass,
+ object,
+ field_arg);
+ jnienv->DeleteLocalRef(method_arg);
+ jnienv->DeleteLocalRef(field_arg);
+ data->in_callback = false;
+}
+
+static void fieldModificationCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thr ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field,
+ char type_char,
+ jvalue new_value) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback) {
+ // Don't do callback recursively to prevent an infinite loop.
+ return;
+ }
+ CHECK(data->field_modify != nullptr);
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
+ jobject value = GetJavaValueByType(jnienv, type_char, new_value);
+ if (jnienv->ExceptionCheck()) {
+ data->in_callback = false;
+ jnienv->DeleteLocalRef(method_arg);
+ jnienv->DeleteLocalRef(field_arg);
+ return;
+ }
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->field_modify,
+ method_arg,
+ static_cast<jlong>(location),
+ field_klass,
+ object,
+ field_arg,
+ value);
+ jnienv->DeleteLocalRef(method_arg);
+ jnienv->DeleteLocalRef(field_arg);
+ data->in_callback = false;
+}
+
static void methodExitCB(jvmtiEnv* jvmti,
JNIEnv* jnienv,
jthread thr ATTRIBUTE_UNUSED,
@@ -172,6 +266,7 @@
// Don't do callback for either of these to prevent an infinite loop.
return;
}
+ CHECK(data->exit_method != nullptr);
data->in_callback = true;
jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
jobject result =
@@ -198,6 +293,7 @@
jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
return;
}
+ CHECK(data->enter_method != nullptr);
if (method == data->exit_method || method == data->enter_method || data->in_callback) {
// Don't do callback for either of these to prevent an infinite loop.
return;
@@ -212,12 +308,179 @@
data->in_callback = false;
}
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableMethodTracing(
+static void classPrepareCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thr ATTRIBUTE_UNUSED,
+ jclass klass) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->access_watch_on_load || data->modify_watch_on_load) {
+ jint nfields;
+ jfieldID* fields;
+ if (JvmtiErrorToException(jnienv, jvmti, jvmti->GetClassFields(klass, &nfields, &fields))) {
+ return;
+ }
+ for (jint i = 0; i < nfields; i++) {
+ jfieldID f = fields[i];
+ // Ignore errors
+ if (data->access_watch_on_load) {
+ jvmti->SetFieldAccessWatch(klass, f);
+ }
+
+ if (data->modify_watch_on_load) {
+ jvmti->SetFieldModificationWatch(klass, f);
+ }
+ }
+ jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields));
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldAccesses(JNIEnv* env) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ data->access_watch_on_load = true;
+ // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_PREPARE,
+ nullptr))) {
+ return;
+ }
+ jint nklasses;
+ jclass* klasses;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
+ return;
+ }
+ for (jint i = 0; i < nklasses; i++) {
+ jclass k = klasses[i];
+
+ jint nfields;
+ jfieldID* fields;
+ jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
+ if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
+ continue;
+ } else if (JvmtiErrorToException(env, jvmti_env, err)) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
+ return;
+ }
+ for (jint j = 0; j < nfields; j++) {
+ jvmti_env->SetFieldAccessWatch(k, fields[j]);
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldModifications(JNIEnv* env) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ data->modify_watch_on_load = true;
+ // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_PREPARE,
+ nullptr))) {
+ return;
+ }
+ jint nklasses;
+ jclass* klasses;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
+ return;
+ }
+ for (jint i = 0; i < nklasses; i++) {
+ jclass k = klasses[i];
+
+ jint nfields;
+ jfieldID* fields;
+ jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
+ if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
+ continue;
+ } else if (JvmtiErrorToException(env, jvmti_env, err)) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
+ return;
+ }
+ for (jint j = 0; j < nfields; j++) {
+ jvmti_env->SetFieldModificationWatch(k, fields[j]);
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
+}
+
+static bool GetFieldAndClass(JNIEnv* env,
+ jobject ref_field,
+ jclass* out_klass,
+ jfieldID* out_field) {
+ *out_field = env->FromReflectedField(ref_field);
+ if (env->ExceptionCheck()) {
+ return false;
+ }
+ jclass field_klass = env->FindClass("java/lang/reflect/Field");
+ if (env->ExceptionCheck()) {
+ return false;
+ }
+ jmethodID get_declaring_class_method =
+ env->GetMethodID(field_klass, "getDeclaringClass", "()Ljava/lang/Class;");
+ if (env->ExceptionCheck()) {
+ env->DeleteLocalRef(field_klass);
+ return false;
+ }
+ *out_klass = static_cast<jclass>(env->CallObjectMethod(ref_field, get_declaring_class_method));
+ if (env->ExceptionCheck()) {
+ *out_klass = nullptr;
+ env->DeleteLocalRef(field_klass);
+ return false;
+ }
+ env->DeleteLocalRef(field_klass);
+ return true;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldModification(
+ JNIEnv* env,
+ jclass trace ATTRIBUTE_UNUSED,
+ jobject field_obj) {
+ jfieldID field;
+ jclass klass;
+ if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
+ return;
+ }
+
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(klass, field));
+ env->DeleteLocalRef(klass);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess(
+ JNIEnv* env,
+ jclass trace ATTRIBUTE_UNUSED,
+ jobject field_obj) {
+ jfieldID field;
+ jclass klass;
+ if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(klass, field));
+ env->DeleteLocalRef(klass);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
JNIEnv* env,
jclass trace ATTRIBUTE_UNUSED,
jclass klass,
jobject enter,
jobject exit,
+ jobject field_access,
+ jobject field_modify,
jthread thr) {
TraceData* data = nullptr;
if (JvmtiErrorToException(env,
@@ -228,8 +491,10 @@
}
memset(data, 0, sizeof(TraceData));
data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
- data->enter_method = env->FromReflectedMethod(enter);
- data->exit_method = env->FromReflectedMethod(exit);
+ data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr;
+ data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr;
+ data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
+ data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
data->in_callback = false;
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
@@ -240,29 +505,62 @@
memset(&cb, 0, sizeof(cb));
cb.MethodEntry = methodEntryCB;
cb.MethodExit = methodExitCB;
+ cb.FieldAccess = fieldAccessCB;
+ cb.FieldModification = fieldModificationCB;
+ cb.ClassPrepare = classPrepareCB;
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
return;
}
- if (JvmtiErrorToException(env,
+ if (enter != nullptr &&
+ JvmtiErrorToException(env,
jvmti_env,
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
JVMTI_EVENT_METHOD_ENTRY,
thr))) {
return;
}
- if (JvmtiErrorToException(env,
+ if (exit != nullptr &&
+ JvmtiErrorToException(env,
jvmti_env,
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
JVMTI_EVENT_METHOD_EXIT,
thr))) {
return;
}
+ if (field_access != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_FIELD_ACCESS,
+ thr))) {
+ return;
+ }
+ if (field_modify != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_FIELD_MODIFICATION,
+ thr))) {
+ return;
+ }
}
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableMethodTracing(
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
if (JvmtiErrorToException(env, jvmti_env,
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_FIELD_ACCESS,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_FIELD_MODIFICATION,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
JVMTI_EVENT_METHOD_ENTRY,
thr))) {
return;