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;