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.util.Arrays; 22 import java.lang.reflect.Method; 23 import java.util.List; 24 import java.util.Set; 25 import java.util.ArrayList; 26 import java.util.HashSet; 27 import java.util.function.IntUnaryOperator; 28 import java.util.function.Function; 29 30 public class Test988 { 31 32 // Methods with non-deterministic output that should not be printed. 33 static Set<Method> NON_DETERMINISTIC_OUTPUT_METHODS = new HashSet<>(); 34 static Set<Method> NON_DETERMINISTIC_OUTPUT_TYPE_METHODS = new HashSet<>(); 35 36 static { 37 try { 38 NON_DETERMINISTIC_OUTPUT_METHODS.add( 39 Throwable.class.getDeclaredMethod("nativeFillInStackTrace")); 40 } catch (Exception e) {} 41 try { 42 NON_DETERMINISTIC_OUTPUT_METHODS.add(Thread.class.getDeclaredMethod("currentThread")); 43 NON_DETERMINISTIC_OUTPUT_TYPE_METHODS.add(Thread.class.getDeclaredMethod("currentThread")); 44 } catch (Exception e) {} 45 } 46 47 static interface Printable { Print()48 public void Print(); 49 } 50 51 static final class MethodEntry implements Printable { 52 private Object m; 53 private int cnt; MethodEntry(Object m, int cnt)54 public MethodEntry(Object m, int cnt) { 55 this.m = m; 56 this.cnt = cnt; 57 } 58 @Override Print()59 public void Print() { 60 System.out.println(whitespace(cnt) + "=> " + methodToString(m)); 61 } 62 } 63 genericToString(Object val)64 private static String genericToString(Object val) { 65 if (val == null) { 66 return "null"; 67 } else if (val.getClass().isArray()) { 68 return arrayToString(val); 69 } else if (val instanceof Throwable) { 70 StringWriter w = new StringWriter(); 71 Throwable thr = ((Throwable) val); 72 w.write(thr.getClass().getName() + ": " + thr.getMessage() + "\n"); 73 for (StackTraceElement e : thr.getStackTrace()) { 74 if (e.getClassName().startsWith("art.")) { 75 w.write("\t" + e + "\n"); 76 } else { 77 w.write("\t<additional hidden frames>\n"); 78 break; 79 } 80 } 81 return w.toString(); 82 } else { 83 return val.toString(); 84 } 85 } 86 charArrayToString(char[] src)87 private static String charArrayToString(char[] src) { 88 String[] res = new String[src.length]; 89 for (int i = 0; i < src.length; i++) { 90 if (Character.isISOControl(src[i])) { 91 res[i] = Character.getName(src[i]); 92 } else { 93 res[i] = Character.toString(src[i]); 94 } 95 } 96 return Arrays.toString(res); 97 } 98 arrayToString(Object val)99 private static String arrayToString(Object val) { 100 Class<?> klass = val.getClass(); 101 if ((new Object[0]).getClass().isAssignableFrom(klass)) { 102 return Arrays.toString( 103 Arrays.stream((Object[])val).map(new Function<Object, String>() { 104 public String apply(Object o) { 105 return Test988.genericToString(o); 106 } 107 }).toArray()); 108 } else if ((new byte[0]).getClass().isAssignableFrom(klass)) { 109 return Arrays.toString((byte[])val); 110 } else if ((new char[0]).getClass().isAssignableFrom(klass)) { 111 return charArrayToString((char[])val); 112 } else if ((new short[0]).getClass().isAssignableFrom(klass)) { 113 return Arrays.toString((short[])val); 114 } else if ((new int[0]).getClass().isAssignableFrom(klass)) { 115 return Arrays.toString((int[])val); 116 } else if ((new long[0]).getClass().isAssignableFrom(klass)) { 117 return Arrays.toString((long[])val); 118 } else if ((new float[0]).getClass().isAssignableFrom(klass)) { 119 return Arrays.toString((float[])val); 120 } else if ((new double[0]).getClass().isAssignableFrom(klass)) { 121 return Arrays.toString((double[])val); 122 } else { 123 throw new Error("Unknown type " + klass); 124 } 125 } 126 127 static String methodToString(Object m) { 128 // Make the output more similar between ART and RI, 129 // by removing the 'native' specifier from methods. 130 String methodStr = m.toString(); 131 return methodStr.replaceFirst(" native", ""); 132 } 133 134 static final class MethodReturn implements Printable { 135 private Object m; 136 private Object val; 137 private int cnt; 138 public MethodReturn(Object m, Object val, int cnt) { 139 this.m = m; 140 this.val = val; 141 this.cnt = cnt; 142 } 143 @Override 144 public void Print() { 145 String print; 146 if (NON_DETERMINISTIC_OUTPUT_METHODS.contains(m)) { 147 print = "<non-deterministic>"; 148 } else { 149 print = genericToString(val); 150 } 151 Class<?> klass = null; 152 if (val != null) { 153 klass = val.getClass(); 154 } 155 String klass_print; 156 if (klass == null) { 157 klass_print = "null"; 158 } else if (NON_DETERMINISTIC_OUTPUT_TYPE_METHODS.contains(m)) { 159 klass_print = "<non-deterministic>"; 160 } else { 161 klass_print = klass.toString(); 162 } 163 System.out.println( 164 whitespace(cnt) + "<= " + methodToString(m) + " -> <" + klass_print + ": " + print + ">"); 165 } 166 } 167 168 static final class MethodThrownThrough implements Printable { 169 private Object m; 170 private int cnt; 171 public MethodThrownThrough(Object m, int cnt) { 172 this.m = m; 173 this.cnt = cnt; 174 } 175 @Override 176 public void Print() { 177 System.out.println(whitespace(cnt) + "<= " + methodToString(m) + " EXCEPTION"); 178 } 179 } 180 181 private static String whitespace(int n) { 182 String out = ""; 183 while (n > 0) { 184 n--; 185 out += "."; 186 } 187 return out; 188 } 189 190 static final class FibThrow implements Printable { 191 private String format; 192 private int arg; 193 private Throwable res; 194 public FibThrow(String format, int arg, Throwable res) { 195 this.format = format; 196 this.arg = arg; 197 this.res = res; 198 } 199 200 @Override 201 public void Print() { 202 System.out.printf(format, arg, genericToString(res)); 203 } 204 } 205 206 static final class FibResult implements Printable { 207 private String format; 208 private int arg; 209 private int res; 210 public FibResult(String format, int arg, int res) { 211 this.format = format; 212 this.arg = arg; 213 this.res = res; 214 } 215 216 @Override 217 public void Print() { 218 System.out.printf(format, arg, res); 219 } 220 } 221 222 private static List<Printable> results = new ArrayList<>(); 223 // Starts with => enableMethodTracing 224 // .=> enableTracing 225 private static int cnt = 2; 226 227 // Iterative version 228 static final class IterOp implements IntUnaryOperator { 229 public int applyAsInt(int x) { 230 return iter_fibonacci(x); 231 } 232 } 233 static int iter_fibonacci(int n) { 234 if (n < 0) { 235 throw new Error("Bad argument: " + n + " < 0"); 236 } else if (n == 0) { 237 return 0; 238 } 239 int x = 1; 240 int y = 1; 241 for (int i = 3; i <= n; i++) { 242 int z = x + y; 243 x = y; 244 y = z; 245 } 246 return y; 247 } 248 249 // Recursive version 250 static final class RecurOp implements IntUnaryOperator { 251 public int applyAsInt(int x) { 252 return fibonacci(x); 253 } 254 } 255 static int fibonacci(int n) { 256 if (n < 0) { 257 throw new Error("Bad argument: " + n + " < 0"); 258 } else if ((n == 0) || (n == 1)) { 259 return n; 260 } else { 261 return fibonacci(n - 1) + (fibonacci(n - 2)); 262 } 263 } 264 265 static final int METHOD_TRACING_IGNORE_DEPTH = 2; 266 static boolean sMethodTracingIgnore = false; 267 268 public static void notifyMethodEntry(Object m) { 269 // Called by native code when a method is entered. This method is ignored by the native 270 // entry and exit hooks. 271 cnt++; 272 if ((cnt - 1) > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) { 273 return; 274 } 275 results.add(new MethodEntry(m, cnt - 1)); 276 } 277 278 public static void notifyMethodExit(Object m, boolean exception, Object result) { 279 cnt--; 280 281 if (cnt > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) { 282 return; 283 } 284 285 if (exception) { 286 results.add(new MethodThrownThrough(m, cnt)); 287 } else { 288 results.add(new MethodReturn(m, result, cnt)); 289 } 290 } 291 292 public static void run() throws Exception { 293 // call this here so it is linked. It doesn't actually do anything here. 294 loadAllClasses(); 295 Trace.disableTracing(Thread.currentThread()); 296 Trace.enableMethodTracing( 297 Test988.class, 298 Test988.class.getDeclaredMethod("notifyMethodEntry", Object.class), 299 Test988.class.getDeclaredMethod( 300 "notifyMethodExit", Object.class, Boolean.TYPE, Object.class), 301 Thread.currentThread()); 302 doFibTest(30, new IterOp()); 303 doFibTest(5, new RecurOp()); 304 doFibTest(-19, new IterOp()); 305 doFibTest(-19, new RecurOp()); 306 307 sMethodTracingIgnore = true; 308 IntrinsicsTest.doTest(); 309 sMethodTracingIgnore = false; 310 // Turn off method tracing so we don't have to deal with print internals. 311 Trace.disableTracing(Thread.currentThread()); 312 printResults(); 313 } 314 315 // This ensures that all classes we touch are loaded before we start recording traces. This 316 // eliminates a major source of divergence between the RI and ART. 317 public static void loadAllClasses() { 318 MethodThrownThrough.class.toString(); 319 MethodEntry.class.toString(); 320 MethodReturn.class.toString(); 321 FibResult.class.toString(); 322 FibThrow.class.toString(); 323 Printable.class.toString(); 324 ArrayList.class.toString(); 325 RecurOp.class.toString(); 326 IterOp.class.toString(); 327 StringBuilder.class.toString(); 328 IntrinsicsTest.initialize(); // ensure <clinit> is executed prior to tracing. 329 } 330 331 public static void printResults() { 332 for (Printable p : results) { 333 p.Print(); 334 } 335 } 336 337 public static void doFibTest(int x, IntUnaryOperator op) { 338 try { 339 int y = op.applyAsInt(x); 340 results.add(new FibResult("fibonacci(%d)=%d\n", x, y)); 341 } catch (Throwable t) { 342 results.add(new FibThrow("fibonacci(%d) -> %s\n", x, t)); 343 } 344 } 345 346 static class IntrinsicsTest { 347 static int[] sSourceArray = { 0, 1, 2, 3, 4, 5 }; 348 static int[] sDestArray = { 5, 6, 7, 8, 9, 10 }; 349 350 static char[] sSourceArrayChar = { '0', '1', '2', '3', '4', '5' }; 351 static char[] sDestArrayChar = { '5', '6', '7', '8', '9', 'a' }; 352 353 static void initialize() { 354 Test988Intrinsics.initialize(); 355 356 // Pre-load all classes used in #doTest manual intrinsics. 357 java.lang.System.class.toString(); 358 } 359 static void doTest() { 360 // Ensure that the ART intrinsics in intrinsics_list.h are also being traced, 361 // since in non-tracing operation they are effectively inlined by the optimizing compiler. 362 363 // Auto-generated test file that uses null/0s as default parameters. 364 Test988Intrinsics.test(); 365 366 // Manual list here for functions that require special non-null/non-zero parameters: 367 System.arraycopy(sSourceArray, 0, sDestArray, 0, 1); 368 System.arraycopy(sSourceArrayChar, 0, sDestArrayChar, 0, 1); 369 } 370 } 371 } 372