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.io.PrintWriter;
     20 import java.io.StringWriter;
     21 import java.lang.reflect.Executable;
     22 import java.lang.reflect.Field;
     23 import java.util.ArrayList;
     24 import java.util.Arrays;
     25 import java.util.Collection;
     26 import java.util.List;
     27 import java.util.Vector;
     28 import java.util.function.Function;
     29 
     30 public class Test990 {
     31 
     32   // Fields of these classes are accessed/modified differently in the RI and ART so we ignore them.
     33   static Collection<Class<?>> IGNORED_CLASSES = Arrays.asList(new Class<?>[] {
     34     ClassLoader.class,
     35     Vector.class,
     36   });
     37 
     38   static interface Printable { public void Print(); }
     39 
     40   static final class FieldWrite implements Printable {
     41     private Executable method;
     42     private Object target;
     43     private Field f;
     44     private String initialValue;
     45     private Class<?> initialValueType;
     46 
     47     public FieldWrite(Executable method, Object target, Field f, Object v) {
     48       this.method = method;
     49       this.target = target;
     50       this.f = f;
     51       this.initialValue =  genericToString(v);
     52       this.initialValueType = v != null ? v.getClass() : null;
     53     }
     54 
     55     @Override
     56     public void Print() {
     57         System.out.println("MODIFY of " + f + " on object of" +
     58             " type: " + (target == null ? null : target.getClass()) +
     59             " in method " + method +
     60             ". New value: " + initialValue + " (type: " + initialValueType + ")");
     61     }
     62   }
     63 
     64   static final class FieldRead implements Printable {
     65     private Executable method;
     66     private Object target;
     67     private Field f;
     68 
     69     public FieldRead(Executable method, Object target, Field f) {
     70       this.method = method;
     71       this.target = target;
     72       this.f = f;
     73     }
     74 
     75     @Override
     76     public void Print() {
     77         System.out.println("ACCESS of " + f + " on object of" +
     78             " type " + (target == null ? null : target.getClass()) +
     79             " in method " + method);
     80     }
     81   }
     82 
     83   private static String genericToString(Object val) {
     84     if (val == null) {
     85       return "null";
     86     } else if (val.getClass().isArray()) {
     87       return arrayToString(val);
     88     } else if (val instanceof Throwable) {
     89       StringWriter w = new StringWriter();
     90       ((Throwable) val).printStackTrace(new PrintWriter(w));
     91       return w.toString();
     92     } else {
     93       return val.toString();
     94     }
     95   }
     96 
     97   private static String charArrayToString(char[] src) {
     98     String[] res = new String[src.length];
     99     for (int i = 0; i < src.length; i++) {
    100       if (Character.isISOControl(src[i])) {
    101         res[i] = Character.getName(src[i]);
    102       } else {
    103         res[i] = Character.toString(src[i]);
    104       }
    105     }
    106     return Arrays.toString(res);
    107   }
    108 
    109   private static String arrayToString(Object val) {
    110     Class<?> klass = val.getClass();
    111     if ((new Object[0]).getClass().isAssignableFrom(klass)) {
    112       return Arrays.toString(
    113           Arrays.stream((Object[])val).map(new Function<Object, String>() {
    114             public String apply(Object o) {
    115               return genericToString(o);
    116             }
    117           }).toArray());
    118     } else if ((new byte[0]).getClass().isAssignableFrom(klass)) {
    119       return Arrays.toString((byte[])val);
    120     } else if ((new char[0]).getClass().isAssignableFrom(klass)) {
    121       return charArrayToString((char[])val);
    122     } else if ((new short[0]).getClass().isAssignableFrom(klass)) {
    123       return Arrays.toString((short[])val);
    124     } else if ((new int[0]).getClass().isAssignableFrom(klass)) {
    125       return Arrays.toString((int[])val);
    126     } else if ((new long[0]).getClass().isAssignableFrom(klass)) {
    127       return Arrays.toString((long[])val);
    128     } else if ((new float[0]).getClass().isAssignableFrom(klass)) {
    129       return Arrays.toString((float[])val);
    130     } else if ((new double[0]).getClass().isAssignableFrom(klass)) {
    131       return Arrays.toString((double[])val);
    132     } else {
    133       throw new Error("Unknown type " + klass);
    134     }
    135   }
    136 
    137   private static List<Printable> results = new ArrayList<>();
    138 
    139   public static void notifyFieldModify(
    140       Executable m, long location, Class<?> f_klass, Object target, Field f, Object value) {
    141     if (IGNORED_CLASSES.contains(f_klass)) {
    142       return;
    143     }
    144     results.add(new FieldWrite(m, target, f, value));
    145   }
    146 
    147   public static void notifyFieldAccess(
    148       Executable m, long location, Class<?> f_klass, Object target, Field f) {
    149     if (IGNORED_CLASSES.contains(f_klass)) {
    150       return;
    151     }
    152     results.add(new FieldRead(m, target, f));
    153   }
    154 
    155   static class TestClass1 {
    156     Object abc;
    157     int xyz;
    158     int foobar;
    159     public TestClass1(int xyz, Object abc) {
    160       this.xyz = xyz;
    161       this.abc = abc;
    162     }
    163 
    164     public void tweak(int def) {
    165       if (def == xyz) {
    166         foobar++;
    167       }
    168     }
    169     public String toString() {
    170       return "TestClass1 { abc: \"" + genericToString(abc) + "\", xyz: " + xyz
    171           + ", foobar: " + foobar + " }";
    172     }
    173   }
    174 
    175   static class TestClass2 extends TestClass1 {
    176     static long TOTAL = 0;
    177     long baz;
    178     public TestClass2(long baz) {
    179       super(1337, "TESTING");
    180       this.baz = baz;
    181     }
    182 
    183     public void tweak(int def) {
    184       TOTAL++;
    185       super.tweak(def);
    186       baz++;
    187     }
    188 
    189     public String toString() {
    190       return "TestClass2 { super: \"%s\", TOTAL: %d, baz: %d }".format(
    191           super.toString(), TOTAL, baz);
    192     }
    193   }
    194 
    195 
    196   public static void run() throws Exception {
    197       Trace.disableTracing(Thread.currentThread());
    198       Trace.enableFieldTracing(
    199           Test990.class,
    200           Test990.class.getDeclaredMethod("notifyFieldAccess",
    201             Executable.class, Long.TYPE, Class.class, Object.class, Field.class),
    202           Test990.class.getDeclaredMethod("notifyFieldModify",
    203             Executable.class, Long.TYPE, Class.class, Object.class, Field.class, Object.class),
    204           Thread.currentThread());
    205       Trace.watchAllFieldAccesses();
    206       Trace.watchAllFieldModifications();
    207       TestClass1 t1 = new TestClass1(1, "tc1");
    208       TestClass1 t2 = new TestClass2(2);
    209       TestClass1 t3 = new TestClass1(3, t1);
    210       TestClass1 t4 = new TestClass1(4, t2);
    211       t1.tweak(1);
    212       t1.tweak(1);
    213       t2.tweak(12);
    214       t2.tweak(1337);
    215       t2.tweak(12);
    216       t2.tweak(1338);
    217       t1.tweak(t3.foobar);
    218       t4.tweak((int)((TestClass2)t2).baz);
    219       t4.tweak((int)TestClass2.TOTAL);
    220       t2.tweak((int)TestClass2.TOTAL);
    221 
    222       // Turn off tracing so we don't have to deal with print internals.
    223       Trace.disableTracing(Thread.currentThread());
    224       printResults();
    225   }
    226 
    227   public static void printResults() {
    228     for (Printable p : results) {
    229       p.Print();
    230     }
    231   }
    232 }
    233