1 /* 2 * Copyright (c) 2013, 2019, 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 com.oracle.java.testlibrary; 25 26 import static com.oracle.java.testlibrary.Asserts.assertTrue; 27 28 import java.io.BufferedReader; 29 import java.io.File; 30 import java.io.FileReader; 31 import java.io.IOException; 32 import java.net.InetAddress; 33 import java.net.ServerSocket; 34 import java.net.UnknownHostException; 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Arrays; 38 import java.util.Collections; 39 import java.util.regex.Pattern; 40 import java.util.regex.Matcher; 41 import java.util.function.BooleanSupplier; 42 import java.util.function.Function; 43 import java.lang.reflect.Field; 44 import sun.misc.Unsafe; 45 46 /** 47 * Common library for various test helper functions. 48 */ 49 public final class Utils { 50 51 /** 52 * Returns the sequence used by operating system to separate lines. 53 */ 54 public static final String NEW_LINE = System.getProperty("line.separator"); 55 56 /** 57 * Returns the value of 'test.vm.opts'system property. 58 */ 59 public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim(); 60 61 /** 62 * Returns the value of 'test.java.opts'system property. 63 */ 64 public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim(); 65 66 public static final String TEST_JDK = System.getProperty("test.jdk"); 67 68 public static final String COMPILE_JDK= System.getProperty("compile.jdk", TEST_JDK); 69 70 public static final String TEST_SRC = System.getProperty("test.src", "").trim(); 71 72 public static final String TEST_CLASSES = System.getProperty("test.classes", "."); 73 74 75 private static Unsafe unsafe = null; 76 77 /** 78 * Returns the value of 'test.timeout.factor' system property 79 * converted to {@code double}. 80 */ 81 public static final double TIMEOUT_FACTOR; 82 static { 83 String toFactor = System.getProperty("test.timeout.factor", "1.0"); 84 TIMEOUT_FACTOR = Double.parseDouble(toFactor); 85 } 86 87 private Utils() { 88 // Private constructor to prevent class instantiation 89 } 90 91 /** 92 * Returns the list of VM options. 93 * 94 * @return List of VM options 95 */ 96 public static List<String> getVmOptions() { 97 return Arrays.asList(safeSplitString(VM_OPTIONS)); 98 } 99 100 /** 101 * Returns the list of VM options with -J prefix. 102 * 103 * @return The list of VM options with -J prefix 104 */ 105 public static List<String> getForwardVmOptions() { 106 String[] opts = safeSplitString(VM_OPTIONS); 107 for (int i = 0; i < opts.length; i++) { 108 opts[i] = "-J" + opts[i]; 109 } 110 return Arrays.asList(opts); 111 } 112 113 /** 114 * Returns the default JTReg arguments for a jvm running a test. 115 * This is the combination of JTReg arguments test.vm.opts and test.java.opts. 116 * @return An array of options, or an empty array if no opptions. 117 */ 118 public static String[] getTestJavaOpts() { 119 List<String> opts = new ArrayList<String>(); 120 Collections.addAll(opts, safeSplitString(VM_OPTIONS)); 121 Collections.addAll(opts, safeSplitString(JAVA_OPTIONS)); 122 return opts.toArray(new String[0]); 123 } 124 125 /** 126 * Returns the default JTReg arguments for a jvm running a test without 127 * options that matches regular expressions in {@code filters}. 128 * This is the combination of JTReg arguments test.vm.opts and test.java.opts. 129 * @param filters Regular expressions used to filter out options. 130 * @return An array of options, or an empty array if no options. 131 */ 132 public static String[] getFilteredTestJavaOpts(String... filters) { 133 String options[] = getTestJavaOpts(); 134 135 if (filters.length == 0) { 136 return options; 137 } 138 139 List<String> filteredOptions = new ArrayList<String>(options.length); 140 Pattern patterns[] = new Pattern[filters.length]; 141 for (int i = 0; i < filters.length; i++) { 142 patterns[i] = Pattern.compile(filters[i]); 143 } 144 145 for (String option : options) { 146 boolean matched = false; 147 for (int i = 0; i < patterns.length && !matched; i++) { 148 Matcher matcher = patterns[i].matcher(option); 149 matched = matcher.find(); 150 } 151 if (!matched) { 152 filteredOptions.add(option); 153 } 154 } 155 156 return filteredOptions.toArray(new String[filteredOptions.size()]); 157 } 158 159 /** 160 * Combines given arguments with default JTReg arguments for a jvm running a test. 161 * This is the combination of JTReg arguments test.vm.opts and test.java.opts 162 * @return The combination of JTReg test java options and user args. 163 */ 164 public static String[] addTestJavaOpts(String... userArgs) { 165 List<String> opts = new ArrayList<String>(); 166 Collections.addAll(opts, getTestJavaOpts()); 167 Collections.addAll(opts, userArgs); 168 return opts.toArray(new String[0]); 169 } 170 171 /** 172 * Splits a string by white space. 173 * Works like String.split(), but returns an empty array 174 * if the string is null or empty. 175 */ 176 private static String[] safeSplitString(String s) { 177 if (s == null || s.trim().isEmpty()) { 178 return new String[] {}; 179 } 180 return s.trim().split("\\s+"); 181 } 182 183 /** 184 * @return The full command line for the ProcessBuilder. 185 */ 186 public static String getCommandLine(ProcessBuilder pb) { 187 StringBuilder cmd = new StringBuilder(); 188 for (String s : pb.command()) { 189 cmd.append(s).append(" "); 190 } 191 return cmd.toString(); 192 } 193 194 /** 195 * Returns the free port on the local host. 196 * The function will spin until a valid port number is found. 197 * 198 * @return The port number 199 * @throws InterruptedException if any thread has interrupted the current thread 200 * @throws IOException if an I/O error occurs when opening the socket 201 */ 202 public static int getFreePort() throws InterruptedException, IOException { 203 int port = -1; 204 205 while (port <= 0) { 206 Thread.sleep(100); 207 208 ServerSocket serverSocket = null; 209 try { 210 serverSocket = new ServerSocket(0); 211 port = serverSocket.getLocalPort(); 212 } finally { 213 serverSocket.close(); 214 } 215 } 216 217 return port; 218 } 219 220 /** 221 * Returns the name of the local host. 222 * 223 * @return The host name 224 * @throws UnknownHostException if IP address of a host could not be determined 225 */ 226 public static String getHostname() throws UnknownHostException { 227 InetAddress inetAddress = InetAddress.getLocalHost(); 228 String hostName = inetAddress.getHostName(); 229 230 assertTrue((hostName != null && !hostName.isEmpty()), 231 "Cannot get hostname"); 232 233 return hostName; 234 } 235 236 /** 237 * Uses "jcmd -l" to search for a jvm pid. This function will wait 238 * forever (until jtreg timeout) for the pid to be found. 239 * @param key Regular expression to search for 240 * @return The found pid. 241 */ 242 public static int waitForJvmPid(String key) throws Throwable { 243 final long iterationSleepMillis = 250; 244 System.out.println("waitForJvmPid: Waiting for key '" + key + "'"); 245 System.out.flush(); 246 while (true) { 247 int pid = tryFindJvmPid(key); 248 if (pid >= 0) { 249 return pid; 250 } 251 Thread.sleep(iterationSleepMillis); 252 } 253 } 254 255 /** 256 * Searches for a jvm pid in the output from "jcmd -l". 257 * 258 * Example output from jcmd is: 259 * 12498 sun.tools.jcmd.JCmd -l 260 * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar 261 * 262 * @param key A regular expression to search for. 263 * @return The found pid, or -1 if Enot found. 264 * @throws Exception If multiple matching jvms are found. 265 */ 266 public static int tryFindJvmPid(String key) throws Throwable { 267 OutputAnalyzer output = null; 268 try { 269 JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd"); 270 jcmdLauncher.addToolArg("-l"); 271 output = ProcessTools.executeProcess(jcmdLauncher.getCommand()); 272 output.shouldHaveExitValue(0); 273 274 // Search for a line starting with numbers (pid), followed by the key. 275 Pattern pattern = Pattern.compile("^([0-9]+)\\s.*(" + key + ")", Pattern.MULTILINE); 276 Matcher matcher = pattern.matcher(output.getStdout()); 277 278 int pid = -1; 279 if (matcher.find()) { 280 pid = Integer.parseInt(matcher.group(1)); 281 System.out.println("findJvmPid.pid: " + pid); 282 if (matcher.find()) { 283 throw new Exception("Found multiple JVM pids for key: " + key); 284 } 285 } 286 return pid; 287 } catch (Throwable t) { 288 System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t)); 289 throw t; 290 } 291 } 292 293 /** 294 * Returns file content as a list of strings 295 * 296 * @param file File to operate on 297 * @return List of strings 298 * @throws IOException 299 */ 300 public static List<String> fileAsList(File file) throws IOException { 301 assertTrue(file.exists() && file.isFile(), 302 file.getAbsolutePath() + " does not exist or not a file"); 303 List<String> output = new ArrayList<>(); 304 try (BufferedReader reader = new BufferedReader(new FileReader(file.getAbsolutePath()))) { 305 while (reader.ready()) { 306 output.add(reader.readLine().replace(NEW_LINE, "")); 307 } 308 } 309 return output; 310 } 311 312 /** 313 * Return the contents of the named file as a single String, 314 * or null if not found. 315 * @param filename name of the file to read 316 * @return String contents of file, or null if file not found. 317 */ 318 public static String fileAsString(String filename) { 319 StringBuilder result = new StringBuilder(); 320 try { 321 File file = new File(filename); 322 if (file.exists()) { 323 BufferedReader reader = new BufferedReader(new FileReader(file)); 324 while (true) { 325 String line = reader.readLine(); 326 if (line == null) { 327 break; 328 } 329 result.append(line).append("\n"); 330 } 331 } else { 332 // Does not exist: 333 return null; 334 } 335 } catch (Exception e) { 336 e.printStackTrace(); 337 } 338 return result.toString(); 339 } 340 341 /** 342 * @return Unsafe instance. 343 */ 344 public static synchronized Unsafe getUnsafe() { 345 if (unsafe == null) { 346 try { 347 Field f = Unsafe.class.getDeclaredField("theUnsafe"); 348 f.setAccessible(true); 349 unsafe = (Unsafe) f.get(null); 350 } catch (NoSuchFieldException | IllegalAccessException e) { 351 throw new RuntimeException("Unable to get Unsafe instance.", e); 352 } 353 } 354 return unsafe; 355 } 356 private static final char[] hexArray = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 357 358 /** 359 * Returns hex view of byte array 360 * 361 * @param bytes byte array to process 362 * @return Space separated hexadecimal string representation of bytes 363 */ 364 365 public static String toHexString(byte[] bytes) { 366 char[] hexView = new char[bytes.length * 3]; 367 int i = 0; 368 for (byte b : bytes) { 369 hexView[i++] = hexArray[(b >> 4) & 0x0F]; 370 hexView[i++] = hexArray[b & 0x0F]; 371 hexView[i++] = ' '; 372 } 373 return new String(hexView); 374 } 375 376 /** 377 * Wait for condition to be true 378 * 379 * @param condition, a condition to wait for 380 */ 381 public static final void waitForCondition(BooleanSupplier condition) { 382 waitForCondition(condition, -1L, 100L); 383 } 384 385 /** 386 * Wait until timeout for condition to be true 387 * 388 * @param condition, a condition to wait for 389 * @param timeout a time in milliseconds to wait for condition to be true 390 * specifying -1 will wait forever 391 * @return condition value, to determine if wait was successful 392 */ 393 public static final boolean waitForCondition(BooleanSupplier condition, 394 long timeout) { 395 return waitForCondition(condition, timeout, 100L); 396 } 397 398 /** 399 * Wait until timeout for condition to be true for specified time 400 * 401 * @param condition, a condition to wait for 402 * @param timeout a time in milliseconds to wait for condition to be true, 403 * specifying -1 will wait forever 404 * @param sleepTime a time to sleep value in milliseconds 405 * @return condition value, to determine if wait was successful 406 */ 407 public static final boolean waitForCondition(BooleanSupplier condition, 408 long timeout, long sleepTime) { 409 long startTime = System.currentTimeMillis(); 410 while (!(condition.getAsBoolean() || (timeout != -1L 411 && ((System.currentTimeMillis() - startTime) > timeout)))) { 412 try { 413 Thread.sleep(sleepTime); 414 } catch (InterruptedException e) { 415 Thread.currentThread().interrupt(); 416 throw new Error(e); 417 } 418 } 419 return condition.getAsBoolean(); 420 } 421 }