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