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