Path: blob/master/test/jtreg-ext/requires/VMProps.java
64509 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 jdk.test.whitebox.code.Compiler;47import jdk.test.whitebox.cpuinfo.CPUInfo;48import jdk.test.whitebox.gc.GC;49import jdk.test.whitebox.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.hasDTrace", this::vmHasDTrace);105map.put("vm.jvmti", this::vmHasJVMTI);106map.put("vm.cpu.features", this::cpuFeatures);107map.put("vm.pageSize", this::vmPageSize);108map.put("vm.rtm.cpu", this::vmRTMCPU);109map.put("vm.rtm.compiler", this::vmRTMCompiler);110// vm.cds is true if the VM is compiled with cds support.111map.put("vm.cds", this::vmCDS);112map.put("vm.cds.custom.loaders", this::vmCDSForCustomLoaders);113map.put("vm.cds.archived.java.heap", this::vmCDSForArchivedJavaHeap);114// vm.graal.enabled is true if Graal is used as JIT115map.put("vm.graal.enabled", this::isGraalEnabled);116map.put("vm.compiler1.enabled", this::isCompiler1Enabled);117map.put("vm.compiler2.enabled", this::isCompiler2Enabled);118map.put("docker.support", this::dockerSupport);119map.put("vm.musl", this::isMusl);120map.put("release.implementor", this::implementor);121map.put("jdk.containerized", this::jdkContainerized);122map.put("vm.flagless", this::isFlagless);123vmGC(map); // vm.gc.X = true/false124vmOptFinalFlags(map);125126dump(map.map);127return map.map;128}129130/**131* Print a stack trace before returning error state;132* Used by the various helper functions which parse information from133* VM properties in the case where they don't find an expected property134* or a property doesn't conform to an expected format.135*136* @return {@link #ERROR_STATE}137*/138private String errorWithMessage(String message) {139new Exception(message).printStackTrace();140return ERROR_STATE + message;141}142143/**144* @return vm.simpleArch value of "os.simpleArch" property of tested JDK.145*/146protected String vmArch() {147String arch = System.getProperty("os.arch");148if (arch.equals("x86_64") || arch.equals("amd64")) {149return "x64";150} else if (arch.contains("86")) {151return "x86";152} else {153return arch;154}155}156157/**158* @return VM type value extracted from the "java.vm.name" property.159*/160protected String vmFlavor() {161// E.g. "Java HotSpot(TM) 64-Bit Server VM"162String vmName = System.getProperty("java.vm.name");163if (vmName == null) {164return errorWithMessage("Can't get 'java.vm.name' property");165}166167Pattern startP = Pattern.compile(".* (\\S+) VM");168Matcher m = startP.matcher(vmName);169if (m.matches()) {170return m.group(1).toLowerCase();171}172return errorWithMessage("Can't get VM flavor from 'java.vm.name'");173}174175/**176* @return VM compilation mode extracted from the "java.vm.info" property.177*/178protected String vmCompMode() {179// E.g. "mixed mode"180String vmInfo = System.getProperty("java.vm.info");181if (vmInfo == null) {182return errorWithMessage("Can't get 'java.vm.info' property");183}184vmInfo = vmInfo.toLowerCase();185if (vmInfo.contains("mixed mode")) {186return "Xmixed";187} else if (vmInfo.contains("compiled mode")) {188return "Xcomp";189} else if (vmInfo.contains("interpreted mode")) {190return "Xint";191} else {192return errorWithMessage("Can't get compilation mode from 'java.vm.info'");193}194}195196/**197* @return VM bitness, the value of the "sun.arch.data.model" property.198*/199protected String vmBits() {200String dataModel = System.getProperty("sun.arch.data.model");201if (dataModel != null) {202return dataModel;203} else {204return errorWithMessage("Can't get 'sun.arch.data.model' property");205}206}207208/**209* @return "true" if Flight Recorder is enabled, "false" if is disabled.210*/211protected String vmFlightRecorder() {212Boolean isFlightRecorder = WB.getBooleanVMFlag("FlightRecorder");213String startFROptions = WB.getStringVMFlag("StartFlightRecording");214if (isFlightRecorder != null && isFlightRecorder) {215return "true";216}217if (startFROptions != null && !startFROptions.isEmpty()) {218return "true";219}220return "false";221}222223/**224* @return debug level value extracted from the "jdk.debug" property.225*/226protected String vmDebug() {227String debug = System.getProperty("jdk.debug");228if (debug != null) {229return "" + debug.contains("debug");230} else {231return errorWithMessage("Can't get 'jdk.debug' property");232}233}234235/**236* @return true if VM supports JVMCI and false otherwise237*/238protected String vmJvmci() {239// builds with jvmci have this flag240if (WB.getBooleanVMFlag("EnableJVMCI") == null) {241return "false";242}243244// Not all GCs have full JVMCI support245if (!WB.isJVMCISupportedByGC()) {246return "false";247}248249// Interpreted mode cannot enable JVMCI250if (vmCompMode().equals("Xint")) {251return "false";252}253254return "true";255}256257/**258* @return true if VM runs in emulated-client mode and false otherwise.259*/260protected String vmEmulatedClient() {261String vmInfo = System.getProperty("java.vm.info");262if (vmInfo == null) {263return errorWithMessage("Can't get 'java.vm.info' property");264}265return "" + vmInfo.contains(" emulated-client");266}267268/**269* @return supported CPU features270*/271protected String cpuFeatures() {272return CPUInfo.getFeatures().toString();273}274275/**276* For all existing GC sets vm.gc.X property.277* Example vm.gc.G1=true means:278* VM supports G1279* User either set G1 explicitely (-XX:+UseG1GC) or did not set any GC280* G1 can be selected, i.e. it doesn't conflict with other VM flags281*282* @param map - property-value pairs283*/284protected void vmGC(SafeMap map) {285var isJVMCIEnabled = Compiler.isJVMCIEnabled();286for (GC gc: GC.values()) {287map.put("vm.gc." + gc.name(),288() -> "" + (gc.isSupported()289&& (!isJVMCIEnabled || gc.isSupportedByJVMCICompiler())290&& (gc.isSelected() || GC.isSelectedErgonomically())));291}292}293294/**295* Selected final flag.296*297* @param map - property-value pairs298* @param flagName - flag name299*/300private void vmOptFinalFlag(SafeMap map, String flagName) {301map.put("vm.opt.final." + flagName,302() -> String.valueOf(WB.getBooleanVMFlag(flagName)));303}304305/**306* Selected sets of final flags.307*308* @param map - property-value pairs309*/310protected void vmOptFinalFlags(SafeMap map) {311vmOptFinalFlag(map, "ClassUnloading");312vmOptFinalFlag(map, "ClassUnloadingWithConcurrentMark");313vmOptFinalFlag(map, "UseCompressedOops");314vmOptFinalFlag(map, "UseVectorizedMismatchIntrinsic");315vmOptFinalFlag(map, "EnableJVMCI");316vmOptFinalFlag(map, "EliminateAllocations");317vmOptFinalFlag(map, "UseVtableBasedCHA");318}319320/**321* @return "true" if VM has a serviceability agent.322*/323protected String vmHasSA() {324return "" + Platform.hasSA();325}326327/**328* @return "true" if the VM is compiled with Java Flight Recorder (JFR)329* support.330*/331protected String vmHasJFR() {332return "" + WB.isJFRIncluded();333}334335/**336* @return "true" if the VM is compiled with JVMTI337*/338protected String vmHasJVMTI() {339return "" + WB.isJVMTIIncluded();340}341342/**343* @return "true" if the VM is compiled with DTrace344*/345protected String vmHasDTrace() {346return "" + WB.isDTraceIncluded();347}348349/**350* @return true if compiler in use supports RTM and false otherwise.351*/352protected String vmRTMCompiler() {353boolean isRTMCompiler = false;354355if (Compiler.isC2Enabled() &&356(Platform.isX86() || Platform.isX64() || Platform.isPPC())) {357isRTMCompiler = true;358}359return "" + isRTMCompiler;360}361362/**363* @return true if VM runs RTM supported CPU and false otherwise.364*/365protected String vmRTMCPU() {366return "" + CPUInfo.hasFeature("rtm");367}368369/**370* Check for CDS support.371*372* @return true if CDS is supported by the VM to be tested.373*/374protected String vmCDS() {375return "" + WB.isCDSIncluded();376}377378/**379* Check for CDS support for custom loaders.380*381* @return true if CDS provides support for customer loader in the VM to be tested.382*/383protected String vmCDSForCustomLoaders() {384return "" + ("true".equals(vmCDS()) && Platform.areCustomLoadersSupportedForCDS());385}386387/**388* Check for CDS support for archived Java heap regions.389*390* @return true if CDS provides support for archive Java heap regions in the VM to be tested.391*/392protected String vmCDSForArchivedJavaHeap() {393return "" + ("true".equals(vmCDS()) && WB.isJavaHeapArchiveSupported());394}395396/**397* @return System page size in bytes.398*/399protected String vmPageSize() {400return "" + WB.getVMPageSize();401}402403/**404* Check if Graal is used as JIT compiler.405*406* @return true if Graal is used as JIT compiler.407*/408protected String isGraalEnabled() {409return "" + Compiler.isGraalEnabled();410}411412/**413* Check if Compiler1 is present.414*415* @return true if Compiler1 is used as JIT compiler, either alone or as part of the tiered system.416*/417protected String isCompiler1Enabled() {418return "" + Compiler.isC1Enabled();419}420421/**422* Check if Compiler2 is present.423*424* @return true if Compiler2 is used as JIT compiler, either alone or as part of the tiered system.425*/426protected String isCompiler2Enabled() {427return "" + Compiler.isC2Enabled();428}429430/**431* A simple check for docker support432*433* @return true if docker is supported in a given environment434*/435protected String dockerSupport() {436boolean isSupported = false;437if (Platform.isLinux()) {438// currently docker testing is only supported for Linux,439// on certain platforms440441String arch = System.getProperty("os.arch");442443if (Platform.isX64()) {444isSupported = true;445} else if (Platform.isAArch64()) {446isSupported = true;447} else if (Platform.isS390x()) {448isSupported = true;449} else if (arch.equals("ppc64le")) {450isSupported = true;451}452}453454if (isSupported) {455try {456isSupported = checkDockerSupport();457} catch (Exception e) {458isSupported = false;459}460}461462return "" + isSupported;463}464465private boolean checkDockerSupport() throws IOException, InterruptedException {466ProcessBuilder pb = new ProcessBuilder(Container.ENGINE_COMMAND, "ps");467Process p = pb.start();468p.waitFor(10, TimeUnit.SECONDS);469470return (p.exitValue() == 0);471}472473/**474* Checks musl libc.475*476* @return true if musl libc is used.477*/478protected String isMusl() {479return Boolean.toString(WB.getLibcName().contains("musl"));480}481482private String implementor() {483try (InputStream in = new BufferedInputStream(new FileInputStream(484System.getProperty("java.home") + "/release"))) {485Properties properties = new Properties();486properties.load(in);487String implementorProperty = properties.getProperty("IMPLEMENTOR");488if (implementorProperty != null) {489return implementorProperty.replace("\"", "");490}491return errorWithMessage("Can't get 'IMPLEMENTOR' property from 'release' file");492} catch (IOException e) {493e.printStackTrace();494return errorWithMessage("Failed to read 'release' file " + e);495}496}497498private String jdkContainerized() {499String isEnabled = System.getenv("TEST_JDK_CONTAINERIZED");500return "" + "true".equalsIgnoreCase(isEnabled);501}502503/**504* Checks if we are in <i>almost</i> out-of-box configuration, i.e. the flags505* which JVM is started with don't affect its behavior "significantly".506* {@code TEST_VM_FLAGLESS} enviroment variable can be used to force this507* method to return true and allow any flags.508*509* @return true if there are no JVM flags510*/511private String isFlagless() {512boolean result = true;513if (System.getenv("TEST_VM_FLAGLESS") != null) {514return "" + result;515}516517List<String> allFlags = new ArrayList<String>();518Collections.addAll(allFlags, System.getProperty("test.vm.opts", "").trim().split("\\s+"));519Collections.addAll(allFlags, System.getProperty("test.java.opts", "").trim().split("\\s+"));520521// check -XX flags522var ignoredXXFlags = Set.of(523// added by run-test framework524"MaxRAMPercentage",525// added by test environment526"CreateCoredumpOnCrash"527);528result &= allFlags.stream()529.filter(s -> s.startsWith("-XX:"))530// map to names:531// remove -XX:532.map(s -> s.substring(4))533// remove +/- from bool flags534.map(s -> s.charAt(0) == '+' || s.charAt(0) == '-' ? s.substring(1) : s)535// remove =.* from others536.map(s -> s.contains("=") ? s.substring(0, s.indexOf('=')) : s)537// skip known-to-be-there flags538.filter(s -> !ignoredXXFlags.contains(s))539.findAny()540.isEmpty();541542// check -X flags543var ignoredXFlags = Set.of(544// default, yet still seen to be explicitly set545"mixed"546);547result &= allFlags.stream()548.filter(s -> s.startsWith("-X") && !s.startsWith("-XX:"))549// map to names:550// remove -X551.map(s -> s.substring(2))552// remove :.* from flags with values553.map(s -> s.contains(":") ? s.substring(0, s.indexOf(':')) : s)554// skip known-to-be-there flags555.filter(s -> !ignoredXFlags.contains(s))556.findAny()557.isEmpty();558559return "" + result;560}561562/**563* Dumps the map to the file if the file name is given as the property.564* This functionality could be helpful to know context in the real565* execution.566*567* @param map568*/569protected static void dump(Map<String, String> map) {570String dumpFileName = System.getProperty("vmprops.dump");571if (dumpFileName == null) {572return;573}574List<String> lines = new ArrayList<>();575map.forEach((k, v) -> lines.add(k + ":" + v));576try {577Files.write(Paths.get(dumpFileName), lines,578StandardOpenOption.APPEND, StandardOpenOption.CREATE);579} catch (IOException e) {580throw new RuntimeException("Failed to dump properties into '"581+ dumpFileName + "'", e);582}583}584585/**586* This method is for the testing purpose only.587*588* @param args589*/590public static void main(String args[]) {591Map<String, String> map = new VMProps().call();592map.forEach((k, v) -> System.out.println(k + ": '" + v + "'"));593}594}595596597