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