1 /* 2 * Copyright (c) 2014, 2015, 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 import static jdk.testlibrary.Asserts.assertGreaterThan; 25 import static jdk.testlibrary.Asserts.assertTrue; 26 27 import java.io.BufferedWriter; 28 import java.io.File; 29 import java.io.FileWriter; 30 import java.io.IOException; 31 import java.nio.file.Files; 32 import java.nio.file.Path; 33 import java.nio.file.Paths; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.List; 37 38 import jdk.test.lib.process.OutputAnalyzer; 39 import jdk.test.lib.process.ProcessTools; 40 import jdk.testlibrary.Asserts; 41 import jdk.testlibrary.JDKToolLauncher; 42 import jdk.testlibrary.Utils; 43 44 /** 45 * The helper class for running jps utility and verifying output from it 46 */ 47 public final class JpsHelper { 48 49 /** 50 * Helper class for handling jps arguments 51 */ 52 public enum JpsArg { 53 q, 54 l, 55 m, 56 v, 57 V; 58 59 /** 60 * Generate all possible combinations of {@link JpsArg} 61 * (31 argument combinations and no arguments case) 62 */ 63 public static List<List<JpsArg>> generateCombinations() { 64 final int argCount = JpsArg.values().length; 65 // If there are more than 30 args this algorithm will overflow. 66 Asserts.assertLessThan(argCount, 31, "Too many args"); 67 68 List<List<JpsArg>> combinations = new ArrayList<>(); 69 int combinationCount = (int) Math.pow(2, argCount); 70 for (int currCombo = 0; currCombo < combinationCount; ++currCombo) { 71 List<JpsArg> combination = new ArrayList<>(); 72 for (int position = 0; position < argCount; ++position) { 73 int bit = 1 << position; 74 if ((bit & currCombo) != 0) { 75 combination.add(JpsArg.values()[position]); 76 } 77 } 78 combinations.add(combination); 79 } 80 return combinations; 81 } 82 83 /** 84 * Return combination of {@link JpsArg} as a String array 85 */ 86 public static String[] asCmdArray(List<JpsArg> jpsArgs) { 87 List<String> list = new ArrayList<>(); 88 for (JpsArg jpsArg : jpsArgs) { 89 list.add("-" + jpsArg.toString()); 90 } 91 return list.toArray(new String[list.size()]); 92 } 93 94 } 95 96 /** 97 * VM arguments to start test application with. 98 * -XX:+UsePerfData is required for running the tests on embedded platforms. 99 */ 100 public static final String[] VM_ARGS = { 101 "-XX:+UsePerfData", "-Xmx512m", "-Xlog:gc", 102 "-Dmultiline.prop=value1\nvalue2\r\nvalue3" 103 }; 104 /** 105 * VM flag to start test application with 106 */ 107 public static final String VM_FLAG = "+DisableExplicitGC"; 108 109 private static File vmFlagsFile = null; 110 private static List<String> testVmArgs = null; 111 private static File manifestFile = null; 112 113 /** 114 * Create a file containing VM_FLAG in the working directory 115 */ 116 public static File getVmFlagsFile() throws IOException { 117 if (vmFlagsFile == null) { 118 vmFlagsFile = new File("vmflags"); 119 try (BufferedWriter output = new BufferedWriter(new FileWriter(vmFlagsFile))) { 120 output.write(VM_FLAG); 121 } 122 vmFlagsFile.deleteOnExit(); 123 } 124 return vmFlagsFile; 125 } 126 127 /** 128 * Return a list of VM arguments 129 */ 130 public static List<String> getVmArgs() throws IOException { 131 if (testVmArgs == null) { 132 testVmArgs = new ArrayList<>(); 133 testVmArgs.addAll(Arrays.asList(VM_ARGS)); 134 testVmArgs.add("-XX:Flags=" + getVmFlagsFile().getAbsolutePath()); 135 } 136 return testVmArgs; 137 } 138 139 /** 140 * Start jps utility without any arguments 141 */ 142 public static OutputAnalyzer jps() throws Exception { 143 return jps(null, null); 144 } 145 146 /** 147 * Start jps utility with tool arguments 148 */ 149 public static OutputAnalyzer jps(String... toolArgs) throws Exception { 150 return jps(null, Arrays.asList(toolArgs)); 151 } 152 153 /** 154 * Start jps utility with VM args and tool arguments 155 */ 156 public static OutputAnalyzer jps(List<String> vmArgs, List<String> toolArgs) throws Exception { 157 JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jps"); 158 launcher.addVMArg("-XX:+UsePerfData"); 159 if (vmArgs != null) { 160 for (String vmArg : vmArgs) { 161 launcher.addVMArg(vmArg); 162 } 163 } 164 if (toolArgs != null) { 165 for (String toolArg : toolArgs) { 166 launcher.addToolArg(toolArg); 167 } 168 } 169 170 ProcessBuilder processBuilder = new ProcessBuilder(launcher.getCommand()); 171 System.out.println(Arrays.toString(processBuilder.command().toArray()).replace(",", "")); 172 OutputAnalyzer output = ProcessTools.executeProcess(processBuilder); 173 System.out.println(output.getOutput()); 174 175 return output; 176 } 177 178 /** 179 * Verify jps stdout contains only pids and programs' name information. 180 * jps stderr may contain VM warning messages which will be ignored. 181 * 182 * The output can look like: 183 * 35536 Jps 184 * 35417 Main 185 * 31103 org.eclipse.equinox.launcher_1.3.0.v20120522-1813.jar 186 */ 187 public static void verifyJpsOutput(OutputAnalyzer output, String regex) throws Exception { 188 output.shouldHaveExitValue(0); 189 int matchedCount = output.stdoutShouldMatchByLine(regex); 190 assertGreaterThan(matchedCount , 0, "Found no lines matching pattern: " + regex); 191 output.stderrShouldNotMatch("[E|e]xception"); 192 output.stderrShouldNotMatch("[E|e]rror"); 193 } 194 195 /** 196 * Compare jps output with a content in a file line by line 197 */ 198 public static void verifyOutputAgainstFile(OutputAnalyzer output) throws IOException { 199 String testSrc = System.getProperty("test.src", "?"); 200 Path path = Paths.get(testSrc, "usage.out"); 201 List<String> fileOutput = Files.readAllLines(path); 202 List<String> outputAsLines = output.asLines(); 203 assertTrue(outputAsLines.containsAll(fileOutput), 204 "The ouput should contain all content of " + path.toAbsolutePath()); 205 } 206 207 public static void runJpsVariants(Long pid, String processName, String fullProcessName, String argument) throws Exception { 208 System.out.printf("INFO: user.dir: '%s''\n", System.getProperty("user.dir")); 209 List<List<JpsHelper.JpsArg>> combinations = JpsHelper.JpsArg.generateCombinations(); 210 for (List<JpsHelper.JpsArg> combination : combinations) { 211 OutputAnalyzer output = JpsHelper.jps(JpsHelper.JpsArg.asCmdArray(combination)); 212 output.shouldHaveExitValue(0); 213 214 boolean isQuiet = false; 215 boolean isFull = false; 216 String pattern; 217 for (JpsHelper.JpsArg jpsArg : combination) { 218 switch (jpsArg) { 219 case q: 220 // If '-q' is specified output should contain only a list of local VM identifiers: 221 // 30673 222 isQuiet = true; 223 JpsHelper.verifyJpsOutput(output, "^\\d+$"); 224 output.shouldContain(Long.toString(pid)); 225 break; 226 case l: 227 // If '-l' is specified output should contain the full package name for the application's main class 228 // or the full path name to the application's JAR file: 229 // 30673 /tmp/jtreg/jtreg-workdir/scratch/LingeredAppForJps.jar ... 230 isFull = true; 231 pattern = "^" + pid + "\\s+" + replaceSpecialChars(fullProcessName) + ".*"; 232 output.shouldMatch(pattern); 233 break; 234 case m: 235 // If '-m' is specified output should contain the arguments passed to the main method: 236 // 30673 LingeredAppForJps lockfilename ... 237 pattern = "^" + pid + ".*" + replaceSpecialChars(argument) + ".*"; 238 output.shouldMatch(pattern); 239 break; 240 case v: 241 // If '-v' is specified output should contain VM arguments: 242 // 30673 LingeredAppForJps -Xmx512m -XX:+UseParallelGC -XX:Flags=/tmp/jtreg/jtreg-workdir/scratch/vmflags ... 243 for (String vmArg : JpsHelper.getVmArgs()) { 244 pattern = "^" + pid + ".*" + replaceSpecialChars(vmArg) + ".*"; 245 output.shouldMatch(pattern); 246 } 247 break; 248 case V: 249 // If '-V' is specified output should contain VM flags: 250 // 30673 LingeredAppForJps +DisableExplicitGC ... 251 pattern = "^" + pid + ".*" + replaceSpecialChars(JpsHelper.VM_FLAG) + ".*"; 252 output.shouldMatch(pattern); 253 break; 254 } 255 256 if (isQuiet) { 257 break; 258 } 259 } 260 261 if (!isQuiet) { 262 // Verify output line by line. 263 // Output should only contain lines with pids after the first line with pid. 264 JpsHelper.verifyJpsOutput(output, "^\\d+\\s+.*"); 265 if (!isFull) { 266 pattern = "^" + pid + "\\s+" + replaceSpecialChars(processName); 267 if (combination.isEmpty()) { 268 // If no arguments are specified output should only contain 269 // pid and process name 270 pattern += "$"; 271 } else { 272 pattern += ".*"; 273 } 274 output.shouldMatch(pattern); 275 } 276 } 277 } 278 } 279 280 private static String replaceSpecialChars(String str) { 281 String tmp = str.replace("\\", "\\\\"); 282 tmp = tmp.replace("+", "\\+"); 283 tmp = tmp.replace(".", "\\."); 284 tmp = tmp.replace("\n", "\\\\n"); 285 tmp = tmp.replace("\r", "\\\\r"); 286 return tmp; 287 } 288 }