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 package org.graalvm.compiler.hotspot.test;
  24 
  25 import static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine;
  26 import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments;
  27 
  28 import java.io.File;
  29 import java.io.IOException;
  30 import java.util.ArrayList;
  31 import java.util.Arrays;
  32 import java.util.Collections;
  33 import java.util.Enumeration;
  34 import java.util.List;
  35 import java.util.zip.ZipEntry;
  36 import java.util.zip.ZipFile;
  37 
  38 import org.graalvm.compiler.core.GraalCompilerOptions;
  39 import org.graalvm.compiler.core.test.GraalCompilerTest;
  40 import org.graalvm.compiler.test.SubprocessUtil;
  41 import org.graalvm.compiler.test.SubprocessUtil.Subprocess;
  42 import org.junit.Assert;
  43 import org.junit.Test;
  44 
  45 /**
  46  * Tests support for dumping graphs and other info useful for debugging a compiler crash.
  47  */
  48 public class CompilationWrapperTest extends GraalCompilerTest {
  49 
  50     /**
  51      * Tests compilation requested by the VM.
  52      */
  53     @Test
  54     public void testVMCompilation1() throws IOException, InterruptedException {
  55         testHelper(Collections.emptyList(), Arrays.asList("-XX:+BootstrapJVMCI",
  56                         "-XX:+UseJVMCICompiler",
  57                         "-Dgraal.CompilationFailureAction=ExitVM",
  58                         "-Dgraal.CrashAt=Object.*,String.*",
  59                         "-version"));
  60     }
  61 
  62     /**
  63      * Tests that {@code -Dgraal.ExitVMOnException=true} works as an alias for
  64      * {@code -Dgraal.CompilationFailureAction=ExitVM}.
  65      */
  66     @Test
  67     public void testVMCompilation2() throws IOException, InterruptedException {
  68         testHelper(Collections.emptyList(), Arrays.asList("-XX:+BootstrapJVMCI",
  69                         "-XX:+UseJVMCICompiler",
  70                         "-Dgraal.ExitVMOnException=true",
  71                         "-Dgraal.CrashAt=Object.*,String.*",
  72                         "-version"));
  73     }
  74 
  75     static class Probe {
  76         final String substring;
  77         final int expectedOccurrences;
  78         int actualOccurrences;
  79         String lastMatchingLine;
  80 
  81         Probe(String substring, int expectedOccurrences) {
  82             this.substring = substring;
  83             this.expectedOccurrences = expectedOccurrences;
  84         }
  85 
  86         boolean matches(String line) {
  87             if (line.contains(substring)) {
  88                 actualOccurrences++;
  89                 lastMatchingLine = line;
  90                 return true;
  91             }
  92             return false;
  93         }
  94 
  95         String test() {
  96             return expectedOccurrences == actualOccurrences ? null : String.format("expected %d, got %d occurrences", expectedOccurrences, actualOccurrences);
  97         }
  98     }
  99 
 100     /**
 101      * Tests {@link GraalCompilerOptions#MaxCompilationProblemsPerAction} in context of a
 102      * compilation requested by the VM.
 103      */
 104     @Test
 105     public void testVMCompilation3() throws IOException, InterruptedException {
 106         final int maxProblems = 4;
 107         Probe[] probes = {
 108                         new Probe("To capture more information for diagnosing or reporting a compilation", maxProblems),
 109                         new Probe("Retrying compilation of", maxProblems),
 110                         new Probe("adjusting CompilationFailureAction from Diagnose to Print", 1),
 111                         new Probe("adjusting CompilationFailureAction from Print to Silent", 1),
 112         };
 113         testHelper(Arrays.asList(probes), Arrays.asList("-XX:+BootstrapJVMCI",
 114                         "-XX:+UseJVMCICompiler",
 115                         "-Dgraal.CompilationFailureAction=Diagnose",
 116                         "-Dgraal.MaxCompilationProblemsPerAction=" + maxProblems,
 117                         "-Dgraal.CrashAt=Object.*,String.*",
 118                         "-version"));
 119     }
 120 
 121     /**
 122      * Tests compilation requested by Truffle.
 123      */
 124     @Test
 125     public void testTruffleCompilation1() throws IOException, InterruptedException {
 126         testHelper(Collections.emptyList(),
 127                         Arrays.asList(
 128                                         "-Dgraal.CompilationFailureAction=ExitVM",
 129                                         "-Dgraal.CrashAt=root test1"),
 130                         "org.graalvm.compiler.truffle.test.SLTruffleGraalTestSuite", "test");
 131     }
 132 
 133     /**
 134      * Tests that TruffleCompilationExceptionsAreFatal works as expected.
 135      */
 136     @Test
 137     public void testTruffleCompilation2() throws IOException, InterruptedException {
 138         Probe[] probes = {
 139                         new Probe("Exiting VM due to TruffleCompilationExceptionsAreFatal=true", 1),
 140         };
 141         testHelper(Arrays.asList(probes),
 142                         Arrays.asList(
 143                                         "-Dgraal.CompilationFailureAction=Silent",
 144                                         "-Dgraal.TruffleCompilationExceptionsAreFatal=true",
 145                                         "-Dgraal.CrashAt=root test1"),
 146                         "org.graalvm.compiler.truffle.test.SLTruffleGraalTestSuite", "test");
 147     }
 148 
 149     private static final boolean VERBOSE = Boolean.getBoolean(CompilationWrapperTest.class.getSimpleName() + ".verbose");
 150 
 151     private static void testHelper(List<Probe> initialProbes, List<String> extraVmArgs, String... mainClassAndArgs) throws IOException, InterruptedException {
 152         final File dumpPath = new File(CompilationWrapperTest.class.getSimpleName() + "_" + System.currentTimeMillis()).getAbsoluteFile();
 153         List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine());
 154         vmArgs.removeIf(a -> a.startsWith("-Dgraal."));
 155         vmArgs.remove("-esa");
 156         vmArgs.remove("-ea");
 157         vmArgs.add("-Dgraal.DumpPath=" + dumpPath);
 158         // Force output to a file even if there's a running IGV instance available.
 159         vmArgs.add("-Dgraal.PrintGraphFile=true");
 160         vmArgs.addAll(extraVmArgs);
 161 
 162         Subprocess proc = SubprocessUtil.java(vmArgs, mainClassAndArgs);
 163         if (VERBOSE) {
 164             System.out.println(proc);
 165         }
 166 
 167         List<Probe> probes = new ArrayList<>(initialProbes);
 168         Probe diagnosticProbe = null;
 169         if (!extraVmArgs.contains("-Dgraal.TruffleCompilationExceptionsAreFatal=true")) {
 170             diagnosticProbe = new Probe("Graal diagnostic output saved in ", 1);
 171             probes.add(diagnosticProbe);
 172             probes.add(new Probe("Forced crash after compiling", Integer.MAX_VALUE) {
 173                 @Override
 174                 String test() {
 175                     return actualOccurrences > 0 ? null : "expected at least 1 occurrence";
 176                 }
 177             });
 178         }
 179 
 180         for (String line : proc.output) {
 181             for (Probe probe : probes) {
 182                 if (probe.matches(line)) {
 183                     break;
 184                 }
 185             }
 186         }
 187         for (Probe probe : probes) {
 188             String error = probe.test();
 189             if (error != null) {
 190                 Assert.fail(String.format("Did not find expected occurences of '%s' in output of command: %s%n%s", probe.substring, error, proc));
 191             }
 192         }
 193         if (diagnosticProbe != null) {
 194             String line = diagnosticProbe.lastMatchingLine;
 195             int substringStart = line.indexOf(diagnosticProbe.substring);
 196             int substringLength = diagnosticProbe.substring.length();
 197             String diagnosticOutputZip = line.substring(substringStart + substringLength).trim();
 198 
 199             List<String> dumpPathEntries = Arrays.asList(dumpPath.list());
 200 
 201             File zip = new File(diagnosticOutputZip).getAbsoluteFile();
 202             Assert.assertTrue(zip.toString(), zip.exists());
 203             Assert.assertTrue(zip + " not in " + dumpPathEntries, dumpPathEntries.contains(zip.getName()));
 204             try {
 205                 int bgv = 0;
 206                 int cfg = 0;
 207                 ZipFile dd = new ZipFile(diagnosticOutputZip);
 208                 List<String> entries = new ArrayList<>();
 209                 for (Enumeration<? extends ZipEntry> e = dd.entries(); e.hasMoreElements();) {
 210                     ZipEntry ze = e.nextElement();
 211                     String name = ze.getName();
 212                     entries.add(name);
 213                     if (name.endsWith(".bgv")) {
 214                         bgv++;
 215                     } else if (name.endsWith(".cfg")) {
 216                         cfg++;
 217                     }
 218                 }
 219                 if (bgv == 0) {
 220                     Assert.fail(String.format("Expected at least one .bgv file in %s: %s%n%s", diagnosticOutputZip, entries, proc));
 221                 }
 222                 if (cfg == 0) {
 223                     Assert.fail(String.format("Expected at least one .cfg file in %s: %s", diagnosticOutputZip, entries));
 224                 }
 225             } finally {
 226                 zip.delete();
 227                 dumpPath.delete();
 228             }
 229         }
 230     }
 231 }