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