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