1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package art; 18 19 import java.lang.reflect.Executable; 20 import java.lang.reflect.Field; 21 import java.util.Arrays; 22 import java.util.List; 23 import java.util.function.Consumer; 24 25 public class Test991 { 26 static List<Field> WATCH_FIELDS = Arrays.asList(TestClass1.class.getDeclaredFields()); 27 28 static FieldTracer TRACE = null; 29 30 static abstract class FieldTracer { notifyFieldAccess( Executable method, long location, Class<?> f_klass, Object target, Field f)31 public final void notifyFieldAccess( 32 Executable method, long location, Class<?> f_klass, Object target, Field f) { 33 System.out.println("FieldTracer: " + this.getClass()); 34 System.out.println("\tACCESS of " + f + " on object of" + 35 " type: " + (target == null ? null : target.getClass()) + 36 " in method " + method); 37 handleFieldAccess(method, location, f_klass, target, f); 38 } 39 notifyFieldModify( Executable method, long location, Class<?> f_klass, Object target, Field f, Object value)40 public final void notifyFieldModify( 41 Executable method, long location, Class<?> f_klass, Object target, Field f, Object value) { 42 System.out.println("FieldTracer: " + this.getClass()); 43 System.out.println("\tMODIFY of " + f + " on object of" + 44 " type: " + (target == null ? null : target.getClass()) + 45 " in method " + method + 46 ". New value: " + value + " (type: " + value.getClass() + ")"); 47 handleFieldModify(method, location, f_klass, target, f, value); 48 } 49 handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f)50 public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {} handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v)51 public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) {} 52 } 53 54 private static class TestError extends Error { 55 private static final long serialVersionUID = 0; TestError(String s)56 public TestError(String s) { super(s); } 57 } 58 static class DoNothingFieldTracer extends FieldTracer {} 59 static class ThrowReadFieldTracer extends FieldTracer { 60 @Override handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f)61 public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) { 62 throw new TestError("Throwing error during access"); 63 } 64 } 65 static class ThrowWriteFieldTracer extends FieldTracer { 66 @Override handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v)67 public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) { 68 throw new TestError("Throwing error during modify"); 69 } 70 } 71 static class ModifyDuringReadAndWriteFieldTracer extends FieldTracer { 72 @Override handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v)73 public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) { 74 // NB This is only safe because the agent doesn't send recursive access/modification events up 75 // to the java layer here. 76 ((TestClass1)t).xyz += 100; 77 } 78 @Override handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f)79 public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) { 80 // NB This is only safe because the agent doesn't send recursive access/modification events up 81 // to the java layer here. 82 ((TestClass1)t).xyz += 10; 83 } 84 } 85 86 static class ModifyDuringWriteFieldTracer extends FieldTracer { 87 @Override handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v)88 public void handleFieldModify(Executable m, long l, Class<?> fk, Object t, Field f, Object v) { 89 // NB This is only safe because the agent doesn't send recursive access/modification events up 90 // to the java layer here. 91 ((TestClass1)t).xyz += 200; 92 } 93 } 94 95 static class ModifyDuringReadFieldTracer extends FieldTracer { 96 @Override handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f)97 public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) { 98 // NB This is only safe because the agent doesn't send recursive access/modification events up 99 // to the java layer here. 100 ((TestClass1)t).xyz += 20; 101 } 102 } 103 notifyFieldModify( Executable m, long location, Class<?> f_klass, Object target, Field f, Object value)104 public static void notifyFieldModify( 105 Executable m, long location, Class<?> f_klass, Object target, Field f, Object value) { 106 if (TRACE != null) { 107 TRACE.notifyFieldModify(m, location, f_klass, target, f, value); 108 } 109 } 110 notifyFieldAccess( Executable m, long location, Class<?> f_klass, Object target, Field f)111 public static void notifyFieldAccess( 112 Executable m, long location, Class<?> f_klass, Object target, Field f) { 113 if (TRACE != null) { 114 TRACE.notifyFieldAccess(m, location, f_klass, target, f); 115 } 116 } 117 118 public static class TestClass1 { 119 public int xyz; TestClass1(int xyz)120 public TestClass1(int xyz) { 121 this.xyz = xyz; 122 } 123 } 124 readFieldUntraced(TestClass1 target)125 public static int readFieldUntraced(TestClass1 target) { 126 FieldTracer tmp = TRACE; 127 TRACE = null; 128 int res = target.xyz; 129 TRACE = tmp; 130 return res; 131 } 132 133 public static class JavaReadWrite implements Consumer<TestClass1> { accept(TestClass1 t1)134 public void accept(TestClass1 t1) { 135 int val = t1.xyz; 136 System.out.println("normal read: xyz = " + val); 137 t1.xyz = val + 1; 138 } 139 } 140 141 public static class ReflectiveReadWrite implements Consumer<TestClass1> { accept(TestClass1 t1)142 public void accept(TestClass1 t1) { 143 try { 144 Field f = t1.getClass().getDeclaredField("xyz"); 145 int val = f.getInt(t1); 146 System.out.println("reflective read: xyz = " + val); 147 f.setInt(t1, val + 1); 148 } catch (IllegalAccessException iae) { 149 throw new InternalError("Could not set field xyz", iae); 150 } catch (NoSuchFieldException nsfe) { 151 throw new InternalError("Could not find field xyz", nsfe); 152 } 153 } 154 } 155 156 public static class NativeReadWrite implements Consumer<TestClass1> { accept(TestClass1 t1)157 public void accept(TestClass1 t1) { 158 doNativeReadWrite(t1); 159 } 160 } 161 createTestClassNonTraced()162 public static TestClass1 createTestClassNonTraced() { 163 FieldTracer tmp = TRACE; 164 TRACE = null; 165 TestClass1 n = new TestClass1(0); 166 TRACE = tmp; 167 return n; 168 } 169 run()170 public static void run() throws Exception { 171 Trace.disableTracing(Thread.currentThread()); 172 Trace.enableFieldTracing( 173 Test991.class, 174 Test991.class.getDeclaredMethod("notifyFieldAccess", 175 Executable.class, Long.TYPE, Class.class, Object.class, Field.class), 176 Test991.class.getDeclaredMethod("notifyFieldModify", 177 Executable.class, Long.TYPE, Class.class, Object.class, Field.class, Object.class), 178 Thread.currentThread()); 179 for (Field f : WATCH_FIELDS) { 180 Trace.watchFieldAccess(f); 181 Trace.watchFieldModification(f); 182 } 183 FieldTracer[] tracers = new FieldTracer[] { 184 new DoNothingFieldTracer(), 185 new ThrowReadFieldTracer(), 186 new ThrowWriteFieldTracer(), 187 new ModifyDuringReadFieldTracer(), 188 new ModifyDuringWriteFieldTracer(), 189 new ModifyDuringReadAndWriteFieldTracer(), 190 }; 191 Consumer<TestClass1>[] field_modification = new Consumer[] { 192 new JavaReadWrite(), 193 new ReflectiveReadWrite(), 194 new NativeReadWrite(), 195 }; 196 for (Consumer<TestClass1> c : field_modification) { 197 for (FieldTracer trace : tracers) { 198 System.out.println("Test is " + trace.getClass() + " & " + c.getClass()); 199 TestClass1 t1 = createTestClassNonTraced(); 200 TRACE = trace; 201 System.out.println("Initial state: xyz = " + readFieldUntraced(t1)); 202 try { 203 c.accept(t1); 204 } catch (TestError e) { 205 System.out.println("Caught error. " + e); 206 } finally { 207 System.out.println("Final state: xyz = " + readFieldUntraced(t1)); 208 } 209 } 210 } 211 Trace.disableTracing(Thread.currentThread()); 212 } 213 doNativeReadWrite(TestClass1 t1)214 public static native void doNativeReadWrite(TestClass1 t1); 215 doPrintNativeNotification(int val)216 public static void doPrintNativeNotification(int val) { 217 System.out.println("native read: xyz = " + val); 218 } 219 } 220