1 /*
   2  * Copyright (c) 2013, 2016, 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 jdk.internal.misc.SharedSecrets;
  28 import jdk.internal.reflect.ConstantPool;
  29 import java.lang.reflect.Executable;
  30 
  31 import java.util.Objects;
  32 import java.util.concurrent.Executor;
  33 import java.util.concurrent.atomic.AtomicLong;
  34 
  35 /**
  36  * Provide method to compile whole class.
  37  * Also contains compiled methods and classes counters.
  38  */
  39 public class Compiler {
  40 
  41     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
  42     private static final AtomicLong METHOD_COUNT = new AtomicLong(0L);
  43 
  44     private Compiler() { }
  45 
  46     /**
  47      * @return count of processed methods
  48      */
  49     public static long getMethodCount() {
  50         return METHOD_COUNT.get();
  51     }
  52 
  53     /**
  54      * Compiles all methods and constructors.
  55      *
  56      * @param aClass class to compile
  57      * @param id an id of the class
  58      * @param executor executor used for compile task invocation
  59      * @throws NullPointerException if {@code class} or {@code executor}
  60      *                              is {@code null}
  61      */
  62     public static void compileClass(Class<?> aClass, long id, Executor executor) {
  63         Objects.requireNonNull(aClass);
  64         Objects.requireNonNull(executor);
  65         try {
  66             ConstantPool constantPool = SharedSecrets.getJavaLangAccess().
  67                     getConstantPool(aClass);
  68             if (Utils.COMPILE_THE_WORLD_PRELOAD_CLASSES) {
  69                 preloadClasses(aClass.getName(), id, constantPool);
  70             }
  71             long methodCount = 0;
  72             for (Executable e : aClass.getDeclaredConstructors()) {
  73                 ++methodCount;
  74                 executor.execute(new CompileMethodCommand(id, e));
  75             }
  76             for (Executable e : aClass.getDeclaredMethods()) {
  77                 ++methodCount;
  78                 executor.execute(new CompileMethodCommand(id, e));
  79             }
  80             METHOD_COUNT.addAndGet(methodCount);
  81 
  82             if (Utils.DEOPTIMIZE_ALL_CLASSES_RATE > 0
  83                     && (id % Utils.DEOPTIMIZE_ALL_CLASSES_RATE == 0)) {
  84                 WHITE_BOX.deoptimizeAll();
  85             }
  86         } catch (Throwable t) {
  87             CompileTheWorld.OUT.printf("[%d]\t%s\tskipping %s%n", id, aClass.getName(), t);
  88             t.printStackTrace();
  89         }
  90     }
  91 
  92     private static void preloadClasses(String className, long id,
  93             ConstantPool constantPool) {
  94         try {
  95             for (int i = 0, n = constantPool.getSize(); i < n; ++i) {
  96                 try {
  97                     constantPool.getClassAt(i);
  98                 } catch (IllegalArgumentException ignore) {
  99                 }
 100             }
 101         } catch (Throwable t) {
 102             CompileTheWorld.OUT.printf("[%d]\t%s\tpreloading failed : %s%n",
 103                     id, className, t);
 104         }
 105     }
 106 
 107 
 108 
 109     /**
 110      * Compilation of method.
 111      * Will compile method on all available comp levels.
 112      */
 113     private static class CompileMethodCommand implements Runnable {
 114         private final long classId;
 115         private final String className;
 116         private final Executable method;
 117 
 118         /**
 119          * @param classId   id of class
 120          * @param method    compiled for compilation
 121          */
 122         public CompileMethodCommand(long classId, Executable method) {
 123             this.classId = classId;
 124             this.className = method.getDeclaringClass().getName();
 125             this.method = method;
 126         }
 127 
 128         @Override
 129         public final void run() {
 130             int compLevel = Utils.INITIAL_COMP_LEVEL;
 131             if (Utils.TIERED_COMPILATION) {
 132                 for (int i = compLevel; i <= Utils.TIERED_STOP_AT_LEVEL; ++i) {
 133                     WHITE_BOX.deoptimizeMethod(method);
 134                     compileAtLevel(i);
 135                 }
 136             } else {
 137                 compileAtLevel(compLevel);
 138             }
 139         }
 140 
 141         private void waitCompilation() {
 142             if (!Utils.BACKGROUND_COMPILATION) {
 143                 return;
 144             }
 145             final Object obj = new Object();
 146             synchronized (obj) {
 147                 for (int i = 0;
 148                      i < 10 && WHITE_BOX.isMethodQueuedForCompilation(method);
 149                      ++i) {
 150                     try {
 151                         obj.wait(1000);
 152                     } catch (InterruptedException e) {
 153                         Thread.currentThread().interrupt();
 154                     }
 155                 }
 156             }
 157         }
 158 
 159         private void compileAtLevel(int compLevel) {
 160             if (WHITE_BOX.isMethodCompilable(method, compLevel)) {
 161                 try {
 162                     WHITE_BOX.enqueueMethodForCompilation(method, compLevel);
 163                     waitCompilation();
 164                     int tmp = WHITE_BOX.getMethodCompilationLevel(method);
 165                     if (tmp != compLevel) {
 166                         log("compilation level = " + tmp
 167                                 + ", but not " + compLevel);
 168                     } else if (Utils.IS_VERBOSE) {
 169                         log("compilation level = " + tmp + ". OK");
 170                     }
 171                 } catch (Throwable t) {
 172                     log("error on compile at " + compLevel
 173                             + " level");
 174                     t.printStackTrace();
 175                 }
 176             } else if (Utils.IS_VERBOSE) {
 177                 log("not compilable at " + compLevel);
 178             }
 179         }
 180 
 181         private void log(String message) {
 182             StringBuilder builder = new StringBuilder("[");
 183             builder.append(classId);
 184             builder.append("]\t");
 185             builder.append(className);
 186             builder.append("::");
 187             builder.append(method.getName());
 188             builder.append('(');
 189             Class[] params = method.getParameterTypes();
 190             for (int i = 0, n = params.length - 1; i < n; ++i) {
 191                 builder.append(params[i].getName());
 192                 builder.append(", ");
 193             }
 194             if (params.length != 0) {
 195                 builder.append(params[params.length - 1].getName());
 196             }
 197             builder.append(')');
 198             if (message != null) {
 199                 builder.append('\t');
 200                 builder.append(message);
 201             }
 202             CompileTheWorld.ERR.println(builder);
 203         }
 204     }
 205 
 206 }