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 }