# HG changeset patch # User bobv # Date 1528843905 14400 # Node ID 292a4a87c32188470acc36b1ed5df32b003b9980 # Parent 5f20bf95c052ae069bb8da3ef25c3c8f7190b64a 8203357: Container Metrics Reviewed-by: mchung, dholmes, mseledtsov, rehn diff -r 5f20bf95c052 -r 292a4a87c321 src/java.base/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.platform.cgroupv1; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Stream; + +public class Metrics implements jdk.internal.platform.Metrics { + private SubSystem memory; + private SubSystem cpu; + private SubSystem cpuacct; + private SubSystem cpuset; + private SubSystem blkio; + private boolean activeSubSystems; + + // Values returned larger than this number are unlimited. + static long unlimited_minimum = 0x7FFFFFFFFF000000L; + + private static final Metrics INSTANCE = initContainerSubSystems(); + + private static final String PROVIDER_NAME = "cgroupv1"; + + private Metrics() { + activeSubSystems = false; + } + + public static Metrics getInstance() { + return INSTANCE; + } + + private static Metrics initContainerSubSystems() { + Metrics metrics = new Metrics(); + + /** + * Find the cgroup mount points for subsystems + * by reading /proc/self/mountinfo + * + * Example for docker MemorySubSystem subsystem: + * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/MemorySubSystem ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,MemorySubSystem + * + * Example for host: + * 34 28 0:29 / /sys/fs/cgroup/MemorySubSystem rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,MemorySubSystem + */ + try (Stream lines = + Files.lines(Paths.get("/proc/self/mountinfo"))) { + + lines.filter(line -> line.contains(" - cgroup ")) + .map(line -> line.split(" ")) + .forEach(entry -> createSubSystem(metrics, entry)); + + } catch (IOException e) { + return null; + } + + /** + * Read /proc/self/cgroup and map host mount point to + * local one via /proc/self/mountinfo content above + * + * Docker example: + * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044 + * + * Host example: + * 5:memory:/user.slice + * + * Construct a path to the process specific memory and cpuset + * cgroup directory. + * + * For a container running under Docker from memory example above + * the paths would be: + * + * /sys/fs/cgroup/memory + * + * For a Host from memory example above the path would be: + * + * /sys/fs/cgroup/memory/user.slice + * + */ + try (Stream lines = + Files.lines(Paths.get("/proc/self/cgroup"))) { + + lines.map(line -> line.split(":")) + .filter(line -> (line.length >= 3)) + .forEach(line -> setSubSystemPath(metrics, line)); + + } catch (IOException e) { + return null; + } + + // Return Metrics object if we found any subsystems. + if (metrics.activeSubSystems()) { + return metrics; + } + + return null; + } + + /** + * createSubSystem objects and initialize mount points + */ + private static void createSubSystem(Metrics metric, String [] mountentry) { + if (mountentry.length < 5) return; + + Path p = Paths.get(mountentry[4]); + String subsystemName = p.getFileName().toString(); + + if (subsystemName != null) { + switch (subsystemName) { + case "memory": + metric.setMemorySubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "cpuset": + metric.setCpuSetSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "cpu,cpuacct": + case "cpuacct,cpu": + metric.setCpuSubSystem(new SubSystem(mountentry[3], mountentry[4])); + metric.setCpuAcctSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "cpuacct": + metric.setCpuAcctSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "cpu": + metric.setCpuSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + case "blkio": + metric.setBlkIOSubSystem(new SubSystem(mountentry[3], mountentry[4])); + break; + default: + // Ignore subsystems that we don't support + break; + } + } + } + + /** + * setSubSystemPath based on the contents of /proc/self/cgroup + */ + private static void setSubSystemPath(Metrics metric, String [] entry) { + String controller; + String base; + SubSystem subsystem = null; + SubSystem subsystem2 = null; + + controller = entry[1]; + base = entry[2]; + if (controller != null && base != null) { + switch (controller) { + case "memory": + subsystem = metric.MemorySubSystem(); + break; + case "cpuset": + subsystem = metric.CpuSetSubSystem(); + break; + case "cpu,cpuacct": + case "cpuacct,cpu": + subsystem = metric.CpuSubSystem(); + subsystem2 = metric.CpuAcctSubSystem(); + break; + case "cpuacct": + subsystem = metric.CpuAcctSubSystem(); + break; + case "cpu": + subsystem = metric.CpuSubSystem(); + break; + case "blkio": + subsystem = metric.BlkIOSubSystem(); + break; + // Ignore subsystems that we don't support + default: + break; + } + } + + if (subsystem != null) { + subsystem.setPath(base); + metric.setActiveSubSystems(); + } + if (subsystem2 != null) { + subsystem2.setPath(base); + } + } + + + private void setActiveSubSystems() { + activeSubSystems = true; + } + + private boolean activeSubSystems() { + return activeSubSystems; + } + + private void setMemorySubSystem(SubSystem memory) { + this.memory = memory; + } + + private void setCpuSubSystem(SubSystem cpu) { + this.cpu = cpu; + } + + private void setCpuAcctSubSystem(SubSystem cpuacct) { + this.cpuacct = cpuacct; + } + + private void setCpuSetSubSystem(SubSystem cpuset) { + this.cpuset = cpuset; + } + + private void setBlkIOSubSystem(SubSystem blkio) { + this.blkio = blkio; + } + + private SubSystem MemorySubSystem() { + return memory; + } + + private SubSystem CpuSubSystem() { + return cpu; + } + + private SubSystem CpuAcctSubSystem() { + return cpuacct; + } + + private SubSystem CpuSetSubSystem() { + return cpuset; + } + + private SubSystem BlkIOSubSystem() { + return blkio; + } + + public String getProvider() { + return PROVIDER_NAME; + } + + /***************************************************************** + * CPU Accounting Subsystem + ****************************************************************/ + + + public long getCpuUsage() { + return SubSystem.getLongValue(cpuacct, "cpuacct.usage"); + } + + public long[] getPerCpuUsage() { + String usagelist = SubSystem.getStringValue(cpuacct, "cpuacct.usage_percpu"); + if (usagelist == null) { + return new long[0]; + } + + String list[] = usagelist.split(" "); + long percpu[] = new long[list.length]; + for (int i = 0; i < list.length; i++) { + percpu[i] = Long.parseLong(list[i]); + } + return percpu; + } + + public long getCpuUserUsage() { + return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "user"); + } + + public long getCpuSystemUsage() { + return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "system"); + } + + + /***************************************************************** + * CPU Subsystem + ****************************************************************/ + + + public long getCpuPeriod() { + return SubSystem.getLongValue(cpuacct, "cpu.cfs_period_us"); + } + + public long getCpuQuota() { + return SubSystem.getLongValue(cpuacct, "cpu.cfs_quota_us"); + } + + public long getCpuShares() { + long retval = SubSystem.getLongValue(cpuacct, "cpu.shares"); + if (retval == 0 || retval == 1024) + return -1; + else + return retval; + } + + public long getCpuNumPeriods() { + return SubSystem.getLongEntry(cpuacct, "cpu.stat", "nr_periods"); + } + + public long getCpuNumThrottled() { + return SubSystem.getLongEntry(cpuacct, "cpu.stat", "nr_throttled"); + } + + public long getCpuThrottledTime() { + return SubSystem.getLongEntry(cpuacct, "cpu.stat", "throttled_time"); + } + + public long getEffectiveCpuCount() { + return Runtime.getRuntime().availableProcessors(); + } + + + /***************************************************************** + * CPUSet Subsystem + ****************************************************************/ + + public int[] getCpuSetCpus() { + return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.cpus")); + } + + public int[] getEffectiveCpuSetCpus() { + return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_cpus")); + } + + public int[] getCpuSetMems() { + return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.mems")); + } + + public int[] getEffectiveCpuSetMems() { + return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_mems")); + } + + public double getCpuSetMemoryPressure() { + return SubSystem.getDoubleValue(cpuset, "cpuset.memory_pressure"); + } + + public boolean isCpuSetMemoryPressureEnabled() { + long val = SubSystem.getLongValue(cpuset, "cpuset.memory_pressure_enabled"); + return (val == 1); + } + + + /***************************************************************** + * Memory Subsystem + ****************************************************************/ + + + public long getMemoryFailCount() { + return SubSystem.getLongValue(memory, "memory.failcnt"); + } + + public long getMemoryLimit() { + long retval = SubSystem.getLongValue(memory, "memory.limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + public long getMemoryMaxUsage() { + return SubSystem.getLongValue(memory, "memory.max_usage_in_bytes"); + } + + public long getMemoryUsage() { + return SubSystem.getLongValue(memory, "memory.usage_in_bytes"); + } + + public long getKernelMemoryFailCount() { + return SubSystem.getLongValue(memory, "memory.kmem.failcnt"); + } + + public long getKernelMemoryLimit() { + long retval = SubSystem.getLongValue(memory, "memory.kmem.limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + public long getKernelMemoryMaxUsage() { + return SubSystem.getLongValue(memory, "memory.kmem.max_usage_in_bytes"); + } + + public long getKernelMemoryUsage() { + return SubSystem.getLongValue(memory, "memory.kmem.usage_in_bytes"); + } + + public long getTcpMemoryFailCount() { + return SubSystem.getLongValue(memory, "memory.kmem.tcp.failcnt"); + } + + public long getTcpMemoryLimit() { + long retval = SubSystem.getLongValue(memory, "memory.kmem.tcp.limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + public long getTcpMemoryMaxUsage() { + return SubSystem.getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes"); + } + + public long getTcpMemoryUsage() { + return SubSystem.getLongValue(memory, "memory.kmem.tcp.usage_in_bytes"); + } + + public long getMemoryAndSwapFailCount() { + return SubSystem.getLongValue(memory, "memory.memsw.failcnt"); + } + + public long getMemoryAndSwapLimit() { + long retval = SubSystem.getLongValue(memory, "memory.memsw.limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + public long getMemoryAndSwapMaxUsage() { + return SubSystem.getLongValue(memory, "memory.memsw.max_usage_in_bytes"); + } + + public long getMemoryAndSwapUsage() { + return SubSystem.getLongValue(memory, "memory.memsw.usage_in_bytes"); + } + + public boolean isMemoryOOMKillEnabled() { + long val = SubSystem.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); + return (val == 0); + } + + public long getMemorySoftLimit() { + long retval = SubSystem.getLongValue(memory, "memory.soft_limit_in_bytes"); + return retval > unlimited_minimum ? -1L : retval; + } + + + /***************************************************************** + * BlKIO Subsystem + ****************************************************************/ + + + public long getBlkIOServiceCount() { + return SubSystem.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total"); + } + + public long getBlkIOServiced() { + return SubSystem.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total"); + } + +} diff -r 5f20bf95c052 -r 292a4a87c321 src/java.base/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.platform.cgroupv1; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Optional; +import java.util.stream.Stream; + +public class SubSystem { + String root; + String mountPoint; + String path; + + public SubSystem(String root, String mountPoint) { + this.root = root; + this.mountPoint = mountPoint; + } + + public void setPath(String cgroupPath) { + if (root != null && cgroupPath != null) { + if (root.equals("/")) { + if (cgroupPath.equals("/")) { + path = mountPoint + cgroupPath; + } + else { + path = mountPoint; + } + } + else { + if (root.equals(cgroupPath)) { + path = mountPoint; + } + else { + if (root.indexOf(cgroupPath) == 0) { + if (cgroupPath.length() > root.length()) { + String cgroupSubstr = cgroupPath.substring(root.length()); + path = mountPoint + cgroupSubstr; + } + } + } + } + } + } + + public String path() { + return path; + } + + /** + * getSubSystemStringValue + * + * Return the first line of the file "parm" argument from the subsystem. + * + * TODO: Consider using weak references for caching BufferedReader object. + * + * @param subsystem + * @param parm + * @return Returns the contents of the file specified by param. + */ + public static String getStringValue(SubSystem subsystem, String parm) { + if (subsystem == null) return null; + + try(BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(subsystem.path(), parm))) { + String line = bufferedReader.readLine(); + return line; + } + catch (IOException e) { + return null; + } + + } + + public static long getLongValue(SubSystem subsystem, String parm) { + String strval = getStringValue(subsystem, parm); + + if (strval == null) return 0L; + + long retval = Long.parseLong(strval); + + return retval; + } + + public static double getDoubleValue(SubSystem subsystem, String parm) { + String strval = getStringValue(subsystem, parm); + + if (strval == null) return 0L; + + double retval = Double.parseDouble(strval); + + return retval; + } + + /** + * getSubSystemlongEntry + * + * Return the long value from the line containing the string "entryname" + * within file "parm" in the "subsystem". + * + * TODO: Consider using weak references for caching BufferedReader object. + * + * @param subsystem + * @param parm + * @param entryname + * @return long value + */ + public static long getLongEntry(SubSystem subsystem, String parm, String entryname) { + String val = null; + + if (subsystem == null) return 0L; + + try (Stream lines = Files.lines(Paths.get(subsystem.path(), parm))) { + + Optional result = lines.map(line -> line.split(" ")) + .filter(line -> (line.length == 2 && + line[0].equals(entryname))) + .map(line -> line[1]) + .findFirst(); + + return result.isPresent() ? Long.parseLong(result.get()) : 0L; + } + catch (IOException e) { + return 0L; + } + } + + public static int getIntValue(SubSystem subsystem, String parm) { + String val = getStringValue(subsystem, parm); + + if (val == null) return 0; + + return Integer.parseInt(val); + } + + /** + * StringRangeToIntArray + * + * Convert a string in the form of 1,3-4,6 to an array of + * integers containing all the numbers in the range. + * + * @param range + * @return int[] containing a sorted list of processors or memory nodes + */ + public static int[] StringRangeToIntArray(String range) { + int[] ints = new int[0]; + + if (range == null) return ints; + + ArrayList results = new ArrayList<>(); + String strs[] = range.split(","); + for (String str : strs) { + if (str.contains("-")) { + String lohi[] = str.split("-"); + // validate format + if (lohi.length != 2) { + continue; + } + int lo = Integer.parseInt(lohi[0]); + int hi = Integer.parseInt(lohi[1]); + for (int i = lo; i <= hi; i++) { + results.add(i); + } + } + else { + results.add(Integer.parseInt(str)); + } + } + + // sort results + results.sort(null); + + // convert ArrayList to primitive int array + ints = new int[results.size()]; + int i = 0; + for (Integer n : results) { + ints[i++] = n; + } + + return ints; + } +} diff -r 5f20bf95c052 -r 292a4a87c321 src/java.base/share/classes/jdk/internal/platform/Container.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/internal/platform/Container.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.platform; + +/* + * @author bobv + * @since 11 + */ + +public class Container { + + private Container() { } + + /** + * Returns the platform specific Container Metrics class or + * null if not supported on this platform. + * + * @return Metrics instance or null if not supported + */ + public static Metrics metrics() { + return Metrics.systemMetrics(); + } +} diff -r 5f20bf95c052 -r 292a4a87c321 src/java.base/share/classes/jdk/internal/platform/Metrics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/internal/platform/Metrics.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.platform; + +import java.lang.reflect.Method; + +/** + * Operating System Metrics class + * + * @implNote Some of the APIs within this class return metrics for an + * "Isolation Group" or "Container". When the term "Isolation Group" + * is used in the API description, this refers to either: + * + *
    + *
  1. All processes, including the current process within a container. + * + *
  2. All processes, including the current process running together + * isolated from other non-isolated processes. + * + *
  3. All processes running on a host when that there is no isolation + * in effect. + *
