1 /* 2 * Copyright (c) 2016, 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 package requires; 24 25 import java.io.BufferedInputStream; 26 import java.io.FileInputStream; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.nio.file.Files; 30 import java.nio.file.Path; 31 import java.nio.file.Paths; 32 import java.nio.file.StandardOpenOption; 33 import java.util.ArrayList; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Properties; 38 import java.util.concurrent.Callable; 39 import java.util.concurrent.TimeUnit; 40 import java.util.regex.Matcher; 41 import java.util.regex.Pattern; 42 43 import sun.hotspot.cpuinfo.CPUInfo; 44 import sun.hotspot.gc.GC; 45 import sun.hotspot.WhiteBox; 46 import jdk.test.lib.Platform; 47 48 /** 49 * The Class to be invoked by jtreg prior Test Suite execution to 50 * collect information about VM. 51 * Do not use any API's that may not be available in all target VMs. 52 * Properties set by this Class will be available in the @requires expressions. 53 */ 54 public class VMProps implements Callable<Map<String, String>> { 55 56 private static final WhiteBox WB = WhiteBox.getWhiteBox(); 57 58 /** 59 * Collects information about VM properties. 60 * This method will be invoked by jtreg. 61 * 62 * @return Map of property-value pairs. 63 */ 64 @Override 65 public Map<String, String> call() { 66 Map<String, String> map = new HashMap<>(); 67 map.put("vm.flavor", vmFlavor()); 68 map.put("vm.compMode", vmCompMode()); 69 map.put("vm.bits", vmBits()); 70 map.put("vm.flightRecorder", vmFlightRecorder()); 71 map.put("vm.simpleArch", vmArch()); 72 map.put("vm.debug", vmDebug()); 73 map.put("vm.jvmci", vmJvmci()); 74 map.put("vm.emulatedClient", vmEmulatedClient()); 75 map.put("vm.cpu.features", cpuFeatures()); 76 map.put("vm.rtm.cpu", vmRTMCPU()); 77 map.put("vm.rtm.os", vmRTMOS()); 78 map.put("vm.aot", vmAOT()); 79 // vm.cds is true if the VM is compiled with cds support. 80 map.put("vm.cds", vmCDS()); 81 map.put("vm.cds.custom.loaders", vmCDSForCustomLoaders()); 82 map.put("vm.cds.archived.java.heap", vmCDSForArchivedJavaHeap()); 83 // vm.graal.enabled is true if Graal is used as JIT 84 map.put("vm.graal.enabled", isGraalEnabled()); 85 map.put("docker.support", dockerSupport()); 86 map.put("release.implementor", implementor()); 87 vmGC(map); // vm.gc.X = true/false 88 vmOptFinalFlags(map); 89 90 VMProps.dump(map); 91 return map; 92 } 93 94 /** 95 * Prints a stack trace before returning null. 96 * Used by the various helper functions which parse information from 97 * VM properties in the case where they don't find an expected property 98 * or a propoerty doesn't conform to an expected format. 99 * 100 * @return null 101 */ 102 private String nullWithException(String message) { 103 new Exception(message).printStackTrace(); 104 return null; 105 } 106 107 /** 108 * @return vm.simpleArch value of "os.simpleArch" property of tested JDK. 109 */ 110 protected String vmArch() { 111 String arch = System.getProperty("os.arch"); 112 if (arch.equals("x86_64") || arch.equals("amd64")) { 113 return "x64"; 114 } 115 else if (arch.contains("86")) { 116 return "x86"; 117 } else { 118 return arch; 119 } 120 } 121 122 123 124 /** 125 * @return VM type value extracted from the "java.vm.name" property. 126 */ 127 protected String vmFlavor() { 128 // E.g. "Java HotSpot(TM) 64-Bit Server VM" 129 String vmName = System.getProperty("java.vm.name"); 130 if (vmName == null) { 131 return nullWithException("Can't get 'java.vm.name' property"); 132 } 133 134 Pattern startP = Pattern.compile(".* (\\S+) VM"); 135 Matcher m = startP.matcher(vmName); 136 if (m.matches()) { 137 return m.group(1).toLowerCase(); 138 } 139 return nullWithException("Can't get VM flavor from 'java.vm.name'"); 140 } 141 142 /** 143 * @return VM compilation mode extracted from the "java.vm.info" property. 144 */ 145 protected String vmCompMode() { 146 // E.g. "mixed mode" 147 String vmInfo = System.getProperty("java.vm.info"); 148 if (vmInfo == null) { 149 return nullWithException("Can't get 'java.vm.info' property"); 150 } 151 if (vmInfo.toLowerCase().indexOf("mixed mode") != -1) { 152 return "Xmixed"; 153 } else if (vmInfo.toLowerCase().indexOf("compiled mode") != -1) { 154 return "Xcomp"; 155 } else if (vmInfo.toLowerCase().indexOf("interpreted mode") != -1) { 156 return "Xint"; 157 } else { 158 return nullWithException("Can't get compilation mode from 'java.vm.info'"); 159 } 160 } 161 162 /** 163 * @return VM bitness, the value of the "sun.arch.data.model" property. 164 */ 165 protected String vmBits() { 166 String dataModel = System.getProperty("sun.arch.data.model"); 167 if (dataModel != null) { 168 return dataModel; 169 } else { 170 return nullWithException("Can't get 'sun.arch.data.model' property"); 171 } 172 } 173 174 /** 175 * @return "true" if Flight Recorder is enabled, "false" if is disabled. 176 */ 177 protected String vmFlightRecorder() { 178 Boolean isUnlockedCommercialFatures = WB.getBooleanVMFlag("UnlockCommercialFeatures"); 179 Boolean isFlightRecorder = WB.getBooleanVMFlag("FlightRecorder"); 180 String startFROptions = WB.getStringVMFlag("StartFlightRecording"); 181 if (isUnlockedCommercialFatures != null && isUnlockedCommercialFatures) { 182 if (isFlightRecorder != null && isFlightRecorder) { 183 return "true"; 184 } 185 if (startFROptions != null && !startFROptions.isEmpty()) { 186 return "true"; 187 } 188 } 189 return "false"; 190 } 191 192 /** 193 * @return debug level value extracted from the "jdk.debug" property. 194 */ 195 protected String vmDebug() { 196 String debug = System.getProperty("jdk.debug"); 197 if (debug != null) { 198 return "" + debug.contains("debug"); 199 } else { 200 return nullWithException("Can't get 'jdk.debug' property"); 201 } 202 } 203 204 /** 205 * @return true if VM supports JVMCI and false otherwise 206 */ 207 protected String vmJvmci() { 208 // builds with jvmci have this flag 209 return "" + (WB.getBooleanVMFlag("EnableJVMCI") != null); 210 } 211 212 /** 213 * @return true if VM runs in emulated-client mode and false otherwise. 214 */ 215 protected String vmEmulatedClient() { 216 String vmInfo = System.getProperty("java.vm.info"); 217 if (vmInfo == null) { 218 return "false"; 219 } 220 return "" + vmInfo.contains(" emulated-client"); 221 } 222 223 /** 224 * @return supported CPU features 225 */ 226 protected String cpuFeatures() { 227 return CPUInfo.getFeatures().toString(); 228 } 229 230 /** 231 * For all existing GC sets vm.gc.X property. 232 * Example vm.gc.G1=true means: 233 * VM supports G1 234 * User either set G1 explicitely (-XX:+UseG1GC) or did not set any GC 235 * @param map - property-value pairs 236 */ 237 protected void vmGC(Map<String, String> map) { 238 for (GC gc: GC.values()) { 239 boolean isAcceptable = gc.isSupported() && (gc.isSelected() || GC.isSelectedErgonomically()); 240 map.put("vm.gc." + gc.name(), "" + isAcceptable); 241 } 242 } 243 244 /** 245 * Selected final flag. 246 * @param map - property-value pairs 247 * @param flagName - flag name 248 */ 249 private void vmOptFinalFlag(Map<String, String> map, String flagName) { 250 String value = WB.getBooleanVMFlag(flagName) ? "true" : "false"; 251 map.put("vm.opt.final." + flagName, value); 252 } 253 254 /** 255 * Selected sets of final flags. 256 * @param map -property-value pairs 257 */ 258 protected void vmOptFinalFlags(Map<String, String> map) { 259 vmOptFinalFlag(map, "ClassUnloading"); 260 vmOptFinalFlag(map, "UseCompressedOops"); 261 } 262 263 /** 264 * @return true if VM runs RTM supported OS and false otherwise. 265 */ 266 protected String vmRTMOS() { 267 boolean isRTMOS = true; 268 269 if (Platform.isAix()) { 270 // Actually, this works since AIX 7.1.3.30, but os.version property 271 // is set to 7.1. 272 isRTMOS = (Platform.getOsVersionMajor() > 7) || 273 (Platform.getOsVersionMajor() == 7 && Platform.getOsVersionMinor() > 1); 274 275 } else if (Platform.isLinux()) { 276 if (Platform.isPPC()) { 277 isRTMOS = (Platform.getOsVersionMajor() > 4) || 278 (Platform.getOsVersionMajor() == 4 && Platform.getOsVersionMinor() > 1); 279 } 280 } 281 return "" + isRTMOS; 282 } 283 284 /** 285 * @return true if VM runs RTM supported CPU and false otherwise. 286 */ 287 protected String vmRTMCPU() { 288 return "" + CPUInfo.hasFeature("rtm"); 289 } 290 291 /** 292 * @return true if VM supports AOT and false otherwise 293 */ 294 protected String vmAOT() { 295 // builds with aot have jaotc in <JDK>/bin 296 Path bin = Paths.get(System.getProperty("java.home")) 297 .resolve("bin"); 298 Path jaotc; 299 if (Platform.isWindows()) { 300 jaotc = bin.resolve("jaotc.exe"); 301 } else { 302 jaotc = bin.resolve("jaotc"); 303 } 304 return "" + Files.exists(jaotc); 305 } 306 307 /** 308 * Check for CDS support. 309 * 310 * @return true if CDS is supported by the VM to be tested. 311 */ 312 protected String vmCDS() { 313 if (WB.isCDSIncludedInVmBuild()) { 314 return "true"; 315 } else { 316 return "false"; 317 } 318 } 319 320 /** 321 * Check for CDS support for custom loaders. 322 * 323 * @return true if CDS provides support for customer loader in the VM to be tested. 324 */ 325 protected String vmCDSForCustomLoaders() { 326 if (vmCDS().equals("true") && Platform.areCustomLoadersSupportedForCDS()) { 327 return "true"; 328 } else { 329 return "false"; 330 } 331 } 332 333 /** 334 * Check for CDS support for archived Java heap regions. 335 * 336 * @return true if CDS provides support for archive Java heap regions in the VM to be tested. 337 */ 338 protected String vmCDSForArchivedJavaHeap() { 339 if (vmCDS().equals("true") && WB.isJavaHeapArchiveSupported()) { 340 return "true"; 341 } else { 342 return "false"; 343 } 344 } 345 346 /** 347 * Check if Graal is used as JIT compiler. 348 * 349 * @return true if Graal is used as JIT compiler. 350 */ 351 protected String isGraalEnabled() { 352 // Graal is enabled if following conditions are true: 353 // - we are not in Interpreter mode 354 // - UseJVMCICompiler flag is true 355 // - jvmci.Compiler variable is equal to 'graal' 356 // - TieredCompilation is not used or TieredStopAtLevel is greater than 3 357 358 Boolean useCompiler = WB.getBooleanVMFlag("UseCompiler"); 359 if (useCompiler == null || !useCompiler) 360 return "false"; 361 362 Boolean useJvmciComp = WB.getBooleanVMFlag("UseJVMCICompiler"); 363 if (useJvmciComp == null || !useJvmciComp) 364 return "false"; 365 366 // This check might be redundant but let's keep it for now. 367 String jvmciCompiler = System.getProperty("jvmci.Compiler"); 368 if (jvmciCompiler == null || !jvmciCompiler.equals("graal")) { 369 return "false"; 370 } 371 372 Boolean tieredCompilation = WB.getBooleanVMFlag("TieredCompilation"); 373 Long compLevel = WB.getIntxVMFlag("TieredStopAtLevel"); 374 // if TieredCompilation is enabled and compilation level is <= 3 then no Graal is used 375 if (tieredCompilation != null && tieredCompilation && compLevel != null && compLevel <= 3) 376 return "false"; 377 378 return "true"; 379 } 380 381 382 /** 383 * A simple check for docker support 384 * 385 * @return true if docker is supported in a given environment 386 */ 387 protected String dockerSupport() { 388 boolean isSupported = false; 389 if (Platform.isLinux()) { 390 // currently docker testing is only supported for Linux, 391 // on certain platforms 392 393 String arch = System.getProperty("os.arch"); 394 395 if (Platform.isX64()) { 396 isSupported = true; 397 } 398 else if (Platform.isAArch64()) { 399 isSupported = true; 400 } 401 else if (Platform.isS390x()) { 402 isSupported = true; 403 } 404 else if (arch.equals("ppc64le")) { 405 isSupported = true; 406 } 407 } 408 409 if (isSupported) { 410 try { 411 isSupported = checkDockerSupport(); 412 } catch (Exception e) { 413 isSupported = false; 414 } 415 } 416 417 return (isSupported) ? "true" : "false"; 418 } 419 420 private boolean checkDockerSupport() throws IOException, InterruptedException { 421 ProcessBuilder pb = new ProcessBuilder("docker", "ps"); 422 Process p = pb.start(); 423 p.waitFor(10, TimeUnit.SECONDS); 424 425 return (p.exitValue() == 0); 426 } 427 428 429 private String implementor() { 430 try (InputStream in = new BufferedInputStream(new FileInputStream( 431 System.getProperty("java.home") + "/release"))) { 432 Properties properties = new Properties(); 433 properties.load(in); 434 return properties.getProperty("IMPLEMENTOR").replace("\"", ""); 435 } catch (IOException e) { 436 e.printStackTrace(); 437 } 438 return null; 439 } 440 441 442 /** 443 * Dumps the map to the file if the file name is given as the property. 444 * This functionality could be helpful to know context in the real 445 * execution. 446 * 447 * @param map 448 */ 449 protected static void dump(Map<String, String> map) { 450 String dumpFileName = System.getProperty("vmprops.dump"); 451 if (dumpFileName == null) { 452 return; 453 } 454 List<String> lines = new ArrayList<>(); 455 map.forEach((k, v) -> lines.add(k + ":" + v)); 456 try { 457 Files.write(Paths.get(dumpFileName), lines, StandardOpenOption.APPEND); 458 } catch (IOException e) { 459 throw new RuntimeException("Failed to dump properties into '" 460 + dumpFileName + "'", e); 461 } 462 } 463 464 /** 465 * This method is for the testing purpose only. 466 * @param args 467 */ 468 public static void main(String args[]) { 469 Map<String, String> map = new VMProps().call(); 470 map.forEach((k, v) -> System.out.println(k + ": '" + v + "'")); 471 } 472 }