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