+ * + * @author bobv + * @since 11 + */ + +public interface Metrics { + + /** + * Returns an instance of the Metrics class. + * + * @return Metrics object or null if not supported on this platform. + */ + public static Metrics systemMetrics() { + try { + // We currently only support cgroupv1 + Class c = Class.forName("jdk.internal.platform.cgroupv1.Metrics"); + @SuppressWarnings("unchecked") + Method m = c.getMethod("getInstance"); + return (Metrics) m.invoke(null); + } catch (ClassNotFoundException e) { + return null; + } catch (ReflectiveOperationException e) { + throw new InternalError(e); + } + } + + /** + * Returns the interface responsible for providing the + * platform metrics. + * + * @implNote + * Metrics are currently only supported Linux. + * The provider for Linux is cgroupsv1. + * + * @return The name of the provider. + * + */ + public String getProvider(); + + + /***************************************************************** + * CPU Accounting Subsystem + ****************************************************************/ + + /** + * Returns the aggregate time, in nanoseconds, consumed by all + * tasks in the Isolation Group. + * + * @return Time in nanoseconds or 0L if metric is not available. + * + */ + public long getCpuUsage(); + + /** + * Returns the aggregate time, in nanoseconds, consumed by all tasks in + * the Isolation Group, separated by CPU. If the current process + * is running within a container, the reported time will only be + * valid for processes running within the same container. The values + * are returned in an array, one entry for each physical processor + * on the system. Time values for processors unavailable to this + * Group are undefined. + * + * @return long array of time values. The size of the array is equal + * to the total number of physical processors in the system. If + * this metric is not available, a zero length array will be + * returned. + * + */ + public long[] getPerCpuUsage(); + + /** + * Returns the aggregate user time, in nanoseconds, consumed by all + * tasks in the Isolation Group. + * + * @return User time in nanoseconds or 0L if metric is not available. + * + */ + public long getCpuUserUsage(); + + /** + * Returns the aggregate system time, in nanoseconds, consumed by + * all tasks in the Isolation Group. + * + * @return System time in nanoseconds or 0L if metric is not available. + * + */ + public long getCpuSystemUsage(); + + /***************************************************************** + * CPU Scheduling Metrics + ****************************************************************/ + + /** + * Returns the length of the scheduling period, in + * microseconds, for processes within the Isolation Group. + * + * @return time in microseconds or 0L if metric is not available. + * + */ + public long getCpuPeriod(); + + /** + * Returns the total available run-time allowed, in microseconds, + * during each scheduling period for all tasks in the Isolation + * Group. + * + * @return time in microseconds or -1 if the quota is unlimited. + * + */ + public long getCpuQuota(); + + + /** + * Returns the relative weighting of processes with the Isolation + * Group used for prioritizing the scheduling of processes across + * all Isolation Groups running on a host. + * + * @implNote + * Popular container orchestration systems have standardized shares + * to be multiples of 1024, where 1024 is interpreted as 1 CPU share + * of execution. Users can distribute CPU resources to multiple + * Isolation Groups by specifying the CPU share weighting needed by + * each process. To request 2 CPUS worth of execution time, CPU shares + * would be set to 2048. + * + * @return shares value or -1 if no share set. + * + */ + public long getCpuShares(); + + /** + * Returns the number of time-slice periods that have elapsed if + * a CPU quota has been setup for the Isolation Group; otherwise + * returns 0. + * + * @return count of elapsed periods or 0 if the quota is unlimited. + * + */ + public long getCpuNumPeriods(); + + /** + * Returns the number of time-slice periods that the group has + * been throttled or limited due to the group exceeding its quota + * if a CPU quota has been setup for the Isolation Group. + * + * @return count of throttled periods or 0 if the quota is unlimited. + * + */ + public long getCpuNumThrottled(); + + /** + * Returns the total time duration, in nanoseconds, that the + * group has been throttled or limited due to the group exceeding + * its quota if a CPU quota has been setup for the Isolation Group. + * + * @return Throttled time in nanoseconds or 0 if the quota is unlimited. + * + */ + public long getCpuThrottledTime(); + + + /** + * Returns the number of effective processors that this Isolation + * group has available to it. This effective processor count is + * computed based on the number of dedicated CPUs, CPU shares and + * CPU quotas in effect for this isolation group. + * + * This method returns the same value as + * {@link java.lang.Runtime#availableProcessors()}. + * + * @return The number of effective CPUs. + * + */ + public long getEffectiveCpuCount(); + + /***************************************************************** + * CPU Sets + ****************************************************************/ + + /** + * Returns the CPUS that are available for execution of processes + * in the current Isolation Group. The size of the array is equal + * to the total number of CPUs and the elements in the array are the + * physical CPU numbers that are available. Some of the CPUs returned + * may be offline. To get the current online CPUs, use + * {@link getEffectiveCpuSetCpus()}. + * + * @return An array of available CPUs or a zero length array + * if the metric is not available. + * + */ + public int[] getCpuSetCpus(); + + /** + * Returns the CPUS that are available and online for execution of + * processes within the current Isolation Group. The size of the + * array is equal to the total number of CPUs and the elements in + * the array are the physical CPU numbers. + * + * @return An array of available and online CPUs or a zero length + * array if the metric is not available. + * + */ + public int[] getEffectiveCpuSetCpus(); + + /** + * Returns the memory nodes that are available for use by processes + * in the current Isolation Group. The size of the array is equal + * to the total number of nodes and the elements in the array are the + * physical node numbers that are available. Some of the nodes returned + * may be offline. To get the current online memory nodes, use + * {@link getEffectiveCpuSetMems()}. + * + * @return An array of available memory nodes or a zero length array + * if the metric is not available. + * + */ + public int[] getCpuSetMems(); + + /** + * Returns the memory nodes that are available and online for use by + * processes within the current Isolation Group. The size of the + * array is equal to the total number of nodes and the elements in + * the array are the physical node numbers. + * + * @return An array of available and online nodes or a zero length + * array if the metric is not available. + * + */ + public int[] getEffectiveCpuSetMems(); + + /** + * Returns the (attempts per second * 1000), if enabled, that the + * operating system tries to satisfy a memory request for any + * process in the current Isolation Group when no free memory is + * readily available. Use {@link #isCpuSetMemoryPressureEnabled()} to + * to determine if this support is enabled. + * + * @return Memory pressure or 0 if not enabled or metric is not + * available. + * + */ + public double getCpuSetMemoryPressure(); + + /** + * Returns the state of the memory pressure detection support. + * + * @return true if the support is available and enabled, otherwise false. + * + */ + public boolean isCpuSetMemoryPressureEnabled(); + + /***************************************************************** + * Memory Subsystem + ****************************************************************/ + + /** + * Returns the number of times that user memory requests in the + * Isolation Group have exceeded the memory limit. + * + * @return The number of exceeded requests or 0 if none or metric + * is not available. + * + */ + public long getMemoryFailCount(); + + /** + * Returns the maximum amount of physical memory, in bytes, that + * can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if either + * there is no limit set or this metric is not available. + * + */ + public long getMemoryLimit(); + + /** + * Returns the largest amount of physical memory, in bytes, that + * have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or or 0 if this + * metric is not available. + * + */ + public long getMemoryMaxUsage(); + + /** + * Returns the amount of physical memory, in bytes, that is currently + * allocated in the current Isolation Group. + * + * @return The amount of memory in bytes allocated or 0 if this + * metric is not available. + * + */ + public long getMemoryUsage(); + + /** + * Returns the number of times that kernel memory requests in the + * Isolation Group have exceeded the kernel memory limit. + * + * @return The number of exceeded requests or 0 if none or metric + * is not available. + * + */ + public long getKernelMemoryFailCount(); + + /** + * Returns the maximum amount of kernel physical memory, in bytes, that + * can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if either + * there is no limit set or this metric is not available. + * + */ + public long getKernelMemoryLimit(); + + /** + * Returns the largest amount of kernel physical memory, in bytes, that + * have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or or 0 if this + * metric is not available. + * + */ + public long getKernelMemoryMaxUsage(); + + /** + * Returns the amount of kernel physical memory, in bytes, that + * is currently allocated in the current Isolation Group. + * + * @return The amount of memory in bytes allocated or 0 if this + * metric is not available. + * + */ + public long getKernelMemoryUsage(); + + /** + * Returns the number of times that networking memory requests in the + * Isolation Group have exceeded the kernel memory limit. + * + * @return The number of exceeded requests or 0 if none or metric + * is not available. + * + */ + public long getTcpMemoryFailCount(); + + /** + * Returns the maximum amount of networking physical memory, in bytes, + * that can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if either + * there is no limit set or this metric is not available. + * + */ + public long getTcpMemoryLimit(); + + /** + * Returns the largest amount of networking physical memory, in bytes, + * that have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or or 0 if this + * metric is not available. + * + */ + public long getTcpMemoryMaxUsage(); + + /** + * Returns the amount of networking physical memory, in bytes, that + * is currently allocated in the current Isolation Group. + * + * @return The amount of memory in bytes allocated or 0 if this + * metric is not available. + * + */ + public long getTcpMemoryUsage(); + + /** + * Returns the number of times that user memory requests in the + * Isolation Group have exceeded the memory + swap limit. + * + * @return The number of exceeded requests or 0 if none or metric + * is not available. + * + */ + public long getMemoryAndSwapFailCount(); + + /** + * Returns the maximum amount of physical memory and swap space, + * in bytes, that can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if either + * there is no limit set or this metric is not available. + * + */ + public long getMemoryAndSwapLimit(); + + /** + * Returns the largest amount of physical memory and swap space, + * in bytes, that have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or or 0 if this + * metric is not available. + * + */ + public long getMemoryAndSwapMaxUsage(); + + /** + * Returns the amount of physical memory and swap space, in bytes, + * that is currently allocated in the current Isolation Group. + * + * @return The amount of memory in bytes allocated or 0 if this + * metric is not available. + * + */ + public long getMemoryAndSwapUsage(); + + /** + * Returns the state of the Operating System Out of Memory termination + * policy. + * + * @return Returns true if operating system will terminate processes + * in the Isolation Group that exceed the amount of available + * memory, otherwise false. Flase will be returned if this + * capability is not available on the current operating system. + * + */ + public boolean isMemoryOOMKillEnabled(); + + /** + * Returns the hint to the operating system that allows groups + * to specify the minimum amount of physical memory that they need to + * achieve reasonable performance in low memory systems. This allows + * host systems to provide greater sharing of memory. + * + * @return The minimum amount of physical memory, in bytes, that the + * operating system will try to maintain under low memory + * conditions. If this metric is not available, 0 will be + * returned. + * + */ + public long getMemorySoftLimit(); + + /***************************************************************** + * BlKIO Subsystem + ****************************************************************/ + + /** + * Returns the number of block I/O requests to the disk that have been + * issued by the Isolation Group. + * + * @return The count of requests or 0 if this metric is not available. + * + */ + public long getBlkIOServiceCount(); + + /** + * Returns the number of block I/O bytes that have been transferred + * to/from the disk by the Isolation Group. + * + * @return The number of bytes transferred or 0 if this metric is not available. + * + */ + public long getBlkIOServiced(); +} diff -r 5f20bf95c052 -r 292a4a87c321 src/java.base/share/classes/sun/launcher/LauncherHelper.java --- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java Tue Jun 12 18:44:01 2018 -0400 +++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java Tue Jun 12 18:51:45 2018 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -91,6 +91,9 @@ import jdk.internal.misc.VM; import jdk.internal.module.ModuleBootstrap; import jdk.internal.module.Modules; +import jdk.internal.platform.Container; +import jdk.internal.platform.Metrics; + public final class LauncherHelper { @@ -151,6 +154,7 @@ * this code determine this value, using a suitable method or omit the * line entirely. */ + @SuppressWarnings("fallthrough") static void showSettings(boolean printToStderr, String optionFlag, long initialHeapSize, long maxHeapSize, long stackSize) { @@ -169,10 +173,18 @@ case "locale": printLocale(); break; + case "system": + if (System.getProperty("os.name").contains("Linux")) { + printSystemMetrics(); + break; + } default: printVmSettings(initialHeapSize, maxHeapSize, stackSize); printProperties(); printLocale(); + if (System.getProperty("os.name").contains("Linux")) { + printSystemMetrics(); + } break; } } @@ -307,6 +319,101 @@ } } + public static void printSystemMetrics() { + Metrics c = Container.metrics(); + + ostream.println("Operating System Metrics:"); + + if (c == null) { + ostream.println(INDENT + "No metrics available for this platform"); + return; + } + + ostream.println(INDENT + "Provider: " + c.getProvider()); + ostream.println(INDENT + "Effective CPU Count: " + c.getEffectiveCpuCount()); + ostream.println(INDENT + "CPU Period: " + c.getCpuPeriod() + + (c.getCpuPeriod() == -1 ? "" : "us")); + ostream.println(INDENT + "CPU Quota: " + c.getCpuQuota() + + (c.getCpuQuota() == -1 ? "" : "us")); + ostream.println(INDENT + "CPU Shares: " + c.getCpuShares()); + + int cpus[] = c.getCpuSetCpus(); + ostream.println(INDENT + "List of Processors, " + + cpus.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < cpus.length; i++) { + ostream.print(cpus[i] + " "); + } + if (cpus.length > 0) { + ostream.println(""); + } + + cpus = c.getEffectiveCpuSetCpus(); + ostream.println(INDENT + "List of Effective Processors, " + + cpus.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < cpus.length; i++) { + ostream.print(cpus[i] + " "); + } + if (cpus.length > 0) { + ostream.println(""); + } + + int mems[] = c.getCpuSetMems(); + ostream.println(INDENT + "List of Memory Nodes, " + + mems.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < mems.length; i++) { + ostream.print(mems[i] + " "); + } + if (mems.length > 0) { + ostream.println(""); + } + + mems = c.getEffectiveCpuSetMems(); + ostream.println(INDENT + "List of Available Memory Nodes, " + + mems.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < mems.length; i++) { + ostream.print(mems[i] + " "); + } + if (mems.length > 0) { + ostream.println(""); + } + + ostream.println(INDENT + "CPUSet Memory Pressure Enabled: " + + c.isCpuSetMemoryPressureEnabled()); + + long limit = c.getMemoryLimit(); + ostream.println(INDENT + "Memory Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + limit = c.getMemorySoftLimit(); + ostream.println(INDENT + "Memory Soft Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + limit = c.getMemoryAndSwapLimit(); + ostream.println(INDENT + "Memory & Swap Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + limit = c.getKernelMemoryLimit(); + ostream.println(INDENT + "Kernel Memory Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + limit = c.getTcpMemoryLimit(); + ostream.println(INDENT + "TCP Memory Limit: " + + ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + + ostream.println(INDENT + "Out Of Memory Killer Enabled: " + + c.isMemoryOOMKillEnabled()); + + ostream.println(""); + } + private enum SizePrefix { KILO(1024, "K"), diff -r 5f20bf95c052 -r 292a4a87c321 src/java.base/share/classes/sun/launcher/resources/launcher.properties --- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties Tue Jun 12 18:44:01 2018 -0400 +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties Tue Jun 12 18:51:45 2018 -0400 @@ -1,5 +1,5 @@ # -# Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -150,7 +150,11 @@ \ show all locale related settings and continue\n\ \ -XshowSettings:properties\n\ \ show all property settings and continue\n\ -\ -XshowSettings:vm show all vm related settings and continue\n\ +\ -XshowSettings:vm\n\ +\ show all vm related settings and continue\n\ +\ -XshowSettings:system\n\ +\ (Linux Only) show host system or container\n\ +\ configuration and continue\n\ \ -Xss set java thread stack size\n\ \ -Xverify sets the mode of the bytecode verifier\n\ \ --add-reads =(,)*\n\ diff -r 5f20bf95c052 -r 292a4a87c321 test/hotspot/jtreg/runtime/containers/docker/CPUSetsReader.java --- a/test/hotspot/jtreg/runtime/containers/docker/CPUSetsReader.java Tue Jun 12 18:44:01 2018 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.io.FileReader; -import java.util.ArrayList; -import java.util.Optional; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import jdk.test.lib.Asserts; - - -// A simple CPU sets reader and parser -public class CPUSetsReader { - public static String PROC_SELF_STATUS_PATH="/proc/self/status"; - - // Test the parser - public static void test() { - assertParse("0-7", "0,1,2,3,4,5,6,7"); - assertParse("1,3,6", "1,3,6"); - assertParse("0,2-4,6,10-11", "0,2,3,4,6,10,11"); - assertParse("0", "0"); - } - - - private static void assertParse(String cpuSet, String expectedResult) { - Asserts.assertEquals(listToString(parseCpuSet(cpuSet)), expectedResult); - } - - - public static String readFromProcStatus(String setType) { - String path = PROC_SELF_STATUS_PATH; - Optional o = Optional.empty(); - - System.out.println("readFromProcStatus() entering for: " + setType); - - try (Stream stream = Files.lines(Paths.get(path))) { - o = stream - .filter(line -> line.contains(setType)) - .findFirst(); - } catch (IOException e) { - return null; - } - - if (!o.isPresent()) { - return null; // entry not found - } - - String[] parts = o.get().replaceAll("\\s","").split(":"); - - // Should be 2 parts, before and after ":" - Asserts.assertEquals(parts.length, 2); - - String result = parts[1]; - System.out.println("readFromProcStatus() returning: " + result); - return result; - } - - - public static List parseCpuSet(String value) { - ArrayList result = new ArrayList(); - - try { - String[] commaSeparated = value.split(","); - - for (String item : commaSeparated) { - if (item.contains("-")) { - addRange(result, item); - } else { - result.add(Integer.parseInt(item)); - } - } - } catch (Exception e) { - System.err.println("Exception in getMaxCpuSets(): " + e); - return null; - } - - return result; - } - - - private static void addRange(ArrayList list, String s) { - String[] range = s.split("-"); - if ( range.length != 2 ) { - throw new RuntimeException("Range should only contain two items, but contains " - + range.length + " items"); - } - - int min = Integer.parseInt(range[0]); - int max = Integer.parseInt(range[1]); - - if (min >= max) { - String msg = String.format("min is greater or equals to max, min = %d, max = %d", - min, max); - throw new RuntimeException(msg); - } - - for (int i = min; i <= max; i++) { - list.add(i); - } - } - - - // Convert list of integers to string with comma-separated values - public static String listToString(List list) { - return listToString(list, Integer.MAX_VALUE); - } - - // Convert list of integers to a string with comma-separated values; - // include up to maxCount. - public static String listToString(List list, int maxCount) { - return list.stream() - .limit(maxCount) - .map(Object::toString) - .collect(Collectors.joining(",")); - } -} diff -r 5f20bf95c052 -r 292a4a87c321 test/hotspot/jtreg/runtime/containers/docker/Common.java --- a/test/hotspot/jtreg/runtime/containers/docker/Common.java Tue Jun 12 18:44:01 2018 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -/* - * Methods and definitions common to docker tests container in this directory - */ - -import java.nio.file.Files; -import java.nio.file.Paths; -import jdk.test.lib.containers.docker.DockerRunOptions; -import jdk.test.lib.containers.docker.DockerTestUtils; -import jdk.test.lib.Utils; -import jdk.test.lib.process.OutputAnalyzer; - - -public class Common { - public static final String imageNameAndTag = "jdk-internal:test"; - - public static String imageName(String suffix) { - return imageNameAndTag + "-" + suffix; - } - - - public static void prepareWhiteBox() throws Exception { - Files.copy(Paths.get(ClassFileInstaller.getJarPath("whitebox.jar")), - Paths.get(Utils.TEST_CLASSES, "whitebox.jar")); - } - - - // create simple commonly used options - public static DockerRunOptions newOpts(String imageNameAndTag) { - return new DockerRunOptions(imageNameAndTag, "/jdk/bin/java", "-version") - .addJavaOpts("-Xlog:os+container=trace"); - } - - - // create commonly used options with class to be launched inside container - public static DockerRunOptions newOpts(String imageNameAndTag, String testClass) { - DockerRunOptions opts = - new DockerRunOptions(imageNameAndTag, "/jdk/bin/java", testClass); - opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); - opts.addJavaOpts("-Xlog:os+container=trace", "-cp", "/test-classes/"); - return opts; - } - - - public static DockerRunOptions addWhiteBoxOpts(DockerRunOptions opts) { - opts.addJavaOpts("-Xbootclasspath/a:/test-classes/whitebox.jar", - "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI"); - return opts; - } - - - // most common type of run and checks - public static OutputAnalyzer run(DockerRunOptions opts) throws Exception { - return DockerTestUtils.dockerRunJava(opts) - .shouldHaveExitValue(0).shouldContain("Initializing Container Support"); - } - - - // log beginning of a test case - public static void logNewTestCase(String msg) { - System.out.println("========== NEW TEST CASE: " + msg); - } - -} diff -r 5f20bf95c052 -r 292a4a87c321 test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java --- a/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java Tue Jun 12 18:44:01 2018 -0400 +++ b/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java Tue Jun 12 18:51:45 2018 -0400 @@ -30,13 +30,13 @@ * @modules java.base/jdk.internal.misc * java.management * jdk.jartool/sun.tools.jar - * @build Common * @run driver TestCPUAwareness */ import java.util.List; +import jdk.test.lib.containers.docker.Common; import jdk.test.lib.containers.docker.DockerRunOptions; import jdk.test.lib.containers.docker.DockerTestUtils; - +import jdk.test.lib.containers.cgroup.CPUSetsReader; public class TestCPUAwareness { private static final String imageName = Common.imageName("cpu"); diff -r 5f20bf95c052 -r 292a4a87c321 test/hotspot/jtreg/runtime/containers/docker/TestCPUSets.java --- a/test/hotspot/jtreg/runtime/containers/docker/TestCPUSets.java Tue Jun 12 18:44:01 2018 -0400 +++ b/test/hotspot/jtreg/runtime/containers/docker/TestCPUSets.java Tue Jun 12 18:51:45 2018 -0400 @@ -31,13 +31,15 @@ * @modules java.base/jdk.internal.misc * java.management * jdk.jartool/sun.tools.jar - * @build Common AttemptOOM CPUSetsReader sun.hotspot.WhiteBox PrintContainerInfo + * @build AttemptOOM sun.hotspot.WhiteBox PrintContainerInfo * @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission * @run driver TestCPUSets */ import java.util.List; +import jdk.test.lib.containers.docker.Common; import jdk.test.lib.containers.docker.DockerRunOptions; import jdk.test.lib.containers.docker.DockerTestUtils; +import jdk.test.lib.containers.cgroup.CPUSetsReader; import jdk.test.lib.Asserts; import jdk.test.lib.Platform; import jdk.test.lib.Utils; diff -r 5f20bf95c052 -r 292a4a87c321 test/hotspot/jtreg/runtime/containers/docker/TestMemoryAwareness.java --- a/test/hotspot/jtreg/runtime/containers/docker/TestMemoryAwareness.java Tue Jun 12 18:44:01 2018 -0400 +++ b/test/hotspot/jtreg/runtime/containers/docker/TestMemoryAwareness.java Tue Jun 12 18:51:45 2018 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,11 @@ * @modules java.base/jdk.internal.misc * java.management * jdk.jartool/sun.tools.jar - * @build Common AttemptOOM sun.hotspot.WhiteBox PrintContainerInfo + * @build AttemptOOM sun.hotspot.WhiteBox PrintContainerInfo * @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission * @run driver TestMemoryAwareness */ +import jdk.test.lib.containers.docker.Common; import jdk.test.lib.containers.docker.DockerRunOptions; import jdk.test.lib.containers.docker.DockerTestUtils; diff -r 5f20bf95c052 -r 292a4a87c321 test/hotspot/jtreg/runtime/containers/docker/TestMisc.java --- a/test/hotspot/jtreg/runtime/containers/docker/TestMisc.java Tue Jun 12 18:44:01 2018 -0400 +++ b/test/hotspot/jtreg/runtime/containers/docker/TestMisc.java Tue Jun 12 18:51:45 2018 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,13 @@ * @modules java.base/jdk.internal.misc * java.management * jdk.jartool/sun.tools.jar - * @build Common CheckContainerized sun.hotspot.WhiteBox PrintContainerInfo + * @build CheckContainerized sun.hotspot.WhiteBox PrintContainerInfo * @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission * @run driver TestMisc */ +import jdk.test.lib.containers.docker.Common; +import jdk.test.lib.containers.docker.DockerTestUtils; import jdk.test.lib.containers.docker.DockerRunOptions; -import jdk.test.lib.containers.docker.DockerTestUtils; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/TEST.ROOT --- a/test/jdk/TEST.ROOT Tue Jun 12 18:44:01 2018 -0400 +++ b/test/jdk/TEST.ROOT Tue Jun 12 18:51:45 2018 -0400 @@ -38,7 +38,8 @@ sun.arch.data.model \ java.runtime.name \ vm.graal.enabled \ - vm.cds + vm.cds \ + docker.support # Minimum jtreg version requiredVersion=4.2 b12 diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/cgroup/TestCgroupMetrics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/cgroup/TestCgroupMetrics.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @requires os.family == "linux" + * @modules java.base/jdk.internal.platform + * @library /test/lib + * @run main TestCgroupMetrics + */ + +import jdk.test.lib.containers.cgroup.MetricsTester; +import jdk.internal.platform.Metrics; + +public class TestCgroupMetrics { + + public static void main(String[] args) throws Exception { + // If cgroups is not configured, report success. + Metrics metrics = Metrics.systemMetrics(); + if (metrics == null) { + System.out.println("TEST PASSED!!!"); + return; + } + + MetricsTester metricsTester = new MetricsTester(); + metricsTester.setup(); + metricsTester.testCpuAccounting(); + metricsTester.testCpuSchedulingMetrics(); + metricsTester.testCpuSets(); + metricsTester.testMemorySubsystem(); + metricsTester.testBlkIO(); + metricsTester.testCpuConsumption(); + metricsTester.testMemoryUsage(); + System.out.println("TEST PASSED!!!"); + } + +} diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/docker/Dockerfile-BasicTest --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/docker/Dockerfile-BasicTest Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,8 @@ +FROM oraclelinux:7.2 +MAINTAINER mikhailo.seledtsov@oracle.com + +COPY /jdk /jdk + +ENV JAVA_HOME=/jdk + +CMD ["/bin/bash"] diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/docker/Dockerfile-BasicTest-aarch64 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/docker/Dockerfile-BasicTest-aarch64 Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,8 @@ +# Use generic ubuntu Linux on AArch64 +FROM aarch64/ubuntu + +COPY /jdk /jdk + +ENV JAVA_HOME=/jdk + +CMD ["/bin/bash"] diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/docker/Dockerfile-BasicTest-ppc64le --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/docker/Dockerfile-BasicTest-ppc64le Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,10 @@ +# test on x86_64 uses Oracle Linux but we do not have this for ppc64le +# so use some other Linux where OpenJDK works +# FROM oraclelinux:7.2 +FROM ppc64le/ubuntu + +COPY /jdk /jdk + +ENV JAVA_HOME=/jdk + +CMD ["/bin/bash"] diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/docker/Dockerfile-BasicTest-s390x --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/docker/Dockerfile-BasicTest-s390x Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,7 @@ +FROM s390x/ubuntu + +COPY /jdk /jdk + +ENV JAVA_HOME=/jdk + +CMD ["/bin/bash"] diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/docker/MetricsCpuTester.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/docker/MetricsCpuTester.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Arrays; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import jdk.internal.platform.Metrics; + +public class MetricsCpuTester { + public static void main(String[] args) { + System.out.println(Arrays.toString(args)); + switch (args[0]) { + case "cpusets": + testCpuSets(args[1]); + break; + case "cpuquota": + testCpuQuotaAndPeriod(Long.parseLong(args[1]), Long.parseLong(args[2])); + break; + case "cpushares": + testCpuShares(Long.parseLong(args[1])); + break; + case "cpus": + testCpuThrottling(); + break; + case "cpumems": + testCpuSetMemNodes(args[1]); + break; + case "combo": + testCombo(args[1], Long.parseLong(args[2]), Long.parseLong(args[3]), Long.parseLong(args[4])); + break; + } + } + + private static void testCpuQuotaAndPeriod(long quota, long period) { + Metrics metrics = Metrics.systemMetrics(); + long newQuota = metrics.getCpuQuota(); + long newPeriod = metrics.getCpuPeriod(); + if (quota != newQuota || period != newPeriod) { + throw new RuntimeException("CPU quota or period not equal, expected : [" + + quota + "," + period + "]" + ", got : " + "[" + newQuota + + "," + newPeriod + "]"); + } + + long cpuNumPeriods = metrics.getCpuNumPeriods(); + long current = System.currentTimeMillis(); + while (System.currentTimeMillis() - current < 1000) ; // 1sec + long newCpuNumPeriods = metrics.getCpuNumPeriods(); + if (newCpuNumPeriods <= cpuNumPeriods) { + throw new RuntimeException("CPU shares failed, expected : [" + + cpuNumPeriods + "]" + ", got : " + "[" + + newCpuNumPeriods + "]"); + } + System.out.println("TEST PASSED!!!"); + } + + private static void testCpuSets(String cpuset) { + int[] ipCpuSet; + String[] tokens = cpuset.split("-"); + if (tokens.length > 1) { // we are given range of CPUs + ipCpuSet = IntStream.rangeClosed(Integer.parseInt(tokens[0]), + Integer.parseInt(tokens[1])).toArray(); + } else if (cpuset.split(",").length > 1) { // list of cpus + ipCpuSet = Stream.of(cpuset.split(",")).mapToInt(Integer::parseInt).toArray(); + } else { // just a single cpu + ipCpuSet = new int[]{Integer.parseInt(cpuset)}; + } + + Metrics metrics = Metrics.systemMetrics(); + int[] cpuSets = metrics.getCpuSetCpus(); + + int[] effectiveCpus = metrics.getEffectiveCpuSetCpus(); + + if (!Arrays.equals(ipCpuSet, cpuSets)) { + throw new RuntimeException("Cpusets not equal, expected : " + + Arrays.toString(ipCpuSet) + ", got : " + Arrays.toString(cpuSets)); + } + + if (!Arrays.equals(ipCpuSet, effectiveCpus)) { + throw new RuntimeException("Effective Cpusets not equal, expected : " + + Arrays.toString(ipCpuSet) + ", got : " + + Arrays.toString(effectiveCpus)); + } + System.out.println("TEST PASSED!!!"); + } + + private static void testCpuSetMemNodes(String cpusetMems) { + Metrics metrics = Metrics.systemMetrics(); + int[] cpuSets = metrics.getCpuSetMems(); + + int[] ipCpuSet; + String[] tokens = cpusetMems.split("-"); + if (tokens.length > 1) { // we are given range of CPUs + ipCpuSet = IntStream.rangeClosed(Integer.parseInt(tokens[0]), + Integer.parseInt(tokens[1])).toArray(); + } else if (cpusetMems.split(",").length > 1) { // list of cpus + ipCpuSet = Stream.of(cpusetMems.split(",")).mapToInt(Integer::parseInt).toArray(); + } else { // just a single cpu + ipCpuSet = new int[]{Integer.parseInt(cpusetMems)}; + } + + int[] effectiveMems = metrics.getEffectiveCpuSetMems(); + + + if (!Arrays.equals(ipCpuSet, cpuSets)) { + throw new RuntimeException("Cpuset.mems not equal, expected : " + + Arrays.toString(ipCpuSet) + ", got : " + + Arrays.toString(cpuSets)); + } + + if (!Arrays.equals(ipCpuSet, effectiveMems)) { + throw new RuntimeException("Effective mem nodes not equal, expected : " + + Arrays.toString(ipCpuSet) + ", got : " + + Arrays.toString(effectiveMems)); + } + System.out.println("TEST PASSED!!!"); + } + + private static void testCpuShares(long shares) { + Metrics metrics = Metrics.systemMetrics(); + long newShares = metrics.getCpuShares(); + if (newShares != shares) { + throw new RuntimeException("CPU shares not equal, expected : [" + + shares + "]" + ", got : " + "[" + newShares + "]"); + } + System.out.println("TEST PASSED!!!"); + } + + private static void testCpuThrottling() { + Metrics metrics = Metrics.systemMetrics(); + long throttledTime = metrics.getCpuThrottledTime(); + long numThrottled = metrics.getCpuNumThrottled(); + + long current = System.currentTimeMillis(); + + while (System.currentTimeMillis() - current < 2000) ; // 2 sec + + long newthrottledTime = metrics.getCpuThrottledTime(); + long newnumThrottled = metrics.getCpuNumThrottled(); + if (newthrottledTime <= throttledTime) { + throw new RuntimeException("CPU throttle failed, expected : [" + + newthrottledTime + "]" + ", got : " + + "[" + throttledTime + "]"); + } + + if (newnumThrottled <= numThrottled) { + throw new RuntimeException("CPU num throttle failed, expected : [" + + newnumThrottled + "]" + ", got : " + "[" + + numThrottled + "]"); + } + System.out.println("TEST PASSED!!!"); + } + + private static void testCombo(String cpuset, long quota, long period, long shares) { + testCpuSets(cpuset); + testCpuQuotaAndPeriod(quota, period); + testCpuShares(shares); + } +} diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Arrays; +import jdk.internal.platform.Metrics; + +public class MetricsMemoryTester { + public static void main(String[] args) { + System.out.println(Arrays.toString(args)); + switch (args[0]) { + case "memory": + testMemoryLimit(args[1]); + break; + case "memoryswap": + testMemoryAndSwapLimit(args[1], args[2]); + break; + case "kernelmem": + testKernelMemoryLimit(args[1]); + break; + case "oomkill": + testOomKillFlag(Boolean.parseBoolean(args[2])); + break; + case "failcount": + testMemoryFailCount(); + break; + case "softlimit": + testMemorySoftLimit(args[1]); + break; + } + } + + private static void testMemoryLimit(String value) { + long limit = getMemoryValue(value); + + if (limit != Metrics.systemMetrics().getMemoryLimit()) { + throw new RuntimeException("Memory limit not equal, expected : [" + + limit + "]" + ", got : [" + + Metrics.systemMetrics().getMemoryLimit() + "]"); + } + System.out.println("TEST PASSED!!!"); + } + + private static void testMemoryFailCount() { + long count = Metrics.systemMetrics().getMemoryFailCount(); + + // Allocate 512M of data + long[][] longs = new long[64][]; + for (int i = 1; i <= 64; i++) { + try { + longs[i] = new long[8 * 1024 * 1024]; + } catch (Error e) { // OOM error + break; + } + } + if (Metrics.systemMetrics().getMemoryFailCount() <= count) { + throw new RuntimeException("Memory fail count : new : [" + + Metrics.systemMetrics().getMemoryFailCount() + "]" + + ", old : [" + count + "]"); + } + System.out.println("TEST PASSED!!!"); + } + + private static void testMemorySoftLimit(String softLimit) { + + long memorySoftLimit = Metrics.systemMetrics().getMemorySoftLimit(); + long newmemorySoftLimit = getMemoryValue(softLimit); + + if (newmemorySoftLimit != memorySoftLimit) { + throw new RuntimeException("Memory softlimit not equal, Actual : [" + + newmemorySoftLimit + "]" + ", Expected : [" + + memorySoftLimit + "]"); + } + System.out.println("TEST PASSED!!!"); + } + + private static void testKernelMemoryLimit(String value) { + long limit = getMemoryValue(value); + if (limit != Metrics.systemMetrics().getKernelMemoryLimit()) { + throw new RuntimeException("Kernel Memory limit not equal, expected : [" + + limit + "]" + ", got : [" + + Metrics.systemMetrics().getKernelMemoryLimit() + "]"); + } + System.out.println("TEST PASSED!!!"); + } + + private static void testMemoryAndSwapLimit(String memory, String memAndSwap) { + long expectedMem = getMemoryValue(memory); + long expectedMemAndSwap = getMemoryValue(memAndSwap); + + if (expectedMem != Metrics.systemMetrics().getMemoryLimit() + || expectedMemAndSwap != Metrics.systemMetrics().getMemoryAndSwapLimit()) { + System.err.println("Memory and swap limit not equal, expected : [" + + expectedMem + ", " + expectedMemAndSwap + "]" + + ", got : [" + Metrics.systemMetrics().getMemoryLimit() + + ", " + Metrics.systemMetrics().getMemoryAndSwapLimit() + "]"); + } + System.out.println("TEST PASSED!!!"); + } + + private static long getMemoryValue(String value) { + long result; + if (value.endsWith("m")) { + result = Long.parseLong(value.substring(0, value.length() - 1)) + * 1024 * 1024; + } else if (value.endsWith("g")) { + result = Long.parseLong(value.substring(0, value.length() - 1)) + * 1024 * 1024 * 1024; + } else { + result = Long.parseLong(value); + } + return result; + } + + private static void testOomKillFlag(boolean oomKillFlag) { + if (!(oomKillFlag ^ Metrics.systemMetrics().isMemoryOOMKillEnabled())) { + throw new RuntimeException("oomKillFlag error"); + } + System.out.println("TEST PASSED!!!"); + } +} diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import jdk.internal.platform.Metrics; +import jdk.test.lib.Utils; +import jdk.test.lib.containers.cgroup.CPUSetsReader; +import jdk.test.lib.containers.docker.Common; +import jdk.test.lib.containers.docker.DockerRunOptions; +import jdk.test.lib.containers.docker.DockerTestUtils; + +/* + * @test + * @summary Test JDK Metrics class when running inside docker container + * @requires docker.support + * @library /test/lib + * @modules java.base/jdk.internal.platform + * @build MetricsCpuTester + * @run main/timeout=360 TestDockerCpuMetrics + */ + +public class TestDockerCpuMetrics { + private static final String imageName = Common.imageName("metrics-cpu"); + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + // These tests create a docker image and run this image with + // varying docker cpu options. The arguments passed to the docker + // container include the Java test class to be run along with the + // resource to be examined and expected result. + + DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + + try { + int numCpus = CPUSetsReader.getNumCpus(); + testCpuSet("0"); + testCpuSet("0-" + (numCpus - 1)); + if (numCpus > 2) { + testCpuSet("0-" + ((numCpus - 1) / 2)); + testCpuSet((((numCpus - 1) / 2) + 1) + "-" + (numCpus - 1)); + } + testCpuSet(IntStream.range(0, numCpus).mapToObj(a -> Integer.toString(a)).collect(Collectors.joining(","))); + + testCpuQuota(50 * 1000, 100 * 1000); + testCpuQuota(100 * 1000, 100 * 1000); + testCpuQuota(150 * 1000, 100 * 1000); + testCpuQuota(400 * 1000, 100 * 1000); + + testCpuShares(256); + testCpuShares(2048); + testCpuShares(4096); + + testCpuThrottling(0.5);// --cpus= + + int[] cpuSetMems = Metrics.systemMetrics().getCpuSetMems(); + String memNodes = null; + if (cpuSetMems.length > 1) { + int endNode = (cpuSetMems[cpuSetMems.length - 1] - cpuSetMems[0]) / 2 + cpuSetMems[0]; + memNodes = cpuSetMems[0] + "-" + endNode; + } else if (cpuSetMems.length == 1) { + memNodes = cpuSetMems[0] + ""; + } + + if(memNodes != null) + testCpuSetMems(memNodes); + + testComboOptions("0-" + (numCpus - 1), 200 * 1000, 100 * 1000, 4 * 1024); + testComboOptions("0", 200 * 1000, 100 * 1000, 1023); + } finally { + DockerTestUtils.removeDockerImage(imageName); + } + } + + private static void testCpuSetMems(String value) throws Exception { + Common.logNewTestCase("testCpuSetMems, mem nodes = " + value); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--cpuset-mems=" + value); + opts.addJavaOpts("-cp", "/test-classes/").addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); + opts.addClassOptions("cpumems", value); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testCpuSet(String value) throws Exception { + Common.logNewTestCase("testCpuSet, value = " + value); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addJavaOpts("-cp", "/test-classes/"); + opts.addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); + opts.addClassOptions("cpusets", value); + opts.addDockerOpts("--cpuset-cpus=" + value); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testCpuQuota(long quota, long period) throws Exception { + Common.logNewTestCase("testCpuQuota, quota = " + quota + ", period = " + period); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--cpu-period=" + period).addDockerOpts("--cpu-quota=" + quota); + opts.addJavaOpts("-cp", "/test-classes/").addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); + opts.addClassOptions("cpuquota", quota + "", period + ""); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testCpuShares(int shares) throws Exception { + Common.logNewTestCase("testCpuShares, shares = " + shares); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--cpu-shares=" + shares); + opts.addJavaOpts("-cp", "/test-classes/").addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); + opts.addClassOptions("cpushares", shares + ""); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testCpuThrottling(double cpus) throws Exception { + Common.logNewTestCase("testCpuThrottling, cpus = " + cpus); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--cpus=" + cpus); + opts.addJavaOpts("-cp", "/test-classes/").addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); + opts.addClassOptions("cpus", cpus + ""); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testComboOptions(String cpuset, int quota, int period, int shares) throws Exception { + Common.logNewTestCase("testComboOptions, shares = " + shares); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsCpuTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addDockerOpts("--cpuset-cpus", "" + cpuset) + .addDockerOpts("--cpu-period=" + period) + .addDockerOpts("--cpu-quota=" + quota) + .addDockerOpts("--cpu-shares=" + shares); + opts.addJavaOpts("-cp", "/test-classes/").addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); + opts.addClassOptions("combo", cpuset, quota + "", period + "", shares + ""); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } +} diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.Utils; +import jdk.test.lib.containers.docker.Common; +import jdk.test.lib.containers.docker.DockerRunOptions; +import jdk.test.lib.containers.docker.DockerTestUtils; + +/* + * @test + * @summary Test JDK Metrics class when running inside docker container + * @requires docker.support + * @library /test/lib + * @modules java.base/jdk.internal.platform + * @build MetricsMemoryTester + * @run main/timeout=360 TestDockerMemoryMetrics + */ + +public class TestDockerMemoryMetrics { + private static final String imageName = Common.imageName("metrics-memory"); + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + // These tests create a docker image and run this image with + // varying docker memory options. The arguments passed to the docker + // container include the Java test class to be run along with the + // resource to be examined and expected result. + + DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + try { + testMemoryLimit("200m"); + testMemoryLimit("1g"); + + testMemoryAndSwapLimit("200m", "1g"); + testMemoryAndSwapLimit("100m", "200m"); + + testKernelMemoryLimit("100m"); + testKernelMemoryLimit("1g"); + + testOomKillFlag("100m", false); + testOomKillFlag("100m", true); + + testMemoryFailCount("20m"); + + testMemorySoftLimit("500m","200m"); + + } finally { + DockerTestUtils.removeDockerImage(imageName); + } + } + + private static void testMemoryLimit(String value) throws Exception { + Common.logNewTestCase("testMemoryLimit, value = " + value); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--memory=" + value) + .addJavaOpts("-cp", "/test-classes/") + .addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED") + .addClassOptions("memory", value); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testMemoryFailCount(String value) throws Exception { + Common.logNewTestCase("testMemoryFailCount" + value); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--memory=" + value) + .addJavaOpts("-cp", "/test-classes/") + .addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED") + .addClassOptions("failcount"); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testMemoryAndSwapLimit(String memory, String memandswap) throws Exception { + Common.logNewTestCase("testMemoryAndSwapLimit, memory = " + memory + ", memory and swap = " + memandswap); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--memory=" + memory) + .addDockerOpts("--memory-swap=" + memandswap) + .addJavaOpts("-cp", "/test-classes/") + .addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED") + .addClassOptions("memoryswap", memory, memandswap); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testKernelMemoryLimit(String value) throws Exception { + Common.logNewTestCase("testKernelMemoryLimit, value = " + value); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--kernel-memory=" + value) + .addJavaOpts("-cp", "/test-classes/") + .addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED") + .addClassOptions("kernelmem", value); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testOomKillFlag(String value, boolean oomKillFlag) throws Exception { + Common.logNewTestCase("testOomKillFlag, oomKillFlag = " + oomKillFlag); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--memory=" + value); + if (!oomKillFlag) { + opts.addDockerOpts("--oom-kill-disable"); + } + opts.addJavaOpts("-cp", "/test-classes/") + .addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED") + .addClassOptions("memory", value, oomKillFlag + ""); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } + + private static void testMemorySoftLimit(String mem, String softLimit) throws Exception { + Common.logNewTestCase("testMemorySoftLimit, memory = " + mem + ", soft limit = " + softLimit); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") + .addDockerOpts("--memory=" + mem) + .addDockerOpts("--memory-reservation=" + softLimit); + opts.addJavaOpts("-cp", "/test-classes/") + .addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED") + .addClassOptions("softlimit", softLimit); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } +} diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test JDK Metrics class when running inside docker container + * @requires docker.support + * @library /test/lib + * @modules java.base/jdk.internal.platform + * @run main TestSystemMetrics + */ + +import jdk.test.lib.Utils; +import jdk.test.lib.containers.docker.Common; +import jdk.test.lib.containers.docker.DockerRunOptions; +import jdk.test.lib.containers.docker.DockerTestUtils; +import jdk.test.lib.containers.cgroup.MetricsTester; + +public class TestSystemMetrics { + private static final String imageName = Common.imageName("metrics"); + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + + try { + Common.logNewTestCase("Test SystemMetrics"); + DockerRunOptions opts = + new DockerRunOptions(imageName, "/jdk/bin/java", "jdk.test.lib.containers.cgroup.MetricsTester"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addJavaOpts("-cp", "/test-classes/"); + opts.addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED"); + DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); + } finally { + DockerTestUtils.removeDockerImage(imageName); + } + } +} diff -r 5f20bf95c052 -r 292a4a87c321 test/jdk/tools/launcher/Settings.java --- a/test/jdk/tools/launcher/Settings.java Tue Jun 12 18:44:01 2018 -0400 +++ b/test/jdk/tools/launcher/Settings.java Tue Jun 12 18:51:45 2018 -0400 @@ -67,11 +67,15 @@ private static final String VM_SETTINGS = "VM settings:"; private static final String PROP_SETTINGS = "Property settings:"; private static final String LOCALE_SETTINGS = "Locale settings:"; + private static final String SYSTEM_SETTINGS = "Operating System Metrics:"; static void containsAllOptions(TestResult tr) { checkContains(tr, VM_SETTINGS); checkContains(tr, PROP_SETTINGS); checkContains(tr, LOCALE_SETTINGS); + if (System.getProperty("os.name").contains("Linux")) { + checkContains(tr, SYSTEM_SETTINGS); + } } static void runTestOptionDefault() throws IOException { @@ -123,6 +127,20 @@ checkContains(tr, LOCALE_SETTINGS); } + static void runTestOptionSystem() throws IOException { + TestResult tr = doExec(javaCmd, "-XshowSettings:system"); + if (System.getProperty("os.name").contains("Linux")) { + checkNotContains(tr, VM_SETTINGS); + checkNotContains(tr, PROP_SETTINGS); + checkNotContains(tr, LOCALE_SETTINGS); + checkContains(tr, SYSTEM_SETTINGS); + } else { + // -XshowSettings prints all available settings when + // settings argument is not recognized. + containsAllOptions(tr); + } + } + static void runTestBadOptions() throws IOException { TestResult tr = doExec(javaCmd, "-XshowSettingsBadOption"); checkNotContains(tr, VM_SETTINGS); @@ -146,6 +164,7 @@ runTestOptionVM(); runTestOptionProperty(); runTestOptionLocale(); + runTestOptionSystem(); runTestBadOptions(); runTest7123582(); if (testExitValue != 0) { diff -r 5f20bf95c052 -r 292a4a87c321 test/lib/jdk/test/lib/containers/cgroup/CPUSetsReader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/containers/cgroup/CPUSetsReader.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.containers.cgroup; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import jdk.test.lib.Asserts; + + +// A simple CPU sets reader and parser +public class CPUSetsReader { + public static String PROC_SELF_STATUS_PATH = "/proc/self/status"; + + // Test the parser + public static void test() { + assertParse("0-7", "0,1,2,3,4,5,6,7"); + assertParse("1,3,6", "1,3,6"); + assertParse("0,2-4,6,10-11", "0,2,3,4,6,10,11"); + assertParse("0", "0"); + } + + + private static void assertParse(String cpuSet, String expectedResult) { + Asserts.assertEquals(listToString(parseCpuSet(cpuSet)), expectedResult); + } + + public static int getNumCpus() { + String path = "/proc/cpuinfo"; + try { + Stream stream = Files.lines(Paths.get(path)); + return (int) stream.filter(line -> line.startsWith("processor")).count(); + } catch (IOException e) { + return 0; + } + } + + + public static String readFromProcStatus(String setType) { + String path = PROC_SELF_STATUS_PATH; + Optional o = Optional.empty(); + + System.out.println("readFromProcStatus() entering for: " + setType); + + try (Stream stream = Files.lines(Paths.get(path))) { + o = stream + .filter(line -> line.contains(setType)) + .findFirst(); + } catch (IOException e) { + return null; + } + + if (!o.isPresent()) { + return null; // entry not found + } + + String[] parts = o.get().replaceAll("\\s", "").split(":"); + + // Should be 2 parts, before and after ":" + Asserts.assertEquals(parts.length, 2); + + String result = parts[1]; + System.out.println("readFromProcStatus() returning: " + result); + return result; + } + + + public static List parseCpuSet(String value) { + ArrayList result = new ArrayList(); + + try { + String[] commaSeparated = value.split(","); + + for (String item : commaSeparated) { + if (item.contains("-")) { + addRange(result, item); + } else { + result.add(Integer.parseInt(item)); + } + } + } catch (Exception e) { + System.err.println("Exception in getMaxCpuSets(): " + e); + return null; + } + + return result; + } + + private static void addRange(ArrayList list, String s) { + String[] range = s.split("-"); + if (range.length != 2) { + throw new RuntimeException("Range should only contain two items, but contains " + + range.length + " items"); + } + + int min = Integer.parseInt(range[0]); + int max = Integer.parseInt(range[1]); + + if (min >= max) { + String msg = String.format("min is greater or equals to max, min = %d, max = %d", + min, max); + throw new RuntimeException(msg); + } + + for (int i = min; i <= max; i++) { + list.add(i); + } + } + + + // Convert list of integers to string with comma-separated values + public static String listToString(List list) { + return listToString(list, Integer.MAX_VALUE); + } + + // Convert list of integers to a string with comma-separated values; + // include up to maxCount. + public static String listToString(List list, int maxCount) { + return list.stream() + .limit(maxCount) + .map(Object::toString) + .collect(Collectors.joining(",")); + } + + public static String numberToString(int num) { + return IntStream.range(0, num).boxed().map(Object::toString).collect(Collectors.joining(",")); + } +} diff -r 5f20bf95c052 -r 292a4a87c321 test/lib/jdk/test/lib/containers/cgroup/MetricsTester.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/containers/cgroup/MetricsTester.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.containers.cgroup; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; +import jdk.internal.platform.Metrics; + +public class MetricsTester { + + private static final double ERROR_MARGIN = 0.1; + private static long unlimited_minimum = 0x7FFFFFFFFF000000L; + long startSysVal; + long startUserVal; + long startUsage; + long startPerCpu[]; + + enum SubSystem { + MEMORY("memory"), + CPUSET("cpuset"), + CPU("cpu"), + CPUACCT("cpuacct"), + BLKIO("blkio"); + + private String value; + + SubSystem(String value) { + this.value = value; + } + + public String value() { + return value; + } + } + + private static final Set allowedSubSystems = + Stream.of(SubSystem.values()).map(SubSystem::value).collect(Collectors.toSet()); + + private static final Map subSystemPaths = new HashMap<>(); + + private static void setPath(String[] line) { + String cgroupPath = line[2]; + String[] subSystems = line[1].split(","); + + for (String subSystem : subSystems) { + if (allowedSubSystems.contains(subSystem)) { + String[] paths = subSystemPaths.get(subSystem); + String finalPath = ""; + String root = paths[0]; + String mountPoint = paths[1]; + if (root != null && cgroupPath != null) { + if (root.equals("/")) { + if (cgroupPath.equals("/")) { + finalPath = mountPoint + cgroupPath; + } else { + finalPath = mountPoint; + } + } else { + if (root.equals(cgroupPath)) { + finalPath = mountPoint; + } else { + if (root.indexOf(cgroupPath) == 0) { + if (cgroupPath.length() > root.length()) { + String cgroupSubstr = cgroupPath.substring(root.length()); + finalPath = mountPoint + cgroupSubstr; + } + } + } + } + } + subSystemPaths.put(subSystem, new String[]{finalPath}); + } + } + } + + private static void createSubsystems(String[] line) { + if (line.length < 5) return; + Path p = Paths.get(line[4]); + String subsystemName = p.getFileName().toString(); + if (subsystemName != null) { + for (String subSystem : subsystemName.split(",")) { + if (allowedSubSystems.contains(subSystem)) { + subSystemPaths.put(subSystem, new String[]{line[3], line[4]}); + } + } + } + } + + public void setup() { + Metrics metrics = Metrics.systemMetrics(); + // Initialize CPU usage metrics before we do any testing. + startSysVal = metrics.getCpuSystemUsage(); + startUserVal = metrics.getCpuUserUsage(); + startUsage = metrics.getCpuUsage(); + startPerCpu = metrics.getPerCpuUsage(); + + try { + Stream lines = Files.lines(Paths.get("/proc/self/mountinfo")); + lines.filter(line -> line.contains(" - cgroup cgroup ")) + .map(line -> line.split(" ")) + .forEach(MetricsTester::createSubsystems); + lines.close(); + + lines = Files.lines(Paths.get("/proc/self/cgroup")); + lines.map(line -> line.split(":")) + .filter(line -> (line.length >= 3)) + .forEach(MetricsTester::setPath); + lines.close(); + } catch (IOException e) { + } + } + + private static String getFileContents(SubSystem subSystem, String fileName) { + String fname = subSystemPaths.get(subSystem.value())[0] + File.separator + fileName; + try { + return new Scanner(new File(fname)).useDelimiter("\\Z").next(); + } catch (FileNotFoundException e) { + System.err.println("Unale to open : " + fname); + return ""; + } + } + + private static long getLongValueFromFile(SubSystem subSystem, String fileName) { + String data = getFileContents(subSystem, fileName); + return data.isEmpty() ? 0L : Long.parseLong(data); + } + + private static long getLongValueFromFile(SubSystem subSystem, String metric, String subMetric) { + String stats = getFileContents(subSystem, metric); + String[] tokens = stats.split("[\\r\\n]+"); + for (int i = 0; i < tokens.length; i++) { + if (tokens[i].startsWith(subMetric)) { + return Long.parseLong(tokens[i].split("\\s+")[1]); + } + } + return 0L; + } + + private static double getDoubleValueFromFile(SubSystem subSystem, String fileName) { + String data = getFileContents(subSystem, fileName); + return data.isEmpty() ? 0.0 : Double.parseDouble(data); + } + + private boolean compareWithErrorMargin(long oldVal, long newVal) { + return Math.abs(oldVal - newVal) <= Math.abs(oldVal * ERROR_MARGIN); + } + + private boolean compareWithErrorMargin(double oldVal, double newVal) { + return Math.abs(oldVal - newVal) <= Math.abs(oldVal * ERROR_MARGIN); + } + + private static void fail(SubSystem system, String metric, long oldVal, long testVal) { + throw new RuntimeException("Test failed for - " + system.value + ":" + + metric + ", expected [" + oldVal + "], got [" + testVal + "]"); + } + + private static void fail(SubSystem system, String metric, String oldVal, String testVal) { + throw new RuntimeException("Test failed for - " + system.value + ":" + + metric + ", expected [" + oldVal + "], got [" + testVal + "]"); + } + + private static void fail(SubSystem system, String metric, double oldVal, double testVal) { + throw new RuntimeException("Test failed for - " + system.value + ":" + + metric + ", expected [" + oldVal + "], got [" + testVal + "]"); + } + + private static void fail(SubSystem system, String metric, boolean oldVal, boolean testVal) { + throw new RuntimeException("Test failed for - " + system.value + ":" + + metric + ", expected [" + oldVal + "], got [" + testVal + "]"); + } + + private static void warn(SubSystem system, String metric, long oldVal, long testVal) { + System.err.println("Warning - " + system.value + ":" + metric + + ", expected [" + oldVal + "], got [" + testVal + "]"); + } + + public void testMemorySubsystem() { + Metrics metrics = Metrics.systemMetrics(); + + // User Memory + long oldVal = metrics.getMemoryFailCount(); + long newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.failcnt"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.failcnt", oldVal, newVal); + } + + oldVal = metrics.getMemoryLimit(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.limit_in_bytes"); + newVal = newVal > unlimited_minimum ? -1L : newVal; + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.limit_in_bytes", oldVal, newVal); + } + + oldVal = metrics.getMemoryMaxUsage(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.max_usage_in_bytes"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.max_usage_in_bytes", oldVal, newVal); + } + + oldVal = metrics.getMemoryUsage(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.usage_in_bytes"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.usage_in_bytes", oldVal, newVal); + } + + // Kernel memory + oldVal = metrics.getKernelMemoryFailCount(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.failcnt"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.kmem.failcnt", oldVal, newVal); + } + + oldVal = metrics.getKernelMemoryLimit(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.limit_in_bytes"); + newVal = newVal > unlimited_minimum ? -1L : newVal; + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.kmem.limit_in_bytes", oldVal, newVal); + } + + oldVal = metrics.getKernelMemoryMaxUsage(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.max_usage_in_bytes"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.kmem.max_usage_in_bytes", oldVal, newVal); + } + + oldVal = metrics.getKernelMemoryUsage(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.usage_in_bytes"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.kmem.usage_in_bytes", oldVal, newVal); + } + + //TCP Memory + oldVal = metrics.getTcpMemoryFailCount(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.tcp.failcnt"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.kmem.tcp.failcnt", oldVal, newVal); + } + + oldVal = metrics.getTcpMemoryLimit(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.tcp.limit_in_bytes"); + newVal = newVal > unlimited_minimum ? -1L : newVal; + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.kmem.tcp.limit_in_bytes", oldVal, newVal); + } + + oldVal = metrics.getTcpMemoryMaxUsage(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.tcp.max_usage_in_bytes"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.kmem.tcp.max_usage_in_bytes", oldVal, newVal); + } + + oldVal = metrics.getTcpMemoryUsage(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.tcp.usage_in_bytes"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.kmem.tcp.usage_in_bytes", oldVal, newVal); + } + + // Memory and Swap + oldVal = metrics.getMemoryAndSwapFailCount(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.memsw.failcnt"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.memsw.failcnt", oldVal, newVal); + } + + oldVal = metrics.getMemoryAndSwapLimit(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.memsw.limit_in_bytes"); + newVal = newVal > unlimited_minimum ? -1L : newVal; + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.memsw.limit_in_bytes", oldVal, newVal); + } + + oldVal = metrics.getMemoryAndSwapMaxUsage(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.memsw.max_usage_in_bytes"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.memsw.max_usage_in_bytes", oldVal, newVal); + } + + oldVal = metrics.getMemoryAndSwapUsage(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.memsw.usage_in_bytes"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.memsw.usage_in_bytes", oldVal, newVal); + } + + oldVal = metrics.getMemorySoftLimit(); + newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.soft_limit_in_bytes"); + newVal = newVal > unlimited_minimum ? -1L : newVal; + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.MEMORY, "memory.soft_limit_in_bytes", oldVal, newVal); + } + + boolean oomKillEnabled = metrics.isMemoryOOMKillEnabled(); + boolean newOomKillEnabled = getLongValueFromFile(SubSystem.MEMORY, + "memory.oom_control", "oom_kill_disable") == 0L ? true : false; + if (oomKillEnabled != newOomKillEnabled) { + throw new RuntimeException("Test failed for - " + SubSystem.MEMORY.value + ":" + + "memory.oom_control:oom_kill_disable" + ", expected [" + + oomKillEnabled + "], got [" + newOomKillEnabled + "]"); + } + } + + public void testCpuAccounting() { + Metrics metrics = Metrics.systemMetrics(); + long oldVal = metrics.getCpuUsage(); + long newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpuacct.usage"); + + if (!compareWithErrorMargin(oldVal, newVal)) { + warn(SubSystem.CPUACCT, "cpuacct.usage", oldVal, newVal); + } + + Long[] newVals = Stream.of(getFileContents(SubSystem.CPUACCT, "cpuacct.usage_percpu") + .split("\\s+")) + .map(Long::parseLong) + .toArray(Long[]::new); + Long[] oldVals = LongStream.of(metrics.getPerCpuUsage()).boxed().toArray(Long[]::new); + for (int i = 0; i < oldVals.length; i++) { + if (!compareWithErrorMargin(oldVals[i], newVals[i])) { + warn(SubSystem.CPUACCT, "cpuacct.usage_percpu", oldVals[i], newVals[i]); + } + } + + oldVal = metrics.getCpuUserUsage(); + newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpuacct.stat", "user"); + if (!compareWithErrorMargin(oldVal, newVal)) { + warn(SubSystem.CPUACCT, "cpuacct.usage - user", oldVal, newVal); + } + + oldVal = metrics.getCpuSystemUsage(); + newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpuacct.stat", "system"); + if (!compareWithErrorMargin(oldVal, newVal)) { + warn(SubSystem.CPUACCT, "cpuacct.usage - system", oldVal, newVal); + } + } + + public void testCpuSchedulingMetrics() { + Metrics metrics = Metrics.systemMetrics(); + long oldVal = metrics.getCpuPeriod(); + long newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.cfs_period_us"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.CPUACCT, "cpu.cfs_period_us", oldVal, newVal); + } + + oldVal = metrics.getCpuQuota(); + newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.cfs_quota_us"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.CPUACCT, "cpu.cfs_quota_us", oldVal, newVal); + } + + oldVal = metrics.getCpuShares(); + newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.shares"); + if (newVal == 0 || newVal == 1024) newVal = -1; + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.CPUACCT, "cpu.shares", oldVal, newVal); + } + + oldVal = metrics.getCpuNumPeriods(); + newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.stat", "nr_periods"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.CPUACCT, "cpu.stat - nr_periods", oldVal, newVal); + } + + oldVal = metrics.getCpuNumThrottled(); + newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.stat", "nr_throttled"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.CPUACCT, "cpu.stat - nr_throttled", oldVal, newVal); + } + + oldVal = metrics.getCpuThrottledTime(); + newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.stat", "throttled_time"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.CPUACCT, "cpu.stat - throttled_time", oldVal, newVal); + } + } + + public void testCpuSets() { + Metrics metrics = Metrics.systemMetrics(); + Integer[] oldVal = Arrays.stream(metrics.getCpuSetCpus()).boxed().toArray(Integer[]::new); + Arrays.sort(oldVal); + + String cpusstr = getFileContents(SubSystem.CPUSET, "cpuset.cpus"); + // Parse range string in the format 1,2-6,7 + Integer[] newVal = Stream.of(cpusstr.split(",")).flatMap(a -> { + if (a.contains("-")) { + String[] range = a.split("-"); + return IntStream.rangeClosed(Integer.parseInt(range[0]), + Integer.parseInt(range[1])).boxed(); + } else { + return Stream.of(Integer.parseInt(a)); + } + }).toArray(Integer[]::new); + Arrays.sort(newVal); + if (Arrays.compare(oldVal, newVal) != 0) { + fail(SubSystem.CPUSET, "cpuset.cpus", Arrays.toString(oldVal), + Arrays.toString(newVal)); + } + + + oldVal = Arrays.stream(metrics.getEffectiveCpuSetCpus()).boxed().toArray(Integer[]::new); + Arrays.sort(oldVal); + + cpusstr = getFileContents(SubSystem.CPUSET, "cpuset.effective_cpus"); + newVal = Stream.of(cpusstr.split(",")).flatMap(a -> { + if (a.contains("-")) { + String[] range = a.split("-"); + return IntStream.rangeClosed(Integer.parseInt(range[0]), + Integer.parseInt(range[1])).boxed(); + } else { + return Stream.of(Integer.parseInt(a)); + } + }).toArray(Integer[]::new); + Arrays.sort(newVal); + if (Arrays.compare(oldVal, newVal) != 0) { + fail(SubSystem.CPUSET, "cpuset.effective_cpus", Arrays.toString(oldVal), + Arrays.toString(newVal)); + } + + oldVal = Arrays.stream(metrics.getCpuSetMems()).boxed().toArray(Integer[]::new); + Arrays.sort(oldVal); + cpusstr = getFileContents(SubSystem.CPUSET, "cpuset.mems"); + newVal = Stream.of(cpusstr.split(",")).flatMap(a -> { + if (a.contains("-")) { + String[] range = a.split("-"); + return IntStream.rangeClosed(Integer.parseInt(range[0]), + Integer.parseInt(range[1])).boxed(); + } else { + return Stream.of(Integer.parseInt(a)); + } + }).toArray(Integer[]::new); + Arrays.sort(newVal); + if (Arrays.compare(oldVal, newVal) != 0) { + fail(SubSystem.CPUSET, "cpuset.mems", Arrays.toString(oldVal), + Arrays.toString(newVal)); + } + + oldVal = Arrays.stream(metrics.getEffectiveCpuSetMems()).boxed().toArray(Integer[]::new); + Arrays.sort(oldVal); + cpusstr = getFileContents(SubSystem.CPUSET, "cpuset.effective_mems"); + newVal = Stream.of(cpusstr.split(",")).flatMap(a -> { + if (a.contains("-")) { + String[] range = a.split("-"); + return IntStream.rangeClosed(Integer.parseInt(range[0]), + Integer.parseInt(range[1])).boxed(); + } else { + return Stream.of(Integer.parseInt(a)); + } + }).toArray(Integer[]::new); + Arrays.sort(newVal); + if (Arrays.compare(oldVal, newVal) != 0) { + fail(SubSystem.CPUSET, "cpuset.effective_mems", Arrays.toString(oldVal), + Arrays.toString(newVal)); + } + + double oldValue = metrics.getCpuSetMemoryPressure(); + double newValue = getDoubleValueFromFile(SubSystem.CPUSET, "cpuset.memory_pressure"); + if (!compareWithErrorMargin(oldValue, newValue)) { + fail(SubSystem.CPUSET, "cpuset.memory_pressure", oldValue, newValue); + } + + boolean oldV = metrics.isCpuSetMemoryPressureEnabled(); + boolean newV = getLongValueFromFile(SubSystem.CPUSET, + "cpuset.memory_pressure_enabled") == 1 ? true : false; + if (oldV != newV) { + fail(SubSystem.CPUSET, "cpuset.memory_pressure_enabled", oldV, newV); + } + } + + public void testBlkIO() { + Metrics metrics = Metrics.systemMetrics(); + long oldVal = metrics.getBlkIOServiceCount(); + long newVal = getLongValueFromFile(SubSystem.BLKIO, + "blkio.throttle.io_service_bytes", "Total"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.BLKIO, "blkio.throttle.io_service_bytes - Total", + oldVal, newVal); + } + + oldVal = metrics.getBlkIOServiced(); + newVal = getLongValueFromFile(SubSystem.BLKIO, "blkio.throttle.io_serviced", "Total"); + if (!compareWithErrorMargin(oldVal, newVal)) { + fail(SubSystem.BLKIO, "blkio.throttle.io_serviced - Total", oldVal, newVal); + } + } + + public void testCpuConsumption() throws IOException, InterruptedException { + Metrics metrics = Metrics.systemMetrics(); + // make system call + long newSysVal = metrics.getCpuSystemUsage(); + long newUserVal = metrics.getCpuUserUsage(); + long newUsage = metrics.getCpuUsage(); + long[] newPerCpu = metrics.getPerCpuUsage(); + + if (newSysVal <= startSysVal) { + fail(SubSystem.CPU, "getCpuSystemUsage", newSysVal, startSysVal); + } + + if (newUserVal <= startUserVal) { + fail(SubSystem.CPU, "getCpuUserUsage", newUserVal, startUserVal); + } + + if (newUsage <= startUsage) { + fail(SubSystem.CPU, "getCpuUserUsage", newUsage, startUsage); + } + + boolean success = false; + for (int i = 0; i < startPerCpu.length; i++) { + if (newPerCpu[i] > startPerCpu[i]) { + success = true; + break; + } + } + + if(!success) fail(SubSystem.CPU, "getPerCpuUsage", Arrays.toString(newPerCpu), + Arrays.toString(startPerCpu)); + } + + public void testMemoryUsage() throws Exception { + Metrics metrics = Metrics.systemMetrics(); + long memoryMaxUsage = metrics.getMemoryMaxUsage(); + long memoryUsage = metrics.getMemoryUsage(); + + long[] ll = new long[64*1024*1024]; // 64M + + long newMemoryMaxUsage = metrics.getMemoryMaxUsage(); + long newMemoryUsage = metrics.getMemoryUsage(); + + if(newMemoryMaxUsage < memoryMaxUsage) { + fail(SubSystem.MEMORY, "getMemoryMaxUsage", newMemoryMaxUsage, + memoryMaxUsage); + } + + if(newMemoryUsage < memoryUsage) { + fail(SubSystem.MEMORY, "getMemoryUsage", newMemoryUsage, memoryUsage); + } + } + + public static void main(String[] args) throws Exception { + // If cgroups is not configured, report success + Metrics metrics = Metrics.systemMetrics(); + if (metrics == null) { + System.out.println("TEST PASSED!!!"); + return; + } + + MetricsTester metricsTester = new MetricsTester(); + metricsTester.setup(); + metricsTester.testCpuAccounting(); + metricsTester.testCpuSchedulingMetrics(); + metricsTester.testCpuSets(); + metricsTester.testMemorySubsystem(); + metricsTester.testBlkIO(); + metricsTester.testCpuConsumption(); + metricsTester.testMemoryUsage(); + System.out.println("TEST PASSED!!!"); + } +} diff -r 5f20bf95c052 -r 292a4a87c321 test/lib/jdk/test/lib/containers/docker/Common.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/containers/docker/Common.java Tue Jun 12 18:51:45 2018 -0400 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.containers.docker; + +/* + * Methods and definitions common to docker tests container in this directory + */ + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import jdk.test.lib.containers.docker.DockerRunOptions; +import jdk.test.lib.containers.docker.DockerTestUtils; +import jdk.test.lib.Utils; +import jdk.test.lib.process.OutputAnalyzer; + + +public class Common { + public static final String imageNameAndTag = "jdk-internal:test"; + + public static String imageName(String suffix) { + return imageNameAndTag + "-" + suffix; + } + + + public static void prepareWhiteBox() throws Exception { + Files.copy(Paths.get(new File("whitebox.jar").getAbsolutePath()), + Paths.get(Utils.TEST_CLASSES, "whitebox.jar")); + } + + + // create simple commonly used options + public static DockerRunOptions newOpts(String imageNameAndTag) { + return new DockerRunOptions(imageNameAndTag, "/jdk/bin/java", "-version") + .addJavaOpts("-Xlog:os+container=trace"); + } + + + // create commonly used options with class to be launched inside container + public static DockerRunOptions newOpts(String imageNameAndTag, String testClass) { + DockerRunOptions opts = + new DockerRunOptions(imageNameAndTag, "/jdk/bin/java", testClass); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addJavaOpts("-Xlog:os+container=trace", "-cp", "/test-classes/"); + return opts; + } + + + public static DockerRunOptions addWhiteBoxOpts(DockerRunOptions opts) { + opts.addJavaOpts("-Xbootclasspath/a:/test-classes/whitebox.jar", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI"); + return opts; + } + + + // most common type of run and checks + public static OutputAnalyzer run(DockerRunOptions opts) throws Exception { + return DockerTestUtils.dockerRunJava(opts) + .shouldHaveExitValue(0).shouldContain("Initializing Container Support"); + } + + + // log beginning of a test case + public static void logNewTestCase(String msg) { + System.out.println("========== NEW TEST CASE: " + msg); + } + +} diff -r 5f20bf95c052 -r 292a4a87c321 test/lib/jdk/test/lib/containers/docker/DockerRunOptions.java --- a/test/lib/jdk/test/lib/containers/docker/DockerRunOptions.java Tue Jun 12 18:44:01 2018 -0400 +++ b/test/lib/jdk/test/lib/containers/docker/DockerRunOptions.java Tue Jun 12 18:51:45 2018 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,6 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.test.lib.containers.docker; import java.util.ArrayList; @@ -69,4 +70,8 @@ return this; } + public DockerRunOptions addClassOptions(String... opts) { + Collections.addAll(classParams,opts); + return this; + } } diff -r 5f20bf95c052 -r 292a4a87c321 test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java --- a/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java Tue Jun 12 18:44:01 2018 -0400 +++ b/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java Tue Jun 12 18:51:45 2018 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,6 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.test.lib.containers.docker; import java.io.File;