1 /*
   2  * Copyright (c) 2013, 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 
  24 
  25 package org.graalvm.compiler.hotspot.test;
  26 
  27 import static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine;
  28 import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments;
  29 
  30 import java.io.File;
  31 import java.io.IOException;
  32 import java.util.ArrayList;
  33 import java.util.Arrays;
  34 import java.util.Collections;
  35 import java.util.Enumeration;
  36 import java.util.List;
  37 import java.util.zip.ZipEntry;
  38 import java.util.zip.ZipFile;
  39 
  40 import org.graalvm.compiler.core.GraalCompilerOptions;
  41 import org.graalvm.compiler.core.test.GraalCompilerTest;
  42 import org.graalvm.compiler.test.SubprocessUtil;
  43 import org.graalvm.compiler.test.SubprocessUtil.Subprocess;
  44 import org.junit.Assert;
  45 import org.junit.Test;
  46 
  47 /**
  48  * Tests support for dumping graphs and other info useful for debugging a compiler crash.
  49  */
  50 public class CompilationWrapperTest extends GraalCompilerTest {
  51 
  52     /**
  53      * Tests compilation requested by the VM.
  54      */
  55     @Test
  56     public void testVMCompilation1() throws IOException, InterruptedException {
  57         testHelper(Collections.emptyList(), Arrays.asList("-XX:-TieredCompilation",
  58                         "-XX:+UseJVMCICompiler",
  59                         "-Dgraal.CompilationFailureAction=ExitVM",
  60                         "-Dgraal.CrashAt=TestProgram.*",
  61                         "-Xcomp",
  62                         "-XX:CompileCommand=compileonly,*/TestProgram.print*",
  63                         TestProgram.class.getName()));
  64     }
  65 
  66     /**
  67      * Tests that {@code -Dgraal.ExitVMOnException=true} works as an alias for
  68      * {@code -Dgraal.CompilationFailureAction=ExitVM}.
  69      */
  70     @Test
  71     public void testVMCompilation2() throws IOException, InterruptedException {
  72         testHelper(Collections.emptyList(), Arrays.asList("-XX:-TieredCompilation",
  73                         "-XX:+UseJVMCICompiler",
  74                         "-Dgraal.ExitVMOnException=true",
  75                         "-Dgraal.CrashAt=TestProgram.*",
  76                         "-Xcomp",
  77                         "-XX:CompileCommand=compileonly,*/TestProgram.print*",
  78                         TestProgram.class.getName()));
  79     }
  80 
  81     static class Probe {
  82         final String substring;
  83         final int expectedOccurrences;
  84         int actualOccurrences;
  85         String lastMatchingLine;
  86 
  87         Probe(String substring, int expectedOccurrences) {
  88             this.substring = substring;
  89             this.expectedOccurrences = expectedOccurrences;
  90         }
  91 
  92         boolean matches(String line) {
  93             if (line.contains(substring)) {
  94                 actualOccurrences++;
  95                 lastMatchingLine = line;
  96                 return true;
  97             }
  98             return false;
  99         }
 100 
 101         String test() {
 102             return expectedOccurrences == actualOccurrences ? null : String.format("expected %d, got %d occurrences", expectedOccurrences, actualOccurrences);
 103         }
 104     }
 105 
 106     /**
 107      * Tests {@link GraalCompilerOptions#MaxCompilationProblemsPerAction} in context of a
 108      * compilation requested by the VM.
 109      */
 110     @Test
 111     public void testVMCompilation3() throws IOException, InterruptedException {
 112         final int maxProblems = 2;
 113         Probe retryingProbe = new Probe("Retrying compilation of", maxProblems) {
 114             @Override
 115             String test() {
 116                 return actualOccurrences > 0 && actualOccurrences <= maxProblems ? null : String.format("expected occurrences to be in [1 .. %d]", maxProblems);
 117             }
 118         };
 119         Probe adjustmentProbe = new Probe("adjusting CompilationFailureAction from Diagnose to Print", 1) {
 120             @Override
 121             String test() {
 122                 if (retryingProbe.actualOccurrences >= maxProblems) {
 123                     if (actualOccurrences == 0) {
 124                         return "expected at least one occurrence";
 125                     }
 126                 }
 127                 return null;
 128             }
 129         };
 130         Probe[] probes = {
 131                         retryingProbe,
 132                         adjustmentProbe
 133         };
 134         testHelper(Arrays.asList(probes), Arrays.asList("-XX:-TieredCompilation",
 135                         "-XX:+UseJVMCICompiler",
 136                         "-Dgraal.CompilationFailureAction=Diagnose",
 137                         "-Dgraal.MaxCompilationProblemsPerAction=" + maxProblems,
 138                         "-Dgraal.CrashAt=TestProgram.*",
 139                         "-Xcomp",
 140                         "-XX:CompileCommand=compileonly,*/TestProgram.print*",
 141                         TestProgram.class.getName()));
 142     }
 143 
 144     /**
 145      * Tests compilation requested by Truffle.
 146      */
 147     @Test
 148     public void testTruffleCompilation1() throws IOException, InterruptedException {
 149         testHelper(Collections.emptyList(),
 150                         Arrays.asList(
 151                                         "-Dgraal.CompilationFailureAction=ExitVM",
 152                                         "-Dgraal.TrufflePerformanceWarningsAreFatal=true",
 153                                         "-Dgraal.CrashAt=root test1"),
 154                         "org.graalvm.compiler.truffle.test.SLTruffleGraalTestSuite", "test");
 155     }
 156 
 157     /**
 158      * Tests that TruffleCompilationExceptionsAreFatal works as expected.
 159      */
 160     @Test
 161     public void testTruffleCompilation2() throws IOException, InterruptedException {
 162         Probe[] probes = {
 163                         new Probe("Exiting VM due to TruffleCompilationExceptionsAreFatal=true", 1),
 164         };
 165         testHelper(Arrays.asList(probes),
 166                         Arrays.asList(
 167                                         "-Dgraal.CompilationFailureAction=Silent",
 168                                         "-Dgraal.TruffleCompilationExceptionsAreFatal=true",
 169                                         "-Dgraal.CrashAt=root test1"),
 170                         "org.graalvm.compiler.truffle.test.SLTruffleGraalTestSuite", "test");
 171     }
 172 
 173     /**
 174      * Tests that TrufflePerformanceWarningsAreFatal generates diagnostic output.
 175      */
 176     @Test
 177     public void testTruffleCompilation3() throws IOException, InterruptedException {
 178         Probe[] probes = {
 179                         new Probe("Exiting VM due to TrufflePerformanceWarningsAreFatal=true", 1),
 180         };
 181         testHelper(Arrays.asList(probes),
 182                         Arrays.asList(
 183                                         "-Dgraal.CompilationFailureAction=Silent",
 184                                         "-Dgraal.TrufflePerformanceWarningsAreFatal=true",
 185                                         "-Dgraal.CrashAt=root test1:PermanentBailout"),
 186                         "org.graalvm.compiler.truffle.test.SLTruffleGraalTestSuite", "test");
 187     }
 188 
 189     private static final boolean VERBOSE = Boolean.getBoolean(CompilationWrapperTest.class.getSimpleName() + ".verbose");
 190 
 191     private static void testHelper(List<Probe> initialProbes, List<String> extraVmArgs, String... mainClassAndArgs) throws IOException, InterruptedException {
 192         final File dumpPath = new File(CompilationWrapperTest.class.getSimpleName() + "_" + System.currentTimeMillis()).getAbsoluteFile();
 193         List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine());
 194         vmArgs.removeIf(a -> a.startsWith("-Dgraal."));
 195         vmArgs.remove("-esa");
 196         vmArgs.remove("-ea");
 197         vmArgs.add("-Dgraal.DumpPath=" + dumpPath);
 198         // Force output to a file even if there's a running IGV instance available.
 199         vmArgs.add("-Dgraal.PrintGraphFile=true");
 200         vmArgs.addAll(extraVmArgs);
 201 
 202         Subprocess proc = SubprocessUtil.java(vmArgs, mainClassAndArgs);
 203         if (VERBOSE) {
 204             System.out.println(proc);
 205         }
 206 
 207         List<Probe> probes = new ArrayList<>(initialProbes);
 208         Probe diagnosticProbe = null;
 209         if (!extraVmArgs.contains("-Dgraal.TruffleCompilationExceptionsAreFatal=true")) {
 210             diagnosticProbe = new Probe("Graal diagnostic output saved in ", 1);
 211             probes.add(diagnosticProbe);
 212             probes.add(new Probe("Forced crash after compiling", Integer.MAX_VALUE) {
 213                 @Override
 214                 String test() {
 215                     return actualOccurrences > 0 ? null : "expected at least 1 occurrence";
 216                 }
 217             });
 218         }
 219 
 220         for (String line : proc.output) {
 221             for (Probe probe : probes) {
 222                 if (probe.matches(line)) {
 223                     break;
 224                 }
 225             }
 226         }
 227         for (Probe probe : probes) {
 228             String error = probe.test();
 229             if (error != null) {
 230                 Assert.fail(String.format("Did not find expected occurences of '%s' in output of command: %s%n%s", probe.substring, error, proc));
 231             }
 232         }
 233         if (diagnosticProbe != null) {
 234             String line = diagnosticProbe.lastMatchingLine;
 235             int substringStart = line.indexOf(diagnosticProbe.substring);
 236             int substringLength = diagnosticProbe.substring.length();
 237             String diagnosticOutputZip = line.substring(substringStart + substringLength).trim();
 238 
 239             List<String> dumpPathEntries = Arrays.asList(dumpPath.list());
 240 
 241             File zip = new File(diagnosticOutputZip).getAbsoluteFile();
 242             Assert.assertTrue(zip.toString(), zip.exists());
 243             Assert.assertTrue(zip + " not in " + dumpPathEntries, dumpPathEntries.contains(zip.getName()));
 244             try {
 245                 int bgvOrCfgFiles = 0;
 246                 ZipFile dd = new ZipFile(diagnosticOutputZip);
 247                 List<String> entries = new ArrayList<>();
 248                 for (Enumeration<? extends ZipEntry> e = dd.entries(); e.hasMoreElements();) {
 249                     ZipEntry ze = e.nextElement();
 250                     String name = ze.getName();
 251                     entries.add(name);
 252                     if (name.endsWith(".bgv") || name.endsWith(".cfg")) {
 253                         bgvOrCfgFiles++;
 254                     }
 255                 }
 256                 if (bgvOrCfgFiles == 0) {
 257                     Assert.fail(String.format("Expected at least one .bgv or .cfg file in %s: %s%n%s", diagnosticOutputZip, entries, proc));
 258                 }
 259             } finally {
 260                 zip.delete();
 261                 dumpPath.delete();
 262             }
 263         }
 264     }
 265 }
 266 
 267 class TestProgram {
 268     public static void main(String[] args) {
 269         printHello1();
 270         printWorld1();
 271         printHello2();
 272         printWorld2();
 273         printHello3();
 274         printWorld3();
 275     }
 276 
 277     private static void printHello1() {
 278         System.out.println("Hello1");
 279     }
 280 
 281     private static void printWorld1() {
 282         System.out.println("World1");
 283     }
 284 
 285     private static void printHello2() {
 286         System.out.println("Hello2");
 287     }
 288 
 289     private static void printWorld2() {
 290         System.out.println("World2");
 291     }
 292 
 293     private static void printHello3() {
 294         System.out.println("Hello3");
 295     }
 296 
 297     private static void printWorld3() {
 298         System.out.println("World3");
 299     }
 300 }