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 }