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 testTruffleCompilation() 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     private static final boolean VERBOSE = Boolean.getBoolean(CompilationWrapperTest.class.getSimpleName() + ".verbose");
 134 
 135     private static void testHelper(List<Probe> initialProbes, List<String> extraVmArgs, String... mainClassAndArgs) throws IOException, InterruptedException {
 136         final File dumpPath = new File(CompilationWrapperTest.class.getSimpleName() + "_" + System.currentTimeMillis()).getAbsoluteFile();
 137         List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine());
 138         vmArgs.removeIf(a -> a.startsWith("-Dgraal."));
 139         vmArgs.remove("-esa");
 140         vmArgs.remove("-ea");
 141         vmArgs.add("-Dgraal.DumpPath=" + dumpPath);
 142         // Force output to a file even if there's a running IGV instance available.
 143         vmArgs.add("-Dgraal.PrintGraphFile=true");
 144         vmArgs.addAll(extraVmArgs);
 145 
 146         Subprocess proc = SubprocessUtil.java(vmArgs, mainClassAndArgs);
 147         if (VERBOSE) {
 148             System.out.println(proc);
 149         }
 150 
 151         List<Probe> probes = new ArrayList<>(initialProbes);
 152         Probe diagnosticProbe = new Probe("Graal diagnostic output saved in ", 1);
 153         probes.add(diagnosticProbe);
 154         probes.add(new Probe("Forced crash after compiling", Integer.MAX_VALUE) {
 155             @Override
 156             String test() {
 157                 return actualOccurrences > 0 ? null : "expected at least 1 occurrence";
 158             }
 159         });
 160 
 161         for (String line : proc.output) {
 162             for (Probe probe : probes) {
 163                 if (probe.matches(line)) {
 164                     break;
 165                 }
 166             }
 167         }
 168         for (Probe probe : probes) {
 169             String error = probe.test();
 170             if (error != null) {
 171                 Assert.fail(String.format("Did not find expected occurences of '%s' in output of command: %s%n%s", probe.substring, error, proc));
 172             }
 173         }
 174 
 175         String diagnosticOutputZip = diagnosticProbe.lastMatchingLine.substring(diagnosticProbe.substring.length()).trim();
 176 
 177         List<String> dumpPathEntries = Arrays.asList(dumpPath.list());
 178 
 179         File zip = new File(diagnosticOutputZip).getAbsoluteFile();
 180         Assert.assertTrue(zip.toString(), zip.exists());
 181         Assert.assertTrue(zip + " not in " + dumpPathEntries, dumpPathEntries.contains(zip.getName()));
 182         try {
 183             int bgv = 0;
 184             int cfg = 0;
 185             ZipFile dd = new ZipFile(diagnosticOutputZip);
 186             List<String> entries = new ArrayList<>();
 187             for (Enumeration<? extends ZipEntry> e = dd.entries(); e.hasMoreElements();) {
 188                 ZipEntry ze = e.nextElement();
 189                 String name = ze.getName();
 190                 entries.add(name);
 191                 if (name.endsWith(".bgv")) {
 192                     bgv++;
 193                 } else if (name.endsWith(".cfg")) {
 194                     cfg++;
 195                 }
 196             }
 197             if (bgv == 0) {
 198                 Assert.fail(String.format("Expected at least one .bgv file in %s: %s%n%s", diagnosticOutputZip, entries, proc));
 199             }
 200             if (cfg == 0) {
 201                 Assert.fail(String.format("Expected at least one .cfg file in %s: %s", diagnosticOutputZip, entries));
 202             }
 203         } finally {
 204             zip.delete();
 205             dumpPath.delete();
 206         }
 207     }
 208 }