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