Home | History | Annotate | Download | only in art
      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 {
     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 
     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 
     50     public void handleFieldAccess(Executable m, long l, Class<?> fk, Object t, Field f) {}
     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;
     56     public TestError(String s) { super(s); }
     57   }
     58   static class DoNothingFieldTracer extends FieldTracer {}
     59   static class ThrowReadFieldTracer extends FieldTracer {
     60     @Override
     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
     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
     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
     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
     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
     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 
    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 
    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;
    120     public TestClass1(int xyz) {
    121       this.xyz = xyz;
    122     }
    123   }
    124 
    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> {
    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> {
    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> {
    157     public void accept(TestClass1 t1) {
    158       doNativeReadWrite(t1);
    159     }
    160   }
    161 
    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 
    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 
    214   public static native void doNativeReadWrite(TestClass1 t1);
    215 
    216   public static void doPrintNativeNotification(int val) {
    217     System.out.println("native read: xyz = " + val);
    218   }
    219 }
    220