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 
  24 package compiler.testlibrary.rtm;
  25 
  26 import jdk.test.lib.process.OutputAnalyzer;
  27 import jdk.test.lib.process.ProcessTools;
  28 import jdk.test.lib.Utils;
  29 import jdk.test.lib.cli.CommandLineOptionTest;
  30 
  31 import java.io.IOException;
  32 import java.nio.file.Files;
  33 import java.nio.file.Paths;
  34 import java.util.Arrays;
  35 import java.util.Collections;
  36 import java.util.LinkedList;
  37 import java.util.List;
  38 import java.util.regex.Matcher;
  39 import java.util.regex.Pattern;
  40 
  41 /**
  42  * Auxiliary methods used for RTM testing.
  43  */
  44 public 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                 "-XaddExports: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 }