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 }