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 sun.hotspot.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  * @author igor.ignatyev@oracle.com
  41  */
  42 public class Compiler {
  43     private Compiler() { }
  44     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
  45     private static final AtomicLong CLASS_COUNT = new AtomicLong(0L);
  46     private static final AtomicLong METHOD_COUNT = new AtomicLong(0L);
  47     private static volatile boolean CLASSES_LIMIT_REACHED = false;
  48 
  49     /**
  50      * @return count of processed classes
  51      */
  52     public static long getClassCount() {
  53         return CLASS_COUNT.get();
  54     }
  55 
  56     /**
  57      * @return count of processed methods
  58      */
  59     public static long getMethodCount() {
  60         return METHOD_COUNT.get();
  61     }
  62 
  63     /**
  64      * @return {@code true} if classes limit is reached
  65      */
  66     public static boolean isLimitReached() {
  67         return CLASSES_LIMIT_REACHED;
  68     }
  69 
  70     /**
  71      * Compiles all methods and constructors.
  72      *
  73      * @param aClass class to compile
  74      * @param executor executor used for compile task invocation
  75      * @throws NullPointerException if {@code class} or {@code executor}
  76      *                              is {@code null}
  77      */
  78     public static void compileClass(Class aClass, Executor executor) {
  79         Objects.requireNonNull(aClass);
  80         Objects.requireNonNull(executor);
  81         long id = CLASS_COUNT.incrementAndGet();
  82         if (id > Utils.COMPILE_THE_WORLD_STOP_AT) {
  83             CLASS_COUNT.decrementAndGet();
  84             CLASSES_LIMIT_REACHED = true;
  85             return;
  86         }
  87 
  88         if (id >= Utils.COMPILE_THE_WORLD_START_AT) {
  89             String name = aClass.getName();
  90             try {
  91                 System.out.printf("[%d]\t%s%n", id, name);
  92                 ConstantPool constantPool = SharedSecrets.getJavaLangAccess().
  93                         getConstantPool(aClass);
  94                 if (Utils.COMPILE_THE_WORLD_PRELOAD_CLASSES) {
  95                     preloadClasses(name, id, constantPool);
  96                 }
  97                 long methodCount = 0;
  98                 for (Executable e : aClass.getDeclaredConstructors()) {
  99                     ++methodCount;
 100                     executor.execute(new CompileMethodCommand(id, name, e));
 101                 }
 102                 for (Executable e : aClass.getDeclaredMethods()) {
 103                     ++methodCount;
 104                     executor.execute(new CompileMethodCommand(id, name, e));
 105                 }
 106                 METHOD_COUNT.addAndGet(methodCount);
 107 
 108                 if (Utils.DEOPTIMIZE_ALL_CLASSES_RATE > 0
 109                         && (id % Utils.DEOPTIMIZE_ALL_CLASSES_RATE == 0)) {
 110                     WHITE_BOX.deoptimizeAll();
 111                 }
 112             } catch (Throwable t) {
 113                 System.out.printf("[%d]\t%s\tskipping %s%n", id, name, t);
 114                 t.printStackTrace();
 115             }
 116         }
 117     }
 118 
 119     private static void preloadClasses(String className, long id,
 120             ConstantPool constantPool) {
 121         try {
 122             for (int i = 0, n = constantPool.getSize(); i < n; ++i) {
 123                 try {
 124                     constantPool.getClassAt(i);
 125                 } catch (IllegalArgumentException ignore) {
 126                 }
 127             }
 128         } catch (Throwable t) {
 129             System.out.printf("[%d]\t%s\tpreloading failed : %s%n", id,
 130                     className, t);
 131         }
 132     }
 133 
 134 
 135 
 136     /**
 137      * Compilation of method.
 138      * Will compile method on all available comp levels.
 139      */
 140     private static class CompileMethodCommand implements Runnable {
 141         private final long classId;
 142         private final String className;
 143         private final Executable method;
 144 
 145         /**
 146          * @param classId   id of class
 147          * @param className name of class
 148          * @param method    compiled for compilation
 149          */
 150         public CompileMethodCommand(long classId, String className,
 151                 Executable method) {
 152             this.classId = classId;
 153             this.className = className;
 154             this.method = method;
 155         }
 156 
 157         @Override
 158         public final void run() {
 159             int compLevel = Utils.INITIAL_COMP_LEVEL;
 160             if (Utils.TIERED_COMPILATION) {
 161                 for (int i = compLevel; i <= Utils.TIERED_STOP_AT_LEVEL; ++i) {
 162                     WHITE_BOX.deoptimizeMethod(method);
 163                     compileMethod(method, i);
 164                 }
 165             } else {
 166                 compileMethod(method, compLevel);
 167             }
 168         }
 169 
 170         private void waitCompilation() {
 171             if (!Utils.BACKGROUND_COMPILATION) {
 172                 return;
 173             }
 174             final Object obj = new Object();
 175             synchronized (obj) {
 176                 for (int i = 0;
 177                      i < 10 && WHITE_BOX.isMethodQueuedForCompilation(method);
 178                      ++i) {
 179                     try {
 180                         obj.wait(1000);
 181                     } catch (InterruptedException e) {
 182                         Thread.currentThread().interrupt();
 183                     }
 184                 }
 185             }
 186         }
 187 
 188         private void compileMethod(Executable method, int compLevel) {
 189             if (WHITE_BOX.isMethodCompilable(method, compLevel)) {
 190                 try {
 191                     WHITE_BOX.enqueueMethodForCompilation(method, compLevel);
 192                     waitCompilation();
 193                     int tmp = WHITE_BOX.getMethodCompilationLevel(method);
 194                     if (tmp != compLevel) {
 195                         logMethod(method, "compilation level = " + tmp
 196                                 + ", but not " + compLevel);
 197                     } else if (Utils.IS_VERBOSE) {
 198                         logMethod(method, "compilation level = " + tmp + ". OK");
 199                     }
 200                 } catch (Throwable t) {
 201                     logMethod(method, "error on compile at " + compLevel
 202                             + " level");
 203                     t.printStackTrace();
 204                 }
 205             } else if (Utils.IS_VERBOSE) {
 206                 logMethod(method, "not compilable at " + compLevel);
 207             }
 208         }
 209 
 210         private void logMethod(Executable method, String message) {
 211             StringBuilder builder = new StringBuilder("[");
 212             builder.append(classId);
 213             builder.append("]\t");
 214             builder.append(className);
 215             builder.append("::");
 216             builder.append(method.getName());
 217             builder.append('(');
 218             Class[] params = method.getParameterTypes();
 219             for (int i = 0, n = params.length - 1; i < n; ++i) {
 220                 builder.append(params[i].getName());
 221                 builder.append(", ");
 222             }
 223             if (params.length != 0) {
 224                 builder.append(params[params.length - 1].getName());
 225             }
 226             builder.append(')');
 227             if (message != null) {
 228                 builder.append('\t');
 229                 builder.append(message);
 230             }
 231             System.err.println(builder);
 232         }
 233     }
 234 
 235 }