1/*
2 * Copyright (c) 2014, 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
24package compiler.testlibrary.rtm;
25
26import jdk.test.lib.process.OutputAnalyzer;
27import jdk.test.lib.process.ProcessTools;
28import jdk.test.lib.Utils;
29import jdk.test.lib.cli.CommandLineOptionTest;
30
31import java.io.IOException;
32import java.nio.file.Files;
33import java.nio.file.Paths;
34import java.util.Arrays;
35import java.util.Collections;
36import java.util.LinkedList;
37import java.util.List;
38import java.util.regex.Matcher;
39import java.util.regex.Pattern;
40
41/**
42 * Auxiliary methods used for RTM testing.
43 */
44public class RTMTestBase {
45    private static final String RTM_STATE_CHANGE_REASON = "rtm_state_change";
46    /**
47     * We don't parse compilation log as XML-document and use regular
48     * expressions instead, because in some cases it could be
49     * malformed.
50     */
51    private static final String FIRED_UNCOMMON_TRAP_PATTERN_TEMPLATE
52            = "<uncommon_trap thread='[0-9]+' reason='%s'";
53    private static final String INSTALLED_UNCOMMON_TRAP_PATTERN_TEMPLATE
54            = "<uncommon_trap bci='[0-9]+' reason='%s'";
55
56    /**
57     * Executes RTM test in a new JVM started with {@code options} cli options.
58     *
59     * @param test test case to execute.
60     * @param options additional options for VM
61     * @throws Exception when something went wrong.
62     */
63    public static OutputAnalyzer executeRTMTest(CompilableTest test,
64            String... options) throws Exception {
65        ProcessBuilder processBuilder
66                = ProcessTools.createJavaProcessBuilder(
67                RTMTestBase.prepareTestOptions(test, options));
68        OutputAnalyzer outputAnalyzer
69                = new OutputAnalyzer(processBuilder.start());
70        System.out.println(outputAnalyzer.getOutput());
71        return outputAnalyzer;
72    }
73
74    /**
75     * Executes test case and save compilation log to {@code logFileName}.
76     *
77     * @param logFileName a name of compilation log file
78     * @param test a test case to execute case to execute
79     * @param options additional options to VM
80     * @return OutputAnalyzer for started test case
81     * @throws Exception when something went wrong
82     */
83    public static OutputAnalyzer executeRTMTest(String logFileName,
84            CompilableTest test, String... options) throws Exception {
85        ProcessBuilder processBuilder
86                = ProcessTools.createJavaProcessBuilder(
87                RTMTestBase.prepareTestOptions(logFileName, test, options));
88        OutputAnalyzer outputAnalyzer
89                = new OutputAnalyzer(processBuilder.start());
90
91        System.out.println(outputAnalyzer.getOutput());
92
93        return outputAnalyzer;
94    }
95
96    /**
97     * Finds count of uncommon traps with reason {@code reason} installed
98     * during compilation.
99     *
100     * @param compilationLogFile a path to file with LogCompilation output.
101     * @param reason reason of installed uncommon traps.
102     * @return count of installed uncommon traps with reason {@code reason}.
103     * @throws IOException
104     */
105    public static int installedUncommonTraps(String compilationLogFile,
106            String reason)throws IOException {
107        String pattern = String.format(
108                RTMTestBase.INSTALLED_UNCOMMON_TRAP_PATTERN_TEMPLATE,
109                reason);
110        return RTMTestBase.findTraps(compilationLogFile, pattern);
111    }
112
113    /**
114     * Finds count of uncommon traps with reason <i>rtm_state_change</i>
115     * installed during compilation.
116     *
117     * @param compilationLogFile a path to file with LogCompilation output.
118     * @return count of installed uncommon traps with reason
119     *         <i>rtm_state_change</i>.
120     * @throws IOException
121     */
122    public static int installedRTMStateChangeTraps(String compilationLogFile)
123            throws IOException {
124        return RTMTestBase.installedUncommonTraps(compilationLogFile,
125                RTMTestBase.RTM_STATE_CHANGE_REASON);
126    }
127
128    /**
129     * Finds count of fired uncommon traps with reason {@code reason}.
130     *
131     * @param compilationLogFile a path to file with LogCompilation output.
132     * @param reason a reason of fired uncommon traps.
133     * @return count of fired uncommon traps with reason {@code reason}.
134     * @throws IOException
135     */
136    public static int firedUncommonTraps(String compilationLogFile,
137            String reason) throws IOException {
138        String pattern = String.format(
139                RTMTestBase.FIRED_UNCOMMON_TRAP_PATTERN_TEMPLATE,
140                reason);
141        return RTMTestBase.findTraps(compilationLogFile, pattern);
142    }
143
144    /**
145     * Finds count of fired uncommon traps with reason <i>rtm_state_change</i>.
146     *
147     * @param compilationLogFile a path to file with LogCompilation output.
148     * @return count of fired uncommon traps with reason
149     *         <i>rtm_state_change</i>.
150     * @throws IOException
151     */
152    public static int firedRTMStateChangeTraps(String compilationLogFile)
153            throws IOException {
154        return RTMTestBase.firedUncommonTraps(compilationLogFile,
155                RTMTestBase.RTM_STATE_CHANGE_REASON);
156    }
157
158    /**
159     * Finds count of uncommon traps that matches regular
160     * expression in {@code re}.
161     *
162     * @param compilationLogFile a path to file with LogCompilation output.
163     * @param re regular expression to match uncommon traps.
164     * @throws IOException
165     */
166    private static int findTraps(String compilationLogFile, String re)
167            throws IOException {
168        String compilationLog = RTMTestBase.fileAsString(compilationLogFile);
169        Pattern pattern = Pattern.compile(re);
170        Matcher matcher = pattern.matcher(compilationLog);
171        int traps = 0;
172        while (matcher.find()) {
173            traps++;
174        }
175        return traps;
176    }
177
178    /**
179     * Returns file's content as a string.
180     *
181     * @param path a path to file to operate on.
182     * @return string with content of file.
183     * @throws IOException
184     */
185    private static String fileAsString(String path) throws IOException {
186        byte[] fileAsBytes = Files.readAllBytes(Paths.get(path));
187        return new String(fileAsBytes);
188    }
189
190    /**
191     * Prepares VM options for test execution.
192     * This method get test java options, filter out all RTM-related options,
193     * adds CompileCommand=compileonly,method_name options for each method
194     * from {@code methodToCompile} and finally appends all {@code vmOpts}.
195     *
196     * @param test test case whose methods that should be compiled.
197     *             If {@code null} then no additional <i>compileonly</i>
198     *             commands will be added to VM options.
199     * @param vmOpts additional options to pass to VM.
200     * @return Array with VM options.
201     */
202    private static String[] prepareTestOptions(CompilableTest test,
203            String... vmOpts) {
204        return RTMTestBase.prepareFilteredTestOptions(test, null, vmOpts);
205    }
206
207    /**
208     * Prepares VM options for test execution.
209     * This method get test java options, filter out all RTM-related options
210     * and all options that matches regexps in {@code additionalFilters},
211     * adds CompileCommand=compileonly,method_name options for each method
212     * from {@code methodToCompile} and finally appends all {@code vmOpts}.
213     *
214     * @param test test case whose methods that should be compiled.
215     *             If {@code null} then no additional <i>compileonly</i>
216     *             commands will be added to VM options.
217     * @param additionalFilters array with regular expression that will be
218     *                          used to filter out test java options.
219     *                          If {@code null} then no additional filters
220     *                          will be used.
221     * @param vmOpts additional options to pass to VM.
222     * @return array with VM options.
223     */
224    private static String[] prepareFilteredTestOptions(CompilableTest test,
225            String[] additionalFilters, String... vmOpts) {
226        List<String> finalVMOpts = new LinkedList<>();
227        String[] filters;
228
229        if (additionalFilters != null) {
230            filters = Arrays.copyOf(additionalFilters,
231                    additionalFilters.length + 1);
232        } else {
233            filters = new String[1];
234        }
235
236        filters[filters.length - 1] = "RTM";
237        String[] filteredVMOpts = Utils.getFilteredTestJavaOpts(filters);
238        Collections.addAll(finalVMOpts, filteredVMOpts);
239        Collections.addAll(finalVMOpts, "-Xcomp", "-server",
240                "-XX:-TieredCompilation", "-XX:+UseRTMLocking",
241                CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS,
242                CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS,
243                "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI",
244                "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED");
245
246        if (test != null) {
247            for (String method : test.getMethodsToCompileNames()) {
248                finalVMOpts.add("-XX:CompileCommand=compileonly," + method);
249            }
250        }
251        Collections.addAll(finalVMOpts, vmOpts);
252        return finalVMOpts.toArray(new String[finalVMOpts.size()]);
253    }
254
255    /**
256     * Adds additional options for VM required for successful execution of test.
257     *
258     * @param logFileName a name of compilation log file
259     * @param test a test case to execute
260     * @param options additional options to VM
261     * @return an array with VM options
262     */
263    private static String[] prepareTestOptions(String logFileName,
264            CompilableTest test, String... options) {
265        String[] preparedOptions = RTMTestBase.prepareFilteredTestOptions(
266                test,
267                new String[] {
268                        "LogCompilation",
269                        "LogFile"
270                });
271        List<String> updatedOptions = new LinkedList<>();
272        Collections.addAll(updatedOptions, preparedOptions);
273        Collections.addAll(updatedOptions,
274                "-XX:+LogCompilation",
275                "-XX:LogFile=" + logFileName);
276        Collections.addAll(updatedOptions, options);
277
278        return updatedOptions.toArray(new String[updatedOptions.size()]);
279    }
280}
281