1 /*
   2  * Copyright (c) 2017, 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.debug.DebugContext.VERBOSE_LEVEL;
  26 import static org.graalvm.compiler.debug.DebugOptions.Dump;
  27 import static org.graalvm.compiler.debug.DebugOptions.DumpPath;
  28 import static org.graalvm.compiler.debug.DebugOptions.MethodFilter;
  29 
  30 import java.io.ByteArrayOutputStream;
  31 import java.io.File;
  32 import java.io.FileNotFoundException;
  33 import java.io.FileOutputStream;
  34 import java.io.PrintStream;
  35 
  36 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  37 import org.graalvm.compiler.debug.DebugContext;
  38 import org.graalvm.compiler.debug.DebugRetryableTask;
  39 import org.graalvm.compiler.debug.TTY;
  40 import org.graalvm.compiler.options.OptionValues;
  41 import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
  42 
  43 import jdk.vm.ci.code.BailoutException;
  44 
  45 /**
  46  * Utility for retrying a compilation that throws an exception where the retry enables extra logging
  47  * for subsequently diagnosing the failure.
  48  */
  49 public abstract class HotSpotRetryableCompilation<T> extends DebugRetryableTask<T> {
  50 
  51     protected final HotSpotGraalRuntimeProvider runtime;
  52 
  53     public HotSpotRetryableCompilation(HotSpotGraalRuntimeProvider runtime) {
  54         this.runtime = runtime;
  55     }
  56 
  57     /**
  58      * Gets a value that represents the compilation unit being compiled.
  59      */
  60     @Override
  61     public abstract String toString();
  62 
  63     @Override
  64     protected DebugContext getRetryContext(DebugContext initialDebug, Throwable t) {
  65         if (t instanceof BailoutException) {
  66             return null;
  67         }
  68 
  69         OptionValues initialOptions = initialDebug.getOptions();
  70         if (Dump.hasBeenSet(initialOptions)) {
  71             // If dumping is explicitly enabled, Graal is being debugged
  72             // so don't interfere with what the user is expecting to see.
  73             return null;
  74         }
  75 
  76         String outputDirectory = runtime.getOutputDirectory();
  77         if (outputDirectory == null) {
  78             return null;
  79         }
  80         String dumpName = GraalDebugHandlersFactory.sanitizedFileName(toString());
  81         File dumpPath = new File(outputDirectory, dumpName);
  82         dumpPath.mkdirs();
  83         if (!dumpPath.exists()) {
  84             TTY.println("Warning: could not create dump directory " + dumpPath);
  85             return null;
  86         }
  87 
  88         TTY.println("Retrying compilation of " + this + " due to " + t);
  89         retryLogPath = new File(dumpPath, "retry.log").getPath();
  90         log("Exception causing retry", t);
  91         OptionValues retryOptions = new OptionValues(initialOptions,
  92                         Dump, ":" + VERBOSE_LEVEL,
  93                         MethodFilter, null,
  94                         DumpPath, dumpPath.getPath());
  95         SnippetReflectionProvider snippetReflection = runtime.getHostProviders().getSnippetReflection();
  96         return DebugContext.create(retryOptions, new GraalDebugHandlersFactory(snippetReflection));
  97     }
  98 
  99     private String retryLogPath;
 100 
 101     /**
 102      * Prints a message to a retry log file.
 103      *
 104      * @param message the message to print
 105      * @param t if non-{@code null}, the stack trace for this exception is written to the retry log
 106      *            after {@code message}
 107      */
 108     protected void log(String message, Throwable t) {
 109         if (retryLogPath != null) {
 110             try (PrintStream retryLog = new PrintStream(new FileOutputStream(retryLogPath), true)) {
 111                 StringBuilder buf = new StringBuilder(Thread.currentThread() + ": " + message);
 112                 if (t != null) {
 113                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
 114                     t.printStackTrace(new PrintStream(baos));
 115                     buf.append(System.lineSeparator()).append(baos.toString());
 116                 }
 117                 retryLog.println(buf);
 118             } catch (FileNotFoundException e) {
 119                 TTY.println("Warning: could not open retry log file " + retryLogPath + " [" + e + "]");
 120             }
 121         }
 122     }
 123 }