diff --git a/.hgtags b/.hgtags index 8d745626ff0e3878bc47405d9483279d3617fbc0..3fecac8a01d26ac1b7d08460214b3c147fdb7418 100644 --- a/.hgtags +++ b/.hgtags @@ -1292,3 +1292,26 @@ ee19c358e3b8deeda2f64d660a0870df7b1abd49 jdk8u242-b03 2c1e9fab6964647f4eeffe55fe5592da6399a3ce jdk8u242-b05 81ddc1072b923330f84c0ace3124226f63877582 jdk8u242-b06 8b80409d5840142a27e274d33948f483a6406a50 jdk8u242-b07 +7c9f6b5f8d119dc1ba3c5536595ce3ae7414599d jdk8u242-b08 +7c9f6b5f8d119dc1ba3c5536595ce3ae7414599d jdk8u242-ga +8c0733543544bbcd32c4404630d764d280299056 jdk8u252-b00 +a67e9c6edcdd73cb860a16990f0905e102c282d7 jdk8u252-b01 +5bd3b8c0555292a967ea3b4c39a220d0c2cf40ad jdk8u252-b02 +2e972804450764c9ec3b7e548b07be869948ecca jdk8u252-b03 +ff1c3c1867b502c289c7c08ebc712663ec657f06 jdk8u252-b04 +9003f35baaa0d57d225bf72cf82fb0a8ee049c98 jdk8u252-b05 +9003f35baaa0d57d225bf72cf82fb0a8ee049c98 jdk8u262-b00 +b4fd7e078c54a23ed44f09aec3f6938bed3e3630 jdk8u252-b06 +d17814ea88e3aa0a2f2fdb125e35a878ee27fe3e jdk8u252-b07 +8f2780b3e4faf4792ac885f470bb1602d4ca5526 jdk8u252-b08 +095e60e7fc8c7813c2e0e68637bd53c01f8cba64 jdk8u252-b09 +095e60e7fc8c7813c2e0e68637bd53c01f8cba64 jdk8u252-ga +0826fcd359395f47b968f3251ec77061109b2821 jdk8u262-b01 +4373df7c4a92ccf76b94769d9ff6b3f0e26f9659 jdk8u262-b02 +545fe7caa2fb4a6dc4b4f9bba9556e30886ca1f1 jdk8u262-b03 +3a3803a0c789c4d09fd53e54760e7bb7d704ca6e jdk8u262-b04 +f7691a80458c365b5dd754b1e117818144ed30f1 jdk8u262-b05 +de6565b66f9458fb97eb66483e48f159b3f39d36 jdk8u262-b06 +d20a5f399218f58f82f4f4503d24957ce7e48e60 jdk8u262-b07 +d2c2cd90513e48822648ff16016aa76577eb7ab1 jdk8u262-b08 +cf6e3496e19a2957f7d8b28bd0a033cefbf6509f jdk8u262-b09 diff --git a/THIRD_PARTY_README b/THIRD_PARTY_README index 643ea79ce6e2469ba63c072cb29d345611f45af2..a76235de290337682e8ba1678014ac7e405ab356 100644 --- a/THIRD_PARTY_README +++ b/THIRD_PARTY_README @@ -1643,7 +1643,7 @@ July 15, 2018 ------------------------------------------------------------------------------- -%% This notice is provided with respect to GIFLIB 5.1.8 & libungif 4.1.3, +%% This notice is provided with respect to GIFLIB 5.2.1 & libungif 4.1.3, which may be included with JRE 8, JDK 8, and OpenJDK 8. --- begin of LICENSE --- @@ -1712,10 +1712,60 @@ which may be included with JRE 8, JDK 8, and OpenJDK 8 source distributions. --- begin of LICENSE --- + Mesa 3-D Graphics Library v19.2.1 + + Copyright (C) 1999-2007 Brian Paul All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Attention, Contributors + +When contributing to the Mesa project you must agree to the licensing terms +of the component to which you're contributing. +The following section lists the primary components of the Mesa distribution +and their respective licenses. +Mesa Component Licenses + + + +Component Location License +------------------------------------------------------------------ +Main Mesa code src/mesa/ MIT +Device drivers src/mesa/drivers/* MIT, generally + +Gallium code src/gallium/ MIT + +Ext headers GL/glext.h Khronos + GL/glxext.h Khronos + GL/wglext.h Khronos + KHR/khrplatform.h Khronos + +***************************************************************************** + +---- +include/GL/gl.h : + + Mesa 3-D graphics library - Version: 5.0 - Copyright (C) 1999-2002 Brian Paul All Rights Reserved. + Copyright (C) 1999-2006 Brian Paul All Rights Reserved. + Copyright (C) 2009 VMware, Inc. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -1730,9 +1780,67 @@ which may be included with JRE 8, JDK 8, and OpenJDK 8 source distributions. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + ***************************************************************************** + +---- +include/GL/glext.h +include/GL/glxext.h +include/GL/wglxext.h : + + + Copyright (c) 2013 - 2018 The Khronos Group Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and/or associated documentation files (the + "Materials"), to deal in the Materials without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Materials, and to + permit persons to whom the Materials are furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Materials. + + THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + + ***************************************************************************** + +---- +include/KHR/khrplatform.h : + + Copyright (c) 2008 - 2018 The Khronos Group Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and/or associated documentation files (the + "Materials"), to deal in the Materials without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Materials, and to + permit persons to whom the Materials are furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Materials. + + THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + + ***************************************************************************** --- end of LICENSE --- @@ -2790,23 +2898,21 @@ to do so,subject to the following conditions: ------------------------------------------------------------------------------- -%% This notice is provided with respect to X Window System 6.8.2, which may be +%% This notice is provided with respect to xwd v1.0.7, which may be included with JRE 8, JDK 8, and OpenJDK 8 on Linux and Solaris. +xwd utility + --- begin of LICENSE --- -This is the copyright for the files in src/solaris/native/sun/awt: list.h, -multiVis.h, wsutils.h, list.c, multiVis.c -Copyright (c) 1994 Hewlett-Packard Co. -Copyright (c) 1996 X Consortium +Copyright 1994 Hewlett-Packard Co. +Copyright 1996, 1998 The Open Group -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. @@ -2814,15 +2920,15 @@ in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Except as contained in this notice, the name of the X Consortium shall +Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization -from the X Consortium. +from The Open Group. --- end of LICENSE --- _____________________________ diff --git a/agent/src/os/bsd/libproc_impl.c b/agent/src/os/bsd/libproc_impl.c index 78da80617fa08a010de743fd4ad68b9d65a09b74..b8ba361b8a94cbbfb4138d0bc644c4fe09c39cae 100644 --- a/agent/src/os/bsd/libproc_impl.c +++ b/agent/src/os/bsd/libproc_impl.c @@ -215,7 +215,12 @@ lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, return NULL; } - strncpy(newlib->name, libname, sizeof(newlib->name)); + if (strlen(libname) >= sizeof(newlib->name)) { + print_debug("libname %s too long\n", libname); + return NULL; + } + strcpy(newlib->name, libname); + newlib->base = base; if (fd == -1) { diff --git a/agent/src/os/linux/libproc_impl.c b/agent/src/os/linux/libproc_impl.c index ca791c95d662de3fa082a70f3ddc82a8faf30978..73a15ce3517f2d36f2b7e672c901668c7fc5682e 100644 --- a/agent/src/os/linux/libproc_impl.c +++ b/agent/src/os/linux/libproc_impl.c @@ -159,7 +159,12 @@ lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, return NULL; } - strncpy(newlib->name, libname, sizeof(newlib->name)); + if (strlen(libname) >= sizeof(newlib->name)) { + print_debug("libname %s too long\n", libname); + return NULL; + } + strcpy(newlib->name, libname); + newlib->base = base; if (fd == -1) { diff --git a/agent/src/os/linux/ps_core.c b/agent/src/os/linux/ps_core.c index c62e58d0fa239449575a21ad09dbd9d36e162ae8..b7fe4c095d139169a2b174fb6ef67e076ee23b3b 100644 --- a/agent/src/os/linux/ps_core.c +++ b/agent/src/os/linux/ps_core.c @@ -868,6 +868,9 @@ err: #define LINK_MAP_LD_OFFSET offsetof(struct link_map, l_ld) #define LINK_MAP_NEXT_OFFSET offsetof(struct link_map, l_next) +#define INVALID_LOAD_ADDRESS -1L +#define ZERO_LOAD_ADDRESS 0x0L + // Calculate the load address of shared library // on prelink-enabled environment. // @@ -884,7 +887,7 @@ static uintptr_t calc_prelinked_load_address(struct ps_prochandle* ph, int lib_f phbuf = read_program_header_table(lib_fd, elf_ehdr); if (phbuf == NULL) { print_debug("can't read program header of shared object\n"); - return 0L; + return INVALID_LOAD_ADDRESS; } // Get the address of .dynamic section from shared library. @@ -900,7 +903,7 @@ static uintptr_t calc_prelinked_load_address(struct ps_prochandle* ph, int lib_f if (ps_pdread(ph, (psaddr_t)link_map_addr + LINK_MAP_LD_OFFSET, &lib_ld, sizeof(uintptr_t)) != PS_OK) { print_debug("can't read address of dynamic section in shared object\n"); - return 0L; + return INVALID_LOAD_ADDRESS; } // Return the load address which is calculated by the address of .dynamic @@ -1011,9 +1014,9 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { // continue with other libraries... } else { if (read_elf_header(lib_fd, &elf_ehdr)) { - if (lib_base_diff == 0x0L) { + if (lib_base_diff == ZERO_LOAD_ADDRESS) { lib_base_diff = calc_prelinked_load_address(ph, lib_fd, &elf_ehdr, link_map_addr); - if (lib_base_diff == 0x0L) { + if (lib_base_diff == INVALID_LOAD_ADDRESS) { close(lib_fd); return false; } diff --git a/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java b/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java index 7c8d43a287a5c7c7832334a92cdcb8e41ffdde29..e6f0c8f96f7088f3f467682ce58e46efc0b43a4f 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java +++ b/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java @@ -336,12 +336,17 @@ public class NMethod extends CodeBlob { /** This is only for use by the debugging system, and is only intended for use in the topmost frame, where we are not guaranteed to be at a PC for which we have a PCDesc. It finds - the PCDesc with realPC closest to the current PC. */ + the PCDesc with realPC closest to the current PC that has + a valid scope decode offset. */ public PCDesc getPCDescNearDbg(Address pc) { PCDesc bestGuessPCDesc = null; long bestDistance = 0; for (Address p = scopesPCsBegin(); p.lessThan(scopesPCsEnd()); p = p.addOffsetTo(pcDescSize)) { PCDesc pcDesc = new PCDesc(p); + if (pcDesc.getScopeDecodeOffset() == DebugInformationRecorder.SERIALIZED_NULL) { + // We've observed a serialized null decode offset. Ignore this PcDesc. + continue; + } // In case pc is null long distance = -pcDesc.getRealPC(this).minus(pc); if ((bestGuessPCDesc == null) || diff --git a/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java b/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java index fa58440970cb8f4547d6dc8c075d6558c7ea6686..7e5dbb707419788c338466dadc5ba3ff11da66f1 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java +++ b/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java @@ -371,14 +371,14 @@ public class JavaThread extends Thread { Address stackBase = getStackBase(); // Be robust if (sp == null) return false; - return stackBase.greaterThanOrEqual(a) && sp.lessThanOrEqual(a); + return stackBase.greaterThan(a) && sp.lessThanOrEqual(a); } public boolean isLockOwned(Address a) { Address stackBase = getStackBase(); Address stackLimit = stackBase.addOffsetTo(-getStackSize()); - return stackBase.greaterThanOrEqual(a) && stackLimit.lessThanOrEqual(a); + return stackBase.greaterThan(a) && stackLimit.lessThanOrEqual(a); // FIXME: should traverse MonitorArray/MonitorChunks as in VM } diff --git a/make/Makefile b/make/Makefile index 5765f1e36c58fa38d9100021bbd8ce410683d4e2..ad195763beb252378a38eae29f4557f1a96b6952 100644 --- a/make/Makefile +++ b/make/Makefile @@ -575,6 +575,11 @@ $(EXPORT_SERVER_DIR)/Xusage.txt $(EXPORT_CLIENT_DIR)/Xusage.txt $(EXPORT_MINIMAL $(SED) 's/\(separated by \)[;:]/\1$(PATH_SEP)/g' $< > $@.temp $(MV) $@.temp $@ +# Java Flight Recorder +$(EXPORT_JRE_LIB_DIR)/jdk/jfr/internal/types/metadata.xml: $(HS_SRC_DIR)/share/vm/jfr/metadata/metadata.xml + mkdir -p $(basename $@) + cp $< $@ + # # Clean rules # diff --git a/make/aix/makefiles/buildtree.make b/make/aix/makefiles/buildtree.make index 07747c0d1fa7f73020a2bce018a40b84bf7137b0..08cabd8d0f4d24acc18c87a44ef3853170b42049 100644 --- a/make/aix/makefiles/buildtree.make +++ b/make/aix/makefiles/buildtree.make @@ -1,6 +1,6 @@ # # Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2020 SAP SE. All rights reserved. # Copyright 2019 Red Hat, Inc. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # @@ -49,7 +49,7 @@ # flags.make - with macro settings # vm.make - to support making "$(MAKE) -v vm.make" in makefiles # adlc.make - -# trace.make - generate tracing event and type definitions +# jfr.make - generate jfr event and type definitions # jvmti.make - generate JVMTI bindings from the spec (JSR-163) # sa.make - generate SA jar file and natives # @@ -111,6 +111,10 @@ TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name opto -o -n endif endif +ifeq ($(ENABLE_JFR),false) +ALWAYS_EXCLUDE_DIRS += -o -name jfr +endif + # Get things from the platform file. COMPILER = $(shell sed -n 's/^compiler[ ]*=[ ]*//p' $(PLATFORM_FILE)) @@ -118,7 +122,7 @@ SIMPLE_DIRS = \ $(PLATFORM_DIR)/generated/dependencies \ $(PLATFORM_DIR)/generated/adfiles \ $(PLATFORM_DIR)/generated/jvmtifiles \ - $(PLATFORM_DIR)/generated/tracefiles + $(PLATFORM_DIR)/generated/jfrfiles TARGETS = debug fastdebug optimized product SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) @@ -126,7 +130,7 @@ SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) # For dependencies and recursive makes. BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make -BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make sa.make +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make jfr.make sa.make BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) @@ -195,6 +199,12 @@ DATA_MODE/ppc64 = 64 DATA_MODE = $(DATA_MODE/$(BUILDARCH)) +ifeq ($(ENABLE_JFR), true) + INCLUDE_JFR = 1 +else + INCLUDE_JFR = 0 +endif + flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst @echo Creating $@ ... $(QUIETLY) ( \ @@ -274,8 +284,7 @@ flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst echo && \ echo "HOTSPOT_EXTRA_SYSDEFS\$$(HOTSPOT_EXTRA_SYSDEFS) = $(HOTSPOT_EXTRA_SYSDEFS)" && \ echo "SYSDEFS += \$$(HOTSPOT_EXTRA_SYSDEFS)"; \ - [ -n "$(INCLUDE_TRACE)" ] && \ - echo && echo "INCLUDE_TRACE = $(INCLUDE_TRACE)"; \ + echo && echo "CFLAGS += -DINCLUDE_JFR=$(INCLUDE_JFR)"; \ echo; \ [ -n "$(SPEC)" ] && \ echo "include $(SPEC)"; \ @@ -344,7 +353,7 @@ jvmti.make: $(BUILDTREE_MAKE) echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ ) > $@ -trace.make: $(BUILDTREE_MAKE) +jfr.make: $(BUILDTREE_MAKE) @echo Creating $@ ... $(QUIETLY) ( \ $(BUILDTREE_COMMENT); \ diff --git a/make/aix/makefiles/jfr.make b/make/aix/makefiles/jfr.make new file mode 100644 index 0000000000000000000000000000000000000000..a74f8772212c017c6c4a7e29d0d435b50e051d44 --- /dev/null +++ b/make/aix/makefiles/jfr.make @@ -0,0 +1,92 @@ +# +# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020 SAP SE. 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. +# +# + +# This makefile (jfr.make) is included from the jfr.make in the +# build directories. +# +# It knows how to build and run the tools to generate jfr. + +include $(GAMMADIR)/make/linux/makefiles/rules.make + +# ######################################################################### +# Build tools needed for the Jfr source code generation + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated + +JFR_TOOLS_SRCDIR := $(GAMMADIR)/src/share/vm/jfr +JFR_TOOLS_OUTPUTDIR := $(GENERATED)/tools/jfr + +JFR_OUTPUTDIR := $(GENERATED)/jfrfiles +JFR_SRCDIR := $(GAMMADIR)/src/share/vm/jfr/metadata + +METADATA_XML ?= $(JFR_SRCDIR)/metadata.xml +METADATA_XSD ?= $(JFR_SRCDIR)/metadata.xsd + +# Changing these will trigger a rebuild of generated jfr files. +JFR_DEPS += \ + $(METADATA_XML) \ + $(METADATA_XSD) \ + # + +JfrGeneratedNames = \ + jfrEventClasses.hpp \ + jfrEventControl.hpp \ + jfrEventIds.hpp \ + jfrPeriodic.hpp \ + jfrTypes.hpp + +JfrGenSource = $(JFR_TOOLS_SRCDIR)/GenerateJfrFiles.java +JfrGenClass = $(JFR_TOOLS_OUTPUTDIR)/build/tools/jfr/GenerateJfrFiles.class + +JfrGeneratedFiles = $(JfrGeneratedNames:%=$(JFR_OUTPUTDIR/%) + +.PHONY: all clean cleanall + +# ######################################################################### + +all: $(JfrGeneratedFiles) + +$(JfrGenClass): $(JfrGenSource) + mkdir -p $(@D) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -d $(JFR_TOOLS_OUTPUTDIR) $(JfrGenSource) + +$(JFR_OUTPUTDIR)/jfrEventClasses.hpp: $(METADATA_XML) $(METADATA_XSD) $(JfrGenClass) + $(QUIETLY) echo Generating $(@F) + mkdir -p $(@D) + $(QUIETLY) $(REMOTE) $(RUN.JAVA) -cp $(JFR_TOOLS_OUTPUTDIR) build.tools.jfr.GenerateJfrFiles $(METADATA_XML) $(METADATA_XSD) $(JFR_OUTPUTDIR) + test -f $@ + +$(filter-out $(JFR_OUTPUTDIR)/jfrEventClasses.hpp, $(JfrGeneratedFiles)): $(JFR_OUTPUTDIR)/jfrEventClasses.hpp + +TARGETS += $(JFR_OUTPUTDIR)/jfrEventClasses.hpp + +# ######################################################################### + +clean cleanall : + rm $(JfrGenClass) $(JfrGeneratedFiles) + +# ######################################################################### + diff --git a/make/aix/makefiles/mapfile-vers-debug b/make/aix/makefiles/mapfile-vers-debug index 8f28690b4b3fbec162a8cf63d27ab13c477514c9..9d1d947f2119f7765103b032a9ef3b7565a1267e 100644 --- a/make/aix/makefiles/mapfile-vers-debug +++ b/make/aix/makefiles/mapfile-vers-debug @@ -39,6 +39,7 @@ SUNWprivate_1.1 { JVM_ArrayCopy; JVM_AssertionStatusDirectives; JVM_Available; + JVM_BeforeHalt; JVM_Bind; JVM_ClassDepth; JVM_ClassLoaderDepth; diff --git a/make/aix/makefiles/mapfile-vers-product b/make/aix/makefiles/mapfile-vers-product index adcb3fc6da1b57ae4c9495db305cbbd4c73c63f9..a54644dcb4240c4af9bac9c498f050ba4ecda78a 100644 --- a/make/aix/makefiles/mapfile-vers-product +++ b/make/aix/makefiles/mapfile-vers-product @@ -39,6 +39,7 @@ SUNWprivate_1.1 { JVM_ArrayCopy; JVM_AssertionStatusDirectives; JVM_Available; + JVM_BeforeHalt; JVM_Bind; JVM_ClassDepth; JVM_ClassLoaderDepth; diff --git a/make/aix/makefiles/rules.make b/make/aix/makefiles/rules.make index 33898f5c85893b0fa2c82836107cfcebe823daba..f41e0b85d6b762ab770e87a9ac7d416de5e299b7 100644 --- a/make/aix/makefiles/rules.make +++ b/make/aix/makefiles/rules.make @@ -126,8 +126,8 @@ QUIETLY$(MAKE_VERBOSE) = @ RUN.JAR$(MAKE_VERBOSE) += >/dev/null # Settings for javac -BOOT_SOURCE_LANGUAGE_VERSION = 6 -BOOT_TARGET_CLASS_VERSION = 6 +BOOT_SOURCE_LANGUAGE_VERSION = 7 +BOOT_TARGET_CLASS_VERSION = 7 JAVAC_FLAGS = -g -encoding ascii BOOTSTRAP_JAVAC_FLAGS = $(JAVAC_FLAGS) -source $(BOOT_SOURCE_LANGUAGE_VERSION) -target $(BOOT_TARGET_CLASS_VERSION) diff --git a/make/aix/makefiles/top.make b/make/aix/makefiles/top.make index 95e6e6856e81a13eea2aef8a9398902cd3dd5803..b09a4e4561668205a80bb772fd9d739ccf2db7cc 100644 --- a/make/aix/makefiles/top.make +++ b/make/aix/makefiles/top.make @@ -1,5 +1,5 @@ # -# Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1999, 2020, 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 @@ -80,7 +80,7 @@ default: vm_build_preliminaries the_vm @echo All done. # This is an explicit dependency for the sake of parallel makes. -vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) trace_stuff jvmti_stuff sa_stuff +vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jfr_stuff jvmti_stuff sa_stuff @# We need a null action here, so implicit rules don't get consulted. $(Cached_plat): $(Plat_File) @@ -94,9 +94,9 @@ ad_stuff: $(Cached_plat) $(adjust-mflags) jvmti_stuff: $(Cached_plat) $(adjust-mflags) @$(MAKE) -f jvmti.make $(MFLAGS-adjusted) -# generate trace files -trace_stuff: jvmti_stuff $(Cached_plat) $(adjust-mflags) - @$(MAKE) -f trace.make $(MFLAGS-adjusted) +# generate JFR files +jfr_stuff: $(Cached_plat) $(adjust-mflags) + @$(MAKE) -f jfr.make $(MFLAGS-adjusted) # generate SA jar files and native header sa_stuff: diff --git a/make/aix/makefiles/trace.make b/make/aix/makefiles/trace.make deleted file mode 100644 index f173e0ad3aba0dcd89d38c16aec30c8940920384..0000000000000000000000000000000000000000 --- a/make/aix/makefiles/trace.make +++ /dev/null @@ -1,120 +0,0 @@ -# -# Copyright (c) 2003, 2013, 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. -# -# - -# This makefile (trace.make) is included from the trace.make in the -# build directories. -# -# It knows how to build and run the tools to generate trace files. - -include $(GAMMADIR)/make/linux/makefiles/rules.make -include $(GAMMADIR)/make/altsrc.make - -# ######################################################################### - -HAS_ALT_SRC:=$(shell if [ -d $(HS_ALT_SRC)/share/vm/trace ]; then \ - echo "true"; else echo "false";\ - fi) - -TOPDIR = $(shell echo `pwd`) -GENERATED = $(TOPDIR)/../generated -JvmtiOutDir = $(GENERATED)/jvmtifiles -TraceOutDir = $(GENERATED)/tracefiles - -TraceAltSrcDir = $(HS_ALT_SRC)/share/vm/trace -TraceSrcDir = $(HS_COMMON_SRC)/share/vm/trace - -# set VPATH so make knows where to look for source files -Src_Dirs_V += $(TraceSrcDir) $(TraceAltSrcDir) -VPATH += $(Src_Dirs_V:%=%:) - -TraceGeneratedNames = \ - traceEventClasses.hpp \ - traceEventIds.hpp \ - traceTypes.hpp - -ifeq ($(HAS_ALT_SRC), true) -TraceGeneratedNames += \ - traceRequestables.hpp \ - traceEventControl.hpp - -ifneq ($(INCLUDE_TRACE), false) -TraceGeneratedNames += traceProducer.cpp -endif - -endif - -TraceGeneratedFiles = $(TraceGeneratedNames:%=$(TraceOutDir)/%) - -XSLT = $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiGen - -XML_DEPS = $(TraceSrcDir)/trace.xml $(TraceSrcDir)/tracetypes.xml \ - $(TraceSrcDir)/trace.dtd $(TraceSrcDir)/xinclude.mod -ifeq ($(HAS_ALT_SRC), true) - XML_DEPS += $(TraceAltSrcDir)/traceevents.xml -endif - -.PHONY: all clean cleanall - -# ######################################################################### - -all: $(TraceGeneratedFiles) - -GENERATE_CODE= \ - $(QUIETLY) echo Generating $@; \ - $(XSLT) -IN $(word 1,$^) -XSL $(word 2,$^) -OUT $@; \ - test -f $@ - -$(TraceOutDir)/traceEventIds.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventIds.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceTypes.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceTypes.xsl $(XML_DEPS) - $(GENERATE_CODE) - -ifeq ($(HAS_ALT_SRC), false) - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventClasses.xsl $(XML_DEPS) - $(GENERATE_CODE) - -else - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventClasses.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceProducer.cpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceProducer.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceRequestables.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceRequestables.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceEventControl.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventControl.xsl $(XML_DEPS) - $(GENERATE_CODE) - -endif - -# ######################################################################### - -clean cleanall: - rm $(TraceGeneratedFiles) - - diff --git a/make/aix/makefiles/vm.make b/make/aix/makefiles/vm.make index 52c88b3a3af4ccd93cda9f2cc47aad31d151c32f..2e565231f469e256495efb33efade2c13b74b549 100644 --- a/make/aix/makefiles/vm.make +++ b/make/aix/makefiles/vm.make @@ -1,6 +1,6 @@ # # Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2020 SAP SE. 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 @@ -53,7 +53,7 @@ endif # Src_Dirs_V is everything in src/share/vm/*, plus the right os/*/vm and cpu/*/vm # The adfiles directory contains ad_.[ch]pp. # The jvmtifiles directory contains jvmti*.[ch]pp -Src_Dirs_V += $(GENERATED)/adfiles $(GENERATED)/jvmtifiles $(GENERATED)/tracefiles +Src_Dirs_V += $(GENERATED)/adfiles $(GENERATED)/jvmtifiles $(GENERATED)/jfrfiles VPATH += $(Src_Dirs_V:%=%:) # set INCLUDES for C preprocessor. @@ -119,6 +119,7 @@ CFLAGS += $(CFLAGS/NOEX) # Extra flags from gnumake's invocation or environment CFLAGS += $(EXTRA_CFLAGS) LFLAGS += $(EXTRA_CFLAGS) $(EXTRA_LDFLAGS) +ASFLAGS += $(EXTRA_ASFLAGS) # Don't set excutable bit on stack segment # the same could be done by separate execstack command @@ -146,24 +147,22 @@ CFLAGS += -DALLOW_OPERATOR_NEW_USAGE LIBJVM_DEBUGINFO = lib$(JVM).debuginfo LIBJVM_DIZ = lib$(JVM).diz +ifeq ($(ENABLE_JFR),false) +EXCLUDE_JFR_PATHS:= -o -name jfr -prune +endif + SPECIAL_PATHS:=adlc c1 gc_implementation opto shark libadt SOURCE_PATHS=\ $(shell find $(HS_COMMON_SRC)/share/vm/* -type d \! \ - \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) \)) + \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) $(EXCLUDE_JFR_PATHS) \)) SOURCE_PATHS+=$(HS_COMMON_SRC)/os/$(Platform_os_family)/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/os/posix/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/cpu/$(SRCARCH)/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/os_cpu/$(Platform_os_family)_$(SRCARCH)/vm CORE_PATHS=$(foreach path,$(SOURCE_PATHS),$(call altsrc,$(path)) $(path)) -CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/tracefiles - -ifneq ($(INCLUDE_TRACE), false) -CORE_PATHS+=$(shell if [ -d $(HS_ALT_SRC)/share/vm/jfr ]; then \ - find $(HS_ALT_SRC)/share/vm/jfr -type d; \ - fi) -endif +CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/jfrfiles COMPILER1_PATHS := $(call altsrc,$(HS_COMMON_SRC)/share/vm/c1) COMPILER1_PATHS += $(HS_COMMON_SRC)/share/vm/c1 diff --git a/make/bsd/makefiles/buildtree.make b/make/bsd/makefiles/buildtree.make index b39bf2236500ce165e14d7260da274b1a957a832..d03a5d53f6a010442c6bd69eb1c8ee6fac81496e 100644 --- a/make/bsd/makefiles/buildtree.make +++ b/make/bsd/makefiles/buildtree.make @@ -48,7 +48,7 @@ # flags.make - with macro settings # vm.make - to support making "$(MAKE) -v vm.make" in makefiles # adlc.make - -# trace.make - generate tracing event and type definitions +# jfr.make - generate jfr event and type definitions # jvmti.make - generate JVMTI bindings from the spec (JSR-163) # sa.make - generate SA jar file and natives # @@ -113,6 +113,10 @@ TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name opto -o -n endif endif +ifeq ($(ENABLE_JFR),false) +ALWAYS_EXCLUDE_DIRS += -o -name jfr +endif + # Get things from the platform file. COMPILER = $(shell sed -n 's/^compiler[ ]*=[ ]*//p' $(PLATFORM_FILE)) @@ -121,7 +125,7 @@ SIMPLE_DIRS = \ $(PLATFORM_DIR)/generated/dependencies \ $(PLATFORM_DIR)/generated/adfiles \ $(PLATFORM_DIR)/generated/jvmtifiles \ - $(PLATFORM_DIR)/generated/tracefiles \ + $(PLATFORM_DIR)/generated/jfrfiles \ $(PLATFORM_DIR)/generated/dtracefiles TARGETS = debug fastdebug optimized product @@ -131,7 +135,7 @@ SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make # dtrace.make is used on BSD versions that implement Dtrace (like MacOS X) -BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make sa.make dtrace.make +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make jfr.make sa.make dtrace.make BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) @@ -201,6 +205,12 @@ DATA_MODE/amd64 = 64 DATA_MODE = $(DATA_MODE/$(BUILDARCH)) +ifeq ($(ENABLE_JFR), true) + INCLUDE_JFR = 1 +else + INCLUDE_JFR = 0 +endif + flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst @echo Creating $@ ... $(QUIETLY) ( \ @@ -280,6 +290,7 @@ flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst echo && \ echo "HOTSPOT_EXTRA_SYSDEFS\$$(HOTSPOT_EXTRA_SYSDEFS) = $(HOTSPOT_EXTRA_SYSDEFS)" && \ echo "SYSDEFS += \$$(HOTSPOT_EXTRA_SYSDEFS)"; \ + echo && echo "CFLAGS += -DINCLUDE_JFR=$(INCLUDE_JFR)"; \ echo; \ [ -n "$(SPEC)" ] && \ echo "include $(SPEC)"; \ @@ -348,7 +359,7 @@ jvmti.make: $(BUILDTREE_MAKE) echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ ) > $@ -trace.make: $(BUILDTREE_MAKE) +jfr.make: $(BUILDTREE_MAKE) @echo Creating $@ ... $(QUIETLY) ( \ $(BUILDTREE_COMMENT); \ diff --git a/make/bsd/makefiles/gcc.make b/make/bsd/makefiles/gcc.make index 9571ad7ad9745dc344016e4358f56efca8e88746..52885199c612223f071d57df416158febb27d54b 100644 --- a/make/bsd/makefiles/gcc.make +++ b/make/bsd/makefiles/gcc.make @@ -260,14 +260,13 @@ ifeq ($(USE_CLANG), true) WARNINGS_ARE_ERRORS += -Wno-empty-body endif -WARNING_FLAGS = -Wpointer-arith -Wsign-compare -Wundef +WARNING_FLAGS = -Wpointer-arith -Wsign-compare -Wundef -Wunused-function -Wunused-value -ifeq "$(shell expr \( $(CC_VER_MAJOR) \> 4 \) \| \( \( $(CC_VER_MAJOR) = 4 \) \& \( $(CC_VER_MINOR) \>= 3 \) \))" "0" +ifeq ($(USE_CLANG),) # Since GCC 4.3, -Wconversion has changed its meanings to warn these implicit # conversions which might affect the values. Only enable it in earlier versions. - WARNING_FLAGS = -Wunused-function - ifeq ($(USE_CLANG),) - WARNING_FLAGS += -Wconversion + ifeq "$(shell expr \( $(CC_VER_MAJOR) \> 4 \) \| \( \( $(CC_VER_MAJOR) = 4 \) \& \( $(CC_VER_MINOR) \>= 3 \) \))" "0" + WARNINGS_FLAGS += -Wconversion endif endif diff --git a/make/bsd/makefiles/jfr.make b/make/bsd/makefiles/jfr.make new file mode 100644 index 0000000000000000000000000000000000000000..d1254dd06ee59d0f646b5897b5fb995974230f15 --- /dev/null +++ b/make/bsd/makefiles/jfr.make @@ -0,0 +1,92 @@ +# +# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018-2019, Azul Systems, Inc. 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. +# +# + +# This makefile (jfr.make) is included from the jfr.make in the +# build directories. +# +# It knows how to build and run the tools to generate jfr. + +include $(GAMMADIR)/make/bsd/makefiles/rules.make + +# ######################################################################### +# Build tools needed for the Jfr source code generation + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated + +JFR_TOOLS_SRCDIR := $(GAMMADIR)/src/share/vm/jfr +JFR_TOOLS_OUTPUTDIR := $(GENERATED)/tools/jfr + +JFR_OUTPUTDIR := $(GENERATED)/jfrfiles +JFR_SRCDIR := $(GAMMADIR)/src/share/vm/jfr/metadata + +METADATA_XML ?= $(JFR_SRCDIR)/metadata.xml +METADATA_XSD ?= $(JFR_SRCDIR)/metadata.xsd + +# Changing these will trigger a rebuild of generated jfr files. +JFR_DEPS += \ + $(METADATA_XML) \ + $(METADATA_XSD) \ + # + +JfrGeneratedNames = \ + jfrEventClasses.hpp \ + jfrEventControl.hpp \ + jfrEventIds.hpp \ + jfrPeriodic.hpp \ + jfrTypes.hpp + +JfrGenSource = $(JFR_TOOLS_SRCDIR)/GenerateJfrFiles.java +JfrGenClass = $(JFR_TOOLS_OUTPUTDIR)/build/tools/jfr/GenerateJfrFiles.class + +JfrGeneratedFiles = $(JfrGeneratedNames:%=$(JFR_OUTPUTDIR/%) + +.PHONY: all clean cleanall + +# ######################################################################### + +all: $(JfrGeneratedFiles) + +$(JfrGenClass): $(JfrGenSource) + mkdir -p $(@D) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -d $(JFR_TOOLS_OUTPUTDIR) $(JfrGenSource) + +$(JFR_OUTPUTDIR)/jfrEventClasses.hpp: $(METADATA_XML) $(METADATA_XSD) $(JfrGenClass) + $(QUIETLY) echo Generating $(@F) + mkdir -p $(@D) + $(QUIETLY) $(REMOTE) $(RUN.JAVA) -cp $(JFR_TOOLS_OUTPUTDIR) build.tools.jfr.GenerateJfrFiles $(METADATA_XML) $(METADATA_XSD) $(JFR_OUTPUTDIR) + test -f $@ + +$(filter-out $(JFR_OUTPUTDIR)/jfrEventClasses.hpp, $(JfrGeneratedFiles)): $(JFR_OUTPUTDIR)/jfrEventClasses.hpp + +TARGETS += $(JFR_OUTPUTDIR)/jfrEventClasses.hpp + +# ######################################################################### + +clean cleanall : + rm $(JfrGenClass) $(JfrGeneratedFiles) + +# ######################################################################### + diff --git a/make/bsd/makefiles/mapfile-vers-debug b/make/bsd/makefiles/mapfile-vers-debug index 28532cb8cbf597a39984a9016e313855a181096d..4e44d66ba14bf7c935a8c8bf6da232e89ff06a66 100644 --- a/make/bsd/makefiles/mapfile-vers-debug +++ b/make/bsd/makefiles/mapfile-vers-debug @@ -37,6 +37,7 @@ _JVM_ArrayCopy _JVM_AssertionStatusDirectives _JVM_Available + _JVM_BeforeHalt _JVM_Bind _JVM_CheckJWarmUpCompilationIsComplete _JVM_ClassDepth diff --git a/make/bsd/makefiles/mapfile-vers-product b/make/bsd/makefiles/mapfile-vers-product index 64f05334d19695e34600fd20d0df5e89f45a7a32..e3e4b8814483a6c6ea6249c8ed51f98a5da08edf 100644 --- a/make/bsd/makefiles/mapfile-vers-product +++ b/make/bsd/makefiles/mapfile-vers-product @@ -37,6 +37,7 @@ _JVM_ArrayCopy _JVM_AssertionStatusDirectives _JVM_Available + _JVM_BeforeHalt _JVM_Bind _JVM_CheckJWarmUpCompilationIsComplete _JVM_ClassDepth diff --git a/make/bsd/makefiles/rules.make b/make/bsd/makefiles/rules.make index d40b88d5ed02f31dc12d2afbd4b43213d275a7a1..31c2e0941ca2dfbb04f46350eedc2a2d11fcad6f 100644 --- a/make/bsd/makefiles/rules.make +++ b/make/bsd/makefiles/rules.make @@ -126,8 +126,8 @@ QUIETLY$(MAKE_VERBOSE) = @ RUN.JAR$(MAKE_VERBOSE) += >/dev/null # Settings for javac -BOOT_SOURCE_LANGUAGE_VERSION = 6 -BOOT_TARGET_CLASS_VERSION = 6 +BOOT_SOURCE_LANGUAGE_VERSION = 7 +BOOT_TARGET_CLASS_VERSION = 7 JAVAC_FLAGS = -g -encoding ascii BOOTSTRAP_JAVAC_FLAGS = $(JAVAC_FLAGS) -source $(BOOT_SOURCE_LANGUAGE_VERSION) -target $(BOOT_TARGET_CLASS_VERSION) diff --git a/make/bsd/makefiles/top.make b/make/bsd/makefiles/top.make index aebadebe35cec9ffd649f0e1d1e37238cbb6d9db..472b439ed0a8a6bbd1ea62f76017bb27e542b4bf 100644 --- a/make/bsd/makefiles/top.make +++ b/make/bsd/makefiles/top.make @@ -80,7 +80,7 @@ default: vm_build_preliminaries the_vm @echo All done. # This is an explicit dependency for the sake of parallel makes. -vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jvmti_stuff trace_stuff sa_stuff dtrace_stuff +vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jvmti_stuff jfr_stuff sa_stuff dtrace_stuff @# We need a null action here, so implicit rules don't get consulted. $(Cached_plat): $(Plat_File) @@ -94,9 +94,9 @@ ad_stuff: $(Cached_plat) $(adjust-mflags) jvmti_stuff: $(Cached_plat) $(adjust-mflags) @$(MAKE) -f jvmti.make $(MFLAGS-adjusted) -# generate trace files -trace_stuff: jvmti_stuff $(Cached_plat) $(adjust-mflags) - @$(MAKE) -f trace.make $(MFLAGS-adjusted) +# generate JFR files +jfr_stuff: $(Cached_plat) $(adjust-mflags) + @$(MAKE) -f jfr.make $(MFLAGS-adjusted) ifeq ($(OS_VENDOR), Darwin) # generate dtrace header files diff --git a/make/bsd/makefiles/trace.make b/make/bsd/makefiles/trace.make deleted file mode 100644 index ceb40c87846f42b9e2ba80a2044ec3291338a4bf..0000000000000000000000000000000000000000 --- a/make/bsd/makefiles/trace.make +++ /dev/null @@ -1,121 +0,0 @@ -# -# Copyright (c) 2003, 2013, 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. -# -# - -# This makefile (trace.make) is included from the trace.make in the -# build directories. -# -# It knows how to build and run the tools to generate trace files. - -include $(GAMMADIR)/make/bsd/makefiles/rules.make -include $(GAMMADIR)/make/altsrc.make - -# ######################################################################### - -HAS_ALT_SRC:=$(shell if [ -d $(HS_ALT_SRC)/share/vm/trace ]; then \ - echo "true"; else echo "false";\ - fi) - -TOPDIR = $(shell echo `pwd`) -GENERATED = $(TOPDIR)/../generated -JvmtiOutDir = $(GENERATED)/jvmtifiles -TraceOutDir = $(GENERATED)/tracefiles - -TraceAltSrcDir = $(HS_ALT_SRC)/share/vm/trace -TraceSrcDir = $(HS_COMMON_SRC)/share/vm/trace - -# set VPATH so make knows where to look for source files -Src_Dirs_V += $(TraceSrcDir) $(TraceAltSrcDir) -VPATH += $(Src_Dirs_V:%=%:) - -TraceGeneratedNames = \ - traceEventClasses.hpp \ - traceEventIds.hpp \ - traceTypes.hpp - -ifeq ($(HAS_ALT_SRC), true) -TraceGeneratedNames += \ - traceRequestables.hpp \ - traceEventControl.hpp - -ifneq ($(INCLUDE_TRACE), false) -TraceGeneratedNames += traceProducer.cpp -endif - -endif - - -TraceGeneratedFiles = $(TraceGeneratedNames:%=$(TraceOutDir)/%) - -XSLT = $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiGen - -XML_DEPS = $(TraceSrcDir)/trace.xml $(TraceSrcDir)/tracetypes.xml \ - $(TraceSrcDir)/trace.dtd $(TraceSrcDir)/xinclude.mod -ifeq ($(HAS_ALT_SRC), true) - XML_DEPS += $(TraceAltSrcDir)/traceevents.xml -endif - -.PHONY: all clean cleanall - -# ######################################################################### - -all: $(TraceGeneratedFiles) - -GENERATE_CODE= \ - $(QUIETLY) echo Generating $@; \ - $(XSLT) -IN $(word 1,$^) -XSL $(word 2,$^) -OUT $@; \ - test -f $@ - -$(TraceOutDir)/traceEventIds.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventIds.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceTypes.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceTypes.xsl $(XML_DEPS) - $(GENERATE_CODE) - -ifeq ($(HAS_ALT_SRC), false) - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventClasses.xsl $(XML_DEPS) - $(GENERATE_CODE) - -else - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventClasses.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceProducer.cpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceProducer.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceRequestables.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceRequestables.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceEventControl.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventControl.xsl $(XML_DEPS) - $(GENERATE_CODE) - -endif - -# ######################################################################### - - -clean cleanall: - rm $(TraceGeneratedFiles) - diff --git a/make/bsd/makefiles/vm.make b/make/bsd/makefiles/vm.make index f07338c6be6184204a273d7a3d70760f7582861f..dd37f44f5a907d0ba12378c7260afa68d2adf719 100644 --- a/make/bsd/makefiles/vm.make +++ b/make/bsd/makefiles/vm.make @@ -52,7 +52,7 @@ endif # Src_Dirs_V is everything in src/share/vm/*, plus the right os/*/vm and cpu/*/vm # The adfiles directory contains ad_.[ch]pp. # The jvmtifiles directory contains jvmti*.[ch]pp -Src_Dirs_V += $(GENERATED)/adfiles $(GENERATED)/jvmtifiles $(GENERATED)/tracefiles +Src_Dirs_V += $(GENERATED)/adfiles $(GENERATED)/jvmtifiles $(GENERATED)/jfrfiles VPATH += $(Src_Dirs_V:%=%:) # set INCLUDES for C preprocessor. @@ -121,6 +121,7 @@ CFLAGS += $(CFLAGS/NOEX) # Extra flags from gnumake's invocation or environment CFLAGS += $(EXTRA_CFLAGS) LFLAGS += $(EXTRA_CFLAGS) $(EXTRA_LDFLAGS) +ASFLAGS += $(EXTRA_ASFLAGS) # Don't set excutable bit on stack segment # the same could be done by separate execstack command @@ -164,24 +165,21 @@ else LIBJVM_DIZ = lib$(JVM).diz endif +ifeq ($(ENABLE_JFR),false) +EXCLUDE_JFR_PATHS:= -o -name jfr -prune +endif SPECIAL_PATHS:=adlc c1 gc_implementation opto shark libadt SOURCE_PATHS=\ $(shell find $(HS_COMMON_SRC)/share/vm/* -type d \! \ - \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) \)) + \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) $(EXCLUDE_JFR_PATHS) \)) SOURCE_PATHS+=$(HS_COMMON_SRC)/os/$(Platform_os_family)/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/os/posix/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/cpu/$(Platform_arch)/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/os_cpu/$(Platform_os_arch)/vm CORE_PATHS=$(foreach path,$(SOURCE_PATHS),$(call altsrc,$(path)) $(path)) -CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/tracefiles - -ifneq ($(INCLUDE_TRACE), false) -CORE_PATHS+=$(shell if [ -d $(HS_ALT_SRC)/share/vm/jfr ]; then \ - find $(HS_ALT_SRC)/share/vm/jfr -type d; \ - fi) -endif +CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/jfrfiles COMPILER1_PATHS := $(call altsrc,$(HS_COMMON_SRC)/share/vm/c1) COMPILER1_PATHS += $(HS_COMMON_SRC)/share/vm/c1 diff --git a/make/defs.make b/make/defs.make index a6f7ed871513435ac99581ae793eec7420ed46c0..0a591420116e06397b195c4ef2d832ed490d31c1 100644 --- a/make/defs.make +++ b/make/defs.make @@ -372,5 +372,9 @@ EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/$(JDK_INCLUDE_SUBDIR)/jni_md.h EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jmm.h EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/tenantenv.h +ifeq ($(ENABLE_JFR), true) +EXPORT_LIST += $(EXPORT_JRE_LIB_DIR)/jdk/jfr/internal/types/metadata.xml +endif + .PHONY: $(HS_ALT_MAKE)/defs.make diff --git a/make/linux/makefiles/buildtree.make b/make/linux/makefiles/buildtree.make index bdd077a3f9cc1524eb3509f01939bc2248135b0b..f3eabb2e65d2354b3298e93f20987d65194aa385 100644 --- a/make/linux/makefiles/buildtree.make +++ b/make/linux/makefiles/buildtree.make @@ -48,7 +48,7 @@ # flags.make - with macro settings # vm.make - to support making "$(MAKE) -v vm.make" in makefiles # adlc.make - -# trace.make - generate tracing event and type definitions +# jfr.make - generate jfr event and type definitions # jvmti.make - generate JVMTI bindings from the spec (JSR-163) # sa.make - generate SA jar file and natives # @@ -112,6 +112,10 @@ TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name opto -o -n endif endif +ifeq ($(ENABLE_JFR),false) +ALWAYS_EXCLUDE_DIRS += -o -name jfr +endif + # Get things from the platform file. COMPILER = $(shell sed -n 's/^compiler[ ]*=[ ]*//p' $(PLATFORM_FILE)) @@ -119,7 +123,7 @@ SIMPLE_DIRS = \ $(PLATFORM_DIR)/generated/dependencies \ $(PLATFORM_DIR)/generated/adfiles \ $(PLATFORM_DIR)/generated/jvmtifiles \ - $(PLATFORM_DIR)/generated/tracefiles + $(PLATFORM_DIR)/generated/jfrfiles TARGETS = debug fastdebug optimized product SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) @@ -127,7 +131,7 @@ SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) # For dependencies and recursive makes. BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make -BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make sa.make +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make jfr.make sa.make BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) @@ -200,6 +204,13 @@ DATA_MODE/ppc64 = 64 DATA_MODE = $(DATA_MODE/$(BUILDARCH)) +ifeq ($(ENABLE_JFR), true) + INCLUDE_JFR = 1 +else + INCLUDE_JFR = 0 +endif + + flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst @echo Creating $@ ... $(QUIETLY) ( \ @@ -279,8 +290,7 @@ flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst echo && \ echo "HOTSPOT_EXTRA_SYSDEFS\$$(HOTSPOT_EXTRA_SYSDEFS) = $(HOTSPOT_EXTRA_SYSDEFS)" && \ echo "SYSDEFS += \$$(HOTSPOT_EXTRA_SYSDEFS)"; \ - [ -n "$(INCLUDE_TRACE)" ] && \ - echo && echo "INCLUDE_TRACE = $(INCLUDE_TRACE)"; \ + echo && echo "CFLAGS += -DINCLUDE_JFR=$(INCLUDE_JFR)"; \ echo; \ [ -n "$(SPEC)" ] && \ echo "include $(SPEC)"; \ @@ -349,7 +359,7 @@ jvmti.make: $(BUILDTREE_MAKE) echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ ) > $@ -trace.make: $(BUILDTREE_MAKE) +jfr.make: $(BUILDTREE_MAKE) @echo Creating $@ ... $(QUIETLY) ( \ $(BUILDTREE_COMMENT); \ diff --git a/make/linux/makefiles/jfr.make b/make/linux/makefiles/jfr.make new file mode 100644 index 0000000000000000000000000000000000000000..5065bf0fae0e1ec49a1a80d4bd027da2cf4f699c --- /dev/null +++ b/make/linux/makefiles/jfr.make @@ -0,0 +1,92 @@ +# +# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018-2019, Azul Systems, Inc. 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. +# +# + +# This makefile (jfr.make) is included from the jfr.make in the +# build directories. +# +# It knows how to build and run the tools to generate jfr. + +include $(GAMMADIR)/make/linux/makefiles/rules.make + +# ######################################################################### +# Build tools needed for the Jfr source code generation + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated + +JFR_TOOLS_SRCDIR := $(GAMMADIR)/src/share/vm/jfr +JFR_TOOLS_OUTPUTDIR := $(GENERATED)/tools/jfr + +JFR_OUTPUTDIR := $(GENERATED)/jfrfiles +JFR_SRCDIR := $(GAMMADIR)/src/share/vm/jfr/metadata + +METADATA_XML ?= $(JFR_SRCDIR)/metadata.xml +METADATA_XSD ?= $(JFR_SRCDIR)/metadata.xsd + +# Changing these will trigger a rebuild of generated jfr files. +JFR_DEPS += \ + $(METADATA_XML) \ + $(METADATA_XSD) \ + # + +JfrGeneratedNames = \ + jfrEventClasses.hpp \ + jfrEventControl.hpp \ + jfrEventIds.hpp \ + jfrPeriodic.hpp \ + jfrTypes.hpp + +JfrGenSource = $(JFR_TOOLS_SRCDIR)/GenerateJfrFiles.java +JfrGenClass = $(JFR_TOOLS_OUTPUTDIR)/build/tools/jfr/GenerateJfrFiles.class + +JfrGeneratedFiles = $(JfrGeneratedNames:%=$(JFR_OUTPUTDIR/%) + +.PHONY: all clean cleanall + +# ######################################################################### + +all: $(JfrGeneratedFiles) + +$(JfrGenClass): $(JfrGenSource) + mkdir -p $(@D) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -d $(JFR_TOOLS_OUTPUTDIR) $(JfrGenSource) + +$(JFR_OUTPUTDIR)/jfrEventClasses.hpp: $(METADATA_XML) $(METADATA_XSD) $(JfrGenClass) + $(QUIETLY) echo Generating $(@F) + mkdir -p $(@D) + $(QUIETLY) $(REMOTE) $(RUN.JAVA) -cp $(JFR_TOOLS_OUTPUTDIR) build.tools.jfr.GenerateJfrFiles $(METADATA_XML) $(METADATA_XSD) $(JFR_OUTPUTDIR) + test -f $@ + +$(filter-out $(JFR_OUTPUTDIR)/jfrEventClasses.hpp, $(JfrGeneratedFiles)): $(JFR_OUTPUTDIR)/jfrEventClasses.hpp + +TARGETS += $(JFR_OUTPUTDIR)/jfrEventClasses.hpp + +# ######################################################################### + +clean cleanall : + rm $(JfrGenClass) $(JfrGeneratedFiles) + +# ######################################################################### + diff --git a/make/linux/makefiles/mapfile-vers-debug b/make/linux/makefiles/mapfile-vers-debug index 9b73771152f0448109e78fdaa13ae633e4017b70..cb2562ba2b9b6dde62d5e60ce6484eb23cd32a62 100644 --- a/make/linux/makefiles/mapfile-vers-debug +++ b/make/linux/makefiles/mapfile-vers-debug @@ -39,6 +39,7 @@ SUNWprivate_1.1 { JVM_ArrayCopy; JVM_AssertionStatusDirectives; JVM_Available; + JVM_BeforeHalt; JVM_Bind; JVM_CheckJWarmUpCompilationIsComplete; JVM_ClassDepth; diff --git a/make/linux/makefiles/mapfile-vers-product b/make/linux/makefiles/mapfile-vers-product index bb0d34765613a86a90f7ee51621207862f363d63..5ccecf9c68f8027602fbdbd4d31b232d9b8264f5 100644 --- a/make/linux/makefiles/mapfile-vers-product +++ b/make/linux/makefiles/mapfile-vers-product @@ -39,6 +39,7 @@ SUNWprivate_1.1 { JVM_ArrayCopy; JVM_AssertionStatusDirectives; JVM_Available; + JVM_BeforeHalt; JVM_Bind; JVM_CheckJWarmUpCompilationIsComplete; JVM_ClassDepth; diff --git a/make/linux/makefiles/rules.make b/make/linux/makefiles/rules.make index d40b88d5ed02f31dc12d2afbd4b43213d275a7a1..31c2e0941ca2dfbb04f46350eedc2a2d11fcad6f 100644 --- a/make/linux/makefiles/rules.make +++ b/make/linux/makefiles/rules.make @@ -126,8 +126,8 @@ QUIETLY$(MAKE_VERBOSE) = @ RUN.JAR$(MAKE_VERBOSE) += >/dev/null # Settings for javac -BOOT_SOURCE_LANGUAGE_VERSION = 6 -BOOT_TARGET_CLASS_VERSION = 6 +BOOT_SOURCE_LANGUAGE_VERSION = 7 +BOOT_TARGET_CLASS_VERSION = 7 JAVAC_FLAGS = -g -encoding ascii BOOTSTRAP_JAVAC_FLAGS = $(JAVAC_FLAGS) -source $(BOOT_SOURCE_LANGUAGE_VERSION) -target $(BOOT_TARGET_CLASS_VERSION) diff --git a/make/linux/makefiles/top.make b/make/linux/makefiles/top.make index 95e6e6856e81a13eea2aef8a9398902cd3dd5803..648ae29160f808edf2855199a767cd88e30d3842 100644 --- a/make/linux/makefiles/top.make +++ b/make/linux/makefiles/top.make @@ -80,7 +80,7 @@ default: vm_build_preliminaries the_vm @echo All done. # This is an explicit dependency for the sake of parallel makes. -vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) trace_stuff jvmti_stuff sa_stuff +vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jvmti_stuff jfr_stuff sa_stuff @# We need a null action here, so implicit rules don't get consulted. $(Cached_plat): $(Plat_File) @@ -94,9 +94,9 @@ ad_stuff: $(Cached_plat) $(adjust-mflags) jvmti_stuff: $(Cached_plat) $(adjust-mflags) @$(MAKE) -f jvmti.make $(MFLAGS-adjusted) -# generate trace files -trace_stuff: jvmti_stuff $(Cached_plat) $(adjust-mflags) - @$(MAKE) -f trace.make $(MFLAGS-adjusted) +# generate JFR stuff +jfr_stuff: $(Cached_plat) $(adjust-mflags) + @$(MAKE) -f jfr.make $(MFLAGS-adjusted) # generate SA jar files and native header sa_stuff: diff --git a/make/linux/makefiles/trace.make b/make/linux/makefiles/trace.make deleted file mode 100644 index f173e0ad3aba0dcd89d38c16aec30c8940920384..0000000000000000000000000000000000000000 --- a/make/linux/makefiles/trace.make +++ /dev/null @@ -1,120 +0,0 @@ -# -# Copyright (c) 2003, 2013, 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. -# -# - -# This makefile (trace.make) is included from the trace.make in the -# build directories. -# -# It knows how to build and run the tools to generate trace files. - -include $(GAMMADIR)/make/linux/makefiles/rules.make -include $(GAMMADIR)/make/altsrc.make - -# ######################################################################### - -HAS_ALT_SRC:=$(shell if [ -d $(HS_ALT_SRC)/share/vm/trace ]; then \ - echo "true"; else echo "false";\ - fi) - -TOPDIR = $(shell echo `pwd`) -GENERATED = $(TOPDIR)/../generated -JvmtiOutDir = $(GENERATED)/jvmtifiles -TraceOutDir = $(GENERATED)/tracefiles - -TraceAltSrcDir = $(HS_ALT_SRC)/share/vm/trace -TraceSrcDir = $(HS_COMMON_SRC)/share/vm/trace - -# set VPATH so make knows where to look for source files -Src_Dirs_V += $(TraceSrcDir) $(TraceAltSrcDir) -VPATH += $(Src_Dirs_V:%=%:) - -TraceGeneratedNames = \ - traceEventClasses.hpp \ - traceEventIds.hpp \ - traceTypes.hpp - -ifeq ($(HAS_ALT_SRC), true) -TraceGeneratedNames += \ - traceRequestables.hpp \ - traceEventControl.hpp - -ifneq ($(INCLUDE_TRACE), false) -TraceGeneratedNames += traceProducer.cpp -endif - -endif - -TraceGeneratedFiles = $(TraceGeneratedNames:%=$(TraceOutDir)/%) - -XSLT = $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiGen - -XML_DEPS = $(TraceSrcDir)/trace.xml $(TraceSrcDir)/tracetypes.xml \ - $(TraceSrcDir)/trace.dtd $(TraceSrcDir)/xinclude.mod -ifeq ($(HAS_ALT_SRC), true) - XML_DEPS += $(TraceAltSrcDir)/traceevents.xml -endif - -.PHONY: all clean cleanall - -# ######################################################################### - -all: $(TraceGeneratedFiles) - -GENERATE_CODE= \ - $(QUIETLY) echo Generating $@; \ - $(XSLT) -IN $(word 1,$^) -XSL $(word 2,$^) -OUT $@; \ - test -f $@ - -$(TraceOutDir)/traceEventIds.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventIds.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceTypes.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceTypes.xsl $(XML_DEPS) - $(GENERATE_CODE) - -ifeq ($(HAS_ALT_SRC), false) - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventClasses.xsl $(XML_DEPS) - $(GENERATE_CODE) - -else - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventClasses.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceProducer.cpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceProducer.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceRequestables.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceRequestables.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceEventControl.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventControl.xsl $(XML_DEPS) - $(GENERATE_CODE) - -endif - -# ######################################################################### - -clean cleanall: - rm $(TraceGeneratedFiles) - - diff --git a/make/linux/makefiles/vm.make b/make/linux/makefiles/vm.make index d3feec8bf71ad71062f6b50744609c56edc0468a..e5a744edda9fbe3a2ac013449f20b0d1ee40cc32 100644 --- a/make/linux/makefiles/vm.make +++ b/make/linux/makefiles/vm.make @@ -54,7 +54,7 @@ endif # Src_Dirs_V is everything in src/share/vm/*, plus the right os/*/vm and cpu/*/vm # The adfiles directory contains ad_.[ch]pp. # The jvmtifiles directory contains jvmti*.[ch]pp -Src_Dirs_V += $(GENERATED)/adfiles $(GENERATED)/jvmtifiles $(GENERATED)/tracefiles +Src_Dirs_V += $(GENERATED)/adfiles $(GENERATED)/jvmtifiles $(GENERATED)/jfrfiles VPATH += $(Src_Dirs_V:%=%:) # set INCLUDES for C preprocessor. @@ -124,6 +124,7 @@ CFLAGS += $(CFLAGS/NOEX) # Extra flags from gnumake's invocation or environment CFLAGS += $(EXTRA_CFLAGS) LFLAGS += $(EXTRA_CFLAGS) $(EXTRA_LDFLAGS) +ASFLAGS += $(EXTRA_ASFLAGS) # Don't set excutable bit on stack segment # the same could be done by separate execstack command @@ -149,24 +150,21 @@ LIBJVM = lib$(JVM).so LIBJVM_DEBUGINFO = lib$(JVM).debuginfo LIBJVM_DIZ = lib$(JVM).diz +ifeq ($(ENABLE_JFR),false) +EXCLUDE_JFR_PATHS:= -o -name jfr -prune +endif SPECIAL_PATHS:=adlc c1 gc_implementation opto shark libadt SOURCE_PATHS=\ $(shell find $(HS_COMMON_SRC)/share/vm/* -type d \! \ - \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) \)) + \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) $(EXCLUDE_JFR_PATHS) \)) SOURCE_PATHS+=$(HS_COMMON_SRC)/os/$(Platform_os_family)/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/os/posix/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/cpu/$(Platform_arch)/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/os_cpu/$(Platform_os_arch)/vm CORE_PATHS=$(foreach path,$(SOURCE_PATHS),$(call altsrc,$(path)) $(path)) -CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/tracefiles - -ifneq ($(INCLUDE_TRACE), false) -CORE_PATHS+=$(shell if [ -d $(HS_ALT_SRC)/share/vm/jfr ]; then \ - find $(HS_ALT_SRC)/share/vm/jfr -type d; \ - fi) -endif +CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/jfrfiles COMPILER1_PATHS := $(call altsrc,$(HS_COMMON_SRC)/share/vm/c1) COMPILER1_PATHS += $(HS_COMMON_SRC)/share/vm/c1 diff --git a/make/solaris/makefiles/buildtree.make b/make/solaris/makefiles/buildtree.make index aab52a9fc5f2c7b9dc5ef6526a3e55fec32c33cd..5a58e15d34b3d7b3ffd8e2ed77eaeed5fdfbad76 100644 --- a/make/solaris/makefiles/buildtree.make +++ b/make/solaris/makefiles/buildtree.make @@ -48,7 +48,7 @@ # flags.make - with macro settings # vm.make - to support making "$(MAKE) -v vm.make" in makefiles # adlc.make - -# trace.make - generate tracing event and type definitions +# jfr.make - generate jfr event and type definitions # jvmti.make - generate JVMTI bindings from the spec (JSR-163) # sa.make - generate SA jar file and natives # @@ -103,6 +103,10 @@ TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name opto -o -n endif endif +ifeq ($(ENABLE_JFR),false) +ALWAYS_EXCLUDE_DIRS += -o -name jfr +endif + # Get things from the platform file. COMPILER = $(shell sed -n 's/^compiler[ ]*=[ ]*//p' $(PLATFORM_FILE)) @@ -110,7 +114,7 @@ SIMPLE_DIRS = \ $(PLATFORM_DIR)/generated/dependencies \ $(PLATFORM_DIR)/generated/adfiles \ $(PLATFORM_DIR)/generated/jvmtifiles \ - $(PLATFORM_DIR)/generated/tracefiles + $(PLATFORM_DIR)/generated/jfrfiles TARGETS = debug fastdebug optimized product SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) @@ -118,7 +122,7 @@ SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) # For dependencies and recursive makes. BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make -BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make sa.make +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make jfr.make sa.make BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ ARCH=$(ARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) @@ -190,6 +194,12 @@ DATA_MODE/amd64 = 64 DATA_MODE = $(DATA_MODE/$(BUILDARCH)) +ifeq ($(ENABLE_JFR), true) + INCLUDE_JFR = 1 +else + INCLUDE_JFR = 0 +endif + flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst @echo Creating $@ ... $(QUIETLY) ( \ @@ -269,9 +279,10 @@ flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst echo && \ echo "HOTSPOT_EXTRA_SYSDEFS\$$(HOTSPOT_EXTRA_SYSDEFS) = $(HOTSPOT_EXTRA_SYSDEFS)" && \ echo "SYSDEFS += \$$(HOTSPOT_EXTRA_SYSDEFS)"; \ + echo && echo "CFLAGS += -DINCLUDE_JFR=$(INCLUDE_JFR)"; \ echo; \ - [ -n "$(INCLUDE_TRACE)" ] && \ - echo && echo "INCLUDE_TRACE = $(INCLUDE_TRACE)"; \ + [ -n "$(INCLUDE_JFR)" ] && \ + echo && echo "INCLUDE_JFR = $(INCLUDE_JFR)"; \ [ -n "$(SPEC)" ] && \ echo "include $(SPEC)"; \ echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(VARIANT).make"; \ @@ -339,7 +350,7 @@ jvmti.make: $(BUILDTREE_MAKE) echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ ) > $@ -trace.make: $(BUILDTREE_MAKE) +jfr.make: $(BUILDTREE_MAKE) @echo Creating $@ ... $(QUIETLY) ( \ $(BUILDTREE_COMMENT); \ diff --git a/make/solaris/makefiles/jfr.make b/make/solaris/makefiles/jfr.make new file mode 100644 index 0000000000000000000000000000000000000000..5065bf0fae0e1ec49a1a80d4bd027da2cf4f699c --- /dev/null +++ b/make/solaris/makefiles/jfr.make @@ -0,0 +1,92 @@ +# +# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018-2019, Azul Systems, Inc. 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. +# +# + +# This makefile (jfr.make) is included from the jfr.make in the +# build directories. +# +# It knows how to build and run the tools to generate jfr. + +include $(GAMMADIR)/make/linux/makefiles/rules.make + +# ######################################################################### +# Build tools needed for the Jfr source code generation + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated + +JFR_TOOLS_SRCDIR := $(GAMMADIR)/src/share/vm/jfr +JFR_TOOLS_OUTPUTDIR := $(GENERATED)/tools/jfr + +JFR_OUTPUTDIR := $(GENERATED)/jfrfiles +JFR_SRCDIR := $(GAMMADIR)/src/share/vm/jfr/metadata + +METADATA_XML ?= $(JFR_SRCDIR)/metadata.xml +METADATA_XSD ?= $(JFR_SRCDIR)/metadata.xsd + +# Changing these will trigger a rebuild of generated jfr files. +JFR_DEPS += \ + $(METADATA_XML) \ + $(METADATA_XSD) \ + # + +JfrGeneratedNames = \ + jfrEventClasses.hpp \ + jfrEventControl.hpp \ + jfrEventIds.hpp \ + jfrPeriodic.hpp \ + jfrTypes.hpp + +JfrGenSource = $(JFR_TOOLS_SRCDIR)/GenerateJfrFiles.java +JfrGenClass = $(JFR_TOOLS_OUTPUTDIR)/build/tools/jfr/GenerateJfrFiles.class + +JfrGeneratedFiles = $(JfrGeneratedNames:%=$(JFR_OUTPUTDIR/%) + +.PHONY: all clean cleanall + +# ######################################################################### + +all: $(JfrGeneratedFiles) + +$(JfrGenClass): $(JfrGenSource) + mkdir -p $(@D) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -d $(JFR_TOOLS_OUTPUTDIR) $(JfrGenSource) + +$(JFR_OUTPUTDIR)/jfrEventClasses.hpp: $(METADATA_XML) $(METADATA_XSD) $(JfrGenClass) + $(QUIETLY) echo Generating $(@F) + mkdir -p $(@D) + $(QUIETLY) $(REMOTE) $(RUN.JAVA) -cp $(JFR_TOOLS_OUTPUTDIR) build.tools.jfr.GenerateJfrFiles $(METADATA_XML) $(METADATA_XSD) $(JFR_OUTPUTDIR) + test -f $@ + +$(filter-out $(JFR_OUTPUTDIR)/jfrEventClasses.hpp, $(JfrGeneratedFiles)): $(JFR_OUTPUTDIR)/jfrEventClasses.hpp + +TARGETS += $(JFR_OUTPUTDIR)/jfrEventClasses.hpp + +# ######################################################################### + +clean cleanall : + rm $(JfrGenClass) $(JfrGeneratedFiles) + +# ######################################################################### + diff --git a/make/solaris/makefiles/mapfile-vers b/make/solaris/makefiles/mapfile-vers index d76be6ccc99c7c4d0eadbf7b27edb3fbecafe638..f368471698bff7347b363f2b5bc124f887cbe36d 100644 --- a/make/solaris/makefiles/mapfile-vers +++ b/make/solaris/makefiles/mapfile-vers @@ -39,6 +39,7 @@ SUNWprivate_1.1 { JVM_ArrayCopy; JVM_AssertionStatusDirectives; JVM_Available; + JVM_BeforeHalt; JVM_Bind; JVM_ClassDepth; JVM_ClassLoaderDepth; diff --git a/make/solaris/makefiles/rules.make b/make/solaris/makefiles/rules.make index b7006f5836e018d115c28c07a221b8675a31efe6..5530aaddf75818f6e5eaf7a5c38e44bd4d481345 100644 --- a/make/solaris/makefiles/rules.make +++ b/make/solaris/makefiles/rules.make @@ -118,8 +118,8 @@ QUIETLY$(MAKE_VERBOSE) = @ RUN.JAR$(MAKE_VERBOSE) += >/dev/null # Settings for javac -BOOT_SOURCE_LANGUAGE_VERSION = 6 -BOOT_TARGET_CLASS_VERSION = 6 +BOOT_SOURCE_LANGUAGE_VERSION = 7 +BOOT_TARGET_CLASS_VERSION = 7 JAVAC_FLAGS = -g -encoding ascii BOOTSTRAP_JAVAC_FLAGS = $(JAVAC_FLAGS) -source $(BOOT_SOURCE_LANGUAGE_VERSION) -target $(BOOT_TARGET_CLASS_VERSION) diff --git a/make/solaris/makefiles/top.make b/make/solaris/makefiles/top.make index 81e34afdee5bf43127ee53e8ec302a3d7d27ba78..518aef908a7124683928faa18cd571a6621036b0 100644 --- a/make/solaris/makefiles/top.make +++ b/make/solaris/makefiles/top.make @@ -73,7 +73,7 @@ default: vm_build_preliminaries the_vm @echo All done. # This is an explicit dependency for the sake of parallel makes. -vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jvmti_stuff trace_stuff sa_stuff +vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jvmti_stuff jfr_stuff sa_stuff @# We need a null action here, so implicit rules don't get consulted. $(Cached_plat): $(Plat_File) @@ -87,9 +87,9 @@ ad_stuff: $(Cached_plat) $(adjust-mflags) jvmti_stuff: $(Cached_plat) $(adjust-mflags) @$(MAKE) -f jvmti.make $(MFLAGS-adjusted) -# generate trace files -trace_stuff: jvmti_stuff $(Cached_plat) $(adjust-mflags) - @$(MAKE) -f trace.make $(MFLAGS-adjusted) +# generate JFR files +jfr_stuff: $(Cached_plat) $(adjust-mflags) + @$(MAKE) -f jfr.make $(MFLAGS-adjusted) # generate SA jar files and native header sa_stuff: diff --git a/make/solaris/makefiles/trace.make b/make/solaris/makefiles/trace.make deleted file mode 100644 index 18c88b925f429a38a2d5d6450083001a95f0550e..0000000000000000000000000000000000000000 --- a/make/solaris/makefiles/trace.make +++ /dev/null @@ -1,120 +0,0 @@ -# -# Copyright (c) 2003, 2013, 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. -# -# - -# This makefile (trace.make) is included from the trace.make in the -# build directories. -# -# It knows how to build and run the tools to generate trace files. - -include $(GAMMADIR)/make/solaris/makefiles/rules.make -include $(GAMMADIR)/make/altsrc.make - -# ######################################################################### - -HAS_ALT_SRC:=$(shell if [ -d $(HS_ALT_SRC)/share/vm/trace ]; then \ - echo "true"; else echo "false";\ - fi) - -TOPDIR = $(shell echo `pwd`) -GENERATED = $(TOPDIR)/../generated -JvmtiOutDir = $(GENERATED)/jvmtifiles -TraceOutDir = $(GENERATED)/tracefiles - -TraceAltSrcDir = $(HS_ALT_SRC)/share/vm/trace -TraceSrcDir = $(HS_COMMON_SRC)/share/vm/trace - -# set VPATH so make knows where to look for source files -Src_Dirs_V += $(TraceSrcDir) $(TraceAltSrcDir) -VPATH += $(Src_Dirs_V:%=%:) - -TraceGeneratedNames = \ - traceEventClasses.hpp \ - traceEventIds.hpp \ - traceTypes.hpp - -ifeq ($(HAS_ALT_SRC), true) -TraceGeneratedNames += \ - traceRequestables.hpp \ - traceEventControl.hpp - -ifneq ($(INCLUDE_TRACE), false) - TraceGeneratedNames += traceProducer.cpp -endif - -endif - -TraceGeneratedFiles = $(TraceGeneratedNames:%=$(TraceOutDir)/%) - -XSLT = $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiGen - -XML_DEPS = $(TraceSrcDir)/trace.xml $(TraceSrcDir)/tracetypes.xml \ - $(TraceSrcDir)/trace.dtd $(TraceSrcDir)/xinclude.mod -ifeq ($(HAS_ALT_SRC), true) - XML_DEPS += $(TraceAltSrcDir)/traceevents.xml -endif - -.PHONY: all clean cleanall - -# ######################################################################### - -all: $(TraceGeneratedFiles) - -GENERATE_CODE= \ - $(QUIETLY) echo Generating $@; \ - $(XSLT) -IN $(word 1,$^) -XSL $(word 2,$^) -OUT $@; \ - test -f $@ - -$(TraceOutDir)/traceEventIds.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventIds.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceTypes.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceTypes.xsl $(XML_DEPS) - $(GENERATE_CODE) - -ifeq ($(HAS_ALT_SRC), false) - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventClasses.xsl $(XML_DEPS) - $(GENERATE_CODE) - -else - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventClasses.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceProducer.cpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceProducer.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceRequestables.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceRequestables.xsl $(XML_DEPS) - $(GENERATE_CODE) - -$(TraceOutDir)/traceEventControl.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventControl.xsl $(XML_DEPS) - $(GENERATE_CODE) - -endif - -# ######################################################################### - -clean cleanall: - rm $(TraceGeneratedFiles) - - diff --git a/make/solaris/makefiles/vm.make b/make/solaris/makefiles/vm.make index c2a3527d5791584892169044cc61137b8261c7fa..bfb670201c9717bbc6f7a59095b96413523f64f8 100644 --- a/make/solaris/makefiles/vm.make +++ b/make/solaris/makefiles/vm.make @@ -48,7 +48,7 @@ include $(MAKEFILES_DIR)/$(BUILDARCH).make # Src_Dirs_V is everything in src/share/vm/*, plus the right os/*/vm and cpu/*/vm # The adfiles directory contains ad_.[ch]pp. # The jvmtifiles directory contains jvmti*.[ch]pp -Src_Dirs_V += $(GENERATED)/adfiles $(GENERATED)/jvmtifiles $(GENERATED)/tracefiles +Src_Dirs_V += $(GENERATED)/adfiles $(GENERATED)/jvmtifiles $(GENERATED)/jfrfiles VPATH += $(Src_Dirs_V:%=%:) # set INCLUDES for C preprocessor @@ -109,6 +109,7 @@ CFLAGS += $(CFLAGS/NOEX) # Extra flags from gnumake's invocation or environment CFLAGS += $(EXTRA_CFLAGS) +ASFLAGS += $(EXTRA_ASFLAGS) # Math Library (libm.so), do not use -lm. # There might be two versions of libm.so on the build system: @@ -144,6 +145,8 @@ endif # sparcWorks LIBS += -lkstat +LIBS += -lrt + # By default, link the *.o into the library, not the executable. LINK_INTO$(LINK_INTO) = LIBJVM @@ -161,25 +164,21 @@ LIBJVM = lib$(JVM).so LIBJVM_DEBUGINFO = lib$(JVM).debuginfo LIBJVM_DIZ = lib$(JVM).diz - +ifeq ($(ENABLE_JFR),false) +EXCLUDE_JFR_PATHS:= -o -name jfr -prune +endif SPECIAL_PATHS:=adlc c1 dist gc_implementation opto shark libadt SOURCE_PATHS=\ $(shell find $(HS_COMMON_SRC)/share/vm/* -type d \! \ - \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) \)) + \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) $(EXCLUDE_JFR_PATHS) \)) SOURCE_PATHS+=$(HS_COMMON_SRC)/os/$(Platform_os_family)/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/os/posix/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/cpu/$(Platform_arch)/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/os_cpu/$(Platform_os_arch)/vm CORE_PATHS=$(foreach path,$(SOURCE_PATHS),$(call altsrc,$(path)) $(path)) -CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/tracefiles - -ifneq ($(INCLUDE_TRACE), false) -CORE_PATHS+=$(shell if [ -d $(HS_ALT_SRC)/share/vm/jfr ]; then \ - find $(HS_ALT_SRC)/share/vm/jfr -type d; \ - fi) -endif +CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/jfrfiles COMPILER1_PATHS := $(call altsrc,$(HS_COMMON_SRC)/share/vm/c1) COMPILER1_PATHS += $(HS_COMMON_SRC)/share/vm/c1 diff --git a/make/windows/build.make b/make/windows/build.make index 725dccf106fe096d271daff9f67416e0ca798c34..1a486d2124ae5113aaa7797591d4c0f969327e5a 100644 --- a/make/windows/build.make +++ b/make/windows/build.make @@ -280,7 +280,7 @@ $(variantDir)\local.make: checks @ echo HS_COMPANY=$(COMPANY_NAME) >> $@ @ echo HS_FILEDESC=$(HS_FILEDESC) >> $@ @ echo HOTSPOT_VM_DISTRO=$(HOTSPOT_VM_DISTRO) >> $@ - @ echo VENDOR=$(VENDOR) >> $@ + @ echo VENDOR=$(COMPANY_NAME) >> $@ @ echo VENDOR_URL=$(VENDOR_URL) >> $@ @ echo VENDOR_URL_BUG=$(VENDOR_URL_BUG) >> $@ @ echo VENDOR_URL_VM_BUG=$(VENDOR_URL_VM_BUG) >> $@ @@ -302,6 +302,7 @@ $(variantDir)\local.make: checks @ echo LD=$(LD) >> $@ @ echo MT=$(MT) >> $@ @ echo RC=$(RC) >> $@ + @ echo ENABLE_JFR=$(ENABLE_JFR) >> $@ @ sh $(WorkSpace)/make/windows/get_msc_ver.sh >> $@ @ if "$(ENABLE_FULL_DEBUG_SYMBOLS)" NEQ "" echo ENABLE_FULL_DEBUG_SYMBOLS=$(ENABLE_FULL_DEBUG_SYMBOLS) >> $@ @ if "$(ZIP_DEBUGINFO_FILES)" NEQ "" echo ZIP_DEBUGINFO_FILES=$(ZIP_DEBUGINFO_FILES) >> $@ diff --git a/make/windows/create_obj_files.sh b/make/windows/create_obj_files.sh index c17b0690e1762b8ab98c297cf2c139e20e84be55..097998242f961a6a52117f1a37a3bed3e6361510 100644 --- a/make/windows/create_obj_files.sh +++ b/make/windows/create_obj_files.sh @@ -57,8 +57,8 @@ ALTSRC_REL=src/closed # Change this to pick up alt sources from somewhere else COMMONSRC=${WorkSpace}/${COMMONSRC_REL} ALTSRC=${WorkSpace}/${ALTSRC_REL} -BASE_PATHS="`if [ -d ${ALTSRC}/share/vm ]; then $FIND ${ALTSRC}/share/vm ! -name vm -prune -type d \! \( -name adlc -o -name c1 -o -name gc_implementation -o -name opto -o -name shark -o -name libadt \); fi`" -BASE_PATHS="${BASE_PATHS} ` $FIND ${COMMONSRC}/share/vm ! -name vm -prune -type d \! \( -name adlc -o -name c1 -o -name gc_implementation -o -name opto -o -name shark -o -name libadt \)`" +BASE_PATHS="`if [ -d ${ALTSRC}/share/vm ]; then $FIND ${ALTSRC}/share/vm ! -name vm -prune -type d \! \( -name adlc -o -name c1 -o -name gc_implementation -o -name opto -o -name shark -o -name libadt -o -name jfr \); fi`" +BASE_PATHS="${BASE_PATHS} ` $FIND ${COMMONSRC}/share/vm ! -name vm -prune -type d \! \( -name adlc -o -name c1 -o -name gc_implementation -o -name opto -o -name shark -o -name libadt -o -name jfr \)`" for sd in \ share/vm/gc_implementation/shared \ @@ -71,10 +71,10 @@ for sd in \ BASE_PATHS="${BASE_PATHS} ${COMMONSRC}/${sd}" done -BASE_PATHS="${BASE_PATHS} ${GENERATED}/jvmtifiles ${GENERATED}/tracefiles" +BASE_PATHS="${BASE_PATHS} ${GENERATED}/jvmtifiles ${GENERATED}/jfrfiles" -if [ -d "${ALTSRC}/share/vm/jfr/buffers" ]; then - BASE_PATHS="${BASE_PATHS} ${ALTSRC}/share/vm/jfr/buffers" +if [ "$ENABLE_JFR" = "true" ]; then +BASE_PATHS="${BASE_PATHS} `$FIND ${COMMONSRC}/share/vm/jfr -type d`" fi BASE_PATHS="${BASE_PATHS} ${COMMONSRC}/share/vm/prims/wbtestmethods" diff --git a/make/windows/makefiles/compile.make b/make/windows/makefiles/compile.make index f91d36827e99116a8339ab04527312b83c771326..f3581c163bb17decea102a9a8d24af78fb7f0b16 100644 --- a/make/windows/makefiles/compile.make +++ b/make/windows/makefiles/compile.make @@ -314,6 +314,13 @@ LD_FLAGS = $(SAFESEH_FLAG) $(LD_FLAGS) CXX_FLAGS = $(CXX_FLAGS) $(MP_FLAG) +!if "$(ENABLE_JFR)" == "true" +INCLUDE_JFR=1 +!else +INCLUDE_JFR=0 +!endif +CXX_FLAGS=$(CXX_FLAGS) /D INCLUDE_JFR=$(INCLUDE_JFR) + # If NO_OPTIMIZATIONS is defined in the environment, turn everything off !ifdef NO_OPTIMIZATIONS PRODUCT_OPT_OPTION = $(DEBUG_OPT_OPTION) @@ -346,7 +353,7 @@ RC_FLAGS=/D "HS_VER=$(HS_VER)" \ /D "HS_BUILD_ID=$(HS_BUILD_ID)" \ /D "JDK_VER=$(JDK_VER)" \ /D "JDK_DOTVER=$(JDK_DOTVER)" \ - /D "HS_COMPANY=$(HS_COMPANY)" \ + /D "HS_COMPANY=$(COMPANY_NAME)" \ /D "HS_FILEDESC=$(HS_FILEDESC)" \ /D "HS_COPYRIGHT=$(HS_COPYRIGHT)" \ /D "HS_FNAME=$(HS_FNAME)" \ @@ -357,4 +364,3 @@ RC_FLAGS=/D "HS_VER=$(HS_VER)" \ !if "$(MFC_DEBUG)" == "true" RC_FLAGS = $(RC_FLAGS) /D "_DEBUG" !endif - diff --git a/make/windows/makefiles/defs.make b/make/windows/makefiles/defs.make index fe4b6c72fbf503e4717ee678b34f4310a122b98d..6b36f0e2bcda6b61ad26a488ba8aac71d5a2ebda 100644 --- a/make/windows/makefiles/defs.make +++ b/make/windows/makefiles/defs.make @@ -157,6 +157,15 @@ endif MAKE_ARGS += RM="$(RM)" MAKE_ARGS += ZIPEXE=$(ZIPEXE) +MAKE_ARGS += COMPANY_NAME="$(COMPANY_NAME)" +MAKE_ARGS += VENDOR_URL=$(VENDOR_URL) +MAKE_ARGS += VENDOR_URL_BUG=$(VENDOR_URL_BUG) +MAKE_ARGS += VENDOR_URL_VM_BUG=$(VENDOR_URL_VM_BUG) +ifneq ($(VERSION_CFLAGS),) + # transform syntax from -DProp='"Value"' to /D "Prop=\"Value\"" for Windows build + MAKE_ARGS += VERSION_CFLAGS="$(subst -D,/D \",$(subst ',,$(subst '$() $(),\"$() $(),$(subst ",\\\",$(VERSION_CFLAGS)))))\"" +endif + # On 32 bit windows we build server and client, on 64 bit just server. ifeq ($(JVM_VARIANTS),) ifeq ($(ARCH_DATA_MODEL), 32) @@ -306,3 +315,6 @@ ifneq ($(SPEC),) MAKE_ARGS += MT="$(subst /,\\,$(MT))" endif endif + +MAKE_ARGS += ENABLE_JFR=$(ENABLE_JFR) + diff --git a/make/windows/makefiles/generated.make b/make/windows/makefiles/generated.make index aaaa8e7c2b64f14158abc832d2b1691a3afb6ee6..63e6419148efd5edd348080156887cc76f351f27 100644 --- a/make/windows/makefiles/generated.make +++ b/make/windows/makefiles/generated.make @@ -30,9 +30,9 @@ JvmtiOutDir=jvmtifiles !include $(WorkSpace)/make/windows/makefiles/jvmti.make -# Pick up rules for building trace -TraceOutDir=tracefiles -!include $(WorkSpace)/make/windows/makefiles/trace.make +# Pick up rules for building JFR +JfrOutDir=jfrfiles +!include $(WorkSpace)/make/windows/makefiles/jfr.make # Pick up rules for building SA !include $(WorkSpace)/make/windows/makefiles/sa.make @@ -40,9 +40,9 @@ TraceOutDir=tracefiles AdlcOutDir=adfiles !if ("$(Variant)" == "compiler2") || ("$(Variant)" == "tiered") -default:: $(AdlcOutDir)/ad_$(Platform_arch_model).cpp $(AdlcOutDir)/dfa_$(Platform_arch_model).cpp $(JvmtiGeneratedFiles) $(TraceGeneratedFiles) buildobjfiles +default:: $(AdlcOutDir)/ad_$(Platform_arch_model).cpp $(AdlcOutDir)/dfa_$(Platform_arch_model).cpp $(JvmtiGeneratedFiles) $(JfrGeneratedFiles) buildobjfiles !else -default:: $(JvmtiGeneratedFiles) $(TraceGeneratedFiles) buildobjfiles +default:: $(JvmtiGeneratedFiles) $(JfrGeneratedFiles) buildobjfiles !endif buildobjfiles: diff --git a/make/windows/makefiles/jfr.make b/make/windows/makefiles/jfr.make new file mode 100644 index 0000000000000000000000000000000000000000..2634abdbf3809ea70ca0677cd44fc613a3522a63 --- /dev/null +++ b/make/windows/makefiles/jfr.make @@ -0,0 +1,81 @@ +# +# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018-2019, Azul Systems, Inc. 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. +# +# + +# This makefile (jfr.make) is included from the jfr.make in the +# build directories. +# +# It knows how to build and run the tools to generate jfr. + +!include $(WorkSpace)/make/windows/makefiles/rules.make + +# ######################################################################### +# Build tools needed for the Jfr source code generation + +GENERATED = ../generated + +JFR_TOOLS_SRCDIR = $(WorkSpace)/src/share/vm/jfr +JFR_TOOLS_OUTPUTDIR = $(GENERATED)/tools/jfr + +JFR_OUTPUTDIR = $(GENERATED)/jfrfiles +JFR_SRCDIR = $(WorkSpace)/src/share/vm/jfr/metadata + +METADATA_XML = $(JFR_SRCDIR)/metadata.xml +METADATA_XSD = $(JFR_SRCDIR)/metadata.xsd + +# Changing these will trigger a rebuild of generated jfr files. +JFR_DEPS = $(METADATA_XML) \ + $(METADATA_XSD) + +JfrGeneratedFiles = \ + $(JFR_OUTPUTDIR)/jfrEventControl.hpp \ + $(JFR_OUTPUTDIR)/jfrEventIds.hpp \ + $(JFR_OUTPUTDIR)/jfrPeriodic.hpp \ + $(JFR_OUTPUTDIR)/jfrTypes.hpp + +JfrGenSource = $(JFR_TOOLS_SRCDIR)/GenerateJfrFiles.java +JfrGenClass = $(JFR_TOOLS_OUTPUTDIR)/build/tools/jfr/GenerateJfrFiles.class + +.PHONY: all cleanall + +# ######################################################################### + +all: $(JfrGeneratedFiles) + +$(JfrGenClass): $(JfrGenSource) + mkdir -p $(@D) + $(COMPILE_JAVAC) -d $(JFR_TOOLS_OUTPUTDIR) $(JfrGenSource) + +$(JFR_OUTPUTDIR)/jfrEventClasses.hpp: $(METADATA_XML) $(METADATA_XSD) $(JfrGenClass) + echo Generating $(@F) + mkdir -p $(@D) + $(RUN_JAVA) -cp $(JFR_TOOLS_OUTPUTDIR) build.tools.jfr.GenerateJfrFiles $(METADATA_XML) $(METADATA_XSD) $(JFR_OUTPUTDIR) + test -f $@ + +$(JfrGeneratedFiles): $(JFR_OUTPUTDIR)/jfrEventClasses.hpp + +# ######################################################################### + +cleanall : + rm $(JfrGenClass) $(JfrGeneratedFiles) $(JFR_OUTPUTDIR)/jfrEventClasses.hpp diff --git a/make/windows/makefiles/rules.make b/make/windows/makefiles/rules.make index c5d565a2880398601dd4fb297413dd07549449b5..3e5b0400f6a57e2c119b5e8e7392279e18a7e4d0 100644 --- a/make/windows/makefiles/rules.make +++ b/make/windows/makefiles/rules.make @@ -44,8 +44,8 @@ BOOT_JAVA_HOME= !endif # Settings for javac -BOOT_SOURCE_LANGUAGE_VERSION=6 -BOOT_TARGET_CLASS_VERSION=6 +BOOT_SOURCE_LANGUAGE_VERSION=7 +BOOT_TARGET_CLASS_VERSION=7 JAVAC_FLAGS=-g -encoding ascii BOOTSTRAP_JAVAC_FLAGS=$(JAVAC_FLAGS) -source $(BOOT_SOURCE_LANGUAGE_VERSION) -target $(BOOT_TARGET_CLASS_VERSION) diff --git a/make/windows/makefiles/trace.make b/make/windows/makefiles/trace.make deleted file mode 100644 index 58fee24653c9e8102c08d01225e7c6267c017390..0000000000000000000000000000000000000000 --- a/make/windows/makefiles/trace.make +++ /dev/null @@ -1,120 +0,0 @@ -# -# Copyright (c) 2003, 2013, 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. -# -# - -# This makefile (trace.make) is included from the trace.make in the -# build directories. -# -# It knows how to build and run the tools to generate trace files. - -!include $(WorkSpace)/make/windows/makefiles/rules.make - -# ######################################################################### - - -TraceAltSrcDir = $(WorkSpace)/src/closed/share/vm/trace -TraceSrcDir = $(WorkSpace)/src/share/vm/trace - -TraceGeneratedNames = \ - traceEventClasses.hpp \ - traceEventIds.hpp \ - traceTypes.hpp - -!if EXISTS($(TraceAltSrcDir)) -TraceGeneratedNames = $(TraceGeneratedNames) \ - traceRequestables.hpp \ - traceEventControl.hpp \ - traceProducer.cpp -!endif - - -#Note: TraceGeneratedFiles must be kept in sync with TraceGeneratedNames by hand. -#Should be equivalent to "TraceGeneratedFiles = $(TraceGeneratedNames:%=$(TraceOutDir)/%)" -TraceGeneratedFiles = \ - $(TraceOutDir)/traceEventClasses.hpp \ - $(TraceOutDir)/traceEventIds.hpp \ - $(TraceOutDir)/traceTypes.hpp - -!if EXISTS($(TraceAltSrcDir)) -TraceGeneratedFiles = $(TraceGeneratedFiles) \ - $(TraceOutDir)/traceRequestables.hpp \ - $(TraceOutDir)/traceEventControl.hpp \ - $(TraceOutDir)/traceProducer.cpp -!endif - -XSLT = $(QUIETLY) $(REMOTE) $(RUN_JAVA) -classpath $(JvmtiOutDir) jvmtiGen - -XML_DEPS = $(TraceSrcDir)/trace.xml $(TraceSrcDir)/tracetypes.xml \ - $(TraceSrcDir)/trace.dtd $(TraceSrcDir)/xinclude.mod - -!if EXISTS($(TraceAltSrcDir)) -XML_DEPS = $(XML_DEPS) $(TraceAltSrcDir)/traceevents.xml -!endif - -.PHONY: all clean cleanall - -# ######################################################################### - -default:: - @if not exist $(TraceOutDir) mkdir $(TraceOutDir) - -$(TraceOutDir)/traceEventIds.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventIds.xsl $(XML_DEPS) - @echo Generating $@ - @$(XSLT) -IN $(TraceSrcDir)/trace.xml -XSL $(TraceSrcDir)/traceEventIds.xsl -OUT $(TraceOutDir)/traceEventIds.hpp - -$(TraceOutDir)/traceTypes.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceTypes.xsl $(XML_DEPS) - @echo Generating $@ - @$(XSLT) -IN $(TraceSrcDir)/trace.xml -XSL $(TraceSrcDir)/traceTypes.xsl -OUT $(TraceOutDir)/traceTypes.hpp - -!if !EXISTS($(TraceAltSrcDir)) - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventClasses.xsl $(XML_DEPS) - @echo Generating OpenJDK $@ - @$(XSLT) -IN $(TraceSrcDir)/trace.xml -XSL $(TraceSrcDir)/traceEventClasses.xsl -OUT $(TraceOutDir)/traceEventClasses.hpp - -!else - -$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventClasses.xsl $(XML_DEPS) - @echo Generating AltSrc $@ - @$(XSLT) -IN $(TraceSrcDir)/trace.xml -XSL $(TraceAltSrcDir)/traceEventClasses.xsl -OUT $(TraceOutDir)/traceEventClasses.hpp - -$(TraceOutDir)/traceProducer.cpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceProducer.xsl $(XML_DEPS) - @echo Generating AltSrc $@ - @$(XSLT) -IN $(TraceSrcDir)/trace.xml -XSL $(TraceAltSrcDir)/traceProducer.xsl -OUT $(TraceOutDir)/traceProducer.cpp - -$(TraceOutDir)/traceRequestables.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceRequestables.xsl $(XML_DEPS) - @echo Generating AltSrc $@ - @$(XSLT) -IN $(TraceSrcDir)/trace.xml -XSL $(TraceAltSrcDir)/traceRequestables.xsl -OUT $(TraceOutDir)/traceRequestables.hpp - -$(TraceOutDir)/traceEventControl.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventControl.xsl $(XML_DEPS) - @echo Generating AltSrc $@ - @$(XSLT) -IN $(TraceSrcDir)/trace.xml -XSL $(TraceAltSrcDir)/traceEventControl.xsl -OUT $(TraceOutDir)/traceEventControl.hpp - -!endif - -# ######################################################################### - -cleanall : - rm $(TraceGeneratedFiles) - - diff --git a/make/windows/makefiles/vm.make b/make/windows/makefiles/vm.make index 7c6b980cbbd52eff2648856e3624dfdc7a780cd6..1394dd92f326bd001f633306411890ad92cd48f0 100644 --- a/make/windows/makefiles/vm.make +++ b/make/windows/makefiles/vm.make @@ -62,10 +62,6 @@ CXX_FLAGS=$(CXX_FLAGS) /D "HOTSPOT_LIB_ARCH=\"$(HOTSPOT_LIB_ARCH)\"" CXX_FLAGS=$(CXX_FLAGS) /D "HOTSPOT_BUILD_TARGET=\"$(BUILD_FLAVOR)\"" CXX_FLAGS=$(CXX_FLAGS) /D "HOTSPOT_BUILD_USER=\"$(BuildUser)\"" CXX_FLAGS=$(CXX_FLAGS) /D "HOTSPOT_VM_DISTRO=\"$(HOTSPOT_VM_DISTRO)\"" -CXX_FLAGS=$(CXX_FLAGS) /D "VENDOR=\"$(COMPANY_NAME)\"" -CXX_FLAGS=$(CXX_FLAGS) /D "VENDOR_URL=\"$(VENDOR_URL)\"" -CXX_FLAGS=$(CXX_FLAGS) /D "VENDOR_URL_BUG=\"$(VENDOR_URL_BUG)\"" -CXX_FLAGS=$(CXX_FLAGS) /D "VENDOR_URL_VM_BUG=\"$(VENDOR_URL_VM_BUG)\"" CXX_FLAGS=$(CXX_FLAGS) $(CXX_INCLUDE_DIRS) @@ -145,7 +141,7 @@ CXX_USE_PCH=$(CXX_DONT_USE_PCH) VM_PATH=../generated VM_PATH=$(VM_PATH);../generated/adfiles VM_PATH=$(VM_PATH);../generated/jvmtifiles -VM_PATH=$(VM_PATH);../generated/tracefiles +VM_PATH=$(VM_PATH);../generated/jfrfiles VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/c1 VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/compiler VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/code @@ -174,11 +170,6 @@ VM_PATH=$(VM_PATH);$(WorkSpace)/src/os_cpu/windows_$(Platform_arch)/vm VM_PATH=$(VM_PATH);$(WorkSpace)/src/cpu/$(Platform_arch)/vm VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/opto -!if exists($(ALTSRC)\share\vm\jfr) -VM_PATH=$(VM_PATH);$(ALTSRC)/share/vm/jfr -VM_PATH=$(VM_PATH);$(ALTSRC)/share/vm/jfr/buffers -!endif - VM_PATH={$(VM_PATH)} # Special case files not using precompiled header files. @@ -210,6 +201,20 @@ bytecodeInterpreter.obj: $(WorkSpace)\src\share\vm\interpreter\bytecodeInterpret bytecodeInterpreterWithChecks.obj: ..\generated\jvmtifiles\bytecodeInterpreterWithChecks.cpp $(CXX) $(CXX_FLAGS) $(CXX_DONT_USE_PCH) /c ..\generated\jvmtifiles\bytecodeInterpreterWithChecks.cpp +iphlp_interface.obj: $(WorkSpace)\src\os\windows\vm\iphlp_interface.cpp + $(CXX) $(CXX_FLAGS) $(CXX_DONT_USE_PCH) /c $(WorkSpace)\src\os\windows\vm\iphlp_interface.cpp + +os_perf_windows.obj: $(WorkSpace)\src\os\windows\vm\os_perf_windows.cpp + $(CXX) $(CXX_FLAGS) $(CXX_DONT_USE_PCH) /c $(WorkSpace)\src\os\windows\vm\os_perf_windows.cpp + +#files compiled with version flags + +vm_version.obj: $(WorkSpace)\src\share\vm\runtime\vm_version.cpp + $(CXX) $(CXX_FLAGS) $(VERSION_CFLAGS) $(CXX_USE_PCH) /c $(WorkSpace)\src\share\vm\runtime\vm_version.cpp + +arguments.obj: $(WorkSpace)\src\share\vm\runtime\arguments.cpp + $(CXX) $(CXX_FLAGS) $(VERSION_CFLAGS) $(CXX_USE_PCH) /c $(WorkSpace)\src\share\vm\runtime\arguments.cpp + # Default rules for the Virtual Machine {$(COMMONSRC)\share\vm\c1}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< @@ -388,13 +393,79 @@ bytecodeInterpreterWithChecks.obj: ..\generated\jvmtifiles\bytecodeInterpreterWi {..\generated\jvmtifiles}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< -{..\generated\tracefiles}.cpp.obj:: +{..\generated\jfrfiles}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\dcmd}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\instrumentation}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\jni}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\leakprofiler}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\leakprofiler\chains}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\leakprofiler\checkpoint}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\leakprofiler\sampling}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\leakprofiler\utilities}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\metadata}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\periodic}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\periodic\sampling}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\recorder}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\recorder\checkpoint}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\recorder\checkpoint\types}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\recorder\checkpoint\types\traceid}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\recorder\repository}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\recorder\service}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\recorder\stacktrace}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\recorder\storage}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\recorder\stringpool}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +{$(COMMONSRC)\share\vm\jfr\support}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< -{$(ALTSRC)\share\vm\jfr}.cpp.obj:: +{$(COMMONSRC)\share\vm\jfr\utilities}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< -{$(ALTSRC)\share\vm\jfr\buffers}.cpp.obj:: +{$(COMMONSRC)\share\vm\jfr\writers}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< default:: diff --git a/make/windows/projectfiles/common/Makefile b/make/windows/projectfiles/common/Makefile index 0bc4fb182ec27f46dadfecbb26aca4fee2283e0c..0d3f065ee3942b7bba78fefddaae3edab75aef3a 100644 --- a/make/windows/projectfiles/common/Makefile +++ b/make/windows/projectfiles/common/Makefile @@ -61,8 +61,8 @@ JvmtiOutDir=$(HOTSPOTBUILDSPACE)\$(Variant)\generated\jvmtifiles !include $(HOTSPOTWORKSPACE)/make/windows/makefiles/jvmti.make # Pick up rules for building trace -TraceOutDir=$(HOTSPOTBUILDSPACE)\$(Variant)\generated\tracefiles -!include $(HOTSPOTWORKSPACE)/make/windows/makefiles/trace.make +JfrOutDir=$(HOTSPOTBUILDSPACE)\$(Variant)\generated\jfrfiles +!include $(HOTSPOTWORKSPACE)/make/windows/makefiles/jfr.make !if "$(Variant)" == "compiler2" # Pick up rules for building adlc diff --git a/src/cpu/ppc/vm/frame_ppc.cpp b/src/cpu/ppc/vm/frame_ppc.cpp index d56eeb2b3bb3f11cb84fed96b41699053466e9d4..a720eda79a0ff80e524ae5cc31c5c48d439976c6 100644 --- a/src/cpu/ppc/vm/frame_ppc.cpp +++ b/src/cpu/ppc/vm/frame_ppc.cpp @@ -49,14 +49,134 @@ void RegisterMap::check_location_valid() { bool frame::safe_for_sender(JavaThread *thread) { bool safe = false; - address cursp = (address)sp(); - address curfp = (address)fp(); - if ((cursp != NULL && curfp != NULL && - (cursp <= thread->stack_base() && cursp >= thread->stack_base() - thread->stack_size())) && - (curfp <= thread->stack_base() && curfp >= thread->stack_base() - thread->stack_size())) { - safe = true; + address sp = (address)_sp; + address fp = (address)_fp; + address unextended_sp = (address)_unextended_sp; + + // Consider stack guards when trying to determine "safe" stack pointers + static size_t stack_guard_size = os::uses_stack_guard_pages() ? + thread->stack_red_zone_size() + thread->stack_yellow_zone_size() : 0; + size_t usable_stack_size = thread->stack_size() - stack_guard_size; + + // sp must be within the usable part of the stack (not in guards) + bool sp_safe = (sp < thread->stack_base()) && + (sp >= thread->stack_base() - usable_stack_size); + + + if (!sp_safe) { + return false; + } + + // Unextended sp must be within the stack and above or equal sp + bool unextended_sp_safe = (unextended_sp < thread->stack_base()) && (unextended_sp >= sp); + + if (!unextended_sp_safe) { + return false; + } + + // An fp must be within the stack and above (but not equal) sp. + bool fp_safe = (fp <= thread->stack_base()) && (fp > sp); + // an interpreter fp must be within the stack and above (but not equal) sp + bool fp_interp_safe = (fp <= thread->stack_base()) && (fp > sp) && + ((fp - sp) >= (ijava_state_size + top_ijava_frame_abi_size)); + + // We know sp/unextended_sp are safe, only fp is questionable here + + // If the current frame is known to the code cache then we can attempt to + // to construct the sender and do some validation of it. This goes a long way + // toward eliminating issues when we get in frame construction code + + if (_cb != NULL ){ + // Entry frame checks + if (is_entry_frame()) { + // An entry frame must have a valid fp. + return fp_safe && is_entry_frame_valid(thread); + } + + // Now check if the frame is complete and the test is + // reliable. Unfortunately we can only check frame completeness for + // runtime stubs and nmethods. Other generic buffer blobs are more + // problematic so we just assume they are OK. Adapter blobs never have a + // complete frame and are never OK + if (!_cb->is_frame_complete_at(_pc)) { + if (_cb->is_nmethod() || _cb->is_adapter_blob() || _cb->is_runtime_stub()) { + return false; + } + } + + // Could just be some random pointer within the codeBlob. + if (!_cb->code_contains(_pc)) { + return false; + } + + if (is_interpreted_frame() && !fp_interp_safe) { + return false; + } + + abi_minframe* sender_abi = (abi_minframe*) fp; + intptr_t* sender_sp = (intptr_t*) fp; + address sender_pc = (address) sender_abi->lr;; + + // We must always be able to find a recognizable pc. + CodeBlob* sender_blob = CodeCache::find_blob_unsafe(sender_pc); + if (sender_blob == NULL) { + return false; + } + + // Could be a zombie method + if (sender_blob->is_zombie() || sender_blob->is_unloaded()) { + return false; + } + + // It should be safe to construct the sender though it might not be valid. + + frame sender(sender_sp, sender_pc); + + // Do we have a valid fp? + address sender_fp = (address) sender.fp(); + + // sender_fp must be within the stack and above (but not + // equal) current frame's fp. + if (sender_fp > thread->stack_base() || sender_fp <= fp) { + return false; + } + + // If the potential sender is the interpreter then we can do some more checking. + if (Interpreter::contains(sender_pc)) { + return sender.is_interpreted_frame_valid(thread); + } + + // Could just be some random pointer within the codeBlob. + if (!sender.cb()->code_contains(sender_pc)) { + return false; + } + + // We should never be able to see an adapter if the current frame is something from code cache. + if (sender_blob->is_adapter_blob()) { + return false; + } + + if (sender.is_entry_frame()) { + return sender.is_entry_frame_valid(thread); + } + + // Frame size is always greater than zero. If the sender frame size is zero or less, + // something is really weird and we better give up. + if (sender_blob->frame_size() <= 0) { + return false; + } + + return true; } - return safe; + + // Must be native-compiled frame. Since sender will try and use fp to find + // linkages it must be safe + + if (!fp_safe) { + return false; + } + + return true; } bool frame::is_interpreted_frame() const { diff --git a/src/cpu/ppc/vm/globals_ppc.hpp b/src/cpu/ppc/vm/globals_ppc.hpp index 28ca2c0c8d83d3b2532a933b8b0a61aea09c500c..6fafcb9f554dd77a9372509921d5156f05f312f7 100644 --- a/src/cpu/ppc/vm/globals_ppc.hpp +++ b/src/cpu/ppc/vm/globals_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright 2012, 2018 SAP AG. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -33,10 +33,8 @@ // (see globals.hpp) define_pd_global(bool, ConvertSleepToYield, true); -define_pd_global(bool, ShareVtableStubs, false); // Improves performance markedly for mtrt and compress. define_pd_global(bool, NeedsDeoptSuspend, false); // Only register window machines need this. - define_pd_global(bool, ImplicitNullChecks, true); // Generate code for implicit null checks. define_pd_global(bool, TrapBasedNullChecks, true); define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs passed to check cast. diff --git a/src/cpu/ppc/vm/vm_version_ext_ppc.cpp b/src/cpu/ppc/vm/vm_version_ext_ppc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50c7a7c5953e756064170304a051a0afa7fd88b5 --- /dev/null +++ b/src/cpu/ppc/vm/vm_version_ext_ppc.cpp @@ -0,0 +1,87 @@ +/* + * 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 + * 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 "jvm.h" +#include "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" +#include "runtime/vm_version.hpp" +#include "vm_version_ext_ppc.hpp" + +// VM_Version_Ext statics +int VM_Version_Ext::_no_of_threads = 0; +int VM_Version_Ext::_no_of_cores = 0; +int VM_Version_Ext::_no_of_sockets = 0; +bool VM_Version_Ext::_initialized = false; +char VM_Version_Ext::_cpu_name[CPU_TYPE_DESC_BUF_SIZE] = {0}; +char VM_Version_Ext::_cpu_desc[CPU_DETAILED_DESC_BUF_SIZE] = {0}; + +// get cpu information. +void VM_Version_Ext::initialize_cpu_information(void) { + // do nothing if cpu info has been initialized + if (_initialized) { + return; + } + + _no_of_cores = os::processor_count(); + _no_of_threads = _no_of_cores; + _no_of_sockets = _no_of_cores; + snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE, "PowerPC POWER%lu", PowerArchitecturePPC64); + snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "PPC %s", cpu_features()); + _initialized = true; +} + +int VM_Version_Ext::number_of_threads(void) { + initialize_cpu_information(); + return _no_of_threads; +} + +int VM_Version_Ext::number_of_cores(void) { + initialize_cpu_information(); + return _no_of_cores; +} + +int VM_Version_Ext::number_of_sockets(void) { + initialize_cpu_information(); + return _no_of_sockets; +} + +const char* VM_Version_Ext::cpu_name(void) { + initialize_cpu_information(); + char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_TYPE_DESC_BUF_SIZE, mtTracing); + if (NULL == tmp) { + return NULL; + } + strncpy(tmp, _cpu_name, CPU_TYPE_DESC_BUF_SIZE); + return tmp; +} + +const char* VM_Version_Ext::cpu_description(void) { + initialize_cpu_information(); + char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_DETAILED_DESC_BUF_SIZE, mtTracing); + if (NULL == tmp) { + return NULL; + } + strncpy(tmp, _cpu_desc, CPU_DETAILED_DESC_BUF_SIZE); + return tmp; +} diff --git a/src/cpu/ppc/vm/vm_version_ext_ppc.hpp b/src/cpu/ppc/vm/vm_version_ext_ppc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5da856956c34d747e7cc3cb504d9888d83f7751e --- /dev/null +++ b/src/cpu/ppc/vm/vm_version_ext_ppc.hpp @@ -0,0 +1,62 @@ +/* + * 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 + * 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 CPU_PPC_VM_VM_VERSION_EXT_PPC_HPP +#define CPU_PPC_VM_VM_VERSION_EXT_PPC_HPP + +#include "utilities/macros.hpp" +#include "vm_version_ppc.hpp" + +#define CPU_INFO "cpu_info" +#define CPU_TYPE "fpu_type" +#define CPU_DESCRIPTION "implementation" +#define CHIP_ID "chip_id" +#define CORE_ID "core_id" + +class VM_Version_Ext : public VM_Version { + private: + + static const size_t CPU_TYPE_DESC_BUF_SIZE = 256; + static const size_t CPU_DETAILED_DESC_BUF_SIZE = 4096; + + static int _no_of_threads; + static int _no_of_cores; + static int _no_of_sockets; + static bool _initialized; + static char _cpu_name[CPU_TYPE_DESC_BUF_SIZE]; + static char _cpu_desc[CPU_DETAILED_DESC_BUF_SIZE]; + + static void initialize_cpu_information(void); + + public: + + static int number_of_threads(void); + static int number_of_cores(void); + static int number_of_sockets(void); + + static const char* cpu_name(void); + static const char* cpu_description(void); +}; + +#endif // CPU_PPC_VM_VM_VERSION_EXT_PPC_HPP diff --git a/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp b/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp index 87a95e5ec8cce05efc787095d190ec4d38966442..269b0a92764e48f71db56a4a0318903418b50264 100644 --- a/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp +++ b/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp @@ -55,6 +55,8 @@ LIR_Opr FrameMap::map_to_opr(BasicType type, VMRegPair* reg, bool outgoing) { opr = as_oop_opr(reg); } else if (type == T_METADATA) { opr = as_metadata_opr(reg); + } else if (type == T_ADDRESS) { + opr = as_address_opr(reg); } else { opr = as_opr(reg); } diff --git a/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp b/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp index b519b34dabd205afd2a301840b6d3a255cd13719..0bd581aaf7a9506969c99ec6010eb762760970f0 100644 --- a/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp +++ b/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp @@ -509,8 +509,13 @@ void LIR_Assembler::jobject2reg(jobject o, Register reg) { if (o == NULL) { __ set(NULL_WORD, reg); } else { +#ifdef ASSERT + { + ThreadInVMfromNative tiv(JavaThread::current()); + assert(Universe::heap()->is_in_reserved(JNIHandles::resolve(o)), "should be real oop"); + } +#endif int oop_index = __ oop_recorder()->find_index(o); - assert(Universe::heap()->is_in_reserved(JNIHandles::resolve(o)), "should be real oop"); RelocationHolder rspec = oop_Relocation::spec(oop_index); __ set(NULL_WORD, reg, rspec); // Will be set when the nmethod is created } diff --git a/src/cpu/sparc/vm/globals_sparc.hpp b/src/cpu/sparc/vm/globals_sparc.hpp index c692f6d9ad816da028543e54f22958c5fb8132ad..7549d1fcaca55dde392b7b59fedce3576f517b39 100644 --- a/src/cpu/sparc/vm/globals_sparc.hpp +++ b/src/cpu/sparc/vm/globals_sparc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -38,7 +38,6 @@ // according to the prior table. So, we let the thread continue and let it block by itself. define_pd_global(bool, DontYieldALot, true); // yield no more than 100 times per second define_pd_global(bool, ConvertSleepToYield, false); // do not convert sleep(0) to yield. Helps GUI -define_pd_global(bool, ShareVtableStubs, false); // improves performance markedly for mtrt and compress define_pd_global(bool, CountInterpCalls, false); // not implemented in the interpreter define_pd_global(bool, NeedsDeoptSuspend, true); // register window machines need this diff --git a/src/cpu/sparc/vm/vm_version_ext_sparc.cpp b/src/cpu/sparc/vm/vm_version_ext_sparc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d31262a0ff57735428f2e25012bbfe4380f2af2a --- /dev/null +++ b/src/cpu/sparc/vm/vm_version_ext_sparc.cpp @@ -0,0 +1,195 @@ +/* + * 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 + * 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 "jvm.h" +#include "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" +#include "vm_version_ext_sparc.hpp" + +// VM_Version_Ext statics +int VM_Version_Ext::_no_of_threads = 0; +int VM_Version_Ext::_no_of_cores = 0; +int VM_Version_Ext::_no_of_sockets = 0; +#if defined(SOLARIS) +kid_t VM_Version_Ext::_kcid = -1; +#endif +char VM_Version_Ext::_cpu_name[CPU_TYPE_DESC_BUF_SIZE] = {0}; +char VM_Version_Ext::_cpu_desc[CPU_DETAILED_DESC_BUF_SIZE] = {0}; + +#if defined(SOLARIS) +// get cpu information. It takes into account if the kstat chain id +// has been changed and update the info if necessary. +bool VM_Version_Ext::initialize_cpu_information(void) { + + int core_id = -1; + int chip_id = -1; + int len = 0; + char* src_string = NULL; + kstat_ctl_t* kc = kstat_open(); + if (!kc) { + return false; + } + + // check if kstat chain has been updated + kid_t kcid = kstat_chain_update(kc); + if (kcid == -1) { + kstat_close(kc); + return false; + } + + bool updated = ((kcid > 0) && (kcid != _kcid)) || + ((kcid == 0) && (_kcid == -1)); + if (!updated) { + kstat_close(kc); + return true; + } + + // update the cached _kcid + _kcid = kcid; + + // find the number of online processors + // for modern processsors, it is also known as the + // hardware threads. + _no_of_threads = sysconf(_SC_NPROCESSORS_ONLN); + + if (_no_of_threads <= 0 ) { + kstat_close(kc); + return false; + } + + _no_of_cores = 0; + _no_of_sockets = 0; + + // loop through the kstat chain + kstat_t* ksp = NULL; + for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { + // only interested in "cpu_info" + if (strcmp(ksp->ks_module, (char*)CPU_INFO) == 0) { + if (kstat_read(kc, ksp, NULL) == -1) { + kstat_close(kc); + return false; + } + if (ksp->ks_data != NULL) { + kstat_named_t* knm = (kstat_named_t *)ksp->ks_data; + // loop through the number of fields in each record + for (int i = 0; i < ksp->ks_ndata; i++) { + // set cpu type if it hasn't been already set + if ((strcmp((const char*)&(knm[i].name), CPU_TYPE) == 0) && + (_cpu_name[0] == '\0')) { + if (knm[i].data_type == KSTAT_DATA_STRING) { + src_string = (char*)KSTAT_NAMED_STR_PTR(&knm[i]); + } else { + src_string = (char*)&(knm[i].value.c[0]); + } + len = strlen(src_string); + if (len < CPU_TYPE_DESC_BUF_SIZE) { + jio_snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE, + "%s", src_string); + } + } + + // set cpu description if it hasn't been already set + if ((strcmp((const char*)&(knm[i].name), CPU_DESCRIPTION) == 0) && + (_cpu_desc[0] == '\0')) { + if (knm[i].data_type == KSTAT_DATA_STRING) { + src_string = (char*)KSTAT_NAMED_STR_PTR(&knm[i]); + } else { + src_string = (char*)&(knm[i].value.c[0]); + } + len = strlen(src_string); + if (len < CPU_DETAILED_DESC_BUF_SIZE) { + jio_snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, + "%s", src_string); + } + } + + // count the number of sockets based on the chip id + if (strcmp((const char*)&(knm[i].name), CHIP_ID) == 0) { + if (chip_id != knm[i].value.l) { + chip_id = knm[i].value.l; + _no_of_sockets++; + } + } + + // count the number of cores based on the core id + if (strcmp((const char*)&(knm[i].name), CORE_ID) == 0) { + if (core_id != knm[i].value.l) { + core_id = knm[i].value.l; + _no_of_cores++; + } + } + } + } + } + } + + kstat_close(kc); + return true; +} +#elif defined(LINUX) +// get cpu information. +bool VM_Version_Ext::initialize_cpu_information(void) { + // Not yet implemented. + return false; +} +#endif + +int VM_Version_Ext::number_of_threads(void) { + initialize_cpu_information(); + return _no_of_threads; +} + +int VM_Version_Ext::number_of_cores(void) { + initialize_cpu_information(); + return _no_of_cores; +} + +int VM_Version_Ext::number_of_sockets(void) { + initialize_cpu_information(); + return _no_of_sockets; +} + +const char* VM_Version_Ext::cpu_name(void) { + if (!initialize_cpu_information()) { + return NULL; + } + char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_TYPE_DESC_BUF_SIZE, mtTracing); + if (NULL == tmp) { + return NULL; + } + strncpy(tmp, _cpu_name, CPU_TYPE_DESC_BUF_SIZE); + return tmp; +} + +const char* VM_Version_Ext::cpu_description(void) { + if (!initialize_cpu_information()) { + return NULL; + } + char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_DETAILED_DESC_BUF_SIZE, mtTracing); + if (NULL == tmp) { + return NULL; + } + strncpy(tmp, _cpu_desc, CPU_DETAILED_DESC_BUF_SIZE); + return tmp; +} diff --git a/src/cpu/sparc/vm/vm_version_ext_sparc.hpp b/src/cpu/sparc/vm/vm_version_ext_sparc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..767cc24cb6707a6e460ab0a57eb6a394765bbfc4 --- /dev/null +++ b/src/cpu/sparc/vm/vm_version_ext_sparc.hpp @@ -0,0 +1,69 @@ +/* + * 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 + * 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 CPU_SPARC_VM_VM_VERSION_EXT_SPARC_HPP +#define CPU_SPARC_VM_VM_VERSION_EXT_SPARC_HPP + +#include "utilities/macros.hpp" +#include "vm_version_sparc.hpp" + +#if defined(SOLARIS) +#include +#include +#endif + +#define CPU_INFO "cpu_info" +#define CPU_TYPE "fpu_type" +#define CPU_DESCRIPTION "implementation" +#define CHIP_ID "chip_id" +#define CORE_ID "core_id" + +class VM_Version_Ext : public VM_Version { + private: + + static const size_t CPU_TYPE_DESC_BUF_SIZE = 256; + static const size_t CPU_DETAILED_DESC_BUF_SIZE = 4096; + + static int _no_of_threads; + static int _no_of_cores; + static int _no_of_sockets; +#if defined(SOLARIS) + static kid_t _kcid; +#endif + static char _cpu_name[CPU_TYPE_DESC_BUF_SIZE]; + static char _cpu_desc[CPU_DETAILED_DESC_BUF_SIZE]; + + static bool initialize_cpu_information(void); + + public: + + static int number_of_threads(void); + static int number_of_cores(void); + static int number_of_sockets(void); + + static const char* cpu_name(void); + static const char* cpu_description(void); +}; + +#endif // CPU_SPARC_VM_VM_VERSION_EXT_SPARC_HPP diff --git a/src/cpu/x86/vm/c1_FrameMap_x86.cpp b/src/cpu/x86/vm/c1_FrameMap_x86.cpp index bec7eed65e9597dc03196a67e289049266408d42..50cfed73e9a098c40a719d3c80f48ad9b2a251c8 100644 --- a/src/cpu/x86/vm/c1_FrameMap_x86.cpp +++ b/src/cpu/x86/vm/c1_FrameMap_x86.cpp @@ -54,6 +54,8 @@ LIR_Opr FrameMap::map_to_opr(BasicType type, VMRegPair* reg, bool) { opr = as_oop_opr(reg); } else if (type == T_METADATA) { opr = as_metadata_opr(reg); + } else if (type == T_ADDRESS) { + opr = as_address_opr(reg); } else { opr = as_opr(reg); } diff --git a/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp b/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp index 9fcb533168cc8b5cf5e5495374f724d7efa4aef8..b1ba43f7626de7e28827d80247aba9c27655c081 100644 --- a/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp +++ b/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp @@ -964,7 +964,7 @@ void LIR_Assembler::reg2stack(LIR_Opr src, LIR_Opr dest, BasicType type, bool po if (type == T_OBJECT || type == T_ARRAY) { __ verify_oop(src->as_register()); __ movptr (dst, src->as_register()); - } else if (type == T_METADATA) { + } else if (type == T_METADATA || type == T_ADDRESS) { __ movptr (dst, src->as_register()); } else { __ movl (dst, src->as_register()); @@ -1145,7 +1145,7 @@ void LIR_Assembler::stack2reg(LIR_Opr src, LIR_Opr dest, BasicType type) { if (type == T_ARRAY || type == T_OBJECT) { __ movptr(dest->as_register(), frame_map()->address_for_slot(src->single_stack_ix())); __ verify_oop(dest->as_register()); - } else if (type == T_METADATA) { + } else if (type == T_METADATA || type == T_ADDRESS) { __ movptr(dest->as_register(), frame_map()->address_for_slot(src->single_stack_ix())); } else { __ movl(dest->as_register(), frame_map()->address_for_slot(src->single_stack_ix())); diff --git a/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp b/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp index 7f6b1d60ea36d6255dc7ab6cd5ae00a43fc76dbf..5eacb6b54d4ab6abdb380f3efe00d5c1caf266d6 100644 --- a/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp +++ b/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp @@ -233,8 +233,8 @@ void LIRGenerator::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr bas } -bool LIRGenerator::strength_reduce_multiply(LIR_Opr left, int c, LIR_Opr result, LIR_Opr tmp) { - if (tmp->is_valid()) { +bool LIRGenerator::strength_reduce_multiply(LIR_Opr left, jint c, LIR_Opr result, LIR_Opr tmp) { + if (tmp->is_valid() && c > 0 && c < max_jint) { if (is_power_of_2(c + 1)) { __ move(left, tmp); __ shift_left(left, log2_jint(c + 1), left); @@ -602,8 +602,8 @@ void LIRGenerator::do_ArithmeticOp_Int(ArithmeticOp* x) { bool use_constant = false; bool use_tmp = false; if (right_arg->is_constant()) { - int iconst = right_arg->get_jint_constant(); - if (iconst > 0) { + jint iconst = right_arg->get_jint_constant(); + if (iconst > 0 && iconst < max_jint) { if (is_power_of_2(iconst)) { use_constant = true; } else if (is_power_of_2(iconst - 1) || is_power_of_2(iconst + 1)) { diff --git a/src/cpu/x86/vm/globals_x86.hpp b/src/cpu/x86/vm/globals_x86.hpp index 57adca2ff81bef48d6412ceea789381f142ffe3b..42ea51d76ebacc1b27128c3cb38877f7e7f6e52e 100644 --- a/src/cpu/x86/vm/globals_x86.hpp +++ b/src/cpu/x86/vm/globals_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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,6 @@ // (see globals.hpp) define_pd_global(bool, ConvertSleepToYield, true); -define_pd_global(bool, ShareVtableStubs, true); define_pd_global(bool, CountInterpCalls, true); define_pd_global(bool, NeedsDeoptSuspend, false); // only register window machines need this diff --git a/src/cpu/x86/vm/rdtsc_x86.cpp b/src/cpu/x86/vm/rdtsc_x86.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f567e9d6fb1d62239e348dd986434d01aa62d8f5 --- /dev/null +++ b/src/cpu/x86/vm/rdtsc_x86.cpp @@ -0,0 +1,225 @@ +/* + * 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 + * 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 "precompiled.hpp" + +//#include "os_linux_x86.inline.hpp" +#ifdef TARGET_OS_ARCH_linux_x86 +# include "os_linux_x86.inline.hpp" +#endif +#ifdef TARGET_OS_ARCH_bsd_x86 +# include "os_bsd_x86.inline.hpp" +#endif +#ifdef TARGET_OS_ARCH_windows_x86 +# include "os_windows_x86.inline.hpp" +#endif +#ifdef TARGET_OS_ARCH_solaris_x86 +# include "os_solaris_x86.inline.hpp" +#endif + +#include "rdtsc_x86.hpp" +#include "runtime/thread.inline.hpp" +#include "vm_version_ext_x86.hpp" +#include "runtime/os.hpp" + +// The following header contains the implementations of rdtsc() + +static jlong _epoch = 0; +static bool rdtsc_elapsed_counter_enabled = false; +static jlong tsc_frequency = 0; + +static jlong set_epoch() { + assert(0 == _epoch, "invariant"); + _epoch = os::rdtsc(); + return _epoch; +} + +// Base loop to estimate ticks frequency for tsc counter from user mode. +// Volatiles and sleep() are used to prevent compiler from applying optimizations. +static void do_time_measurements(volatile jlong& time_base, + volatile jlong& time_fast, + volatile jlong& time_base_elapsed, + volatile jlong& time_fast_elapsed) { + static const unsigned int FT_SLEEP_MILLISECS = 1; + const unsigned int loopcount = 3; + + volatile jlong start = 0; + volatile jlong fstart = 0; + volatile jlong end = 0; + volatile jlong fend = 0; + + // Figure out the difference between rdtsc and os provided timer. + // base algorithm adopted from JRockit. + for (unsigned int times = 0; times < loopcount; times++) { + start = os::elapsed_counter(); + OrderAccess::fence(); + fstart = os::rdtsc(); + + // use sleep to prevent compiler from optimizing + os::sleep(Thread::current(), FT_SLEEP_MILLISECS, true); + + end = os::elapsed_counter(); + OrderAccess::fence(); + fend = os::rdtsc(); + + time_base += end - start; + time_fast += fend - fstart; + + // basis for calculating the os tick start + // to fast time tick start offset + time_base_elapsed += end; + time_fast_elapsed += (fend - _epoch); + } + + time_base /= loopcount; + time_fast /= loopcount; + time_base_elapsed /= loopcount; + time_fast_elapsed /= loopcount; +} + +static jlong initialize_frequency() { + assert(0 == tsc_frequency, "invariant"); + assert(0 == _epoch, "invariant"); + const jlong initial_counter = set_epoch(); + if (initial_counter == 0) { + return 0; + } + // os time frequency + static double os_freq = (double)os::elapsed_frequency(); + assert(os_freq > 0, "os_elapsed frequency corruption!"); + + double tsc_freq = .0; + double os_to_tsc_conv_factor = 1.0; + + // if platform supports invariant tsc, + // apply higher resolution and granularity for conversion calculations + if (VM_Version_Ext::supports_tscinv_ext()) { + // for invariant tsc platforms, take the maximum qualified cpu frequency + tsc_freq = (double)VM_Version_Ext::maximum_qualified_cpu_frequency(); + os_to_tsc_conv_factor = tsc_freq / os_freq; + } else { + // use measurements to estimate + // a conversion factor and the tsc frequency + + volatile jlong time_base = 0; + volatile jlong time_fast = 0; + volatile jlong time_base_elapsed = 0; + volatile jlong time_fast_elapsed = 0; + + // do measurements to get base data + // on os timer and fast ticks tsc time relation. + do_time_measurements(time_base, time_fast, time_base_elapsed, time_fast_elapsed); + + // if invalid measurements, cannot proceed + if (time_fast == 0 || time_base == 0) { + return 0; + } + + os_to_tsc_conv_factor = (double)time_fast / (double)time_base; + if (os_to_tsc_conv_factor > 1) { + // estimate on tsc counter frequency + tsc_freq = os_to_tsc_conv_factor * os_freq; + } + } + + if ((tsc_freq < 0) || (tsc_freq > 0 && tsc_freq <= os_freq) || (os_to_tsc_conv_factor <= 1)) { + // safer to run with normal os time + tsc_freq = .0; + } + + // frequency of the tsc_counter + return (jlong)tsc_freq; +} + +static bool initialize_elapsed_counter() { + tsc_frequency = initialize_frequency(); + return tsc_frequency != 0 && _epoch != 0; +} + +static bool ergonomics() { + const bool invtsc_support = Rdtsc::is_supported(); + if (FLAG_IS_DEFAULT(UseFastUnorderedTimeStamps) && invtsc_support) { + FLAG_SET_ERGO(bool, UseFastUnorderedTimeStamps, true); + } + + bool ft_enabled = UseFastUnorderedTimeStamps && invtsc_support; + + if (!ft_enabled) { + if (UseFastUnorderedTimeStamps && VM_Version::supports_tsc()) { + warning("\nThe hardware does not support invariant tsc (INVTSC) register and/or cannot guarantee tsc synchronization between sockets at startup.\n"\ + "Values returned via rdtsc() are not guaranteed to be accurate, esp. when comparing values from cross sockets reads. Enabling UseFastUnorderedTimeStamps on non-invariant tsc hardware should be considered experimental.\n"); + ft_enabled = true; + } + } + + if (!ft_enabled) { + // Warn if unable to support command-line flag + if (UseFastUnorderedTimeStamps && !VM_Version::supports_tsc()) { + warning("Ignoring UseFastUnorderedTimeStamps, hardware does not support normal tsc"); + } + } + + return ft_enabled; +} + +bool Rdtsc::is_supported() { + return VM_Version_Ext::supports_tscinv_ext(); +} + +bool Rdtsc::is_elapsed_counter_enabled() { + return rdtsc_elapsed_counter_enabled; +} + +jlong Rdtsc::frequency() { + return tsc_frequency; +} + +jlong Rdtsc::elapsed_counter() { + return os::rdtsc() - _epoch; +} + +jlong Rdtsc::epoch() { + return _epoch; +} + +jlong Rdtsc::raw() { + return os::rdtsc(); +} + +bool Rdtsc::initialize() { + static bool initialized = false; + if (!initialized) { + assert(!rdtsc_elapsed_counter_enabled, "invariant"); + VM_Version_Ext::initialize(); + assert(0 == tsc_frequency, "invariant"); + assert(0 == _epoch, "invariant"); + bool result = initialize_elapsed_counter(); // init hw + if (result) { + result = ergonomics(); // check logical state + } + rdtsc_elapsed_counter_enabled = result; + initialized = true; + } + return rdtsc_elapsed_counter_enabled; +} diff --git a/src/cpu/x86/vm/rdtsc_x86.hpp b/src/cpu/x86/vm/rdtsc_x86.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c455c0018b671de73f2edb7089bb7b906b82c0da --- /dev/null +++ b/src/cpu/x86/vm/rdtsc_x86.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012, 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 CPU_X86_VM_RDTSC_X86_HPP +#define CPU_X86_VM_RDTSC_X86_HPP + +#include "memory/allocation.hpp" +#include "utilities/macros.hpp" + +// Interface to the x86 rdtsc() time counter, if available. +// Not guaranteed to be synchronized across hardware threads and +// therefore software threads, and can be updated asynchronously +// by software. elapsed_counter() can jump backwards +// as well as jump forward when threads query different cores/sockets. +// Very much not recommended for general use. +// INVTSC is a minimal requirement for auto-enablement. + +class Rdtsc : AllStatic { + public: + static jlong elapsed_counter(); // provides quick time stamps + static jlong frequency(); // tsc register + static bool is_supported(); // InvariantTSC + static jlong raw(); // direct rdtsc() access + static bool is_elapsed_counter_enabled(); // turn off with -XX:-UseFastUnorderedTimeStamps + static jlong epoch(); + static bool initialize(); +}; + +#endif // CPU_X86_VM_RDTSC_X86_HPP diff --git a/src/cpu/x86/vm/sharedRuntime_x86_64.cpp b/src/cpu/x86/vm/sharedRuntime_x86_64.cpp index c2f69f7c3c0954b9e5ddc2ff62b3171cd52c6212..2ba2c1531e64deb3f9d143d7be1aa89275145c65 100644 --- a/src/cpu/x86/vm/sharedRuntime_x86_64.cpp +++ b/src/cpu/x86/vm/sharedRuntime_x86_64.cpp @@ -2098,7 +2098,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // critical natives they are offset down. GrowableArray arg_order(2 * total_in_args); VMRegPair tmp_vmreg; - tmp_vmreg.set1(rbx->as_VMReg()); + tmp_vmreg.set2(rbx->as_VMReg()); if (!is_critical_native) { for (int i = total_in_args - 1, c_arg = total_c_args - 1; i >= 0; i--, c_arg--) { diff --git a/src/cpu/x86/vm/vm_version_ext_x86.cpp b/src/cpu/x86/vm/vm_version_ext_x86.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cea3d8900dc4646109bd882b23e2b56646794300 --- /dev/null +++ b/src/cpu/x86/vm/vm_version_ext_x86.cpp @@ -0,0 +1,966 @@ +/* + * 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 + * 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 "precompiled.hpp" +#include "jvm.h" +#include "utilities/macros.hpp" +#include "asm/macroAssembler.hpp" +#include "asm/macroAssembler.inline.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/java.hpp" +#include "runtime/stubCodeGenerator.hpp" +#include "vm_version_ext_x86.hpp" + +typedef enum { + CPU_FAMILY_8086_8088 = 0, + CPU_FAMILY_INTEL_286 = 2, + CPU_FAMILY_INTEL_386 = 3, + CPU_FAMILY_INTEL_486 = 4, + CPU_FAMILY_PENTIUM = 5, + CPU_FAMILY_PENTIUMPRO = 6, // Same family several models + CPU_FAMILY_PENTIUM_4 = 0xF +} FamilyFlag; + + typedef enum { + RDTSCP_FLAG = 0x08000000, // bit 27 + INTEL64_FLAG = 0x20000000 // bit 29 + } _featureExtendedEdxFlag; + +#define CPUID_STANDARD_FN 0x0 +#define CPUID_STANDARD_FN_1 0x1 +#define CPUID_STANDARD_FN_4 0x4 +#define CPUID_STANDARD_FN_B 0xb + +#define CPUID_EXTENDED_FN 0x80000000 +#define CPUID_EXTENDED_FN_1 0x80000001 +#define CPUID_EXTENDED_FN_2 0x80000002 +#define CPUID_EXTENDED_FN_3 0x80000003 +#define CPUID_EXTENDED_FN_4 0x80000004 +#define CPUID_EXTENDED_FN_7 0x80000007 +#define CPUID_EXTENDED_FN_8 0x80000008 + +typedef enum { + FPU_FLAG = 0x00000001, + VME_FLAG = 0x00000002, + DE_FLAG = 0x00000004, + PSE_FLAG = 0x00000008, + TSC_FLAG = 0x00000010, + MSR_FLAG = 0x00000020, + PAE_FLAG = 0x00000040, + MCE_FLAG = 0x00000080, + CX8_FLAG = 0x00000100, + APIC_FLAG = 0x00000200, + SEP_FLAG = 0x00000800, + MTRR_FLAG = 0x00001000, + PGE_FLAG = 0x00002000, + MCA_FLAG = 0x00004000, + CMOV_FLAG = 0x00008000, + PAT_FLAG = 0x00010000, + PSE36_FLAG = 0x00020000, + PSNUM_FLAG = 0x00040000, + CLFLUSH_FLAG = 0x00080000, + DTS_FLAG = 0x00200000, + ACPI_FLAG = 0x00400000, + MMX_FLAG = 0x00800000, + FXSR_FLAG = 0x01000000, + SSE_FLAG = 0x02000000, + SSE2_FLAG = 0x04000000, + SS_FLAG = 0x08000000, + HTT_FLAG = 0x10000000, + TM_FLAG = 0x20000000 +} FeatureEdxFlag; + +static BufferBlob* cpuid_brand_string_stub_blob; +static const int cpuid_brand_string_stub_size = 550; + +extern "C" { + typedef void (*getCPUIDBrandString_stub_t)(void*); +} + +static getCPUIDBrandString_stub_t getCPUIDBrandString_stub = NULL; + +class VM_Version_Ext_StubGenerator: public StubCodeGenerator { + public: + + VM_Version_Ext_StubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} + + address generate_getCPUIDBrandString(void) { + // Flags to test CPU type. + const uint32_t HS_EFL_AC = 0x40000; + const uint32_t HS_EFL_ID = 0x200000; + // Values for when we don't have a CPUID instruction. + const int CPU_FAMILY_SHIFT = 8; + const uint32_t CPU_FAMILY_386 = (3 << CPU_FAMILY_SHIFT); + const uint32_t CPU_FAMILY_486 = (4 << CPU_FAMILY_SHIFT); + + Label detect_486, cpu486, detect_586, done, ext_cpuid; + + StubCodeMark mark(this, "VM_Version_Ext", "getCPUIDNameInfo_stub"); +# define __ _masm-> + + address start = __ pc(); + + // + // void getCPUIDBrandString(VM_Version::CpuidInfo* cpuid_info); + // + // LP64: rcx and rdx are first and second argument registers on windows + + __ push(rbp); +#ifdef _LP64 + __ mov(rbp, c_rarg0); // cpuid_info address +#else + __ movptr(rbp, Address(rsp, 8)); // cpuid_info address +#endif + __ push(rbx); + __ push(rsi); + __ pushf(); // preserve rbx, and flags + __ pop(rax); + __ push(rax); + __ mov(rcx, rax); + // + // if we are unable to change the AC flag, we have a 386 + // + __ xorl(rax, HS_EFL_AC); + __ push(rax); + __ popf(); + __ pushf(); + __ pop(rax); + __ cmpptr(rax, rcx); + __ jccb(Assembler::notEqual, detect_486); + + __ movl(rax, CPU_FAMILY_386); + __ jmp(done); + + // + // If we are unable to change the ID flag, we have a 486 which does + // not support the "cpuid" instruction. + // + __ bind(detect_486); + __ mov(rax, rcx); + __ xorl(rax, HS_EFL_ID); + __ push(rax); + __ popf(); + __ pushf(); + __ pop(rax); + __ cmpptr(rcx, rax); + __ jccb(Assembler::notEqual, detect_586); + + __ bind(cpu486); + __ movl(rax, CPU_FAMILY_486); + __ jmp(done); + + // + // At this point, we have a chip which supports the "cpuid" instruction + // + __ bind(detect_586); + __ xorl(rax, rax); + __ cpuid(); + __ orl(rax, rax); + __ jcc(Assembler::equal, cpu486); // if cpuid doesn't support an input + // value of at least 1, we give up and + // assume a 486 + + // + // Extended cpuid(0x80000000) for processor brand string detection + // + __ bind(ext_cpuid); + __ movl(rax, CPUID_EXTENDED_FN); + __ cpuid(); + __ cmpl(rax, CPUID_EXTENDED_FN_4); + __ jcc(Assembler::below, done); + + // + // Extended cpuid(0x80000002) // first 16 bytes in brand string + // + __ movl(rax, CPUID_EXTENDED_FN_2); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_0_offset()))); + __ movl(Address(rsi, 0), rax); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_1_offset()))); + __ movl(Address(rsi, 0), rbx); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_2_offset()))); + __ movl(Address(rsi, 0), rcx); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_3_offset()))); + __ movl(Address(rsi,0), rdx); + + // + // Extended cpuid(0x80000003) // next 16 bytes in brand string + // + __ movl(rax, CPUID_EXTENDED_FN_3); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_4_offset()))); + __ movl(Address(rsi, 0), rax); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_5_offset()))); + __ movl(Address(rsi, 0), rbx); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_6_offset()))); + __ movl(Address(rsi, 0), rcx); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_7_offset()))); + __ movl(Address(rsi,0), rdx); + + // + // Extended cpuid(0x80000004) // last 16 bytes in brand string + // + __ movl(rax, CPUID_EXTENDED_FN_4); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_8_offset()))); + __ movl(Address(rsi, 0), rax); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_9_offset()))); + __ movl(Address(rsi, 0), rbx); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_10_offset()))); + __ movl(Address(rsi, 0), rcx); + __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_11_offset()))); + __ movl(Address(rsi,0), rdx); + + // + // return + // + __ bind(done); + __ popf(); + __ pop(rsi); + __ pop(rbx); + __ pop(rbp); + __ ret(0); + +# undef __ + + return start; + }; +}; + + +// VM_Version_Ext statics +const size_t VM_Version_Ext::VENDOR_LENGTH = 13; +const size_t VM_Version_Ext::CPU_EBS_MAX_LENGTH = (3 * 4 * 4 + 1); +const size_t VM_Version_Ext::CPU_TYPE_DESC_BUF_SIZE = 256; +const size_t VM_Version_Ext::CPU_DETAILED_DESC_BUF_SIZE = 4096; +char* VM_Version_Ext::_cpu_brand_string = NULL; +jlong VM_Version_Ext::_max_qualified_cpu_frequency = 0; + +int VM_Version_Ext::_no_of_threads = 0; +int VM_Version_Ext::_no_of_cores = 0; +int VM_Version_Ext::_no_of_packages = 0; + +void VM_Version_Ext::initialize(void) { + ResourceMark rm; + + cpuid_brand_string_stub_blob = BufferBlob::create("getCPUIDBrandString_stub", cpuid_brand_string_stub_size); + if (cpuid_brand_string_stub_blob == NULL) { + vm_exit_during_initialization("Unable to allocate getCPUIDBrandString_stub"); + } + CodeBuffer c(cpuid_brand_string_stub_blob); + VM_Version_Ext_StubGenerator g(&c); + getCPUIDBrandString_stub = CAST_TO_FN_PTR(getCPUIDBrandString_stub_t, + g.generate_getCPUIDBrandString()); +} + +const char* VM_Version_Ext::cpu_model_description(void) { + uint32_t cpu_family = extended_cpu_family(); + uint32_t cpu_model = extended_cpu_model(); + const char* model = NULL; + + if (cpu_family == CPU_FAMILY_PENTIUMPRO) { + for (uint32_t i = 0; i <= cpu_model; i++) { + model = _model_id_pentium_pro[i]; + if (model == NULL) { + break; + } + } + } + return model; +} + +const char* VM_Version_Ext::cpu_brand_string(void) { + if (_cpu_brand_string == NULL) { + _cpu_brand_string = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_EBS_MAX_LENGTH, mtInternal); + if (NULL == _cpu_brand_string) { + return NULL; + } + int ret_val = cpu_extended_brand_string(_cpu_brand_string, CPU_EBS_MAX_LENGTH); + if (ret_val != OS_OK) { + FREE_C_HEAP_ARRAY(char, _cpu_brand_string, mtInternal); + _cpu_brand_string = NULL; + } + } + return _cpu_brand_string; +} + +const char* VM_Version_Ext::cpu_brand(void) { + const char* brand = NULL; + + if ((_cpuid_info.std_cpuid1_ebx.value & 0xFF) > 0) { + int brand_num = _cpuid_info.std_cpuid1_ebx.value & 0xFF; + brand = _brand_id[0]; + for (int i = 0; brand != NULL && i <= brand_num; i += 1) { + brand = _brand_id[i]; + } + } + return brand; +} + +bool VM_Version_Ext::cpu_is_em64t(void) { + return ((_cpuid_info.ext_cpuid1_edx.value & INTEL64_FLAG) == INTEL64_FLAG); +} + +bool VM_Version_Ext::is_netburst(void) { + return (is_intel() && (extended_cpu_family() == CPU_FAMILY_PENTIUM_4)); +} + +bool VM_Version_Ext::supports_tscinv_ext(void) { + if (!supports_tscinv_bit()) { + return false; + } + + if (is_intel()) { + return true; + } + + if (is_amd()) { + return !is_amd_Barcelona(); + } + + return false; +} + +void VM_Version_Ext::resolve_cpu_information_details(void) { + + // in future we want to base this information on proper cpu + // and cache topology enumeration such as: + // Intel 64 Architecture Processor Topology Enumeration + // which supports system cpu and cache topology enumeration + // either using 2xAPICIDs or initial APICIDs + + // currently only rough cpu information estimates + // which will not necessarily reflect the exact configuration of the system + + // this is the number of logical hardware threads + // visible to the operating system + _no_of_threads = os::processor_count(); + + // find out number of threads per cpu package + int threads_per_package = threads_per_core() * cores_per_cpu(); + + // use amount of threads visible to the process in order to guess number of sockets + _no_of_packages = _no_of_threads / threads_per_package; + + // process might only see a subset of the total number of threads + // from a single processor package. Virtualization/resource management for example. + // If so then just write a hard 1 as num of pkgs. + if (0 == _no_of_packages) { + _no_of_packages = 1; + } + + // estimate the number of cores + _no_of_cores = cores_per_cpu() * _no_of_packages; +} + +int VM_Version_Ext::number_of_threads(void) { + if (_no_of_threads == 0) { + resolve_cpu_information_details(); + } + return _no_of_threads; +} + +int VM_Version_Ext::number_of_cores(void) { + if (_no_of_cores == 0) { + resolve_cpu_information_details(); + } + return _no_of_cores; +} + +int VM_Version_Ext::number_of_sockets(void) { + if (_no_of_packages == 0) { + resolve_cpu_information_details(); + } + return _no_of_packages; +} + +const char* VM_Version_Ext::cpu_family_description(void) { + int cpu_family_id = extended_cpu_family(); + if (is_amd()) { + return _family_id_amd[cpu_family_id]; + } + if (is_intel()) { + if (cpu_family_id == CPU_FAMILY_PENTIUMPRO) { + return cpu_model_description(); + } + return _family_id_intel[cpu_family_id]; + } + return "Unknown x86"; +} + +int VM_Version_Ext::cpu_type_description(char* const buf, size_t buf_len) { + assert(buf != NULL, "buffer is NULL!"); + assert(buf_len >= CPU_TYPE_DESC_BUF_SIZE, "buffer len should at least be == CPU_TYPE_DESC_BUF_SIZE!"); + + const char* cpu_type = NULL; + const char* x64 = NULL; + + if (is_intel()) { + cpu_type = "Intel"; + x64 = cpu_is_em64t() ? " Intel64" : ""; + } else if (is_amd()) { + cpu_type = "AMD"; + x64 = cpu_is_em64t() ? " AMD64" : ""; + } else { + cpu_type = "Unknown x86"; + x64 = cpu_is_em64t() ? " x86_64" : ""; + } + + jio_snprintf(buf, buf_len, "%s %s%s SSE SSE2%s%s%s%s%s%s%s%s", + cpu_type, + cpu_family_description(), + supports_ht() ? " (HT)" : "", + supports_sse3() ? " SSE3" : "", + supports_ssse3() ? " SSSE3" : "", + supports_sse4_1() ? " SSE4.1" : "", + supports_sse4_2() ? " SSE4.2" : "", + supports_sse4a() ? " SSE4A" : "", + is_netburst() ? " Netburst" : "", + is_intel_family_core() ? " Core" : "", + x64); + + return OS_OK; +} + +int VM_Version_Ext::cpu_extended_brand_string(char* const buf, size_t buf_len) { + assert(buf != NULL, "buffer is NULL!"); + assert(buf_len >= CPU_EBS_MAX_LENGTH, "buffer len should at least be == CPU_EBS_MAX_LENGTH!"); + assert(getCPUIDBrandString_stub != NULL, "not initialized"); + + // invoke newly generated asm code to fetch CPU Brand String + getCPUIDBrandString_stub(&_cpuid_info); + + // fetch results into buffer + *((uint32_t*) &buf[0]) = _cpuid_info.proc_name_0; + *((uint32_t*) &buf[4]) = _cpuid_info.proc_name_1; + *((uint32_t*) &buf[8]) = _cpuid_info.proc_name_2; + *((uint32_t*) &buf[12]) = _cpuid_info.proc_name_3; + *((uint32_t*) &buf[16]) = _cpuid_info.proc_name_4; + *((uint32_t*) &buf[20]) = _cpuid_info.proc_name_5; + *((uint32_t*) &buf[24]) = _cpuid_info.proc_name_6; + *((uint32_t*) &buf[28]) = _cpuid_info.proc_name_7; + *((uint32_t*) &buf[32]) = _cpuid_info.proc_name_8; + *((uint32_t*) &buf[36]) = _cpuid_info.proc_name_9; + *((uint32_t*) &buf[40]) = _cpuid_info.proc_name_10; + *((uint32_t*) &buf[44]) = _cpuid_info.proc_name_11; + + return OS_OK; +} + +size_t VM_Version_Ext::cpu_write_support_string(char* const buf, size_t buf_len) { + guarantee(buf != NULL, "buffer is NULL!"); + guarantee(buf_len > 0, "buffer len not enough!"); + + unsigned int flag = 0; + unsigned int fi = 0; + size_t written = 0; + const char* prefix = ""; + +#define WRITE_TO_BUF(string) \ + { \ + int res = jio_snprintf(&buf[written], buf_len - written, "%s%s", prefix, string); \ + if (res < 0) { \ + return buf_len - 1; \ + } \ + written += res; \ + if (prefix[0] == '\0') { \ + prefix = ", "; \ + } \ + } + + for (flag = 1, fi = 0; flag <= 0x20000000 ; flag <<= 1, fi++) { + if (flag == HTT_FLAG && (((_cpuid_info.std_cpuid1_ebx.value >> 16) & 0xff) <= 1)) { + continue; /* no hyperthreading */ + } else if (flag == SEP_FLAG && (cpu_family() == CPU_FAMILY_PENTIUMPRO && ((_cpuid_info.std_cpuid1_eax.value & 0xff) < 0x33))) { + continue; /* no fast system call */ + } + if ((_cpuid_info.std_cpuid1_edx.value & flag) && strlen(_feature_edx_id[fi]) > 0) { + WRITE_TO_BUF(_feature_edx_id[fi]); + } + } + + for (flag = 1, fi = 0; flag <= 0x20000000; flag <<= 1, fi++) { + if ((_cpuid_info.std_cpuid1_ecx.value & flag) && strlen(_feature_ecx_id[fi]) > 0) { + WRITE_TO_BUF(_feature_ecx_id[fi]); + } + } + + for (flag = 1, fi = 0; flag <= 0x20000000 ; flag <<= 1, fi++) { + if ((_cpuid_info.ext_cpuid1_ecx.value & flag) && strlen(_feature_extended_ecx_id[fi]) > 0) { + WRITE_TO_BUF(_feature_extended_ecx_id[fi]); + } + } + + for (flag = 1, fi = 0; flag <= 0x20000000; flag <<= 1, fi++) { + if ((_cpuid_info.ext_cpuid1_edx.value & flag) && strlen(_feature_extended_edx_id[fi]) > 0) { + WRITE_TO_BUF(_feature_extended_edx_id[fi]); + } + } + + if (supports_tscinv_bit()) { + WRITE_TO_BUF("Invariant TSC"); + } + + return written; +} + +/** + * Write a detailed description of the cpu to a given buffer, including + * feature set. + */ +int VM_Version_Ext::cpu_detailed_description(char* const buf, size_t buf_len) { + assert(buf != NULL, "buffer is NULL!"); + assert(buf_len >= CPU_DETAILED_DESC_BUF_SIZE, "buffer len should at least be == CPU_DETAILED_DESC_BUF_SIZE!"); + + static const char* unknown = ""; + char vendor_id[VENDOR_LENGTH]; + const char* family = NULL; + const char* model = NULL; + const char* brand = NULL; + int outputLen = 0; + + family = cpu_family_description(); + if (family == NULL) { + family = unknown; + } + + model = cpu_model_description(); + if (model == NULL) { + model = unknown; + } + + brand = cpu_brand_string(); + + if (brand == NULL) { + brand = cpu_brand(); + if (brand == NULL) { + brand = unknown; + } + } + + *((uint32_t*) &vendor_id[0]) = _cpuid_info.std_vendor_name_0; + *((uint32_t*) &vendor_id[4]) = _cpuid_info.std_vendor_name_2; + *((uint32_t*) &vendor_id[8]) = _cpuid_info.std_vendor_name_1; + vendor_id[VENDOR_LENGTH-1] = '\0'; + + outputLen = jio_snprintf(buf, buf_len, "Brand: %s, Vendor: %s\n" + "Family: %s (0x%x), Model: %s (0x%x), Stepping: 0x%x\n" + "Ext. family: 0x%x, Ext. model: 0x%x, Type: 0x%x, Signature: 0x%8.8x\n" + "Features: ebx: 0x%8.8x, ecx: 0x%8.8x, edx: 0x%8.8x\n" + "Ext. features: eax: 0x%8.8x, ebx: 0x%8.8x, ecx: 0x%8.8x, edx: 0x%8.8x\n" + "Supports: ", + brand, + vendor_id, + family, + extended_cpu_family(), + model, + extended_cpu_model(), + cpu_stepping(), + _cpuid_info.std_cpuid1_eax.bits.ext_family, + _cpuid_info.std_cpuid1_eax.bits.ext_model, + _cpuid_info.std_cpuid1_eax.bits.proc_type, + _cpuid_info.std_cpuid1_eax.value, + _cpuid_info.std_cpuid1_ebx.value, + _cpuid_info.std_cpuid1_ecx.value, + _cpuid_info.std_cpuid1_edx.value, + _cpuid_info.ext_cpuid1_eax, + _cpuid_info.ext_cpuid1_ebx, + _cpuid_info.ext_cpuid1_ecx, + _cpuid_info.ext_cpuid1_edx); + + if (outputLen < 0 || (size_t) outputLen >= buf_len - 1) { + if (buf_len > 0) { buf[buf_len-1] = '\0'; } + return OS_ERR; + } + + cpu_write_support_string(&buf[outputLen], buf_len - outputLen); + + return OS_OK; +} + +const char* VM_Version_Ext::cpu_name(void) { + char cpu_type_desc[CPU_TYPE_DESC_BUF_SIZE]; + size_t cpu_desc_len = sizeof(cpu_type_desc); + + cpu_type_description(cpu_type_desc, cpu_desc_len); + char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, cpu_desc_len, mtTracing); + if (NULL == tmp) { + return NULL; + } + strncpy(tmp, cpu_type_desc, cpu_desc_len); + return tmp; +} + +const char* VM_Version_Ext::cpu_description(void) { + char cpu_detailed_desc_buffer[CPU_DETAILED_DESC_BUF_SIZE]; + size_t cpu_detailed_desc_len = sizeof(cpu_detailed_desc_buffer); + + cpu_detailed_description(cpu_detailed_desc_buffer, cpu_detailed_desc_len); + + char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, cpu_detailed_desc_len, mtTracing); + + if (NULL == tmp) { + return NULL; + } + + strncpy(tmp, cpu_detailed_desc_buffer, cpu_detailed_desc_len); + return tmp; +} + +/** + * See Intel Application note 485 (chapter 10) for details + * on frequency extraction from cpu brand string. + * http://www.intel.com/content/dam/www/public/us/en/documents/application-notes/processor-identification-cpuid-instruction-note.pdf + * + */ +jlong VM_Version_Ext::max_qualified_cpu_freq_from_brand_string(void) { + // get brand string + const char* const brand_string = cpu_brand_string(); + if (brand_string == NULL) { + return 0; + } + + const u8 MEGA = 1000000; + u8 multiplier = 0; + jlong frequency = 0; + + // the frequency information in the cpu brand string + // is given in either of two formats "x.xxyHz" or "xxxxyHz", + // where y=M,G,T and x is digits + const char* Hz_location = strchr(brand_string, 'H'); + + if (Hz_location != NULL) { + if (*(Hz_location + 1) == 'z') { + // switch on y in "yHz" + switch(*(Hz_location - 1)) { + case 'M' : + // Set multiplier to frequency is in Hz + multiplier = MEGA; + break; + case 'G' : + multiplier = MEGA * 1000; + break; + case 'T' : + multiplier = MEGA * 1000 * 1000; + break; + } + } + } + + if (multiplier > 0) { + // compute frequency (in Hz) from brand string + if (*(Hz_location - 4) == '.') { // if format is "x.xx" + frequency = (jlong)(*(Hz_location - 5) - '0') * (multiplier); + frequency += (jlong)(*(Hz_location - 3) - '0') * (multiplier / 10); + frequency += (jlong)(*(Hz_location - 2) - '0') * (multiplier / 100); + } else { // format is "xxxx" + frequency = (jlong)(*(Hz_location - 5) - '0') * 1000; + frequency += (jlong)(*(Hz_location - 4) - '0') * 100; + frequency += (jlong)(*(Hz_location - 3) - '0') * 10; + frequency += (jlong)(*(Hz_location - 2) - '0'); + frequency *= multiplier; + } + } + return frequency; +} + + +jlong VM_Version_Ext::maximum_qualified_cpu_frequency(void) { + if (_max_qualified_cpu_frequency == 0) { + _max_qualified_cpu_frequency = max_qualified_cpu_freq_from_brand_string(); + } + return _max_qualified_cpu_frequency; +} + +const char* const VM_Version_Ext::_family_id_intel[] = { + "8086/8088", + "", + "286", + "386", + "486", + "Pentium", + "Pentium Pro", //or Pentium-M/Woodcrest depeding on model + "", + "", + "", + "", + "", + "", + "", + "", + "Pentium 4" +}; + +const char* const VM_Version_Ext::_family_id_amd[] = { + "", + "", + "", + "", + "5x86", + "K5/K6", + "Athlon/AthlonXP", + "", + "", + "", + "", + "", + "", + "", + "", + "Opteron/Athlon64", + "Opteron QC/Phenom" // Barcelona et.al. +}; +// Partially from Intel 64 and IA-32 Architecture Software Developer's Manual, +// September 2013, Vol 3C Table 35-1 +const char* const VM_Version_Ext::_model_id_pentium_pro[] = { + "", + "Pentium Pro", + "", + "Pentium II model 3", + "", + "Pentium II model 5/Xeon/Celeron", + "Celeron", + "Pentium III/Pentium III Xeon", + "Pentium III/Pentium III Xeon", + "Pentium M model 9", // Yonah + "Pentium III, model A", + "Pentium III, model B", + "", + "Pentium M model D", // Dothan + "", + "Core 2", // 0xf Woodcrest/Conroe/Merom/Kentsfield/Clovertown + "", + "", + "", + "", + "", + "", + "Celeron", // 0x16 Celeron 65nm + "Core 2", // 0x17 Penryn / Harpertown + "", + "", + "Core i7", // 0x1A CPU_MODEL_NEHALEM_EP + "Atom", // 0x1B Z5xx series Silverthorn + "", + "Core 2", // 0x1D Dunnington (6-core) + "Nehalem", // 0x1E CPU_MODEL_NEHALEM + "", + "", + "", + "", + "", + "", + "Westmere", // 0x25 CPU_MODEL_WESTMERE + "", + "", + "", // 0x28 + "", + "Sandy Bridge", // 0x2a "2nd Generation Intel Core i7, i5, i3" + "", + "Westmere-EP", // 0x2c CPU_MODEL_WESTMERE_EP + "Sandy Bridge-EP", // 0x2d CPU_MODEL_SANDYBRIDGE_EP + "Nehalem-EX", // 0x2e CPU_MODEL_NEHALEM_EX + "Westmere-EX", // 0x2f CPU_MODEL_WESTMERE_EX + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Ivy Bridge", // 0x3a + "", + "Haswell", // 0x3c "4th Generation Intel Core Processor" + "", // 0x3d "Next Generation Intel Core Processor" + "Ivy Bridge-EP", // 0x3e "Next Generation Intel Xeon Processor E7 Family" + "", // 0x3f "Future Generation Intel Xeon Processor" + "", + "", + "", + "", + "", + "Haswell", // 0x45 "4th Generation Intel Core Processor" + "Haswell", // 0x46 "4th Generation Intel Core Processor" + NULL +}; + +/* Brand ID is for back compability + * Newer CPUs uses the extended brand string */ +const char* const VM_Version_Ext::_brand_id[] = { + "", + "Celeron processor", + "Pentium III processor", + "Intel Pentium III Xeon processor", + "", + "", + "", + "", + "Intel Pentium 4 processor", + NULL +}; + + +const char* const VM_Version_Ext::_feature_edx_id[] = { + "On-Chip FPU", + "Virtual Mode Extensions", + "Debugging Extensions", + "Page Size Extensions", + "Time Stamp Counter", + "Model Specific Registers", + "Physical Address Extension", + "Machine Check Exceptions", + "CMPXCHG8B Instruction", + "On-Chip APIC", + "", + "Fast System Call", + "Memory Type Range Registers", + "Page Global Enable", + "Machine Check Architecture", + "Conditional Mov Instruction", + "Page Attribute Table", + "36-bit Page Size Extension", + "Processor Serial Number", + "CLFLUSH Instruction", + "", + "Debug Trace Store feature", + "ACPI registers in MSR space", + "Intel Architecture MMX Technology", + "Fast Float Point Save and Restore", + "Streaming SIMD extensions", + "Streaming SIMD extensions 2", + "Self-Snoop", + "Hyper Threading", + "Thermal Monitor", + "", + "Pending Break Enable" +}; + +const char* const VM_Version_Ext::_feature_extended_edx_id[] = { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "SYSCALL/SYSRET", + "", + "", + "", + "", + "", + "", + "", + "", + "Execute Disable Bit", + "", + "", + "", + "", + "", + "", + "RDTSCP", + "", + "Intel 64 Architecture", + "", + "" +}; + +const char* const VM_Version_Ext::_feature_ecx_id[] = { + "Streaming SIMD Extensions 3", + "PCLMULQDQ", + "64-bit DS Area", + "MONITOR/MWAIT instructions", + "CPL Qualified Debug Store", + "Virtual Machine Extensions", + "Safer Mode Extensions", + "Enhanced Intel SpeedStep technology", + "Thermal Monitor 2", + "Supplemental Streaming SIMD Extensions 3", + "L1 Context ID", + "", + "Fused Multiply-Add", + "CMPXCHG16B", + "xTPR Update Control", + "Perfmon and Debug Capability", + "", + "Process-context identifiers", + "Direct Cache Access", + "Streaming SIMD extensions 4.1", + "Streaming SIMD extensions 4.2", + "x2APIC", + "MOVBE", + "Popcount instruction", + "TSC-Deadline", + "AESNI", + "XSAVE", + "OSXSAVE", + "AVX", + "F16C", + "RDRAND", + "" +}; + +const char* const VM_Version_Ext::_feature_extended_ecx_id[] = { + "LAHF/SAHF instruction support", + "Core multi-processor leagacy mode", + "", + "", + "", + "Advanced Bit Manipulations: LZCNT", + "SSE4A: MOVNTSS, MOVNTSD, EXTRQ, INSERTQ", + "Misaligned SSE mode", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" +}; diff --git a/src/cpu/x86/vm/vm_version_ext_x86.hpp b/src/cpu/x86/vm/vm_version_ext_x86.hpp new file mode 100644 index 0000000000000000000000000000000000000000..78fb001cb3907dedf9db3c7e917a2045367bb2ed --- /dev/null +++ b/src/cpu/x86/vm/vm_version_ext_x86.hpp @@ -0,0 +1,100 @@ +/* + * 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 + * 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 CPU_X86_VM_VM_VERSION_EXT_X86_HPP +#define CPU_X86_VM_VM_VERSION_EXT_X86_HPP + +#include "utilities/macros.hpp" +#include "vm_version_x86.hpp" + +class VM_Version_Ext : public VM_Version { + private: + static const size_t VENDOR_LENGTH; + static const size_t CPU_EBS_MAX_LENGTH; + static const size_t CPU_TYPE_DESC_BUF_SIZE; + static const size_t CPU_DETAILED_DESC_BUF_SIZE; + + static const char* const _family_id_intel[]; + static const char* const _family_id_amd[]; + static const char* const _brand_id[]; + static const char* const _model_id_pentium_pro[]; + + static const char* const _feature_edx_id[]; + static const char* const _feature_extended_edx_id[]; + static const char* const _feature_ecx_id[]; + static const char* const _feature_extended_ecx_id[]; + + static int _no_of_threads; + static int _no_of_cores; + static int _no_of_packages; + static char* _cpu_brand_string; + static jlong _max_qualified_cpu_frequency; + + static const char* cpu_family_description(void); + static const char* cpu_model_description(void); + static const char* cpu_brand(void); + static const char* cpu_brand_string(void); + + static int cpu_type_description(char* const buf, size_t buf_len); + static int cpu_detailed_description(char* const buf, size_t buf_len); + static int cpu_extended_brand_string(char* const buf, size_t buf_len); + + static bool cpu_is_em64t(void); + static bool is_netburst(void); + + // Returns bytes written excluding termninating null byte. + static size_t cpu_write_support_string(char* const buf, size_t buf_len); + static void resolve_cpu_information_details(void); + static jlong max_qualified_cpu_freq_from_brand_string(void); + + public: + // Offsets for cpuid asm stub brand string + static ByteSize proc_name_0_offset() { return byte_offset_of(CpuidInfo, proc_name_0); } + static ByteSize proc_name_1_offset() { return byte_offset_of(CpuidInfo, proc_name_1); } + static ByteSize proc_name_2_offset() { return byte_offset_of(CpuidInfo, proc_name_2); } + static ByteSize proc_name_3_offset() { return byte_offset_of(CpuidInfo, proc_name_3); } + static ByteSize proc_name_4_offset() { return byte_offset_of(CpuidInfo, proc_name_4); } + static ByteSize proc_name_5_offset() { return byte_offset_of(CpuidInfo, proc_name_5); } + static ByteSize proc_name_6_offset() { return byte_offset_of(CpuidInfo, proc_name_6); } + static ByteSize proc_name_7_offset() { return byte_offset_of(CpuidInfo, proc_name_7); } + static ByteSize proc_name_8_offset() { return byte_offset_of(CpuidInfo, proc_name_8); } + static ByteSize proc_name_9_offset() { return byte_offset_of(CpuidInfo, proc_name_9); } + static ByteSize proc_name_10_offset() { return byte_offset_of(CpuidInfo, proc_name_10); } + static ByteSize proc_name_11_offset() { return byte_offset_of(CpuidInfo, proc_name_11); } + + static int number_of_threads(void); + static int number_of_cores(void); + static int number_of_sockets(void); + + static jlong maximum_qualified_cpu_frequency(void); + + static bool supports_tscinv_ext(void); + + static const char* cpu_name(void); + static const char* cpu_description(void); + + static void initialize(); +}; + +#endif // CPU_X86_VM_VM_VERSION_EXT_X86_HPP diff --git a/src/cpu/zero/vm/globals_zero.hpp b/src/cpu/zero/vm/globals_zero.hpp index 016540d2c5d69278caa2555fa1c1f673f7a784fd..bc703d351d2783d05cf744e16bca97076ef3777c 100644 --- a/src/cpu/zero/vm/globals_zero.hpp +++ b/src/cpu/zero/vm/globals_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2009, 2010, 2011 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -33,7 +33,6 @@ // runtime system. See globals.hpp for details of what they do. define_pd_global(bool, ConvertSleepToYield, true); -define_pd_global(bool, ShareVtableStubs, true); define_pd_global(bool, CountInterpCalls, true); define_pd_global(bool, NeedsDeoptSuspend, false); diff --git a/src/cpu/zero/vm/vm_version_ext_zero.cpp b/src/cpu/zero/vm/vm_version_ext_zero.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97db9742d1c7ea4e6d1afdbdf3b3c7da3983e425 --- /dev/null +++ b/src/cpu/zero/vm/vm_version_ext_zero.cpp @@ -0,0 +1,82 @@ +/* + * 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 + * 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 "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" +#include "runtime/os.hpp" +#include "vm_version_ext_zero.hpp" + +// VM_Version_Ext statics +int VM_Version_Ext::_no_of_threads = 0; +int VM_Version_Ext::_no_of_cores = 0; +int VM_Version_Ext::_no_of_sockets = 0; +bool VM_Version_Ext::_initialized = false; +char VM_Version_Ext::_cpu_name[CPU_TYPE_DESC_BUF_SIZE] = {0}; + +void VM_Version_Ext::initialize_cpu_information(void) { + // do nothing if cpu info has been initialized + if (_initialized) { + return; + } + + int core_id = -1; + int chip_id = -1; + int len = 0; + char* src_string = NULL; + + _no_of_cores = os::processor_count(); + _no_of_threads = _no_of_cores; + _no_of_sockets = _no_of_cores; + snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE - 1, "Zero VM"); + _initialized = true; +} + +int VM_Version_Ext::number_of_threads(void) { + initialize_cpu_information(); + return _no_of_threads; +} + +int VM_Version_Ext::number_of_cores(void) { + initialize_cpu_information(); + return _no_of_cores; +} + +int VM_Version_Ext::number_of_sockets(void) { + initialize_cpu_information(); + return _no_of_sockets; +} + +const char* VM_Version_Ext::cpu_name(void) { + initialize_cpu_information(); + char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_TYPE_DESC_BUF_SIZE, mtTracing); + if (NULL == tmp) { + return NULL; + } + strncpy(tmp, _cpu_name, CPU_TYPE_DESC_BUF_SIZE); + return tmp; +} + +const char* VM_Version_Ext::cpu_description(void) { + return ""; +} diff --git a/src/share/vm/trace/traceBackend.hpp b/src/cpu/zero/vm/vm_version_ext_zero.hpp similarity index 55% rename from src/share/vm/trace/traceBackend.hpp rename to src/cpu/zero/vm/vm_version_ext_zero.hpp index c65d89e41d8e2f634903a7f8d6942279a7d3bb80..f9645e9f74a658ae37e4c27580990cb4f3af466e 100644 --- a/src/share/vm/trace/traceBackend.hpp +++ b/src/cpu/zero/vm/vm_version_ext_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 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,42 +21,32 @@ * questions. * */ -#ifndef SHARE_VM_TRACE_TRACEBACKEND_HPP -#define SHARE_VM_TRACE_TRACEBACKEND_HPP + +#ifndef CPU_ZERO_VM_VM_VERSION_EXT_ZERO_HPP +#define CPU_ZERO_VM_VM_VERSION_EXT_ZERO_HPP #include "utilities/macros.hpp" -#if INCLUDE_TRACE -#include "runtime/globals.hpp" -#include "runtime/os.hpp" -#include "trace/traceTime.hpp" -#include "tracefiles/traceEventIds.hpp" - -class TraceBackend { -public: - static bool enabled(void) { - return EnableTracing; - } - - static bool is_event_enabled(TraceEventId id) { - return enabled(); - } - - static TracingTime time() { - return os::elapsed_counter(); - } - - static void on_unloading_classes(void) { - } -}; +#include "vm_version_zero.hpp" -class TraceThreadData { -public: - TraceThreadData() {} -}; +class VM_Version_Ext : public VM_Version { + private: + static const size_t CPU_TYPE_DESC_BUF_SIZE = 256; + + static int _no_of_threads; + static int _no_of_cores; + static int _no_of_sockets; + static bool _initialized; + static char _cpu_name[CPU_TYPE_DESC_BUF_SIZE]; -typedef TraceBackend Tracing; + public: + static int number_of_threads(void); + static int number_of_cores(void); + static int number_of_sockets(void); + + static const char* cpu_name(void); + static const char* cpu_description(void); + static void initialize_cpu_information(void); + +}; -#else // !INCLUDE_TRACE -#include "trace/noTraceBackend.hpp" -#endif // INCLUDE_TRACE -#endif // SHARE_VM_TRACE_TRACEBACKEND_HPP +#endif // CPU_ZERO_VM_VM_VERSION_EXT_ZERO_HPP diff --git a/src/os/aix/vm/os_aix.cpp b/src/os/aix/vm/os_aix.cpp index 717f692c229c7f42be8fe5476390eeacbf437d83..4abd2f03df552e21788771606a31a55f9043e65e 100644 --- a/src/os/aix/vm/os_aix.cpp +++ b/src/os/aix/vm/os_aix.cpp @@ -1555,6 +1555,11 @@ void os::print_os_info(outputStream* st) { st->cr(); } +int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *param) { + // Not yet implemented. + return 0; +} + void os::print_memory_info(outputStream* st) { st->print_cr("Memory:"); @@ -2793,6 +2798,10 @@ size_t os::read(int fd, void *buf, unsigned int nBytes) { return ::read(fd, buf, nBytes); } +size_t os::read_at(int fd, void *buf, unsigned int nBytes, jlong offset) { + return ::pread(fd, buf, nBytes, offset); +} + #define NANOSECS_PER_MILLISEC 1000000 int os::sleep(Thread* thread, jlong millis, bool interruptible) { diff --git a/src/os/aix/vm/os_perf_aix.cpp b/src/os/aix/vm/os_perf_aix.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bfdc16c2e0e65743fedfd4ece6b0a27f699f752c --- /dev/null +++ b/src/os/aix/vm/os_perf_aix.cpp @@ -0,0 +1,1053 @@ +/* + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 SAP SE. 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 "precompiled.hpp" +#include "jvm.h" +#include "memory/allocation.inline.hpp" +#include "os_aix.inline.hpp" +#include "runtime/os.hpp" +#include "runtime/os_perf.hpp" +#include "vm_version_ext_ppc.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + /proc/[number]/stat + Status information about the process. This is used by ps(1). It is defined in /usr/src/linux/fs/proc/array.c. + + The fields, in order, with their proper scanf(3) format specifiers, are: + + 1. pid %d The process id. + + 2. comm %s + The filename of the executable, in parentheses. This is visible whether or not the executable is swapped out. + + 3. state %c + One character from the string "RSDZTW" where R is running, S is sleeping in an interruptible wait, D is waiting in uninterruptible disk + sleep, Z is zombie, T is traced or stopped (on a signal), and W is paging. + + 4. ppid %d + The PID of the parent. + + 5. pgrp %d + The process group ID of the process. + + 6. session %d + The session ID of the process. + + 7. tty_nr %d + The tty the process uses. + + 8. tpgid %d + The process group ID of the process which currently owns the tty that the process is connected to. + + 9. flags %lu + The flags of the process. The math bit is decimal 4, and the traced bit is decimal 10. + + 10. minflt %lu + The number of minor faults the process has made which have not required loading a memory page from disk. + + 11. cminflt %lu + The number of minor faults that the process's waited-for children have made. + + 12. majflt %lu + The number of major faults the process has made which have required loading a memory page from disk. + + 13. cmajflt %lu + The number of major faults that the process's waited-for children have made. + + 14. utime %lu + The number of jiffies that this process has been scheduled in user mode. + + 15. stime %lu + The number of jiffies that this process has been scheduled in kernel mode. + + 16. cutime %ld + The number of jiffies that this process's waited-for children have been scheduled in user mode. (See also times(2).) + + 17. cstime %ld + The number of jiffies that this process' waited-for children have been scheduled in kernel mode. + + 18. priority %ld + The standard nice value, plus fifteen. The value is never negative in the kernel. + + 19. nice %ld + The nice value ranges from 19 (nicest) to -19 (not nice to others). + + 20. 0 %ld This value is hard coded to 0 as a placeholder for a removed field. + + 21. itrealvalue %ld + The time in jiffies before the next SIGALRM is sent to the process due to an interval timer. + + 22. starttime %lu + The time in jiffies the process started after system boot. + + 23. vsize %lu + Virtual memory size in bytes. + + 24. rss %ld + Resident Set Size: number of pages the process has in real memory, minus 3 for administrative purposes. This is just the pages which count + towards text, data, or stack space. This does not include pages which have not been demand-loaded in, or which are swapped out. + + 25. rlim %lu + Current limit in bytes on the rss of the process (usually 4294967295 on i386). + + 26. startcode %lu + The address above which program text can run. + + 27. endcode %lu + The address below which program text can run. + + 28. startstack %lu + The address of the start of the stack. + + 29. kstkesp %lu + The current value of esp (stack pointer), as found in the kernel stack page for the process. + + 30. kstkeip %lu + The current EIP (instruction pointer). + + 31. signal %lu + The bitmap of pending signals (usually 0). + + 32. blocked %lu + The bitmap of blocked signals (usually 0, 2 for shells). + + 33. sigignore %lu + The bitmap of ignored signals. + + 34. sigcatch %lu + The bitmap of catched signals. + + 35. wchan %lu + This is the "channel" in which the process is waiting. It is the address of a system call, and can be looked up in a namelist if you need + a textual name. (If you have an up-to-date /etc/psdatabase, then try ps -l to see the WCHAN field in action.) + + 36. nswap %lu + Number of pages swapped - not maintained. + + 37. cnswap %lu + Cumulative nswap for child processes. + + 38. exit_signal %d + Signal to be sent to parent when we die. + + 39. processor %d + CPU number last executed on. + + + + ///// SSCANF FORMAT STRING. Copy and use. + +field: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 +format: %d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d + + +*/ + +struct CPUPerfTicks { + uint64_t used; + uint64_t usedKernel; + uint64_t total; +}; + +typedef enum { + CPU_LOAD_VM_ONLY, + CPU_LOAD_GLOBAL, +} CpuLoadTarget; + +enum { + UNDETECTED, + UNDETECTABLE, + LINUX26_NPTL, + BAREMETAL +}; + +struct CPUPerfCounters { + int nProcs; + CPUPerfTicks jvmTicks; + CPUPerfTicks* cpus; +}; + +static double get_cpu_load(int which_logical_cpu, CPUPerfCounters* counters, double* pkernelLoad, CpuLoadTarget target); + +/** reads /proc//stat data, with some checks and some skips. + * Ensure that 'fmt' does _NOT_ contain the first two "%d %s" + */ +static int vread_statdata(const char* procfile, const char* fmt, va_list args) { + FILE*f; + int n; + char buf[2048]; + + if ((f = fopen(procfile, "r")) == NULL) { + return -1; + } + + if ((n = fread(buf, 1, sizeof(buf), f)) != -1) { + char *tmp; + + buf[n-1] = '\0'; + /** skip through pid and exec name. */ + if ((tmp = strrchr(buf, ')')) != NULL) { + // skip the ')' and the following space + // but check that buffer is long enough + tmp += 2; + if (tmp < buf + n) { + n = vsscanf(tmp, fmt, args); + } + } + } + + fclose(f); + + return n; +} + +static int read_statdata(const char* procfile, const char* fmt, ...) { + int n; + va_list args; + + va_start(args, fmt); + n = vread_statdata(procfile, fmt, args); + va_end(args); + return n; +} + +static FILE* open_statfile(void) { + FILE *f; + + if ((f = fopen("/proc/stat", "r")) == NULL) { + static int haveWarned = 0; + if (!haveWarned) { + haveWarned = 1; + } + } + return f; +} + +static void +next_line(FILE *f) { + int c; + do { + c = fgetc(f); + } while (c != '\n' && c != EOF); +} + +/** + * Return the total number of ticks since the system was booted. + * If the usedTicks parameter is not NULL, it will be filled with + * the number of ticks spent on actual processes (user, system or + * nice processes) since system boot. Note that this is the total number + * of "executed" ticks on _all_ CPU:s, that is on a n-way system it is + * n times the number of ticks that has passed in clock time. + * + * Returns a negative value if the reading of the ticks failed. + */ +static OSReturn get_total_ticks(int which_logical_cpu, CPUPerfTicks* pticks) { + FILE* fh; + uint64_t userTicks, niceTicks, systemTicks, idleTicks; + uint64_t iowTicks = 0, irqTicks = 0, sirqTicks= 0; + int logical_cpu = -1; + const int expected_assign_count = (-1 == which_logical_cpu) ? 4 : 5; + int n; + + if ((fh = open_statfile()) == NULL) { + return OS_ERR; + } + if (-1 == which_logical_cpu) { + n = fscanf(fh, "cpu " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " + UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT, + &userTicks, &niceTicks, &systemTicks, &idleTicks, + &iowTicks, &irqTicks, &sirqTicks); + } else { + // Move to next line + next_line(fh); + + // find the line for requested cpu faster to just iterate linefeeds? + for (int i = 0; i < which_logical_cpu; i++) { + next_line(fh); + } + + n = fscanf(fh, "cpu%u " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " + UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT, + &logical_cpu, &userTicks, &niceTicks, + &systemTicks, &idleTicks, &iowTicks, &irqTicks, &sirqTicks); + } + + fclose(fh); + if (n < expected_assign_count || logical_cpu != which_logical_cpu) { +#ifdef DEBUG_LINUX_PROC_STAT + vm_fprintf(stderr, "[stat] read failed"); +#endif + return OS_ERR; + } + +#ifdef DEBUG_LINUX_PROC_STAT + vm_fprintf(stderr, "[stat] read " + UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " + UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " \n", + userTicks, niceTicks, systemTicks, idleTicks, + iowTicks, irqTicks, sirqTicks); +#endif + + pticks->used = userTicks + niceTicks; + pticks->usedKernel = systemTicks + irqTicks + sirqTicks; + pticks->total = userTicks + niceTicks + systemTicks + idleTicks + + iowTicks + irqTicks + sirqTicks; + + return OS_OK; +} + + +static int get_systemtype(void) { + static int procEntriesType = UNDETECTED; + DIR *taskDir; + + if (procEntriesType != UNDETECTED) { + return procEntriesType; + } + + // Check whether we have a task subdirectory + if ((taskDir = opendir("/proc/self/task")) == NULL) { + procEntriesType = UNDETECTABLE; + } else { + // The task subdirectory exists; we're on a Linux >= 2.6 system + closedir(taskDir); + procEntriesType = LINUX26_NPTL; + } + + return procEntriesType; +} + +/** read user and system ticks from a named procfile, assumed to be in 'stat' format then. */ +static int read_ticks(const char* procfile, uint64_t* userTicks, uint64_t* systemTicks) { + return read_statdata(procfile, "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u " UINT64_FORMAT " " UINT64_FORMAT, + userTicks, systemTicks); +} + +/** + * Return the number of ticks spent in any of the processes belonging + * to the JVM on any CPU. + */ +static OSReturn get_jvm_ticks(CPUPerfTicks* pticks) { + uint64_t userTicks; + uint64_t systemTicks; + + if (get_systemtype() != LINUX26_NPTL) { + return OS_ERR; + } + + if (read_ticks("/proc/self/stat", &userTicks, &systemTicks) != 2) { + return OS_ERR; + } + + // get the total + if (get_total_ticks(-1, pticks) != OS_OK) { + return OS_ERR; + } + + pticks->used = userTicks; + pticks->usedKernel = systemTicks; + + return OS_OK; +} + +/** + * Return the load of the CPU as a double. 1.0 means the CPU process uses all + * available time for user or system processes, 0.0 means the CPU uses all time + * being idle. + * + * Returns a negative value if there is a problem in determining the CPU load. + */ +static double get_cpu_load(int which_logical_cpu, CPUPerfCounters* counters, double* pkernelLoad, CpuLoadTarget target) { + uint64_t udiff, kdiff, tdiff; + CPUPerfTicks* pticks; + CPUPerfTicks tmp; + double user_load; + + *pkernelLoad = 0.0; + + if (target == CPU_LOAD_VM_ONLY) { + pticks = &counters->jvmTicks; + } else if (-1 == which_logical_cpu) { + pticks = &counters->cpus[counters->nProcs]; + } else { + pticks = &counters->cpus[which_logical_cpu]; + } + + tmp = *pticks; + + if (target == CPU_LOAD_VM_ONLY) { + if (get_jvm_ticks(pticks) != OS_OK) { + return -1.0; + } + } else if (get_total_ticks(which_logical_cpu, pticks) != OS_OK) { + return -1.0; + } + + // seems like we sometimes end up with less kernel ticks when + // reading /proc/self/stat a second time, timing issue between cpus? + if (pticks->usedKernel < tmp.usedKernel) { + kdiff = 0; + } else { + kdiff = pticks->usedKernel - tmp.usedKernel; + } + tdiff = pticks->total - tmp.total; + udiff = pticks->used - tmp.used; + + if (tdiff == 0) { + return 0.0; + } else if (tdiff < (udiff + kdiff)) { + tdiff = udiff + kdiff; + } + *pkernelLoad = (kdiff / (double)tdiff); + // BUG9044876, normalize return values to sane values + *pkernelLoad = MAX2(*pkernelLoad, 0.0); + *pkernelLoad = MIN2(*pkernelLoad, 1.0); + + user_load = (udiff / (double)tdiff); + user_load = MAX2(user_load, 0.0); + user_load = MIN2(user_load, 1.0); + + return user_load; +} + +static int parse_stat(const char* fmt, ...) { + FILE *f; + va_list args; + + va_start(args, fmt); + + if ((f = open_statfile()) == NULL) { + va_end(args); + return OS_ERR; + } + for (;;) { + char line[80]; + if (fgets(line, sizeof(line), f) != NULL) { + if (vsscanf(line, fmt, args) == 1) { + fclose(f); + va_end(args); + return OS_OK; + } + } else { + fclose(f); + va_end(args); + return OS_ERR; + } + } +} + +static int get_noof_context_switches(uint64_t* switches) { + return parse_stat("ctxt " UINT64_FORMAT "\n", switches); +} + +/** returns boot time in _seconds_ since epoch */ +static int get_boot_time(uint64_t* time) { + return parse_stat("btime " UINT64_FORMAT "\n", time); +} + +static int perf_context_switch_rate(double* rate) { + static pthread_mutex_t contextSwitchLock = PTHREAD_MUTEX_INITIALIZER; + static uint64_t lastTime; + static uint64_t lastSwitches; + static double lastRate; + + uint64_t lt = 0; + int res = 0; + + if (lastTime == 0) { + uint64_t tmp; + if (get_boot_time(&tmp) < 0) { + return OS_ERR; + } + lt = tmp * 1000; + } + + res = OS_OK; + + pthread_mutex_lock(&contextSwitchLock); + { + + uint64_t sw; + s8 t, d; + + if (lastTime == 0) { + lastTime = lt; + } + + t = os::javaTimeMillis(); + d = t - lastTime; + + if (d == 0) { + *rate = lastRate; + } else if (!get_noof_context_switches(&sw)) { + *rate = ( (double)(sw - lastSwitches) / d ) * 1000; + lastRate = *rate; + lastSwitches = sw; + lastTime = t; + } else { + *rate = 0; + res = OS_ERR; + } + if (*rate <= 0) { + *rate = 0; + lastRate = 0; + } + } + pthread_mutex_unlock(&contextSwitchLock); + + return res; +} + +class CPUPerformanceInterface::CPUPerformance : public CHeapObj { + friend class CPUPerformanceInterface; + private: + CPUPerfCounters _counters; + + int cpu_load(int which_logical_cpu, double* cpu_load); + int context_switch_rate(double* rate); + int cpu_load_total_process(double* cpu_load); + int cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad); + + public: + CPUPerformance(); + bool initialize(); + ~CPUPerformance(); +}; + +CPUPerformanceInterface::CPUPerformance::CPUPerformance() { + _counters.nProcs = os::active_processor_count(); + _counters.cpus = NULL; +} + +bool CPUPerformanceInterface::CPUPerformance::initialize() { + size_t tick_array_size = (_counters.nProcs +1) * sizeof(CPUPerfTicks); + _counters.cpus = (CPUPerfTicks*)NEW_C_HEAP_ARRAY(char, tick_array_size, mtInternal); + if (NULL == _counters.cpus) { + return false; + } + memset(_counters.cpus, 0, tick_array_size); + + // For the CPU load total + get_total_ticks(-1, &_counters.cpus[_counters.nProcs]); + + // For each CPU + for (int i = 0; i < _counters.nProcs; i++) { + get_total_ticks(i, &_counters.cpus[i]); + } + // For JVM load + get_jvm_ticks(&_counters.jvmTicks); + + // initialize context switch system + // the double is only for init + double init_ctx_switch_rate; + perf_context_switch_rate(&init_ctx_switch_rate); + + return true; +} + +CPUPerformanceInterface::CPUPerformance::~CPUPerformance() { + if (_counters.cpus != NULL) { + FREE_C_HEAP_ARRAY(char, _counters.cpus, mtInternal); + } +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load(int which_logical_cpu, double* cpu_load) { + double u, s; + u = get_cpu_load(which_logical_cpu, &_counters, &s, CPU_LOAD_GLOBAL); + if (u < 0) { + *cpu_load = 0.0; + return OS_ERR; + } + // Cap total systemload to 1.0 + *cpu_load = MIN2((u + s), 1.0); + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) { + double u, s; + u = get_cpu_load(-1, &_counters, &s, CPU_LOAD_VM_ONLY); + if (u < 0) { + *cpu_load = 0.0; + return OS_ERR; + } + *cpu_load = u + s; + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) { + double u, s, t; + + assert(pjvmUserLoad != NULL, "pjvmUserLoad not inited"); + assert(pjvmKernelLoad != NULL, "pjvmKernelLoad not inited"); + assert(psystemTotalLoad != NULL, "psystemTotalLoad not inited"); + + u = get_cpu_load(-1, &_counters, &s, CPU_LOAD_VM_ONLY); + if (u < 0) { + *pjvmUserLoad = 0.0; + *pjvmKernelLoad = 0.0; + *psystemTotalLoad = 0.0; + return OS_ERR; + } + + cpu_load(-1, &t); + // clamp at user+system and 1.0 + if (u + s > t) { + t = MIN2(u + s, 1.0); + } + + *pjvmUserLoad = u; + *pjvmKernelLoad = s; + *psystemTotalLoad = t; + + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) { + return perf_context_switch_rate(rate); +} + +CPUPerformanceInterface::CPUPerformanceInterface() { + _impl = NULL; +} + +bool CPUPerformanceInterface::initialize() { + _impl = new CPUPerformanceInterface::CPUPerformance(); + return NULL == _impl ? false : _impl->initialize(); +} + +CPUPerformanceInterface::~CPUPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +int CPUPerformanceInterface::cpu_load(int which_logical_cpu, double* cpu_load) const { + return _impl->cpu_load(which_logical_cpu, cpu_load); +} + +int CPUPerformanceInterface::cpu_load_total_process(double* cpu_load) const { + return _impl->cpu_load_total_process(cpu_load); +} + +int CPUPerformanceInterface::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) const { + return _impl->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotalLoad); +} + +int CPUPerformanceInterface::context_switch_rate(double* rate) const { + return _impl->context_switch_rate(rate); +} + +class SystemProcessInterface::SystemProcesses : public CHeapObj { + friend class SystemProcessInterface; + private: + class ProcessIterator : public CHeapObj { + friend class SystemProcessInterface::SystemProcesses; + private: + DIR* _dir; + struct dirent* _entry; + bool _valid; + char _exeName[PATH_MAX]; + char _exePath[PATH_MAX]; + + ProcessIterator(); + ~ProcessIterator(); + bool initialize(); + + bool is_valid() const { return _valid; } + bool is_valid_entry(struct dirent* entry) const; + bool is_dir(const char* name) const; + int fsize(const char* name, uint64_t& size) const; + + char* allocate_string(const char* str) const; + void get_exe_name(); + char* get_exe_path(); + char* get_cmdline(); + + int current(SystemProcess* process_info); + int next_process(); + }; + + ProcessIterator* _iterator; + SystemProcesses(); + bool initialize(); + ~SystemProcesses(); + + //information about system processes + int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const; +}; + +bool SystemProcessInterface::SystemProcesses::ProcessIterator::is_dir(const char* name) const { + struct stat mystat; + int ret_val = 0; + + ret_val = stat(name, &mystat); + if (ret_val < 0) { + return false; + } + ret_val = S_ISDIR(mystat.st_mode); + return ret_val > 0; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::fsize(const char* name, uint64_t& size) const { + assert(name != NULL, "name pointer is NULL!"); + size = 0; + struct stat fbuf; + + if (stat(name, &fbuf) < 0) { + return OS_ERR; + } + size = fbuf.st_size; + return OS_OK; +} + +// if it has a numeric name, is a directory and has a 'stat' file in it +bool SystemProcessInterface::SystemProcesses::ProcessIterator::is_valid_entry(struct dirent* entry) const { + char buffer[PATH_MAX]; + uint64_t size = 0; + + if (atoi(entry->d_name) != 0) { + jio_snprintf(buffer, PATH_MAX, "/proc/%s", entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + + if (is_dir(buffer)) { + jio_snprintf(buffer, PATH_MAX, "/proc/%s/stat", entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + if (fsize(buffer, size) != OS_ERR) { + return true; + } + } + } + return false; +} + +// get exe-name from /proc//stat +void SystemProcessInterface::SystemProcesses::ProcessIterator::get_exe_name() { + FILE* fp; + char buffer[PATH_MAX]; + + jio_snprintf(buffer, PATH_MAX, "/proc/%s/stat", _entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + if ((fp = fopen(buffer, "r")) != NULL) { + if (fgets(buffer, PATH_MAX, fp) != NULL) { + char* start, *end; + // exe-name is between the first pair of ( and ) + start = strchr(buffer, '('); + if (start != NULL && start[1] != '\0') { + start++; + end = strrchr(start, ')'); + if (end != NULL) { + size_t len; + len = MIN2(end - start, sizeof(_exeName) - 1); + memcpy(_exeName, start, len); + _exeName[len] = '\0'; + } + } + } + fclose(fp); + } +} + +// get command line from /proc//cmdline +char* SystemProcessInterface::SystemProcesses::ProcessIterator::get_cmdline() { + FILE* fp; + char buffer[PATH_MAX]; + char* cmdline = NULL; + + jio_snprintf(buffer, PATH_MAX, "/proc/%s/cmdline", _entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + if ((fp = fopen(buffer, "r")) != NULL) { + size_t size = 0; + char dummy; + + // find out how long the file is (stat always returns 0) + while (fread(&dummy, 1, 1, fp) == 1) { + size++; + } + if (size > 0) { + cmdline = NEW_C_HEAP_ARRAY(char, size + 1, mtInternal); + if (cmdline != NULL) { + cmdline[0] = '\0'; + if (fseek(fp, 0, SEEK_SET) == 0) { + if (fread(cmdline, 1, size, fp) == size) { + // the file has the arguments separated by '\0', + // so we translate '\0' to ' ' + for (size_t i = 0; i < size; i++) { + if (cmdline[i] == '\0') { + cmdline[i] = ' '; + } + } + cmdline[size] = '\0'; + } + } + } + } + fclose(fp); + } + return cmdline; +} + +// get full path to exe from /proc//exe symlink +char* SystemProcessInterface::SystemProcesses::ProcessIterator::get_exe_path() { + char buffer[PATH_MAX]; + + jio_snprintf(buffer, PATH_MAX, "/proc/%s/exe", _entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + return realpath(buffer, _exePath); +} + +char* SystemProcessInterface::SystemProcesses::ProcessIterator::allocate_string(const char* str) const { + if (str != NULL) { + size_t len = strlen(str); + char* tmp = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); + strncpy(tmp, str, len); + tmp[len] = '\0'; + return tmp; + } + return NULL; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::current(SystemProcess* process_info) { + if (!is_valid()) { + return OS_ERR; + } + + process_info->set_pid(atoi(_entry->d_name)); + + get_exe_name(); + process_info->set_name(allocate_string(_exeName)); + + if (get_exe_path() != NULL) { + process_info->set_path(allocate_string(_exePath)); + } + + char* cmdline = NULL; + cmdline = get_cmdline(); + if (cmdline != NULL) { + process_info->set_command_line(allocate_string(cmdline)); + FREE_C_HEAP_ARRAY(char, cmdline, mtInternal); + } + + return OS_OK; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::next_process() { + if (!is_valid()) { + return OS_ERR; + } + + do { + _entry = os::readdir(_dir); + if (_entry == NULL) { + // Error or reached end. Could use errno to distinguish those cases. + _valid = false; + return OS_ERR; + } + } while(!is_valid_entry(_entry)); + + _valid = true; + return OS_OK; +} + +SystemProcessInterface::SystemProcesses::ProcessIterator::ProcessIterator() { + _dir = NULL; + _entry = NULL; + _valid = false; +} + +bool SystemProcessInterface::SystemProcesses::ProcessIterator::initialize() { + // Not yet implemented. + return false; +} + +SystemProcessInterface::SystemProcesses::ProcessIterator::~ProcessIterator() { + if (_dir != NULL) { + os::closedir(_dir); + } +} + +SystemProcessInterface::SystemProcesses::SystemProcesses() { + _iterator = NULL; +} + +bool SystemProcessInterface::SystemProcesses::initialize() { + _iterator = new SystemProcessInterface::SystemProcesses::ProcessIterator(); + return NULL == _iterator ? false : _iterator->initialize(); +} + +SystemProcessInterface::SystemProcesses::~SystemProcesses() { + if (_iterator != NULL) { + delete _iterator; + } +} + +int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const { + assert(system_processes != NULL, "system_processes pointer is NULL!"); + assert(no_of_sys_processes != NULL, "system_processes counter pointers is NULL!"); + assert(_iterator != NULL, "iterator is NULL!"); + + // initialize pointers + *no_of_sys_processes = 0; + *system_processes = NULL; + + while (_iterator->is_valid()) { + SystemProcess* tmp = new SystemProcess(); + _iterator->current(tmp); + + //if already existing head + if (*system_processes != NULL) { + //move "first to second" + tmp->set_next(*system_processes); + } + // new head + *system_processes = tmp; + // increment + (*no_of_sys_processes)++; + // step forward + _iterator->next_process(); + } + return OS_OK; +} + +int SystemProcessInterface::system_processes(SystemProcess** system_procs, int* no_of_sys_processes) const { + return _impl->system_processes(system_procs, no_of_sys_processes); +} + +SystemProcessInterface::SystemProcessInterface() { + _impl = NULL; +} + +bool SystemProcessInterface::initialize() { + _impl = new SystemProcessInterface::SystemProcesses(); + return NULL == _impl ? false : _impl->initialize(); +} + +SystemProcessInterface::~SystemProcessInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +CPUInformationInterface::CPUInformationInterface() { + _cpu_info = NULL; +} + +bool CPUInformationInterface::initialize() { + _cpu_info = new CPUInformation(); + if (NULL == _cpu_info) { + return false; + } + _cpu_info->set_number_of_hardware_threads(VM_Version_Ext::number_of_threads()); + _cpu_info->set_number_of_cores(VM_Version_Ext::number_of_cores()); + _cpu_info->set_number_of_sockets(VM_Version_Ext::number_of_sockets()); + _cpu_info->set_cpu_name(VM_Version_Ext::cpu_name()); + _cpu_info->set_cpu_description(VM_Version_Ext::cpu_description()); + + return true; +} + +CPUInformationInterface::~CPUInformationInterface() { + if (_cpu_info != NULL) { + if (_cpu_info->cpu_name() != NULL) { + const char* cpu_name = _cpu_info->cpu_name(); + FREE_C_HEAP_ARRAY(char, cpu_name, mtInternal); + _cpu_info->set_cpu_name(NULL); + } + if (_cpu_info->cpu_description() != NULL) { + const char* cpu_desc = _cpu_info->cpu_description(); + FREE_C_HEAP_ARRAY(char, cpu_desc, mtInternal); + _cpu_info->set_cpu_description(NULL); + } + delete _cpu_info; + } +} + +int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) { + if (_cpu_info == NULL) { + return OS_ERR; + } + + cpu_info = *_cpu_info; // shallow copy assignment + return OS_OK; +} + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() { + +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + return true; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const +{ + return FUNCTIONALITY_NOT_IMPLEMENTED; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff --git a/src/os/bsd/dtrace/libjvm_db.c b/src/os/bsd/dtrace/libjvm_db.c index 169a0e93c0de08298c3caa2f791aa1419c559cac..b55900a9b36e8bc7170fce43427b119f93dbd04b 100644 --- a/src/os/bsd/dtrace/libjvm_db.c +++ b/src/os/bsd/dtrace/libjvm_db.c @@ -543,13 +543,14 @@ name_for_methodPtr(jvm_agent_t* J, uint64_t methodPtr, char * result, size_t siz CHECK_FAIL(err); result[0] = '\0'; - strncat(result, klassString, size); - size -= strlen(klassString); - strncat(result, ".", size); - size -= 1; - strncat(result, nameString, size); - size -= strlen(nameString); - strncat(result, signatureString, size); + if (snprintf(result, size, + "%s.%s%s", + klassString, + nameString, + signatureString) >= size) { + // truncation + goto fail; + } if (nameString != NULL) free(nameString); if (klassString != NULL) free(klassString); @@ -1056,9 +1057,9 @@ name_for_nmethod(jvm_agent_t* J, CHECK_FAIL(err); } if (deoptimized) { - strncat(result + 1, " [deoptimized frame]; ", size-1); + strncat(result, " [deoptimized frame]; ", size - strlen(result) - 1); } else { - strncat(result + 1, " [compiled] ", size-1); + strncat(result, " [compiled] ", size - strlen(result) - 1); } if (debug) fprintf(stderr, "name_for_nmethod: END: method name: %s, vf_cnt: %d\n\n", diff --git a/src/os/bsd/vm/decoder_machO.cpp b/src/os/bsd/vm/decoder_machO.cpp index 6ef6314a1d1ab133b99bfe43119b3faadbc6af07..5026ea83471895f29c688697d058d40b61563ec2 100644 --- a/src/os/bsd/vm/decoder_machO.cpp +++ b/src/os/bsd/vm/decoder_machO.cpp @@ -97,6 +97,7 @@ bool MachODecoder::decode(address addr, char *buf, char * symname = mach_find_in_stringtable((char*) ((uintptr_t)mach_base + stroff), strsize, found_strx); if (symname) { strncpy(buf, symname, buflen); + buf[buflen - 1] = '\0'; return true; } DEBUG_ONLY(tty->print_cr("no string or null string found.")); diff --git a/src/os/bsd/vm/os_bsd.cpp b/src/os/bsd/vm/os_bsd.cpp index 1c0336edd24e7bcc489664b0be0c7a6b9290e56d..46673771ff7741a3b61dc2916d523b45888d5d70 100644 --- a/src/os/bsd/vm/os_bsd.cpp +++ b/src/os/bsd/vm/os_bsd.cpp @@ -1690,6 +1690,53 @@ void os::print_dll_info(outputStream *st) { #endif } +int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *param) { +#ifdef RTLD_DI_LINKMAP + Dl_info dli; + void *handle; + Link_map *map; + Link_map *p; + + if (dladdr(CAST_FROM_FN_PTR(void *, os::print_dll_info), &dli) == 0 || + dli.dli_fname == NULL) { + return 1; + } + handle = dlopen(dli.dli_fname, RTLD_LAZY); + if (handle == NULL) { + return 1; + } + dlinfo(handle, RTLD_DI_LINKMAP, &map); + if (map == NULL) { + dlclose(handle); + return 1; + } + + while (map->l_prev != NULL) + map = map->l_prev; + + while (map != NULL) { + // Value for top_address is returned as 0 since we don't have any information about module size + if (callback(map->l_name, (address)map->l_addr, (address)0, param)) { + dlclose(handle); + return 1; + } + map = map->l_next; + } + + dlclose(handle); +#elif defined(__APPLE__) + for (uint32_t i = 1; i < _dyld_image_count(); i++) { + // Value for top_address is returned as 0 since we don't have any information about module size + if (callback(_dyld_get_image_name(i), (address)_dyld_get_image_header(i), (address)0, param)) { + return 1; + } + } + return 0; +#else + return 1; +#endif +} + void os::print_os_info_brief(outputStream* st) { st->print("Bsd"); @@ -2562,6 +2609,10 @@ size_t os::read(int fd, void *buf, unsigned int nBytes) { RESTARTABLE_RETURN_INT(::read(fd, buf, nBytes)); } +size_t os::read_at(int fd, void *buf, unsigned int nBytes, jlong offset) { + RESTARTABLE_RETURN_INT(::pread(fd, buf, nBytes, offset)); +} + // TODO-FIXME: reconcile Solaris' os::sleep with the bsd variation. // Solaris uses poll(), bsd uses park(). // Poll() is likely a better choice, assuming that Thread.interrupt() diff --git a/src/os/bsd/vm/os_perf_bsd.cpp b/src/os/bsd/vm/os_perf_bsd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e8fa0ef119c06e8a8b5e87be3f07a2fd5ca99de --- /dev/null +++ b/src/os/bsd/vm/os_perf_bsd.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2012, 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 "precompiled.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/os.hpp" +#include "runtime/os_perf.hpp" +#include "vm_version_ext_x86.hpp" + +#ifdef __APPLE__ + #import + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +static const double NANOS_PER_SEC = 1000000000.0; + +class CPUPerformanceInterface::CPUPerformance : public CHeapObj { + friend class CPUPerformanceInterface; + private: + long _total_cpu_nanos; + long _total_csr_nanos; + long _jvm_user_nanos; + long _jvm_system_nanos; + long _jvm_context_switches; + long _used_ticks; + long _total_ticks; + int _active_processor_count; + + bool now_in_nanos(long* resultp) { + timeval current_time; + if (gettimeofday(¤t_time, NULL) != 0) { + // Error getting current time + return false; + } + *resultp = (long)(current_time.tv_sec * NANOS_PER_SEC + 1000L * current_time.tv_usec); + return true; + } + + double normalize(double value) { + return MIN2(MAX2(value, 0.0), 1.0); + } + int cpu_load(int which_logical_cpu, double* cpu_load); + int context_switch_rate(double* rate); + int cpu_load_total_process(double* cpu_load); + int cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad); + + CPUPerformance(const CPUPerformance& rhs); // no impl + CPUPerformance& operator=(const CPUPerformance& rhs); // no impl + public: + CPUPerformance(); + bool initialize(); + ~CPUPerformance(); +}; + +CPUPerformanceInterface::CPUPerformance::CPUPerformance() { + _total_cpu_nanos= 0; + _total_csr_nanos= 0; + _jvm_context_switches = 0; + _jvm_user_nanos = 0; + _jvm_system_nanos = 0; + _used_ticks = 0; + _total_ticks = 0; + _active_processor_count = 0; +} + +bool CPUPerformanceInterface::CPUPerformance::initialize() { + return true; +} + +CPUPerformanceInterface::CPUPerformance::~CPUPerformance() { +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load(int which_logical_cpu, double* cpu_load) { + return FUNCTIONALITY_NOT_IMPLEMENTED; +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) { +#ifdef __APPLE__ + host_name_port_t host = mach_host_self(); + host_flavor_t flavor = HOST_CPU_LOAD_INFO; + mach_msg_type_number_t host_info_count = HOST_CPU_LOAD_INFO_COUNT; + host_cpu_load_info_data_t cpu_load_info; + + kern_return_t kr = host_statistics(host, flavor, (host_info_t)&cpu_load_info, &host_info_count); + if (kr != KERN_SUCCESS) { + return OS_ERR; + } + + long used_ticks = cpu_load_info.cpu_ticks[CPU_STATE_USER] + cpu_load_info.cpu_ticks[CPU_STATE_NICE] + cpu_load_info.cpu_ticks[CPU_STATE_SYSTEM]; + long total_ticks = used_ticks + cpu_load_info.cpu_ticks[CPU_STATE_IDLE]; + + if (_used_ticks == 0 || _total_ticks == 0) { + // First call, just set the values + _used_ticks = used_ticks; + _total_ticks = total_ticks; + return OS_ERR; + } + + long used_delta = used_ticks - _used_ticks; + long total_delta = total_ticks - _total_ticks; + + _used_ticks = used_ticks; + _total_ticks = total_ticks; + + if (total_delta == 0) { + // Avoid division by zero + return OS_ERR; + } + + *cpu_load = (double)used_delta / total_delta; + + return OS_OK; +#else + return FUNCTIONALITY_NOT_IMPLEMENTED; +#endif +} + +int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) { +#ifdef __APPLE__ + int result = cpu_load_total_process(psystemTotalLoad); + mach_port_t task = mach_task_self(); + mach_msg_type_number_t task_info_count = TASK_INFO_MAX; + task_info_data_t task_info_data; + kern_return_t kr = task_info(task, TASK_ABSOLUTETIME_INFO, (task_info_t)task_info_data, &task_info_count); + if (kr != KERN_SUCCESS) { + return OS_ERR; + } + task_absolutetime_info_t absolutetime_info = (task_absolutetime_info_t)task_info_data; + + int active_processor_count = os::active_processor_count(); + long jvm_user_nanos = absolutetime_info->total_user; + long jvm_system_nanos = absolutetime_info->total_system; + + long total_cpu_nanos; + if(!now_in_nanos(&total_cpu_nanos)) { + return OS_ERR; + } + + if (_total_cpu_nanos == 0 || active_processor_count != _active_processor_count) { + // First call or change in active processor count + result = OS_ERR; + } + + long delta_nanos = active_processor_count * (total_cpu_nanos - _total_cpu_nanos); + if (delta_nanos == 0) { + // Avoid division by zero + return OS_ERR; + } + + *pjvmUserLoad = normalize((double)(jvm_user_nanos - _jvm_user_nanos)/delta_nanos); + *pjvmKernelLoad = normalize((double)(jvm_system_nanos - _jvm_system_nanos)/delta_nanos); + + _active_processor_count = active_processor_count; + _total_cpu_nanos = total_cpu_nanos; + _jvm_user_nanos = jvm_user_nanos; + _jvm_system_nanos = jvm_system_nanos; + + return result; +#else + return FUNCTIONALITY_NOT_IMPLEMENTED; +#endif +} + +int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) { +#ifdef __APPLE__ + mach_port_t task = mach_task_self(); + mach_msg_type_number_t task_info_count = TASK_INFO_MAX; + task_info_data_t task_info_data; + kern_return_t kr = task_info(task, TASK_EVENTS_INFO, (task_info_t)task_info_data, &task_info_count); + if (kr != KERN_SUCCESS) { + return OS_ERR; + } + + int result = OS_OK; + if (_total_csr_nanos == 0 || _jvm_context_switches == 0) { + // First call just set initial values. + result = OS_ERR; + } + + long jvm_context_switches = ((task_events_info_t)task_info_data)->csw; + + long total_csr_nanos; + if(!now_in_nanos(&total_csr_nanos)) { + return OS_ERR; + } + double delta_in_sec = (double)(total_csr_nanos - _total_csr_nanos) / NANOS_PER_SEC; + if (delta_in_sec == 0.0) { + // Avoid division by zero + return OS_ERR; + } + + *rate = (jvm_context_switches - _jvm_context_switches) / delta_in_sec; + + _jvm_context_switches = jvm_context_switches; + _total_csr_nanos = total_csr_nanos; + + return result; +#else + return FUNCTIONALITY_NOT_IMPLEMENTED; +#endif +} + +CPUPerformanceInterface::CPUPerformanceInterface() { + _impl = NULL; +} + +bool CPUPerformanceInterface::initialize() { + _impl = new CPUPerformanceInterface::CPUPerformance(); + return _impl != NULL && _impl->initialize(); +} + +CPUPerformanceInterface::~CPUPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +int CPUPerformanceInterface::cpu_load(int which_logical_cpu, double* cpu_load) const { + return _impl->cpu_load(which_logical_cpu, cpu_load); +} + +int CPUPerformanceInterface::cpu_load_total_process(double* cpu_load) const { + return _impl->cpu_load_total_process(cpu_load); +} + +int CPUPerformanceInterface::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) const { + return _impl->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotalLoad); +} + +int CPUPerformanceInterface::context_switch_rate(double* rate) const { + return _impl->context_switch_rate(rate); +} + +class SystemProcessInterface::SystemProcesses : public CHeapObj { + friend class SystemProcessInterface; + private: + SystemProcesses(); + bool initialize(); + SystemProcesses(const SystemProcesses& rhs); // no impl + SystemProcesses& operator=(const SystemProcesses& rhs); // no impl + ~SystemProcesses(); + + //information about system processes + int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const; +}; + +SystemProcessInterface::SystemProcesses::SystemProcesses() { +} + +bool SystemProcessInterface::SystemProcesses::initialize() { + return true; +} + +SystemProcessInterface::SystemProcesses::~SystemProcesses() { +} +int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const { + assert(system_processes != NULL, "system_processes pointer is NULL!"); + assert(no_of_sys_processes != NULL, "system_processes counter pointer is NULL!"); +#ifdef __APPLE__ + pid_t* pids = NULL; + int pid_count = 0; + ResourceMark rm; + + int try_count = 0; + while (pids == NULL) { + // Find out buffer size + size_t pids_bytes = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0); + if (pids_bytes <= 0) { + return OS_ERR; + } + pid_count = pids_bytes / sizeof(pid_t); + pids = NEW_RESOURCE_ARRAY(pid_t, pid_count); + memset(pids, 0, pids_bytes); + + pids_bytes = proc_listpids(PROC_ALL_PIDS, 0, pids, pids_bytes); + if (pids_bytes <= 0) { + // couldn't fit buffer, retry. + FREE_RESOURCE_ARRAY(pid_t, pids, pid_count); + pids = NULL; + try_count++; + if (try_count > 3) { + return OS_ERR; + } + } else { + pid_count = pids_bytes / sizeof(pid_t); + } + } + + int process_count = 0; + SystemProcess* next = NULL; + for (int i = 0; i < pid_count; i++) { + pid_t pid = pids[i]; + if (pid != 0) { + char buffer[PROC_PIDPATHINFO_MAXSIZE]; + memset(buffer, 0 , sizeof(buffer)); + if (proc_pidpath(pid, buffer, sizeof(buffer)) != -1) { + int length = strlen(buffer); + if (length > 0) { + SystemProcess* current = new SystemProcess(); + char * path = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal); + strcpy(path, buffer); + current->set_path(path); + current->set_pid((int)pid); + current->set_next(next); + next = current; + process_count++; + } + } + } + } + + *no_of_sys_processes = process_count; + *system_processes = next; + + return OS_OK; +#endif + return FUNCTIONALITY_NOT_IMPLEMENTED; +} + +int SystemProcessInterface::system_processes(SystemProcess** system_procs, int* no_of_sys_processes) const { + return _impl->system_processes(system_procs, no_of_sys_processes); +} + +SystemProcessInterface::SystemProcessInterface() { + _impl = NULL; +} + +bool SystemProcessInterface::initialize() { + _impl = new SystemProcessInterface::SystemProcesses(); + return _impl != NULL && _impl->initialize(); +} + +SystemProcessInterface::~SystemProcessInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +CPUInformationInterface::CPUInformationInterface() { + _cpu_info = NULL; +} + +bool CPUInformationInterface::initialize() { + _cpu_info = new CPUInformation(); + + if (NULL == _cpu_info) { + return false; + } + _cpu_info->set_number_of_hardware_threads(VM_Version_Ext::number_of_threads()); + _cpu_info->set_number_of_cores(VM_Version_Ext::number_of_cores()); + _cpu_info->set_number_of_sockets(VM_Version_Ext::number_of_sockets()); + _cpu_info->set_cpu_name(VM_Version_Ext::cpu_name()); + _cpu_info->set_cpu_description(VM_Version_Ext::cpu_description()); + + return true; +} + +CPUInformationInterface::~CPUInformationInterface() { + if (_cpu_info != NULL) { + if (_cpu_info->cpu_name() != NULL) { + const char* cpu_name = _cpu_info->cpu_name(); + FREE_C_HEAP_ARRAY(char, cpu_name, mtInternal); + _cpu_info->set_cpu_name(NULL); + } + if (_cpu_info->cpu_description() != NULL) { + const char* cpu_desc = _cpu_info->cpu_description(); + FREE_C_HEAP_ARRAY(char, cpu_desc, mtInternal); + _cpu_info->set_cpu_description(NULL); + } + delete _cpu_info; + } +} + +int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) { + if (NULL == _cpu_info) { + return OS_ERR; + } + + cpu_info = *_cpu_info; // shallow copy assignment + return OS_OK; +} + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() { +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + return true; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const { + size_t len; + int mib[] = {CTL_NET, PF_ROUTE, /* protocol number */ 0, /* address family */ 0, NET_RT_IFLIST2, /* NET_RT_FLAGS mask*/ 0}; + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &len, NULL, 0) != 0) { + return OS_ERR; + } + uint8_t* buf = NEW_RESOURCE_ARRAY(uint8_t, len); + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &len, NULL, 0) != 0) { + return OS_ERR; + } + + size_t index = 0; + NetworkInterface* ret = NULL; + while (index < len) { + if_msghdr* msghdr = reinterpret_cast(buf + index); + index += msghdr->ifm_msglen; + + if (msghdr->ifm_type != RTM_IFINFO2) { + continue; + } + + if_msghdr2* msghdr2 = reinterpret_cast(msghdr); + sockaddr_dl* sockaddr = reinterpret_cast(msghdr2 + 1); + + // The interface name is not necessarily NUL-terminated + char name_buf[128]; + size_t name_len = MIN2(sizeof(name_buf) - 1, static_cast(sockaddr->sdl_nlen)); + strncpy(name_buf, sockaddr->sdl_data, name_len); + name_buf[name_len] = '\0'; + + uint64_t bytes_in = msghdr2->ifm_data.ifi_ibytes; + uint64_t bytes_out = msghdr2->ifm_data.ifi_obytes; + + NetworkInterface* cur = new NetworkInterface(name_buf, bytes_in, bytes_out, ret); + ret = cur; + } + + *network_interfaces = ret; + + return OS_OK; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff --git a/src/os/bsd/vm/semaphore_bsd.cpp b/src/os/bsd/vm/semaphore_bsd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b88bb144be8fc5cd3622b1c3150549f621cea8b3 --- /dev/null +++ b/src/os/bsd/vm/semaphore_bsd.cpp @@ -0,0 +1,107 @@ +/* + * 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. + * + */ + +#include "precompiled/precompiled.hpp" +#include "semaphore_bsd.hpp" +#include "utilities/debug.hpp" + +#include + +#ifdef __APPLE__ +// OS X doesn't support unamed POSIX semaphores, so the implementation in os_posix.cpp can't be used. + +static const char* sem_init_strerror(kern_return_t value) { + switch (value) { + case KERN_INVALID_ARGUMENT: return "Invalid argument"; + case KERN_RESOURCE_SHORTAGE: return "Resource shortage"; + default: return "Unknown"; + } +} + +OSXSemaphore::OSXSemaphore(uint value) { + kern_return_t ret = semaphore_create(mach_task_self(), &_semaphore, SYNC_POLICY_FIFO, value); + + guarantee(ret == KERN_SUCCESS, err_msg("Failed to create semaphore: %s", sem_init_strerror(ret))); +} + +OSXSemaphore::~OSXSemaphore() { + semaphore_destroy(mach_task_self(), _semaphore); +} + +void OSXSemaphore::signal(uint count) { + for (uint i = 0; i < count; i++) { + kern_return_t ret = semaphore_signal(_semaphore); + + assert(ret == KERN_SUCCESS, "Failed to signal semaphore"); + } +} + +void OSXSemaphore::wait() { + kern_return_t ret; + while ((ret = semaphore_wait(_semaphore)) == KERN_ABORTED) { + // Semaphore was interrupted. Retry. + } + assert(ret == KERN_SUCCESS, "Failed to wait on semaphore"); +} + +int64_t OSXSemaphore::currenttime() { + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec * NANOSECS_PER_SEC) + (tv.tv_usec * 1000); +} + +bool OSXSemaphore::trywait() { + return timedwait(0, 0); +} + +bool OSXSemaphore::timedwait(unsigned int sec, int nsec) { + kern_return_t kr = KERN_ABORTED; + mach_timespec_t waitspec; + waitspec.tv_sec = sec; + waitspec.tv_nsec = nsec; + + int64_t starttime = currenttime(); + + kr = semaphore_timedwait(_semaphore, waitspec); + while (kr == KERN_ABORTED) { + int64_t totalwait = (sec * NANOSECS_PER_SEC) + nsec; + + int64_t current = currenttime(); + int64_t passedtime = current - starttime; + + if (passedtime >= totalwait) { + waitspec.tv_sec = 0; + waitspec.tv_nsec = 0; + } else { + int64_t waittime = totalwait - (current - starttime); + waitspec.tv_sec = waittime / NANOSECS_PER_SEC; + waitspec.tv_nsec = waittime % NANOSECS_PER_SEC; + } + + kr = semaphore_timedwait(_semaphore, waitspec); + } + + return kr == KERN_SUCCESS; +} +#endif // __APPLE__ diff --git a/src/os/bsd/vm/semaphore_bsd.hpp b/src/os/bsd/vm/semaphore_bsd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..901d81eab0bbc055d3b1e33ddbc063596cd4b365 --- /dev/null +++ b/src/os/bsd/vm/semaphore_bsd.hpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015, 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_BSD_VM_SEMAPHORE_BSD_HPP +#define OS_BSD_VM_SEMAPHORE_BSD_HPP + +#ifndef __APPLE__ +// Use POSIX semaphores. +# include "semaphore_posix.hpp" + +#else +// OS X doesn't support unamed POSIX semaphores, so the implementation in os_posix.cpp can't be used. +# include "memory/allocation.hpp" +# include + +class OSXSemaphore : public CHeapObj{ + semaphore_t _semaphore; + + // Prevent copying and assignment. + OSXSemaphore(const OSXSemaphore&); + OSXSemaphore& operator=(const OSXSemaphore&); + + public: + OSXSemaphore(uint value = 0); + ~OSXSemaphore(); + + void signal(uint count = 1); + + void wait(); + + bool trywait(); + bool timedwait(unsigned int sec, int nsec); + + private: + static int64_t currenttime(); +}; + +typedef OSXSemaphore SemaphoreImpl; + +#endif // __APPLE__ + +#endif // OS_BSD_VM_SEMAPHORE_BSD_HPP diff --git a/src/os/linux/vm/os_linux.cpp b/src/os/linux/vm/os_linux.cpp index 03cabfefb4effb2ae29aba95b67b1b3f6cee0ff1..c477851c1ba85600503c15cc674411fc31645a5a 100644 --- a/src/os/linux/vm/os_linux.cpp +++ b/src/os/linux/vm/os_linux.cpp @@ -2146,6 +2146,41 @@ void os::print_dll_info(outputStream *st) { } } +int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *param) { + FILE *procmapsFile = NULL; + + // Open the procfs maps file for the current process + if ((procmapsFile = fopen("/proc/self/maps", "r")) != NULL) { + // Allocate PATH_MAX for file name plus a reasonable size for other fields. + char line[PATH_MAX + 100]; + + // Read line by line from 'file' + while (fgets(line, sizeof(line), procmapsFile) != NULL) { + u8 base, top, offset, inode; + char permissions[5]; + char device[6]; + char name[PATH_MAX + 1]; + + // Parse fields from line + sscanf(line, UINT64_FORMAT_X "-" UINT64_FORMAT_X " %4s " UINT64_FORMAT_X " %7s " INT64_FORMAT " %s", + &base, &top, permissions, &offset, device, &inode, name); + + // Filter by device id '00:00' so that we only get file system mapped files. + if (strcmp(device, "00:00") != 0) { + + // Call callback with the fields of interest + if(callback(name, (address)base, (address)top, param)) { + // Oops abort, callback aborted + fclose(procmapsFile); + return 1; + } + } + } + fclose(procmapsFile); + } + return 0; +} + void os::print_os_info_brief(outputStream* st) { os::Linux::print_distro_info(st); @@ -4030,6 +4065,10 @@ size_t os::read(int fd, void *buf, unsigned int nBytes) { return ::read(fd, buf, nBytes); } +size_t os::read_at(int fd, void *buf, unsigned int nBytes, jlong offset) { + return ::pread(fd, buf, nBytes, offset); +} + // TODO-FIXME: reconcile Solaris' os::sleep with the linux variation. // Solaris uses poll(), linux uses park(). // Poll() is likely a better choice, assuming that Thread.interrupt() diff --git a/src/os/linux/vm/os_perf_linux.cpp b/src/os/linux/vm/os_perf_linux.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8519089168fb74b6a80c126f99033ef6f6b3a6b1 --- /dev/null +++ b/src/os/linux/vm/os_perf_linux.cpp @@ -0,0 +1,1148 @@ +/* + * Copyright (c) 2012, 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 "precompiled.hpp" +#include "jvm.h" +#include "memory/allocation.inline.hpp" +#include "os_linux.inline.hpp" +#include "runtime/os.hpp" +#include "runtime/os_perf.hpp" + +#ifdef TARGET_ARCH_aarch32 +# include "vm_version_ext_aarch32.hpp" +#endif +#ifdef TARGET_ARCH_x86 +# include "vm_version_ext_x86.hpp" +#endif +#ifdef TARGET_ARCH_sparc +# include "vm_version_ext_sparc.hpp" +#endif +#ifdef TARGET_ARCH_zero +# include "vm_version_ext_zero.hpp" +#endif +#ifdef TARGET_ARCH_arm +# include "vm_version_ext_arm.hpp" +#endif +#ifdef TARGET_ARCH_ppc +# include "vm_version_ext_ppc.hpp" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + /proc/[number]/stat + Status information about the process. This is used by ps(1). It is defined in /usr/src/linux/fs/proc/array.c. + + The fields, in order, with their proper scanf(3) format specifiers, are: + + 1. pid %d The process id. + + 2. comm %s + The filename of the executable, in parentheses. This is visible whether or not the executable is swapped out. + + 3. state %c + One character from the string "RSDZTW" where R is running, S is sleeping in an interruptible wait, D is waiting in uninterruptible disk + sleep, Z is zombie, T is traced or stopped (on a signal), and W is paging. + + 4. ppid %d + The PID of the parent. + + 5. pgrp %d + The process group ID of the process. + + 6. session %d + The session ID of the process. + + 7. tty_nr %d + The tty the process uses. + + 8. tpgid %d + The process group ID of the process which currently owns the tty that the process is connected to. + + 9. flags %lu + The flags of the process. The math bit is decimal 4, and the traced bit is decimal 10. + + 10. minflt %lu + The number of minor faults the process has made which have not required loading a memory page from disk. + + 11. cminflt %lu + The number of minor faults that the process's waited-for children have made. + + 12. majflt %lu + The number of major faults the process has made which have required loading a memory page from disk. + + 13. cmajflt %lu + The number of major faults that the process's waited-for children have made. + + 14. utime %lu + The number of jiffies that this process has been scheduled in user mode. + + 15. stime %lu + The number of jiffies that this process has been scheduled in kernel mode. + + 16. cutime %ld + The number of jiffies that this process's waited-for children have been scheduled in user mode. (See also times(2).) + + 17. cstime %ld + The number of jiffies that this process' waited-for children have been scheduled in kernel mode. + + 18. priority %ld + The standard nice value, plus fifteen. The value is never negative in the kernel. + + 19. nice %ld + The nice value ranges from 19 (nicest) to -19 (not nice to others). + + 20. 0 %ld This value is hard coded to 0 as a placeholder for a removed field. + + 21. itrealvalue %ld + The time in jiffies before the next SIGALRM is sent to the process due to an interval timer. + + 22. starttime %lu + The time in jiffies the process started after system boot. + + 23. vsize %lu + Virtual memory size in bytes. + + 24. rss %ld + Resident Set Size: number of pages the process has in real memory, minus 3 for administrative purposes. This is just the pages which count + towards text, data, or stack space. This does not include pages which have not been demand-loaded in, or which are swapped out. + + 25. rlim %lu + Current limit in bytes on the rss of the process (usually 4294967295 on i386). + + 26. startcode %lu + The address above which program text can run. + + 27. endcode %lu + The address below which program text can run. + + 28. startstack %lu + The address of the start of the stack. + + 29. kstkesp %lu + The current value of esp (stack pointer), as found in the kernel stack page for the process. + + 30. kstkeip %lu + The current EIP (instruction pointer). + + 31. signal %lu + The bitmap of pending signals (usually 0). + + 32. blocked %lu + The bitmap of blocked signals (usually 0, 2 for shells). + + 33. sigignore %lu + The bitmap of ignored signals. + + 34. sigcatch %lu + The bitmap of catched signals. + + 35. wchan %lu + This is the "channel" in which the process is waiting. It is the address of a system call, and can be looked up in a namelist if you need + a textual name. (If you have an up-to-date /etc/psdatabase, then try ps -l to see the WCHAN field in action.) + + 36. nswap %lu + Number of pages swapped - not maintained. + + 37. cnswap %lu + Cumulative nswap for child processes. + + 38. exit_signal %d + Signal to be sent to parent when we die. + + 39. processor %d + CPU number last executed on. + + + + ///// SSCANF FORMAT STRING. Copy and use. + +field: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 +format: %d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d + + +*/ + +/** + * For platforms that have them, when declaring + * a printf-style function, + * formatSpec is the parameter number (starting at 1) + * that is the format argument ("%d pid %s") + * params is the parameter number where the actual args to + * the format starts. If the args are in a va_list, this + * should be 0. + */ +#ifndef PRINTF_ARGS +# define PRINTF_ARGS(formatSpec, params) ATTRIBUTE_PRINTF(formatSpec, params) +#endif + +#ifndef SCANF_ARGS +# define SCANF_ARGS(formatSpec, params) ATTRIBUTE_SCANF(formatSpec, params) +#endif + +#ifndef _PRINTFMT_ +# define _PRINTFMT_ +#endif + +#ifndef _SCANFMT_ +# define _SCANFMT_ +#endif + + +struct CPUPerfTicks { + uint64_t used; + uint64_t usedKernel; + uint64_t total; +}; + +typedef enum { + CPU_LOAD_VM_ONLY, + CPU_LOAD_GLOBAL, +} CpuLoadTarget; + +enum { + UNDETECTED, + UNDETECTABLE, + LINUX26_NPTL, + BAREMETAL +}; + +struct CPUPerfCounters { + int nProcs; + CPUPerfTicks jvmTicks; + CPUPerfTicks* cpus; +}; + +static double get_cpu_load(int which_logical_cpu, CPUPerfCounters* counters, double* pkernelLoad, CpuLoadTarget target); + +/** reads /proc//stat data, with some checks and some skips. + * Ensure that 'fmt' does _NOT_ contain the first two "%d %s" + */ +static int SCANF_ARGS(2, 0) vread_statdata(const char* procfile, _SCANFMT_ const char* fmt, va_list args) { + FILE*f; + int n; + char buf[2048]; + + if ((f = fopen(procfile, "r")) == NULL) { + return -1; + } + + if ((n = fread(buf, 1, sizeof(buf), f)) != -1) { + char *tmp; + + buf[n-1] = '\0'; + /** skip through pid and exec name. */ + if ((tmp = strrchr(buf, ')')) != NULL) { + // skip the ')' and the following space + // but check that buffer is long enough + tmp += 2; + if (tmp < buf + n) { + n = vsscanf(tmp, fmt, args); + } + } + } + + fclose(f); + + return n; +} + +static int SCANF_ARGS(2, 3) read_statdata(const char* procfile, _SCANFMT_ const char* fmt, ...) { + int n; + va_list args; + + va_start(args, fmt); + n = vread_statdata(procfile, fmt, args); + va_end(args); + return n; +} + +static FILE* open_statfile(void) { + FILE *f; + + if ((f = fopen("/proc/stat", "r")) == NULL) { + static int haveWarned = 0; + if (!haveWarned) { + haveWarned = 1; + } + } + return f; +} + +static void +next_line(FILE *f) { + int c; + do { + c = fgetc(f); + } while (c != '\n' && c != EOF); +} + +/** + * Return the total number of ticks since the system was booted. + * If the usedTicks parameter is not NULL, it will be filled with + * the number of ticks spent on actual processes (user, system or + * nice processes) since system boot. Note that this is the total number + * of "executed" ticks on _all_ CPU:s, that is on a n-way system it is + * n times the number of ticks that has passed in clock time. + * + * Returns a negative value if the reading of the ticks failed. + */ +static OSReturn get_total_ticks(int which_logical_cpu, CPUPerfTicks* pticks) { + FILE* fh; + uint64_t userTicks, niceTicks, systemTicks, idleTicks; + uint64_t iowTicks = 0, irqTicks = 0, sirqTicks= 0; + int logical_cpu = -1; + const int expected_assign_count = (-1 == which_logical_cpu) ? 4 : 5; + int n; + + if ((fh = open_statfile()) == NULL) { + return OS_ERR; + } + if (-1 == which_logical_cpu) { + n = fscanf(fh, "cpu " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " + UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT, + &userTicks, &niceTicks, &systemTicks, &idleTicks, + &iowTicks, &irqTicks, &sirqTicks); + } else { + // Move to next line + next_line(fh); + + // find the line for requested cpu faster to just iterate linefeeds? + for (int i = 0; i < which_logical_cpu; i++) { + next_line(fh); + } + + n = fscanf(fh, "cpu%u " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " + UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT, + &logical_cpu, &userTicks, &niceTicks, + &systemTicks, &idleTicks, &iowTicks, &irqTicks, &sirqTicks); + } + + fclose(fh); + if (n < expected_assign_count || logical_cpu != which_logical_cpu) { +#ifdef DEBUG_LINUX_PROC_STAT + vm_fprintf(stderr, "[stat] read failed"); +#endif + return OS_ERR; + } + +#ifdef DEBUG_LINUX_PROC_STAT + vm_fprintf(stderr, "[stat] read " + UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " " + UINT64_FORMAT " " UINT64_FORMAT " " UINT64_FORMAT " \n", + userTicks, niceTicks, systemTicks, idleTicks, + iowTicks, irqTicks, sirqTicks); +#endif + + pticks->used = userTicks + niceTicks; + pticks->usedKernel = systemTicks + irqTicks + sirqTicks; + pticks->total = userTicks + niceTicks + systemTicks + idleTicks + + iowTicks + irqTicks + sirqTicks; + + return OS_OK; +} + + +static int get_systemtype(void) { + static int procEntriesType = UNDETECTED; + DIR *taskDir; + + if (procEntriesType != UNDETECTED) { + return procEntriesType; + } + + // Check whether we have a task subdirectory + if ((taskDir = opendir("/proc/self/task")) == NULL) { + procEntriesType = UNDETECTABLE; + } else { + // The task subdirectory exists; we're on a Linux >= 2.6 system + closedir(taskDir); + procEntriesType = LINUX26_NPTL; + } + + return procEntriesType; +} + +/** read user and system ticks from a named procfile, assumed to be in 'stat' format then. */ +static int read_ticks(const char* procfile, uint64_t* userTicks, uint64_t* systemTicks) { + return read_statdata(procfile, "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u " UINT64_FORMAT " " UINT64_FORMAT, + userTicks, systemTicks); +} + +/** + * Return the number of ticks spent in any of the processes belonging + * to the JVM on any CPU. + */ +static OSReturn get_jvm_ticks(CPUPerfTicks* pticks) { + uint64_t userTicks; + uint64_t systemTicks; + + if (get_systemtype() != LINUX26_NPTL) { + return OS_ERR; + } + + if (read_ticks("/proc/self/stat", &userTicks, &systemTicks) != 2) { + return OS_ERR; + } + + // get the total + if (get_total_ticks(-1, pticks) != OS_OK) { + return OS_ERR; + } + + pticks->used = userTicks; + pticks->usedKernel = systemTicks; + + return OS_OK; +} + +/** + * Return the load of the CPU as a double. 1.0 means the CPU process uses all + * available time for user or system processes, 0.0 means the CPU uses all time + * being idle. + * + * Returns a negative value if there is a problem in determining the CPU load. + */ +static double get_cpu_load(int which_logical_cpu, CPUPerfCounters* counters, double* pkernelLoad, CpuLoadTarget target) { + uint64_t udiff, kdiff, tdiff; + CPUPerfTicks* pticks; + CPUPerfTicks tmp; + double user_load; + + *pkernelLoad = 0.0; + + if (target == CPU_LOAD_VM_ONLY) { + pticks = &counters->jvmTicks; + } else if (-1 == which_logical_cpu) { + pticks = &counters->cpus[counters->nProcs]; + } else { + pticks = &counters->cpus[which_logical_cpu]; + } + + tmp = *pticks; + + if (target == CPU_LOAD_VM_ONLY) { + if (get_jvm_ticks(pticks) != OS_OK) { + return -1.0; + } + } else if (get_total_ticks(which_logical_cpu, pticks) != OS_OK) { + return -1.0; + } + + // seems like we sometimes end up with less kernel ticks when + // reading /proc/self/stat a second time, timing issue between cpus? + if (pticks->usedKernel < tmp.usedKernel) { + kdiff = 0; + } else { + kdiff = pticks->usedKernel - tmp.usedKernel; + } + tdiff = pticks->total - tmp.total; + udiff = pticks->used - tmp.used; + + if (tdiff == 0) { + return 0.0; + } else if (tdiff < (udiff + kdiff)) { + tdiff = udiff + kdiff; + } + *pkernelLoad = (kdiff / (double)tdiff); + // BUG9044876, normalize return values to sane values + *pkernelLoad = MAX2(*pkernelLoad, 0.0); + *pkernelLoad = MIN2(*pkernelLoad, 1.0); + + user_load = (udiff / (double)tdiff); + user_load = MAX2(user_load, 0.0); + user_load = MIN2(user_load, 1.0); + + return user_load; +} + +static int SCANF_ARGS(1, 2) parse_stat(_SCANFMT_ const char* fmt, ...) { + FILE *f; + va_list args; + + va_start(args, fmt); + + if ((f = open_statfile()) == NULL) { + va_end(args); + return OS_ERR; + } + for (;;) { + char line[80]; + if (fgets(line, sizeof(line), f) != NULL) { + if (vsscanf(line, fmt, args) == 1) { + fclose(f); + va_end(args); + return OS_OK; + } + } else { + fclose(f); + va_end(args); + return OS_ERR; + } + } +} + +static int get_noof_context_switches(uint64_t* switches) { + return parse_stat("ctxt " UINT64_FORMAT "\n", switches); +} + +/** returns boot time in _seconds_ since epoch */ +static int get_boot_time(uint64_t* time) { + return parse_stat("btime " UINT64_FORMAT "\n", time); +} + +static int perf_context_switch_rate(double* rate) { + static pthread_mutex_t contextSwitchLock = PTHREAD_MUTEX_INITIALIZER; + static uint64_t lastTime; + static uint64_t lastSwitches; + static double lastRate; + + uint64_t lt = 0; + int res = 0; + + if (lastTime == 0) { + uint64_t tmp; + if (get_boot_time(&tmp) < 0) { + return OS_ERR; + } + lt = tmp * 1000; + } + + res = OS_OK; + + pthread_mutex_lock(&contextSwitchLock); + { + + uint64_t sw; + s8 t, d; + + if (lastTime == 0) { + lastTime = lt; + } + + t = os::javaTimeMillis(); + d = t - lastTime; + + if (d == 0) { + *rate = lastRate; + } else if (!get_noof_context_switches(&sw)) { + *rate = ( (double)(sw - lastSwitches) / d ) * 1000; + lastRate = *rate; + lastSwitches = sw; + lastTime = t; + } else { + *rate = 0; + res = OS_ERR; + } + if (*rate <= 0) { + *rate = 0; + lastRate = 0; + } + } + pthread_mutex_unlock(&contextSwitchLock); + + return res; +} + +class CPUPerformanceInterface::CPUPerformance : public CHeapObj { + friend class CPUPerformanceInterface; + private: + CPUPerfCounters _counters; + + int cpu_load(int which_logical_cpu, double* cpu_load); + int context_switch_rate(double* rate); + int cpu_load_total_process(double* cpu_load); + int cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad); + + public: + CPUPerformance(); + bool initialize(); + ~CPUPerformance(); +}; + +CPUPerformanceInterface::CPUPerformance::CPUPerformance() { + _counters.nProcs = os::active_processor_count(); + _counters.cpus = NULL; +} + +bool CPUPerformanceInterface::CPUPerformance::initialize() { + size_t tick_array_size = (_counters.nProcs +1) * sizeof(CPUPerfTicks); + _counters.cpus = (CPUPerfTicks*)NEW_C_HEAP_ARRAY(char, tick_array_size, mtInternal); + if (NULL == _counters.cpus) { + return false; + } + memset(_counters.cpus, 0, tick_array_size); + + // For the CPU load total + get_total_ticks(-1, &_counters.cpus[_counters.nProcs]); + + // For each CPU + for (int i = 0; i < _counters.nProcs; i++) { + get_total_ticks(i, &_counters.cpus[i]); + } + // For JVM load + get_jvm_ticks(&_counters.jvmTicks); + + // initialize context switch system + // the double is only for init + double init_ctx_switch_rate; + perf_context_switch_rate(&init_ctx_switch_rate); + + return true; +} + +CPUPerformanceInterface::CPUPerformance::~CPUPerformance() { + if (_counters.cpus != NULL) { + FREE_C_HEAP_ARRAY(char, _counters.cpus, mtInternal); + } +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load(int which_logical_cpu, double* cpu_load) { + double u, s; + u = get_cpu_load(which_logical_cpu, &_counters, &s, CPU_LOAD_GLOBAL); + if (u < 0) { + *cpu_load = 0.0; + return OS_ERR; + } + // Cap total systemload to 1.0 + *cpu_load = MIN2((u + s), 1.0); + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) { + double u, s; + u = get_cpu_load(-1, &_counters, &s, CPU_LOAD_VM_ONLY); + if (u < 0) { + *cpu_load = 0.0; + return OS_ERR; + } + *cpu_load = u + s; + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) { + double u, s, t; + + assert(pjvmUserLoad != NULL, "pjvmUserLoad not inited"); + assert(pjvmKernelLoad != NULL, "pjvmKernelLoad not inited"); + assert(psystemTotalLoad != NULL, "psystemTotalLoad not inited"); + + u = get_cpu_load(-1, &_counters, &s, CPU_LOAD_VM_ONLY); + if (u < 0) { + *pjvmUserLoad = 0.0; + *pjvmKernelLoad = 0.0; + *psystemTotalLoad = 0.0; + return OS_ERR; + } + + cpu_load(-1, &t); + // clamp at user+system and 1.0 + if (u + s > t) { + t = MIN2(u + s, 1.0); + } + + *pjvmUserLoad = u; + *pjvmKernelLoad = s; + *psystemTotalLoad = t; + + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) { + return perf_context_switch_rate(rate); +} + +CPUPerformanceInterface::CPUPerformanceInterface() { + _impl = NULL; +} + +bool CPUPerformanceInterface::initialize() { + _impl = new CPUPerformanceInterface::CPUPerformance(); + return NULL == _impl ? false : _impl->initialize(); +} + +CPUPerformanceInterface::~CPUPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +int CPUPerformanceInterface::cpu_load(int which_logical_cpu, double* cpu_load) const { + return _impl->cpu_load(which_logical_cpu, cpu_load); +} + +int CPUPerformanceInterface::cpu_load_total_process(double* cpu_load) const { + return _impl->cpu_load_total_process(cpu_load); +} + +int CPUPerformanceInterface::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) const { + return _impl->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotalLoad); +} + +int CPUPerformanceInterface::context_switch_rate(double* rate) const { + return _impl->context_switch_rate(rate); +} + +class SystemProcessInterface::SystemProcesses : public CHeapObj { + friend class SystemProcessInterface; + private: + class ProcessIterator : public CHeapObj { + friend class SystemProcessInterface::SystemProcesses; + private: + DIR* _dir; + struct dirent* _entry; + bool _valid; + char _exeName[PATH_MAX]; + char _exePath[PATH_MAX]; + + ProcessIterator(); + ~ProcessIterator(); + bool initialize(); + + bool is_valid() const { return _valid; } + bool is_valid_entry(struct dirent* entry) const; + bool is_dir(const char* name) const; + int fsize(const char* name, uint64_t& size) const; + + char* allocate_string(const char* str) const; + void get_exe_name(); + char* get_exe_path(); + char* get_cmdline(); + + int current(SystemProcess* process_info); + int next_process(); + }; + + ProcessIterator* _iterator; + SystemProcesses(); + bool initialize(); + ~SystemProcesses(); + + //information about system processes + int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const; +}; + +bool SystemProcessInterface::SystemProcesses::ProcessIterator::is_dir(const char* name) const { + struct stat mystat; + int ret_val = 0; + + ret_val = stat(name, &mystat); + if (ret_val < 0) { + return false; + } + ret_val = S_ISDIR(mystat.st_mode); + return ret_val > 0; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::fsize(const char* name, uint64_t& size) const { + assert(name != NULL, "name pointer is NULL!"); + size = 0; + struct stat fbuf; + + if (stat(name, &fbuf) < 0) { + return OS_ERR; + } + size = fbuf.st_size; + return OS_OK; +} + +// if it has a numeric name, is a directory and has a 'stat' file in it +bool SystemProcessInterface::SystemProcesses::ProcessIterator::is_valid_entry(struct dirent* entry) const { + char buffer[PATH_MAX]; + uint64_t size = 0; + + if (atoi(entry->d_name) != 0) { + jio_snprintf(buffer, PATH_MAX, "/proc/%s", entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + + if (is_dir(buffer)) { + jio_snprintf(buffer, PATH_MAX, "/proc/%s/stat", entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + if (fsize(buffer, size) != OS_ERR) { + return true; + } + } + } + return false; +} + +// get exe-name from /proc//stat +void SystemProcessInterface::SystemProcesses::ProcessIterator::get_exe_name() { + FILE* fp; + char buffer[PATH_MAX]; + + jio_snprintf(buffer, PATH_MAX, "/proc/%s/stat", _entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + if ((fp = fopen(buffer, "r")) != NULL) { + if (fgets(buffer, PATH_MAX, fp) != NULL) { + char* start, *end; + // exe-name is between the first pair of ( and ) + start = strchr(buffer, '('); + if (start != NULL && start[1] != '\0') { + start++; + end = strrchr(start, ')'); + if (end != NULL) { + size_t len; + len = MIN2(end - start, sizeof(_exeName) - 1); + memcpy(_exeName, start, len); + _exeName[len] = '\0'; + } + } + } + fclose(fp); + } +} + +// get command line from /proc//cmdline +char* SystemProcessInterface::SystemProcesses::ProcessIterator::get_cmdline() { + FILE* fp; + char buffer[PATH_MAX]; + char* cmdline = NULL; + + jio_snprintf(buffer, PATH_MAX, "/proc/%s/cmdline", _entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + if ((fp = fopen(buffer, "r")) != NULL) { + size_t size = 0; + char dummy; + + // find out how long the file is (stat always returns 0) + while (fread(&dummy, 1, 1, fp) == 1) { + size++; + } + if (size > 0) { + cmdline = NEW_C_HEAP_ARRAY(char, size + 1, mtInternal); + if (cmdline != NULL) { + cmdline[0] = '\0'; + if (fseek(fp, 0, SEEK_SET) == 0) { + if (fread(cmdline, 1, size, fp) == size) { + // the file has the arguments separated by '\0', + // so we translate '\0' to ' ' + for (size_t i = 0; i < size; i++) { + if (cmdline[i] == '\0') { + cmdline[i] = ' '; + } + } + cmdline[size] = '\0'; + } + } + } + } + fclose(fp); + } + return cmdline; +} + +// get full path to exe from /proc//exe symlink +char* SystemProcessInterface::SystemProcesses::ProcessIterator::get_exe_path() { + char buffer[PATH_MAX]; + + jio_snprintf(buffer, PATH_MAX, "/proc/%s/exe", _entry->d_name); + buffer[PATH_MAX - 1] = '\0'; + return realpath(buffer, _exePath); +} + +char* SystemProcessInterface::SystemProcesses::ProcessIterator::allocate_string(const char* str) const { + if (str != NULL) { + size_t len = strlen(str); + char* tmp = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); + strncpy(tmp, str, len); + tmp[len] = '\0'; + return tmp; + } + return NULL; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::current(SystemProcess* process_info) { + if (!is_valid()) { + return OS_ERR; + } + + process_info->set_pid(atoi(_entry->d_name)); + + get_exe_name(); + process_info->set_name(allocate_string(_exeName)); + + if (get_exe_path() != NULL) { + process_info->set_path(allocate_string(_exePath)); + } + + char* cmdline = NULL; + cmdline = get_cmdline(); + if (cmdline != NULL) { + process_info->set_command_line(allocate_string(cmdline)); + FREE_C_HEAP_ARRAY(char, cmdline, mtInternal); + } + + return OS_OK; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::next_process() { + if (!is_valid()) { + return OS_ERR; + } + + do { + _entry = os::readdir(_dir); + if (_entry == NULL) { + // Error or reached end. Could use errno to distinguish those cases. + _valid = false; + return OS_ERR; + } + } while(!is_valid_entry(_entry)); + + _valid = true; + return OS_OK; +} + +SystemProcessInterface::SystemProcesses::ProcessIterator::ProcessIterator() { + _dir = NULL; + _entry = NULL; + _valid = false; +} + +bool SystemProcessInterface::SystemProcesses::ProcessIterator::initialize() { + _dir = os::opendir("/proc"); + _entry = NULL; + _valid = true; + next_process(); + + return true; +} + +SystemProcessInterface::SystemProcesses::ProcessIterator::~ProcessIterator() { + if (_dir != NULL) { + os::closedir(_dir); + } +} + +SystemProcessInterface::SystemProcesses::SystemProcesses() { + _iterator = NULL; +} + +bool SystemProcessInterface::SystemProcesses::initialize() { + _iterator = new SystemProcessInterface::SystemProcesses::ProcessIterator(); + return NULL == _iterator ? false : _iterator->initialize(); +} + +SystemProcessInterface::SystemProcesses::~SystemProcesses() { + if (_iterator != NULL) { + delete _iterator; + } +} + +int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const { + assert(system_processes != NULL, "system_processes pointer is NULL!"); + assert(no_of_sys_processes != NULL, "system_processes counter pointers is NULL!"); + assert(_iterator != NULL, "iterator is NULL!"); + + // initialize pointers + *no_of_sys_processes = 0; + *system_processes = NULL; + + while (_iterator->is_valid()) { + SystemProcess* tmp = new SystemProcess(); + _iterator->current(tmp); + + //if already existing head + if (*system_processes != NULL) { + //move "first to second" + tmp->set_next(*system_processes); + } + // new head + *system_processes = tmp; + // increment + (*no_of_sys_processes)++; + // step forward + _iterator->next_process(); + } + return OS_OK; +} + +int SystemProcessInterface::system_processes(SystemProcess** system_procs, int* no_of_sys_processes) const { + return _impl->system_processes(system_procs, no_of_sys_processes); +} + +SystemProcessInterface::SystemProcessInterface() { + _impl = NULL; +} + +bool SystemProcessInterface::initialize() { + _impl = new SystemProcessInterface::SystemProcesses(); + return NULL == _impl ? false : _impl->initialize(); +} + +SystemProcessInterface::~SystemProcessInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +CPUInformationInterface::CPUInformationInterface() { + _cpu_info = NULL; +} + +bool CPUInformationInterface::initialize() { + _cpu_info = new CPUInformation(); + if (NULL == _cpu_info) { + return false; + } + _cpu_info->set_number_of_hardware_threads(VM_Version_Ext::number_of_threads()); + _cpu_info->set_number_of_cores(VM_Version_Ext::number_of_cores()); + _cpu_info->set_number_of_sockets(VM_Version_Ext::number_of_sockets()); + _cpu_info->set_cpu_name(VM_Version_Ext::cpu_name()); + _cpu_info->set_cpu_description(VM_Version_Ext::cpu_description()); + + return true; +} + +CPUInformationInterface::~CPUInformationInterface() { + if (_cpu_info != NULL) { + if (_cpu_info->cpu_name() != NULL) { + const char* cpu_name = _cpu_info->cpu_name(); + FREE_C_HEAP_ARRAY(char, cpu_name, mtInternal); + _cpu_info->set_cpu_name(NULL); + } + if (_cpu_info->cpu_description() != NULL) { + const char* cpu_desc = _cpu_info->cpu_description(); + FREE_C_HEAP_ARRAY(char, cpu_desc, mtInternal); + _cpu_info->set_cpu_description(NULL); + } + delete _cpu_info; + } +} + +int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) { + if (_cpu_info == NULL) { + return OS_ERR; + } + + cpu_info = *_cpu_info; // shallow copy assignment + return OS_OK; +} + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int64_t read_counter(const char* iface, const char* counter) const; + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() { + +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + return true; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { +} + +int64_t NetworkPerformanceInterface::NetworkPerformance::read_counter(const char* iface, const char* counter) const { + char buf[128]; + + snprintf(buf, sizeof(buf), "/sys/class/net/%s/statistics/%s", iface, counter); + + int fd = open(buf, O_RDONLY); + if (fd == -1) { + return -1; + } + + ssize_t num_bytes = read(fd, buf, sizeof(buf)); + close(fd); + if ((num_bytes == -1) || (num_bytes >= static_cast(sizeof(buf))) || (num_bytes < 1)) { + return -1; + } + + buf[num_bytes] = '\0'; + int64_t value = strtoll(buf, NULL, 10); + + return value; +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const +{ + ifaddrs* addresses; + ifaddrs* cur_address; + + if (getifaddrs(&addresses) != 0) { + return OS_ERR; + } + + NetworkInterface* ret = NULL; + for (cur_address = addresses; cur_address != NULL; cur_address = cur_address->ifa_next) { + if ((cur_address->ifa_addr == NULL) || (cur_address->ifa_addr->sa_family != AF_PACKET)) { + continue; + } + + int64_t bytes_in = read_counter(cur_address->ifa_name, "rx_bytes"); + int64_t bytes_out = read_counter(cur_address->ifa_name, "tx_bytes"); + + NetworkInterface* cur = new NetworkInterface(cur_address->ifa_name, bytes_in, bytes_out, ret); + ret = cur; + } + + freeifaddrs(addresses); + *network_interfaces = ret; + + return OS_OK; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff --git a/src/os/posix/vm/os_posix.cpp b/src/os/posix/vm/os_posix.cpp index a43a855b2741c59e2dc3d2bc04f545953f4a676f..ac00e85ba11ccd2f093457844e4d84a174559120 100644 --- a/src/os/posix/vm/os_posix.cpp +++ b/src/os/posix/vm/os_posix.cpp @@ -858,8 +858,11 @@ void os::Posix::print_siginfo_brief(outputStream* os, const siginfo_t* si) { } } -os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() { - assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread"); +Thread* os::ThreadCrashProtection::_protected_thread = NULL; +os::ThreadCrashProtection* os::ThreadCrashProtection::_crash_protection = NULL; +volatile intptr_t os::ThreadCrashProtection::_crash_mux = 0; + +os::ThreadCrashProtection::ThreadCrashProtection() { } /* @@ -868,12 +871,13 @@ os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() { * method and returns false. If none of the signals are raised, returns true. * The callback is supposed to provide the method that should be protected. */ -bool os::WatcherThreadCrashProtection::call(os::CrashProtectionCallback& cb) { +bool os::ThreadCrashProtection::call(os::CrashProtectionCallback& cb) { sigset_t saved_sig_mask; - assert(Thread::current()->is_Watcher_thread(), "Only for WatcherThread"); - assert(!WatcherThread::watcher_thread()->has_crash_protection(), - "crash_protection already set?"); + Thread::muxAcquire(&_crash_mux, "CrashProtection"); + + _protected_thread = ThreadLocalStorage::thread(); + assert(_protected_thread != NULL, "Cannot crash protect a NULL thread"); // we cannot rely on sigsetjmp/siglongjmp to save/restore the signal mask // since on at least some systems (OS X) siglongjmp will restore the mask @@ -882,34 +886,36 @@ bool os::WatcherThreadCrashProtection::call(os::CrashProtectionCallback& cb) { if (sigsetjmp(_jmpbuf, 0) == 0) { // make sure we can see in the signal handler that we have crash protection // installed - WatcherThread::watcher_thread()->set_crash_protection(this); + _crash_protection = this; cb.call(); // and clear the crash protection - WatcherThread::watcher_thread()->set_crash_protection(NULL); + _crash_protection = NULL; + _protected_thread = NULL; + Thread::muxRelease(&_crash_mux); return true; } // this happens when we siglongjmp() back pthread_sigmask(SIG_SETMASK, &saved_sig_mask, NULL); - WatcherThread::watcher_thread()->set_crash_protection(NULL); + _crash_protection = NULL; + _protected_thread = NULL; + Thread::muxRelease(&_crash_mux); return false; } -void os::WatcherThreadCrashProtection::restore() { - assert(WatcherThread::watcher_thread()->has_crash_protection(), - "must have crash protection"); - +void os::ThreadCrashProtection::restore() { + assert(_crash_protection != NULL, "must have crash protection"); siglongjmp(_jmpbuf, 1); } -void os::WatcherThreadCrashProtection::check_crash_protection(int sig, +void os::ThreadCrashProtection::check_crash_protection(int sig, Thread* thread) { if (thread != NULL && - thread->is_Watcher_thread() && - WatcherThread::watcher_thread()->has_crash_protection()) { + thread == _protected_thread && + _crash_protection != NULL) { if (sig == SIGSEGV || sig == SIGBUS) { - WatcherThread::watcher_thread()->crash_protection()->restore(); + _crash_protection->restore(); } } } diff --git a/src/os/posix/vm/os_posix.hpp b/src/os/posix/vm/os_posix.hpp index 5313f41466056e9c93f77e3865b970075ae88660..5cdbb9ae6011653aa57f10d1b97471a7433d8b1e 100644 --- a/src/os/posix/vm/os_posix.hpp +++ b/src/os/posix/vm/os_posix.hpp @@ -62,7 +62,7 @@ public: }; /* - * Crash protection for the watcher thread. Wrap the callback + * Crash protection utility used by JFR. Wrap the callback * with a sigsetjmp and in case of a SIGSEGV/SIGBUS we siglongjmp * back. * To be able to use this - don't take locks, don't rely on destructors, @@ -70,13 +70,20 @@ public: * don't call code that could leave the heap / memory in an inconsistent state, * or anything else where we are not in control if we suddenly jump out. */ -class WatcherThreadCrashProtection : public StackObj { +class ThreadCrashProtection : public StackObj { public: - WatcherThreadCrashProtection(); + static bool is_crash_protected(Thread* thr) { + return _crash_protection != NULL && _protected_thread == thr; + } + + ThreadCrashProtection(); bool call(os::CrashProtectionCallback& cb); static void check_crash_protection(int signal, Thread* thread); private: + static Thread* _protected_thread; + static ThreadCrashProtection* _crash_protection; + static volatile intptr_t _crash_mux; void restore(); sigjmp_buf _jmpbuf; }; diff --git a/src/os/posix/vm/semaphore_posix.cpp b/src/os/posix/vm/semaphore_posix.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d7ef127a8e15e2773a4fab59f438dbbfca0cb692 --- /dev/null +++ b/src/os/posix/vm/semaphore_posix.cpp @@ -0,0 +1,98 @@ +/* + * 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. + * + */ + +#include "precompiled/precompiled.hpp" +#ifndef __APPLE__ +#include "runtime/os.hpp" +// POSIX unamed semaphores are not supported on OS X. +#include "semaphore_posix.hpp" +#include + +#define check_with_errno(check_type, cond, msg) \ + do { \ + int err = errno; \ + check_type(cond, /*"%s; error='%s' (errno=%s)", */msg/*, os::strerror(err),*/ \ + /*os::errno_name(err)*/); \ +} while (false) + +#define assert_with_errno(cond, msg) check_with_errno(assert, cond, msg) +#define guarantee_with_errno(cond, msg) check_with_errno(guarantee, cond, msg) + +PosixSemaphore::PosixSemaphore(uint value) { + int ret = sem_init(&_semaphore, 0, value); + + guarantee_with_errno(ret == 0, "Failed to initialize semaphore"); +} + +PosixSemaphore::~PosixSemaphore() { + sem_destroy(&_semaphore); +} + +void PosixSemaphore::signal(uint count) { + for (uint i = 0; i < count; i++) { + int ret = sem_post(&_semaphore); + + assert_with_errno(ret == 0, "sem_post failed"); + } +} + +void PosixSemaphore::wait() { + int ret; + + do { + ret = sem_wait(&_semaphore); + } while (ret != 0 && errno == EINTR); + + assert_with_errno(ret == 0, "sem_wait failed"); +} + +bool PosixSemaphore::trywait() { + int ret; + + do { + ret = sem_trywait(&_semaphore); + } while (ret != 0 && errno == EINTR); + + assert_with_errno(ret == 0 || errno == EAGAIN, "trywait failed"); + + return ret == 0; +} + +bool PosixSemaphore::timedwait(struct timespec ts) { + while (true) { + int result = sem_timedwait(&_semaphore, &ts); + if (result == 0) { + return true; + } else if (errno == EINTR) { + continue; + } else if (errno == ETIMEDOUT) { + return false; + } else { + assert_with_errno(false, "timedwait failed"); + return false; + } + } +} +#endif // __APPLE__ + diff --git a/src/os/posix/vm/semaphore_posix.hpp b/src/os/posix/vm/semaphore_posix.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1fda6741fe0fbd4b0706aa9a4665a8c98f2c8f61 --- /dev/null +++ b/src/os/posix/vm/semaphore_posix.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, 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_POSIX_VM_SEMAPHORE_POSIX_HPP +#define OS_POSIX_VM_SEMAPHORE_POSIX_HPP + +#include "memory/allocation.hpp" + +#include + +class PosixSemaphore : public CHeapObj { + sem_t _semaphore; + + // Prevent copying and assignment. + PosixSemaphore(const PosixSemaphore&); + PosixSemaphore& operator=(const PosixSemaphore&); + + public: + PosixSemaphore(uint value = 0); + ~PosixSemaphore(); + + void signal(uint count = 1); + + void wait(); + + bool trywait(); + bool timedwait(struct timespec ts); +}; + +typedef PosixSemaphore SemaphoreImpl; + +#endif // OS_POSIX_VM_SEMAPHORE_POSIX_HPP diff --git a/src/os/solaris/dtrace/libjvm_db.c b/src/os/solaris/dtrace/libjvm_db.c index a3830145c1a0162377d3ca6b1eca82f89f8b5237..1d2369b2549ec92fd59398488ee53d647d924d0b 100644 --- a/src/os/solaris/dtrace/libjvm_db.c +++ b/src/os/solaris/dtrace/libjvm_db.c @@ -543,13 +543,14 @@ name_for_methodPtr(jvm_agent_t* J, uint64_t methodPtr, char * result, size_t siz CHECK_FAIL(err); result[0] = '\0'; - strncat(result, klassString, size); - size -= strlen(klassString); - strncat(result, ".", size); - size -= 1; - strncat(result, nameString, size); - size -= strlen(nameString); - strncat(result, signatureString, size); + if (snprintf(result, size, + "%s.%s%s", + klassString, + nameString, + signatureString) >= size) { + // truncation + goto fail; + } if (nameString != NULL) free(nameString); if (klassString != NULL) free(klassString); @@ -1056,9 +1057,9 @@ name_for_nmethod(jvm_agent_t* J, CHECK_FAIL(err); } if (deoptimized) { - strncat(result + 1, " [deoptimized frame]; ", size-1); + strncat(result, " [deoptimized frame]; ", size - strlen(result) - 1); } else { - strncat(result + 1, " [compiled] ", size-1); + strncat(result, " [compiled] ", size - strlen(result) - 1); } if (debug) fprintf(stderr, "name_for_nmethod: END: method name: %s, vf_cnt: %d\n\n", diff --git a/src/os/solaris/vm/os_perf_solaris.cpp b/src/os/solaris/vm/os_perf_solaris.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08f097586bbe36f856c432450660383c98f6f10c --- /dev/null +++ b/src/os/solaris/vm/os_perf_solaris.cpp @@ -0,0 +1,851 @@ +/* + * Copyright (c) 2012, 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 "precompiled.hpp" +#include "jvm.h" +#include "memory/allocation.inline.hpp" +#include "runtime/os.hpp" +#include "runtime/os_perf.hpp" +#include "os_solaris.inline.hpp" +#include "utilities/macros.hpp" + +#ifdef TARGET_ARCH_aarch32 +# include "vm_version_ext_aarch32.hpp" +#endif +#ifdef TARGET_ARCH_x86 +# include "vm_version_ext_x86.hpp" +#endif +#ifdef TARGET_ARCH_sparc +# include "vm_version_ext_sparc.hpp" +#endif +#ifdef TARGET_ARCH_zero +# include "vm_version_ext_zero.hpp" +#endif +#ifdef TARGET_ARCH_arm +# include "vm_version_ext_arm.hpp" +#endif +#ifdef TARGET_ARCH_ppc +# include "vm_version_ext_ppc.hpp" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const double NANOS_PER_SEC = 1000000000.0; + +struct CPUPerfTicks { + kstat_t* kstat; + uint64_t last_idle; + uint64_t last_total; + double last_ratio; +}; + +struct CPUPerfCounters { + int nProcs; + CPUPerfTicks* jvmTicks; + kstat_ctl_t* kstat_ctrl; +}; + +static int get_info(const char* path, void* info, size_t s, off_t o) { + assert(path != NULL, "path is NULL!"); + assert(info != NULL, "info is NULL!"); + + int fd = -1; + + if ((fd = open(path, O_RDONLY)) < 0) { + return OS_ERR; + } + if (pread(fd, info, s, o) != s) { + close(fd); + return OS_ERR; + } + close(fd); + return OS_OK; +} + +static int get_psinfo2(void* info, size_t s, off_t o) { + return get_info("/proc/self/psinfo", info, s, o); +} + +static int get_psinfo(psinfo_t* info) { + return get_psinfo2(info, sizeof(*info), 0); +} + +static int get_psinfo(char* file, psinfo_t* info) { + assert(file != NULL, "file is NULL!"); + assert(info != NULL, "info is NULL!"); + return get_info(file, info, sizeof(*info), 0); +} + + +static int get_usage(prusage_t* usage) { + assert(usage != NULL, "usage is NULL!"); + return get_info("/proc/self/usage", usage, sizeof(*usage), 0); +} + +static int read_cpustat(kstat_ctl_t* kstat_ctrl, CPUPerfTicks* load, cpu_stat_t* cpu_stat) { + assert(kstat_ctrl != NULL, "kstat_ctrl pointer is NULL!"); + assert(load != NULL, "load pointer is NULL!"); + assert(cpu_stat != NULL, "cpu_stat pointer is NULL!"); + + if (load->kstat == NULL) { + // no handle. + return OS_ERR; + } + if (kstat_read(kstat_ctrl, load->kstat, cpu_stat) == OS_ERR) { + // disable handle for this CPU + load->kstat = NULL; + return OS_ERR; + } + return OS_OK; +} + +static double get_cpu_load(int which_logical_cpu, CPUPerfCounters* counters) { + assert(counters != NULL, "counters pointer is NULL!"); + + cpu_stat_t cpu_stat = {0}; + + if (which_logical_cpu >= counters->nProcs) { + return .0; + } + + CPUPerfTicks load = counters->jvmTicks[which_logical_cpu]; + if (read_cpustat(counters->kstat_ctrl, &load, &cpu_stat) != OS_OK) { + return .0; + } + + uint_t* usage = cpu_stat.cpu_sysinfo.cpu; + if (usage == NULL) { + return .0; + } + + uint64_t c_idle = usage[CPU_IDLE]; + uint64_t c_total = 0; + + for (int i = 0; i < CPU_STATES; i++) { + c_total += usage[i]; + } + + // Calculate diff against previous snapshot + uint64_t d_idle = c_idle - load.last_idle; + uint64_t d_total = c_total - load.last_total; + + /** update if weve moved */ + if (d_total > 0) { + // Save current values for next time around + load.last_idle = c_idle; + load.last_total = c_total; + load.last_ratio = (double) (d_total - d_idle) / d_total; + } + + return load.last_ratio; +} + +static int get_boot_time(uint64_t* time) { + assert(time != NULL, "time pointer is NULL!"); + setutxent(); + for(;;) { + struct utmpx* u; + if ((u = getutxent()) == NULL) { + break; + } + if (u->ut_type == BOOT_TIME) { + *time = u->ut_xtime; + endutxent(); + return OS_OK; + } + } + endutxent(); + return OS_ERR; +} + +static int get_noof_context_switches(CPUPerfCounters* counters, uint64_t* switches) { + assert(switches != NULL, "switches pointer is NULL!"); + assert(counters != NULL, "counter pointer is NULL!"); + *switches = 0; + uint64_t s = 0; + + // Collect data from all CPUs + for (int i = 0; i < counters->nProcs; i++) { + cpu_stat_t cpu_stat = {0}; + CPUPerfTicks load = counters->jvmTicks[i]; + + if (read_cpustat(counters->kstat_ctrl, &load, &cpu_stat) == OS_OK) { + s += cpu_stat.cpu_sysinfo.pswitch; + } else { + //fail fast... + return OS_ERR; + } + } + *switches = s; + return OS_OK; +} + +static int perf_context_switch_rate(CPUPerfCounters* counters, double* rate) { + assert(counters != NULL, "counters is NULL!"); + assert(rate != NULL, "rate pointer is NULL!"); + static pthread_mutex_t contextSwitchLock = PTHREAD_MUTEX_INITIALIZER; + static uint64_t lastTime = 0; + static uint64_t lastSwitches = 0; + static double lastRate = 0.0; + + uint64_t lt = 0; + int res = 0; + + if (lastTime == 0) { + uint64_t tmp; + if (get_boot_time(&tmp) < 0) { + return OS_ERR; + } + lt = tmp * 1000; + } + + res = OS_OK; + + pthread_mutex_lock(&contextSwitchLock); + { + + uint64_t sw = 0; + clock_t t, d; + + if (lastTime == 0) { + lastTime = lt; + } + + t = clock(); + d = t - lastTime; + + if (d == 0) { + *rate = lastRate; + } else if (get_noof_context_switches(counters, &sw)== OS_OK) { + *rate = ((double)(sw - lastSwitches) / d) * 1000; + lastRate = *rate; + lastSwitches = sw; + lastTime = t; + } else { + *rate = 0.0; + res = OS_ERR; + } + if (*rate < 0.0) { + *rate = 0.0; + lastRate = 0.0; + } + } + pthread_mutex_unlock(&contextSwitchLock); + return res; + } + + + +class CPUPerformanceInterface::CPUPerformance : public CHeapObj { + friend class CPUPerformanceInterface; + private: + CPUPerfCounters _counters; + int cpu_load(int which_logical_cpu, double* cpu_load); + int context_switch_rate(double* rate); + int cpu_load_total_process(double* cpu_load); + int cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad); + + CPUPerformance(); + ~CPUPerformance(); + bool initialize(); +}; + +CPUPerformanceInterface::CPUPerformance::CPUPerformance() { + _counters.nProcs = 0; + _counters.jvmTicks = NULL; + _counters.kstat_ctrl = NULL; +} + +bool CPUPerformanceInterface::CPUPerformance::initialize() { + // initialize kstat control structure, + _counters.kstat_ctrl = kstat_open(); + assert(_counters.kstat_ctrl != NULL, "error initializing kstat control structure!"); + + if (NULL == _counters.kstat_ctrl) { + return false; + } + + // Get number of CPU(s) + if ((_counters.nProcs = sysconf(_SC_NPROCESSORS_ONLN)) == OS_ERR) { + // ignore error? + _counters.nProcs = 1; + } + + assert(_counters.nProcs > 0, "no CPUs detected in sysconf call!"); + if (_counters.nProcs == 0) { + return false; + } + + // Data structure(s) for saving CPU load (one per CPU) + size_t tick_array_size = _counters.nProcs * sizeof(CPUPerfTicks); + _counters.jvmTicks = (CPUPerfTicks*)NEW_C_HEAP_ARRAY(char, tick_array_size, mtInternal); + if (NULL == _counters.jvmTicks) { + return false; + } + memset(_counters.jvmTicks, 0, tick_array_size); + + // Get kstat cpu_stat counters for every CPU + // loop over kstat to find our cpu_stat(s) + int i = 0; + for (kstat_t* kstat = _counters.kstat_ctrl->kc_chain; kstat != NULL; kstat = kstat->ks_next) { + if (strncmp(kstat->ks_module, "cpu_stat", 8) == 0) { + if (kstat_read(_counters.kstat_ctrl, kstat, NULL) == OS_ERR) { + continue; + } + if (i == _counters.nProcs) { + // more cpu_stats than reported CPUs + break; + } + _counters.jvmTicks[i++].kstat = kstat; + } + } + return true; +} + +CPUPerformanceInterface::CPUPerformance::~CPUPerformance() { + if (_counters.jvmTicks != NULL) { + FREE_C_HEAP_ARRAY(char, _counters.jvmTicks, mtInternal); + } + if (_counters.kstat_ctrl != NULL) { + kstat_close(_counters.kstat_ctrl); + } +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load(int which_logical_cpu, double* cpu_load) { + assert(cpu_load != NULL, "cpu_load pointer is NULL!"); + double t = .0; + if (-1 == which_logical_cpu) { + for (int i = 0; i < _counters.nProcs; i++) { + t += get_cpu_load(i, &_counters); + } + // Cap total systemload to 1.0 + t = MIN2((t / _counters.nProcs), 1.0); + } else { + t = MIN2(get_cpu_load(which_logical_cpu, &_counters), 1.0); + } + + *cpu_load = t; + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) { + assert(cpu_load != NULL, "cpu_load pointer is NULL!"); + + psinfo_t info; + + // Get the percentage of "recent cpu usage" from all the lwp:s in the JVM:s + // process. This is returned as a value between 0.0 and 1.0 multiplied by 0x8000. + if (get_psinfo2(&info.pr_pctcpu, sizeof(info.pr_pctcpu), offsetof(psinfo_t, pr_pctcpu)) != 0) { + *cpu_load = 0.0; + return OS_ERR; + } + *cpu_load = (double) info.pr_pctcpu / 0x8000; + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) { + assert(pjvmUserLoad != NULL, "pjvmUserLoad not inited"); + assert(pjvmKernelLoad != NULL, "pjvmKernelLoad not inited"); + assert(psystemTotalLoad != NULL, "psystemTotalLoad not inited"); + + static uint64_t lastTime; + static uint64_t lastUser, lastKernel; + static double lastUserRes, lastKernelRes; + + pstatus_t pss; + psinfo_t info; + + *pjvmKernelLoad = *pjvmUserLoad = *psystemTotalLoad = 0; + if (get_info("/proc/self/status", &pss.pr_utime, sizeof(timestruc_t)*2, offsetof(pstatus_t, pr_utime)) != 0) { + return OS_ERR; + } + + if (get_psinfo(&info) != 0) { + return OS_ERR; + } + + // get the total time in user, kernel and total time + // check ratios for 'lately' and multiply the 'recent load'. + uint64_t time = (info.pr_time.tv_sec * NANOS_PER_SEC) + info.pr_time.tv_nsec; + uint64_t user = (pss.pr_utime.tv_sec * NANOS_PER_SEC) + pss.pr_utime.tv_nsec; + uint64_t kernel = (pss.pr_stime.tv_sec * NANOS_PER_SEC) + pss.pr_stime.tv_nsec; + uint64_t diff = time - lastTime; + double load = (double) info.pr_pctcpu / 0x8000; + + if (diff > 0) { + lastUserRes = (load * (user - lastUser)) / diff; + lastKernelRes = (load * (kernel - lastKernel)) / diff; + + // BUG9182835 - patch for clamping these values to sane ones. + lastUserRes = MIN2(1, lastUserRes); + lastUserRes = MAX2(0, lastUserRes); + lastKernelRes = MIN2(1, lastKernelRes); + lastKernelRes = MAX2(0, lastKernelRes); + } + + double t = .0; + cpu_load(-1, &t); + // clamp at user+system and 1.0 + if (lastUserRes + lastKernelRes > t) { + t = MIN2(lastUserRes + lastKernelRes, 1.0); + } + + *pjvmUserLoad = lastUserRes; + *pjvmKernelLoad = lastKernelRes; + *psystemTotalLoad = t; + + lastTime = time; + lastUser = user; + lastKernel = kernel; + + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) { + return perf_context_switch_rate(&_counters, rate); +} + +CPUPerformanceInterface::CPUPerformanceInterface() { + _impl = NULL; +} + +bool CPUPerformanceInterface::initialize() { + _impl = new CPUPerformanceInterface::CPUPerformance(); + return _impl != NULL && _impl->initialize(); +} + +CPUPerformanceInterface::~CPUPerformanceInterface(void) { + if (_impl != NULL) { + delete _impl; + } +} + +int CPUPerformanceInterface::cpu_load(int which_logical_cpu, double* const cpu_load) const { + return _impl->cpu_load(which_logical_cpu, cpu_load); +} + +int CPUPerformanceInterface::cpu_load_total_process(double* const cpu_load) const { + return _impl->cpu_load_total_process(cpu_load); +} + +int CPUPerformanceInterface::cpu_loads_process(double* const pjvmUserLoad, double* const pjvmKernelLoad, double* const psystemTotalLoad) const { + return _impl->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotalLoad); +} + +int CPUPerformanceInterface::context_switch_rate(double* const rate) const { + return _impl->context_switch_rate(rate); +} + +class SystemProcessInterface::SystemProcesses : public CHeapObj { + friend class SystemProcessInterface; + private: + class ProcessIterator : public CHeapObj { + friend class SystemProcessInterface::SystemProcesses; + private: + DIR* _dir; + struct dirent* _entry; + bool _valid; + + ProcessIterator(); + ~ProcessIterator(); + bool initialize(); + + bool is_valid() const { return _valid; } + bool is_valid_entry(struct dirent* const entry) const; + bool is_dir(const char* const name) const; + char* allocate_string(const char* const str) const; + int current(SystemProcess* const process_info); + int next_process(); + }; + + ProcessIterator* _iterator; + SystemProcesses(); + bool initialize(); + ~SystemProcesses(); + + //information about system processes + int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const; +}; + +bool SystemProcessInterface::SystemProcesses::ProcessIterator::is_dir(const char* name) const { + struct stat64 mystat; + int ret_val = 0; + + ret_val = ::stat64(name, &mystat); + + if (ret_val < 0) { + return false; + } + ret_val = S_ISDIR(mystat.st_mode); + return ret_val > 0; +} + +// if it has a numeric name, is a directory and has a 'psinfo' file in it +bool SystemProcessInterface::SystemProcesses::ProcessIterator::is_valid_entry(struct dirent* entry) const { + // ignore the "." and ".." directories + if ((strcmp(entry->d_name, ".") == 0) || + (strcmp(entry->d_name, "..") == 0)) { + return false; + } + + char buffer[PATH_MAX] = {0}; + uint64_t size = 0; + bool result = false; + FILE *fp = NULL; + + if (atoi(entry->d_name) != 0) { + jio_snprintf(buffer, PATH_MAX, "/proc/%s", entry->d_name); + + if (is_dir(buffer)) { + memset(buffer, 0, PATH_MAX); + jio_snprintf(buffer, PATH_MAX, "/proc/%s/psinfo", entry->d_name); + if ((fp = fopen(buffer, "r")) != NULL) { + int nread = 0; + psinfo_t psinfo_data; + if ((nread = fread(&psinfo_data, 1, sizeof(psinfo_t), fp)) != -1) { + // only considering system process owned by root + if (psinfo_data.pr_uid == 0) { + result = true; + } + } + } + } + } + + if (fp != NULL) { + fclose(fp); + } + + return result; +} + +char* SystemProcessInterface::SystemProcesses::ProcessIterator::allocate_string(const char* str) const { + if (str != NULL) { + size_t len = strlen(str); + char* tmp = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); + strncpy(tmp, str, len); + tmp[len] = '\0'; + return tmp; + } + return NULL; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::current(SystemProcess* process_info) { + if (!is_valid()) { + return OS_ERR; + } + + char psinfo_path[PATH_MAX] = {0}; + jio_snprintf(psinfo_path, PATH_MAX, "/proc/%s/psinfo", _entry->d_name); + + FILE *fp = NULL; + if ((fp = fopen(psinfo_path, "r")) == NULL) { + return OS_ERR; + } + + int nread = 0; + psinfo_t psinfo_data; + if ((nread = fread(&psinfo_data, 1, sizeof(psinfo_t), fp)) == -1) { + fclose(fp); + return OS_ERR; + } + + char *exe_path = NULL; + if ((psinfo_data.pr_fname != NULL) && + (psinfo_data.pr_psargs != NULL)) { + char *path_substring = strstr(psinfo_data.pr_psargs, psinfo_data.pr_fname); + if (path_substring != NULL) { + int len = path_substring - psinfo_data.pr_psargs; + exe_path = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); + if (exe_path != NULL) { + jio_snprintf(exe_path, len, "%s", psinfo_data.pr_psargs); + exe_path[len] = '\0'; + } + } + } + + process_info->set_pid(atoi(_entry->d_name)); + process_info->set_name(allocate_string(psinfo_data.pr_fname)); + process_info->set_path(allocate_string(exe_path)); + process_info->set_command_line(allocate_string(psinfo_data.pr_psargs)); + + if (exe_path != NULL) { + FREE_C_HEAP_ARRAY(char, exe_path, mtInternal); + } + + if (fp != NULL) { + fclose(fp); + } + + return OS_OK; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::next_process() { + if (!is_valid()) { + return OS_ERR; + } + + do { + _entry = os::readdir(_dir); + if (_entry == NULL) { + // Error or reached end. Could use errno to distinguish those cases. + _valid = false; + return OS_ERR; + } + } while(!is_valid_entry(_entry)); + + _valid = true; + return OS_OK; +} + +SystemProcessInterface::SystemProcesses::ProcessIterator::ProcessIterator() { + _dir = NULL; + _entry = NULL; + _valid = false; +} + +bool SystemProcessInterface::SystemProcesses::ProcessIterator::initialize() { + _dir = os::opendir("/proc"); + _entry = NULL; + _valid = true; + next_process(); + + return true; +} + +SystemProcessInterface::SystemProcesses::ProcessIterator::~ProcessIterator() { + if (_dir != NULL) { + os::closedir(_dir); + } +} + +SystemProcessInterface::SystemProcesses::SystemProcesses() { + _iterator = NULL; +} + +bool SystemProcessInterface::SystemProcesses::initialize() { + _iterator = new SystemProcessInterface::SystemProcesses::ProcessIterator(); + return _iterator != NULL && _iterator->initialize(); +} + +SystemProcessInterface::SystemProcesses::~SystemProcesses() { + if (_iterator != NULL) { + delete _iterator; + } +} + +int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const { + assert(system_processes != NULL, "system_processes pointer is NULL!"); + assert(no_of_sys_processes != NULL, "system_processes counter pointer is NULL!"); + assert(_iterator != NULL, "iterator is NULL!"); + + // initialize pointers + *no_of_sys_processes = 0; + *system_processes = NULL; + + while (_iterator->is_valid()) { + SystemProcess* tmp = new SystemProcess(); + _iterator->current(tmp); + + //if already existing head + if (*system_processes != NULL) { + //move "first to second" + tmp->set_next(*system_processes); + } + // new head + *system_processes = tmp; + // increment + (*no_of_sys_processes)++; + // step forward + _iterator->next_process(); + } + return OS_OK; +} + +int SystemProcessInterface::system_processes(SystemProcess** system_procs, int* const no_of_sys_processes) const { + return _impl->system_processes(system_procs, no_of_sys_processes); +} + +SystemProcessInterface::SystemProcessInterface() { + _impl = NULL; +} + +bool SystemProcessInterface::initialize() { + _impl = new SystemProcessInterface::SystemProcesses(); + return _impl != NULL && _impl->initialize(); + +} + +SystemProcessInterface::~SystemProcessInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +CPUInformationInterface::CPUInformationInterface() { + _cpu_info = NULL; +} + +bool CPUInformationInterface::initialize() { + _cpu_info = new CPUInformation(); + if (_cpu_info == NULL) { + return false; + } + _cpu_info->set_number_of_hardware_threads(VM_Version_Ext::number_of_threads()); + _cpu_info->set_number_of_cores(VM_Version_Ext::number_of_cores()); + _cpu_info->set_number_of_sockets(VM_Version_Ext::number_of_sockets()); + _cpu_info->set_cpu_name(VM_Version_Ext::cpu_name()); + _cpu_info->set_cpu_description(VM_Version_Ext::cpu_description()); + return true; +} + +CPUInformationInterface::~CPUInformationInterface() { + if (_cpu_info != NULL) { + if (_cpu_info->cpu_name() != NULL) { + const char* cpu_name = _cpu_info->cpu_name(); + FREE_C_HEAP_ARRAY(char, cpu_name, mtInternal); + _cpu_info->set_cpu_name(NULL); + } + if (_cpu_info->cpu_description() != NULL) { + const char* cpu_desc = _cpu_info->cpu_description(); + FREE_C_HEAP_ARRAY(char, cpu_desc, mtInternal); + _cpu_info->set_cpu_description(NULL); + } + delete _cpu_info; + } +} + +int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) { + if (_cpu_info == NULL) { + return OS_ERR; + } + + cpu_info = *_cpu_info; // shallow copy assignment + return OS_OK; +} + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() { + +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + return true; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { + +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const +{ + kstat_ctl_t* ctl = kstat_open(); + if (ctl == NULL) { + return OS_ERR; + } + + NetworkInterface* ret = NULL; + for (kstat_t* k = ctl->kc_chain; k != NULL; k = k->ks_next) { + if (strcmp(k->ks_class, "net") != 0) { + continue; + } + if (strcmp(k->ks_module, "link") != 0) { + continue; + } + + if (kstat_read(ctl, k, NULL) == -1) { + return OS_ERR; + } + + uint64_t bytes_in = UINT64_MAX; + uint64_t bytes_out = UINT64_MAX; + for (int i = 0; i < k->ks_ndata; ++i) { + kstat_named_t* data = &reinterpret_cast(k->ks_data)[i]; + if (strcmp(data->name, "rbytes64") == 0) { + bytes_in = data->value.ui64; + } + else if (strcmp(data->name, "obytes64") == 0) { + bytes_out = data->value.ui64; + } + } + + if ((bytes_in != UINT64_MAX) && (bytes_out != UINT64_MAX)) { + NetworkInterface* cur = new NetworkInterface(k->ks_name, bytes_in, bytes_out, ret); + ret = cur; + } + } + + kstat_close(ctl); + *network_interfaces = ret; + + return OS_OK; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff --git a/src/os/solaris/vm/os_solaris.cpp b/src/os/solaris/vm/os_solaris.cpp index 6956f63d95c249e8ae4d8fce1e04cfdaf7cd4ee7..9c9de85ad1cc567a7903cd8b6f3a4442a71c70d3 100644 --- a/src/os/solaris/vm/os_solaris.cpp +++ b/src/os/solaris/vm/os_solaris.cpp @@ -1817,6 +1817,43 @@ void os::print_dll_info(outputStream * st) { dlclose(handle); } +int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *param) { + Dl_info dli; + // Sanity check? + if (dladdr(CAST_FROM_FN_PTR(void *, os::get_loaded_modules_info), &dli) == 0 || + dli.dli_fname == NULL) { + return 1; + } + + void * handle = dlopen(dli.dli_fname, RTLD_LAZY); + if (handle == NULL) { + return 1; + } + + Link_map *map; + dlinfo(handle, RTLD_DI_LINKMAP, &map); + if (map == NULL) { + dlclose(handle); + return 1; + } + + while (map->l_prev != NULL) { + map = map->l_prev; + } + + while (map != NULL) { + // Iterate through all map entries and call callback with fields of interest + if(callback(map->l_name, (address)map->l_addr, (address)0, param)) { + dlclose(handle); + return 1; + } + map = map->l_next; + } + + dlclose(handle); + return 0; +} + // Loads .dll/.so and // in case of error it checks if .dll/.so was built for the // same architecture as Hotspot is running on @@ -3259,6 +3296,15 @@ size_t os::read(int fd, void *buf, unsigned int nBytes) { INTERRUPTIBLE_RETURN_INT_VM(::read(fd, buf, nBytes), os::Solaris::clear_interrupted); } +size_t os::read_at(int fd, void *buf, unsigned int nBytes, jlong offset) { + size_t res; + JavaThread* thread = (JavaThread*)Thread::current(); + assert(thread->thread_state() == _thread_in_vm, "Assumed _thread_in_vm"); + ThreadBlockInVM tbiv(thread); + RESTARTABLE(::pread(fd, buf, (size_t) nBytes, offset), res); + return res; +} + size_t os::restartable_read(int fd, void *buf, unsigned int nBytes) { INTERRUPTIBLE_RETURN_INT(::read(fd, buf, nBytes), os::Solaris::clear_interrupted); } diff --git a/src/os/windows/vm/iphlp_interface.cpp b/src/os/windows/vm/iphlp_interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6dba804fca333e4c964254bb48f7efce4907ad19 --- /dev/null +++ b/src/os/windows/vm/iphlp_interface.cpp @@ -0,0 +1,104 @@ +/* + * 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. + * + */ + +#include "iphlp_interface.hpp" +#include "runtime/os.hpp" + +// IPHLP API +typedef DWORD(WINAPI *GetIfTable2_Fn)(PMIB_IF_TABLE2*); +typedef DWORD(WINAPI *FreeMibTable_Fn)(PVOID); + +// IPHLP statics +GetIfTable2_Fn IphlpDll::_GetIfTable2 = NULL; +FreeMibTable_Fn IphlpDll::_FreeMibTable = NULL; + +LONG IphlpDll::_critical_section = 0; +LONG IphlpDll::_initialized = 0; +LONG IphlpDll::_iphlp_reference_count = 0; +HMODULE IphlpDll::_hModule = NULL; + +void IphlpDll::initialize(void) { + _hModule = os::win32::load_Windows_dll("iphlpapi.dll", NULL, 0); + + if (NULL == _hModule) { + return; + } + + // The 'A' at the end means the ANSI (not the UNICODE) vesions of the methods + _GetIfTable2 = (GetIfTable2_Fn)::GetProcAddress(_hModule, "GetIfTable2"); + _FreeMibTable = (FreeMibTable_Fn)::GetProcAddress(_hModule, "FreeMibTable"); + + // interlock is used for fencing + InterlockedExchange(&_initialized, 1); +} + +bool IphlpDll::IphlpDetach(void) { + LONG prev_ref_count = InterlockedExchangeAdd(&_iphlp_reference_count, -1); + BOOL ret = false; + + if (1 == prev_ref_count) { + if (_initialized && _hModule != NULL) { + ret = FreeLibrary(_hModule); + if (ret) { + _hModule = NULL; + _GetIfTable2 = NULL; + _FreeMibTable = NULL; + InterlockedExchange(&_initialized, 0); + } + } + } + return ret != 0; +} + +bool IphlpDll::IphlpAttach(void) { + InterlockedExchangeAdd(&_iphlp_reference_count, 1); + + if (1 == _initialized) { + return true; + } + + while (InterlockedCompareExchange(&_critical_section, 1, 0) == 1); + + if (0 == _initialized) { + initialize(); + } + + while (InterlockedCompareExchange(&_critical_section, 0, 1) == 0); + + return (_GetIfTable2 != NULL && _FreeMibTable != NULL); +} + +DWORD IphlpDll::GetIfTable2(PMIB_IF_TABLE2* Table) { + assert(_initialized && _GetIfTable2 != NULL, + "IphlpAttach() not yet called"); + + return _GetIfTable2(Table); +} + +DWORD IphlpDll::FreeMibTable(PVOID Memory) { + assert(_initialized && _FreeMibTable != NULL, + "IphlpAttach() not yet called"); + + return _FreeMibTable(Memory); +} diff --git a/src/os/windows/vm/iphlp_interface.hpp b/src/os/windows/vm/iphlp_interface.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2a3a1bdc9c81a10690b40d7ec167e509faefa384 --- /dev/null +++ b/src/os/windows/vm/iphlp_interface.hpp @@ -0,0 +1,51 @@ +/* + * 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_WINDOWS_VM_IPHLP_INTERFACE_HPP +#define OS_WINDOWS_VM_IPHLP_INTERFACE_HPP + +#include +#include "memory/allocation.hpp" +#include "utilities/macros.hpp" +#include +#include + +class IphlpDll : public AllStatic { + private: + static LONG _iphlp_reference_count; + static LONG _critical_section; + static LONG _initialized; + static HMODULE _hModule; + static void initialize(void); + static DWORD(WINAPI *_GetIfTable2)(PMIB_IF_TABLE2*); + static DWORD(WINAPI *_FreeMibTable)(PVOID); + + public: + static DWORD GetIfTable2(PMIB_IF_TABLE2*); + static DWORD FreeMibTable(PVOID); + static bool IphlpAttach(void); + static bool IphlpDetach(void); +}; + +#endif // OS_WINDOWS_VM_IPHLP_INTERFACE_HPP diff --git a/src/os/windows/vm/os_perf_windows.cpp b/src/os/windows/vm/os_perf_windows.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4a2581181de1c42b88c73582ccd023c919973353 --- /dev/null +++ b/src/os/windows/vm/os_perf_windows.cpp @@ -0,0 +1,1456 @@ +/* + * Copyright (c) 2012, 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 "iphlp_interface.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" +#include "pdh_interface.hpp" +#include "runtime/os_perf.hpp" +#include "runtime/os.hpp" +#include "utilities/macros.hpp" +#include "vm_version_ext_x86.hpp" +#include +#include +#include + +/* + * Windows provides a vast plethora of performance objects and counters, + * consumption of which is assisted using the Performance Data Helper (PDH) interface. + * We import a selected few api entry points from PDH, see pdh_interface.hpp. + * + * The code located in this file is to a large extent an abstraction over much of the + * plumbing needed to start consuming an object and/or counter of choice. + * + */ + + /* + * How to use: + * 1. Create query + * 2. Add counters to the query + * 3. Collect the performance data using the query + * 4. Display the performance data using the counters associated with the query + * 5. Destroy query (counter destruction implied) + */ + +/* + * Every PDH artifact, like processor, process, thread, memory, and so forth are + * identified with an index that is always the same irrespective + * of the localized version of the operating system or service pack installed. + * INFO: Using PDH APIs Correctly in a Localized Language (Q287159) + * http://support.microsoft.com/default.aspx?scid=kb;EN-US;q287159 + * + * To find the correct index for an object or counter, inspect the registry key / value: + * [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009\Counter] + * + * some common PDH indexes + */ +static const DWORD PDH_PROCESSOR_IDX = 238; +static const DWORD PDH_PROCESSOR_TIME_IDX = 6; +static const DWORD PDH_PRIV_PROCESSOR_TIME_IDX = 144; +static const DWORD PDH_PROCESS_IDX = 230; +static const DWORD PDH_ID_PROCESS_IDX = 784; +static const DWORD PDH_CONTEXT_SWITCH_RATE_IDX = 146; +static const DWORD PDH_SYSTEM_IDX = 2; + +/* useful pdh fmt's */ +static const char* const OBJECT_COUNTER_FMT = "\\%s\\%s"; +static const size_t OBJECT_COUNTER_FMT_LEN = 2; +static const char* const OBJECT_WITH_INSTANCES_COUNTER_FMT = "\\%s(%s)\\%s"; +static const size_t OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN = 4; +static const char* const PROCESS_OBJECT_INSTANCE_COUNTER_FMT = "\\%s(%s#%s)\\%s"; +static const size_t PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN = 5; + +static const char* process_image_name = NULL; // for example, "java" but could have another image name +static char* pdh_IDProcess_counter_fmt = NULL; // "\Process(java#%d)\ID Process" */ + +// Need to limit how often we update a query to minimize the heisenberg effect. +// (PDH behaves erratically if the counters are queried too often, especially counters that +// store and use values from two consecutive updates, like cpu load.) +static const int min_update_interval_millis = 500; + +/* +* Structs for PDH queries. +*/ +typedef struct { + HQUERY query; + s8 lastUpdate; // Last time query was updated (current millis). +} UpdateQueryS, *UpdateQueryP; + + +typedef struct { + UpdateQueryS query; + HCOUNTER counter; + bool initialized; +} CounterQueryS, *CounterQueryP; + +typedef struct { + UpdateQueryS query; + HCOUNTER* counters; + int noOfCounters; + bool initialized; +} MultiCounterQueryS, *MultiCounterQueryP; + +typedef struct { + MultiCounterQueryP queries; + int size; + bool initialized; +} MultiCounterQuerySetS, *MultiCounterQuerySetP; + +typedef struct { + MultiCounterQuerySetS set; + int process_index; +} ProcessQueryS, *ProcessQueryP; + +static void pdh_cleanup(HQUERY* const query, HCOUNTER* const counter) { + if (counter != NULL && *counter != NULL) { + PdhDll::PdhRemoveCounter(*counter); + *counter = NULL; + } + if (query != NULL && *query != NULL) { + PdhDll::PdhCloseQuery(*query); + *query = NULL; + } +} + +static CounterQueryP create_counter_query() { + CounterQueryP const query = NEW_C_HEAP_ARRAY(CounterQueryS, 1, mtInternal); + memset(query, 0, sizeof(CounterQueryS)); + return query; +} + +static void destroy_counter_query(CounterQueryP query) { + assert(query != NULL, "invariant"); + pdh_cleanup(&query->query.query, &query->counter); + FREE_C_HEAP_ARRAY(CounterQueryS, query, mtInternal); +} + +static MultiCounterQueryP create_multi_counter_query() { + MultiCounterQueryP const query = NEW_C_HEAP_ARRAY(MultiCounterQueryS, 1, mtInternal); + memset(query, 0, sizeof(MultiCounterQueryS)); + return query; +} + +static void destroy_counter_query(MultiCounterQueryP counter_query) { + if (counter_query != NULL) { + for (int i = 0; i < counter_query->noOfCounters; ++i) { + pdh_cleanup(NULL, &counter_query->counters[i]); + } + FREE_C_HEAP_ARRAY(char, counter_query->counters, mtInternal); + pdh_cleanup(&counter_query->query.query, NULL); + FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query, mtInternal); + } +} + +static void destroy_multi_counter_query(MultiCounterQuerySetP counter_query_set) { + for (int i = 0; i < counter_query_set->size; i++) { + for (int j = 0; j < counter_query_set->queries[i].noOfCounters; ++j) { + pdh_cleanup(NULL, &counter_query_set->queries[i].counters[j]); + } + FREE_C_HEAP_ARRAY(char, counter_query_set->queries[i].counters, mtInternal); + pdh_cleanup(&counter_query_set->queries[i].query.query, NULL); + } + FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query_set->queries, mtInternal); +} + +static void destroy_counter_query(MultiCounterQuerySetP counter_query_set) { + destroy_multi_counter_query(counter_query_set); + FREE_C_HEAP_ARRAY(MultiCounterQuerySetS, counter_query_set, mtInternal); +} + +static void destroy_counter_query(ProcessQueryP process_query) { + destroy_multi_counter_query(&process_query->set); + FREE_C_HEAP_ARRAY(ProcessQueryS, process_query, mtInternal); +} + +static int open_query(HQUERY* query) { + return PdhDll::PdhOpenQuery(NULL, 0, query); +} + +template +static int open_query(QueryP query) { + return open_query(&query->query); +} + +static int allocate_counters(MultiCounterQueryP query, size_t nofCounters) { + assert(query != NULL, "invariant"); + assert(!query->initialized, "invariant"); + assert(0 == query->noOfCounters, "invariant"); + assert(query->counters == NULL, "invariant"); + query->counters = (HCOUNTER*)NEW_C_HEAP_ARRAY(char, nofCounters * sizeof(HCOUNTER), mtInternal); + if (query->counters == NULL) { + return OS_ERR; + } + memset(query->counters, 0, nofCounters * sizeof(HCOUNTER)); + query->noOfCounters = (int)nofCounters; + return OS_OK; +} + +static int allocate_counters(MultiCounterQuerySetP query_set, size_t nofCounters) { + assert(query_set != NULL, "invariant"); + assert(!query_set->initialized, "invariant"); + for (int i = 0; i < query_set->size; ++i) { + if (allocate_counters(&query_set->queries[i], nofCounters) != OS_OK) { + return OS_ERR; + } + } + return OS_OK; +} + +static int allocate_counters(ProcessQueryP process_query, size_t nofCounters) { + assert(process_query != NULL, "invariant"); + return allocate_counters(&process_query->set, nofCounters); +} + +static void deallocate_counters(MultiCounterQueryP query) { + if (query->counters != NULL) { + FREE_C_HEAP_ARRAY(char, query->counters, mtInternal); + query->counters = NULL; + query->noOfCounters = 0; + } +} + +static OSReturn add_counter(UpdateQueryP query, HCOUNTER* counter, const char* path, bool first_sample_on_init) { + assert(query != NULL, "invariant"); + assert(counter != NULL, "invariant"); + assert(path != NULL, "invariant"); + if (query->query == NULL) { + if (open_query(query) != ERROR_SUCCESS) { + return OS_ERR; + } + } + assert(query->query != NULL, "invariant"); + PDH_STATUS status = PdhDll::PdhAddCounter(query->query, path, 0, counter); + if (PDH_CSTATUS_NO_OBJECT == status || PDH_CSTATUS_NO_COUNTER == status) { + return OS_ERR; + } + /* + * According to the MSDN documentation, rate counters must be read twice: + * + * "Obtaining the value of rate counters such as Page faults/sec requires that + * PdhCollectQueryData be called twice, with a specific time interval between + * the two calls, before calling PdhGetFormattedCounterValue. Call Sleep to + * implement the waiting period between the two calls to PdhCollectQueryData." + * + * Take the first sample here already to allow for the next "real" sample + * to succeed. + */ + if (first_sample_on_init) { + PdhDll::PdhCollectQueryData(query->query); + } + return OS_OK; +} + +template +static OSReturn add_counter(QueryP counter_query, HCOUNTER* counter, const char* path, bool first_sample_on_init) { + assert(counter_query != NULL, "invariant"); + assert(counter != NULL, "invariant"); + assert(path != NULL, "invariant"); + return add_counter(&counter_query->query, counter, path, first_sample_on_init); +} + +static OSReturn add_counter(CounterQueryP counter_query, const char* path, bool first_sample_on_init) { + if (add_counter(counter_query, &counter_query->counter, path, first_sample_on_init) != OS_OK) { + // performance counter might be disabled in the registry + return OS_ERR; + } + counter_query->initialized = true; + return OS_OK; +} + +static OSReturn add_process_counter(MultiCounterQueryP query, int slot_index, const char* path, bool first_sample_on_init) { + assert(query != NULL, "invariant"); + assert(slot_index < query->noOfCounters, "invariant"); + assert(query->counters[slot_index] == NULL, "invariant"); + const OSReturn ret = add_counter(query, &query->counters[slot_index], path, first_sample_on_init); + if (OS_OK == ret) { + if (slot_index + 1 == query->noOfCounters) { + query->initialized = true; + } + } + return ret; +} + +static int collect_query_data(UpdateQueryP update_query) { + assert(update_query != NULL, "invariant"); + const s8 now = os::javaTimeMillis(); + if (now - update_query->lastUpdate > min_update_interval_millis) { + if (PdhDll::PdhCollectQueryData(update_query->query) != ERROR_SUCCESS) { + return OS_ERR; + } + update_query->lastUpdate = now; + } + return OS_OK; +} + +template +static int collect_query_data(Query* counter_query) { + assert(counter_query != NULL, "invariant"); + return collect_query_data(&counter_query->query); +} + +static int formatted_counter_value(HCOUNTER counter, DWORD format, PDH_FMT_COUNTERVALUE* const value) { + assert(value != NULL, "invariant"); + if (PdhDll::PdhGetFormattedCounterValue(counter, format, NULL, value) != ERROR_SUCCESS) { + return OS_ERR; + } + return OS_OK; +} + +/* +* Working against the Process object and it's related counters is inherently problematic +* when using the PDH API: +* +* Using PDH, a process is not primarily identified by the process id, +* but with a sequential number, for example \Process(java#0), \Process(java#1), ... +* The really bad part is that this list is reset as soon as a process exits: +* If \Process(java#1) exits, \Process(java#3) now becomes \Process(java#2) etc. +* +* The PDH api requires a process identifier to be submitted when registering +* a query, but as soon as the list resets, the query is invalidated (since the name changed). +* +* Solution: +* The #number identifier for a Process query can only decrease after process creation. +* +* We therefore create an array of counter queries for all process object instances +* up to and including ourselves: +* +* Ex. we come in as third process instance (java#2), we then create and register +* queries for the following Process object instances: +* java#0, java#1, java#2 +* +* current_query_index_for_process() keeps track of the current "correct" query +* (in order to keep this index valid when the list resets from underneath, +* ensure to call current_query_index_for_process() before every query involving +* Process object instance data). +* +* if unable to query, returns OS_ERR(-1) +*/ +static int current_query_index_for_process() { + assert(process_image_name != NULL, "invariant"); + assert(pdh_IDProcess_counter_fmt != NULL, "invariant"); + HQUERY tmpQuery = NULL; + if (open_query(&tmpQuery) != ERROR_SUCCESS) { + return OS_ERR; + } + char counter[512]; + HCOUNTER handle_counter = NULL; + // iterate over all instance indexes and try to find our own pid + for (int index = 0; index < max_intx; index++) { + jio_snprintf(counter, sizeof(counter) - 1, pdh_IDProcess_counter_fmt, index); + assert(strlen(counter) < sizeof(counter), "invariant"); + if (PdhDll::PdhAddCounter(tmpQuery, counter, 0, &handle_counter) != ERROR_SUCCESS) { + pdh_cleanup(&tmpQuery, &handle_counter); + return OS_ERR; + } + const PDH_STATUS res = PdhDll::PdhCollectQueryData(tmpQuery); + if (res == PDH_INVALID_HANDLE || res == PDH_NO_DATA) { + pdh_cleanup(&tmpQuery, &handle_counter); + return OS_ERR; + } else { + PDH_FMT_COUNTERVALUE counter_value; + formatted_counter_value(handle_counter, PDH_FMT_LONG, &counter_value); + pdh_cleanup(NULL, &handle_counter); + if ((LONG)os::current_process_id() == counter_value.longValue) { + pdh_cleanup(&tmpQuery, NULL); + return index; + } + } + } + pdh_cleanup(&tmpQuery, NULL); + return OS_ERR; +} + +static ProcessQueryP create_process_query() { + const int current_process_idx = current_query_index_for_process(); + if (OS_ERR == current_process_idx) { + return NULL; + } + ProcessQueryP const process_query = NEW_C_HEAP_ARRAY(ProcessQueryS, 1, mtInternal); + memset(process_query, 0, sizeof(ProcessQueryS)); + process_query->set.queries = NEW_C_HEAP_ARRAY(MultiCounterQueryS, current_process_idx + 1, mtInternal); + memset(process_query->set.queries, 0, sizeof(MultiCounterQueryS) * (current_process_idx + 1)); + process_query->process_index = current_process_idx; + process_query->set.size = current_process_idx + 1; + assert(process_query->set.size > process_query->process_index, "invariant"); + return process_query; +} + +static MultiCounterQueryP current_process_counter_query(ProcessQueryP process_query) { + assert(process_query != NULL, "invariant"); + assert(process_query->process_index < process_query->set.size, "invariant"); + return &process_query->set.queries[process_query->process_index]; +} + +static void clear_multi_counter(MultiCounterQueryP query) { + for (int i = 0; i < query->noOfCounters; ++i) { + pdh_cleanup(NULL, &query->counters[i]); + } + pdh_cleanup(&query->query.query, NULL); + query->initialized = false; +} + +static int ensure_valid_process_query_index(ProcessQueryP process_query) { + assert(process_query != NULL, "invariant"); + const int previous_process_idx = process_query->process_index; + if (previous_process_idx == 0) { + return previous_process_idx; + } + const int current_process_idx = current_query_index_for_process(); + if (current_process_idx == previous_process_idx || OS_ERR == current_process_idx || + current_process_idx >= process_query->set.size) { + return previous_process_idx; + } + + assert(current_process_idx >= 0 && current_process_idx < process_query->set.size, "out of bounds!"); + while (current_process_idx < process_query->set.size - 1) { + const int new_size = --process_query->set.size; + clear_multi_counter(&process_query->set.queries[new_size]); + } + assert(current_process_idx < process_query->set.size, "invariant"); + process_query->process_index = current_process_idx; + return current_process_idx; +} + +static MultiCounterQueryP current_process_query(ProcessQueryP process_query) { + assert(process_query != NULL, "invariant"); + const int current_process_idx = ensure_valid_process_query_index(process_query); + assert(current_process_idx == process_query->process_index, "invariant"); + assert(current_process_idx < process_query->set.size, "invariant"); + return &process_query->set.queries[current_process_idx]; +} + +static int collect_process_query_data(ProcessQueryP process_query) { + assert(process_query != NULL, "invariant"); + return collect_query_data(current_process_query(process_query)); +} + +static int query_process_counter(ProcessQueryP process_query, int slot_index, DWORD format, PDH_FMT_COUNTERVALUE* const value) { + MultiCounterQueryP const current_query = current_process_counter_query(process_query); + assert(current_query != NULL, "invariant"); + assert(slot_index < current_query->noOfCounters, "invariant"); + assert(current_query->counters[slot_index] != NULL, "invariant"); + return formatted_counter_value(current_query->counters[slot_index], format, value); +} + +/* + * Construct a fully qualified PDH path + * + * @param objectName a PDH Object string representation(required) + * @param counterName a PDH Counter string representation(required) + * @param imageName a process image name string, ex. "java" (opt) + * @param instance an instance string, ex. "0", "1", ... (opt) + * @return the fully qualified PDH path. + * + * Caller will need a ResourceMark. + * + * (PdhMakeCounterPath() seems buggy on concatenating instances, hence this function instead) + */ +static const char* make_fully_qualified_counter_path(const char* object_name, + const char* counter_name, + const char* image_name = NULL, + const char* instance = NULL) { + assert(object_name != NULL, "invariant"); + assert(counter_name != NULL, "invariant"); + size_t full_counter_path_len = strlen(object_name) + strlen(counter_name); + + char* full_counter_path; + size_t jio_snprintf_result = 0; + if (image_name) { + /* + * For paths using the "Process" Object. + * + * Examples: + * form: "\object_name(image_name#instance)\counter_name" + * actual: "\Process(java#2)\ID Process" + */ + full_counter_path_len += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN; + full_counter_path_len += strlen(image_name); + /* + * image_name must be passed together with an associated + * instance "number" ("0", "1", "2", ...). + * This is required in order to create valid "Process" Object paths. + * + * Examples: "\Process(java#0)", \Process(java#1"), ... + */ + assert(instance != NULL, "invariant"); + full_counter_path_len += strlen(instance); + full_counter_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, full_counter_path_len + 1); + if (full_counter_path == NULL) { + return NULL; + } + jio_snprintf_result = jio_snprintf(full_counter_path, + full_counter_path_len + 1, + PROCESS_OBJECT_INSTANCE_COUNTER_FMT, + object_name, + image_name, + instance, + counter_name); + } else { + if (instance) { + /* + * For paths where the Object has multiple instances. + * + * Examples: + * form: "\object_name(instance)\counter_name" + * actual: "\Processor(0)\% Privileged Time" + */ + full_counter_path_len += strlen(instance); + full_counter_path_len += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN; + } else { + /* + * For "normal" paths. + * + * Examples: + * form: "\object_name\counter_name" + * actual: "\Memory\Available Mbytes" + */ + full_counter_path_len += OBJECT_COUNTER_FMT_LEN; + } + full_counter_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, full_counter_path_len + 1); + if (full_counter_path == NULL) { + return NULL; + } + if (instance) { + jio_snprintf_result = jio_snprintf(full_counter_path, + full_counter_path_len + 1, + OBJECT_WITH_INSTANCES_COUNTER_FMT, + object_name, + instance, + counter_name); + } else { + jio_snprintf_result = jio_snprintf(full_counter_path, + full_counter_path_len + 1, + OBJECT_COUNTER_FMT, + object_name, + counter_name); + } + } + assert(full_counter_path_len == jio_snprintf_result, "invariant"); + return full_counter_path; +} + +static void log_invalid_pdh_index(DWORD index) { + if (LogJFR) tty->print_cr("Unable to resolve PDH index: (%ld)", index); + if (LogJFR) tty->print_cr("Please check the registry if this performance object/counter is disabled"); +} + +static bool is_valid_pdh_index(DWORD index) { + DWORD dummy = 0; + if (PdhDll::PdhLookupPerfNameByIndex(NULL, index, NULL, &dummy) != PDH_MORE_DATA) { + log_invalid_pdh_index(index); + return false; + } + return true; +} + +/* + * Maps an index to a resource area allocated string for the localized PDH artifact. + * + * Caller will need a ResourceMark. + * + * @param index the counter index as specified in the registry + * @param ppBuffer pointer to a char* + * @return OS_OK if successful, OS_ERR on failure. + */ +static OSReturn lookup_name_by_index(DWORD index, char** p_string) { + assert(p_string != NULL, "invariant"); + if (!is_valid_pdh_index(index)) { + return OS_ERR; + } + // determine size needed + DWORD size = 0; + PDH_STATUS status = PdhDll::PdhLookupPerfNameByIndex(NULL, index, NULL, &size); + assert(status == PDH_MORE_DATA, "invariant"); + *p_string = NEW_RESOURCE_ARRAY_RETURN_NULL(char, size); + if (*p_string== NULL) { + return OS_ERR; + } + if (PdhDll::PdhLookupPerfNameByIndex(NULL, index, *p_string, &size) != ERROR_SUCCESS) { + return OS_ERR; + } + if (0 == size || *p_string == NULL) { + return OS_ERR; + } + // windows vista does not null-terminate the string (although the docs says it will) + (*p_string)[size - 1] = '\0'; + return OS_OK; +} + +static const char* copy_string_to_c_heap(const char* string) { + assert(string != NULL, "invariant"); + const size_t len = strlen(string); + char* const cheap_allocated_string = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal); + if (NULL == cheap_allocated_string) { + return NULL; + } + strncpy(cheap_allocated_string, string, len + 1); + return cheap_allocated_string; +} + +/* +* Maps an index to a resource area allocated string for the localized PDH artifact. +* +* Caller will need a ResourceMark. +* +* @param index the counter index as specified in the registry +* @return localized pdh artifact string if successful, NULL on failure. +*/ +static const char* pdh_localized_artifact(DWORD pdh_artifact_index) { + char* pdh_localized_artifact_string = NULL; + // get localized name from pdh artifact index + if (lookup_name_by_index(pdh_artifact_index, &pdh_localized_artifact_string) != OS_OK) { + return NULL; + } + return pdh_localized_artifact_string; +} + +/* + * Returns the PDH string identifying the current process image name. + * Use this prefix when getting counters from the PDH process object + * representing your process. + * Ex. "Process(java#0)\Virtual Bytes" - where "java" is the PDH process + * image description. + * + * Caller needs ResourceMark. + * + * @return the process image description. NULL if the call failed. +*/ +static const char* pdh_process_image_name() { + char* module_name = NEW_RESOURCE_ARRAY_RETURN_NULL(char, MAX_PATH); + if (NULL == module_name) { + return NULL; + } + // Find our module name and use it to extract the image name used by PDH + DWORD getmfn_return = GetModuleFileName(NULL, module_name, MAX_PATH); + if (getmfn_return >= MAX_PATH || 0 == getmfn_return) { + return NULL; + } + if (os::get_last_error() == ERROR_INSUFFICIENT_BUFFER) { + return NULL; + } + char* process_image_name = strrchr(module_name, '\\'); //drop path + process_image_name++; //skip slash + char* dot_pos = strrchr(process_image_name, '.'); //drop .exe + dot_pos[0] = '\0'; + return process_image_name; +} + +static void deallocate_pdh_constants() { + if (process_image_name != NULL) { + FREE_C_HEAP_ARRAY(char, process_image_name, mtInternal); + process_image_name = NULL; + } + if (pdh_IDProcess_counter_fmt != NULL) { + FREE_C_HEAP_ARRAY(char, pdh_IDProcess_counter_fmt, mtInternal); + pdh_IDProcess_counter_fmt = NULL; + } +} + +static int allocate_pdh_constants() { + assert(process_image_name == NULL, "invariant"); + const char* pdh_image_name = pdh_process_image_name(); + if (pdh_image_name == NULL) { + return OS_ERR; + } + process_image_name = copy_string_to_c_heap(pdh_image_name); + + const char* pdh_localized_process_object = pdh_localized_artifact(PDH_PROCESS_IDX); + if (pdh_localized_process_object == NULL) { + return OS_ERR; + } + + const char* pdh_localized_IDProcess_counter = pdh_localized_artifact(PDH_ID_PROCESS_IDX); + if (pdh_localized_IDProcess_counter == NULL) { + return OS_ERR; + } + + size_t pdh_IDProcess_counter_fmt_len = strlen(process_image_name); + pdh_IDProcess_counter_fmt_len += strlen(pdh_localized_process_object); + pdh_IDProcess_counter_fmt_len += strlen(pdh_localized_IDProcess_counter); + pdh_IDProcess_counter_fmt_len += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN; + pdh_IDProcess_counter_fmt_len += 2; // "%d" + + assert(pdh_IDProcess_counter_fmt == NULL, "invariant"); + pdh_IDProcess_counter_fmt = NEW_C_HEAP_ARRAY_RETURN_NULL(char, pdh_IDProcess_counter_fmt_len + 1, mtInternal); + if (pdh_IDProcess_counter_fmt == NULL) { + return OS_ERR; + } + + /* "\Process(java#%d)\ID Process" */ + const size_t len = jio_snprintf(pdh_IDProcess_counter_fmt, + pdh_IDProcess_counter_fmt_len + 1, + PROCESS_OBJECT_INSTANCE_COUNTER_FMT, + pdh_localized_process_object, + process_image_name, + "%d", + pdh_localized_IDProcess_counter); + + assert(pdh_IDProcess_counter_fmt != NULL, "invariant"); + assert(len == pdh_IDProcess_counter_fmt_len, "invariant"); + return OS_OK; +} + +/* + * Enuerate the Processor PDH object and returns a buffer containing the enumerated instances. + * Caller needs ResourceMark; + * + * @return buffer if successful, NULL on failure. +*/ +static const char* enumerate_cpu_instances() { + char* processor; //'Processor' == PDH_PROCESSOR_IDX + if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) { + return NULL; + } + DWORD c_size = 0; + DWORD i_size = 0; + // enumerate all processors. + PDH_STATUS pdhStat = PdhDll::PdhEnumObjectItems(NULL, // reserved + NULL, // local machine + processor, // object to enumerate + NULL, + &c_size, + NULL, // instance buffer is NULL and + &i_size, // pass 0 length in order to get the required size + PERF_DETAIL_WIZARD, // counter detail level + 0); + if (PdhDll::PdhStatusFail((pdhStat))) { + return NULL; + } + char* const instances = NEW_RESOURCE_ARRAY_RETURN_NULL(char, i_size); + if (instances == NULL) { + return NULL; + } + c_size = 0; + pdhStat = PdhDll::PdhEnumObjectItems(NULL, // reserved + NULL, // local machine + processor, // object to enumerate + NULL, + &c_size, + instances, // now instance buffer is allocated to be filled in + &i_size, // and the required size is known + PERF_DETAIL_WIZARD, // counter detail level + 0); + if (PdhDll::PdhStatusFail((pdhStat))) { + return NULL; + } + return instances; +} + +static int count_logical_cpus(const char* instances) { + assert(instances != NULL, "invariant"); + // count logical instances. + DWORD count; + char* tmp; + for (count = 0, tmp = const_cast(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], count++); + // PDH reports an instance for each logical processor plus an instance for the total (_Total) + assert(count == os::processor_count() + 1, "invalid enumeration!"); + return count - 1; +} + +static int number_of_logical_cpus() { + static int numberOfCPUS = 0; + if (numberOfCPUS == 0) { + const char* instances = enumerate_cpu_instances(); + if (instances == NULL) { + return OS_ERR; + } + numberOfCPUS = count_logical_cpus(instances); + } + return numberOfCPUS; +} + +static double cpu_factor() { + static DWORD numCpus = 0; + static double cpuFactor = .0; + if (numCpus == 0) { + numCpus = number_of_logical_cpus(); + assert(os::processor_count() <= (int)numCpus, "invariant"); + cpuFactor = numCpus * 100; + } + return cpuFactor; +} + +static void log_error_message_on_no_PDH_artifact(const char* full_counter_name) { + if (LogJFR) tty->print_cr("Unable to register PDH query for \"%s\"", full_counter_name); + if (LogJFR) tty->print_cr("Please check the registry if this performance object/counter is disabled"); +} + +static int initialize_cpu_query_counters(MultiCounterQueryP cpu_query, DWORD pdh_counter_idx) { + assert(cpu_query != NULL, "invariant"); + assert(cpu_query->counters != NULL, "invariant"); + char* processor; //'Processor' == PDH_PROCESSOR_IDX + if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) { + return OS_ERR; + } + char* counter_name = NULL; + if (lookup_name_by_index(pdh_counter_idx, &counter_name) != OS_OK) { + return OS_ERR; + } + if (cpu_query->query.query == NULL) { + if (open_query(cpu_query)) { + return OS_ERR; + } + } + assert(cpu_query->query.query != NULL, "invariant"); + size_t counter_len = strlen(processor); + counter_len += strlen(counter_name); + counter_len += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN; // "\\%s(%s)\\%s" + + DWORD index; + char* tmp; + const char* instances = enumerate_cpu_instances(); + for (index = 0, tmp = const_cast(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], index++) { + const size_t tmp_len = strlen(tmp); + char* counter_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, counter_len + tmp_len + 1); + if (counter_path == NULL) { + return OS_ERR; + } + const size_t jio_snprintf_result = jio_snprintf(counter_path, + counter_len + tmp_len + 1, + OBJECT_WITH_INSTANCES_COUNTER_FMT, + processor, + tmp, // instance "0", "1", .."_Total" + counter_name); + assert(counter_len + tmp_len == jio_snprintf_result, "invariant"); + if (add_counter(cpu_query, &cpu_query->counters[index], counter_path, false) != OS_OK) { + // performance counter is disabled in registry and not accessible via PerfLib + log_error_message_on_no_PDH_artifact(counter_path); + // return OS_OK to have the system continue to run without the missing counter + return OS_OK; + } + } + cpu_query->initialized = true; + // Query once to initialize the counters which require at least two samples + // (like the % CPU usage) to calculate correctly. + collect_query_data(cpu_query); + return OS_OK; +} + +static int initialize_cpu_query(MultiCounterQueryP cpu_query, DWORD pdh_counter_idx) { + assert(cpu_query != NULL, "invariant"); + assert(!cpu_query->initialized, "invariant"); + const int logical_cpu_count = number_of_logical_cpus(); + assert(logical_cpu_count >= os::processor_count(), "invariant"); + // we also add another counter for instance "_Total" + if (allocate_counters(cpu_query, logical_cpu_count + 1) != OS_OK) { + return OS_ERR; + } + assert(cpu_query->noOfCounters == logical_cpu_count + 1, "invariant"); + return initialize_cpu_query_counters(cpu_query, pdh_counter_idx); +} + +static int initialize_process_counter(ProcessQueryP process_query, int slot_index, DWORD pdh_counter_index) { + char* localized_process_object; + if (lookup_name_by_index(PDH_PROCESS_IDX, &localized_process_object) != OS_OK) { + return OS_ERR; + } + assert(localized_process_object != NULL, "invariant"); + char* localized_counter_name; + if (lookup_name_by_index(pdh_counter_index, &localized_counter_name) != OS_OK) { + return OS_ERR; + } + assert(localized_counter_name != NULL, "invariant"); + for (int i = 0; i < process_query->set.size; ++i) { + char instanceIndexBuffer[32]; + const char* counter_path = make_fully_qualified_counter_path(localized_process_object, + localized_counter_name, + process_image_name, + itoa(i, instanceIndexBuffer, 10)); + if (counter_path == NULL) { + return OS_ERR; + } + MultiCounterQueryP const query = &process_query->set.queries[i]; + if (add_process_counter(query, slot_index, counter_path, true)) { + return OS_ERR; + } + } + return OS_OK; +} + +static CounterQueryP create_counter_query(DWORD pdh_object_idx, DWORD pdh_counter_idx) { + if (!((is_valid_pdh_index(pdh_object_idx) && is_valid_pdh_index(pdh_counter_idx)))) { + return NULL; + } + CounterQueryP const query = create_counter_query(); + const char* object = pdh_localized_artifact(pdh_object_idx); + assert(object != NULL, "invariant"); + const char* counter = pdh_localized_artifact(pdh_counter_idx); + assert(counter != NULL, "invariant"); + const char* full_counter_path = make_fully_qualified_counter_path(object, counter); + assert(full_counter_path != NULL, "invariant"); + add_counter(query, full_counter_path, true); + return query; +} + +static void deallocate() { + deallocate_pdh_constants(); + PdhDll::PdhDetach(); +} + +static LONG critical_section = 0; +static LONG reference_count = 0; +static bool pdh_initialized = false; + +static void on_initialization_failure() { + // still holder of critical section + deallocate(); + InterlockedExchangeAdd(&reference_count, -1); +} + +static OSReturn initialize() { + ResourceMark rm; + if (!PdhDll::PdhAttach()) { + return OS_ERR; + } + if (allocate_pdh_constants() != OS_OK) { + on_initialization_failure(); + return OS_ERR; + } + return OS_OK; +} + +/* +* Helper to initialize the PDH library, function pointers, constants and counters. +* +* Reference counting allows for unloading of pdh.dll granted all sessions use the pair: +* +* pdh_acquire(); +* pdh_release(); +* +* @return OS_OK if successful, OS_ERR on failure. +*/ +static bool pdh_acquire() { + while (InterlockedCompareExchange(&critical_section, 1, 0) == 1); + InterlockedExchangeAdd(&reference_count, 1); + if (pdh_initialized) { + return true; + } + const OSReturn ret = initialize(); + if (OS_OK == ret) { + pdh_initialized = true; + } + while (InterlockedCompareExchange(&critical_section, 0, 1) == 0); + return ret == OS_OK; +} + +static void pdh_release() { + while (InterlockedCompareExchange(&critical_section, 1, 0) == 1); + const LONG prev_ref_count = InterlockedExchangeAdd(&reference_count, -1); + if (1 == prev_ref_count) { + deallocate(); + pdh_initialized = false; + } + while (InterlockedCompareExchange(&critical_section, 0, 1) == 0); +} + +class CPUPerformanceInterface::CPUPerformance : public CHeapObj { + friend class CPUPerformanceInterface; + private: + CounterQueryP _context_switches; + ProcessQueryP _process_cpu_load; + MultiCounterQueryP _machine_cpu_load; + + int cpu_load(int which_logical_cpu, double* cpu_load); + int context_switch_rate(double* rate); + int cpu_load_total_process(double* cpu_load); + int cpu_loads_process(double* jvm_user_load, double* jvm_kernel_load, double* psystemTotalLoad); + CPUPerformance(); + ~CPUPerformance(); + bool initialize(); +}; + +class SystemProcessInterface::SystemProcesses : public CHeapObj { + friend class SystemProcessInterface; + private: + class ProcessIterator : public CHeapObj { + friend class SystemProcessInterface::SystemProcesses; + private: + HANDLE _hProcessSnap; + PROCESSENTRY32 _pe32; + BOOL _valid; + char _exePath[MAX_PATH]; + ProcessIterator(); + ~ProcessIterator(); + bool initialize(); + + int current(SystemProcess* const process_info); + int next_process(); + bool is_valid() const { return _valid != FALSE; } + char* allocate_string(const char* str) const; + int snapshot(); + }; + + ProcessIterator* _iterator; + SystemProcesses(); + ~SystemProcesses(); + bool initialize(); + + // information about system processes + int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const; +}; + +CPUPerformanceInterface::CPUPerformance::CPUPerformance() : _context_switches(NULL), _process_cpu_load(NULL), _machine_cpu_load(NULL) {} + +bool CPUPerformanceInterface::CPUPerformance::initialize() { + if (!pdh_acquire()) { + return true; + } + _context_switches = create_counter_query(PDH_SYSTEM_IDX, PDH_CONTEXT_SWITCH_RATE_IDX); + _process_cpu_load = create_process_query(); + if (_process_cpu_load == NULL) { + return true; + } + if (allocate_counters(_process_cpu_load, 2) != OS_OK) { + return true; + } + if (initialize_process_counter(_process_cpu_load, 0, PDH_PROCESSOR_TIME_IDX) != OS_OK) { + return true; + } + if (initialize_process_counter(_process_cpu_load, 1, PDH_PRIV_PROCESSOR_TIME_IDX) != OS_OK) { + return true; + } + _process_cpu_load->set.initialized = true; + _machine_cpu_load = create_multi_counter_query(); + if (_machine_cpu_load == NULL) { + return true; + } + initialize_cpu_query(_machine_cpu_load, PDH_PROCESSOR_TIME_IDX); + return true; +} + +CPUPerformanceInterface::CPUPerformance::~CPUPerformance() { + if (_context_switches != NULL) { + destroy_counter_query(_context_switches); + _context_switches = NULL; + } + if (_process_cpu_load != NULL) { + destroy_counter_query(_process_cpu_load); + _process_cpu_load = NULL; + } + if (_machine_cpu_load != NULL) { + destroy_counter_query(_machine_cpu_load); + _machine_cpu_load = NULL; + } + pdh_release(); +} + +CPUPerformanceInterface::CPUPerformanceInterface() { + _impl = NULL; +} + +bool CPUPerformanceInterface::initialize() { + _impl = new CPUPerformanceInterface::CPUPerformance(); + return _impl != NULL && _impl->initialize(); +} + +CPUPerformanceInterface::~CPUPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +int CPUPerformanceInterface::cpu_load(int which_logical_cpu, double* cpu_load) const { + return _impl->cpu_load(which_logical_cpu, cpu_load); +} + +int CPUPerformanceInterface::context_switch_rate(double* rate) const { + return _impl->context_switch_rate(rate); +} + +int CPUPerformanceInterface::cpu_load_total_process(double* cpu_load) const { + return _impl->cpu_load_total_process(cpu_load); +} + +int CPUPerformanceInterface::cpu_loads_process(double* pjvmUserLoad, + double* pjvmKernelLoad, + double* psystemTotalLoad) const { + return _impl->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotalLoad); +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load(int which_logical_cpu, double* cpu_load) { + *cpu_load = .0; + if (_machine_cpu_load == NULL || !_machine_cpu_load->initialized) { + return OS_ERR; + } + assert(_machine_cpu_load != NULL, "invariant"); + assert(which_logical_cpu < _machine_cpu_load->noOfCounters, "invariant"); + + if (collect_query_data(_machine_cpu_load)) { + return OS_ERR; + } + // -1 is total (all cpus) + const int counter_idx = -1 == which_logical_cpu ? _machine_cpu_load->noOfCounters - 1 : which_logical_cpu; + PDH_FMT_COUNTERVALUE counter_value; + formatted_counter_value(_machine_cpu_load->counters[counter_idx], PDH_FMT_DOUBLE, &counter_value); + *cpu_load = counter_value.doubleValue / 100; + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) { + *cpu_load = .0; + if (_process_cpu_load == NULL || !_process_cpu_load->set.initialized) { + return OS_ERR; + } + assert(_process_cpu_load != NULL, "invariant"); + if (collect_process_query_data(_process_cpu_load)) { + return OS_ERR; + } + PDH_FMT_COUNTERVALUE counter_value; + if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) { + return OS_ERR; + } + double process_load = counter_value.doubleValue / cpu_factor(); + process_load = MIN2(1, process_load); + process_load = MAX2(0, process_load); + *cpu_load = process_load; + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, + double* pjvmKernelLoad, + double* psystemTotalLoad) { + assert(pjvmUserLoad != NULL, "pjvmUserLoad is NULL!"); + assert(pjvmKernelLoad != NULL, "pjvmKernelLoad is NULL!"); + assert(psystemTotalLoad != NULL, "psystemTotalLoad is NULL!"); + *pjvmUserLoad = .0; + *pjvmKernelLoad = .0; + *psystemTotalLoad = .0; + + if (_process_cpu_load == NULL || !_process_cpu_load->set.initialized) { + return OS_ERR; + } + assert(_process_cpu_load != NULL, "invariant"); + if (collect_process_query_data(_process_cpu_load)) { + return OS_ERR; + } + double process_load = .0; + PDH_FMT_COUNTERVALUE counter_value; + // Read PDH_PROCESSOR_TIME_IDX + if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) { + return OS_ERR; + } + process_load = counter_value.doubleValue / cpu_factor(); + process_load = MIN2(1, process_load); + process_load = MAX2(0, process_load); + // Read PDH_PRIV_PROCESSOR_TIME_IDX + if (query_process_counter(_process_cpu_load, 1, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) { + return OS_ERR; + } + double kernel_load = counter_value.doubleValue / cpu_factor(); + kernel_load = MIN2(1, kernel_load); + kernel_load = MAX2(0, kernel_load); + *pjvmKernelLoad = kernel_load; + + double user_load = process_load - kernel_load; + user_load = MIN2(1, user_load); + user_load = MAX2(0, user_load); + *pjvmUserLoad = user_load; + + if (collect_query_data(_machine_cpu_load)) { + return OS_ERR; + } + if (formatted_counter_value(_machine_cpu_load->counters[_machine_cpu_load->noOfCounters - 1], PDH_FMT_DOUBLE, &counter_value) != OS_OK) { + return OS_ERR; + } + double machine_load = counter_value.doubleValue / 100; + assert(machine_load >= 0, "machine_load is negative!"); + // clamp at user+system and 1.0 + if (*pjvmKernelLoad + *pjvmUserLoad > machine_load) { + machine_load = MIN2(*pjvmKernelLoad + *pjvmUserLoad, 1.0); + } + *psystemTotalLoad = machine_load; + return OS_OK; +} + +int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) { + assert(rate != NULL, "invariant"); + *rate = .0; + if (_context_switches == NULL || !_context_switches->initialized) { + return OS_ERR; + } + assert(_context_switches != NULL, "invariant"); + if (collect_query_data(_context_switches) != OS_OK) { + return OS_ERR; + } + PDH_FMT_COUNTERVALUE counter_value; + if (formatted_counter_value(_context_switches->counter, PDH_FMT_DOUBLE, &counter_value) != OS_OK) { + return OS_ERR; + } + *rate = counter_value.doubleValue; + return OS_OK; +} + +SystemProcessInterface::SystemProcesses::ProcessIterator::ProcessIterator() { + _hProcessSnap = INVALID_HANDLE_VALUE; + _valid = FALSE; + _pe32.dwSize = sizeof(PROCESSENTRY32); +} + +bool SystemProcessInterface::SystemProcesses::ProcessIterator::initialize() { + return true; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::snapshot() { + // take snapshot of all process in the system + _hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (_hProcessSnap == INVALID_HANDLE_VALUE) { + return OS_ERR; + } + // step to first process + _valid = Process32First(_hProcessSnap, &_pe32); + return is_valid() ? OS_OK : OS_ERR; +} + +SystemProcessInterface::SystemProcesses::ProcessIterator::~ProcessIterator() { + if (_hProcessSnap != INVALID_HANDLE_VALUE) { + CloseHandle(_hProcessSnap); + } +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::current(SystemProcess* process_info) { + assert(is_valid(), "no current process to be fetched!"); + assert(process_info != NULL, "process_info is NULL!"); + char* exePath = NULL; + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, _pe32.th32ProcessID); + if (hProcess != NULL) { + HMODULE hMod; + DWORD cbNeeded; + if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded) != 0) { + if (GetModuleFileNameExA(hProcess, hMod, _exePath, sizeof(_exePath)) != 0) { + exePath = _exePath; + } + } + CloseHandle (hProcess); + } + process_info->set_pid((int)_pe32.th32ProcessID); + process_info->set_name(allocate_string(_pe32.szExeFile)); + process_info->set_path(allocate_string(exePath)); + return OS_OK; +} + +char* SystemProcessInterface::SystemProcesses::ProcessIterator::allocate_string(const char* str) const { + if (str != NULL) { + size_t len = strlen(str); + char* tmp = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); + if (NULL == tmp) { + return NULL; + } + strncpy(tmp, str, len); + tmp[len] = '\0'; + return tmp; + } + return NULL; +} + +int SystemProcessInterface::SystemProcesses::ProcessIterator::next_process() { + _valid = Process32Next(_hProcessSnap, &_pe32); + return OS_OK; +} + +SystemProcessInterface::SystemProcesses::SystemProcesses() { + _iterator = NULL; +} + +bool SystemProcessInterface::SystemProcesses::initialize() { + _iterator = new SystemProcessInterface::SystemProcesses::ProcessIterator(); + return _iterator != NULL && _iterator->initialize(); +} + +SystemProcessInterface::SystemProcesses::~SystemProcesses() { + if (_iterator != NULL) { + delete _iterator; + _iterator = NULL; + } +} + +int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes, + int* no_of_sys_processes) const { + assert(system_processes != NULL, "system_processes pointer is NULL!"); + assert(no_of_sys_processes != NULL, "system_processes counter pointers is NULL!"); + assert(_iterator != NULL, "iterator is NULL!"); + + // initialize pointers + *no_of_sys_processes = 0; + *system_processes = NULL; + + // take process snapshot + if (_iterator->snapshot() != OS_OK) { + return OS_ERR; + } + + while (_iterator->is_valid()) { + SystemProcess* tmp = new SystemProcess(); + _iterator->current(tmp); + + //if already existing head + if (*system_processes != NULL) { + //move "first to second" + tmp->set_next(*system_processes); + } + // new head + *system_processes = tmp; + // increment + (*no_of_sys_processes)++; + // step forward + _iterator->next_process(); + } + return OS_OK; +} + +int SystemProcessInterface::system_processes(SystemProcess** system_procs, + int* no_of_sys_processes) const { + return _impl->system_processes(system_procs, no_of_sys_processes); +} + +SystemProcessInterface::SystemProcessInterface() { + _impl = NULL; +} + +bool SystemProcessInterface::initialize() { + _impl = new SystemProcessInterface::SystemProcesses(); + return _impl != NULL && _impl->initialize(); +} + +SystemProcessInterface::~SystemProcessInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +CPUInformationInterface::CPUInformationInterface() { + _cpu_info = NULL; +} + +bool CPUInformationInterface::initialize() { + _cpu_info = new CPUInformation(); + if (NULL == _cpu_info) { + return false; + } + _cpu_info->set_number_of_hardware_threads(VM_Version_Ext::number_of_threads()); + _cpu_info->set_number_of_cores(VM_Version_Ext::number_of_cores()); + _cpu_info->set_number_of_sockets(VM_Version_Ext::number_of_sockets()); + _cpu_info->set_cpu_name(VM_Version_Ext::cpu_name()); + _cpu_info->set_cpu_description(VM_Version_Ext::cpu_description()); + return true; +} + +CPUInformationInterface::~CPUInformationInterface() { + if (_cpu_info != NULL) { + const char* cpu_name = _cpu_info->cpu_name(); + if (cpu_name != NULL) { + FREE_C_HEAP_ARRAY(char, cpu_name, mtInternal); + _cpu_info->set_cpu_name(NULL); + } + const char* cpu_desc = _cpu_info->cpu_description(); + if (cpu_desc != NULL) { + FREE_C_HEAP_ARRAY(char, cpu_desc, mtInternal); + _cpu_info->set_cpu_description(NULL); + } + delete _cpu_info; + _cpu_info = NULL; + } +} + +int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) { + if (NULL == _cpu_info) { + return OS_ERR; + } + cpu_info = *_cpu_info; // shallow copy assignment + return OS_OK; +} + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + bool _iphlp_attached; + + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() +: _iphlp_attached(false) { +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + _iphlp_attached = IphlpDll::IphlpAttach(); + return _iphlp_attached; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { + if (_iphlp_attached) { + IphlpDll::IphlpDetach(); + } +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const { + MIB_IF_TABLE2* table; + + if (IphlpDll::GetIfTable2(&table) != NO_ERROR) { + return OS_ERR; + } + + NetworkInterface* ret = NULL; + for (ULONG i = 0; i < table->NumEntries; ++i) { + if (table->Table[i].InterfaceAndOperStatusFlags.FilterInterface) { + continue; + } + + char buf[256]; + if (WideCharToMultiByte(CP_UTF8, 0, table->Table[i].Description, -1, buf, sizeof(buf), NULL, NULL) == 0) { + continue; + } + + NetworkInterface* cur = new NetworkInterface(buf, table->Table[i].InOctets, table->Table[i].OutOctets, ret); + ret = cur; + } + + IphlpDll::FreeMibTable(table); + *network_interfaces = ret; + + return OS_OK; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff --git a/src/os/windows/vm/os_windows.cpp b/src/os/windows/vm/os_windows.cpp index 06eebde006cf631464dfe217b3e0aa90a876ae66..74412a3ebb0c489b4ab378fd98c08c7a1b28729f 100644 --- a/src/os/windows/vm/os_windows.cpp +++ b/src/os/windows/vm/os_windows.cpp @@ -1669,6 +1669,50 @@ void os::print_dll_info(outputStream *st) { enumerate_modules(pid, _print_module, (void *)st); } +int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *param) { + HANDLE hProcess; + +# define MAX_NUM_MODULES 128 + HMODULE modules[MAX_NUM_MODULES]; + static char filename[MAX_PATH]; + int result = 0; + + int pid = os::current_process_id(); + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, pid); + if (hProcess == NULL) return 0; + + DWORD size_needed; + if (!EnumProcessModules(hProcess, modules, sizeof(modules), &size_needed)) { + CloseHandle(hProcess); + return 0; + } + + // number of modules that are currently loaded + int num_modules = size_needed / sizeof(HMODULE); + + for (int i = 0; i < MIN2(num_modules, MAX_NUM_MODULES); i++) { + // Get Full pathname: + if (!GetModuleFileNameEx(hProcess, modules[i], filename, sizeof(filename))) { + filename[0] = '\0'; + } + + MODULEINFO modinfo; + if (!GetModuleInformation(hProcess, modules[i], &modinfo, sizeof(modinfo))) { + modinfo.lpBaseOfDll = NULL; + modinfo.SizeOfImage = 0; + } + + // Invoke callback function + result = callback(filename, (address)modinfo.lpBaseOfDll, + (address)((u8)modinfo.lpBaseOfDll + (u8)modinfo.SizeOfImage), param); + if (result) break; + } + + CloseHandle(hProcess); + return result; +} + void os::print_os_info_brief(outputStream* st) { os::print_os_info(st); } @@ -4352,6 +4396,22 @@ jlong os::lseek(int fd, jlong offset, int whence) { return (jlong) ::_lseeki64(fd, offset, whence); } +size_t os::read_at(int fd, void *buf, unsigned int nBytes, jlong offset) { + OVERLAPPED ov; + DWORD nread; + BOOL result; + + ZeroMemory(&ov, sizeof(ov)); + ov.Offset = (DWORD)offset; + ov.OffsetHigh = (DWORD)(offset >> 32); + + HANDLE h = (HANDLE)::_get_osfhandle(fd); + + result = ReadFile(h, (LPVOID)buf, nBytes, &nread, &ov); + + return result ? nread : 0; +} + // This method is a slightly reworked copy of JDK's sysNativePath // from src/windows/hpi/src/path_md.c @@ -4811,31 +4871,37 @@ void os::pause() { } } -os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() { - assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread"); +Thread* os::ThreadCrashProtection::_protected_thread = NULL; +os::ThreadCrashProtection* os::ThreadCrashProtection::_crash_protection = NULL; +volatile intptr_t os::ThreadCrashProtection::_crash_mux = 0; + +os::ThreadCrashProtection::ThreadCrashProtection() { } -/* - * See the caveats for this class in os_windows.hpp - * Protects the callback call so that raised OS EXCEPTIONS causes a jump back - * into this method and returns false. If no OS EXCEPTION was raised, returns - * true. - * The callback is supposed to provide the method that should be protected. - */ -bool os::WatcherThreadCrashProtection::call(os::CrashProtectionCallback& cb) { - assert(Thread::current()->is_Watcher_thread(), "Only for WatcherThread"); - assert(!WatcherThread::watcher_thread()->has_crash_protection(), - "crash_protection already set?"); +// See the caveats for this class in os_windows.hpp +// Protects the callback call so that raised OS EXCEPTIONS causes a jump back +// into this method and returns false. If no OS EXCEPTION was raised, returns +// true. +// The callback is supposed to provide the method that should be protected. +// +bool os::ThreadCrashProtection::call(os::CrashProtectionCallback& cb) { + + Thread::muxAcquire(&_crash_mux, "CrashProtection"); + + _protected_thread = ThreadLocalStorage::thread(); + assert(_protected_thread != NULL, "Cannot crash protect a NULL thread"); bool success = true; __try { - WatcherThread::watcher_thread()->set_crash_protection(this); + _crash_protection = this; cb.call(); } __except(EXCEPTION_EXECUTE_HANDLER) { // only for protection, nothing to do success = false; } - WatcherThread::watcher_thread()->set_crash_protection(NULL); + _crash_protection = NULL; + _protected_thread = NULL; + Thread::muxRelease(&_crash_mux); return success; } diff --git a/src/os/windows/vm/os_windows.hpp b/src/os/windows/vm/os_windows.hpp index 8db5319da41f855fd1d8ae7d3c920084f8697301..20e2ca2f5c3575c3a70b608b36ea246e2b486c3e 100644 --- a/src/os/windows/vm/os_windows.hpp +++ b/src/os/windows/vm/os_windows.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -111,10 +111,18 @@ class win32 { * don't call code that could leave the heap / memory in an inconsistent state, * or anything else where we are not in control if we suddenly jump out. */ -class WatcherThreadCrashProtection : public StackObj { +class ThreadCrashProtection : public StackObj { public: - WatcherThreadCrashProtection(); + static bool is_crash_protected(Thread* thr) { + return _crash_protection != NULL && _protected_thread == thr; + } + + ThreadCrashProtection(); bool call(os::CrashProtectionCallback& cb); +private: + static Thread* _protected_thread; + static ThreadCrashProtection* _crash_protection; + static volatile intptr_t _crash_mux; }; class PlatformEvent : public CHeapObj { diff --git a/src/os/windows/vm/pdh_interface.cpp b/src/os/windows/vm/pdh_interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ad7af2789e466bb8864097b4a09d0e20a515630 --- /dev/null +++ b/src/os/windows/vm/pdh_interface.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012, 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 "precompiled.hpp" +#include "pdh_interface.hpp" +#include "runtime/os.hpp" +#include "utilities/macros.hpp" + +// PDH API +typedef PDH_STATUS (WINAPI *PdhAddCounter_Fn)(HQUERY, LPCSTR, DWORD, HCOUNTER*); +typedef PDH_STATUS (WINAPI *PdhOpenQuery_Fn)(LPCWSTR, DWORD, HQUERY*); +typedef DWORD (WINAPI *PdhCloseQuery_Fn)(HQUERY); +typedef PDH_STATUS (WINAPI *PdhCollectQueryData_Fn)(HQUERY); +typedef DWORD (WINAPI *PdhGetFormattedCounterValue_Fn)(HCOUNTER, DWORD, LPDWORD, PPDH_FMT_COUNTERVALUE); +typedef PDH_STATUS (WINAPI *PdhEnumObjectItems_Fn)(LPCTSTR, LPCTSTR, LPCTSTR, LPTSTR, LPDWORD, LPTSTR, LPDWORD, DWORD, DWORD); +typedef PDH_STATUS (WINAPI *PdhRemoveCounter_Fn)(HCOUNTER); +typedef PDH_STATUS (WINAPI *PdhLookupPerfNameByIndex_Fn)(LPCSTR, DWORD, LPSTR, LPDWORD); +typedef PDH_STATUS (WINAPI *PdhMakeCounterPath_Fn)(PDH_COUNTER_PATH_ELEMENTS*, LPTSTR, LPDWORD, DWORD); + +PdhAddCounter_Fn PdhDll::_PdhAddCounter = NULL; +PdhOpenQuery_Fn PdhDll::_PdhOpenQuery = NULL; +PdhCloseQuery_Fn PdhDll::_PdhCloseQuery = NULL; +PdhCollectQueryData_Fn PdhDll::_PdhCollectQueryData = NULL; +PdhGetFormattedCounterValue_Fn PdhDll::_PdhGetFormattedCounterValue = NULL; +PdhEnumObjectItems_Fn PdhDll::_PdhEnumObjectItems = NULL; +PdhRemoveCounter_Fn PdhDll::_PdhRemoveCounter = NULL; +PdhLookupPerfNameByIndex_Fn PdhDll::_PdhLookupPerfNameByIndex = NULL; +PdhMakeCounterPath_Fn PdhDll::_PdhMakeCounterPath = NULL; + +LONG PdhDll::_critical_section = 0; +LONG PdhDll::_initialized = 0; +LONG PdhDll::_pdh_reference_count = 0; +HMODULE PdhDll::_hModule = NULL; + +void PdhDll::initialize(void) { + _hModule = os::win32::load_Windows_dll("pdh.dll", NULL, 0); + if (NULL == _hModule) { + return; + } + // The 'A' at the end means the ANSI (not the UNICODE) vesions of the methods + _PdhAddCounter = (PdhAddCounter_Fn)::GetProcAddress(_hModule, "PdhAddCounterA"); + _PdhOpenQuery = (PdhOpenQuery_Fn)::GetProcAddress(_hModule, "PdhOpenQueryA"); + _PdhCloseQuery = (PdhCloseQuery_Fn)::GetProcAddress(_hModule, "PdhCloseQuery"); + _PdhCollectQueryData = (PdhCollectQueryData_Fn)::GetProcAddress(_hModule, "PdhCollectQueryData"); + _PdhGetFormattedCounterValue = (PdhGetFormattedCounterValue_Fn)::GetProcAddress(_hModule, "PdhGetFormattedCounterValue"); + _PdhEnumObjectItems = (PdhEnumObjectItems_Fn)::GetProcAddress(_hModule, "PdhEnumObjectItemsA"); + _PdhRemoveCounter = (PdhRemoveCounter_Fn)::GetProcAddress(_hModule, "PdhRemoveCounter"); + _PdhLookupPerfNameByIndex = (PdhLookupPerfNameByIndex_Fn)::GetProcAddress(_hModule, "PdhLookupPerfNameByIndexA"); + _PdhMakeCounterPath = (PdhMakeCounterPath_Fn)::GetProcAddress(_hModule, "PdhMakeCounterPathA"); + InterlockedExchange(&_initialized, 1); +} + +bool PdhDll::PdhDetach(void) { + LONG prev_ref_count = InterlockedExchangeAdd(&_pdh_reference_count, -1); + BOOL ret = false; + if (1 == prev_ref_count) { + if (_initialized && _hModule != NULL) { + ret = FreeLibrary(_hModule); + if (ret) { + _hModule = NULL; + _PdhAddCounter = NULL; + _PdhOpenQuery = NULL; + _PdhCloseQuery = NULL; + _PdhCollectQueryData = NULL; + _PdhGetFormattedCounterValue = NULL; + _PdhEnumObjectItems = NULL; + _PdhRemoveCounter = NULL; + _PdhLookupPerfNameByIndex = NULL; + _PdhMakeCounterPath = NULL; + InterlockedExchange(&_initialized, 0); + } + } + } + return ret != 0; +} + +bool PdhDll::PdhAttach(void) { + InterlockedExchangeAdd(&_pdh_reference_count, 1); + if (1 == _initialized) { + return true; + } + while (InterlockedCompareExchange(&_critical_section, 1, 0) == 1); + if (0 == _initialized) { + initialize(); + } + while (InterlockedCompareExchange(&_critical_section, 0, 1) == 0); + return (_PdhAddCounter != NULL && _PdhOpenQuery != NULL + && _PdhCloseQuery != NULL && PdhCollectQueryData != NULL + && _PdhGetFormattedCounterValue != NULL && _PdhEnumObjectItems != NULL + && _PdhRemoveCounter != NULL && PdhLookupPerfNameByIndex != NULL + && _PdhMakeCounterPath != NULL); +} + +PDH_STATUS PdhDll::PdhAddCounter(HQUERY hQuery, LPCSTR szFullCounterPath, DWORD dwUserData, HCOUNTER* phCounter) { + assert(_initialized && _PdhAddCounter != NULL, "PdhAvailable() not yet called"); + return _PdhAddCounter(hQuery, szFullCounterPath, dwUserData, phCounter); +} + +PDH_STATUS PdhDll::PdhOpenQuery(LPCWSTR szDataSource, DWORD dwUserData, HQUERY* phQuery) { + assert(_initialized && _PdhOpenQuery != NULL, "PdhAvailable() not yet called"); + return _PdhOpenQuery(szDataSource, dwUserData, phQuery); +} + +DWORD PdhDll::PdhCloseQuery(HQUERY hQuery) { + assert(_initialized && _PdhCloseQuery != NULL, "PdhAvailable() not yet called"); + return _PdhCloseQuery(hQuery); +} + +PDH_STATUS PdhDll::PdhCollectQueryData(HQUERY hQuery) { + assert(_initialized && _PdhCollectQueryData != NULL, "PdhAvailable() not yet called"); + return _PdhCollectQueryData(hQuery); +} + +DWORD PdhDll::PdhGetFormattedCounterValue(HCOUNTER hCounter, DWORD dwFormat, LPDWORD lpdwType, PPDH_FMT_COUNTERVALUE pValue) { + assert(_initialized && _PdhGetFormattedCounterValue != NULL, "PdhAvailable() not yet called"); + return _PdhGetFormattedCounterValue(hCounter, dwFormat, lpdwType, pValue); +} + +PDH_STATUS PdhDll::PdhEnumObjectItems(LPCTSTR szDataSource, LPCTSTR szMachineName, LPCTSTR szObjectName, + LPTSTR mszCounterList, LPDWORD pcchCounterListLength, LPTSTR mszInstanceList, + LPDWORD pcchInstanceListLength, DWORD dwDetailLevel, DWORD dwFlags) { + assert(_initialized && _PdhEnumObjectItems != NULL, "PdhAvailable() not yet called"); + return _PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, mszCounterList, pcchCounterListLength, + mszInstanceList, pcchInstanceListLength, dwDetailLevel, dwFlags); +} + +PDH_STATUS PdhDll::PdhRemoveCounter(HCOUNTER hCounter) { + assert(_initialized && _PdhRemoveCounter != NULL, "PdhAvailable() not yet called"); + return _PdhRemoveCounter(hCounter); +} + +PDH_STATUS PdhDll::PdhLookupPerfNameByIndex(LPCSTR szMachineName, DWORD dwNameIndex, LPSTR szNameBuffer, LPDWORD pcchNameBufferSize) { + assert(_initialized && _PdhLookupPerfNameByIndex != NULL, "PdhAvailable() not yet called"); + return _PdhLookupPerfNameByIndex(szMachineName, dwNameIndex, szNameBuffer, pcchNameBufferSize); +} + +PDH_STATUS PdhDll::PdhMakeCounterPath(PDH_COUNTER_PATH_ELEMENTS* pCounterPathElements, LPTSTR szFullPathBuffer, LPDWORD pcchBufferSize, DWORD dwFlags) { + assert(_initialized && _PdhMakeCounterPath != NULL, "PdhAvailable() not yet called"); + return _PdhMakeCounterPath(pCounterPathElements, szFullPathBuffer, pcchBufferSize, dwFlags); +} + +bool PdhDll::PdhStatusFail(PDH_STATUS pdhStat) { + return pdhStat != ERROR_SUCCESS && pdhStat != PDH_MORE_DATA; +} diff --git a/src/os/windows/vm/pdh_interface.hpp b/src/os/windows/vm/pdh_interface.hpp new file mode 100644 index 0000000000000000000000000000000000000000..06a9526e028beee8666295b331a6d2c247397175 --- /dev/null +++ b/src/os/windows/vm/pdh_interface.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012, 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_WINDOWS_VM_PDH_INTERFACE_HPP +#define OS_WINDOWS_VM_PDH_INTERFACE_HPP + +#include "memory/allocation.hpp" +#include +#include + +class PdhDll: public AllStatic { + private: + static LONG _pdh_reference_count; + static LONG _critical_section; + static LONG _initialized; + static HMODULE _hModule; + static void initialize(); + static PDH_STATUS (WINAPI *_PdhAddCounter)(HQUERY, LPCSTR, DWORD, HCOUNTER*); + static PDH_STATUS (WINAPI *_PdhOpenQuery)(LPCWSTR, DWORD, HQUERY*); + static DWORD (WINAPI *_PdhCloseQuery)(HQUERY); + static PDH_STATUS (WINAPI *_PdhCollectQueryData)(HQUERY); + static DWORD (WINAPI *_PdhGetFormattedCounterValue)(HCOUNTER, DWORD, LPDWORD, PPDH_FMT_COUNTERVALUE); + static PDH_STATUS (WINAPI *_PdhEnumObjectItems)(LPCTSTR, LPCTSTR, LPCTSTR, LPTSTR, LPDWORD, LPTSTR, LPDWORD, DWORD, DWORD); + static PDH_STATUS (WINAPI *_PdhRemoveCounter)(HCOUNTER); + static PDH_STATUS (WINAPI *_PdhLookupPerfNameByIndex)(LPCSTR, DWORD, LPSTR, LPDWORD); + static PDH_STATUS (WINAPI *_PdhMakeCounterPath)(PPDH_COUNTER_PATH_ELEMENTS, LPTSTR, LPDWORD, DWORD); + + public: + static PDH_STATUS PdhAddCounter(HQUERY, LPCSTR, DWORD, HCOUNTER*); + static PDH_STATUS PdhOpenQuery(LPCWSTR, DWORD, HQUERY*); + static DWORD PdhCloseQuery(HQUERY); + static PDH_STATUS PdhCollectQueryData(HQUERY); + static DWORD PdhGetFormattedCounterValue(HCOUNTER, DWORD, LPDWORD, PPDH_FMT_COUNTERVALUE); + static PDH_STATUS PdhEnumObjectItems(LPCTSTR, LPCTSTR, LPCTSTR, LPTSTR, LPDWORD, LPTSTR, LPDWORD, DWORD, DWORD); + static PDH_STATUS PdhRemoveCounter(HCOUNTER); + static PDH_STATUS PdhLookupPerfNameByIndex(LPCSTR, DWORD, LPSTR, LPDWORD); + static PDH_STATUS PdhMakeCounterPath(PPDH_COUNTER_PATH_ELEMENTS, LPTSTR, LPDWORD, DWORD); + static bool PdhStatusFail(PDH_STATUS pdhStat); + static bool PdhAttach(); + static bool PdhDetach(); +}; + +#endif // OS_WINDOWS_VM_PDH_INTERFACE_HPP diff --git a/src/os/windows/vm/semaphore_windows.cpp b/src/os/windows/vm/semaphore_windows.cpp new file mode 100644 index 0000000000000000000000000000000000000000..271c009984bdc8e5bab710260d5a0ae3cf1edf09 --- /dev/null +++ b/src/os/windows/vm/semaphore_windows.cpp @@ -0,0 +1,60 @@ +/* + * 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. + * + */ + +#include "precompiled.hpp" +#include "semaphore_windows.hpp" +#include "utilities/debug.hpp" + +#include +#include + +WindowsSemaphore::WindowsSemaphore(uint value) { + _semaphore = ::CreateSemaphore(NULL, value, LONG_MAX, NULL); + + guarantee(_semaphore != NULL, err_msg("CreateSemaphore failed with error code: %lu", GetLastError())); +} + +WindowsSemaphore::~WindowsSemaphore() { + ::CloseHandle(_semaphore); +} + +void WindowsSemaphore::signal(uint count) { + if (count > 0) { + BOOL ret = ::ReleaseSemaphore(_semaphore, count, NULL); + + assert(ret != 0, err_msg("ReleaseSemaphore failed with error code: %lu", GetLastError())); + } +} + +void WindowsSemaphore::wait() { + DWORD ret = ::WaitForSingleObject(_semaphore, INFINITE); + assert(ret != WAIT_FAILED, err_msg("WaitForSingleObject failed with error code: %lu", GetLastError())); + assert(ret == WAIT_OBJECT_0, err_msg("WaitForSingleObject failed with return value: %lu", ret)); +} + +bool WindowsSemaphore::trywait() { + DWORD ret = ::WaitForSingleObject(_semaphore, 0); + assert(ret != WAIT_FAILED, err_msg("WaitForSingleObject failed with error code: %lu", GetLastError())); + return ret == WAIT_OBJECT_0; +} diff --git a/src/os/windows/vm/semaphore_windows.hpp b/src/os/windows/vm/semaphore_windows.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cb62acda61fa37c3ccfcbaf85e41dcc7c3e8555c --- /dev/null +++ b/src/os/windows/vm/semaphore_windows.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, 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_WINDOWS_VM_SEMAPHORE_WINDOWS_HPP +#define OS_WINDOWS_VM_SEMAPHORE_WINDOWS_HPP + +#include "memory/allocation.hpp" + +#include + +class WindowsSemaphore : public CHeapObj { + HANDLE _semaphore; + + // Prevent copying and assignment. + WindowsSemaphore(const WindowsSemaphore&); + WindowsSemaphore& operator=(const WindowsSemaphore&); + + public: + WindowsSemaphore(uint value = 0); + ~WindowsSemaphore(); + + void signal(uint count = 1); + + void wait(); + + bool trywait(); +}; + +typedef WindowsSemaphore SemaphoreImpl; + +#endif // OS_WINDOWS_VM_SEMAPHORE_WINDOWS_HPP diff --git a/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp b/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp index 50051d7c699da8bbc9c5c12534c38e09db66cd0f..018feea1e0309145efdb03b0cc17d24f85a12abd 100644 --- a/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp +++ b/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp @@ -407,7 +407,7 @@ JVM_handle_bsd_signal(int sig, // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away // (no destructors can be run) - os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + os::ThreadCrashProtection::check_crash_protection(sig, t); SignalHandlerMark shm(t); diff --git a/src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp b/src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp index 92661222b07ddb9af1b24b273a75c68155f20e7c..242367b9b7cee31fe4f3477c918c879480f9241b 100644 --- a/src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp +++ b/src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp @@ -24,13 +24,64 @@ */ #include "precompiled.hpp" -#include "runtime/frame.hpp" +#include "runtime/frame.inline.hpp" #include "runtime/thread.hpp" +bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava) { + assert(this->is_Java_thread(), "must be JavaThread"); + + // If we have a last_Java_frame, then we should use it even if + // isInJava == true. It should be more reliable than ucontext info. + if (has_last_Java_frame() && frame_anchor()->walkable()) { + *fr_addr = pd_last_frame(); + return true; + } + + // At this point, we don't have a last_Java_frame, so + // we try to glean some information out of the ucontext + // if we were running Java code when SIGPROF came in. + if (isInJava) { + ucontext_t* uc = (ucontext_t*) ucontext; + frame ret_frame((intptr_t*)uc->uc_mcontext.regs->gpr[1/*REG_SP*/], + (address)uc->uc_mcontext.regs->nip); + + if (ret_frame.pc() == NULL) { + // ucontext wasn't useful + return false; + } + + if (ret_frame.is_interpreted_frame()) { + frame::ijava_state* istate = ret_frame.get_ijava_state(); + if (!((Method*)(istate->method))->is_metaspace_object()) { + return false; + } + uint64_t reg_bcp = uc->uc_mcontext.regs->gpr[14/*R14_bcp*/]; + uint64_t istate_bcp = istate->bcp; + uint64_t code_start = (uint64_t)(((Method*)(istate->method))->code_base()); + uint64_t code_end = (uint64_t)(((Method*)istate->method)->code_base() + ((Method*)istate->method)->code_size()); + if (istate_bcp >= code_start && istate_bcp < code_end) { + // we have a valid bcp, don't touch it, do nothing + } else if (reg_bcp >= code_start && reg_bcp < code_end) { + istate->bcp = reg_bcp; + } else { + return false; + } + } + if (!ret_frame.safe_for_sender(this)) { + // nothing else to try if the frame isn't good + return false; + } + *fr_addr = ret_frame; + return true; + } + // nothing else to try + return false; +} + // Forte Analyzer AsyncGetCallTrace profiling support is not implemented on Linux/PPC. bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, bool isInJava) { - Unimplemented(); - return false; + assert(this->is_Java_thread(), "must be JavaThread"); + return pd_get_top_frame_for_profiling(fr_addr, ucontext, isInJava); } void JavaThread::cache_global_variables() { } diff --git a/src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp b/src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp index 237070ec1fe49fa8483386840198f362b3ab8ad3..6c8635f58e195f7b89332e42859d7eb4019f5a50 100644 --- a/src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp +++ b/src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp @@ -62,6 +62,8 @@ bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, bool isInJava); + bool pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava); + protected: // -Xprof support diff --git a/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp b/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp index 2a07de42ef6558eaa9d67f633af34bb1ed3dcff2..16a0686a6cc53a419e14379ac55d1a5eac4fb028 100644 --- a/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp +++ b/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp @@ -544,7 +544,7 @@ JVM_handle_linux_signal(int sig, // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away // (no destructors can be run) - os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + os::ThreadCrashProtection::check_crash_protection(sig, t); SignalHandlerMark shm(t); diff --git a/src/os_cpu/linux_x86/vm/os_linux_x86.cpp b/src/os_cpu/linux_x86/vm/os_linux_x86.cpp index 1a7375afc7b7486085f7392859a77eb735690aba..c35d8677fb0f29a96a1973b6973894486e37c8fd 100644 --- a/src/os_cpu/linux_x86/vm/os_linux_x86.cpp +++ b/src/os_cpu/linux_x86/vm/os_linux_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -222,7 +222,7 @@ JVM_handle_linux_signal(int sig, // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away // (no destructors can be run) - os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + os::ThreadCrashProtection::check_crash_protection(sig, t); SignalHandlerMark shm(t); diff --git a/src/os_cpu/linux_zero/vm/thread_linux_zero.hpp b/src/os_cpu/linux_zero/vm/thread_linux_zero.hpp index 39f77f858d039d0775f3939f961fb88b20723402..94bc83a30cd07fdeb0910aa2846514783a450cf5 100644 --- a/src/os_cpu/linux_zero/vm/thread_linux_zero.hpp +++ b/src/os_cpu/linux_zero/vm/thread_linux_zero.hpp @@ -112,6 +112,14 @@ ShouldNotCallThis(); } + bool pd_get_top_frame_for_profiling(frame* fr_addr, + void* ucontext, + bool isInJava) { + ShouldNotCallThis(); + return false; // silence compile warning + } + + // These routines are only used on cpu architectures that // have separate register stacks (Itanium). static bool register_stack_overflow() { return false; } diff --git a/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp b/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp index 1785dc374b365bf25f4ccee0aa1272dc89485abf..77fcc52e6859e03d22e2a7b8d417a0b42a03a753 100644 --- a/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp +++ b/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp @@ -312,7 +312,7 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away // (no destructors can be run) - os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + os::ThreadCrashProtection::check_crash_protection(sig, t); SignalHandlerMark shm(t); diff --git a/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp b/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp index 36bf8058a9f56cfd5f8b92351362f5462441c41e..21c4570005b2c770e10bbaa9436ce3a9f9134652 100644 --- a/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp +++ b/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp @@ -369,7 +369,7 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away // (no destructors can be run) - os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + os::ThreadCrashProtection::check_crash_protection(sig, t); SignalHandlerMark shm(t); diff --git a/src/os_cpu/solaris_x86/vm/os_solaris_x86.inline.hpp b/src/os_cpu/solaris_x86/vm/os_solaris_x86.inline.hpp index cedbe5e50338444e94acd2b143d0917d95895cc8..db90eb4b1f2c44c659d06e86fdb7c2065d7e1cbf 100644 --- a/src/os_cpu/solaris_x86/vm/os_solaris_x86.inline.hpp +++ b/src/os_cpu/solaris_x86/vm/os_solaris_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -27,6 +27,8 @@ #include "runtime/os.hpp" +extern "C" jlong _raw_rdtsc(); // In .il file + inline jlong os::rdtsc() { return _raw_rdtsc(); } #endif // OS_CPU_SOLARIS_X86_VM_OS_SOLARIS_X86_INLINE_HPP diff --git a/src/share/tools/hsdis/hsdis.c b/src/share/tools/hsdis/hsdis.c index 7bef1040fbd882a8644dd8e03c3db877098f7b5f..1907d479e147cc75774adfbbfa53e0d967e48f5c 100644 --- a/src/share/tools/hsdis/hsdis.c +++ b/src/share/tools/hsdis/hsdis.c @@ -438,6 +438,7 @@ static void parse_caller_options(struct hsdis_app_data* app_data, const char* ca } p = q; } + *iop = '\0'; } static void print_help(struct hsdis_app_data* app_data, diff --git a/src/share/vm/adlc/archDesc.cpp b/src/share/vm/adlc/archDesc.cpp index 303106b0e7ae062e82a3c24a38c4ebf754cab927..f3f5970b4f1052114b3a6f22a6e78aa87f61a778 100644 --- a/src/share/vm/adlc/archDesc.cpp +++ b/src/share/vm/adlc/archDesc.cpp @@ -43,32 +43,6 @@ char *toUpper(const char *str) { return result; } -// Utilities to characterize effect statements -static bool is_def(int usedef) { - switch(usedef) { - case Component::DEF: - case Component::USE_DEF: return true; break; - } - return false; -} - -static bool is_use(int usedef) { - switch(usedef) { - case Component::USE: - case Component::USE_DEF: - case Component::USE_KILL: return true; break; - } - return false; -} - -static bool is_kill(int usedef) { - switch(usedef) { - case Component::KILL: - case Component::USE_KILL: return true; break; - } - return false; -} - //---------------------------ChainList Methods------------------------------- ChainList::ChainList() { } diff --git a/src/share/vm/adlc/filebuff.cpp b/src/share/vm/adlc/filebuff.cpp index 5f54d72463897bfc4573658d78097d57d7ae8584..71daa5cc4bd3b3e2a9d1b0a4b5588e6cb1a16cb4 100644 --- a/src/share/vm/adlc/filebuff.cpp +++ b/src/share/vm/adlc/filebuff.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -89,200 +89,6 @@ char *FileBuff::get_line(void) { return retval; } -//------------------------------FileBuffRegion--------------------------------- -// Create a new region in a FileBuff. -FileBuffRegion::FileBuffRegion( FileBuff* bufr, int soln, int ln, - int off, int len) -: _bfr(bufr), _sol(soln), _line(ln), _offset(off), _length(len) { - _next = NULL; // No chained regions -} - -//------------------------------~FileBuffRegion-------------------------------- -// Delete the entire linked list of buffer regions. -FileBuffRegion::~FileBuffRegion() { - if( _next ) delete _next; -} - -//------------------------------copy------------------------------------------- -// Deep copy a FileBuffRegion -FileBuffRegion *FileBuffRegion::copy() { - if( !this ) return NULL; // The empty buffer region - FileBuffRegion *br = new FileBuffRegion(_bfr,_sol,_line,_offset,_length); - if( _next ) br->_next = _next->copy(); - return br; -} - -//------------------------------merge------------------------------------------ -// Merge another buffer region into this buffer region. Make overlapping areas -// become a single region. Remove (delete) the input FileBuffRegion. -// Since the buffer regions are sorted by file offset, this is a varient of a -// "sorted-merge" running in linear time. -FileBuffRegion *FileBuffRegion::merge( FileBuffRegion *br ) { - if( !br ) return this; // Merging nothing - if( !this ) return br; // Merging into nothing - - assert( _bfr == br->_bfr, "" ); // Check for pointer-equivalent buffers - - if( _offset < br->_offset ) { // "this" starts before "br" - if( _offset+_length < br->_offset ) { // "this" ends before "br" - if( _next ) _next->merge( br ); // Merge with remainder of list - else _next = br; // No more in this list; just append. - } else { // Regions overlap. - int l = br->_offset + br->_length - _offset; - if( l > _length ) _length = l; // Pick larger region - FileBuffRegion *nr = br->_next; // Get rest of region - br->_next = NULL; // Remove indication of rest of region - delete br; // Delete this region (it's been subsumed). - if( nr ) merge( nr ); // Merge with rest of region - } // End of if regions overlap or not. - } else { // "this" starts after "br" - if( br->_offset+br->_length < _offset ) { // "br" ends before "this" - FileBuffRegion *nr = new FileBuffRegion(_bfr,_sol,_line,_offset,_length); - nr->_next = _next; // Structure copy "this" guy to "nr" - *this = *br; // Structure copy "br" over "this". - br->_next = NULL; // Remove indication of rest of region - delete br; // Delete this region (it's been copied) - merge( nr ); // Finish merging - } else { // Regions overlap. - int l = _offset + _length - br->_offset; - if( l > _length ) _length = l; // Pick larger region - _offset = br->_offset; // Start with earlier region - _sol = br->_sol; // Also use earlier line start - _line = br->_line; // Also use earlier line - FileBuffRegion *nr = br->_next; // Get rest of region - br->_next = NULL; // Remove indication of rest of region - delete br; // Delete this region (it's been subsumed). - if( nr ) merge( nr ); // Merge with rest of region - } // End of if regions overlap or not. - } - return this; -} - -//------------------------------expandtab-------------------------------------- -static int expandtab( ostream &os, int off, char c, char fill1, char fill2 ) { - if( c == '\t' ) { // Tab? - do os << fill1; // Expand the tab; Output space - while( (++off) & 7 ); // Expand to tab stop - } else { // Normal character - os << fill2; // Display normal character - off++; // Increment "cursor" offset - } - return off; -} - -//------------------------------printline-------------------------------------- -// Print and highlite a region of a line. Return the amount of highliting left -// to do (i.e. highlite length minus length of line). -static int printline( ostream& os, const char *fname, int line, - const char *_sol, int skip, int len ) { - - // Display the entire tab-expanded line - os << fname << ":" << line << ": "; - const char *t = strchr(_sol,'\n')+1; // End of line - int off = 0; // Cursor offset for tab expansion - const char *s = _sol; // Nice string pointer - while( t-s ) { // Display whole line - char c = *s++; // Get next character to display - off = expandtab(os,off,c,' ',c); - } - - // Display the tab-expanded skippings before underlining. - os << fname << ":" << line << ": "; - off = 0; // Cursor offset for tab expansion - s = _sol; // Restart string pointer - - // Start underlining. - if( skip != -1 ) { // The no-start-indicating flag - const char *u = _sol+skip; // Amount to skip - while( u-s ) // Display skipped part - off = expandtab(os,off,*s++,' ',' '); - os << '^'; // Start region - off++; // Moved cursor - len--; // 1 less char to do - if( *s++ == '\t' ) // Starting character is a tab? - off = expandtab(os,off,'\t','-','^'); - } - - // Long region doesn't end on this line - int llen = (int)(t-s); // Length of line, minus what's already done - if( len > llen ) { // Doing entire rest of line? - while( t-s ) // Display rest of line - off = expandtab(os,off,*s++,'-','-'); - os << '\n'; // EOL - return len-llen; // Return what's not yet done. - } - - // Region does end on this line. This code fails subtly if the region ends - // in a tab character. - int i; - for( i=1; iprint(os); // Print region - brp = brp->_next; // Chain to next - } - return os; // Return final stream -} - -//------------------------------print------------------------------------------ -// Print the FileBuffRegion to a stream. FileBuffRegions are printed with the -// filename and line number to the left, and complete text lines to the right. -// Selected portions (portions of a line actually in the FileBuffRegion are -// underlined. Ellipses are used for long multi-line regions. -//void FileBuffRegion::print( std::ostream& os ) { -void FileBuffRegion::print( ostream& os ) { - if( !this ) return; // Nothing to print - char *s = _bfr->get_line(); - int skip = (int)(_offset - _sol); // Amount to skip to start of data - int len = printline( os, _bfr->_fp->_name, _line, s, skip, _length ); - - if( !len ) return; // All done; exit - - // Here we require at least 2 lines - int off1 = _length - len + skip; // Length of line 1 - int off2 = off1 + _sol; // Offset to start of line 2 - char *s2 = _bfr->get_line(); // Start of line 2 - char *s3 = strchr( s2, '\n' )+1; // Start of line 3 (unread) - if( len <= (s3-s2) ) { // It all fits on the next line - printline( os, _bfr->_fp->_name, _line+1, s2, -1, len ); // Print&underline - return; - } - - // Here we require at least 3 lines - int off3 = off2 + (int)(s3-s2); // Offset to start of line 3 - s3 = _bfr->get_line(); // Start of line 3 (read) - const char *s4 = strchr( s3, '\n' )+1;// Start of line 4 (unread) - if( len < (s4-s3) ) { // It all fits on the next 2 lines - s2 = _bfr->get_line(); - len = printline( os, _bfr->_fp->_name, _line+1, s2, -1, len ); // Line 2 - s3 = _bfr->get_line(); - printline( os, _bfr->_fp->_name, _line+2, s3, -1, len ); // Line 3 - return; - } - - // Here we require at least 4 lines. - // Print only the 1st and last line, with ellipses in middle. - os << "...\n"; // The ellipses - int cline = _line+1; // Skipped 2 lines - do { // Do until find last line - len -= (int)(s3-s2); // Remove length of line - cline++; // Next line - s2 = _bfr->get_line(); // Get next line from end of this line - s3 = strchr( s2, '\n' ) + 1;// Get end of next line - } while( len > (s3-s2) ); // Repeat until last line - printline( os, _bfr->_fp->_name, cline, s2, -1, len ); // Print & underline -} - //------------------------------file_error------------------------------------- void FileBuff::file_error(int flag, int linenum, const char *fmt, ...) { diff --git a/src/share/vm/adlc/filebuff.hpp b/src/share/vm/adlc/filebuff.hpp index 894a2c5bad21a6443bdeb7b991f073211a4d37ab..292fd1781f646f7f339999a60e20dd913be706d2 100644 --- a/src/share/vm/adlc/filebuff.hpp +++ b/src/share/vm/adlc/filebuff.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -46,7 +46,6 @@ class ArchDesc; // This class defines a nicely behaved buffer of text. Entire file of text // is read into buffer at creation, with sentinels at start and end. class FileBuff { - friend class FileBuffRegion; private: long _bufferSize; // Size of text holding buffer. long _offset; // Expected filepointer offset. @@ -82,29 +81,4 @@ class FileBuff { // when the pointer is valid (i.e. just obtained from getline()). long getoff(const char* s) { return _bufoff + (long)(s - _buf); } }; - -//------------------------------FileBuffRegion--------------------------------- -// A buffer region is really a region of some file, specified as a linked list -// of offsets and lengths. These regions can be merged; overlapping regions -// will coalesce. -class FileBuffRegion { - public: // Workaround dev-studio friend/private bug - FileBuffRegion *_next; // Linked list of regions sorted by offset. - private: - FileBuff *_bfr; // The Buffer of the file - int _offset, _length; // The file area - int _sol; // Start of line where the file area starts - int _line; // First line of region - - public: - FileBuffRegion(FileBuff*, int sol, int line, int offset, int len); - ~FileBuffRegion(); - - FileBuffRegion *copy(); // Deep copy - FileBuffRegion *merge(FileBuffRegion*); // Merge 2 regions; delete input - - void print(ostream&); - friend ostream& operator<< (ostream&, FileBuffRegion&); -}; - #endif // SHARE_VM_ADLC_FILEBUFF_HPP diff --git a/src/share/vm/adlc/main.cpp b/src/share/vm/adlc/main.cpp index 9c558a5f564e45b2d21def2f4cdd300ca910590c..0e0ea74dc1abdb5df0a143e2bab607731bf8f31e 100644 --- a/src/share/vm/adlc/main.cpp +++ b/src/share/vm/adlc/main.cpp @@ -29,7 +29,6 @@ static void usage(ArchDesc& AD); // Print usage message and exit static char *strip_ext(char *fname); // Strip off name extension static char *base_plus_suffix(const char* base, const char *suffix);// New concatenated string -static char *prefix_plus_base_plus_suffix(const char* prefix, const char* base, const char *suffix);// New concatenated string static int get_legal_text(FileBuff &fbuf, char **legal_text); // Get pointer to legal text ArchDesc* globalAD = NULL; // global reference to Architecture Description object diff --git a/src/share/vm/adlc/output_c.cpp b/src/share/vm/adlc/output_c.cpp index b3766636d4cf80f19eb533ee7bb0da8343f7730b..199169046e9ab12312d152c2847738d0ba9ff801 100644 --- a/src/share/vm/adlc/output_c.cpp +++ b/src/share/vm/adlc/output_c.cpp @@ -35,23 +35,6 @@ static bool is_def(int usedef) { return false; } -static bool is_use(int usedef) { - switch(usedef) { - case Component::USE: - case Component::USE_DEF: - case Component::USE_KILL: return true; break; - } - return false; -} - -static bool is_kill(int usedef) { - switch(usedef) { - case Component::KILL: - case Component::USE_KILL: return true; break; - } - return false; -} - // Define an array containing the machine register names, strings. static void defineRegNames(FILE *fp, RegisterForm *registers) { if (registers) { diff --git a/src/share/vm/c1/c1_FrameMap.hpp b/src/share/vm/c1/c1_FrameMap.hpp index 052621e8d66c2f284cf4d220fe08c060720f387b..82117418bb675fa79041285e5c81881b69ee8ac9 100644 --- a/src/share/vm/c1/c1_FrameMap.hpp +++ b/src/share/vm/c1/c1_FrameMap.hpp @@ -198,6 +198,10 @@ class FrameMap : public CompilationResourceObj { return LIR_OprFact::single_cpu_metadata(cpu_reg2rnr(r)); } + static LIR_Opr as_address_opr(Register r) { + return LIR_OprFact::single_cpu_address(cpu_reg2rnr(r)); + } + FrameMap(ciMethod* method, int monitors, int reserved_argument_area_size); bool finalize_frame(int nof_slots); diff --git a/src/share/vm/c1/c1_GraphBuilder.cpp b/src/share/vm/c1/c1_GraphBuilder.cpp index ce5918e5d280d047e063f7ddf7b59846016746e0..fd595a82553eda1b5757bbf7a2e48777a54cd887 100644 --- a/src/share/vm/c1/c1_GraphBuilder.cpp +++ b/src/share/vm/c1/c1_GraphBuilder.cpp @@ -34,6 +34,7 @@ #include "ci/ciMemberName.hpp" #include "compiler/compileBroker.hpp" #include "interpreter/bytecode.hpp" +#include "jfr/jfrEvents.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/compilationPolicy.hpp" #include "utilities/bitMap.inline.hpp" @@ -3460,10 +3461,16 @@ bool GraphBuilder::try_inline_intrinsics(ciMethod* callee) { if (!InlineArrayCopy) return false; break; -#ifdef TRACE_HAVE_INTRINSICS - case vmIntrinsics::_classID: - case vmIntrinsics::_threadID: - preserves_state = true; +#ifdef JFR_HAVE_INTRINSICS +#if defined(_LP64) || !defined(TRACE_ID_CLASS_SHIFT) + case vmIntrinsics::_getClassId: + preserves_state = false; + cantrap = false; + break; +#endif + + case vmIntrinsics::_getEventWriter: + preserves_state = false; cantrap = true; break; @@ -4396,6 +4403,30 @@ void GraphBuilder::append_unsafe_CAS(ciMethod* callee) { } +static void post_inlining_event(EventCompilerInlining* event, + int compile_id, + const char* msg, + bool success, + int bci, + ciMethod* caller, + ciMethod* callee) { + assert(caller != NULL, "invariant"); + assert(callee != NULL, "invariant"); + assert(event != NULL, "invariant"); + assert(event->should_commit(), "invariant"); + JfrStructCalleeMethod callee_struct; + callee_struct.set_type(callee->holder()->name()->as_utf8()); + callee_struct.set_name(callee->name()->as_utf8()); + callee_struct.set_descriptor(callee->signature()->as_symbol()->as_utf8()); + event->set_compileId(compile_id); + event->set_message(msg); + event->set_succeeded(success); + event->set_bci(bci); + event->set_caller(caller->get_Method()); + event->set_callee(callee_struct); + event->commit(); +} + void GraphBuilder::print_inlining(ciMethod* callee, const char* msg, bool success) { CompileLog* log = compilation()->log(); if (log != NULL) { @@ -4412,6 +4443,11 @@ void GraphBuilder::print_inlining(ciMethod* callee, const char* msg, bool succes } } + EventCompilerInlining event; + if (event.should_commit()) { + post_inlining_event(&event, compilation()->env()->task()->compile_id(), msg, success, bci(), method(), callee); + } + if (!PrintInlining && !compilation()->method()->has_option("PrintInlining")) { return; } diff --git a/src/share/vm/c1/c1_LIRGenerator.cpp b/src/share/vm/c1/c1_LIRGenerator.cpp index 21cd08da9e9c1969af0ea18070bc8813d1c1ae58..03b6a66e3e8f60bb122d6e1a2b421780f128a3da 100644 --- a/src/share/vm/c1/c1_LIRGenerator.cpp +++ b/src/share/vm/c1/c1_LIRGenerator.cpp @@ -539,11 +539,11 @@ void LIRGenerator::arithmetic_op(Bytecodes::Code code, LIR_Opr result, LIR_Opr l case Bytecodes::_imul: { - bool did_strength_reduce = false; + bool did_strength_reduce = false; if (right->is_constant()) { - int c = right->as_jint(); - if (is_power_of_2(c)) { + jint c = right->as_jint(); + if (c > 0 && is_power_of_2(c)) { // do not need tmp here __ shift_left(left_op, exact_log2(c), result_op); did_strength_reduce = true; @@ -2334,7 +2334,7 @@ void LIRGenerator::do_UnsafeGetObject(UnsafeGetObject* x) { __ cmp(lir_cond_equal, src_reg, LIR_OprFact::oopConst(NULL)); __ branch(lir_cond_equal, T_OBJECT, Lcont->label()); } - LIR_Opr src_klass = new_register(T_OBJECT); + LIR_Opr src_klass = new_register(T_METADATA); if (gen_type_check) { // We have determined that offset == referent_offset && src != null. // if (src->_klass->_reference_type == REF_NONE) -> continue @@ -3059,6 +3059,51 @@ void LIRGenerator::do_IfOp(IfOp* x) { __ cmove(lir_cond(x->cond()), t_val.result(), f_val.result(), reg, as_BasicType(x->x()->type())); } +#ifdef JFR_HAVE_INTRINSICS +void LIRGenerator::do_ClassIDIntrinsic(Intrinsic* x) { + CodeEmitInfo* info = state_for(x); + CodeEmitInfo* info2 = new CodeEmitInfo(info); // Clone for the second null check + + assert(info != NULL, "must have info"); + LIRItem arg(x->argument_at(0), this); + + arg.load_item(); + LIR_Opr klass = new_register(T_METADATA); + __ move(new LIR_Address(arg.result(), java_lang_Class::klass_offset_in_bytes(), T_ADDRESS), klass, info); + LIR_Opr id = new_register(T_LONG); + ByteSize offset = KLASS_TRACE_ID_OFFSET; + LIR_Address* trace_id_addr = new LIR_Address(klass, in_bytes(offset), T_LONG); + + __ move(trace_id_addr, id); + __ logical_or(id, LIR_OprFact::longConst(0x01l), id); + __ store(id, trace_id_addr); + +#ifdef TRACE_ID_META_BITS + __ logical_and(id, LIR_OprFact::longConst(~TRACE_ID_META_BITS), id); +#endif +#ifdef TRACE_ID_SHIFT + __ unsigned_shift_right(id, TRACE_ID_SHIFT, id); +#endif + + __ move(id, rlock_result(x)); +} + +void LIRGenerator::do_getEventWriter(Intrinsic* x) { + LabelObj* L_end = new LabelObj(); + + LIR_Address* jobj_addr = new LIR_Address(getThreadPointer(), + in_bytes(THREAD_LOCAL_WRITER_OFFSET_JFR), + T_OBJECT); + LIR_Opr result = rlock_result(x); + __ move_wide(jobj_addr, result); + __ cmp(lir_cond_equal, result, LIR_OprFact::oopConst(NULL)); + __ branch(lir_cond_equal, T_OBJECT, L_end->label()); + __ move_wide(new LIR_Address(result, T_OBJECT), result); + + __ branch_destination(L_end->label()); +} +#endif + void LIRGenerator::do_RuntimeCall(address routine, int expected_arguments, Intrinsic* x) { assert(x->number_of_arguments() == expected_arguments, "wrong type"); LIR_Opr reg = result_register_for(x->type()); @@ -3115,11 +3160,15 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) { break; } -#ifdef TRACE_HAVE_INTRINSICS - case vmIntrinsics::_threadID: do_ThreadIDIntrinsic(x); break; - case vmIntrinsics::_classID: do_ClassIDIntrinsic(x); break; +#ifdef JFR_HAVE_INTRINSICS + case vmIntrinsics::_getClassId: + do_ClassIDIntrinsic(x); + break; + case vmIntrinsics::_getEventWriter: + do_getEventWriter(x); + break; case vmIntrinsics::_counterTime: - do_RuntimeCall(CAST_FROM_FN_PTR(address, TRACE_TIME_METHOD), 0, x); + do_RuntimeCall(CAST_FROM_FN_PTR(address, JFR_TIME_FUNCTION), 0, x); break; #endif @@ -3299,7 +3348,7 @@ void LIRGenerator::profile_parameters_at_call(ProfileCall* x) { void LIRGenerator::do_ProfileCall(ProfileCall* x) { // Need recv in a temporary register so it interferes with the other temporaries LIR_Opr recv = LIR_OprFact::illegalOpr; - LIR_Opr mdo = new_register(T_OBJECT); + LIR_Opr mdo = new_register(T_METADATA); // tmp is used to hold the counters on SPARC LIR_Opr tmp = new_pointer_register(); diff --git a/src/share/vm/c1/c1_LIRGenerator.hpp b/src/share/vm/c1/c1_LIRGenerator.hpp index 8312a4bb6f61ff9316c01b112bbe93164c8ddebc..cb16361764e6425ebae65dc59d72acbd29f699fa 100644 --- a/src/share/vm/c1/c1_LIRGenerator.hpp +++ b/src/share/vm/c1/c1_LIRGenerator.hpp @@ -28,6 +28,7 @@ #include "c1/c1_Instruction.hpp" #include "c1/c1_LIR.hpp" #include "ci/ciMethodData.hpp" +#include "jfr/support/jfrIntrinsics.hpp" #include "utilities/sizes.hpp" // The classes responsible for code emission and register allocation @@ -308,7 +309,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { // is_strictfp is only needed for mul and div (and only generates different code on i486) void arithmetic_op(Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, bool is_strictfp, LIR_Opr tmp, CodeEmitInfo* info = NULL); // machine dependent. returns true if it emitted code for the multiply - bool strength_reduce_multiply(LIR_Opr left, int constant, LIR_Opr result, LIR_Opr tmp); + bool strength_reduce_multiply(LIR_Opr left, jint constant, LIR_Opr result, LIR_Opr tmp); void store_stack_parameter (LIR_Opr opr, ByteSize offset_from_sp_in_bytes); @@ -436,9 +437,9 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { void do_SwitchRanges(SwitchRangeArray* x, LIR_Opr value, BlockBegin* default_sux); void do_RuntimeCall(address routine, int expected_arguments, Intrinsic* x); -#ifdef TRACE_HAVE_INTRINSICS - void do_ThreadIDIntrinsic(Intrinsic* x); +#ifdef JFR_HAVE_INTRINSICS void do_ClassIDIntrinsic(Intrinsic* x); + void do_getEventWriter(Intrinsic* x); #endif ciKlass* profile_type(ciMethodData* md, int md_first_offset, int md_offset, intptr_t profiled_k, Value arg, LIR_Opr& mdp, bool not_null, ciKlass* signature_at_call_k, diff --git a/src/share/vm/c1/c1_Runtime1.cpp b/src/share/vm/c1/c1_Runtime1.cpp index 5a614b6a22462dd4558f7023c573bde1326b2586..0786fd7a53205f3c01d8b0a17aa106474c51babf 100644 --- a/src/share/vm/c1/c1_Runtime1.cpp +++ b/src/share/vm/c1/c1_Runtime1.cpp @@ -41,6 +41,7 @@ #include "gc_interface/collectedHeap.hpp" #include "interpreter/bytecode.hpp" #include "interpreter/interpreter.hpp" +#include "jfr/support/jfrIntrinsics.hpp" #include "memory/allocation.inline.hpp" #include "memory/barrierSet.hpp" #include "memory/oopFactory.hpp" @@ -296,8 +297,8 @@ const char* Runtime1::name_for_address(address entry) { FUNCTION_CASE(entry, SharedRuntime::dtrace_method_exit); FUNCTION_CASE(entry, is_instance_of); FUNCTION_CASE(entry, trace_block_entry); -#ifdef TRACE_HAVE_INTRINSICS - FUNCTION_CASE(entry, TRACE_TIME_METHOD); +#ifdef JFR_HAVE_INTRINSICS + FUNCTION_CASE(entry, JFR_TIME_FUNCTION); #endif FUNCTION_CASE(entry, StubRoutines::updateBytesCRC32()); diff --git a/src/share/vm/ci/ciEnv.cpp b/src/share/vm/ci/ciEnv.cpp index 54b0deb428af65859ea1c90066c07e6c42512b13..e4705070920961b344fd103092da75ad14339435 100644 --- a/src/share/vm/ci/ciEnv.cpp +++ b/src/share/vm/ci/ciEnv.cpp @@ -40,6 +40,7 @@ #include "compiler/compilerOracle.hpp" #include "gc_interface/collectedHeap.inline.hpp" #include "interpreter/linkResolver.hpp" +#include "jfr/jfrEvents.hpp" #include "memory/allocation.inline.hpp" #include "memory/oopFactory.hpp" #include "memory/universe.inline.hpp" diff --git a/src/share/vm/ci/ciMethod.hpp b/src/share/vm/ci/ciMethod.hpp index 5560e6da813c2a8d50774c05a32f5502743df48b..307452422bb1c178d9e18eec3a9ddc67d6952ead 100644 --- a/src/share/vm/ci/ciMethod.hpp +++ b/src/share/vm/ci/ciMethod.hpp @@ -93,12 +93,6 @@ class ciMethod : public ciMetadata { ciMethod(methodHandle h_m, ciInstanceKlass* holder); ciMethod(ciInstanceKlass* holder, ciSymbol* name, ciSymbol* signature, ciInstanceKlass* accessor); - Method* get_Method() const { - Method* m = (Method*)_metadata; - assert(m != NULL, "illegal use of unloaded method"); - return m; - } - oop loader() const { return _holder->loader(); } const char* type_string() { return "ciMethod"; } @@ -156,6 +150,11 @@ class ciMethod : public ciMetadata { } } + Method* get_Method() const { + Method* m = (Method*)_metadata; + assert(m != NULL, "illegal use of unloaded method"); + return m; + } // Method code and related information. address code() { if (_code == NULL) load_code(); return _code; } diff --git a/src/share/vm/classfile/classFileParser.cpp b/src/share/vm/classfile/classFileParser.cpp index 44599b673234e2f1f920cd7465ad3c45993dafad..44df833dd73716508f82691c3a8916edcd49839c 100644 --- a/src/share/vm/classfile/classFileParser.cpp +++ b/src/share/vm/classfile/classFileParser.cpp @@ -3884,14 +3884,14 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, access_flags.set_flags(flags); // This class and superclass - u2 this_class_index = cfs->get_u2_fast(); + _this_class_index = cfs->get_u2_fast(); check_property( - valid_cp_range(this_class_index, cp_size) && - cp->tag_at(this_class_index).is_unresolved_klass(), + valid_cp_range(_this_class_index, cp_size) && + cp->tag_at(_this_class_index).is_unresolved_klass(), "Invalid this class index %u in constant pool in class file %s", - this_class_index, CHECK_(nullHandle)); + _this_class_index, CHECK_(nullHandle)); - Symbol* class_name = cp->unresolved_klass_at(this_class_index); + Symbol* class_name = cp->unresolved_klass_at(_this_class_index); assert(class_name != NULL, "class_name can't be null"); // It's important to set parsed_name *before* resolving the super class. @@ -4122,9 +4122,9 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, // that changes, then InstanceKlass::idnum_can_increment() // has to be changed accordingly. this_klass->set_initial_method_idnum(methods->length()); - this_klass->set_name(cp->klass_name_at(this_class_index)); + this_klass->set_name(cp->klass_name_at(_this_class_index)); if (is_anonymous()) // I am well known to myself - cp->klass_at_put(this_class_index, this_klass()); // eagerly resolve + cp->klass_at_put(_this_class_index, this_klass()); // eagerly resolve this_klass->set_minor_version(minor_version); this_klass->set_major_version(major_version); @@ -4270,6 +4270,8 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, preserve_this_klass = this_klass(); } + JFR_ONLY(INIT_ID(preserve_this_klass);) + // Create new handle outside HandleMark (might be needed for // Extended Class Redefinition) instanceKlassHandle this_klass (THREAD, preserve_this_klass); @@ -5288,3 +5290,25 @@ char* ClassFileParser::skip_over_field_signature(char* signature, } return NULL; } + +#if INCLUDE_JFR + +// Caller responsible for ResourceMark +// clone stream with rewound position +ClassFileStream* ClassFileParser::clone_stream() const { + assert(_stream != NULL, "invariant"); + + return _stream->clone(); +} + +void ClassFileParser::set_klass_to_deallocate(InstanceKlass* klass) { +#ifdef ASSERT + if (klass != NULL) { + assert(NULL == _klass, "leaking?"); + } +#endif + + _klass = klass; +} + +#endif // INCLUDE_JFR diff --git a/src/share/vm/classfile/classFileParser.hpp b/src/share/vm/classfile/classFileParser.hpp index 73ccdeb724d0b0fad8c8418a0577b997bf62972f..3a5b0fd1097f11238c95b9d4ac992bdd5a58aba6 100644 --- a/src/share/vm/classfile/classFileParser.hpp +++ b/src/share/vm/classfile/classFileParser.hpp @@ -47,6 +47,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { bool _relax_verify; u2 _major_version; u2 _minor_version; + u2 _this_class_index; Symbol* _class_name; ClassLoaderData* _loader_data; KlassHandle _host_klass; @@ -491,6 +492,13 @@ PRAGMA_DIAG_POP static void check_super_interface_access(instanceKlassHandle this_klass, TRAPS); static void check_final_method_override(instanceKlassHandle this_klass, TRAPS); static void check_illegal_static_method(instanceKlassHandle this_klass, TRAPS); + + u2 this_class_index() const { return _this_class_index; } + +#if INCLUDE_JFR + ClassFileStream* clone_stream() const; + void set_klass_to_deallocate(InstanceKlass* klass); +#endif // INCLUDE_JFR }; #endif // SHARE_VM_CLASSFILE_CLASSFILEPARSER_HPP diff --git a/src/share/vm/classfile/classFileStream.cpp b/src/share/vm/classfile/classFileStream.cpp index 4cd811f88b37f58501888de65608e46e8b8b8031..046b63a1eebd1e270df51bdeadc3bb305f38a4b0 100644 --- a/src/share/vm/classfile/classFileStream.cpp +++ b/src/share/vm/classfile/classFileStream.cpp @@ -30,12 +30,12 @@ void ClassFileStream::truncated_file_error(TRAPS) { THROW_MSG(vmSymbols::java_lang_ClassFormatError(), "Truncated class file"); } -ClassFileStream::ClassFileStream(u1* buffer, int length, const char* source) { +ClassFileStream::ClassFileStream(u1* buffer, int length, const char* source, bool need_verify) { _buffer_start = buffer; _buffer_end = buffer + length; _current = buffer; _source = source; - _need_verify = false; + _need_verify = need_verify; } u1 ClassFileStream::get_u1(TRAPS) { @@ -100,3 +100,31 @@ void ClassFileStream::skip_u4(int length, TRAPS) { } _current += length * 4; } + +#if INCLUDE_JFR + +u1* ClassFileStream::clone_buffer() const { + u1* const new_buffer_start = NEW_RESOURCE_ARRAY(u1, length()); + memcpy(new_buffer_start, _buffer_start, length()); + return new_buffer_start; +} + +const char* const ClassFileStream::clone_source() const { + const char* const src = source(); + char* source_copy = NULL; + if (src != NULL) { + size_t source_len = strlen(src); + source_copy = NEW_RESOURCE_ARRAY(char, source_len + 1); + strncpy(source_copy, src, source_len + 1); + } + return source_copy; +} + +ClassFileStream* ClassFileStream::clone() const { + u1* const new_buffer_start = clone_buffer(); + return new ClassFileStream(new_buffer_start, + length(), + clone_source(), + need_verify()); +} +#endif // INCLUDE_JFR diff --git a/src/share/vm/classfile/classFileStream.hpp b/src/share/vm/classfile/classFileStream.hpp index cf69c32eba7f950e22dcfe4e22530a3e1beca878..1587c7b5e666b6f2d0113ea89ffa3d96c22da72f 100644 --- a/src/share/vm/classfile/classFileStream.hpp +++ b/src/share/vm/classfile/classFileStream.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, 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 @@ -57,15 +57,25 @@ class ClassFileStream: public ResourceObj { bool _need_verify; // True if verification is on for the class file void truncated_file_error(TRAPS); + +#if INCLUDE_JFR + u1* clone_buffer() const; + const char* const clone_source() const; +#endif + public: // Constructor - ClassFileStream(u1* buffer, int length, const char* source); + ClassFileStream(u1* buffer, int length, const char* source, bool need_verify = false); // Buffer access u1* buffer() const { return _buffer_start; } int length() const { return _buffer_end - _buffer_start; } u1* current() const { return _current; } void set_current(u1* pos) { _current = pos; } + // for relative positioning + juint current_offset() const { + return (juint)(_current - _buffer_start); + } const char* source() const { return _source; } void set_verify(bool flag) { _need_verify = flag; } @@ -140,6 +150,12 @@ class ClassFileStream: public ResourceObj { // Tells whether eos is reached bool at_eos() const { return _current == _buffer_end; } + +#if INCLUDE_JFR + ClassFileStream* clone() const; + + bool need_verify() const { return _need_verify; } +#endif }; #endif // SHARE_VM_CLASSFILE_CLASSFILESTREAM_HPP diff --git a/src/share/vm/classfile/classLoader.cpp b/src/share/vm/classfile/classLoader.cpp index a33263b0f42127fcfbe63813336db34327b9d053..b9f46dfd841cf3976af74fa47aef503a79254982 100644 --- a/src/share/vm/classfile/classLoader.cpp +++ b/src/share/vm/classfile/classLoader.cpp @@ -162,6 +162,64 @@ bool string_ends_with(const char* str, const char* str_to_find) { return (strncmp(str + (str_len - str_to_find_len), str_to_find, str_to_find_len) == 0); } +// Used to obtain the package name from a fully qualified class name. +// It is the responsibility of the caller to establish a ResourceMark. +const char* ClassLoader::package_from_name(const char* const class_name, bool* bad_class_name) { + if (class_name == NULL) { + if (bad_class_name != NULL) { + *bad_class_name = true; + } + return NULL; + } + + if (bad_class_name != NULL) { + *bad_class_name = false; + } + + const char* const last_slash = strrchr(class_name, '/'); + if (last_slash == NULL) { + // No package name + return NULL; + } + + char* class_name_ptr = (char*) class_name; + // Skip over '['s + if (*class_name_ptr == '[') { + do { + class_name_ptr++; + } while (*class_name_ptr == '['); + + // Fully qualified class names should not contain a 'L'. + // Set bad_class_name to true to indicate that the package name + // could not be obtained due to an error condition. + // In this situation, is_same_class_package returns false. + if (*class_name_ptr == 'L') { + if (bad_class_name != NULL) { + *bad_class_name = true; + } + return NULL; + } + } + + int length = last_slash - class_name_ptr; + + // A class name could have just the slash character in the name. + if (length <= 0) { + // No package name + if (bad_class_name != NULL) { + *bad_class_name = true; + } + return NULL; + } + + // drop name after last slash (including slash) + // Ex., "java/lang/String.class" => "java/lang" + char* pkg_name = NEW_RESOURCE_ARRAY(char, length + 1); + strncpy(pkg_name, class_name_ptr, length); + *(pkg_name+length) = '\0'; + + return (const char *)pkg_name; +} MetaIndex::MetaIndex(char** meta_package_names, int num_meta_package_names) { if (num_meta_package_names == 0) { @@ -1134,6 +1192,15 @@ instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) { } return h; } + +#if INCLUDE_JFR + { + InstanceKlass* ik = result(); + ON_KLASS_CREATION(ik, parser, THREAD); + result = instanceKlassHandle(ik); + } +#endif + h = context.record_result(classpath_index, e, result, THREAD); } else { if (DumpSharedSpaces) { diff --git a/src/share/vm/classfile/classLoader.hpp b/src/share/vm/classfile/classLoader.hpp index 27f5068f709887781d741e3e54015aaacd225d0b..a46839528117f633ec581588c0d71beb7574277c 100644 --- a/src/share/vm/classfile/classLoader.hpp +++ b/src/share/vm/classfile/classLoader.hpp @@ -366,6 +366,11 @@ class ClassLoader: AllStatic { // creates a class path zip entry (returns NULL if JAR file cannot be opened) static ClassPathZipEntry* create_class_path_zip_entry(const char *apath); + // obtain package name from a fully qualified class name + // *bad_class_name is set to true if there's a problem with parsing class_name, to + // distinguish from a class_name with no package name, as both cases have a NULL return value + static const char* package_from_name(const char* const class_name, bool* bad_class_name = NULL); + // Debugging static void verify() PRODUCT_RETURN; diff --git a/src/share/vm/classfile/classLoaderData.cpp b/src/share/vm/classfile/classLoaderData.cpp index 4f02ac9da4b6d5888e03126a7be522fc0e9ef0df..690e1e570fb40efbf2491942a68076300af0c879 100644 --- a/src/share/vm/classfile/classLoaderData.cpp +++ b/src/share/vm/classfile/classLoaderData.cpp @@ -64,9 +64,6 @@ #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" -#if INCLUDE_TRACE -#include "trace/tracing.hpp" -#endif ClassLoaderData * ClassLoaderData::_the_null_class_loader_data = NULL; @@ -81,7 +78,8 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_anonymous, Depen _claimed(0), _jmethod_ids(NULL), _handles(), _deallocate_list(NULL), _next(NULL), _dependencies(dependencies), _metaspace_lock(new Mutex(Monitor::leaf+1, "Metaspace allocation lock", true)) { - // empty + + JFR_ONLY(INIT_ID(this);) } void ClassLoaderData::init_dependencies(TRAPS) { @@ -646,6 +644,16 @@ void ClassLoaderDataGraph::cld_do(CLDClosure* cl) { } } +void ClassLoaderDataGraph::cld_unloading_do(CLDClosure* cl) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); + // Only walk the head until any clds not purged from prior unloading + // (CMS doesn't purge right away). + for (ClassLoaderData* cld = _unloading; cld != _saved_unloading; cld = cld->next()) { + assert(cld->is_unloading(), "invariant"); + cl->do_cld(cld); + } +} + void ClassLoaderDataGraph::roots_cld_do(CLDClosure* strong, CLDClosure* weak) { for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->_next) { CLDClosure* closure = cld->keep_alive() ? strong : weak; @@ -740,7 +748,6 @@ bool ClassLoaderDataGraph::contains_loader_data(ClassLoaderData* loader_data) { } #endif // PRODUCT - // Move class loader data from main list to the unloaded list for unloading // and deallocation later. bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure, bool clean_alive) { @@ -788,10 +795,6 @@ bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure, boo ClassLoaderDataGraph::clean_metaspaces(); } - if (seen_dead_loader) { - post_class_unload_events(); - } - return seen_dead_loader; } @@ -828,20 +831,6 @@ void ClassLoaderDataGraph::purge() { Metaspace::purge(); } -void ClassLoaderDataGraph::post_class_unload_events(void) { -#if INCLUDE_TRACE - assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); - if (Tracing::enabled()) { - if (Tracing::is_event_enabled(TraceClassUnloadEvent)) { - assert(_unloading != NULL, "need class loader data unload list!"); - _class_unload_time = Ticks::now(); - classes_unloading_do(&class_unload_event); - } - Tracing::on_unloading_classes(); - } -#endif -} - void ClassLoaderDataGraph::free_deallocate_lists() { for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) { // We need to keep this data until InstanceKlass::purge_previous_version has been @@ -977,21 +966,3 @@ void ClassLoaderData::print_value_on(outputStream* out) const { class_loader()->print_value_on(out); } } - -#if INCLUDE_TRACE - -Ticks ClassLoaderDataGraph::_class_unload_time; - -void ClassLoaderDataGraph::class_unload_event(Klass* const k) { - - // post class unload event - EventClassUnload event(UNTIMED); - event.set_endtime(_class_unload_time); - event.set_unloadedClass(k); - oop defining_class_loader = k->class_loader(); - event.set_definingClassLoader(defining_class_loader != NULL ? - defining_class_loader->klass() : (Klass*)NULL); - event.commit(); -} - -#endif // INCLUDE_TRACE diff --git a/src/share/vm/classfile/classLoaderData.hpp b/src/share/vm/classfile/classLoaderData.hpp index 8083b70c5ada909618db71b8bb669fe97b03541c..9b901303a67d96341b58ddaf2f729908bb9f59ca 100644 --- a/src/share/vm/classfile/classLoaderData.hpp +++ b/src/share/vm/classfile/classLoaderData.hpp @@ -32,8 +32,8 @@ #include "runtime/mutex.hpp" #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" -#if INCLUDE_TRACE -#include "utilities/ticks.hpp" +#if INCLUDE_JFR +#include "jfr/support/jfrTraceIdExtension.hpp" #endif // @@ -70,7 +70,6 @@ class ClassLoaderDataGraph : public AllStatic { static bool _should_purge; static ClassLoaderData* add(Handle class_loader, bool anonymous, TRAPS); - static void post_class_unload_events(void); static void clean_metaspaces(); public: static ClassLoaderData* find_or_create(Handle class_loader, TRAPS); @@ -82,6 +81,7 @@ class ClassLoaderDataGraph : public AllStatic { static void always_strong_oops_do(OopClosure* blk, KlassClosure* klass_closure, bool must_claim); // cld do static void cld_do(CLDClosure* cl); + static void cld_unloading_do(CLDClosure* cl); static void roots_cld_do(CLDClosure* strong, CLDClosure* weak); static void keep_alive_cld_do(CLDClosure* cl); static void always_strong_cld_do(CLDClosure* cl); @@ -116,12 +116,6 @@ class ClassLoaderDataGraph : public AllStatic { #ifndef PRODUCT static bool contains_loader_data(ClassLoaderData* loader_data); #endif - -#if INCLUDE_TRACE - private: - static Ticks _class_unload_time; - static void class_unload_event(Klass* const k); -#endif }; // ClassLoaderData class @@ -213,6 +207,8 @@ class ClassLoaderData : public CHeapObj { static Metaspace* _ro_metaspace; static Metaspace* _rw_metaspace; + JFR_ONLY(DEFINE_TRACE_ID_FIELD;) + void set_next(ClassLoaderData* next) { _next = next; } ClassLoaderData* next() const { return _next; } @@ -223,11 +219,6 @@ class ClassLoaderData : public CHeapObj { Mutex* metaspace_lock() const { return _metaspace_lock; } - // GC interface. - void clear_claimed() { _claimed = 0; } - bool claimed() const { return _claimed == 1; } - bool claim(); - void unload(); bool keep_alive() const { return _keep_alive; } void classes_do(void f(Klass*)); @@ -242,6 +233,11 @@ class ClassLoaderData : public CHeapObj { public: + // GC interface. + void clear_claimed() { _claimed = 0; } + bool claimed() const { return _claimed == 1; } + bool claim(); + bool is_alive(BoolObjectClosure* is_alive_closure) const; // Accessors @@ -325,6 +321,8 @@ class ClassLoaderData : public CHeapObj { Metaspace* ro_metaspace(); Metaspace* rw_metaspace(); void initialize_shared_metaspaces(); + + JFR_ONLY(DEFINE_TRACE_ID_METHODS;) }; // An iterator that distributes Klasses to parallel worker threads. diff --git a/src/share/vm/classfile/classLoaderStats.hpp b/src/share/vm/classfile/classLoaderStats.hpp index 72904c12d7c529acc0c95400032b06210f8f07c2..b9e0fc2cd758fd7a8b4cd32596a107e9bb52e5f1 100644 --- a/src/share/vm/classfile/classLoaderStats.hpp +++ b/src/share/vm/classfile/classLoaderStats.hpp @@ -101,8 +101,17 @@ protected: } static unsigned oop_hash(oop const& s1) { - unsigned hash = (unsigned)((uintptr_t)&s1); - return hash ^ (hash >> LogMinObjAlignment); + // Robert Jenkins 1996 & Thomas Wang 1997 + // http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm + uintptr_t tmp = cast_from_oop(s1); + unsigned hash = (unsigned)tmp; + hash = ~hash + (hash << 15); + hash = hash ^ (hash >> 12); + hash = hash + (hash << 2); + hash = hash ^ (hash >> 4); + hash = hash * 2057; + hash = hash ^ (hash >> 16); + return hash; } typedef ResourceHashtableis_Watcher_thread() || Thread::current()->is_VM_thread() || + assert(Threads_lock->owned_by_self() || Thread::current()->is_Watcher_thread() || + Thread::current()->is_VM_thread() || JavaThread::current()->thread_state() == _thread_in_vm, "Java Thread is not running in vm"); // The threadStatus is only present starting in 1.5 diff --git a/src/share/vm/classfile/systemDictionary.cpp b/src/share/vm/classfile/systemDictionary.cpp index 7cd8d9542c49bb5fd0f76189aba39e7d0ccd0a49..d1d1e80426ef9ee5d1976255455604e0ee1cc113 100644 --- a/src/share/vm/classfile/systemDictionary.cpp +++ b/src/share/vm/classfile/systemDictionary.cpp @@ -38,6 +38,8 @@ #include "compiler/compileBroker.hpp" #include "interpreter/bytecodeStream.hpp" #include "interpreter/interpreter.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/jni/jfrUpcalls.hpp" #include "memory/filemap.hpp" #include "memory/gcLocker.hpp" #include "memory/oopFactory.hpp" @@ -64,9 +66,6 @@ #include "services/threadService.hpp" #include "utilities/macros.hpp" #include "utilities/ticks.hpp" -#if INCLUDE_TRACE -#include "trace/tracing.hpp" -#endif Dictionary* SystemDictionary::_dictionary = NULL; PlaceholderTable* SystemDictionary::_placeholders = NULL; @@ -96,6 +95,9 @@ bool SystemDictionary::_has_checkPackageAccess = false; // lazily initialized klass variables Klass* volatile SystemDictionary::_abstract_ownable_synchronizer_klass = NULL; +#if INCLUDE_JFR +static const Symbol* jfr_event_handler_proxy = NULL; +#endif // INCLUDE_JFR // ---------------------------------------------------------------------------- // Java-level SystemLoader @@ -140,6 +142,9 @@ bool SystemDictionary::is_internal_format(Symbol* class_name) { } } +#endif +#if INCLUDE_JFR +#include "jfr/jfr.hpp" #endif // ---------------------------------------------------------------------------- @@ -631,6 +636,22 @@ instanceKlassHandle SystemDictionary::handle_parallel_super_load( return (nh); } +// utility function for class load event +static void post_class_load_event(EventClassLoad &event, + instanceKlassHandle k, + Handle initiating_loader) { +#if INCLUDE_JFR + if (event.should_commit()) { + event.set_loadedClass(k()); + event.set_definingClassLoader(k->class_loader_data()); + oop class_loader = initiating_loader.is_null() ? (oop)NULL : initiating_loader(); + event.set_initiatingClassLoader(class_loader != NULL ? + ClassLoaderData::class_loader_data_or_null(class_loader) : + (ClassLoaderData*)NULL); + event.commit(); + } +#endif +} Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle class_loader, @@ -639,7 +660,7 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, assert(name != NULL && !FieldType::is_array(name) && !FieldType::is_obj(name), "invalid class name"); - Ticks class_load_start_time = Ticks::now(); + EventClassLoad class_load_start_event; // UseNewReflection // Fix for 4474172; see evaluation for more details @@ -890,7 +911,7 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, return NULL; } - post_class_load_event(class_load_start_time, k, class_loader); + post_class_load_event(class_load_start_event, k, class_loader); #ifdef ASSERT { @@ -1023,7 +1044,7 @@ Klass* SystemDictionary::parse_stream(Symbol* class_name, TRAPS) { TempNewSymbol parsed_name = NULL; - Ticks class_load_start_time = Ticks::now(); + EventClassLoad class_load_start_event; ClassLoaderData* loader_data; if (host_klass.not_null()) { @@ -1084,7 +1105,7 @@ Klass* SystemDictionary::parse_stream(Symbol* class_name, JvmtiExport::post_class_load((JavaThread *) THREAD, k()); } - post_class_load_event(class_load_start_time, k, class_loader); + post_class_load_event(class_load_start_event, k, class_loader); } assert(host_klass.not_null() || cp_patches == NULL, "cp_patches only found with host_klass"); @@ -1126,12 +1147,13 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, // // Note: "name" is updated. - instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, - loader_data, - protection_domain, - parsed_name, - verify, - THREAD); + ClassFileParser parser(st); + instanceKlassHandle k = parser.parseClassFile(class_name, + loader_data, + protection_domain, + parsed_name, + verify, + THREAD); const char* pkg = "java/"; size_t pkglen = strlen(pkg); @@ -1166,6 +1188,14 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, assert(is_internal_format(parsed_name), "external class name format used internally"); +#if INCLUDE_JFR + { + InstanceKlass* ik = k(); + ON_KLASS_CREATION(ik, parser, THREAD); + k = instanceKlassHandle(ik); + } +#endif + // Add class just loaded // If a class loader supports parallel classloading handle parallel define requests // find_or_define_instance_class may return a different InstanceKlass @@ -1354,6 +1384,24 @@ instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Ha if (!k.is_null()) { k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh)); } + +#if INCLUDE_JFR + if (k.is_null() && (class_name == jfr_event_handler_proxy)) { + assert(jfr_event_handler_proxy != NULL, "invariant"); + // EventHandlerProxy class is generated dynamically in + // EventHandlerProxyCreator::makeEventHandlerProxyClass + // method, so we generate a Java call from here. + // + // EventHandlerProxy class will finally be defined in + // SystemDictionary::resolve_from_stream method, down + // the call stack. Bootstrap classloader is parallel-capable, + // so no concurrency issues are expected. + CLEAR_PENDING_EXCEPTION; + k = JfrUpcalls::load_event_handler_proxy_class(THREAD); + assert(!k.is_null(), "invariant"); + } +#endif + return k; } else { // Use user specified class loader to load class. Call loadClass operation on class_loader. @@ -1432,6 +1480,15 @@ instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Ha } } +static void post_class_define_event(InstanceKlass* k, const ClassLoaderData* def_cld) { + EventClassDefine event; + if (event.should_commit()) { + event.set_definedClass(k); + event.set_definingClassLoader(def_cld); + event.commit(); + } +} + void SystemDictionary::define_instance_class(instanceKlassHandle k, TRAPS) { ClassLoaderData* loader_data = k->class_loader_data(); @@ -1502,6 +1559,7 @@ void SystemDictionary::define_instance_class(instanceKlassHandle k, TRAPS) { } + post_class_define_event(k(), loader_data); } // Support parallel classloading @@ -1763,6 +1821,7 @@ bool SystemDictionary::do_unloading(BoolObjectClosure* is_alive, bool clean_aliv // First, mark for unload all ClassLoaderData referencing a dead class loader. bool unloading_occurred = ClassLoaderDataGraph::do_unloading(is_alive, clean_alive); if (unloading_occurred) { + JFR_ONLY(Jfr::on_unloading_classes();) dictionary()->do_unloading(); constraints()->purge_loader_constraints(); resolution_errors()->purge_resolution_errors(); @@ -1896,6 +1955,9 @@ void SystemDictionary::initialize(TRAPS) { _system_loader_lock_obj = oopFactory::new_intArray(0, CHECK); // Initialize basic classes initialize_preloaded_classes(CHECK); +#if INCLUDE_JFR + jfr_event_handler_proxy = SymbolTable::new_permanent_symbol("jdk/jfr/proxy/internal/EventHandlerProxy", CHECK); +#endif // INCLUDE_JFR } // Compact table of directions on the initialization of klasses: @@ -2745,26 +2807,6 @@ void SystemDictionary::verify() { constraints()->verify(dictionary(), placeholders()); } -// utility function for class load event -void SystemDictionary::post_class_load_event(const Ticks& start_time, - instanceKlassHandle k, - Handle initiating_loader) { -#if INCLUDE_TRACE - EventClassLoad event(UNTIMED); - if (event.should_commit()) { - event.set_starttime(start_time); - event.set_loadedClass(k()); - oop defining_class_loader = k->class_loader(); - event.set_definingClassLoader(defining_class_loader != NULL ? - defining_class_loader->klass() : (Klass*)NULL); - oop class_loader = initiating_loader.is_null() ? (oop)NULL : initiating_loader(); - event.set_initiatingClassLoader(class_loader != NULL ? - class_loader->klass() : (Klass*)NULL); - event.commit(); - } -#endif // INCLUDE_TRACE -} - #ifndef PRODUCT // statistics code diff --git a/src/share/vm/classfile/systemDictionary.hpp b/src/share/vm/classfile/systemDictionary.hpp index 8bb0705688c556648d943f1a9a02c7b3efe98048..20030e2895bd8d04684abc45acf8f284da8b60ca 100644 --- a/src/share/vm/classfile/systemDictionary.hpp +++ b/src/share/vm/classfile/systemDictionary.hpp @@ -78,7 +78,6 @@ class LoaderConstraintTable; template class HashtableBucket; class ResolutionErrorTable; class SymbolPropertyTable; -class Ticks; // Certain classes are preloaded, such as java.lang.Object and java.lang.String. // They are all "well-known", in the sense that no class loader is allowed @@ -667,9 +666,6 @@ protected: // Setup link to hierarchy static void add_to_hierarchy(instanceKlassHandle k, TRAPS); - // event based tracing - static void post_class_load_event(const Ticks& start_time, instanceKlassHandle k, - Handle initiating_loader); // We pass in the hashtable index so we can calculate it outside of // the SystemDictionary_lock. diff --git a/src/share/vm/classfile/vmSymbols.cpp b/src/share/vm/classfile/vmSymbols.cpp index 3d1b37e5fbc78273e0805517081697a01b947d1c..a5f89dbf81e27d84843907f678938d6a7685c394 100644 --- a/src/share/vm/classfile/vmSymbols.cpp +++ b/src/share/vm/classfile/vmSymbols.cpp @@ -328,7 +328,7 @@ vmIntrinsics::ID vmIntrinsics::for_raw_conversion(BasicType src, BasicType dest) bool vmIntrinsics::should_be_pinned(vmIntrinsics::ID id) { assert(id != vmIntrinsics::_none, "must be a VM intrinsic"); switch(id) { -#ifdef TRACE_HAVE_INTRINSICS +#ifdef JFR_HAVE_INTRINSICS case vmIntrinsics::_counterTime: #endif case vmIntrinsics::_currentTimeMillis: diff --git a/src/share/vm/classfile/vmSymbols.hpp b/src/share/vm/classfile/vmSymbols.hpp index 071bc2d2ee79b8f2f7582c1445a964baeb9ce0ae..1a547dbc7111d3be062b1202101f8f9d0d9ab044 100644 --- a/src/share/vm/classfile/vmSymbols.hpp +++ b/src/share/vm/classfile/vmSymbols.hpp @@ -25,9 +25,10 @@ #ifndef SHARE_VM_CLASSFILE_VMSYMBOLS_HPP #define SHARE_VM_CLASSFILE_VMSYMBOLS_HPP -#include "oops/symbol.hpp" +#include "jfr/support/jfrIntrinsics.hpp" #include "memory/iterator.hpp" -#include "trace/traceMacros.hpp" +#include "oops/symbol.hpp" +#include "utilities/macros.hpp" // The class vmSymbols is a name space for fast lookup of // symbols commonly used in the VM. @@ -624,8 +625,8 @@ template(classRedefinedCount_name, "classRedefinedCount") \ template(classLoader_name, "classLoader") \ \ - /* trace signatures */ \ - TRACE_TEMPLATES(template) \ + /* jfr signatures */ \ + JFR_TEMPLATES(template) \ \ /*end*/ @@ -754,7 +755,7 @@ do_intrinsic(_nanoTime, java_lang_System, nanoTime_name, void_long_signature, F_S) \ do_name( nanoTime_name, "nanoTime") \ \ - TRACE_INTRINSICS(do_intrinsic, do_class, do_name, do_signature, do_alias) \ + JFR_INTRINSICS(do_intrinsic, do_class, do_name, do_signature, do_alias) \ \ do_intrinsic(_arraycopy, java_lang_System, arraycopy_name, arraycopy_signature, F_S) \ do_name( arraycopy_name, "arraycopy") \ diff --git a/src/share/vm/code/codeBlob.cpp b/src/share/vm/code/codeBlob.cpp index 87ae60f81ec44ff834d272332e07b358d1d91292..671282a2b3f2b821739cad3216af8464c9a3c34f 100644 --- a/src/share/vm/code/codeBlob.cpp +++ b/src/share/vm/code/codeBlob.cpp @@ -58,7 +58,7 @@ #include "c1/c1_Runtime1.hpp" #endif -unsigned int align_code_offset(int offset) { +unsigned int CodeBlob::align_code_offset(int offset) { // align the size to CodeEntryAlignment return ((offset + (int)CodeHeap::header_size() + (CodeEntryAlignment-1)) & ~(CodeEntryAlignment-1)) diff --git a/src/share/vm/code/codeBlob.hpp b/src/share/vm/code/codeBlob.hpp index 4f83fac6a2601cc66d75104d2b5bafe01e7c4df1..b9b1458fd13e0bf7420fe307199b3acf60377a01 100644 --- a/src/share/vm/code/codeBlob.hpp +++ b/src/share/vm/code/codeBlob.hpp @@ -30,6 +30,15 @@ #include "runtime/frame.hpp" #include "runtime/handles.hpp" +// CodeBlob Types +// Used in the CodeCache to assign CodeBlobs to different CodeHeaps +struct CodeBlobType { + enum { + All = 0, // All types (No code cache segmentation) + NumTypes = 1 // Number of CodeBlobTypes + }; +}; + // CodeBlob - superclass for all entries in the CodeCache. // // Suptypes are: @@ -71,6 +80,7 @@ class CodeBlob VALUE_OBJ_CLASS_SPEC { public: // Returns the space needed for CodeBlob static unsigned int allocation_size(CodeBuffer* cb, int header_size); + static unsigned int align_code_offset(int offset); // Creation // a) simple CodeBlob @@ -205,6 +215,7 @@ class BufferBlob: public CodeBlob { friend class AdapterBlob; friend class VtableBlob; friend class MethodHandlesAdapterBlob; + friend class WhiteBox; private: // Creation support diff --git a/src/share/vm/code/codeCache.cpp b/src/share/vm/code/codeCache.cpp index 5f9792d8fbbf91603f450c69218c8eaca143a162..5f452ad8fc441db03602efb8bf94006eb87f23c6 100644 --- a/src/share/vm/code/codeCache.cpp +++ b/src/share/vm/code/codeCache.cpp @@ -32,6 +32,7 @@ #include "code/pcDesc.hpp" #include "compiler/compileBroker.hpp" #include "gc_implementation/shared/markSweep.hpp" +#include "jfr/jfrEvents.hpp" #include "memory/allocation.inline.hpp" #include "memory/gcLocker.hpp" #include "memory/iterator.hpp" @@ -46,9 +47,9 @@ #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" #include "services/memoryService.hpp" -#include "trace/tracing.hpp" #include "utilities/xmlstream.hpp" + // Helper class for printing in CodeCache class CodeBlob_sizes { @@ -188,6 +189,12 @@ CodeBlob* CodeCache::allocate(int size, bool is_critical) { if (cb != NULL) break; if (!_heap->expand_by(CodeCacheExpansionSize)) { // Expansion failed + if (CodeCache_lock->owned_by_self()) { + MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + report_codemem_full(); + } else { + report_codemem_full(); + } return NULL; } if (PrintCodeCacheExtension) { @@ -779,6 +786,7 @@ void CodeCache::report_codemem_full() { _codemem_full_count++; EventCodeCacheFull event; if (event.should_commit()) { + event.set_codeBlobType((u1)CodeBlobType::All); event.set_startAddress((u8)low_bound()); event.set_commitedTopAddress((u8)high()); event.set_reservedTopAddress((u8)high_bound()); diff --git a/src/share/vm/code/debugInfo.cpp b/src/share/vm/code/debugInfo.cpp index 3b01884c6e41975822f3bfaef1d98fef5401369e..f04d93f8854ae44d401b03a735911c20940db183 100644 --- a/src/share/vm/code/debugInfo.cpp +++ b/src/share/vm/code/debugInfo.cpp @@ -27,6 +27,8 @@ #include "code/debugInfoRec.hpp" #include "code/nmethod.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/thread.hpp" PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC @@ -204,14 +206,24 @@ void ConstantDoubleValue::print_on(outputStream* st) const { // ConstantOopWriteValue void ConstantOopWriteValue::write_on(DebugInfoWriteStream* stream) { - assert(JNIHandles::resolve(value()) == NULL || - Universe::heap()->is_in_reserved(JNIHandles::resolve(value())), - "Should be in heap"); +#ifdef ASSERT + { + // cannot use ThreadInVMfromNative here since in case of JVMCI compiler, + // thread is already in VM state. + ThreadInVMfromUnknown tiv; + assert(JNIHandles::resolve(value()) == NULL || + Universe::heap()->is_in_reserved(JNIHandles::resolve(value())), + "Should be in heap"); + } +#endif stream->write_int(CONSTANT_OOP_CODE); stream->write_handle(value()); } void ConstantOopWriteValue::print_on(outputStream* st) const { + // using ThreadInVMfromUnknown here since in case of JVMCI compiler, + // thread is already in VM state. + ThreadInVMfromUnknown tiv; JNIHandles::resolve(value())->print_value_on(st); } diff --git a/src/share/vm/code/vtableStubs.cpp b/src/share/vm/code/vtableStubs.cpp index 8424378d7eea416e5191aee5fdf7c4787335c3bf..8d0679bebc31267b88c6c79086c446dc13733727 100644 --- a/src/share/vm/code/vtableStubs.cpp +++ b/src/share/vm/code/vtableStubs.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, 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 @@ -110,7 +110,7 @@ void VtableStubs::initialize() { address VtableStubs::find_stub(bool is_vtable_stub, int vtable_index) { assert(vtable_index >= 0, "must be positive"); - VtableStub* s = ShareVtableStubs ? lookup(is_vtable_stub, vtable_index) : NULL; + VtableStub* s = lookup(is_vtable_stub, vtable_index); if (s == NULL) { if (is_vtable_stub) { s = create_vtable_stub(vtable_index); diff --git a/src/share/vm/compiler/compileBroker.cpp b/src/share/vm/compiler/compileBroker.cpp index f99b3782e6a6ab96ad72bca219b06855a24d8289..2a1ff0a03bd1345da4495425925b7b94df1c1cfc 100644 --- a/src/share/vm/compiler/compileBroker.cpp +++ b/src/share/vm/compiler/compileBroker.cpp @@ -30,6 +30,7 @@ #include "compiler/compileLog.hpp" #include "compiler/compilerOracle.hpp" #include "interpreter/linkResolver.hpp" +#include "jfr/jfrEvents.hpp" #include "memory/allocation.inline.hpp" #include "oops/methodData.hpp" #include "oops/method.hpp" @@ -43,7 +44,6 @@ #include "runtime/os.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/sweeper.hpp" -#include "trace/tracing.hpp" #include "utilities/dtrace.hpp" #include "utilities/events.hpp" #ifdef COMPILER1 @@ -1918,6 +1918,19 @@ static void codecache_print(bool detailed) tty->print("%s", s.as_string()); } +static void post_compilation_event(EventCompilation* event, CompileTask* task) { + assert(event != NULL, "invariant"); + assert(event->should_commit(), "invariant"); + event->set_method(task->method()); + event->set_compileId(task->compile_id()); + event->set_compileLevel(task->comp_level()); + event->set_succeded(task->is_success()); + event->set_isOsr(task->osr_bci() != CompileBroker::standard_entry_bci); + event->set_codeSize((task->code() == NULL) ? 0 : task->code()->total_size()); + event->set_inlinedBytes(task->num_inlined_bytecodes()); + event->commit(); +} + // ------------------------------------------------------------------ // CompileBroker::invoke_compiler_on_method // @@ -2015,8 +2028,9 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { compilable = ci_env.compilable(); if (ci_env.failing()) { - task->set_failure_reason(ci_env.failure_reason()); + const char* failure_reason = ci_env.failure_reason(); const char* retry_message = ci_env.retry_message(); + task->set_failure_reason(failure_reason); if (_compilation_log != NULL) { _compilation_log->log_failure(thread, task, ci_env.failure_reason(), retry_message); } @@ -2026,6 +2040,13 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { err_msg_res("COMPILE SKIPPED: %s", ci_env.failure_reason()); task->print_compilation(tty, msg); } + + EventCompilationFailure event; + if (event.should_commit()) { + event.set_compileId(compile_id); + event.set_failureMessage(failure_reason); + event.commit(); + } } else { task->mark_success(); task->set_num_inlined_bytecodes(ci_env.num_inlined_bytecodes()); @@ -2039,14 +2060,7 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { // simulate crash during compilation assert(task->compile_id() != CICrashAt, "just as planned"); if (event.should_commit()) { - event.set_method(target->get_Method()); - event.set_compileID(compile_id); - event.set_compileLevel(task->comp_level()); - event.set_succeded(task->is_success()); - event.set_isOsr(is_osr); - event.set_codeSize((task->code() == NULL) ? 0 : task->code()->total_size()); - event.set_inlinedBytes(task->num_inlined_bytecodes()); - event.commit(); + post_compilation_event(&event, task); } } pop_jni_handle_block(); diff --git a/src/share/vm/compiler/compileBroker.hpp b/src/share/vm/compiler/compileBroker.hpp index 36ccd49b8a09256e822cdf3916b49a154bebc8b5..e2ce61ab393b21dff31c1aeee89e95f55ba831fe 100644 --- a/src/share/vm/compiler/compileBroker.hpp +++ b/src/share/vm/compiler/compileBroker.hpp @@ -176,7 +176,8 @@ class CompilerCounters : public CHeapObj { // these methods should be called in a thread safe context void set_current_method(const char* method) { - strncpy(_current_method, method, (size_t)cmname_buffer_length); + strncpy(_current_method, method, (size_t)cmname_buffer_length-1); + _current_method[cmname_buffer_length-1] = '\0'; if (UsePerfData) _perf_current_method->set_value(method); } diff --git a/src/share/vm/compiler/disassembler.cpp b/src/share/vm/compiler/disassembler.cpp index 93cd9e8547b30b241f0118a56897507e0a870885..e7b32cd6b05bdf73ec06f0fc2805713fac39f9a3 100644 --- a/src/share/vm/compiler/disassembler.cpp +++ b/src/share/vm/compiler/disassembler.cpp @@ -295,6 +295,7 @@ address decode_env::handle_event(const char* event, address arg) { strlen((const char*)arg) > sizeof(buffer) - 1) { // Only print this when the mach changes strncpy(buffer, (const char*)arg, sizeof(buffer) - 1); + buffer[sizeof(buffer) - 1] = '\0'; output()->print_cr("[Disassembling for mach='%s']", arg); } } else if (match(event, "format bytes-per-line")) { diff --git a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index 02dd32fcec86931bb699911329b2d384c880d809..28ac56f2393976b40c516d61c486103bc5e8524e 100644 --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -1928,9 +1928,7 @@ NOT_PRODUCT( // Has the GC time limit been exceeded? DefNewGeneration* young_gen = _young_gen->as_DefNewGeneration(); - size_t max_eden_size = young_gen->max_capacity() - - young_gen->to()->capacity() - - young_gen->from()->capacity(); + size_t max_eden_size = young_gen->max_eden_size(); GenCollectedHeap* gch = GenCollectedHeap::heap(); GCCause::Cause gc_cause = gch->gc_cause(); size_policy()->check_gc_overhead_limit(_young_gen->used(), diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 07f881416c4ab17003c552dcc5edc825cfdc912d..3159989de66d1440d0e383b1ac1a5ca9999487b5 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -3659,6 +3659,28 @@ void G1CollectedHeap::print_all_rsets() { } #endif // PRODUCT +G1HeapSummary G1CollectedHeap::create_g1_heap_summary() { + + size_t eden_used_bytes = _young_list->eden_used_bytes(); + size_t survivor_used_bytes = _young_list->survivor_used_bytes(); + size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked(); + + size_t eden_capacity_bytes = + (g1_policy()->young_list_target_length() * HeapRegion::GrainBytes) - survivor_used_bytes; + + VirtualSpaceSummary heap_summary = create_heap_space_summary(); + return G1HeapSummary(heap_summary, heap_used, eden_used_bytes, + eden_capacity_bytes, survivor_used_bytes, num_regions()); +} + +void G1CollectedHeap::trace_heap(GCWhen::Type when, GCTracer* gc_tracer) { + const G1HeapSummary& heap_summary = create_g1_heap_summary(); + gc_tracer->report_gc_heap_summary(when, heap_summary); + + const MetaspaceSummary& metaspace_summary = create_metaspace_summary(); + gc_tracer->report_metaspace_summary(when, metaspace_summary); +} + G1CollectedHeap* G1CollectedHeap::heap() { assert(_sh->kind() == CollectedHeap::G1CollectedHeap, "not a garbage-first heap"); @@ -4184,7 +4206,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty); #endif // YOUNG_LIST_VERBOSE - g1_policy()->record_collection_pause_start(sample_start_time_sec); + g1_policy()->record_collection_pause_start(sample_start_time_sec, *_gc_tracer_stw); double scan_wait_start = os::elapsedTime(); // We have to wait until the CM threads finish scanning the diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index 08a311a21247890d95737f05e74a4f44df44755e..b08f5d4cc70eebb0056de0c4b40aff8dc72434b9 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -39,6 +39,7 @@ #include "gc_implementation/g1/g1YCTypes.hpp" #include "gc_implementation/g1/heapRegionManager.hpp" #include "gc_implementation/g1/heapRegionSet.hpp" +#include "gc_implementation/shared/gcHeapSummary.hpp" #include "gc_implementation/shared/hSpaceCounters.hpp" #include "gc_implementation/shared/parGCAllocBuffer.hpp" #include "memory/barrierSet.hpp" @@ -380,6 +381,8 @@ private: size_t size, size_t translation_factor); + void trace_heap(GCWhen::Type when, GCTracer* tracer); + double verify(bool guard, const char* msg); void verify_before_gc(); void verify_after_gc(); @@ -1636,6 +1639,8 @@ public: bool is_obj_dead_cond(const oop obj, const VerifyOption vo) const; + G1HeapSummary create_g1_heap_summary(); + // Printing virtual void print_on(outputStream* st) const; diff --git a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp index 3d44558fd7fd902eb88b861dd804a3960a23004a..32bd24c9c0363d9ee7147efe41e4226a147ac007 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp @@ -840,11 +840,11 @@ void G1CollectorPolicy::record_stop_world_start() { _stop_world_start = os::elapsedTime(); } -void G1CollectorPolicy::record_collection_pause_start(double start_time_sec) { +void G1CollectorPolicy::record_collection_pause_start(double start_time_sec, GCTracer &tracer) { // We only need to do this here as the policy will only be applied // to the GC we're about to start. so, no point is calculating this // every time we calculate / recalculate the target young length. - update_survivors_policy(); + update_survivors_policy(tracer); assert(_g1->used() == _g1->recalculate_used(), err_msg("sanity, used: " SIZE_FORMAT " recalculate_used: " SIZE_FORMAT, @@ -1454,7 +1454,7 @@ void G1CollectorPolicy::update_max_gc_locker_expansion() { } // Calculates survivor space parameters. -void G1CollectorPolicy::update_survivors_policy() { +void G1CollectorPolicy::update_survivors_policy(GCTracer &tracer) { double max_survivor_regions_d = (double) _young_list_target_length / (double) SurvivorRatio; // We use ceiling so that if max_survivor_regions_d is > 0.0 (but @@ -1468,7 +1468,7 @@ void G1CollectorPolicy::update_survivors_policy() { } _tenuring_threshold = _survivors_age_table.compute_tenuring_threshold( - HeapRegion::GrainWords * _max_survivor_regions); + HeapRegion::GrainWords * _max_survivor_regions, tracer); } bool G1CollectorPolicy::force_initial_mark_if_outside_cycle( diff --git a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp index cff5cd6fd85943f40cd7bf9942580f4278c344cc..8062150330b7696045e572d5dcee7cd810b4a949 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp @@ -685,7 +685,7 @@ public: bool need_to_start_conc_mark(const char* source, size_t alloc_word_size = 0); // Record the start and end of an evacuation pause. - void record_collection_pause_start(double start_time_sec); + void record_collection_pause_start(double start_time_sec, GCTracer &tracer); void record_collection_pause_end(double pause_time_ms, EvacuationInfo& evacuation_info); // Record the start and end of a full collection. @@ -937,7 +937,7 @@ public: void update_max_gc_locker_expansion(); // Calculates survivor space parameters. - void update_survivors_policy(); + void update_survivors_policy(GCTracer &tracer); virtual void post_heap_initialize(); }; diff --git a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp index 2683386ee403c8121fd1557ccb200d29f8cf5ca1..05d3574563317f3b8d21cee5d41157da55c1e211 100644 --- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp @@ -375,13 +375,12 @@ void G1GCPhaseTimes::print(double pause_time_sec) { G1GCParPhaseTimesTracker::G1GCParPhaseTimesTracker(G1GCPhaseTimes* phase_times, G1GCPhaseTimes::GCParPhases phase, uint worker_id) : _phase_times(phase_times), _phase(phase), _worker_id(worker_id) { if (_phase_times != NULL) { - _start_time = os::elapsedTime(); + _start_time = Ticks::now(); } } G1GCParPhaseTimesTracker::~G1GCParPhaseTimesTracker() { if (_phase_times != NULL) { - _phase_times->record_time_secs(_phase, _worker_id, os::elapsedTime() - _start_time); + _phase_times->record_time_secs(_phase, _worker_id, (Ticks::now() - _start_time).seconds()); } } - diff --git a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp index a8b2c71a26a5b4fb34cbe0393840346151fb99a7..de4eb9ab366aa0c456d8225f63c17f1300e5f1fe 100644 --- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp @@ -280,7 +280,7 @@ class G1GCPhaseTimes : public CHeapObj { }; class G1GCParPhaseTimesTracker : public StackObj { - double _start_time; + Ticks _start_time; G1GCPhaseTimes::GCParPhases _phase; G1GCPhaseTimes* _phase_times; uint _worker_id; diff --git a/src/share/vm/gc_implementation/g1/g1HeapRegionEventSender.cpp b/src/share/vm/gc_implementation/g1/g1HeapRegionEventSender.cpp new file mode 100644 index 0000000000000000000000000000000000000000..430ac649ea6e91f9585b2b981c12d8b4cf111a92 --- /dev/null +++ b/src/share/vm/gc_implementation/g1/g1HeapRegionEventSender.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "gc_implementation/g1/g1CollectedHeap.hpp" +#include "gc_implementation/g1/heapRegion.hpp" +#include "g1HeapRegionEventSender.hpp" +#include "jfr/jfrEvents.hpp" + +class DumpEventInfoClosure : public HeapRegionClosure { +public: + bool doHeapRegion(HeapRegion* r) { + EventG1HeapRegionInformation evt; + evt.set_index(r->hrm_index()); + // XXX TODO evt.set_type(r->get_trace_type()); + evt.set_start((uintptr_t)r->bottom()); + evt.set_used(r->used()); + evt.commit(); + return false; + } +}; + + +void G1HeapRegionEventSender::send_events() { + DumpEventInfoClosure c; + + G1CollectedHeap::heap()->heap_region_iterate(&c); +} diff --git a/src/share/vm/trace/traceTime.hpp b/src/share/vm/gc_implementation/g1/g1HeapRegionEventSender.hpp similarity index 74% rename from src/share/vm/trace/traceTime.hpp rename to src/share/vm/gc_implementation/g1/g1HeapRegionEventSender.hpp index c37e0b5cfebe3f829f435137693e3a7d3f3ee052..1a7dc7b0ca24b6a196755fd2283017abc99c1591 100644 --- a/src/share/vm/trace/traceTime.hpp +++ b/src/share/vm/gc_implementation/g1/g1HeapRegionEventSender.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -22,11 +22,14 @@ * */ -#ifndef SHARE_VM_TRACE_TRACETIME_HPP -#define SHARE_VM_TRACE_TRACETIME_HPP +#ifndef SHARE_VM_GC_G1_G1HEAPREGIONEVENTSENDER_HPP +#define SHARE_VM_GC_G1_G1HEAPREGIONEVENTSENDER_HPP -#include "prims/jni.h" +#include "memory/allocation.hpp" -typedef jlong TracingTime; +class G1HeapRegionEventSender : public AllStatic { +public: + static void send_events(); +}; -#endif // SHARE_VM_TRACE_TRACETIME_HPP +#endif // SHARE_VM_GC_G1_G1HEAPREGIONEVENTSENDER_HPP diff --git a/src/share/vm/gc_implementation/g1/g1HeapRegionTraceType.hpp b/src/share/vm/gc_implementation/g1/g1HeapRegionTraceType.hpp new file mode 100644 index 0000000000000000000000000000000000000000..abd119057ab3b772bc3189b812af99209235b88c --- /dev/null +++ b/src/share/vm/gc_implementation/g1/g1HeapRegionTraceType.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 2019, 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 SHARE_GC_G1_G1HEAPREGIONTRACETYPE_HPP +#define SHARE_GC_G1_G1HEAPREGIONTRACETYPE_HPP + +#include "memory/allocation.hpp" +#include "utilities/debug.hpp" + +class G1HeapRegionTraceType : AllStatic { + public: + enum Type { + Free, + Eden, + Survivor, + StartsHumongous, + ContinuesHumongous, + Old, + G1HeapRegionTypeEndSentinel + }; + + static const char* to_string(G1HeapRegionTraceType::Type type) { + switch (type) { + case Free: return "Free"; + case Eden: return "Eden"; + case Survivor: return "Survivor"; + case StartsHumongous: return "Starts Humongous"; + case ContinuesHumongous: return "Continues Humongous"; + case Old: return "Old"; + default: ShouldNotReachHere(); return NULL; + } + } +}; + +#endif // SHARE_GC_G1_G1HEAPREGIONTRACETYPE_HPP diff --git a/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp b/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp index bd91c8fd29a639e60f93cf58dd8558f6161de56b..456bb4ead6e0e2b359dd0c75fc0c96062f5edea8 100644 --- a/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp +++ b/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "gc_implementation/shared/gcTrace.hpp" #include "gc_implementation/g1/g1MMUTracker.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/ostream.hpp" @@ -105,6 +106,9 @@ void G1MMUTrackerQueue::add_pause(double start, double end, bool gc_thread) { ++_no_entries; } _array[_head_index] = G1MMUTrackerQueueElem(start, end); + + double slice_time = calculate_gc_time(end); + G1MMUTracer::report_mmu(_time_slice, slice_time, _max_gc_time); } // basically the _internal call does not remove expired entries diff --git a/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp b/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp index 8f46b0cb927ee4114adb3c36beb0efd16457a76d..a8fb5c7509a8d7edd55876cf862b3284e2cebe7e 100644 --- a/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp +++ b/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp @@ -52,6 +52,9 @@ #include "runtime/vmThread.hpp" #include "utilities/copy.hpp" #include "utilities/events.hpp" +#if INCLUDE_JFR +#include "jfr/jfr.hpp" +#endif // INCLUDE_JFR class HeapRegion; @@ -261,6 +264,7 @@ void G1MarkSweep::mark_sweep_phase3() { // Now adjust pointers in remaining weak roots. (All of which should // have been cleared if they pointed to non-surviving objects.) JNIHandles::weak_oops_do(&GenMarkSweep::adjust_pointer_closure); + JFR_ONLY(Jfr::weak_oops_do(&GenMarkSweep::adjust_pointer_closure)); if (G1StringDedup::is_enabled()) { G1StringDedup::oops_do(&GenMarkSweep::adjust_pointer_closure); diff --git a/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp b/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp index fc86a50cf8aeed56a603952af6ef39332fa5eb8e..e765620bcfaf1e9e3a308af8e69621d62fd38c2c 100644 --- a/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp +++ b/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp @@ -187,6 +187,21 @@ HeapWord* G1ParScanThreadState::allocate_in_next_plab(InCSetState const state, } } +void G1ParScanThreadState::report_promotion_event(InCSetState const dest_state, + oop const old, size_t word_sz, uint age, + HeapWord * const obj_ptr, + AllocationContext_t context) const { + ParGCAllocBuffer* alloc_buf = _g1_par_allocator->alloc_buffer(dest_state, context); + if (alloc_buf->contains(obj_ptr)) { + _g1h->_gc_tracer_stw->report_promotion_in_new_plab_event(old->klass(), word_sz, age, + dest_state.value() == InCSetState::Old, + alloc_buf->word_sz()); + } else { + _g1h->_gc_tracer_stw->report_promotion_outside_plab_event(old->klass(), word_sz, age, + dest_state.value() == InCSetState::Old); + } +} + InCSetState G1ParScanThreadState::next_state(InCSetState const state, markOop const m, uint& age) { if (state.is_young()) { age = !m->has_displaced_mark_helper() ? m->age() @@ -225,6 +240,10 @@ oop G1ParScanThreadState::copy_to_survivor_space(InCSetState const state, return _g1h->handle_evacuation_failure_par(this, old); } } + if (_g1h->_gc_tracer_stw->should_report_promotion_events()) { + // The events are checked individually as part of the actual commit + report_promotion_event(dest_state, old, word_sz, age, obj_ptr, context); + } } assert(obj_ptr != NULL, "when we get here, allocation should have succeeded"); diff --git a/src/share/vm/gc_implementation/g1/g1ParScanThreadState.hpp b/src/share/vm/gc_implementation/g1/g1ParScanThreadState.hpp index d5350310e15e9d6dda733a210bd27122871171b1..d9403aa43d284166107205b2ee6151c4670ca591 100644 --- a/src/share/vm/gc_implementation/g1/g1ParScanThreadState.hpp +++ b/src/share/vm/gc_implementation/g1/g1ParScanThreadState.hpp @@ -212,6 +212,10 @@ class G1ParScanThreadState : public StackObj { size_t word_sz, AllocationContext_t const context); + void report_promotion_event(InCSetState const dest_state, + oop const old, size_t word_sz, uint age, + HeapWord * const obj_ptr, AllocationContext_t context) const; + inline InCSetState next_state(InCSetState const state, markOop const m, uint& age); public: diff --git a/src/share/vm/gc_implementation/g1/heapRegion.cpp b/src/share/vm/gc_implementation/g1/heapRegion.cpp index 2e800544a37fe0dc9d13931587765c9a9aa3e938..1ae8d880537f4cbf7d4e2886e6732df18d1233f0 100644 --- a/src/share/vm/gc_implementation/g1/heapRegion.cpp +++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp @@ -37,6 +37,7 @@ #include "memory/space.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/orderAccess.inline.hpp" +#include "gc_implementation/g1/heapRegionTracer.hpp" PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC @@ -211,6 +212,31 @@ void HeapRegion::calc_gc_efficiency() { _gc_efficiency = (double) reclaimable_bytes() / region_elapsed_time_ms; } +void HeapRegion::set_free() { + report_region_type_change(G1HeapRegionTraceType::Free); + _type.set_free(); +} + +void HeapRegion::set_eden() { + report_region_type_change(G1HeapRegionTraceType::Eden); + _type.set_eden(); +} + +void HeapRegion::set_eden_pre_gc() { + report_region_type_change(G1HeapRegionTraceType::Eden); + _type.set_eden_pre_gc(); +} + +void HeapRegion::set_survivor() { + report_region_type_change(G1HeapRegionTraceType::Survivor); + _type.set_survivor(); +} + +void HeapRegion::set_old() { + report_region_type_change(G1HeapRegionTraceType::Old); + _type.set_old(); +} + void HeapRegion::set_startsHumongous(HeapWord* new_top, HeapWord* new_end) { assert(!isHumongous(), "sanity / pre-condition"); assert(end() == _orig_end, @@ -218,6 +244,7 @@ void HeapRegion::set_startsHumongous(HeapWord* new_top, HeapWord* new_end) { assert(top() == bottom(), "should be empty"); assert(bottom() <= new_top && new_top <= new_end, "pre-condition"); + report_region_type_change(G1HeapRegionTraceType::StartsHumongous); _type.set_starts_humongous(); _humongous_start_region = this; @@ -232,6 +259,7 @@ void HeapRegion::set_continuesHumongous(HeapRegion* first_hr) { assert(top() == bottom(), "should be empty"); assert(first_hr->startsHumongous(), "pre-condition"); + report_region_type_change(G1HeapRegionTraceType::ContinuesHumongous); _type.set_continues_humongous(); _humongous_start_region = first_hr; } @@ -303,6 +331,14 @@ void HeapRegion::initialize(MemRegion mr, bool clear_space, bool mangle_space) { record_timestamp(); } +void HeapRegion::report_region_type_change(G1HeapRegionTraceType::Type to) { + HeapRegionTracer::send_region_type_change(_hrm_index, + get_trace_type(), + to, + (uintptr_t)bottom(), + used()); +} + CompactibleSpace* HeapRegion::next_compaction_space() const { if (TenantHeapIsolation) { assert_at_safepoint(true /* in vm thread */); diff --git a/src/share/vm/gc_implementation/g1/heapRegion.hpp b/src/share/vm/gc_implementation/g1/heapRegion.hpp index da23aac14e52c4c698a01f239ec17d0349e93e31..d9ef393ce16fc0d6b62c34c1a8ae5f85fa0ea845 100644 --- a/src/share/vm/gc_implementation/g1/heapRegion.hpp +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp @@ -35,6 +35,7 @@ #include "memory/space.inline.hpp" #include "memory/watermark.hpp" #include "utilities/macros.hpp" +#include "gc_implementation/g1/g1HeapRegionTraceType.hpp" // A HeapRegion is the smallest piece of a G1CollectedHeap that // can be collected independently. @@ -211,6 +212,8 @@ class HeapRegion: public G1OffsetTableContigSpace { G1BlockOffsetArrayContigSpace* offsets() { return &_offsets; } + void report_region_type_change(G1HeapRegionTraceType::Type to); + protected: // The index of this region in the heap region sequence. uint _hrm_index; @@ -411,6 +414,7 @@ class HeapRegion: public G1OffsetTableContigSpace { const char* get_type_str() const { return _type.get_str(); } const char* get_short_type_str() const { return _type.get_short_str(); } + G1HeapRegionTraceType::Type get_trace_type() { return _type.get_trace_type(); } bool is_free() const { return _type.is_free(); } @@ -671,13 +675,13 @@ class HeapRegion: public G1OffsetTableContigSpace { } } - void set_free() { _type.set_free(); } + void set_free(); - void set_eden() { _type.set_eden(); } - void set_eden_pre_gc() { _type.set_eden_pre_gc(); } - void set_survivor() { _type.set_survivor(); } + void set_eden(); + void set_eden_pre_gc(); + void set_survivor(); - void set_old() { _type.set_old(); } + void set_old(); // Determine if an object has been allocated since the last // mark performed by the collector. This returns true iff the object diff --git a/src/share/vm/gc_implementation/g1/heapRegionSet.cpp b/src/share/vm/gc_implementation/g1/heapRegionSet.cpp index 9ffb46f83d670285eaf601df5fd4b5ea540bb898..213380e767d1443a37f9804e76bb147c6e247049 100644 --- a/src/share/vm/gc_implementation/g1/heapRegionSet.cpp +++ b/src/share/vm/gc_implementation/g1/heapRegionSet.cpp @@ -231,7 +231,7 @@ void FreeRegionList::remove_starting_at(HeapRegion* first, uint num_regions) { assert(_tail != curr, hrs_ext_msg(this, "invariant")); next->set_prev(prev); } - if (_last = curr) { + if (_last == curr) { _last = NULL; } diff --git a/src/share/vm/gc_implementation/g1/heapRegionTracer.cpp b/src/share/vm/gc_implementation/g1/heapRegionTracer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41b8b1197a54ca409d3197bb63ded95e19693a6b --- /dev/null +++ b/src/share/vm/gc_implementation/g1/heapRegionTracer.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013, 2015, 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 "precompiled.hpp" +#include "gc_implementation/g1/heapRegionTracer.hpp" +#include "jfr/jfrEvents.hpp" + +void HeapRegionTracer::send_region_type_change(uint index, + G1HeapRegionTraceType::Type from, + G1HeapRegionTraceType::Type to, + uintptr_t start, + size_t used) { + EventG1HeapRegionTypeChange e; + if (e.should_commit()) { + e.set_index(index); + e.set_from(from); + e.set_to(to); + e.set_start(start); + e.set_used(used); + e.commit(); + } +} diff --git a/src/share/vm/gc_implementation/g1/heapRegionTracer.hpp b/src/share/vm/gc_implementation/g1/heapRegionTracer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f6f6d65980705b0316176f50d5490d161ef73c67 --- /dev/null +++ b/src/share/vm/gc_implementation/g1/heapRegionTracer.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, 2019, 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 SHARE_GC_G1_HEAPREGIONTRACER_HPP +#define SHARE_GC_G1_HEAPREGIONTRACER_HPP + +#include "gc_implementation/g1/g1HeapRegionTraceType.hpp" +#include "memory/allocation.hpp" + +class HeapRegionTracer : AllStatic { + public: + static void send_region_type_change(uint index, + G1HeapRegionTraceType::Type from, + G1HeapRegionTraceType::Type to, + uintptr_t start, + size_t used); +}; + +#endif // SHARE_GC_G1_HEAPREGIONTRACER_HPP diff --git a/src/share/vm/gc_implementation/g1/heapRegionType.cpp b/src/share/vm/gc_implementation/g1/heapRegionType.cpp index 347b58d79960c5f5b45ce6af9aef716c0a1f012b..904187e5062d6b19dd8fb7531db7c0ea48612c9b 100644 --- a/src/share/vm/gc_implementation/g1/heapRegionType.cpp +++ b/src/share/vm/gc_implementation/g1/heapRegionType.cpp @@ -67,3 +67,18 @@ const char* HeapRegionType::get_short_str() const { // keep some compilers happy return NULL; } + +G1HeapRegionTraceType::Type HeapRegionType::get_trace_type() { + hrt_assert_is_valid(_tag); + switch (_tag) { + case FreeTag: return G1HeapRegionTraceType::Free; + case EdenTag: return G1HeapRegionTraceType::Eden; + case SurvTag: return G1HeapRegionTraceType::Survivor; + case HumStartsTag: return G1HeapRegionTraceType::StartsHumongous; + case HumContTag: return G1HeapRegionTraceType::ContinuesHumongous; + case OldTag: return G1HeapRegionTraceType::Old; + default: + ShouldNotReachHere(); + return G1HeapRegionTraceType::Free; // keep some compilers happy + } +} diff --git a/src/share/vm/gc_implementation/g1/heapRegionType.hpp b/src/share/vm/gc_implementation/g1/heapRegionType.hpp index b00590a6b78101d44caa59edf547f66318c29f8b..a9a4fbc25429bd91a1c06a1f03b6d3b1e50f8fa9 100644 --- a/src/share/vm/gc_implementation/g1/heapRegionType.hpp +++ b/src/share/vm/gc_implementation/g1/heapRegionType.hpp @@ -26,6 +26,7 @@ #define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONTYPE_HPP #include "memory/allocation.hpp" +#include "gc_implementation/g1/g1HeapRegionTraceType.hpp" #define hrt_assert_is_valid(tag) \ assert(is_valid((tag)), err_msg("invalid HR type: %u", (uint) (tag))) @@ -127,6 +128,7 @@ public: const char* get_str() const; const char* get_short_str() const; + G1HeapRegionTraceType::Type get_trace_type(); HeapRegionType() : _tag(FreeTag) { hrt_assert_is_valid(_tag); } }; diff --git a/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp b/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp index 2728986a7bbb4a57777d088974909937879bb6b2..980ac99252a976e41bc1d4f4deff79c547425c18 100644 --- a/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp +++ b/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp @@ -1061,7 +1061,7 @@ void ParNewGeneration::collect(bool full, assert(to()->is_empty(), "to space should be empty now"); - adjust_desired_tenuring_threshold(); + adjust_desired_tenuring_threshold(gc_tracer); } else { handle_promotion_failed(gch, thread_state_set, gc_tracer); } diff --git a/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp b/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp index 5f4f98c83fb0583d92cc85d94fb9409336156555..2542ba1ca2f2e4b7293611165f6a637a7f5fef40 100644 --- a/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp +++ b/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp @@ -53,6 +53,9 @@ #include "services/memoryService.hpp" #include "utilities/events.hpp" #include "utilities/stack.inline.hpp" +#if INCLUDE_JFR +#include "jfr/jfr.hpp" +#endif // INCLUDE_JFR PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC @@ -624,6 +627,7 @@ void PSMarkSweep::mark_sweep_phase3() { // have been cleared if they pointed to non-surviving objects.) // Global (weak) JNI handles JNIHandles::weak_oops_do(adjust_pointer_closure()); + JFR_ONLY(Jfr::weak_oops_do(adjust_pointer_closure())); CodeBlobToOopClosure adjust_from_blobs(adjust_pointer_closure(), CodeBlobToOopClosure::FixRelocations); CodeCache::blobs_do(&adjust_from_blobs); diff --git a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp index bdf254d61da097797cd57bbd1361ae72e868d439..0fa980ef83c2c1e11116fe10df93bae7ec983abb 100644 --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp @@ -58,6 +58,9 @@ #include "services/memTracker.hpp" #include "utilities/events.hpp" #include "utilities/stack.inline.hpp" +#if INCLUDE_JFR +#include "jfr/jfr.hpp" +#endif // INCLUDE_JFR #include @@ -2464,6 +2467,7 @@ void PSParallelCompact::adjust_roots() { // have been cleared if they pointed to non-surviving objects.) // Global (weak) JNI handles JNIHandles::weak_oops_do(adjust_pointer_closure()); + JFR_ONLY(Jfr::weak_oops_do(adjust_pointer_closure())); CodeBlobToOopClosure adjust_from_blobs(adjust_pointer_closure(), CodeBlobToOopClosure::FixRelocations); CodeCache::blobs_do(&adjust_from_blobs); diff --git a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp index 69292400f33c50a11aa1047a968e9a420a12156d..6eef954b7e8401ff1a35483b3ccfa1484fefc7a6 100644 --- a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp +++ b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp @@ -148,6 +148,10 @@ class PSPromotionManager VALUE_OBJ_CLASS_SPEC { claimed_stack_depth()->push(p); } + inline void promotion_trace_event(oop new_obj, oop old_obj, size_t obj_size, + uint age, bool tenured, + const PSPromotionLAB* lab); + protected: static OopStarTaskQueueSet* stack_array_depth() { return _stack_array_depth; } public: diff --git a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp index b2de74d41754c7653dcc07acfd97331d37ca00b1..a33132009c326002192d2207f6c93f1fb00e44a8 100644 --- a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp +++ b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp @@ -64,6 +64,33 @@ inline void PSPromotionManager::claim_or_forward_depth(T* p) { claim_or_forward_internal_depth(p); } +inline void PSPromotionManager::promotion_trace_event(oop new_obj, oop old_obj, + size_t obj_size, + uint age, bool tenured, + const PSPromotionLAB* lab) { + // Skip if memory allocation failed + if (new_obj != NULL) { + const ParallelScavengeTracer* gc_tracer = PSScavenge::gc_tracer(); + + if (lab != NULL) { + // Promotion of object through newly allocated PLAB + if (gc_tracer->should_report_promotion_in_new_plab_event()) { + size_t obj_bytes = obj_size * HeapWordSize; + size_t lab_size = lab->capacity(); + gc_tracer->report_promotion_in_new_plab_event(old_obj->klass(), obj_bytes, + age, tenured, lab_size); + } + } else { + // Promotion of object directly to heap + if (gc_tracer->should_report_promotion_outside_plab_event()) { + size_t obj_bytes = obj_size * HeapWordSize; + gc_tracer->report_promotion_outside_plab_event(old_obj->klass(), obj_bytes, + age, tenured); + } + } + } +} + // // This method is pretty bulky. It would be nice to split it up // into smaller submethods, but we need to be careful not to hurt @@ -85,11 +112,11 @@ oop PSPromotionManager::copy_to_survivor_space(oop o) { bool new_obj_is_tenured = false; size_t new_obj_size = o->size(); - if (!promote_immediately) { - // Find the objects age, MT safe. - uint age = (test_mark->has_displaced_mark_helper() /* o->has_displaced_mark() */) ? - test_mark->displaced_mark_helper()->age() : test_mark->age(); + // Find the objects age, MT safe. + uint age = (test_mark->has_displaced_mark_helper() /* o->has_displaced_mark() */) ? + test_mark->displaced_mark_helper()->age() : test_mark->age(); + if (!promote_immediately) { // Try allocating obj in to-space (unless too old) if (age < PSScavenge::tenuring_threshold()) { new_obj = (oop) _young_lab.allocate(new_obj_size); @@ -98,6 +125,7 @@ oop PSPromotionManager::copy_to_survivor_space(oop o) { if (new_obj_size > (YoungPLABSize / 2)) { // Allocate this object directly new_obj = (oop)young_space()->cas_allocate(new_obj_size); + promotion_trace_event(new_obj, o, new_obj_size, age, false, NULL); } else { // Flush and fill _young_lab.flush(); @@ -107,6 +135,7 @@ oop PSPromotionManager::copy_to_survivor_space(oop o) { _young_lab.initialize(MemRegion(lab_base, YoungPLABSize)); // Try the young lab allocation again. new_obj = (oop) _young_lab.allocate(new_obj_size); + promotion_trace_event(new_obj, o, new_obj_size, age, false, &_young_lab); } else { _young_gen_is_full = true; } @@ -132,6 +161,7 @@ oop PSPromotionManager::copy_to_survivor_space(oop o) { if (new_obj_size > (OldPLABSize / 2)) { // Allocate this object directly new_obj = (oop)old_gen()->cas_allocate(new_obj_size); + promotion_trace_event(new_obj, o, new_obj_size, age, true, NULL); } else { // Flush and fill _old_lab.flush(); @@ -148,6 +178,7 @@ oop PSPromotionManager::copy_to_survivor_space(oop o) { _old_lab.initialize(MemRegion(lab_base, OldPLABSize)); // Try the old lab allocation again. new_obj = (oop) _old_lab.allocate(new_obj_size); + promotion_trace_event(new_obj, o, new_obj_size, age, true, &_old_lab); } } } diff --git a/src/share/vm/gc_implementation/parallelScavenge/psScavenge.hpp b/src/share/vm/gc_implementation/parallelScavenge/psScavenge.hpp index 896b705c2049bfe063007dd2b6c87d91bc2db741..cf1c21d1e490c6ffff8b7e23b976a96cc1f861f0 100644 --- a/src/share/vm/gc_implementation/parallelScavenge/psScavenge.hpp +++ b/src/share/vm/gc_implementation/parallelScavenge/psScavenge.hpp @@ -92,6 +92,7 @@ class PSScavenge: AllStatic { // Private accessors static CardTableExtension* const card_table() { assert(_card_table != NULL, "Sanity"); return _card_table; } + static const ParallelScavengeTracer* gc_tracer() { return &_gc_tracer; } public: // Accessors diff --git a/src/share/vm/gc_implementation/shared/ageTable.cpp b/src/share/vm/gc_implementation/shared/ageTable.cpp index d148cd4d3192ad2afe7003f0040ecfb1cc6eece3..0e394b8b873126689c7437d9c80f9d3a9c465cc1 100644 --- a/src/share/vm/gc_implementation/shared/ageTable.cpp +++ b/src/share/vm/gc_implementation/shared/ageTable.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc_implementation/shared/ageTable.hpp" +#include "gc_implementation/shared/ageTableTracer.hpp" #include "gc_implementation/shared/gcPolicyCounters.hpp" #include "memory/collectorPolicy.hpp" #include "memory/resourceArea.hpp" @@ -78,7 +79,7 @@ void ageTable::merge_par(ageTable* subTable) { } } -uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) { +uint ageTable::compute_tenuring_threshold(size_t survivor_capacity, GCTracer &tracer) { size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100); size_t total = 0; uint age = 1; @@ -92,7 +93,7 @@ uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) { } uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold; - if (PrintTenuringDistribution || UsePerfData) { + if (PrintTenuringDistribution || UsePerfData || AgeTableTracer::is_tenuring_distribution_event_enabled()) { if (PrintTenuringDistribution) { gclog_or_tty->cr(); @@ -110,6 +111,7 @@ uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) { age, sizes[age]*oopSize, total*oopSize); } } + AgeTableTracer::send_tenuring_distribution_event(age, wordSize * oopSize, tracer); if (UsePerfData) { _perf_sizes[age]->set_value(sizes[age]*oopSize); } diff --git a/src/share/vm/gc_implementation/shared/ageTable.hpp b/src/share/vm/gc_implementation/shared/ageTable.hpp index 44d8e0ace7fdb3aa68c067ac8c7f5d64a7d917df..ef25b3e5269776883d1bb3aabc83cf895aa474ec 100644 --- a/src/share/vm/gc_implementation/shared/ageTable.hpp +++ b/src/share/vm/gc_implementation/shared/ageTable.hpp @@ -27,6 +27,7 @@ #include "oops/markOop.hpp" #include "oops/oop.hpp" +#include "gc_implementation/shared/gcTrace.hpp" #include "runtime/perfData.hpp" /* Copyright (c) 1992-2009 Oracle and/or its affiliates, and Stanford University. @@ -69,7 +70,7 @@ class ageTable VALUE_OBJ_CLASS_SPEC { void merge_par(ageTable* subTable); // calculate new tenuring threshold based on age information - uint compute_tenuring_threshold(size_t survivor_capacity); + uint compute_tenuring_threshold(size_t survivor_capacity, GCTracer &tracer); private: PerfVariable* _perf_sizes[table_size]; diff --git a/src/share/vm/gc_implementation/shared/ageTableTracer.cpp b/src/share/vm/gc_implementation/shared/ageTableTracer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b66330e817e56b7ab4052cbe818ec4329eacc0ea --- /dev/null +++ b/src/share/vm/gc_implementation/shared/ageTableTracer.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "gc_implementation/shared/ageTableTracer.hpp" +#include "gc_implementation/shared/gcId.hpp" +#include "gc_implementation/shared/gcTrace.hpp" +#include "jfr/jfrEvents.hpp" + +void AgeTableTracer::send_tenuring_distribution_event(uint age, size_t size, GCTracer &tracer) { + EventTenuringDistribution e; + if (e.should_commit()) { + e.set_gcId(tracer.gc_id().id()); + e.set_age(age); + e.set_size(size); + e.commit(); + } +} + +bool AgeTableTracer::is_tenuring_distribution_event_enabled() { + return EventTenuringDistribution::is_enabled(); +} diff --git a/src/share/vm/gc_implementation/shared/ageTableTracer.hpp b/src/share/vm/gc_implementation/shared/ageTableTracer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..48e3831e5ece029a84d6803a0850a90115ed5331 --- /dev/null +++ b/src/share/vm/gc_implementation/shared/ageTableTracer.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_GC_SHARED_AGETABLETRACER_HPP +#define SHARE_VM_GC_SHARED_AGETABLETRACER_HPP + +#include "gc_implementation/shared/gcTrace.hpp" +#include "memory/allocation.hpp" + +class AgeTableTracer : AllStatic { + public: + static void send_tenuring_distribution_event(uint age, size_t size, GCTracer &tracer); + static bool is_tenuring_distribution_event_enabled(); +}; + +#endif // SHARE_VM_GC_SHARED_AGETABLETRACER_HPP diff --git a/src/share/vm/gc_implementation/shared/gcConfiguration.cpp b/src/share/vm/gc_implementation/shared/gcConfiguration.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2868d2275f4f6a15a2341e00bdbfc9c5f5699a85 --- /dev/null +++ b/src/share/vm/gc_implementation/shared/gcConfiguration.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2012, 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 "precompiled.hpp" + +#include "gc_interface/collectedHeap.hpp" +#include "gc_implementation/shared/gcConfiguration.hpp" +#include "memory/universe.hpp" +#include "runtime/arguments.hpp" +#include "runtime/globals.hpp" +#include "utilities/debug.hpp" + +GCName GCConfiguration::young_collector() const { + if (UseG1GC) { + return G1New; + } + + if (UseParallelGC) { + return ParallelScavenge; + } + + if (UseConcMarkSweepGC) { + return ParNew; + } + + return DefNew; +} + +GCName GCConfiguration::old_collector() const { + if (UseG1GC) { + return G1Old; + } + + if (UseConcMarkSweepGC) { + return ConcurrentMarkSweep; + } + + if (UseParallelOldGC) { + return ParallelOld; + } + + return SerialOld; +} + +uint GCConfiguration::num_parallel_gc_threads() const { + return ParallelGCThreads; +} + +uint GCConfiguration::num_concurrent_gc_threads() const { + return ConcGCThreads; +} + +bool GCConfiguration::uses_dynamic_gc_threads() const { + return UseDynamicNumberOfGCThreads; +} + +bool GCConfiguration::is_explicit_gc_concurrent() const { + return ExplicitGCInvokesConcurrent; +} + +bool GCConfiguration::is_explicit_gc_disabled() const { + return DisableExplicitGC; +} + +bool GCConfiguration::has_pause_target_default_value() const { + return FLAG_IS_DEFAULT(MaxGCPauseMillis); +} + +uintx GCConfiguration::pause_target() const { + return MaxGCPauseMillis; +} + +uintx GCConfiguration::gc_time_ratio() const { + return GCTimeRatio; +} + +bool GCTLABConfiguration::uses_tlabs() const { + return UseTLAB; +} + +size_t GCTLABConfiguration::min_tlab_size() const { + return MinTLABSize; +} + +uint GCTLABConfiguration::tlab_refill_waste_limit() const { + return TLABRefillWasteFraction; +} + +intx GCSurvivorConfiguration::max_tenuring_threshold() const { + return MaxTenuringThreshold; +} + +intx GCSurvivorConfiguration::initial_tenuring_threshold() const { + return InitialTenuringThreshold; +} + +size_t GCHeapConfiguration::max_size() const { + return MaxHeapSize; +} + +size_t GCHeapConfiguration::min_size() const { + return Arguments::min_heap_size(); +} + +size_t GCHeapConfiguration::initial_size() const { + return InitialHeapSize; +} + +bool GCHeapConfiguration::uses_compressed_oops() const { + return UseCompressedOops; +} + +Universe::NARROW_OOP_MODE GCHeapConfiguration::narrow_oop_mode() const { + return Universe::narrow_oop_mode(); +} + +uint GCHeapConfiguration::object_alignment_in_bytes() const { + return ObjectAlignmentInBytes; +} + +int GCHeapConfiguration::heap_address_size_in_bits() const { + return BitsPerHeapOop; +} + +bool GCYoungGenerationConfiguration::has_max_size_default_value() const { + return FLAG_IS_DEFAULT(MaxNewSize); +} + +uintx GCYoungGenerationConfiguration::max_size() const { + return MaxNewSize; +} + +uintx GCYoungGenerationConfiguration::min_size() const { + return NewSize; +} + +intx GCYoungGenerationConfiguration::new_ratio() const { + return NewRatio; +} diff --git a/src/share/vm/gc_implementation/shared/gcConfiguration.hpp b/src/share/vm/gc_implementation/shared/gcConfiguration.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1fdf6496d6a8b74410e878d6f3b7c2b1e2a92601 --- /dev/null +++ b/src/share/vm/gc_implementation/shared/gcConfiguration.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2012, 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 SHARE_VM_GC_SHARED_GCCONFIGURATION_HPP +#define SHARE_VM_GC_SHARED_GCCONFIGURATION_HPP + +#include "gc_interface/gcName.hpp" +#include "memory/universe.hpp" +#include "utilities/globalDefinitions.hpp" + +class GCConfiguration { + public: + GCName young_collector() const; + GCName old_collector() const; + uint num_parallel_gc_threads() const; + uint num_concurrent_gc_threads() const; + bool uses_dynamic_gc_threads() const; + bool is_explicit_gc_concurrent() const; + bool is_explicit_gc_disabled() const; + uintx gc_time_ratio() const; + + bool has_pause_target_default_value() const; + uintx pause_target() const; +}; + +class GCTLABConfiguration { + public: + bool uses_tlabs() const; + size_t min_tlab_size() const; + uint tlab_refill_waste_limit() const; +}; + +class GCSurvivorConfiguration { + public: + intx initial_tenuring_threshold() const; + intx max_tenuring_threshold() const; +}; + +class GCHeapConfiguration { + public: + size_t max_size() const; + size_t min_size() const; + size_t initial_size() const; + bool uses_compressed_oops() const; + Universe::NARROW_OOP_MODE narrow_oop_mode() const; + uint object_alignment_in_bytes() const; + int heap_address_size_in_bits() const; +}; + +class GCYoungGenerationConfiguration { + public: + bool has_max_size_default_value() const; + uintx max_size() const; + + uintx min_size() const; + intx new_ratio() const; +}; + +#endif // SHARE_VM_GC_SHARED_GCCONFIGURATION_HPP diff --git a/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp b/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp index 23cb113c11b949ef00e9635cd0922cc59b8a4253..8b3c910b5823d9c1681a9a1e7cf85e93b73e36d5 100644 --- a/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp +++ b/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp @@ -78,11 +78,13 @@ class MetaspaceSizes : public StackObj { class GCHeapSummary; class PSHeapSummary; +class G1HeapSummary; class GCHeapSummaryVisitor { public: virtual void visit(const GCHeapSummary* heap_summary) const = 0; virtual void visit(const PSHeapSummary* heap_summary) const {} + virtual void visit(const G1HeapSummary* heap_summary) const {} }; class GCHeapSummary : public StackObj { @@ -125,6 +127,24 @@ class PSHeapSummary : public GCHeapSummary { } }; +class G1HeapSummary : public GCHeapSummary { + size_t _edenUsed; + size_t _edenCapacity; + size_t _survivorUsed; + uint _numberOfRegions; + public: + G1HeapSummary(VirtualSpaceSummary& heap_space, size_t heap_used, size_t edenUsed, size_t edenCapacity, size_t survivorUsed, uint numberOfRegions) : + GCHeapSummary(heap_space, heap_used), _edenUsed(edenUsed), _edenCapacity(edenCapacity), _survivorUsed(survivorUsed), _numberOfRegions(numberOfRegions) { } + const size_t edenUsed() const { return _edenUsed; } + const size_t edenCapacity() const { return _edenCapacity; } + const size_t survivorUsed() const { return _survivorUsed; } + const uint numberOfRegions() const { return _numberOfRegions; } + + virtual void accept(GCHeapSummaryVisitor* visitor) const { + visitor->visit(this); + } +}; + class MetaspaceSummary : public StackObj { size_t _capacity_until_GC; MetaspaceSizes _meta_space; diff --git a/src/share/vm/gc_implementation/shared/gcTimer.cpp b/src/share/vm/gc_implementation/shared/gcTimer.cpp index 246be3ecc6fa8dcb6e45dcf5e1fee01fd90ff4c5..d0acb05255d215c79b4365aba40beb6c0ff4ed98 100644 --- a/src/share/vm/gc_implementation/shared/gcTimer.cpp +++ b/src/share/vm/gc_implementation/shared/gcTimer.cpp @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "gc_implementation/shared/gcTimer.hpp" #include "utilities/growableArray.hpp" -#include "utilities/ticks.inline.hpp" +#include "utilities/ticks.hpp" // the "time" parameter for most functions // has a default value set by Ticks::now() @@ -349,7 +349,7 @@ public: GCTimer gc_timer; gc_timer.register_gc_start(1); - assert(gc_timer.gc_start() == 1, "Incorrect"); + assert(gc_timer.gc_start() == Ticks(1), "Incorrect"); } static void gc_end() { @@ -357,7 +357,7 @@ public: gc_timer.register_gc_start(1); gc_timer.register_gc_end(2); - assert(gc_timer.gc_end() == 2, "Incorrect"); + assert(gc_timer.gc_end() == Ticks(2), "Incorrect"); } }; diff --git a/src/share/vm/gc_implementation/shared/gcTrace.cpp b/src/share/vm/gc_implementation/shared/gcTrace.cpp index ddac9531667c17e85aa921b6d41c1f31188f4faf..7036157b7eaa5c010ebddbc54bb2dcacac748429 100644 --- a/src/share/vm/gc_implementation/shared/gcTrace.cpp +++ b/src/share/vm/gc_implementation/shared/gcTrace.cpp @@ -33,7 +33,7 @@ #include "memory/referenceProcessorStats.hpp" #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/ticks.inline.hpp" +#include "utilities/ticks.hpp" #if INCLUDE_ALL_GCS #include "gc_implementation/g1/evacuationInfo.hpp" @@ -172,6 +172,30 @@ void YoungGCTracer::report_tenuring_threshold(const uint tenuring_threshold) { _tenuring_threshold = tenuring_threshold; } +bool YoungGCTracer::should_report_promotion_events() const { + return should_report_promotion_in_new_plab_event() || + should_report_promotion_outside_plab_event(); +} + +bool YoungGCTracer::should_report_promotion_in_new_plab_event() const { + return should_send_promotion_in_new_plab_event(); +} + +bool YoungGCTracer::should_report_promotion_outside_plab_event() const { + return should_send_promotion_outside_plab_event(); +} + +void YoungGCTracer::report_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const { + send_promotion_in_new_plab_event(klass, obj_size, age, tenured, plab_size); +} + +void YoungGCTracer::report_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const { + send_promotion_outside_plab_event(klass, obj_size, age, tenured); +} + void OldGCTracer::report_gc_end_impl(const Ticks& timestamp, TimePartitions* time_partitions) { assert_set_gc_id(); @@ -199,6 +223,12 @@ void OldGCTracer::report_concurrent_mode_failure() { } #if INCLUDE_ALL_GCS +void G1MMUTracer::report_mmu(double time_slice_sec, double gc_time_sec, double max_time_sec) { + send_g1_mmu_event(time_slice_sec * MILLIUNITS, + gc_time_sec * MILLIUNITS, + max_time_sec * MILLIUNITS); +} + void G1NewTracer::report_yc_type(G1YCType type) { assert_set_gc_id(); diff --git a/src/share/vm/gc_implementation/shared/gcTrace.hpp b/src/share/vm/gc_implementation/shared/gcTrace.hpp index dd13344155fcd6fb0b6cf74d9df7b4750e742658..34bd118dc8dec21695732fe7ee01631fb2400d09 100644 --- a/src/share/vm/gc_implementation/shared/gcTrace.hpp +++ b/src/share/vm/gc_implementation/shared/gcTrace.hpp @@ -156,10 +156,39 @@ class YoungGCTracer : public GCTracer { public: void report_promotion_failed(const PromotionFailedInfo& pf_info); void report_tenuring_threshold(const uint tenuring_threshold); + /* + * Methods for reporting Promotion in new or outside PLAB Events. + * + * The object age is always required as it is not certain that the mark word + * of the oop can be trusted at this stage. + * + * obj_size is the size of the promoted object in bytes. + * + * tenured should be true if the object has been promoted to the old + * space during this GC, if the object is copied to survivor space + * from young space or survivor space (aging) tenured should be false. + * + * plab_size is the size of the newly allocated PLAB in bytes. + */ + bool should_report_promotion_events() const; + bool should_report_promotion_in_new_plab_event() const; + bool should_report_promotion_outside_plab_event() const; + void report_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const; + void report_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const; private: void send_young_gc_event() const; void send_promotion_failed_event(const PromotionFailedInfo& pf_info) const; + bool should_send_promotion_in_new_plab_event() const; + bool should_send_promotion_outside_plab_event() const; + void send_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const; + void send_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const; }; class OldGCTracer : public GCTracer { @@ -210,6 +239,13 @@ class ParNewTracer : public YoungGCTracer { }; #if INCLUDE_ALL_GCS +class G1MMUTracer : public AllStatic { + static void send_g1_mmu_event(double time_slice_ms, double gc_time_ms, double max_time_ms); + + public: + static void report_mmu(double time_slice_sec, double gc_time_sec, double max_time_sec); +}; + class G1NewTracer : public YoungGCTracer { G1YoungGCInfo _g1_young_gc_info; diff --git a/src/share/vm/gc_implementation/shared/gcTraceSend.cpp b/src/share/vm/gc_implementation/shared/gcTraceSend.cpp index 5462135a0145c3e655a9e0ca98ebe6a45360ebab..d74e95aa5962c71bde6ac3bbf1575ae0ac5076d1 100644 --- a/src/share/vm/gc_implementation/shared/gcTraceSend.cpp +++ b/src/share/vm/gc_implementation/shared/gcTraceSend.cpp @@ -23,14 +23,13 @@ */ #include "precompiled.hpp" +#include "jfr/jfrEvents.hpp" #include "gc_implementation/shared/gcHeapSummary.hpp" #include "gc_implementation/shared/gcTimer.hpp" #include "gc_implementation/shared/gcTrace.hpp" #include "gc_implementation/shared/gcWhen.hpp" #include "gc_implementation/shared/copyFailedInfo.hpp" #include "runtime/os.hpp" -#include "trace/tracing.hpp" -#include "trace/traceBackend.hpp" #if INCLUDE_ALL_GCS #include "gc_implementation/g1/evacuationInfo.hpp" #include "gc_implementation/g1/g1YCTypes.hpp" @@ -41,7 +40,7 @@ typedef uintptr_t TraceAddress; void GCTracer::send_garbage_collection_event() const { - EventGCGarbageCollection event(UNTIMED); + EventGarbageCollection event(UNTIMED); if (event.should_commit()) { event.set_gcId(_shared_gc_info.gc_id().id()); event.set_name(_shared_gc_info.name()); @@ -89,7 +88,7 @@ void GCTracer::send_metaspace_chunk_free_list_summary(GCWhen::Type when, Metaspa } void ParallelOldTracer::send_parallel_old_event() const { - EventGCParallelOld e(UNTIMED); + EventParallelOldGarbageCollection e(UNTIMED); if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_densePrefix((TraceAddress)_parallel_old_gc_info.dense_prefix()); @@ -100,7 +99,7 @@ void ParallelOldTracer::send_parallel_old_event() const { } void YoungGCTracer::send_young_gc_event() const { - EventGCYoungGarbageCollection e(UNTIMED); + EventYoungGarbageCollection e(UNTIMED); if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_tenuringThreshold(_tenuring_threshold); @@ -110,8 +109,46 @@ void YoungGCTracer::send_young_gc_event() const { } } +bool YoungGCTracer::should_send_promotion_in_new_plab_event() const { + return EventPromoteObjectInNewPLAB::is_enabled(); +} + +bool YoungGCTracer::should_send_promotion_outside_plab_event() const { + return EventPromoteObjectOutsidePLAB::is_enabled(); +} + +void YoungGCTracer::send_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const { + + EventPromoteObjectInNewPLAB event; + if (event.should_commit()) { + event.set_gcId(_shared_gc_info.gc_id().id()); + event.set_objectClass(klass); + event.set_objectSize(obj_size); + event.set_tenured(tenured); + event.set_tenuringAge(age); + event.set_plabSize(plab_size); + event.commit(); + } +} + +void YoungGCTracer::send_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const { + + EventPromoteObjectOutsidePLAB event; + if (event.should_commit()) { + event.set_gcId(_shared_gc_info.gc_id().id()); + event.set_objectClass(klass); + event.set_objectSize(obj_size); + event.set_tenured(tenured); + event.set_tenuringAge(age); + event.commit(); + } +} + void OldGCTracer::send_old_gc_event() const { - EventGCOldGarbageCollection e(UNTIMED); + EventOldGarbageCollection e(UNTIMED); if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_starttime(_shared_gc_info.start_timestamp()); @@ -120,8 +157,8 @@ void OldGCTracer::send_old_gc_event() const { } } -static TraceStructCopyFailed to_trace_struct(const CopyFailedInfo& cf_info) { - TraceStructCopyFailed failed_info; +static JfrStructCopyFailed to_struct(const CopyFailedInfo& cf_info) { + JfrStructCopyFailed failed_info; failed_info.set_objectCount(cf_info.failed_count()); failed_info.set_firstSize(cf_info.first_size()); failed_info.set_smallestSize(cf_info.smallest_size()); @@ -133,7 +170,7 @@ void YoungGCTracer::send_promotion_failed_event(const PromotionFailedInfo& pf_in EventPromotionFailed e; if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); - e.set_data(to_trace_struct(pf_info)); + e.set_promotionFailed(to_struct(pf_info)); e.set_thread(pf_info.thread()->thread_id()); e.commit(); } @@ -150,7 +187,7 @@ void OldGCTracer::send_concurrent_mode_failure_event() { #if INCLUDE_ALL_GCS void G1NewTracer::send_g1_young_gc_event() { - EventGCG1GarbageCollection e(UNTIMED); + EventG1GarbageCollection e(UNTIMED); if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_type(_g1_young_gc_info.type()); @@ -160,16 +197,27 @@ void G1NewTracer::send_g1_young_gc_event() { } } +void G1MMUTracer::send_g1_mmu_event(double time_slice_ms, double gc_time_ms, double max_time_ms) { + EventG1MMU e; + if (e.should_commit()) { + e.set_gcId(GCId::peek().id()); + e.set_timeSlice((s8)time_slice_ms); + e.set_gcTime((s8)gc_time_ms); + e.set_pauseTarget((s8)max_time_ms); + e.commit(); + } +} + void G1NewTracer::send_evacuation_info_event(EvacuationInfo* info) { - EventEvacuationInfo e; + EventEvacuationInformation e; if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_cSetRegions(info->collectionset_regions()); e.set_cSetUsedBefore(info->collectionset_used_before()); e.set_cSetUsedAfter(info->collectionset_used_after()); e.set_allocationRegions(info->allocation_regions()); - e.set_allocRegionsUsedBefore(info->alloc_regions_used_before()); - e.set_allocRegionsUsedAfter(info->alloc_regions_used_before() + info->bytes_copied()); + e.set_allocationRegionsUsedBefore(info->alloc_regions_used_before()); + e.set_allocationRegionsUsedAfter(info->alloc_regions_used_before() + info->bytes_copied()); e.set_bytesCopied(info->bytes_copied()); e.set_regionsFreed(info->regions_freed()); e.commit(); @@ -180,14 +228,15 @@ void G1NewTracer::send_evacuation_failed_event(const EvacuationFailedInfo& ef_in EventEvacuationFailed e; if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); - e.set_data(to_trace_struct(ef_info)); + e.set_evacuationFailed(to_struct(ef_info)); e.commit(); } } -#endif -static TraceStructVirtualSpace to_trace_struct(const VirtualSpaceSummary& summary) { - TraceStructVirtualSpace space; +#endif // INCLUDE_ALL_GCS + +static JfrStructVirtualSpace to_struct(const VirtualSpaceSummary& summary) { + JfrStructVirtualSpace space; space.set_start((TraceAddress)summary.start()); space.set_committedEnd((TraceAddress)summary.committed_end()); space.set_committedSize(summary.committed_size()); @@ -196,8 +245,8 @@ static TraceStructVirtualSpace to_trace_struct(const VirtualSpaceSummary& summar return space; } -static TraceStructObjectSpace to_trace_struct(const SpaceSummary& summary) { - TraceStructObjectSpace space; +static JfrStructObjectSpace to_struct(const SpaceSummary& summary) { + JfrStructObjectSpace space; space.set_start((TraceAddress)summary.start()); space.set_end((TraceAddress)summary.end()); space.set_used(summary.used()); @@ -218,12 +267,27 @@ class GCHeapSummaryEventSender : public GCHeapSummaryVisitor { if (e.should_commit()) { e.set_gcId(_gc_id.id()); e.set_when((u1)_when); - e.set_heapSpace(to_trace_struct(heap_space)); + e.set_heapSpace(to_struct(heap_space)); e.set_heapUsed(heap_summary->used()); e.commit(); } } + void visit(const G1HeapSummary* g1_heap_summary) const { + visit((GCHeapSummary*)g1_heap_summary); + + EventG1HeapSummary e; + if (e.should_commit()) { + e.set_gcId(_gc_id.id()); + e.set_when((u1)_when); + e.set_edenUsedSize(g1_heap_summary->edenUsed()); + e.set_edenTotalSize(g1_heap_summary->edenCapacity()); + e.set_survivorUsedSize(g1_heap_summary->survivorUsed()); + e.set_numberOfRegions(g1_heap_summary->numberOfRegions()); + e.commit(); + } + } + void visit(const PSHeapSummary* ps_heap_summary) const { visit((GCHeapSummary*)ps_heap_summary); @@ -239,12 +303,12 @@ class GCHeapSummaryEventSender : public GCHeapSummaryVisitor { e.set_gcId(_gc_id.id()); e.set_when((u1)_when); - e.set_oldSpace(to_trace_struct(ps_heap_summary->old())); - e.set_oldObjectSpace(to_trace_struct(ps_heap_summary->old_space())); - e.set_youngSpace(to_trace_struct(ps_heap_summary->young())); - e.set_edenSpace(to_trace_struct(ps_heap_summary->eden())); - e.set_fromSpace(to_trace_struct(ps_heap_summary->from())); - e.set_toSpace(to_trace_struct(ps_heap_summary->to())); + e.set_oldSpace(to_struct(ps_heap_summary->old())); + e.set_oldObjectSpace(to_struct(ps_heap_summary->old_space())); + e.set_youngSpace(to_struct(ps_heap_summary->young())); + e.set_edenSpace(to_struct(ps_heap_summary->eden())); + e.set_fromSpace(to_struct(ps_heap_summary->from())); + e.set_toSpace(to_struct(ps_heap_summary->to())); e.commit(); } } @@ -255,8 +319,8 @@ void GCTracer::send_gc_heap_summary_event(GCWhen::Type when, const GCHeapSummary heap_summary.accept(&visitor); } -static TraceStructMetaspaceSizes to_trace_struct(const MetaspaceSizes& sizes) { - TraceStructMetaspaceSizes meta_sizes; +static JfrStructMetaspaceSizes to_struct(const MetaspaceSizes& sizes) { + JfrStructMetaspaceSizes meta_sizes; meta_sizes.set_committed(sizes.committed()); meta_sizes.set_used(sizes.used()); @@ -271,9 +335,9 @@ void GCTracer::send_meta_space_summary_event(GCWhen::Type when, const MetaspaceS e.set_gcId(_shared_gc_info.gc_id().id()); e.set_when((u1) when); e.set_gcThreshold(meta_space_summary.capacity_until_GC()); - e.set_metaspace(to_trace_struct(meta_space_summary.meta_space())); - e.set_dataSpace(to_trace_struct(meta_space_summary.data_space())); - e.set_classSpace(to_trace_struct(meta_space_summary.class_space())); + e.set_metaspace(to_struct(meta_space_summary.meta_space())); + e.set_dataSpace(to_struct(meta_space_summary.data_space())); + e.set_classSpace(to_struct(meta_space_summary.class_space())); e.commit(); } } @@ -283,14 +347,14 @@ class PhaseSender : public PhaseVisitor { public: PhaseSender(GCId gc_id) : _gc_id(gc_id) {} - template - void send_phase(PausePhase* pause) { + template + void send_phase(GCPhase* phase) { T event(UNTIMED); if (event.should_commit()) { event.set_gcId(_gc_id.id()); - event.set_name(pause->name()); - event.set_starttime(pause->start()); - event.set_endtime(pause->end()); + event.set_name(phase->name()); + event.set_starttime(phase->start()); + event.set_endtime(phase->end()); event.commit(); } } diff --git a/src/share/vm/gc_implementation/shared/gcTraceTime.cpp b/src/share/vm/gc_implementation/shared/gcTraceTime.cpp index 30169a1113d505d6ce03b448d7f5065d3d58324e..3c3e3c4a26c7d775811f05df195ced327eb748bc 100644 --- a/src/share/vm/gc_implementation/shared/gcTraceTime.cpp +++ b/src/share/vm/gc_implementation/shared/gcTraceTime.cpp @@ -33,7 +33,7 @@ #include "runtime/thread.inline.hpp" #include "runtime/timer.hpp" #include "utilities/ostream.hpp" -#include "utilities/ticks.inline.hpp" +#include "utilities/ticks.hpp" GCTraceTime::GCTraceTime(const char* title, bool doit, bool print_cr, GCTimer* timer, GCId gc_id) : _title(title), _doit(doit), _print_cr(print_cr), _timer(timer), _start_counter() { @@ -72,7 +72,7 @@ GCTraceTime::~GCTraceTime() { if (_doit) { const Tickspan duration = stop_counter - _start_counter; - double duration_in_seconds = TicksToTimeHelper::seconds(duration); + double duration_in_seconds = duration.seconds(); if (_print_cr) { gclog_or_tty->print_cr(", %3.7f secs]", duration_in_seconds); } else { diff --git a/src/share/vm/gc_implementation/shared/objectCountEventSender.cpp b/src/share/vm/gc_implementation/shared/objectCountEventSender.cpp index 8b9d6a141f5091e4b03f798d3cf6870504b7c8b4..eff72bea372890dc58355a67f51baaa7b2691f14 100644 --- a/src/share/vm/gc_implementation/shared/objectCountEventSender.cpp +++ b/src/share/vm/gc_implementation/shared/objectCountEventSender.cpp @@ -26,34 +26,53 @@ #include "precompiled.hpp" #include "gc_implementation/shared/gcId.hpp" #include "gc_implementation/shared/objectCountEventSender.hpp" +#include "jfr/jfrEvents.hpp" #include "memory/heapInspection.hpp" -#include "trace/tracing.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "utilities/ticks.hpp" -#if INCLUDE_SERVICES -void ObjectCountEventSender::send(const KlassInfoEntry* entry, GCId gc_id, const Ticks& timestamp) { -#if INCLUDE_TRACE - assert(Tracing::is_event_enabled(EventObjectCountAfterGC::eventId), - "Only call this method if the event is enabled"); - - EventObjectCountAfterGC event(UNTIMED); - event.set_gcId(gc_id.id()); - event.set_class(entry->klass()); - event.set_count(entry->count()); - event.set_totalSize(entry->words() * BytesPerWord); - event.set_endtime(timestamp); - event.commit(); -#endif // INCLUDE_TRACE -} + +#if INCLUDE_SERVICES bool ObjectCountEventSender::should_send_event() { -#if INCLUDE_TRACE - return Tracing::is_event_enabled(EventObjectCountAfterGC::eventId); +#if INCLUDE_JFR + return _should_send_requestable_event || EventObjectCountAfterGC::is_enabled(); #else return false; -#endif // INCLUDE_TRACE +#endif // INCLUDE_JFR +} + +bool ObjectCountEventSender::_should_send_requestable_event = false; + +void ObjectCountEventSender::enable_requestable_event() { + _should_send_requestable_event = true; +} + +void ObjectCountEventSender::disable_requestable_event() { + _should_send_requestable_event = false; +} + +template +void ObjectCountEventSender::send_event_if_enabled(Klass* klass, GCId gc_id, jlong count, julong size, const Ticks& timestamp) { + T event(UNTIMED); + if (event.should_commit()) { + event.set_gcId(gc_id.id()); + event.set_objectClass(klass); + event.set_count(count); + event.set_totalSize(size); + event.set_endtime(timestamp); + event.commit(); + } +} + +void ObjectCountEventSender::send(const KlassInfoEntry* entry, GCId gc_id, const Ticks& timestamp) { + Klass* klass = entry->klass(); + jlong count = entry->count(); + julong total_size = entry->words() * BytesPerWord; + + send_event_if_enabled(klass, gc_id, count, total_size, timestamp); + send_event_if_enabled(klass, gc_id, count, total_size, timestamp); } #endif // INCLUDE_SERVICES diff --git a/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp b/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp index b68e86dea8a84adf1028c6bca92a72b1859d9a6a..28c861143201dd6e33683e3e4e301f4ef2a667fb 100644 --- a/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp +++ b/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp @@ -32,10 +32,17 @@ #if INCLUDE_SERVICES class KlassInfoEntry; -class Ticks; class ObjectCountEventSender : public AllStatic { + static bool _should_send_requestable_event; + + template + static void send_event_if_enabled(Klass* klass, GCId gc_id, jlong count, julong size, const Ticks& timestamp); + public: + static void enable_requestable_event(); + static void disable_requestable_event(); + static void send(const KlassInfoEntry* entry, GCId gc_id, const Ticks& timestamp); static bool should_send_event(); }; diff --git a/src/share/vm/gc_interface/allocTracer.cpp b/src/share/vm/gc_interface/allocTracer.cpp index 777925945cefab623058b95bd3a4f6ccd20eb637..acb731f20227508d3dc7a85b6a253d0df7926088 100644 --- a/src/share/vm/gc_interface/allocTracer.cpp +++ b/src/share/vm/gc_interface/allocTracer.cpp @@ -25,23 +25,28 @@ #include "precompiled.hpp" #include "gc_implementation/shared/gcId.hpp" #include "gc_interface/allocTracer.hpp" -#include "trace/tracing.hpp" +#include "jfr/jfrEvents.hpp" #include "runtime/handles.hpp" #include "utilities/globalDefinitions.hpp" +#if INCLUDE_JFR +#include "jfr/support/jfrAllocationTracer.hpp" +#endif -void AllocTracer::send_allocation_outside_tlab_event(KlassHandle klass, size_t alloc_size) { - EventAllocObjectOutsideTLAB event; +void AllocTracer::send_allocation_outside_tlab_event(KlassHandle klass, HeapWord* obj, size_t alloc_size, Thread* thread) { + JFR_ONLY(JfrAllocationTracer tracer(obj, alloc_size, thread);) + EventObjectAllocationOutsideTLAB event; if (event.should_commit()) { - event.set_class(klass()); + event.set_objectClass(klass()); event.set_allocationSize(alloc_size); event.commit(); } } -void AllocTracer::send_allocation_in_new_tlab_event(KlassHandle klass, size_t tlab_size, size_t alloc_size) { - EventAllocObjectInNewTLAB event; +void AllocTracer::send_allocation_in_new_tlab_event(KlassHandle klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, Thread* thread) { + JFR_ONLY(JfrAllocationTracer tracer(obj, alloc_size, thread);) + EventObjectAllocationInNewTLAB event; if (event.should_commit()) { - event.set_class(klass()); + event.set_objectClass(klass()); event.set_allocationSize(alloc_size); event.set_tlabSize(tlab_size); event.commit(); diff --git a/src/share/vm/gc_interface/allocTracer.hpp b/src/share/vm/gc_interface/allocTracer.hpp index ddd9877d26b2d1d81c577d4f82940bf7fd03df99..ac565a247c5c549eec5426008fba4f3ea8316596 100644 --- a/src/share/vm/gc_interface/allocTracer.hpp +++ b/src/share/vm/gc_interface/allocTracer.hpp @@ -30,8 +30,8 @@ class AllocTracer : AllStatic { public: - static void send_allocation_outside_tlab_event(KlassHandle klass, size_t alloc_size); - static void send_allocation_in_new_tlab_event(KlassHandle klass, size_t tlab_size, size_t alloc_size); + static void send_allocation_outside_tlab_event(KlassHandle klass, HeapWord* obj, size_t alloc_size, Thread* thread); + static void send_allocation_in_new_tlab_event(KlassHandle klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, Thread* thread); static void send_allocation_requiring_gc_event(size_t size, const GCId& gcId); }; diff --git a/src/share/vm/gc_interface/collectedHeap.cpp b/src/share/vm/gc_interface/collectedHeap.cpp index b6219740408f3cbb94406d795c09fd6879c10411..f416dd1c80ad64d4e75dd2372f72159c43ca4d8a 100644 --- a/src/share/vm/gc_interface/collectedHeap.cpp +++ b/src/share/vm/gc_interface/collectedHeap.cpp @@ -286,7 +286,7 @@ HeapWord* CollectedHeap::allocate_from_tlab_slow(KlassHandle klass, Thread* thre return NULL; } - AllocTracer::send_allocation_in_new_tlab_event(klass, new_tlab_size * HeapWordSize, size * HeapWordSize); + AllocTracer::send_allocation_in_new_tlab_event(klass, obj, new_tlab_size * HeapWordSize, size * HeapWordSize, Thread::current()); if (ZeroTLAB) { // ..and clear it. diff --git a/src/share/vm/gc_interface/collectedHeap.inline.hpp b/src/share/vm/gc_interface/collectedHeap.inline.hpp index 60e80bfeac694aad8a2879ec673382c2485fb461..b8c1817e3cca2a806dfbbce5239b9b9cf3e25efc 100644 --- a/src/share/vm/gc_interface/collectedHeap.inline.hpp +++ b/src/share/vm/gc_interface/collectedHeap.inline.hpp @@ -140,7 +140,7 @@ HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t si "Unexpected exception, will result in uninitialized storage"); THREAD->incr_allocated_bytes(size * HeapWordSize); - AllocTracer::send_allocation_outside_tlab_event(klass, size * HeapWordSize); + AllocTracer::send_allocation_outside_tlab_event(klass, result, size * HeapWordSize, Thread::current()); return result; } diff --git a/src/share/vm/jfr/GenerateJfrFiles.java b/src/share/vm/jfr/GenerateJfrFiles.java new file mode 100644 index 0000000000000000000000000000000000000000..ac3b5b6d5b30ee2b6ed399d1ca1c40f3bdfa7fe3 --- /dev/null +++ b/src/share/vm/jfr/GenerateJfrFiles.java @@ -0,0 +1,756 @@ +package build.tools.jfr; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.xml.XMLConstants; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.validation.SchemaFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +public class GenerateJfrFiles { + + public static void main(String... args) throws Exception { + if (args.length != 3) { + System.err.println("Incorrect number of command line arguments."); + System.err.println("Usage:"); + System.err.println("java GenerateJfrFiles[.java] "); + System.exit(1); + } + try { + File metadataXml = new File(args[0]); + File metadataSchema = new File(args[1]); + File outputDirectory = new File(args[2]); + + Metadata metadata = new Metadata(metadataXml, metadataSchema); + metadata.verify(); + metadata.wireUpTypes(); + + printJfrPeriodicHpp(metadata, outputDirectory); + printJfrEventIdsHpp(metadata, outputDirectory); + printJfrEventControlHpp(metadata, outputDirectory); + printJfrTypesHpp(metadata, outputDirectory); + printJfrEventClassesHpp(metadata, outputDirectory); + + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + static class XmlType { + final String fieldType; + final String parameterType; + XmlType(String fieldType, String parameterType) { + this.fieldType = fieldType; + this.parameterType = parameterType; + } + } + + static class TypeElement { + List fields = new ArrayList<>(); + String name; + String fieldType; + String parameterType; + boolean supportStruct; + } + + interface TypePredicate { + boolean isType(TypeElement type); + } + + static class StringJoiner { + private final CharSequence delimiter; + private final List elements; + + public StringJoiner(CharSequence delimiter) { + this.delimiter = delimiter; + elements = new LinkedList(); + } + + public StringJoiner add(CharSequence newElement) { + elements.add(newElement); + return this; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + Iterator i = elements.iterator(); + while (i.hasNext()) { + builder.append(i.next()); + if (i.hasNext()) { + builder.append(delimiter); + } + } + return builder.toString(); + } + } + + static class Metadata { + final Map types = new LinkedHashMap<>(); + final Map xmlTypes = new HashMap<>(); + Metadata(File metadataXml, File metadataSchema) throws ParserConfigurationException, SAXException, FileNotFoundException, IOException { + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setSchema(schemaFactory.newSchema(metadataSchema)); + SAXParser sp = factory.newSAXParser(); + sp.parse(metadataXml, new MetadataHandler(this)); + } + + List getEvents() { + return getList(new TypePredicate() { + @Override + public boolean isType(TypeElement t) { + return t.getClass() == EventElement.class; + } + }); + } + + List getEventsAndStructs() { + return getList(new TypePredicate() { + @Override + public boolean isType(TypeElement t) { + return t.getClass() == EventElement.class || t.supportStruct; + } + }); + } + + List getTypesAndStructs() { + return getList(new TypePredicate() { + @Override + public boolean isType(TypeElement t) { + return t.getClass() == TypeElement.class || t.supportStruct; + } + }); + } + + @SuppressWarnings("unchecked") + List getList(TypePredicate pred) { + List result = new ArrayList<>(types.size()); + for (TypeElement t : types.values()) { + if (pred.isType(t)) { + result.add((T) t); + } + } + return result; + } + + List getPeriodicEvents() { + return getList(new TypePredicate() { + @Override + public boolean isType(TypeElement t) { + return t.getClass() == EventElement.class && ((EventElement) t).periodic; + } + }); + } + + List getNonEventsAndNonStructs() { + return getList(new TypePredicate() { + @Override + public boolean isType(TypeElement t) { + return t.getClass() != EventElement.class && !t.supportStruct; + } + }); + } + + List getTypes() { + return getList(new TypePredicate() { + @Override + public boolean isType(TypeElement t) { + return t.getClass() == TypeElement.class && !t.supportStruct; + } + }); + } + + List getStructs() { + return getList(new TypePredicate() { + @Override + public boolean isType(TypeElement t) { + return t.getClass() == TypeElement.class && t.supportStruct; + } + }); + } + + void verify() { + for (TypeElement t : types.values()) { + for (FieldElement f : t.fields) { + if (!xmlTypes.containsKey(f.typeName)) { // ignore primitives + if (!types.containsKey(f.typeName)) { + throw new IllegalStateException("Could not find definition of type '" + f.typeName + "' used by " + t.name + "#" + f.name); + } + } + } + } + } + + void wireUpTypes() { + for (TypeElement t : types.values()) { + for (FieldElement f : t.fields) { + TypeElement type = types.get(f.typeName); + if (f.struct) { + type.supportStruct = true; + } + f.type = type; + } + } + } + } + + static class EventElement extends TypeElement { + String representation; + boolean thread; + boolean stackTrace; + boolean startTime; + boolean periodic; + boolean cutoff; + } + + static class FieldElement { + final Metadata metadata; + TypeElement type; + String name; + String typeName; + boolean struct; + + FieldElement(Metadata metadata) { + this.metadata = metadata; + } + + String getParameterType() { + if (struct) { + return "const JfrStruct" + typeName + "&"; + } + XmlType xmlType = metadata.xmlTypes.get(typeName); + if (xmlType != null) { + return xmlType.parameterType; + } + return type != null ? "u8" : typeName; + } + + String getParameterName() { + return struct ? "value" : "new_value"; + } + + String getFieldType() { + if (struct) { + return "JfrStruct" + typeName; + } + XmlType xmlType = metadata.xmlTypes.get(typeName); + if (xmlType != null) { + return xmlType.fieldType; + } + return type != null ? "u8" : typeName; + } + } + + static class MetadataHandler extends DefaultHandler { + final Metadata metadata; + FieldElement currentField; + TypeElement currentType; + MetadataHandler(Metadata metadata) { + this.metadata = metadata; + } + @Override + public void error(SAXParseException e) throws SAXException { + throw e; + } + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + switch (qName) { + case "XmlType": + String name = attributes.getValue("name"); + String parameterType = attributes.getValue("parameterType"); + String fieldType = attributes.getValue("fieldType"); + metadata.xmlTypes.put(name, new XmlType(fieldType, parameterType)); + break; + case "Type": + currentType = new TypeElement(); + currentType.name = attributes.getValue("name"); + break; + case "Event": + EventElement eventtType = new EventElement(); + eventtType.name = attributes.getValue("name"); + eventtType.thread = getBoolean(attributes, "thread", false); + eventtType.stackTrace = getBoolean(attributes, "stackTrace", false); + eventtType.startTime = getBoolean(attributes, "startTime", true); + eventtType.periodic = attributes.getValue("period") != null; + eventtType.cutoff = getBoolean(attributes, "cutoff", false); + currentType = eventtType; + break; + case "Field": + currentField = new FieldElement(metadata); + currentField.struct = getBoolean(attributes, "struct", false); + currentField.name = attributes.getValue("name"); + currentField.typeName = attributes.getValue("type"); + break; + } + } + + private boolean getBoolean(Attributes attributes, String name, boolean defaultValue) { + String value = attributes.getValue(name); + return value == null ? defaultValue : Boolean.valueOf(value); + } + + @Override + public void endElement(String uri, String localName, String qName) { + switch (qName) { + case "Type": + case "Event": + metadata.types.put(currentType.name, currentType); + currentType = null; + break; + case "Field": + currentType.fields.add(currentField); + currentField = null; + break; + } + } + } + + static class Printer implements AutoCloseable { + final PrintStream out; + Printer(File outputDirectory, String filename) throws FileNotFoundException { + out = new PrintStream(new BufferedOutputStream(new FileOutputStream(new File(outputDirectory, filename)))); + write("/* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */"); + write(""); + } + + void write(String text) { + out.print(text); + out.print("\n"); // Don't use Windows line endings + } + + @Override + public void close() throws Exception { + out.close(); + } + } + + private static void printJfrPeriodicHpp(Metadata metadata, File outputDirectory) throws Exception { + try (Printer out = new Printer(outputDirectory, "jfrPeriodic.hpp")) { + out.write("#ifndef JFRFILES_JFRPERIODICEVENTSET_HPP"); + out.write("#define JFRFILES_JFRPERIODICEVENTSET_HPP"); + out.write(""); + out.write("#include \"utilities/macros.hpp\""); + out.write("#if INCLUDE_JFR"); + out.write("#include \"jfrfiles/jfrEventIds.hpp\""); + out.write("#include \"memory/allocation.hpp\""); + out.write(""); + out.write("class JfrPeriodicEventSet : public AllStatic {"); + out.write(" public:"); + out.write(" static void requestEvent(JfrEventId id) {"); + out.write(" switch(id) {"); + out.write(" "); + for (EventElement e : metadata.getPeriodicEvents()) { + out.write(" case Jfr" + e.name + "Event:"); + out.write(" request" + e.name + "();"); + out.write(" break;"); + out.write(" "); + } + out.write(" default:"); + out.write(" break;"); + out.write(" }"); + out.write(" }"); + out.write(""); + out.write(" private:"); + out.write(""); + for (EventElement e : metadata.getPeriodicEvents()) { + out.write(" static void request" + e.name + "(void);"); + out.write(""); + } + out.write("};"); + out.write(""); + out.write("#endif // INCLUDE_JFR"); + out.write("#endif // JFRFILES_JFRPERIODICEVENTSET_HPP"); + } + } + + private static void printJfrEventControlHpp(Metadata metadata, File outputDirectory) throws Exception { + try (Printer out = new Printer(outputDirectory, "jfrEventControl.hpp")) { + out.write("#ifndef JFRFILES_JFR_NATIVE_EVENTSETTING_HPP"); + out.write("#define JFRFILES_JFR_NATIVE_EVENTSETTING_HPP"); + out.write(""); + out.write("#include \"utilities/macros.hpp\""); + out.write("#if INCLUDE_JFR"); + out.write("#include \"jfrfiles/jfrEventIds.hpp\""); + out.write(""); + out.write("/**"); + out.write(" * Event setting. We add some padding so we can use our"); + out.write(" * event IDs as indexes into this."); + out.write(" */"); + out.write(""); + out.write("struct jfrNativeEventSetting {"); + out.write(" jlong threshold_ticks;"); + out.write(" jlong cutoff_ticks;"); + out.write(" u1 stacktrace;"); + out.write(" u1 enabled;"); + out.write(" u1 pad[6]; // Because GCC on linux ia32 at least tries to pack this."); + out.write("};"); + out.write(""); + out.write("union JfrNativeSettings {"); + out.write(" // Array version."); + out.write(" jfrNativeEventSetting bits[MaxJfrEventId];"); + out.write(" // Then, to make it easy to debug,"); + out.write(" // add named struct members also."); + out.write(" struct {"); + out.write(" jfrNativeEventSetting pad[NUM_RESERVED_EVENTS];"); + for (TypeElement t : metadata.getEventsAndStructs()) { + out.write(" jfrNativeEventSetting " + t.name + ";"); + } + out.write(" } ev;"); + out.write("};"); + out.write(""); + out.write("#endif // INCLUDE_JFR"); + out.write("#endif // JFRFILES_JFR_NATIVE_EVENTSETTING_HPP"); + } + } + + private static void printJfrEventIdsHpp(Metadata metadata, File outputDirectory) throws Exception { + try (Printer out = new Printer(outputDirectory, "jfrEventIds.hpp")) { + out.write("#ifndef JFRFILES_JFREVENTIDS_HPP"); + out.write("#define JFRFILES_JFREVENTIDS_HPP"); + out.write(""); + out.write("#include \"utilities/macros.hpp\""); + out.write("#if INCLUDE_JFR"); + out.write("#include \"jfrfiles/jfrTypes.hpp\""); + out.write(""); + out.write("/**"); + out.write(" * Enum of the event types in the JVM"); + out.write(" */"); + out.write("enum JfrEventId {"); + out.write(" _jfreventbase = (NUM_RESERVED_EVENTS-1), // Make sure we start at right index."); + out.write(" "); + out.write(" // Events -> enum entry"); + for (TypeElement t : metadata.getEventsAndStructs()) { + out.write(" Jfr" + t.name + "Event,"); + } + out.write(""); + out.write(" MaxJfrEventId"); + out.write("};"); + out.write(""); + out.write("/**"); + out.write(" * Struct types in the JVM"); + out.write(" */"); + out.write("enum JfrStructId {"); + for (TypeElement t : metadata.getNonEventsAndNonStructs()) { + out.write(" Jfr" + t.name + "Struct,"); + } + for (TypeElement t : metadata.getEventsAndStructs()) { + out.write(" Jfr" + t.name + "Struct,"); + } + out.write(""); + out.write(" MaxJfrStructId"); + out.write("};"); + out.write(""); + out.write("typedef enum JfrEventId JfrEventId;"); + out.write("typedef enum JfrStructId JfrStructId;"); + out.write(""); + out.write("#endif // INCLUDE_JFR"); + out.write("#endif // JFRFILES_JFREVENTIDS_HPP"); + } + } + + private static void printJfrTypesHpp(Metadata metadata, File outputDirectory) throws Exception { + List knownTypes = Arrays.asList(new String[] {"Thread", "StackTrace", "Class", "StackFrame"}); + try (Printer out = new Printer(outputDirectory, "jfrTypes.hpp")) { + out.write("#ifndef JFRFILES_JFRTYPES_HPP"); + out.write("#define JFRFILES_JFRTYPES_HPP"); + out.write(""); + out.write("#include \"utilities/macros.hpp\""); + out.write("#if INCLUDE_JFR"); + out.write(""); + out.write("enum JfrTypeId {"); + out.write(" TYPE_NONE = 0,"); + out.write(" TYPE_CLASS = 20,"); + out.write(" TYPE_STRING = 21,"); + out.write(" TYPE_THREAD = 22,"); + out.write(" TYPE_STACKTRACE = 23,"); + out.write(" TYPE_BYTES = 24,"); + out.write(" TYPE_EPOCHMILLIS = 25,"); + out.write(" TYPE_MILLIS = 26,"); + out.write(" TYPE_NANOS = 27,"); + out.write(" TYPE_TICKS = 28,"); + out.write(" TYPE_ADDRESS = 29,"); + out.write(" TYPE_PERCENTAGE = 30,"); + out.write(" TYPE_DUMMY,"); + out.write(" TYPE_DUMMY_1,"); + for (TypeElement type : metadata.getTypes()) { + if (!knownTypes.contains(type.name)) { + out.write(" TYPE_" + type.name.toUpperCase() + ","); + } + } + out.write(""); + out.write(" NUM_JFR_TYPES,"); + out.write(" TYPES_END = 255"); + out.write("};"); + out.write(""); + out.write("enum ReservedEvent {"); + out.write(" EVENT_METADATA,"); + out.write(" EVENT_CHECKPOINT,"); + out.write(" EVENT_BUFFERLOST,"); + out.write(" NUM_RESERVED_EVENTS = TYPES_END"); + out.write("};"); + out.write(""); + out.write("#endif // INCLUDE_JFR"); + out.write("#endif // JFRFILES_JFRTYPES_HPP"); + }; + } + + private static void printJfrEventClassesHpp(Metadata metadata, File outputDirectory) throws Exception { + try (Printer out = new Printer(outputDirectory, "jfrEventClasses.hpp")) { + out.write("#ifndef JFRFILES_JFREVENTCLASSES_HPP"); + out.write("#define JFRFILES_JFREVENTCLASSES_HPP"); + out.write(""); + out.write("#include \"oops/klass.hpp\""); + out.write("#include \"jfrfiles/jfrTypes.hpp\""); + out.write("#include \"jfr/utilities/jfrTypes.hpp\""); + out.write("#include \"utilities/macros.hpp\""); + out.write("#include \"utilities/ticks.hpp\""); + out.write("#if INCLUDE_JFR"); + out.write("#include \"jfr/recorder/service/jfrEvent.hpp\""); + out.write("/*"); + out.write(" * Each event class has an assert member function verify() which is invoked"); + out.write(" * just before the engine writes the event and its fields to the data stream."); + out.write(" * The purpose of verify() is to ensure that all fields in the event are initialized"); + out.write(" * and set before attempting to commit."); + out.write(" *"); + out.write(" * We enforce this requirement because events are generally stack allocated and therefore"); + out.write(" * *not* initialized to default values. This prevents us from inadvertently committing"); + out.write(" * uninitialized values to the data stream."); + out.write(" *"); + out.write(" * The assert message contains both the index (zero based) as well as the name of the field."); + out.write(" */"); + out.write(""); + printTypes(out, metadata, false); + out.write(""); + out.write(""); + out.write("#else // !INCLUDE_JFR"); + out.write(""); + out.write("template "); + out.write("class JfrEvent {"); + out.write(" public:"); + out.write(" JfrEvent() {}"); + out.write(" void set_starttime(const Ticks&) const {}"); + out.write(" void set_endtime(const Ticks&) const {}"); + out.write(" bool should_commit() const { return false; }"); + out.write(" static bool is_enabled() { return false; }"); + out.write(" void commit() {}"); + out.write("};"); + out.write(""); + printTypes(out, metadata, true); + out.write(""); + out.write(""); + out.write("#endif // INCLUDE_JFR"); + out.write("#endif // JFRFILES_JFREVENTCLASSES_HPP"); + } + } + + private static void printTypes(Printer out, Metadata metadata, boolean empty) { + for (TypeElement t : metadata.getStructs()) { + printType(out, t, empty); + out.write(""); + } + for (EventElement e : metadata.getEvents()) { + printEvent(out, e, empty); + out.write(""); + } + } + + private static void printType(Printer out, TypeElement t, boolean empty) { + out.write("struct JfrStruct" + t.name); + out.write("{"); + if (!empty) { + out.write(" private:"); + for (FieldElement f : t.fields) { + printField(out, f); + } + out.write(""); + } + out.write(" public:"); + for (FieldElement f : t.fields) { + printTypeSetter(out, f, empty); + } + out.write(""); + if (!empty) { + printWriteData(out, t.fields); + } + out.write("};"); + out.write(""); + } + + private static void printEvent(Printer out, EventElement event, boolean empty) { + out.write("class Event" + event.name + " : public JfrEvent"); + out.write("{"); + if (!empty) { + out.write(" private:"); + for (FieldElement f : event.fields) { + printField(out, f); + } + out.write(""); + } + out.write(" public:"); + if (!empty) { + out.write(" static const bool hasThread = " + event.thread + ";"); + out.write(" static const bool hasStackTrace = " + event.stackTrace + ";"); + out.write(" static const bool isInstant = " + !event.startTime + ";"); + out.write(" static const bool hasCutoff = " + event.cutoff + ";"); + out.write(" static const bool isRequestable = " + event.periodic + ";"); + out.write(" static const JfrEventId eventId = Jfr" + event.name + "Event;"); + out.write(""); + } + if (!empty) { + out.write(" Event" + event.name + "(EventStartTime timing=TIMED) : JfrEvent(timing) {}"); + } else { + out.write(" Event" + event.name + "(EventStartTime timing=TIMED) {}"); + } + out.write(""); + int index = 0; + for (FieldElement f : event.fields) { + out.write(" void set_" + f.name + "(" + f.getParameterType() + " " + f.getParameterName() + ") {"); + if (!empty) { + out.write(" this->_" + f.name + " = " + f.getParameterName() + ";"); + out.write(" DEBUG_ONLY(set_field_bit(" + index++ + "));"); + } + out.write(" }"); + } + out.write(""); + if (!empty) { + printWriteData(out, event.fields); + out.write(""); + } + out.write(" using JfrEvent::commit; // else commit() is hidden by overloaded versions in this class"); + printConstructor2(out, event, empty); + printCommitMethod(out, event, empty); + if (!empty) { + printVerify(out, event.fields); + } + out.write("};"); + } + + private static void printWriteData(Printer out, List fields) { + out.write(" template "); + out.write(" void writeData(Writer& w) {"); + for (FieldElement field : fields) { + if (field.struct) { + out.write(" _" + field.name + ".writeData(w);"); + } else { + out.write(" w.write(_" + field.name + ");"); + } + } + out.write(" }"); + } + + private static void printTypeSetter(Printer out, FieldElement field, boolean empty) { + if (!empty) { + out.write(" void set_" + field.name + "(" + field.getParameterType() + " new_value) { this->_" + field.name + " = new_value; }"); + } else { + out.write(" void set_" + field.name + "(" + field.getParameterType() + " new_value) { }"); + } + } + + private static void printVerify(Printer out, List fields) { + out.write(""); + out.write("#ifdef ASSERT"); + out.write(" void verify() const {"); + int index = 0; + for (FieldElement f : fields) { + out.write(" assert(verify_field_bit(" + index++ + "), \"Attempting to write an uninitialized event field: " + f.name + "\");"); + } + out.write(" }"); + out.write("#endif"); + } + + private static void printCommitMethod(Printer out, EventElement event, boolean empty) { + if (event.startTime) { + StringJoiner sj = new StringJoiner(",\n "); + for (FieldElement f : event.fields) { + sj.add(f.getParameterType() + " " + f.name); + } + out.write(""); + out.write(" void commit(" + sj.toString() + ") {"); + if (!empty) { + out.write(" if (should_commit()) {"); + for (FieldElement f : event.fields) { + out.write(" set_" + f.name + "(" + f.name + ");"); + } + out.write(" commit();"); + out.write(" }"); + } + out.write(" }"); + } + out.write(""); + StringJoiner sj = new StringJoiner(",\n "); + if (event.startTime) { + sj.add("const Ticks& startTicks"); + sj.add("const Ticks& endTicks"); + } + for (FieldElement f : event.fields) { + sj.add(f.getParameterType() + " " + f.name); + } + out.write(" static void commit(" + sj.toString() + ") {"); + if (!empty) { + out.write(" Event" + event.name + " me(UNTIMED);"); + out.write(""); + out.write(" if (me.should_commit()) {"); + if (event.startTime) { + out.write(" me.set_starttime(startTicks);"); + out.write(" me.set_endtime(endTicks);"); + } + for (FieldElement f : event.fields) { + out.write(" me.set_" + f.name + "(" + f.name + ");"); + } + out.write(" me.commit();"); + out.write(" }"); + } + out.write(" }"); + } + + private static void printConstructor2(Printer out, EventElement event, boolean empty) { + if (!event.startTime) { + out.write(""); + out.write(""); + } + if (event.startTime) { + out.write(""); + out.write(" Event" + event.name + "("); + StringJoiner sj = new StringJoiner(",\n "); + for (FieldElement f : event.fields) { + sj.add(f.getParameterType() + " " + f.name); + } + if (!empty) { + out.write(" " + sj.toString() + ") : JfrEvent(TIMED) {"); + out.write(" if (should_commit()) {"); + for (FieldElement f : event.fields) { + out.write(" set_" + f.name + "(" + f.name + ");"); + } + out.write(" }"); + } else { + out.write(" " + sj.toString() + ") {"); + } + out.write(" }"); + } + } + + private static void printField(Printer out, FieldElement field) { + out.write(" " + field.getFieldType() + " _" + field.name + ";"); + } +} diff --git a/src/share/vm/jfr/dcmd/jfrDcmds.cpp b/src/share/vm/jfr/dcmd/jfrDcmds.cpp new file mode 100644 index 0000000000000000000000000000000000000000..167405e3923209f5d5fb2487d39f088d65d8c778 --- /dev/null +++ b/src/share/vm/jfr/dcmd/jfrDcmds.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (c) 2012, 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 "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/vmSymbols.hpp" +#include "jfr/jfr.hpp" +#include "jfr/dcmd/jfrDcmds.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "memory/resourceArea.hpp" +#include "oops/oop.inline.hpp" +#include "oops/symbol.hpp" +#include "runtime/handles.inline.hpp" +#include "services/diagnosticArgument.hpp" +#include "services/diagnosticFramework.hpp" +#include "utilities/globalDefinitions.hpp" + +#ifdef _WINDOWS +#define JFR_FILENAME_EXAMPLE "C:\\Users\\user\\My Recording.jfr" +#endif + +#ifdef __APPLE__ +#define JFR_FILENAME_EXAMPLE "/Users/user/My Recording.jfr" +#endif + +#ifndef JFR_FILENAME_EXAMPLE +#define JFR_FILENAME_EXAMPLE "/home/user/My Recording.jfr" +#endif + +// JNIHandle management + +// ------------------------------------------------------------------ +// push_jni_handle_block +// +// Push on a new block of JNI handles. +static void push_jni_handle_block(Thread* const thread) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread)); + + // Allocate a new block for JNI handles. + // Inlined code from jni_PushLocalFrame() + JNIHandleBlock* prev_handles = thread->active_handles(); + JNIHandleBlock* entry_handles = JNIHandleBlock::allocate_block(thread); + assert(entry_handles != NULL && prev_handles != NULL, "should not be NULL"); + entry_handles->set_pop_frame_link(prev_handles); // make sure prev handles get gc'd. + thread->set_active_handles(entry_handles); +} + +// ------------------------------------------------------------------ +// pop_jni_handle_block +// +// Pop off the current block of JNI handles. +static void pop_jni_handle_block(Thread* const thread) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread)); + + // Release our JNI handle block + JNIHandleBlock* entry_handles = thread->active_handles(); + JNIHandleBlock* prev_handles = entry_handles->pop_frame_link(); + // restore + thread->set_active_handles(prev_handles); + entry_handles->set_pop_frame_link(NULL); + JNIHandleBlock::release_block(entry_handles, thread); // may block +} + +class JNIHandleBlockManager : public StackObj { + private: + Thread* const _thread; + public: + JNIHandleBlockManager(Thread* thread) : _thread(thread) { + push_jni_handle_block(_thread); + } + + ~JNIHandleBlockManager() { + pop_jni_handle_block(_thread); + } +}; + +static bool is_disabled(outputStream* output) { + if (Jfr::is_disabled()) { + if (output != NULL) { + output->print_cr("Flight Recorder is disabled.\n"); + } + return true; + } + return false; +} + +static bool is_recorder_instance_created(outputStream* output) { + if (!JfrRecorder::is_created()) { + if (output != NULL) { + output->print_cr("No available recordings.\n"); + output->print_cr("Use JFR.start to start a recording.\n"); + } + return false; + } + return true; +} + +static bool invalid_state(outputStream* out, TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + return is_disabled(out); +} + +static void print_pending_exception(outputStream* output, oop throwable) { + assert(throwable != NULL, "invariant"); + + oop msg = java_lang_Throwable::message(throwable); + + if (msg != NULL) { + char* text = java_lang_String::as_utf8_string(msg); + output->print_raw_cr(text); + } +} + +static void print_message(outputStream* output, const char* message) { + if (message != NULL) { + output->print_raw(message); + } +} + +static void handle_dcmd_result(outputStream* output, + const oop result, + const DCmdSource source, + TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + assert(output != NULL, "invariant"); + if (HAS_PENDING_EXCEPTION) { + print_pending_exception(output, PENDING_EXCEPTION); + // Don't clear excption on startup, JVM should fail initialization. + if (DCmd_Source_Internal != source) { + CLEAR_PENDING_EXCEPTION; + } + return; + } + + assert(!HAS_PENDING_EXCEPTION, "invariant"); + + if (result != NULL) { + const char* result_chars = java_lang_String::as_utf8_string(result); + print_message(output, result_chars); + } +} + +static oop construct_dcmd_instance(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + assert(args->klass() != NULL, "invariant"); + args->set_name("", CHECK_NULL); + args->set_signature("()V", CHECK_NULL); + JfrJavaSupport::new_object(args, CHECK_NULL); + return (oop)args->result()->get_jobject(); +} + +JfrDumpFlightRecordingDCmd::JfrDumpFlightRecordingDCmd(outputStream* output, + bool heap) : DCmdWithParser(output, heap), + _name("name", "Recording name, e.g. \\\"My Recording\\\"", "STRING", false, NULL), + _filename("filename", "Copy recording data to file, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false), + _maxage("maxage", "Maximum duration to dump, in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"), + _maxsize("maxsize", "Maximum amount of bytes to dump, in (M)B or (G)B, e.g. 500M, or 0 for no limit", "MEMORY SIZE", false, "0"), + _begin("begin", "Point in time to dump data from, e.g. 09:00, 21:35:00, 2018-06-03T18:12:56.827Z, 2018-06-03T20:13:46.832, -10m, -3h, or -1d", "STRING", false), + _end("end", "Point in time to dump data to, e.g. 09:00, 21:35:00, 2018-06-03T18:12:56.827Z, 2018-06-03T20:13:46.832, -10m, -3h, or -1d", "STRING", false), + _path_to_gc_roots("path-to-gc-roots", "Collect path to GC roots", "BOOLEAN", false, "false") { + _dcmdparser.add_dcmd_option(&_name); + _dcmdparser.add_dcmd_option(&_filename); + _dcmdparser.add_dcmd_option(&_maxage); + _dcmdparser.add_dcmd_option(&_maxsize); + _dcmdparser.add_dcmd_option(&_begin); + _dcmdparser.add_dcmd_option(&_end); + _dcmdparser.add_dcmd_option(&_path_to_gc_roots); +}; + +int JfrDumpFlightRecordingDCmd::num_arguments() { + ResourceMark rm; + JfrDumpFlightRecordingDCmd* dcmd = new JfrDumpFlightRecordingDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } + return 0; +} + +void JfrDumpFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + + if (invalid_state(output(), THREAD) || !is_recorder_instance_created(output())) { + return; + } + + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + JNIHandleBlockManager jni_handle_management(THREAD); + + JavaValue result(T_OBJECT); + JfrJavaArguments constructor_args(&result); + constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdDump", CHECK); + const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK); + Handle h_dcmd_instance(THREAD, dcmd); + assert(h_dcmd_instance.not_null(), "invariant"); + + jstring name = NULL; + if (_name.is_set() && _name.value() != NULL) { + name = JfrJavaSupport::new_string(_name.value(), CHECK); + } + + jstring filepath = NULL; + if (_filename.is_set() && _filename.value() != NULL) { + filepath = JfrJavaSupport::new_string(_filename.value(), CHECK); + } + + jobject maxage = NULL; + if (_maxage.is_set()) { + maxage = JfrJavaSupport::new_java_lang_Long(_maxage.value()._nanotime, CHECK); + } + + jobject maxsize = NULL; + if (_maxsize.is_set()) { + maxsize = JfrJavaSupport::new_java_lang_Long(_maxsize.value()._size, CHECK); + } + + jstring begin = NULL; + if (_begin.is_set() && _begin.value() != NULL) { + begin = JfrJavaSupport::new_string(_begin.value(), CHECK); + } + + jstring end = NULL; + if (_end.is_set() && _end.value() != NULL) { + end = JfrJavaSupport::new_string(_end.value(), CHECK); + } + + jobject path_to_gc_roots = NULL; + if (_path_to_gc_roots.is_set()) { + path_to_gc_roots = JfrJavaSupport::new_java_lang_Boolean(_path_to_gc_roots.value(), CHECK); + } + + static const char klass[] = "jdk/jfr/internal/dcmd/DCmdDump"; + static const char method[] = "execute"; + static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/String;"; + + JfrJavaArguments execute_args(&result, klass, method, signature, CHECK); + execute_args.set_receiver(h_dcmd_instance); + + // arguments + execute_args.push_jobject(name); + execute_args.push_jobject(filepath); + execute_args.push_jobject(maxage); + execute_args.push_jobject(maxsize); + execute_args.push_jobject(begin); + execute_args.push_jobject(end); + execute_args.push_jobject(path_to_gc_roots); + + JfrJavaSupport::call_virtual(&execute_args, THREAD); + handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD); +} + +JfrCheckFlightRecordingDCmd::JfrCheckFlightRecordingDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), + _name("name","Recording name, e.g. \\\"My Recording\\\" or omit to see all recordings","STRING",false, NULL), + _verbose("verbose","Print event settings for the recording(s)","BOOLEAN", + false, "false") { + _dcmdparser.add_dcmd_option(&_name); + _dcmdparser.add_dcmd_option(&_verbose); +}; + +int JfrCheckFlightRecordingDCmd::num_arguments() { + ResourceMark rm; + JfrCheckFlightRecordingDCmd* dcmd = new JfrCheckFlightRecordingDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } + return 0; +} + +void JfrCheckFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + + if (invalid_state(output(), THREAD) || !is_recorder_instance_created(output())) { + return; + } + + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + JNIHandleBlockManager jni_handle_management(THREAD); + + JavaValue result(T_OBJECT); + JfrJavaArguments constructor_args(&result); + constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdCheck", CHECK); + const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK); + Handle h_dcmd_instance(THREAD, dcmd); + assert(h_dcmd_instance.not_null(), "invariant"); + + jstring name = NULL; + if (_name.is_set() && _name.value() != NULL) { + name = JfrJavaSupport::new_string(_name.value(), CHECK); + } + + jobject verbose = NULL; + if (_verbose.is_set()) { + verbose = JfrJavaSupport::new_java_lang_Boolean(_verbose.value(), CHECK); + } + + static const char klass[] = "jdk/jfr/internal/dcmd/DCmdCheck"; + static const char method[] = "execute"; + static const char signature[] = "(Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/String;"; + + JfrJavaArguments execute_args(&result, klass, method, signature, CHECK); + execute_args.set_receiver(h_dcmd_instance); + + // arguments + execute_args.push_jobject(name); + execute_args.push_jobject(verbose); + + JfrJavaSupport::call_virtual(&execute_args, THREAD); + handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD); +} + +JfrStartFlightRecordingDCmd::JfrStartFlightRecordingDCmd(outputStream* output, + bool heap) : DCmdWithParser(output, heap), + _name("name", "Name that can be used to identify recording, e.g. \\\"My Recording\\\"", "STRING", false, NULL), + _settings("settings", "Settings file(s), e.g. profile or default. See JRE_HOME/lib/jfr", "STRING SET", false), + _delay("delay", "Delay recording start with (s)econds, (m)inutes), (h)ours), or (d)ays, e.g. 5h.", "NANOTIME", false, "0"), + _duration("duration", "Duration of recording in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 300s.", "NANOTIME", false, "0"), + _filename("filename", "Resulting recording filename, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false), + _disk("disk", "Recording should be persisted to disk", "BOOLEAN", false), + _maxage("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"), + _maxsize("maxsize", "Maximum amount of bytes to keep (on disk) in (k)B, (M)B or (G)B, e.g. 500M, or 0 for no limit", "MEMORY SIZE", false, "0"), + _dump_on_exit("dumponexit", "Dump running recording when JVM shuts down", "BOOLEAN", false), + _path_to_gc_roots("path-to-gc-roots", "Collect path to GC roots", "BOOLEAN", false, "false") { + _dcmdparser.add_dcmd_option(&_name); + _dcmdparser.add_dcmd_option(&_settings); + _dcmdparser.add_dcmd_option(&_delay); + _dcmdparser.add_dcmd_option(&_duration); + _dcmdparser.add_dcmd_option(&_disk); + _dcmdparser.add_dcmd_option(&_filename); + _dcmdparser.add_dcmd_option(&_maxage); + _dcmdparser.add_dcmd_option(&_maxsize); + _dcmdparser.add_dcmd_option(&_dump_on_exit); + _dcmdparser.add_dcmd_option(&_path_to_gc_roots); +}; + +int JfrStartFlightRecordingDCmd::num_arguments() { + ResourceMark rm; + JfrStartFlightRecordingDCmd* dcmd = new JfrStartFlightRecordingDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } + return 0; +} + +void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + + if (invalid_state(output(), THREAD)) { + return; + } + + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + JNIHandleBlockManager jni_handle_management(THREAD); + + JavaValue result(T_OBJECT); + JfrJavaArguments constructor_args(&result); + constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdStart", THREAD); + const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK); + Handle h_dcmd_instance(THREAD, dcmd); + assert(h_dcmd_instance.not_null(), "invariant"); + + jstring name = NULL; + if (_name.is_set() && _name.value() != NULL) { + name = JfrJavaSupport::new_string(_name.value(), CHECK); + } + + jstring filename = NULL; + if (_filename.is_set() && _filename.value() != NULL) { + filename = JfrJavaSupport::new_string(_filename.value(), CHECK); + } + + jobject maxage = NULL; + if (_maxage.is_set()) { + maxage = JfrJavaSupport::new_java_lang_Long(_maxage.value()._nanotime, CHECK); + } + + jobject maxsize = NULL; + if (_maxsize.is_set()) { + maxsize = JfrJavaSupport::new_java_lang_Long(_maxsize.value()._size, CHECK); + } + + jobject duration = NULL; + if (_duration.is_set()) { + duration = JfrJavaSupport::new_java_lang_Long(_duration.value()._nanotime, CHECK); + } + + jobject delay = NULL; + if (_delay.is_set()) { + delay = JfrJavaSupport::new_java_lang_Long(_delay.value()._nanotime, CHECK); + } + + jobject disk = NULL; + if (_disk.is_set()) { + disk = JfrJavaSupport::new_java_lang_Boolean(_disk.value(), CHECK); + } + + jobject dump_on_exit = NULL; + if (_dump_on_exit.is_set()) { + dump_on_exit = JfrJavaSupport::new_java_lang_Boolean(_dump_on_exit.value(), CHECK); + } + + jobject path_to_gc_roots = NULL; + if (_path_to_gc_roots.is_set()) { + path_to_gc_roots = JfrJavaSupport::new_java_lang_Boolean(_path_to_gc_roots.value(), CHECK); + } + + jobjectArray settings = NULL; + if (_settings.is_set()) { + int length = _settings.value()->array()->length(); + if (length == 1) { + const char* c_str = _settings.value()->array()->at(0); + if (strcmp(c_str, "none") == 0) { + length = 0; + } + } + settings = JfrJavaSupport::new_string_array(length, CHECK); + assert(settings != NULL, "invariant"); + for (int i = 0; i < length; ++i) { + jobject element = JfrJavaSupport::new_string(_settings.value()->array()->at(i), CHECK); + assert(element != NULL, "invariant"); + JfrJavaSupport::set_array_element(settings, element, i, CHECK); + } + } else { + settings = JfrJavaSupport::new_string_array(1, CHECK); + assert(settings != NULL, "invariant"); + jobject element = JfrJavaSupport::new_string("default", CHECK); + assert(element != NULL, "invariant"); + JfrJavaSupport::set_array_element(settings, element, 0, CHECK); + } + + static const char klass[] = "jdk/jfr/internal/dcmd/DCmdStart"; + static const char method[] = "execute"; + static const char signature[] = "(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Long;" + "Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;" + "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;"; + + JfrJavaArguments execute_args(&result, klass, method, signature, CHECK); + execute_args.set_receiver(h_dcmd_instance); + + // arguments + execute_args.push_jobject(name); + execute_args.push_jobject(settings); + execute_args.push_jobject(delay); + execute_args.push_jobject(duration); + execute_args.push_jobject(disk); + execute_args.push_jobject(filename); + execute_args.push_jobject(maxage); + execute_args.push_jobject(maxsize); + execute_args.push_jobject(dump_on_exit); + execute_args.push_jobject(path_to_gc_roots); + + JfrJavaSupport::call_virtual(&execute_args, THREAD); + handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD); +} + +JfrStopFlightRecordingDCmd::JfrStopFlightRecordingDCmd(outputStream* output, + bool heap) : DCmdWithParser(output, heap), + _name("name", "Recording text,.e.g \\\"My Recording\\\"", "STRING", true, NULL), + _filename("filename", "Copy recording data to file, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false, NULL) { + _dcmdparser.add_dcmd_option(&_name); + _dcmdparser.add_dcmd_option(&_filename); +}; + +int JfrStopFlightRecordingDCmd::num_arguments() { + ResourceMark rm; + JfrStopFlightRecordingDCmd* dcmd = new JfrStopFlightRecordingDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } + return 0; +} + +void JfrStopFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + + if (invalid_state(output(), THREAD) || !is_recorder_instance_created(output())) { + return; + } + + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + JNIHandleBlockManager jni_handle_management(THREAD); + + JavaValue result(T_OBJECT); + JfrJavaArguments constructor_args(&result); + constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdStop", CHECK); + const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK); + Handle h_dcmd_instance(THREAD, dcmd); + assert(h_dcmd_instance.not_null(), "invariant"); + + jstring name = NULL; + if (_name.is_set() && _name.value() != NULL) { + name = JfrJavaSupport::new_string(_name.value(), CHECK); + } + + jstring filepath = NULL; + if (_filename.is_set() && _filename.value() != NULL) { + filepath = JfrJavaSupport::new_string(_filename.value(), CHECK); + } + + static const char klass[] = "jdk/jfr/internal/dcmd/DCmdStop"; + static const char method[] = "execute"; + static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"; + + JfrJavaArguments execute_args(&result, klass, method, signature, CHECK); + execute_args.set_receiver(h_dcmd_instance); + + // arguments + execute_args.push_jobject(name); + execute_args.push_jobject(filepath); + + JfrJavaSupport::call_virtual(&execute_args, THREAD); + handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD); +} + +JfrConfigureFlightRecorderDCmd::JfrConfigureFlightRecorderDCmd(outputStream* output, + bool heap) : DCmdWithParser(output, heap), + _repository_path("repositorypath", "Path to repository,.e.g \\\"My Repository\\\"", "STRING", false, NULL), + _dump_path("dumppath", "Path to dump,.e.g \\\"My Dump path\\\"", "STRING", false, NULL), + _stack_depth("stackdepth", "Stack Depth", "JULONG", false, "64"), + _global_buffer_count("globalbuffercount", "Number of global buffers,", "JULONG", false, "20"), + _global_buffer_size("globalbuffersize", "Size of a global buffers,", "MEMORY SIZE", false, "512k"), + _thread_buffer_size("thread_buffer_size", "Size of a thread buffer", "MEMORY SIZE", false, "8k"), + _memory_size("memorysize", "Overall memory size, ", "MEMORY SIZE", false, "10m"), + _max_chunk_size("maxchunksize", "Size of an individual disk chunk", "MEMORY SIZE", false, "12m"), + _sample_threads("samplethreads", "Activate Thread sampling", "BOOLEAN", false, "true") { + _dcmdparser.add_dcmd_option(&_repository_path); + _dcmdparser.add_dcmd_option(&_dump_path); + _dcmdparser.add_dcmd_option(&_stack_depth); + _dcmdparser.add_dcmd_option(&_global_buffer_count); + _dcmdparser.add_dcmd_option(&_global_buffer_size); + _dcmdparser.add_dcmd_option(&_thread_buffer_size); + _dcmdparser.add_dcmd_option(&_memory_size); + _dcmdparser.add_dcmd_option(&_max_chunk_size); + _dcmdparser.add_dcmd_option(&_sample_threads); +}; + +int JfrConfigureFlightRecorderDCmd::num_arguments() { + ResourceMark rm; + JfrConfigureFlightRecorderDCmd* dcmd = new JfrConfigureFlightRecorderDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } + return 0; +} + +void JfrConfigureFlightRecorderDCmd::execute(DCmdSource source, TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + + if (invalid_state(output(), THREAD)) { + return; + } + + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + JNIHandleBlockManager jni_handle_management(THREAD); + + JavaValue result(T_OBJECT); + JfrJavaArguments constructor_args(&result); + constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdConfigure", CHECK); + const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK); + Handle h_dcmd_instance(THREAD, dcmd); + assert(h_dcmd_instance.not_null(), "invariant"); + + jstring repository_path = NULL; + if (_repository_path.is_set() && _repository_path.value() != NULL) { + repository_path = JfrJavaSupport::new_string(_repository_path.value(), CHECK); + } + + jstring dump_path = NULL; + if (_dump_path.is_set() && _dump_path.value() != NULL) { + dump_path = JfrJavaSupport::new_string(_dump_path.value(), CHECK); + } + + jobject stack_depth = NULL; + if (_stack_depth.is_set()) { + stack_depth = JfrJavaSupport::new_java_lang_Integer((jint)_stack_depth.value(), CHECK); + } + + jobject global_buffer_count = NULL; + if (_global_buffer_count.is_set()) { + global_buffer_count = JfrJavaSupport::new_java_lang_Long(_global_buffer_count.value(), CHECK); + } + + jobject global_buffer_size = NULL; + if (_global_buffer_size.is_set()) { + global_buffer_size = JfrJavaSupport::new_java_lang_Long(_global_buffer_size.value()._size, CHECK); + } + + jobject thread_buffer_size = NULL; + if (_thread_buffer_size.is_set()) { + thread_buffer_size = JfrJavaSupport::new_java_lang_Long(_thread_buffer_size.value()._size, CHECK); + } + + jobject max_chunk_size = NULL; + if (_max_chunk_size.is_set()) { + max_chunk_size = JfrJavaSupport::new_java_lang_Long(_max_chunk_size.value()._size, CHECK); + } + + jobject memory_size = NULL; + if (_memory_size.is_set()) { + memory_size = JfrJavaSupport::new_java_lang_Long(_memory_size.value()._size, CHECK); + } + + jobject sample_threads = NULL; + if (_sample_threads.is_set()) { + sample_threads = JfrJavaSupport::new_java_lang_Boolean(_sample_threads.value(), CHECK); + } + + static const char klass[] = "jdk/jfr/internal/dcmd/DCmdConfigure"; + static const char method[] = "execute"; + static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;" + "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;" + "Ljava/lang/Long;Ljava/lang/Boolean;)Ljava/lang/String;"; + + JfrJavaArguments execute_args(&result, klass, method, signature, CHECK); + execute_args.set_receiver(h_dcmd_instance); + + // params + execute_args.push_jobject(repository_path); + execute_args.push_jobject(dump_path); + execute_args.push_jobject(stack_depth); + execute_args.push_jobject(global_buffer_count); + execute_args.push_jobject(global_buffer_size); + execute_args.push_jobject(thread_buffer_size); + execute_args.push_jobject(memory_size); + execute_args.push_jobject(max_chunk_size); + execute_args.push_jobject(sample_threads); + + JfrJavaSupport::call_virtual(&execute_args, THREAD); + handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD); +} + +bool register_jfr_dcmds() { + uint32_t full_export = DCmd_Source_Internal | DCmd_Source_AttachAPI | DCmd_Source_MBean; + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + return true; +} + diff --git a/src/share/vm/jfr/dcmd/jfrDcmds.hpp b/src/share/vm/jfr/dcmd/jfrDcmds.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f6de5d68d197a76b7fad7da38d0e375872b22de1 --- /dev/null +++ b/src/share/vm/jfr/dcmd/jfrDcmds.hpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2012, 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 SHARE_VM_JFR_JFRDCMDS_HPP +#define SHARE_VM_JFR_JFRDCMDS_HPP + +#include "services/diagnosticCommand.hpp" + +class JfrDumpFlightRecordingDCmd : public DCmdWithParser { + protected: + DCmdArgument _name; + DCmdArgument _filename; + DCmdArgument _maxage; + DCmdArgument _maxsize; + DCmdArgument _begin; + DCmdArgument _end; + DCmdArgument _path_to_gc_roots; + + public: + JfrDumpFlightRecordingDCmd(outputStream* output, bool heap); + static const char* name() { + return "JFR.dump"; + } + static const char* description() { + return "Copies contents of a JFR recording to file. Either the name or the recording id must be specified."; + } + static const char* impact() { + return "Low"; + } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL}; + return p; + } + static int num_arguments(); + virtual void execute(DCmdSource source, TRAPS); +}; + +class JfrCheckFlightRecordingDCmd : public DCmdWithParser { + protected: + DCmdArgument _name; + DCmdArgument _verbose; + + public: + JfrCheckFlightRecordingDCmd(outputStream* output, bool heap); + static const char* name() { + return "JFR.check"; + } + static const char* description() { + return "Checks running JFR recording(s)"; + } + static const char* impact() { + return "Low"; + } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL}; + return p; + } + static int num_arguments(); + virtual void execute(DCmdSource source, TRAPS); +}; + +class JfrStartFlightRecordingDCmd : public DCmdWithParser { + protected: + DCmdArgument _name; + DCmdArgument _settings; + DCmdArgument _delay; + DCmdArgument _duration; + DCmdArgument _disk; + DCmdArgument _filename; + DCmdArgument _maxage; + DCmdArgument _maxsize; + DCmdArgument _dump_on_exit; + DCmdArgument _path_to_gc_roots; + + public: + JfrStartFlightRecordingDCmd(outputStream* output, bool heap); + static const char* name() { + return "JFR.start"; + } + static const char* description() { + return "Starts a new JFR recording"; + } + static const char* impact() { + return "Medium: Depending on the settings for a recording, the impact can range from low to high."; + } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL}; + return p; + } + static int num_arguments(); + virtual void execute(DCmdSource source, TRAPS); +}; + +class JfrStopFlightRecordingDCmd : public DCmdWithParser { + protected: + DCmdArgument _name; + DCmdArgument _filename; + + public: + JfrStopFlightRecordingDCmd(outputStream* output, bool heap); + static const char* name() { + return "JFR.stop"; + } + static const char* description() { + return "Stops a JFR recording"; + } + static const char* impact() { + return "Low"; + } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL}; + return p; + } + static int num_arguments(); + virtual void execute(DCmdSource source, TRAPS); +}; + +class JfrRuntimeOptions; + +class JfrConfigureFlightRecorderDCmd : public DCmdWithParser { + friend class JfrOptionSet; + protected: + DCmdArgument _repository_path; + DCmdArgument _dump_path; + DCmdArgument _stack_depth; + DCmdArgument _global_buffer_count; + DCmdArgument _global_buffer_size; + DCmdArgument _thread_buffer_size; + DCmdArgument _memory_size; + DCmdArgument _max_chunk_size; + DCmdArgument _sample_threads; + + public: + JfrConfigureFlightRecorderDCmd(outputStream* output, bool heap); + static const char* name() { + return "JFR.configure"; + } + static const char* description() { + return "Configure JFR"; + } + static const char* impact() { + return "Low"; + } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", NULL}; + return p; + } + static int num_arguments(); + virtual void execute(DCmdSource source, TRAPS); +}; + +class JfrUnlockCommercialFeaturesDCmd : public DCmd { +public: + JfrUnlockCommercialFeaturesDCmd(outputStream* output, bool heap) : DCmd(output, heap) { } + static const char* name() { return "VM.unlock_commercial_features"; } + static const char* description() { + return "Simulate commercial features unlocking for Zulu."; + } + static const char* impact() { return "Low"; } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", + "monitor", NULL}; + return p; + } + static int num_arguments() { return 0; } + virtual void execute(DCmdSource source, TRAPS) { + UnlockCommercialFeatures = true; + } +}; + +bool register_jfr_dcmds(); + +#endif // SHARE_VM_JFR_JFRDCMDS_HPP diff --git a/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.cpp b/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ffd8acfa10cc63801119b35cd3259f98b0d2d57 --- /dev/null +++ b/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.cpp @@ -0,0 +1,1551 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "jvm.h" +#include "classfile/classFileParser.hpp" +#include "classfile/classFileStream.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/stackMapTable.hpp" +#include "classfile/verificationType.hpp" +#include "interpreter/bytecodes.hpp" +#include "jfr/instrumentation/jfrEventClassTransformer.hpp" +#include "jfr/jfr.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/jni/jfrUpcalls.hpp" +#include "jfr/support/jfrEventClass.hpp" +#include "jfr/utilities/jfrBigEndian.hpp" +#include "jfr/writers/jfrBigEndianWriter.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" +//#include "oops/array.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/method.hpp" +#include "prims/jvmtiRedefineClasses.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.inline.hpp" +#include "utilities/exceptions.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/macros.hpp" + +static const u2 number_of_new_methods = 5; +static const u2 number_of_new_fields = 3; +static const int extra_stream_bytes = 0x280; +static const u2 invalid_cp_index = 0; + +static const char* utf8_constants[] = { + "Code", // 0 + "J", // 1 + "commit", // 2 + "eventHandler", // 3 + "Ljdk/jfr/proxy/internal/EventHandlerProxy;", // 4 + "duration", // 5 + "begin", // 6 + "()V", // 7 + "isEnabled", // 8 + "()Z", // 9 + "end", // 10 + "shouldCommit", // 11 + "startTime", // 12 + "", // 13 + "jdk/jfr/FlightRecorder", // 14 + "register", // 15 + "(Ljava/lang/Class;)V", // 16 // LAST_REQUIRED_UTF8 + "StackMapTable", // 17 + "Exceptions", // 18 + "LineNumberTable", // 20 + "LocalVariableTable", // 21 + "LocalVariableTypeTable", // 22 + "RuntimeVisibleAnnotation" // 23 +}; + +enum utf8_req_symbols { + UTF8_REQ_Code, + UTF8_REQ_J_FIELD_DESC, + UTF8_REQ_commit, + UTF8_REQ_eventHandler, + UTF8_REQ_eventHandler_FIELD_DESC, + UTF8_REQ_duration, + UTF8_REQ_begin, + UTF8_REQ_EMPTY_VOID_METHOD_DESC, + UTF8_REQ_isEnabled, + UTF8_REQ_EMPTY_BOOLEAN_METHOD_DESC, + UTF8_REQ_end, + UTF8_REQ_shouldCommit, + UTF8_REQ_startTime, + UTF8_REQ_clinit, + UTF8_REQ_FlightRecorder, + UTF8_REQ_register, + UTF8_REQ_CLASS_VOID_METHOD_DESC, + NOF_UTF8_REQ_SYMBOLS +}; + +enum utf8_opt_symbols { + UTF8_OPT_StackMapTable = NOF_UTF8_REQ_SYMBOLS, + UTF8_OPT_Exceptions, + UTF8_OPT_LineNumberTable, + UTF8_OPT_LocalVariableTable, + UTF8_OPT_LocalVariableTypeTable, + UTF8_OPT_RuntimeVisibleAnnotation, + NOF_UTF8_SYMBOLS +}; + +static u1 empty_void_method_code_attribute[] = { + 0x0, + 0x0, + 0x0, + 0xd, // attribute len + 0x0, + 0x0, // max stack + 0x0, + 0x1, // max locals + 0x0, + 0x0, + 0x0, + 0x1, // code length + Bytecodes::_return, + 0x0, + 0x0, // ex table len + 0x0, + 0x0 // attributes_count +}; + +static u1 boolean_method_code_attribute[] = { + 0x0, + 0x0, + 0x0, + 0xe, + 0x0, + 0x1, // max stack + 0x0, + 0x1, // max locals + 0x0, + 0x0, + 0x0, + 0x2, + Bytecodes::_iconst_0, + Bytecodes::_ireturn, + 0x0, + 0x0, // ex table len + 0x0, + 0x0, // attributes_count +}; + +// annotation processing support + +enum { // initial annotation layout + atype_off = 0, // utf8 such as 'Ljava/lang/annotation/Retention;' + count_off = 2, // u2 such as 1 (one value) + member_off = 4, // utf8 such as 'value' + tag_off = 6, // u1 such as 'c' (type) or 'e' (enum) + e_tag_val = 'e', + e_type_off = 7, // utf8 such as 'Ljava/lang/annotation/RetentionPolicy;' + e_con_off = 9, // utf8 payload, such as 'SOURCE', 'CLASS', 'RUNTIME' + e_size = 11, // end of 'e' annotation + c_tag_val = 'c', // payload is type + c_con_off = 7, // utf8 payload, such as 'I' + c_size = 9, // end of 'c' annotation + s_tag_val = 's', // payload is String + s_con_off = 7, // utf8 payload, such as 'Ljava/lang/String;' + s_size = 9, + min_size = 6 // smallest possible size (zero members) +}; + +static int skip_annotation_value(const address, int, int); // fwd decl + +// Skip an annotation. Return >=limit if there is any problem. +static int next_annotation_index(const address buffer, int limit, int index) { + assert(buffer != NULL, "invariant"); + index += 2; // skip atype + if ((index += 2) >= limit) { + return limit; + } + int nof_members = JfrBigEndian::read(buffer + index - 2); + while (--nof_members >= 0 && index < limit) { + index += 2; // skip member + index = skip_annotation_value(buffer, limit, index); + } + return index; +} + +// Skip an annotation value. Return >=limit if there is any problem. +static int skip_annotation_value(const address buffer, int limit, int index) { + assert(buffer != NULL, "invariant"); + // value := switch (tag:u1) { + // case B, C, I, S, Z, D, F, J, c: con:u2; + // case e: e_class:u2 e_name:u2; + // case s: s_con:u2; + // case [: do(nval:u2) {value}; + // case @: annotation; + // case s: s_con:u2; + // } + if ((index += 1) >= limit) { + return limit; + } + const u1 tag = buffer[index - 1]; + switch (tag) { + case 'B': + case 'C': + case 'I': + case 'S': + case 'Z': + case 'D': + case 'F': + case 'J': + case 'c': + case 's': + index += 2; // skip con or s_con + break; + case 'e': + index += 4; // skip e_class, e_name + break; + case '[': + { + if ((index += 2) >= limit) { + return limit; + } + int nof_values = JfrBigEndian::read(buffer + index - 2); + while (--nof_values >= 0 && index < limit) { + index = skip_annotation_value(buffer, limit, index); + } + } + break; + case '@': + index = next_annotation_index(buffer, limit, index); + break; + default: + return limit; // bad tag byte + } + return index; +} + +static const u2 number_of_elements_offset = (u2)2; +static const u2 element_name_offset = (u2)(number_of_elements_offset + 2); +static const u2 element_name_size = (u2)2; +static const u2 value_type_relative_offset = (u2)2; +static const u2 value_relative_offset = (u2)(value_type_relative_offset + 1); + +// see JVMS - 4.7.16. The RuntimeVisibleAnnotations Attribute + +class AnnotationElementIterator : public StackObj { + private: + const InstanceKlass* _ik; + const address _buffer; + const u2 _limit; // length of annotation + mutable u2 _current; // element + mutable u2 _next; // element + u2 value_index() const { + return JfrBigEndian::read(_buffer + _current + value_relative_offset); + } + + public: + AnnotationElementIterator(const InstanceKlass* ik, address buffer, u2 limit) : _ik(ik), + _buffer(buffer), + _limit(limit), + _next(element_name_offset), + _current(element_name_offset) { + assert(_buffer != NULL, "invariant"); + assert(_next == element_name_offset, "invariant"); + assert(_current == element_name_offset, "invariant"); + } + + bool has_next() const { + return _next < _limit; + } + + void move_to_next() const { + assert(has_next(), "invariant"); + _current = _next; + if (_next < _limit) { + _next = skip_annotation_value(_buffer, _limit, _next + element_name_size); + } + assert(_next <= _limit, "invariant"); + assert(_current <= _limit, "invariant"); + } + + u2 number_of_elements() const { + return JfrBigEndian::read(_buffer + number_of_elements_offset); + } + + const Symbol* name() const { + assert(_current < _next, "invariant"); + return _ik->constants()->symbol_at(JfrBigEndian::read(_buffer + _current)); + } + + char value_type() const { + return JfrBigEndian::read(_buffer + _current + value_type_relative_offset); + } + + jint read_int() const { + return _ik->constants()->int_at(value_index()); + } + + bool read_bool() const { + return read_int() != 0; + } +}; + +class AnnotationIterator : public StackObj { + private: + const InstanceKlass* _ik; + // ensure _limit field is declared before _buffer + u2 _limit; // length of annotations array + const address _buffer; + mutable u2 _current; // annotation + mutable u2 _next; // annotation + + public: + AnnotationIterator(const InstanceKlass* ik, AnnotationArray* ar) : _ik(ik), + _current(0), + _next(0), + _limit(ar != NULL ? ar->length() : 0), + _buffer(_limit > 2 ? ar->adr_at(2) : NULL) { + if (_buffer != NULL) { + _limit -= 2; // subtract sizeof(u2) number of annotations field + } + } + bool has_next() const { + return _next < _limit; + } + + void move_to_next() const { + assert(has_next(), "invariant"); + _current = _next; + if (_next < _limit) { + _next = next_annotation_index(_buffer, _limit, _next); + } + assert(_next <= _limit, "invariant"); + assert(_current <= _limit, "invariant"); + } + const AnnotationElementIterator elements() const { + assert(_current < _next, "invariant"); + return AnnotationElementIterator(_ik, _buffer + _current, _next - _current); + } + const Symbol* type() const { + assert(_buffer != NULL, "invariant"); + assert(_current < _limit, "invariant"); + return _ik->constants()->symbol_at(JfrBigEndian::read(_buffer + _current)); + } +}; + +static unsigned int unused_hash = 0; +static const char value_name[] = "value"; +static bool has_registered_annotation(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) { + assert(annotation_type != NULL, "invariant"); + AnnotationArray* class_annotations = ik->class_annotations(); + if (class_annotations == NULL) { + return false; + } + + const AnnotationIterator annotation_iterator(ik, class_annotations); + while (annotation_iterator.has_next()) { + annotation_iterator.move_to_next(); + if (annotation_iterator.type() == annotation_type) { + // target annotation found + static const Symbol* value_symbol = + SymbolTable::lookup_only(value_name, sizeof value_name - 1, unused_hash); + assert(value_symbol != NULL, "invariant"); + const AnnotationElementIterator element_iterator = annotation_iterator.elements(); + while (element_iterator.has_next()) { + element_iterator.move_to_next(); + if (value_symbol == element_iterator.name()) { + // "value" element + assert('Z' == element_iterator.value_type(), "invariant"); + value = element_iterator.read_bool(); + return true; + } + } + } + } + return false; +} + +static bool registered_annotation_value(const InstanceKlass* ik, const Symbol* const registered_symbol) { + assert(registered_symbol != NULL, "invariant"); + assert(ik != NULL, "invariant"); + assert(JdkJfrEvent::is_a(ik), "invariant"); + bool registered_value = false; + if (has_registered_annotation(ik, registered_symbol, registered_value)) { + return registered_value; + } + InstanceKlass* super = InstanceKlass::cast(ik->super()); + return registered_annotation_value(super, registered_symbol); +} + +static const char registered_constant[] = "Ljdk/jfr/Registered;"; + +// Evaluate to the value of the first found "Ljdk/jfr/Registered;" annotation. +// Searching moves upwards in the klass hierarchy in order to support +// inherited annotations in addition to the ability to override. +static bool should_register_klass(const InstanceKlass* ik) { + static const Symbol* const registered_symbol = SymbolTable::lookup_only(registered_constant, + sizeof registered_constant - 1, + unused_hash); + assert(registered_symbol != NULL, "invariant"); + return registered_annotation_value(ik, registered_symbol); +} +/* + * Map an utf8 constant back to its CONSTANT_UTF8_INFO + */ +static u2 utf8_info_index(const InstanceKlass* ik, const Symbol* const target, TRAPS) { + assert(target != NULL, "invariant"); + ConstantPool* cp = ik->constants(); + const int cp_len = cp->length(); + for (u2 index = 1; index < cp_len; ++index) { + const constantTag tag = cp->tag_at(index); + if (tag.is_utf8()) { + const Symbol* const utf8_sym = cp->symbol_at(index); + assert(utf8_sym != NULL, "invariant"); + if (utf8_sym == target) { + return index; + } + } + } + // not in constant pool + return invalid_cp_index; +} + +#ifdef ASSERT +static bool is_index_within_range(u2 index, u2 orig_cp_len, u2 new_cp_entries_len) { + return index > 0 && index < orig_cp_len + new_cp_entries_len; +} +#endif + +static u2 add_utf8_info(JfrBigEndianWriter& writer, const char* utf8_constant, u2 orig_cp_len, u2& new_cp_entries_len) { + assert(utf8_constant != NULL, "invariant"); + writer.write(JVM_CONSTANT_Utf8); + writer.write_utf8_u2_len(utf8_constant); + assert(writer.is_valid(), "invariant"); + // return index for the added utf8 info + return orig_cp_len + new_cp_entries_len++; +} + +static u2 add_method_ref_info(JfrBigEndianWriter& writer, + u2 cls_name_index, + u2 method_index, + u2 desc_index, + u2 orig_cp_len, + u2& number_of_new_constants, + TRAPS) { + assert(is_index_within_range(cls_name_index, orig_cp_len, number_of_new_constants), "invariant"); + assert(is_index_within_range(method_index, orig_cp_len, number_of_new_constants), "invariant"); + assert(is_index_within_range(desc_index, orig_cp_len, number_of_new_constants), "invariant"); + writer.write(JVM_CONSTANT_Class); + writer.write(cls_name_index); + const u2 cls_entry_index = orig_cp_len + number_of_new_constants; + ++number_of_new_constants; + writer.write(JVM_CONSTANT_NameAndType); + writer.write(method_index); + writer.write(desc_index); + const u2 nat_entry_index = orig_cp_len + number_of_new_constants; + ++number_of_new_constants; + writer.write(JVM_CONSTANT_Methodref); + writer.write(cls_entry_index); + writer.write(nat_entry_index); + // post-increment number_of_new_constants + // value returned is the index to the added method_ref + return orig_cp_len + number_of_new_constants++; +} + +static u2 add_flr_register_method_constants(JfrBigEndianWriter& writer, + const u2* utf8_indexes, + u2 orig_cp_len, + u2& number_of_new_constants, + TRAPS) { + assert(utf8_indexes != NULL, "invariant"); + return add_method_ref_info(writer, + utf8_indexes[UTF8_REQ_FlightRecorder], + utf8_indexes[UTF8_REQ_register], + utf8_indexes[UTF8_REQ_CLASS_VOID_METHOD_DESC], + orig_cp_len, + number_of_new_constants, + THREAD); +} + +/* + * field_info { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + */ +static jlong add_field_info(JfrBigEndianWriter& writer, u2 name_index, u2 desc_index, bool is_static = false) { + assert(name_index > 0, "invariant"); + assert(desc_index > 0, "invariant"); + DEBUG_ONLY(const jlong start_offset = writer.current_offset();) + writer.write(JVM_ACC_SYNTHETIC | JVM_ACC_PRIVATE | (is_static ? JVM_ACC_STATIC : JVM_ACC_TRANSIENT)); // flags + writer.write(name_index); + writer.write(desc_index); + writer.write((u2)0x0); // attributes_count + assert(writer.is_valid(), "invariant"); + DEBUG_ONLY(assert(start_offset + 8 == writer.current_offset(), "invariant");) + return writer.current_offset(); +} + +static u2 add_field_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes) { + assert(utf8_indexes != NULL, "invariant"); + add_field_info(writer, + utf8_indexes[UTF8_REQ_eventHandler], + utf8_indexes[UTF8_REQ_eventHandler_FIELD_DESC], + true); // static + + add_field_info(writer, + utf8_indexes[UTF8_REQ_startTime], + utf8_indexes[UTF8_REQ_J_FIELD_DESC]); + + add_field_info(writer, + utf8_indexes[UTF8_REQ_duration], + utf8_indexes[UTF8_REQ_J_FIELD_DESC]); + + return number_of_new_fields; +} + +/* + * method_info { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + * + * Code_attribute { + * u2 attribute_name_index; + * u4 attribute_length; + * u2 max_stack; + * u2 max_locals; + * u4 code_length; + * u1 code[code_length]; + * u2 exception_table_length; + * { u2 start_pc; + * u2 end_pc; + * u2 handler_pc; + * u2 catch_type; + * } exception_table[exception_table_length]; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + */ + +static jlong add_method_info(JfrBigEndianWriter& writer, + u2 name_index, + u2 desc_index, + u2 code_index, + const u1* const code, + const size_t code_len) { + assert(name_index > 0, "invariant"); + assert(desc_index > 0, "invariant"); + assert(code_index > 0, "invariant"); + DEBUG_ONLY(const jlong start_offset = writer.current_offset();) + writer.write(JVM_ACC_SYNTHETIC | JVM_ACC_PUBLIC); // flags + writer.write(name_index); + writer.write(desc_index); + writer.write(0x1); // attributes_count ; 1 for "Code" attribute + assert(writer.is_valid(), "invariant"); + DEBUG_ONLY(assert(start_offset + 8 == writer.current_offset(), "invariant");) + // Code attribute + writer.write(code_index); // "Code" + writer.bytes(code, code_len); + DEBUG_ONLY(assert((start_offset + 8 + 2 + (jlong)code_len) == writer.current_offset(), "invariant");) + return writer.current_offset(); +} + +/* + * On return, the passed stream will be positioned + * just after the constant pool section in the classfile + * and the cp length is returned. + * + * Stream should come in at the start position. + */ +static u2 position_stream_after_cp(ClassFileStream* stream) { + assert(stream != NULL, "invariant"); + assert(stream->current_offset() == 0, "invariant"); + stream->skip_u4_fast(2); // 8 bytes skipped + const u2 cp_len = stream->get_u2_fast(); + assert(cp_len > 0, "invariant"); + // now spin the stream position to just after the constant pool + for (u2 index = 1; index < cp_len; ++index) { + const u1 tag = stream->get_u1_fast(); // cp tag + switch (tag) { + case JVM_CONSTANT_Class: + case JVM_CONSTANT_String: { + stream->skip_u2_fast(1); // skip 2 bytes + continue; + } + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_Integer: + case JVM_CONSTANT_Float: + case JVM_CONSTANT_NameAndType: + case JVM_CONSTANT_InvokeDynamic: { + stream->skip_u4_fast(1); // skip 4 bytes + continue; + } + case JVM_CONSTANT_Long: + case JVM_CONSTANT_Double: { + stream->skip_u4_fast(2); // skip 8 bytes + // Skip entry following eigth-byte constant, see JVM book p. 98 + ++index; + continue; + } + case JVM_CONSTANT_Utf8: { + u2 utf8_length = stream->get_u2_fast(); + stream->skip_u1_fast(utf8_length); // skip 2 + len bytes + continue; + } + case JVM_CONSTANT_MethodHandle: + case JVM_CONSTANT_MethodType: { + if (tag == JVM_CONSTANT_MethodHandle) { + stream->skip_u1_fast(1); + stream->skip_u2_fast(1); // skip 3 bytes + } + else if (tag == JVM_CONSTANT_MethodType) { + stream->skip_u2_fast(1); // skip 3 bytes + } + } + continue; + default: + assert(false, "error in skip logic!"); + break; + } // end switch(tag) + } + return cp_len; +} + +/* +* On return, the passed stream will be positioned +* just after the fields section in the classfile +* and the number of fields will be returned. +* +* Stream should come in positioned just before fields_count +*/ +static u2 position_stream_after_fields(ClassFileStream* stream) { + assert(stream != NULL, "invariant"); + assert(stream->current_offset() > 0, "invariant"); + // fields len + const u2 orig_fields_len = stream->get_u2_fast(); + // fields + for (u2 i = 0; i < orig_fields_len; ++i) { + stream->skip_u2_fast(3); + const u2 attrib_info_len = stream->get_u2_fast(); + for (u2 j = 0; j < attrib_info_len; ++j) { + stream->skip_u2_fast(1); + const u4 attrib_len = stream->get_u4_fast(); + stream->skip_u1_fast(attrib_len); + } + } + return orig_fields_len; +} + +/* +* On return, the passed stream will be positioned +* just after the methods section in the classfile +* and the number of methods will be returned. +* +* Stream should come in positioned just before methods_count +*/ +static u2 position_stream_after_methods(JfrBigEndianWriter& writer, + ClassFileStream* stream, + const u2* utf8_indexes, + bool register_klass, + const Method* clinit_method, + u4& orig_method_len_offset) { + assert(stream != NULL, "invariant"); + assert(stream->current_offset() > 0, "invariant"); + assert(utf8_indexes != NULL, "invariant"); + // We will come back to this location when we + // know how many methods there will be. + writer.reserve(sizeof(u2)); + const u2 orig_methods_len = stream->get_u2_fast(); + // Move copy position past original method_count + // in order to not copy the original count + orig_method_len_offset += sizeof(u2); + for (u2 i = 0; i < orig_methods_len; ++i) { + const u4 method_offset = stream->current_offset(); + stream->skip_u2_fast(1); // Access Flags + const u2 name_index = stream->get_u2_fast(); // Name index + stream->skip_u2_fast(1); // Descriptor index + const u2 attributes_count = stream->get_u2_fast(); + for (u2 j = 0; j < attributes_count; ++j) { + stream->skip_u2_fast(1); + const u4 attrib_len = stream->get_u4_fast(); + stream->skip_u1_fast(attrib_len); + } + if (clinit_method != NULL && name_index == clinit_method->name_index()) { + // The method just parsed is an existing method. + // If the class has the @Registered(false) annotation, i.e. marking a class + // for opting out from automatic registration, then we do not need to do anything. + if (!register_klass) { + continue; + } + // Automatic registration with the jfr system is acccomplished + // by pre-pending code to the method of the class. + // We will need to re-create a new in a later step. + // For now, ensure that this method is excluded from the methods + // being copied. + writer.bytes(stream->buffer() + orig_method_len_offset, + method_offset - orig_method_len_offset); + assert(writer.is_valid(), "invariant"); + + // Update copy position to skip copy of method + orig_method_len_offset = stream->current_offset(); + } + } + return orig_methods_len; +} + +static u2 add_method_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes) { + assert(utf8_indexes != NULL, "invariant"); + add_method_info(writer, + utf8_indexes[UTF8_REQ_begin], + utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC], + utf8_indexes[UTF8_REQ_Code], + empty_void_method_code_attribute, + sizeof(empty_void_method_code_attribute)); + + assert(writer.is_valid(), "invariant"); + + add_method_info(writer, + utf8_indexes[UTF8_REQ_end], + utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC], + utf8_indexes[UTF8_REQ_Code], + empty_void_method_code_attribute, + sizeof(empty_void_method_code_attribute)); + + assert(writer.is_valid(), "invariant"); + + add_method_info(writer, + utf8_indexes[UTF8_REQ_commit], + utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC], + utf8_indexes[UTF8_REQ_Code], + empty_void_method_code_attribute, + sizeof(empty_void_method_code_attribute)); + + assert(writer.is_valid(), "invariant"); + + add_method_info(writer, + utf8_indexes[UTF8_REQ_isEnabled], + utf8_indexes[UTF8_REQ_EMPTY_BOOLEAN_METHOD_DESC], + utf8_indexes[UTF8_REQ_Code], + boolean_method_code_attribute, + sizeof(boolean_method_code_attribute)); + + assert(writer.is_valid(), "invariant"); + + add_method_info(writer, + utf8_indexes[UTF8_REQ_shouldCommit], + utf8_indexes[UTF8_REQ_EMPTY_BOOLEAN_METHOD_DESC], + utf8_indexes[UTF8_REQ_Code], + boolean_method_code_attribute, + sizeof(boolean_method_code_attribute)); + assert(writer.is_valid(), "invariant"); + return number_of_new_methods; +} + +static void adjust_exception_table(JfrBigEndianWriter& writer, u2 bci_adjustment_offset, const Method* method, TRAPS) { + const u2 ex_table_length = method != NULL ? (u2)method->exception_table_length() : 0; + writer.write(ex_table_length); // Exception table length + if (ex_table_length > 0) { + assert(method != NULL, "invariant"); + const ExceptionTableElement* const ex_elements = method->exception_table_start(); + for (int i = 0; i < ex_table_length; ++i) { + assert(ex_elements != NULL, "invariant"); + writer.write(ex_elements[i].start_pc + bci_adjustment_offset); + writer.write(ex_elements[i].end_pc + bci_adjustment_offset); + writer.write(ex_elements[i].handler_pc + bci_adjustment_offset); + writer.write(ex_elements[i].catch_type_index); // no adjustment + } + } +} + +enum StackMapFrameTypes { + SAME_FRAME_BEGIN = 0, + SAME_FRAME_END = 63, + SAME_LOCALS_1_STACK_ITEM_FRAME_BEGIN = 64, + SAME_LOCALS_1_STACK_ITEM_FRAME_END = 127, + SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247, + CHOP_FRAME_BEGIN = 248, + CHOP_FRAME_END = 250, + SAME_FRAME_EXTENDED = 251, + APPEND_FRAME_BEGIN = 252, + APPEND_FRAME_END = 254, + FULL_FRAME = 255 +}; + +static void adjust_stack_map(JfrBigEndianWriter& writer, + Array* stack_map, + const u2* utf8_indexes, + u2 bci_adjustment_offset, + TRAPS) { + assert(stack_map != NULL, "invariant"); + assert(utf8_indexes != NULL, "invariant"); + writer.write(utf8_indexes[UTF8_OPT_StackMapTable]); + const jlong stack_map_attrib_len_offset = writer.current_offset(); + writer.reserve(sizeof(u4)); + StackMapStream stream(stack_map); + const u2 stack_map_entries = stream.get_u2(THREAD); + // number of entries + writer.write(stack_map_entries); // new stack map entry added + const u1 frame_type = stream.get_u1(THREAD); + // SAME_FRAME and SAME_LOCALS_1_STACK_ITEM_FRAME encode + // their offset_delta into the actual frame type itself. + // If such a frame type is the first frame, then we transform + // it to a SAME_FRAME_EXTENDED or a SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED frame. + // This is done in order to not overflow frame types accidentally + // when adjusting the offset_delta. In changing the frame types, + // we can work with an explicit u2 offset_delta field (like the other frame types) + if (frame_type <= SAME_FRAME_END) { + writer.write(SAME_FRAME_EXTENDED); + writer.write(frame_type + bci_adjustment_offset); + } else if (frame_type >= SAME_LOCALS_1_STACK_ITEM_FRAME_BEGIN && + frame_type <= SAME_LOCALS_1_STACK_ITEM_FRAME_END) { + writer.write(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED); + writer.write((frame_type - SAME_LOCALS_1_STACK_ITEM_FRAME_BEGIN) + bci_adjustment_offset); + } else if (frame_type >= SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { + // SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED to FULL_FRAME + // has a u2 offset_delta field + writer.write(frame_type); + writer.write(stream.get_u2(THREAD) + bci_adjustment_offset); + } else { + assert(false, "stackMapFrame type is invalid"); + } + + while (!stream.at_end()) { + writer.write(stream.get_u1(THREAD)); + } + + u4 stack_map_attrib_len = writer.current_offset() - stack_map_attrib_len_offset; + // the stack_map_table_attributes_length value is exclusive + stack_map_attrib_len -= sizeof(u4); + writer.write_at_offset(stack_map_attrib_len, stack_map_attrib_len_offset); +} + +static void adjust_line_number_table(JfrBigEndianWriter& writer, + const u2* utf8_indexes, + u4 bci_adjustement_offset, + const Method* method, + TRAPS) { + assert(utf8_indexes != NULL, "invariant"); + assert(method != NULL, "invariant"); + assert(method->has_linenumber_table(), "invariant"); + writer.write(utf8_indexes[UTF8_OPT_LineNumberTable]); + const jlong lnt_attributes_length_offset = writer.current_offset(); + writer.reserve(sizeof(u4)); + const jlong lnt_attributes_entries_offset = writer.current_offset(); + writer.reserve(sizeof(u2)); + u1* lnt = method->compressed_linenumber_table(); + CompressedLineNumberReadStream lnt_stream(lnt); + u2 line_number_table_entries = 0; + while (lnt_stream.read_pair()) { + ++line_number_table_entries; + const u2 bci = (u2)lnt_stream.bci(); + writer.write(bci + (u2)bci_adjustement_offset); + writer.write((u2)lnt_stream.line()); + } + writer.write_at_offset(line_number_table_entries, lnt_attributes_entries_offset); + u4 lnt_table_attributes_len = writer.current_offset() - lnt_attributes_length_offset; + // the line_number_table_attributes_length value is exclusive + lnt_table_attributes_len -= sizeof(u4); + writer.write_at_offset(lnt_table_attributes_len, lnt_attributes_length_offset); +} + +// returns the number of lvtt entries +static u2 adjust_local_variable_table(JfrBigEndianWriter& writer, + const u2* utf8_indexes, + u2 bci_adjustment_offset, + const Method* method, + TRAPS) { + assert(utf8_indexes != NULL, "invariant"); + assert(method != NULL, "invariant"); + assert(method->has_localvariable_table(), "invariant"); + writer.write(utf8_indexes[UTF8_OPT_LocalVariableTable]); + const jlong lvt_attributes_length_offset = writer.current_offset(); + writer.reserve(sizeof(u4)); + const int lvt_len = method->localvariable_table_length(); + writer.write((u2)lvt_len); + const LocalVariableTableElement* table = method->localvariable_table_start(); + assert(table != NULL, "invariant"); + u2 num_lvtt_entries = 0; + for (int i = 0; i < lvt_len; ++i) { + writer.write(table[i].start_bci + bci_adjustment_offset); + writer.write(table[i].length); + writer.write(table[i].name_cp_index); + writer.write(table[i].descriptor_cp_index); + writer.write(table[i].slot); + if (table[i].signature_cp_index > 0) { + ++num_lvtt_entries; + } + } + u4 lvt_table_attributes_len = writer.current_offset() - lvt_attributes_length_offset; + // the lvt_table_attributes_length value is exclusive + lvt_table_attributes_len -= sizeof(u4); + writer.write_at_offset(lvt_table_attributes_len, lvt_attributes_length_offset); + return num_lvtt_entries; +} + +static void adjust_local_variable_type_table(JfrBigEndianWriter& writer, + const u2* utf8_indexes, + u2 bci_adjustment_offset, + u2 num_lvtt_entries, + const Method* method, + TRAPS) { + assert(num_lvtt_entries > 0, "invariant"); + writer.write(utf8_indexes[UTF8_OPT_LocalVariableTypeTable]); + const jlong lvtt_attributes_length_offset = writer.current_offset(); + writer.reserve(sizeof(u4)); + writer.write(num_lvtt_entries); + const LocalVariableTableElement* table = method->localvariable_table_start(); + assert(table != NULL, "invariant"); + const int lvt_len = method->localvariable_table_length(); + for (int i = 0; i < lvt_len; ++i) { + if (table[i].signature_cp_index > 0) { + writer.write(table[i].start_bci + bci_adjustment_offset); + writer.write(table[i].length); + writer.write(table[i].name_cp_index); + writer.write(table[i].signature_cp_index); + writer.write(table[i].slot); + } + } + u4 lvtt_table_attributes_len = writer.current_offset() - lvtt_attributes_length_offset; + // the lvtt_table_attributes_length value is exclusive + lvtt_table_attributes_len -= sizeof(u4); + writer.write_at_offset(lvtt_table_attributes_len, lvtt_attributes_length_offset); +} + +static void adjust_code_attributes(JfrBigEndianWriter& writer, + const u2* utf8_indexes, + u2 bci_adjustment_offset, + const Method* clinit_method, + TRAPS) { + // "Code" attributes + assert(utf8_indexes != NULL, "invariant"); + const jlong code_attributes_offset = writer.current_offset(); + writer.reserve(sizeof(u2)); + u2 number_of_code_attributes = 0; + if (clinit_method != NULL) { + Array* stack_map = clinit_method->stackmap_data(); + if (stack_map != NULL) { + ++number_of_code_attributes; + adjust_stack_map(writer, stack_map, utf8_indexes, bci_adjustment_offset, THREAD); + assert(writer.is_valid(), "invariant"); + } + if (clinit_method != NULL && clinit_method->has_linenumber_table()) { + ++number_of_code_attributes; + adjust_line_number_table(writer, utf8_indexes, bci_adjustment_offset, clinit_method, THREAD); + assert(writer.is_valid(), "invariant"); + } + if (clinit_method != NULL && clinit_method->has_localvariable_table()) { + ++number_of_code_attributes; + const u2 num_of_lvtt_entries = adjust_local_variable_table(writer, utf8_indexes, bci_adjustment_offset, clinit_method, THREAD); + assert(writer.is_valid(), "invariant"); + if (num_of_lvtt_entries > 0) { + ++number_of_code_attributes; + adjust_local_variable_type_table(writer, utf8_indexes, bci_adjustment_offset, num_of_lvtt_entries, clinit_method, THREAD); + assert(writer.is_valid(), "invariant"); + } + } + } + + // Store the number of code_attributes + writer.write_at_offset(number_of_code_attributes, code_attributes_offset); +} + +static jlong insert_clinit_method(const InstanceKlass* ik, + ClassFileParser& parser, + JfrBigEndianWriter& writer, + u2 orig_constant_pool_len, + const u2* utf8_indexes, + const u2 register_method_ref_index, + const Method* clinit_method, + TRAPS) { + assert(utf8_indexes != NULL, "invariant"); + // The injected code length is always this value. + // This is to ensure that padding can be done + // where needed and to simplify size calculations. + static const u2 injected_code_length = 8; + const u2 name_index = utf8_indexes[UTF8_REQ_clinit]; + const u2 desc_index = utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC]; + const u2 max_stack = MAX2(clinit_method != NULL ? clinit_method->verifier_max_stack() : 1, 1); + const u2 max_locals = MAX2(clinit_method != NULL ? clinit_method->max_locals() : 0, 0); + const u2 orig_bytecodes_length = clinit_method != NULL ? (u2)clinit_method->code_size() : 0; + const address orig_bytecodes = clinit_method != NULL ? clinit_method->code_base() : NULL; + const u2 new_code_length = injected_code_length + orig_bytecodes_length; + DEBUG_ONLY(const jlong start_offset = writer.current_offset();) + writer.write(JVM_ACC_STATIC); // flags + writer.write(name_index); + writer.write(desc_index); + writer.write((u2)0x1); // attributes_count // "Code" + assert(writer.is_valid(), "invariant"); + DEBUG_ONLY(assert(start_offset + 8 == writer.current_offset(), "invariant");) + // "Code" attribute + writer.write(utf8_indexes[UTF8_REQ_Code]); // "Code" + const jlong code_attribute_length_offset = writer.current_offset(); + writer.reserve(sizeof(u4)); + writer.write(max_stack); // max stack + writer.write(max_locals); // max locals + writer.write((u4)new_code_length); // code length + + /* BEGIN CLINIT CODE */ + + // Note the use of ldc_w here instead of ldc. + // This is to handle all values of "this_class_index" + writer.write((u1)Bytecodes::_ldc_w); + writer.write((u2)parser.this_class_index()); // load constant "this class" + writer.write((u1)Bytecodes::_invokestatic); + // invoke "FlightRecorder.register(Ljava/lang/Class;") + writer.write(register_method_ref_index); + if (clinit_method == NULL) { + writer.write((u1)Bytecodes::_nop); + writer.write((u1)Bytecodes::_return); + } else { + // If we are pre-pending to original code, + // do padding to minimize disruption to the original. + // It might have dependencies on 4-byte boundaries + // i.e. lookupswitch and tableswitch instructions + writer.write((u1)Bytecodes::_nop); + writer.write((u1)Bytecodes::_nop); + // insert original clinit code + writer.bytes(orig_bytecodes, orig_bytecodes_length); + } + + /* END CLINIT CODE */ + + assert(writer.is_valid(), "invariant"); + adjust_exception_table(writer, injected_code_length, clinit_method, THREAD); + assert(writer.is_valid(), "invariant"); + adjust_code_attributes(writer, utf8_indexes, injected_code_length, clinit_method, THREAD); + assert(writer.is_valid(), "invariant"); + u4 code_attribute_len = writer.current_offset() - code_attribute_length_offset; + // the code_attribute_length value is exclusive + code_attribute_len -= sizeof(u4); + writer.write_at_offset(code_attribute_len, code_attribute_length_offset); + return writer.current_offset(); +} + +// Caller needs ResourceMark +static ClassFileStream* create_new_bytes_for_event_klass(const InstanceKlass* ik, const ClassFileParser& parser, TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + static const u2 public_final_flag_mask = JVM_ACC_PUBLIC | JVM_ACC_FINAL; + ClassFileStream* const orig_stream = parser.clone_stream(); + const int orig_stream_length = orig_stream->length(); + // allocate an identically sized buffer + u1* const new_buffer = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, u1, orig_stream_length); + if (new_buffer == NULL) { + return NULL; + } + assert(new_buffer != NULL, "invariant"); + // memcpy the entire [B + memcpy(new_buffer, orig_stream->buffer(), orig_stream_length); + const u2 orig_cp_len = position_stream_after_cp(orig_stream); + assert(orig_cp_len > 0, "invariant"); + assert(orig_stream->current_offset() > 0, "invariant"); + orig_stream->skip_u2_fast(3); // access_flags, this_class_index, super_class_index + const u2 iface_len = orig_stream->get_u2_fast(); + orig_stream->skip_u2_fast(iface_len); + // fields len + const u2 orig_fields_len = orig_stream->get_u2_fast(); + // fields + for (u2 i = 0; i < orig_fields_len; ++i) { + orig_stream->skip_u2_fast(3); + const u2 attrib_info_len = orig_stream->get_u2_fast(); + for (u2 j = 0; j < attrib_info_len; ++j) { + orig_stream->skip_u2_fast(1); + const u4 attrib_len = orig_stream->get_u4_fast(); + orig_stream->skip_u1_fast(attrib_len); + } + } + // methods + const u2 orig_methods_len = orig_stream->get_u2_fast(); + for (u2 i = 0; i < orig_methods_len; ++i) { + const u4 access_flag_offset = orig_stream->current_offset(); + const u2 flags = orig_stream->get_u2_fast(); + // Rewrite JVM_ACC_FINAL -> JVM_ACC_PUBLIC + if (public_final_flag_mask == flags) { + JfrBigEndianWriter accessflagsrewriter(new_buffer + access_flag_offset, sizeof(u2)); + accessflagsrewriter.write(JVM_ACC_PUBLIC); + assert(accessflagsrewriter.is_valid(), "invariant"); + } + orig_stream->skip_u2_fast(2); + const u2 attributes_count = orig_stream->get_u2_fast(); + for (u2 j = 0; j < attributes_count; ++j) { + orig_stream->skip_u2_fast(1); + const u4 attrib_len = orig_stream->get_u4_fast(); + orig_stream->skip_u1_fast(attrib_len); + } + } + return new ClassFileStream(new_buffer, orig_stream_length, NULL); +} + +// Attempt to locate an existing UTF8_INFO mapping the utf8_constant. +// If no UTF8_INFO exists, add (append) a new one to the constant pool. +static u2 find_or_add_utf8_info(JfrBigEndianWriter& writer, + const InstanceKlass* ik, + const char* const utf8_constant, + u2 orig_cp_len, + u2& added_cp_entries, + TRAPS) { + assert(utf8_constant != NULL, "invariant"); + TempNewSymbol utf8_sym = SymbolTable::new_symbol(utf8_constant, THREAD); + // lookup existing + const int utf8_orig_idx = utf8_info_index(ik, utf8_sym, THREAD); + if (utf8_orig_idx != invalid_cp_index) { + // existing constant pool entry found + return utf8_orig_idx; + } + // no existing match, need to add a new utf8 cp entry + assert(invalid_cp_index == utf8_orig_idx, "invariant"); + // add / append new + return add_utf8_info(writer, utf8_constant, orig_cp_len, added_cp_entries); +} + +/* + * This routine will resolve the required utf8_constants array + * to their constant pool indexes (mapping to their UTF8_INFO's) + * Only if a constant is actually needed and does not already exist + * will it be added. + * + * The passed in indexes array will be populated with the resolved indexes. + * The number of newly added constant pool entries is returned. + */ +static u2 resolve_utf8_indexes(JfrBigEndianWriter& writer, + const InstanceKlass* ik, + u2* const utf8_indexes, + u2 orig_cp_len, + const Method* clinit_method, + TRAPS) { + assert(utf8_indexes != NULL, "invariant"); + u2 added_cp_entries = 0; + // resolve all required symbols + for (u2 index = 0; index < NOF_UTF8_REQ_SYMBOLS; ++index) { + utf8_indexes[index] = find_or_add_utf8_info(writer, + ik, + utf8_constants[index], + orig_cp_len, + added_cp_entries, + THREAD); + } + // Now determine optional constants (mainly "Code" attributes) + if (clinit_method != NULL && clinit_method->has_stackmap_table()) { + utf8_indexes[UTF8_OPT_StackMapTable] = + find_or_add_utf8_info(writer, + ik, + utf8_constants[UTF8_OPT_StackMapTable], + orig_cp_len, + added_cp_entries, + THREAD); + } else { + utf8_indexes[UTF8_OPT_StackMapTable] = invalid_cp_index; + } + + if (clinit_method != NULL && clinit_method->has_linenumber_table()) { + utf8_indexes[UTF8_OPT_LineNumberTable] = + find_or_add_utf8_info(writer, + ik, + utf8_constants[UTF8_OPT_LineNumberTable], + orig_cp_len, + added_cp_entries, + THREAD); + } else { + utf8_indexes[UTF8_OPT_LineNumberTable] = invalid_cp_index; + } + + if (clinit_method != NULL && clinit_method->has_localvariable_table()) { + utf8_indexes[UTF8_OPT_LocalVariableTable] = + find_or_add_utf8_info(writer, + ik, + utf8_constants[UTF8_OPT_LocalVariableTable], + orig_cp_len, + added_cp_entries, + THREAD); + + utf8_indexes[UTF8_OPT_LocalVariableTypeTable] = + find_or_add_utf8_info(writer, + ik, + utf8_constants[UTF8_OPT_LocalVariableTypeTable], + orig_cp_len, + added_cp_entries, + THREAD); + } else { + utf8_indexes[UTF8_OPT_LocalVariableTable] = invalid_cp_index; + utf8_indexes[UTF8_OPT_LocalVariableTypeTable] = invalid_cp_index; + } + + return added_cp_entries; +} + +static u1* new_bytes_for_lazy_instrumentation(InstanceKlass* ik, + ClassFileParser& parser, + jint& size_of_new_bytes, + TRAPS) { + assert(ik != NULL, "invariant"); + // If the class already has a clinit method + // we need to take that into account + const Method* clinit_method = ik->class_initializer(); + const bool register_klass = should_register_klass(ik); + ClassFileStream* const orig_stream = parser.clone_stream(); + const int orig_stream_size = orig_stream->length(); + assert(orig_stream->current_offset() == 0, "invariant"); + const u2 orig_cp_len = position_stream_after_cp(orig_stream); + assert(orig_cp_len > 0, "invariant"); + assert(orig_stream->current_offset() > 0, "invariant"); + // Dimension and allocate a working byte buffer + // to be used in building up a modified class [B. + const jint new_buffer_size = extra_stream_bytes + orig_stream_size; + u1* const new_buffer = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, u1, new_buffer_size); + if (new_buffer == NULL) { + if (true) tty->print_cr ("Thread local allocation (native) for " SIZE_FORMAT + " bytes failed in JfrClassAdapter::on_klass_creation", (size_t)new_buffer_size); + return NULL; + } + assert(new_buffer != NULL, "invariant"); + // [B wrapped in a big endian writer + JfrBigEndianWriter writer(new_buffer, new_buffer_size); + assert(writer.current_offset() == 0, "invariant"); + const u4 orig_access_flag_offset = orig_stream->current_offset(); + // Copy original stream from the beginning up to AccessFlags + // This means the original constant pool contents are copied unmodified + writer.bytes(orig_stream->buffer(), orig_access_flag_offset); + assert(writer.is_valid(), "invariant"); + assert(writer.current_offset() == (intptr_t)orig_access_flag_offset, "invariant"); // same positions + // Our writer now sits just after the last original constant pool entry. + // I.e. we are in a good position to append new constant pool entries + // This array will contain the resolved indexes + // in order to reference UTF8_INFO's needed + u2 utf8_indexes[NOF_UTF8_SYMBOLS]; + // Resolve_utf8_indexes will be conservative in attempting to + // locate an existing UTF8_INFO; it will only append constants + // that is absolutely required + u2 number_of_new_constants = resolve_utf8_indexes(writer, ik, utf8_indexes, orig_cp_len, clinit_method, THREAD); + // UTF8_INFO entries now added to the constant pool + // In order to invoke a method we would need additional + // constants, JVM_CONSTANT_Class, JVM_CONSTANT_NameAndType + // and JVM_CONSTANT_Methodref. + const u2 flr_register_method_ref_index = + register_klass ? + add_flr_register_method_constants(writer, + utf8_indexes, + orig_cp_len, + number_of_new_constants, + THREAD) : invalid_cp_index; + + // New constant pool entries added and all UTF8_INFO indexes resolved + // Now update the class file constant_pool_count with an updated count + writer.write_at_offset(orig_cp_len + number_of_new_constants, 8); + assert(writer.is_valid(), "invariant"); + orig_stream->skip_u2_fast(3); // access_flags, this_class_index, super_class_index + const u2 iface_len = orig_stream->get_u2_fast(); // interfaces + orig_stream->skip_u2_fast(iface_len); + const u4 orig_fields_len_offset = orig_stream->current_offset(); + // Copy from AccessFlags up to and including interfaces + writer.bytes(orig_stream->buffer() + orig_access_flag_offset, + orig_fields_len_offset - orig_access_flag_offset); + assert(writer.is_valid(), "invariant"); + const jlong new_fields_len_offset = writer.current_offset(); + const u2 orig_fields_len = position_stream_after_fields(orig_stream); + u4 orig_method_len_offset = orig_stream->current_offset(); + // Copy up to and including fields + writer.bytes(orig_stream->buffer() + orig_fields_len_offset, orig_method_len_offset - orig_fields_len_offset); + assert(writer.is_valid(), "invariant"); + // We are sitting just after the original number of field_infos + // so this is a position where we can add (append) new field_infos + const u2 number_of_new_fields = add_field_infos(writer, utf8_indexes); + assert(writer.is_valid(), "invariant"); + const jlong new_method_len_offset = writer.current_offset(); + // Additional field_infos added, update classfile fields_count + writer.write_at_offset(orig_fields_len + number_of_new_fields, new_fields_len_offset); + assert(writer.is_valid(), "invariant"); + // Our current location is now at classfile methods_count + const u2 orig_methods_len = position_stream_after_methods(writer, + orig_stream, + utf8_indexes, + register_klass, + clinit_method, + orig_method_len_offset); + const u4 orig_attributes_count_offset = orig_stream->current_offset(); + // Copy existing methods + writer.bytes(orig_stream->buffer() + orig_method_len_offset, orig_attributes_count_offset - orig_method_len_offset); + assert(writer.is_valid(), "invariant"); + // We are sitting just after the original number of method_infos + // so this is a position where we can add (append) new method_infos + u2 number_of_new_methods = add_method_infos(writer, utf8_indexes); + + // We have just added the new methods. + // + // What about the state of ? + // We would need to do: + // 1. Nothing (@Registered(false) annotation) + // 2. Build up a new - and if the original class already contains a , + // merging will be neccessary. + // + if (register_klass) { + insert_clinit_method(ik, parser, writer, orig_cp_len, utf8_indexes, flr_register_method_ref_index, clinit_method, THREAD); + } + number_of_new_methods += clinit_method != NULL ? 0 : register_klass ? 1 : 0; + // Update classfile methods_count + writer.write_at_offset(orig_methods_len + number_of_new_methods, new_method_len_offset); + assert(writer.is_valid(), "invariant"); + // Copy last remaining bytes + writer.bytes(orig_stream->buffer() + orig_attributes_count_offset, orig_stream_size - orig_attributes_count_offset); + assert(writer.is_valid(), "invariant"); + assert(writer.current_offset() > orig_stream->length(), "invariant"); + size_of_new_bytes = (jint)writer.current_offset(); + return new_buffer; +} + +static void log_pending_exception(oop throwable) { + assert(throwable != NULL, "invariant"); + oop msg = java_lang_Throwable::message(throwable); + if (msg != NULL) { + char* text = java_lang_String::as_utf8_string(msg); + if (text != NULL) { + if (true) tty->print_cr ("%s", text); + } + } +} + +static bool should_force_instrumentation() { + return !JfrOptionSet::allow_event_retransforms() || JfrEventClassTransformer::is_force_instrumentation(); +} + +static ClassFileStream* create_new_bytes_for_subklass(InstanceKlass* ik, ClassFileParser& parser, Thread* t) { + assert(JdkJfrEvent::is_a(ik), "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(t)); + jint size_of_new_bytes = 0; + u1* new_bytes = new_bytes_for_lazy_instrumentation(ik, parser, size_of_new_bytes, t); + if (new_bytes == NULL) { + return NULL; + } + assert(new_bytes != NULL, "invariant"); + assert(size_of_new_bytes > 0, "invariant"); + + bool force_instrumentation = should_force_instrumentation(); + if (Jfr::is_recording() || force_instrumentation) { + jint size_instrumented_data = 0; + unsigned char* instrumented_data = NULL; + const jclass super = (jclass)JNIHandles::make_local(ik->super()->java_mirror()); + JfrUpcalls::new_bytes_eager_instrumentation(TRACE_ID(ik), + force_instrumentation, + super, + size_of_new_bytes, + new_bytes, + &size_instrumented_data, + &instrumented_data, + t); + if (t->has_pending_exception()) { + log_pending_exception(t->pending_exception()); + t->clear_pending_exception(); + return NULL; + } + assert(instrumented_data != NULL, "invariant"); + assert(size_instrumented_data > 0, "invariant"); + return new ClassFileStream(instrumented_data, size_instrumented_data, NULL); + } + return new ClassFileStream(new_bytes, size_of_new_bytes, NULL); +} + +static bool cache_bytes(InstanceKlass* ik, ClassFileStream* new_stream, InstanceKlass* new_ik, TRAPS) { + assert(ik != NULL, "invariant"); + assert(new_ik != NULL, "invariant"); + assert(new_ik->name() != NULL, "invariant"); + assert(new_stream != NULL, "invariant"); + assert(!HAS_PENDING_EXCEPTION, "invariant"); + static const bool can_retransform = JfrOptionSet::allow_retransforms(); + if (!can_retransform) { + return true; + } + const jint stream_len = new_stream->length(); + JvmtiCachedClassFileData* p = + (JvmtiCachedClassFileData*)NEW_C_HEAP_ARRAY_RETURN_NULL(u1, offset_of(JvmtiCachedClassFileData, data) + stream_len, mtInternal); + if (p == NULL) { + if (true) tty->print_cr("Allocation using C_HEAP_ARRAY for " SIZE_FORMAT + " bytes failed in JfrClassAdapter::on_klass_creation", (size_t)offset_of(JvmtiCachedClassFileData, data) + stream_len); + return false; + } + p->length = stream_len; + memcpy(p->data, new_stream->buffer(), stream_len); + new_ik->set_cached_class_file(p); + JvmtiCachedClassFileData* const cached_class_data = ik->get_cached_class_file(); + if (cached_class_data != NULL) { + os::free(cached_class_data); + ik->set_cached_class_file(NULL); + } + return true; +} + +static InstanceKlass* create_new_instance_klass(InstanceKlass* ik, ClassFileStream* stream, TRAPS) { + assert(stream != NULL, "invariant"); + ResourceMark rm(THREAD); +TempNewSymbol parsed_name = NULL; + ClassLoaderData* const cld = ik->class_loader_data(); + Handle pd(THREAD, ik->protection_domain()); + Symbol* const class_name = ik->name(); + const char* const klass_name = class_name != NULL ? class_name->as_C_string() : ""; + InstanceKlass* const new_ik = ClassFileParser(stream).parseClassFile( + class_name, + cld, + pd, + NULL, // host klass + NULL, // cp_patches + parsed_name, + true, // need_verify + THREAD)(); + if (HAS_PENDING_EXCEPTION) { + log_pending_exception(PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + return NULL; + } + assert(new_ik != NULL, "invariant"); + assert(new_ik->name() != NULL, "invariant"); + assert(strncmp(ik->name()->as_C_string(), new_ik->name()->as_C_string(), strlen(ik->name()->as_C_string())) == 0, "invariant"); + return cache_bytes(ik, stream, new_ik, THREAD) ? new_ik : NULL; +} + +static void rewrite_klass_pointer(InstanceKlass*& ik, InstanceKlass* new_ik, ClassFileParser& parser, TRAPS) { + assert(ik != NULL, "invariant"); + assert(new_ik != NULL, "invariant"); + assert(new_ik->name() != NULL, "invariant"); + assert(JdkJfrEvent::is(new_ik) || JdkJfrEvent::is_subklass(new_ik), "invariant"); + assert(!HAS_PENDING_EXCEPTION, "invariant"); + // assign original InstanceKlass* back onto "its" parser object for proper destruction + parser.set_klass_to_deallocate(ik); + // now rewrite original pointer to newly created InstanceKlass + ik = new_ik; +} + +// During retransform/redefine, copy the Method specific trace flags +// from the previous ik ("the original klass") to the new ik ("the scratch_klass"). +// The open code for retransform/redefine does not know about these. +// In doing this migration here, we ensure the new Methods (defined in scratch klass) +// will carry over trace tags from the old Methods being replaced, +// ensuring flag/tag continuity while being transparent to open code. +static void copy_method_trace_flags(const InstanceKlass* the_original_klass, const InstanceKlass* the_scratch_klass) { + assert(the_original_klass != NULL, "invariant"); + assert(the_scratch_klass != NULL, "invariant"); + assert(the_original_klass->name() == the_scratch_klass->name(), "invariant"); + const Array* old_methods = the_original_klass->methods(); + const Array* new_methods = the_scratch_klass->methods(); + const bool equal_array_length = old_methods->length() == new_methods->length(); + // The Method array has the property of being sorted. + // If they are the same length, there is a one-to-one mapping. + // If they are unequal, there was a method added (currently only + // private static methods allowed to be added), use lookup. + for (int i = 0; i < old_methods->length(); ++i) { + const Method* const old_method = old_methods->at(i); + Method* const new_method = equal_array_length ? new_methods->at(i) : + the_scratch_klass->find_method(old_method->name(), old_method->signature()); + assert(new_method != NULL, "invariant"); + assert(new_method->name() == old_method->name(), "invariant"); + assert(new_method->signature() == old_method->signature(), "invariant"); + *new_method->trace_flags_addr() = old_method->trace_flags(); + assert(new_method->trace_flags() == old_method->trace_flags(), "invariant"); + } +} + +static bool is_retransforming(const InstanceKlass* ik, TRAPS) { + assert(ik != NULL, "invariant"); + assert(JdkJfrEvent::is_a(ik), "invariant"); + Symbol* const name = ik->name(); + assert(name != NULL, "invariant"); + Handle class_loader(THREAD, ik->class_loader()); + Handle protection_domain(THREAD, ik->protection_domain()); + // nota bene: use lock-free dictionary lookup + const InstanceKlass* prev_ik = (const InstanceKlass*)SystemDictionary::find(name, class_loader, protection_domain, THREAD); + if (prev_ik == NULL) { + return false; + } + // an existing ik implies a retransform/redefine + assert(prev_ik != NULL, "invariant"); + assert(JdkJfrEvent::is_a(prev_ik), "invariant"); + copy_method_trace_flags(prev_ik, ik); + return true; +} + +// target for JFR_ON_KLASS_CREATION hook +void JfrEventClassTransformer::on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS) { + assert(ik != NULL, "invariant"); + if (JdkJfrEvent::is(ik)) { + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + ClassFileStream* new_stream = create_new_bytes_for_event_klass(ik, parser, THREAD); + if (new_stream == NULL) { + if (true) tty->print_cr("JfrClassAdapter: unable to create ClassFileStream"); + return; + } + assert(new_stream != NULL, "invariant"); + InstanceKlass* new_ik = create_new_instance_klass(ik, new_stream, THREAD); + if (new_ik == NULL) { + if (true) tty->print_cr("JfrClassAdapter: unable to create InstanceKlass"); + return; + } + assert(new_ik != NULL, "invariant"); + // We now need to explicitly tag the replaced klass as the jdk.jfr.Event klass + assert(!JdkJfrEvent::is(new_ik), "invariant"); + JdkJfrEvent::tag_as(new_ik); + assert(JdkJfrEvent::is(new_ik), "invariant"); + rewrite_klass_pointer(ik, new_ik, parser, THREAD); + return; + } + assert(JdkJfrEvent::is_subklass(ik), "invariant"); + if (is_retransforming(ik, THREAD)) { + // not the initial klass load + return; + } + if (ik->is_abstract()) { + // abstract classes are not instrumented + return; + } + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + ClassFileStream* const new_stream = create_new_bytes_for_subklass(ik, parser, THREAD); + if (NULL == new_stream) { + if (true) tty->print_cr("JfrClassAdapter: unable to create ClassFileStream"); + return; + } + assert(new_stream != NULL, "invariant"); + InstanceKlass* new_ik = create_new_instance_klass(ik, new_stream, THREAD); + if (new_ik == NULL) { + if (true) tty->print_cr("JfrClassAdapter: unable to create InstanceKlass"); + return; + } + assert(new_ik != NULL, "invariant"); + // would have been tagged already as a subklass during the normal process of traceid assignment + assert(JdkJfrEvent::is_subklass(new_ik), "invariant"); + traceid id = ik->trace_id(); + ik->set_trace_id(0); + new_ik->set_trace_id(id); + rewrite_klass_pointer(ik, new_ik, parser, THREAD); +} + +static bool _force_instrumentation = false; +void JfrEventClassTransformer::set_force_instrumentation(bool force_instrumentation) { + _force_instrumentation = force_instrumentation; +} + +bool JfrEventClassTransformer::is_force_instrumentation() { + return _force_instrumentation; +} diff --git a/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.hpp b/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1b35fb183d3e01a510bba89867c7eedbd8498e92 --- /dev/null +++ b/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_INSTRUMENTATION_JFREVENTCLASSTRANSFORMER_HPP +#define SHARE_VM_JFR_INSTRUMENTATION_JFREVENTCLASSTRANSFORMER_HPP + +#include "memory/allocation.hpp" +#include "utilities/exceptions.hpp" + +class ClassFileParser; +class InstanceKlass; + +// +// Intercepts the initial class load of jdk.jfr.Event and subclasses. +// Will replace the sent in InstanceKlass* with a class file schema extended InstanceKlass*. +// +class JfrEventClassTransformer : AllStatic { + public: + static void on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS); + static void set_force_instrumentation(bool force_instrumentation); + static bool is_force_instrumentation(); +}; + +#endif // SHARE_VM_JFR_INSTRUMENTATION_JFREVENTCLASSTRANSFORMER_HPP diff --git a/src/share/vm/jfr/instrumentation/jfrJvmtiAgent.cpp b/src/share/vm/jfr/instrumentation/jfrJvmtiAgent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b303d94e7cdb43164fa8ecaefc844abca9a70df --- /dev/null +++ b/src/share/vm/jfr/instrumentation/jfrJvmtiAgent.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2016, 2019, 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 "precompiled.hpp" +#include "jvm.h" +#include "jfr/instrumentation/jfrJvmtiAgent.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/jni/jfrUpcalls.hpp" +#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/support/jfrEventClass.hpp" +#include "memory/resourceArea.hpp" +#include "prims/jvmtiEnvBase.hpp" +#include "prims/jvmtiExport.hpp" +#include "prims/jvmtiUtil.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/thread.inline.hpp" +#include "utilities/exceptions.hpp" + +static const size_t ERROR_MSG_BUFFER_SIZE = 256; +static JfrJvmtiAgent* agent = NULL; +static jvmtiEnv* jfr_jvmti_env = NULL; + +static void check_jvmti_error(jvmtiEnv* jvmti, jvmtiError errnum, const char* str) { + if (errnum != JVMTI_ERROR_NONE) { + char* errnum_str = NULL; + jvmti->GetErrorName(errnum, &errnum_str); + if (true) tty->print_cr("ERROR: JfrJvmtiAgent: " INT32_FORMAT " (%s): %s\n", + errnum, + NULL == errnum_str ? "Unknown" : errnum_str, + NULL == str ? "" : str); + } +} + +static bool set_event_notification_mode(jvmtiEventMode mode, + jvmtiEvent event, + jthread event_thread, + ...) { + assert(jfr_jvmti_env != NULL, "invariant"); + const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventNotificationMode(mode, event, event_thread); + check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventNotificationMode"); + return jvmti_ret_code == JVMTI_ERROR_NONE; +} + +static bool update_class_file_load_hook_event(jvmtiEventMode mode) { + return set_event_notification_mode(mode, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); +} + +static JavaThread* current_java_thread() { + Thread* this_thread = Thread::current(); + assert(this_thread != NULL && this_thread->is_Java_thread(), "invariant"); + return static_cast(this_thread); +} + +// jvmti event callbacks require C linkage +extern "C" void JNICALL jfr_on_class_file_load_hook(jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data) { + if (class_being_redefined == NULL) { + return; + } + JavaThread* jt = JavaThread::thread_from_jni_environment(jni_env); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));; + ThreadInVMfromNative tvmfn(jt); + JfrUpcalls::on_retransform(JfrTraceId::get(class_being_redefined), + class_being_redefined, + class_data_len, + class_data, + new_class_data_len, + new_class_data, + jt); +} + +// caller needs ResourceMark +static jclass* create_classes_array(jint classes_count, TRAPS) { + assert(classes_count > 0, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD)); + ThreadInVMfromNative tvmfn((JavaThread*)THREAD); + jclass* const classes = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jclass, classes_count); + if (NULL == classes) { + char error_buffer[ERROR_MSG_BUFFER_SIZE]; + jio_snprintf(error_buffer, ERROR_MSG_BUFFER_SIZE, + "Thread local allocation (native) of " SIZE_FORMAT " bytes failed " + "in retransform classes", sizeof(jclass) * classes_count); + if (true) tty->print_cr("%s", error_buffer); + JfrJavaSupport::throw_out_of_memory_error(error_buffer, CHECK_NULL); + } + return classes; +} + +// caller needs ResourceMark +static void log_and_throw(jvmtiError error, TRAPS) { + if (!HAS_PENDING_EXCEPTION) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD)); + ThreadInVMfromNative tvmfn((JavaThread*)THREAD); + const char base_error_msg[] = "JfrJvmtiAgent::retransformClasses failed: "; + size_t length = sizeof base_error_msg; // includes terminating null + const char* const jvmti_error_name = JvmtiUtil::error_name(error); + assert(jvmti_error_name != NULL, "invariant"); + length += strlen(jvmti_error_name); + char* error_msg = NEW_RESOURCE_ARRAY(char, length); + jio_snprintf(error_msg, length, "%s%s", base_error_msg, jvmti_error_name); + if (JVMTI_ERROR_INVALID_CLASS_FORMAT == error) { + JfrJavaSupport::throw_class_format_error(error_msg, THREAD); + } else { + JfrJavaSupport::throw_runtime_exception(error_msg, THREAD); + } + } +} + +static void check_exception_and_log(JNIEnv* env, TRAPS) { + assert(env != NULL, "invariant"); + if (env->ExceptionOccurred()) { + // array index out of bound + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD)); + ThreadInVMfromNative tvmfn((JavaThread*)THREAD); + if (true) tty->print_cr("GetObjectArrayElement threw an exception"); + return; + } +} + +static bool is_valid_jvmti_phase() { + return JvmtiEnvBase::get_phase() == JVMTI_PHASE_LIVE; +} + +void JfrJvmtiAgent::retransform_classes(JNIEnv* env, jobjectArray classes_array, TRAPS) { + assert(env != NULL, "invariant"); + assert(classes_array != NULL, "invariant"); + assert(is_valid_jvmti_phase(), "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD)); + const jint classes_count = env->GetArrayLength(classes_array); + if (classes_count <= 0) { + return; + } + ResourceMark rm(THREAD); + jclass* const classes = create_classes_array(classes_count, CHECK); + assert(classes != NULL, "invariant"); + for (jint i = 0; i < classes_count; i++) { + jclass clz = (jclass)env->GetObjectArrayElement(classes_array, i); + check_exception_and_log(env, THREAD); + classes[i] = clz; + } + { + // inspecting the oop/klass requires a thread transition + ThreadInVMfromNative transition((JavaThread*)THREAD); + for (jint i = 0; i < classes_count; ++i) { + jclass clz = classes[i]; + if (!JdkJfrEvent::is_a(clz)) { + // outside the event hierarchy + JdkJfrEvent::tag_as_host(clz); + } + } + } + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD)); + const jvmtiError result = jfr_jvmti_env->RetransformClasses(classes_count, classes); + if (result != JVMTI_ERROR_NONE) { + log_and_throw(result, THREAD); + } +} + +static bool register_callbacks(JavaThread* jt) { + assert(jfr_jvmti_env != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + jvmtiEventCallbacks callbacks; + /* Set callbacks */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.ClassFileLoadHook = jfr_on_class_file_load_hook; + const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); + check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks"); + return jvmti_ret_code == JVMTI_ERROR_NONE; +} + +static bool register_capabilities(JavaThread* jt) { + assert(jfr_jvmti_env != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + jvmtiCapabilities capabilities; + /* Add JVMTI capabilities */ + (void)memset(&capabilities, 0, sizeof(capabilities)); + capabilities.can_retransform_classes = 1; + capabilities.can_retransform_any_class = 1; + const jvmtiError jvmti_ret_code = jfr_jvmti_env->AddCapabilities(&capabilities); + check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "Add Capabilities"); + return jvmti_ret_code == JVMTI_ERROR_NONE; +} + +static jint create_jvmti_env(JavaThread* jt) { + assert(jfr_jvmti_env == NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + extern struct JavaVM_ main_vm; + JavaVM* vm = &main_vm; + return vm->GetEnv((void **)&jfr_jvmti_env, JVMTI_VERSION); +} + +static bool unregister_callbacks(JavaThread* jt) { + assert(jfr_jvmti_env != NULL, "invariant"); + jvmtiEventCallbacks callbacks; + /* Set empty callbacks */ + memset(&callbacks, 0, sizeof(callbacks)); + const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); + check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks"); + return jvmti_ret_code == JVMTI_ERROR_NONE; +} + +JfrJvmtiAgent::JfrJvmtiAgent() {} + +JfrJvmtiAgent::~JfrJvmtiAgent() { + JavaThread* jt = current_java_thread(); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); + if (jfr_jvmti_env != NULL) { + ThreadToNativeFromVM transition(jt); + update_class_file_load_hook_event(JVMTI_DISABLE); + unregister_callbacks(jt); + jfr_jvmti_env->DisposeEnvironment(); + jfr_jvmti_env = NULL; + } +} + +static bool initialize(JavaThread* jt) { + assert(jt != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); + ThreadToNativeFromVM transition(jt); + if (create_jvmti_env(jt) != JNI_OK) { + assert(jfr_jvmti_env == NULL, "invariant"); + return false; + } + assert(jfr_jvmti_env != NULL, "invariant"); + if (!register_capabilities(jt)) { + return false; + } + if (!register_callbacks(jt)) { + return false; + } + return update_class_file_load_hook_event(JVMTI_ENABLE); +} + +static void log_and_throw_illegal_state_exception(TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + const char* const illegal_state_msg = "An attempt was made to start JFR too early in the VM initialization sequence."; + if (true) { + tty->print_cr("%s\n", illegal_state_msg); + tty->print_cr("JFR uses JVMTI RetransformClasses and requires the JVMTI state to have entered JVMTI_PHASE_LIVE.\n"); + tty->print_cr("Please initialize JFR in response to event JVMTI_EVENT_VM_INIT instead of JVMTI_EVENT_VM_START.\n"); + } + JfrJavaSupport::throw_illegal_state_exception(illegal_state_msg, THREAD); +} + +bool JfrJvmtiAgent::create() { + assert(agent == NULL, "invariant"); + JavaThread* const jt = current_java_thread(); + if (!is_valid_jvmti_phase()) { + log_and_throw_illegal_state_exception(jt); + return false; + } + agent = new JfrJvmtiAgent(); + if (agent == NULL) { + return false; + } + if (!initialize(jt)) { + delete agent; + agent = NULL; + return false; + } + return true; +} + +void JfrJvmtiAgent::destroy() { + if (agent != NULL) { + delete agent; + agent = NULL; + } +} diff --git a/src/share/vm/jfr/instrumentation/jfrJvmtiAgent.hpp b/src/share/vm/jfr/instrumentation/jfrJvmtiAgent.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d4ad1fe6da74b0cc5c40b15177b54e3f40deb30d --- /dev/null +++ b/src/share/vm/jfr/instrumentation/jfrJvmtiAgent.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_INSTRUMENTATION_JFRJVMTIAGENT_HPP +#define SHARE_VM_JFR_INSTRUMENTATION_JFRJVMTIAGENT_HPP + +#include "jfr/utilities/jfrAllocation.hpp" + +class JfrJvmtiAgent : public JfrCHeapObj { + friend class JfrRecorder; + private: + JfrJvmtiAgent(); + ~JfrJvmtiAgent(); + static bool create(); + static void destroy(); + public: + static void retransform_classes(JNIEnv* env, jobjectArray classes, TRAPS); +}; + +#endif // SHARE_VM_JFR_INSTRUMENTATION_JFRJVMTIAGENT_HPP diff --git a/src/share/vm/jfr/jfr.cpp b/src/share/vm/jfr/jfr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ed5de6d950be51011cb2ac1b6d3532e0ddeee62f --- /dev/null +++ b/src/share/vm/jfr/jfr.cpp @@ -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. + * + */ + +#include "precompiled.hpp" +#include "jfr/jfr.hpp" +#include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/periodic/sampling/jfrThreadSampler.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" +#include "jfr/recorder/repository/jfrEmergencyDump.hpp" +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/support/jfrThreadLocal.hpp" +#include "runtime/java.hpp" +#include "utilities/defaultStream.hpp" + +bool Jfr::is_enabled() { + return JfrRecorder::is_enabled(); +} + +bool Jfr::is_disabled() { + return JfrRecorder::is_disabled(); +} + +bool Jfr::is_recording() { + return JfrRecorder::is_recording(); +} + +void Jfr::on_create_vm_1() { + if (!JfrRecorder::on_create_vm_1()) { + vm_exit_during_initialization("Failure when starting JFR on_create_vm_1"); + } +} + +void Jfr::on_create_vm_2() { + if (!JfrRecorder::on_create_vm_2()) { + vm_exit_during_initialization("Failure when starting JFR on_create_vm_2"); + } +} + +void Jfr::on_create_vm_3() { + if (!JfrRecorder::on_create_vm_3()) { + vm_exit_during_initialization("Failure when starting JFR on_create_vm_3"); + } +} + +void Jfr::on_unloading_classes() { + if (JfrRecorder::is_created()) { + JfrCheckpointManager::write_type_set_for_unloaded_classes(); + } +} + +void Jfr::on_thread_start(Thread* t) { + JfrThreadLocal::on_start(t); +} + +void Jfr::on_thread_exit(Thread* t) { + JfrThreadLocal::on_exit(t); +} + +void Jfr::on_java_thread_dismantle(JavaThread* jt) { + if (JfrRecorder::is_recording()) { + JfrCheckpointManager::write_thread_checkpoint(jt); + } +} + +void Jfr::on_vm_shutdown(bool exception_handler) { + JfrRecorder::set_is_shutting_down(); + if (JfrRecorder::is_recording()) { + JfrEmergencyDump::on_vm_shutdown(exception_handler); + } +} + +void Jfr::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) { + LeakProfiler::oops_do(is_alive, f); +} + +void Jfr::weak_oops_do(OopClosure* f) { + AlwaysTrueClosure always_true; + LeakProfiler::oops_do(&always_true, f); +} + +bool Jfr::on_flight_recorder_option(const JavaVMOption** option, char* delimiter) { + return JfrOptionSet::parse_flight_recorder_option(option, delimiter); +} + +bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* delimiter) { + return JfrOptionSet::parse_start_flight_recording_option(option, delimiter); +} + +Thread* Jfr::sampler_thread() { + return JfrThreadSampling::sampler_thread(); +} diff --git a/src/share/vm/jfr/jfr.hpp b/src/share/vm/jfr/jfr.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a09d7ef7c6c17d75d2838bc39a5d8534b47c4de2 --- /dev/null +++ b/src/share/vm/jfr/jfr.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_JFR_JFR_HPP +#define SHARE_VM_JFR_JFR_HPP + +#include "jni.h" +#include "memory/allocation.hpp" + +class BoolObjectClosure; +class JavaThread; +class OopClosure; +class Thread; + +extern "C" void JNICALL jfr_register_natives(JNIEnv*, jclass); + +// +// The VM interface to Flight Recorder. +// +class Jfr : AllStatic { + public: + static bool is_enabled(); + static bool is_disabled(); + static bool is_recording(); + static void on_create_vm_1(); + static void on_create_vm_2(); + static void on_create_vm_3(); + static void on_unloading_classes(); + static void on_thread_start(Thread* thread); + static void on_thread_exit(Thread* thread); + static void on_java_thread_dismantle(JavaThread* jt); + static void on_vm_shutdown(bool exception_handler = false); + static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter); + static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter); + static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f); + static void weak_oops_do(OopClosure* f); + static Thread* sampler_thread(); +}; + +#endif // SHARE_VM_JFR_JFR_HPP diff --git a/src/share/vm/trace/tracing.hpp b/src/share/vm/jfr/jfrEvents.hpp similarity index 72% rename from src/share/vm/trace/tracing.hpp rename to src/share/vm/jfr/jfrEvents.hpp index e70696eb38c53f2335be8bafc24dfaf7fe58b9e4..d8ea0079c61224f47108c4c609b148e727f3d87a 100644 --- a/src/share/vm/trace/tracing.hpp +++ b/src/share/vm/jfr/jfrEvents.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -22,10 +22,14 @@ * */ -#ifndef SHARE_VM_TRACE_TRACING_HPP -#define SHARE_VM_TRACE_TRACING_HPP - -#include "tracefiles/traceEventClasses.hpp" -#include "tracefiles/traceEventIds.hpp" +#ifndef SHARE_VM_JFR_JFREVENTS_HPP +#define SHARE_VM_JFR_JFREVENTS_HPP +/* + * Declare your event in jfr/metadata/metadata.xml. + * + * Include this header to access the machine generated event class. + */ +#include "jfrfiles/jfrEventClasses.hpp" +#include "jfrfiles/jfrEventIds.hpp" -#endif // SHARE_VM_TRACE_TRACING_HPP +#endif // SHARE_VM_JFR_JFREVENTS_HPP diff --git a/src/share/vm/jfr/jni/jfrGetAllEventClasses.cpp b/src/share/vm/jfr/jni/jfrGetAllEventClasses.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3eba29ea0eca9cc4fcd0777550e7789bb132f7e9 --- /dev/null +++ b/src/share/vm/jfr/jni/jfrGetAllEventClasses.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/symbolTable.hpp" +#include "jfr/jni/jfrGetAllEventClasses.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/support/jfrEventClass.hpp" +#include "oops/instanceKlass.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/thread.inline.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/stack.inline.hpp" + + // incremented during class unloading (safepoint) for each unloaded event class +static jlong unloaded_event_classes = 0; + +jlong JfrEventClasses::unloaded_event_classes_count() { + return unloaded_event_classes; +} + +void JfrEventClasses::increment_unloaded_event_class() { + // incremented during class unloading (safepoint) for each unloaded event class + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + ++unloaded_event_classes; +} + +static jobject empty_java_util_arraylist = NULL; + +static oop new_java_util_arraylist(TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + JavaValue result(T_OBJECT); + JfrJavaArguments args(&result, "java/util/ArrayList", "", "()V", CHECK_NULL); + JfrJavaSupport::new_object(&args, CHECK_NULL); + return (oop)result.get_jobject(); +} + +static bool initialize(TRAPS) { + static bool initialized = false; + if (!initialized) { + unloaded_event_classes = 0; + assert(NULL == empty_java_util_arraylist, "invariant"); + const oop array_list = new_java_util_arraylist(CHECK_false); + empty_java_util_arraylist = JfrJavaSupport::global_jni_handle(array_list, THREAD); + initialized = empty_java_util_arraylist != NULL; + } + return initialized; +} + +/* + * Abstract klasses are filtered out unconditionally. + * If a klass is not yet initialized, i.e yet to run its + * it is also filtered out so we don't accidentally + * trigger initialization. + */ +static bool is_whitelisted(const Klass* k) { + assert(k != NULL, "invariant"); + return !(k->is_abstract() || k->should_be_initialized()); +} + +static void fill_klasses(GrowableArray& event_subklasses, const Klass* event_klass, Thread* thread) { + assert(event_subklasses.length() == 0, "invariant"); + assert(event_klass != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread)); + + Stack mark_stack; + MutexLocker ml(Compile_lock, thread); + mark_stack.push(event_klass->subklass()); + + while (!mark_stack.is_empty()) { + const Klass* const current = mark_stack.pop(); + assert(current != NULL, "null element in stack!"); + + if (is_whitelisted(current)) { + event_subklasses.append(current); + } + + // subclass (depth) + const Klass* next_klass = current->subklass(); + if (next_klass != NULL) { + mark_stack.push(next_klass); + } + + // siblings (breadth) + next_klass = current->next_sibling(); + if (next_klass != NULL) { + mark_stack.push(next_klass); + } + } + assert(mark_stack.is_empty(), "invariant"); +} + + static void transform_klasses_to_local_jni_handles(GrowableArray& event_subklasses, Thread* thread) { + assert(event_subklasses.is_nonempty(), "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread)); + + for (int i = 0; i < event_subklasses.length(); ++i) { + const InstanceKlass* k = static_cast(event_subklasses.at(i)); + assert(is_whitelisted(k), "invariant"); + event_subklasses.at_put(i, JfrJavaSupport::local_jni_handle(k->java_mirror(), thread)); + } +} + +static const int initial_size_growable_array = 64; + +jobject JfrEventClasses::get_all_event_classes(TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + initialize(THREAD); + assert(empty_java_util_arraylist != NULL, "should have been setup already!"); + static const char jdk_jfr_event_name[] = "jdk/jfr/Event"; + unsigned int unused_hash = 0; + Symbol* const event_klass_name = SymbolTable::lookup_only(jdk_jfr_event_name, sizeof jdk_jfr_event_name - 1, unused_hash); + + if (NULL == event_klass_name) { + // not loaded yet + return empty_java_util_arraylist; + } + + const Klass* const klass = SystemDictionary::resolve_or_null(event_klass_name, THREAD); + assert(klass != NULL, "invariant"); + assert(JdkJfrEvent::is(klass), "invariant"); + + if (klass->subklass() == NULL) { + return empty_java_util_arraylist; + } + + ResourceMark rm(THREAD); + GrowableArray event_subklasses(THREAD, initial_size_growable_array); + fill_klasses(event_subklasses, klass, THREAD); + + if (event_subklasses.is_empty()) { + return empty_java_util_arraylist; + } + + transform_klasses_to_local_jni_handles(event_subklasses, THREAD); + + Handle h_array_list(THREAD, new_java_util_arraylist(THREAD)); + assert(h_array_list.not_null(), "invariant"); + + static const char add_method_name[] = "add"; + static const char add_method_signature[] = "(Ljava/lang/Object;)Z"; + const Klass* const array_list_klass = JfrJavaSupport::klass(empty_java_util_arraylist); + assert(array_list_klass != NULL, "invariant"); + + const Symbol* const add_method_sym = SymbolTable::lookup(add_method_name, sizeof add_method_name - 1, THREAD); + assert(add_method_sym != NULL, "invariant"); + + const Symbol* const add_method_sig_sym = SymbolTable::lookup(add_method_signature, sizeof add_method_signature - 1, THREAD); + assert(add_method_signature != NULL, "invariant"); + + JavaValue result(T_BOOLEAN); + for (int i = 0; i < event_subklasses.length(); ++i) { + const jclass clazz = (const jclass)event_subklasses.at(i); + assert(JdkJfrEvent::is_subklass(clazz), "invariant"); + JfrJavaArguments args(&result, array_list_klass, add_method_sym, add_method_sig_sym); + args.set_receiver(h_array_list()); + args.push_jobject(clazz); + JfrJavaSupport::call_virtual(&args, THREAD); + if (HAS_PENDING_EXCEPTION || JNI_FALSE == result.get_jboolean()) { + return empty_java_util_arraylist; + } + } + return JfrJavaSupport::local_jni_handle(h_array_list(), THREAD); +} diff --git a/src/share/vm/jfr/jni/jfrGetAllEventClasses.hpp b/src/share/vm/jfr/jni/jfrGetAllEventClasses.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a94a0b0eb822ee793fab1134e0fe700a51f3ef1c --- /dev/null +++ b/src/share/vm/jfr/jni/jfrGetAllEventClasses.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_JNI_JFRGETALLEVENTCLASSES_HPP +#define SHARE_VM_JFR_JNI_JFRGETALLEVENTCLASSES_HPP + +#include "jni.h" +#include "memory/allocation.hpp" +#include "utilities/exceptions.hpp" + +// +// Responsible for the delivery of currently loaded jdk.jfr.Event subklasses to Java. +// +class JfrEventClasses : AllStatic { + public: + static void increment_unloaded_event_class(); + static jlong unloaded_event_classes_count(); + static jobject get_all_event_classes(TRAPS); +}; + +#endif // SHARE_VM_JFR_JNI_JFRGETALLEVENTCLASSES_HPP diff --git a/src/share/vm/jfr/jni/jfrJavaCall.cpp b/src/share/vm/jfr/jni/jfrJavaCall.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec6f2c1a20f5a01975a3021d815769978702830d --- /dev/null +++ b/src/share/vm/jfr/jni/jfrJavaCall.cpp @@ -0,0 +1,380 @@ +/* + * 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 "precompiled.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "jfr/jni/jfrJavaCall.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "utilities/globalDefinitions.hpp" + +#ifdef ASSERT +static bool is_large_value(const JavaValue& value) { + return value.get_type() == T_LONG || value.get_type() == T_DOUBLE; +} +#endif // ASSERT + +static Symbol* resolve(const char* str, TRAPS) { + assert(str != NULL, "invariant"); + return SymbolTable::lookup(str, (int)strlen(str), THREAD); +} + +static Klass* resolve(Symbol* k_sym, TRAPS) { + assert(k_sym != NULL, "invariant"); + return SystemDictionary::resolve_or_fail(k_sym, true, THREAD); +} + +JfrJavaArguments::Parameters::Parameters() : _storage_index(0), _java_stack_slots(0) { + JavaValue value(T_VOID); + push(value); +} + +void JfrJavaArguments::Parameters::push(const JavaValue& value) { + assert(_storage != NULL, "invariant"); + assert(!is_large_value(value), "invariant"); + assert(_storage_index < SIZE, "invariant"); + _storage[_storage_index++] = value; + _java_stack_slots++; +} + +void JfrJavaArguments::Parameters::push_large(const JavaValue& value) { + assert(_storage != NULL, "invariant"); + assert(is_large_value(value), "invariant"); + assert(_storage_index < SIZE, "invariant"); + _storage[_storage_index++] = value; + _java_stack_slots += 2; +} + +void JfrJavaArguments::Parameters::set_receiver(const oop receiver) { + assert(_storage != NULL, "invariant"); + assert(receiver != NULL, "invariant"); + JavaValue value(T_OBJECT); + value.set_jobject((jobject)receiver); + _storage[0] = value; +} + +void JfrJavaArguments::Parameters::set_receiver(Handle receiver) { + set_receiver(receiver()); +} + +oop JfrJavaArguments::Parameters::receiver() const { + assert(has_receiver(), "invariant"); + assert(_storage[0].get_type() == T_OBJECT, "invariant"); + return (oop)_storage[0].get_jobject(); +} + +bool JfrJavaArguments::Parameters::has_receiver() const { + assert(_storage != NULL, "invariant"); + assert(_storage_index >= 1, "invariant"); + assert(_java_stack_slots >= 1, "invariant"); + return _storage[0].get_type() == T_OBJECT; +} + +void JfrJavaArguments::Parameters::push_oop(const oop obj) { + JavaValue value(T_OBJECT); + value.set_jobject((jobject)obj); + push(value); +} + +void JfrJavaArguments::Parameters::push_oop(Handle h_obj) { + push_oop(h_obj()); +} + +void JfrJavaArguments::Parameters::push_jobject(jobject h) { + JavaValue value(T_ADDRESS); + value.set_jobject(h); + push(value); +} + +void JfrJavaArguments::Parameters::push_jint(jint i) { + JavaValue value(T_INT); + value.set_jint(i); + push(value); +} + +void JfrJavaArguments::Parameters::push_jfloat(jfloat f) { + JavaValue value(T_FLOAT); + value.set_jfloat(f); + push(value); +} + +void JfrJavaArguments::Parameters::push_jdouble(jdouble d) { + JavaValue value(T_DOUBLE); + value.set_jdouble(d); + push_large(value); +} + +void JfrJavaArguments::Parameters::push_jlong(jlong l) { + JavaValue value(T_LONG); + value.set_jlong(l); + push_large(value); +} + +// including receiver (even if there is none) +inline int JfrJavaArguments::Parameters::length() const { + assert(_storage_index >= 1, "invariant"); + return _storage_index; +} + +inline int JfrJavaArguments::Parameters::java_stack_slots() const { + return _java_stack_slots; +} + +const JavaValue& JfrJavaArguments::Parameters::values(int idx) const { + assert(idx >= 0, "invariant"); + assert(idx < SIZE, "invariant"); + return _storage[idx]; +} + +void JfrJavaArguments::Parameters::copy(JavaCallArguments& args, TRAPS) const { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + if (has_receiver()) { + args.set_receiver(Handle(THREAD, receiver())); + } + for (int i = 1; i < length(); ++i) { + switch(values(i).get_type()) { + case T_BOOLEAN: + case T_CHAR: + case T_SHORT: + case T_INT: + args.push_int(values(i).get_jint()); + break; + case T_LONG: + args.push_long(values(i).get_jlong()); + break; + case T_FLOAT: + args.push_float(values(i).get_jfloat()); + break; + case T_DOUBLE: + args.push_double(values(i).get_jdouble()); + break; + case T_OBJECT: + args.push_oop(Handle(THREAD, (oop)values(i).get_jobject())); + break; + case T_ADDRESS: + args.push_oop(Handle(THREAD, JNIHandles::resolve(values(i).get_jobject()))); + break; + default: + ShouldNotReachHere(); + } + } +} + +JfrJavaArguments::JfrJavaArguments(JavaValue* result) : _result(result), _klass(NULL), _name(NULL), _signature(NULL), _array_length(-1) { + assert(result != NULL, "invariant"); +} + +JfrJavaArguments::JfrJavaArguments(JavaValue* result, const char* klass_name, const char* name, const char* signature, TRAPS) : + _result(result), + _klass(NULL), + _name(NULL), + _signature(NULL), + _array_length(-1) { + assert(result != NULL, "invariant"); + if (klass_name != NULL) { + set_klass(klass_name, CHECK); + } + if (name != NULL) { + set_name(name, CHECK); + } + if (signature != NULL) { + set_signature(signature, THREAD); + } +} + +JfrJavaArguments::JfrJavaArguments(JavaValue* result, const Klass* klass, const Symbol* name, const Symbol* signature) : _result(result), + _klass(NULL), + _name(NULL), + _signature(NULL), + _array_length(-1) { + assert(result != NULL, "invariant"); + if (klass != NULL) { + set_klass(klass); + } + if (name != NULL) { + set_name(name); + } + if (signature != NULL) { + set_signature(signature); + } +} + +Klass* JfrJavaArguments::klass() const { + assert(_klass != NULL, "invariant"); + return const_cast(_klass); +} + +void JfrJavaArguments::set_klass(const char* klass_name, TRAPS) { + assert(klass_name != NULL, "invariant"); + Symbol* const k_sym = resolve(klass_name, CHECK); + assert(k_sym != NULL, "invariant"); + const Klass* const klass = resolve(k_sym, CHECK); + set_klass(klass); +} + +void JfrJavaArguments::set_klass(const Klass* klass) { + assert(klass != NULL, "invariant"); + _klass = klass; +} + +Symbol* JfrJavaArguments::name() const { + assert(_name != NULL, "invariant"); + return const_cast(_name); +} + +void JfrJavaArguments::set_name(const char* name, TRAPS) { + assert(name != NULL, "invariant"); + const Symbol* const sym = resolve(name, CHECK); + set_name(sym); +} + +void JfrJavaArguments::set_name(const Symbol* name) { + assert(name != NULL, "invariant"); + _name = name; +} + +Symbol* JfrJavaArguments::signature() const { + assert(_signature != NULL, "invariant"); + return const_cast(_signature); +} + +void JfrJavaArguments::set_signature(const char* signature, TRAPS) { + assert(signature != NULL, "invariant"); + const Symbol* const sym = resolve(signature, CHECK); + set_signature(sym); +} + +void JfrJavaArguments::set_signature(const Symbol* signature) { + assert(signature != NULL, "invariant"); + _signature = signature; +} + +int JfrJavaArguments::array_length() const { + return _array_length; +} + +void JfrJavaArguments::set_array_length(int length) { + assert(length >= 0, "invariant"); + _array_length = length; +} + +JavaValue* JfrJavaArguments::result() const { + assert(_result != NULL, "invariant"); + return const_cast(_result); +} + +int JfrJavaArguments::length() const { + return _params.length(); +} + +bool JfrJavaArguments::has_receiver() const { + return _params.has_receiver(); +} + +oop JfrJavaArguments::receiver() const { + return _params.receiver(); +} + +void JfrJavaArguments::set_receiver(const oop receiver) { + _params.set_receiver(receiver); +} + +void JfrJavaArguments::set_receiver(Handle receiver) { + _params.set_receiver(receiver); +} + +void JfrJavaArguments::push_oop(const oop obj) { + _params.push_oop(obj); +} + +void JfrJavaArguments::push_oop(Handle h_obj) { + _params.push_oop(h_obj); +} + +void JfrJavaArguments::push_jobject(jobject h) { + _params.push_jobject(h); +} + +void JfrJavaArguments::push_int(jint i) { + _params.push_jint(i); +} + +void JfrJavaArguments::push_float(jfloat f) { + _params.push_jfloat(f); +} + +void JfrJavaArguments::push_double(jdouble d) { + _params.push_jdouble(d); +} + +void JfrJavaArguments::push_long(jlong l) { + _params.push_jlong(l); +} + +const JavaValue& JfrJavaArguments::param(int idx) const { + return _params.values(idx); +} + +int JfrJavaArguments::java_call_arg_slots() const { + return _params.java_stack_slots(); +} + +void JfrJavaArguments::copy(JavaCallArguments& args, TRAPS) { + _params.copy(args, THREAD); +} + +void JfrJavaCall::call_static(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + JavaCallArguments jcas(args->java_call_arg_slots()); + args->copy(jcas, CHECK); + JavaCalls::call_static(args->result(), args->klass(), args->name(), args->signature(), &jcas, THREAD); +} + +void JfrJavaCall::call_special(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + assert(args->has_receiver(), "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + JavaCallArguments jcas(args->java_call_arg_slots()); + args->copy(jcas, CHECK); + JavaCalls::call_special(args->result(), args->klass(), args->name(), args->signature(), &jcas, THREAD); +} + +void JfrJavaCall::call_virtual(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + assert(args->has_receiver(), "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + JavaCallArguments jcas(args->java_call_arg_slots()); + args->copy(jcas, CHECK); + JavaCalls::call_virtual(args->result(), args->klass(), args->name(), args->signature(), &jcas, THREAD); +} diff --git a/src/share/vm/jfr/jni/jfrJavaCall.hpp b/src/share/vm/jfr/jni/jfrJavaCall.hpp new file mode 100644 index 0000000000000000000000000000000000000000..89b2f7b98dde34b4f243b9e62760a73ad40ee553 --- /dev/null +++ b/src/share/vm/jfr/jni/jfrJavaCall.hpp @@ -0,0 +1,134 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_JFR_JNI_JFRJAVACALL_HPP +#define SHARE_VM_JFR_JNI_JFRJAVACALL_HPP + +#include "jni.h" +#include "jfr/utilities/jfrAllocation.hpp" +#include "utilities/exceptions.hpp" + +class JavaCallArguments; +class JavaThread; +class JavaValue; +class Klass; +class Symbol; + +class JfrJavaArguments : public StackObj { + friend class JfrJavaCall; + public: + JfrJavaArguments(JavaValue* result); + JfrJavaArguments(JavaValue* result, const char* klass_name, const char* name, const char* signature, TRAPS); + JfrJavaArguments(JavaValue* result, const Klass* klass, const Symbol* name, const Symbol* signature); + + Klass* klass() const; + void set_klass(const char* klass_name, TRAPS); + void set_klass(const Klass* klass); + + Symbol* name() const; + void set_name(const char* name, TRAPS); + void set_name(const Symbol* name); + + Symbol* signature() const; + void set_signature(const char* signature, TRAPS); + void set_signature(const Symbol* signature); + + int array_length() const; + void set_array_length(int length); + + JavaValue* result() const; + + bool has_receiver() const; + void set_receiver(const oop receiver); + void set_receiver(Handle receiver); + oop receiver() const; + + // parameters + void push_oop(const oop obj); + void push_oop(Handle h_obj); + void push_jobject(jobject h); + void push_int(jint i); + void push_double(jdouble d); + void push_long(jlong l); + void push_float(jfloat f); + + int length() const; + const JavaValue& param(int idx) const; + + private: + class Parameters { + friend class JfrJavaArguments; + private: + enum { SIZE = 16}; + JavaValue _storage[SIZE]; + int _storage_index; + int _java_stack_slots; + + Parameters(); + Parameters(const Parameters&); // no impl + Parameters& operator=(const Parameters&); // no impl + + void push(const JavaValue& value); + void push_large(const JavaValue& value); + + void push_oop(const oop obj); + void push_oop(Handle h_obj); + void push_jobject(jobject h); + void push_jint(jint i); + void push_jdouble(jdouble d); + void push_jlong(jlong l); + void push_jfloat(jfloat f); + + bool has_receiver() const; + void set_receiver(const oop receiver); + void set_receiver(Handle receiver); + oop receiver() const; + + int length() const; + int java_stack_slots() const; + + void copy(JavaCallArguments& args, TRAPS) const; + const JavaValue& values(int idx) const; + }; + + Parameters _params; + const JavaValue* const _result; + const Klass* _klass; + const Symbol* _name; + const Symbol* _signature; + int _array_length; + + int java_call_arg_slots() const; + void copy(JavaCallArguments& args, TRAPS); +}; + +class JfrJavaCall : public AllStatic { + friend class JfrJavaSupport; + private: + static void call_static(JfrJavaArguments* args, TRAPS); + static void call_special(JfrJavaArguments* args, TRAPS); + static void call_virtual(JfrJavaArguments* args, TRAPS); +}; + +#endif // SHARE_VM_JFR_JNI_JFRJAVACALL_HPP diff --git a/src/share/vm/jfr/jni/jfrJavaSupport.cpp b/src/share/vm/jfr/jni/jfrJavaSupport.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3232b56ff5e05cd64929bc2898e3e193c552b50 --- /dev/null +++ b/src/share/vm/jfr/jni/jfrJavaSupport.cpp @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "jni.h" +#include "classfile/javaClasses.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/vmSymbols.hpp" +#include "jfr/jni/jfrJavaCall.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/support/jfrThreadId.hpp" +#include "memory/resourceArea.hpp" +#include "oops/instanceOop.hpp" +#include "oops/oop.inline.hpp" +#include "oops/objArrayKlass.hpp" +#include "oops/objArrayOop.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/fieldDescriptor.hpp" +#include "runtime/java.hpp" +#include "runtime/jniHandles.hpp" +#include "runtime/synchronizer.hpp" +#include "runtime/thread.inline.hpp" +//#include "runtime/threadSMR.hpp" + +#ifdef ASSERT +void JfrJavaSupport::check_java_thread_in_vm(Thread* t) { + assert(t != NULL, "invariant"); + assert(t->is_Java_thread(), "invariant"); + assert(((JavaThread*)t)->thread_state() == _thread_in_vm, "invariant"); +} + +void JfrJavaSupport::check_java_thread_in_native(Thread* t) { + assert(t != NULL, "invariant"); + assert(t->is_Java_thread(), "invariant"); + assert(((JavaThread*)t)->thread_state() == _thread_in_native, "invariant"); +} +#endif + +/* + * Handles and references + */ +jobject JfrJavaSupport::local_jni_handle(const oop obj, Thread* t) { + DEBUG_ONLY(check_java_thread_in_vm(t)); + return t->active_handles()->allocate_handle(obj); +} + +jobject JfrJavaSupport::local_jni_handle(const jobject handle, Thread* t) { + DEBUG_ONLY(check_java_thread_in_vm(t)); + const oop obj = JNIHandles::resolve(handle); + return obj == NULL ? NULL : local_jni_handle(obj, t); +} + +void JfrJavaSupport::destroy_local_jni_handle(jobject handle) { + JNIHandles::destroy_local(handle); +} + +jobject JfrJavaSupport::global_jni_handle(const oop obj, Thread* t) { + DEBUG_ONLY(check_java_thread_in_vm(t)); + HandleMark hm(t); + return JNIHandles::make_global(Handle(t, obj)); +} + +jobject JfrJavaSupport::global_jni_handle(const jobject handle, Thread* t) { + const oop obj = JNIHandles::resolve(handle); + return obj == NULL ? NULL : global_jni_handle(obj, t); +} + +void JfrJavaSupport::destroy_global_jni_handle(const jobject handle) { + JNIHandles::destroy_global(handle); +} + +oop JfrJavaSupport::resolve_non_null(jobject obj) { + return JNIHandles::resolve_non_null(obj); +} + +/* + * Method invocation + */ +void JfrJavaSupport::call_static(JfrJavaArguments* args, TRAPS) { + JfrJavaCall::call_static(args, THREAD); +} + +void JfrJavaSupport::call_special(JfrJavaArguments* args, TRAPS) { + JfrJavaCall::call_special(args, THREAD); +} + +void JfrJavaSupport::call_virtual(JfrJavaArguments* args, TRAPS) { + JfrJavaCall::call_virtual(args, THREAD); +} + +void JfrJavaSupport::notify_all(jobject object, TRAPS) { + assert(object != NULL, "invariant"); + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + HandleMark hm(THREAD); + Handle h_obj(THREAD, resolve_non_null(object)); + assert(h_obj.not_null(), "invariant"); + ObjectSynchronizer::jni_enter(h_obj, THREAD); + ObjectSynchronizer::notifyall(h_obj, THREAD); + ObjectSynchronizer::jni_exit(h_obj(), THREAD); + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); +} + +/* + * Object construction + */ +static void object_construction(JfrJavaArguments* args, JavaValue* result, InstanceKlass* klass, TRAPS) { + assert(args != NULL, "invariant"); + assert(result != NULL, "invariant"); + assert(klass != NULL, "invariant"); + assert(klass->is_initialized(), "invariant"); + + HandleMark hm(THREAD); + instanceOop obj = klass->allocate_instance(CHECK); + instanceHandle h_obj(THREAD, obj); + assert(h_obj.not_null(), "invariant"); + args->set_receiver(h_obj); + result->set_type(T_VOID); // constructor result type + JfrJavaSupport::call_special(args, CHECK); + result->set_type(T_OBJECT); // set back to original result type + result->set_jobject((jobject)h_obj()); +} + +static void array_construction(JfrJavaArguments* args, JavaValue* result, InstanceKlass* klass, int array_length, TRAPS) { + assert(args != NULL, "invariant"); + assert(result != NULL, "invariant"); + assert(klass != NULL, "invariant"); + assert(klass->is_initialized(), "invariant"); + + Klass* const ak = klass->array_klass(THREAD); + ObjArrayKlass::cast(ak)->initialize(THREAD); + HandleMark hm(THREAD); + objArrayOop arr = ObjArrayKlass::cast(ak)->allocate(array_length, CHECK); + result->set_jobject((jobject)arr); +} + +static void create_object(JfrJavaArguments* args, JavaValue* result, TRAPS) { + assert(args != NULL, "invariant"); + assert(result != NULL, "invariant"); + assert(result->get_type() == T_OBJECT, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + + InstanceKlass* const klass = static_cast(args->klass()); + klass->initialize(CHECK); + + const int array_length = args->array_length(); + + if (array_length >= 0) { + array_construction(args, result, klass, array_length, CHECK); + } else { + object_construction(args, result, klass, THREAD); + } +} + +static void handle_result(JavaValue* result, bool global_ref, Thread* t) { + assert(result != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(t)); + const oop result_oop = (const oop)result->get_jobject(); + if (result_oop == NULL) { + return; + } + result->set_jobject(global_ref ? + JfrJavaSupport::global_jni_handle(result_oop, t) : + JfrJavaSupport::local_jni_handle(result_oop, t)); +} + +void JfrJavaSupport::new_object(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + create_object(args, args->result(), THREAD); +} + +void JfrJavaSupport::new_object_local_ref(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + JavaValue* const result = args->result(); + assert(result != NULL, "invariant"); + create_object(args, result, CHECK); + handle_result(result, false, THREAD); +} + +void JfrJavaSupport::new_object_global_ref(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + JavaValue* const result = args->result(); + assert(result != NULL, "invariant"); + create_object(args, result, CHECK); + handle_result(result, true, THREAD); +} + +jstring JfrJavaSupport::new_string(const char* c_str, TRAPS) { + assert(c_str != NULL, "invariant"); + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + const oop result = java_lang_String::create_oop_from_str(c_str, THREAD); + return (jstring)local_jni_handle(result, THREAD); +} + +jobjectArray JfrJavaSupport::new_string_array(int length, TRAPS) { + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + JavaValue result(T_OBJECT); + JfrJavaArguments args(&result, "java/lang/String", "", "()V", CHECK_NULL); + args.set_array_length(length); + new_object_local_ref(&args, THREAD); + return (jobjectArray)args.result()->get_jobject(); +} + +jobject JfrJavaSupport::new_java_lang_Boolean(bool value, TRAPS) { + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + JavaValue result(T_OBJECT); + JfrJavaArguments args(&result, "java/lang/Boolean", "", "(Z)V", CHECK_NULL); + args.push_int(value ? (jint)JNI_TRUE : (jint)JNI_FALSE); + new_object_local_ref(&args, THREAD); + return args.result()->get_jobject(); +} + +jobject JfrJavaSupport::new_java_lang_Integer(jint value, TRAPS) { + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + JavaValue result(T_OBJECT); + JfrJavaArguments args(&result, "java/lang/Integer", "", "(I)V", CHECK_NULL); + args.push_int(value); + new_object_local_ref(&args, THREAD); + return args.result()->get_jobject(); +} + +jobject JfrJavaSupport::new_java_lang_Long(jlong value, TRAPS) { + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + JavaValue result(T_OBJECT); + JfrJavaArguments args(&result, "java/lang/Long", "", "(J)V", CHECK_NULL); + args.push_long(value); + new_object_local_ref(&args, THREAD); + return args.result()->get_jobject(); +} + +void JfrJavaSupport::set_array_element(jobjectArray arr, jobject element, int index, Thread* t) { + assert(arr != NULL, "invariant"); + DEBUG_ONLY(check_java_thread_in_vm(t)); + HandleMark hm(t); + objArrayHandle a(t, (objArrayOop)resolve_non_null(arr)); + a->obj_at_put(index, resolve_non_null(element)); +} + +/* + * Field access + */ +static void write_int_field(const Handle& h_oop, fieldDescriptor* fd, jint value) { + assert(h_oop.not_null(), "invariant"); + assert(fd != NULL, "invariant"); + h_oop->int_field_put(fd->offset(), value); +} + +static void write_float_field(const Handle& h_oop, fieldDescriptor* fd, jfloat value) { + assert(h_oop.not_null(), "invariant"); + assert(fd != NULL, "invariant"); + h_oop->float_field_put(fd->offset(), value); +} + +static void write_double_field(const Handle& h_oop, fieldDescriptor* fd, jdouble value) { + assert(h_oop.not_null(), "invariant"); + assert(fd != NULL, "invariant"); + h_oop->double_field_put(fd->offset(), value); +} + +static void write_long_field(const Handle& h_oop, fieldDescriptor* fd, jlong value) { + assert(h_oop.not_null(), "invariant"); + assert(fd != NULL, "invariant"); + h_oop->long_field_put(fd->offset(), value); +} + +static void write_oop_field(const Handle& h_oop, fieldDescriptor* fd, const oop value) { + assert(h_oop.not_null(), "invariant"); + assert(fd != NULL, "invariant"); + h_oop->obj_field_put(fd->offset(), value); +} + +static void write_specialized_field(JfrJavaArguments* args, const Handle& h_oop, fieldDescriptor* fd, bool static_field) { + assert(args != NULL, "invariant"); + assert(h_oop.not_null(), "invariant"); + assert(fd != NULL, "invariant"); + assert(fd->offset() > 0, "invariant"); + assert(args->length() >= 1, "invariant"); + + // attempt must set a real value + assert(args->param(1).get_type() != T_VOID, "invariant"); + + switch(fd->field_type()) { + case T_BOOLEAN: + case T_CHAR: + case T_SHORT: + case T_INT: + write_int_field(h_oop, fd, args->param(1).get_jint()); + break; + case T_FLOAT: + write_float_field(h_oop, fd, args->param(1).get_jfloat()); + break; + case T_DOUBLE: + write_double_field(h_oop, fd, args->param(1).get_jdouble()); + break; + case T_LONG: + write_long_field(h_oop, fd, args->param(1).get_jlong()); + break; + case T_OBJECT: + write_oop_field(h_oop, fd, (oop)args->param(1).get_jobject()); + break; + case T_ADDRESS: + write_oop_field(h_oop, fd, JfrJavaSupport::resolve_non_null(args->param(1).get_jobject())); + break; + default: + ShouldNotReachHere(); + } +} + +static void read_specialized_field(JavaValue* result, const Handle& h_oop, fieldDescriptor* fd) { + assert(result != NULL, "invariant"); + assert(h_oop.not_null(), "invariant"); + assert(fd != NULL, "invariant"); + assert(fd->offset() > 0, "invariant"); + + switch(fd->field_type()) { + case T_BOOLEAN: + case T_CHAR: + case T_SHORT: + case T_INT: + result->set_jint(h_oop->int_field(fd->offset())); + break; + case T_FLOAT: + result->set_jfloat(h_oop->float_field(fd->offset())); + break; + case T_DOUBLE: + result->set_jdouble(h_oop->double_field(fd->offset())); + break; + case T_LONG: + result->set_jlong(h_oop->long_field(fd->offset())); + break; + case T_OBJECT: + result->set_jobject((jobject)h_oop->obj_field(fd->offset())); + break; + default: + ShouldNotReachHere(); + } +} + +static bool find_field(InstanceKlass* ik, + Symbol* name_symbol, + Symbol* signature_symbol, + fieldDescriptor* fd, + bool is_static = false, + bool allow_super = false) { + if (allow_super || is_static) { + return ik->find_field(name_symbol, signature_symbol, is_static, fd) != NULL; + } + return ik->find_local_field(name_symbol, signature_symbol, fd); +} + +static void lookup_field(JfrJavaArguments* args, InstanceKlass* klass, fieldDescriptor* fd, bool static_field) { + assert(args != NULL, "invariant"); + assert(klass != NULL, "invariant"); + assert(klass->is_initialized(), "invariant"); + assert(fd != NULL, "invariant"); + find_field(klass, args->name(), args->signature(), fd, static_field, true); +} + +static void read_field(JfrJavaArguments* args, JavaValue* result, TRAPS) { + assert(args != NULL, "invariant"); + assert(result != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + + InstanceKlass* const klass = static_cast(args->klass()); + klass->initialize(CHECK); + const bool static_field = !args->has_receiver(); + fieldDescriptor fd; + lookup_field(args, klass, &fd, static_field); + assert(fd.offset() > 0, "invariant"); + + HandleMark hm(THREAD); + Handle h_oop(static_field ? Handle(THREAD, klass->java_mirror()) : Handle(THREAD, args->receiver())); + read_specialized_field(result, h_oop, &fd); +} + +static void write_field(JfrJavaArguments* args, JavaValue* result, TRAPS) { + assert(args != NULL, "invariant"); + assert(result != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + + InstanceKlass* const klass = static_cast(args->klass()); + klass->initialize(CHECK); + + const bool static_field = !args->has_receiver(); + fieldDescriptor fd; + lookup_field(args, klass, &fd, static_field); + assert(fd.offset() > 0, "invariant"); + + HandleMark hm(THREAD); + Handle h_oop(static_field ? Handle(THREAD, klass->java_mirror()) : Handle(THREAD, args->receiver())); + write_specialized_field(args, h_oop, &fd, static_field); +} + +void JfrJavaSupport::set_field(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + write_field(args, args->result(), THREAD); +} + +void JfrJavaSupport::get_field(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + read_field(args, args->result(), THREAD); +} + +void JfrJavaSupport::get_field_local_ref(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + + JavaValue* const result = args->result(); + assert(result != NULL, "invariant"); + assert(result->get_type() == T_OBJECT, "invariant"); + + read_field(args, result, CHECK); + const oop obj = (const oop)result->get_jobject(); + + if (obj != NULL) { + result->set_jobject(local_jni_handle(obj, THREAD)); + } +} + +void JfrJavaSupport::get_field_global_ref(JfrJavaArguments* args, TRAPS) { + assert(args != NULL, "invariant"); + DEBUG_ONLY(check_java_thread_in_vm(THREAD)); + + JavaValue* const result = args->result(); + assert(result != NULL, "invariant"); + assert(result->get_type() == T_OBJECT, "invariant"); + read_field(args, result, CHECK); + const oop obj = (const oop)result->get_jobject(); + if (obj != NULL) { + result->set_jobject(global_jni_handle(obj, THREAD)); + } +} + +/* + * Misc + */ +Klass* JfrJavaSupport::klass(const jobject handle) { + const oop obj = resolve_non_null(handle); + assert(obj != NULL, "invariant"); + return obj->klass(); +} + +// caller needs ResourceMark +const char* JfrJavaSupport::c_str(jstring string, Thread* t) { + DEBUG_ONLY(check_java_thread_in_vm(t)); + if (string == NULL) { + return NULL; + } + const char* temp = NULL; + const oop java_string = resolve_non_null(string); + if (java_lang_String::value(java_string) != NULL) { + const size_t length = java_lang_String::utf8_length(java_string); + temp = NEW_RESOURCE_ARRAY_IN_THREAD(t, const char, (length + 1)); + if (temp == NULL) { + JfrJavaSupport::throw_out_of_memory_error("Unable to allocate thread local native memory", t); + return NULL; + } + assert(temp != NULL, "invariant"); + java_lang_String::as_utf8_string(java_string, const_cast(temp), (int) length + 1); + } + return temp; +} + +/* + * Exceptions and errors + */ +static void create_and_throw(Symbol* name, const char* message, TRAPS) { + assert(name != NULL, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + assert(!HAS_PENDING_EXCEPTION, "invariant"); + THROW_MSG(name, message); +} + +void JfrJavaSupport::throw_illegal_state_exception(const char* message, TRAPS) { + create_and_throw(vmSymbols::java_lang_IllegalStateException(), message, THREAD); +} + +void JfrJavaSupport::throw_internal_error(const char* message, TRAPS) { + create_and_throw(vmSymbols::java_lang_InternalError(), message, THREAD); +} + +void JfrJavaSupport::throw_illegal_argument_exception(const char* message, TRAPS) { + create_and_throw(vmSymbols::java_lang_IllegalArgumentException(), message, THREAD); +} + +void JfrJavaSupport::throw_out_of_memory_error(const char* message, TRAPS) { + create_and_throw(vmSymbols::java_lang_OutOfMemoryError(), message, THREAD); +} + +void JfrJavaSupport::throw_class_format_error(const char* message, TRAPS) { + create_and_throw(vmSymbols::java_lang_ClassFormatError(), message, THREAD); +} + +void JfrJavaSupport::throw_runtime_exception(const char* message, TRAPS) { + create_and_throw(vmSymbols::java_lang_RuntimeException(), message, THREAD); +} + +void JfrJavaSupport::abort(jstring errorMsg, Thread* t) { + DEBUG_ONLY(check_java_thread_in_vm(t)); + + ResourceMark rm(t); + const char* const error_msg = c_str(errorMsg, t); + if (error_msg != NULL) { + if (true) tty->print_cr("%s",error_msg); + } + if (true) tty->print_cr("%s", "An irrecoverable error in Jfr. Shutting down VM..."); + vm_abort(); +} + +JfrJavaSupport::CAUSE JfrJavaSupport::_cause = JfrJavaSupport::VM_ERROR; +void JfrJavaSupport::set_cause(jthrowable throwable, Thread* t) { + DEBUG_ONLY(check_java_thread_in_vm(t)); + + HandleMark hm(t); + Handle ex(t, JNIHandles::resolve_external_guard(throwable)); + + if (ex.is_null()) { + return; + } + + if (ex->is_a(SystemDictionary::OutOfMemoryError_klass())) { + _cause = OUT_OF_MEMORY; + return; + } + if (ex->is_a(SystemDictionary::StackOverflowError_klass())) { + _cause = STACK_OVERFLOW; + return; + } + if (ex->is_a(SystemDictionary::Error_klass())) { + _cause = VM_ERROR; + return; + } + if (ex->is_a(SystemDictionary::RuntimeException_klass())) { + _cause = RUNTIME_EXCEPTION; + return; + } + if (ex->is_a(SystemDictionary::Exception_klass())) { + _cause = UNKNOWN; + return; + } +} + +void JfrJavaSupport::uncaught_exception(jthrowable throwable, Thread* t) { + DEBUG_ONLY(check_java_thread_in_vm(t)); + assert(throwable != NULL, "invariant"); + set_cause(throwable, t); +} + +JfrJavaSupport::CAUSE JfrJavaSupport::cause() { + return _cause; +} + +// XXX +//const char* const JDK_JFR_MODULE_NAME = "jdk.jfr"; +//const char* const JDK_JFR_PACKAGE_NAME = "jdk/jfr"; + +jlong JfrJavaSupport::jfr_thread_id(jobject target_thread) { +// ThreadsListHandle tlh; + // XXX is it correct and safe? + JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(target_thread)); +// (void)tlh.cv_internal_thread_to_JavaThread(target_thread, &native_thread, NULL); + return native_thread != NULL ? JFR_THREAD_ID(native_thread) : 0; +} diff --git a/src/share/vm/jfr/jni/jfrJavaSupport.hpp b/src/share/vm/jfr/jni/jfrJavaSupport.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d08c4b96e1b1a219c3e939fd2f40b333cebf1603 --- /dev/null +++ b/src/share/vm/jfr/jni/jfrJavaSupport.hpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_JNI_JFRJAVASUPPORT_HPP +#define SHARE_VM_JFR_JNI_JFRJAVASUPPORT_HPP + +#include "jfr/jni/jfrJavaCall.hpp" +#include "utilities/exceptions.hpp" + +class Klass; +class JavaThread; +class outputStream; + +class JfrJavaSupport : public AllStatic { + public: + static jobject local_jni_handle(const oop obj, Thread* t); + static jobject local_jni_handle(const jobject handle, Thread* t); + static void destroy_local_jni_handle(const jobject handle); + + static jobject global_jni_handle(const oop obj, Thread* t); + static jobject global_jni_handle(const jobject handle, Thread* t); + static void destroy_global_jni_handle(const jobject handle); + + static oop resolve_non_null(jobject obj); + static void notify_all(jobject obj, TRAPS); + static void set_array_element(jobjectArray arr, jobject element, int index, Thread* t); + + // naked oop result + static void call_static(JfrJavaArguments* args, TRAPS); + static void call_special(JfrJavaArguments* args, TRAPS); + static void call_virtual(JfrJavaArguments* args, TRAPS); + + static void set_field(JfrJavaArguments* args, TRAPS); + static void get_field(JfrJavaArguments* args, TRAPS); + static void new_object(JfrJavaArguments* args, TRAPS); + + // global jni handle result + static void new_object_global_ref(JfrJavaArguments* args, TRAPS); + static void get_field_global_ref(JfrJavaArguments* args, TRAPS); + + // local jni handle result + static void new_object_local_ref(JfrJavaArguments* args, TRAPS); + static void get_field_local_ref(JfrJavaArguments* args, TRAPS); + + static jstring new_string(const char* c_str, TRAPS); + static jobjectArray new_string_array(int length, TRAPS); + + static jobject new_java_lang_Boolean(bool value, TRAPS); + static jobject new_java_lang_Integer(jint value, TRAPS); + static jobject new_java_lang_Long(jlong value, TRAPS); + + // misc + static Klass* klass(const jobject handle); + // caller needs ResourceMark + static const char* c_str(jstring string, Thread* jt); + + // exceptions + static void throw_illegal_state_exception(const char* message, TRAPS); + static void throw_illegal_argument_exception(const char* message, TRAPS); + static void throw_internal_error(const char* message, TRAPS); + static void throw_out_of_memory_error(const char* message, TRAPS); + static void throw_class_format_error(const char* message, TRAPS); + static void throw_runtime_exception(const char* message, TRAPS); + + static jlong jfr_thread_id(jobject target_thread); + + // critical + static void abort(jstring errorMsg, TRAPS); + static void uncaught_exception(jthrowable throwable, Thread* t); + + // asserts + DEBUG_ONLY(static void check_java_thread_in_vm(Thread* t);) + DEBUG_ONLY(static void check_java_thread_in_native(Thread* t);) + + enum CAUSE { + VM_ERROR, + OUT_OF_MEMORY, + STACK_OVERFLOW, + RUNTIME_EXCEPTION, + UNKNOWN, + NOF_CAUSES + }; + + static CAUSE cause(); + + private: + static CAUSE _cause; + static void set_cause(jthrowable throwable, Thread* t); +}; + +#endif // SHARE_VM_JFR_JNI_JFRJAVASUPPORT_HPP diff --git a/src/share/vm/jfr/jni/jfrJniMethod.cpp b/src/share/vm/jfr/jni/jfrJniMethod.cpp new file mode 100644 index 0000000000000000000000000000000000000000..688c77cb71d9d2175c2f7daa00e0f405e64fa271 --- /dev/null +++ b/src/share/vm/jfr/jni/jfrJniMethod.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2014, 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 "precompiled.hpp" +#include "jni.h" +#include "jvm.h" +#include "jfr/jfr.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/periodic/sampling/jfrThreadSampler.hpp" +#include "jfr/recorder/jfrEventSetting.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/recorder/checkpoint/jfrMetadataEvent.hpp" +#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" +#include "jfr/recorder/repository/jfrRepository.hpp" +#include "jfr/recorder/repository/jfrChunkRotation.hpp" +#include "jfr/recorder/repository/jfrChunkWriter.hpp" +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" +#include "jfr/recorder/stringpool/jfrStringPool.hpp" +#include "jfr/jni/jfrGetAllEventClasses.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/jni/jfrJniMethodRegistration.hpp" +#include "jfr/instrumentation/jfrEventClassTransformer.hpp" +#include "jfr/instrumentation/jfrJvmtiAgent.hpp" +#include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/utilities/jfrJavaLog.hpp" +#include "jfr/utilities/jfrTimeConverter.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "jfr/writers/jfrJavaEventWriter.hpp" +#include "jfrfiles/jfrPeriodic.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.hpp" +#include "utilities/debug.hpp" + +#define NO_TRANSITION(result_type, header) extern "C" { result_type JNICALL header { +#define NO_TRANSITION_END } } + +/* + * NO_TRANSITION entries + * + * Thread remains _thread_in_native + */ + +NO_TRANSITION(void, jfr_register_natives(JNIEnv* env, jclass jvmclass)) + JfrJniMethodRegistration register_native_methods(env); +NO_TRANSITION_END + +NO_TRANSITION(jboolean, jfr_is_enabled()) + return Jfr::is_enabled() ? JNI_TRUE : JNI_FALSE; +NO_TRANSITION_END + +NO_TRANSITION(jboolean, jfr_is_disabled()) + return Jfr::is_disabled() ? JNI_TRUE : JNI_FALSE; +NO_TRANSITION_END + +NO_TRANSITION(jboolean, jfr_is_started()) + return JfrRecorder::is_created() ? JNI_TRUE : JNI_FALSE; +NO_TRANSITION_END + +NO_TRANSITION(jstring, jfr_get_pid(JNIEnv* env, jobject jvm)) + char pid_buf[32] = { 0 }; + jio_snprintf(pid_buf, sizeof(pid_buf), "%d", os::current_process_id()); + jstring pid_string = env->NewStringUTF(pid_buf); + return pid_string; // exception pending if NULL +NO_TRANSITION_END + +NO_TRANSITION(jlong, jfr_elapsed_frequency(JNIEnv* env, jobject jvm)) + return JfrTime::frequency(); +NO_TRANSITION_END + +NO_TRANSITION(jlong, jfr_elapsed_counter(JNIEnv* env, jobject jvm)) + return JfrTicks::now(); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_retransform_classes(JNIEnv* env, jobject jvm, jobjectArray classes)) + JfrJvmtiAgent::retransform_classes(env, classes, JavaThread::thread_from_jni_environment(env)); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_enabled(JNIEnv* env, jobject jvm, jlong event_type_id, jboolean enabled)) + JfrEventSetting::set_enabled(event_type_id, JNI_TRUE == enabled); + if (EventOldObjectSample::eventId == event_type_id) { + ThreadInVMfromNative transition(JavaThread::thread_from_jni_environment(env)); + if (JNI_TRUE == enabled) { + LeakProfiler::start(JfrOptionSet::old_object_queue_size()); + } else { + LeakProfiler::stop(); + } + } +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_file_notification(JNIEnv* env, jobject jvm, jlong threshold)) + JfrChunkRotation::set_threshold((intptr_t)threshold); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_sample_threads(JNIEnv* env, jobject jvm, jboolean sampleThreads)) + JfrOptionSet::set_sample_threads(sampleThreads); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_stack_depth(JNIEnv* env, jobject jvm, jint depth)) + JfrOptionSet::set_stackdepth((jlong)depth); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_stacktrace_enabled(JNIEnv* env, jobject jvm, jlong event_type_id, jboolean enabled)) + JfrEventSetting::set_stacktrace(event_type_id, JNI_TRUE == enabled); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_global_buffer_count(JNIEnv* env, jobject jvm, jlong count)) + JfrOptionSet::set_num_global_buffers(count); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_global_buffer_size(JNIEnv* env, jobject jvm, jlong size)) +JfrOptionSet::set_global_buffer_size(size); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_thread_buffer_size(JNIEnv* env, jobject jvm, jlong size)) + JfrOptionSet::set_thread_buffer_size(size); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_memory_size(JNIEnv* env, jobject jvm, jlong size)) + JfrOptionSet::set_memory_size(size); +NO_TRANSITION_END + +NO_TRANSITION(jboolean, jfr_set_threshold(JNIEnv* env, jobject jvm, jlong event_type_id, jlong thresholdTicks)) + return JfrEventSetting::set_threshold(event_type_id, thresholdTicks) ? JNI_TRUE : JNI_FALSE; +NO_TRANSITION_END + +NO_TRANSITION(jboolean, jfr_allow_event_retransforms(JNIEnv* env, jobject jvm)) + return JfrOptionSet::allow_event_retransforms() ? JNI_TRUE : JNI_FALSE; +NO_TRANSITION_END + +NO_TRANSITION(jboolean, jfr_is_available(JNIEnv* env, jclass jvm)) + return !Jfr::is_disabled() ? JNI_TRUE : JNI_FALSE; +NO_TRANSITION_END + +NO_TRANSITION(jlong, jfr_get_epoch_address(JNIEnv* env, jobject jvm)) + return JfrTraceIdEpoch::epoch_address(); +NO_TRANSITION_END + +NO_TRANSITION(jlong, jfr_get_unloaded_event_classes_count(JNIEnv* env, jobject jvm)) + return JfrEventClasses::unloaded_event_classes_count(); +NO_TRANSITION_END + +NO_TRANSITION(jdouble, jfr_time_conv_factor(JNIEnv* env, jobject jvm)) + return (jdouble)JfrTimeConverter::nano_to_counter_multiplier(); +NO_TRANSITION_END + +NO_TRANSITION(jboolean, jfr_set_cutoff(JNIEnv* env, jobject jvm, jlong event_type_id, jlong cutoff_ticks)) + return JfrEventSetting::set_cutoff(event_type_id, cutoff_ticks) ? JNI_TRUE : JNI_FALSE; +NO_TRANSITION_END + +NO_TRANSITION(jboolean, jfr_should_rotate_disk(JNIEnv* env, jobject jvm)) + return JfrChunkRotation::should_rotate() ? JNI_TRUE : JNI_FALSE; +NO_TRANSITION_END + +/* + * JVM_ENTRY_NO_ENV entries + * + * Transitions: + * Entry: _thread_in_native -> _thread_in_vm + * Exit: _thread_in_vm -> _thread_in_native + * + * Current JavaThread available as "thread" variable + */ + +JVM_ENTRY_NO_ENV(jboolean, jfr_create_jfr(JNIEnv* env, jobject jvm, jboolean simulate_failure)) + if (JfrRecorder::is_created()) { + return JNI_TRUE; + } + if (!JfrRecorder::create(simulate_failure == JNI_TRUE)) { + if (!thread->has_pending_exception()) { + JfrJavaSupport::throw_illegal_state_exception("Unable to start Jfr", thread); + } + return JNI_FALSE; + } + return JNI_TRUE; +JVM_END + +JVM_ENTRY_NO_ENV(jboolean, jfr_destroy_jfr(JNIEnv* env, jobject jvm)) + JfrRecorder::destroy(); + return JNI_TRUE; +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_begin_recording(JNIEnv* env, jobject jvm)) + if (JfrRecorder::is_recording()) { + return; + } + JfrRecorder::start_recording(); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_end_recording(JNIEnv* env, jobject jvm)) + if (!JfrRecorder::is_recording()) { + return; + } + JfrRecorder::stop_recording(); +JVM_END + + +JVM_ENTRY_NO_ENV(jboolean, jfr_emit_event(JNIEnv* env, jobject jvm, jlong eventTypeId, jlong timeStamp, jlong when)) + JfrPeriodicEventSet::requestEvent((JfrEventId)eventTypeId); + return thread->has_pending_exception() ? JNI_FALSE : JNI_TRUE; +JVM_END + +JVM_ENTRY_NO_ENV(jobject, jfr_get_all_event_classes(JNIEnv* env, jobject jvm)) + return JfrEventClasses::get_all_event_classes(thread); +JVM_END + +JVM_ENTRY_NO_ENV(jlong, jfr_class_id(JNIEnv* env, jclass jvm, jclass jc)) + return JfrTraceId::use(jc); +JVM_END + +JVM_ENTRY_NO_ENV(jlong, jfr_stacktrace_id(JNIEnv* env, jobject jvm, jint skip)) + return JfrStackTraceRepository::record(thread, skip); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_log(JNIEnv* env, jobject jvm, jint tag_set, jint level, jstring message)) + JfrJavaLog::log(tag_set, level, message, thread); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_subscribe_log_level(JNIEnv* env, jobject jvm, jobject log_tag, jint id)) + JfrJavaLog::subscribe_log_level(log_tag, id, thread); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_set_output(JNIEnv* env, jobject jvm, jstring path)) + JfrRepository::set_chunk_path(path, thread); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_set_method_sampling_interval(JNIEnv* env, jobject jvm, jlong type, jlong intervalMillis)) + if (intervalMillis < 0) { + intervalMillis = 0; + } + JfrEventId typed_event_id = (JfrEventId)type; + assert(EventExecutionSample::eventId == typed_event_id || EventNativeMethodSample::eventId == typed_event_id, "invariant"); + if (intervalMillis > 0) { + JfrEventSetting::set_enabled(typed_event_id, true); // ensure sampling event is enabled + } + if (EventExecutionSample::eventId == type) { + JfrThreadSampling::set_java_sample_interval(intervalMillis); + } else { + JfrThreadSampling::set_native_sample_interval(intervalMillis); + } +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_store_metadata_descriptor(JNIEnv* env, jobject jvm, jbyteArray descriptor)) + JfrMetadataEvent::update(descriptor); +JVM_END + +// trace thread id for a thread object +JVM_ENTRY_NO_ENV(jlong, jfr_id_for_thread(JNIEnv* env, jobject jvm, jobject t)) + return JfrJavaSupport::jfr_thread_id(t); +JVM_END + +JVM_ENTRY_NO_ENV(jobject, jfr_get_event_writer(JNIEnv* env, jclass cls)) + return JfrJavaEventWriter::event_writer(thread); +JVM_END + +JVM_ENTRY_NO_ENV(jobject, jfr_new_event_writer(JNIEnv* env, jclass cls)) + return JfrJavaEventWriter::new_event_writer(thread); +JVM_END + +JVM_ENTRY_NO_ENV(jboolean, jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size)) + return JfrJavaEventWriter::flush(writer, used_size, requested_size, thread); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location)) + return JfrRepository::set_path(location, thread); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_uncaught_exception(JNIEnv* env, jobject jvm, jobject t, jthrowable throwable)) + JfrJavaSupport::uncaught_exception(throwable, thread); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg)) + JfrJavaSupport::abort(errorMsg, thread); +JVM_END + +JVM_ENTRY_NO_ENV(jlong, jfr_type_id(JNIEnv* env, jobject jvm, jclass jc)) + return JfrTraceId::get(jc); +JVM_END + +JVM_ENTRY_NO_ENV(jboolean, jfr_add_string_constant(JNIEnv* env, jclass jvm, jboolean epoch, jlong id, jstring string)) + return JfrStringPool::add(epoch == JNI_TRUE, id, string, thread) ? JNI_TRUE : JNI_FALSE; +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_set_force_instrumentation(JNIEnv* env, jobject jvm, jboolean force_instrumentation)) + JfrEventClassTransformer::set_force_instrumentation(force_instrumentation == JNI_TRUE); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean emit_all)) + LeakProfiler::emit_events(cutoff_ticks, emit_all == JNI_TRUE); +JVM_END diff --git a/src/share/vm/jfr/jni/jfrJniMethod.hpp b/src/share/vm/jfr/jni/jfrJniMethod.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7b5394a2c582d909793057bf2634de27d471ab81 --- /dev/null +++ b/src/share/vm/jfr/jni/jfrJniMethod.hpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_JNI_JFRJNIMETHOD_HPP +#define SHARE_VM_JFR_JNI_JFRJNIMETHOD_HPP + +#include "jni.h" + +/* + * Native methods for jdk.jfr.internal.JVM + */ + +#ifdef __cplusplus +extern "C" { +#endif + +jboolean JNICALL jfr_is_enabled(); + +jboolean JNICALL jfr_is_disabled(); + +jboolean JNICALL jfr_is_started(); + +jlong JNICALL jfr_elapsed_counter(JNIEnv* env, jobject jvm); + +jboolean JNICALL jfr_create_jfr(JNIEnv* env, jobject jvm, jboolean simulate_failure); + +jboolean JNICALL jfr_destroy_jfr(JNIEnv* env, jobject jvm); + +void JNICALL jfr_begin_recording(JNIEnv* env, jobject jvm); + +void JNICALL jfr_end_recording(JNIEnv* env, jobject jvm); + +jboolean JNICALL jfr_emit_event(JNIEnv* env, jobject jvm, jlong eventTypeId, jlong timeStamp, jlong when); + +jobject JNICALL jfr_get_all_event_classes(JNIEnv* env, jobject jvm); + +jlong JNICALL jfr_class_id(JNIEnv* env, jclass jvm, jclass jc); + +jstring JNICALL jfr_get_pid(JNIEnv* env, jobject jvm); + +jlong JNICALL jfr_stacktrace_id(JNIEnv* env, jobject jvm, jint skip); + +jlong JNICALL jfr_elapsed_frequency(JNIEnv* env, jobject jvm); + +void JNICALL jfr_subscribe_log_level(JNIEnv* env, jobject jvm, jobject log_tag, jint id); + +void JNICALL jfr_log(JNIEnv* env, jobject jvm, jint tag_set, jint level, jstring message); + +void JNICALL jfr_retransform_classes(JNIEnv* env, jobject jvm, jobjectArray classes); + +void JNICALL jfr_set_enabled(JNIEnv* env, jobject jvm, jlong event_type_id, jboolean enabled); + +void JNICALL jfr_set_file_notification(JNIEnv* env, jobject jvm, jlong delta); + +void JNICALL jfr_set_global_buffer_count(JNIEnv* env, jobject jvm, jlong count); + +void JNICALL jfr_set_global_buffer_size(JNIEnv* env, jobject jvm, jlong size); + +void JNICALL jfr_set_method_sampling_interval(JNIEnv* env, jobject jvm, jlong type, jlong intervalMillis); + +void JNICALL jfr_set_output(JNIEnv* env, jobject jvm, jstring path); + +void JNICALL jfr_set_sample_threads(JNIEnv* env, jobject jvm, jboolean sampleThreads); + +void JNICALL jfr_set_stack_depth(JNIEnv* env, jobject jvm, jint depth); + +void JNICALL jfr_set_stacktrace_enabled(JNIEnv* env, jobject jvm, jlong event_type_id, jboolean enabled); + +void JNICALL jfr_set_thread_buffer_size(JNIEnv* env, jobject jvm, jlong size); + +void JNICALL jfr_set_memory_size(JNIEnv* env, jobject jvm, jlong size); + +jboolean JNICALL jfr_set_threshold(JNIEnv* env, jobject jvm, jlong event_type_id, jlong thresholdTicks); + +void JNICALL jfr_store_metadata_descriptor(JNIEnv* env, jobject jvm, jbyteArray descriptor); + +jlong JNICALL jfr_id_for_thread(JNIEnv* env, jobject jvm, jobject t); + +jboolean JNICALL jfr_allow_event_retransforms(JNIEnv* env, jobject jvm); + +jboolean JNICALL jfr_is_available(JNIEnv* env, jclass jvm); + +jdouble JNICALL jfr_time_conv_factor(JNIEnv* env, jobject jvm); + +jlong JNICALL jfr_type_id(JNIEnv* env, jobject jvm, jclass jc); + +void JNICALL jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location); + +jobject JNICALL jfr_get_event_writer(JNIEnv* env, jclass cls); + +jobject JNICALL jfr_new_event_writer(JNIEnv* env, jclass cls); + +jboolean JNICALL jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size); + +void JNICALL jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg); + +jlong JNICALL jfr_get_epoch_address(JNIEnv* env, jobject jvm); + +jboolean JNICALL jfr_add_string_constant(JNIEnv* env, jclass jvm, jboolean epoch, jlong id, jstring string); + +void JNICALL jfr_uncaught_exception(JNIEnv* env, jobject jvm, jobject thread, jthrowable throwable); + +void JNICALL jfr_set_force_instrumentation(JNIEnv* env, jobject jvm, jboolean force); + +jlong JNICALL jfr_get_unloaded_event_classes_count(JNIEnv* env, jobject jvm); + +jboolean JNICALL jfr_set_cutoff(JNIEnv* env, jobject jvm, jlong event_type_id, jlong cutoff_ticks); + +void JNICALL jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean); + +jboolean JNICALL jfr_should_rotate_disk(JNIEnv* env, jobject jvm); + + +#ifdef __cplusplus +} +#endif + +#endif // SHARE_VM_JFR_JNI_JFRJNIMETHOD_HPP diff --git a/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp b/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eff29fe0c6b58a667a2b2d46cb48d1d65ad764ca --- /dev/null +++ b/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "jfr/jni/jfrJniMethod.hpp" +#include "jfr/jni/jfrJniMethodRegistration.hpp" +#include "jfr/utilities/jfrJavaLog.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/thread.hpp" +#include "utilities/exceptions.hpp" + +JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { + assert(env != NULL, "invariant"); + jclass jfr_clz = env->FindClass("jdk/jfr/internal/JVM"); + if (jfr_clz != NULL) { + JNINativeMethod method[] = { + (char*)"beginRecording", (char*)"()V", (void*)jfr_begin_recording, + (char*)"endRecording", (char*)"()V", (void*)jfr_end_recording, + (char*)"counterTime", (char*)"()J", (void*)jfr_elapsed_counter, + (char*)"createJFR", (char*)"(Z)Z", (void*)jfr_create_jfr, + (char*)"destroyJFR", (char*)"()Z", (void*)jfr_destroy_jfr, + (char*)"emitEvent", (char*)"(JJJ)Z", (void*)jfr_emit_event, + (char*)"getAllEventClasses", (char*)"()Ljava/util/List;", (void*)jfr_get_all_event_classes, + (char*)"getClassIdNonIntrinsic", (char*)"(Ljava/lang/Class;)J", (void*)jfr_class_id, + (char*)"getPid", (char*)"()Ljava/lang/String;", (void*)jfr_get_pid, + (char*)"getStackTraceId", (char*)"(I)J", (void*)jfr_stacktrace_id, + (char*)"getThreadId", (char*)"(Ljava/lang/Thread;)J", (void*)jfr_id_for_thread, + (char*)"getTicksFrequency", (char*)"()J", (void*)jfr_elapsed_frequency, + (char*)"subscribeLogLevel", (char*)"(Ljdk/jfr/internal/LogTag;I)V", (void*)jfr_subscribe_log_level, + (char*)"log", (char*)"(IILjava/lang/String;)V", (void*)jfr_log, + (char*)"retransformClasses", (char*)"([Ljava/lang/Class;)V", (void*)jfr_retransform_classes, + (char*)"setEnabled", (char*)"(JZ)V", (void*)jfr_set_enabled, + (char*)"setFileNotification", (char*)"(J)V", (void*)jfr_set_file_notification, + (char*)"setGlobalBufferCount", (char*)"(J)V", (void*)jfr_set_global_buffer_count, + (char*)"setGlobalBufferSize", (char*)"(J)V", (void*)jfr_set_global_buffer_size, + (char*)"setMethodSamplingInterval", (char*)"(JJ)V", (void*)jfr_set_method_sampling_interval, + (char*)"setOutput", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_output, + (char*)"setSampleThreads", (char*)"(Z)V", (void*)jfr_set_sample_threads, + (char*)"setStackDepth", (char*)"(I)V", (void*)jfr_set_stack_depth, + (char*)"setStackTraceEnabled", (char*)"(JZ)V", (void*)jfr_set_stacktrace_enabled, + (char*)"setThreadBufferSize", (char*)"(J)V", (void*)jfr_set_thread_buffer_size, + (char*)"setMemorySize", (char*)"(J)V", (void*)jfr_set_memory_size, + (char*)"setThreshold", (char*)"(JJ)Z", (void*)jfr_set_threshold, + (char*)"storeMetadataDescriptor", (char*)"([B)V", (void*)jfr_store_metadata_descriptor, + (char*)"getAllowedToDoEventRetransforms", (char*)"()Z", (void*)jfr_allow_event_retransforms, + (char*)"isAvailable", (char*)"()Z", (void*)jfr_is_available, + (char*)"getTimeConversionFactor", (char*)"()D", (void*)jfr_time_conv_factor, + (char*)"getTypeId", (char*)"(Ljava/lang/Class;)J", (void*)jfr_type_id, + (char*)"getEventWriter", (char*)"()Ljava/lang/Object;", (void*)jfr_get_event_writer, + (char*)"newEventWriter", (char*)"()Ljdk/jfr/internal/EventWriter;", (void*)jfr_new_event_writer, + (char*)"flush", (char*)"(Ljdk/jfr/internal/EventWriter;II)Z", (void*)jfr_event_writer_flush, + (char*)"setRepositoryLocation", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_repository_location, + (char*)"abort", (char*)"(Ljava/lang/String;)V", (void*)jfr_abort, + (char*)"getEpochAddress", (char*)"()J",(void*)jfr_get_epoch_address, + (char*)"addStringConstant", (char*)"(ZJLjava/lang/String;)Z", (void*)jfr_add_string_constant, + (char*)"uncaughtException", (char*)"(Ljava/lang/Thread;Ljava/lang/Throwable;)V", (void*)jfr_uncaught_exception, + (char*)"setForceInstrumentation", (char*)"(Z)V", (void*)jfr_set_force_instrumentation, + (char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count, + (char*)"setCutoff", (char*)"(JJ)Z", (void*)jfr_set_cutoff, + (char*)"emitOldObjectSamples", (char*)"(JZ)V", (void*)jfr_emit_old_object_samples, + (char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk + }; + + const size_t method_array_length = sizeof(method) / sizeof(JNINativeMethod); + if (env->RegisterNatives(jfr_clz, method, (jint)method_array_length) != JNI_OK) { + JavaThread* jt = JavaThread::thread_from_jni_environment(env); + assert(jt != NULL, "invariant"); + assert(jt->thread_state() == _thread_in_native, "invariant"); + ThreadInVMfromNative transition(jt); + if (true) tty->print_cr("RegisterNatives for JVM class failed!"); + } + env->DeleteLocalRef(jfr_clz); + } +} diff --git a/src/share/vm/runtime/vmStructs_trace.hpp b/src/share/vm/jfr/jni/jfrJniMethodRegistration.hpp similarity index 68% rename from src/share/vm/runtime/vmStructs_trace.hpp rename to src/share/vm/jfr/jni/jfrJniMethodRegistration.hpp index f4a9f4d89acc9118bf5cd3c3898d4a96f379b68e..f0a25f69ccff5864b427a3123dfcac6a35e4057d 100644 --- a/src/share/vm/runtime/vmStructs_trace.hpp +++ b/src/share/vm/jfr/jni/jfrJniMethodRegistration.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -22,14 +22,18 @@ * */ -#ifndef SHARE_VM_RUNTIME_VMSTRUCTS_TRACE_HPP -#define SHARE_VM_RUNTIME_VMSTRUCTS_TRACE_HPP +#ifndef SHARE_VM_JFR_JNI_JFRJNIMETHODREGISTRATION_HPP +#define SHARE_VM_JFR_JNI_JFRJNIMETHODREGISTRATION_HPP -#define VM_INT_CONSTANTS_TRACE(a) +#include "jni.h" +#include "memory/allocation.hpp" -#define VM_STRUCTS_TRACE(a, b) +// +// RegisterNatives for jdk.jfr.internal.JVM +// +class JfrJniMethodRegistration : public StackObj { + public: + JfrJniMethodRegistration(JNIEnv* env); +}; -#define VM_TYPES_TRACE(a, b) - - -#endif // SHARE_VM_RUNTIME_VMSTRUCTS_TRACE_HPP +#endif // SHARE_VM_JFR_JNI_JFRJNIMETHODREGISTRATION_HPP diff --git a/src/share/vm/jfr/jni/jfrUpcalls.cpp b/src/share/vm/jfr/jni/jfrUpcalls.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa4bfd619a78d423a984eb493591210c2f55319f --- /dev/null +++ b/src/share/vm/jfr/jni/jfrUpcalls.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/jni/jfrUpcalls.hpp" +#include "jfr/support/jfrEventClass.hpp" +#include "memory/oopFactory.hpp" +#include "oops/oop.inline.hpp" +#include "oops/typeArrayKlass.hpp" +#include "oops/typeArrayOop.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.inline.hpp" +#include "utilities/exceptions.hpp" + +static Symbol* jvm_upcalls_class_sym = NULL; +static Symbol* on_retransform_method_sym = NULL; +static Symbol* on_retransform_signature_sym = NULL; +static Symbol* bytes_for_eager_instrumentation_sym = NULL; +static Symbol* bytes_for_eager_instrumentation_sig_sym = NULL; + +static bool initialize(TRAPS) { + static bool initialized = false; + if (!initialized) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + jvm_upcalls_class_sym = SymbolTable::new_permanent_symbol("jdk/jfr/internal/JVMUpcalls", CHECK_false); + on_retransform_method_sym = SymbolTable::new_permanent_symbol("onRetransform", CHECK_false); + on_retransform_signature_sym = SymbolTable::new_permanent_symbol("(JZLjava/lang/Class;[B)[B", CHECK_false); + bytes_for_eager_instrumentation_sym = SymbolTable::new_permanent_symbol("bytesForEagerInstrumentation", CHECK_false); + bytes_for_eager_instrumentation_sig_sym = SymbolTable::new_permanent_symbol("(JZLjava/lang/Class;[B)[B", THREAD); + initialized = bytes_for_eager_instrumentation_sig_sym != NULL; + } + return initialized; +} + +static const typeArrayOop invoke(jlong trace_id, + jboolean force_instrumentation, + jclass class_being_redefined, + jint class_data_len, + const unsigned char* class_data, + Symbol* method_sym, + Symbol* signature_sym, + jint& new_bytes_length, + TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + const Klass* klass = SystemDictionary::resolve_or_fail(jvm_upcalls_class_sym, true, CHECK_NULL); + assert(klass != NULL, "invariant"); + typeArrayOop old_byte_array = oopFactory::new_byteArray(class_data_len, CHECK_NULL); + memcpy(old_byte_array->byte_at_addr(0), class_data, class_data_len); + JavaValue result(T_OBJECT); + JfrJavaArguments args(&result, klass, method_sym, signature_sym); + args.push_long(trace_id); + args.push_int(force_instrumentation); + args.push_jobject(class_being_redefined); + args.push_oop(old_byte_array); + JfrJavaSupport::call_static(&args, THREAD); + if (HAS_PENDING_EXCEPTION) { + if (true) tty->print_cr("JfrUpcall failed"); + return NULL; + } + // The result should be a [B + const oop res = (oop)result.get_jobject(); + assert(res != NULL, "invariant"); + assert(res->is_typeArray(), "invariant"); + assert(TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "invariant"); + const typeArrayOop new_byte_array = typeArrayOop(res); + new_bytes_length = (jint)new_byte_array->length(); + return new_byte_array; +} + +static const size_t ERROR_MSG_BUFFER_SIZE = 256; +static void log_error_and_throw_oom(jint new_bytes_length, TRAPS) { + char error_buffer[ERROR_MSG_BUFFER_SIZE]; + jio_snprintf(error_buffer, ERROR_MSG_BUFFER_SIZE, + "Thread local allocation (native) for " SIZE_FORMAT " bytes failed in JfrUpcalls", (size_t)new_bytes_length); + if (true) tty->print_cr("%s", error_buffer); + JfrJavaSupport::throw_out_of_memory_error(error_buffer, CHECK); +} + +void JfrUpcalls::on_retransform(jlong trace_id, + jclass class_being_redefined, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data, + TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + assert(class_being_redefined != NULL, "invariant"); + assert(class_data != NULL, "invariant"); + assert(new_class_data_len != NULL, "invariant"); + assert(new_class_data != NULL, "invariant"); + if (!JdkJfrEvent::is_visible(class_being_redefined)) { + return; + } + jint new_bytes_length = 0; + initialize(THREAD); + const typeArrayOop new_byte_array = invoke(trace_id, + false, + class_being_redefined, + class_data_len, + class_data, + on_retransform_method_sym, + on_retransform_signature_sym, + new_bytes_length, + CHECK); + assert(new_byte_array != NULL, "invariant"); + assert(new_bytes_length > 0, "invariant"); + // memory space must be malloced as mtInternal + // as it will be deallocated by JVMTI routines + unsigned char* const new_bytes = (unsigned char* const)os::malloc(new_bytes_length, mtInternal); + if (new_bytes == NULL) { + log_error_and_throw_oom(new_bytes_length, THREAD); // unwinds + } + assert(new_bytes != NULL, "invariant"); + memcpy(new_bytes, new_byte_array->byte_at_addr(0), (size_t)new_bytes_length); + *new_class_data_len = new_bytes_length; + *new_class_data = new_bytes; +} + +void JfrUpcalls::new_bytes_eager_instrumentation(jlong trace_id, + jboolean force_instrumentation, + jclass super, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data, + TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + assert(super != NULL, "invariant"); + assert(class_data != NULL, "invariant"); + assert(new_class_data_len != NULL, "invariant"); + assert(new_class_data != NULL, "invariant"); + jint new_bytes_length = 0; + initialize(THREAD); + const typeArrayOop new_byte_array = invoke(trace_id, + force_instrumentation, + super, + class_data_len, + class_data, + bytes_for_eager_instrumentation_sym, + bytes_for_eager_instrumentation_sig_sym, + new_bytes_length, + CHECK); + assert(new_byte_array != NULL, "invariant"); + assert(new_bytes_length > 0, "invariant"); + unsigned char* const new_bytes = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, unsigned char, new_bytes_length); + if (new_bytes == NULL) { + log_error_and_throw_oom(new_bytes_length, THREAD); // this unwinds + } + assert(new_bytes != NULL, "invariant"); + memcpy(new_bytes, new_byte_array->byte_at_addr(0), (size_t)new_bytes_length); + *new_class_data_len = new_bytes_length; + *new_class_data = new_bytes; +} + +instanceKlassHandle JfrUpcalls::load_event_handler_proxy_class(TRAPS) { + JavaValue result(T_OBJECT); + JfrJavaArguments call_args(&result, "jdk/jfr/internal/JVMUpcalls", + "getEventHandlerProxyClass", "()Ljava/lang/Class;", CHECK_NULL); + JfrJavaSupport::call_static(&call_args, CHECK_NULL); + assert(result.get_type() == T_OBJECT, "invariant"); + instanceHandle h_java_proxy(THREAD, (instanceOop)result.get_jobject()); + assert(h_java_proxy.not_null(), "invariant"); + return java_lang_Class::as_Klass(h_java_proxy()); +} diff --git a/src/share/vm/jfr/jni/jfrUpcalls.hpp b/src/share/vm/jfr/jni/jfrUpcalls.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3626983f1ad7e6c3058e2670ca87f9e981e1352b --- /dev/null +++ b/src/share/vm/jfr/jni/jfrUpcalls.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_JNI_JFRUPCALLS_HPP +#define SHARE_VM_JFR_JNI_JFRUPCALLS_HPP + +#include "jni.h" +#include "jfr/utilities/jfrAllocation.hpp" +#include "utilities/exceptions.hpp" + +class JavaThread; + +// +// Upcalls to Java for instrumentation purposes. +// Targets are located in jdk.jfr.internal.JVMUpcalls. +// +class JfrUpcalls : AllStatic { + public: + static void new_bytes_eager_instrumentation(jlong trace_id, + jboolean force_instrumentation, + jclass super, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data, + TRAPS); + + static void on_retransform(jlong trace_id, + jclass class_being_redefined, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data, + TRAPS); + + static instanceKlassHandle load_event_handler_proxy_class(TRAPS); +}; + +#endif // SHARE_VM_JFR_JNI_JFRUPCALLS_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/bfsClosure.cpp b/src/share/vm/jfr/leakprofiler/chains/bfsClosure.cpp new file mode 100644 index 0000000000000000000000000000000000000000..be6be5d7320957fb954bbbcb7fadbf79881436f7 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/bfsClosure.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2014, 2019, 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 "precompiled.hpp" +#include "jfr/leakprofiler/chains/bitset.hpp" +#include "jfr/leakprofiler/chains/bfsClosure.hpp" +#include "jfr/leakprofiler/chains/dfsClosure.hpp" +#include "jfr/leakprofiler/chains/edge.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/edgeQueue.hpp" +#include "jfr/leakprofiler/utilities/granularTimer.hpp" +#include "jfr/leakprofiler/utilities/unifiedOop.hpp" +#include "memory/iterator.inline.hpp" +#include "memory/resourceArea.hpp" +#include "oops/oop.inline.hpp" +#include "utilities/align.hpp" + +BFSClosure::BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, BitSet* mark_bits) : + _edge_queue(edge_queue), + _edge_store(edge_store), + _mark_bits(mark_bits), + _current_parent(NULL), + _current_frontier_level(0), + _next_frontier_idx(0), + _prev_frontier_idx(0), + _dfs_fallback_idx(0), + _use_dfs(false) { +} + +static void log_frontier_level_summary(size_t level, + size_t high_idx, + size_t low_idx, + size_t edge_size) { + const size_t nof_edges_in_frontier = high_idx - low_idx; + if (LogJFR && Verbose) tty->print_cr( + "BFS front: " SIZE_FORMAT " edges: " SIZE_FORMAT " size: " SIZE_FORMAT " [KB]", + level, + nof_edges_in_frontier, + (nof_edges_in_frontier * edge_size) / K + ); +} + +void BFSClosure::log_completed_frontier() const { + log_frontier_level_summary(_current_frontier_level, + _next_frontier_idx, + _prev_frontier_idx, + _edge_queue->sizeof_edge()); +} + +void BFSClosure::log_dfs_fallback() const { + const size_t edge_size = _edge_queue->sizeof_edge(); + // first complete summary for frontier in progress + log_frontier_level_summary(_current_frontier_level, + _next_frontier_idx, + _prev_frontier_idx, + edge_size); + + // and then also complete the last frontier + log_frontier_level_summary(_current_frontier_level + 1, + _edge_queue->bottom(), + _next_frontier_idx, + edge_size); + + // additional information about DFS fallover + if (LogJFR && Verbose) tty->print_cr( + "BFS front: " SIZE_FORMAT " filled edge queue at edge: " SIZE_FORMAT, + _current_frontier_level, + _dfs_fallback_idx + ); + + const size_t nof_dfs_completed_edges = _edge_queue->bottom() - _dfs_fallback_idx; + if (LogJFR && Verbose) tty->print_cr( + "DFS to complete " SIZE_FORMAT " edges size: " SIZE_FORMAT " [KB]", + nof_dfs_completed_edges, + (nof_dfs_completed_edges * edge_size) / K + ); +} + +void BFSClosure::process() { + process_root_set(); + process_queue(); +} + +void BFSClosure::process_root_set() { + for (size_t idx = _edge_queue->bottom(); idx < _edge_queue->top(); ++idx) { + const Edge* edge = _edge_queue->element_at(idx); + assert(edge->parent() == NULL, "invariant"); + process(edge->reference(), edge->pointee()); + } +} + +void BFSClosure::process(const oop* reference, const oop pointee) { + closure_impl(reference, pointee); +} +void BFSClosure::closure_impl(const oop* reference, const oop pointee) { + assert(reference != NULL, "invariant"); + assert(UnifiedOop::dereference(reference) == pointee, "invariant"); + + if (GranularTimer::is_finished()) { + return; + } + + if (_use_dfs) { + assert(_current_parent != NULL, "invariant"); + DFSClosure::find_leaks_from_edge(_edge_store, _mark_bits, _current_parent); + return; + } + + if (!_mark_bits->is_marked(pointee)) { + _mark_bits->mark_obj(pointee); + // is the pointee a sample object? + if (NULL == pointee->mark()) { + add_chain(reference, pointee); + } + + // if we are processinig initial root set, don't add to queue + if (_current_parent != NULL) { + _edge_queue->add(_current_parent, reference); + } + + if (_edge_queue->is_full()) { + dfs_fallback(); + } + } +} + +void BFSClosure::add_chain(const oop* reference, const oop pointee) { + assert(pointee != NULL, "invariant"); + assert(NULL == pointee->mark(), "invariant"); + Edge leak_edge(_current_parent, reference); + _edge_store->put_chain(&leak_edge, _current_parent == NULL ? 1 : _current_frontier_level + 2); +} + +void BFSClosure::dfs_fallback() { + assert(_edge_queue->is_full(), "invariant"); + _use_dfs = true; + _dfs_fallback_idx = _edge_queue->bottom(); + while (!_edge_queue->is_empty()) { + const Edge* edge = _edge_queue->remove(); + if (edge->pointee() != NULL) { + DFSClosure::find_leaks_from_edge(_edge_store, _mark_bits, edge); + } + } +} + +void BFSClosure::process_queue() { + assert(_current_frontier_level == 0, "invariant"); + assert(_next_frontier_idx == 0, "invariant"); + assert(_prev_frontier_idx == 0, "invariant"); + + _next_frontier_idx = _edge_queue->top(); + while (!is_complete()) { + iterate(_edge_queue->remove()); // edge_queue.remove() increments bottom + } +} + +void BFSClosure::step_frontier() const { + log_completed_frontier(); + ++_current_frontier_level; + _prev_frontier_idx = _next_frontier_idx; + _next_frontier_idx = _edge_queue->top(); +} + +bool BFSClosure::is_complete() const { + if (_edge_queue->bottom() < _next_frontier_idx) { + return false; + } + if (_edge_queue->bottom() > _next_frontier_idx) { + // fallback onto DFS as part of processing the frontier + assert(_dfs_fallback_idx >= _prev_frontier_idx, "invariant"); + assert(_dfs_fallback_idx < _next_frontier_idx, "invariant"); + log_dfs_fallback(); + return true; + } + assert(_edge_queue->bottom() == _next_frontier_idx, "invariant"); + if (_edge_queue->is_empty()) { + return true; + } + step_frontier(); + return false; +} + +void BFSClosure::iterate(const Edge* parent) { + assert(parent != NULL, "invariant"); + const oop pointee = parent->pointee(); + assert(pointee != NULL, "invariant"); + _current_parent = parent; + pointee->oop_iterate(this); +} + +void BFSClosure::do_oop(oop* ref) { + assert(ref != NULL, "invariant"); + assert(is_aligned(ref, HeapWordSize), "invariant"); + const oop pointee = *ref; + if (pointee != NULL) { + closure_impl(ref, pointee); + } +} + +void BFSClosure::do_oop(narrowOop* ref) { + assert(ref != NULL, "invariant"); + assert(is_aligned(ref, sizeof(narrowOop)), "invariant"); + const oop pointee = oopDesc::load_decode_heap_oop(ref); + if (pointee != NULL) { + closure_impl(UnifiedOop::encode(ref), pointee); + } +} + +void BFSClosure::do_root(const oop* ref) { + assert(ref != NULL, "invariant"); + if (!_edge_queue->is_full()) { + _edge_queue->add(NULL, ref); + } +} diff --git a/src/share/vm/jfr/leakprofiler/chains/bfsClosure.hpp b/src/share/vm/jfr/leakprofiler/chains/bfsClosure.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3a5a6e6b28059a8b05401f34fe537724d9b97e1a --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/bfsClosure.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_CHAINS_BFSCLOSURE_HPP +#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_BFSCLOSURE_HPP + +#include "memory/iterator.hpp" + +class BitSet; +class Edge; +class EdgeStore; +class EdgeQueue; + +// Class responsible for iterating the heap breadth-first +class BFSClosure : public ExtendedOopClosure { // XXX BasicOopIterateClosure + private: + EdgeQueue* _edge_queue; + EdgeStore* _edge_store; + BitSet* _mark_bits; + const Edge* _current_parent; + mutable size_t _current_frontier_level; + mutable size_t _next_frontier_idx; + mutable size_t _prev_frontier_idx; + size_t _dfs_fallback_idx; + bool _use_dfs; + + void log_completed_frontier() const; + void log_dfs_fallback() const; + + bool is_complete() const; + void step_frontier() const; + + void closure_impl(const oop* reference, const oop pointee); + void add_chain(const oop* reference, const oop pointee); + void dfs_fallback(); + + void iterate(const Edge* parent); + void process(const oop* reference, const oop pointee); + + void process_root_set(); + void process_queue(); + + public: + BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, BitSet* mark_bits); + void process(); + void do_root(const oop* ref); + + virtual void do_oop(oop* ref); + virtual void do_oop(narrowOop* ref); +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_BFSCLOSURE_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/bitset.cpp b/src/share/vm/jfr/leakprofiler/chains/bitset.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a2dd1649552cbbed19e6e92580f954f8ec979813 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/bitset.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 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 "precompiled.hpp" +#include "jfr/leakprofiler/chains/bitset.hpp" +#include "jfr/recorder/storage/jfrVirtualMemory.hpp" +#include "memory/memRegion.hpp" + +BitSet::BitSet(const MemRegion& covered_region) : + _vmm(NULL), + _region_start(covered_region.start()), + _region_size(covered_region.word_size()) { +} + +BitSet::~BitSet() { + delete _vmm; +} + +bool BitSet::initialize() { + assert(_vmm == NULL, "invariant"); + _vmm = new JfrVirtualMemory(); + if (_vmm == NULL) { + return false; + } + + const BitMap::idx_t bits = _region_size >> LogMinObjAlignment; + const size_t words = bits / BitsPerWord; + const size_t raw_bytes = words * sizeof(BitMap::idx_t); + + // the virtual memory invocation will reserve and commit the entire space + BitMap::bm_word_t* map = (BitMap::bm_word_t*)_vmm->initialize(raw_bytes, raw_bytes); + if (map == NULL) { + return false; + } + _bits = BitMap(map, bits); + return true; +} + diff --git a/src/share/vm/jfr/leakprofiler/chains/bitset.hpp b/src/share/vm/jfr/leakprofiler/chains/bitset.hpp new file mode 100644 index 0000000000000000000000000000000000000000..54f81090745b021ad70515386d72811d2f0c4bd8 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/bitset.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_CHAINS_BITSET_HPP +#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_BITSET_HPP + +#include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" +#include "utilities/bitMap.inline.hpp" + +class JfrVirtualMemory; +class MemRegion; + +class BitSet : public CHeapObj { + private: + JfrVirtualMemory* _vmm; + const HeapWord* const _region_start; + BitMap _bits; + const size_t _region_size; + + public: + BitSet(const MemRegion& covered_region); + ~BitSet(); + + bool initialize(); + + BitMap::idx_t mark_obj(const HeapWord* addr) { + const BitMap::idx_t bit = addr_to_bit(addr); + _bits.set_bit(bit); + return bit; + } + + BitMap::idx_t mark_obj(oop obj) { + return mark_obj((HeapWord*)obj); + } + + bool is_marked(const HeapWord* addr) const { + return is_marked(addr_to_bit(addr)); + } + + bool is_marked(oop obj) const { + return is_marked((HeapWord*)obj); + } + + BitMap::idx_t size() const { + return _bits.size(); + } + + BitMap::idx_t addr_to_bit(const HeapWord* addr) const { + return pointer_delta(addr, _region_start) >> LogMinObjAlignment; + } + + bool is_marked(const BitMap::idx_t bit) const { + return _bits.at(bit); + } +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_BITSET_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/dfsClosure.cpp b/src/share/vm/jfr/leakprofiler/chains/dfsClosure.cpp new file mode 100644 index 0000000000000000000000000000000000000000..388731faf5d5a4962f59bd7810a2650de7f451ad --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/dfsClosure.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2014, 2019, 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 "precompiled.hpp" +#include "jfr/leakprofiler/chains/bitset.hpp" +#include "jfr/leakprofiler/chains/dfsClosure.hpp" +#include "jfr/leakprofiler/chains/edge.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/rootSetClosure.hpp" +#include "jfr/leakprofiler/utilities/granularTimer.hpp" +#include "jfr/leakprofiler/utilities/rootType.hpp" +#include "jfr/leakprofiler/utilities/unifiedOop.hpp" +#include "memory/iterator.inline.hpp" +#include "memory/resourceArea.hpp" +#include "oops/oop.inline.hpp" +#include "utilities/align.hpp" + +// max dfs depth should not exceed size of stack +static const size_t max_dfs_depth = 5000; + +EdgeStore* DFSClosure::_edge_store = NULL; +BitSet* DFSClosure::_mark_bits = NULL; +const Edge* DFSClosure::_start_edge = NULL; +size_t DFSClosure::_max_depth = max_dfs_depth; +bool DFSClosure::_ignore_root_set = false; + +DFSClosure::DFSClosure() : + _parent(NULL), + _reference(NULL), + _depth(0) { +} + +DFSClosure::DFSClosure(DFSClosure* parent, size_t depth) : + _parent(parent), + _reference(NULL), + _depth(depth) { +} + +void DFSClosure::find_leaks_from_edge(EdgeStore* edge_store, + BitSet* mark_bits, + const Edge* start_edge) { + assert(edge_store != NULL, "invariant"); + assert(mark_bits != NULL," invariant"); + assert(start_edge != NULL, "invariant"); + + _edge_store = edge_store; + _mark_bits = mark_bits; + _start_edge = start_edge; + _ignore_root_set = false; + assert(_max_depth == max_dfs_depth, "invariant"); + + // Depth-first search, starting from a BFS egde + DFSClosure dfs; + start_edge->pointee()->oop_iterate(&dfs); +} + +void DFSClosure::find_leaks_from_root_set(EdgeStore* edge_store, + BitSet* mark_bits) { + assert(edge_store != NULL, "invariant"); + assert(mark_bits != NULL, "invariant"); + + _edge_store = edge_store; + _mark_bits = mark_bits; + _start_edge = NULL; + + // Mark root set, to avoid going sideways + _max_depth = 1; + _ignore_root_set = false; + DFSClosure dfs; + RootSetClosure rs(&dfs); + rs.process(); + + // Depth-first search + _max_depth = max_dfs_depth; + _ignore_root_set = true; + assert(_start_edge == NULL, "invariant"); + rs.process(); +} + +void DFSClosure::closure_impl(const oop* reference, const oop pointee) { + assert(pointee != NULL, "invariant"); + assert(reference != NULL, "invariant"); + + if (GranularTimer::is_finished()) { + return; + } + if (_depth == 0 && _ignore_root_set) { + // Root set is already marked, but we want + // to continue, so skip is_marked check. + assert(_mark_bits->is_marked(pointee), "invariant"); + } else { + if (_mark_bits->is_marked(pointee)) { + return; + } + } + + _reference = reference; + _mark_bits->mark_obj(pointee); + assert(_mark_bits->is_marked(pointee), "invariant"); + + // is the pointee a sample object? + if (NULL == pointee->mark()) { + add_chain(); + } + + assert(_max_depth >= 1, "invariant"); + if (_depth < _max_depth - 1) { + DFSClosure next_level(this, _depth + 1); + pointee->oop_iterate(&next_level); + } +} + +void DFSClosure::add_chain() { + const size_t array_length = _depth + 2; + + ResourceMark rm; + Edge* const chain = NEW_RESOURCE_ARRAY(Edge, array_length); + size_t idx = 0; + + // aggregate from depth-first search + const DFSClosure* c = this; + while (c != NULL) { + const size_t next = idx + 1; + chain[idx++] = Edge(&chain[next], c->reference()); + c = c->parent(); + } + assert(_depth + 1 == idx, "invariant"); + assert(array_length == idx + 1, "invariant"); + + // aggregate from breadth-first search + if (_start_edge != NULL) { + chain[idx++] = *_start_edge; + } else { + chain[idx - 1] = Edge(NULL, chain[idx - 1].reference()); + } + _edge_store->put_chain(chain, idx + (_start_edge != NULL ? _start_edge->distance_to_root() : 0)); +} + +void DFSClosure::do_oop(oop* ref) { + assert(ref != NULL, "invariant"); + assert(is_aligned(ref, HeapWordSize), "invariant"); + const oop pointee = *ref; + if (pointee != NULL) { + closure_impl(ref, pointee); + } +} + +void DFSClosure::do_oop(narrowOop* ref) { + assert(ref != NULL, "invariant"); + assert(is_aligned(ref, sizeof(narrowOop)), "invariant"); + const oop pointee = oopDesc::load_decode_heap_oop(ref); + if (pointee != NULL) { + closure_impl(UnifiedOop::encode(ref), pointee); + } +} + +void DFSClosure::do_root(const oop* ref) { + assert(ref != NULL, "invariant"); + const oop pointee = UnifiedOop::dereference(ref); + assert(pointee != NULL, "invariant"); + closure_impl(ref, pointee); +} diff --git a/src/share/vm/jfr/leakprofiler/chains/dfsClosure.hpp b/src/share/vm/jfr/leakprofiler/chains/dfsClosure.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8098fc84241b661aab910487c7e62eb33e606b39 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/dfsClosure.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_CHAINS_DFSCLOSURE_HPP +#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_DFSCLOSURE_HPP + +#include "memory/iterator.hpp" + +class BitSet; +class Edge; +class EdgeStore; +class EdgeQueue; + +// Class responsible for iterating the heap depth-first +class DFSClosure : public ExtendedOopClosure { // XXX BasicOopIterateClosure + private: + static EdgeStore* _edge_store; + static BitSet* _mark_bits; + static const Edge*_start_edge; + static size_t _max_depth; + static bool _ignore_root_set; + DFSClosure* _parent; + const oop* _reference; + size_t _depth; + + void add_chain(); + void closure_impl(const oop* reference, const oop pointee); + + DFSClosure* parent() const { return _parent; } + const oop* reference() const { return _reference; } + + DFSClosure(DFSClosure* parent, size_t depth); + DFSClosure(); + + public: + static void find_leaks_from_edge(EdgeStore* edge_store, BitSet* mark_bits, const Edge* start_edge); + static void find_leaks_from_root_set(EdgeStore* edge_store, BitSet* mark_bits); + void do_root(const oop* ref); + + virtual void do_oop(oop* ref); + virtual void do_oop(narrowOop* ref); +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_DFSCLOSURE_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/edge.cpp b/src/share/vm/jfr/leakprofiler/chains/edge.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c8d32bb8d16b2e1ebc7e0fb81ef50d9b1f74904 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/edge.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, 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 "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "jfr/leakprofiler/chains/edge.hpp" +#include "jfr/leakprofiler/utilities/unifiedOop.hpp" + +Edge::Edge() : _parent(NULL), _reference(NULL) {} + +Edge::Edge(const Edge* parent, const oop* reference) : _parent(parent), + _reference(reference) {} + +const oop Edge::pointee() const { + return UnifiedOop::dereference(_reference); +} + +const oop Edge::reference_owner() const { + return is_root() ? (oop)NULL : UnifiedOop::dereference(_parent->reference()); +} + +static const Klass* resolve_klass(const oop obj) { + assert(obj != NULL, "invariant"); + return java_lang_Class::is_instance(obj) ? + java_lang_Class::as_Klass(obj) : obj->klass(); +} + +const Klass* Edge::pointee_klass() const { + return resolve_klass(pointee()); +} + +const Klass* Edge::reference_owner_klass() const { + const oop ref_owner = reference_owner(); + return ref_owner != NULL ? resolve_klass(ref_owner) : NULL; +} + +size_t Edge::distance_to_root() const { + size_t depth = 0; + const Edge* current = _parent; + while (current != NULL) { + depth++; + current = current->parent(); + } + return depth; +} diff --git a/src/share/vm/jfr/leakprofiler/chains/edge.hpp b/src/share/vm/jfr/leakprofiler/chains/edge.hpp new file mode 100644 index 0000000000000000000000000000000000000000..aac7b570c1eaa8d61103cceae2fcdd550a03f981 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/edge.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGE_HPP +#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGE_HPP + +#include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" + +class Edge { + protected: + const Edge* _parent; + const oop* _reference; + public: + Edge(); + Edge(const Edge* parent, const oop* reference); + + const oop* reference() const { + return _reference; + } + const Edge* parent() const { + return _parent; + } + bool is_root() const { + return _parent == NULL; + } + const oop pointee() const; + const Klass* pointee_klass() const; + const oop reference_owner() const; + const Klass* reference_owner_klass() const; + size_t distance_to_root() const; + + void* operator new (size_t sz, void* here) { + return here; + } +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGE_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/edgeQueue.cpp b/src/share/vm/jfr/leakprofiler/chains/edgeQueue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3abf573fc99eca8094f26a7fb57c4cf54374298 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/edgeQueue.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, 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 "precompiled.hpp" +#include "jfr/leakprofiler/chains/edgeQueue.hpp" +#include "jfr/recorder/storage/jfrVirtualMemory.hpp" + +EdgeQueue::EdgeQueue(size_t reservation_size_bytes, size_t commit_block_size_bytes) : + _vmm(NULL), + _reservation_size_bytes(reservation_size_bytes), + _commit_block_size_bytes(commit_block_size_bytes), + _top_index(0), + _bottom_index(0) { +} + +bool EdgeQueue::initialize() { + assert(_reservation_size_bytes >= _commit_block_size_bytes, "invariant"); + assert(_vmm == NULL, "invariant"); + _vmm = new JfrVirtualMemory(); + return _vmm != NULL && _vmm->initialize(_reservation_size_bytes, _commit_block_size_bytes, sizeof(Edge)); +} + +EdgeQueue::~EdgeQueue() { + delete _vmm; +} + +void EdgeQueue::add(const Edge* parent, const oop* ref) { + assert(ref != NULL, "Null objects not allowed in EdgeQueue"); + assert(!is_full(), "EdgeQueue is full. Check is_full before adding another Edge"); + assert(!_vmm->is_full(), "invariant"); + void* const allocation = _vmm->new_datum(); + assert(allocation != NULL, "invariant"); + new (allocation)Edge(parent, ref); + _top_index++; + assert(_vmm->count() == _top_index, "invariant"); +} + +size_t EdgeQueue::top() const { + return _top_index; +} + +size_t EdgeQueue::bottom() const { + return EdgeQueue::_bottom_index; +} + +bool EdgeQueue::is_empty() const { + return _top_index == _bottom_index; +} + +bool EdgeQueue::is_full() const { + return _vmm->is_full(); +} + +const Edge* EdgeQueue::remove() const { + assert(!is_empty(), "EdgeQueue is empty. Check if empty before removing Edge"); + assert(!_vmm->is_empty(), "invariant"); + return (const Edge*)_vmm->get(_bottom_index++); +} + +const Edge* EdgeQueue::element_at(size_t index) const { + assert(index >= _bottom_index, "invariant"); + assert(index <_top_index, "invariant"); + return (Edge*)_vmm->get(index); +} + +size_t EdgeQueue::reserved_size() const { + assert(_vmm != NULL, "invariant"); + return _vmm->reserved_size(); +} + +size_t EdgeQueue::live_set() const { + assert(_vmm != NULL, "invariant"); + return _vmm->live_set(); +} + +size_t EdgeQueue::sizeof_edge() const { + assert(_vmm != NULL, "invariant"); + return _vmm->aligned_datum_size_bytes(); +} diff --git a/src/share/vm/jfr/leakprofiler/chains/edgeQueue.hpp b/src/share/vm/jfr/leakprofiler/chains/edgeQueue.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8211624616a4d9add06bce38b9d1c59b0045ac90 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/edgeQueue.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGEQUEUE_HPP +#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGEQUEUE_HPP + +#include "memory/allocation.hpp" +#include "jfr/leakprofiler/chains/edge.hpp" + +class JfrVirtualMemory; + +class EdgeQueue : public CHeapObj { + private: + JfrVirtualMemory* _vmm; + const size_t _reservation_size_bytes; + const size_t _commit_block_size_bytes; + mutable size_t _top_index; + mutable size_t _bottom_index; + public: + EdgeQueue(size_t reservation_size_bytes, size_t commit_block_size_bytes); + ~EdgeQueue(); + + bool initialize(); + + void add(const Edge* parent, const oop* ref); + const Edge* remove() const; + const Edge* element_at(size_t index) const; + + size_t top() const; + size_t bottom() const; + bool is_empty() const; + bool is_full() const; + + size_t reserved_size() const; + size_t live_set() const; + size_t sizeof_edge() const; // with alignments +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGEQUEUE_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp b/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d18713151f370be08081b6fa607e5b113031a72a --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2014, 2019, 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 "precompiled.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/edgeUtils.hpp" +#include "oops/oop.inline.hpp" + +StoredEdge::StoredEdge() : Edge() {} +StoredEdge::StoredEdge(const Edge* parent, const oop* reference) : Edge(parent, reference), _gc_root_id(0), _skip_length(0) {} + +StoredEdge::StoredEdge(const Edge& edge) : Edge(edge), _gc_root_id(0), _skip_length(0) {} + +StoredEdge::StoredEdge(const StoredEdge& edge) : Edge(edge), _gc_root_id(edge._gc_root_id), _skip_length(edge._skip_length) {} + +void StoredEdge::operator=(const StoredEdge& edge) { + Edge::operator=(edge); + _gc_root_id = edge._gc_root_id; + _skip_length = edge._skip_length; +} + +traceid EdgeStore::_edge_id_counter = 0; + +EdgeStore::EdgeStore() : _edges(NULL) { + _edges = new EdgeHashTable(this); +} + +EdgeStore::~EdgeStore() { + assert(_edges != NULL, "invariant"); + delete _edges; +} + +bool EdgeStore::is_empty() const { + return !_edges->has_entries(); +} + +void EdgeStore::assign_id(EdgeEntry* entry) { + assert(entry != NULL, "invariant"); + assert(entry->id() == 0, "invariant"); + entry->set_id(++_edge_id_counter); +} + +bool EdgeStore::equals(const Edge& query, uintptr_t hash, const EdgeEntry* entry) { + assert(entry != NULL, "invariant"); + assert(entry->hash() == hash, "invariant"); + return true; +} + +#ifdef ASSERT +bool EdgeStore::contains(const oop* reference) const { + return get(reference) != NULL; +} +#endif + +StoredEdge* EdgeStore::get(const oop* reference) const { + assert(reference != NULL, "invariant"); + const StoredEdge e(NULL, reference); + EdgeEntry* const entry = _edges->lookup_only(e, (uintptr_t)reference); + return entry != NULL ? entry->literal_addr() : NULL; +} + +StoredEdge* EdgeStore::put(const oop* reference) { + assert(reference != NULL, "invariant"); + const StoredEdge e(NULL, reference); + assert(NULL == _edges->lookup_only(e, (uintptr_t)reference), "invariant"); + EdgeEntry& entry = _edges->put(e, (uintptr_t)reference); + return entry.literal_addr(); +} + +traceid EdgeStore::get_id(const Edge* edge) const { + assert(edge != NULL, "invariant"); + EdgeEntry* const entry = _edges->lookup_only(*edge, (uintptr_t)edge->reference()); + assert(entry != NULL, "invariant"); + return entry->id(); +} + +traceid EdgeStore::gc_root_id(const Edge* edge) const { + assert(edge != NULL, "invariant"); + const traceid gc_root_id = static_cast(edge)->gc_root_id(); + if (gc_root_id != 0) { + return gc_root_id; + } + // not cached + assert(edge != NULL, "invariant"); + const Edge* const root = EdgeUtils::root(*edge); + assert(root != NULL, "invariant"); + assert(root->parent() == NULL, "invariant"); + return get_id(root); +} + +static const Edge* get_skip_ancestor(const Edge** current, size_t distance_to_root, size_t* skip_length) { + assert(distance_to_root >= EdgeUtils::root_context, "invariant"); + assert(*skip_length == 0, "invariant"); + *skip_length = distance_to_root - (EdgeUtils::root_context - 1); + const Edge* const target = EdgeUtils::ancestor(**current, *skip_length); + assert(target != NULL, "invariant"); + assert(target->distance_to_root() + 1 == EdgeUtils::root_context, "invariant"); + return target; +} + +bool EdgeStore::put_skip_edge(StoredEdge** previous, const Edge** current, size_t distance_to_root) { + assert(*previous != NULL, "invariant"); + assert((*previous)->parent() == NULL, "invariant"); + assert(*current != NULL, "invariant"); + assert((*current)->distance_to_root() == distance_to_root, "invariant"); + + if (distance_to_root < EdgeUtils::root_context) { + // nothing to skip + return false; + } + + size_t skip_length = 0; + const Edge* const skip_ancestor = get_skip_ancestor(current, distance_to_root, &skip_length); + assert(skip_ancestor != NULL, "invariant"); + (*previous)->set_skip_length(skip_length); + + // lookup target + StoredEdge* stored_target = get(skip_ancestor->reference()); + if (stored_target != NULL) { + (*previous)->set_parent(stored_target); + // linked to existing, complete + return true; + } + + assert(stored_target == NULL, "invariant"); + stored_target = put(skip_ancestor->reference()); + assert(stored_target != NULL, "invariant"); + (*previous)->set_parent(stored_target); + *previous = stored_target; + *current = skip_ancestor->parent(); + return false; +} + +static void link_edge(const StoredEdge* current_stored, StoredEdge** previous) { + assert(current_stored != NULL, "invariant"); + assert(*previous != NULL, "invariant"); + assert((*previous)->parent() == NULL, "invariant"); + (*previous)->set_parent(current_stored); +} + +static const StoredEdge* find_closest_skip_edge(const StoredEdge* edge, size_t* distance) { + assert(edge != NULL, "invariant"); + assert(distance != NULL, "invariant"); + const StoredEdge* current = edge; + *distance = 1; + while (current != NULL && !current->is_skip_edge()) { + ++(*distance); + current = current->parent(); + } + return current; +} + +void EdgeStore::link_with_existing_chain(const StoredEdge* current_stored, StoredEdge** previous, size_t previous_length) { + assert(current_stored != NULL, "invariant"); + assert((*previous)->parent() == NULL, "invariant"); + size_t distance_to_skip_edge; // including the skip edge itself + const StoredEdge* const closest_skip_edge = find_closest_skip_edge(current_stored, &distance_to_skip_edge); + if (closest_skip_edge == NULL) { + // no found skip edge implies root + if (distance_to_skip_edge + previous_length <= EdgeUtils::max_ref_chain_depth) { + link_edge(current_stored, previous); + return; + } + assert(current_stored->distance_to_root() == distance_to_skip_edge - 2, "invariant"); + put_skip_edge(previous, reinterpret_cast(¤t_stored), distance_to_skip_edge - 2); + return; + } + assert(closest_skip_edge->is_skip_edge(), "invariant"); + if (distance_to_skip_edge + previous_length <= EdgeUtils::leak_context) { + link_edge(current_stored, previous); + return; + } + // create a new skip edge with derived information from closest skip edge + (*previous)->set_skip_length(distance_to_skip_edge + closest_skip_edge->skip_length()); + (*previous)->set_parent(closest_skip_edge->parent()); +} + +StoredEdge* EdgeStore::link_new_edge(StoredEdge** previous, const Edge** current) { + assert(*previous != NULL, "invariant"); + assert((*previous)->parent() == NULL, "invariant"); + assert(*current != NULL, "invariant"); + assert(!contains((*current)->reference()), "invariant"); + StoredEdge* const stored_edge = put((*current)->reference()); + assert(stored_edge != NULL, "invariant"); + link_edge(stored_edge, previous); + return stored_edge; +} + +bool EdgeStore::put_edges(StoredEdge** previous, const Edge** current, size_t limit) { + assert(*previous != NULL, "invariant"); + assert(*current != NULL, "invariant"); + size_t depth = 1; + while (*current != NULL && depth < limit) { + StoredEdge* stored_edge = get((*current)->reference()); + if (stored_edge != NULL) { + link_with_existing_chain(stored_edge, previous, depth); + return true; + } + stored_edge = link_new_edge(previous, current); + assert((*previous)->parent() != NULL, "invariant"); + *previous = stored_edge; + *current = (*current)->parent(); + ++depth; + } + return NULL == *current; +} + +// Install the immediate edge into the mark word of the leak candidate object +StoredEdge* EdgeStore::associate_leak_context_with_candidate(const Edge* edge) { + assert(edge != NULL, "invariant"); + assert(!contains(edge->reference()), "invariant"); + StoredEdge* const leak_context_edge = put(edge->reference()); + oop sample_object = edge->pointee(); + assert(sample_object != NULL, "invariant"); + assert(NULL == sample_object->mark(), "invariant"); + sample_object->set_mark(markOop(leak_context_edge)); + return leak_context_edge; +} + +/* + * The purpose of put_chain() is to reify the edge sequence + * discovered during heap traversal with a normalized logical copy. + * This copy consist of two sub-sequences and a connecting link (skip edge). + * + * "current" can be thought of as the cursor (search) edge, it is not in the edge store. + * "previous" is always an edge in the edge store. + * The leak context edge is the edge adjacent to the leak candidate object, always an edge in the edge store. + */ +void EdgeStore::put_chain(const Edge* chain, size_t length) { + assert(chain != NULL, "invariant"); + assert(chain->distance_to_root() + 1 == length, "invariant"); + StoredEdge* const leak_context_edge = associate_leak_context_with_candidate(chain); + assert(leak_context_edge != NULL, "invariant"); + assert(leak_context_edge->parent() == NULL, "invariant"); + + if (1 == length) { + return; + } + + const Edge* current = chain->parent(); + assert(current != NULL, "invariant"); + StoredEdge* previous = leak_context_edge; + + // a leak context is the sequence of (limited) edges reachable from the leak candidate + if (put_edges(&previous, ¤t, EdgeUtils::leak_context)) { + // complete + assert(previous != NULL, "invariant"); + put_chain_epilogue(leak_context_edge, EdgeUtils::root(*previous)); + return; + } + + const size_t distance_to_root = length > EdgeUtils::leak_context ? length - 1 - EdgeUtils::leak_context : length - 1; + assert(current->distance_to_root() == distance_to_root, "invariant"); + + // a skip edge is the logical link + // connecting the leak context sequence with the root context sequence + if (put_skip_edge(&previous, ¤t, distance_to_root)) { + // complete + assert(previous != NULL, "invariant"); + assert(previous->is_skip_edge(), "invariant"); + assert(previous->parent() != NULL, "invariant"); + put_chain_epilogue(leak_context_edge, EdgeUtils::root(*previous->parent())); + return; + } + + assert(current->distance_to_root() < EdgeUtils::root_context, "invariant"); + + // a root context is the sequence of (limited) edges reachable from the root + put_edges(&previous, ¤t, EdgeUtils::root_context); + assert(previous != NULL, "invariant"); + put_chain_epilogue(leak_context_edge, EdgeUtils::root(*previous)); +} + +void EdgeStore::put_chain_epilogue(StoredEdge* leak_context_edge, const Edge* root) const { + assert(leak_context_edge != NULL, "invariant"); + assert(root != NULL, "invariant"); + store_gc_root_id_in_leak_context_edge(leak_context_edge, root); + assert(leak_context_edge->distance_to_root() + 1 <= EdgeUtils::max_ref_chain_depth, "invariant"); +} + +// To avoid another traversal to resolve the root edge id later, +// cache it in the immediate leak context edge for fast retrieval. +void EdgeStore::store_gc_root_id_in_leak_context_edge(StoredEdge* leak_context_edge, const Edge* root) const { + assert(leak_context_edge != NULL, "invariant"); + assert(leak_context_edge->gc_root_id() == 0, "invariant"); + assert(root != NULL, "invariant"); + assert(root->parent() == NULL, "invariant"); + assert(root->distance_to_root() == 0, "invariant"); + const StoredEdge* const stored_root = static_cast(root); + traceid root_id = stored_root->gc_root_id(); + if (root_id == 0) { + root_id = get_id(root); + stored_root->set_gc_root_id(root_id); + } + assert(root_id != 0, "invariant"); + leak_context_edge->set_gc_root_id(root_id); + assert(leak_context_edge->gc_root_id() == stored_root->gc_root_id(), "invariant"); +} diff --git a/src/share/vm/jfr/leakprofiler/chains/edgeStore.hpp b/src/share/vm/jfr/leakprofiler/chains/edgeStore.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7f8db737d48b86f9c50a9d1c5f1e26508b4b90f1 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/edgeStore.hpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_LEAKPROFILER_CHAINS_EDGESTORE_HPP +#define SHARE_VM_LEAKPROFILER_CHAINS_EDGESTORE_HPP + +#include "jfr/leakprofiler/chains/edge.hpp" +#include "jfr/utilities/jfrHashtable.hpp" +#include "memory/allocation.hpp" + +typedef u8 traceid; + +class StoredEdge : public Edge { + private: + mutable traceid _gc_root_id; + size_t _skip_length; + + public: + StoredEdge(); + StoredEdge(const Edge* parent, const oop* reference); + StoredEdge(const Edge& edge); + StoredEdge(const StoredEdge& edge); + void operator=(const StoredEdge& edge); + + traceid gc_root_id() const { return _gc_root_id; } + void set_gc_root_id(traceid root_id) const { _gc_root_id = root_id; } + + bool is_skip_edge() const { return _skip_length != 0; } + size_t skip_length() const { return _skip_length; } + void set_skip_length(size_t length) { _skip_length = length; } + + void set_parent(const Edge* edge) { this->_parent = edge; } + + StoredEdge* parent() const { + return const_cast(static_cast(Edge::parent())); + } +}; + +class EdgeStore : public CHeapObj { + typedef HashTableHost EdgeHashTable; + typedef EdgeHashTable::HashEntry EdgeEntry; + template class, + typename, + size_t> + friend class HashTableHost; + friend class EventEmitter; + friend class ObjectSampleWriter; + friend class ObjectSampleCheckpoint; + private: + static traceid _edge_id_counter; + EdgeHashTable* _edges; + + // Hash table callbacks + void assign_id(EdgeEntry* entry); + bool equals(const Edge& query, uintptr_t hash, const EdgeEntry* entry); + + StoredEdge* get(const oop* reference) const; + StoredEdge* put(const oop* reference); + traceid gc_root_id(const Edge* edge) const; + + bool put_edges(StoredEdge** previous, const Edge** current, size_t length); + bool put_skip_edge(StoredEdge** previous, const Edge** current, size_t distance_to_root); + void put_chain_epilogue(StoredEdge* leak_context_edge, const Edge* root) const; + + StoredEdge* associate_leak_context_with_candidate(const Edge* edge); + void store_gc_root_id_in_leak_context_edge(StoredEdge* leak_context_edge, const Edge* root) const; + StoredEdge* link_new_edge(StoredEdge** previous, const Edge** current); + void link_with_existing_chain(const StoredEdge* current_stored, StoredEdge** previous, size_t previous_length); + + template + void iterate(T& functor) const { _edges->iterate_value(functor); } + + DEBUG_ONLY(bool contains(const oop* reference) const;) + + public: + EdgeStore(); + ~EdgeStore(); + + bool is_empty() const; + traceid get_id(const Edge* edge) const; + void put_chain(const Edge* chain, size_t length); +}; + +#endif // SHARE_VM_LEAKPROFILER_CHAINS_EDGESTORE_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/edgeUtils.cpp b/src/share/vm/jfr/leakprofiler/chains/edgeUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85de5094819b8a9577aa387ac6a342b0f6f1c297 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/edgeUtils.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014, 2019, 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 "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "jfr/leakprofiler/chains/edge.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/edgeUtils.hpp" +#include "jfr/leakprofiler/utilities/unifiedOop.hpp" +#include "oops/fieldStreams.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/objArrayOop.hpp" +#include "oops/oopsHierarchy.hpp" +#include "runtime/handles.inline.hpp" + +bool EdgeUtils::is_leak_edge(const Edge& edge) { + return (const Edge*)edge.pointee()->mark() == &edge; +} + +static int field_offset(const StoredEdge& edge) { + assert(!edge.is_root(), "invariant"); + const oop ref_owner = edge.reference_owner(); + assert(ref_owner != NULL, "invariant"); + const oop* reference = UnifiedOop::decode(edge.reference()); + assert(reference != NULL, "invariant"); + assert(!UnifiedOop::is_narrow(reference), "invariant"); + assert(!ref_owner->is_array(), "invariant"); + assert(ref_owner->is_instance(), "invariant"); + const int offset = (int)pointer_delta(reference, ref_owner, sizeof(char)); + assert(offset < (ref_owner->size() * HeapWordSize), "invariant"); + return offset; +} + +static const InstanceKlass* field_type(const StoredEdge& edge) { + assert(!edge.is_root() || !EdgeUtils::is_array_element(edge), "invariant"); + return (const InstanceKlass*)edge.reference_owner_klass(); +} + +const Symbol* EdgeUtils::field_name_symbol(const Edge& edge) { + assert(!edge.is_root(), "invariant"); + assert(!is_array_element(edge), "invariant"); + const int offset = field_offset(edge); + const InstanceKlass* ik = field_type(edge); + while (ik != NULL) { + JavaFieldStream jfs(ik); + while (!jfs.done()) { + if (offset == jfs.offset()) { + return jfs.name(); + } + jfs.next(); + } + ik = (InstanceKlass*)ik->super(); + } + return NULL; +} + +jshort EdgeUtils::field_modifiers(const Edge& edge) { + const int offset = field_offset(edge); + const InstanceKlass* ik = field_type(edge); + + while (ik != NULL) { + JavaFieldStream jfs(ik); + while (!jfs.done()) { + if (offset == jfs.offset()) { + return jfs.access_flags().as_short(); + } + jfs.next(); + } + ik = (InstanceKlass*)ik->super(); + } + return 0; +} + +bool EdgeUtils::is_array_element(const Edge& edge) { + assert(!edge.is_root(), "invariant"); + const oop ref_owner = edge.reference_owner(); + assert(ref_owner != NULL, "invariant"); + return ref_owner->is_objArray(); +} + +static int array_offset(const Edge& edge) { + assert(!edge.is_root(), "invariant"); + const oop ref_owner = edge.reference_owner(); + assert(ref_owner != NULL, "invariant"); + const oop* reference = UnifiedOop::decode(edge.reference()); + assert(reference != NULL, "invariant"); + assert(!UnifiedOop::is_narrow(reference), "invariant"); + assert(ref_owner->is_array(), "invariant"); + const objArrayOop ref_owner_array = static_cast(ref_owner); + const int offset = (int)pointer_delta(reference, ref_owner_array->base(), heapOopSize); + assert(offset >= 0 && offset < ref_owner_array->length(), "invariant"); + return offset; +} + +int EdgeUtils::array_index(const Edge& edge) { + return is_array_element(edge) ? array_offset(edge) : 0; +} + +int EdgeUtils::array_size(const Edge& edge) { + if (is_array_element(edge)) { + const oop ref_owner = edge.reference_owner(); + assert(ref_owner != NULL, "invariant"); + assert(ref_owner->is_objArray(), "invariant"); + return ((objArrayOop)(ref_owner))->length(); + } + return 0; +} + +const Edge* EdgeUtils::root(const Edge& edge) { + const Edge* current = &edge; + const Edge* parent = current->parent(); + while (parent != NULL) { + current = parent; + parent = current->parent(); + } + assert(current != NULL, "invariant"); + return current; +} + +const Edge* EdgeUtils::ancestor(const Edge& edge, size_t distance) { + const Edge* current = &edge; + const Edge* parent = current->parent(); + size_t seek = 0; + while (parent != NULL && seek != distance) { + seek++; + current = parent; + parent = parent->parent(); + } + return current; +} diff --git a/src/share/vm/jfr/leakprofiler/chains/edgeUtils.hpp b/src/share/vm/jfr/leakprofiler/chains/edgeUtils.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f00269a5bf949458647bd27443d46025cf6caba6 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/edgeUtils.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_LEAKPROFILER_CHAINS_EDGEUTILS_HPP +#define SHARE_VM_LEAKPROFILER_CHAINS_EDGEUTILS_HPP + +#include "memory/allocation.hpp" + +class Edge; +class Symbol; + +class EdgeUtils : public AllStatic { + public: + static const size_t leak_context = 100; + static const size_t root_context = 100; + static const size_t max_ref_chain_depth = leak_context + root_context; + + static bool is_leak_edge(const Edge& edge); + static const Edge* root(const Edge& edge); + static const Edge* ancestor(const Edge& edge, size_t distance); + + static bool is_array_element(const Edge& edge); + static int array_index(const Edge& edge); + static int array_size(const Edge& edge); + + static const Symbol* field_name_symbol(const Edge& edge); + static jshort field_modifiers(const Edge& edge); +}; + +#endif // SHARE_VM_LEAKPROFILER_CHAINS_EDGEUTILS_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/objectSampleMarker.hpp b/src/share/vm/jfr/leakprofiler/chains/objectSampleMarker.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9a97a373fbcd8376815db2630bc81f1bc4650c62 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/objectSampleMarker.hpp @@ -0,0 +1,81 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_OBJECTSAMPLEMARKER_HPP +#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_OBJECTSAMPLEMARKER_HPP + +#include "memory/allocation.hpp" +#include "oops/markOop.hpp" +#include "utilities/growableArray.hpp" +// +// This class will save the original mark oop of a object sample object. +// It will then install an "identifier" mark oop to be used for +// identification purposes in the search for reference chains. +// The destructor will restore each modified oop with its original mark oop. +// +class ObjectSampleMarker : public StackObj { + private: + class ObjectSampleMarkOop : public ResourceObj { + friend class ObjectSampleMarker; + private: + oop _obj; + markOop _mark_oop; + ObjectSampleMarkOop(const oop obj, + const markOop mark_oop) : _obj(obj), + _mark_oop(mark_oop) {} + public: + ObjectSampleMarkOop() : _obj(NULL), _mark_oop(NULL) {} + }; + + GrowableArray* _store; + + public: + ObjectSampleMarker() : + _store(new GrowableArray(16)) {} + ~ObjectSampleMarker() { + assert(_store != NULL, "invariant"); + // restore the saved, original, markOop for sample objects + while (_store->is_nonempty()) { + ObjectSampleMarkOop sample_oop = _store->pop(); + sample_oop._obj->set_mark(sample_oop._mark_oop); + assert(sample_oop._obj->mark() == sample_oop._mark_oop, "invariant"); + } + } + + void mark(oop obj) { + assert(obj != NULL, "invariant"); + // save the original markOop + _store->push(ObjectSampleMarkOop(obj, obj->mark())); + // now we will "poison" the mark word of the sample object + // to the intermediate monitor INFLATING state. + // This is an "impossible" state during a safepoint, + // hence we will use it to quickly identify sample objects + // during the reachability search from gc roots. + assert(NULL == markOopDesc::INFLATING(), "invariant"); + obj->set_mark(markOopDesc::INFLATING()); + assert(NULL == obj->mark(), "invariant"); + } +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_OBJECTSAMPLEMARKER_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp b/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3f23f898f627367609995df206314368915ff32 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2019, 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 "precompiled.hpp" +#include "gc_interface/collectedHeap.hpp" +#include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/leakprofiler/chains/bfsClosure.hpp" +#include "jfr/leakprofiler/chains/bitset.hpp" +#include "jfr/leakprofiler/chains/dfsClosure.hpp" +#include "jfr/leakprofiler/chains/edge.hpp" +#include "jfr/leakprofiler/chains/edgeQueue.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/objectSampleMarker.hpp" +#include "jfr/leakprofiler/chains/rootSetClosure.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/objectSampleMarker.hpp" +#include "jfr/leakprofiler/chains/pathToGcRootsOperation.hpp" +#include "jfr/leakprofiler/checkpoint/eventEmitter.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" +#include "jfr/leakprofiler/sampling/objectSample.hpp" +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/leakprofiler/utilities/granularTimer.hpp" +#include "memory/universe.hpp" +#include "oops/markOop.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/safepoint.hpp" +#include "utilities/globalDefinitions.hpp" + +PathToGcRootsOperation::PathToGcRootsOperation(ObjectSampler* sampler, EdgeStore* edge_store, int64_t cutoff, bool emit_all) : + _sampler(sampler),_edge_store(edge_store), _cutoff_ticks(cutoff), _emit_all(emit_all) {} + +/* The EdgeQueue is backed by directly managed virtual memory. + * We will attempt to dimension an initial reservation + * in proportion to the size of the heap (represented by heap_region). + * Initial memory reservation: 5% of the heap OR at least 32 Mb + * Commit ratio: 1 : 10 (subject to allocation granularties) + */ +static size_t edge_queue_memory_reservation(const MemRegion& heap_region) { + const size_t memory_reservation_bytes = MAX2(heap_region.byte_size() / 20, 32*M); + assert(memory_reservation_bytes >= (size_t)32*M, "invariant"); + return memory_reservation_bytes; +} + +static size_t edge_queue_memory_commit_size(size_t memory_reservation_bytes) { + const size_t memory_commit_block_size_bytes = memory_reservation_bytes / 10; + assert(memory_commit_block_size_bytes >= (size_t)3*M, "invariant"); + return memory_commit_block_size_bytes; +} + +static void log_edge_queue_summary(const EdgeQueue& edge_queue) { + if (LogJFR && Verbose) tty->print_cr("EdgeQueue reserved size total: " SIZE_FORMAT " [KB]", edge_queue.reserved_size() / K); + if (LogJFR && Verbose) tty->print_cr("EdgeQueue edges total: " SIZE_FORMAT, edge_queue.top()); + if (LogJFR && Verbose) tty->print_cr("EdgeQueue liveset total: " SIZE_FORMAT " [KB]", edge_queue.live_set() / K); + if (edge_queue.reserved_size() > 0) { + if (LogJFR && Verbose) tty->print_cr("EdgeQueue commit reserve ratio: %f\n", + ((double)edge_queue.live_set() / (double)edge_queue.reserved_size())); + } +} + +void PathToGcRootsOperation::doit() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + assert(_cutoff_ticks > 0, "invariant"); + + // The bitset used for marking is dimensioned as a function of the heap size + const MemRegion heap_region = Universe::heap()->reserved_region(); + BitSet mark_bits(heap_region); + + // The edge queue is dimensioned as a fraction of the heap size + const size_t edge_queue_reservation_size = edge_queue_memory_reservation(heap_region); + EdgeQueue edge_queue(edge_queue_reservation_size, edge_queue_memory_commit_size(edge_queue_reservation_size)); + + // The initialize() routines will attempt to reserve and allocate backing storage memory. + // Failure to accommodate will render root chain processing impossible. + // As a fallback on failure, just write out the existing samples, flat, without chains. + if (!(mark_bits.initialize() && edge_queue.initialize())) { + if (LogJFR) tty->print_cr("Unable to allocate memory for root chain processing"); + return; + } + + // Save the original markWord for the potential leak objects, + // to be restored on function exit + ObjectSampleMarker marker; + if (ObjectSampleCheckpoint::mark(_sampler, marker, _emit_all) == 0) { + // no valid samples to process + return; + } + + // Necessary condition for attempting a root set iteration + Universe::heap()->ensure_parsability(false); + + BFSClosure bfs(&edge_queue, _edge_store, &mark_bits); + RootSetClosure roots(&bfs); + + GranularTimer::start(_cutoff_ticks, 1000000); + roots.process(); + if (edge_queue.is_full()) { + // Pathological case where roots don't fit in queue + // Do a depth-first search, but mark roots first + // to avoid walking sideways over roots + DFSClosure::find_leaks_from_root_set(_edge_store, &mark_bits); + } else { + bfs.process(); + } + GranularTimer::stop(); + log_edge_queue_summary(edge_queue); + + // Emit old objects including their reference chains as events + EventEmitter emitter(GranularTimer::start_time(), GranularTimer::end_time()); + emitter.write_events(_sampler, _edge_store, _emit_all); +} diff --git a/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.hpp b/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ca42d2b97ac2005a3889451c1a697dfaeb2fc63e --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019, 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 SHARE_JFR_LEAKPROFILER_CHAINS_PATHTOGCROOTSOPERATION_HPP +#define SHARE_JFR_LEAKPROFILER_CHAINS_PATHTOGCROOTSOPERATION_HPP + +#include "jfr/leakprofiler/utilities/vmOperation.hpp" + +class EdgeStore; +class ObjectSampler; + +// Safepoint operation for finding paths to gc roots +class PathToGcRootsOperation : public OldObjectVMOperation { + private: + ObjectSampler* _sampler; + EdgeStore* const _edge_store; + const int64_t _cutoff_ticks; + const bool _emit_all; + + public: + PathToGcRootsOperation(ObjectSampler* sampler, EdgeStore* edge_store, int64_t cutoff, bool emit_all); + virtual void doit(); +}; + +#endif // SHARE_JFR_LEAKPROFILER_CHAINS_PATHTOGCROOTSOPERATION_HPP diff --git a/src/share/vm/jfr/leakprofiler/chains/rootSetClosure.cpp b/src/share/vm/jfr/leakprofiler/chains/rootSetClosure.cpp new file mode 100644 index 0000000000000000000000000000000000000000..47aa1c602b815487d76bed1931e8271f0ada3f7b --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/rootSetClosure.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014, 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 "precompiled.hpp" +#include "classfile/classLoaderData.hpp" +#include "classfile/systemDictionary.hpp" +#include "jfr/leakprofiler/chains/bfsClosure.hpp" +#include "jfr/leakprofiler/chains/dfsClosure.hpp" +#include "jfr/leakprofiler/chains/edgeQueue.hpp" +#include "jfr/leakprofiler/chains/rootSetClosure.hpp" +#include "jfr/leakprofiler/utilities/saveRestore.hpp" +#include "jfr/leakprofiler/utilities/unifiedOop.hpp" +#include "memory/universe.hpp" +#include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" +#include "runtime/jniHandles.hpp" +#include "runtime/synchronizer.hpp" +#include "runtime/thread.hpp" +#include "services/management.hpp" +#include "utilities/align.hpp" + +template +RootSetClosure::RootSetClosure(Delegate* delegate) : _delegate(delegate) {} + +template +void RootSetClosure::do_oop(oop* ref) { + assert(ref != NULL, "invariant"); + // We discard unaligned root references because + // our reference tagging scheme will use + // the lowest bit in a represented reference + // to indicate the reference is narrow. + // It is mainly roots delivered via nmethods::do_oops() + // that come in unaligned. It should be ok to duck these + // since they are supposedly weak. + if (!is_aligned(ref, HeapWordSize)) { + return; + } + + assert(is_aligned(ref, HeapWordSize), "invariant"); + if (*ref != NULL) { + _delegate->do_root(ref); + } +} + +template +void RootSetClosure::do_oop(narrowOop* ref) { + assert(ref != NULL, "invariant"); + assert(is_aligned(ref, sizeof(narrowOop)), "invariant"); + const oop pointee = oopDesc::load_decode_heap_oop(ref); + if (pointee != NULL) { + _delegate->do_root(UnifiedOop::encode(ref)); + } +} + +class RootSetClosureMarkScope : public MarkingCodeBlobClosure::MarkScope {}; + +template +void RootSetClosure::process() { + RootSetClosureMarkScope mark_scope; + CLDToOopClosure cldt_closure(this); + ClassLoaderDataGraph::always_strong_cld_do(&cldt_closure); + CodeBlobToOopClosure blobs(this, false); + Threads::oops_do(this, NULL, &blobs); // XXX set CLDClosure to NULL + ObjectSynchronizer::oops_do(this); + Universe::oops_do(this); + JNIHandles::oops_do(this); + JvmtiExport::oops_do(this); + SystemDictionary::oops_do(this); + Management::oops_do(this); + StringTable::oops_do(this); +} + +template class RootSetClosure; +template class RootSetClosure; diff --git a/src/share/vm/jfr/leakprofiler/chains/rootSetClosure.hpp b/src/share/vm/jfr/leakprofiler/chains/rootSetClosure.hpp new file mode 100644 index 0000000000000000000000000000000000000000..879323c7c23e31b208b3182209bb25aa19b22671 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/chains/rootSetClosure.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_CHAINS_ROOTSETCLOSURE_HPP +#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_ROOTSETCLOSURE_HPP + +#include "memory/iterator.hpp" + +template +class RootSetClosure: public ExtendedOopClosure { // BasicOopIterateClosure + private: + Delegate* const _delegate; + public: + RootSetClosure(Delegate* delegate); + void process(); + + virtual void do_oop(oop* reference); + virtual void do_oop(narrowOop* reference); +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_ROOTSETCLOSURE_HPP diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff58db7dd4b8501d5029657ac128bdc40a649efb --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2019, 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 "precompiled.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/pathToGcRootsOperation.hpp" +#include "jfr/leakprofiler/checkpoint/eventEmitter.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" +#include "jfr/leakprofiler/sampling/objectSample.hpp" +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "memory/resourceArea.hpp" +#include "oops/markOop.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/vmThread.hpp" + +EventEmitter::EventEmitter(const JfrTicks& start_time, const JfrTicks& end_time) : + _start_time(start_time), + _end_time(end_time), + _thread(Thread::current()), + _jfr_thread_local(_thread->jfr_thread_local()), + _thread_id(_thread->jfr_thread_local()->thread_id()) {} + +EventEmitter::~EventEmitter() { + // restore / reset thread local stack trace and thread id + _jfr_thread_local->set_thread_id(_thread_id); + _jfr_thread_local->clear_cached_stack_trace(); +} + +void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_all) { + assert(sampler != NULL, "invariant"); + + ResourceMark rm; + EdgeStore edge_store; + if (cutoff_ticks <= 0) { + // no reference chains + JfrTicks time_stamp = JfrTicks::now(); + EventEmitter emitter(time_stamp, time_stamp); + emitter.write_events(sampler, &edge_store, emit_all); + return; + } + // events emitted with reference chains require a safepoint operation + PathToGcRootsOperation op(sampler, &edge_store, cutoff_ticks, emit_all); + VMThread::execute(&op); +} + +size_t EventEmitter::write_events(ObjectSampler* object_sampler, EdgeStore* edge_store, bool emit_all) { + assert(_thread == Thread::current(), "invariant"); + assert(_thread->jfr_thread_local() == _jfr_thread_local, "invariant"); + assert(object_sampler != NULL, "invariant"); + assert(edge_store != NULL, "invariant"); + + const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value(); + size_t count = 0; + + const ObjectSample* current = object_sampler->first(); + while (current != NULL) { + ObjectSample* prev = current->prev(); + if (current->is_alive_and_older_than(last_sweep)) { + write_event(current, edge_store); + ++count; + } + current = prev; + } + + if (count > 0) { + // serialize associated checkpoints and potential chains + ObjectSampleCheckpoint::write(object_sampler, edge_store, emit_all, _thread); + } + return count; +} + +static int array_size(const oop object) { + assert(object != NULL, "invariant"); + if (object->is_array()) { + return arrayOop(object)->length(); + } + return min_jint; +} + +void EventEmitter::write_event(const ObjectSample* sample, EdgeStore* edge_store) { + assert(sample != NULL, "invariant"); + assert(!sample->is_dead(), "invariant"); + assert(edge_store != NULL, "invariant"); + assert(_jfr_thread_local != NULL, "invariant"); + + const oop* object_addr = sample->object_addr(); + traceid gc_root_id = 0; + const Edge* edge = NULL; + if (SafepointSynchronize::is_at_safepoint()) { + edge = (const Edge*)(*object_addr)->mark(); + } + if (edge == NULL) { + // In order to dump out a representation of the event + // even though it was not reachable / too long to reach, + // we need to register a top level edge for this object. + edge = edge_store->put(object_addr); + } else { + gc_root_id = edge_store->gc_root_id(edge); + } + + assert(edge != NULL, "invariant"); + const traceid object_id = edge_store->get_id(edge); + assert(object_id != 0, "invariant"); + + EventOldObjectSample e(UNTIMED); + e.set_starttime(_start_time); + e.set_endtime(_end_time); + e.set_allocationTime(sample->allocation_time()); + e.set_lastKnownHeapUsage(sample->heap_used_at_last_gc()); + e.set_object(object_id); + e.set_arrayElements(array_size(edge->pointee())); + e.set_root(gc_root_id); + + // Temporarily assigning both the stack trace id and thread id + // onto the thread local data structure of the emitter thread (for the duration + // of the commit() call). This trick provides a means to override + // the event generation mechanism by injecting externally provided id's. + // At this particular location, it allows us to emit an old object event + // supplying information from where the actual sampling occurred. + _jfr_thread_local->set_cached_stack_trace_id(sample->stack_trace_id()); + assert(sample->has_thread(), "invariant"); + _jfr_thread_local->set_thread_id(sample->thread_id()); + e.commit(); +} diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.hpp b/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c72d505683e55b3f3f5e3c036cde88267f4c799d --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, 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 SHARE_JFR_LEAKPROFILER_CHECKPOINT_EVENTEMITTER_HPP +#define SHARE_JFR_LEAKPROFILER_CHECKPOINT_EVENTEMITTER_HPP + +#include "memory/allocation.hpp" +#include "jfr/utilities/jfrTime.hpp" + +typedef u8 traceid; + +class EdgeStore; +class JfrThreadLocal; +class ObjectSample; +class ObjectSampler; +class Thread; + +class EventEmitter : public CHeapObj { + friend class LeakProfiler; + friend class PathToGcRootsOperation; + private: + const JfrTicks& _start_time; + const JfrTicks& _end_time; + Thread* _thread; + JfrThreadLocal* _jfr_thread_local; + traceid _thread_id; + + EventEmitter(const JfrTicks& start_time, const JfrTicks& end_time); + ~EventEmitter(); + + void write_event(const ObjectSample* sample, EdgeStore* edge_store); + size_t write_events(ObjectSampler* sampler, EdgeStore* store, bool emit_all); + + static void emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_all); +}; + +#endif // SHARE_JFR_LEAKPROFILER_CHECKPOINT_EVENTEMITTER_HPP diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0fbccab78fceb3d3474fe5d48ae43e06427e1b7a --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2014, 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 "precompiled.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/objectSampleMarker.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp" +#include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/leakprofiler/sampling/objectSample.hpp" +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/leakprofiler/utilities/rootType.hpp" +#include "jfr/metadata/jfrSerializer.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/thread.inline.hpp" + +template +static void do_samples(ObjectSample* sample, const ObjectSample* const end, SampleProcessor& processor) { + assert(sample != NULL, "invariant"); + while (sample != end) { + processor.sample_do(sample); + sample = sample->next(); + } +} + +class RootSystemType : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer) { + const u4 nof_root_systems = OldObjectRoot::_number_of_systems; + writer.write_count(nof_root_systems); + for (u4 i = 0; i < nof_root_systems; ++i) { + writer.write_key(i); + writer.write(OldObjectRoot::system_description((OldObjectRoot::System)i)); + } + } +}; + +class RootType : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer) { + const u4 nof_root_types = OldObjectRoot::_number_of_types; + writer.write_count(nof_root_types); + for (u4 i = 0; i < nof_root_types; ++i) { + writer.write_key(i); + writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i)); + } + } +}; + +class CheckpointInstall { + private: + const JfrCheckpointBlobHandle& _cp; + public: + CheckpointInstall(const JfrCheckpointBlobHandle& cp) : _cp(cp) {} + void sample_do(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + if (!sample->is_dead()) { + sample->set_klass_checkpoint(_cp); + } + } +}; + +class CheckpointWrite { + private: + JfrCheckpointWriter& _writer; + const jlong _last_sweep; + public: + CheckpointWrite(JfrCheckpointWriter& writer, jlong last_sweep) : _writer(writer), _last_sweep(last_sweep) {} + void sample_do(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + if (sample->is_alive_and_older_than(_last_sweep)) { + if (sample->has_thread_checkpoint()) { + const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint(); + thread_cp->exclusive_write(_writer); + } + if (sample->has_klass_checkpoint()) { + const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint(); + klass_cp->exclusive_write(_writer); + } + } + } +}; + +class CheckpointStateReset { + private: + const jlong _last_sweep; + public: + CheckpointStateReset(jlong last_sweep) : _last_sweep(last_sweep) {} + void sample_do(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + if (sample->is_alive_and_older_than(_last_sweep)) { + if (sample->has_thread_checkpoint()) { + const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint(); + thread_cp->reset_write_state(); + } + if (sample->has_klass_checkpoint()) { + const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint(); + klass_cp->reset_write_state(); + } + } + } +}; + +class StackTraceWrite { + private: + JfrStackTraceRepository& _stack_trace_repo; + JfrCheckpointWriter& _writer; + int _count; + public: + StackTraceWrite(JfrStackTraceRepository& stack_trace_repo, JfrCheckpointWriter& writer) : + _stack_trace_repo(stack_trace_repo), _writer(writer), _count(0) { + JfrStacktrace_lock->lock(); + } + ~StackTraceWrite() { + assert(JfrStacktrace_lock->owned_by_self(), "invariant"); + JfrStacktrace_lock->unlock(); + } + + void sample_do(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + if (!sample->is_dead()) { + if (sample->has_stack_trace()) { + JfrTraceId::use(sample->klass(), true); + _stack_trace_repo.write(_writer, sample->stack_trace_id(), sample->stack_trace_hash()); + ++_count; + } + } + } + + int count() const { + return _count; + } +}; + +class SampleMark { + private: + ObjectSampleMarker& _marker; + jlong _last_sweep; + int _count; + public: + SampleMark(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker), + _last_sweep(last_sweep), + _count(0) {} + void sample_do(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + if (sample->is_alive_and_older_than(_last_sweep)) { + _marker.mark(sample->object()); + ++_count; + } + } + + int count() const { + return _count; + } +}; + +void ObjectSampleCheckpoint::install(JfrCheckpointWriter& writer, bool class_unload, bool type_set) { + if (!writer.has_data()) { + return; + } + + assert(writer.has_data(), "invariant"); + const JfrCheckpointBlobHandle h_cp = writer.checkpoint_blob(); + CheckpointInstall install(h_cp); + + // Class unload implies a safepoint. + // Not class unload implies the object sampler is locked, because it was claimed exclusively earlier. + // Therefore: direct access the object sampler instance is safe. + ObjectSampler* const object_sampler = ObjectSampler::sampler(); + assert(object_sampler != NULL, "invariant"); + + ObjectSample* const last = const_cast(object_sampler->last()); + const ObjectSample* const last_resolved = object_sampler->last_resolved(); + + // install only to new samples since last resolved checkpoint + if (last != last_resolved) { + do_samples(last, last_resolved, install); + if (class_unload) { + return; + } + if (type_set) { + object_sampler->set_last_resolved(last); + } + } +} + +void ObjectSampleCheckpoint::write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) { + assert(sampler != NULL, "invariant"); + assert(edge_store != NULL, "invariant"); + assert(thread != NULL, "invariant"); + + static bool types_registered = false; + if (!types_registered) { + JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType()); + JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType()); + types_registered = true; + } + + const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value(); + ObjectSample* const last = const_cast(sampler->last()); + { + JfrCheckpointWriter writer(false, false, thread); + CheckpointWrite checkpoint_write(writer, last_sweep); + do_samples(last, NULL, checkpoint_write); + } + + CheckpointStateReset state_reset(last_sweep); + do_samples(last, NULL, state_reset); + + if (!edge_store->is_empty()) { + // java object and chain representations + JfrCheckpointWriter writer(false, true, thread); + ObjectSampleWriter osw(writer, edge_store); + edge_store->iterate(osw); + } +} + +int ObjectSampleCheckpoint::mark(ObjectSampler* object_sampler, ObjectSampleMarker& marker, bool emit_all) { + assert(object_sampler != NULL, "invariant"); + ObjectSample* const last = const_cast(object_sampler->last()); + if (last == NULL) { + return 0; + } + const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value(); + SampleMark mark(marker, last_sweep); + do_samples(last, NULL, mark); + return mark.count(); +} + +WriteObjectSampleStacktrace::WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo) : + _sampler(sampler), _stack_trace_repo(repo) {} + +bool WriteObjectSampleStacktrace::process() { + assert(LeakProfiler::is_running(), "invariant"); + assert(_sampler != NULL, "invariant"); + + ObjectSample* const last = const_cast(_sampler->last()); + const ObjectSample* const last_resolved = _sampler->last_resolved(); + if (last == last_resolved) { + return true; + } + + JfrCheckpointWriter writer(false, true, Thread::current()); + const JfrCheckpointContext ctx = writer.context(); + + writer.write_type(TYPE_STACKTRACE); + const jlong count_offset = writer.reserve(sizeof(u4)); + + int count = 0; + { + StackTraceWrite stack_trace_write(_stack_trace_repo, writer); // JfrStacktrace_lock + do_samples(last, last_resolved, stack_trace_write); + count = stack_trace_write.count(); + } + if (count == 0) { + writer.set_context(ctx); + return true; + } + assert(count > 0, "invariant"); + writer.write_count((u4)count, count_offset); + JfrStackTraceRepository::write_metadata(writer); + + // install the stacktrace checkpoint information to the candidates + ObjectSampleCheckpoint::install(writer, false, false); + return true; +} diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp new file mode 100644 index 0000000000000000000000000000000000000000..86725a4977b50052e79fbbf83a66962080d39732 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP +#define SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP + +#include "memory/allocation.hpp" + +class EdgeStore; +class JfrCheckpointWriter; +class JfrStackTraceRepository; +class ObjectSampleMarker; +class ObjectSampler; + +class ObjectSampleCheckpoint : AllStatic { + public: + static void install(JfrCheckpointWriter& writer, bool class_unload, bool type_set); + static void write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread); + static int mark(ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all); +}; + +class WriteObjectSampleStacktrace : public StackObj { + private: + ObjectSampler* const _sampler; + JfrStackTraceRepository& _stack_trace_repo; + public: + WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo); + bool process(); +}; + +#endif // SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb397283ec37612497ced692d4c835572976a08d --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp @@ -0,0 +1,216 @@ +/* + * 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 "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleDescription.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/thread.hpp" +#include "utilities/ostream.hpp" + +static Symbol* symbol_size = NULL; + +ObjectDescriptionBuilder::ObjectDescriptionBuilder() { + reset(); +} + +void ObjectDescriptionBuilder::write_int(jint value) { + char buf[20]; + jio_snprintf(buf, sizeof(buf), "%d", value); + write_text(buf); +} + +void ObjectDescriptionBuilder::write_text(const char* text) { + if (_index == sizeof(_buffer) - 2) { + return; + } + while (*text != '\0' && _index < sizeof(_buffer) - 2) { + _buffer[_index] = *text; + _index++; + text++; + } + assert(_index < sizeof(_buffer) - 1, "index should not exceed buffer size"); + // add ellipsis if we reached end + if (_index == sizeof(_buffer) - 2) { + _buffer[_index-3] = '.'; + _buffer[_index-2] = '.'; + _buffer[_index-1] = '.'; + } + // terminate string + _buffer[_index] = '\0'; +} + +void ObjectDescriptionBuilder::reset() { + _index = 0; + _buffer[0] = '\0'; +} + +void ObjectDescriptionBuilder::print_description(outputStream* out) { + out->print("%s", (const char*)_buffer); +} + +const char* ObjectDescriptionBuilder::description() { + if (_buffer[0] == '\0') { + return NULL; + } + const size_t len = strlen(_buffer); + char* copy = NEW_RESOURCE_ARRAY(char, len + 1); + assert(copy != NULL, "invariant"); + strncpy(copy, _buffer, len + 1); + return copy; +} + +ObjectSampleDescription::ObjectSampleDescription(oop object) : + _object(object) { +} + +void ObjectSampleDescription::ensure_initialized() { + if (symbol_size == NULL) { + symbol_size = SymbolTable::new_permanent_symbol("size", Thread::current()); + } +} + +void ObjectSampleDescription::print_description(outputStream* out) { + write_object_to_buffer(); + _description.print_description(out); +} + +const char* ObjectSampleDescription::description() { + write_object_to_buffer(); + return _description.description(); +} + +void ObjectSampleDescription::write_text(const char* text) { + _description.write_text(text); +} + +void ObjectSampleDescription::write_int(jint value) { + _description.write_int(value); +} + +void ObjectSampleDescription::write_object_to_buffer() { + ensure_initialized(); + _description.reset(); + write_object_details(); +} + +void ObjectSampleDescription::write_object_details() { + Klass* klass = _object->klass(); + Symbol* class_name = klass->name(); + jint size; + + if (_object->is_a(SystemDictionary::Class_klass())) { + write_class_name(); + return; + } + + if (_object->is_a(SystemDictionary::Thread_klass())) { + write_thread_name(); + return; + } + + if (_object->is_a(SystemDictionary::ThreadGroup_klass())) { + write_thread_group_name(); + return; + } + + if (read_int_size(&size)) { + write_size(size); + return; + } +} + +void ObjectSampleDescription::write_class_name() { + assert(_object->is_a(SystemDictionary::Class_klass()), "invariant"); + Klass* const k = java_lang_Class::as_Klass(_object); + if (k == NULL) { + // might represent a primitive + const Klass* const ak = java_lang_Class::array_klass(_object); + // If ak is NULL, this is most likely a mirror associated with a + // jvmti redefine/retransform scratch klass. We can't get any additional + // information from it. + if (ak != NULL) { + write_text(type2name(java_lang_Class::primitive_type(_object))); + } + return; + } + + if (k->oop_is_instance()) { + const InstanceKlass* ik = InstanceKlass::cast(k); + if (ik->is_anonymous()) { + return; + } + assert(!ik->is_anonymous(), "invariant"); + const Symbol* name = ik->name(); + if (name != NULL) { + write_text("Class Name: "); + write_text(name->as_klass_external_name()); + } + } +} + +void ObjectSampleDescription::write_thread_group_name() { + assert(_object->is_a(SystemDictionary::ThreadGroup_klass()), "invariant"); + typeArrayOop tg_name = java_lang_ThreadGroup::name(_object); + if (tg_name != NULL) { + write_text("Thread Group: "); + write_text(UNICODE::as_utf8((jchar*) tg_name->base(T_CHAR), tg_name->length())); + } +} + +void ObjectSampleDescription::write_thread_name() { + assert(_object->is_a(SystemDictionary::Thread_klass()), "invariant"); + oop name = java_lang_Thread::name(_object); + if (name != NULL) { + char* p = java_lang_String::as_utf8_string(name); + if (p != NULL) { + write_text("Thread Name: "); + write_text(p); + } + } +} + +void ObjectSampleDescription::write_size(jint size) { + if (size >= 0) { + write_text("Size: "); + write_int(size); + } +} + +bool ObjectSampleDescription::read_int_size(jint* result_size) { + fieldDescriptor fd; + Klass* klass = _object->klass(); + if (klass->oop_is_instance()) { + InstanceKlass* ik = InstanceKlass::cast(klass); + if (ik->find_field(symbol_size, vmSymbols::int_signature(), false, &fd) != NULL) { + jint size = _object->int_field(fd.offset()); + *result_size = size; + return true; + } + } + return false; +} diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleDescription.hpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleDescription.hpp new file mode 100644 index 0000000000000000000000000000000000000000..691e2fe551e05ea042ad6c4ff4ec15c58255f21d --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleDescription.hpp @@ -0,0 +1,74 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLEDESCRIPTION_HPP +#define SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLEDESCRIPTION_HPP + +#define OBJECT_SAMPLE_DESCRIPTION_BUFFER_SIZE 100 + +#include "memory/allocation.hpp" + +class outputStream; + +class ObjectDescriptionBuilder : public StackObj { +private: + char _buffer[OBJECT_SAMPLE_DESCRIPTION_BUFFER_SIZE]; + size_t _index; + +public: + ObjectDescriptionBuilder(); + + void write_text(const char* text); + void write_int(jint value); + void reset(); + + void print_description(outputStream* out); + const char* description(); +}; + +class ObjectSampleDescription : public StackObj { +private: + ObjectDescriptionBuilder _description; + oop _object; + + void write_text(const char* text); + void write_int(jint value); + + void write_object_details(); + void write_size(jint size); + void write_thread_name(); + void write_thread_group_name(); + void write_class_name(); + void write_object_to_buffer(); + bool is_class(Symbol* s1, const char* s2); + void ensure_initialized(); + bool read_int_size(jint* result); + +public: + ObjectSampleDescription(oop object); + void print_description(outputStream* out); + const char* description(); +}; + +#endif // SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLEDESCRIPTION_HPP diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59553a95371cb47284d40771218d432665a4d94f --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp @@ -0,0 +1,602 @@ +/* + * Copyright (c) 2014, 2019, 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 "precompiled.hpp" +#include "jfrfiles/jfrTypes.hpp" +#include "jfr/leakprofiler/chains/edge.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/edgeUtils.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleDescription.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp" +#include "jfr/leakprofiler/checkpoint/rootResolver.hpp" +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/leakprofiler/utilities/rootType.hpp" +#include "jfr/leakprofiler/utilities/unifiedOop.hpp" +#include "jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp" +#include "jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp" +#include "oops/oop.inline.hpp" +#include "oops/symbol.hpp" +#include "utilities/growableArray.hpp" + +template +class ObjectSampleAuxInfo : public ResourceObj { + public: + Data _data; + traceid _id; + ObjectSampleAuxInfo() : _data(), _id(0) {} +}; + +class ObjectSampleArrayData { + public: + int _array_size; + int _array_index; + ObjectSampleArrayData() : _array_size(0), _array_index(0) {} +}; + +class ObjectSampleFieldInfo : public ResourceObj { + public: + const Symbol* _field_name_symbol; + jshort _field_modifiers; + ObjectSampleFieldInfo() : _field_name_symbol(NULL), _field_modifiers(0) {} +}; + +class ObjectSampleRootDescriptionData { + public: + const Edge* _root_edge; + const char* _description; + OldObjectRoot::System _system; + OldObjectRoot::Type _type; + ObjectSampleRootDescriptionData() : _root_edge(NULL), + _description(NULL), + _system(OldObjectRoot::_system_undetermined), + _type(OldObjectRoot::_type_undetermined) {} +}; + +class OldObjectSampleData { + public: + oop _object; + traceid _reference_id; +}; + +class ReferenceData { + public: + traceid _field_info_id; + traceid _array_info_id; + traceid _old_object_sample_id; + size_t _skip; +}; + +static int initial_storage_size = 16; + +template +class SampleSet : public ResourceObj { + private: + GrowableArray* _storage; + public: + SampleSet() : _storage(NULL) {} + + traceid store(Data data) { + assert(data != NULL, "invariant"); + if (_storage == NULL) { + _storage = new GrowableArray(initial_storage_size); + } + assert(_storage != NULL, "invariant"); + assert(_storage->find(data) == -1, "invariant"); + _storage->append(data); + return data->_id; + } + + size_t size() const { + return _storage != NULL ? (size_t)_storage->length() : 0; + } + + template + void iterate(Functor& functor) { + if (_storage != NULL) { + for (int i = 0; i < _storage->length(); ++i) { + functor(_storage->at(i)); + } + } + } + + const GrowableArray& storage() const { + return *_storage; + } +}; + +typedef ObjectSampleAuxInfo ObjectSampleArrayInfo; +typedef ObjectSampleAuxInfo ObjectSampleRootDescriptionInfo; +typedef ObjectSampleAuxInfo OldObjectSampleInfo; +typedef ObjectSampleAuxInfo ReferenceInfo; + +class FieldTable : public ResourceObj { + template class, + typename, + size_t> + friend class HashTableHost; + typedef HashTableHost FieldInfoTable; + public: + typedef FieldInfoTable::HashEntry FieldInfoEntry; + + private: + static traceid _field_id_counter; + FieldInfoTable* _table; + + void assign_id(FieldInfoEntry* entry) { + assert(entry != NULL, "invariant"); + entry->set_id(++_field_id_counter); + } + + bool equals(const ObjectSampleFieldInfo* query, uintptr_t hash, const FieldInfoEntry* entry) { + assert(hash == entry->hash(), "invariant"); + assert(query != NULL, "invariant"); + const ObjectSampleFieldInfo* stored = entry->literal(); + assert(stored != NULL, "invariant"); + assert(((Symbol*)stored->_field_name_symbol)->identity_hash() == ((Symbol*)query->_field_name_symbol)->identity_hash(), "invariant"); + return stored->_field_modifiers == query->_field_modifiers; + } + + public: + FieldTable() : _table(new FieldInfoTable(this)) {} + ~FieldTable() { + assert(_table != NULL, "invariant"); + delete _table; + } + + traceid store(const ObjectSampleFieldInfo* field_info) { + assert(field_info != NULL, "invariant"); + const FieldInfoEntry& entry =_table->lookup_put(field_info, + ((Symbol*)field_info->_field_name_symbol)->identity_hash()); + return entry.id(); + } + + size_t size() const { + return _table->cardinality(); + } + + template + void iterate(T& functor) const { + _table->iterate_entry(functor); + } +}; + +traceid FieldTable::_field_id_counter = 0; + +typedef SampleSet SampleInfo; +typedef SampleSet RefInfo; +typedef SampleSet ArrayInfo; +typedef SampleSet RootDescriptionInfo; + +static SampleInfo* sample_infos = NULL; +static RefInfo* ref_infos = NULL; +static ArrayInfo* array_infos = NULL; +static FieldTable* field_infos = NULL; +static RootDescriptionInfo* root_infos = NULL; + +int __write_sample_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* si) { + assert(writer != NULL, "invariant"); + assert(si != NULL, "invariant"); + const OldObjectSampleInfo* const oosi = (const OldObjectSampleInfo*)si; + oop object = oosi->_data._object; + assert(object != NULL, "invariant"); + writer->write(oosi->_id); + writer->write((u8)(const HeapWord*)object); + writer->write(const_cast(object->klass())); + ObjectSampleDescription od(object); + writer->write(od.description()); + writer->write(oosi->_data._reference_id); + return 1; +} + +typedef JfrArtifactWriterImplHost SampleWriterImpl; +typedef JfrArtifactWriterHost SampleWriter; + +static void write_sample_infos(JfrCheckpointWriter& writer) { + if (sample_infos != NULL) { + SampleWriter sw(&writer, NULL, false); + sample_infos->iterate(sw); + } +} + +int __write_reference_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ri) { + assert(writer != NULL, "invariant"); + assert(ri != NULL, "invariant"); + const ReferenceInfo* const ref_info = (const ReferenceInfo*)ri; + writer->write(ref_info->_id); + writer->write(ref_info->_data._array_info_id); + writer->write(ref_info->_data._field_info_id); + writer->write(ref_info->_data._old_object_sample_id); + writer->write((s4)ref_info->_data._skip); + return 1; +} + +typedef JfrArtifactWriterImplHost ReferenceWriterImpl; +typedef JfrArtifactWriterHost ReferenceWriter; + +static void write_reference_infos(JfrCheckpointWriter& writer) { + if (ref_infos != NULL) { + ReferenceWriter rw(&writer, NULL, false); + ref_infos->iterate(rw); + } +} + +int __write_array_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ai) { + assert(writer != NULL, "invariant"); + assert(ai != NULL, "invariant"); + const ObjectSampleArrayInfo* const osai = (const ObjectSampleArrayInfo*)ai; + writer->write(osai->_id); + writer->write(osai->_data._array_size); + writer->write(osai->_data._array_index); + return 1; +} + +static traceid get_array_info_id(const Edge& edge, traceid id) { + if (edge.is_root() || !EdgeUtils::is_array_element(edge)) { + return 0; + } + if (array_infos == NULL) { + array_infos = new ArrayInfo(); + } + assert(array_infos != NULL, "invariant"); + + ObjectSampleArrayInfo* const osai = new ObjectSampleArrayInfo(); + assert(osai != NULL, "invariant"); + osai->_id = id; + osai->_data._array_size = EdgeUtils::array_size(edge); + osai->_data._array_index = EdgeUtils::array_index(edge); + return array_infos->store(osai); +} + +typedef JfrArtifactWriterImplHost ArrayWriterImpl; +typedef JfrArtifactWriterHost ArrayWriter; + +static void write_array_infos(JfrCheckpointWriter& writer) { + if (array_infos != NULL) { + ArrayWriter aw(&writer, NULL, false); + array_infos->iterate(aw); + } +} + +int __write_field_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* fi) { + assert(writer != NULL, "invariant"); + assert(fi != NULL, "invariant"); + const FieldTable::FieldInfoEntry* field_info_entry = (const FieldTable::FieldInfoEntry*)fi; + writer->write(field_info_entry->id()); + const ObjectSampleFieldInfo* const osfi = field_info_entry->literal(); + writer->write(osfi->_field_name_symbol->as_C_string()); + writer->write(osfi->_field_modifiers); + return 1; +} + +static traceid get_field_info_id(const Edge& edge) { + if (edge.is_root()) { + return 0; + } + + assert(!EdgeUtils::is_array_element(edge), "invariant"); + const Symbol* const field_name_symbol = EdgeUtils::field_name_symbol(edge); + if (field_name_symbol == NULL) { + return 0; + } + + if (field_infos == NULL) { + field_infos = new FieldTable(); + } + assert(field_infos != NULL, "invariant"); + + ObjectSampleFieldInfo* const osfi = new ObjectSampleFieldInfo(); + assert(osfi != NULL, "invariant"); + osfi->_field_name_symbol = field_name_symbol; + osfi->_field_modifiers = EdgeUtils::field_modifiers(edge); + return field_infos->store(osfi); +} + +typedef JfrArtifactWriterImplHost FieldWriterImpl; +typedef JfrArtifactWriterHost FieldWriter; + +static void write_field_infos(JfrCheckpointWriter& writer) { + if (field_infos != NULL) { + FieldWriter fw(&writer, NULL, false); + field_infos->iterate(fw); + } +} + +static const char* description(const ObjectSampleRootDescriptionInfo* osdi) { + assert(osdi != NULL, "invariant"); + + if (osdi->_data._description == NULL) { + return NULL; + } + + ObjectDescriptionBuilder description; + if (osdi->_data._system == OldObjectRoot::_threads) { + description.write_text("Thread Name: "); + } + description.write_text(osdi->_data._description); + return description.description(); +} + +int __write_root_description_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* di) { + assert(writer != NULL, "invariant"); + assert(di != NULL, "invariant"); + const ObjectSampleRootDescriptionInfo* const osdi = (const ObjectSampleRootDescriptionInfo*)di; + writer->write(osdi->_id); + writer->write(description(osdi)); + writer->write(osdi->_data._system); + writer->write(osdi->_data._type); + return 1; +} + +static traceid get_gc_root_description_info_id(const Edge& edge, traceid id) { + assert(edge.is_root(), "invariant"); + if (EdgeUtils::is_leak_edge(edge)) { + return 0; + } + + if (root_infos == NULL) { + root_infos = new RootDescriptionInfo(); + } + assert(root_infos != NULL, "invariant"); + ObjectSampleRootDescriptionInfo* const oodi = new ObjectSampleRootDescriptionInfo(); + oodi->_id = id; + oodi->_data._root_edge = &edge; + return root_infos->store(oodi); +} + +typedef JfrArtifactWriterImplHost RootDescriptionWriterImpl; +typedef JfrArtifactWriterHost RootDescriptionWriter; + + +int _edge_reference_compare_(uintptr_t lhs, uintptr_t rhs) { + return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; +} + +int _root_desc_compare_(const ObjectSampleRootDescriptionInfo*const & lhs, const ObjectSampleRootDescriptionInfo* const& rhs) { + const uintptr_t lhs_ref = (uintptr_t)lhs->_data._root_edge->reference(); + const uintptr_t rhs_ref = (uintptr_t)rhs->_data._root_edge->reference(); + return _edge_reference_compare_(lhs_ref, rhs_ref); +} + +static int find_sorted(const RootCallbackInfo& callback_info, + const GrowableArray* arr, + int length, + bool& found) { + assert(arr != NULL, "invariant"); + assert(length >= 0, "invariant"); + assert(length <= arr->length(), "invariant"); + + found = false; + int min = 0; + int max = length; + while (max >= min) { + const int mid = (int)(((uint)max + min) / 2); + int diff = _edge_reference_compare_((uintptr_t)callback_info._high, + (uintptr_t)arr->at(mid)->_data._root_edge->reference()); + if (diff > 0) { + min = mid + 1; + } else if (diff < 0) { + max = mid - 1; + } else { + found = true; + return mid; + } + } + return min; +} + +class RootResolutionSet : public ResourceObj, public RootCallback { + private: + GrowableArray* _unresolved_roots; + + const uintptr_t high() const { + return (uintptr_t)_unresolved_roots->top()->_data._root_edge->reference(); + } + + const uintptr_t low() const { + return (uintptr_t)_unresolved_roots->first()->_data._root_edge->reference(); + } + + bool in_set_address_range(const RootCallbackInfo& callback_info) const { + assert(callback_info._low == NULL, "invariant"); + const uintptr_t addr = (uintptr_t)callback_info._high; + return low() <= addr && high() >= addr; + } + + int compare_to_range(const RootCallbackInfo& callback_info) const { + assert(callback_info._high != NULL, "invariant"); + assert(callback_info._low != NULL, "invariant"); + + for (int i = 0; i < _unresolved_roots->length(); ++i) { + const uintptr_t ref_addr = (uintptr_t)_unresolved_roots->at(i)->_data._root_edge->reference(); + if ((uintptr_t)callback_info._low <= ref_addr && (uintptr_t)callback_info._high >= ref_addr) { + return i; + } + } + return -1; + } + + int exact(const RootCallbackInfo& callback_info) const { + assert(callback_info._high != NULL, "invariant"); + assert(in_set_address_range(callback_info), "invariant"); + + bool found; + const int idx = find_sorted(callback_info, _unresolved_roots, _unresolved_roots->length(), found); + return found ? idx : -1; + } + + bool resolve_root(const RootCallbackInfo& callback_info, int idx) const { + assert(idx >= 0, "invariant"); + assert(idx < _unresolved_roots->length(), "invariant"); + + ObjectSampleRootDescriptionInfo* const desc = + const_cast(_unresolved_roots->at(idx)); + assert(desc != NULL, "invariant"); + assert((uintptr_t)callback_info._high == (uintptr_t)desc->_data._root_edge->reference(), "invariant"); + + desc->_data._system = callback_info._system; + desc->_data._type = callback_info._type; + + if (callback_info._system == OldObjectRoot::_threads) { + const JavaThread* jt = (const JavaThread*)callback_info._context; + assert(jt != NULL, "invariant"); + desc->_data._description = jt->name(); + } + + _unresolved_roots->remove_at(idx); + return _unresolved_roots->is_empty(); + } + + public: + RootResolutionSet(RootDescriptionInfo* info) : _unresolved_roots(NULL) { + assert(info != NULL, "invariant"); + // construct a sorted copy + const GrowableArray& info_storage = info->storage(); + const int length = info_storage.length(); + _unresolved_roots = new GrowableArray(length); + assert(_unresolved_roots != NULL, "invariant"); + + for (int i = 0; i < length; ++i) { + _unresolved_roots->insert_sorted<_root_desc_compare_>(info_storage.at(i)); + } + } + + bool process(const RootCallbackInfo& callback_info) { + if (NULL == callback_info._low) { + if (in_set_address_range(callback_info)) { + const int idx = exact(callback_info); + return idx == -1 ? false : resolve_root(callback_info, idx); + } + return false; + } + assert(callback_info._low != NULL, "invariant"); + const int idx = compare_to_range(callback_info); + return idx == -1 ? false : resolve_root(callback_info, idx); + } + + int entries() const { + return _unresolved_roots->length(); + } + + const void* at(int idx) const { + assert(idx >= 0, "invariant"); + assert(idx < _unresolved_roots->length(), "invariant"); + return _unresolved_roots->at(idx)->_data._root_edge->reference(); + } +}; + +static void write_root_descriptors(JfrCheckpointWriter& writer) { + if (root_infos != NULL) { + // resolve roots + RootResolutionSet rrs(root_infos); + RootResolver::resolve(rrs); + // write roots + RootDescriptionWriter rw(&writer, NULL, false); + root_infos->iterate(rw); + } +} + +static void add_old_object_sample_info(const StoredEdge* current, traceid id) { + assert(current != NULL, "invariant"); + if (sample_infos == NULL) { + sample_infos = new SampleInfo(); + } + assert(sample_infos != NULL, "invariant"); + OldObjectSampleInfo* const oosi = new OldObjectSampleInfo(); + assert(oosi != NULL, "invariant"); + oosi->_id = id; + oosi->_data._object = current->pointee(); + oosi->_data._reference_id = current->parent() == NULL ? (traceid)0 : id; + sample_infos->store(oosi); +} + +static void add_reference_info(const StoredEdge* current, traceid id, traceid parent_id) { + assert(current != NULL, "invariant"); + if (ref_infos == NULL) { + ref_infos = new RefInfo(); + } + + assert(ref_infos != NULL, "invariant"); + ReferenceInfo* const ri = new ReferenceInfo(); + assert(ri != NULL, "invariant"); + + ri->_id = id; + ri->_data._array_info_id = !current->is_skip_edge() ? get_array_info_id(*current, id) : 0; + ri->_data._field_info_id = ri->_data._array_info_id == 0 && !current->is_skip_edge() ? get_field_info_id(*current) : (traceid)0; + ri->_data._old_object_sample_id = parent_id; + ri->_data._skip = current->skip_length(); + ref_infos->store(ri); +} + +static bool is_gc_root(const StoredEdge* current) { + assert(current != NULL, "invariant"); + return current->parent() == NULL && current->gc_root_id() != 0; +} + +static traceid add_gc_root_info(const StoredEdge* root, traceid id) { + assert(root != NULL, "invariant"); + assert(is_gc_root(root), "invariant"); + return get_gc_root_description_info_id(*root, id); +} + +void ObjectSampleWriter::write(const StoredEdge* edge) { + assert(edge != NULL, "invariant"); + const traceid id = _store->get_id(edge); + add_old_object_sample_info(edge, id); + const StoredEdge* const parent = edge->parent(); + if (parent != NULL) { + add_reference_info(edge, id, _store->get_id(parent)); + } else { + if (is_gc_root(edge)) { + assert(edge->gc_root_id() == id, "invariant"); + add_gc_root_info(edge, id); + } + } +} + +ObjectSampleWriter::ObjectSampleWriter(JfrCheckpointWriter& writer, EdgeStore* store) : + _writer(writer), + _store(store) { + assert(store != NULL, "invariant"); + assert(!store->is_empty(), "invariant"); + sample_infos = NULL; + ref_infos = NULL; + array_infos = NULL; + field_infos = NULL; + root_infos = NULL; +} + +ObjectSampleWriter::~ObjectSampleWriter() { + write_sample_infos(_writer); + write_reference_infos(_writer); + write_array_infos(_writer); + write_field_infos(_writer); + write_root_descriptors(_writer); +} + +bool ObjectSampleWriter::operator()(StoredEdge& e) { + write(&e); + return true; +} diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.hpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8922f275f1587dd45a0ad827ac5e5bc073276678 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLEWRITER_HPP +#define SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLEWRITER_HPP + +#include "memory/allocation.hpp" + +class Edge; +class EdgeStore; +class JfrCheckpointWriter; +class StoredEdge; + +class ObjectSampleWriter : public StackObj { + private: + JfrCheckpointWriter& _writer; + EdgeStore* const _store; + void write(const StoredEdge* edge); + public: + ObjectSampleWriter(JfrCheckpointWriter& writer, EdgeStore* store); + ~ObjectSampleWriter(); + bool operator()(StoredEdge& edge); +}; + +#endif // SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLEWRITER_HPP diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90f61bea3d43181d4c9f2e4f480aa6f6532603aa --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2014, 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 "precompiled.hpp" +//#include "classfile/stringTable.hpp" +//#include "gc_interface/strongRootsScope.hpp" +#include "jfr/leakprofiler/utilities/unifiedOop.hpp" +#include "jfr/leakprofiler/checkpoint/rootResolver.hpp" +#include "memory/iterator.hpp" +#include "oops/klass.hpp" +#include "oops/markOop.hpp" +#include "oops/oop.hpp" +#include "prims/jvmtiThreadState.hpp" +#include "prims/privilegedStack.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/vframe_hp.hpp" +#include "services/management.hpp" +#include "utilities/growableArray.hpp" + +class ReferenceLocateClosure : public OopClosure { + protected: + RootCallback& _callback; + RootCallbackInfo _info; + bool _complete; + + void do_oop_shared(const void* ref); + + public: + ReferenceLocateClosure(RootCallback& callback, + OldObjectRoot::System system, + OldObjectRoot::Type type, + const void* context) : _callback(callback), + _info(), + _complete(false) { + _info._high = NULL; + _info._low = NULL; + _info._system = system; + _info._type = type; + _info._context = context; + } + + virtual void do_oop(oop* ref); + virtual void do_oop(narrowOop* ref); + + bool complete() const { + return _complete; + } +}; + +void ReferenceLocateClosure::do_oop_shared(const void* ref) { + assert(ref != NULL, "invariant"); + if (!_complete) { + _info._high = ref; + _complete = _callback.process(_info); + } +} + +void ReferenceLocateClosure::do_oop(oop* ref) { + do_oop_shared(ref); +} + +void ReferenceLocateClosure::do_oop(narrowOop* ref) { + do_oop_shared(ref); +} + +class ReferenceToRootClosure : public StackObj { + private: + RootCallback& _callback; + RootCallbackInfo _info; + bool _complete; + + bool do_cldg_roots(); + bool do_object_synchronizer_roots(); + bool do_universe_roots(); + bool do_jni_handle_roots(); + bool do_jvmti_roots(); + bool do_system_dictionary_roots(); + bool do_management_roots(); + bool do_string_table_roots(); +// bool do_aot_loader_roots(); + + bool do_roots(); + + public: + ReferenceToRootClosure(RootCallback& callback) : _callback(callback), + _info(), + _complete(false) { + _info._high = NULL; + _info._low = NULL; + _info._context = NULL; + _info._system = OldObjectRoot::_system_undetermined; + _info._type = OldObjectRoot::_type_undetermined; + + assert_locked_or_safepoint(Threads_lock); + do_roots(); + } + + bool complete() const { + return _complete; + } +}; + +bool ReferenceToRootClosure::do_cldg_roots() { + assert(!complete(), "invariant"); + ReferenceLocateClosure rlc(_callback, OldObjectRoot::_class_loader_data, OldObjectRoot::_type_undetermined, NULL); + CLDToOopClosure cldt_closure(&rlc); + ClassLoaderDataGraph::always_strong_cld_do(&cldt_closure); + return rlc.complete(); +} + +bool ReferenceToRootClosure::do_object_synchronizer_roots() { + assert(!complete(), "invariant"); + ReferenceLocateClosure rlc(_callback, OldObjectRoot::_object_synchronizer, OldObjectRoot::_type_undetermined, NULL); + ObjectSynchronizer::oops_do(&rlc); + return rlc.complete(); +} + +bool ReferenceToRootClosure::do_universe_roots() { + assert(!complete(), "invariant"); + ReferenceLocateClosure rlc(_callback, OldObjectRoot::_universe, OldObjectRoot::_type_undetermined, NULL); + Universe::oops_do(&rlc); + return rlc.complete(); +} + +bool ReferenceToRootClosure::do_jni_handle_roots() { + assert(!complete(), "invariant"); + ReferenceLocateClosure rlc(_callback, OldObjectRoot::_global_jni_handles, OldObjectRoot::_global_jni_handle, NULL); + JNIHandles::oops_do(&rlc); + return rlc.complete(); +} + +bool ReferenceToRootClosure::do_jvmti_roots() { + assert(!complete(), "invariant"); + ReferenceLocateClosure rlc(_callback, OldObjectRoot::_jvmti, OldObjectRoot::_global_jni_handle, NULL); + JvmtiExport::oops_do(&rlc); + return rlc.complete(); +} + +bool ReferenceToRootClosure::do_system_dictionary_roots() { + assert(!complete(), "invariant"); + ReferenceLocateClosure rlc(_callback, OldObjectRoot::_system_dictionary, OldObjectRoot::_type_undetermined, NULL); + SystemDictionary::oops_do(&rlc); + return rlc.complete(); +} + +bool ReferenceToRootClosure::do_management_roots() { + assert(!complete(), "invariant"); + ReferenceLocateClosure rlc(_callback, OldObjectRoot::_management, OldObjectRoot::_type_undetermined, NULL); + Management::oops_do(&rlc); + return rlc.complete(); +} + +bool ReferenceToRootClosure::do_string_table_roots() { + assert(!complete(), "invariant"); + ReferenceLocateClosure rlc(_callback, OldObjectRoot::_string_table, OldObjectRoot::_type_undetermined, NULL); + StringTable::oops_do(&rlc); + return rlc.complete(); +} + +//bool ReferenceToRootClosure::do_aot_loader_roots() { +// assert(!complete(), "invariant"); +// ReferenceLocateClosure rcl(_callback, OldObjectRoot::_aot, OldObjectRoot::_type_undetermined, NULL); +// AOTLoader::oops_do(&rcl); +// return rcl.complete(); +//} + +bool ReferenceToRootClosure::do_roots() { + assert(!complete(), "invariant"); + assert(OldObjectRoot::_system_undetermined == _info._system, "invariant"); + assert(OldObjectRoot::_type_undetermined == _info._type, "invariant"); + + if (do_cldg_roots()) { + _complete = true; + return true; + } + + if (do_object_synchronizer_roots()) { + _complete = true; + return true; + } + + if (do_universe_roots()) { + _complete = true; + return true; + } + + if (do_jni_handle_roots()) { + _complete = true; + return true; + } + + if (do_jvmti_roots()) { + _complete = true; + return true; + } + + if (do_system_dictionary_roots()) { + _complete = true; + return true; + } + + if (do_management_roots()) { + _complete = true; + return true; + } + + if (do_string_table_roots()) { + _complete = true; + return true; + } + +// if (do_aot_loader_roots()) { +// _complete = true; +// return true; +// } + + return false; +} + +class ReferenceToThreadRootClosure : public StackObj { + private: + RootCallback& _callback; + bool _complete; + + bool do_java_threads_oops(JavaThread* jt); + bool do_thread_roots(JavaThread* jt); + bool do_thread_stack_fast(JavaThread* jt); + bool do_thread_stack_detailed(JavaThread* jt); + bool do_thread_jni_handles(JavaThread* jt); + bool do_thread_handle_area(JavaThread* jt); + + public: + ReferenceToThreadRootClosure(RootCallback& callback) :_callback(callback), _complete(false) { + assert_locked_or_safepoint(Threads_lock); + for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { + if (do_thread_roots(thread)) { + return; + } + } + } + + bool complete() const { + return _complete; + } +}; + +bool ReferenceToThreadRootClosure::do_thread_handle_area(JavaThread* jt) { + assert(jt != NULL, "invariant"); + assert(!complete(), "invariant"); + ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_handle_area, jt); + jt->handle_area()->oops_do(&rcl); + return rcl.complete(); +} + +bool ReferenceToThreadRootClosure::do_thread_jni_handles(JavaThread* jt) { + assert(jt != NULL, "invariant"); + assert(!complete(), "invariant"); + + ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_local_jni_handle, jt); + jt->active_handles()->oops_do(&rcl); + return rcl.complete(); +} + +bool ReferenceToThreadRootClosure::do_thread_stack_fast(JavaThread* jt) { + assert(jt != NULL, "invariant"); + assert(!complete(), "invariant"); + + if (_callback.entries() == 0) { + _complete = true; + return true; + } + + RootCallbackInfo info; + info._high = NULL; + info._low = NULL; + info._context = jt; + info._system = OldObjectRoot::_threads; + info._type = OldObjectRoot::_stack_variable; + + for (int i = 0; i < _callback.entries(); ++i) { + const address adr = (address)_callback.at(i); + if (jt->is_in_usable_stack(adr)) { + info._high = adr; + _complete = _callback.process(info); + if (_complete) { + return true; + } + } + } + assert(!complete(), "invariant"); + return false; +} + +bool ReferenceToThreadRootClosure::do_thread_stack_detailed(JavaThread* jt) { + assert(jt != NULL, "invariant"); + assert(!complete(), "invariant"); + + ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_stack_variable, jt); + + if (jt->has_last_Java_frame()) { + PrivilegedElement* const pelem = jt->privileged_stack_top(); + if (pelem != NULL) { + pelem->oops_do(&rcl); + if (rcl.complete()) { + return true; + } + } + + // traverse the registered growable array gc_array + // can't do this as it is not reachable from outside + + // Traverse the monitor chunks + MonitorChunk* chunk = jt->monitor_chunks(); + for (; chunk != NULL; chunk = chunk->next()) { + chunk->oops_do(&rcl); + } + + if (rcl.complete()) { + return true; + } + + // Traverse the execution stack + for (StackFrameStream fst(jt); !fst.is_done(); fst.next()) { + // XXX set CLDClosure to NULL + fst.current()->oops_do(&rcl, NULL, NULL, fst.register_map()); + } + + } // last java frame + + if (rcl.complete()) { + return true; + } + + GrowableArray* const list = jt->deferred_locals(); + if (list != NULL) { + for (int i = 0; i < list->length(); i++) { + list->at(i)->oops_do(&rcl); + } + } + + if (rcl.complete()) { + return true; + } + + // Traverse instance variables at the end since the GC may be moving things + // around using this function + /* + * // can't reach these oop* from the outside + f->do_oop((oop*) &_threadObj); + f->do_oop((oop*) &_vm_result); + f->do_oop((oop*) &_exception_oop); + f->do_oop((oop*) &_pending_async_exception); + */ + + JvmtiThreadState* const jvmti_thread_state = jt->jvmti_thread_state(); + if (jvmti_thread_state != NULL) { + jvmti_thread_state->oops_do(&rcl); + } + + return rcl.complete(); +} + +bool ReferenceToThreadRootClosure::do_java_threads_oops(JavaThread* jt) { + assert(jt != NULL, "invariant"); + assert(!complete(), "invariant"); + + ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_global_jni_handle, jt); + jt->oops_do(&rcl, NULL, NULL); + return rcl.complete(); +} + +bool ReferenceToThreadRootClosure::do_thread_roots(JavaThread* jt) { + assert(jt != NULL, "invariant"); + + if (do_thread_stack_fast(jt)) { + _complete = true; + return true; + } + + if (do_thread_jni_handles(jt)) { + _complete = true; + return true; + } + + if (do_thread_handle_area(jt)) { + _complete = true; + return true; + } + + if (do_thread_stack_detailed(jt)) { + _complete = true; + return true; + } + + return false; +} + +class RootResolverMarkScope : public MarkingCodeBlobClosure::MarkScope { +}; + +void RootResolver::resolve(RootCallback& callback) { + + // Need to clear cld claim bit before starting + ClassLoaderDataGraph::clear_claimed_marks(); + RootResolverMarkScope mark_scope; + + // thread local roots + ReferenceToThreadRootClosure rtrc(callback); + if (rtrc.complete()) { + return; + } + // system global roots + ReferenceToRootClosure rrc(callback); +} diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.hpp b/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.hpp new file mode 100644 index 0000000000000000000000000000000000000000..36af1376ecc95443ec3b9bcec3efa7773f389a52 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_CHECKPOINT_ROOTRESOLVER_HPP +#define SHARE_VM_JFR_LEAKPROFILER_CHECKPOINT_ROOTRESOLVER_HPP + +#include "jfr/leakprofiler/utilities/rootType.hpp" +#include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" + +struct RootCallbackInfo { + const void* _high; + const void* _low; + const void* _context; + OldObjectRoot::System _system; + OldObjectRoot::Type _type; +}; + +class RootCallback { + public: + virtual bool process(const RootCallbackInfo& info) = 0; + virtual int entries() const = 0; + virtual const void* at(int idx) const = 0; +}; + +class RootResolver : public AllStatic { + public: + static void resolve(RootCallback& callback); +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_CHECKPOINT_ROOTRESOLVER_HPP diff --git a/src/share/vm/jfr/leakprofiler/leakProfiler.cpp b/src/share/vm/jfr/leakprofiler/leakProfiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46ad79aa6eabf33de879d4ef10d84f87503b1992 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/leakProfiler.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014, 2019, 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 "precompiled.hpp" +#include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/leakprofiler/startOperation.hpp" +#include "jfr/leakprofiler/stopOperation.hpp" +#include "jfr/leakprofiler/checkpoint/eventEmitter.hpp" +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "memory/iterator.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/vmThread.hpp" + +bool LeakProfiler::is_running() { + return ObjectSampler::is_created(); +} + +bool LeakProfiler::start(int sample_count) { + if (is_running()) { + return true; + } + + // Allows user to disable leak profiler on command line by setting queue size to zero. + if (sample_count == 0) { + return false; + } + + assert(!is_running(), "invariant"); + assert(sample_count > 0, "invariant"); + + // schedule the safepoint operation for installing the object sampler + StartOperation op(sample_count); + VMThread::execute(&op); + + if (!is_running()) { + if (LogJFR && Verbose) tty->print_cr("Object sampling could not be started because the sampler could not be allocated"); + return false; + } + assert(is_running(), "invariant"); + if (LogJFR && Verbose) tty->print_cr("Object sampling started"); + return true; +} + +bool LeakProfiler::stop() { + if (!is_running()) { + return false; + } + + // schedule the safepoint operation for uninstalling and destroying the object sampler + StopOperation op; + VMThread::execute(&op); + + assert(!is_running(), "invariant"); + if (LogJFR && Verbose) tty->print_cr("Object sampling stopped"); + return true; +} + +void LeakProfiler::emit_events(int64_t cutoff_ticks, bool emit_all) { + if (!is_running()) { + return; + } + // exclusive access to object sampler instance + ObjectSampler* const sampler = ObjectSampler::acquire(); + assert(sampler != NULL, "invariant"); + EventEmitter::emit(sampler, cutoff_ticks, emit_all); + ObjectSampler::release(); +} + +void LeakProfiler::oops_do(BoolObjectClosure* is_alive, OopClosure* f) { + assert(SafepointSynchronize::is_at_safepoint(), + "Leak Profiler::oops_do(...) may only be called during safepoint"); + if (is_running()) { + ObjectSampler::oops_do(is_alive, f); + } +} + +void LeakProfiler::sample(HeapWord* object, size_t size, JavaThread* thread) { + assert(is_running(), "invariant"); + assert(thread != NULL, "invariant"); + assert(thread->thread_state() == _thread_in_vm, "invariant"); + + // exclude compiler threads and code sweeper thread + if (thread->is_hidden_from_external_view()) { + return; + } + + ObjectSampler::sample(object, size, thread); +} diff --git a/src/share/vm/jfr/leakprofiler/leakProfiler.hpp b/src/share/vm/jfr/leakprofiler/leakProfiler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..00fb01ab5cf4125088890e5f29d62b134ac6c32e --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/leakProfiler.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_LEAKPROFILER_HPP +#define SHARE_VM_JFR_LEAKPROFILER_LEAKPROFILER_HPP + +#include "memory/allocation.hpp" + +class BoolObjectClosure; +class OopClosure; + +class LeakProfiler : public AllStatic { + public: + static bool start(int sample_count); + static bool stop(); + static bool is_running(); + + static void emit_events(int64_t cutoff_ticks, bool emit_all); + static void sample(HeapWord* object, size_t size, JavaThread* thread); + + // Called by GC + static void oops_do(BoolObjectClosure* is_alive, OopClosure* f); +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_LEAKPROFILER_HPP diff --git a/src/share/vm/jfr/leakprofiler/sampling/objectSample.hpp b/src/share/vm/jfr/leakprofiler/sampling/objectSample.hpp new file mode 100644 index 0000000000000000000000000000000000000000..48baea52dd4196d04a6ef387c686d2b24f7c24f3 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/sampling/objectSample.hpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_SAMPLING_OBJECTSAMPLE_HPP +#define SHARE_VM_JFR_LEAKPROFILER_SAMPLING_OBJECTSAMPLE_HPP + +#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp" +#include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "memory/allocation.hpp" +#include "oops/oop.hpp" +#include "utilities/ticks.hpp" +/* + * Handle for diagnosing Java memory leaks. + * + * The class tracks the time the object was + * allocated, the thread and the stack trace. + */ +class ObjectSample : public JfrCHeapObj { + friend class ObjectSampler; + friend class SampleList; + private: + ObjectSample* _next; + ObjectSample* _previous; + JfrCheckpointBlobHandle _thread_cp; + JfrCheckpointBlobHandle _klass_cp; + oop _object; + Ticks _allocation_time; + traceid _stack_trace_id; + traceid _thread_id; + int _index; + size_t _span; + size_t _allocated; + size_t _heap_used_at_last_gc; + unsigned int _stack_trace_hash; + bool _dead; + + void set_dead() { + _dead = true; + } + + void release_references() { + if (_thread_cp.valid()) { + _thread_cp.~JfrCheckpointBlobHandle(); + } + if (_klass_cp.valid()) { + _klass_cp.~JfrCheckpointBlobHandle(); + } + } + + void reset() { + set_stack_trace_id(0); + set_stack_trace_hash(0), + release_references(); + _dead = false; + } + + public: + ObjectSample() : _next(NULL), + _previous(NULL), + _thread_cp(), + _klass_cp(), + _object(NULL), + _allocation_time(), + _stack_trace_id(0), + _thread_id(0), + _index(0), + _span(0), + _allocated(0), + _heap_used_at_last_gc(0), + _stack_trace_hash(0), + _dead(false) {} + + ObjectSample* next() const { + return _next; + } + + void set_next(ObjectSample* next) { + _next = next; + } + + ObjectSample* prev() const { + return _previous; + } + + void set_prev(ObjectSample* prev) { + _previous = prev; + } + + bool is_dead() const { + return _dead; + } + + const oop object() const { + return _object; + } + + const oop* object_addr() const { + return &_object; + } + + void set_object(oop object) { + _object = object; + } + + const Klass* klass() const { + assert(_object != NULL, "invariant"); + return _object->klass(); + } + + int index() const { + return _index; + } + + void set_index(int index) { + _index = index; + } + + size_t span() const { + return _span; + } + + void set_span(size_t span) { + _span = span; + } + + void add_span(size_t span) { + _span += span; + } + + size_t allocated() const { + return _allocated; + } + + void set_allocated(size_t size) { + _allocated = size; + } + + const Ticks& allocation_time() const { + return _allocation_time; + } + + const void set_allocation_time(const JfrTicks& time) { + _allocation_time = Ticks(time.value()); + } + + void set_heap_used_at_last_gc(size_t heap_used) { + _heap_used_at_last_gc = heap_used; + } + + size_t heap_used_at_last_gc() const { + return _heap_used_at_last_gc; + } + + bool has_stack_trace() const { + return stack_trace_id() != 0; + } + + traceid stack_trace_id() const { + return _stack_trace_id; + } + + void set_stack_trace_id(traceid id) { + _stack_trace_id = id; + } + + unsigned int stack_trace_hash() const { + return _stack_trace_hash; + } + + void set_stack_trace_hash(unsigned int hash) { + _stack_trace_hash = hash; + } + + bool has_thread() const { + return _thread_id != 0; + } + + traceid thread_id() const { + return _thread_id; + } + + void set_thread_id(traceid id) { + _thread_id = id; + } + + bool is_alive_and_older_than(jlong time_stamp) const { + return !is_dead() && (JfrTime::is_ft_enabled() ? + _allocation_time.ft_value() : _allocation_time.value()) < time_stamp; + } + + const JfrCheckpointBlobHandle& thread_checkpoint() const { + return _thread_cp; + } + + bool has_thread_checkpoint() const { + return _thread_cp.valid(); + } + + // JfrCheckpointBlobHandle assignment operator + // maintains proper reference counting + void set_thread_checkpoint(const JfrCheckpointBlobHandle& ref) { + if (_thread_cp != ref) { + _thread_cp = ref; + } + } + + const JfrCheckpointBlobHandle& klass_checkpoint() const { + return _klass_cp; + } + + bool has_klass_checkpoint() const { + return _klass_cp.valid(); + } + + void set_klass_checkpoint(const JfrCheckpointBlobHandle& ref) { + if (_klass_cp != ref) { + if (_klass_cp.valid()) { + _klass_cp->set_next(ref); + return; + } + _klass_cp = ref; + } + } +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_SAMPLING_OBJECTSAMPLE_HPP diff --git a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..27792721594dc2ce76670048cbeff1d4266151fc --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp @@ -0,0 +1,285 @@ +/* + * 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 "precompiled.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/leakprofiler/sampling/objectSample.hpp" +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/leakprofiler/sampling/sampleList.hpp" +#include "jfr/leakprofiler/sampling/samplePriorityQueue.hpp" +#include "jfr/recorder/jfrEventSetting.inline.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" +#include "jfr/support/jfrThreadLocal.hpp" +#include "jfr/utilities/jfrTryLock.hpp" +#include "memory/universe.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" +#include "runtime/orderAccess.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/thread.hpp" + +static ObjectSampler* _instance = NULL; + +static ObjectSampler& instance() { + assert(_instance != NULL, "invariant"); + return *_instance; +} + +ObjectSampler::ObjectSampler(size_t size) : + _priority_queue(new SamplePriorityQueue(size)), + _list(new SampleList(size)), + _last_sweep(JfrTicks::now()), + _total_allocated(0), + _threshold(0), + _size(size), + _dead_samples(false) {} + +ObjectSampler::~ObjectSampler() { + delete _priority_queue; + _priority_queue = NULL; + delete _list; + _list = NULL; +} + +bool ObjectSampler::create(size_t size) { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + assert(_instance == NULL, "invariant"); + _instance = new ObjectSampler(size); + return _instance != NULL; +} + +bool ObjectSampler::is_created() { + return _instance != NULL; +} + +ObjectSampler* ObjectSampler::sampler() { + assert(is_created(), "invariant"); + return _instance; +} + +void ObjectSampler::destroy() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + if (_instance != NULL) { + ObjectSampler* const sampler = _instance; + _instance = NULL; + delete sampler; + } +} + +static volatile int _lock = 0; + +ObjectSampler* ObjectSampler::acquire() { + assert(is_created(), "invariant"); + while (Atomic::cmpxchg(1, &_lock, 0) == 1) {} + return _instance; +} + +void ObjectSampler::release() { + assert(is_created(), "invariant"); + OrderAccess::fence(); + _lock = 0; +} + +static traceid get_thread_id(JavaThread* thread) { + assert(thread != NULL, "invariant"); + if (thread->threadObj() == NULL) { + return 0; + } + const JfrThreadLocal* const tl = thread->jfr_thread_local(); + assert(tl != NULL, "invariant"); + if (!tl->has_thread_checkpoint()) { + JfrCheckpointManager::create_thread_checkpoint(thread); + } + assert(tl->has_thread_checkpoint(), "invariant"); + return tl->thread_id(); +} + +// Populates the thread local stack frames, but does not add them +// to the stacktrace repository (...yet, see stacktrace_id() below) +// +void ObjectSampler::fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* thread) { + assert(stacktrace != NULL, "invariant"); + assert(thread != NULL, "invariant"); + if (JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) { + JfrStackTraceRepository::fill_stacktrace_for(thread, stacktrace, 0); + } +} + +// We were successful in acquiring the try lock and have been selected for adding a sample. +// Go ahead with installing our previously taken stacktrace into the stacktrace repository. +// +traceid ObjectSampler::stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread) { + assert(stacktrace != NULL, "invariant"); + assert(stacktrace->hash() != 0, "invariant"); + const traceid stacktrace_id = JfrStackTraceRepository::add(stacktrace, thread); + thread->jfr_thread_local()->set_cached_stack_trace_id(stacktrace_id, stacktrace->hash()); + return stacktrace_id; +} + +void ObjectSampler::sample(HeapWord* obj, size_t allocated, JavaThread* thread) { + assert(thread != NULL, "invariant"); + assert(is_created(), "invariant"); + + const traceid thread_id = get_thread_id(thread); + if (thread_id == 0) { + return; + } + const JfrThreadLocal* const tl = thread->jfr_thread_local(); + JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth()); + fill_stacktrace(&stacktrace, thread); + + // try enter critical section + JfrTryLock tryLock(&_lock); + if (!tryLock.has_lock()) { + if (LogJFR && Verbose) tty->print_cr("Skipping old object sample due to lock contention"); + return; + } + + instance().add(obj, allocated, thread_id, &stacktrace, thread); +} + +void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread) { + assert(stacktrace != NULL, "invariant"); + assert(thread_id != 0, "invariant"); + assert(thread != NULL, "invariant"); + assert(thread->jfr_thread_local()->has_thread_checkpoint(), "invariant"); + + if (_dead_samples) { + scavenge(); + assert(!_dead_samples, "invariant"); + } + + _total_allocated += allocated; + const size_t span = _total_allocated - _priority_queue->total(); + ObjectSample* sample; + if ((size_t)_priority_queue->count() == _size) { + assert(_list->count() == _size, "invariant"); + const ObjectSample* peek = _priority_queue->peek(); + if (peek->span() > span) { + // quick reject, will not fit + return; + } + sample = _list->reuse(_priority_queue->pop()); + } else { + sample = _list->get(); + } + + assert(sample != NULL, "invariant"); + sample->set_thread_id(thread_id); + sample->set_thread_checkpoint(thread->jfr_thread_local()->thread_checkpoint()); + + const unsigned int stacktrace_hash = stacktrace->hash(); + if (stacktrace_hash != 0) { + sample->set_stack_trace_id(stacktrace_id(stacktrace, thread)); + sample->set_stack_trace_hash(stacktrace_hash); + } + + sample->set_span(allocated); + sample->set_object((oop)obj); + sample->set_allocated(allocated); + sample->set_allocation_time(JfrTicks::now()); + sample->set_heap_used_at_last_gc(Universe::get_heap_used_at_last_gc()); + _priority_queue->push(sample); +} + +void ObjectSampler::scavenge() { + ObjectSample* current = _list->last(); + while (current != NULL) { + ObjectSample* next = current->next(); + if (current->is_dead()) { + remove_dead(current); + } + current = next; + } + _dead_samples = false; +} + +void ObjectSampler::remove_dead(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + assert(sample->is_dead(), "invariant"); + ObjectSample* const previous = sample->prev(); + // push span on to previous + if (previous != NULL) { + _priority_queue->remove(previous); + previous->add_span(sample->span()); + _priority_queue->push(previous); + } + _priority_queue->remove(sample); + _list->release(sample); +} + +void ObjectSampler::oops_do(BoolObjectClosure* is_alive, OopClosure* f) { + assert(is_created(), "invariant"); + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + ObjectSampler& sampler = instance(); + ObjectSample* current = sampler._list->last(); + while (current != NULL) { + ObjectSample* next = current->next(); + if (!current->is_dead()) { + if (is_alive->do_object_b(current->object())) { + // The weakly referenced object is alive, update pointer + f->do_oop(const_cast(current->object_addr())); + } else { + current->set_dead(); + sampler._dead_samples = true; + } + } + current = next; + } + sampler._last_sweep = JfrTicks::now(); +} + +const ObjectSample* ObjectSampler::last() const { + return _list->last(); +} + +const ObjectSample* ObjectSampler::first() const { + return _list->first(); +} + +const ObjectSample* ObjectSampler::last_resolved() const { + return _list->last_resolved(); +} + +void ObjectSampler::set_last_resolved(const ObjectSample* sample) { + _list->set_last_resolved(sample); +} + +int ObjectSampler::item_count() const { + return _priority_queue->count(); +} + +const ObjectSample* ObjectSampler::item_at(int index) const { + return _priority_queue->item_at(index); +} + +ObjectSample* ObjectSampler::item_at(int index) { + return const_cast( + const_cast(this)->item_at(index) + ); +} + +const JfrTicks& ObjectSampler::last_sweep() const { + return _last_sweep; +} diff --git a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.hpp b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dceb1350aa06996009d9f50fcf54545097345fb3 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_LEAKPROFILER_SAMPLING_OBJECTSAMPLER_HPP +#define SHARE_VM_LEAKPROFILER_SAMPLING_OBJECTSAMPLER_HPP + +#include "memory/allocation.hpp" +#include "jfr/utilities/jfrTime.hpp" + +typedef u8 traceid; + +class BoolObjectClosure; +class JfrStackTrace; +class OopClosure; +class ObjectSample; +class ObjectSampler; +class SampleList; +class SamplePriorityQueue; +class Thread; + +// Class reponsible for holding samples and +// making sure the samples are evenly distributed as +// new entries are added and removed. +class ObjectSampler : public CHeapObj { + friend class EventEmitter; + friend class JfrRecorderService; + friend class LeakProfiler; + friend class StartOperation; + friend class StopOperation; + friend class ObjectSampleCheckpoint; + friend class WriteObjectSampleStacktrace; + private: + SamplePriorityQueue* _priority_queue; + SampleList* _list; + JfrTicks _last_sweep; + size_t _total_allocated; + size_t _threshold; + size_t _size; + bool _dead_samples; + + // Lifecycle + explicit ObjectSampler(size_t size); + ~ObjectSampler(); + static bool create(size_t size); + static bool is_created(); + static ObjectSampler* sampler(); + static void destroy(); + + // For operations that require exclusive access (non-safepoint) + static ObjectSampler* acquire(); + static void release(); + + // Stacktrace + static void fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* thread); + traceid stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread); + + // Sampling + static void sample(HeapWord* object, size_t size, JavaThread* thread); + void add(HeapWord* object, size_t size, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread); + void scavenge(); + void remove_dead(ObjectSample* sample); + + // Called by GC + static void oops_do(BoolObjectClosure* is_alive, OopClosure* f); + + const ObjectSample* item_at(int index) const; + ObjectSample* item_at(int index); + int item_count() const; + const ObjectSample* first() const; + const ObjectSample* last() const; + const ObjectSample* last_resolved() const; + void set_last_resolved(const ObjectSample* sample); + const JfrTicks& last_sweep() const; +}; + +#endif // SHARE_VM_LEAKPROFILER_SAMPLING_OBJECTSAMPLER_HPP diff --git a/src/share/vm/jfr/leakprofiler/sampling/sampleList.cpp b/src/share/vm/jfr/leakprofiler/sampling/sampleList.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ef3f5615b4ea6ef6c5f97acc6cc63110752e9e4 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/sampling/sampleList.cpp @@ -0,0 +1,146 @@ +/* + * 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 "precompiled.hpp" +#include "jfr/leakprofiler/sampling/objectSample.hpp" +#include "jfr/leakprofiler/sampling/sampleList.hpp" +#include "oops/oop.inline.hpp" + +SampleList::SampleList(size_t limit, size_t cache_size) : + _free_list(), + _in_use_list(), + _last_resolved(NULL), + _limit(limit), + _cache_size(cache_size), + _allocated(0) { +} + +SampleList::~SampleList() { + deallocate_samples(_free_list); + deallocate_samples(_in_use_list); +} + +ObjectSample* SampleList::last() const { + return _in_use_list.head(); +} + +ObjectSample* SampleList::first() const { + return _in_use_list.tail(); +} + +const ObjectSample* SampleList::last_resolved() const { + return _last_resolved; +} + +void SampleList::set_last_resolved(const ObjectSample* sample) { + assert(last() == sample, "invariant"); + _last_resolved = sample; +} + +void SampleList::link(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + _in_use_list.prepend(sample); +} + +void SampleList::unlink(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + if (_last_resolved == sample) { + _last_resolved = sample->next(); + } + reset(_in_use_list.remove(sample)); +} + +ObjectSample* SampleList::reuse(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + unlink(sample); + link(sample); + return sample; +} + +void SampleList::populate_cache() { + if (_free_list.count() < _cache_size) { + const size_t cache_delta = _cache_size - _free_list.count(); + for (size_t i = 0; i < cache_delta; ++i) { + ObjectSample* sample = newSample(); + if (sample != NULL) { + _free_list.append(sample); + } + } + } +} + +ObjectSample* SampleList::newSample() const { + if (_limit == _allocated) { + return NULL; + } + ++_allocated; + return new ObjectSample(); +} + +ObjectSample* SampleList::get() { + ObjectSample* sample = _free_list.head(); + if (sample != NULL) { + link(_free_list.remove(sample)); + } else { + sample = newSample(); + if (sample != NULL) { + _in_use_list.prepend(sample); + } + } + if (_cache_size > 0 && sample != NULL) { + populate_cache(); + } + return sample; +} + +void SampleList::release(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + unlink(sample); + _free_list.append(sample); +} + +void SampleList::deallocate_samples(List& list) { + if (list.count() > 0) { + ObjectSample* sample = list.head(); + while (sample != NULL) { + list.remove(sample); + delete sample; + sample = list.head(); + } + } + assert(list.count() == 0, "invariant"); +} + +void SampleList::reset(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + sample->reset(); +} + +bool SampleList::is_full() const { + return _in_use_list.count() == _limit; +} + +size_t SampleList::count() const { + return _in_use_list.count(); +} diff --git a/src/share/vm/jfr/leakprofiler/sampling/sampleList.hpp b/src/share/vm/jfr/leakprofiler/sampling/sampleList.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6754ed3ba9fbfc5bd7b9259900b6f4f7c961ac53 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/sampling/sampleList.hpp @@ -0,0 +1,64 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_JFR_LEAKPROFILER_SAMPLING_SAMPLELIST_HPP +#define SHARE_VM_JFR_LEAKPROFILER_SAMPLING_SAMPLELIST_HPP +#include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrDoublyLinkedList.hpp" + +class ObjectSample; + +class SampleList : public JfrCHeapObj { + typedef JfrDoublyLinkedList List; + private: + List _free_list; + List _in_use_list; + const ObjectSample* _last_resolved; + mutable size_t _allocated; + const size_t _limit; + const size_t _cache_size; + + void populate_cache(); + ObjectSample* newSample() const; + void link(ObjectSample* sample); + void unlink(ObjectSample* sample); + void deallocate_samples(List& list); + void reset(ObjectSample* sample); + + public: + SampleList(size_t limit, size_t cache_size = 0); + ~SampleList(); + + void set_last_resolved(const ObjectSample* sample); + ObjectSample* get(); + ObjectSample* last() const; + ObjectSample* first() const; + void release(ObjectSample* sample); + const ObjectSample* last_resolved() const; + ObjectSample* reuse(ObjectSample* sample); + bool is_full() const; + size_t count() const; +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_SAMPLING_SAMPLELIST_HPP diff --git a/src/share/vm/jfr/leakprofiler/sampling/samplePriorityQueue.cpp b/src/share/vm/jfr/leakprofiler/sampling/samplePriorityQueue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46bc748d371f3440cb38832a8ba49178410ff363 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/sampling/samplePriorityQueue.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014, 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 "precompiled.hpp" +#include "jfr/leakprofiler/sampling/objectSample.hpp" +#include "jfr/leakprofiler/sampling/samplePriorityQueue.hpp" +#include "memory/allocation.inline.hpp" +#include "oops/oop.inline.hpp" + +SamplePriorityQueue::SamplePriorityQueue(size_t size) : + _allocated_size(size), + _count(0), + _total(0) { + _items = NEW_C_HEAP_ARRAY(ObjectSample*, size, mtTracing); + memset(_items, 0, sizeof(ObjectSample*) * size); +} + +SamplePriorityQueue::~SamplePriorityQueue() { + FREE_C_HEAP_ARRAY(ObjectSample*, _items, mtTracing); + _items = NULL; +} + +void SamplePriorityQueue::push(ObjectSample* item) { + assert(item != NULL, "invariant"); + assert(_items[_count] == NULL, "invariant"); + + _items[_count] = item; + _items[_count]->set_index(_count); + _count++; + moveUp(_count - 1); + _total += item->span(); +} + +size_t SamplePriorityQueue::total() const { + return _total; +} + +ObjectSample* SamplePriorityQueue::pop() { + if (_count == 0) { + return NULL; + } + + ObjectSample* const s = _items[0]; + assert(s != NULL, "invariant"); + swap(0, _count - 1); + _count--; + assert(s == _items[_count], "invariant"); + // clear from heap + _items[_count] = NULL; + moveDown(0); + _total -= s->span(); + return s; +} + +void SamplePriorityQueue::swap(int i, int j) { + ObjectSample* tmp = _items[i]; + _items[i] = _items[j]; + _items[j] = tmp; + _items[i]->set_index(i); + _items[j]->set_index(j); +} + +static int left(int i) { + return 2 * i + 1; +} + +static int right(int i) { + return 2 * i + 2; +} + +static int parent(int i) { + return (i - 1) / 2; +} + +void SamplePriorityQueue::moveDown(int i) { + do { + int j = -1; + int r = right(i); + if (r < _count && _items[r]->span() < _items[i]->span()) { + int l = left(i); + if (_items[l]->span() < _items[r]->span()) { + j = l; + } else { + j = r; + } + } else { + int l = left(i); + if (l < _count && _items[l]->span() < _items[i]->span()) { + j = l; + } + } + if (j >= 0) { + swap(i, j); + } + i = j; + } while (i >= 0); + +} + +void SamplePriorityQueue::moveUp(int i) { + int p = parent(i); + while (i > 0 && _items[i]->span() < _items[p]->span()) { + swap(i,p); + i = p; + p = parent(i); + } +} + +void SamplePriorityQueue::remove(ObjectSample* s) { + assert(s != NULL, "invariant"); + const size_t realSpan = s->span(); + s->set_span(0); + moveUp(s->index()); + s->set_span(realSpan); + pop(); +} + +int SamplePriorityQueue::count() const { + return _count; +} + +const ObjectSample* SamplePriorityQueue::peek() const { + return _count == 0 ? NULL : _items[0]; +} + +ObjectSample* SamplePriorityQueue::item_at(int index) { + assert(index >= 0 && index < _count, "out of range"); + return _items[index]; +} diff --git a/src/share/vm/jfr/leakprofiler/sampling/samplePriorityQueue.hpp b/src/share/vm/jfr/leakprofiler/sampling/samplePriorityQueue.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1189e6d73101c9b1685d6fa4ccb5bd7286b9733c --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/sampling/samplePriorityQueue.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_JFR_LEAKPROFILER_SAMPLING_SAMPLEPRIORITYQUEUE_HPP +#define SHARE_VM_JFR_LEAKPROFILER_SAMPLING_SAMPLEPRIORITYQUEUE_HPP + +#include "memory/allocation.hpp" + +class ObjectSample; + +// Priority queue that keeps object samples ordered +// by the amount of allocation they span. +class SamplePriorityQueue : public CHeapObj { + private: + ObjectSample** _items; + size_t _allocated_size; + int _count; + size_t _total; + + void swap(int i, int j); + void moveDown(int index); + void moveUp(int index); + + public: + SamplePriorityQueue(size_t size); + ~SamplePriorityQueue(); + + void push(ObjectSample* sample); + ObjectSample* pop(); + const ObjectSample* peek() const; + void remove(ObjectSample* sample); + ObjectSample* item_at(int index); + size_t total() const; + int count() const; +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_SAMPLING_SAMPLEPRIORITYQUEUE_HPP diff --git a/src/share/vm/jfr/leakprofiler/startOperation.hpp b/src/share/vm/jfr/leakprofiler/startOperation.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fcfdbdcf87662e1a2475395ff61e01361e5a58b2 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/startOperation.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_LEAKPROFILER_STARTOPERATION_HPP +#define SHARE_VM_LEAKPROFILER_STARTOPERATION_HPP + +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/leakprofiler/utilities/vmOperation.hpp" + +// Safepoint operation for creating and starting the leak profiler object sampler +class StartOperation : public OldObjectVMOperation { + private: + int _sample_count; + public: + StartOperation(int sample_count) : _sample_count(sample_count) {} + virtual void doit() { + ObjectSampler::create(_sample_count); + } +}; + +#endif // SHARE_VM_LEAKPROFILER_STARTOPERATION_HPP diff --git a/src/share/vm/jfr/leakprofiler/stopOperation.hpp b/src/share/vm/jfr/leakprofiler/stopOperation.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6cba4ff8a1927c419f8ae512f75911ebf12c15a2 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/stopOperation.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_LEAKPROFILER_STOPOPERATION_HPP +#define SHARE_VM_LEAKPROFILER_STOPOPERATION_HPP + +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/leakprofiler/utilities/vmOperation.hpp" + +// Safepoint operation for stopping and destroying the leak profiler object sampler +class StopOperation : public OldObjectVMOperation { + public: + virtual void doit() { + ObjectSampler::destroy(); + } +}; + +#endif // SHARE_VM_LEAKPROFILER_STOPOPERATION_HPP diff --git a/src/share/vm/jfr/leakprofiler/utilities/granularTimer.cpp b/src/share/vm/jfr/leakprofiler/utilities/granularTimer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7de7061666722da22b32f317f402b212f97fbdae --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/utilities/granularTimer.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, 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 "precompiled.hpp" +#include "jfr/leakprofiler/utilities/granularTimer.hpp" + +long GranularTimer::_granularity = 0; +long GranularTimer::_counter = 0; +JfrTicks GranularTimer::_finish_time_ticks = 0; +JfrTicks GranularTimer::_start_time_ticks = 0; +bool GranularTimer::_finished = false; + +void GranularTimer::start(jlong duration_ticks, long granularity) { + assert(granularity > 0, "granularity must be at least 1"); + _granularity = granularity; + _counter = granularity; + _start_time_ticks = JfrTicks::now(); + const jlong end_time_ticks = _start_time_ticks.value() + duration_ticks; + _finish_time_ticks = end_time_ticks < 0 ? JfrTicks(max_jlong) : JfrTicks(end_time_ticks); + _finished = _finish_time_ticks == _start_time_ticks; + assert(_finish_time_ticks.value() >= 0, "invariant"); + assert(_finish_time_ticks >= _start_time_ticks, "invariant"); +} +void GranularTimer::stop() { + if (!_finished) { + _finish_time_ticks = JfrTicks::now(); + } +} +const JfrTicks& GranularTimer::start_time() { + return _start_time_ticks; +} + +const JfrTicks& GranularTimer::end_time() { + return _finish_time_ticks; +} + +bool GranularTimer::is_finished() { + assert(_granularity != 0, "GranularTimer::is_finished must be called after GranularTimer::start"); + if (--_counter == 0) { + if (_finished) { + // reset so we decrease to zero at next iteration + _counter = 1; + return true; + } + if (JfrTicks::now() > _finish_time_ticks) { + _finished = true; + _counter = 1; + return true; + } + assert(_counter == 0, "invariant"); + _counter = _granularity; // restore next batch + } + return false; +} diff --git a/src/share/vm/jfr/leakprofiler/utilities/granularTimer.hpp b/src/share/vm/jfr/leakprofiler/utilities/granularTimer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..aa046da4ec93c857eb2e47b013ea7074328d3d39 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/utilities/granularTimer.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, 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 SHARE_VM_LEAKPROFILER_UTILITIES_GRANULARTIMER_HPP +#define SHARE_VM_LEAKPROFILER_UTILITIES_GRANULARTIMER_HPP + +#include "jfr/utilities/jfrTime.hpp" +#include "memory/allocation.hpp" + +class GranularTimer : public AllStatic { + private: + static JfrTicks _finish_time_ticks; + static JfrTicks _start_time_ticks; + static long _counter; + static long _granularity; + static bool _finished; + public: + static void start(jlong duration_ticks, long granularity); + static void stop(); + static const JfrTicks& start_time(); + static const JfrTicks& end_time(); + static bool is_finished(); +}; + +#endif // SHARE_VM_LEAKPROFILER_UTILITIES_GRANULARTIMER_HPP diff --git a/src/share/vm/jfr/leakprofiler/utilities/rootType.hpp b/src/share/vm/jfr/leakprofiler/utilities/rootType.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c8cd633293f7305f449ada80582d7b096b17e9e3 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/utilities/rootType.hpp @@ -0,0 +1,109 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_LEAKPROFILER_UTILITIES_ROOTTYPE_HPP +#define SHARE_VM_LEAKPROFILER_UTILITIES_ROOTTYPE_HPP + +#include "memory/allocation.hpp" +#include "utilities/debug.hpp" + +class OldObjectRoot : public AllStatic { + public: + enum System { + _system_undetermined, + _universe, + _global_jni_handles, + _threads, + _object_synchronizer, + _system_dictionary, + _class_loader_data, + _management, + _jvmti, + _code_cache, + _string_table, + _aot, + _number_of_systems + }; + + enum Type { + _type_undetermined, + _stack_variable, + _local_jni_handle, + _global_jni_handle, + _handle_area, + _number_of_types + }; + + static const char* system_description(System system) { + switch (system) { + case _system_undetermined: + return ""; + case _universe: + return "Universe"; + case _global_jni_handles: + return "Global JNI Handles"; + case _threads: + return "Threads"; + case _object_synchronizer: + return "Object Monitor"; + case _system_dictionary: + return "System Dictionary"; + case _class_loader_data: + return "Class Loader Data"; + case _management: + return "Management"; + case _jvmti: + return "JVMTI"; + case _code_cache: + return "Code Cache"; + case _string_table: + return "String Table"; + case _aot: + return "AOT"; + default: + ShouldNotReachHere(); + } + return NULL; + } + + static const char* type_description(Type type) { + switch (type) { + case _type_undetermined: + return ""; + case _stack_variable: + return "Stack Variable"; + case _local_jni_handle: + return "Local JNI Handle"; + case _global_jni_handle: + return "Global JNI Handle"; + case _handle_area: + return "Handle Area"; + default: + ShouldNotReachHere(); + } + return NULL; + } +}; + +#endif // SHARE_VM_LEAKPROFILER_UTILITIES_ROOTTYPE_HPP diff --git a/src/share/vm/jfr/leakprofiler/utilities/saveRestore.cpp b/src/share/vm/jfr/leakprofiler/utilities/saveRestore.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3cfff5a708b60060ebfc366b48069c78fcf04ee7 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/utilities/saveRestore.cpp @@ -0,0 +1,111 @@ +/* + * 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 "precompiled.hpp" +#include "classfile/classLoaderData.hpp" +#include "jfr/leakprofiler/utilities/saveRestore.hpp" +#include "oops/oop.inline.hpp" + +MarkOopContext::MarkOopContext() : _obj(NULL), _mark_oop(NULL) {} + +MarkOopContext::MarkOopContext(const oop obj) : _obj(obj), _mark_oop(obj->mark()) { + assert(_obj->mark() == _mark_oop, "invariant"); + // now we will "poison" the mark word of the object + // to the intermediate monitor INFLATING state. + // This is an "impossible" state during a safepoint, + // hence we will use it to quickly identify objects + // during the reachability search from gc roots. + assert(NULL == markOopDesc::INFLATING(), "invariant"); + _obj->set_mark(markOopDesc::INFLATING()); + assert(NULL == obj->mark(), "invariant"); +} + +MarkOopContext::~MarkOopContext() { + if (_obj != NULL) { + _obj->set_mark(_mark_oop); + assert(_obj->mark() == _mark_oop, "invariant"); + } +} + +MarkOopContext::MarkOopContext(const MarkOopContext& rhs) : _obj(NULL), _mark_oop(NULL) { + swap(const_cast(rhs)); +} + +void MarkOopContext::operator=(MarkOopContext rhs) { + swap(rhs); +} + +void MarkOopContext::swap(MarkOopContext& rhs) { + oop temp_obj = rhs._obj; + markOop temp_mark_oop = rhs._mark_oop; + rhs._obj = _obj; + rhs._mark_oop = _mark_oop; + _obj = temp_obj; + _mark_oop = temp_mark_oop; +} + +CLDClaimContext::CLDClaimContext() : _cld(NULL) {} + +CLDClaimContext::CLDClaimContext(ClassLoaderData* cld) : _cld(cld) { + assert(_cld->claimed(), "invariant"); + _cld->clear_claimed(); +} + +CLDClaimContext::~CLDClaimContext() { + if (_cld != NULL) { + _cld->claim(); + assert(_cld->claimed(), "invariant"); + } +} + +CLDClaimContext::CLDClaimContext(const CLDClaimContext& rhs) : _cld(NULL) { + swap(const_cast(rhs)); +} + +void CLDClaimContext::operator=(CLDClaimContext rhs) { + swap(rhs); +} + +void CLDClaimContext::swap(CLDClaimContext& rhs) { + ClassLoaderData* temp_cld = rhs._cld; + rhs._cld = _cld; + _cld = temp_cld; +} + +CLDClaimStateClosure::CLDClaimStateClosure() : CLDClosure(), _state() {} + +void CLDClaimStateClosure::do_cld(ClassLoaderData* cld) { + assert(cld != NULL, "invariant"); + if (cld->claimed()) { + _state.save(cld); + } +} + +SaveRestoreCLDClaimBits::SaveRestoreCLDClaimBits() : _claim_state_closure() { + ClassLoaderDataGraph::cld_do(&_claim_state_closure); +} + +SaveRestoreCLDClaimBits::~SaveRestoreCLDClaimBits() { + ClassLoaderDataGraph::clear_claimed_marks(); +} diff --git a/src/share/vm/jfr/leakprofiler/utilities/saveRestore.hpp b/src/share/vm/jfr/leakprofiler/utilities/saveRestore.hpp new file mode 100644 index 0000000000000000000000000000000000000000..abc40dbd5883c12ab3b303f880be4576498a9af1 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/utilities/saveRestore.hpp @@ -0,0 +1,128 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_JFR_LEAKPROFILER_UTILITIES_SAVERESTORE_HPP +#define SHARE_VM_JFR_LEAKPROFILER_UTILITIES_SAVERESTORE_HPP + +#include "memory/allocation.hpp" +#include "memory/iterator.hpp" +#include "oops/markOop.hpp" +#include "utilities/growableArray.hpp" + +template +class SaveRestore { + private: + Impl _impl; + public: + SaveRestore() : _impl() { + _impl.setup(); + } + + void save(T const& value) { + _impl.save(value); + } + + ~SaveRestore() { + _impl.restore(); + } +}; + +template +class ContextStore { +private: + GrowableArray* _storage; +public: + ContextStore() : _storage(NULL) {} + + void setup() { + assert(_storage == NULL, "invariant"); + _storage = new GrowableArray(16); + } + + void save(T const& value) { + _storage->push(Context(value)); + } + + void restore() { + for (int i = 0; i < _storage->length(); ++i) { + _storage->at(i).~Context(); + } + } +}; + +/* +* This class will save the original mark oop of an object sample object. +* It will then install an "identifier" mark oop to be used for +* identification purposes in the search for reference chains. +* The destructor will restore the original mark oop. +*/ + +class MarkOopContext { + private: + oop _obj; + markOop _mark_oop; + void swap(MarkOopContext& rhs); + public: + MarkOopContext(); + MarkOopContext(const oop obj); + MarkOopContext(const MarkOopContext& rhs); + void operator=(MarkOopContext rhs); + ~MarkOopContext(); +}; + +typedef SaveRestore > SaveRestoreMarkOops; + +class ClassLoaderData; + +class CLDClaimContext { + private: + ClassLoaderData* _cld; + void swap(CLDClaimContext& rhs); + public: + CLDClaimContext(); + CLDClaimContext(ClassLoaderData* cld); + CLDClaimContext(const CLDClaimContext& rhs); + void operator=(CLDClaimContext rhs); + ~CLDClaimContext(); +}; + +typedef SaveRestore > SaveRestoreCLDClaimState; + +class CLDClaimStateClosure : public CLDClosure { + private: + SaveRestoreCLDClaimState _state; + public: + CLDClaimStateClosure(); + void do_cld(ClassLoaderData* cld); +}; + +class SaveRestoreCLDClaimBits : public StackObj { + private: + CLDClaimStateClosure _claim_state_closure; + public: + SaveRestoreCLDClaimBits(); + ~SaveRestoreCLDClaimBits(); +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_UTILITIES_SAVERESTORE_HPP diff --git a/src/share/vm/jfr/leakprofiler/utilities/unifiedOop.hpp b/src/share/vm/jfr/leakprofiler/utilities/unifiedOop.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9c9858bf40fcc992164d444123b22cb8850c7419 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/utilities/unifiedOop.hpp @@ -0,0 +1,55 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_JFR_LEAKPROFILER_UTILITIES_UNIFIEDOOP_HPP +#define SHARE_VM_JFR_LEAKPROFILER_UTILITIES_UNIFIEDOOP_HPP + +#include "oops/oop.inline.hpp" + +class UnifiedOop : public AllStatic { + public: + static const bool is_narrow(const oop* ref) { + assert(ref != NULL, "invariant"); + return 1 == (((u8)ref) & 1); + } + + static const oop* decode(const oop* ref) { + assert(ref != NULL, "invariant"); + return is_narrow(ref) ? (const oop*)(((u8)ref) & ~1) : ref; + } + + static const oop* encode(narrowOop* ref) { + assert(ref != NULL, "invariant"); + return (const oop*)((u8)ref | 1); + } + + static oop dereference(const oop* ref) { + assert(ref != NULL, "invariant"); + return is_narrow(ref) ? + oopDesc::load_decode_heap_oop((narrowOop*)decode(ref)) : + oopDesc::load_decode_heap_oop(const_cast(ref)); + } +}; + +#endif // SHARE_VM_JFR_LEAKPROFILER_UTILITIES_UNIFIEDOOP_HPP diff --git a/src/share/vm/jfr/leakprofiler/utilities/vmOperation.hpp b/src/share/vm/jfr/leakprofiler/utilities/vmOperation.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c54eb179c8427ee790fdac738b7d0ca14bf64002 --- /dev/null +++ b/src/share/vm/jfr/leakprofiler/utilities/vmOperation.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019, 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 SHARE_JFR_LEAKPROFILER_UTILITIES_VMOPERATION_HPP +#define SHARE_JFR_LEAKPROFILER_UTILITIES_VMOPERATION_HPP + +#include "runtime/vm_operations.hpp" + +class OldObjectVMOperation : public VM_Operation { + public: + Mode evaluation_mode() const { + return _safepoint; + } + + VMOp_Type type() const { + return VMOp_JFROldObject; + } +}; + +#endif // SHARE_JFR_LEAKPROFILER_UTILITIES_VMOPERATION_HPP diff --git a/src/share/vm/jfr/metadata/jfrSerializer.hpp b/src/share/vm/jfr/metadata/jfrSerializer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f5a4d7d9f2475f7fe9277d31bc37b13db9644507 --- /dev/null +++ b/src/share/vm/jfr/metadata/jfrSerializer.hpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_METADATA_JFRSERIALIZER_HPP +#define SHARE_VM_JFR_METADATA_JFRSERIALIZER_HPP + +#include "memory/allocation.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfrfiles/jfrTypes.hpp" + +/* + * A "type" in Jfr is a binary relation defined by enumerating a set of ordered pairs: + * + * { <1, myvalue>, <2, mysecondvalue>, ... } + * + * The key should be a type relative unique id. A value is an instance of the type. + * + * By defining and registering a type, keys can be written to event fields and the + * framework will maintain the mapping to the corresponding value (if you register as below). + * + * Inherit JfrSerializer, create a CHeapObj instance and then use JfrSerializer::register_serializer(...) to register. + * Once registered, the ownership of the serializer instance is transferred to Jfr. + * + * How to register: + * + * bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer) + * + * The type identifiers are machine generated into an enum located in jfrfiles/jfrTypes.hpp (included). + * + * enum JfrTypeId { + * ... + * TYPE_THREADGROUP, + * TYPE_CLASSLOADER, + * TYPE_METHOD, + * TYPE_SYMBOL, + * TYPE_THREADSTATE, + * TYPE_INFLATECAUSE, + * ... + * + * id this is the id of the type your are defining (see the enum above). + * require_safepoint indicate if your type need to be evaluated and serialized under a safepoint. + * permit_cache indicate if your type constants are stable to be cached. + * (implies the callback is invoked only once and the contents will be cached. Set this to true for static information). + * serializer the serializer instance. + * + * See below for guidance about how to implement serialize(). + * + */ +class JfrSerializer : public CHeapObj { + public: + virtual ~JfrSerializer() {} + static bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer); + virtual void serialize(JfrCheckpointWriter& writer) = 0; +}; + +/* + * Defining serialize(JfrCheckpointWriter& writer): + * + * Invoke writer.write_count(N) for the number of ordered pairs (cardinality) to be defined. + * + * You then write each individual ordered pair, ... + * + * Here is a simple example, describing a type defining string constants: + * + * void MyType::serialize(JfrCheckpointWriter& writer) { + * const int nof_causes = ObjectSynchronizer::inflate_cause_nof; + * writer.write_count(nof_causes); // write number of ordered pairs (mappings) to follow + * for (int i = 0; i < nof_causes; i++) { + * writer.write_key(i); // write key + * writer.write(ObjectSynchronizer::inflate_cause_name((ObjectSynchronizer::InflateCause)i)); // write value + * } + * } + * + * Note that values can be complex, and can also referer to other types. + * + * Please see jfr/recorder/checkpoint/types/jfrType.cpp for reference. + */ + +#endif // SHARE_VM_JFR_METADATA_JFRSERIALIZER_HPP diff --git a/src/share/vm/jfr/metadata/metadata.xml b/src/share/vm/jfr/metadata/metadata.xml new file mode 100644 index 0000000000000000000000000000000000000000..12c3c638b52f49a085c2a4ef93ef47a43391e4d2 --- /dev/null +++ b/src/share/vm/jfr/metadata/metadata.xml @@ -0,0 +1,1143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/share/vm/jfr/metadata/metadata.xsd b/src/share/vm/jfr/metadata/metadata.xsd new file mode 100644 index 0000000000000000000000000000000000000000..4bcb9675354727068382f261622249cd0d983809 --- /dev/null +++ b/src/share/vm/jfr/metadata/metadata.xsd @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/share/vm/jfr/periodic/jfrNetworkUtilization.cpp b/src/share/vm/jfr/periodic/jfrNetworkUtilization.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a7c0d84f3606d369e55dd8a36313ac04ce5958e --- /dev/null +++ b/src/share/vm/jfr/periodic/jfrNetworkUtilization.cpp @@ -0,0 +1,190 @@ +/* + * 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. + * + */ + +#include "precompiled.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/metadata/jfrSerializer.hpp" +#include "jfr/periodic/jfrNetworkUtilization.hpp" +#include "jfr/periodic/jfrOSInterface.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "runtime/os_perf.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" + +struct InterfaceEntry { + char* name; + traceid id; + uint64_t bytes_in; + uint64_t bytes_out; + bool in_use; +}; + +static GrowableArray* _interfaces = NULL; + +void JfrNetworkUtilization::destroy() { + if (_interfaces != NULL) { + for (int i = 0; i < _interfaces->length(); ++i) { + FREE_C_HEAP_ARRAY(char, _interfaces->at(i).name, mtInternal); + } + delete _interfaces; + _interfaces = NULL; + } +} + +static InterfaceEntry& new_entry(const NetworkInterface* iface, GrowableArray* interfaces) { + assert(iface != NULL, "invariant"); + assert(interfaces != NULL, "invariant"); + + // single threaded premise + static traceid interface_id = 0; + + const char* name = iface->get_name(); + assert(name != NULL, "invariant"); + + InterfaceEntry entry; + const size_t length = strlen(name); + entry.name = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal); + strncpy(entry.name, name, length + 1); + entry.id = ++interface_id; + entry.bytes_in = iface->get_bytes_in(); + entry.bytes_out = iface->get_bytes_out(); + entry.in_use = false; + return _interfaces->at(_interfaces->append(entry)); +} + +static GrowableArray* get_interfaces() { + if (_interfaces == NULL) { + _interfaces = new(ResourceObj::C_HEAP, mtTracing) GrowableArray(10, true, mtTracing); + } + return _interfaces; +} + +static InterfaceEntry& get_entry(const NetworkInterface* iface) { + // Remember the index we started at last time, since we're most likely looking at them + // in the same order every time. + static int saved_index = -1; + + GrowableArray* interfaces = get_interfaces(); + assert(interfaces != NULL, "invariant"); + for (int i = 0; i < _interfaces->length(); ++i) { + saved_index = (saved_index + 1) % _interfaces->length(); + if (strcmp(_interfaces->at(saved_index).name, iface->get_name()) == 0) { + return _interfaces->at(saved_index); + } + } + return new_entry(iface, interfaces); +} + +// If current counters are less than previous we assume the interface has been reset +// If no bytes have been either sent or received, we'll also skip the event +static uint64_t rate_per_second(uint64_t current, uint64_t old, const JfrTickspan& interval) { + assert(interval.value() > 0, "invariant"); + if (current <= old) { + return 0; + } + return ((current - old) * NANOSECS_PER_SEC) / interval.nanoseconds(); +} + +static bool get_interfaces(NetworkInterface** network_interfaces) { + const int ret_val = JfrOSInterface::network_utilization(network_interfaces); + if (ret_val == OS_ERR) { + if (LogJFR) tty->print_cr("Unable to generate network utilization events"); + return false; + } + return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED; +} + +class JfrNetworkInterfaceName : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer) { + assert(_interfaces != NULL, "invariant"); + const JfrCheckpointContext ctx = writer.context(); + const intptr_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet + int active_interfaces = 0; + for (int i = 0; i < _interfaces->length(); ++i) { + InterfaceEntry& entry = _interfaces->at(i); + if (entry.in_use) { + entry.in_use = false; + writer.write_key(entry.id); + writer.write(entry.name); + ++active_interfaces; + } + } + if (active_interfaces == 0) { + // nothing to write, restore context + writer.set_context(ctx); + return; + } + writer.write_count(active_interfaces, count_offset); + } +}; + +static bool register_network_interface_name_serializer() { + assert(_interfaces != NULL, "invariant"); + return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME, + false, // require safepoint + false, // disallow caching; we want a callback every rotation + new JfrNetworkInterfaceName()); +} + +void JfrNetworkUtilization::send_events() { + ResourceMark rm; + NetworkInterface* network_interfaces; + if (!get_interfaces(&network_interfaces)) { + return; + } + if (LogJFR && Verbose) tty->print_cr("Reporting network utilization"); + static JfrTicks last_sample_instant; + const JfrTicks cur_time = JfrTicks::now(); + const JfrTickspan interval = last_sample_instant == 0 ? cur_time - cur_time : cur_time - last_sample_instant; + last_sample_instant = cur_time; + for (NetworkInterface *cur = network_interfaces; cur != NULL; cur = cur->next()) { + InterfaceEntry& entry = get_entry(cur); + if (interval.value() > 0) { + const uint64_t current_bytes_in = cur->get_bytes_in(); + const uint64_t current_bytes_out = cur->get_bytes_out(); + const uint64_t read_rate = rate_per_second(current_bytes_in, entry.bytes_in, interval); + const uint64_t write_rate = rate_per_second(current_bytes_out, entry.bytes_out, interval); + if (read_rate > 0 || write_rate > 0) { + entry.in_use = true; + EventNetworkUtilization event(UNTIMED); + event.set_starttime(cur_time); + event.set_endtime(cur_time); + event.set_networkInterface(entry.id); + event.set_readRate(8 * read_rate); + event.set_writeRate(8 * write_rate); + event.commit(); + } + // update existing entry with new values + entry.bytes_in = current_bytes_in; + entry.bytes_out = current_bytes_out; + } + } + + static bool is_serializer_registered = false; + if (!is_serializer_registered) { + is_serializer_registered = register_network_interface_name_serializer(); + } +} diff --git a/src/share/vm/trace/noTraceBackend.hpp b/src/share/vm/jfr/periodic/jfrNetworkUtilization.hpp similarity index 71% rename from src/share/vm/trace/noTraceBackend.hpp rename to src/share/vm/jfr/periodic/jfrNetworkUtilization.hpp index a0b1f33dd26fb0a98959b1b2e31cce79115c3823..8d1b595912877a9d0d269fcbc2df7523a00e482a 100644 --- a/src/share/vm/trace/noTraceBackend.hpp +++ b/src/share/vm/jfr/periodic/jfrNetworkUtilization.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -21,24 +21,18 @@ * questions. * */ -#ifndef SHARE_VM_TRACE_NOTRACEBACKEND_HPP -#define SHARE_VM_TRACE_NOTRACEBACKEND_HPP -#include "prims/jni.h" -#include "trace/traceTime.hpp" +#ifndef SHARE_VM_JFR_PERIODIC_JFRNETWORKUTILIZATION_HPP +#define SHARE_VM_JFR_PERIODIC_JFRNETWORKUTILIZATION_HPP -class NoTraceBackend { -public: - static TracingTime time() { - return 0; - } -}; +#include "memory/allocation.hpp" -class TraceThreadData { +class NetworkInterface; + +class JfrNetworkUtilization : public AllStatic { public: - TraceThreadData() {} + static void destroy(); + static void send_events(); }; -typedef NoTraceBackend Tracing; - -#endif // SHARE_VM_TRACE_NOTRACEBACKEND_HPP +#endif // SHARE_VM_JFR_PERIODIC_JFRNETWORKUTILIZATION_HPP diff --git a/src/share/vm/jfr/periodic/jfrOSInterface.cpp b/src/share/vm/jfr/periodic/jfrOSInterface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..331b249a735fac31aadfeadb6ab080fd8f172a8f --- /dev/null +++ b/src/share/vm/jfr/periodic/jfrOSInterface.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2012, 2019, 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 "precompiled.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/periodic/jfrNetworkUtilization.hpp" +#include "jfr/periodic/jfrOSInterface.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/os.hpp" +#include "runtime/os_perf.hpp" +#include "utilities/ostream.hpp" + +#include // for environment variables +#ifdef __APPLE__ +#include +#define environ (*_NSGetEnviron()) +#endif + +#ifndef environ +extern char** environ; +#endif + +static JfrOSInterface* _instance = NULL; + +JfrOSInterface& JfrOSInterface::instance() { + return *_instance; +} + +JfrOSInterface* JfrOSInterface::create() { + assert(_instance == NULL, "invariant"); + _instance = new JfrOSInterface(); + return _instance; +} + +void JfrOSInterface::destroy() { + JfrNetworkUtilization::destroy(); + if (_instance != NULL) { + delete _instance; + _instance = NULL; + } +} + +class JfrOSInterface::JfrOSInterfaceImpl : public JfrCHeapObj { + friend class JfrOSInterface; + private: + CPUInformationInterface* _cpu_info_interface; + CPUPerformanceInterface* _cpu_perf_interface; + SystemProcessInterface* _system_process_interface; + NetworkPerformanceInterface* _network_performance_interface; + + JfrOSInterfaceImpl(); + bool initialize(); + ~JfrOSInterfaceImpl(); + + // cpu info + int cpu_information(CPUInformation& cpu_info); + int cpu_load(int which_logical_cpu, double* cpu_load); + int context_switch_rate(double* rate); + int cpu_load_total_process(double* cpu_load); + int cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotal); + + // os information + int os_version(char** os_version) const; + + // environment information + void generate_environment_variables_events(); + + // system processes information + int system_processes(SystemProcess** system_processes, int* no_of_sys_processes); + + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +JfrOSInterface::JfrOSInterfaceImpl::JfrOSInterfaceImpl() : _cpu_info_interface(NULL), + _cpu_perf_interface(NULL), + _system_process_interface(NULL) {} + +bool JfrOSInterface::JfrOSInterfaceImpl::initialize() { + _cpu_info_interface = new CPUInformationInterface(); + if (!(_cpu_info_interface != NULL && _cpu_info_interface->initialize())) { + return false; + } + _cpu_perf_interface = new CPUPerformanceInterface(); + if (!(_cpu_perf_interface != NULL && _cpu_perf_interface->initialize())) { + return false; + } + _system_process_interface = new SystemProcessInterface(); + if (!(_system_process_interface != NULL && _system_process_interface->initialize())) { + return false; + } + _network_performance_interface = new NetworkPerformanceInterface(); + return _network_performance_interface != NULL && _network_performance_interface->initialize(); +} + +JfrOSInterface::JfrOSInterfaceImpl::~JfrOSInterfaceImpl(void) { + if (_cpu_info_interface != NULL) { + delete _cpu_info_interface; + _cpu_info_interface = NULL; + } + if (_cpu_perf_interface != NULL) { + delete _cpu_perf_interface; + _cpu_perf_interface = NULL; + } + if (_system_process_interface != NULL) { + delete _system_process_interface; + _system_process_interface = NULL; + } + if (_network_performance_interface != NULL) { + delete _network_performance_interface; + _network_performance_interface = NULL; + } +} + +int JfrOSInterface::JfrOSInterfaceImpl::cpu_load(int which_logical_cpu, double* cpu_load) { + return _cpu_perf_interface->cpu_load(which_logical_cpu, cpu_load); +} + +int JfrOSInterface::JfrOSInterfaceImpl::context_switch_rate(double* rate) { + return _cpu_perf_interface->context_switch_rate(rate); +} + +int JfrOSInterface::JfrOSInterfaceImpl::cpu_load_total_process(double* cpu_load) { + return _cpu_perf_interface->cpu_load_total_process(cpu_load); +} + +int JfrOSInterface::JfrOSInterfaceImpl::cpu_loads_process(double* pjvmUserLoad, + double* pjvmKernelLoad, + double* psystemTotal) { + return _cpu_perf_interface->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotal); +} + +int JfrOSInterface::JfrOSInterfaceImpl::cpu_information(CPUInformation& cpu_info) { + return _cpu_info_interface->cpu_information(cpu_info); +} + +int JfrOSInterface::JfrOSInterfaceImpl::system_processes(SystemProcess** system_processes, int* no_of_sys_processes) { + assert(system_processes != NULL, "system_processes pointer is NULL!"); + assert(no_of_sys_processes != NULL, "no_of_sys_processes pointer is NULL!"); + return _system_process_interface->system_processes(system_processes, no_of_sys_processes); +} + +int JfrOSInterface::JfrOSInterfaceImpl::network_utilization(NetworkInterface** network_interfaces) const { + return _network_performance_interface->network_utilization(network_interfaces); +} + +// assigned char* is RESOURCE_HEAP_ALLOCATED +// caller need to ensure proper ResourceMark placement. +int JfrOSInterface::JfrOSInterfaceImpl::os_version(char** os_version) const { + assert(os_version != NULL, "os_version pointer is NULL!"); + stringStream os_ver_info; + os::print_os_info_brief(&os_ver_info); + *os_version = os_ver_info.as_string(); + return OS_OK; +} + +JfrOSInterface::JfrOSInterface() { + _impl = NULL; +} + +bool JfrOSInterface::initialize() { + _impl = new JfrOSInterface::JfrOSInterfaceImpl(); + return _impl != NULL && _impl->initialize(); +} + +JfrOSInterface::~JfrOSInterface() { + if (_impl != NULL) { + delete _impl; + _impl = NULL; + } +} + +int JfrOSInterface::cpu_information(CPUInformation& cpu_info) { + return instance()._impl->cpu_information(cpu_info); +} + +int JfrOSInterface::cpu_load(int which_logical_cpu, double* cpu_load) { + return instance()._impl->cpu_load(which_logical_cpu, cpu_load); +} + +int JfrOSInterface::context_switch_rate(double* rate) { + return instance()._impl->context_switch_rate(rate); +} + +int JfrOSInterface::cpu_load_total_process(double* cpu_load) { + return instance()._impl->cpu_load_total_process(cpu_load); +} + +int JfrOSInterface::cpu_loads_process(double* jvm_user_load, double* jvm_kernel_load, double* system_total_load){ + return instance()._impl->cpu_loads_process(jvm_user_load, jvm_kernel_load, system_total_load); +} + +int JfrOSInterface::os_version(char** os_version) { + return instance()._impl->os_version(os_version); +} + +int JfrOSInterface::generate_initial_environment_variable_events() { + if (environ == NULL) { + return OS_ERR; + } + + if (EventInitialEnvironmentVariable::is_enabled()) { + // One time stamp for all events, so they can be grouped together + JfrTicks time_stamp = JfrTicks::now(); + for (char** p = environ; *p != NULL; p++) { + char* variable = *p; + char* equal_sign = strchr(variable, '='); + if (equal_sign != NULL) { + // Extract key/value + ResourceMark rm; + ptrdiff_t key_length = equal_sign - variable; + char* key = NEW_RESOURCE_ARRAY(char, key_length + 1); + char* value = equal_sign + 1; + strncpy(key, variable, key_length); + key[key_length] = '\0'; + EventInitialEnvironmentVariable event(UNTIMED); + event.set_endtime(time_stamp); + event.set_key(key); + event.set_value(value); + event.commit(); + } + } + } + return OS_OK; +} + +int JfrOSInterface::system_processes(SystemProcess** sys_processes, int* no_of_sys_processes) { + return instance()._impl->system_processes(sys_processes, no_of_sys_processes); +} + +int JfrOSInterface::network_utilization(NetworkInterface** network_interfaces) { + return instance()._impl->network_utilization(network_interfaces); +} diff --git a/src/share/vm/jfr/periodic/jfrOSInterface.hpp b/src/share/vm/jfr/periodic/jfrOSInterface.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5c1d070f593be228729803a63829bbf2d0684946 --- /dev/null +++ b/src/share/vm/jfr/periodic/jfrOSInterface.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012, 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 SHARE_VM_JFR_PERIODIC_JFROSINTERFACE_HPP +#define SHARE_VM_JFR_PERIODIC_JFROSINTERFACE_HPP + +#include "jfr/utilities/jfrAllocation.hpp" + +class CPUInformation; +// XXX +//class EnvironmentVariable; +class NetworkInterface; +class SystemProcess; + +class JfrOSInterface: public JfrCHeapObj { + friend class JfrRecorder; + private: + class JfrOSInterfaceImpl; + JfrOSInterfaceImpl* _impl; + + JfrOSInterface(); + ~JfrOSInterface(); + bool initialize(); + static JfrOSInterface& instance(); + static JfrOSInterface* create(); + static void destroy(); + + public: + static int cpu_information(CPUInformation& cpu_info); + static int cpu_load(int which_logical_cpu, double* cpu_load); + static int context_switch_rate(double* rate); + static int cpu_load_total_process(double* cpu_load); + static int cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad); + static int os_version(char** os_version); + static int generate_initial_environment_variable_events(); + static int system_processes(SystemProcess** system_processes, int* no_of_sys_processes); + static int network_utilization(NetworkInterface** network_interfaces); +}; + +#endif // SHARE_VM_JFR_PERIODIC_JFROSINTERFACE_HPP diff --git a/src/share/vm/jfr/periodic/jfrPeriodic.cpp b/src/share/vm/jfr/periodic/jfrPeriodic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..92f516361dcb3271ebcfab37291d4fe7ac80c80c --- /dev/null +++ b/src/share/vm/jfr/periodic/jfrPeriodic.cpp @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2012, 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 "precompiled.hpp" +#include "jvm.h" +#include "classfile/classLoaderStats.hpp" +#include "classfile/javaClasses.hpp" +#include "code/codeCache.hpp" +#include "compiler/compileBroker.hpp" +#include "gc_implementation/g1/g1HeapRegionEventSender.hpp" +#include "gc_implementation/shared/gcConfiguration.hpp" +#include "gc_implementation/shared/gcTrace.hpp" +#include "gc_implementation/shared/objectCountEventSender.hpp" +#include "gc_implementation/shared/vmGCOperations.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/periodic/jfrOSInterface.hpp" +#include "jfr/periodic/jfrThreadCPULoadEvent.hpp" +#include "jfr/periodic/jfrThreadDumpEvent.hpp" +#include "jfr/periodic/jfrNetworkUtilization.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/support/jfrThreadId.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "jfrfiles/jfrPeriodic.hpp" +#include "memory/heapInspection.hpp" +#include "memory/resourceArea.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/arguments.hpp" +#include "runtime/globals.hpp" +#include "runtime/os.hpp" +#include "runtime/os_perf.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/sweeper.hpp" +#include "runtime/vmThread.hpp" +#include "services/classLoadingService.hpp" +#include "services/management.hpp" +#include "services/threadService.hpp" +#include "utilities/exceptions.hpp" +#include "utilities/globalDefinitions.hpp" + +/** + * JfrPeriodic class + * Implementation of declarations in + * xsl generated traceRequestables.hpp + */ +#define TRACE_REQUEST_FUNC(id) void JfrPeriodicEventSet::request##id(void) + +TRACE_REQUEST_FUNC(JVMInformation) { + ResourceMark rm; + EventJVMInformation event; + event.set_jvmName(VM_Version::vm_name()); + event.set_jvmVersion(VM_Version::internal_vm_info_string()); + event.set_javaArguments(Arguments::java_command()); + event.set_jvmArguments(Arguments::jvm_args()); + event.set_jvmFlags(Arguments::jvm_flags()); + event.set_jvmStartTime(Management::vm_init_done_time()); + event.set_pid(os::current_process_id()); + event.commit(); + } + +TRACE_REQUEST_FUNC(OSInformation) { + ResourceMark rm; + char* os_name = NEW_RESOURCE_ARRAY(char, 2048); + JfrOSInterface::os_version(&os_name); + EventOSInformation event; + event.set_osVersion(os_name); + event.commit(); +} + +/* + * This is left empty on purpose, having ExecutionSample as a requestable + * is a way of getting the period. The period is passed to ThreadSampling::update_period. + * Implementation in jfrSamples.cpp + */ +TRACE_REQUEST_FUNC(ExecutionSample) { +} +TRACE_REQUEST_FUNC(NativeMethodSample) { +} + +TRACE_REQUEST_FUNC(ThreadDump) { + ResourceMark rm; + EventThreadDump event; + event.set_result(JfrDcmdEvent::thread_dump()); + event.commit(); +} + +static int _native_library_callback(const char* name, address base, address top, void *param) { + EventNativeLibrary event(UNTIMED); + event.set_name(name); + event.set_baseAddress((u8)base); + event.set_topAddress((u8)top); + event.set_endtime(*(JfrTicks*) param); + event.commit(); + return 0; +} + +TRACE_REQUEST_FUNC(NativeLibrary) { + JfrTicks ts= JfrTicks::now(); + os::get_loaded_modules_info(&_native_library_callback, (void *)&ts); +} + +TRACE_REQUEST_FUNC(InitialEnvironmentVariable) { + JfrOSInterface::generate_initial_environment_variable_events(); +} + +TRACE_REQUEST_FUNC(CPUInformation) { + CPUInformation cpu_info; + int ret_val = JfrOSInterface::cpu_information(cpu_info); + if (ret_val == OS_ERR) { + if (LogJFR) tty->print_cr( "Unable to generate requestable event CPUInformation"); + return; + } + if (ret_val == FUNCTIONALITY_NOT_IMPLEMENTED) { + return; + } + if (ret_val == OS_OK) { + EventCPUInformation event; + event.set_cpu(cpu_info.cpu_name()); + event.set_description(cpu_info.cpu_description()); + event.set_sockets(cpu_info.number_of_sockets()); + event.set_cores(cpu_info.number_of_cores()); + event.set_hwThreads(cpu_info.number_of_hardware_threads()); + event.commit(); + } +} + +TRACE_REQUEST_FUNC(CPULoad) { + double u = 0; // user time + double s = 0; // kernel time + double t = 0; // total time + int ret_val = JfrOSInterface::cpu_loads_process(&u, &s, &t); + if (ret_val == OS_ERR) { + if (LogJFR) tty->print_cr( "Unable to generate requestable event CPULoad"); + return; + } + if (ret_val == OS_OK) { + EventCPULoad event; + event.set_jvmUser((float)u); + event.set_jvmSystem((float)s); + event.set_machineTotal((float)t); + event.commit(); + } +} + +TRACE_REQUEST_FUNC(ThreadCPULoad) { + JfrThreadCPULoadEvent::send_events(); +} + +TRACE_REQUEST_FUNC(NetworkUtilization) { + JfrNetworkUtilization::send_events(); +} + +TRACE_REQUEST_FUNC(CPUTimeStampCounter) { + EventCPUTimeStampCounter event; + event.set_fastTimeEnabled(JfrTime::is_ft_enabled()); + event.set_fastTimeAutoEnabled(JfrTime::is_ft_supported()); + event.set_osFrequency(os::elapsed_frequency()); + event.set_fastTimeFrequency(JfrTime::frequency()); + event.commit(); +} + +TRACE_REQUEST_FUNC(SystemProcess) { + char pid_buf[16]; + SystemProcess* processes = NULL; + int num_of_processes = 0; + JfrTicks start_time = JfrTicks::now(); + int ret_val = JfrOSInterface::system_processes(&processes, &num_of_processes); + if (ret_val == OS_ERR) { + if (LogJFR) tty->print_cr( "Unable to generate requestable event SystemProcesses"); + return; + } + JfrTicks end_time = JfrTicks::now(); + if (ret_val == FUNCTIONALITY_NOT_IMPLEMENTED) { + return; + } + if (ret_val == OS_OK) { + // feature is implemented, write real event + while (processes != NULL) { + SystemProcess* tmp = processes; + const char* info = processes->command_line(); + if (info == NULL) { + info = processes->path(); + } + if (info == NULL) { + info = processes->name(); + } + if (info == NULL) { + info = "?"; + } + jio_snprintf(pid_buf, sizeof(pid_buf), "%d", processes->pid()); + EventSystemProcess event(UNTIMED); + event.set_pid(pid_buf); + event.set_commandLine(info); + event.set_starttime(start_time); + event.set_endtime(end_time); + event.commit(); + processes = processes->next(); + delete tmp; + } + } +} + +TRACE_REQUEST_FUNC(ThreadContextSwitchRate) { + double rate = 0.0; + int ret_val = JfrOSInterface::context_switch_rate(&rate); + if (ret_val == OS_ERR) { + if (LogJFR) tty->print_cr( "Unable to generate requestable event ThreadContextSwitchRate"); + return; + } + if (ret_val == FUNCTIONALITY_NOT_IMPLEMENTED) { + return; + } + if (ret_val == OS_OK) { + EventThreadContextSwitchRate event; + event.set_switchRate((float)rate + 0.0f); + event.commit(); + } +} + +#define SEND_FLAGS_OF_TYPE(eventType, flagType) \ + do { \ + Flag *flag = Flag::flags; \ + while (flag->_name != NULL) { \ + if (flag->is_ ## flagType()) { \ + if (flag->is_unlocked()) { \ + Event ## eventType event; \ + event.set_name(flag->_name); \ + event.set_value(flag->get_ ## flagType()); \ + event.set_origin(flag->get_origin()); \ + event.commit(); \ + } \ + } \ + ++flag; \ + } \ + } while (0) + +TRACE_REQUEST_FUNC(IntFlag) { + SEND_FLAGS_OF_TYPE(IntFlag, intx); +} + +TRACE_REQUEST_FUNC(UnsignedIntFlag) { + SEND_FLAGS_OF_TYPE(UnsignedIntFlag, uintx); +} + +TRACE_REQUEST_FUNC(LongFlag) { + SEND_FLAGS_OF_TYPE(LongFlag, intx); +} + +TRACE_REQUEST_FUNC(UnsignedLongFlag) { + SEND_FLAGS_OF_TYPE(UnsignedLongFlag, uintx); + SEND_FLAGS_OF_TYPE(UnsignedLongFlag, uint64_t); +} + +TRACE_REQUEST_FUNC(DoubleFlag) { + SEND_FLAGS_OF_TYPE(DoubleFlag, double); +} + +TRACE_REQUEST_FUNC(BooleanFlag) { + SEND_FLAGS_OF_TYPE(BooleanFlag, bool); +} + +TRACE_REQUEST_FUNC(StringFlag) { + SEND_FLAGS_OF_TYPE(StringFlag, ccstr); +} + +class VM_GC_SendObjectCountEvent : public VM_GC_HeapInspection { + public: + VM_GC_SendObjectCountEvent() : VM_GC_HeapInspection(NULL, true) {} + virtual void doit() { + ObjectCountEventSender::enable_requestable_event(); + collect(); + ObjectCountEventSender::disable_requestable_event(); + } +}; + +TRACE_REQUEST_FUNC(ObjectCount) { + VM_GC_SendObjectCountEvent op; + VMThread::execute(&op); +} + +class VM_G1SendHeapRegionInfoEvents : public VM_Operation { + virtual void doit() { + G1HeapRegionEventSender::send_events(); + } + virtual VMOp_Type type() const { return VMOp_HeapIterateOperation; } +}; + +TRACE_REQUEST_FUNC(G1HeapRegionInformation) { + if (UseG1GC) { + VM_G1SendHeapRegionInfoEvents op; + VMThread::execute(&op); + } +} + +// Java Mission Control (JMC) uses (Java) Long.MIN_VALUE to describe that a +// long value is undefined. +static jlong jmc_undefined_long = min_jlong; + +TRACE_REQUEST_FUNC(GCConfiguration) { + GCConfiguration conf; + jlong pause_target = conf.has_pause_target_default_value() ? jmc_undefined_long : conf.pause_target(); + EventGCConfiguration event; + event.set_youngCollector(conf.young_collector()); + event.set_oldCollector(conf.old_collector()); + event.set_parallelGCThreads(conf.num_parallel_gc_threads()); + event.set_concurrentGCThreads(conf.num_concurrent_gc_threads()); + event.set_usesDynamicGCThreads(conf.uses_dynamic_gc_threads()); + event.set_isExplicitGCConcurrent(conf.is_explicit_gc_concurrent()); + event.set_isExplicitGCDisabled(conf.is_explicit_gc_disabled()); + event.set_gcTimeRatio(conf.gc_time_ratio()); + event.set_pauseTarget((s8)pause_target); + event.commit(); +} + +TRACE_REQUEST_FUNC(GCTLABConfiguration) { + GCTLABConfiguration conf; + EventGCTLABConfiguration event; + event.set_usesTLABs(conf.uses_tlabs()); + event.set_minTLABSize(conf.min_tlab_size()); + event.set_tlabRefillWasteLimit(conf.tlab_refill_waste_limit()); + event.commit(); +} + +TRACE_REQUEST_FUNC(GCSurvivorConfiguration) { + GCSurvivorConfiguration conf; + EventGCSurvivorConfiguration event; + event.set_maxTenuringThreshold(conf.max_tenuring_threshold()); + event.set_initialTenuringThreshold(conf.initial_tenuring_threshold()); + event.commit(); +} + +TRACE_REQUEST_FUNC(GCHeapConfiguration) { + GCHeapConfiguration conf; + EventGCHeapConfiguration event; + event.set_minSize(conf.min_size()); + event.set_maxSize(conf.max_size()); + event.set_initialSize(conf.initial_size()); + event.set_usesCompressedOops(conf.uses_compressed_oops()); + event.set_compressedOopsMode(conf.narrow_oop_mode()); + event.set_objectAlignment(conf.object_alignment_in_bytes()); + event.set_heapAddressBits(conf.heap_address_size_in_bits()); + event.commit(); +} + +TRACE_REQUEST_FUNC(YoungGenerationConfiguration) { + GCYoungGenerationConfiguration conf; + jlong max_size = conf.has_max_size_default_value() ? jmc_undefined_long : conf.max_size(); + EventYoungGenerationConfiguration event; + event.set_maxSize((u8)max_size); + event.set_minSize(conf.min_size()); + event.set_newRatio(conf.new_ratio()); + event.commit(); +} + +TRACE_REQUEST_FUNC(InitialSystemProperty) { + SystemProperty* p = Arguments::system_properties(); + JfrTicks time_stamp = JfrTicks::now(); + while (p != NULL) { + if (true/* XXX fix me if you want !p->internal()*/) { + EventInitialSystemProperty event(UNTIMED); + event.set_key(p->key()); + event.set_value(p->value()); + event.set_endtime(time_stamp); + event.commit(); + } + p = p->next(); + } +} + +TRACE_REQUEST_FUNC(ThreadAllocationStatistics) { + ResourceMark rm; + int initial_size = Threads::number_of_threads(); + GrowableArray allocated(initial_size); + GrowableArray thread_ids(initial_size); + JfrTicks time_stamp = JfrTicks::now(); + { + // Collect allocation statistics while holding threads lock + MutexLockerEx ml(Threads_lock); + for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { + allocated.append(thread->cooked_allocated_bytes()); + thread_ids.append(JFR_THREAD_ID(thread)); + } + } + + // Write allocation statistics to buffer. + for(int i = 0; i < thread_ids.length(); i++) { + EventThreadAllocationStatistics event(UNTIMED); + event.set_allocated(allocated.at(i)); + event.set_thread(thread_ids.at(i)); + event.set_endtime(time_stamp); + event.commit(); + } +} + +/** + * PhysicalMemory event represents: + * + * @totalSize == The amount of physical memory (hw) installed and reported by the OS, in bytes. + * @usedSize == The amount of physical memory currently in use in the system (reserved/committed), in bytes. + * + * Both fields are systemwide, i.e. represents the entire OS/HW environment. + * These fields do not include virtual memory. + * + * If running inside a guest OS on top of a hypervisor in a virtualized environment, + * the total memory reported is the amount of memory configured for the guest OS by the hypervisor. + */ +TRACE_REQUEST_FUNC(PhysicalMemory) { + u8 totalPhysicalMemory = os::physical_memory(); + EventPhysicalMemory event; + event.set_totalSize(totalPhysicalMemory); + event.set_usedSize(totalPhysicalMemory - os::available_memory()); + event.commit(); +} + +TRACE_REQUEST_FUNC(JavaThreadStatistics) { + EventJavaThreadStatistics event; + event.set_activeCount(ThreadService::get_live_thread_count()); + event.set_daemonCount(ThreadService::get_daemon_thread_count()); + event.set_accumulatedCount(ThreadService::get_total_thread_count()); + event.set_peakCount(ThreadService::get_peak_thread_count()); + event.commit(); +} + +TRACE_REQUEST_FUNC(ClassLoadingStatistics) { + EventClassLoadingStatistics event; + event.set_loadedClassCount(ClassLoadingService::loaded_class_count()); + event.set_unloadedClassCount(ClassLoadingService::unloaded_class_count()); + event.commit(); +} + +class JfrClassLoaderStatsClosure : public ClassLoaderStatsClosure { +public: + JfrClassLoaderStatsClosure() : ClassLoaderStatsClosure(NULL) {} + + bool do_entry(oop const& key, ClassLoaderStats* const& cls) { + const ClassLoaderData* this_cld = cls->_class_loader != NULL ? + java_lang_ClassLoader::loader_data(cls->_class_loader) : (ClassLoaderData*)NULL; + const ClassLoaderData* parent_cld = cls->_parent != NULL ? + java_lang_ClassLoader::loader_data(cls->_parent) : (ClassLoaderData*)NULL; + EventClassLoaderStatistics event; + event.set_classLoader(this_cld); + event.set_parentClassLoader(parent_cld); + event.set_classLoaderData((intptr_t)cls->_cld); + event.set_classCount(cls->_classes_count); + event.set_chunkSize(cls->_chunk_sz); + event.set_blockSize(cls->_block_sz); + event.set_anonymousClassCount(cls->_anon_classes_count); + event.set_anonymousChunkSize(cls->_anon_chunk_sz); + event.set_anonymousBlockSize(cls->_anon_block_sz); + event.commit(); + return true; + } + + void createEvents(void) { + _stats->iterate(this); + } +}; + +class JfrClassLoaderStatsVMOperation : public ClassLoaderStatsVMOperation { + public: + JfrClassLoaderStatsVMOperation() : ClassLoaderStatsVMOperation(NULL) { } + + void doit() { + JfrClassLoaderStatsClosure clsc; + ClassLoaderDataGraph::cld_do(&clsc); + clsc.createEvents(); + } +}; + +TRACE_REQUEST_FUNC(ClassLoaderStatistics) { + JfrClassLoaderStatsVMOperation op; + VMThread::execute(&op); +} + +TRACE_REQUEST_FUNC(CompilerStatistics) { + EventCompilerStatistics event; + event.set_compileCount(CompileBroker::get_total_compile_count()); + event.set_bailoutCount(CompileBroker::get_total_bailout_count()); + event.set_invalidatedCount(CompileBroker::get_total_invalidated_count()); + event.set_osrCompileCount(CompileBroker::get_total_osr_compile_count()); + event.set_standardCompileCount(CompileBroker::get_total_standard_compile_count()); + event.set_osrBytesCompiled(CompileBroker::get_sum_osr_bytes_compiled()); + event.set_standardBytesCompiled(CompileBroker::get_sum_standard_bytes_compiled()); + event.set_nmetodsSize(CompileBroker::get_sum_nmethod_size()); + event.set_nmetodCodeSize(CompileBroker::get_sum_nmethod_code_size()); + event.set_peakTimeSpent(CompileBroker::get_peak_compilation_time()); + event.set_totalTimeSpent(CompileBroker::get_total_compilation_time()); + event.commit(); +} + +TRACE_REQUEST_FUNC(CompilerConfiguration) { + EventCompilerConfiguration event; + event.set_threadCount(CICompilerCount); + event.set_tieredCompilation(TieredCompilation); + event.commit(); +} + +TRACE_REQUEST_FUNC(CodeCacheStatistics) { + EventCodeCacheStatistics event; + event.set_codeBlobType((u1)0/*bt*/); // XXX + event.set_startAddress((u8)CodeCache::low_bound()); + event.set_reservedTopAddress((u8)CodeCache::high_bound()); + event.set_entryCount(CodeCache::nof_blobs()); + event.set_methodCount(CodeCache::nof_nmethods()); + event.set_adaptorCount(CodeCache::nof_adapters()); + event.set_unallocatedCapacity(CodeCache::unallocated_capacity()); + event.set_fullCount(CodeCache::get_codemem_full_count()); + event.commit(); +} + +TRACE_REQUEST_FUNC(CodeCacheConfiguration) { + EventCodeCacheConfiguration event; + event.set_initialSize(InitialCodeCacheSize); + event.set_reservedSize(ReservedCodeCacheSize); + event.set_nonNMethodSize(0/*NonNMethodCodeHeapSize*/); // XXX + event.set_profiledSize(0/*ProfiledCodeHeapSize*/); // XXX + event.set_nonProfiledSize(0/*NonProfiledCodeHeapSize*/); // XXX + event.set_expansionSize(CodeCacheExpansionSize); + event.set_minBlockLength(CodeCacheMinBlockLength); + event.set_startAddress((u8)CodeCache::low_bound()); + event.set_reservedTopAddress((u8)CodeCache::high_bound()); + event.commit(); +} + +TRACE_REQUEST_FUNC(CodeSweeperStatistics) { + EventCodeSweeperStatistics event; + event.set_sweepCount(NMethodSweeper::traversal_count()); + event.set_methodReclaimedCount(NMethodSweeper::total_nof_methods_reclaimed()); + event.set_totalSweepTime(NMethodSweeper::total_time_sweeping()); + event.set_peakFractionTime(NMethodSweeper::peak_sweep_fraction_time()); + event.set_peakSweepTime(NMethodSweeper::peak_sweep_time()); + event.commit(); +} + +TRACE_REQUEST_FUNC(CodeSweeperConfiguration) { + EventCodeSweeperConfiguration event; + event.set_sweeperEnabled(MethodFlushing); + event.set_flushingEnabled(UseCodeCacheFlushing); + event.commit(); +} diff --git a/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.cpp b/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d80ceca90f9daf851fd56704eaf705de5c8ab57 --- /dev/null +++ b/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.cpp @@ -0,0 +1,147 @@ +/* + * 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 "precompiled.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/periodic/jfrThreadCPULoadEvent.hpp" +#include "jfr/support/jfrThreadId.hpp" +#include "jfr/support/jfrThreadLocal.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "utilities/globalDefinitions.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.inline.hpp" + +jlong JfrThreadCPULoadEvent::get_wallclock_time() { + return os::javaTimeNanos(); +} + +int JfrThreadCPULoadEvent::_last_active_processor_count = 0; + +int JfrThreadCPULoadEvent::get_processor_count() { + int cur_processor_count = os::active_processor_count(); + int last_processor_count = _last_active_processor_count; + _last_active_processor_count = cur_processor_count; + + // If the number of processors decreases, we don't know at what point during + // the sample interval this happened, so use the largest number to try + // to avoid percentages above 100% + return MAX2(cur_processor_count, last_processor_count); +} + +// Returns false if the thread has not been scheduled since the last call to updateEvent +// (i.e. the delta for both system and user time is 0 milliseconds) +bool JfrThreadCPULoadEvent::update_event(EventThreadCPULoad& event, JavaThread* thread, jlong cur_wallclock_time, int processor_count) { + JfrThreadLocal* const tl = thread->jfr_thread_local(); + + jlong cur_cpu_time = os::thread_cpu_time(thread, true); + jlong prev_cpu_time = tl->get_cpu_time(); + + jlong prev_wallclock_time = tl->get_wallclock_time(); + tl->set_wallclock_time(cur_wallclock_time); + + // Threshold of 1 ms + if (cur_cpu_time - prev_cpu_time < 1 * NANOSECS_PER_MILLISEC) { + return false; + } + + jlong cur_user_time = os::thread_cpu_time(thread, false); + jlong prev_user_time = tl->get_user_time(); + + jlong cur_system_time = cur_cpu_time - cur_user_time; + jlong prev_system_time = prev_cpu_time - prev_user_time; + + // The user and total cpu usage clocks can have different resolutions, which can + // make us see decreasing system time. Ensure time doesn't go backwards. + if (prev_system_time > cur_system_time) { + cur_cpu_time += prev_system_time - cur_system_time; + cur_system_time = prev_system_time; + } + + jlong user_time = cur_user_time - prev_user_time; + jlong system_time = cur_system_time - prev_system_time; + jlong wallclock_time = cur_wallclock_time - prev_wallclock_time; + jlong total_available_time = wallclock_time * processor_count; + + // Avoid reporting percentages above the theoretical max + if (user_time + system_time > wallclock_time) { + jlong excess = user_time + system_time - wallclock_time; + if (user_time > excess) { + user_time -= excess; + cur_user_time -= excess; + cur_cpu_time -= excess; + } else { + cur_cpu_time -= excess; + excess -= user_time; + user_time = 0; + cur_user_time = 0; + system_time -= excess; + } + } + event.set_user(total_available_time > 0 ? (double)user_time / total_available_time : 0); + event.set_system(total_available_time > 0 ? (double)system_time / total_available_time : 0); + tl->set_user_time(cur_user_time); + tl->set_cpu_time(cur_cpu_time); + return true; +} + +void JfrThreadCPULoadEvent::send_events() { + Thread* periodic_thread = Thread::current(); + JfrThreadLocal* const periodic_thread_tl = periodic_thread->jfr_thread_local(); + traceid periodic_thread_id = periodic_thread_tl->thread_id(); + const int processor_count = JfrThreadCPULoadEvent::get_processor_count(); + JfrTicks event_time = JfrTicks::now(); + jlong cur_wallclock_time = JfrThreadCPULoadEvent::get_wallclock_time(); + + { + MutexLockerEx ml(Threads_lock); + unsigned jt_count = 0; + for (JavaThread *jt = Threads::first(); jt != NULL; jt = jt->next()) { + EventThreadCPULoad event(UNTIMED); + if (JfrThreadCPULoadEvent::update_event(event, jt, cur_wallclock_time, processor_count)) { + event.set_starttime(event_time); + if (jt != periodic_thread) { + // Commit reads the thread id from this thread's trace data, so put it there temporarily + periodic_thread_tl->set_thread_id(JFR_THREAD_ID(jt)); + } else { + periodic_thread_tl->set_thread_id(periodic_thread_id); + } + event.commit(); + } + jt_count++; + } + if (LogJFR && Verbose) tty->print_cr("Measured CPU usage for %d threads in %.3f milliseconds", jt_count, + (double)(JfrTicks::now() - event_time).milliseconds()); + } + // Restore this thread's thread id + periodic_thread_tl->set_thread_id(periodic_thread_id); +} + +void JfrThreadCPULoadEvent::send_event_for_thread(JavaThread* jt) { + EventThreadCPULoad event; + if (event.should_commit()) { + if (update_event(event, jt, get_wallclock_time(), get_processor_count())) { + event.commit(); + } + } +} diff --git a/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.hpp b/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.hpp new file mode 100644 index 0000000000000000000000000000000000000000..99b3fa7fe9bc8d8206d73219392280f239ddec0b --- /dev/null +++ b/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.hpp @@ -0,0 +1,45 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_JFR_PERIODIC_JFRTHREADCPULOAD_HPP +#define SHARE_VM_JFR_PERIODIC_JFRTHREADCPULOAD_HPP + +#include "jni.h" +#include "memory/allocation.hpp" + +class JavaThread; +class EventThreadCPULoad; + +class JfrThreadCPULoadEvent : public AllStatic { + static int _last_active_processor_count; + public: + static jlong get_wallclock_time(); + static int get_processor_count(); + static bool update_event(EventThreadCPULoad& event, JavaThread* thread, jlong cur_wallclock_time, int processor_count); + static void send_events(); + static void send_event_for_thread(JavaThread* jt); +}; + +#endif // SHARE_VM_JFR_PERIODIC_JFRTHREADCPULOAD_HPP + diff --git a/src/share/vm/jfr/periodic/jfrThreadDumpEvent.cpp b/src/share/vm/jfr/periodic/jfrThreadDumpEvent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9eea144aa5bfbe743f16a93f44819537b1dcef7 --- /dev/null +++ b/src/share/vm/jfr/periodic/jfrThreadDumpEvent.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015, 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 "precompiled.hpp" +#include "jfr/dcmd/jfrDcmds.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/periodic/jfrThreadDumpEvent.hpp" +#include "utilities/exceptions.hpp" +#include "utilities/ostream.hpp" + +/** +* Worker impl for generating and writing dcmd commands +* as jfr events. +* dispatch to diagnosticcommands "parse_and_execute" +* +* param: cmd = the DCMD to execute (including options) +*/ +static bool execute_dcmd(bufferedStream& st, const char* const cmd) { + Thread* THREAD = Thread::current(); + assert(!HAS_PENDING_EXCEPTION, "dcmd does not expect pending exceptions on entry!"); + // delegate to DCmd execution + DCmd::parse_and_execute(DCmd_Source_Internal, &st, cmd, ' ', THREAD); + if (HAS_PENDING_EXCEPTION) { + if (LogJFR) tty->print_cr("unable to create jfr event for DCMD %s", cmd); + if (LogJFR) tty->print_cr("exception type: %s", PENDING_EXCEPTION->klass()->external_name()); + // don't unwind this exception + CLEAR_PENDING_EXCEPTION; + // if exception occurred, + // reset stream. + st.reset(); + return false; + } + return true; +} + +// caller needs ResourceMark +const char* JfrDcmdEvent::thread_dump() { + assert(EventThreadDump::is_enabled(), "invariant"); + bufferedStream st; + execute_dcmd(st, "Thread.print"); + return st.as_string(); +} diff --git a/src/share/vm/jfr/periodic/jfrThreadDumpEvent.hpp b/src/share/vm/jfr/periodic/jfrThreadDumpEvent.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d1b90644ebe9ffd959e12ee0a50af51584a8c83b --- /dev/null +++ b/src/share/vm/jfr/periodic/jfrThreadDumpEvent.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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 SHARE_VM_JFR_PERIODIC_JFRDCMDEVENT_HPP +#define SHARE_VM_JFR_PERIODIC_JFRDCMDEVENT_HPP + +#include "memory/allocation.hpp" + +/* + * Helper for generating jfr events using output data from Dcmd's. + */ +class JfrDcmdEvent : public AllStatic { + public: + // caller needs ResourceMark + static const char* thread_dump(); +}; + +#endif // SHARE_VM_JFR_PERIODIC_JFRDCMDEVENT_HPP diff --git a/src/share/vm/jfr/periodic/sampling/jfrCallTrace.cpp b/src/share/vm/jfr/periodic/sampling/jfrCallTrace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..66095f05dfe3f49770a6f2411e7e69abe9ac2e8d --- /dev/null +++ b/src/share/vm/jfr/periodic/sampling/jfrCallTrace.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2012, 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 "precompiled.hpp" +#include "code/debugInfoRec.hpp" +#include "code/nmethod.hpp" +#include "code/pcDesc.hpp" +#include "jfr/periodic/sampling/jfrCallTrace.hpp" +#include "oops/method.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/registerMap.hpp" +#include "runtime/thread.inline.hpp" + +bool JfrGetCallTrace::find_top_frame(frame& top_frame, Method** method, frame& first_frame) { + assert(top_frame.cb() != NULL, "invariant"); + RegisterMap map(_thread, false); + frame candidate = top_frame; + for (int i = 0; i < MaxJavaStackTraceDepth * 2; ++i) { + if (candidate.is_entry_frame()) { + JavaCallWrapper *jcw = candidate.entry_frame_call_wrapper_if_safe(_thread); + if (jcw == NULL || jcw->is_first_frame()) { + return false; + } + } + + if (candidate.is_interpreted_frame()) { + JavaThreadState state = _thread->thread_state(); + const bool known_valid = (state == _thread_in_native || state == _thread_in_vm || state == _thread_blocked); + if (known_valid || candidate.is_interpreted_frame_valid(_thread)) { + Method* im = candidate.interpreter_frame_method(); + if (known_valid && !im->is_valid_method()) { + return false; + } + *method = im; + first_frame = candidate; + return true; + } + } + + if (candidate.cb()->is_nmethod()) { + // first check to make sure that we have a sane stack, + // the PC is actually inside the code part of the codeBlob, + // and we are past is_frame_complete_at (stack has been setup) + if (!candidate.safe_for_sender(_thread)) { + return false; + } + nmethod* nm = (nmethod*)candidate.cb(); + *method = nm->method(); + + if (_in_java) { + PcDesc* pc_desc = nm->pc_desc_near(candidate.pc() + 1); + if (pc_desc == NULL || pc_desc->scope_decode_offset() == DebugInformationRecorder::serialized_null) { + return false; + } + candidate.set_pc(pc_desc->real_pc(nm)); + assert(nm->pc_desc_at(candidate.pc()) != NULL, "invalid pc"); + } + first_frame = candidate; + return true; + } + + if (!candidate.safe_for_sender(_thread) || + candidate.is_stub_frame() || + candidate.cb()->frame_size() <= 0) { + return false; + } + + candidate = candidate.sender(&map); + if (candidate.cb() == NULL) { + return false; + } + } + return false; +} + +bool JfrGetCallTrace::get_topframe(void* ucontext, frame& topframe) { + if (!_thread->pd_get_top_frame_for_profiling(&topframe, ucontext, _in_java)) { + return false; + } + + if (topframe.cb() == NULL) { + return false; + } + + frame first_java_frame; + Method* method = NULL; + if (find_top_frame(topframe, &method, first_java_frame)) { + if (method == NULL) { + return false; + } + topframe = first_java_frame; + return true; + } + return false; +} diff --git a/src/share/vm/jfr/periodic/sampling/jfrCallTrace.hpp b/src/share/vm/jfr/periodic/sampling/jfrCallTrace.hpp new file mode 100644 index 0000000000000000000000000000000000000000..768778b6b0bb16bc7ec3afe8e56d18af844ae9ff --- /dev/null +++ b/src/share/vm/jfr/periodic/sampling/jfrCallTrace.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012, 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 SHARE_VM_JFR_ENGINE_SAMPLING_JFRCALLTRACE_HPP +#define SHARE_VM_JFR_ENGINE_SAMPLING_JFRCALLTRACE_HPP + +#include "memory/allocation.hpp" + +class frame; +class Method; +class JavaThread; + +class JfrGetCallTrace : public StackObj { + private: + JavaThread* _thread; + bool _in_java; + + public: + JfrGetCallTrace(bool in_java, JavaThread* thread) : _in_java(in_java), _thread(thread) {} + bool find_top_frame(frame& topframe, Method** method, frame& first_frame); + bool get_topframe(void* ucontext, frame& top); +}; + +#endif // SHARE_VM_JFR_ENGINE_SAMPLING_JFRCALLTRACE_HPP diff --git a/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp b/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..969ce8ff6d61a0da63cff4bb9766151c74ff6602 --- /dev/null +++ b/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2012, 2019, 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 "precompiled.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/periodic/sampling/jfrCallTrace.hpp" +#include "jfr/periodic/sampling/jfrThreadSampler.hpp" +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" +#include "jfr/support/jfrThreadId.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/os.hpp" +#include "runtime/semaphore.hpp" +#include "runtime/thread.inline.hpp" + +enum JfrSampleType { + NO_SAMPLE = 0, + JAVA_SAMPLE = 1, + NATIVE_SAMPLE = 2 +}; + +static bool thread_state_in_java(JavaThread* thread) { + assert(thread != NULL, "invariant"); + switch(thread->thread_state()) { + case _thread_new: + case _thread_uninitialized: + case _thread_new_trans: + case _thread_in_vm_trans: + case _thread_blocked_trans: + case _thread_in_native_trans: + case _thread_blocked: + case _thread_in_vm: + case _thread_in_native: + case _thread_in_Java_trans: + break; + case _thread_in_Java: + return true; + default: + ShouldNotReachHere(); + break; + } + return false; +} + +static bool thread_state_in_native(JavaThread* thread) { + assert(thread != NULL, "invariant"); + switch(thread->thread_state()) { + case _thread_new: + case _thread_uninitialized: + case _thread_new_trans: + case _thread_blocked_trans: + case _thread_blocked: + case _thread_in_vm: + case _thread_in_vm_trans: + case _thread_in_Java_trans: + case _thread_in_Java: + case _thread_in_native_trans: + break; + case _thread_in_native: + return true; + default: + ShouldNotReachHere(); + break; + } + return false; +} + +class JfrThreadSampleClosure { + public: + JfrThreadSampleClosure(EventExecutionSample* events, EventNativeMethodSample* events_native); + ~JfrThreadSampleClosure() {} + EventExecutionSample* next_event() { return &_events[_added_java++]; } + EventNativeMethodSample* next_event_native() { return &_events_native[_added_native++]; } + void commit_events(JfrSampleType type); + bool do_sample_thread(JavaThread* thread, JfrStackFrame* frames, u4 max_frames, JfrSampleType type); + uint java_entries() { return _added_java; } + uint native_entries() { return _added_native; } + + private: + bool sample_thread_in_java(JavaThread* thread, JfrStackFrame* frames, u4 max_frames); + bool sample_thread_in_native(JavaThread* thread, JfrStackFrame* frames, u4 max_frames); + EventExecutionSample* _events; + EventNativeMethodSample* _events_native; + Thread* _self; + uint _added_java; + uint _added_native; +}; + +class OSThreadSampler : public os::SuspendedThreadTask { + public: + OSThreadSampler(JavaThread* thread, + JfrThreadSampleClosure& closure, + JfrStackFrame *frames, + u4 max_frames) : os::SuspendedThreadTask((Thread*)thread), + _success(false), + _stacktrace(frames, max_frames), + _closure(closure), + _suspend_time() {} + + void take_sample(); + void do_task(const os::SuspendedThreadTaskContext& context); + void protected_task(const os::SuspendedThreadTaskContext& context); + bool success() const { return _success; } + const JfrStackTrace& stacktrace() const { return _stacktrace; } + + private: + bool _success; + JfrStackTrace _stacktrace; + JfrThreadSampleClosure& _closure; + JfrTicks _suspend_time; +}; + +class OSThreadSamplerCallback : public os::CrashProtectionCallback { + public: + OSThreadSamplerCallback(OSThreadSampler& sampler, const os::SuspendedThreadTaskContext &context) : + _sampler(sampler), _context(context) { + } + virtual void call() { + _sampler.protected_task(_context); + } + private: + OSThreadSampler& _sampler; + const os::SuspendedThreadTaskContext& _context; +}; + +void OSThreadSampler::do_task(const os::SuspendedThreadTaskContext& context) { +#ifndef ASSERT + guarantee(JfrOptionSet::sample_protection(), "Sample Protection should be on in product builds"); +#endif + assert(_suspend_time.value() == 0, "already timestamped!"); + _suspend_time = JfrTicks::now(); + + if (JfrOptionSet::sample_protection()) { + OSThreadSamplerCallback cb(*this, context); + os::ThreadCrashProtection crash_protection; + if (!crash_protection.call(cb)) { + if (true) tty->print_cr("Thread method sampler crashed"); + } + } else { + protected_task(context); + } +} + +/* +* From this method and down the call tree we attempt to protect against crashes +* using a signal handler / __try block. Don't take locks, rely on destructors or +* leave memory (in case of signal / exception) in an inconsistent state. */ +void OSThreadSampler::protected_task(const os::SuspendedThreadTaskContext& context) { + JavaThread* jth = (JavaThread*)context.thread(); + // Skip sample if we signaled a thread that moved to other state + if (!thread_state_in_java(jth)) { + return; + } + JfrGetCallTrace trace(true, jth); + frame topframe; + if (trace.get_topframe(context.ucontext(), topframe)) { + if (_stacktrace.record_thread(*jth, topframe)) { + /* If we managed to get a topframe and a stacktrace, create an event + * and put it into our array. We can't call Jfr::_stacktraces.add() + * here since it would allocate memory using malloc. Doing so while + * the stopped thread is inside malloc would deadlock. */ + _success = true; + EventExecutionSample *ev = _closure.next_event(); + ev->set_starttime(_suspend_time); + ev->set_endtime(_suspend_time); // fake to not take an end time + ev->set_sampledThread(JFR_THREAD_ID(jth)); + ev->set_state(java_lang_Thread::get_thread_status(jth->threadObj())); + } + } +} + +void OSThreadSampler::take_sample() { + run(); +} + +class JfrNativeSamplerCallback : public os::CrashProtectionCallback { + public: + JfrNativeSamplerCallback(JfrThreadSampleClosure& closure, JavaThread* jt, JfrStackFrame* frames, u4 max_frames) : + _closure(closure), _jt(jt), _stacktrace(frames, max_frames), _success(false) { + } + virtual void call(); + bool success() { return _success; } + JfrStackTrace& stacktrace() { return _stacktrace; } + + private: + JfrThreadSampleClosure& _closure; + JavaThread* _jt; + JfrStackTrace _stacktrace; + bool _success; +}; + +static void write_native_event(JfrThreadSampleClosure& closure, JavaThread* jt) { + EventNativeMethodSample *ev = closure.next_event_native(); + ev->set_starttime(JfrTicks::now()); + ev->set_sampledThread(JFR_THREAD_ID(jt)); + ev->set_state(java_lang_Thread::get_thread_status(jt->threadObj())); +} + +void JfrNativeSamplerCallback::call() { + // When a thread is only attach it will be native without a last java frame + if (!_jt->has_last_Java_frame()) { + return; + } + + frame topframe = _jt->last_frame(); + frame first_java_frame; + Method* method = NULL; + JfrGetCallTrace gct(false, _jt); + if (!gct.find_top_frame(topframe, &method, first_java_frame)) { + return; + } + if (method == NULL) { + return; + } + topframe = first_java_frame; + _success = _stacktrace.record_thread(*_jt, topframe); + if (_success) { + write_native_event(_closure, _jt); + } +} + +bool JfrThreadSampleClosure::sample_thread_in_java(JavaThread* thread, JfrStackFrame* frames, u4 max_frames) { + OSThreadSampler sampler(thread, *this, frames, max_frames); + sampler.take_sample(); + /* We don't want to allocate any memory using malloc/etc while the thread + * is stopped, so everything is stored in stack allocated memory until this + * point where the thread has been resumed again, if the sampling was a success + * we need to store the stacktrace in the stacktrace repository and update + * the event with the id that was returned. */ + if (!sampler.success()) { + return false; + } + EventExecutionSample *event = &_events[_added_java - 1]; + traceid id = JfrStackTraceRepository::add(sampler.stacktrace()); + assert(id != 0, "Stacktrace id should not be 0"); + event->set_stackTrace(id); + return true; +} + +bool JfrThreadSampleClosure::sample_thread_in_native(JavaThread* thread, JfrStackFrame* frames, u4 max_frames) { + JfrNativeSamplerCallback cb(*this, thread, frames, max_frames); + if (JfrOptionSet::sample_protection()) { + os::ThreadCrashProtection crash_protection; + if (!crash_protection.call(cb)) { + if (true) tty->print_cr("Thread method sampler crashed for native"); + } + } else { + cb.call(); + } + if (!cb.success()) { + return false; + } + EventNativeMethodSample *event = &_events_native[_added_native - 1]; + traceid id = JfrStackTraceRepository::add(cb.stacktrace()); + assert(id != 0, "Stacktrace id should not be 0"); + event->set_stackTrace(id); + return true; +} + +static const uint MAX_NR_OF_JAVA_SAMPLES = 5; +static const uint MAX_NR_OF_NATIVE_SAMPLES = 1; + +void JfrThreadSampleClosure::commit_events(JfrSampleType type) { + if (JAVA_SAMPLE == type) { + assert(_added_java <= MAX_NR_OF_JAVA_SAMPLES, "invariant"); + for (uint i = 0; i < _added_java; ++i) { + _events[i].commit(); + } + } else { + assert(NATIVE_SAMPLE == type, "invariant"); + assert(_added_native <= MAX_NR_OF_NATIVE_SAMPLES, "invariant"); + for (uint i = 0; i < _added_native; ++i) { + _events_native[i].commit(); + } + } +} + +JfrThreadSampleClosure::JfrThreadSampleClosure(EventExecutionSample* events, EventNativeMethodSample* events_native) : + _events(events), + _events_native(events_native), + _self(Thread::current()), + _added_java(0), + _added_native(0) { +} + +class JfrThreadSampler : public Thread { + friend class JfrThreadSampling; + private: + Semaphore _sample; + Thread* _sampler_thread; + JfrStackFrame* const _frames; + JavaThread* _last_thread_java; + JavaThread* _last_thread_native; + size_t _interval_java; + size_t _interval_native; + int _cur_index; + const u4 _max_frames; + volatile bool _disenrolled; + static Monitor* _transition_block_lock; + + int find_index_of_JavaThread(JavaThread** t_list, uint length, JavaThread *target); + JavaThread* next_thread(JavaThread** t_list, uint length, JavaThread* first_sampled, JavaThread* current); + void task_stacktrace(JfrSampleType type, JavaThread** last_thread); + JfrThreadSampler(size_t interval_java, size_t interval_native, u4 max_frames); + ~JfrThreadSampler(); + + void start_thread(); + + void enroll(); + void disenroll(); + void set_java_interval(size_t interval) { _interval_java = interval; }; + void set_native_interval(size_t interval) { _interval_native = interval; }; + size_t get_java_interval() { return _interval_java; }; + size_t get_native_interval() { return _interval_native; }; + + public: + void run(); + static Monitor* transition_block() { return _transition_block_lock; } + static void on_javathread_suspend(JavaThread* thread); +}; + +Monitor* JfrThreadSampler::_transition_block_lock = new Monitor(Mutex::leaf, "Trace block", true); + +static void clear_transition_block(JavaThread* jt) { + jt->clear_trace_flag(); + JfrThreadLocal* const tl = jt->jfr_thread_local(); + if (tl->is_trace_block()) { + MutexLockerEx ml(JfrThreadSampler::transition_block(), Mutex::_no_safepoint_check_flag); + JfrThreadSampler::transition_block()->notify_all(); + } +} + +bool JfrThreadSampleClosure::do_sample_thread(JavaThread* thread, JfrStackFrame* frames, u4 max_frames, JfrSampleType type) { + assert(Threads_lock->owned_by_self(), "Holding the thread table lock."); + if (thread->is_hidden_from_external_view() || thread->in_deopt_handler()) { + return false; + } + + bool ret = false; + thread->set_trace_flag(); + if (!UseMembar) { + os::serialize_thread_states(); + } + if (JAVA_SAMPLE == type) { + if (thread_state_in_java(thread)) { + ret = sample_thread_in_java(thread, frames, max_frames); + } + } else { + assert(NATIVE_SAMPLE == type, "invariant"); + if (thread_state_in_native(thread)) { + ret = sample_thread_in_native(thread, frames, max_frames); + } + } + clear_transition_block(thread); + return ret; +} + +JfrThreadSampler::JfrThreadSampler(size_t interval_java, size_t interval_native, u4 max_frames) : + _sample(), + _sampler_thread(NULL), + _frames(JfrCHeapObj::new_array(max_frames)), + _last_thread_java(NULL), + _last_thread_native(NULL), + _interval_java(interval_java), + _interval_native(interval_native), + _cur_index(-1), + _max_frames(max_frames), + _disenrolled(true) { +} + +JfrThreadSampler::~JfrThreadSampler() { + JfrCHeapObj::free(_frames, sizeof(JfrStackFrame) * _max_frames); +} + +void JfrThreadSampler::on_javathread_suspend(JavaThread* thread) { + JfrThreadLocal* const tl = thread->jfr_thread_local(); + tl->set_trace_block(); + { + MutexLockerEx ml(transition_block(), Mutex::_no_safepoint_check_flag); + while (thread->is_trace_suspend()) { + transition_block()->wait(true); + } + tl->clear_trace_block(); + } +} + +int JfrThreadSampler::find_index_of_JavaThread(JavaThread** t_list, uint length, JavaThread *target) { + assert(Threads_lock->owned_by_self(), "Holding the thread table lock."); + if (target == NULL) { + return -1; + } + for (uint i = 0; i < length; i++) { + if (target == t_list[i]) { + return (int)i; + } + } + return -1; +} + +JavaThread* JfrThreadSampler::next_thread(JavaThread** t_list, uint length, JavaThread* first_sampled, JavaThread* current) { + assert(Threads_lock->owned_by_self(), "Holding the thread table lock."); + if (current == NULL) { + _cur_index = 0; + return t_list[_cur_index]; + } + + if (_cur_index == -1 || t_list[_cur_index] != current) { + // 'current' is not at '_cur_index' so find it: + _cur_index = find_index_of_JavaThread(t_list, length, current); + assert(_cur_index != -1, "current JavaThread should be findable."); + } + _cur_index++; + + JavaThread* next = NULL; + // wrap + if ((uint)_cur_index >= length) { + _cur_index = 0; + } + next = t_list[_cur_index]; + + // sample wrap + if (next == first_sampled) { + return NULL; + } + return next; +} + +void JfrThreadSampler::start_thread() { + if (os::create_thread(this, os::os_thread)) { + os::start_thread(this); + } else { + tty->print_cr("Failed to create thread for thread sampling"); + } +} + +void JfrThreadSampler::enroll() { + if (_disenrolled) { + if (LogJFR) tty->print_cr("Enrolling thread sampler"); + _sample.signal(); + _disenrolled = false; + } +} + +void JfrThreadSampler::disenroll() { + if (!_disenrolled) { + _sample.wait(); + _disenrolled = true; + if (LogJFR) tty->print_cr("Disenrolling thread sampler"); + } +} + +static jlong get_monotonic_ms() { + return os::javaTimeNanos() / 1000000; +} + +void JfrThreadSampler::run() { + assert(_sampler_thread == NULL, "invariant"); + + initialize_thread_local_storage(); + record_stack_base_and_size(); + + _sampler_thread = this; + + jlong last_java_ms = get_monotonic_ms(); + jlong last_native_ms = last_java_ms; + while (true) { + if (!_sample.trywait()) { + // disenrolled + _sample.wait(); + last_java_ms = get_monotonic_ms(); + last_native_ms = last_java_ms; + } + _sample.signal(); + jlong java_interval = _interval_java == 0 ? max_jlong : MAX2(_interval_java, 10); + jlong native_interval = _interval_native == 0 ? max_jlong : MAX2(_interval_native, 10); + + jlong now_ms = get_monotonic_ms(); + + /* + * Let I be java_interval or native_interval. + * Let L be last_java_ms or last_native_ms. + * Let N be now_ms. + * + * Interval, I, might be max_jlong so the addition + * could potentially overflow without parenthesis (UB). Also note that + * L - N < 0. Avoid UB, by adding parenthesis. + */ + jlong next_j = java_interval + (last_java_ms - now_ms); + jlong next_n = native_interval + (last_native_ms - now_ms); + + jlong sleep_to_next = MIN2(next_j, next_n); + + if (sleep_to_next > 0) { + os::naked_short_sleep(sleep_to_next); + } + + if ((next_j - sleep_to_next) <= 0) { + task_stacktrace(JAVA_SAMPLE, &_last_thread_java); + last_java_ms = get_monotonic_ms(); + } + if ((next_n - sleep_to_next) <= 0) { + task_stacktrace(NATIVE_SAMPLE, &_last_thread_native); + last_native_ms = get_monotonic_ms(); + } + } + delete this; +} + + +void JfrThreadSampler::task_stacktrace(JfrSampleType type, JavaThread** last_thread) { + ResourceMark rm; + EventExecutionSample samples[MAX_NR_OF_JAVA_SAMPLES]; + EventNativeMethodSample samples_native[MAX_NR_OF_NATIVE_SAMPLES]; + JfrThreadSampleClosure sample_task(samples, samples_native); + + const uint sample_limit = JAVA_SAMPLE == type ? MAX_NR_OF_JAVA_SAMPLES : MAX_NR_OF_NATIVE_SAMPLES; + uint num_sample_attempts = 0; + JavaThread* start = NULL; + + { + elapsedTimer sample_time; + sample_time.start(); + { + MonitorLockerEx tlock(Threads_lock, Mutex::_allow_vm_block_flag); + int max_threads = Threads::number_of_threads(); + assert(max_threads >= 0, "Threads list is empty"); + uint index = 0; + JavaThread** threads_list = NEW_C_HEAP_ARRAY(JavaThread *, max_threads, mtInternal); + for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) { + threads_list[index++] = tp; + } + JavaThread* current = Threads::includes(*last_thread) ? *last_thread : NULL; + JavaThread* start = NULL; + + while (num_sample_attempts < sample_limit) { + current = next_thread(threads_list, index, start, current); + if (current == NULL) { + break; + } + if (start == NULL) { + start = current; // remember the thread where we started to attempt sampling + } + if (current->is_Compiler_thread()) { + continue; + } + sample_task.do_sample_thread(current, _frames, _max_frames, type); + num_sample_attempts++; + } + *last_thread = current; // remember the thread we last attempted to sample + FREE_C_HEAP_ARRAY(JavaThread *, threads_list, mtInternal); + } + sample_time.stop(); + if (LogJFR && Verbose) tty->print_cr("JFR thread sampling done in %3.7f secs with %d java %d native samples", + sample_time.seconds(), sample_task.java_entries(), sample_task.native_entries()); + } + if (num_sample_attempts > 0) { + sample_task.commit_events(type); + } +} + +static JfrThreadSampling* _instance = NULL; + +JfrThreadSampling& JfrThreadSampling::instance() { + return *_instance; +} + +JfrThreadSampling* JfrThreadSampling::create() { + assert(_instance == NULL, "invariant"); + _instance = new JfrThreadSampling(); + return _instance; +} + +void JfrThreadSampling::destroy() { + if (_instance != NULL) { + delete _instance; + _instance = NULL; + } +} + +JfrThreadSampling::JfrThreadSampling() : _sampler(NULL) {} + +JfrThreadSampling::~JfrThreadSampling() { + if (_sampler != NULL) { + _sampler->disenroll(); + } +} + +static void log(size_t interval_java, size_t interval_native) { + if (LogJFR) tty->print_cr("Updated thread sampler for java: " SIZE_FORMAT " ms, native " SIZE_FORMAT " ms", interval_java, interval_native); +} + +void JfrThreadSampling::start_sampler(size_t interval_java, size_t interval_native) { + assert(_sampler == NULL, "invariant"); + if (LogJFR) tty->print_cr("Enrolling thread sampler"); + _sampler = new JfrThreadSampler(interval_java, interval_native, JfrOptionSet::stackdepth()); + _sampler->start_thread(); + _sampler->enroll(); +} + +void JfrThreadSampling::set_sampling_interval(bool java_interval, size_t period) { + size_t interval_java = 0; + size_t interval_native = 0; + if (_sampler != NULL) { + interval_java = _sampler->get_java_interval(); + interval_native = _sampler->get_native_interval(); + } + if (java_interval) { + interval_java = period; + } else { + interval_native = period; + } + if (interval_java > 0 || interval_native > 0) { + if (_sampler == NULL) { + if (LogJFR) tty->print_cr("Creating thread sampler for java:%zu ms, native %zu ms", interval_java, interval_native); + start_sampler(interval_java, interval_native); + } else { + _sampler->set_java_interval(interval_java); + _sampler->set_native_interval(interval_native); + _sampler->enroll(); + } + assert(_sampler != NULL, "invariant"); + log(interval_java, interval_native); + } else if (_sampler != NULL) { + _sampler->disenroll(); + } +} + +void JfrThreadSampling::set_java_sample_interval(size_t period) { + if (_instance == NULL && 0 == period) { + return; + } + instance().set_sampling_interval(true, period); +} + +void JfrThreadSampling::set_native_sample_interval(size_t period) { + if (_instance == NULL && 0 == period) { + return; + } + instance().set_sampling_interval(false, period); +} + +void JfrThreadSampling::on_javathread_suspend(JavaThread* thread) { + JfrThreadSampler::on_javathread_suspend(thread); +} + +Thread* JfrThreadSampling::sampler_thread() { + if (_instance == NULL) { + return NULL; + } + return _instance->_sampler != NULL ? _instance->_sampler->_sampler_thread : NULL; +} diff --git a/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.hpp b/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..befc1d7030f0f3024e9e7c38743cd7b8df4408d7 --- /dev/null +++ b/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012, 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 SHARE_VM_JFR_PERIODIC_SAMPLING_JFRTHREADSAMPLER_HPP +#define SHARE_VM_JFR_PERIODIC_SAMPLING_JFRTHREADSAMPLER_HPP + +#include "jfr/utilities/jfrAllocation.hpp" + +class JavaThread; +class JfrStackFrame; +class JfrThreadSampler; +class Thread; + +class JfrThreadSampling : public JfrCHeapObj { + friend class JfrRecorder; + private: + JfrThreadSampler* _sampler; + void start_sampler(size_t interval_java, size_t interval_native); + void set_sampling_interval(bool java_interval, size_t period); + + JfrThreadSampling(); + ~JfrThreadSampling(); + + static JfrThreadSampling& instance(); + static JfrThreadSampling* create(); + static void destroy(); + + public: + static void set_java_sample_interval(size_t period); + static void set_native_sample_interval(size_t period); + static void on_javathread_suspend(JavaThread* thread); + static Thread* sampler_thread(); +}; + +#endif // SHARE_VM_JFR_PERIODIC_SAMPLING_JFRTHREADSAMPLER_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aaf3352f3e3d795483757891c3174ad8d61a3cbd --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp @@ -0,0 +1,93 @@ +/* + * 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 "precompiled.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" + +JfrCheckpointBlob::JfrCheckpointBlob(const u1* checkpoint, size_t size) : + _checkpoint(JfrCHeapObj::new_array(size)), + _size(size), + _next(), + _written(false) { + assert(checkpoint != NULL, "invariant"); + assert(_checkpoint != NULL, "invariant"); + memcpy(const_cast(_checkpoint), checkpoint, size); +} + +JfrCheckpointBlob::~JfrCheckpointBlob() { + JfrCHeapObj::free(const_cast(_checkpoint), _size); +} + +const JfrCheckpointBlobHandle& JfrCheckpointBlob::next() const { + return _next; +} + +void JfrCheckpointBlob::write_this(JfrCheckpointWriter& writer) const { + writer.bytes(_checkpoint, _size); +} + +void JfrCheckpointBlob::exclusive_write(JfrCheckpointWriter& writer) const { + if (!_written) { + write_this(writer); + _written = true; + } + if (_next.valid()) { + _next->exclusive_write(writer); + } +} + +void JfrCheckpointBlob::write(JfrCheckpointWriter& writer) const { + write_this(writer); + if (_next.valid()) { + _next->write(writer); + } +} + +void JfrCheckpointBlob::reset_write_state() const { + if (_written) { + _written = false; + } + if (_next.valid()) { + _next->reset_write_state(); + } +} + +void JfrCheckpointBlob::set_next(const JfrCheckpointBlobHandle& ref) { + if (_next == ref) { + return; + } + assert(_next != ref, "invariant"); + if (_next.valid()) { + _next->set_next(ref); + return; + } + _next = ref; +} + +JfrCheckpointBlobHandle JfrCheckpointBlob::make(const u1* checkpoint, size_t size) { + const JfrCheckpointBlob* cp_blob = new JfrCheckpointBlob(checkpoint, size); + assert(cp_blob != NULL, "invariant"); + return JfrCheckpointBlobReference::make(cp_blob); +} diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.hpp new file mode 100644 index 0000000000000000000000000000000000000000..50617ecb684219f8d4532d2a873e397c120417a2 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTBLOB_HPP +#define SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTBLOB_HPP + +#include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrRefCountPointer.hpp" + +class JfrCheckpointBlob; +class JfrCheckpointWriter; + +typedef RefCountPointer JfrCheckpointBlobReference; +typedef RefCountHandle JfrCheckpointBlobHandle; + +class JfrCheckpointBlob : public JfrCHeapObj { + template + friend class RefCountPointer; + private: + const u1* _checkpoint; + const size_t _size; + JfrCheckpointBlobHandle _next; + mutable bool _written; + + JfrCheckpointBlob(const u1* checkpoint, size_t size); + ~JfrCheckpointBlob(); + const JfrCheckpointBlobHandle& next() const; + void write_this(JfrCheckpointWriter& writer) const; + + public: + void write(JfrCheckpointWriter& writer) const; + void exclusive_write(JfrCheckpointWriter& writer) const; + void reset_write_state() const; + void set_next(const JfrCheckpointBlobHandle& ref); + static JfrCheckpointBlobHandle make(const u1* checkpoint, size_t size); +}; + +#endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTBLOB_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..69a4ccfef8dd74194bafee8492fe92a3a8bbad96 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2016, 2019, 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 "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp" +#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp" +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/recorder/storage/jfrMemorySpace.inline.hpp" +#include "jfr/recorder/storage/jfrStorageUtils.inline.hpp" +#include "jfr/recorder/repository/jfrChunkWriter.hpp" +#include "jfr/utilities/jfrBigEndian.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/orderAccess.inline.hpp" +#include "runtime/os.hpp" +#include "runtime/safepoint.hpp" + +typedef JfrCheckpointManager::Buffer* BufferPtr; + +static JfrCheckpointManager* _instance = NULL; + +JfrCheckpointManager& JfrCheckpointManager::instance() { + return *_instance; +} + +JfrCheckpointManager* JfrCheckpointManager::create(JfrChunkWriter& cw) { + assert(_instance == NULL, "invariant"); + _instance = new JfrCheckpointManager(cw); + return _instance; +} + +void JfrCheckpointManager::destroy() { + assert(_instance != NULL, "invariant"); + delete _instance; + _instance = NULL; +} + +JfrCheckpointManager::JfrCheckpointManager(JfrChunkWriter& cw) : + _free_list_mspace(NULL), + _epoch_transition_mspace(NULL), + _lock(NULL), + _service_thread(NULL), + _chunkwriter(cw), + _checkpoint_epoch_state(JfrTraceIdEpoch::current()) {} + +JfrCheckpointManager::~JfrCheckpointManager() { + if (_free_list_mspace != NULL) { + delete _free_list_mspace; + } + if (_epoch_transition_mspace != NULL) { + delete _epoch_transition_mspace; + } + if (_lock != NULL) { + delete _lock; + } + JfrTypeManager::clear(); +} + +static const size_t unlimited_mspace_size = 0; +static const size_t checkpoint_buffer_cache_count = 2; +static const size_t checkpoint_buffer_size = 512 * K; + +static JfrCheckpointMspace* create_mspace(size_t buffer_size, size_t limit, size_t cache_count, JfrCheckpointManager* system) { + JfrCheckpointMspace* mspace = new JfrCheckpointMspace(buffer_size, limit, cache_count, system); + if (mspace != NULL) { + mspace->initialize(); + } + return mspace; +} + +bool JfrCheckpointManager::initialize() { + assert(_free_list_mspace == NULL, "invariant"); + _free_list_mspace = create_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this); + if (_free_list_mspace == NULL) { + return false; + } + assert(_epoch_transition_mspace == NULL, "invariant"); + _epoch_transition_mspace = create_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this); + if (_epoch_transition_mspace == NULL) { + return false; + } + assert(_lock == NULL, "invariant"); + _lock = new Mutex(Monitor::leaf - 1, "Checkpoint mutex", Mutex::_allow_vm_block_flag); + if (_lock == NULL) { + return false; + } + return JfrTypeManager::initialize(); +} + +bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const { + return _service_thread != thread && OrderAccess::load_acquire((u1*)&_checkpoint_epoch_state) != JfrTraceIdEpoch::current(); +} + +void JfrCheckpointManager::synchronize_epoch() { + assert(_checkpoint_epoch_state != JfrTraceIdEpoch::current(), "invariant"); + OrderAccess::storestore(); + _checkpoint_epoch_state = JfrTraceIdEpoch::current(); +} + +void JfrCheckpointManager::shift_epoch() { + debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();) + JfrTraceIdEpoch::shift_epoch(); + assert(current_epoch != JfrTraceIdEpoch::current(), "invariant"); +} + +void JfrCheckpointManager::register_service_thread(const Thread* thread) { + _service_thread = thread; +} + +void JfrCheckpointManager::register_full(BufferPtr t, Thread* thread) { + // nothing here at the moment + assert(t != NULL, "invariant"); + assert(t->acquired_by(thread), "invariant"); + assert(t->retired(), "invariant"); +} + +void JfrCheckpointManager::lock() { + assert(!_lock->owned_by_self(), "invariant"); + _lock->lock_without_safepoint_check(); +} + +void JfrCheckpointManager::unlock() { + _lock->unlock(); +} + +#ifdef ASSERT + +bool JfrCheckpointManager::is_locked() const { + return _lock->owned_by_self(); +} + +static void assert_free_lease(const BufferPtr buffer) { + assert(buffer != NULL, "invariant"); + assert(buffer->acquired_by_self(), "invariant"); + assert(buffer->lease(), "invariant"); +} + +static void assert_release(const BufferPtr buffer) { + assert(buffer != NULL, "invariant"); + assert(buffer->lease(), "invariant"); + assert(buffer->acquired_by_self(), "invariant"); +} + +#endif // ASSERT + +static BufferPtr lease_free(size_t size, JfrCheckpointMspace* mspace, size_t retry_count, Thread* thread) { + static const size_t max_elem_size = mspace->min_elem_size(); // min is max + BufferPtr buffer; + if (size <= max_elem_size) { + BufferPtr buffer = mspace_get_free_lease_with_retry(size, mspace, retry_count, thread); + if (buffer != NULL) { + DEBUG_ONLY(assert_free_lease(buffer);) + return buffer; + } + } + buffer = mspace_allocate_transient_lease_to_free(size, mspace, thread); + DEBUG_ONLY(assert_free_lease(buffer);) + return buffer; +} + +static const size_t lease_retry = 10; + +BufferPtr JfrCheckpointManager::lease_buffer(Thread* thread, size_t size /* 0 */) { + JfrCheckpointManager& manager = instance(); + if (manager.use_epoch_transition_mspace(thread)) { + return lease_free(size, manager._epoch_transition_mspace, lease_retry, thread); + } + return lease_free(size, manager._free_list_mspace, lease_retry, thread); +} + +/* +* If the buffer was a "lease" from the free list, release back. +* +* The buffer is effectively invalidated for the thread post-return, +* and the caller should take means to ensure that it is not referenced. +*/ +static void release(BufferPtr const buffer, Thread* thread) { + DEBUG_ONLY(assert_release(buffer);) + buffer->clear_lease(); + buffer->release(); +} + +BufferPtr JfrCheckpointManager::flush(BufferPtr old, size_t used, size_t requested, Thread* thread) { + assert(old != NULL, "invariant"); + assert(old->lease(), "invariant"); + if (0 == requested) { + // indicates a lease is being returned + release(old, thread); + return NULL; + } + // migration of in-flight information + BufferPtr const new_buffer = lease_buffer(thread, used + requested); + if (new_buffer != NULL) { + migrate_outstanding_writes(old, new_buffer, used, requested); + } + release(old, thread); + return new_buffer; // might be NULL +} + +// offsets into the JfrCheckpointEntry +static const juint starttime_offset = sizeof(jlong); +static const juint duration_offset = starttime_offset + sizeof(jlong); +static const juint flushpoint_offset = duration_offset + sizeof(jlong); +static const juint types_offset = flushpoint_offset + sizeof(juint); +static const juint payload_offset = types_offset + sizeof(juint); + +template +static Return read_data(const u1* data) { + return JfrBigEndian::read(data); +} + +static jlong total_size(const u1* data) { + return read_data(data); +} + +static jlong starttime(const u1* data) { + return read_data(data + starttime_offset); +} + +static jlong duration(const u1* data) { + return read_data(data + duration_offset); +} + +static bool is_flushpoint(const u1* data) { + return read_data(data + flushpoint_offset) == (juint)1; +} + +static juint number_of_types(const u1* data) { + return read_data(data + types_offset); +} + +static void write_checkpoint_header(JfrChunkWriter& cw, intptr_t offset_prev_cp_event, const u1* data) { + cw.reserve(sizeof(u4)); + cw.write((u8)EVENT_CHECKPOINT); + cw.write(starttime(data)); + cw.write(duration(data)); + cw.write((jlong)offset_prev_cp_event); + cw.write(is_flushpoint(data)); + cw.write(number_of_types(data)); +} + +static void write_checkpoint_content(JfrChunkWriter& cw, const u1* data, size_t size) { + assert(data != NULL, "invariant"); + cw.write_unbuffered(data + payload_offset, size); +} + +static size_t write_checkpoint_event(JfrChunkWriter& cw, const u1* data) { + assert(data != NULL, "invariant"); + const intptr_t previous_checkpoint_event = cw.previous_checkpoint_offset(); + const intptr_t event_begin = cw.current_offset(); + const intptr_t offset_to_previous_checkpoint_event = 0 == previous_checkpoint_event ? 0 : previous_checkpoint_event - event_begin; + const jlong total_checkpoint_size = total_size(data); + write_checkpoint_header(cw, offset_to_previous_checkpoint_event, data); + write_checkpoint_content(cw, data, total_checkpoint_size - sizeof(JfrCheckpointEntry)); + const jlong checkpoint_event_size = cw.current_offset() - event_begin; + cw.write_padded_at_offset(checkpoint_event_size, event_begin); + cw.set_previous_checkpoint_offset(event_begin); + return (size_t)total_checkpoint_size; +} + +static size_t write_checkpoints(JfrChunkWriter& cw, const u1* data, size_t size) { + assert(cw.is_valid(), "invariant"); + assert(data != NULL, "invariant"); + assert(size > 0, "invariant"); + const u1* const limit = data + size; + const u1* next_entry = data; + size_t processed = 0; + while (next_entry < limit) { + const size_t checkpoint_size = write_checkpoint_event(cw, next_entry); + processed += checkpoint_size; + next_entry += checkpoint_size; + } + assert(next_entry == limit, "invariant"); + return processed; +} + +template +class CheckpointWriteOp { + private: + JfrChunkWriter& _writer; + size_t _processed; + public: + typedef T Type; + CheckpointWriteOp(JfrChunkWriter& writer) : _writer(writer), _processed(0) {} + bool write(Type* t, const u1* data, size_t size) { + _processed += write_checkpoints(_writer, data, size); + return true; + } + size_t processed() const { return _processed; } +}; + +typedef CheckpointWriteOp WriteOperation; +typedef MutexedWriteOp MutexedWriteOperation; +typedef ReleaseOp CheckpointReleaseOperation; +typedef CompositeOperation CheckpointWriteOperation; + +static size_t write_mspace_exclusive(JfrCheckpointMspace* mspace, JfrChunkWriter& chunkwriter) { + Thread* const thread = Thread::current(); + WriteOperation wo(chunkwriter); + MutexedWriteOperation mwo(wo); + CheckpointReleaseOperation cro(mspace, thread, false); + CheckpointWriteOperation cpwo(&mwo, &cro); + assert(mspace->is_full_empty(), "invariant"); + process_free_list(cpwo, mspace); + return wo.processed(); +} + +size_t JfrCheckpointManager::write() { + const size_t processed = write_mspace_exclusive(_free_list_mspace, _chunkwriter); + synchronize_epoch(); + return processed; +} + +size_t JfrCheckpointManager::write_epoch_transition_mspace() { + return write_mspace_exclusive(_epoch_transition_mspace, _chunkwriter); +} + +typedef DiscardOp > DiscardOperation; +size_t JfrCheckpointManager::clear() { + DiscardOperation discarder(mutexed); // mutexed discard mode + process_free_list(discarder, _free_list_mspace); + process_free_list(discarder, _epoch_transition_mspace); + synchronize_epoch(); + return discarder.processed(); +} + +size_t JfrCheckpointManager::write_types() { + JfrCheckpointWriter writer(false, true, Thread::current()); + JfrTypeManager::write_types(writer); + return writer.used_size(); +} + +size_t JfrCheckpointManager::write_safepoint_types() { + // this is also a "flushpoint" + JfrCheckpointWriter writer(true, true, Thread::current()); + JfrTypeManager::write_safepoint_types(writer); + return writer.used_size(); +} + +void JfrCheckpointManager::write_type_set() { + JfrTypeManager::write_type_set(); +} + +void JfrCheckpointManager::write_type_set_for_unloaded_classes() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); + JfrTypeManager::write_type_set_for_unloaded_classes(); +} + +void JfrCheckpointManager::create_thread_checkpoint(JavaThread* jt) { + JfrTypeManager::create_thread_checkpoint(jt); +} + +void JfrCheckpointManager::write_thread_checkpoint(JavaThread* jt) { + JfrTypeManager::write_thread_checkpoint(jt); +} diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e7d4d81f72e989ea9f3119f6e375dffc289d9502 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTMANAGER_HPP +#define SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTMANAGER_HPP + +#include "jfr/recorder/storage/jfrBuffer.hpp" +#include "jfr/recorder/storage/jfrMemorySpace.hpp" +#include "jfr/recorder/storage/jfrMemorySpaceRetrieval.hpp" + +class JfrCheckpointManager; +class JfrChunkWriter; +class JfrSerializer; +class JfrTypeManager; +class Mutex; +class Thread; + +struct JfrCheckpointEntry { + jlong size; + jlong start_time; + jlong duration; + juint flushpoint; + juint nof_segments; +}; + +typedef JfrMemorySpace JfrCheckpointMspace; + +// +// Responsible for maintaining checkpoints and by implication types. +// A checkpoint is an event that has a payload consisting of constant types. +// A constant type is a binary relation, a set of key-value pairs. +// +class JfrCheckpointManager : public JfrCHeapObj { + public: + typedef JfrCheckpointMspace::Type Buffer; + private: + JfrCheckpointMspace* _free_list_mspace; + JfrCheckpointMspace* _epoch_transition_mspace; + Mutex* _lock; + const Thread* _service_thread; + JfrChunkWriter& _chunkwriter; + u1 _checkpoint_epoch_state; + + // mspace callback + void register_full(Buffer* t, Thread* thread); + void lock(); + void unlock(); + DEBUG_ONLY(bool is_locked() const;) + + static Buffer* lease_buffer(Thread* t, size_t size = 0); + static Buffer* flush(Buffer* old, size_t used, size_t requested, Thread* t); + + size_t clear(); + size_t write(); + size_t write_epoch_transition_mspace(); + size_t write_types(); + size_t write_safepoint_types(); + void write_type_set(); + void shift_epoch(); + void synchronize_epoch(); + bool use_epoch_transition_mspace(const Thread* t) const; + + JfrCheckpointManager(JfrChunkWriter& cw); + ~JfrCheckpointManager(); + + static JfrCheckpointManager& instance(); + static JfrCheckpointManager* create(JfrChunkWriter& cw); + bool initialize(); + static void destroy(); + + public: + void register_service_thread(const Thread* t); + static void write_type_set_for_unloaded_classes(); + static void create_thread_checkpoint(JavaThread* jt); + static void write_thread_checkpoint(JavaThread* jt); + + friend class JfrRecorder; + friend class JfrRecorderService; + friend class JfrCheckpointFlush; + friend class JfrCheckpointWriter; + friend class JfrSerializer; + friend class JfrStackTraceRepository; + template class, typename> + friend class JfrMemorySpace; +}; + +#endif //SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTMANAGER_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1fc884780b2e79217982b3b3d61f3e3fb60d5939 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/writers/jfrBigEndianWriter.hpp" + +JfrCheckpointFlush::JfrCheckpointFlush(Type* old, size_t used, size_t requested, Thread* t) : + _result(JfrCheckpointManager::flush(old, used, requested, t)) {} + +JfrCheckpointWriter::JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread) : + JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(thread), thread), + _time(JfrTicks::now()), + _offset(0), + _count(0), + _flushpoint(flushpoint), + _header(header) { + assert(this->is_acquired(), "invariant"); + assert(0 == this->current_offset(), "invariant"); + if (_header) { + reserve(sizeof(JfrCheckpointEntry)); + } +} + +static void write_checkpoint_header(u1* pos, jlong size, jlong time, bool flushpoint, juint type_count) { + assert(pos != NULL, "invariant"); + JfrBigEndianWriter be_writer(pos, sizeof(JfrCheckpointEntry)); + be_writer.write(size); + be_writer.write(time); + be_writer.write(JfrTicks::now().value() - time); + be_writer.write(flushpoint ? (juint)1 : (juint)0); + be_writer.write(type_count); + assert(be_writer.is_valid(), "invariant"); +} + +JfrCheckpointWriter::~JfrCheckpointWriter() { + assert(this->is_acquired(), "invariant"); + if (!this->is_valid() || !_header) { + release(); + return; + } + if (0 == count()) { + assert(this->used_size() == sizeof(JfrCheckpointEntry), "invariant"); + this->seek(_offset); + release(); + return; + } + assert(_header, "invariant"); + assert(this->is_valid(), "invariant"); + assert(count() > 0, "invariant"); + assert(this->used_size() > sizeof(JfrCheckpointEntry), "invariant"); + const jlong size = this->current_offset(); + assert(size + this->start_pos() == this->current_pos(), "invariant"); + write_checkpoint_header(const_cast(this->start_pos()), size, _time, is_flushpoint(), count()); + release(); +} + +void JfrCheckpointWriter::set_flushpoint(bool flushpoint) { + _flushpoint = flushpoint; +} + +bool JfrCheckpointWriter::is_flushpoint() const { + return _flushpoint; +} + +juint JfrCheckpointWriter::count() const { + return _count; +} + +void JfrCheckpointWriter::set_count(juint count) { + _count = count; +} + +void JfrCheckpointWriter::release() { + assert(this->is_acquired(), "invariant"); + if (!this->is_valid() || this->used_size() == 0) { + return; + } + assert(this->used_size() > 0, "invariant"); + // write through to backing storage + this->commit(); + assert(0 == this->current_offset(), "invariant"); +} + +void JfrCheckpointWriter::write_type(JfrTypeId type_id) { + assert(type_id < TYPES_END, "invariant"); + write(type_id); + increment(); +} + +void JfrCheckpointWriter::write_key(u8 key) { + write(key); +} + +void JfrCheckpointWriter::increment() { + ++_count; +} + +void JfrCheckpointWriter::write_count(u4 nof_entries) { + write((u4)nof_entries); +} + +void JfrCheckpointWriter::write_count(u4 nof_entries, jlong offset) { + write_padded_at_offset(nof_entries, offset); +} + +const u1* JfrCheckpointWriter::session_data(size_t* size, const JfrCheckpointContext* ctx /* 0 */) { + assert(this->is_acquired(), "wrong state!"); + if (!this->is_valid()) { + *size = 0; + return NULL; + } + if (ctx != NULL) { + const u1* session_start_pos = this->start_pos() + ctx->offset; + *size = this->current_pos() - session_start_pos; + return session_start_pos; + } + *size = this->used_size(); + assert(this->start_pos() + *size == this->current_pos(), "invariant"); + write_checkpoint_header(const_cast(this->start_pos()), this->used_offset(), _time, is_flushpoint(), count()); + this->seek(_offset + (_header ? sizeof(JfrCheckpointEntry) : 0)); + set_count(0); + return this->start_pos(); +} + +const JfrCheckpointContext JfrCheckpointWriter::context() const { + JfrCheckpointContext ctx; + ctx.offset = this->current_offset(); + ctx.count = this->count(); + return ctx; +} + +void JfrCheckpointWriter::set_context(const JfrCheckpointContext ctx) { + this->seek(ctx.offset); + set_count(ctx.count); +} +bool JfrCheckpointWriter::has_data() const { + return this->used_size() > sizeof(JfrCheckpointEntry); +} + +JfrCheckpointBlobHandle JfrCheckpointWriter::checkpoint_blob() { + size_t size = 0; + const u1* data = session_data(&size); + return JfrCheckpointBlob::make(data, size); +} + +JfrCheckpointBlobHandle JfrCheckpointWriter::copy(const JfrCheckpointContext* ctx /* 0 */) { + if (ctx == NULL) { + return checkpoint_blob(); + } + size_t size = 0; + const u1* data = session_data(&size, ctx); + return JfrCheckpointBlob::make(data, size); +} + +JfrCheckpointBlobHandle JfrCheckpointWriter::move(const JfrCheckpointContext* ctx /* 0 */) { + JfrCheckpointBlobHandle data = copy(ctx); + if (ctx != NULL) { + const_cast(ctx)->count = 0; + set_context(*ctx); + } + return data; +} diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bb01cb56e4d16b805237a328e70b6ff6ef2935a2 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTWRITER_HPP +#define SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTWRITER_HPP + +#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp" +#include "jfr/recorder/storage/jfrBuffer.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "jfr/writers/jfrEventWriterHost.inline.hpp" +#include "jfr/writers/jfrMemoryWriterHost.inline.hpp" +#include "jfr/writers/jfrStorageAdapter.hpp" + +class Thread; + +class JfrCheckpointFlush : public StackObj { + public: + typedef JfrBuffer Type; + JfrCheckpointFlush(Type* old, size_t used, size_t requested, Thread* t); + Type* result() { return _result; } + private: + Type* _result; +}; + +typedef Adapter JfrCheckpointAdapter; +typedef AcquireReleaseMemoryWriterHost JfrTransactionalCheckpointWriter; +typedef EventWriterHost JfrCheckpointWriterBase; + +struct JfrCheckpointContext { + jlong offset; + juint count; +}; + +class JfrCheckpointWriter : public JfrCheckpointWriterBase { + friend class JfrSerializerRegistration; + private: + JfrTicks _time; + jlong _offset; + juint _count; + bool _flushpoint; + bool _header; + + juint count() const; + void set_count(juint count); + void increment(); + void set_flushpoint(bool flushpoint); + bool is_flushpoint() const; + const u1* session_data(size_t* size, const JfrCheckpointContext* ctx = NULL); + void release(); + + public: + JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread); + ~JfrCheckpointWriter(); + void write_type(JfrTypeId type_id); + void write_count(u4 nof_entries); + void write_count(u4 nof_entries, jlong offset); + void write_key(u8 key); + const JfrCheckpointContext context() const; + void set_context(const JfrCheckpointContext ctx); + bool has_data() const; + JfrCheckpointBlobHandle checkpoint_blob(); + JfrCheckpointBlobHandle copy(const JfrCheckpointContext* ctx = NULL); + JfrCheckpointBlobHandle move(const JfrCheckpointContext* ctx = NULL); +}; + +#endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTWRITER_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c4d72f4d6870a1130b7ff1e5cc852e6e9df3de4 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/recorder/checkpoint/jfrMetadataEvent.hpp" +#include "jfr/recorder/repository/jfrChunkWriter.hpp" +#include "oops/klass.inline.hpp" +#include "oops/oop.inline.hpp" +#include "oops/typeArrayOop.hpp" +#include "runtime/semaphore.hpp" +#include "runtime/thread.inline.hpp" + +static jbyteArray _metadata_blob = NULL; +static Semaphore metadata_mutex_semaphore(1); + +void JfrMetadataEvent::lock() { + metadata_mutex_semaphore.wait(); +} + +void JfrMetadataEvent::unlock() { + metadata_mutex_semaphore.signal(); +} + +static void write_metadata_blob(JfrChunkWriter& chunkwriter, jbyteArray metadata_blob) { + if (metadata_blob != NULL) { + const typeArrayOop arr = (typeArrayOop)JfrJavaSupport::resolve_non_null(metadata_blob); + assert(arr != NULL, "invariant"); + const int length = arr->length(); + Klass* const k = arr->klass(); + assert(k != NULL && k->oop_is_array(), "invariant"); + const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast(k); + const jbyte* const data_address = arr->byte_at_addr(0); + chunkwriter.write_unbuffered(data_address, length); + } +} + +// the semaphore is assumed to be locked (was locked previous safepoint) +size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter, jlong metadata_offset) { + assert(chunkwriter.is_valid(), "invariant"); + assert(chunkwriter.current_offset() == metadata_offset, "invariant"); + // header + chunkwriter.reserve(sizeof(u4)); + chunkwriter.write(EVENT_METADATA); // ID 0 + // time data + chunkwriter.write(JfrTicks::now()); + chunkwriter.write((u8)0); // duration + chunkwriter.write((u8)0); // metadata id + write_metadata_blob(chunkwriter, _metadata_blob); // payload + unlock(); // open up for java to provide updated metadata + // fill in size of metadata descriptor event + const jlong size_written = chunkwriter.current_offset() - metadata_offset; + chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset); + return size_written; +} + +void JfrMetadataEvent::update(jbyteArray metadata) { + JavaThread* thread = (JavaThread*)Thread::current(); + assert(thread->is_Java_thread(), "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread)); + lock(); + if (_metadata_blob != NULL) { + JfrJavaSupport::destroy_global_jni_handle(_metadata_blob); + } + const oop new_desc_oop = JfrJavaSupport::resolve_non_null(metadata); + _metadata_blob = new_desc_oop != NULL ? (jbyteArray)JfrJavaSupport::global_jni_handle(new_desc_oop, thread) : NULL; + unlock(); +} diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.hpp new file mode 100644 index 0000000000000000000000000000000000000000..09845069df63d942e1ef7adf8cccd96d24dfd482 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRMETADATAEVENT_HPP +#define SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRMETADATAEVENT_HPP + +#include "jni.h" +#include "memory/allocation.hpp" + +class JfrChunkWriter; + +// +// Metadata is continuously updated in Java as event classes are loaded / unloaded. +// Using update(), Java stores a binary representation back to native. +// This is for easy access on chunk finalization as well as having it readily available in the case of fatal error. +// +class JfrMetadataEvent : AllStatic { + public: + static void lock(); + static void unlock(); + static size_t write(JfrChunkWriter& writer, jlong metadata_offset); + static void update(jbyteArray metadata); +}; + +#endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRMETADATAEVENT_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d090a1891dbbb3158050e25b2dea22386ea10ef2 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2016, 2019, 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 "precompiled.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp" +#include "jfr/utilities/jfrResourceManager.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/jniHandles.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/semaphore.hpp" +#include "utilities/growableArray.hpp" + +class ThreadGroupExclusiveAccess : public StackObj { + private: + static Semaphore _mutex_semaphore; + public: + ThreadGroupExclusiveAccess() { _mutex_semaphore.wait(); } + ~ThreadGroupExclusiveAccess() { _mutex_semaphore.signal(); } +}; + +Semaphore ThreadGroupExclusiveAccess::_mutex_semaphore(1); +JfrThreadGroup* JfrThreadGroup::_instance = NULL; + +class JfrThreadGroupPointers : public ResourceObj { + private: + const Handle _thread_group_handle; + jweak _thread_group_weak_ref; + public: + JfrThreadGroupPointers(Handle thread_group_handle, jweak thread_group_weak_ref); + Handle thread_group_handle() const; + jweak thread_group_weak_ref() const; + oopDesc* const thread_group_oop() const; + jweak transfer_weak_global_handle_ownership(); + void clear_weak_ref(); +}; + +JfrThreadGroupPointers::JfrThreadGroupPointers(Handle thread_group_handle, jweak thread_group_weak_ref) : + _thread_group_handle(thread_group_handle), + _thread_group_weak_ref(thread_group_weak_ref) {} + +Handle JfrThreadGroupPointers::thread_group_handle() const { + return _thread_group_handle; +} + +jweak JfrThreadGroupPointers::thread_group_weak_ref() const { + return _thread_group_weak_ref; +} + +oopDesc* const JfrThreadGroupPointers::thread_group_oop() const { + assert(_thread_group_weak_ref == NULL || + JNIHandles::resolve_non_null(_thread_group_weak_ref) == _thread_group_handle(), "invariant"); + return _thread_group_handle(); +} + +jweak JfrThreadGroupPointers::transfer_weak_global_handle_ownership() { + jweak temp = _thread_group_weak_ref; + _thread_group_weak_ref = NULL; + return temp; +} + +void JfrThreadGroupPointers::clear_weak_ref() { + if (NULL != _thread_group_weak_ref) { + JNIHandles::destroy_weak_global(_thread_group_weak_ref); + } +} + +class JfrThreadGroupsHelper : public ResourceObj { + private: + static const int invalid_iterator_pos = -1; + GrowableArray* _thread_group_hierarchy; + int _current_iterator_pos; + + int populate_thread_group_hierarchy(const JavaThread* jt, Thread* current); + JfrThreadGroupPointers& at(int index); + + public: + JfrThreadGroupsHelper(const JavaThread* jt, Thread* current); + ~JfrThreadGroupsHelper(); + JfrThreadGroupPointers& next(); + bool is_valid() const; + bool has_next() const; +}; + +JfrThreadGroupsHelper::JfrThreadGroupsHelper(const JavaThread* jt, Thread* current) { + _thread_group_hierarchy = new GrowableArray(10, false, mtTracing); + _current_iterator_pos = populate_thread_group_hierarchy(jt, current) - 1; +} + +JfrThreadGroupsHelper::~JfrThreadGroupsHelper() { + assert(_current_iterator_pos == invalid_iterator_pos, "invariant"); + for (int i = 0; i < _thread_group_hierarchy->length(); ++i) { + _thread_group_hierarchy->at(i)->clear_weak_ref(); + } +} + +JfrThreadGroupPointers& JfrThreadGroupsHelper::at(int index) { + assert(_thread_group_hierarchy != NULL, "invariant"); + assert(index > invalid_iterator_pos && index < _thread_group_hierarchy->length(), "invariant"); + return *(_thread_group_hierarchy->at(index)); +} + +bool JfrThreadGroupsHelper::has_next() const { + return _current_iterator_pos > invalid_iterator_pos; +} + +bool JfrThreadGroupsHelper::is_valid() const { + return (_thread_group_hierarchy != NULL && _thread_group_hierarchy->length() > 0); +} + +JfrThreadGroupPointers& JfrThreadGroupsHelper::next() { + assert(is_valid(), "invariant"); + return at(_current_iterator_pos--); +} + +/* + * If not at a safepoint, we create global weak references for + * all reachable threadgroups for this thread. + * If we are at a safepoint, the caller is the VMThread during + * JFR checkpointing. It can use naked oops, because nothing + * will move before the list of threadgroups is cleared and + * mutator threads restarted. The threadgroup list is cleared + * later by the VMThread as one of the final steps in JFR checkpointing + * (not here). + */ +int JfrThreadGroupsHelper::populate_thread_group_hierarchy(const JavaThread* jt, Thread* current) { + assert(jt != NULL && jt->is_Java_thread(), "invariant"); + assert(current != NULL, "invariant"); + assert(_thread_group_hierarchy != NULL, "invariant"); + + // immediate thread group + Handle thread_group_handle(current, java_lang_Thread::threadGroup(jt->threadObj())); + if (thread_group_handle == NULL) { + return 0; + } + + const bool use_weak_handles = !SafepointSynchronize::is_at_safepoint(); + jweak thread_group_weak_ref = use_weak_handles ? JNIHandles::make_weak_global(thread_group_handle) : NULL; + + JfrThreadGroupPointers* thread_group_pointers = new JfrThreadGroupPointers(thread_group_handle, thread_group_weak_ref); + _thread_group_hierarchy->append(thread_group_pointers); + // immediate parent thread group + oop parent_thread_group_obj = java_lang_ThreadGroup::parent(thread_group_handle()); + Handle parent_thread_group_handle(current, parent_thread_group_obj); + + // and check parents parents... + while (!(parent_thread_group_handle == NULL)) { + const jweak parent_group_weak_ref = use_weak_handles ? JNIHandles::make_weak_global(parent_thread_group_handle) : NULL; + thread_group_pointers = new JfrThreadGroupPointers(parent_thread_group_handle, parent_group_weak_ref); + _thread_group_hierarchy->append(thread_group_pointers); + parent_thread_group_obj = java_lang_ThreadGroup::parent(parent_thread_group_handle()); + parent_thread_group_handle = Handle(current, parent_thread_group_obj); + } + return _thread_group_hierarchy->length(); +} + +static traceid next_id() { + static traceid _current_threadgroup_id = 0; + return ++_current_threadgroup_id; +} + +class JfrThreadGroup::JfrThreadGroupEntry : public JfrCHeapObj { + friend class JfrThreadGroup; + private: + traceid _thread_group_id; + traceid _parent_group_id; + char* _thread_group_name; // utf8 format + // If an entry is created during a safepoint, the + // _thread_group_oop contains a direct oop to + // the java.lang.ThreadGroup object. + // If an entry is created on javathread exit time (not at safepoint), + // _thread_group_weak_ref contains a JNI weak global handle + // indirection to the java.lang.ThreadGroup object. + // Note: we cannot use a union here since CHECK_UNHANDLED_OOPS makes oop have + // a ctor which isn't allowed in a union by the SunStudio compiler + oop _thread_group_oop; + jweak _thread_group_weak_ref; + + JfrThreadGroupEntry(const char* tgstr, JfrThreadGroupPointers& ptrs); + ~JfrThreadGroupEntry(); + + traceid thread_group_id() const { return _thread_group_id; } + void set_thread_group_id(traceid tgid) { _thread_group_id = tgid; } + + const char* const thread_group_name() const { return _thread_group_name; } + void set_thread_group_name(const char* tgname); + + traceid parent_group_id() const { return _parent_group_id; } + void set_parent_group_id(traceid pgid) { _parent_group_id = pgid; } + + void set_thread_group(JfrThreadGroupPointers& ptrs); + bool is_equal(const JfrThreadGroupPointers& ptrs) const; + const oop thread_group() const; +}; + +JfrThreadGroup::JfrThreadGroupEntry::JfrThreadGroupEntry(const char* tgname, JfrThreadGroupPointers& ptrs) : + _thread_group_id(0), + _parent_group_id(0), + _thread_group_name(NULL), + _thread_group_oop(NULL), + _thread_group_weak_ref(NULL) { + set_thread_group_name(tgname); + set_thread_group(ptrs); +} + +JfrThreadGroup::JfrThreadGroupEntry::~JfrThreadGroupEntry() { + if (_thread_group_name != NULL) { + JfrCHeapObj::free(_thread_group_name, strlen(_thread_group_name) + 1); + } + if (_thread_group_weak_ref != NULL) { + JNIHandles::destroy_weak_global(_thread_group_weak_ref); + } +} + +void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group_name(const char* tgname) { + assert(_thread_group_name == NULL, "invariant"); + if (tgname != NULL) { + size_t len = strlen(tgname); + _thread_group_name = JfrCHeapObj::new_array(len + 1); + strncpy(_thread_group_name, tgname, len + 1); + } +} + +const oop JfrThreadGroup::JfrThreadGroupEntry::thread_group() const { + return _thread_group_weak_ref != NULL ? JNIHandles::resolve(_thread_group_weak_ref) : _thread_group_oop; +} + +void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group(JfrThreadGroupPointers& ptrs) { + _thread_group_weak_ref = ptrs.transfer_weak_global_handle_ownership(); + if (_thread_group_weak_ref == NULL) { + _thread_group_oop = ptrs.thread_group_oop(); + assert(_thread_group_oop != NULL, "invariant"); + } else { + _thread_group_oop = NULL; + } +} + +JfrThreadGroup::JfrThreadGroup() : _list(NULL) { + _list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray(30, true); +} + +JfrThreadGroup::~JfrThreadGroup() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + if (_list != NULL) { + for (int i = 0; i < _list->length(); i++) { + JfrThreadGroupEntry* e = _list->at(i); + delete e; + } + delete _list; + } +} + +JfrThreadGroup* JfrThreadGroup::instance() { + return _instance; +} + +void JfrThreadGroup::set_instance(JfrThreadGroup* new_instance) { + _instance = new_instance; +} + +traceid JfrThreadGroup::thread_group_id(const JavaThread* jt, Thread* current) { + ResourceMark rm(current); + HandleMark hm(current); + JfrThreadGroupsHelper helper(jt, current); + return helper.is_valid() ? thread_group_id_internal(helper) : 0; +} + +traceid JfrThreadGroup::thread_group_id(JavaThread* jt) { + assert(!JfrStream_lock->owned_by_self(), "holding stream lock but should not hold it here"); + return thread_group_id(jt, jt); +} + +traceid JfrThreadGroup::thread_group_id_internal(JfrThreadGroupsHelper& helper) { + ThreadGroupExclusiveAccess lock; + JfrThreadGroup* tg_instance = instance(); + if (tg_instance == NULL) { + tg_instance = new JfrThreadGroup(); + if (tg_instance == NULL) { + return 0; + } + set_instance(tg_instance); + } + + JfrThreadGroupEntry* tge = NULL; + int parent_thread_group_id = 0; + while (helper.has_next()) { + JfrThreadGroupPointers& ptrs = helper.next(); + tge = tg_instance->find_entry(ptrs); + if (NULL == tge) { + tge = tg_instance->new_entry(ptrs); + assert(tge != NULL, "invariant"); + tge->set_parent_group_id(parent_thread_group_id); + } + parent_thread_group_id = tge->thread_group_id(); + } + // the last entry in the hierarchy is the immediate thread group + return tge->thread_group_id(); +} + +bool JfrThreadGroup::JfrThreadGroupEntry::is_equal(const JfrThreadGroupPointers& ptrs) const { + return ptrs.thread_group_oop() == thread_group(); +} + +JfrThreadGroup::JfrThreadGroupEntry* +JfrThreadGroup::find_entry(const JfrThreadGroupPointers& ptrs) const { + for (int index = 0; index < _list->length(); ++index) { + JfrThreadGroupEntry* curtge = _list->at(index); + if (curtge->is_equal(ptrs)) { + return curtge; + } + } + return (JfrThreadGroupEntry*) NULL; +} + +// Assumes you already searched for the existence +// of a corresponding entry in find_entry(). +JfrThreadGroup::JfrThreadGroupEntry* +JfrThreadGroup::new_entry(JfrThreadGroupPointers& ptrs) { + typeArrayOop tg_name = java_lang_ThreadGroup::name(ptrs.thread_group_oop()); + JfrThreadGroupEntry* const tge = + new JfrThreadGroupEntry(UNICODE::as_utf8((jchar*) tg_name->base(T_CHAR), tg_name->length()), ptrs); + add_entry(tge); + return tge; +} + +int JfrThreadGroup::add_entry(JfrThreadGroupEntry* tge) { + assert(tge != NULL, "attempting to add a null entry!"); + assert(0 == tge->thread_group_id(), "id must be unassigned!"); + tge->set_thread_group_id(next_id()); + return _list->append(tge); +} + +void JfrThreadGroup::write_thread_group_entries(JfrCheckpointWriter& writer) const { + assert(_list != NULL && !_list->is_empty(), "should not need be here!"); + const int number_of_tg_entries = _list->length(); + writer.write_count(number_of_tg_entries); + for (int index = 0; index < number_of_tg_entries; ++index) { + const JfrThreadGroupEntry* const curtge = _list->at(index); + writer.write_key(curtge->thread_group_id()); + writer.write(curtge->parent_group_id()); + writer.write(curtge->thread_group_name()); + } +} + +void JfrThreadGroup::write_selective_thread_group(JfrCheckpointWriter* writer, traceid thread_group_id) const { + assert(writer != NULL, "invariant"); + assert(_list != NULL && !_list->is_empty(), "should not need be here!"); + const int number_of_tg_entries = _list->length(); + + // save context + const JfrCheckpointContext ctx = writer->context(); + writer->write_type(TYPE_THREADGROUP); + const jlong count_offset = writer->reserve(sizeof(u4)); // Don't know how many yet + int number_of_entries_written = 0; + for (int index = number_of_tg_entries - 1; index >= 0; --index) { + const JfrThreadGroupEntry* const curtge = _list->at(index); + if (thread_group_id == curtge->thread_group_id()) { + writer->write_key(curtge->thread_group_id()); + writer->write(curtge->parent_group_id()); + writer->write(curtge->thread_group_name()); + ++number_of_entries_written; + thread_group_id = curtge->parent_group_id(); + } + } + if (number_of_entries_written == 0) { + // nothing to write, restore context + writer->set_context(ctx); + return; + } + assert(number_of_entries_written > 0, "invariant"); + writer->write_count(number_of_entries_written, count_offset); +} + +// Write out JfrThreadGroup instance and then delete it +void JfrThreadGroup::serialize(JfrCheckpointWriter& writer) { + ThreadGroupExclusiveAccess lock; + JfrThreadGroup* tg_instance = instance(); + assert(tg_instance != NULL, "invariant"); + ResourceManager tg_handle(tg_instance); + set_instance(NULL); + tg_handle->write_thread_group_entries(writer); +} + +// for writing a particular thread group +void JfrThreadGroup::serialize(JfrCheckpointWriter* writer, traceid thread_group_id) { + assert(writer != NULL, "invariant"); + ThreadGroupExclusiveAccess lock; + JfrThreadGroup* const tg_instance = instance(); + assert(tg_instance != NULL, "invariant"); + tg_instance->write_selective_thread_group(writer, thread_group_id); +} diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e11dd75da41340e2ae2f45ba04e945f48a3d9e6b --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADGROUP_HPP +#define SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADGROUP_HPP + +#include "jni.h" +#include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrTypes.hpp" + +class JfrCheckpointWriter; +template +class GrowableArray; +class JfrThreadGroupsHelper; +class JfrThreadGroupPointers; + +class JfrThreadGroup : public JfrCHeapObj { + friend class JfrCheckpointThreadClosure; + private: + static JfrThreadGroup* _instance; + class JfrThreadGroupEntry; + GrowableArray* _list; + + JfrThreadGroup(); + JfrThreadGroupEntry* find_entry(const JfrThreadGroupPointers& ptrs) const; + JfrThreadGroupEntry* new_entry(JfrThreadGroupPointers& ptrs); + int add_entry(JfrThreadGroupEntry* const tge); + + void write_thread_group_entries(JfrCheckpointWriter& writer) const; + void write_selective_thread_group(JfrCheckpointWriter* writer, traceid thread_group_id) const; + + static traceid thread_group_id_internal(JfrThreadGroupsHelper& helper); + static JfrThreadGroup* instance(); + static void set_instance(JfrThreadGroup* new_instance); + + public: + ~JfrThreadGroup(); + static void serialize(JfrCheckpointWriter& w); + static void serialize(JfrCheckpointWriter* w, traceid thread_group_id); + static traceid thread_group_id(JavaThread* thread); + static traceid thread_group_id(const JavaThread* thread, Thread* current); +}; + +#endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADGROUP_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50e213b3ce5d3ae10a274b4a78be5a3556f4e36a --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.cpp @@ -0,0 +1,82 @@ +/* +* Copyright (c) 2016, 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 "precompiled.hpp" +#include "jfr/recorder/checkpoint/types/jfrThreadState.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jvmtifiles/jvmti.h" + +struct jvmti_thread_state { + u8 id; + const char* description; +}; + +static jvmti_thread_state states[] = { + { + JVMTI_JAVA_LANG_THREAD_STATE_NEW, + "STATE_NEW" + }, + { + JVMTI_THREAD_STATE_TERMINATED, + "STATE_TERMINATED" + }, + { + JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE, + "STATE_RUNNABLE" + }, + { + (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | JVMTI_THREAD_STATE_SLEEPING), + "STATE_SLEEPING" + }, + { + (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_IN_OBJECT_WAIT), + "STATE_IN_OBJECT_WAIT" + }, + { + (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | JVMTI_THREAD_STATE_IN_OBJECT_WAIT), + "STATE_IN_OBJECT_WAIT_TIMED" + }, + { + (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_PARKED), + "STATE_PARKED" + }, + { + (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | JVMTI_THREAD_STATE_PARKED), + "STATE_PARKED_TIMED" + }, + { + JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED, + "STATE_BLOCKED_ON_MONITOR_ENTER" + } +}; + +void JfrThreadState::serialize(JfrCheckpointWriter& writer) { + const u4 number_of_states = sizeof(states) / sizeof(jvmti_thread_state); + writer.write_count(number_of_states); + for (u4 i = 0; i < number_of_states; ++i) { + writer.write_key(states[i].id); + writer.write(states[i].description); + } +} + diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bcedaa7949e082c9521785268977ce71c4644483 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.hpp @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2016, 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 SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP +#define SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP + +#include "memory/allocation.hpp" + +class JfrCheckpointWriter; + +class JfrThreadState : public AllStatic { + public: + static void serialize(JfrCheckpointWriter& writer); +}; + +#endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..668083c7b03d2768163588bc33ae355f044da55b --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2016, 2019, 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 "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "code/codeBlob.hpp" +#include "code/codeCache.hpp" +#include "gc_interface/gcCause.hpp" +#include "gc_interface/gcName.hpp" +#include "gc_implementation/shared/gcTrace.hpp" +#include "gc_implementation/shared/gcWhen.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" +#include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" +#include "jfr/recorder/checkpoint/types/jfrType.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp" +#include "jfr/recorder/checkpoint/types/jfrThreadState.hpp" +#include "jfr/recorder/checkpoint/types/jfrTypeSet.hpp" +#include "jfr/support/jfrThreadLocal.hpp" +#include "jfr/writers/jfrJavaEventWriter.hpp" +#include "memory/metaspaceGCThresholdUpdater.hpp" +#include "memory/referenceType.hpp" +#include "memory/universe.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/osThread.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/synchronizer.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/vm_operations.hpp" + +#ifdef COMPILER2 +#include "opto/compile.hpp" +#include "opto/node.hpp" +#endif +#if INCLUDE_ALL_GCS +#include "gc_implementation/g1/g1HeapRegionTraceType.hpp" +#include "gc_implementation/g1/g1YCTypes.hpp" +#endif + +// Requires a ResourceMark for get_thread_name/as_utf8 +class JfrCheckpointThreadClosure : public ThreadClosure { + private: + JfrCheckpointWriter& _writer; + JfrCheckpointContext _ctx; + const intptr_t _count_position; + Thread* const _curthread; + u4 _count; + + public: + JfrCheckpointThreadClosure(JfrCheckpointWriter& writer) : _writer(writer), + _ctx(writer.context()), + _count_position(writer.reserve(sizeof(u4))), + _curthread(Thread::current()), + _count(0) { + } + + ~JfrCheckpointThreadClosure() { + if (_count == 0) { + // restore + _writer.set_context(_ctx); + return; + } + _writer.write_count(_count, _count_position); + } + + void do_thread(Thread* t); +}; + +// Requires a ResourceMark for get_thread_name/as_utf8 +void JfrCheckpointThreadClosure::do_thread(Thread* t) { + assert(t != NULL, "invariant"); + assert_locked_or_safepoint(Threads_lock); + const JfrThreadLocal* const tl = t->jfr_thread_local(); + assert(tl != NULL, "invariant"); + if (tl->is_dead()) { + return; + } + ++_count; + _writer.write_key(tl->thread_id()); + _writer.write(t->name()); + const OSThread* const os_thread = t->osthread(); + _writer.write(os_thread != NULL ? os_thread->thread_id() : 0); + if (t->is_Java_thread()) { + JavaThread* const jt = (JavaThread*)t; + _writer.write(jt->name()); + _writer.write(java_lang_Thread::thread_id(jt->threadObj())); + _writer.write(JfrThreadGroup::thread_group_id(jt, _curthread)); + // since we are iterating threads during a safepoint, also issue notification + JfrJavaEventWriter::notify(jt); + return; + } + _writer.write((const char*)NULL); // java name + _writer.write((traceid)0); // java thread id + _writer.write((traceid)0); // java thread group +} + +void JfrThreadConstantSet::serialize(JfrCheckpointWriter& writer) { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + JfrCheckpointThreadClosure tc(writer); + Threads::threads_do(&tc); +} + +void JfrThreadGroupConstant::serialize(JfrCheckpointWriter& writer) { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + JfrThreadGroup::serialize(writer); +} + +static const char* flag_value_origin_to_string(Flag::Flags origin) { + switch (origin) { + case Flag::DEFAULT: return "Default"; + case Flag::COMMAND_LINE: return "Command line"; + case Flag::ENVIRON_VAR: return "Environment variable"; + case Flag::CONFIG_FILE: return "Config file"; + case Flag::MANAGEMENT: return "Management"; + case Flag::ERGONOMIC: return "Ergonomic"; + case Flag::ATTACH_ON_DEMAND: return "Attach on demand"; + case Flag::INTERNAL: return "Internal"; + default: ShouldNotReachHere(); return ""; + } +} + +void FlagValueOriginConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = Flag::LAST_VALUE_ORIGIN + 1; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(flag_value_origin_to_string((Flag::Flags)i)); + } +} + +void MonitorInflateCauseConstant::serialize(JfrCheckpointWriter& writer) { + // XXX no such counters. implement? +// static const u4 nof_entries = ObjectSynchronizer::inflate_cause_nof; +// writer.write_count(nof_entries); +// for (u4 i = 0; i < nof_entries; ++i) { +// writer.write_key(i); +// writer.write(ObjectSynchronizer::inflate_cause_name((ObjectSynchronizer::InflateCause)i)); +// } +} + +void GCCauseConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = GCCause::_last_gc_cause; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(GCCause::to_string((GCCause::Cause)i)); + } +} + +void GCNameConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = GCNameEndSentinel; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(GCNameHelper::to_string((GCName)i)); + } +} + +void GCWhenConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = GCWhen::GCWhenEndSentinel; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(GCWhen::to_string((GCWhen::Type)i)); + } +} + +void G1HeapRegionTypeConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = G1HeapRegionTraceType::G1HeapRegionTypeEndSentinel; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(G1HeapRegionTraceType::to_string((G1HeapRegionTraceType::Type)i)); + } +} + +void GCThresholdUpdaterConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = MetaspaceGCThresholdUpdater::Last; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(MetaspaceGCThresholdUpdater::to_string((MetaspaceGCThresholdUpdater::Type)i)); + } +} + +void MetadataTypeConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = Metaspace::MetadataTypeCount; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(Metaspace::metadata_type_name((Metaspace::MetadataType)i)); + } +} + +void MetaspaceObjectTypeConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = MetaspaceObj::_number_of_types; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(MetaspaceObj::type_name((MetaspaceObj::Type)i)); + } +} + +void G1YCTypeConstant::serialize(JfrCheckpointWriter& writer) { +#if INCLUDE_ALL_GCS + static const u4 nof_entries = G1YCTypeEndSentinel; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(G1YCTypeHelper::to_string((G1YCType)i)); + } +#endif +} + +static const char* reference_type_to_string(ReferenceType rt) { + switch (rt) { + case REF_NONE: return "None reference"; + case REF_OTHER: return "Other reference"; + case REF_SOFT: return "Soft reference"; + case REF_WEAK: return "Weak reference"; + case REF_FINAL: return "Final reference"; + case REF_PHANTOM: return "Phantom reference"; + default: + ShouldNotReachHere(); + return NULL; + } +} + +void ReferenceTypeConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = REF_PHANTOM + 1; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(reference_type_to_string((ReferenceType)i)); + } +} + +void NarrowOopModeConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = Universe::HeapBasedNarrowOop + 1; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(Universe::narrow_oop_mode_to_string((Universe::NARROW_OOP_MODE)i)); + } +} + +void CompilerPhaseTypeConstant::serialize(JfrCheckpointWriter& writer) { +#ifdef COMPILER2 + static const u4 nof_entries = PHASE_NUM_TYPES; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(CompilerPhaseTypeHelper::to_string((CompilerPhaseType)i)); + } +#endif +} + +void CodeBlobTypeConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = CodeBlobType::NumTypes; + writer.write_count(nof_entries); + writer.write_key((u4)CodeBlobType::All); + writer.write("CodeCache"); +}; + +void VMOperationTypeConstant::serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = VM_Operation::VMOp_Terminating; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(VM_Operation::name(VM_Operation::VMOp_Type(i))); + } +} + +class TypeSetSerialization { + private: + bool _class_unload; + public: + explicit TypeSetSerialization(bool class_unload) : _class_unload(class_unload) {} + void write(JfrCheckpointWriter& writer, JfrCheckpointWriter* leakp_writer) { + JfrTypeSet::serialize(&writer, leakp_writer, _class_unload); + } +}; + +void ClassUnloadTypeSet::serialize(JfrCheckpointWriter& writer) { + TypeSetSerialization type_set(true); + if (LeakProfiler::is_running()) { + JfrCheckpointWriter leakp_writer(false, true, Thread::current()); + type_set.write(writer, &leakp_writer); + ObjectSampleCheckpoint::install(leakp_writer, true, true); + return; + } + type_set.write(writer, NULL); +}; + +void TypeSet::serialize(JfrCheckpointWriter& writer) { + TypeSetSerialization type_set(false); + if (LeakProfiler::is_running()) { + JfrCheckpointWriter leakp_writer(false, true, Thread::current()); + type_set.write(writer, &leakp_writer); + ObjectSampleCheckpoint::install(leakp_writer, false, true); + return; + } + type_set.write(writer, NULL); +}; + +void ThreadStateConstant::serialize(JfrCheckpointWriter& writer) { + JfrThreadState::serialize(writer); +} + +void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) { + assert(_thread != NULL, "invariant"); + assert(_thread == Thread::current(), "invariant"); + assert(_thread->is_Java_thread(), "invariant"); + assert(!_thread->jfr_thread_local()->has_thread_checkpoint(), "invariant"); + ResourceMark rm(_thread); + const oop threadObj = _thread->threadObj(); + assert(threadObj != NULL, "invariant"); + const u8 java_lang_thread_id = java_lang_Thread::thread_id(threadObj); + const char* const thread_name = _thread->name(); + const traceid thread_group_id = JfrThreadGroup::thread_group_id(_thread); + writer.write_count(1); + writer.write_key(_thread->jfr_thread_local()->thread_id()); + writer.write(thread_name); + writer.write((traceid)_thread->osthread()->thread_id()); + writer.write(thread_name); + writer.write(java_lang_thread_id); + writer.write(thread_group_id); + JfrThreadGroup::serialize(&writer, thread_group_id); +} diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5f91819b9e17016c92062460f8c5378f1809e286 --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2016, 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 SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPE_HPP +#define SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPE_HPP + +#include "jfr/metadata/jfrSerializer.hpp" + +class JfrThreadConstantSet : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class JfrThreadGroupConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class ClassUnloadTypeSet : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class FlagValueOriginConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class MonitorInflateCauseConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class GCCauseConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class GCNameConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class GCWhenConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class G1HeapRegionTypeConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class GCThresholdUpdaterConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class MetadataTypeConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class MetaspaceObjectTypeConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class G1YCTypeConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class ReferenceTypeConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class NarrowOopModeConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class CompilerPhaseTypeConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class CodeBlobTypeConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class VMOperationTypeConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class TypeSet : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class ThreadStateConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class JfrThreadConstant : public JfrSerializer { + private: + JavaThread* _thread; + public: + JfrThreadConstant(JavaThread* jt) : _thread(jt) {} + void serialize(JfrCheckpointWriter& writer); +}; + +#endif // SHARE_VM_JFR_CHECKPOINT_CONSTANT_JFRCONSTANT_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0520478f4782e39ff6e64b347b947971b70c797f --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "jfr/metadata/jfrSerializer.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/recorder/checkpoint/types/jfrType.hpp" +#include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp" +#include "jfr/utilities/jfrDoublyLinkedList.hpp" +#include "jfr/utilities/jfrIterator.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/thread.inline.hpp" +#include "utilities/exceptions.hpp" +#include "runtime/semaphore.hpp" + +class JfrSerializerRegistration : public JfrCHeapObj { + private: + JfrSerializerRegistration* _next; + JfrSerializerRegistration* _prev; + JfrSerializer* _serializer; + mutable JfrCheckpointBlobHandle _cache; + JfrTypeId _id; + bool _permit_cache; + + public: + JfrSerializerRegistration(JfrTypeId id, bool permit_cache, JfrSerializer* serializer) : + _next(NULL), _prev(NULL), _serializer(serializer), _cache(), _id(id), _permit_cache(permit_cache) {} + + ~JfrSerializerRegistration() { + delete _serializer; + } + + JfrSerializerRegistration* next() const { + return _next; + } + + void set_next(JfrSerializerRegistration* next) { + _next = next; + } + + JfrSerializerRegistration* prev() const { + return _prev; + } + + void set_prev(JfrSerializerRegistration* prev) { + _prev = prev; + } + + JfrTypeId id() const { + return _id; + } + + void invoke(JfrCheckpointWriter& writer) const; +}; + +void JfrSerializerRegistration::invoke(JfrCheckpointWriter& writer) const { + if (_cache.valid()) { + writer.increment(); + _cache->write(writer); + return; + } + const JfrCheckpointContext ctx = writer.context(); + // serialize the type id before invoking callback + writer.write_type(_id); + const intptr_t start = writer.current_offset(); + // invoke the serializer routine + _serializer->serialize(writer); + if (start == writer.current_offset() ) { + // the serializer implementation did nothing, rewind to restore + writer.set_context(ctx); + return; + } + if (_permit_cache) { + _cache = writer.copy(&ctx); + } +} + +class SerializerRegistrationGuard : public StackObj { + private: + static Semaphore _mutex_semaphore; + public: + SerializerRegistrationGuard() { + _mutex_semaphore.wait(); + } + ~SerializerRegistrationGuard() { + _mutex_semaphore.signal(); + } +}; + +Semaphore SerializerRegistrationGuard::_mutex_semaphore(1); + +typedef JfrDoublyLinkedList List; +typedef StopOnNullIterator Iterator; +static List types; +static List safepoint_types; + +void JfrTypeManager::clear() { + SerializerRegistrationGuard guard; + Iterator iter(types); + JfrSerializerRegistration* registration; + while (iter.has_next()) { + registration = types.remove(iter.next()); + assert(registration != NULL, "invariant"); + delete registration; + } + Iterator sp_type_iter(safepoint_types); + while (sp_type_iter.has_next()) { + registration = safepoint_types.remove(sp_type_iter.next()); + assert(registration != NULL, "invariant"); + delete registration; + } +} + +void JfrTypeManager::write_types(JfrCheckpointWriter& writer) { + const Iterator iter(types); + while (iter.has_next()) { + iter.next()->invoke(writer); + } +} + +void JfrTypeManager::write_safepoint_types(JfrCheckpointWriter& writer) { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + const Iterator iter(safepoint_types); + while (iter.has_next()) { + iter.next()->invoke(writer); + } +} + +void JfrTypeManager::write_type_set() { + // can safepoint here because of PackageTable_lock + MutexLockerEx lock(SafepointSynchronize::is_at_safepoint() ? NULL : PackageTable_lock); + JfrCheckpointWriter writer(true, true, Thread::current()); + TypeSet set; + set.serialize(writer); +} + +void JfrTypeManager::write_type_set_for_unloaded_classes() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + JfrCheckpointWriter writer(false, true, Thread::current()); + ClassUnloadTypeSet class_unload_set; + class_unload_set.serialize(writer); +} + +void JfrTypeManager::create_thread_checkpoint(JavaThread* jt) { + assert(jt != NULL, "invariant"); + JfrThreadConstant type_thread(jt); + JfrCheckpointWriter writer(false, true, jt); + writer.write_type(TYPE_THREAD); + type_thread.serialize(writer); + // create and install a checkpoint blob + jt->jfr_thread_local()->set_thread_checkpoint(writer.checkpoint_blob()); + assert(jt->jfr_thread_local()->has_thread_checkpoint(), "invariant"); +} + +void JfrTypeManager::write_thread_checkpoint(JavaThread* jt) { + assert(jt != NULL, "JavaThread is NULL!"); + ResourceMark rm(jt); + if (jt->jfr_thread_local()->has_thread_checkpoint()) { + JfrCheckpointWriter writer(false, false, jt); + jt->jfr_thread_local()->thread_checkpoint()->write(writer); + } else { + JfrThreadConstant type_thread(jt); + JfrCheckpointWriter writer(false, true, jt); + writer.write_type(TYPE_THREAD); + type_thread.serialize(writer); + } +} + +#ifdef ASSERT +static void assert_not_registered_twice(JfrTypeId id, List& list) { + const Iterator iter(list); + while (iter.has_next()) { + assert(iter.next()->id() != id, "invariant"); + } +} +#endif + +static bool register_type(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer) { + assert(serializer != NULL, "invariant"); + JfrSerializerRegistration* const registration = new JfrSerializerRegistration(id, permit_cache, serializer); + if (registration == NULL) { + delete serializer; + return false; + } + if (require_safepoint) { + assert(!safepoint_types.in_list(registration), "invariant"); + DEBUG_ONLY(assert_not_registered_twice(id, safepoint_types);) + safepoint_types.prepend(registration); + } else { + assert(!types.in_list(registration), "invariant"); + DEBUG_ONLY(assert_not_registered_twice(id, types);) + types.prepend(registration); + } + return true; +} + +bool JfrTypeManager::initialize() { + SerializerRegistrationGuard guard; + + // register non-safepointing type serialization + register_type(TYPE_FLAGVALUEORIGIN, false, true, new FlagValueOriginConstant()); + register_type(TYPE_INFLATECAUSE, false, true, new MonitorInflateCauseConstant()); + register_type(TYPE_GCCAUSE, false, true, new GCCauseConstant()); + register_type(TYPE_GCNAME, false, true, new GCNameConstant()); + register_type(TYPE_GCWHEN, false, true, new GCWhenConstant()); + register_type(TYPE_G1HEAPREGIONTYPE, false, true, new G1HeapRegionTypeConstant()); + register_type(TYPE_GCTHRESHOLDUPDATER, false, true, new GCThresholdUpdaterConstant()); + register_type(TYPE_METADATATYPE, false, true, new MetadataTypeConstant()); + register_type(TYPE_METASPACEOBJECTTYPE, false, true, new MetaspaceObjectTypeConstant()); + register_type(TYPE_G1YCTYPE, false, true, new G1YCTypeConstant()); + register_type(TYPE_REFERENCETYPE, false, true, new ReferenceTypeConstant()); + register_type(TYPE_NARROWOOPMODE, false, true, new NarrowOopModeConstant()); + register_type(TYPE_COMPILERPHASETYPE, false, true, new CompilerPhaseTypeConstant()); + register_type(TYPE_CODEBLOBTYPE, false, true, new CodeBlobTypeConstant()); + register_type(TYPE_VMOPERATIONTYPE, false, true, new VMOperationTypeConstant()); + register_type(TYPE_THREADSTATE, false, true, new ThreadStateConstant()); + + // register safepointing type serialization + register_type(TYPE_THREADGROUP, true, false, new JfrThreadGroupConstant()); + register_type(TYPE_THREAD, true, false, new JfrThreadConstantSet()); + return true; +} + +// implementation for the static registration function exposed in the JfrSerializer api +bool JfrSerializer::register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer) { + SerializerRegistrationGuard guard; + return register_type(id, require_safepoint, permit_cache, serializer); +} diff --git a/src/share/vm/trace/traceMacros.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp similarity index 56% rename from src/share/vm/trace/traceMacros.hpp rename to src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp index 371ee7f8d099c49fa88aa90ea38941706bf27adc..a37d54711a26079d121ad73fb06a43ca21019f18 100644 --- a/src/share/vm/trace/traceMacros.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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,24 +21,24 @@ * questions. * */ +#ifndef SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP +#define SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP -#ifndef SHARE_VM_TRACE_TRACEMACROS_HPP -#define SHARE_VM_TRACE_TRACEMACROS_HPP +#include "jfr/utilities/jfrAllocation.hpp" -#define EVENT_THREAD_EXIT(thread) -#define EVENT_THREAD_DESTRUCT(thread) +class JavaThread; +class JfrCheckpointWriter; -#define TRACE_INIT_ID(k) -#define TRACE_DATA TraceThreadData +class JfrTypeManager : public AllStatic { + public: + static bool initialize(); + static void clear(); + static void write_types(JfrCheckpointWriter& writer); + static void write_safepoint_types(JfrCheckpointWriter& writer); + static void write_type_set(); + static void write_type_set_for_unloaded_classes(); + static void create_thread_checkpoint(JavaThread* jt); + static void write_thread_checkpoint(JavaThread* jt); +}; -#define TRACE_START() JNI_OK -#define TRACE_INITIALIZE() JNI_OK - -#define TRACE_DEFINE_KLASS_METHODS typedef int ___IGNORED_hs_trace_type1 -#define TRACE_DEFINE_KLASS_TRACE_ID typedef int ___IGNORED_hs_trace_type2 -#define TRACE_DEFINE_OFFSET typedef int ___IGNORED_hs_trace_type3 -#define TRACE_ID_OFFSET in_ByteSize(0); ShouldNotReachHere() -#define TRACE_TEMPLATES(template) -#define TRACE_INTRINSICS(do_intrinsic, do_class, do_name, do_signature, do_alias) - -#endif // SHARE_VM_TRACE_TRACEMACROS_HPP +#endif // SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp new file mode 100644 index 0000000000000000000000000000000000000000..877acca9fa5fc02941c181dd9763f3c69d7e7b5b --- /dev/null +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2016, 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 "precompiled.hpp" +#include "classfile/classLoaderData.inline.hpp" +#include "classfile/javaClasses.hpp" +// XXX #include "classfile/packageEntry.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "jfr/jfr.hpp" +#include "jfr/jni/jfrGetAllEventClasses.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/recorder/checkpoint/types/jfrTypeSet.hpp" +#include "jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp" +#include "jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp" +#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" +#include "jfr/recorder/storage/jfrBuffer.hpp" +#include "jfr/utilities/jfrHashtable.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "memory/iterator.hpp" +#include "memory/resourceArea.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/objArrayKlass.hpp" +#include "oops/oop.inline.hpp" +#include "memory/resourceArea.hpp" +#include "utilities/accessFlags.hpp" + +// incremented on each checkpoint +static u8 checkpoint_id = 0; + +// creates a unique id by combining a checkpoint relative symbol id (2^24) +// with the current checkpoint id (2^40) +#define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id))) +#define CREATE_PACKAGE_ID(pkg_id) (((u8)((checkpoint_id << 24) | pkg_id))) + +typedef const Klass* KlassPtr; +// XXX typedef const PackageEntry* PkgPtr; +typedef const ClassLoaderData* CldPtr; +typedef const Method* MethodPtr; +typedef const Symbol* SymbolPtr; +typedef const JfrSymbolId::SymbolEntry* SymbolEntryPtr; +typedef const JfrSymbolId::CStringEntry* CStringEntryPtr; + +inline uintptr_t package_name_hash(const char *s) { + uintptr_t val = 0; + while (*s != 0) { + val = *s++ + 31 * val; + } + return val; +} + +static traceid package_id(KlassPtr klass, JfrArtifactSet* artifacts) { + assert(klass != NULL, "invariant"); + char* klass_name = klass->name()->as_C_string(); // uses ResourceMark declared in JfrTypeSet::serialize() + const char* pkg_name = ClassLoader::package_from_name(klass_name, NULL); + if (pkg_name == NULL) { + return 0; + } + return CREATE_PACKAGE_ID(artifacts->markPackage(pkg_name, package_name_hash(pkg_name))); +} + +static traceid cld_id(CldPtr cld) { + assert(cld != NULL, "invariant"); + return cld->is_anonymous() ? 0 : TRACE_ID(cld); +} + +static void tag_leakp_klass_artifacts(KlassPtr k, bool class_unload) { + assert(k != NULL, "invariant"); + // XXX + // PkgPtr pkg = k->package(); + // if (pkg != NULL) { + // tag_leakp_artifact(pkg, class_unload); + // } + CldPtr cld = k->class_loader_data(); + assert(cld != NULL, "invariant"); + if (!cld->is_anonymous()) { + tag_leakp_artifact(cld, class_unload); + } +} + +class TagLeakpKlassArtifact { + bool _class_unload; + public: + TagLeakpKlassArtifact(bool class_unload) : _class_unload(class_unload) {} + bool operator()(KlassPtr klass) { + if (_class_unload) { + if (LEAKP_USED_THIS_EPOCH(klass)) { + tag_leakp_klass_artifacts(klass, _class_unload); + } + } else { + if (LEAKP_USED_PREV_EPOCH(klass)) { + tag_leakp_klass_artifacts(klass, _class_unload); + } + } + return true; + } +}; + +/* + * In C++03, functions used as template parameters must have external linkage; + * this restriction was removed in C++11. Change back to "static" and + * rename functions when C++11 becomes available. + * + * The weird naming is an effort to decrease the risk of name clashes. + */ + +int write__artifact__klass(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* k) { + assert(writer != NULL, "invariant"); + assert(artifacts != NULL, "invariant"); + assert(k != NULL, "invariant"); + KlassPtr klass = (KlassPtr)k; + traceid pkg_id = 0; + KlassPtr theklass = klass; + if (theklass->oop_is_objArray()) { + const ObjArrayKlass* obj_arr_klass = ObjArrayKlass::cast((Klass*)klass); + theklass = obj_arr_klass->bottom_klass(); + } + if (theklass->oop_is_instance()) { + pkg_id = package_id(theklass, artifacts); + } else { + assert(theklass->oop_is_typeArray(), "invariant"); + } + const traceid symbol_id = artifacts->mark(klass); + assert(symbol_id > 0, "need to have an address for symbol!"); + writer->write(TRACE_ID(klass)); + writer->write(cld_id(klass->class_loader_data())); + writer->write((traceid)CREATE_SYMBOL_ID(symbol_id)); + writer->write(pkg_id); + writer->write((s4)klass->access_flags().get_flags()); + return 1; +} + +typedef LeakPredicate LeakKlassPredicate; +typedef JfrPredicatedArtifactWriterImplHost LeakKlassWriterImpl; +typedef JfrArtifactWriterHost LeakKlassWriter; +typedef JfrArtifactWriterImplHost KlassWriterImpl; +typedef JfrArtifactWriterHost KlassWriter; + +int write__artifact__method(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) { + assert(writer != NULL, "invariant"); + assert(artifacts != NULL, "invariant"); + assert(m != NULL, "invariant"); + MethodPtr method = (MethodPtr)m; + const traceid method_name_symbol_id = artifacts->mark(method->name()); + assert(method_name_symbol_id > 0, "invariant"); + const traceid method_sig_symbol_id = artifacts->mark(method->signature()); + assert(method_sig_symbol_id > 0, "invariant"); + KlassPtr klass = method->method_holder(); + assert(klass != NULL, "invariant"); + assert(METHOD_USED_ANY_EPOCH(klass), "invariant"); + writer->write((u8)METHOD_ID(klass, method)); + writer->write((u8)TRACE_ID(klass)); + writer->write((u8)CREATE_SYMBOL_ID(method_name_symbol_id)); + writer->write((u8)CREATE_SYMBOL_ID(method_sig_symbol_id)); + writer->write((u2)method->access_flags().get_flags()); + writer->write(const_cast(method)->is_hidden() ? (u1)1 : (u1)0); + return 1; +} + +typedef JfrArtifactWriterImplHost MethodWriterImplTarget; +typedef JfrArtifactWriterHost MethodWriterImpl; + +int write__artifact__package(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* p) { + assert(writer != NULL, "invariant"); + assert(artifacts != NULL, "invariant"); + assert(p != NULL, "invariant"); + + CStringEntryPtr entry = (CStringEntryPtr)p; + const traceid package_name_symbol_id = artifacts->mark(entry->value(), package_name_hash(entry->value())); + assert(package_name_symbol_id > 0, "invariant"); + writer->write((traceid)CREATE_PACKAGE_ID(entry->id())); + writer->write((traceid)CREATE_SYMBOL_ID(package_name_symbol_id)); + writer->write((bool)true); // exported + return 1; +} + +int write__artifact__classloader(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* c) { + assert(c != NULL, "invariant"); + CldPtr cld = (CldPtr)c; + assert(!cld->is_anonymous(), "invariant"); + const traceid cld_id = TRACE_ID(cld); + // class loader type + const Klass* class_loader_klass = cld->class_loader() != NULL ? cld->class_loader()->klass() : NULL; + if (class_loader_klass == NULL) { + // (primordial) boot class loader + writer->write(cld_id); // class loader instance id + writer->write((traceid)0); // class loader type id (absence of) + writer->write((traceid)CREATE_SYMBOL_ID(1)); // 1 maps to synthetic name -> "bootstrap" + } else { + Symbol* symbol_name = class_loader_klass->name(); + const traceid symbol_name_id = symbol_name != NULL ? artifacts->mark(symbol_name) : 0; + writer->write(cld_id); // class loader instance id + writer->write(TRACE_ID(class_loader_klass)); // class loader type id + writer->write(symbol_name_id == 0 ? (traceid)0 : + (traceid)CREATE_SYMBOL_ID(symbol_name_id)); // class loader instance name + } + return 1; +} + +typedef LeakPredicate LeakCldPredicate; +int _compare_cld_ptr_(CldPtr const& lhs, CldPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; } +typedef UniquePredicate CldPredicate; +typedef JfrPredicatedArtifactWriterImplHost LeakCldWriterImpl; +typedef JfrPredicatedArtifactWriterImplHost CldWriterImpl; +typedef JfrArtifactWriterHost LeakCldWriter; +typedef JfrArtifactWriterHost CldWriter; + +typedef const JfrSymbolId::SymbolEntry* SymbolEntryPtr; + +static int write__artifact__symbol__entry__(JfrCheckpointWriter* writer, + SymbolEntryPtr entry) { + assert(writer != NULL, "invariant"); + assert(entry != NULL, "invariant"); + ResourceMark rm; + writer->write(CREATE_SYMBOL_ID(entry->id())); + writer->write(entry->value()->as_C_string()); + return 1; +} + +int write__artifact__symbol__entry(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* e) { + assert(e != NULL, "invariant"); + return write__artifact__symbol__entry__(writer, (SymbolEntryPtr)e); +} + +typedef JfrArtifactWriterImplHost SymbolEntryWriterImpl; +typedef JfrArtifactWriterHost SymbolEntryWriter; + +typedef const JfrSymbolId::CStringEntry* CStringEntryPtr; + +static int write__artifact__cstring__entry__(JfrCheckpointWriter* writer, CStringEntryPtr entry) { + assert(writer != NULL, "invariant"); + assert(entry != NULL, "invariant"); + writer->write(CREATE_SYMBOL_ID(entry->id())); + writer->write(entry->value()); + return 1; +} + +int write__artifact__cstring__entry(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* e) { + assert(e != NULL, "invariant"); + return write__artifact__cstring__entry__(writer, (CStringEntryPtr)e); +} + +typedef JfrArtifactWriterImplHost CStringEntryWriterImpl; +typedef JfrArtifactWriterHost CStringEntryWriter; + +int write__artifact__klass__symbol(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* k) { + assert(writer != NULL, "invariant"); + assert(artifacts != NULL, "invaiant"); + assert(k != NULL, "invariant"); + const InstanceKlass* const ik = (const InstanceKlass*)k; + if (ik->is_anonymous()) { + CStringEntryPtr entry = + artifacts->map_cstring(JfrSymbolId::anonymous_klass_name_hash_code(ik)); + assert(entry != NULL, "invariant"); + return write__artifact__cstring__entry__(writer, entry); + } + + SymbolEntryPtr entry = artifacts->map_symbol(JfrSymbolId::regular_klass_name_hash_code(ik)); + return write__artifact__symbol__entry__(writer, entry); +} + +int _compare_traceid_(const traceid& lhs, const traceid& rhs) { + return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; +} + +template