1 /* 2 * Copyright (c) 2012, 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 package org.graalvm.compiler.hotspot; 24 25 import static org.graalvm.compiler.core.GraalCompilerOptions.ExitVMOnBailout; 26 import static org.graalvm.compiler.core.GraalCompilerOptions.ExitVMOnException; 27 import static org.graalvm.compiler.core.GraalCompilerOptions.PrintAfterCompilation; 28 import static org.graalvm.compiler.core.GraalCompilerOptions.PrintBailout; 29 import static org.graalvm.compiler.core.GraalCompilerOptions.PrintCompilation; 30 import static org.graalvm.compiler.core.GraalCompilerOptions.PrintFilter; 31 import static org.graalvm.compiler.core.GraalCompilerOptions.PrintStackTraceOnException; 32 import static org.graalvm.compiler.core.phases.HighTier.Options.Inline; 33 34 import java.util.List; 35 36 import org.graalvm.compiler.code.CompilationResult; 37 import org.graalvm.compiler.debug.Debug; 38 import org.graalvm.compiler.debug.Debug.Scope; 39 import org.graalvm.compiler.debug.DebugCloseable; 40 import org.graalvm.compiler.debug.DebugCounter; 41 import org.graalvm.compiler.debug.DebugDumpScope; 42 import org.graalvm.compiler.debug.DebugTimer; 43 import org.graalvm.compiler.debug.GraalError; 44 import org.graalvm.compiler.debug.Management; 45 import org.graalvm.compiler.debug.TTY; 46 import org.graalvm.compiler.debug.TimeSource; 47 import org.graalvm.compiler.options.OptionValue; 48 import org.graalvm.compiler.options.OptionValue.OverrideScope; 49 50 import jdk.vm.ci.code.BailoutException; 51 import jdk.vm.ci.code.CodeCacheProvider; 52 import jdk.vm.ci.hotspot.EventProvider; 53 import jdk.vm.ci.hotspot.HotSpotCompilationRequest; 54 import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult; 55 import jdk.vm.ci.hotspot.HotSpotCompiledCode; 56 import jdk.vm.ci.hotspot.HotSpotInstalledCode; 57 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider; 58 import jdk.vm.ci.hotspot.HotSpotNmethod; 59 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; 60 import jdk.vm.ci.runtime.JVMCICompiler; 61 import jdk.vm.ci.services.JVMCIServiceLocator; 62 63 //JaCoCo Exclude 64 65 public class CompilationTask { 66 67 private static final DebugCounter BAILOUTS = Debug.counter("Bailouts"); 68 69 private static final EventProvider eventProvider; 70 71 static { 72 List<EventProvider> providers = JVMCIServiceLocator.getProviders(EventProvider.class); 73 if (providers.size() > 1) { 74 throw new GraalError("Multiple %s providers found: %s", EventProvider.class.getName(), providers); 75 } else if (providers.isEmpty()) { 76 eventProvider = EventProvider.createEmptyEventProvider(); 77 } else { 78 eventProvider = providers.get(0); 79 } 80 } 81 82 private final HotSpotJVMCIRuntimeProvider jvmciRuntime; 83 84 private final HotSpotGraalCompiler compiler; 85 private final HotSpotCompilationIdentifier compilationId; 86 87 private HotSpotInstalledCode installedCode; 88 89 /** 90 * Specifies whether the compilation result is installed as the 91 * {@linkplain HotSpotNmethod#isDefault() default} nmethod for the compiled method. 92 */ 93 private final boolean installAsDefault; 94 95 private final boolean useProfilingInfo; 96 97 static class Lazy { 98 /** 99 * A {@link com.sun.management.ThreadMXBean} to be able to query some information about the 100 * current compiler thread, e.g. total allocated bytes. 101 */ 102 static final com.sun.management.ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) Management.getThreadMXBean(); 103 } 104 105 public CompilationTask(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean installAsDefault) { 106 this.jvmciRuntime = jvmciRuntime; 107 this.compiler = compiler; 108 this.compilationId = new HotSpotCompilationIdentifier(request); 109 this.useProfilingInfo = useProfilingInfo; 110 this.installAsDefault = installAsDefault; 111 } 112 113 public HotSpotResolvedJavaMethod getMethod() { 114 return getRequest().getMethod(); 115 } 116 117 /** 118 * Returns the compilation id of this task. 119 * 120 * @return compile id 121 */ 122 public int getId() { 123 return getRequest().getId(); 124 } 125 126 public int getEntryBCI() { 127 return getRequest().getEntryBCI(); 128 } 129 130 /** 131 * @return the compilation id plus a trailing '%' is the compilation is an OSR to match 132 * PrintCompilation style output 133 */ 134 public String getIdString() { 135 if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) { 136 return getId() + "%"; 137 } else { 138 return Integer.toString(getId()); 139 } 140 } 141 142 public HotSpotInstalledCode getInstalledCode() { 143 return installedCode; 144 } 145 146 /** 147 * Time spent in compilation. 148 */ 149 private static final DebugTimer CompilationTime = Debug.timer("CompilationTime"); 150 151 /** 152 * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes}. 153 */ 154 private static final DebugCounter CompiledBytecodes = Debug.counter("CompiledBytecodes"); 155 156 /** 157 * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes} for 158 * which {@linkplain CompilationResult#getTargetCode()} code was installed. 159 */ 160 private static final DebugCounter CompiledAndInstalledBytecodes = Debug.counter("CompiledAndInstalledBytecodes"); 161 162 /** 163 * Counts the number of installed {@linkplain CompilationResult#getTargetCodeSize()} bytes. 164 */ 165 private static final DebugCounter InstalledCodeSize = Debug.counter("InstalledCodeSize"); 166 167 /** 168 * Time spent in code installation. 169 */ 170 public static final DebugTimer CodeInstallationTime = Debug.timer("CodeInstallation"); 171 172 @SuppressWarnings("try") 173 public HotSpotCompilationRequestResult runCompilation() { 174 HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); 175 GraalHotSpotVMConfig config = graalRuntime.getVMConfig(); 176 final long threadId = Thread.currentThread().getId(); 177 int entryBCI = getEntryBCI(); 178 final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI; 179 HotSpotResolvedJavaMethod method = getMethod(); 180 181 // register the compilation id in the method metrics 182 if (Debug.isMethodMeterEnabled()) { 183 if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) { 184 Debug.methodMetrics(method).addToMetric(getId(), "CompilationIdOSR"); 185 } else { 186 Debug.methodMetrics(method).addToMetric(getId(), "CompilationId"); 187 } 188 } 189 190 // Log a compilation event. 191 EventProvider.CompilationEvent compilationEvent = eventProvider.newCompilationEvent(); 192 193 // If there is already compiled code for this method on our level we simply return. 194 // JVMCI compiles are always at the highest compile level, even in non-tiered mode so we 195 // only need to check for that value. 196 if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) { 197 return null; 198 } 199 200 CompilationResult result = null; 201 try (DebugCloseable a = CompilationTime.start()) { 202 CompilationStatistics stats = CompilationStatistics.create(method, isOSR); 203 final boolean printCompilation = PrintCompilation.getValue() && !TTY.isSuppressed(); 204 final boolean printAfterCompilation = PrintAfterCompilation.getValue() && !TTY.isSuppressed(); 205 if (printCompilation) { 206 TTY.println(getMethodDescription() + "..."); 207 } 208 209 TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(), method); 210 final long start; 211 final long allocatedBytesBefore; 212 if (printAfterCompilation || printCompilation) { 213 start = TimeSource.getTimeNS(); 214 allocatedBytesBefore = printAfterCompilation || printCompilation ? Lazy.threadMXBean.getThreadAllocatedBytes(threadId) : 0L; 215 } else { 216 start = 0L; 217 allocatedBytesBefore = 0L; 218 } 219 220 try (Scope s = Debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) { 221 // Begin the compilation event. 222 compilationEvent.begin(); 223 /* 224 * Disable inlining if HotSpot has it disabled unless it's been explicitly set in 225 * Graal. 226 */ 227 boolean disableInlining = !config.inline && !Inline.hasBeenSet(); 228 try (OverrideScope s1 = disableInlining ? OptionValue.override(Inline, false) : null) { 229 result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId); 230 } 231 } catch (Throwable e) { 232 throw Debug.handle(e); 233 } finally { 234 // End the compilation event. 235 compilationEvent.end(); 236 237 filter.remove(); 238 239 if (printAfterCompilation || printCompilation) { 240 final long stop = TimeSource.getTimeNS(); 241 final long duration = (stop - start) / 1000000; 242 final int targetCodeSize = result != null ? result.getTargetCodeSize() : -1; 243 final int bytecodeSize = result != null ? result.getBytecodeSize() : 0; 244 final long allocatedBytesAfter = Lazy.threadMXBean.getThreadAllocatedBytes(threadId); 245 final long allocatedKBytes = (allocatedBytesAfter - allocatedBytesBefore) / 1024; 246 247 if (printAfterCompilation) { 248 TTY.println(getMethodDescription() + String.format(" | %4dms %5dB %5dB %5dkB", duration, bytecodeSize, targetCodeSize, allocatedKBytes)); 249 } else if (printCompilation) { 250 TTY.println(String.format("%-6d JVMCI %-70s %-45s %-50s | %4dms %5dB %5dB %5dkB", getId(), "", "", "", duration, bytecodeSize, targetCodeSize, allocatedKBytes)); 251 } 252 } 253 } 254 255 if (result != null) { 256 try (DebugCloseable b = CodeInstallationTime.start()) { 257 installMethod(result); 258 } 259 } 260 stats.finish(method, installedCode); 261 if (result != null) { 262 return HotSpotCompilationRequestResult.success(result.getBytecodeSize() - method.getCodeSize()); 263 } 264 return null; 265 } catch (BailoutException bailout) { 266 BAILOUTS.increment(); 267 if (ExitVMOnBailout.getValue()) { 268 TTY.out.println(method.format("Bailout in %H.%n(%p)")); 269 bailout.printStackTrace(TTY.out); 270 System.exit(-1); 271 } else if (PrintBailout.getValue()) { 272 TTY.out.println(method.format("Bailout in %H.%n(%p)")); 273 bailout.printStackTrace(TTY.out); 274 } 275 /* 276 * Handling of permanent bailouts: Permanent bailouts that can happen for example due to 277 * unsupported unstructured control flow in the bytecodes of a method must not be 278 * retried. Hotspot compile broker will ensure that no recompilation at the given tier 279 * will happen if retry is false. 280 */ 281 final boolean permanentBailout = bailout.isPermanent(); 282 if (permanentBailout && PrintBailout.getValue()) { 283 TTY.println("Permanent bailout %s compiling method %s %s.", bailout.getMessage(), HotSpotGraalCompiler.str(method), (isOSR ? "OSR" : "")); 284 } 285 return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !permanentBailout); 286 } catch (Throwable t) { 287 // Log a failure event. 288 EventProvider.CompilerFailureEvent event = eventProvider.newCompilerFailureEvent(); 289 if (event.shouldWrite()) { 290 event.setCompileId(getId()); 291 event.setMessage(t.getMessage()); 292 event.commit(); 293 } 294 295 handleException(t); 296 /* 297 * Treat random exceptions from the compiler as indicating a problem compiling this 298 * method. Report the result of toString instead of getMessage to ensure that the 299 * exception type is included in the output in case there's no detail mesage. 300 */ 301 return HotSpotCompilationRequestResult.failure(t.toString(), false); 302 } finally { 303 try { 304 int compiledBytecodes = 0; 305 int codeSize = 0; 306 if (result != null) { 307 compiledBytecodes = result.getBytecodeSize(); 308 CompiledBytecodes.add(compiledBytecodes); 309 if (installedCode != null) { 310 codeSize = installedCode.getSize(); 311 CompiledAndInstalledBytecodes.add(compiledBytecodes); 312 InstalledCodeSize.add(codeSize); 313 } 314 } 315 316 // Log a compilation event. 317 if (compilationEvent.shouldWrite()) { 318 compilationEvent.setMethod(method.format("%H.%n(%p)")); 319 compilationEvent.setCompileId(getId()); 320 compilationEvent.setCompileLevel(config.compilationLevelFullOptimization); 321 compilationEvent.setSucceeded(result != null && installedCode != null); 322 compilationEvent.setIsOsr(isOSR); 323 compilationEvent.setCodeSize(codeSize); 324 compilationEvent.setInlinedBytes(compiledBytecodes); 325 compilationEvent.commit(); 326 } 327 } catch (Throwable t) { 328 handleException(t); 329 } 330 } 331 } 332 333 protected void handleException(Throwable t) { 334 /* 335 * Automatically enable ExitVMOnException during bootstrap or when asserts are enabled but 336 * respect ExitVMOnException if it's been explicitly set. 337 */ 338 boolean exitVMOnException = ExitVMOnException.getValue(); 339 if (!ExitVMOnException.hasBeenSet()) { 340 assert (exitVMOnException = true) == true; 341 if (!exitVMOnException) { 342 HotSpotGraalRuntimeProvider runtime = compiler.getGraalRuntime(); 343 if (runtime.isBootstrapping()) { 344 exitVMOnException = true; 345 } 346 } 347 } 348 349 if (PrintStackTraceOnException.getValue() || exitVMOnException) { 350 try { 351 t.printStackTrace(TTY.out); 352 } catch (Throwable throwable) { 353 // Don't let an exception here change the other control flow 354 } 355 } 356 357 if (exitVMOnException) { 358 System.exit(-1); 359 } 360 } 361 362 private String getMethodDescription() { 363 HotSpotResolvedJavaMethod method = getMethod(); 364 return String.format("%-6d JVMCI %-70s %-45s %-50s %s", getId(), method.getDeclaringClass().getName(), method.getName(), method.getSignature().toMethodDescriptor(), 365 getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "(OSR@" + getEntryBCI() + ") "); 366 } 367 368 @SuppressWarnings("try") 369 private void installMethod(final CompilationResult compResult) { 370 final CodeCacheProvider codeCache = jvmciRuntime.getHostJVMCIBackend().getCodeCache(); 371 installedCode = null; 372 Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult}; 373 try (Scope s = Debug.scope("CodeInstall", context)) { 374 HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(getRequest().getMethod(), getRequest(), compResult); 375 installedCode = (HotSpotInstalledCode) codeCache.installCode(getRequest().getMethod(), compiledCode, null, getRequest().getMethod().getSpeculationLog(), installAsDefault); 376 } catch (Throwable e) { 377 throw Debug.handle(e); 378 } 379 } 380 381 @Override 382 public String toString() { 383 return "Compilation[id=" + getId() + ", " + getMethod().format("%H.%n(%p)") + (getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "@" + getEntryBCI()) + "]"; 384 } 385 386 private HotSpotCompilationRequest getRequest() { 387 return compilationId.getRequest(); 388 } 389 }