1 /*
  2  * Copyright (c) 2013, 2022, 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.access.SharedSecrets;
 27 import jdk.internal.misc.Unsafe;
 28 import jdk.internal.reflect.ConstantPool;
 29 import jdk.test.whitebox.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 
 88     private static void preloadClasses(String className, long id,
 89             ConstantPool constantPool) {
 90         try {
 91             for (int i = 0, n = constantPool.getSize(); i < n; ++i) {
 92                 try {
 93                     constantPool.getClassAt(i);
 94                 } catch (IllegalArgumentException ignore) {
 95                 }
 96             }
 97         } catch (Throwable t) {
 98             CompileTheWorld.OUT.println(String.format("[%d]\t%s\tWARNING preloading failed : %s",
 99                     id, className, t));
100             t.printStackTrace(CompileTheWorld.ERR);
101         }
102     }
103 
104     private static void compileClinit(Class<?> aClass, long id) {
105         int startLevel = Utils.INITIAL_COMP_LEVEL;
106         int endLevel = Utils.TIERED_COMPILATION ? Utils.TIERED_STOP_AT_LEVEL : startLevel;
107         for (int i = startLevel; i <= endLevel; ++i) {
108             try {
109                 WHITE_BOX.enqueueInitializerForCompilation(aClass, i);
110             } catch (Throwable t) {
111                 CompileTheWorld.OUT.println(String.format("[%d]\t%s::<clinit>\tERROR at level %d : %s",
112                         id, aClass.getName(), i, t));
113                 t.printStackTrace(CompileTheWorld.ERR);
114             }
115         }
116     }
117 
118     /**
119      * Compilation of method.
120      * Will compile method on all available comp levels.
121      */
122     private static class CompileMethodCommand implements Runnable {
123         private final long classId;
124         private final String className;
125         private final Executable method;
126 
127         /**
128          * @param classId   id of class
129          * @param method    compiled for compilation
130          */
131         public CompileMethodCommand(long classId, Executable method) {
132             this.classId = classId;
133             this.className = method.getDeclaringClass().getName();
134             this.method = method;
135         }
136 
137         @Override
138         public final void run() {
139             int compLevel = Utils.INITIAL_COMP_LEVEL;
140             if (Utils.TIERED_COMPILATION) {
141                 for (int i = compLevel; i <= Utils.TIERED_STOP_AT_LEVEL; ++i) {
142                     WHITE_BOX.deoptimizeMethod(method);
143                     compileAtLevel(i);
144                 }
145             } else {
146                 compileAtLevel(compLevel);
147             }
148 
149             // Make the method eligible for sweeping sooner
150             WHITE_BOX.deoptimizeMethod(method);
151         }
152 
153         private void waitCompilation() {
154             if (!Utils.BACKGROUND_COMPILATION) {
155                 return;
156             }
157             final Object obj = new Object();
158             synchronized (obj) {
159                 for (int i = 0;
160                      i < 10 && WHITE_BOX.isMethodQueuedForCompilation(method);
161                      ++i) {
162                     try {
163                         obj.wait(1000);
164                     } catch (InterruptedException e) {
165                         Thread.currentThread().interrupt();
166                     }
167                 }
168             }
169         }
170 
171         private void compileAtLevel(int compLevel) {
172             if (WHITE_BOX.isMethodCompilable(method, compLevel)) {
173                 try {
174                     WHITE_BOX.enqueueMethodForCompilation(method, compLevel);
175                     waitCompilation();
176                     int tmp = WHITE_BOX.getMethodCompilationLevel(method);
177                     if (tmp != compLevel) {
178                         log("WARNING compilation level = " + tmp
179                                 + ", but not " + compLevel);
180                     } else if (Utils.IS_VERBOSE) {
181                         log("compilation level = " + tmp + ". OK");
182                     }
183                 } catch (Throwable t) {
184                     log("ERROR at level " + compLevel);
185                     t.printStackTrace(CompileTheWorld.ERR);
186                 }
187             } else if (Utils.IS_VERBOSE) {
188                 log("not compilable at " + compLevel);
189             }
190         }
191 
192         private String methodName() {
193             return String.format("%s::%s(%s)",
194                     className,
195                     method.getName(),
196                     Arrays.stream(method.getParameterTypes())
197                           .map(Class::getName)
198                           .collect(Collectors.joining(", ")));
199         }
200 
201         private void log(String message) {
202             StringBuilder builder = new StringBuilder("[")
203                     .append(classId)
204                     .append("]\t")
205                     .append(methodName());
206             if (message != null) {
207                 builder.append('\t')
208                        .append(message);
209             }
210             CompileTheWorld.ERR.println(builder);
211         }
212     }
213 
214 }