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 */
23package org.graalvm.compiler.hotspot.test;
24
25import static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine;
26import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments;
27
28import java.io.File;
29import java.io.IOException;
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.Collections;
33import java.util.Enumeration;
34import java.util.List;
35import java.util.zip.ZipEntry;
36import java.util.zip.ZipFile;
37
38import org.graalvm.compiler.core.GraalCompilerOptions;
39import org.graalvm.compiler.core.test.GraalCompilerTest;
40import org.graalvm.compiler.test.SubprocessUtil;
41import org.graalvm.compiler.test.SubprocessUtil.Subprocess;
42import org.junit.Assert;
43import org.junit.Test;
44
45/**
46 * Tests support for dumping graphs and other info useful for debugging a compiler crash.
47 */
48public 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}
209