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 } 271 272 /** 273 * @return "true" if VM has a serviceability agent. 274 */ 275 protected String vmHasSA() { 276 return "" + Platform.hasSA(); 277 } 278 279 /** 280 * @return "true" if VM has a serviceability agent and it can 281 * attach to the VM. 282 */ 283 protected String vmHasSAandCanAttach() { 284 try { 285 return "" + Platform.shouldSAAttach(); 286 } catch (IOException e) { 287 System.out.println("Checking whether SA can attach to the VM failed."); 288 e.printStackTrace(); 289 // Run the tests anyways. 290 return "true"; 291 } 292 } 293 294 /** 295 * @return "true" if the VM is compiled with Java Flight Recorder (JFR) 296 * support. 297 */ 298 protected String vmHasJFR() { 299 return "" + WB.isJFRIncludedInVmBuild(); 300 } 301 302 /** 303 * @return true if compiler in use supports RTM and false otherwise. 304 */ 305 protected String vmRTMCompiler() { 306 boolean isRTMCompiler = false; 307 308 if (Compiler.isC2Enabled() && 309 (Platform.isX86() || Platform.isX64() || Platform.isPPC())) { 310 isRTMCompiler = true; 311 } 312 return "" + isRTMCompiler; 313 } 314 315 /** 316 * @return true if VM runs RTM supported CPU and false otherwise. 317 */ 318 protected String vmRTMCPU() { 319 return "" + CPUInfo.hasFeature("rtm"); 320 } 321 322 /** 323 * @return true if VM supports AOT and false otherwise 324 */ 325 protected String vmAOT() { 326 // builds with aot have jaotc in <JDK>/bin 327 Path bin = Paths.get(System.getProperty("java.home")) 328 .resolve("bin"); 329 Path jaotc; 330 if (Platform.isWindows()) { 331 jaotc = bin.resolve("jaotc.exe"); 332 } else { 333 jaotc = bin.resolve("jaotc"); 334 } 335 return "" + Files.exists(jaotc); 336 } 337 338 /* 339 * @return true if there is at least one loaded AOT'ed library. 340 */ 341 protected String vmAotEnabled() { 342 return "" + (WB.aotLibrariesCount() > 0); 343 } 344 345 /** 346 * Check for CDS support. 347 * 348 * @return true if CDS is supported by the VM to be tested. 349 */ 350 protected String vmCDS() { 351 if (WB.isCDSIncludedInVmBuild()) { 352 return "true"; 353 } else { 354 return "false"; 355 } 356 } 357 358 /** 359 * Check for CDS support for custom loaders. 360 * 361 * @return true if CDS provides support for customer loader in the VM to be tested. 362 */ 363 protected String vmCDSForCustomLoaders() { 364 if (vmCDS().equals("true") && Platform.areCustomLoadersSupportedForCDS()) { 365 return "true"; 366 } else { 367 return "false"; 368 } 369 } 370 371 /** 372 * Check for CDS support for archived Java heap regions. 373 * 374 * @return true if CDS provides support for archive Java heap regions in the VM to be tested. 375 */ 376 protected String vmCDSForArchivedJavaHeap() { 377 if (vmCDS().equals("true") && WB.isJavaHeapArchiveSupported()) { 378 return "true"; 379 } else { 380 return "false"; 381 } 382 } 383 384 /** 385 * Check if Graal is used as JIT compiler. 386 * 387 * @return true if Graal is used as JIT compiler. 388 */ 389 protected String isGraalEnabled() { 390 return Compiler.isGraalEnabled() ? "true" : "false"; 391 } 392 393 /** 394 * Check if Compiler1 is present. 395 * 396 * @return true if Compiler1 is used as JIT compiler, either alone or as part of the tiered system. 397 */ 398 protected String isCompiler1Enabled() { 399 return Compiler.isC1Enabled() ? "true" : "false"; 400 } 401 402 /** 403 * Check if Compiler2 is present. 404 * 405 * @return true if Compiler2 is used as JIT compiler, either alone or as part of the tiered system. 406 */ 407 protected String isCompiler2Enabled() { 408 return Compiler.isC2Enabled() ? "true" : "false"; 409 } 410 411 /** 412 * A simple check for docker support 413 * 414 * @return true if docker is supported in a given environment 415 */ 416 protected String dockerSupport() { 417 boolean isSupported = false; 418 if (Platform.isLinux()) { 419 // currently docker testing is only supported for Linux, 420 // on certain platforms 421 422 String arch = System.getProperty("os.arch"); 423 424 if (Platform.isX64()) { 425 isSupported = true; 426 } 427 else if (Platform.isAArch64()) { 428 isSupported = true; 429 } 430 else if (Platform.isS390x()) { 431 isSupported = true; 432 } 433 else if (arch.equals("ppc64le")) { 434 isSupported = true; 435 } 436 } 437 438 if (isSupported) { 439 try { 440 isSupported = checkDockerSupport(); 441 } catch (Exception e) { 442 isSupported = false; 443 } 444 } 445 446 return (isSupported) ? "true" : "false"; 447 } 448 449 private boolean checkDockerSupport() throws IOException, InterruptedException { 450 ProcessBuilder pb = new ProcessBuilder("docker", "ps"); 451 Process p = pb.start(); 452 p.waitFor(10, TimeUnit.SECONDS); 453 454 return (p.exitValue() == 0); 455 } 456 457 458 private String implementor() { 459 try (InputStream in = new BufferedInputStream(new FileInputStream( 460 System.getProperty("java.home") + "/release"))) { 461 Properties properties = new Properties(); 462 properties.load(in); 463 String implementorProperty = properties.getProperty("IMPLEMENTOR"); 464 return (implementorProperty == null) ? "null" : implementorProperty.replace("\"", ""); 465 } catch (IOException e) { 466 e.printStackTrace(); 467 } 468 return null; 469 } 470 471 472 /** 473 * Dumps the map to the file if the file name is given as the property. 474 * This functionality could be helpful to know context in the real 475 * execution. 476 * 477 * @param map 478 */ 479 protected static void dump(Map<String, String> map) { 480 String dumpFileName = System.getProperty("vmprops.dump"); 481 if (dumpFileName == null) { 482 return; 483 } 484 List<String> lines = new ArrayList<>(); 485 map.forEach((k, v) -> lines.add(k + ":" + v)); 486 try { 487 Files.write(Paths.get(dumpFileName), lines, StandardOpenOption.APPEND); 488 } catch (IOException e) { 489 throw new RuntimeException("Failed to dump properties into '" 490 + dumpFileName + "'", e); 491 } 492 } 493 494 /** 495 * This method is for the testing purpose only. 496 * @param args 497 */ 498 public static void main(String args[]) { 499 Map<String, String> map = new VMProps().call(); 500 map.forEach((k, v) -> System.out.println(k + ": '" + v + "'")); 501 } 502 }