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