Mercurial > people > andrew > aarch32 > hotspot
changeset 9061:f38b47a322eb jdk8u191-b12-aarch32-181022
Merge
author | snazarki |
---|---|
date | Thu, 25 Oct 2018 12:52:11 +0300 |
parents | fc4c240b74a7 (current diff) 4fc288749a23 (diff) |
children | 3fe503c1d233 |
files | .hgtags make/linux/makefiles/gcc.make src/os/linux/vm/os_linux.cpp src/os/linux/vm/os_linux.hpp src/share/vm/c1/c1_Runtime1.cpp src/share/vm/opto/runtime.cpp src/share/vm/runtime/globals.hpp src/share/vm/runtime/os.hpp src/share/vm/runtime/sharedRuntime.cpp |
diffstat | 46 files changed, 2914 insertions(+), 234 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Thu Aug 02 15:15:43 2018 +0300 +++ b/.hgtags Thu Oct 25 12:52:11 2018 +0300 @@ -1147,6 +1147,13 @@ 2dc629cf3eccfc0c8af9fbb53e7d60b1a91d0ef2 jdk8u162-b12-aarch32-180220 69aec2ca5d905dde1d0f29a89076d02a531808a3 jdk8u162-b12 caac74fe3cfa9a8c859c28c97d1046a58252af27 jdk8u162-b31 +c9b7abadf150328d2187de05b9e8a9cba2486e47 jdk8u162-b32 +e8041f2ec96eb6a41307732e6cf6ed90901438ae jdk8u162-b33 +bf2e8b1e8e8e6bc1f9b9475de54ba0329a6b24b1 jdk8u162-b34 +9b3f207379cf6ecfb8603640269e31ff4e064294 jdk8u162-b35 +d2ebd6530396b0afc700cd1a8eaf1f7a7f9fce8d jdk8u162-b36 +700ad8745f3fdc5ba3702616fc5ed6a6248dfa78 jdk8u162-b37 +405800ccc4c7b81475b01392f2145cc3675d1f86 jdk8u162-b38 a17bab9405474602b18cd62e060a09b17d6413ac jdk8u171-b00 ebfd57cc21e6b7f0c22b17c666b6b28c9340e207 jdk8u171-b01 1acd7c1b80241def8fac90f70b0df16356adad47 jdk8u171-b02 @@ -1166,6 +1173,8 @@ 653d9e0cd3f4023675c9eece7f0d563287f1d34f jdk8u172-b02 771d9e1fbe1ae2ec4d5d937ebcbfd18e9c800098 jdk8u172-b03 efd7a4e211e8fddf52053d4b033d8d307f356bc3 jdk8u172-b04 +5587cde50bbc2aa031aefb47eaa36b041f5e7c4b jdk8u181-b00 +8f3131e04030eb35434a27c1a0866fc3651af6d5 jdk8u191-b00 4235fb1dceebde1192498ef388a32e56b1ed5a46 jdk8u172-b05 68b234d5df6f01f3c677a114ecd6878c25f23f3c jdk8u172-b06 a311a45523b19d59f77e76b0441a2085bb5355c8 jdk8u172-b07 @@ -1177,6 +1186,16 @@ 6e2be123a2e1c7671086c767e79ffe8ad5d4f9ca jdk8u181-b01 1d0b6fcff115a57ca02081da84589630ba282789 jdk8u181-b02 1127faef22f14d56cdd6c0c8bded598f492c2611 jdk8u181-b03 +d5a33d109309138a1e9bed43d2a2bda04356dbac jdk8u172-b31 +b62c44a689e4d339b1129bffceee94119c84b1b2 jdk8u172-b32 +e8745ad08d55bb56b2ac5a70ec0a972c38fa6ca2 jdk8u172-b33 +74350ee9c013a39acb6af32049599a26e6dc3911 jdk8u172-b34 +0d1b5f9b3ab040eb9023cde206cd67d4b5a54535 jdk8u172-b35 +1e7855b1ecd3d069bcaaf35259d35f79a7c66987 jdk8u172-b36 +6a9482b43d79e3e017f58a23ec4574dd696e04db jdk8u172-b37 +6e2be123a2e1c7671086c767e79ffe8ad5d4f9ca jdk8u181-b01 +1d0b6fcff115a57ca02081da84589630ba282789 jdk8u181-b02 +1127faef22f14d56cdd6c0c8bded598f492c2611 jdk8u181-b03 c53e56e8904926f4efdde8a0483109ed3c60c064 jdk8u181-b04 cea033df30750958ffc999d647ee43587d4a06b2 jdk8u181-b05 2198f5b865507118b644830293dc9f0ec3b4439e jdk8u181-b06 @@ -1186,5 +1205,17 @@ 08b5e1f3d022fbe122b11b6f45f62b5a4ce92ed9 jdk8u181-b10 e4f39d283b55faf6074308797615298bd1a45a66 jdk8u181-b11 464ed8cea5d6cdbfacc9be7035297af88f57f708 jdk8u181-b12 -9062a259cecfe8e1f3386e2982eb77bd117c81e1 jdk8u181-b31 891d70e93fb068360713bdb562151f8f253d1b92 jdk8u181-b13-aarch32-180802 +eed8e846c982d7474dd07fc873ba02f83ad1f847 jdk8u181-b13 +21a3fffc43418f4d75c2091bf03478330b8a9a98 jdk8u191-b01 +5aa3d728164a674d08ad847811be6bdd853e9bf8 jdk8u191-b02 +dd79b482625361458b2b34e7d669ee982eee06a4 jdk8u191-b03 +541c205d7fd15ab840f48aaeeaea3f63209d1687 jdk8u191-b04 +14c62eae2f8f56f571abfc8435055bb6094c8440 jdk8u191-b05 +6cfec782c42c25f772bfd51a8b47e6926aa8f69f jdk8u191-b06 +96be5f6ab83349c971edd9aeb35cafce267d3bf8 jdk8u191-b07 +113b4a1676db205922668f5b9c69b3dce22a095e jdk8u191-b08 +a339c1437badce44c7137da58d817159c9c80e4f jdk8u191-b09 +c0bd247ecd1cd09a129040e3fa1745c64db43b35 jdk8u191-b10 +055e1c867479452dd5d8c08905f7475aab25fcc1 jdk8u191-b25 +12e4de4b2499e9d9a1ae4fb0b63ca277cca150f4 jdk8u191-b11
--- a/THIRD_PARTY_README Thu Aug 02 15:15:43 2018 +0300 +++ b/THIRD_PARTY_README Thu Oct 25 12:52:11 2018 +0300 @@ -1497,7 +1497,7 @@ ------------------------------------------------------------------------------- -%% This notice is provided with respect to libpng 1.6.16, which may be +%% This notice is provided with respect to libpng 1.6.35, which may be included with JRE 8, JDK 8, and OpenJDK 8. --- begin of LICENSE --- @@ -1513,21 +1513,21 @@ This code is released under the libpng license. -libpng versions 1.2.6, August 15, 2004, through 1.6.16, December 22, 2014, are -Copyright (c) 2004, 2006-2014 Glenn Randers-Pehrson, and are -distributed according to the same disclaimer and license as libpng-1.2.5 -with the following individual added to the list of Contributing Authors - - Cosmin Truta - -libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are -Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are -distributed according to the same disclaimer and license as libpng-1.0.6 -with the following individuals added to the list of Contributing Authors +libpng versions 1.0.7, July 1, 2000 through 1.6.35, July 15, 2018 are +Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are +derived from libpng-1.0.6, and are distributed according to the same +disclaimer and license as libpng-1.0.6 with the following individuals +added to the list of Contributing Authors: Simon-Pierre Cadieux Eric S. Raymond + Mans Rullgard + Cosmin Truta Gilles Vollant + James Yu + Mandar Sahastrabuddhe + Google Inc. + Vadim Barkov and with the following additions to the disclaimer: @@ -1538,19 +1538,25 @@ risk of satisfactory quality, performance, accuracy, and effort is with the user. +Some files in the "contrib" directory and some configure-generated +files that are distributed with libpng have other copyright owners and +are released under other open source licenses. + libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are -Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are -distributed according to the same disclaimer and license as libpng-0.96, -with the following individuals added to the list of Contributing Authors: +Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from +libpng-0.96, and are distributed according to the same disclaimer and +license as libpng-0.96, with the following individuals added to the list +of Contributing Authors: Tom Lane Glenn Randers-Pehrson Willem van Schaik libpng versions 0.89, June 1996, through 0.96, May 1997, are -Copyright (c) 1996, 1997 Andreas Dilger -Distributed according to the same disclaimer and license as libpng-0.88, -with the following individuals added to the list of Contributing Authors: +Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, +and are distributed according to the same disclaimer and license as +libpng-0.88, with the following individuals added to the list of +Contributing Authors: John Bowler Kevin Bracey @@ -1559,8 +1565,11 @@ Greg Roelofs Tom Tanner +Some files in the "scripts" directory have other copyright owners +but are released under this license. + libpng versions 0.5, May 1995, through 0.88, January 1996, are -Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. +Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. For the purposes of this copyright and license, "Contributing Authors" is defined as the following set of individuals: @@ -1583,13 +1592,13 @@ source code, or portions hereof, for any purpose, without fee, subject to the following restrictions: -1. The origin of this source code must not be misrepresented. - -2. Altered versions must be plainly marked as such and must not - be misrepresented as being the original source. - -3. This Copyright notice may not be removed or altered from any - source or altered source distribution. + 1. The origin of this source code must not be misrepresented. + + 2. Altered versions must be plainly marked as such and must not + be misrepresented as being the original source. + + 3. This Copyright notice may not be removed or altered from any + source or altered source distribution. The Contributing Authors and Group 42, Inc. specifically permit, without fee, and encourage the use of this source code as a component to @@ -1597,21 +1606,34 @@ source code in a product, acknowledgment is not required but would be appreciated. - -A "png_get_copyright" function is available, for convenient use in "about" -boxes and the like: - - printf("%s",png_get_copyright(NULL)); - -Also, the PNG logo (in PNG format, of course) is supplied in the -files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). - -Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a -certification mark of the Open Source Initiative. +END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + +TRADEMARK: + +The name "libpng" has not been registered by the Copyright owner +as a trademark in any jurisdiction. However, because libpng has +been distributed and maintained world-wide, continually since 1995, +the Copyright owner claims "common-law trademark protection" in any +jurisdiction where common-law trademark is recognized. + +OSI CERTIFICATION: + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is +a certification mark of the Open Source Initiative. OSI has not addressed +the additional disclaimers inserted at version 1.0.7. + +EXPORT CONTROL: + +The Copyright owner believes that the Export Control Classification +Number (ECCN) for libpng is EAR99, which means not subject to export +controls or International Traffic in Arms Regulations (ITAR) because +it is open source, publicly available software, that does not contain +any encryption software. See the EAR, paragraphs 734.3(b)(3) and +734.7(b). Glenn Randers-Pehrson glennrp at users.sourceforge.net -December 22, 2014 +July 15, 2018 --- end of LICENSE ---
--- a/make/linux/makefiles/gcc.make Thu Aug 02 15:15:43 2018 +0300 +++ b/make/linux/makefiles/gcc.make Thu Oct 25 12:52:11 2018 +0300 @@ -332,53 +332,20 @@ ifeq ($(DEBUG_BINARIES), true) CFLAGS += -g else - # Use the stabs format for debugging information (this is the default - # on gcc-2.91). It's good enough, has all the information about line - # numbers and local variables, and libjvm.so is only about 16M. - # Change this back to "-g" if you want the most expressive format. - # (warning: that could easily inflate libjvm.so to 150M!) - # Note: The Itanium gcc compiler crashes when using -gstabs. - DEBUG_CFLAGS/ia64 = -g - DEBUG_CFLAGS/amd64 = -g - DEBUG_CFLAGS/ppc64 = -g - DEBUG_CFLAGS/aarch32 = -g DEBUG_CFLAGS += $(DEBUG_CFLAGS/$(BUILDARCH)) ifeq ($(DEBUG_CFLAGS/$(BUILDARCH)),) - ifeq ($(USE_CLANG), true) - # Clang doesn't understand -gstabs - DEBUG_CFLAGS/$(BUILDARCH) = -g - else - DEBUG_CFLAGS/$(BUILDARCH) = -gstabs - endif + DEBUG_CFLAGS += -g endif ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - FASTDEBUG_CFLAGS/ia64 = -g - FASTDEBUG_CFLAGS/amd64 = -g - FASTDEBUG_CFLAGS/ppc64 = -g - FASTDEBUG_CFLAGS/aarch32 = -g FASTDEBUG_CFLAGS += $(FASTDEBUG_CFLAGS/$(BUILDARCH)) ifeq ($(FASTDEBUG_CFLAGS/$(BUILDARCH)),) - ifeq ($(USE_CLANG), true) - # Clang doesn't understand -gstabs - FASTDEBUG_CFLAGS/$(BUILDARCH) = -g - else - FASTDEBUG_CFLAGS/$(BUILDARCH) = -gstabs - endif + FASTDEBUG_CFLAGS/$(BUILDARCH) = -g endif - OPT_CFLAGS/ia64 = -g - OPT_CFLAGS/amd64 = -g - OPT_CFLAGS/ppc64 = -g - OPT_CFLAGS/aarch32 = -g OPT_CFLAGS += $(OPT_CFLAGS/$(BUILDARCH)) ifeq ($(OPT_CFLAGS/$(BUILDARCH)),) - ifeq ($(USE_CLANG), true) - # Clang doesn't understand -gstabs - OPT_CFLAGS/$(BUILDARCH) = -g - else - OPT_CFLAGS/$(BUILDARCH) = -gstabs - endif + OPT_CFLAGS/$(BUILDARCH) = -g endif endif endif
--- a/src/os/aix/vm/os_aix.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/os/aix/vm/os_aix.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -4008,6 +4008,16 @@ }; int os::active_processor_count() { + // User has overridden the number of active processors + if (ActiveProcessorCount > 0) { + if (PrintActiveCpus) { + tty->print_cr("active_processor_count: " + "active processor count set by user : %d", + ActiveProcessorCount); + } + return ActiveProcessorCount; + } + int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); return online_cpus;
--- a/src/os/bsd/vm/os_bsd.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/os/bsd/vm/os_bsd.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -3765,6 +3765,16 @@ }; int os::active_processor_count() { + // User has overridden the number of active processors + if (ActiveProcessorCount > 0) { + if (PrintActiveCpus) { + tty->print_cr("active_processor_count: " + "active processor count set by user : %d", + ActiveProcessorCount); + } + return ActiveProcessorCount; + } + return _processor_count; }
--- a/src/os/linux/vm/globals_linux.hpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/os/linux/vm/globals_linux.hpp Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -49,8 +49,13 @@ product(bool, UseSHM, false, \ "Use SYSV shared memory for large pages") \ \ - diagnostic(bool, PrintActiveCpus, false, \ - "Print the number of CPUs detected in os::active_processor_count") + product(bool, UseContainerSupport, true, \ + "Enable detection and runtime container configuration support") \ + \ + product(bool, PreferContainerQuotaForCPUCount, true, \ + "Calculate the container CPU availability based on the value" \ + " of quotas (if set), when true. Otherwise, use the CPU" \ + " shares value, provided it is less than quota.") // // Defines Linux-specific default values. The flags are available on all
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/linux/vm/osContainer_linux.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,680 @@ +/* + * 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. + * + */ + +#include <string.h> +#include <math.h> +#include <errno.h> +#include "utilities/globalDefinitions.hpp" +#include "memory/allocation.hpp" +#include "runtime/os.hpp" +#include "osContainer_linux.hpp" + +#define PER_CPU_SHARES 1024 + +bool OSContainer::_is_initialized = false; +bool OSContainer::_is_containerized = false; +julong _unlimited_memory; + +class CgroupSubsystem: CHeapObj<mtInternal> { + friend class OSContainer; + + private: + /* mountinfo contents */ + char *_root; + char *_mount_point; + + /* Constructed subsystem directory */ + char *_path; + + public: + CgroupSubsystem(char *root, char *mountpoint) { + _root = os::strdup(root); + _mount_point = os::strdup(mountpoint); + _path = NULL; + } + + /* + * Set directory to subsystem specific files based + * on the contents of the mountinfo and cgroup files. + */ + void set_subsystem_path(char *cgroup_path) { + char buf[MAXPATHLEN+1]; + if (_root != NULL && cgroup_path != NULL) { + if (strcmp(_root, "/") == 0) { + int buflen; + strncpy(buf, _mount_point, MAXPATHLEN); + buf[MAXPATHLEN-1] = '\0'; + if (strcmp(cgroup_path,"/") != 0) { + buflen = strlen(buf); + if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) { + return; + } + strncat(buf, cgroup_path, MAXPATHLEN-buflen); + buf[MAXPATHLEN-1] = '\0'; + } + _path = os::strdup(buf); + } else { + if (strcmp(_root, cgroup_path) == 0) { + strncpy(buf, _mount_point, MAXPATHLEN); + buf[MAXPATHLEN-1] = '\0'; + _path = os::strdup(buf); + } else { + char *p = strstr(_root, cgroup_path); + if (p != NULL && p == _root) { + if (strlen(cgroup_path) > strlen(_root)) { + int buflen; + strncpy(buf, _mount_point, MAXPATHLEN); + buf[MAXPATHLEN-1] = '\0'; + buflen = strlen(buf); + if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) { + return; + } + strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen); + buf[MAXPATHLEN-1] = '\0'; + _path = os::strdup(buf); + } + } + } + } + } + } + + char *subsystem_path() { return _path; } +}; + +CgroupSubsystem* memory = NULL; +CgroupSubsystem* cpuset = NULL; +CgroupSubsystem* cpu = NULL; +CgroupSubsystem* cpuacct = NULL; + +typedef char * cptr; + +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED +template <typename T> int subsystem_file_contents(CgroupSubsystem* c, + const char *filename, + const char *scan_fmt, + T returnval) { + FILE *fp = NULL; + char *p; + char file[MAXPATHLEN+1]; + char buf[MAXPATHLEN+1]; + + if (c == NULL) { + if (PrintContainerInfo) { + tty->print_cr("subsystem_file_contents: CgroupSubsytem* is NULL"); + } + return OSCONTAINER_ERROR; + } + if (c->subsystem_path() == NULL) { + if (PrintContainerInfo) { + tty->print_cr("subsystem_file_contents: subsystem path is NULL"); + } + return OSCONTAINER_ERROR; + } + + strncpy(file, c->subsystem_path(), MAXPATHLEN); + file[MAXPATHLEN-1] = '\0'; + int filelen = strlen(file); + if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) { + if (PrintContainerInfo) { + tty->print_cr("File path too long %s, %s", file, filename); + } + return OSCONTAINER_ERROR; + } + strncat(file, filename, MAXPATHLEN-filelen); + if (PrintContainerInfo) { + tty->print_cr("Path to %s is %s", filename, file); + } + fp = fopen(file, "r"); + if (fp != NULL) { + p = fgets(buf, MAXPATHLEN, fp); + if (p != NULL) { + int matched = sscanf(p, scan_fmt, returnval); + if (matched == 1) { + fclose(fp); + return 0; + } else { + if (PrintContainerInfo) { + tty->print_cr("Type %s not found in file %s", scan_fmt, file); + } + } + } else { + if (PrintContainerInfo) { + tty->print_cr("Empty file %s", file); + } + } + } else { + if (PrintContainerInfo) { + tty->print_cr("Open of file %s failed, %s", file, strerror(errno)); + } + } + if (fp != NULL) + fclose(fp); + return OSCONTAINER_ERROR; +} +PRAGMA_DIAG_POP + +#define GET_CONTAINER_INFO(return_type, subsystem, filename, \ + logstring, scan_fmt, variable) \ + return_type variable; \ +{ \ + int err; \ + err = subsystem_file_contents(subsystem, \ + filename, \ + scan_fmt, \ + &variable); \ + if (err != 0) \ + return (return_type) OSCONTAINER_ERROR; \ + \ + if (PrintContainerInfo) \ + tty->print_cr(logstring, variable); \ +} + +#define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename, \ + logstring, scan_fmt, variable, bufsize) \ + char variable[bufsize]; \ +{ \ + int err; \ + err = subsystem_file_contents(subsystem, \ + filename, \ + scan_fmt, \ + variable); \ + if (err != 0) \ + return (return_type) NULL; \ + \ + if (PrintContainerInfo) \ + tty->print_cr(logstring, variable); \ +} + +/* init + * + * Initialize the container support and determine if + * we are running under cgroup control. + */ +void OSContainer::init() { + int mountid; + int parentid; + int major; + int minor; + FILE *mntinfo = NULL; + FILE *cgroup = NULL; + char buf[MAXPATHLEN+1]; + char tmproot[MAXPATHLEN+1]; + char tmpmount[MAXPATHLEN+1]; + char tmpbase[MAXPATHLEN+1]; + char *p; + jlong mem_limit; + + assert(!_is_initialized, "Initializing OSContainer more than once"); + + _is_initialized = true; + _is_containerized = false; + + _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size(); + + if (PrintContainerInfo) { + tty->print_cr("OSContainer::init: Initializing Container Support"); + } + if (!UseContainerSupport) { + if (PrintContainerInfo) { + tty->print_cr("Container Support not enabled"); + } + return; + } + + /* + * Find the cgroup mount point for memory and cpuset + * by reading /proc/self/mountinfo + * + * Example for docker: + * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory + * + * Example for host: + * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory + */ + mntinfo = fopen("/proc/self/mountinfo", "r"); + if (mntinfo == NULL) { + if (PrintContainerInfo) { + tty->print_cr("Can't open /proc/self/mountinfo, %s", + strerror(errno)); + } + return; + } + + while ( (p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) { + // Look for the filesystem type and see if it's cgroup + char fstype[MAXPATHLEN+1]; + fstype[0] = '\0'; + char *s = strstr(p, " - "); + if (s != NULL && + sscanf(s, " - %s", fstype) == 1 && + strcmp(fstype, "cgroup") == 0) { + + if (strstr(p, "memory") != NULL) { + int matched = sscanf(p, "%d %d %d:%d %s %s", + &mountid, + &parentid, + &major, + &minor, + tmproot, + tmpmount); + if (matched == 6) { + memory = new CgroupSubsystem(tmproot, tmpmount); + } + else + if (PrintContainerInfo) { + tty->print_cr("Incompatible str containing cgroup and memory: %s", p); + } + } else if (strstr(p, "cpuset") != NULL) { + int matched = sscanf(p, "%d %d %d:%d %s %s", + &mountid, + &parentid, + &major, + &minor, + tmproot, + tmpmount); + if (matched == 6) { + cpuset = new CgroupSubsystem(tmproot, tmpmount); + } + else { + if (PrintContainerInfo) { + tty->print_cr("Incompatible str containing cgroup and cpuset: %s", p); + } + } + } else if (strstr(p, "cpu,cpuacct") != NULL || strstr(p, "cpuacct,cpu") != NULL) { + int matched = sscanf(p, "%d %d %d:%d %s %s", + &mountid, + &parentid, + &major, + &minor, + tmproot, + tmpmount); + if (matched == 6) { + cpu = new CgroupSubsystem(tmproot, tmpmount); + cpuacct = new CgroupSubsystem(tmproot, tmpmount); + } + else { + if (PrintContainerInfo) { + tty->print_cr("Incompatible str containing cgroup and cpu,cpuacct: %s", p); + } + } + } else if (strstr(p, "cpuacct") != NULL) { + int matched = sscanf(p, "%d %d %d:%d %s %s", + &mountid, + &parentid, + &major, + &minor, + tmproot, + tmpmount); + if (matched == 6) { + cpuacct = new CgroupSubsystem(tmproot, tmpmount); + } + else { + if (PrintContainerInfo) { + tty->print_cr("Incompatible str containing cgroup and cpuacct: %s", p); + } + } + } else if (strstr(p, "cpu") != NULL) { + int matched = sscanf(p, "%d %d %d:%d %s %s", + &mountid, + &parentid, + &major, + &minor, + tmproot, + tmpmount); + if (matched == 6) { + cpu = new CgroupSubsystem(tmproot, tmpmount); + } + else { + if (PrintContainerInfo) { + tty->print_cr("Incompatible str containing cgroup and cpu: %s", p); + } + } + } + } + } + + fclose(mntinfo); + + if (memory == NULL) { + if (PrintContainerInfo) { + tty->print_cr("Required cgroup memory subsystem not found"); + } + return; + } + if (cpuset == NULL) { + if (PrintContainerInfo) { + tty->print_cr("Required cgroup cpuset subsystem not found"); + } + return; + } + if (cpu == NULL) { + if (PrintContainerInfo) { + tty->print_cr("Required cgroup cpu subsystem not found"); + } + return; + } + if (cpuacct == NULL) { + if (PrintContainerInfo) { + tty->print_cr("Required cgroup cpuacct subsystem not found"); + } + return; + } + + /* + * 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 + * + */ + cgroup = fopen("/proc/self/cgroup", "r"); + if (cgroup == NULL) { + if (PrintContainerInfo) { + tty->print_cr("Can't open /proc/self/cgroup, %s", + strerror(errno)); + } + return; + } + + while ( (p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) { + int cgno; + int matched; + char *controller; + char *base; + + /* Skip cgroup number */ + strsep(&p, ":"); + /* Get controller and base */ + controller = strsep(&p, ":"); + base = strsep(&p, "\n"); + + if (controller != NULL) { + if (strstr(controller, "memory") != NULL) { + memory->set_subsystem_path(base); + } else if (strstr(controller, "cpuset") != NULL) { + cpuset->set_subsystem_path(base); + } else if (strstr(controller, "cpu,cpuacct") != NULL || strstr(controller, "cpuacct,cpu") != NULL) { + cpu->set_subsystem_path(base); + cpuacct->set_subsystem_path(base); + } else if (strstr(controller, "cpuacct") != NULL) { + cpuacct->set_subsystem_path(base); + } else if (strstr(controller, "cpu") != NULL) { + cpu->set_subsystem_path(base); + } + } + } + + fclose(cgroup); + + // We need to update the amount of physical memory now that + // command line arguments have been processed. + if ((mem_limit = memory_limit_in_bytes()) > 0) { + os::Linux::set_physical_memory(mem_limit); + } + + _is_containerized = true; + +} + +const char * OSContainer::container_type() { + if (is_containerized()) { + return "cgroupv1"; + } else { + return NULL; + } +} + + +/* memory_limit_in_bytes + * + * Return the limit of available memory for this process. + * + * return: + * memory limit in bytes or + * -1 for unlimited + * OSCONTAINER_ERROR for not supported + */ +jlong OSContainer::memory_limit_in_bytes() { + GET_CONTAINER_INFO(julong, memory, "/memory.limit_in_bytes", + "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit); + + if (memlimit >= _unlimited_memory) { + if (PrintContainerInfo) { + tty->print_cr("Memory Limit is: Unlimited"); + } + return (jlong)-1; + } + else { + return (jlong)memlimit; + } +} + +jlong OSContainer::memory_and_swap_limit_in_bytes() { + GET_CONTAINER_INFO(julong, memory, "/memory.memsw.limit_in_bytes", + "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit); + if (memswlimit >= _unlimited_memory) { + if (PrintContainerInfo) { + tty->print_cr("Memory and Swap Limit is: Unlimited"); + } + return (jlong)-1; + } else { + return (jlong)memswlimit; + } +} + +jlong OSContainer::memory_soft_limit_in_bytes() { + GET_CONTAINER_INFO(julong, memory, "/memory.soft_limit_in_bytes", + "Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit); + if (memsoftlimit >= _unlimited_memory) { + if (PrintContainerInfo) { + tty->print_cr("Memory Soft Limit is: Unlimited"); + } + return (jlong)-1; + } else { + return (jlong)memsoftlimit; + } +} + +/* memory_usage_in_bytes + * + * Return the amount of used memory for this process. + * + * return: + * memory usage in bytes or + * -1 for unlimited + * OSCONTAINER_ERROR for not supported + */ +jlong OSContainer::memory_usage_in_bytes() { + GET_CONTAINER_INFO(jlong, memory, "/memory.usage_in_bytes", + "Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage); + return memusage; +} + +/* memory_max_usage_in_bytes + * + * Return the maximum amount of used memory for this process. + * + * return: + * max memory usage in bytes or + * OSCONTAINER_ERROR for not supported + */ +jlong OSContainer::memory_max_usage_in_bytes() { + GET_CONTAINER_INFO(jlong, memory, "/memory.max_usage_in_bytes", + "Maximum Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memmaxusage); + return memmaxusage; +} + +/* active_processor_count + * + * Calculate an appropriate number of active processors for the + * VM to use based on these three inputs. + * + * cpu affinity + * cgroup cpu quota & cpu period + * cgroup cpu shares + * + * Algorithm: + * + * Determine the number of available CPUs from sched_getaffinity + * + * If user specified a quota (quota != -1), calculate the number of + * required CPUs by dividing quota by period. + * + * If shares are in effect (shares != -1), calculate the number + * of CPUs required for the shares by dividing the share value + * by PER_CPU_SHARES. + * + * All results of division are rounded up to the next whole number. + * + * If neither shares or quotas have been specified, return the + * number of active processors in the system. + * + * If both shares and quotas have been specified, the results are + * based on the flag PreferContainerQuotaForCPUCount. If true, + * return the quota value. If false return the smallest value + * between shares or quotas. + * + * If shares and/or quotas have been specified, the resulting number + * returned will never exceed the number of active processors. + * + * return: + * number of CPUs + */ +int OSContainer::active_processor_count() { + int quota_count = 0, share_count = 0; + int cpu_count, limit_count; + int result; + + cpu_count = limit_count = os::Linux::active_processor_count(); + int quota = cpu_quota(); + int period = cpu_period(); + int share = cpu_shares(); + + if (quota > -1 && period > 0) { + quota_count = ceilf((float)quota / (float)period); + if (PrintContainerInfo) { + tty->print_cr("CPU Quota count based on quota/period: %d", quota_count); + } + } + if (share > -1) { + share_count = ceilf((float)share / (float)PER_CPU_SHARES); + if (PrintContainerInfo) { + tty->print_cr("CPU Share count based on shares: %d", share_count); + } + } + + // If both shares and quotas are setup results depend + // on flag PreferContainerQuotaForCPUCount. + // If true, limit CPU count to quota + // If false, use minimum of shares and quotas + if (quota_count !=0 && share_count != 0) { + if (PreferContainerQuotaForCPUCount) { + limit_count = quota_count; + } else { + limit_count = MIN2(quota_count, share_count); + } + } else if (quota_count != 0) { + limit_count = quota_count; + } else if (share_count != 0) { + limit_count = share_count; + } + + result = MIN2(cpu_count, limit_count); + if (PrintContainerInfo) { + tty->print_cr("OSContainer::active_processor_count: %d", result); + } + return result; +} + +char * OSContainer::cpu_cpuset_cpus() { + GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.cpus", + "cpuset.cpus is: %s", "%1023s", cpus, 1024); + return os::strdup(cpus); +} + +char * OSContainer::cpu_cpuset_memory_nodes() { + GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.mems", + "cpuset.mems is: %s", "%1023s", mems, 1024); + return os::strdup(mems); +} + +/* cpu_quota + * + * Return the number of milliseconds per period + * process is guaranteed to run. + * + * return: + * quota time in milliseconds + * -1 for no quota + * OSCONTAINER_ERROR for not supported + */ +int OSContainer::cpu_quota() { + GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_quota_us", + "CPU Quota is: %d", "%d", quota); + return quota; +} + +int OSContainer::cpu_period() { + GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_period_us", + "CPU Period is: %d", "%d", period); + return period; +} + +/* cpu_shares + * + * Return the amount of cpu shares available to the process + * + * return: + * Share number (typically a number relative to 1024) + * (2048 typically expresses 2 CPUs worth of processing) + * -1 for no share setup + * OSCONTAINER_ERROR for not supported + */ +int OSContainer::cpu_shares() { + GET_CONTAINER_INFO(int, cpu, "/cpu.shares", + "CPU Shares is: %d", "%d", shares); + // Convert 1024 to no shares setup + if (shares == 1024) return -1; + + return shares; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/linux/vm/osContainer_linux.hpp Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,68 @@ +/* + * 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. + * + */ + +#ifndef OS_LINUX_VM_OSCONTAINER_LINUX_HPP +#define OS_LINUX_VM_OSCONTAINER_LINUX_HPP + +#include "utilities/globalDefinitions.hpp" +#include "utilities/macros.hpp" +#include "memory/allocation.hpp" + +#define OSCONTAINER_ERROR (-2) + +class OSContainer: AllStatic { + + private: + static bool _is_initialized; + static bool _is_containerized; + + public: + static void init(); + static inline bool is_containerized(); + static const char * container_type(); + + static jlong memory_limit_in_bytes(); + static jlong memory_and_swap_limit_in_bytes(); + static jlong memory_soft_limit_in_bytes(); + static jlong memory_usage_in_bytes(); + static jlong memory_max_usage_in_bytes(); + + static int active_processor_count(); + + static char * cpu_cpuset_cpus(); + static char * cpu_cpuset_memory_nodes(); + + static int cpu_quota(); + static int cpu_period(); + + static int cpu_shares(); + +}; + +inline bool OSContainer::is_containerized() { + assert(_is_initialized, "OSContainer not initialized"); + return _is_containerized; +} + +#endif // OS_LINUX_VM_OSCONTAINER_LINUX_HPP
--- a/src/os/linux/vm/os_linux.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/os/linux/vm/os_linux.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -37,6 +37,7 @@ #include "mutex_linux.inline.hpp" #include "oops/oop.inline.hpp" #include "os_share_linux.hpp" +#include "osContainer_linux.hpp" #include "prims/jniFastGetField.hpp" #include "prims/jvm.h" #include "prims/jvm_misc.hpp" @@ -181,13 +182,62 @@ julong os::Linux::available_memory() { // values in struct sysinfo are "unsigned long" struct sysinfo si; + julong avail_mem; + + if (OSContainer::is_containerized()) { + jlong mem_limit, mem_usage; + if ((mem_limit = OSContainer::memory_limit_in_bytes()) < 1) { + if (PrintContainerInfo) { + tty->print_cr("container memory limit %s: " JLONG_FORMAT ", using host value", + mem_limit == OSCONTAINER_ERROR ? "failed" : "unlimited", mem_limit); + } + } + + if (mem_limit > 0 && (mem_usage = OSContainer::memory_usage_in_bytes()) < 1) { + if (PrintContainerInfo) { + tty->print_cr("container memory usage failed: " JLONG_FORMAT ", using host value", mem_usage); + } + } + + if (mem_limit > 0 && mem_usage > 0 ) { + avail_mem = mem_limit > mem_usage ? (julong)mem_limit - (julong)mem_usage : 0; + if (PrintContainerInfo) { + tty->print_cr("available container memory: " JULONG_FORMAT, avail_mem); + } + return avail_mem; + } + } + sysinfo(&si); - - return (julong)si.freeram * si.mem_unit; + avail_mem = (julong)si.freeram * si.mem_unit; + if (Verbose) { + tty->print_cr("available memory: " JULONG_FORMAT, avail_mem); + } + return avail_mem; } julong os::physical_memory() { - return Linux::physical_memory(); + jlong phys_mem = 0; + if (OSContainer::is_containerized()) { + jlong mem_limit; + if ((mem_limit = OSContainer::memory_limit_in_bytes()) > 0) { + if (PrintContainerInfo) { + tty->print_cr("total container memory: " JLONG_FORMAT, mem_limit); + } + return mem_limit; + } + + if (PrintContainerInfo) { + tty->print_cr("container memory limit %s: " JLONG_FORMAT ", using host value", + mem_limit == OSCONTAINER_ERROR ? "failed" : "unlimited", mem_limit); + } + } + + phys_mem = Linux::physical_memory(); + if (Verbose) { + tty->print_cr("total system memory: " JLONG_FORMAT, phys_mem); + } + return phys_mem; } //////////////////////////////////////////////////////////////////////////////// @@ -2122,6 +2172,8 @@ os::Posix::print_load_average(st); os::Linux::print_full_memory_info(st); + + os::Linux::print_container_info(st); } // Try to identify popular distros. @@ -2187,6 +2239,57 @@ st->cr(); } +void os::Linux::print_container_info(outputStream* st) { +if (!OSContainer::is_containerized()) { + return; + } + + st->print("container (cgroup) information:\n"); + + const char *p_ct = OSContainer::container_type(); + st->print("container_type: %s\n", p_ct != NULL ? p_ct : "failed"); + + char *p = OSContainer::cpu_cpuset_cpus(); + st->print("cpu_cpuset_cpus: %s\n", p != NULL ? p : "failed"); + free(p); + + p = OSContainer::cpu_cpuset_memory_nodes(); + st->print("cpu_memory_nodes: %s\n", p != NULL ? p : "failed"); + free(p); + + int i = OSContainer::active_processor_count(); + if (i > 0) { + st->print("active_processor_count: %d\n", i); + } else { + st->print("active_processor_count: failed\n"); + } + + i = OSContainer::cpu_quota(); + st->print("cpu_quota: %d\n", i); + + i = OSContainer::cpu_period(); + st->print("cpu_period: %d\n", i); + + i = OSContainer::cpu_shares(); + st->print("cpu_shares: %d\n", i); + + jlong j = OSContainer::memory_limit_in_bytes(); + st->print("memory_limit_in_bytes: " JLONG_FORMAT "\n", j); + + j = OSContainer::memory_and_swap_limit_in_bytes(); + st->print("memory_and_swap_limit_in_bytes: " JLONG_FORMAT "\n", j); + + j = OSContainer::memory_soft_limit_in_bytes(); + st->print("memory_soft_limit_in_bytes: " JLONG_FORMAT "\n", j); + + j = OSContainer::OSContainer::memory_usage_in_bytes(); + st->print("memory_usage_in_bytes: " JLONG_FORMAT "\n", j); + + j = OSContainer::OSContainer::memory_max_usage_in_bytes(); + st->print("memory_max_usage_in_bytes: " JLONG_FORMAT "\n", j); + st->cr(); +} + void os::print_memory_info(outputStream* st) { st->print("Memory:"); @@ -4963,6 +5066,10 @@ } } +void os::pd_init_container_support() { + OSContainer::init(); +} + // this is called _after_ the global arguments have been parsed jint os::init_2(void) { @@ -5143,7 +5250,7 @@ // sched_getaffinity gives an accurate answer as it accounts for cpusets. // If anything goes wrong we fallback to returning the number of online // processors - which can be greater than the number available to the process. -int os::active_processor_count() { +int os::Linux::active_processor_count() { cpu_set_t cpus; // can represent at most 1024 (CPU_SETSIZE) processors int cpus_size = sizeof(cpu_set_t); int cpu_count = 0; @@ -5161,10 +5268,48 @@ "which may exceed available processors", strerror(errno), cpu_count); } - assert(cpu_count > 0 && cpu_count <= processor_count(), "sanity check"); + assert(cpu_count > 0 && cpu_count <= os::processor_count(), "sanity check"); return cpu_count; } +// Determine the active processor count from one of +// three different sources: +// +// 1. User option -XX:ActiveProcessorCount +// 2. kernel os calls (sched_getaffinity or sysconf(_SC_NPROCESSORS_ONLN) +// 3. extracted from cgroup cpu subsystem (shares and quotas) +// +// Option 1, if specified, will always override. +// If the cgroup subsystem is active and configured, we +// will return the min of the cgroup and option 2 results. +// This is required since tools, such as numactl, that +// alter cpu affinity do not update cgroup subsystem +// cpuset configuration files. +int os::active_processor_count() { + // User has overridden the number of active processors + if (ActiveProcessorCount > 0) { + if (PrintActiveCpus) { + tty->print_cr("active_processor_count: " + "active processor count set by user : %d", + ActiveProcessorCount); + } + return ActiveProcessorCount; + } + + int active_cpus; + if (OSContainer::is_containerized()) { + active_cpus = OSContainer::active_processor_count(); + if (PrintActiveCpus) { + tty->print_cr("active_processor_count: determined by OSContainer: %d", + active_cpus); + } + } else { + active_cpus = os::Linux::active_processor_count(); + } + + return active_cpus; +} + void os::set_native_thread_name(const char *name) { // Not yet implemented. return;
--- a/src/os/linux/vm/os_linux.hpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/os/linux/vm/os_linux.hpp Thu Oct 25 12:52:11 2018 +0300 @@ -35,6 +35,7 @@ class Linux { friend class os; + friend class OSContainer; friend class TestReserveMemorySpecial; // For signal-chaining @@ -79,6 +80,9 @@ static julong available_memory(); static julong physical_memory() { return _physical_memory; } + static void set_physical_memory(julong phys_mem) { _physical_memory = phys_mem; } + static int active_processor_count(); + static void initialize_system_info(); static int commit_memory_impl(char* addr, size_t bytes, bool exec); @@ -116,6 +120,7 @@ static bool release_memory_special_huge_tlbfs(char* base, size_t bytes); static void print_full_memory_info(outputStream* st); + static void print_container_info(outputStream* st); static void print_distro_info(outputStream* st); static void print_libversion_info(outputStream* st);
--- a/src/os/solaris/vm/os_solaris.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/os/solaris/vm/os_solaris.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -357,6 +357,16 @@ } int os::active_processor_count() { + // User has overridden the number of active processors + if (ActiveProcessorCount > 0) { + if (Verbose) { + tty->print_cr("active_processor_count: " + "active processor count set by user : %d", + ActiveProcessorCount); + } + return ActiveProcessorCount; + } + int online_cpus = sysconf(_SC_NPROCESSORS_ONLN); pid_t pid = getpid(); psetid_t pset = PS_NONE;
--- a/src/os/windows/vm/os_windows.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/os/windows/vm/os_windows.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -716,6 +716,16 @@ #endif int os::active_processor_count() { + // User has overridden the number of active processors + if (ActiveProcessorCount > 0) { + if (PrintActiveCpus) { + tty->print_cr("active_processor_count: " + "active processor count set by user : %d", + ActiveProcessorCount); + } + return ActiveProcessorCount; + } + DWORD_PTR lpProcessAffinityMask = 0; DWORD_PTR lpSystemAffinityMask = 0; int proc_count = processor_count();
--- a/src/share/vm/c1/c1_Runtime1.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/c1/c1_Runtime1.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -547,9 +547,8 @@ // normal bytecode execution. thread->clear_exception_oop_and_pc(); - Handle original_exception(thread, exception()); - - continuation = SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, false, false); + bool recursive_exception = false; + continuation = SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, false, false, recursive_exception); // If an exception was thrown during exception dispatch, the exception oop may have changed thread->set_exception_oop(exception()); thread->set_exception_pc(pc); @@ -557,8 +556,9 @@ // the exception cache is used only by non-implicit exceptions // Update the exception cache only when there didn't happen // another exception during the computation of the compiled - // exception handler. - if (continuation != NULL && original_exception() == exception()) { + // exception handler. Checking for exception oop equality is not + // sufficient because some exceptions are pre-allocated and reused. + if (continuation != NULL && !recursive_exception) { nm->add_handler_for_exception_and_pc(exception, pc, continuation); } }
--- a/src/share/vm/interpreter/linkResolver.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/interpreter/linkResolver.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -779,37 +779,37 @@ THROW_MSG(vmSymbols::java_lang_NoSuchFieldError(), field->as_C_string()); } - if (!check_access) - // Access checking may be turned off when calling from within the VM. - return; + // Access checking may be turned off when calling from within the VM. + if (check_access) { + + // check access + check_field_accessability(current_klass, resolved_klass, sel_klass, fd, CHECK); - // check access - check_field_accessability(current_klass, resolved_klass, sel_klass, fd, CHECK); + // check for errors + if (is_static != fd.is_static()) { + ResourceMark rm(THREAD); + char msg[200]; + jio_snprintf(msg, sizeof(msg), "Expected %s field %s.%s", is_static ? "static" : "non-static", resolved_klass()->external_name(), fd.name()->as_C_string()); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), msg); + } - // check for errors - if (is_static != fd.is_static()) { - ResourceMark rm(THREAD); - char msg[200]; - jio_snprintf(msg, sizeof(msg), "Expected %s field %s.%s", is_static ? "static" : "non-static", resolved_klass()->external_name(), fd.name()->as_C_string()); - THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), msg); + // Final fields can only be accessed from its own class. + if (is_put && fd.access_flags().is_final() && sel_klass() != current_klass()) { + THROW(vmSymbols::java_lang_IllegalAccessError()); + } + + // initialize resolved_klass if necessary + // note 1: the klass which declared the field must be initialized (i.e, sel_klass) + // according to the newest JVM spec (5.5, p.170) - was bug (gri 7/28/99) + // + // note 2: we don't want to force initialization if we are just checking + // if the field access is legal; e.g., during compilation + if (is_static && initialize_class) { + sel_klass->initialize(CHECK); + } } - // Final fields can only be accessed from its own class. - if (is_put && fd.access_flags().is_final() && sel_klass() != current_klass()) { - THROW(vmSymbols::java_lang_IllegalAccessError()); - } - - // initialize resolved_klass if necessary - // note 1: the klass which declared the field must be initialized (i.e, sel_klass) - // according to the newest JVM spec (5.5, p.170) - was bug (gri 7/28/99) - // - // note 2: we don't want to force initialization if we are just checking - // if the field access is legal; e.g., during compilation - if (is_static && initialize_class) { - sel_klass->initialize(CHECK); - } - - if (sel_klass() != current_klass()) { + if (sel_klass() != current_klass() && !current_klass.is_null()) { HandleMark hm(THREAD); Handle ref_loader (THREAD, InstanceKlass::cast(current_klass())->class_loader()); Handle sel_loader (THREAD, InstanceKlass::cast(sel_klass())->class_loader());
--- a/src/share/vm/opto/runtime.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/opto/runtime.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -1236,17 +1236,23 @@ force_unwind ? NULL : nm->handler_for_exception_and_pc(exception, pc); if (handler_address == NULL) { - Handle original_exception(thread, exception()); - handler_address = SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, force_unwind, true); + bool recursive_exception = false; + handler_address = SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, force_unwind, true, recursive_exception); assert (handler_address != NULL, "must have compiled handler"); // Update the exception cache only when the unwind was not forced // and there didn't happen another exception during the computation of the - // compiled exception handler. - if (!force_unwind && original_exception() == exception()) { + // compiled exception handler. Checking for exception oop equality is not + // sufficient because some exceptions are pre-allocated and reused. + if (!force_unwind && !recursive_exception) { nm->add_handler_for_exception_and_pc(exception,pc,handler_address); } } else { - assert(handler_address == SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, force_unwind, true), "Must be the same"); +#ifdef ASSERT + bool recursive_exception = false; + address computed_address = SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, force_unwind, true, recursive_exception); + assert(recursive_exception || (handler_address == computed_address), err_msg("Handler address inconsistency: " PTR_FORMAT " != " PTR_FORMAT, + p2i(handler_address), p2i(computed_address))); +#endif } }
--- a/src/share/vm/prims/whitebox.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/prims/whitebox.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -160,6 +160,7 @@ #ifdef LINUX #include "utilities/elfFile.hpp" +#include "osContainer_linux.hpp" #endif WB_ENTRY(jlong, WB_GetCompressedOopsMaxHeapSize(JNIEnv* env, jobject o)) { @@ -1028,6 +1029,15 @@ return ret; WB_END +WB_ENTRY(jboolean, WB_IsContainerized(JNIEnv* env, jobject o)) + LINUX_ONLY(return OSContainer::is_containerized();) + return false; +WB_END + +WB_ENTRY(void, WB_PrintOsInfo(JNIEnv* env, jobject o)) + os::print_os_info(tty); +WB_END + #define CC (char*) static JNINativeMethod methods[] = { @@ -1141,6 +1151,8 @@ {CC"forceSafepoint", CC"()V", (void*)&WB_ForceSafepoint }, {CC"checkLibSpecifiesNoexecstack", CC"(Ljava/lang/String;)Z", (void*)&WB_CheckLibSpecifiesNoexecstack}, + {CC"isContainerized", CC"()Z", (void*)&WB_IsContainerized }, + {CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo }, }; #undef CC
--- a/src/share/vm/runtime/arguments.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/runtime/arguments.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -1801,20 +1801,34 @@ } } + // Convert Fraction to Precentage values + if (FLAG_IS_DEFAULT(MaxRAMPercentage) && + !FLAG_IS_DEFAULT(MaxRAMFraction)) + MaxRAMPercentage = 100.0 / MaxRAMFraction; + + if (FLAG_IS_DEFAULT(MinRAMPercentage) && + !FLAG_IS_DEFAULT(MinRAMFraction)) + MinRAMPercentage = 100.0 / MinRAMFraction; + + if (FLAG_IS_DEFAULT(InitialRAMPercentage) && + !FLAG_IS_DEFAULT(InitialRAMFraction)) + InitialRAMPercentage = 100.0 / InitialRAMFraction; + // If the maximum heap size has not been set with -Xmx, // then set it as fraction of the size of physical memory, // respecting the maximum and minimum sizes of the heap. if (FLAG_IS_DEFAULT(MaxHeapSize)) { - julong reasonable_max = phys_mem / MaxRAMFraction; - - if (phys_mem <= MaxHeapSize * MinRAMFraction) { + julong reasonable_max = (julong)((phys_mem * MaxRAMPercentage) / 100); + const julong reasonable_min = (julong)((phys_mem * MinRAMPercentage) / 100); + if (reasonable_min < MaxHeapSize) { // Small physical memory, so use a minimum fraction of it for the heap - reasonable_max = phys_mem / MinRAMFraction; + reasonable_max = reasonable_min; } else { // Not-small physical memory, so require a heap at least // as large as MaxHeapSize reasonable_max = MAX2(reasonable_max, (julong)MaxHeapSize); } + if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) { // Limit the heap size to ErgoHeapSizeLimit reasonable_max = MIN2(reasonable_max, (julong)ErgoHeapSizeLimit); @@ -1856,7 +1870,7 @@ reasonable_minimum = limit_by_allocatable_memory(reasonable_minimum); if (InitialHeapSize == 0) { - julong reasonable_initial = phys_mem / InitialRAMFraction; + julong reasonable_initial = (julong)((phys_mem * InitialRAMPercentage) / 100); reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, (julong)min_heap_size()); reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize); @@ -1881,6 +1895,94 @@ } } +// This option inspects the machine and attempts to set various +// parameters to be optimal for long-running, memory allocation +// intensive jobs. It is intended for machines with large +// amounts of cpu and memory. +jint Arguments::set_aggressive_heap_flags() { + // initHeapSize is needed since _initial_heap_size is 4 bytes on a 32 bit + // VM, but we may not be able to represent the total physical memory + // available (like having 8gb of memory on a box but using a 32bit VM). + // Thus, we need to make sure we're using a julong for intermediate + // calculations. + julong initHeapSize; + julong total_memory = os::physical_memory(); + + if (total_memory < (julong) 256 * M) { + jio_fprintf(defaultStream::error_stream(), + "You need at least 256mb of memory to use -XX:+AggressiveHeap\n"); + vm_exit(1); + } + + // The heap size is half of available memory, or (at most) + // all of possible memory less 160mb (leaving room for the OS + // when using ISM). This is the maximum; because adaptive sizing + // is turned on below, the actual space used may be smaller. + + initHeapSize = MIN2(total_memory / (julong) 2, + total_memory - (julong) 160 * M); + + initHeapSize = limit_by_allocatable_memory(initHeapSize); + + if (FLAG_IS_DEFAULT(MaxHeapSize)) { + FLAG_SET_CMDLINE(uintx, MaxHeapSize, initHeapSize); + FLAG_SET_CMDLINE(uintx, InitialHeapSize, initHeapSize); + // Currently the minimum size and the initial heap sizes are the same. + set_min_heap_size(initHeapSize); + } + if (FLAG_IS_DEFAULT(NewSize)) { + // Make the young generation 3/8ths of the total heap. + FLAG_SET_CMDLINE(uintx, NewSize, + ((julong) MaxHeapSize / (julong) 8) * (julong) 3); + FLAG_SET_CMDLINE(uintx, MaxNewSize, NewSize); + } + +#ifndef _ALLBSD_SOURCE // UseLargePages is not yet supported on BSD. + FLAG_SET_DEFAULT(UseLargePages, true); +#endif + + // Increase some data structure sizes for efficiency + FLAG_SET_CMDLINE(uintx, BaseFootPrintEstimate, MaxHeapSize); + FLAG_SET_CMDLINE(bool, ResizeTLAB, false); + FLAG_SET_CMDLINE(uintx, TLABSize, 256 * K); + + // See the OldPLABSize comment below, but replace 'after promotion' + // with 'after copying'. YoungPLABSize is the size of the survivor + // space per-gc-thread buffers. The default is 4kw. + FLAG_SET_CMDLINE(uintx, YoungPLABSize, 256 * K); // Note: this is in words + + // OldPLABSize is the size of the buffers in the old gen that + // UseParallelGC uses to promote live data that doesn't fit in the + // survivor spaces. At any given time, there's one for each gc thread. + // The default size is 1kw. These buffers are rarely used, since the + // survivor spaces are usually big enough. For specjbb, however, there + // are occasions when there's lots of live data in the young gen + // and we end up promoting some of it. We don't have a definite + // explanation for why bumping OldPLABSize helps, but the theory + // is that a bigger PLAB results in retaining something like the + // original allocation order after promotion, which improves mutator + // locality. A minor effect may be that larger PLABs reduce the + // number of PLAB allocation events during gc. The value of 8kw + // was arrived at by experimenting with specjbb. + FLAG_SET_CMDLINE(uintx, OldPLABSize, 8 * K); // Note: this is in words + + // Enable parallel GC and adaptive generation sizing + FLAG_SET_CMDLINE(bool, UseParallelGC, true); + + // Encourage steady state memory management + FLAG_SET_CMDLINE(uintx, ThresholdTolerance, 100); + + // This appears to improve mutator locality + FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); + + // Get around early Solaris scheduling bug + // (affinity vs other jobs on system) + // but disallow DR and offlining (5008695). + FLAG_SET_CMDLINE(bool, BindGCTaskThreadsToCPUs, true); + + return JNI_OK; +} + // This must be called after ergonomics because we want bytecode rewriting // if the server compiler is used, or if UseSharedSpaces is disabled. void Arguments::set_bytecode_flags() { @@ -2644,6 +2746,14 @@ return result; } + // We need to ensure processor and memory resources have been properly + // configured - which may rely on arguments we just processed - before + // doing the final argument processing. Any argument processing that + // needs to know about processor and memory resources must occur after + // this point. + + os::init_container_support(); + // Do final processing now that all arguments have been parsed result = finalize_vm_init_args(&scp, scp_assembly_required); if (result != JNI_OK) { @@ -3117,94 +3227,6 @@ _exit_hook = CAST_TO_FN_PTR(exit_hook_t, option->extraInfo); } else if (match_option(option, "abort", &tail)) { _abort_hook = CAST_TO_FN_PTR(abort_hook_t, option->extraInfo); - // -XX:+AggressiveHeap - } else if (match_option(option, "-XX:+AggressiveHeap", &tail)) { - - // This option inspects the machine and attempts to set various - // parameters to be optimal for long-running, memory allocation - // intensive jobs. It is intended for machines with large - // amounts of cpu and memory. - - // initHeapSize is needed since _initial_heap_size is 4 bytes on a 32 bit - // VM, but we may not be able to represent the total physical memory - // available (like having 8gb of memory on a box but using a 32bit VM). - // Thus, we need to make sure we're using a julong for intermediate - // calculations. - julong initHeapSize; - julong total_memory = os::physical_memory(); - - if (total_memory < (julong)256*M) { - jio_fprintf(defaultStream::error_stream(), - "You need at least 256mb of memory to use -XX:+AggressiveHeap\n"); - vm_exit(1); - } - - // The heap size is half of available memory, or (at most) - // all of possible memory less 160mb (leaving room for the OS - // when using ISM). This is the maximum; because adaptive sizing - // is turned on below, the actual space used may be smaller. - - initHeapSize = MIN2(total_memory / (julong)2, - total_memory - (julong)160*M); - - initHeapSize = limit_by_allocatable_memory(initHeapSize); - - if (FLAG_IS_DEFAULT(MaxHeapSize)) { - FLAG_SET_CMDLINE(uintx, MaxHeapSize, initHeapSize); - FLAG_SET_CMDLINE(uintx, InitialHeapSize, initHeapSize); - // Currently the minimum size and the initial heap sizes are the same. - set_min_heap_size(initHeapSize); - } - if (FLAG_IS_DEFAULT(NewSize)) { - // Make the young generation 3/8ths of the total heap. - FLAG_SET_CMDLINE(uintx, NewSize, - ((julong)MaxHeapSize / (julong)8) * (julong)3); - FLAG_SET_CMDLINE(uintx, MaxNewSize, NewSize); - } - -#ifndef _ALLBSD_SOURCE // UseLargePages is not yet supported on BSD. - FLAG_SET_DEFAULT(UseLargePages, true); -#endif - - // Increase some data structure sizes for efficiency - FLAG_SET_CMDLINE(uintx, BaseFootPrintEstimate, MaxHeapSize); - FLAG_SET_CMDLINE(bool, ResizeTLAB, false); - FLAG_SET_CMDLINE(uintx, TLABSize, 256*K); - - // See the OldPLABSize comment below, but replace 'after promotion' - // with 'after copying'. YoungPLABSize is the size of the survivor - // space per-gc-thread buffers. The default is 4kw. - FLAG_SET_CMDLINE(uintx, YoungPLABSize, 256*K); // Note: this is in words - - // OldPLABSize is the size of the buffers in the old gen that - // UseParallelGC uses to promote live data that doesn't fit in the - // survivor spaces. At any given time, there's one for each gc thread. - // The default size is 1kw. These buffers are rarely used, since the - // survivor spaces are usually big enough. For specjbb, however, there - // are occasions when there's lots of live data in the young gen - // and we end up promoting some of it. We don't have a definite - // explanation for why bumping OldPLABSize helps, but the theory - // is that a bigger PLAB results in retaining something like the - // original allocation order after promotion, which improves mutator - // locality. A minor effect may be that larger PLABs reduce the - // number of PLAB allocation events during gc. The value of 8kw - // was arrived at by experimenting with specjbb. - FLAG_SET_CMDLINE(uintx, OldPLABSize, 8*K); // Note: this is in words - - // Enable parallel GC and adaptive generation sizing - FLAG_SET_CMDLINE(bool, UseParallelGC, true); - - // Encourage steady state memory management - FLAG_SET_CMDLINE(uintx, ThresholdTolerance, 100); - - // This appears to improve mutator locality - FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); - - // Get around early Solaris scheduling bug - // (affinity vs other jobs on system) - // but disallow DR and offlining (5008695). - FLAG_SET_CMDLINE(bool, BindGCTaskThreadsToCPUs, true); - } else if (match_option(option, "-XX:+NeverTenure", &tail)) { // The last option must always win. FLAG_SET_CMDLINE(bool, AlwaysTenure, false); @@ -3605,6 +3627,15 @@ return JNI_ERR; } + // This must be done after all arguments have been processed + // and the container support has been initialized since AggressiveHeap + // relies on the amount of total memory available. + if (AggressiveHeap) { + jint result = set_aggressive_heap_flags(); + if (result != JNI_OK) { + return result; + } + } // This must be done after all arguments have been processed. // java_compiler() true means set to "NONE" or empty. if (java_compiler() && !xdebug_mode()) {
--- a/src/share/vm/runtime/arguments.hpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/runtime/arguments.hpp Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -365,6 +365,8 @@ // Aggressive optimization flags. static void set_aggressive_opts_flags(); + static jint set_aggressive_heap_flags(); + // Argument parsing static void do_pd_flag_adjustments(); static bool parse_argument(const char* arg, Flag::Flags origin);
--- a/src/share/vm/runtime/globals.hpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/runtime/globals.hpp Thu Oct 25 12:52:11 2018 +0300 @@ -2077,13 +2077,23 @@ product_pd(uint64_t, MaxRAM, \ "Real memory size (in bytes) used to set maximum heap size") \ \ + product(bool, AggressiveHeap, false, \ + "Optimize heap options for long-running memory intensive apps") \ + \ product(uintx, ErgoHeapSizeLimit, 0, \ "Maximum ergonomically set heap size (in bytes); zero means use " \ - "MaxRAM / MaxRAMFraction") \ + "MaxRAM * MaxRAMPercentage / 100") \ \ experimental(bool, UseCGroupMemoryLimitForHeap, false, \ "Use CGroup memory limit as physical memory limit for heap " \ - "sizing") \ + "sizing" \ + "Deprecated, replaced by container support") \ + \ + diagnostic(bool, PrintContainerInfo, false, \ + "Print container related information") \ + \ + diagnostic(bool, PrintActiveCpus, false, \ + "Print the number of CPUs detected in os::active_processor_count") \ \ product(uintx, MaxRAMFraction, 4, \ "Maximum fraction (1/n) of real memory used for maximum heap " \ @@ -2100,6 +2110,19 @@ product(uintx, InitialRAMFraction, 64, \ "Fraction (1/n) of real memory used for initial heap size") \ \ + product(double, MaxRAMPercentage, 25.0, \ + "Maximum percentage of real memory used for maximum heap size") \ + \ + product(double, MinRAMPercentage, 50.0, \ + "Minimum percentage of real memory used for maximum heap" \ + "size on systems with small physical memory size") \ + \ + product(double, InitialRAMPercentage, 1.5625, \ + "Percentage of real memory used for initial heap size") \ + \ + product(intx, ActiveProcessorCount, -1, \ + "Specify the CPU count the VM should use and report as active") \ + \ develop(uintx, MaxVirtMemFraction, 2, \ "Maximum fraction (1/n) of virtual memory used for ergonomically "\ "determining maximum heap size") \
--- a/src/share/vm/runtime/os.hpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/runtime/os.hpp Thu Oct 25 12:52:11 2018 +0300 @@ -152,8 +152,16 @@ static size_t page_size_for_region(size_t region_size, size_t min_pages, bool must_be_aligned); static void initialize_initial_active_processor_count(); + + LINUX_ONLY(static void pd_init_container_support();) + public: static void init(void); // Called before command line parsing + + static void init_container_support() { // Called during command line parsing. + LINUX_ONLY(pd_init_container_support();) + } + static void init_before_ergo(void); // Called after command line parsing // before VM ergonomics processing. static jint init_2(void); // Called after command line parsing
--- a/src/share/vm/runtime/sharedRuntime.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/runtime/sharedRuntime.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -643,7 +643,7 @@ // ret_pc points into caller; we are returning caller's exception handler // for given exception address SharedRuntime::compute_compiled_exc_handler(nmethod* nm, address ret_pc, Handle& exception, - bool force_unwind, bool top_frame_only) { + bool force_unwind, bool top_frame_only, bool& recursive_exception_occurred) { assert(nm != NULL, "must exist"); ResourceMark rm; @@ -671,6 +671,7 @@ // BCI of the exception handler which caused the exception to be // thrown (bugs 4307310 and 4546590). Set "exception" reference // argument to ensure that the correct exception is thrown (4870175). + recursive_exception_occurred = true; exception = Handle(THREAD, PENDING_EXCEPTION); CLEAR_PENDING_EXCEPTION; if (handler_bci >= 0) {
--- a/src/share/vm/runtime/sharedRuntime.hpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/runtime/sharedRuntime.hpp Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -184,7 +184,7 @@ // exception handling and implicit exceptions static address compute_compiled_exc_handler(nmethod* nm, address ret_pc, Handle& exception, - bool force_unwind, bool top_frame_only); + bool force_unwind, bool top_frame_only, bool& recursive_exception_occurred); enum ImplicitExceptionKind { IMPLICIT_NULL, IMPLICIT_DIVIDE_BY_ZERO,
--- a/src/share/vm/runtime/thread.cpp Thu Aug 02 15:15:43 2018 +0300 +++ b/src/share/vm/runtime/thread.cpp Thu Oct 25 12:52:11 2018 +0300 @@ -3335,6 +3335,7 @@ Arguments::init_version_specific_system_properties(); // Parse arguments + // Note: this internally calls os::init_container_support() jint parse_result = Arguments::parse(args); if (parse_result != JNI_OK) return parse_result;
--- a/test/compiler/loopopts/TestCMovSplitThruPhi.java Thu Aug 02 15:15:43 2018 +0300 +++ b/test/compiler/loopopts/TestCMovSplitThruPhi.java Thu Oct 25 12:52:11 2018 +0300 @@ -25,7 +25,7 @@ * @test * @bug 8187822 * @summary C2 conditonal move optimization might create broken graph - * @run main/othervm -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestCMovSplitThruPhi::not_inlined -XX:CompileOnly=TestCMovSplitThruPhi::test -XX:-LoopUnswitching TestCMovSplitThruPhi + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestCMovSplitThruPhi::not_inlined -XX:CompileOnly=TestCMovSplitThruPhi::test -XX:-LoopUnswitching TestCMovSplitThruPhi * */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/AttemptOOM.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,49 @@ +/* + * 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. + */ + +public class AttemptOOM { + private static MyObj[] data; + + public static void main(String[] args) throws Exception { + System.out.println("Entering AttemptOOM main"); + + // each MyObj will allocate 1024 byte array + int sizeInMb = Integer.parseInt(args[0]); + data = new MyObj[sizeInMb*1024]; + + System.out.println("data.length = " + data.length); + + for (int i=0; i < data.length; i++) { + data[i] = new MyObj(1024); + } + + System.out.println("AttemptOOM allocation successful"); + } + + private static class MyObj { + private byte[] myData; + MyObj(int size) { + myData = new byte[size]; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/CPUSetsReader.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,141 @@ +/* + * 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.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 com.oracle.java.testlibrary.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<String> o = Optional.empty(); + + System.out.println("readFromProcStatus() entering for: " + setType); + + try (Stream<String> 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<Integer> parseCpuSet(String value) { + ArrayList<Integer> result = new ArrayList<Integer>(); + + 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<Integer> 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<Integer> 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<Integer> list, int maxCount) { + return list.stream() + .limit(maxCount) + .map(Object::toString) + .collect(Collectors.joining(",")); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/CheckContainerized.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,44 @@ +/* + * 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 sun.hotspot.WhiteBox; + +public class CheckContainerized { + public static String OUTSIDE_OF_CONTAINER = + "CheckContainerized: Running outside of a container"; + public static String INSIDE_A_CONTAINER = + "CheckContainerized: Running inside a container"; + + public static void main(String[] args) { + System.out.println("CheckContainerized: Entering"); + WhiteBox wb = WhiteBox.getWhiteBox(); + + if (wb.isContainerized()) { + System.out.println(INSIDE_A_CONTAINER); + + } else { + System.out.println(OUTSIDE_OF_CONTAINER); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/DockerBasicTest.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,80 @@ +/* + * 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 Basic (sanity) test for JDK-under-test inside a docker image. + * @library /testlibrary + * @build HelloDocker + * @run driver DockerBasicTest + */ + +import com.oracle.java.testlibrary.Utils; +import com.oracle.java.testlibrary.Platform; +import com.oracle.java.testlibrary.DockerTestUtils; +import com.oracle.java.testlibrary.DockerRunOptions; + +public class DockerBasicTest { + private static final String imageNameAndTag = "jdk8-internal:test"; + // Diganostics: set to false to examine image after the test + private static final boolean removeImageAfterTest = true; + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + DockerTestUtils.buildJdkDockerImage(imageNameAndTag, "Dockerfile-BasicTest", "jdk-docker"); + + try { + testJavaVersion(); + testHelloDocker(); + } finally { + if (removeImageAfterTest) + DockerTestUtils.removeDockerImage(imageNameAndTag); + } + } + + + private static void testJavaVersion() throws Exception { + DockerRunOptions opts = + new DockerRunOptions(imageNameAndTag, "/jdk/bin/java", "-version"); + + DockerTestUtils.dockerRunJava(opts) + .shouldHaveExitValue(0) + .shouldContain(Platform.vmName); + } + + + private static void testHelloDocker() throws Exception { + DockerRunOptions opts = + new DockerRunOptions(imageNameAndTag, "/jdk/bin/java", "HelloDocker") + .addJavaOpts("-cp", "/test-classes/") + .addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + + DockerTestUtils.dockerRunJava(opts) + .shouldHaveExitValue(0) + .shouldContain("Hello Docker"); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/Dockerfile-BasicTest Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,8 @@ +FROM oraclelinux:7.2 +MAINTAINER mikhailo.seledtsov@oracle.com + +COPY /jdk /jdk + +ENV JAVA_HOME=/jdk + +CMD ["/bin/bash"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/Dockerfile-BasicTest-aarch64 Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,8 @@ +# Use generic ubuntu Linux on AArch64 +FROM aarch64/ubuntu + +COPY /jdk /jdk + +ENV JAVA_HOME=/jdk + +CMD ["/bin/bash"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/Dockerfile-BasicTest-ppc64le Thu Oct 25 12:52:11 2018 +0300 @@ -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"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/Dockerfile-BasicTest-s390x Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,7 @@ +FROM s390x/ubuntu + +COPY /jdk /jdk + +ENV JAVA_HOME=/jdk + +CMD ["/bin/bash"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/HelloDocker.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,28 @@ +/* + * 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. + */ + +public class HelloDocker { + public static void main(String args[]) { + System.out.println("Hello Docker"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/PrintContainerInfo.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,34 @@ +/* + * 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 sun.hotspot.WhiteBox; + +public class PrintContainerInfo { + + public static void main(String[] args) { + System.out.println("PrintContainerInfo: Entering"); + WhiteBox wb = WhiteBox.getWhiteBox(); + + wb.printOsInfo(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/TEST.properties Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,1 @@ +exclusiveAccess.dirs=.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/TestCPUAwareness.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,204 @@ +/* + * 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 JVM's CPU resource awareness when running inside docker container + * @library /testlibrary + * @run driver TestCPUAwareness + */ + +import java.util.List; +import com.oracle.java.testlibrary.Common; +import com.oracle.java.testlibrary.DockerTestUtils; +import com.oracle.java.testlibrary.DockerRunOptions; + +public class TestCPUAwareness { +private static final String imageName = Common.imageName("cpu"); + private static final int availableCPUs = Runtime.getRuntime().availableProcessors(); + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + System.out.println("Test Environment: detected availableCPUs = " + availableCPUs); + DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + + try { + // cpuset, period, shares, expected Active Processor Count + testComboWithCpuSets(); + + // cpu shares - it should be safe to use CPU shares exceeding available CPUs + testCpuShares(256, 1); + testCpuShares(2048, 2); + testCpuShares(4096, 4); + + // leave one CPU for system and tools, otherwise this test may be unstable + int maxNrOfAvailableCpus = availableCPUs - 1; + for (int i=1; i < maxNrOfAvailableCpus; i = i * 2) { + testCpus(i, i); + } + + // If ActiveProcessorCount is set, the VM should use it, regardless of other + // container settings, host settings or available CPUs on the host. + testActiveProcessorCount(1, 1); + testActiveProcessorCount(2, 2); + + // cpu quota and period + testCpuQuotaAndPeriod(50*1000, 100*1000); + testCpuQuotaAndPeriod(100*1000, 100*1000); + testCpuQuotaAndPeriod(150*1000, 100*1000); + testCpuQuotaAndPeriod(400*1000, 100*1000); + + } finally { + DockerTestUtils.removeDockerImage(imageName); + } + } + + + private static void testComboWithCpuSets() throws Exception { + String cpuSetStr = CPUSetsReader.readFromProcStatus("Cpus_allowed_list"); + System.out.println("cpuSetStr = " + cpuSetStr); + + if (cpuSetStr == null) { + System.out.printf("The cpuset test cases are skipped"); + } else { + List<Integer> cpuSet = CPUSetsReader.parseCpuSet(cpuSetStr); + + // Test subset of cpuset with one element + if (cpuSet.size() >= 1) { + String testCpuSet = CPUSetsReader.listToString(cpuSet, 1); + testAPCCombo(testCpuSet, 200*1000, 100*1000, 4*1024, true, 1); + } + + // Test subset of cpuset with two elements + if (cpuSet.size() >= 2) { + String testCpuSet = CPUSetsReader.listToString(cpuSet, 2); + testAPCCombo(testCpuSet, 200*1000, 100*1000, 4*1024, true, 2); + testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023, true, 2); + testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023, false, 1); + } + + // Test subset of cpuset with three elements + if (cpuSet.size() >= 3) { + String testCpuSet = CPUSetsReader.listToString(cpuSet, 3); + testAPCCombo(testCpuSet, 100*1000, 100*1000, 2*1024, true, 1); + testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023, true, 2); + testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023, false, 1); + } + } + } + + + private static void testActiveProcessorCount(int valueToSet, int expectedValue) throws Exception { + Common.logNewTestCase("Test ActiveProcessorCount: valueToSet = " + valueToSet); + + DockerRunOptions opts = Common.newOpts(imageName) + .addJavaOpts("-XX:ActiveProcessorCount=" + valueToSet, "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintActiveCpus"); + Common.run(opts) + .shouldMatch("active processor count set by user.*" + expectedValue); + } + + + private static void testCpus(int valueToSet, int expectedTraceValue) throws Exception { + Common.logNewTestCase("test cpus: " + valueToSet); + DockerRunOptions opts = Common.newOpts(imageName) + .addDockerOpts("--cpus", "" + valueToSet); + Common.run(opts) + .shouldMatch("active_processor_count.*" + expectedTraceValue); + } + + + // Expected active processor count can not exceed available CPU count + private static int adjustExpectedAPCForAvailableCPUs(int expectedAPC) { + if (expectedAPC > availableCPUs) { + expectedAPC = availableCPUs; + System.out.println("Adjusted expectedAPC = " + expectedAPC); + } + return expectedAPC; + } + + + private static void testCpuQuotaAndPeriod(int quota, int period) + throws Exception { + Common.logNewTestCase("test cpu quota and period: "); + System.out.println("quota = " + quota); + System.out.println("period = " + period); + + int expectedAPC = (int) Math.ceil((float) quota / (float) period); + System.out.println("expectedAPC = " + expectedAPC); + expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); + + DockerRunOptions opts = Common.newOpts(imageName) + .addDockerOpts("--cpu-period=" + period) + .addDockerOpts("--cpu-quota=" + quota); + + Common.run(opts) + .shouldMatch("CPU Period is.*" + period) + .shouldMatch("CPU Quota is.*" + quota) + .shouldMatch("active_processor_count.*" + expectedAPC); + } + + + // Test correctess of automatically selected active processor cound + private static void testAPCCombo(String cpuset, int quota, int period, int shares, + boolean usePreferContainerQuotaForCPUCount, + int expectedAPC) throws Exception { + Common.logNewTestCase("test APC Combo"); + System.out.println("cpuset = " + cpuset); + System.out.println("quota = " + quota); + System.out.println("period = " + period); + System.out.println("shares = " + period); + System.out.println("usePreferContainerQuotaForCPUCount = " + usePreferContainerQuotaForCPUCount); + System.out.println("expectedAPC = " + expectedAPC); + + expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); + + DockerRunOptions opts = Common.newOpts(imageName) + .addDockerOpts("--cpuset-cpus", "" + cpuset) + .addDockerOpts("--cpu-period=" + period) + .addDockerOpts("--cpu-quota=" + quota) + .addDockerOpts("--cpu-shares=" + shares); + + if (!usePreferContainerQuotaForCPUCount) opts.addJavaOpts("-XX:-PreferContainerQuotaForCPUCount"); + + Common.run(opts) + .shouldMatch("active_processor_count.*" + expectedAPC); + } + + + private static void testCpuShares(int shares, int expectedAPC) throws Exception { + Common.logNewTestCase("test cpu shares, shares = " + shares); + System.out.println("expectedAPC = " + expectedAPC); + + expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); + + DockerRunOptions opts = Common.newOpts(imageName) + .addDockerOpts("--cpu-shares=" + shares); + Common.run(opts) + .shouldMatch("CPU Shares is.*" + shares) + .shouldMatch("active_processor_count.*" + expectedAPC); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/TestCPUSets.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,137 @@ +/* + * 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 JVM's awareness of cpu sets (cpus and mems) + * @library /testlibrary /testlibrary/whitebox + * @build AttemptOOM CPUSetsReader 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 com.oracle.java.testlibrary.Common; +import com.oracle.java.testlibrary.DockerRunOptions; +import com.oracle.java.testlibrary.DockerTestUtils; +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.Platform; +import com.oracle.java.testlibrary.Utils; +import com.oracle.java.testlibrary.OutputAnalyzer; + + +public class TestCPUSets { + private static final String imageName = Common.imageName("cpusets"); + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + Common.prepareWhiteBox(); + DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + + try { + // Sanity test the cpu sets reader and parser + CPUSetsReader.test(); + testTheSet("Cpus_allowed_list"); + testTheSet("Mems_allowed_list"); + } finally { + DockerTestUtils.removeDockerImage(imageName); + } + } + + + private static void testTheSet(String setType) throws Exception { + String cpuSetStr = CPUSetsReader.readFromProcStatus(setType); + + if (cpuSetStr == null) { + System.out.printf("The %s test is skipped %n", setType); + } else { + List<Integer> cpuSet = CPUSetsReader.parseCpuSet(cpuSetStr); + + // Test subset of one, full subset, and half of the subset + testCpuSet(CPUSetsReader.listToString(cpuSet, 1)); + if (cpuSet.size() > 1) { + testCpuSet(CPUSetsReader.listToString(cpuSet)); + } + if (cpuSet.size() > 2) { + testCpuSet(CPUSetsReader.listToString(cpuSet, cpuSet.size()/2 )); + } + } + } + + + private static DockerRunOptions commonOpts() { + DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", + "PrintContainerInfo"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addJavaOpts("-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintContainerInfo", "-cp", "/test-classes/"); + Common.addWhiteBoxOpts(opts); + return opts; + } + + + private static void checkResult(List<String> lines, String lineMarker, String value) { + boolean lineMarkerFound = false; + + for (String line : lines) { + if (line.contains(lineMarker)) { + lineMarkerFound = true; + String[] parts = line.split(":"); + System.out.println("DEBUG: line = " + line); + System.out.println("DEBUG: parts.length = " + parts.length); + + Asserts.assertEquals(parts.length, 2); + String set = parts[1].replaceAll("\\s",""); + String actual = CPUSetsReader.listToString(CPUSetsReader.parseCpuSet(set)); + Asserts.assertEquals(actual, value); + break; + } + } + Asserts.assertTrue(lineMarkerFound); + } + + + private static void testCpuSet(String value) throws Exception { + Common.logNewTestCase("cpusets.cpus, value = " + value); + + DockerRunOptions opts = commonOpts(); + opts.addDockerOpts("--cpuset-cpus=" + value); + + List<String> lines = Common.run(opts).asLines(); + checkResult(lines, "cpuset.cpus is:", value); + } + + private static void testMemSet(String value) throws Exception { + Common.logNewTestCase("cpusets.mems, value = " + value); + + DockerRunOptions opts = commonOpts(); + opts.addDockerOpts("--cpuset-mems=" + value); + + List<String> lines = Common.run(opts).asLines(); + checkResult(lines, "cpuset.mems is:", value); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/TestMemoryAwareness.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,109 @@ +/* + * 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 JVM's memory resource awareness when running inside docker container + * @library /testlibrary /testlibrary/whitebox + * @build AttemptOOM sun.hotspot.WhiteBox PrintContainerInfo + * @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run driver TestMemoryAwareness + */ + +import com.oracle.java.testlibrary.Common; +import com.oracle.java.testlibrary.DockerRunOptions; +import com.oracle.java.testlibrary.DockerTestUtils; + + +public class TestMemoryAwareness { + private static final String imageName = Common.imageName("memory"); + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + Common.prepareWhiteBox(); + DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + + try { + testMemoryLimit("100m", "104857600"); + testMemoryLimit("500m", "524288000"); + testMemoryLimit("1g", "1073741824"); + testMemoryLimit("4g", "4294967296"); + + testMemorySoftLimit("500m", "524288000"); + testMemorySoftLimit("1g", "1073741824"); + + // Add extra 10 Mb to allocator limit, to be sure to cause OOM + testOOM("256m", 256 + 10); + + } finally { + DockerTestUtils.removeDockerImage(imageName); + } + } + + + private static void testMemoryLimit(String valueToSet, String expectedTraceValue) + throws Exception { + + Common.logNewTestCase("memory limit: " + valueToSet); + + DockerRunOptions opts = Common.newOpts(imageName) + .addDockerOpts("--memory", valueToSet); + + Common.run(opts) + .shouldMatch("Memory Limit is:.*" + expectedTraceValue); + } + + + private static void testMemorySoftLimit(String valueToSet, String expectedTraceValue) + throws Exception { + Common.logNewTestCase("memory soft limit: " + valueToSet); + + DockerRunOptions opts = Common.newOpts(imageName, "PrintContainerInfo"); + Common.addWhiteBoxOpts(opts); + opts.addDockerOpts("--memory-reservation=" + valueToSet); + + Common.run(opts) + .shouldMatch("Memory Soft Limit.*" + expectedTraceValue); + } + + + // provoke OOM inside the container, see how VM reacts + private static void testOOM(String dockerMemLimit, int sizeToAllocInMb) throws Exception { + Common.logNewTestCase("OOM"); + + DockerRunOptions opts = Common.newOpts(imageName, "AttemptOOM") + .addDockerOpts("--memory", dockerMemLimit, "--memory-swap", dockerMemLimit); + opts.classParams.add("" + sizeToAllocInMb); + + DockerTestUtils.dockerRunJava(opts) + .shouldHaveExitValue(1) + .shouldContain("Entering AttemptOOM main") + .shouldNotContain("AttemptOOM allocation successful") + .shouldContain("java.lang.OutOfMemoryError"); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/containers/docker/TestMisc.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,113 @@ +/* + * 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 miscellanous functionality related to JVM running in docker container + * @library /testlibrary /testlibrary/whitebox + * @build CheckContainerized sun.hotspot.WhiteBox PrintContainerInfo + * @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run driver TestMisc + */ + +import com.oracle.java.testlibrary.Common; +import com.oracle.java.testlibrary.DockerTestUtils; +import com.oracle.java.testlibrary.DockerRunOptions; +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + + +public class TestMisc { + private static final String imageName = Common.imageName("misc"); + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + Common.prepareWhiteBox(); + DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + + try { + testMinusContainerSupport(); + testIsContainerized(); + testPrintContainerInfo(); + } finally { + DockerTestUtils.removeDockerImage(imageName); + } + } + + + private static void testMinusContainerSupport() throws Exception { + Common.logNewTestCase("Test related flags: '-UseContainerSupport'"); + DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "-version"); + opts.addJavaOpts("-XX:+UnlockDiagnosticVMOptions", "-XX:-UseContainerSupport", "-XX:+PrintContainerInfo"); + + Common.run(opts) + .shouldContain("Container Support not enabled"); + } + + + private static void testIsContainerized() throws Exception { + Common.logNewTestCase("Test is_containerized() inside a docker container"); + + DockerRunOptions opts = Common.newOpts(imageName, "CheckContainerized"); + Common.addWhiteBoxOpts(opts); + + Common.run(opts) + .shouldContain(CheckContainerized.INSIDE_A_CONTAINER); + } + + + private static void testPrintContainerInfo() throws Exception { + Common.logNewTestCase("Test print_container_info()"); + + DockerRunOptions opts = Common.newOpts(imageName, "PrintContainerInfo"); + Common.addWhiteBoxOpts(opts); + + checkContainerInfo(Common.run(opts)); + } + + + private static void checkContainerInfo(OutputAnalyzer out) throws Exception { + String[] expectedToContain = new String[] { + "cpuset.cpus", + "cpuset.mems", + "CPU Shares", + "CPU Quota", + "CPU Period", + "OSContainer::active_processor_count", + "Memory Limit", + "Memory Soft Limit", + "Memory Usage", + "Maximum Memory Usage", + "memory_max_usage_in_bytes" + }; + + for (String s : expectedToContain) { + out.shouldContain(s); + } + } + +}
--- a/test/testlibrary/ClassFileInstaller.java Thu Aug 02 15:15:43 2018 +0300 +++ b/test/testlibrary/ClassFileInstaller.java Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -21,28 +21,229 @@ * questions. */ +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; import java.io.InputStream; +import java.io.ByteArrayInputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** - * Dump a class file for a class on the class path in the current directory + * Dump a class file for a class on the class path in the current directory, or + * in the specified JAR file. This class is usually used when you build a class + * from a test library, but want to use this class in a sub-process. + * + * For example, to build the following library class: + * test/lib/sun/hotspot/WhiteBox.java + * + * You would use the following tags: + * + * @library /test/lib + * @build sun.hotspot.WhiteBox + * + * JTREG would build the class file under + * ${JTWork}/classes/test/lib/sun/hotspot/WhiteBox.class + * + * With you run your main test class using "@run main MyMainClass", JTREG would setup the + * -classpath to include "${JTWork}/classes/test/lib/", so MyMainClass would be able to + * load the WhiteBox class. + * + * However, if you run a sub process, and do not wish to use the exact same -classpath, + * You can use ClassFileInstaller to ensure that WhiteBox is available in the current + * directory of your test: + * + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * + * Or, you can use the -jar option to store the class in the specified JAR file. If a relative + * path name is given, the JAR file would be relative to the current directory of + * + * @run main ClassFileInstaller -jar myjar.jar sun.hotspot.WhiteBox */ public class ClassFileInstaller { /** + * You can enable debug tracing of ClassFileInstaller by running JTREG with + * jtreg -DClassFileInstaller.debug=true ... <names of tests> + */ + public static boolean DEBUG = Boolean.getBoolean("ClassFileInstaller.debug"); + + /** * @param args The names of the classes to dump * @throws Exception */ public static void main(String... args) throws Exception { - for (String arg : args) { - ClassLoader cl = ClassFileInstaller.class.getClassLoader(); + if (args.length > 1 && args[0].equals("-jar")) { + if (args.length < 2) { + throw new RuntimeException("Usage: ClassFileInstaller <options> <classes>\n" + + "where possible options include:\n" + + " -jar <path> Write to the JAR file <path>"); + } + writeJar(args[1], null, args, 2, args.length); + } else { + if (DEBUG) { + System.out.println("ClassFileInstaller: Writing to " + System.getProperty("user.dir")); + } + for (String arg : args) { + writeClassToDisk(arg); + } + } + } + + public static class Manifest { + private InputStream in; + + private Manifest(InputStream in) { + this.in = in; + } + + static Manifest fromSourceFile(String fileName) throws Exception { + String pathName = System.getProperty("test.src") + File.separator + fileName; + return new Manifest(new FileInputStream(pathName)); + } + + // Example: + // String manifest = "Premain-Class: RedefineClassHelper\n" + + // "Can-Redefine-Classes: true\n"; + // ClassFileInstaller.writeJar("redefineagent.jar", + // ClassFileInstaller.Manifest.fromString(manifest), + // "RedefineClassHelper"); + static Manifest fromString(String manifest) throws Exception { + return new Manifest(new ByteArrayInputStream(manifest.getBytes())); + } + + public InputStream getInputStream() { + return in; + } + } + + private static void writeJar(String jarFile, Manifest manifest, String classes[], int from, int to) throws Exception { + if (DEBUG) { + System.out.println("ClassFileInstaller: Writing to " + getJarPath(jarFile)); + } + + (new File(jarFile)).delete(); + FileOutputStream fos = new FileOutputStream(jarFile); + ZipOutputStream zos = new ZipOutputStream(fos); + + // The manifest must be the first or second entry. See comments in JarInputStream + // constructor and JDK-5046178. + if (manifest != null) { + writeToDisk(zos, "META-INF/MANIFEST.MF", manifest.getInputStream()); + } + + for (int i=from; i<to; i++) { + writeClassToDisk(zos, classes[i]); + } + + zos.close(); + fos.close(); + } + + /* + * You can call ClassFileInstaller.writeJar() from your main test class instead of + * using "@run ClassFileInstaller -jar ...". E.g., + * + * String jarPath = ClassFileInstaller.getJarPath("myjar.jar", "sun.hotspot.WhiteBox") + * + * If you call this API, make sure you build ClassFileInstaller with the following tags: + * + * @library testlibrary + * @build ClassFileInstaller + */ + public static String writeJar(String jarFile, String... classes) throws Exception { + writeJar(jarFile, null, classes, 0, classes.length); + return getJarPath(jarFile); + } - // Convert dotted class name to a path to a class file - String pathName = arg.replace('.', '/').concat(".class"); - InputStream is = cl.getResourceAsStream(pathName); + public static String writeJar(String jarFile, Manifest manifest, String... classes) throws Exception { + writeJar(jarFile, manifest, classes, 0, classes.length); + return getJarPath(jarFile); + } + + /** + * This returns the absolute path to the file specified in "@ClassFileInstaller -jar myjar.jar", + * In your test program, instead of using the JAR file name directly: + * + * String jarPath = "myjar.jar"; + * + * you should call this function, like: + * + * String jarPath = ClassFileInstaller.getJarPath("myjar.jar") + * + * The reasons are: + * (1) Using absolute path makes it easy to cut-and-paste from the JTR file and rerun your + * test in any directory. + * (2) In the future, we may make the JAR file name unique to avoid clobbering + * during parallel JTREG execution. + * + */ + public static String getJarPath(String jarFileName) { + return new File(jarFileName).getAbsolutePath(); + } + + public static void writeClassToDisk(String className) throws Exception { + writeClassToDisk((ZipOutputStream)null, className); + } + private static void writeClassToDisk(ZipOutputStream zos, String className) throws Exception { + writeClassToDisk(zos, className, ""); + } + + public static void writeClassToDisk(String className, String prependPath) throws Exception { + writeClassToDisk(null, className, prependPath); + } + private static void writeClassToDisk(ZipOutputStream zos, String className, String prependPath) throws Exception { + ClassLoader cl = ClassFileInstaller.class.getClassLoader(); + // Convert dotted class name to a path to a class file + String pathName = className.replace('.', '/').concat(".class"); + InputStream is = cl.getResourceAsStream(pathName); + if (is == null) { + throw new RuntimeException("Failed to find " + pathName); + } + if (prependPath.length() > 0) { + pathName = prependPath + "/" + pathName; + } + writeToDisk(zos, pathName, is); + } + + public static void writeClassToDisk(String className, byte[] bytecode) throws Exception { + writeClassToDisk(null, className, bytecode); + } + private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode) throws Exception { + writeClassToDisk(zos, className, bytecode, ""); + } + + public static void writeClassToDisk(String className, byte[] bytecode, String prependPath) throws Exception { + writeClassToDisk(null, className, bytecode, prependPath); + } + private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode, String prependPath) throws Exception { + // Convert dotted class name to a path to a class file + String pathName = className.replace('.', '/').concat(".class"); + if (prependPath.length() > 0) { + pathName = prependPath + "/" + pathName; + } + writeToDisk(zos, pathName, new ByteArrayInputStream(bytecode)); + } + + private static void writeToDisk(ZipOutputStream zos, String pathName, InputStream is) throws Exception { + if (DEBUG) { + System.out.println("ClassFileInstaller: Writing " + pathName); + } + if (zos != null) { + ZipEntry ze = new ZipEntry(pathName); + zos.putNextEntry(ze); + byte[] buf = new byte[1024]; + int len; + while ((len = is.read(buf))>0){ + zos.write(buf, 0, len); + } + } else { // Create the class file's package directory Path p = Paths.get(pathName); if (pathName.contains("/")) { @@ -51,5 +252,6 @@ // Create the class file Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING); } + is.close(); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/Common.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,89 @@ +/* + * 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 com.oracle.java.testlibrary; + +/* + * Methods and definitions common to docker tests container in this directory + */ + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import com.oracle.java.testlibrary.DockerTestUtils; +import com.oracle.java.testlibrary.DockerRunOptions; +import com.oracle.java.testlibrary.Utils; +import com.oracle.java.testlibrary.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 { + Path whiteboxPath = Paths.get(Utils.TEST_CLASSES, "whitebox.jar"); + if( !Files.exists(whiteboxPath) ) { + 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("-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintContainerInfo"); + } + + + // 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("-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintContainerInfo", "-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); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/DockerRunOptions.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,73 @@ +/* + * 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 com.oracle.java.testlibrary; + +import java.util.ArrayList; +import java.util.Collections; + + +// This class represents options for running java inside docker containers +// in test environment. +public class DockerRunOptions { + public String imageNameAndTag; + public ArrayList<String> dockerOpts = new ArrayList<String>(); + public String command; // normally a full path to java + public ArrayList<String> javaOpts = new ArrayList<String>(); + public String classToRun; // class or "-version" + public ArrayList<String> classParams = new ArrayList<String>(); + + public boolean tty = true; + public boolean removeContainerAfterUse = true; + public boolean appendTestJavaOptions = true; + public boolean retainChildStdout = false; + + /** + * Convenience constructor for most common use cases in testing. + * @param imageNameAndTag a string representing name and tag for the + * docker image to run, as "name:tag" + * @param javaCmd a java command to run (e.g. /jdk/bin/java) + * @param classToRun a class to run, or "-version" + * @param javaOpts java options to use + * + * @return Default docker run options + */ + public DockerRunOptions(String imageNameAndTag, String javaCmd, + String classToRun, String... javaOpts) { + this.imageNameAndTag = imageNameAndTag; + this.command = javaCmd; + this.classToRun = classToRun; + this.addJavaOpts(javaOpts); + } + + public DockerRunOptions addDockerOpts(String... opts) { + Collections.addAll(dockerOpts, opts); + return this; + } + + public DockerRunOptions addJavaOpts(String... opts) { + Collections.addAll(javaOpts, opts); + return this; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/DockerTestUtils.java Thu Oct 25 12:52:11 2018 +0300 @@ -0,0 +1,282 @@ +/* + * 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 com.oracle.java.testlibrary; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.oracle.java.testlibrary.Utils; +import com.oracle.java.testlibrary.Platform; +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + +public class DockerTestUtils { + private static final String FS = File.separator; + private static boolean isDockerEngineAvailable = false; + private static boolean wasDockerEngineChecked = false; + + // Diagnostics: set to true to enable more diagnostic info + private static final boolean DEBUG = false; + + /** + * Optimized check of whether the docker engine is available in a given + * environment. Checks only once, then remembers the result in a singleton. + * + * @return true if docker engine is available + * @throws Exception + */ + public static boolean isDockerEngineAvailable() throws Exception { + if (wasDockerEngineChecked) + return isDockerEngineAvailable; + + isDockerEngineAvailable = isDockerEngineAvailableCheck(); + wasDockerEngineChecked = true; + return isDockerEngineAvailable; + } + + + /** + * Convenience method, will check if docker engine is available and usable; + * will print the appropriate message when not available. + * + * @return true if docker engine is available + * @throws Exception + */ + public static boolean canTestDocker() throws Exception { + if (isDockerEngineAvailable()) { + return true; + } else { + System.out.println("Docker engine is not available on this system"); + System.out.println("This test is SKIPPED"); + return false; + } + } + + + /** + * Simple check - is docker engine available, accessible and usable. + * Run basic docker command: 'docker ps' - list docker instances. + * If docker engine is available and accesible then true is returned + * and we can proceed with testing docker. + * + * @return true if docker engine is available and usable + * @throws Exception + */ + private static boolean isDockerEngineAvailableCheck() throws Exception { + try { + execute("docker", "ps") + .shouldHaveExitValue(0) + .shouldContain("CONTAINER") + .shouldContain("IMAGE"); + } catch (Exception e) { + return false; + } + return true; + } + + + /** + * Build a docker image that contains JDK under test. + * The jdk will be placed under the "/jdk/" folder inside the docker file system. + * + * @param imageName name of the image to be created, including version tag + * @param dockerfile name of the dockerfile residing in the test source; + * we check for a platform specific dockerfile as well + * and use this one in case it exists + * @param buildDirName name of the docker build/staging directory, which will + * be created in the jtreg's scratch folder + * @throws Exception + */ + public static void + buildJdkDockerImage(String imageName, String dockerfile, String buildDirName) + throws Exception { + Path buildDir = Paths.get(".", buildDirName); + if (Files.exists(buildDir)) { + throw new RuntimeException("The docker build directory already exists: " + buildDir); + } + // check for the existance of a platform specific docker file as well + String platformSpecificDockerfile = dockerfile + "-" + Platform.getOsArch(); + if (Files.exists(Paths.get(Utils.TEST_SRC, platformSpecificDockerfile))) { + dockerfile = platformSpecificDockerfile; + } + + Path jdkSrcDir = Paths.get(Utils.TEST_JDK); + Path jdkDstDir = buildDir.resolve("jdk"); + + Files.createDirectories(jdkDstDir); + + // Copy JDK-under-test tree to the docker build directory. + // This step is required for building a docker image. + Files.walkFileTree(jdkSrcDir, new CopyFileVisitor(jdkSrcDir, jdkDstDir)); + buildDockerImage(imageName, Paths.get(Utils.TEST_SRC, dockerfile), buildDir); + } + + + /** + * Build a docker image based on given docker file and docker build directory. + * + * @param imageName name of the image to be created, including version tag + * @param dockerfile path to the Dockerfile to be used for building the docker + * image. The specified dockerfile will be copied to the docker build + * directory as 'Dockerfile' + * @param buildDir build directory; it should already contain all the content + * needed to build the docker image. + * @throws Exception + */ + public static void + buildDockerImage(String imageName, Path dockerfile, Path buildDir) throws Exception { + // Copy docker file to the build dir + Files.copy(dockerfile, buildDir.resolve("Dockerfile")); + + // Build the docker + execute("docker", "build", "--no-cache", "--tag", imageName, buildDir.toString()) + .shouldHaveExitValue(0) + .shouldContain("Successfully built"); + } + + + /** + * Run Java inside the docker image with specified parameters and options. + * + * @param DockerRunOptions optins for running docker + * + * @return output of the run command + * @throws Exception + */ + public static OutputAnalyzer dockerRunJava(DockerRunOptions opts) throws Exception { + ArrayList<String> cmd = new ArrayList<>(); + + cmd.add("docker"); + cmd.add("run"); + if (opts.tty) + cmd.add("--tty=true"); + if (opts.removeContainerAfterUse) + cmd.add("--rm"); + + cmd.addAll(opts.dockerOpts); + cmd.add(opts.imageNameAndTag); + cmd.add(opts.command); + + cmd.addAll(opts.javaOpts); + if (opts.appendTestJavaOptions) { + Collections.addAll(cmd, Utils.getTestJavaOpts()); + } + + cmd.add(opts.classToRun); + cmd.addAll(opts.classParams); + return execute(cmd); + } + + + /** + * Remove docker image + * + * @param DockerRunOptions optins for running docker + * @return output of the command + * @throws Exception + */ + public static OutputAnalyzer removeDockerImage(String imageNameAndTag) throws Exception { + return execute("docker", "rmi", "--force", imageNameAndTag); + } + + + + /** + * Convenience method - express command as sequence of strings + * + * @param command to execute + * @return The output from the process + * @throws Exception + */ + public static OutputAnalyzer execute(List<String> command) throws Exception { + return execute(command.toArray(new String[command.size()])); + } + + + /** + * Execute a specified command in a process, report diagnostic info. + * + * @param command to be executed + * @return The output from the process + * @throws Exception + */ + public static OutputAnalyzer execute(String... command) throws Exception { + + ProcessBuilder pb = new ProcessBuilder(command); + System.out.println("[COMMAND]\n" + Utils.getCommandLine(pb)); + + long started = System.currentTimeMillis(); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("[ELAPSED: " + (System.currentTimeMillis() - started) + " ms]"); + System.out.println("[STDERR]\n" + output.getStderr()); + System.out.println("[STDOUT]\n" + output.getStdout()); + + return output; + } + + + private static class CopyFileVisitor extends SimpleFileVisitor<Path> { + private final Path src; + private final Path dst; + + public CopyFileVisitor(Path src, Path dst) { + this.src = src; + this.dst = dst; + } + + + @Override + public FileVisitResult preVisitDirectory(Path file, + BasicFileAttributes attrs) throws IOException { + Path dstDir = dst.resolve(src.relativize(file)); + if (!dstDir.toFile().exists()) { + Files.createDirectories(dstDir); + } + return FileVisitResult.CONTINUE; + } + + + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attrs) throws IOException { + if (!file.toFile().isFile()) { + return FileVisitResult.CONTINUE; + } + Path dstFile = dst.resolve(src.relativize(file)); + Files.copy(file, dstFile, StandardCopyOption.COPY_ATTRIBUTES); + return FileVisitResult.CONTINUE; + } + } +}
--- a/test/testlibrary/com/oracle/java/testlibrary/Platform.java Thu Aug 02 15:15:43 2018 +0300 +++ b/test/testlibrary/com/oracle/java/testlibrary/Platform.java Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -32,7 +32,7 @@ private static final String dataModel = System.getProperty("sun.arch.data.model"); private static final String vmVersion = System.getProperty("java.vm.version"); private static final String osArch = System.getProperty("os.arch"); - private static final String vmName = System.getProperty("java.vm.name"); + public static final String vmName = System.getProperty("java.vm.name"); private static final String userName = System.getProperty("user.name"); public static boolean isClient() {
--- a/test/testlibrary/com/oracle/java/testlibrary/Utils.java Thu Aug 02 15:15:43 2018 +0300 +++ b/test/testlibrary/com/oracle/java/testlibrary/Utils.java Thu Oct 25 12:52:11 2018 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -61,6 +61,15 @@ */ public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim(); + public static final String TEST_JDK = System.getProperty("test.jdk"); + + public static final String COMPILE_JDK= System.getProperty("compile.jdk", TEST_JDK); + + public static final String TEST_SRC = System.getProperty("test.src", "").trim(); + + public static final String TEST_CLASSES = System.getProperty("test.classes", "."); + + private static Unsafe unsafe = null; /**
--- a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Thu Aug 02 15:15:43 2018 +0300 +++ b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Thu Oct 25 12:52:11 2018 +0300 @@ -238,4 +238,9 @@ // Returns true on linux if library has the noexecstack flag set. public native boolean checkLibSpecifiesNoexecstack(String libfilename); + + // Container testing + public native boolean isContainerized(); + public native void printOsInfo(); + }