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