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