1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package sun.hotspot.tools.ctw;
  25 
  26 import jdk.testlib.WhiteBox;
  27 import sun.misc.SharedSecrets;
  28 import sun.reflect.ConstantPool;
  29 
  30 import java.lang.reflect.Executable;
  31 
  32 import java.util.Objects;
  33 import java.util.concurrent.Executor;
  34 import java.util.concurrent.atomic.AtomicLong;
  35 
  36 /**
  37  * Provide method to compile whole class.
  38  * Also contains compiled methods and classes counters.
  39  */
  40 public class Compiler {
  41     private Compiler() { }
  42     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
  43     private static final AtomicLong CLASS_COUNT = new AtomicLong(0L);
  44     private static final AtomicLong METHOD_COUNT = new AtomicLong(0L);
  45     private static volatile boolean CLASSES_LIMIT_REACHED = false;
  46 
  47     /**
  48      * @return count of processed classes
  49      */
  50     public static long getClassCount() {
  51         return CLASS_COUNT.get();
  52     }
  53 
  54     /**
  55      * @return count of processed methods
  56      */
  57     public static long getMethodCount() {
  58         return METHOD_COUNT.get();
  59     }
  60 
  61     /**
  62      * @return {@code true} if classes limit is reached
  63      */
  64     public static boolean isLimitReached() {
  65         return CLASSES_LIMIT_REACHED;
  66     }
  67 
  68     /**
  69      * Compiles all methods and constructors.
  70      *
  71      * @param aClass class to compile
  72      * @param executor executor used for compile task invocation
  73      * @throws NullPointerException if {@code class} or {@code executor}
  74      *                              is {@code null}
  75      */
  76     public static void compileClass(Class aClass, Executor executor) {
  77         Objects.requireNonNull(aClass);
  78         Objects.requireNonNull(executor);
  79         long id = CLASS_COUNT.incrementAndGet();
  80         if (id > Utils.COMPILE_THE_WORLD_STOP_AT) {
  81             CLASS_COUNT.decrementAndGet();
  82             CLASSES_LIMIT_REACHED = true;
  83             return;
  84         }
  85 
  86         if (id >= Utils.COMPILE_THE_WORLD_START_AT) {
  87             String name = aClass.getName();
  88             try {
  89                 System.out.printf("[%d]\t%s%n", id, name);
  90                 ConstantPool constantPool = SharedSecrets.getJavaLangAccess().
  91                         getConstantPool(aClass);
  92                 if (Utils.COMPILE_THE_WORLD_PRELOAD_CLASSES) {
  93                     preloadClasses(name, id, constantPool);
  94                 }
  95                 long methodCount = 0;
  96                 for (Executable e : aClass.getDeclaredConstructors()) {
  97                     ++methodCount;
  98                     executor.execute(new CompileMethodCommand(id, name, e));
  99                 }
 100                 for (Executable e : aClass.getDeclaredMethods()) {
 101                     ++methodCount;
 102                     executor.execute(new CompileMethodCommand(id, name, e));
 103                 }
 104                 METHOD_COUNT.addAndGet(methodCount);
 105 
 106                 if (Utils.DEOPTIMIZE_ALL_CLASSES_RATE > 0
 107                         && (id % Utils.DEOPTIMIZE_ALL_CLASSES_RATE == 0)) {
 108                     WHITE_BOX.deoptimizeAll();
 109                 }
 110             } catch (Throwable t) {
 111                 System.out.printf("[%d]\t%s\tskipping %s%n", id, name, t);
 112                 t.printStackTrace();
 113             }
 114         }
 115     }
 116 
 117     private static void preloadClasses(String className, long id,
 118             ConstantPool constantPool) {
 119         try {
 120             for (int i = 0, n = constantPool.getSize(); i < n; ++i) {
 121                 try {
 122                     constantPool.getClassAt(i);
 123                 } catch (IllegalArgumentException ignore) {
 124                 }
 125             }
 126         } catch (Throwable t) {
 127             System.out.printf("[%d]\t%s\tpreloading failed : %s%n", id,
 128                     className, t);
 129         }
 130     }
 131 
 132 
 133 
 134     /**
 135      * Compilation of method.
 136      * Will compile method on all available comp levels.
 137      */
 138     private static class CompileMethodCommand implements Runnable {
 139         private final long classId;
 140         private final String className;
 141         private final Executable method;
 142 
 143         /**
 144          * @param classId   id of class
 145          * @param className name of class
 146          * @param method    compiled for compilation
 147          */
 148         public CompileMethodCommand(long classId, String className,
 149                 Executable method) {
 150             this.classId = classId;
 151             this.className = className;
 152             this.method = method;
 153         }
 154 
 155         @Override
 156         public final void run() {
 157             int compLevel = Utils.INITIAL_COMP_LEVEL;
 158             if (Utils.TIERED_COMPILATION) {
 159                 for (int i = compLevel; i <= Utils.TIERED_STOP_AT_LEVEL; ++i) {
 160                     WHITE_BOX.deoptimizeMethod(method);
 161                     compileMethod(method, i);
 162                 }
 163             } else {
 164                 compileMethod(method, compLevel);
 165             }
 166         }
 167 
 168         private void waitCompilation() {
 169             if (!Utils.BACKGROUND_COMPILATION) {
 170                 return;
 171             }
 172             final Object obj = new Object();
 173             synchronized (obj) {
 174                 for (int i = 0;
 175                      i < 10 && WHITE_BOX.isMethodQueuedForCompilation(method);
 176                      ++i) {
 177                     try {
 178                         obj.wait(1000);
 179                     } catch (InterruptedException e) {
 180                         Thread.currentThread().interrupt();
 181                     }
 182                 }
 183             }
 184         }
 185 
 186         private void compileMethod(Executable method, int compLevel) {
 187             if (WHITE_BOX.isMethodCompilable(method, compLevel)) {
 188                 try {
 189                     WHITE_BOX.enqueueMethodForCompilation(method, compLevel);
 190                     waitCompilation();
 191                     int tmp = WHITE_BOX.getMethodCompilationLevel(method);
 192                     if (tmp != compLevel) {
 193                         logMethod(method, "compilation level = " + tmp
 194                                 + ", but not " + compLevel);
 195                     } else if (Utils.IS_VERBOSE) {
 196                         logMethod(method, "compilation level = " + tmp + ". OK");
 197                     }
 198                 } catch (Throwable t) {
 199                     logMethod(method, "error on compile at " + compLevel
 200                             + " level");
 201                     t.printStackTrace();
 202                 }
 203             } else if (Utils.IS_VERBOSE) {
 204                 logMethod(method, "not compilable at " + compLevel);
 205             }
 206         }
 207 
 208         private void logMethod(Executable method, String message) {
 209             StringBuilder builder = new StringBuilder("[");
 210             builder.append(classId);
 211             builder.append("]\t");
 212             builder.append(className);
 213             builder.append("::");
 214             builder.append(method.getName());
 215             builder.append('(');
 216             Class[] params = method.getParameterTypes();
 217             for (int i = 0, n = params.length - 1; i < n; ++i) {
 218                 builder.append(params[i].getName());
 219                 builder.append(", ");
 220             }
 221             if (params.length != 0) {
 222                 builder.append(params[params.length - 1].getName());
 223             }
 224             builder.append(')');
 225             if (message != null) {
 226                 builder.append('\t');
 227                 builder.append(message);
 228             }
 229             System.err.println(builder);
 230         }
 231     }
 232 
 233 }