24
25 package org.graalvm.compiler.core;
26
27 import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.ExitVM;
28 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAction;
29 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
30 import static org.graalvm.compiler.core.GraalCompilerOptions.ExitVMOnException;
31 import static org.graalvm.compiler.core.GraalCompilerOptions.MaxCompilationProblemsPerAction;
32 import static org.graalvm.compiler.debug.DebugContext.VERBOSE_LEVEL;
33 import static org.graalvm.compiler.debug.DebugOptions.Dump;
34 import static org.graalvm.compiler.debug.DebugOptions.DumpPath;
35 import static org.graalvm.compiler.debug.DebugOptions.MethodFilter;
36
37 import java.io.ByteArrayOutputStream;
38 import java.io.File;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.io.PrintStream;
42 import java.util.Map;
43
44 import org.graalvm.compiler.debug.DebugCloseable;
45 import org.graalvm.compiler.debug.DebugContext;
46 import org.graalvm.compiler.debug.DiagnosticsOutputDirectory;
47 import org.graalvm.compiler.debug.PathUtilities;
48 import org.graalvm.compiler.debug.TTY;
49 import org.graalvm.compiler.options.EnumOptionKey;
50 import org.graalvm.compiler.options.OptionValues;
51
52 import jdk.vm.ci.code.BailoutException;
53
54 /**
55 * Wrapper for a compilation that centralizes what action to take based on
56 * {@link GraalCompilerOptions#CompilationBailoutAction} and
57 * {@link GraalCompilerOptions#CompilationFailureAction} when an uncaught exception occurs during
58 * compilation.
59 */
60 public abstract class CompilationWrapper<T> {
61
62 /**
63 * Actions to take upon an exception being raised during compilation performed via
64 * {@link CompilationWrapper}. The actions are with respect to what the user sees on the
146 return actionKey.getValue(options);
147 }
148
149 /**
150 * Perform the compilation wrapped by this object.
151 *
152 * @param debug the debug context to use for the compilation
153 */
154 protected abstract T performCompilation(DebugContext debug);
155
156 /**
157 * Gets a value that represents the input to the compilation.
158 */
159 @Override
160 public abstract String toString();
161
162 /**
163 * Creates the {@link DebugContext} to use when retrying a compilation.
164 *
165 * @param options the options for configuring the debug context
166 */
167 protected abstract DebugContext createRetryDebugContext(OptionValues options);
168
169 @SuppressWarnings("try")
170 public final T run(DebugContext initialDebug) {
171 try {
172 return performCompilation(initialDebug);
173 } catch (Throwable cause) {
174 OptionValues initialOptions = initialDebug.getOptions();
175
176 String causeType = "failure";
177 EnumOptionKey<ExceptionAction> actionKey;
178 if (cause instanceof BailoutException) {
179 actionKey = CompilationBailoutAction;
180 causeType = "bailout";
181 } else {
182 actionKey = CompilationFailureAction;
183 causeType = "failure";
184 }
185 synchronized (CompilationFailureAction) {
186 // Serialize all compilation failure handling.
187 // This prevents retry compilation storms and interleaving
210 actionKey.getName(), ExceptionAction.Silent);
211 ps.printf("To capture more information for diagnosing or reporting a compilation %s, " +
212 "set %s to %s or %s (e.g., -Dgraal.%s=%s).%n",
213 causeType,
214 actionKey.getName(), ExceptionAction.Diagnose,
215 ExceptionAction.ExitVM,
216 actionKey.getName(), ExceptionAction.Diagnose);
217 }
218 TTY.print(baos.toString());
219 return handleException(cause);
220 }
221
222 // action is Diagnose or ExitVM
223
224 if (Dump.hasBeenSet(initialOptions)) {
225 // If dumping is explicitly enabled, Graal is being debugged
226 // so don't interfere with what the user is expecting to see.
227 return handleException(cause);
228 }
229
230 String dir = this.outputDirectory.getPath();
231 if (dir == null) {
232 return handleException(cause);
233 }
234 String dumpName = PathUtilities.sanitizeFileName(toString());
235 File dumpPath = new File(dir, dumpName);
236 dumpPath.mkdirs();
237 if (!dumpPath.exists()) {
238 TTY.println("Warning: could not create diagnostics directory " + dumpPath);
239 return handleException(cause);
240 }
241
242 String message;
243 ByteArrayOutputStream baos = new ByteArrayOutputStream();
244 try (PrintStream ps = new PrintStream(baos)) {
245 ps.printf("%s: Compilation of %s failed: ", Thread.currentThread(), this);
246 cause.printStackTrace(ps);
247 ps.printf("To disable compilation %s notifications, set %s to %s (e.g., -Dgraal.%s=%s).%n",
248 causeType,
249 actionKey.getName(), ExceptionAction.Silent,
250 actionKey.getName(), ExceptionAction.Silent);
251 ps.printf("To print a message for a compilation %s without retrying the compilation, " +
252 "set %s to %s (e.g., -Dgraal.%s=%s).%n",
253 causeType,
254 actionKey.getName(), ExceptionAction.Print,
255 actionKey.getName(), ExceptionAction.Print);
256 ps.println("Retrying compilation of " + this);
257 message = baos.toString();
258 }
259
260 TTY.print(message);
261 File retryLogFile = new File(dumpPath, "retry.log");
262 try (PrintStream ps = new PrintStream(new FileOutputStream(retryLogFile))) {
263 ps.print(message);
264 } catch (IOException ioe) {
265 TTY.printf("Error writing to %s: %s%n", retryLogFile, ioe);
266 }
267
268 OptionValues retryOptions = new OptionValues(initialOptions,
269 Dump, ":" + VERBOSE_LEVEL,
270 MethodFilter, null,
271 DumpPath, dumpPath.getPath());
272
273 try (DebugContext retryDebug = createRetryDebugContext(retryOptions); DebugCloseable s = retryDebug.disableIntercept()) {
274 T res = performCompilation(retryDebug);
275 maybeExitVM(action);
276 return res;
277 } catch (Throwable ignore) {
278 // Failures during retry are silent
279 T res = handleException(cause);
280 maybeExitVM(action);
281 return res;
282 }
283 }
284 }
285 }
286
287 private void maybeExitVM(ExceptionAction action) {
288 if (action == ExitVM) {
289 TTY.println("Exiting VM after retry compilation of " + this);
290 System.exit(-1);
291 }
292 }
293
294 /**
295 * Adjusts {@code initialAction} if necessary based on
296 * {@link GraalCompilerOptions#MaxCompilationProblemsPerAction}.
297 */
298 private ExceptionAction adjustAction(OptionValues initialOptions, EnumOptionKey<ExceptionAction> actionKey, ExceptionAction initialAction) {
299 ExceptionAction action = initialAction;
300 int maxProblems = MaxCompilationProblemsPerAction.getValue(initialOptions);
301 if (action != ExceptionAction.ExitVM) {
|
24
25 package org.graalvm.compiler.core;
26
27 import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.ExitVM;
28 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAction;
29 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
30 import static org.graalvm.compiler.core.GraalCompilerOptions.ExitVMOnException;
31 import static org.graalvm.compiler.core.GraalCompilerOptions.MaxCompilationProblemsPerAction;
32 import static org.graalvm.compiler.debug.DebugContext.VERBOSE_LEVEL;
33 import static org.graalvm.compiler.debug.DebugOptions.Dump;
34 import static org.graalvm.compiler.debug.DebugOptions.DumpPath;
35 import static org.graalvm.compiler.debug.DebugOptions.MethodFilter;
36
37 import java.io.ByteArrayOutputStream;
38 import java.io.File;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.io.PrintStream;
42 import java.util.Map;
43
44 import org.graalvm.compiler.debug.DebugContext;
45 import org.graalvm.compiler.debug.DiagnosticsOutputDirectory;
46 import org.graalvm.compiler.debug.PathUtilities;
47 import org.graalvm.compiler.debug.TTY;
48 import org.graalvm.compiler.options.EnumOptionKey;
49 import org.graalvm.compiler.options.OptionValues;
50
51 import jdk.vm.ci.code.BailoutException;
52
53 /**
54 * Wrapper for a compilation that centralizes what action to take based on
55 * {@link GraalCompilerOptions#CompilationBailoutAction} and
56 * {@link GraalCompilerOptions#CompilationFailureAction} when an uncaught exception occurs during
57 * compilation.
58 */
59 public abstract class CompilationWrapper<T> {
60
61 /**
62 * Actions to take upon an exception being raised during compilation performed via
63 * {@link CompilationWrapper}. The actions are with respect to what the user sees on the
145 return actionKey.getValue(options);
146 }
147
148 /**
149 * Perform the compilation wrapped by this object.
150 *
151 * @param debug the debug context to use for the compilation
152 */
153 protected abstract T performCompilation(DebugContext debug);
154
155 /**
156 * Gets a value that represents the input to the compilation.
157 */
158 @Override
159 public abstract String toString();
160
161 /**
162 * Creates the {@link DebugContext} to use when retrying a compilation.
163 *
164 * @param options the options for configuring the debug context
165 * @param logStream the log stream to use in the debug context
166 */
167 protected abstract DebugContext createRetryDebugContext(OptionValues options, PrintStream logStream);
168
169 @SuppressWarnings("try")
170 public final T run(DebugContext initialDebug) {
171 try {
172 return performCompilation(initialDebug);
173 } catch (Throwable cause) {
174 OptionValues initialOptions = initialDebug.getOptions();
175
176 String causeType = "failure";
177 EnumOptionKey<ExceptionAction> actionKey;
178 if (cause instanceof BailoutException) {
179 actionKey = CompilationBailoutAction;
180 causeType = "bailout";
181 } else {
182 actionKey = CompilationFailureAction;
183 causeType = "failure";
184 }
185 synchronized (CompilationFailureAction) {
186 // Serialize all compilation failure handling.
187 // This prevents retry compilation storms and interleaving
210 actionKey.getName(), ExceptionAction.Silent);
211 ps.printf("To capture more information for diagnosing or reporting a compilation %s, " +
212 "set %s to %s or %s (e.g., -Dgraal.%s=%s).%n",
213 causeType,
214 actionKey.getName(), ExceptionAction.Diagnose,
215 ExceptionAction.ExitVM,
216 actionKey.getName(), ExceptionAction.Diagnose);
217 }
218 TTY.print(baos.toString());
219 return handleException(cause);
220 }
221
222 // action is Diagnose or ExitVM
223
224 if (Dump.hasBeenSet(initialOptions)) {
225 // If dumping is explicitly enabled, Graal is being debugged
226 // so don't interfere with what the user is expecting to see.
227 return handleException(cause);
228 }
229
230 File dumpPath = null;
231 try {
232 String dir = this.outputDirectory.getPath();
233 if (dir != null) {
234 String dumpName = PathUtilities.sanitizeFileName(toString());
235 dumpPath = new File(dir, dumpName);
236 dumpPath.mkdirs();
237 if (!dumpPath.exists()) {
238 TTY.println("Warning: could not create diagnostics directory " + dumpPath);
239 dumpPath = null;
240 }
241 }
242 } catch (Throwable t) {
243 TTY.println("Warning: could not create Graal diagnostic directory");
244 t.printStackTrace(TTY.out);
245 }
246
247 String message;
248 ByteArrayOutputStream baos = new ByteArrayOutputStream();
249 try (PrintStream ps = new PrintStream(baos)) {
250 ps.printf("%s: Compilation of %s failed:%n", Thread.currentThread(), this);
251 cause.printStackTrace(ps);
252 ps.printf("To disable compilation %s notifications, set %s to %s (e.g., -Dgraal.%s=%s).%n",
253 causeType,
254 actionKey.getName(), ExceptionAction.Silent,
255 actionKey.getName(), ExceptionAction.Silent);
256 ps.printf("To print a message for a compilation %s without retrying the compilation, " +
257 "set %s to %s (e.g., -Dgraal.%s=%s).%n",
258 causeType,
259 actionKey.getName(), ExceptionAction.Print,
260 actionKey.getName(), ExceptionAction.Print);
261 if (dumpPath != null) {
262 ps.println("Retrying compilation of " + this);
263 } else {
264 ps.println("Not retrying compilation of " + this + " as the dump path could not be created.");
265 }
266 message = baos.toString();
267 }
268
269 TTY.print(message);
270 if (dumpPath == null) {
271 return handleException(cause);
272 }
273
274 File retryLogFile = new File(dumpPath, "retry.log");
275 try (PrintStream ps = new PrintStream(new FileOutputStream(retryLogFile))) {
276 ps.print(message);
277 } catch (IOException ioe) {
278 TTY.printf("Error writing to %s: %s%n", retryLogFile, ioe);
279 }
280
281 OptionValues retryOptions = new OptionValues(initialOptions,
282 Dump, ":" + VERBOSE_LEVEL,
283 MethodFilter, null,
284 DumpPath, dumpPath.getPath());
285
286 ByteArrayOutputStream logBaos = new ByteArrayOutputStream();
287 PrintStream ps = new PrintStream(logBaos);
288 try (DebugContext retryDebug = createRetryDebugContext(retryOptions, ps)) {
289 T res = performCompilation(retryDebug);
290 ps.println("There was no exception during retry.");
291 maybeExitVM(action);
292 return res;
293 } catch (Throwable e) {
294 ps.println("Exception during retry:");
295 e.printStackTrace(ps);
296 // Failures during retry are silent
297 T res = handleException(cause);
298 maybeExitVM(action);
299 return res;
300 } finally {
301 ps.close();
302 try (FileOutputStream fos = new FileOutputStream(retryLogFile, true)) {
303 fos.write(logBaos.toByteArray());
304 } catch (Throwable e) {
305 TTY.printf("Error writing to %s: %s%n", retryLogFile, e);
306 }
307 }
308 }
309 }
310 }
311
312 private void maybeExitVM(ExceptionAction action) {
313 if (action == ExitVM) {
314 TTY.println("Exiting VM after retry compilation of " + this);
315 System.exit(-1);
316 }
317 }
318
319 /**
320 * Adjusts {@code initialAction} if necessary based on
321 * {@link GraalCompilerOptions#MaxCompilationProblemsPerAction}.
322 */
323 private ExceptionAction adjustAction(OptionValues initialOptions, EnumOptionKey<ExceptionAction> actionKey, ExceptionAction initialAction) {
324 ExceptionAction action = initialAction;
325 int maxProblems = MaxCompilationProblemsPerAction.getValue(initialOptions);
326 if (action != ExceptionAction.ExitVM) {
|