1 /* 2 * Copyright (c) 2014, 2018, 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.test.lib.Asserts.assertGreaterThan; 25 import static jdk.test.lib.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.JDKToolLauncher; 39 import jdk.test.lib.process.OutputAnalyzer; 40 import jdk.test.lib.process.ProcessTools; 41 import jdk.test.lib.Asserts; 42 import jdk.test.lib.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) { 188 output.shouldHaveExitValue(0); 189 output.stdoutShouldMatchByLine(regex); 190 output.stderrShouldNotMatch("[E|e]xception"); 191 output.stderrShouldNotMatch("[E|e]rror"); 192 } 193 194 /** 195 * Compare jps output with a content in a file line by line 196 */ 197 public static void verifyOutputAgainstFile(OutputAnalyzer output) throws IOException { 198 String testSrc = System.getProperty("test.src", "?"); 199 Path path = Paths.get(testSrc, "usage.out"); 200 List<String> fileOutput = Files.readAllLines(path); 201 List<String> outputAsLines = output.asLines(); 202 assertTrue(outputAsLines.containsAll(fileOutput), 203 "The ouput should contain all content of " + path.toAbsolutePath()); 204 } 205 206 public static void runJpsVariants(Long pid, String processName, String fullProcessName, String argument) throws Exception { 207 System.out.printf("INFO: user.dir: '%s''\n", System.getProperty("user.dir")); 208 List<List<JpsHelper.JpsArg>> combinations = JpsHelper.JpsArg.generateCombinations(); 209 for (List<JpsHelper.JpsArg> combination : combinations) { 210 OutputAnalyzer output = JpsHelper.jps(JpsHelper.JpsArg.asCmdArray(combination)); 211 output.shouldHaveExitValue(0); 212 213 boolean isQuiet = false; 214 boolean isFull = false; 215 String pattern; 216 for (JpsHelper.JpsArg jpsArg : combination) { 217 switch (jpsArg) { 218 case q: 219 // If '-q' is specified output should contain only a list of local VM identifiers: 220 // 30673 221 isQuiet = true; 222 JpsHelper.verifyJpsOutput(output, "^\\d+$"); 223 output.shouldContain(Long.toString(pid)); 224 break; 225 case l: 226 // If '-l' is specified output should contain the full package name for the application's main class 227 // or the full path name to the application's JAR file: 228 // 30673 /tmp/jtreg/jtreg-workdir/scratch/LingeredAppForJps.jar ... 229 isFull = true; 230 pattern = "^" + pid + "\\s+" + replaceSpecialChars(fullProcessName) + ".*"; 231 output.shouldMatch(pattern); 232 break; 233 case m: 234 // If '-m' is specified output should contain the arguments passed to the main method: 235 // 30673 LingeredAppForJps lockfilename ... 236 pattern = "^" + pid + ".*" + replaceSpecialChars(argument) + ".*"; 237 output.shouldMatch(pattern); 238 break; 239 case v: 240 // If '-v' is specified output should contain VM arguments: 241 // 30673 LingeredAppForJps -Xmx512m -XX:+UseParallelGC -XX:Flags=/tmp/jtreg/jtreg-workdir/scratch/vmflags ... 242 for (String vmArg : JpsHelper.getVmArgs()) { 243 pattern = "^" + pid + ".*" + replaceSpecialChars(vmArg) + ".*"; 244 output.shouldMatch(pattern); 245 } 246 break; 247 case V: 248 // If '-V' is specified output should contain VM flags: 249 // 30673 LingeredAppForJps +DisableExplicitGC ... 250 pattern = "^" + pid + ".*" + replaceSpecialChars(JpsHelper.VM_FLAG) + ".*"; 251 output.shouldMatch(pattern); 252 break; 253 } 254 255 if (isQuiet) { 256 break; 257 } 258 } 259 260 if (!isQuiet) { 261 // Verify output line by line. 262 // Output should only contain lines with pids after the first line with pid. 263 JpsHelper.verifyJpsOutput(output, "^\\d+\\s+.*"); 264 if (!isFull) { 265 pattern = "^" + pid + "\\s+" + replaceSpecialChars(processName); 266 if (combination.isEmpty()) { 267 // If no arguments are specified output should only contain 268 // pid and process name 269 pattern += "$"; 270 } else { 271 pattern += ".*"; 272 } 273 output.shouldMatch(pattern); 274 } 275 } 276 } 277 } 278 279 private static String replaceSpecialChars(String str) { 280 String tmp = str.replace("\\", "\\\\"); 281 tmp = tmp.replace("+", "\\+"); 282 tmp = tmp.replace(".", "\\."); 283 tmp = tmp.replace("\n", "\\\\n"); 284 tmp = tmp.replace("\r", "\\\\r"); 285 return tmp; 286 } 287 }