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