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 
Print()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 
FieldWrite(Executable method, Object target, Field f, Object v)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
Print()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 
FieldRead(Executable method, Object target, Field f)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
Print()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 
genericToString(Object val)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 
charArrayToString(char[] src)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 
arrayToString(Object val)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