Path: blob/master/test/jtreg-ext/requires/VMProps.java
40948 views
/*1* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package requires;2425import java.io.BufferedInputStream;26import java.io.FileInputStream;27import java.io.IOException;28import java.io.InputStream;29import java.nio.file.Files;30import java.nio.file.Path;31import java.nio.file.Paths;32import java.nio.file.StandardOpenOption;33import java.util.ArrayList;34import java.util.Collections;35import java.util.HashMap;36import java.util.List;37import java.util.Map;38import java.util.Properties;39import java.util.Set;40import java.util.concurrent.Callable;41import java.util.concurrent.TimeUnit;42import java.util.function.Supplier;43import java.util.regex.Matcher;44import java.util.regex.Pattern;4546import sun.hotspot.code.Compiler;47import sun.hotspot.cpuinfo.CPUInfo;48import sun.hotspot.gc.GC;49import sun.hotspot.WhiteBox;50import jdk.test.lib.Platform;51import jdk.test.lib.Container;5253/**54* The Class to be invoked by jtreg prior Test Suite execution to55* collect information about VM.56* Do not use any APIs that may not be available in all target VMs.57* Properties set by this Class will be available in the @requires expressions.58*/59public class VMProps implements Callable<Map<String, String>> {60// value known to jtreg as an indicator of error state61private static final String ERROR_STATE = "__ERROR__";6263private static final WhiteBox WB = WhiteBox.getWhiteBox();6465private static class SafeMap {66private final Map<String, String> map = new HashMap<>();6768public void put(String key, Supplier<String> s) {69String value;70try {71value = s.get();72} catch (Throwable t) {73System.err.println("failed to get value for " + key);74t.printStackTrace(System.err);75value = ERROR_STATE + t;76}77map.put(key, value);78}79}8081/**82* Collects information about VM properties.83* This method will be invoked by jtreg.84*85* @return Map of property-value pairs.86*/87@Override88public Map<String, String> call() {89SafeMap map = new SafeMap();90map.put("vm.flavor", this::vmFlavor);91map.put("vm.compMode", this::vmCompMode);92map.put("vm.bits", this::vmBits);93map.put("vm.flightRecorder", this::vmFlightRecorder);94map.put("vm.simpleArch", this::vmArch);95map.put("vm.debug", this::vmDebug);96map.put("vm.jvmci", this::vmJvmci);97map.put("vm.emulatedClient", this::vmEmulatedClient);98// vm.hasSA is "true" if the VM contains the serviceability agent99// and jhsdb.100map.put("vm.hasSA", this::vmHasSA);101// vm.hasJFR is "true" if JFR is included in the build of the VM and102// so tests can be executed.103map.put("vm.hasJFR", this::vmHasJFR);104map.put("vm.jvmti", this::vmHasJVMTI);105map.put("vm.cpu.features", this::cpuFeatures);106map.put("vm.pageSize", this::vmPageSize);107map.put("vm.rtm.cpu", this::vmRTMCPU);108map.put("vm.rtm.compiler", this::vmRTMCompiler);109// vm.cds is true if the VM is compiled with cds support.110map.put("vm.cds", this::vmCDS);111map.put("vm.cds.custom.loaders", this::vmCDSForCustomLoaders);112map.put("vm.cds.archived.java.heap", this::vmCDSForArchivedJavaHeap);113// vm.graal.enabled is true if Graal is used as JIT114map.put("vm.graal.enabled", this::isGraalEnabled);115map.put("vm.compiler1.enabled", this::isCompiler1Enabled);116map.put("vm.compiler2.enabled", this::isCompiler2Enabled);117map.put("docker.support", this::dockerSupport);118map.put("vm.musl", this::isMusl);119map.put("release.implementor", this::implementor);120map.put("jdk.containerized", this::jdkContainerized);121map.put("vm.flagless", this::isFlagless);122vmGC(map); // vm.gc.X = true/false123vmOptFinalFlags(map);124125dump(map.map);126return map.map;127}128129/**130* Print a stack trace before returning error state;131* Used by the various helper functions which parse information from132* VM properties in the case where they don't find an expected property133* or a property doesn't conform to an expected format.134*135* @return {@link #ERROR_STATE}136*/137private String errorWithMessage(String message) {138new Exception(message).printStackTrace();139return ERROR_STATE + message;140}141142/**143* @return vm.simpleArch value of "os.simpleArch" property of tested JDK.144*/145protected String vmArch() {146String arch = System.getProperty("os.arch");147if (arch.equals("x86_64") || arch.equals("amd64")) {148return "x64";149} else if (arch.contains("86")) {150return "x86";151} else {152return arch;153}154}155156/**157* @return VM type value extracted from the "java.vm.name" property.158*/159protected String vmFlavor() {160// E.g. "Java HotSpot(TM) 64-Bit Server VM"161String vmName = System.getProperty("java.vm.name");162if (vmName == null) {163return errorWithMessage("Can't get 'java.vm.name' property");164}165166Pattern startP = Pattern.compile(".* (\\S+) VM");167Matcher m = startP.matcher(vmName);168if (m.matches()) {169return m.group(1).toLowerCase();170}171return errorWithMessage("Can't get VM flavor from 'java.vm.name'");172}173174/**175* @return VM compilation mode extracted from the "java.vm.info" property.176*/177protected String vmCompMode() {178// E.g. "mixed mode"179String vmInfo = System.getProperty("java.vm.info");180if (vmInfo == null) {181return errorWithMessage("Can't get 'java.vm.info' property");182}183vmInfo = vmInfo.toLowerCase();184if (vmInfo.contains("mixed mode")) {185return "Xmixed";186} else if (vmInfo.contains("compiled mode")) {187return "Xcomp";188} else if (vmInfo.contains("interpreted mode")) {189return "Xint";190} else {191return errorWithMessage("Can't get compilation mode from 'java.vm.info'");192}193}194195/**196* @return VM bitness, the value of the "sun.arch.data.model" property.197*/198protected String vmBits() {199String dataModel = System.getProperty("sun.arch.data.model");200if (dataModel != null) {201return dataModel;202} else {203return errorWithMessage("Can't get 'sun.arch.data.model' property");204}205}206207/**208* @return "true" if Flight Recorder is enabled, "false" if is disabled.209*/210protected String vmFlightRecorder() {211Boolean isFlightRecorder = WB.getBooleanVMFlag("FlightRecorder");212String startFROptions = WB.getStringVMFlag("StartFlightRecording");213if (isFlightRecorder != null && isFlightRecorder) {214return "true";215}216if (startFROptions != null && !startFROptions.isEmpty()) {217return "true";218}219return "false";220}221222/**223* @return debug level value extracted from the "jdk.debug" property.224*/225protected String vmDebug() {226String debug = System.getProperty("jdk.debug");227if (debug != null) {228return "" + debug.contains("debug");229} else {230return errorWithMessage("Can't get 'jdk.debug' property");231}232}233234/**235* @return true if VM supports JVMCI and false otherwise236*/237protected String vmJvmci() {238// builds with jvmci have this flag239if (WB.getBooleanVMFlag("EnableJVMCI") == null) {240return "false";241}242243// Not all GCs have full JVMCI support244if (!WB.isJVMCISupportedByGC()) {245return "false";246}247248// Interpreted mode cannot enable JVMCI249if (vmCompMode().equals("Xint")) {250return "false";251}252253return "true";254}255256/**257* @return true if VM runs in emulated-client mode and false otherwise.258*/259protected String vmEmulatedClient() {260String vmInfo = System.getProperty("java.vm.info");261if (vmInfo == null) {262return errorWithMessage("Can't get 'java.vm.info' property");263}264return "" + vmInfo.contains(" emulated-client");265}266267/**268* @return supported CPU features269*/270protected String cpuFeatures() {271return CPUInfo.getFeatures().toString();272}273274/**275* For all existing GC sets vm.gc.X property.276* Example vm.gc.G1=true means:277* VM supports G1278* User either set G1 explicitely (-XX:+UseG1GC) or did not set any GC279* G1 can be selected, i.e. it doesn't conflict with other VM flags280*281* @param map - property-value pairs282*/283protected void vmGC(SafeMap map) {284var isJVMCIEnabled = Compiler.isJVMCIEnabled();285for (GC gc: GC.values()) {286map.put("vm.gc." + gc.name(),287() -> "" + (gc.isSupported()288&& (!isJVMCIEnabled || gc.isSupportedByJVMCICompiler())289&& (gc.isSelected() || GC.isSelectedErgonomically())));290}291}292293/**294* Selected final flag.295*296* @param map - property-value pairs297* @param flagName - flag name298*/299private void vmOptFinalFlag(SafeMap map, String flagName) {300map.put("vm.opt.final." + flagName,301() -> String.valueOf(WB.getBooleanVMFlag(flagName)));302}303304/**305* Selected sets of final flags.306*307* @param map - property-value pairs308*/309protected void vmOptFinalFlags(SafeMap map) {310vmOptFinalFlag(map, "ClassUnloading");311vmOptFinalFlag(map, "ClassUnloadingWithConcurrentMark");312vmOptFinalFlag(map, "UseCompressedOops");313vmOptFinalFlag(map, "EnableJVMCI");314vmOptFinalFlag(map, "EliminateAllocations");315vmOptFinalFlag(map, "UseVtableBasedCHA");316}317318/**319* @return "true" if VM has a serviceability agent.320*/321protected String vmHasSA() {322return "" + Platform.hasSA();323}324325/**326* @return "true" if the VM is compiled with Java Flight Recorder (JFR)327* support.328*/329protected String vmHasJFR() {330return "" + WB.isJFRIncluded();331}332333/**334* @return "true" if the VM is compiled with JVMTI335*/336protected String vmHasJVMTI() {337return "" + WB.isJVMTIIncluded();338}339340/**341* @return true if compiler in use supports RTM and false otherwise.342*/343protected String vmRTMCompiler() {344boolean isRTMCompiler = false;345346if (Compiler.isC2Enabled() &&347(Platform.isX86() || Platform.isX64() || Platform.isPPC())) {348isRTMCompiler = true;349}350return "" + isRTMCompiler;351}352353/**354* @return true if VM runs RTM supported CPU and false otherwise.355*/356protected String vmRTMCPU() {357return "" + CPUInfo.hasFeature("rtm");358}359360/**361* Check for CDS support.362*363* @return true if CDS is supported by the VM to be tested.364*/365protected String vmCDS() {366return "" + WB.isCDSIncluded();367}368369/**370* Check for CDS support for custom loaders.371*372* @return true if CDS provides support for customer loader in the VM to be tested.373*/374protected String vmCDSForCustomLoaders() {375return "" + ("true".equals(vmCDS()) && Platform.areCustomLoadersSupportedForCDS());376}377378/**379* Check for CDS support for archived Java heap regions.380*381* @return true if CDS provides support for archive Java heap regions in the VM to be tested.382*/383protected String vmCDSForArchivedJavaHeap() {384return "" + ("true".equals(vmCDS()) && WB.isJavaHeapArchiveSupported());385}386387/**388* @return System page size in bytes.389*/390protected String vmPageSize() {391return "" + WB.getVMPageSize();392}393394/**395* Check if Graal is used as JIT compiler.396*397* @return true if Graal is used as JIT compiler.398*/399protected String isGraalEnabled() {400return "" + Compiler.isGraalEnabled();401}402403/**404* Check if Compiler1 is present.405*406* @return true if Compiler1 is used as JIT compiler, either alone or as part of the tiered system.407*/408protected String isCompiler1Enabled() {409return "" + Compiler.isC1Enabled();410}411412/**413* Check if Compiler2 is present.414*415* @return true if Compiler2 is used as JIT compiler, either alone or as part of the tiered system.416*/417protected String isCompiler2Enabled() {418return "" + Compiler.isC2Enabled();419}420421/**422* A simple check for docker support423*424* @return true if docker is supported in a given environment425*/426protected String dockerSupport() {427boolean isSupported = false;428if (Platform.isLinux()) {429// currently docker testing is only supported for Linux,430// on certain platforms431432String arch = System.getProperty("os.arch");433434if (Platform.isX64()) {435isSupported = true;436} else if (Platform.isAArch64()) {437isSupported = true;438} else if (Platform.isS390x()) {439isSupported = true;440} else if (arch.equals("ppc64le")) {441isSupported = true;442}443}444445if (isSupported) {446try {447isSupported = checkDockerSupport();448} catch (Exception e) {449isSupported = false;450}451}452453return "" + isSupported;454}455456private boolean checkDockerSupport() throws IOException, InterruptedException {457ProcessBuilder pb = new ProcessBuilder(Container.ENGINE_COMMAND, "ps");458Process p = pb.start();459p.waitFor(10, TimeUnit.SECONDS);460461return (p.exitValue() == 0);462}463464/**465* Checks musl libc.466*467* @return true if musl libc is used.468*/469protected String isMusl() {470return Boolean.toString(WB.getLibcName().contains("musl"));471}472473private String implementor() {474try (InputStream in = new BufferedInputStream(new FileInputStream(475System.getProperty("java.home") + "/release"))) {476Properties properties = new Properties();477properties.load(in);478String implementorProperty = properties.getProperty("IMPLEMENTOR");479if (implementorProperty != null) {480return implementorProperty.replace("\"", "");481}482return errorWithMessage("Can't get 'IMPLEMENTOR' property from 'release' file");483} catch (IOException e) {484e.printStackTrace();485return errorWithMessage("Failed to read 'release' file " + e);486}487}488489private String jdkContainerized() {490String isEnabled = System.getenv("TEST_JDK_CONTAINERIZED");491return "" + "true".equalsIgnoreCase(isEnabled);492}493494/**495* Checks if we are in <i>almost</i> out-of-box configuration, i.e. the flags496* which JVM is started with don't affect its behavior "significantly".497* {@code TEST_VM_FLAGLESS} enviroment variable can be used to force this498* method to return true and allow any flags.499*500* @return true if there are no JVM flags501*/502private String isFlagless() {503boolean result = true;504if (System.getenv("TEST_VM_FLAGLESS") != null) {505return "" + result;506}507508List<String> allFlags = new ArrayList<String>();509Collections.addAll(allFlags, System.getProperty("test.vm.opts", "").trim().split("\\s+"));510Collections.addAll(allFlags, System.getProperty("test.java.opts", "").trim().split("\\s+"));511512// check -XX flags513var ignoredXXFlags = Set.of(514// added by run-test framework515"MaxRAMPercentage",516// added by test environment517"CreateCoredumpOnCrash"518);519result &= allFlags.stream()520.filter(s -> s.startsWith("-XX:"))521// map to names:522// remove -XX:523.map(s -> s.substring(4))524// remove +/- from bool flags525.map(s -> s.charAt(0) == '+' || s.charAt(0) == '-' ? s.substring(1) : s)526// remove =.* from others527.map(s -> s.contains("=") ? s.substring(0, s.indexOf('=')) : s)528// skip known-to-be-there flags529.filter(s -> !ignoredXXFlags.contains(s))530.findAny()531.isEmpty();532533// check -X flags534var ignoredXFlags = Set.of(535// default, yet still seen to be explicitly set536"mixed"537);538result &= allFlags.stream()539.filter(s -> s.startsWith("-X") && !s.startsWith("-XX:"))540// map to names:541// remove -X542.map(s -> s.substring(2))543// remove :.* from flags with values544.map(s -> s.contains(":") ? s.substring(0, s.indexOf(':')) : s)545// skip known-to-be-there flags546.filter(s -> !ignoredXFlags.contains(s))547.findAny()548.isEmpty();549550return "" + result;551}552553/**554* Dumps the map to the file if the file name is given as the property.555* This functionality could be helpful to know context in the real556* execution.557*558* @param map559*/560protected static void dump(Map<String, String> map) {561String dumpFileName = System.getProperty("vmprops.dump");562if (dumpFileName == null) {563return;564}565List<String> lines = new ArrayList<>();566map.forEach((k, v) -> lines.add(k + ":" + v));567try {568Files.write(Paths.get(dumpFileName), lines,569StandardOpenOption.APPEND, StandardOpenOption.CREATE);570} catch (IOException e) {571throw new RuntimeException("Failed to dump properties into '"572+ dumpFileName + "'", e);573}574}575576/**577* This method is for the testing purpose only.578*579* @param args580*/581public static void main(String args[]) {582Map<String, String> map = new VMProps().call();583map.forEach((k, v) -> System.out.println(k + ": '" + v + "'"));584}585}586587588