diff --git a/make/CompileJavaModules.gmk b/make/CompileJavaModules.gmk
index 4f33b17247a..72c7391d45d 100644
--- a/make/CompileJavaModules.gmk
+++ b/make/CompileJavaModules.gmk
@@ -509,6 +509,15 @@ jdk.localedata_COPY += _dict _th
# Exclude BreakIterator classes that are just used in compile process to generate
# data files and shouldn't go in the product
jdk.localedata_EXCLUDE_FILES += sun/text/resources/ext/BreakIteratorRules_th.java
+################################################################################
+
+# There is an issue in sjavac that triggers a warning in jdk.jfr that isn't
+# triggered without sjavac.
+ifeq ($(ENABLE_SJAVAC), yes)
+ jdk.jfr_SETUP := GENERATE_JDKBYTECODE_NOWARNINGS
+endif
+jdk.jfr_COPY := .xsd .xml .dtd
+jdk.jfr_ADD_JAVAC_FLAGS := -XDstringConcat=inline -Xlint:-exports
################################################################################
# If this is an imported module that has prebuilt classes, only compile
diff --git a/make/autoconf/hotspot.m4 b/make/autoconf/hotspot.m4
index 1cca6d883d4..7df870379fb 100644
--- a/make/autoconf/hotspot.m4
+++ b/make/autoconf/hotspot.m4
@@ -26,7 +26,7 @@
# All valid JVM features, regardless of platform
VALID_JVM_FEATURES="compiler1 compiler2 zero minimal dtrace jvmti jvmci \
graal vm-structs jni-check services management cmsgc g1gc parallelgc serialgc nmt cds \
- static-build link-time-opt aot"
+ static-build link-time-opt aot jfr"
# All valid JVM variants
VALID_JVM_VARIANTS="server client minimal core zero custom"
@@ -309,6 +309,11 @@ AC_DEFUN_ONCE([HOTSPOT_SETUP_JVM_FEATURES],
AC_MSG_ERROR([Specified JVM feature 'cmsgc' requires feature 'serialgc'])
fi
+ # Enable JFR by default, except on linux-sparcv9 and on minimal.
+ if test "x$OPENJDK_TARGET_OS" != xlinux || test "x$OPENJDK_TARGET_CPU" != xsparcv9; then
+ NON_MINIMAL_FEATURES="$NON_MINIMAL_FEATURES jfr"
+ fi
+
# Turn on additional features based on other parts of configure
if test "x$INCLUDE_DTRACE" = "xtrue"; then
JVM_FEATURES="$JVM_FEATURES dtrace"
@@ -396,7 +401,7 @@ AC_DEFUN_ONCE([HOTSPOT_SETUP_JVM_FEATURES],
NON_MINIMAL_FEATURES="$NON_MINIMAL_FEATURES cds"
fi
- # Enable default features depending on variant.
+ # Enable features depending on variant.
JVM_FEATURES_server="compiler1 compiler2 $NON_MINIMAL_FEATURES $JVM_FEATURES $JVM_FEATURES_jvmci $JVM_FEATURES_aot $JVM_FEATURES_graal"
JVM_FEATURES_client="compiler1 $NON_MINIMAL_FEATURES $JVM_FEATURES $JVM_FEATURES_jvmci"
JVM_FEATURES_core="$NON_MINIMAL_FEATURES $JVM_FEATURES"
diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4
index 1d33778e2ad..5bf8569ab2f 100644
--- a/make/autoconf/libraries.m4
+++ b/make/autoconf/libraries.m4
@@ -130,7 +130,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES],
if test "x$OPENJDK_TARGET_OS" = xsolaris; then
BASIC_JVM_LIBS="$BASIC_JVM_LIBS -lsocket -lsched -ldoor -ldemangle -lnsl \
- -lrt"
+ -lrt -lkstat"
BASIC_JVM_LIBS="$BASIC_JVM_LIBS $LIBCXX_JVM"
fi
diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk
index 9bc76095abe..f4646f16efd 100644
--- a/make/common/Modules.gmk
+++ b/make/common/Modules.gmk
@@ -59,7 +59,9 @@ BOOT_MODULES += \
java.security.sasl \
java.xml \
jdk.internal.vm.ci \
+ jdk.jfr \
jdk.management \
+ jdk.management.jfr \
jdk.management.agent \
jdk.net \
jdk.sctp \
@@ -152,6 +154,7 @@ DOCS_MODULES += \
jdk.jdeps \
jdk.jdi \
jdk.jdwp.agent \
+ jdk.jfr \
jdk.jlink \
jdk.jsobject \
jdk.jshell \
@@ -159,6 +162,7 @@ DOCS_MODULES += \
jdk.localedata \
jdk.management \
jdk.management.agent \
+ jdk.management.jfr \
jdk.naming.dns \
jdk.naming.rmi \
jdk.net \
diff --git a/make/copy/Copy-jdk.jfr.gmk b/make/copy/Copy-jdk.jfr.gmk
new file mode 100644
index 00000000000..dc752d3a44f
--- /dev/null
+++ b/make/copy/Copy-jdk.jfr.gmk
@@ -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. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+include CopyCommon.gmk
+
+################################################################################
+
+$(eval $(call SetupCopyFiles, COPY_JFR_METADATA, \
+ SRC := $(TOPDIR)/src/hotspot/share/jfr/metadata, \
+ DEST := $(JDK_OUTPUTDIR)/modules/jdk.jfr/jdk/jfr/internal/types, \
+ FILES := metadata.xml \
+))
+
+TARGETS += $(COPY_JFR_METADATA)
+
+JFR_CONF_DIR := $(TOPDIR)/src/jdk.jfr/share/conf/jfr
+$(eval $(call SetupCopyFiles, COPY_JFR_CONF, \
+ DEST := $(LIB_DST_DIR)/jfr, \
+ FILES := $(wildcard $(JFR_CONF_DIR)/*.jfc), \
+ FLATTEN := true, \
+))
+TARGETS += $(COPY_JFR_CONF)
+
+################################################################################
diff --git a/make/hotspot/gensrc/GenerateSources.gmk b/make/hotspot/gensrc/GenerateSources.gmk
index 644bb4f5cb1..cdcf644c148 100644
--- a/make/hotspot/gensrc/GenerateSources.gmk
+++ b/make/hotspot/gensrc/GenerateSources.gmk
@@ -38,6 +38,7 @@ include HotspotCommon.gmk
include gensrc/GensrcAdlc.gmk
include gensrc/GensrcDtrace.gmk
include gensrc/GensrcJvmti.gmk
+include gensrc/GensrcJfr.gmk
$(eval $(call IncludeCustomExtension, hotspot/gensrc/GenerateSources.gmk))
diff --git a/make/hotspot/gensrc/GensrcJfr.gmk b/make/hotspot/gensrc/GensrcJfr.gmk
new file mode 100644
index 00000000000..61706113922
--- /dev/null
+++ b/make/hotspot/gensrc/GensrcJfr.gmk
@@ -0,0 +1,94 @@
+#
+# 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. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+$(eval $(call IncludeCustomExtension, hotspot/gensrc/GensrcJfr.gmk))
+
+################################################################################
+# Build tools needed for the Jfr source code generation
+
+JFR_TOOLS_SRCDIR := $(TOPDIR)/src/hotspot/share/jfr/metadata
+JFR_TOOLS_OUTPUTDIR := $(JVM_VARIANT_OUTPUTDIR)/tools/jfr
+
+$(eval $(call SetupJavaCompiler, GENERATE_JFRBYTECODE, \
+ JAVAC := $(JAVAC), \
+ FLAGS := $(DISABLE_WARNINGS), \
+ SERVER_DIR := $(SJAVAC_SERVER_DIR), \
+ SERVER_JVM := $(SJAVAC_SERVER_JAVA), \
+ DISABLE_SJAVAC := true, \
+))
+
+$(eval $(call SetupJavaCompilation, BUILD_JFR_TOOLS, \
+ SETUP := GENERATE_JFRBYTECODE, \
+ SRC := $(JFR_TOOLS_SRCDIR), \
+ INCLUDE_FILES := GenerateJfrFiles.java, \
+ BIN := $(JFR_TOOLS_OUTPUTDIR), \
+))
+
+TOOL_JFR_GEN := $(JAVA_SMALL) -cp $(JFR_TOOLS_OUTPUTDIR) GenerateJfrFiles
+
+################################################################################
+# Setup make rules for Jfr file file generation.
+#
+# Parameter 1 is the name of the rule. This name is used as variable prefix,
+# and the targets generated are listed in a variable by that name. This name is
+# also used as the name of the output file.
+#
+# Remaining parameters are named arguments. These include:
+# XML_FILE -- The input source file to use
+# XSD_FILE -- The input schema for validation
+# OUTPUT_DIR -- The directory to put the generated file in
+SetupJfrGeneration = $(NamedParamsMacroTemplate)
+define SetupJfrGenerationBody
+ $$($1_OUTPUT_DIR)/$1: $$($1_XML_FILE) $$($1_XSD_FILE) $$(BUILD_JFR_TOOLS)
+ $$(call LogInfo, Generating $$(@F))
+ $$(call MakeDir, $$(@D))
+ $$(call ExecuteWithLog, $$@, $$(TOOL_JFR_GEN) $$($1_XML_FILE) $$($1_XSD_FILE) $$($1_OUTPUT_DIR))
+ test -f $$@
+
+ TARGETS += $$($1_OUTPUT_DIR)/$1
+
+endef
+
+################################################################################
+# Create files in gensrc/jfrfiles
+
+JFR_OUTPUTDIR := $(JVM_VARIANT_OUTPUTDIR)/gensrc/jfrfiles
+JFR_SRCDIR := $(TOPDIR)/src/hotspot/share/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) \
+ #
+
+# our generator will generate all files in one go, so only need to setup one target rule
+$(eval $(call SetupJfrGeneration, jfrEventClasses.hpp, \
+ XML_FILE := $(METADATA_XML), \
+ XSD_FILE := $(METADATA_XSD), \
+ OUTPUT_DIR := $(JFR_OUTPUTDIR), \
+))
\ No newline at end of file
diff --git a/make/hotspot/gensrc/GensrcJvmti.gmk b/make/hotspot/gensrc/GensrcJvmti.gmk
index 70a35e1989c..d48981c5a3f 100644
--- a/make/hotspot/gensrc/GensrcJvmti.gmk
+++ b/make/hotspot/gensrc/GensrcJvmti.gmk
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -50,7 +50,7 @@ TOOL_JVMTI_GEN := $(JAVA_SMALL) -cp $(JVMTI_TOOLS_OUTPUTDIR) jvmtiGen
TOOL_JVMTI_ENV_FILL := $(JAVA_SMALL) -cp $(JVMTI_TOOLS_OUTPUTDIR) jvmtiEnvFill
################################################################################
-# Setup make rules for an xml transform for jvmti/trace file generation.
+# Setup make rules for an xml transform for jvmti file generation.
#
# Parameter 1 is the name of the rule. This name is used as variable prefix,
# and the targets generated are listed in a variable by that name. This name is
@@ -126,48 +126,3 @@ ifeq ($(JVM_VARIANT), $(firstword $(JVM_VARIANTS)))
TARGETS += $(COPY_JVMTI_H)
endif
-
-################################################################################
-# Create trace files in gensrc/tracefiles
-
-TRACE_OUTPUTDIR := $(JVM_VARIANT_OUTPUTDIR)/gensrc/tracefiles
-TRACE_SRCDIR := $(TOPDIR)/src/hotspot/share/trace
-
-# Append list of XSL files to search (might have been set by custom extensions)
-TRACE_XSL_FILES += $(wildcard $(TRACE_SRCDIR)/*.xsl)
-
-TRACE_XML ?= $(TRACE_SRCDIR)/trace.xml
-
-# Changing these will trigger a rebuild of generated trace files.
-TRACE_DEPS += \
- $(TRACE_XML) \
- $(TRACE_SRCDIR)/tracetypes.xml \
- $(TRACE_SRCDIR)/tracerelationdecls.xml \
- $(TRACE_SRCDIR)/traceevents.xml \
- $(TRACE_SRCDIR)/trace.dtd \
- $(TRACE_SRCDIR)/xinclude.mod \
- #
-
-# Setup rule for generating a trace file
-#
-# $1 is generated source file name in $(TRACE_OUTPUTDIR)
-define SetupTraceGeneration
- $$(eval $$(call SetupXslTransform, $1, \
- XML_FILE := $$(TRACE_XML), \
- XSL_FILE := $$(firstword $$(filter %/$$(basename $1).xsl, $$(TRACE_XSL_FILES))), \
- OUTPUT_DIR := $$(TRACE_OUTPUTDIR), \
- DEPS := $$(TRACE_DEPS), \
- ))
-endef
-
-# Append files to generated (might have been set by custom extensions)
-TRACE_GENSRC_FILES += \
- traceEventClasses.hpp \
- traceEventIds.hpp \
- traceTypes.hpp \
- #
-
-# Call SetupTraceGeneration for all trace gensrc files
-$(foreach tracefile, $(TRACE_GENSRC_FILES), \
- $(eval $(call SetupTraceGeneration, $(tracefile))) \
-)
diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk
index 25149c9c13e..caf30c9dc8b 100644
--- a/make/hotspot/lib/JvmFeatures.gmk
+++ b/make/hotspot/lib/JvmFeatures.gmk
@@ -154,6 +154,12 @@ ifneq ($(call check-jvm-feature, serialgc), true)
# If serial is disabled, we cannot use serial as OldGC in parallel
JVM_EXCLUDE_FILES += psMarkSweep.cpp psMarkSweepDecorator.cpp
endif
+
+ifneq ($(call check-jvm-feature, jfr), true)
+ JVM_CFLAGS_FEATURES += -DINCLUDE_JFR=0
+ JVM_EXCLUDE_PATTERNS += jfr
+endif
+
################################################################################
ifeq ($(call check-jvm-feature, link-time-opt), true)
diff --git a/make/hotspot/src/classes/build/tools/projectcreator/BuildConfig.java b/make/hotspot/src/classes/build/tools/projectcreator/BuildConfig.java
index 81e555233e2..76e2b467565 100644
--- a/make/hotspot/src/classes/build/tools/projectcreator/BuildConfig.java
+++ b/make/hotspot/src/classes/build/tools/projectcreator/BuildConfig.java
@@ -223,7 +223,7 @@ class BuildConfig {
sysDefines.add("_WINDOWS");
sysDefines.add("HOTSPOT_BUILD_USER=\\\""+System.getProperty("user.name")+"\\\"");
sysDefines.add("HOTSPOT_BUILD_TARGET=\\\""+get("Build")+"\\\"");
- sysDefines.add("INCLUDE_TRACE=1");
+ sysDefines.add("INCLUDE_JFR=1");
sysDefines.add("_JNI_IMPLEMENTATION_");
if (vars.get("PlatformName").equals("Win32")) {
sysDefines.add("HOTSPOT_LIB_ARCH=\\\"i386\\\"");
diff --git a/make/jprt.properties b/make/jprt.properties
index 9e10f8cf1a2..c4c5541f82c 100644
--- a/make/jprt.properties
+++ b/make/jprt.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 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
@@ -273,6 +273,7 @@ my.make.rule.test.targets.svc= \
${my.test.target.set:TESTNAME=jdk_instrument}, \
${my.test.target.set:TESTNAME=jdk_jmx}, \
${my.test.target.set:TESTNAME=jdk_jdi}, \
+ ${my.test.target.set:TESTNAME=jdk_jfr}, \
${my.test.target.set:TESTNAME=svc_tools}, \
${my.make.rule.test.targets.svc.extra}
diff --git a/make/nashorn/project.properties b/make/nashorn/project.properties
index 1839ff5cfee..0e6c69b4698 100644
--- a/make/nashorn/project.properties
+++ b/make/nashorn/project.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2010, 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
@@ -98,8 +98,8 @@ dist.javadoc.dir=${dist.dir}/javadoc
dist.nashornapi.javadoc.dir=${dist.javadoc.dir}/nashornapi
dist.dynalinkapi.javadoc.dir=${dist.javadoc.dir}/dynalinkapi
-# configuration for java flight recorder
-run.test.jvmargs.jfr=-XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=${build.dir},stackdepth=128
+# configuration for flight recorder
+run.test.jvmargs.jfr=XX:StartFlightRecording=disk=true,dumponexit=true,dumponexitpath=${build.dir},stackdepth=128
# test library location
test.lib=test/nashorn/lib
@@ -354,7 +354,7 @@ run.test.xms=2G
# uncomment this jfr.args to enable light recordings. the stack needs to be cranked up to 1024 frames,
# or everything will as of the now drown in lambda forms and be cut off.
#
-#jfr.args=-XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath="test_suite.jfr",stackdepth=1024 \
+#jfr.args=-XX:StartFlightRecording=disk=true,dumponexit=true,dumponexitpath="test_suite.jfr",stackdepth=1024
jfr.args=
diff --git a/make/nb_native/nbproject/configurations.xml b/make/nb_native/nbproject/configurations.xml
index 29922e333ff..43bcc96ffd0 100644
--- a/make/nb_native/nbproject/configurations.xml
+++ b/make/nb_native/nbproject/configurations.xml
@@ -2891,7 +2891,6 @@
stringUtils.hpp
ticks.cpp
ticks.hpp
- ticks.inline.hpp
utf8.cpp
utf8.hpp
vmError.cpp
@@ -6887,7 +6886,7 @@
../../hotspot/src/share/vm/ci
../../hotspot/src/share/vm/oops
../../hotspot/src/share/vm/trace
- ../../build/macosx-x86_64-normal-server-release/hotspot/variant-server/gensrc/tracefiles
+ ../../build/macosx-x86_64-normal-server-release/hotspot/variant-server/gensrc/jfrfiles
../../hotspot/src/share/vm/gc/parallel
../../hotspot/src/share/vm/gc/shared
../../hotspot/src/share/vm/classfile
@@ -15408,11 +15407,6 @@
tool="3"
flavor2="0">
- -
-
-
- -
-
- 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;
+}
+
+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/hotspot/cpu/sparc/vm_version_ext_sparc.hpp b/src/hotspot/cpu/sparc/vm_version_ext_sparc.hpp
new file mode 100644
index 00000000000..f7e4368ccc9
--- /dev/null
+++ b/src/hotspot/cpu/sparc/vm_version_ext_sparc.hpp
@@ -0,0 +1,64 @@
+/*
+ * 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"
+#include
+#include
+
+#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 kid_t _kcid;
+ 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/hotspot/cpu/x86/rdtsc_x86.cpp b/src/hotspot/cpu/x86/rdtsc_x86.cpp
new file mode 100644
index 00000000000..8d3f8f303a9
--- /dev/null
+++ b/src/hotspot/cpu/x86/rdtsc_x86.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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 "rdtsc_x86.hpp"
+#include "runtime/thread.inline.hpp"
+#include "vm_version_ext_x86.hpp"
+
+// The following header contains the implementations of rdtsc()
+#include OS_CPU_HEADER_INLINE(os)
+
+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/hotspot/cpu/x86/rdtsc_x86.hpp b/src/hotspot/cpu/x86/rdtsc_x86.hpp
new file mode 100644
index 00000000000..c455c0018b6
--- /dev/null
+++ b/src/hotspot/cpu/x86/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/hotspot/cpu/x86/vm_version_ext_x86.cpp b/src/hotspot/cpu/x86/vm_version_ext_x86.cpp
new file mode 100644
index 00000000000..a5d60aebe2e
--- /dev/null
+++ b/src/hotspot/cpu/x86/vm_version_ext_x86.cpp
@@ -0,0 +1,967 @@
+/*
+ * 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);
+ _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) {
+ assert(buf != NULL, "buffer is NULL!");
+ assert(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 || (size_t) res >= buf_len - 1) { \
+ buf[buf_len-1] = '\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) {
+ 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/hotspot/cpu/x86/vm_version_ext_x86.hpp b/src/hotspot/cpu/x86/vm_version_ext_x86.hpp
new file mode 100644
index 00000000000..de35616b6e2
--- /dev/null
+++ b/src/hotspot/cpu/x86/vm_version_ext_x86.hpp
@@ -0,0 +1,99 @@
+/*
+ * 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);
+
+ 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/hotspot/os/bsd/os_perf_bsd.cpp b/src/hotspot/os/bsd/os_perf_bsd.cpp
new file mode 100644
index 00000000000..c0aaa1bb02b
--- /dev/null
+++ b/src/hotspot/os/bsd/os_perf_bsd.cpp
@@ -0,0 +1,405 @@
+/*
+ * 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
+#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 = 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);
+ _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);
+ _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;
+}
diff --git a/src/hotspot/os/linux/os_perf_linux.cpp b/src/hotspot/os/linux/os_perf_linux.cpp
new file mode 100644
index 00000000000..3ad0a6ef136
--- /dev/null
+++ b/src/hotspot/os/linux/os_perf_linux.cpp
@@ -0,0 +1,1060 @@
+/*
+ * 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 X86
+#include "vm_version_ext_x86.hpp"
+#endif
+#ifdef ARM
+#include "vm_version_ext_arm.hpp"
+#endif
+#ifndef ARM
+#ifdef AARCH64
+#include "vm_version_ext_aarch64.hpp"
+#endif
+#endif
+
+#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);
+ }
+}
+
+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);
+ }
+
+ return OS_OK;
+}
+
+int SystemProcessInterface::SystemProcesses::ProcessIterator::next_process() {
+ struct dirent* entry;
+
+ if (!is_valid()) {
+ return OS_ERR;
+ }
+
+ do {
+ entry = os::readdir(_dir, _entry);
+ if (entry == NULL) {
+ // error
+ _valid = false;
+ return OS_ERR;
+ }
+ if (_entry == NULL) {
+ // reached end
+ _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 = opendir("/proc");
+ _entry = (struct dirent*)NEW_C_HEAP_ARRAY(char, sizeof(struct dirent) + NAME_MAX + 1, mtInternal);
+ if (NULL == _entry) {
+ return false;
+ }
+ _valid = true;
+ next_process();
+
+ return true;
+}
+
+SystemProcessInterface::SystemProcesses::ProcessIterator::~ProcessIterator() {
+ if (_entry != NULL) {
+ FREE_C_HEAP_ARRAY(char, _entry);
+ }
+ if (_dir != NULL) {
+ 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);
+ _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);
+ _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;
+}
diff --git a/src/hotspot/os/solaris/os_perf_solaris.cpp b/src/hotspot/os/solaris/os_perf_solaris.cpp
new file mode 100644
index 00000000000..9b04ee7a966
--- /dev/null
+++ b/src/hotspot/os/solaris/os_perf_solaris.cpp
@@ -0,0 +1,756 @@
+/*
+ * 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"
+
+#include CPU_HEADER(vm_version_ext)
+
+#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);
+ }
+ 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* 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;
+
+ 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);
+ }
+
+ if (fp != NULL) {
+ fclose(fp);
+ }
+
+ return OS_OK;
+}
+
+int SystemProcessInterface::SystemProcesses::ProcessIterator::next_process() {
+ struct dirent* entry;
+
+ if (!is_valid()) {
+ return OS_ERR;
+ }
+
+ do {
+ if ((entry = os::readdir(_dir, _entry)) == NULL) {
+ // error
+ _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 = opendir("/proc");
+ _entry = (struct dirent*)NEW_C_HEAP_ARRAY(char, sizeof(struct dirent) + _PC_NAME_MAX + 1, mtInternal);
+ if (NULL == _entry) {
+ return false;
+ }
+ _valid = true;
+ next_process();
+
+ return true;
+}
+
+SystemProcessInterface::SystemProcesses::ProcessIterator::~ProcessIterator() {
+ if (_entry != NULL) {
+ FREE_C_HEAP_ARRAY(char, _entry);
+ }
+
+ if (_dir != NULL) {
+ 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* 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);
+ _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);
+ _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;
+}
diff --git a/src/hotspot/os/windows/os_perf_windows.cpp b/src/hotspot/os/windows/os_perf_windows.cpp
new file mode 100644
index 00000000000..88d3f314f22
--- /dev/null
+++ b/src/hotspot/os/windows/os_perf_windows.cpp
@@ -0,0 +1,1332 @@
+/*
+ * 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 "logging/log.hpp"
+#include "memory/allocation.inline.hpp"
+#include "memory/resourceArea.hpp"
+#include "pdh_interface.hpp"
+#include "runtime/os_perf.hpp"
+#include "runtime/os.hpp"
+#include "vm_version_ext_x86.hpp"
+#include "utilities/macros.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;
+
+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);
+}
+
+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);
+ pdh_cleanup(&counter_query->query.query, NULL);
+ FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query);
+ }
+}
+
+static void destroy_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);
+ pdh_cleanup(&counter_query_set->queries[i].query.query, NULL);
+ }
+ FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query_set->queries);
+ FREE_C_HEAP_ARRAY(MultiCounterQuerySetS, counter_query_set);
+}
+
+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 void deallocate_counters(MultiCounterQueryP query) {
+ if (query->counters != NULL) {
+ FREE_C_HEAP_ARRAY(char, query->counters);
+ 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(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).
+*/
+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 0;
+ }
+ 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 0;
+ }
+ const PDH_STATUS res = PdhDll::PdhCollectQueryData(tmpQuery);
+ if (res == PDH_INVALID_HANDLE || res == PDH_NO_DATA) {
+ pdh_cleanup(&tmpQuery, &handle_counter);
+ return 0;
+ } 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 0;
+}
+
+static MultiCounterQuerySetP create_process_counter_query() {
+ MultiCounterQuerySetP const query = NEW_C_HEAP_ARRAY(MultiCounterQuerySetS, 1, mtInternal);
+ memset(query, 0, sizeof(MultiCounterQuerySetS));
+ const int current_process_idx = current_query_index_for_process();
+ query->queries = NEW_C_HEAP_ARRAY(MultiCounterQueryS, current_process_idx + 1, mtInternal);
+ memset(query->queries, 0, sizeof(MultiCounterQueryS) * (current_process_idx + 1));
+ query->size = current_process_idx + 1;
+ return query;
+}
+
+static MultiCounterQueryP current_process_counter_query(MultiCounterQuerySetP process_query_set) {
+ assert(process_query_set != NULL, "invariant");
+ const int current_query_index = current_query_index_for_process();
+ assert(current_query_index < process_query_set->size, "invariant");
+ return &process_query_set->queries[current_query_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);
+}
+
+static int collect_process_query_data(MultiCounterQuerySetP counter_query_set) {
+ const int current_process_idx = current_query_index_for_process();
+ while (current_process_idx < counter_query_set->size - 1) {
+ const int new_size = --counter_query_set->size;
+ clear_multi_counter(&counter_query_set->queries[new_size]);
+ }
+ return collect_query_data(&counter_query_set->queries[current_process_idx]);
+}
+
+static int query_process_counter(MultiCounterQuerySetP process_query_set, int slot_index, DWORD format, PDH_FMT_COUNTERVALUE* const value) {
+ MultiCounterQueryP const current_query = current_process_counter_query(process_query_set);
+ 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) {
+ log_warning(os)("Unable to resolve PDH index: (%ld)", index);
+ log_warning(os)("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);
+ process_image_name = NULL;
+ }
+ if (pdh_IDProcess_counter_fmt != NULL) {
+ FREE_C_HEAP_ARRAY(char, pdh_IDProcess_counter_fmt);
+ 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) {
+ log_warning(os)("Unable to register PDH query for \"%s\"", full_counter_name);
+ log_warning(os)("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(MultiCounterQuerySetP query_set, 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 < 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 = &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) {
+ assert(is_valid_pdh_index(pdh_object_idx), "invariant");
+ assert(is_valid_pdh_index(pdh_counter_idx), "invariant");
+ 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;
+ MultiCounterQuerySetP _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 false;
+ }
+ _context_switches = create_counter_query(PDH_SYSTEM_IDX, PDH_CONTEXT_SWITCH_RATE_IDX);
+ if (_context_switches == NULL) {
+ return false;
+ }
+ _process_cpu_load = create_process_counter_query();
+ if (_process_cpu_load == NULL) {
+ return false;
+ }
+ if (allocate_counters(_process_cpu_load, 2) != OS_OK) {
+ return false;
+ }
+ if (initialize_process_counter(_process_cpu_load, 0, PDH_PROCESSOR_TIME_IDX) != OS_OK) {
+ return false;
+ }
+ if (initialize_process_counter(_process_cpu_load, 1, PDH_PRIV_PROCESSOR_TIME_IDX) != OS_OK) {
+ return false;
+ }
+ _process_cpu_load->initialized = true;
+
+ _machine_cpu_load = create_multi_counter_query();
+ if (_machine_cpu_load == NULL) {
+ return false;
+ }
+ if (initialize_cpu_query(_machine_cpu_load, PDH_PROCESSOR_TIME_IDX) != OS_OK) {
+ return false;
+ }
+ 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) {
+ assert(_machine_cpu_load != NULL, "invariant");
+ assert(which_logical_cpu < _machine_cpu_load->noOfCounters, "invariant");
+ *cpu_load = .0;
+ if (!_machine_cpu_load->initialized) {
+ return OS_ERR;
+ }
+ 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) {
+ assert(_process_cpu_load != NULL, "invariant");
+ *cpu_load = .0;
+ if (!_process_cpu_load->initialized) {
+ return OS_ERR;
+ }
+ 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->initialized) {
+ return OS_ERR;
+ }
+ 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->initialized) {
+ return OS_ERR;
+ }
+ 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);
+ _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);
+ _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;
+}
diff --git a/src/hotspot/os/windows/pdh_interface.cpp b/src/hotspot/os/windows/pdh_interface.cpp
new file mode 100644
index 00000000000..2ad7af2789e
--- /dev/null
+++ b/src/hotspot/os/windows/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/hotspot/os/windows/pdh_interface.hpp b/src/hotspot/os/windows/pdh_interface.hpp
new file mode 100644
index 00000000000..06a9526e028
--- /dev/null
+++ b/src/hotspot/os/windows/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/hotspot/os_cpu/solaris_x86/os_solaris_x86.inline.hpp b/src/hotspot/os_cpu/solaris_x86/os_solaris_x86.inline.hpp
index cedbe5e5033..db90eb4b1f2 100644
--- a/src/hotspot/os_cpu/solaris_x86/os_solaris_x86.inline.hpp
+++ b/src/hotspot/os_cpu/solaris_x86/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/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp
index e7f37ee5401..6ed4a29a48e 100644
--- a/src/hotspot/share/c1/c1_Compiler.cpp
+++ b/src/hotspot/share/c1/c1_Compiler.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,6 +33,7 @@
#include "c1/c1_ValueType.hpp"
#include "compiler/compileBroker.hpp"
#include "interpreter/linkResolver.hpp"
+#include "jfr/support/jfrIntrinsics.hpp"
#include "memory/allocation.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
@@ -41,6 +42,7 @@
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "utilities/bitMap.inline.hpp"
+#include "utilities/macros.hpp"
Compiler::Compiler() : AbstractCompiler(compiler_c1) {
@@ -222,10 +224,10 @@ bool Compiler::is_intrinsic_supported(const methodHandle& method) {
case vmIntrinsics::_compareAndSetObject:
case vmIntrinsics::_getCharStringU:
case vmIntrinsics::_putCharStringU:
-#ifdef TRACE_HAVE_INTRINSICS
+#ifdef JFR_HAVE_INTRINSICS
case vmIntrinsics::_counterTime:
- case vmIntrinsics::_getBufferWriter:
-#if defined(_LP64) || !defined(TRACE_ID_CLASS_SHIFT)
+ case vmIntrinsics::_getEventWriter:
+#if defined(_LP64) || !defined(TRACE_ID_SHIFT)
case vmIntrinsics::_getClassId:
#endif
#endif
diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp
index ca9fc3edd15..360e965aad3 100644
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp
@@ -35,6 +35,7 @@
#include "ci/ciUtilities.inline.hpp"
#include "compiler/compileBroker.hpp"
#include "interpreter/bytecode.hpp"
+#include "jfr/jfrEvents.hpp"
#include "memory/resourceArea.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/sharedRuntime.hpp"
@@ -4300,6 +4301,30 @@ void GraphBuilder::append_char_access(ciMethod* callee, bool is_store) {
}
}
+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) {
@@ -4315,18 +4340,10 @@ void GraphBuilder::print_inlining(ciMethod* callee, const char* msg, bool succes
log->inline_fail("reason unknown");
}
}
-#if INCLUDE_TRACE
EventCompilerInlining event;
if (event.should_commit()) {
- event.set_compileId(compilation()->env()->task()->compile_id());
- event.set_message(msg);
- event.set_succeeded(success);
- event.set_bci(bci());
- event.set_caller(method()->get_Method());
- event.set_callee(callee->to_trace_struct());
- event.commit();
+ post_inlining_event(&event, compilation()->env()->task()->compile_id(), msg, success, bci(), method(), callee);
}
-#endif // INCLUDE_TRACE
CompileTask::print_inlining_ul(callee, scope()->level(), bci(), msg);
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp
index 560d248bf81..768ab6343ec 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.cpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp
@@ -42,9 +42,6 @@
#include "runtime/vm_version.hpp"
#include "utilities/bitMap.inline.hpp"
#include "utilities/macros.hpp"
-#ifdef TRACE_HAVE_INTRINSICS
-#include "trace/traceMacros.hpp"
-#endif
#ifdef ASSERT
#define __ gen()->lir(__FILE__, __LINE__)->
@@ -2916,7 +2913,7 @@ void LIRGenerator::do_IfOp(IfOp* x) {
__ cmove(lir_cond(x->cond()), t_val.result(), f_val.result(), reg, as_BasicType(x->x()->type()));
}
-#ifdef TRACE_HAVE_INTRINSICS
+#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
@@ -2928,7 +2925,7 @@ void LIRGenerator::do_ClassIDIntrinsic(Intrinsic* x) {
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 = TRACE_KLASS_TRACE_ID_OFFSET;
+ 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);
@@ -2938,18 +2935,18 @@ void LIRGenerator::do_ClassIDIntrinsic(Intrinsic* x) {
#ifdef TRACE_ID_META_BITS
__ logical_and(id, LIR_OprFact::longConst(~TRACE_ID_META_BITS), id);
#endif
-#ifdef TRACE_ID_CLASS_SHIFT
- __ unsigned_shift_right(id, TRACE_ID_CLASS_SHIFT, id);
+#ifdef TRACE_ID_SHIFT
+ __ unsigned_shift_right(id, TRACE_ID_SHIFT, id);
#endif
__ move(id, rlock_result(x));
}
-void LIRGenerator::do_getBufferWriter(Intrinsic* x) {
+void LIRGenerator::do_getEventWriter(Intrinsic* x) {
LabelObj* L_end = new LabelObj();
LIR_Address* jobj_addr = new LIR_Address(getThreadPointer(),
- in_bytes(TRACE_THREAD_DATA_WRITER_OFFSET),
+ in_bytes(THREAD_LOCAL_WRITER_OFFSET_JFR),
T_OBJECT);
LIR_Opr result = rlock_result(x);
__ move_wide(jobj_addr, result);
@@ -2987,15 +2984,15 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) {
break;
}
-#ifdef TRACE_HAVE_INTRINSICS
+#ifdef JFR_HAVE_INTRINSICS
case vmIntrinsics::_getClassId:
do_ClassIDIntrinsic(x);
break;
- case vmIntrinsics::_getBufferWriter:
- do_getBufferWriter(x);
+ case vmIntrinsics::_getEventWriter:
+ do_getEventWriter(x);
break;
case vmIntrinsics::_counterTime:
- do_RuntimeCall(CAST_FROM_FN_PTR(address, TRACE_TIME_METHOD), x);
+ do_RuntimeCall(CAST_FROM_FN_PTR(address, JFR_TIME_FUNCTION), x);
break;
#endif
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp
index 5f152cff6e3..fff7d6f9fb6 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.hpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp
@@ -30,6 +30,7 @@
#include "c1/c1_LIR.hpp"
#include "ci/ciMethodData.hpp"
#include "gc/shared/barrierSet.hpp"
+#include "jfr/support/jfrIntrinsics.hpp"
#include "utilities/macros.hpp"
#include "utilities/sizes.hpp"
@@ -459,9 +460,9 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure {
SwitchRangeArray* create_lookup_ranges(LookupSwitch* x);
void do_SwitchRanges(SwitchRangeArray* x, LIR_Opr value, BlockBegin* default_sux);
-#ifdef TRACE_HAVE_INTRINSICS
+#ifdef JFR_HAVE_INTRINSICS
void do_ClassIDIntrinsic(Intrinsic* x);
- void do_getBufferWriter(Intrinsic* x);
+ void do_getEventWriter(Intrinsic* x);
#endif
void do_RuntimeCall(address routine, Intrinsic* x);
diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp
index 48059c27741..1627849bcd2 100644
--- a/src/hotspot/share/c1/c1_Runtime1.cpp
+++ b/src/hotspot/share/c1/c1_Runtime1.cpp
@@ -43,6 +43,7 @@
#include "gc/shared/collectedHeap.hpp"
#include "interpreter/bytecode.hpp"
#include "interpreter/interpreter.hpp"
+#include "jfr/support/jfrIntrinsics.hpp"
#include "logging/log.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/oopFactory.hpp"
@@ -320,8 +321,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());
FUNCTION_CASE(entry, StubRoutines::updateBytesCRC32C());
diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp
index 4870b70fbc8..dd107632a3a 100644
--- a/src/hotspot/share/ci/ciEnv.cpp
+++ b/src/hotspot/share/ci/ciEnv.cpp
@@ -42,6 +42,7 @@
#include "compiler/disassembler.hpp"
#include "gc/shared/collectedHeap.inline.hpp"
#include "interpreter/linkResolver.hpp"
+#include "jfr/jfrEvents.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
@@ -60,7 +61,6 @@
#include "runtime/safepointVerifiers.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/thread.inline.hpp"
-#include "trace/tracing.hpp"
#include "utilities/dtrace.hpp"
#include "utilities/macros.hpp"
#ifdef COMPILER1
@@ -1144,7 +1144,6 @@ void ciEnv::record_failure(const char* reason) {
}
void ciEnv::report_failure(const char* reason) {
- // Create and fire JFR event
EventCompilationFailure event;
if (event.should_commit()) {
event.set_compileId(compile_id());
diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp
index 251ce5b0c76..5b0403fed1d 100644
--- a/src/hotspot/share/ci/ciMethod.cpp
+++ b/src/hotspot/share/ci/ciMethod.cpp
@@ -48,7 +48,6 @@
#include "runtime/deoptimization.hpp"
#include "utilities/bitMap.inline.hpp"
#include "utilities/xmlstream.hpp"
-#include "trace/tracing.hpp"
#ifdef COMPILER2
#include "ci/bcEscapeAnalyzer.hpp"
#include "ci/ciTypeFlow.hpp"
@@ -1495,13 +1494,3 @@ bool ciMethod::is_consistent_info(ciMethod* declared_method, ciMethod* resolved_
}
// ------------------------------------------------------------------
-
-#if INCLUDE_TRACE
-TraceStructCalleeMethod ciMethod::to_trace_struct() const {
- TraceStructCalleeMethod result;
- result.set_type(holder()->name()->as_utf8());
- result.set_name(name()->as_utf8());
- result.set_descriptor(signature()->as_symbol()->as_utf8());
- return result;
-}
-#endif
diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp
index a9b0352aa25..57f1a9efdf4 100644
--- a/src/hotspot/share/ci/ciMethod.hpp
+++ b/src/hotspot/share/ci/ciMethod.hpp
@@ -32,7 +32,6 @@
#include "compiler/methodLiveness.hpp"
#include "prims/methodHandles.hpp"
#include "utilities/bitMap.hpp"
-#include "trace/tracing.hpp"
class ciMethodBlocks;
class MethodLiveness;
@@ -362,10 +361,6 @@ class ciMethod : public ciMetadata {
void print_short_name(outputStream* st = tty);
static bool is_consistent_info(ciMethod* declared_method, ciMethod* resolved_method);
-
-#if INCLUDE_TRACE
- TraceStructCalleeMethod to_trace_struct() const;
-#endif
};
#endif // SHARE_VM_CI_CIMETHOD_HPP
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index 6e75e392183..059f9abca00 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -67,7 +67,6 @@
#include "runtime/timer.hpp"
#include "services/classLoadingService.hpp"
#include "services/threadService.hpp"
-#include "trace/traceMacros.hpp"
#include "utilities/align.hpp"
#include "utilities/bitMap.inline.hpp"
#include "utilities/copy.hpp"
@@ -80,6 +79,9 @@
#if INCLUDE_CDS
#include "classfile/systemDictionaryShared.hpp"
#endif
+#if INCLUDE_JFR
+#include "jfr/support/jfrTraceIdExtension.hpp"
+#endif
// We generally try to create the oops directly when parsing, rather than
// allocating temporary data structures and copying the bytes twice. A
@@ -5639,7 +5641,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
}
}
- TRACE_INIT_ID(ik);
+ JFR_ONLY(INIT_ID(ik);)
// If we reach here, all is well.
// Now remove the InstanceKlass* from the _klass_to_deallocate field
diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp
index 0120c9f10e5..655cc79eedf 100644
--- a/src/hotspot/share/classfile/classLoaderData.cpp
+++ b/src/hotspot/share/classfile/classLoaderData.cpp
@@ -76,8 +76,10 @@
#include "utilities/growableArray.hpp"
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
-#if INCLUDE_TRACE
-#include "trace/tracing.hpp"
+#include "utilities/ticks.hpp"
+#if INCLUDE_JFR
+#include "jfr/jfr.hpp"
+#include "jfr/jfrEvents.hpp"
#endif
volatile size_t ClassLoaderDataGraph::_num_array_classes = 0;
@@ -161,7 +163,7 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_anonymous) :
NOT_PRODUCT(_dependency_count = 0); // number of class loader dependencies
- TRACE_INIT_ID(this);
+ JFR_ONLY(INIT_ID(this);)
}
ClassLoaderData::ChunkedHandleList::~ChunkedHandleList() {
@@ -1276,6 +1278,28 @@ bool ClassLoaderDataGraph::contains_loader_data(ClassLoaderData* loader_data) {
}
#endif // PRODUCT
+#if INCLUDE_JFR
+static Ticks class_unload_time;
+static void post_class_unload_event(Klass* const k) {
+ assert(k != NULL, "invariant");
+ EventClassUnload event(UNTIMED);
+ event.set_endtime(class_unload_time);
+ event.set_unloadedClass(k);
+ event.set_definingClassLoader(k->class_loader_data());
+ event.commit();
+}
+
+static void post_class_unload_events() {
+ assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
+ if (Jfr::is_enabled()) {
+ if (EventClassUnload::is_enabled()) {
+ class_unload_time = Ticks::now();
+ ClassLoaderDataGraph::classes_unloading_do(&post_class_unload_event);
+ }
+ Jfr::on_unloading_classes();
+ }
+}
+#endif // INCLUDE_JFR
// Move class loader data from main list to the unloaded list for unloading
// and deallocation later.
@@ -1353,8 +1377,7 @@ bool ClassLoaderDataGraph::do_unloading(bool clean_previous_versions) {
}
data = data->next();
}
-
- post_class_unload_events();
+ JFR_ONLY(post_class_unload_events();)
}
log_debug(class, loader, data)("do_unloading: loaders processed %u, loaders removed %u", loaders_processed, loaders_removed);
@@ -1393,20 +1416,6 @@ int ClassLoaderDataGraph::resize_if_needed() {
return resized;
}
-void ClassLoaderDataGraph::post_class_unload_events() {
-#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
-}
-
ClassLoaderDataGraphKlassIteratorAtomic::ClassLoaderDataGraphKlassIteratorAtomic()
: _next_klass(NULL) {
ClassLoaderData* cld = ClassLoaderDataGraph::_head;
@@ -1490,20 +1499,3 @@ void ClassLoaderDataGraph::print_on(outputStream * const out) {
}
}
#endif // PRODUCT
-
-#if INCLUDE_TRACE
-
-Ticks ClassLoaderDataGraph::_class_unload_time;
-
-void ClassLoaderDataGraph::class_unload_event(Klass* const k) {
- assert(k != NULL, "invariant");
-
- // post class unload event
- EventClassUnload event(UNTIMED);
- event.set_endtime(_class_unload_time);
- event.set_unloadedClass(k);
- event.set_definingClassLoader(k->class_loader_data());
- event.commit();
-}
-
-#endif // INCLUDE_TRACE
diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp
index 92745fc46b5..e88f77ecebf 100644
--- a/src/hotspot/share/classfile/classLoaderData.hpp
+++ b/src/hotspot/share/classfile/classLoaderData.hpp
@@ -31,13 +31,13 @@
#include "oops/oopHandle.hpp"
#include "oops/weakHandle.hpp"
#include "runtime/mutex.hpp"
-#include "trace/traceMacros.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
+
//
// A class loader represents a linkset. Conceptually, a linkset identifies
// the complete transitive closure of resolved links that a dynamic linker can
@@ -85,7 +85,6 @@ class ClassLoaderDataGraph : public AllStatic {
static ClassLoaderData* add_to_graph(Handle class_loader, bool anonymous);
static ClassLoaderData* add(Handle class_loader, bool anonymous);
- static void post_class_unload_events();
public:
static ClassLoaderData* find_or_create(Handle class_loader);
static void purge();
@@ -167,12 +166,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
@@ -268,7 +261,7 @@ class ClassLoaderData : public CHeapObj {
// JFR support
Klass* _class_loader_klass;
Symbol* _class_loader_name;
- TRACE_DEFINE_TRACE_ID_FIELD;
+ JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
void set_next(ClassLoaderData* next) { _next = next; }
ClassLoaderData* next() const { return _next; }
@@ -410,7 +403,7 @@ class ClassLoaderData : public CHeapObj {
Klass* class_loader_klass() const { return _class_loader_klass; }
Symbol* class_loader_name() const { return _class_loader_name; }
- TRACE_DEFINE_TRACE_ID_METHODS;
+ JFR_ONLY(DEFINE_TRACE_ID_METHODS;)
};
// An iterator that distributes Klasses to parallel worker threads.
diff --git a/src/hotspot/share/classfile/klassFactory.cpp b/src/hotspot/share/classfile/klassFactory.cpp
index 474d7f8c66a..d6214e6fd70 100644
--- a/src/hotspot/share/classfile/klassFactory.cpp
+++ b/src/hotspot/share/classfile/klassFactory.cpp
@@ -35,7 +35,11 @@
#include "prims/jvmtiEnvBase.hpp"
#include "prims/jvmtiRedefineClasses.hpp"
#include "runtime/handles.inline.hpp"
-#include "trace/traceMacros.hpp"
+#include "utilities/macros.hpp"
+#if INCLUDE_JFR
+#include "jfr/support/jfrKlassExtension.hpp"
+#endif
+
// called during initial loading of a shared class
InstanceKlass* KlassFactory::check_shared_class_file_load_hook(
@@ -228,7 +232,7 @@ InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream,
result->store_fingerprint(stream->compute_fingerprint());
}
- TRACE_KLASS_CREATION(result, parser, THREAD);
+ JFR_ONLY(ON_KLASS_CREATION(result, parser, THREAD);)
#if INCLUDE_CDS
if (DumpSharedSpaces) {
diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp
index ce2c960b714..1a64a6e6a58 100644
--- a/src/hotspot/share/classfile/moduleEntry.cpp
+++ b/src/hotspot/share/classfile/moduleEntry.cpp
@@ -33,7 +33,6 @@
#include "oops/symbol.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/safepoint.hpp"
-#include "trace/traceMacros.hpp"
#include "utilities/events.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp"
@@ -215,7 +214,7 @@ void ModuleEntry::purge_reads() {
}
}
-void ModuleEntry::module_reads_do(ModuleClosure* const f) {
+void ModuleEntry::module_reads_do(ModuleClosure* f) {
assert_locked_or_safepoint(Module_lock);
assert(f != NULL, "invariant");
@@ -279,7 +278,7 @@ ModuleEntry* ModuleEntry::new_unnamed_module_entry(Handle module_handle, ClassLo
entry->set_loader_data(cld);
entry->_is_open = true;
- TRACE_INIT_ID(entry);
+ JFR_ONLY(INIT_ID(entry);)
return entry;
}
@@ -367,7 +366,7 @@ ModuleEntry* ModuleEntryTable::new_entry(unsigned int hash, Handle module_handle
}
}
- TRACE_INIT_ID(entry);
+ JFR_ONLY(INIT_ID(entry);)
return entry;
}
diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp
index 37a6756003c..232b2644f3c 100644
--- a/src/hotspot/share/classfile/moduleEntry.hpp
+++ b/src/hotspot/share/classfile/moduleEntry.hpp
@@ -32,10 +32,13 @@
#include "oops/symbol.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/mutexLocker.hpp"
-#include "trace/traceMacros.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.hpp"
+#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
+#if INCLUDE_JFR
+#include "jfr/support/jfrTraceIdExtension.hpp"
+#endif
#define UNNAMED_MODULE "Unnamed Module"
#define JAVAPKG "java"
@@ -69,7 +72,7 @@ private:
bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules
bool _is_open; // whether the packages in the module are all unqualifiedly exported
bool _is_patched; // whether the module is patched via --patch-module
- TRACE_DEFINE_TRACE_ID_FIELD;
+ JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
enum {MODULE_READS_SIZE = 101}; // Initial size of list of modules that the module can read.
public:
@@ -164,8 +167,6 @@ public:
// iteration support for readability
void module_reads_do(ModuleClosure* const f);
- TRACE_DEFINE_TRACE_ID_METHODS;
-
// Purge dead weak references out of reads list when any given class loader is unloaded.
void purge_reads();
void delete_reads();
@@ -178,12 +179,14 @@ public:
void print(outputStream* st = tty);
void verify();
+
+ JFR_ONLY(DEFINE_TRACE_ID_METHODS;)
};
// Iterator interface
class ModuleClosure: public StackObj {
public:
- virtual void do_module(ModuleEntry* const module) = 0;
+ virtual void do_module(ModuleEntry* module) = 0;
};
diff --git a/src/hotspot/share/classfile/packageEntry.cpp b/src/hotspot/share/classfile/packageEntry.cpp
index aba979937b9..1495c207767 100644
--- a/src/hotspot/share/classfile/packageEntry.cpp
+++ b/src/hotspot/share/classfile/packageEntry.cpp
@@ -29,7 +29,6 @@
#include "memory/resourceArea.hpp"
#include "oops/symbol.hpp"
#include "runtime/handles.inline.hpp"
-#include "trace/traceMacros.hpp"
#include "utilities/events.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp"
@@ -200,7 +199,7 @@ PackageEntry* PackageEntryTable::new_entry(unsigned int hash, Symbol* name, Modu
assert(Module_lock->owned_by_self(), "should have the Module_lock");
PackageEntry* entry = (PackageEntry*)Hashtable::allocate_new_entry(hash, name);
- TRACE_INIT_ID(entry);
+ JFR_ONLY(INIT_ID(entry);)
// Initialize fields specific to a PackageEntry
entry->init();
@@ -283,7 +282,7 @@ void PackageEntryTable::verify_javabase_packages(GrowableArray *pkg_lis
}
// iteration of qualified exports
-void PackageEntry::package_exports_do(ModuleClosure* const f) {
+void PackageEntry::package_exports_do(ModuleClosure* f) {
assert_locked_or_safepoint(Module_lock);
assert(f != NULL, "invariant");
diff --git a/src/hotspot/share/classfile/packageEntry.hpp b/src/hotspot/share/classfile/packageEntry.hpp
index ecee6a1d083..f26ce3a6487 100644
--- a/src/hotspot/share/classfile/packageEntry.hpp
+++ b/src/hotspot/share/classfile/packageEntry.hpp
@@ -29,7 +29,12 @@
#include "oops/symbol.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.hpp"
+#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
+#if INCLUDE_JFR
+#include "jfr/support/jfrTraceIdExtension.hpp"
+#endif
+
// A PackageEntry basically represents a Java package. It contains:
// - Symbol* containing the package's name.
@@ -104,7 +109,7 @@ private:
// Contains list of modules this package is qualifiedly exported to. Access
// to this list is protected by the Module_lock.
GrowableArray* _qualified_exports;
- TRACE_DEFINE_TRACE_ID_FIELD;
+ JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
// Initial size of a package entry's list of qualified exports.
enum {QUAL_EXP_SIZE = 43};
@@ -197,9 +202,9 @@ public:
}
// iteration of qualified exports
- void package_exports_do(ModuleClosure* const f);
+ void package_exports_do(ModuleClosure* f);
- TRACE_DEFINE_TRACE_ID_METHODS;
+ JFR_ONLY(DEFINE_TRACE_ID_METHODS;)
// Purge dead weak references out of exported list when any given class loader is unloaded.
void purge_qualified_exports();
diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp
index cd705214330..b578e63283e 100644
--- a/src/hotspot/share/classfile/systemDictionary.cpp
+++ b/src/hotspot/share/classfile/systemDictionary.cpp
@@ -47,6 +47,7 @@
#include "gc/shared/oopStorage.inline.hpp"
#include "interpreter/bytecodeStream.hpp"
#include "interpreter/interpreter.hpp"
+#include "jfr/jfrEvents.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/filemap.hpp"
@@ -81,7 +82,6 @@
#include "services/classLoadingService.hpp"
#include "services/diagnosticCommand.hpp"
#include "services/threadService.hpp"
-#include "trace/tracing.hpp"
#include "utilities/macros.hpp"
#if INCLUDE_CDS
#include "classfile/systemDictionaryShared.hpp"
@@ -623,32 +623,16 @@ InstanceKlass* SystemDictionary::handle_parallel_super_load(
return NULL;
}
-static void post_class_load_event(EventClassLoad* event,
- const InstanceKlass* k,
- const ClassLoaderData* init_cld) {
-#if INCLUDE_TRACE
+static void post_class_load_event(EventClassLoad* event, const InstanceKlass* k, const ClassLoaderData* init_cld) {
assert(event != NULL, "invariant");
assert(k != NULL, "invariant");
- if (event->should_commit()) {
- event->set_loadedClass(k);
- event->set_definingClassLoader(k->class_loader_data());
- event->set_initiatingClassLoader(init_cld);
- event->commit();
- }
-#endif // INCLUDE_TRACE
+ assert(event->should_commit(), "invariant");
+ event->set_loadedClass(k);
+ event->set_definingClassLoader(k->class_loader_data());
+ event->set_initiatingClassLoader(init_cld);
+ event->commit();
}
-static void class_define_event(InstanceKlass* k,
- const ClassLoaderData* def_cld) {
-#if INCLUDE_TRACE
- EventClassDefine event;
- if (event.should_commit()) {
- event.set_definedClass(k);
- event.set_definingClassLoader(def_cld);
- event.commit();
- }
-#endif // INCLUDE_TRACE
-}
// Be careful when modifying this code: once you have run
// placeholders()->find_and_add(PlaceholderTable::LOAD_INSTANCE),
@@ -881,9 +865,9 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
if (HAS_PENDING_EXCEPTION || k == NULL) {
return NULL;
}
-
- post_class_load_event(&class_load_start_event, k, loader_data);
-
+ if (class_load_start_event.should_commit()) {
+ post_class_load_event(&class_load_start_event, k, loader_data);
+ }
#ifdef ASSERT
{
ClassLoaderData* loader_data = k->class_loader_data();
@@ -1045,8 +1029,9 @@ InstanceKlass* SystemDictionary::parse_stream(Symbol* class_name,
assert(THREAD->is_Java_thread(), "thread->is_Java_thread()");
JvmtiExport::post_class_load((JavaThread *) THREAD, k);
}
-
- post_class_load_event(&class_load_start_event, k, loader_data);
+ if (class_load_start_event.should_commit()) {
+ post_class_load_event(&class_load_start_event, k, loader_data);
+ }
}
assert(host_klass != NULL || NULL == cp_patches,
"cp_patches only found with host_klass");
@@ -1558,6 +1543,15 @@ InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle
}
}
+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(InstanceKlass* k, TRAPS) {
HandleMark hm(THREAD);
@@ -1626,7 +1620,7 @@ void SystemDictionary::define_instance_class(InstanceKlass* k, TRAPS) {
JvmtiExport::post_class_load((JavaThread *) THREAD, k);
}
- class_define_event(k, loader_data);
+ post_class_define_event(k, loader_data);
}
// Support parallel classloading
diff --git a/src/hotspot/share/classfile/vmSymbols.cpp b/src/hotspot/share/classfile/vmSymbols.cpp
index 50ac81381d9..ebbf66f8ab0 100644
--- a/src/hotspot/share/classfile/vmSymbols.cpp
+++ b/src/hotspot/share/classfile/vmSymbols.cpp
@@ -350,7 +350,7 @@ vmIntrinsics::ID vmIntrinsics::for_raw_conversion(BasicType src, BasicType dest)
bool vmIntrinsics::preserves_state(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:
@@ -388,7 +388,7 @@ bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) {
bool vmIntrinsics::can_trap(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:
case vmIntrinsics::_getClassId:
#endif
@@ -424,7 +424,7 @@ bool vmIntrinsics::can_trap(vmIntrinsics::ID id) {
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/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index 46554467f70..f9f22a68649 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -26,10 +26,12 @@
#define SHARE_VM_CLASSFILE_VMSYMBOLS_HPP
#include "classfile/moduleEntry.hpp"
-#include "oops/symbol.hpp"
-#include "memory/iterator.hpp"
-#include "trace/traceMacros.hpp"
+#include "jfr/support/jfrIntrinsics.hpp"
#include "jvmci/vmSymbols_jvmci.hpp"
+#include "memory/iterator.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.
@@ -640,8 +642,8 @@
/* forEachRemaining support */ \
template(java_util_stream_StreamsRangeIntSpliterator, "java/util/stream/Streams$RangeIntSpliterator") \
\
- /* trace signatures */ \
- TRACE_TEMPLATES(template) \
+ /* jfr signatures */ \
+ JFR_TEMPLATES(template) \
\
/* cds */ \
template(jdk_internal_loader_ClassLoaders, "jdk/internal/loader/ClassLoaders") \
@@ -827,7 +829,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/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp
index 68e2d624391..8c3438210c2 100644
--- a/src/hotspot/share/code/codeCache.cpp
+++ b/src/hotspot/share/code/codeCache.cpp
@@ -33,6 +33,7 @@
#include "code/nmethod.hpp"
#include "code/pcDesc.hpp"
#include "compiler/compileBroker.hpp"
+#include "jfr/jfrEvents.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/allocation.inline.hpp"
@@ -53,7 +54,6 @@
#include "runtime/sweeper.hpp"
#include "runtime/vmThread.hpp"
#include "services/memoryService.hpp"
-#include "trace/tracing.hpp"
#include "utilities/align.hpp"
#include "utilities/vmError.hpp"
#include "utilities/xmlstream.hpp"
diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp
index e72a809fa0c..375153c0c7a 100644
--- a/src/hotspot/share/compiler/compileBroker.cpp
+++ b/src/hotspot/share/compiler/compileBroker.cpp
@@ -35,6 +35,7 @@
#include "compiler/compilerOracle.hpp"
#include "compiler/directivesParser.hpp"
#include "interpreter/linkResolver.hpp"
+#include "jfr/jfrEvents.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/allocation.inline.hpp"
@@ -57,11 +58,11 @@
#include "runtime/sweeper.hpp"
#include "runtime/timerTrace.hpp"
#include "runtime/vframe.inline.hpp"
-#include "trace/tracing.hpp"
#include "utilities/debug.hpp"
#include "utilities/dtrace.hpp"
#include "utilities/events.hpp"
#include "utilities/formatBuffer.hpp"
+#include "utilities/macros.hpp"
#ifdef COMPILER1
#include "c1/c1_Compiler.hpp"
#endif
@@ -1945,8 +1946,7 @@ static void codecache_print(outputStream* out, bool detailed) {
}
}
-void CompileBroker::post_compile(CompilerThread* thread, CompileTask* task, EventCompilation& event, bool success, ciEnv* ci_env) {
-
+void CompileBroker::post_compile(CompilerThread* thread, CompileTask* task, bool success, ciEnv* ci_env) {
if (success) {
task->mark_success();
if (ci_env != NULL) {
@@ -1959,19 +1959,21 @@ void CompileBroker::post_compile(CompilerThread* thread, CompileTask* task, Even
}
}
}
-
// simulate crash during compilation
assert(task->compile_id() != CICrashAt, "just as planned");
- if (event.should_commit()) {
- 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();
- }
+}
+
+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();
}
int DirectivesStack::_depth = 0;
@@ -2066,7 +2068,10 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
compilable = ciEnv::MethodCompilable_not_at_tier;
}
}
- post_compile(thread, task, event, task->code() != NULL, NULL);
+ post_compile(thread, task, task->code() != NULL, NULL);
+ if (event.should_commit()) {
+ post_compilation_event(&event, task);
+ }
} else
#endif // INCLUDE_JVMCI
@@ -2123,7 +2128,10 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
ci_env.report_failure(failure_reason);
}
- post_compile(thread, task, event, !ci_env.failing(), &ci_env);
+ post_compile(thread, task, !ci_env.failing(), &ci_env);
+ if (event.should_commit()) {
+ post_compilation_event(&event, task);
+ }
}
// Remove the JNI handle block after the ciEnv destructor has run in
// the previous block.
diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp
index e0a9b4be3a8..312a4a5c526 100644
--- a/src/hotspot/share/compiler/compileBroker.hpp
+++ b/src/hotspot/share/compiler/compileBroker.hpp
@@ -30,7 +30,6 @@
#include "compiler/compileTask.hpp"
#include "compiler/compilerDirectives.hpp"
#include "runtime/perfData.hpp"
-#include "trace/tracing.hpp"
#include "utilities/stack.hpp"
#if INCLUDE_JVMCI
#include "jvmci/jvmciCompiler.hpp"
@@ -252,7 +251,7 @@ class CompileBroker: AllStatic {
#endif
static void invoke_compiler_on_method(CompileTask* task);
- static void post_compile(CompilerThread* thread, CompileTask* task, EventCompilation& event, bool success, ciEnv* ci_env);
+ static void post_compile(CompilerThread* thread, CompileTask* task, bool success, ciEnv* ci_env);
static void set_last_compile(CompilerThread *thread, const methodHandle& method, bool is_osr, int comp_level);
static void push_jni_handle_block();
static void pop_jni_handle_block();
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
index 951e1408aca..2d9c042bced 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
@@ -86,7 +86,6 @@ class STWGCTimer;
class G1NewTracer;
class EvacuationFailedInfo;
class nmethod;
-class Ticks;
class WorkGang;
class G1Allocator;
class G1ArchiveAllocator;
diff --git a/src/hotspot/share/gc/g1/g1EvacStats.cpp b/src/hotspot/share/gc/g1/g1EvacStats.cpp
index bdfef475ddf..70c3996911a 100644
--- a/src/hotspot/share/gc/g1/g1EvacStats.cpp
+++ b/src/hotspot/share/gc/g1/g1EvacStats.cpp
@@ -23,12 +23,11 @@
*/
#include "precompiled.hpp"
-#include "memory/allocation.inline.hpp"
#include "gc/g1/g1_globals.hpp"
#include "gc/g1/g1EvacStats.hpp"
#include "gc/shared/gcId.hpp"
#include "logging/log.hpp"
-#include "trace/tracing.hpp"
+#include "memory/allocation.inline.hpp"
void G1EvacStats::log_plab_allocation() {
PLABStats::log_plab_allocation();
diff --git a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp
index b608984fe84..5e7b291e26e 100644
--- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp
+++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp
@@ -34,7 +34,6 @@
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "logging/log.hpp"
-#include "utilities/ticks.inline.hpp"
class G1AdjustLiveClosure : public StackObj {
G1AdjustClosure* _adjust_closure;
diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp
index 222737bbf5a..660afc88f02 100644
--- a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp
+++ b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp
@@ -32,7 +32,7 @@
#include "gc/shared/gcTraceTime.inline.hpp"
#include "logging/log.hpp"
#include "oops/oop.inline.hpp"
-#include "utilities/ticks.inline.hpp"
+#include "utilities/ticks.hpp"
class G1ResetHumongousClosure : public HeapRegionClosure {
G1CMBitMap* _bitmap;
diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactTask.hpp b/src/hotspot/share/gc/g1/g1FullGCCompactTask.hpp
index cd6a41f078a..6c8eaf5967e 100644
--- a/src/hotspot/share/gc/g1/g1FullGCCompactTask.hpp
+++ b/src/hotspot/share/gc/g1/g1FullGCCompactTask.hpp
@@ -31,7 +31,6 @@
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/shared/referenceProcessor.hpp"
-#include "utilities/ticks.hpp"
class G1CollectedHeap;
class G1CMBitMap;
diff --git a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp
index 05335ca837d..3e08b5a41bd 100644
--- a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp
+++ b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp
@@ -36,7 +36,7 @@
#include "gc/shared/referenceProcessor.hpp"
#include "logging/log.hpp"
#include "oops/oop.inline.hpp"
-#include "utilities/ticks.inline.hpp"
+#include "utilities/ticks.hpp"
bool G1FullGCPrepareTask::G1CalculatePointersClosure::do_heap_region(HeapRegion* hr) {
if (hr->is_humongous()) {
diff --git a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp
index 3be04d160f5..fcaf797a12f 100644
--- a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp
+++ b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp
@@ -32,7 +32,6 @@
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/shared/referenceProcessor.hpp"
-#include "utilities/ticks.hpp"
class G1CMBitMap;
diff --git a/src/hotspot/share/gc/g1/g1FullGCTask.cpp b/src/hotspot/share/gc/g1/g1FullGCTask.cpp
index 52fe559098e..b9faa7af603 100644
--- a/src/hotspot/share/gc/g1/g1FullGCTask.cpp
+++ b/src/hotspot/share/gc/g1/g1FullGCTask.cpp
@@ -25,7 +25,7 @@
#include "precompiled.hpp"
#include "gc/g1/g1FullGCTask.hpp"
#include "logging/log.hpp"
-#include "utilities/ticks.inline.hpp"
+#include "utilities/ticks.hpp"
void G1FullGCTask::log_task(const char* name, uint worker_id, const Ticks& start, const Ticks& stop) {
Tickspan duration = stop - start;
diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp
index 50662f273a5..5aeb4baa453 100644
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp
@@ -488,7 +488,7 @@ G1GCParPhaseTimesTracker::G1GCParPhaseTimesTracker(G1GCPhaseTimes* phase_times,
G1GCParPhaseTimesTracker::~G1GCParPhaseTimesTracker() {
if (_phase_times != NULL) {
- _phase_times->record_time_secs(_phase, _worker_id, TicksToTimeHelper::seconds(Ticks::now() - _start_time));
+ _phase_times->record_time_secs(_phase, _worker_id, (Ticks::now() - _start_time).seconds());
}
}
@@ -506,7 +506,7 @@ G1EvacPhaseTimesTracker::~G1EvacPhaseTimesTracker() {
if (_phase_times != NULL) {
// Exclude trim time by increasing the start time.
_start_time += _trim_time;
- _phase_times->record_or_add_objcopy_time_secs(_worker_id, TicksToTimeHelper::seconds(_trim_time));
+ _phase_times->record_or_add_objcopy_time_secs(_worker_id, _trim_time.seconds());
}
}
diff --git a/src/hotspot/share/gc/g1/g1HeapRegionEventSender.cpp b/src/hotspot/share/gc/g1/g1HeapRegionEventSender.cpp
new file mode 100644
index 00000000000..28fc58d19a5
--- /dev/null
+++ b/src/hotspot/share/gc/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/g1/g1CollectedHeap.hpp"
+#include "gc/g1/heapRegion.hpp"
+#include "g1HeapRegionEventSender.hpp"
+#include "jfr/jfrEvents.hpp"
+
+class DumpEventInfoClosure : public HeapRegionClosure {
+public:
+ bool do_heap_region(HeapRegion* r) {
+ EventG1HeapRegionInformation evt;
+ evt.set_index(r->hrm_index());
+ 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/hotspot/share/trace/noTraceBackend.hpp b/src/hotspot/share/gc/g1/g1HeapRegionEventSender.hpp
similarity index 71%
rename from src/hotspot/share/trace/noTraceBackend.hpp
rename to src/hotspot/share/gc/g1/g1HeapRegionEventSender.hpp
index fc557907c95..1a7dc7b0ca2 100644
--- a/src/hotspot/share/trace/noTraceBackend.hpp
+++ b/src/hotspot/share/gc/g1/g1HeapRegionEventSender.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2017, 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,15 @@
* questions.
*
*/
-#ifndef SHARE_VM_TRACE_NOTRACEBACKEND_HPP
-#define SHARE_VM_TRACE_NOTRACEBACKEND_HPP
-#include "jni.h"
-#include "trace/traceTime.hpp"
+#ifndef SHARE_VM_GC_G1_G1HEAPREGIONEVENTSENDER_HPP
+#define SHARE_VM_GC_G1_G1HEAPREGIONEVENTSENDER_HPP
-class NoTraceBackend {
+#include "memory/allocation.hpp"
+
+class G1HeapRegionEventSender : public AllStatic {
public:
- static TracingTime time() {
- return 0;
- }
+ static void send_events();
};
-class TraceThreadData {
-public:
- TraceThreadData() {}
-};
-
-typedef NoTraceBackend Tracing;
-
-#endif // SHARE_VM_TRACE_NOTRACEBACKEND_HPP
+#endif // SHARE_VM_GC_G1_G1HEAPREGIONEVENTSENDER_HPP
diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp
index 06eb276a08a..d59afd27e2a 100644
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp
@@ -29,7 +29,6 @@
#include "gc/g1/g1RemSet.hpp"
#include "oops/access.inline.hpp"
#include "oops/oop.inline.hpp"
-#include "utilities/ticks.inline.hpp"
template void G1ParScanThreadState::do_oop_evac(T* p) {
// Reference should not be NULL here as such are never pushed to the task queue.
diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp
index 9e3fcb3e8c1..de759878bba 100644
--- a/src/hotspot/share/gc/g1/g1RemSet.cpp
+++ b/src/hotspot/share/gc/g1/g1RemSet.cpp
@@ -44,11 +44,12 @@
#include "memory/resourceArea.hpp"
#include "oops/access.inline.hpp"
#include "oops/oop.inline.hpp"
+#include "runtime/os.hpp"
#include "utilities/align.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/intHisto.hpp"
#include "utilities/stack.inline.hpp"
-#include "utilities/ticks.inline.hpp"
+#include "utilities/ticks.hpp"
// Collects information about the overall remembered set scan progress during an evacuation.
class G1RemSetScanState : public CHeapObj {
@@ -428,15 +429,15 @@ void G1RemSet::scan_rem_set(G1ParScanThreadState* pss, uint worker_i) {
G1GCPhaseTimes* p = _g1p->phase_times();
- p->record_time_secs(G1GCPhaseTimes::ScanRS, worker_i, TicksToTimeHelper::seconds(cl.rem_set_root_scan_time()));
- p->add_time_secs(G1GCPhaseTimes::ObjCopy, worker_i, TicksToTimeHelper::seconds(cl.rem_set_trim_partially_time()));
+ p->record_time_secs(G1GCPhaseTimes::ScanRS, worker_i, cl.rem_set_root_scan_time().seconds());
+ p->add_time_secs(G1GCPhaseTimes::ObjCopy, worker_i, cl.rem_set_trim_partially_time().seconds());
p->record_thread_work_item(G1GCPhaseTimes::ScanRS, worker_i, cl.cards_scanned(), G1GCPhaseTimes::ScanRSScannedCards);
p->record_thread_work_item(G1GCPhaseTimes::ScanRS, worker_i, cl.cards_claimed(), G1GCPhaseTimes::ScanRSClaimedCards);
p->record_thread_work_item(G1GCPhaseTimes::ScanRS, worker_i, cl.cards_skipped(), G1GCPhaseTimes::ScanRSSkippedCards);
- p->record_time_secs(G1GCPhaseTimes::CodeRoots, worker_i, TicksToTimeHelper::seconds(cl.strong_code_root_scan_time()));
- p->add_time_secs(G1GCPhaseTimes::ObjCopy, worker_i, TicksToTimeHelper::seconds(cl.strong_code_root_trim_partially_time()));
+ p->record_time_secs(G1GCPhaseTimes::CodeRoots, worker_i, cl.strong_code_root_scan_time().seconds());
+ p->add_time_secs(G1GCPhaseTimes::ObjCopy, worker_i, cl.strong_code_root_trim_partially_time().seconds());
}
// Closure used for updating rem sets. Only called during an evacuation pause.
@@ -935,7 +936,7 @@ public:
"TARS " PTR_FORMAT,
region_idx,
_cm->liveness(region_idx) * HeapWordSize,
- TicksToTimeHelper::seconds(time) * 1000.0,
+ time.seconds() * 1000.0,
marked_bytes,
p2i(hr->bottom()),
p2i(top_at_mark_start),
diff --git a/src/hotspot/share/gc/g1/heapRegionTracer.cpp b/src/hotspot/share/gc/g1/heapRegionTracer.cpp
index 6cffce22bfe..e039ae219a1 100644
--- a/src/hotspot/share/gc/g1/heapRegionTracer.cpp
+++ b/src/hotspot/share/gc/g1/heapRegionTracer.cpp
@@ -24,7 +24,7 @@
#include "precompiled.hpp"
#include "gc/g1/heapRegionTracer.hpp"
-#include "trace/tracing.hpp"
+#include "jfr/jfrEvents.hpp"
void HeapRegionTracer::send_region_type_change(uint index,
G1HeapRegionTraceType::Type from,
diff --git a/src/hotspot/share/gc/shared/ageTableTracer.cpp b/src/hotspot/share/gc/shared/ageTableTracer.cpp
index 2ae79b7cd37..5e0fbcfbcb1 100644
--- a/src/hotspot/share/gc/shared/ageTableTracer.cpp
+++ b/src/hotspot/share/gc/shared/ageTableTracer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 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
@@ -25,7 +25,7 @@
#include "precompiled.hpp"
#include "gc/shared/ageTableTracer.hpp"
#include "gc/shared/gcId.hpp"
-#include "trace/tracing.hpp"
+#include "jfr/jfrEvents.hpp"
void AgeTableTracer::send_tenuring_distribution_event(uint age, size_t size) {
EventTenuringDistribution e;
diff --git a/src/hotspot/share/gc/shared/allocTracer.cpp b/src/hotspot/share/gc/shared/allocTracer.cpp
index a1efeed104e..db89e4cf08a 100644
--- a/src/hotspot/share/gc/shared/allocTracer.cpp
+++ b/src/hotspot/share/gc/shared/allocTracer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2017, 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
@@ -24,13 +24,16 @@
#include "precompiled.hpp"
#include "gc/shared/allocTracer.hpp"
+#include "jfr/jfrEvents.hpp"
#include "runtime/handles.hpp"
-#include "trace/tracing.hpp"
-#include "trace/traceMacros.hpp"
#include "utilities/globalDefinitions.hpp"
+#include "utilities/macros.hpp"
+#if INCLUDE_JFR
+#include "jfr/support/jfrAllocationTracer.hpp"
+#endif
void AllocTracer::send_allocation_outside_tlab(Klass* klass, HeapWord* obj, size_t alloc_size, Thread* thread) {
- TRACE_ALLOCATION(obj, alloc_size, thread);
+ JFR_ONLY(JfrAllocationTracer tracer(obj, alloc_size, thread);)
EventObjectAllocationOutsideTLAB event;
if (event.should_commit()) {
event.set_objectClass(klass);
@@ -40,7 +43,7 @@ void AllocTracer::send_allocation_outside_tlab(Klass* klass, HeapWord* obj, size
}
void AllocTracer::send_allocation_in_new_tlab(Klass* klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, Thread* thread) {
- TRACE_ALLOCATION(obj, tlab_size, thread);
+ JFR_ONLY(JfrAllocationTracer tracer(obj, alloc_size, thread);)
EventObjectAllocationInNewTLAB event;
if (event.should_commit()) {
event.set_objectClass(klass);
diff --git a/src/hotspot/share/gc/shared/copyFailedInfo.hpp b/src/hotspot/share/gc/shared/copyFailedInfo.hpp
index b7e0bbe98b0..2b0854789b6 100644
--- a/src/hotspot/share/gc/shared/copyFailedInfo.hpp
+++ b/src/hotspot/share/gc/shared/copyFailedInfo.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2016, 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
@@ -25,8 +25,8 @@
#ifndef SHARE_VM_GC_SHARED_COPYFAILEDINFO_HPP
#define SHARE_VM_GC_SHARED_COPYFAILEDINFO_HPP
+#include "jfr/support/jfrThreadId.hpp"
#include "runtime/thread.hpp"
-#include "trace/traceMacros.hpp"
#include "utilities/globalDefinitions.hpp"
class CopyFailedInfo : public CHeapObj {
@@ -72,9 +72,9 @@ class PromotionFailedInfo : public CopyFailedInfo {
void register_copy_failure(size_t size) {
CopyFailedInfo::register_copy_failure(size);
if (_thread_trace_id == 0) {
- _thread_trace_id = THREAD_TRACE_ID(Thread::current());
+ _thread_trace_id = JFR_THREAD_ID(Thread::current());
} else {
- assert(THREAD_TRACE_ID(Thread::current()) == _thread_trace_id,
+ assert(JFR_THREAD_ID(Thread::current()) == _thread_trace_id,
"The PromotionFailedInfo should be thread local.");
}
}
diff --git a/src/hotspot/share/gc/shared/gcConfiguration.cpp b/src/hotspot/share/gc/shared/gcConfiguration.cpp
new file mode 100644
index 00000000000..453d8c633c2
--- /dev/null
+++ b/src/hotspot/share/gc/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/shared/collectedHeap.hpp"
+#include "gc/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/hotspot/share/gc/shared/gcConfiguration.hpp b/src/hotspot/share/gc/shared/gcConfiguration.hpp
new file mode 100644
index 00000000000..3360eeac2ce
--- /dev/null
+++ b/src/hotspot/share/gc/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/shared/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/hotspot/share/gc/shared/gcTimer.cpp b/src/hotspot/share/gc/shared/gcTimer.cpp
index c17cd184586..229812a0a63 100644
--- a/src/hotspot/share/gc/shared/gcTimer.cpp
+++ b/src/hotspot/share/gc/shared/gcTimer.cpp
@@ -25,7 +25,6 @@
#include "precompiled.hpp"
#include "gc/shared/gcTimer.hpp"
#include "utilities/growableArray.hpp"
-#include "utilities/ticks.inline.hpp"
// the "time" parameter for most functions
// has a default value set by Ticks::now()
@@ -376,7 +375,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() {
@@ -384,7 +383,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/hotspot/share/gc/shared/gcTrace.cpp b/src/hotspot/share/gc/shared/gcTrace.cpp
index 6b5f4eca33e..eea530760b7 100644
--- a/src/hotspot/share/gc/shared/gcTrace.cpp
+++ b/src/hotspot/share/gc/shared/gcTrace.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2016, 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
@@ -35,7 +35,7 @@
#include "runtime/os.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
-#include "utilities/ticks.inline.hpp"
+#include "utilities/ticks.hpp"
#if INCLUDE_G1GC
#include "gc/g1/evacuationInfo.hpp"
#endif
diff --git a/src/hotspot/share/gc/shared/gcTraceSend.cpp b/src/hotspot/share/gc/shared/gcTraceSend.cpp
index 869987bd7d0..191d6bf89fe 100644
--- a/src/hotspot/share/gc/shared/gcTraceSend.cpp
+++ b/src/hotspot/share/gc/shared/gcTraceSend.cpp
@@ -28,10 +28,8 @@
#include "gc/shared/gcTimer.hpp"
#include "gc/shared/gcTrace.hpp"
#include "gc/shared/gcWhen.hpp"
+#include "jfr/jfrEvents.hpp"
#include "runtime/os.hpp"
-#include "trace/traceBackend.hpp"
-#include "trace/tracing.hpp"
-#include "tracefiles/traceEventClasses.hpp"
#include "utilities/macros.hpp"
#if INCLUDE_G1GC
#include "gc/g1/evacuationInfo.hpp"
@@ -160,8 +158,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());
@@ -173,7 +171,7 @@ void YoungGCTracer::send_promotion_failed_event(const PromotionFailedInfo& pf_in
EventPromotionFailed e;
if (e.should_commit()) {
e.set_gcId(GCId::current());
- e.set_promotionFailed(to_trace_struct(pf_info));
+ e.set_promotionFailed(to_struct(pf_info));
e.set_thread(pf_info.thread_trace_id());
e.commit();
}
@@ -231,14 +229,14 @@ void G1NewTracer::send_evacuation_failed_event(const EvacuationFailedInfo& ef_in
EventEvacuationFailed e;
if (e.should_commit()) {
e.set_gcId(GCId::current());
- e.set_evacuationFailed(to_trace_struct(ef_info));
+ e.set_evacuationFailed(to_struct(ef_info));
e.commit();
}
}
-static TraceStructG1EvacuationStatistics
+static JfrStructG1EvacuationStatistics
create_g1_evacstats(unsigned gcid, const G1EvacSummary& summary) {
- TraceStructG1EvacuationStatistics s;
+ JfrStructG1EvacuationStatistics s;
s.set_gcId(gcid);
s.set_allocated(summary.allocated() * HeapWordSize);
s.set_wasted(summary.wasted() * HeapWordSize);
@@ -313,8 +311,8 @@ void G1NewTracer::send_adaptive_ihop_statistics(size_t threshold,
#endif // INCLUDE_G1GC
-static TraceStructVirtualSpace to_trace_struct(const VirtualSpaceSummary& summary) {
- TraceStructVirtualSpace space;
+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());
@@ -323,8 +321,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());
@@ -344,7 +342,7 @@ class GCHeapSummaryEventSender : public GCHeapSummaryVisitor {
if (e.should_commit()) {
e.set_gcId(GCId::current());
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();
}
@@ -380,12 +378,12 @@ class GCHeapSummaryEventSender : public GCHeapSummaryVisitor {
e.set_gcId(GCId::current());
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();
}
}
@@ -396,8 +394,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());
@@ -412,9 +410,9 @@ void GCTracer::send_meta_space_summary_event(GCWhen::Type when, const MetaspaceS
e.set_gcId(GCId::current());
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();
}
}
diff --git a/src/hotspot/share/gc/shared/objectCountEventSender.cpp b/src/hotspot/share/gc/shared/objectCountEventSender.cpp
index d9ca9294da6..bf24a98cf67 100644
--- a/src/hotspot/share/gc/shared/objectCountEventSender.cpp
+++ b/src/hotspot/share/gc/shared/objectCountEventSender.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,34 +26,50 @@
#include "precompiled.hpp"
#include "gc/shared/gcId.hpp"
#include "gc/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, 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(GCId::current());
- event.set_objectClass(entry->klass());
- event.set_count(entry->count());
- event.set_totalSize(entry->words() * BytesPerWord);
- event.set_endtime(timestamp);
- event.commit();
-#endif // INCLUDE_TRACE
-}
-
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, jlong count, julong size, const Ticks& timestamp) {
+ T event(UNTIMED);
+ if (event.should_commit()) {
+ event.set_gcId(GCId::current());
+ event.set_objectClass(klass);
+ event.set_count(count);
+ event.set_totalSize(size);
+ event.set_endtime(timestamp);
+ event.commit();
+ }
+}
+
+void ObjectCountEventSender::send(const KlassInfoEntry* entry, const Ticks& timestamp) {
+ Klass* klass = entry->klass();
+ jlong count = entry->count();
+ julong total_size = entry->words() * BytesPerWord;
+
+ send_event_if_enabled(klass, count, total_size, timestamp);
+ send_event_if_enabled(klass, count, total_size, timestamp);
}
#endif // INCLUDE_SERVICES
diff --git a/src/hotspot/share/gc/shared/objectCountEventSender.hpp b/src/hotspot/share/gc/shared/objectCountEventSender.hpp
index c96e7790d2d..29c8d975a15 100644
--- a/src/hotspot/share/gc/shared/objectCountEventSender.hpp
+++ b/src/hotspot/share/gc/shared/objectCountEventSender.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,15 +27,25 @@
#include "gc/shared/gcTrace.hpp"
#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
+#include "utilities/ticks.hpp"
#if INCLUDE_SERVICES
class KlassInfoEntry;
-class Ticks;
+class Klass;
class ObjectCountEventSender : public AllStatic {
+ static bool _should_send_requestable_event;
+
+ template
+ static void send_event_if_enabled(Klass* klass, jlong count, julong size, const Ticks& timestamp);
+
public:
+ static void enable_requestable_event();
+ static void disable_requestable_event();
+
static void send(const KlassInfoEntry* entry, const Ticks& timestamp);
static bool should_send_event();
};
diff --git a/src/hotspot/share/gc/shared/weakProcessor.cpp b/src/hotspot/share/gc/shared/weakProcessor.cpp
index c230b14d549..6be1bd2d96d 100644
--- a/src/hotspot/share/gc/shared/weakProcessor.cpp
+++ b/src/hotspot/share/gc/shared/weakProcessor.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,13 +26,15 @@
#include "gc/shared/weakProcessor.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/jniHandles.hpp"
-#include "trace/tracing.hpp"
-#include "trace/traceMacros.hpp"
+#include "utilities/macros.hpp"
+#if INCLUDE_JFR
+#include "jfr/jfr.hpp"
+#endif
void WeakProcessor::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive) {
JNIHandles::weak_oops_do(is_alive, keep_alive);
JvmtiExport::weak_oops_do(is_alive, keep_alive);
- TRACE_WEAK_OOPS_DO(is_alive, keep_alive);
+ JFR_ONLY(Jfr::weak_oops_do(is_alive, keep_alive);)
}
void WeakProcessor::oops_do(OopClosure* closure) {
diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp
new file mode 100644
index 00000000000..a4df453d133
--- /dev/null
+++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp
@@ -0,0 +1,633 @@
+/*
+ * 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_module_available(outputStream* output, TRAPS) {
+ return JfrJavaSupport::is_jdk_jfr_module_available(output, 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) || !is_module_available(out, THREAD);
+}
+
+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", true, NULL),
+ _filename("filename", "Copy recording data to file, i.e \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", true),
+ _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(&_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 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/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(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 text, 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()) {
+ const int length = _settings.value()->array()->length();
+ 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);
+ }
+ }
+
+ 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", false, 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", "JLONG", false, "64"),
+ _global_buffer_count("globalbuffercount", "Number of global buffers,", "JLONG", false, "32"),
+ _global_buffer_size("globalbuffersize", "Size of a global buffers,", "JLONG", false, "524288"),
+ _thread_buffer_size("thread_buffer_size", "Size of a thread buffer", "JLONG", false, "8192"),
+ _memory_size("memorysize", "Overall memory size, ", "JLONG", false, "16777216"),
+ _max_chunk_size("maxchunksize", "Size of an individual disk chunk", "JLONG", false, "12582912"),
+ _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(), CHECK);
+ }
+
+ jobject thread_buffer_size = NULL;
+ if (_thread_buffer_size.is_set()) {
+ thread_buffer_size = JfrJavaSupport::new_java_lang_Long(_thread_buffer_size.value(), CHECK);
+ }
+
+ jobject max_chunk_size = NULL;
+ if (_max_chunk_size.is_set()) {
+ max_chunk_size = JfrJavaSupport::new_java_lang_Long(_max_chunk_size.value(), CHECK);
+ }
+
+ jobject memory_size = NULL;
+ if (_memory_size.is_set()) {
+ memory_size = JfrJavaSupport::new_java_lang_Long(_memory_size.value(), 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));
+ return true;
+}
+
diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp
new file mode 100644
index 00000000000..afd2f86138d
--- /dev/null
+++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp
@@ -0,0 +1,171 @@
+/*
+ * 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 _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);
+};
+
+bool register_jfr_dcmds();
+
+#endif // SHARE_VM_JFR_JFRDCMDS_HPP
diff --git a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
new file mode 100644
index 00000000000..6c25a2e56c6
--- /dev/null
+++ b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
@@ -0,0 +1,1556 @@
+/*
+ * 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.inline.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 "logging/log.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/internal/handlers/EventHandler;", // 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");
+ const 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(const 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(const 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,
+ const 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,
+ const 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;
+ const 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, ClassFileStream::verify);
+}
+
+// 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(const InstanceKlass* ik,
+ const 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);
+ const 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) {
+ log_error(jfr, system) ("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() == 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) {
+ log_error(jfr, system) ("%s", text);
+ }
+ }
+}
+
+static bool should_force_instrumentation() {
+ return !JfrOptionSet::allow_event_retransforms() || JfrEventClassTransformer::is_force_instrumentation();
+}
+
+static ClassFileStream* create_new_bytes_for_subklass(const InstanceKlass* ik, const 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;
+ const 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, ClassFileStream::verify);
+ }
+ return new ClassFileStream(new_bytes, size_of_new_bytes, NULL, ClassFileStream::verify);
+}
+
+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) {
+ log_error(jfr, system)("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);
+ 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() : "";
+ ClassFileParser new_parser(stream,
+ class_name,
+ cld,
+ pd,
+ NULL, // host klass
+ NULL, // cp_patches
+ ClassFileParser::INTERNAL, // internal visibility
+ THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ log_pending_exception(PENDING_EXCEPTION);
+ CLEAR_PENDING_EXCEPTION;
+ return NULL;
+ }
+ InstanceKlass* const new_ik = new_parser.create_instance_klass(false, 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) {
+ log_error(jfr, system)("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) {
+ log_error(jfr, system)("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) {
+ log_error(jfr, system)("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) {
+ log_error(jfr, system)("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/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.hpp b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.hpp
new file mode 100644
index 00000000000..1b35fb183d3
--- /dev/null
+++ b/src/hotspot/share/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/hotspot/share/jfr/instrumentation/jfrJvmtiAgent.cpp b/src/hotspot/share/jfr/instrumentation/jfrJvmtiAgent.cpp
new file mode 100644
index 00000000000..3a77db84490
--- /dev/null
+++ b/src/hotspot/share/jfr/instrumentation/jfrJvmtiAgent.cpp
@@ -0,0 +1,279 @@
+/*
+ * 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 "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 "logging/log.hpp"
+#include "memory/resourceArea.hpp"
+#include "prims/jvmtiExport.hpp"
+#include "runtime/interfaceSupport.inline.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);
+ log_error(jfr, system)("ERROR: JfrJvmtiAgent: " INT32_FORMAT " (%s): %s\n",
+ errnum,
+ NULL == errnum_str ? "Unknown" : errnum_str,
+ NULL == str ? "" : str);
+ }
+}
+
+static jvmtiError set_event_notification_mode(jvmtiEventMode mode,
+ jvmtiEvent event,
+ jthread event_thread,
+ ...) {
+ if (jfr_jvmti_env == NULL) {
+ return JVMTI_ERROR_NONE;
+ }
+ 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;
+}
+
+static jvmtiError 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);
+ log_error(jfr, system)("%s", error_buffer);
+ JfrJavaSupport::throw_out_of_memory_error(error_buffer, CHECK_NULL);
+ }
+ return classes;
+}
+
+static void log_and_throw(TRAPS) {
+ if (!HAS_PENDING_EXCEPTION) {
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
+ ThreadInVMfromNative tvmfn((JavaThread*)THREAD);
+ log_error(jfr, system)("JfrJvmtiAgent::retransformClasses failed");
+ JfrJavaSupport::throw_class_format_error("JfrJvmtiAgent::retransformClasses failed", 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);
+ log_error(jfr, system)("GetObjectArrayElement threw an exception");
+ return;
+ }
+}
+
+void JfrJvmtiAgent::retransform_classes(JNIEnv* env, jobjectArray classes_array, TRAPS) {
+ assert(env != NULL, "invariant");
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
+ if (classes_array == NULL) {
+ return;
+ }
+ 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);
+
+ // inspecting the oop/klass requires a thread transition
+ {
+ ThreadInVMfromNative transition((JavaThread*)THREAD);
+ if (JdkJfrEvent::is_a(clz)) {
+ // should have been tagged already
+ assert(JdkJfrEvent::is_subklass(clz), "invariant");
+ } else {
+ // outside the event hierarchy
+ JdkJfrEvent::tag_as_host(clz);
+ }
+ }
+
+ classes[i] = clz;
+ }
+ if (jfr_jvmti_env->RetransformClasses(classes_count, classes) != JVMTI_ERROR_NONE) {
+ log_and_throw(THREAD);
+ }
+}
+
+static jvmtiError 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;
+}
+
+static jvmtiError 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;
+}
+
+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 jvmtiError unregister_callbacks(JavaThread* jt) {
+ if (jfr_jvmti_env == NULL) {
+ return JVMTI_ERROR_NONE;
+ }
+ 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;
+}
+
+JfrJvmtiAgent::JfrJvmtiAgent() {}
+
+JfrJvmtiAgent::~JfrJvmtiAgent() {
+ JavaThread* jt = current_java_thread();
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
+ ThreadToNativeFromVM transition(jt);
+ update_class_file_load_hook_event(JVMTI_DISABLE);
+ unregister_callbacks(jt);
+ if (jfr_jvmti_env != NULL) {
+ jfr_jvmti_env->DisposeEnvironment();
+ jfr_jvmti_env = NULL;
+ }
+ agent = NULL;
+}
+
+static bool initialize() {
+ JavaThread* const jt = current_java_thread();
+ assert(jt != NULL, "invariant");
+ assert(jt->thread_state() == _thread_in_vm, "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) != JVMTI_ERROR_NONE) {
+ return false;
+ }
+ if (register_callbacks(jt) != JVMTI_ERROR_NONE) {
+ return false;
+ }
+ if (update_class_file_load_hook_event(JVMTI_ENABLE) != JVMTI_ERROR_NONE) {
+ return false;
+ }
+ return true;
+}
+
+bool JfrJvmtiAgent::create() {
+ assert(jfr_jvmti_env == NULL, "invariant");
+ agent = new JfrJvmtiAgent();
+ if (agent == NULL) {
+ return false;
+ }
+ if (!initialize()) {
+ delete agent;
+ agent = NULL;
+ return false;
+ }
+ return true;
+}
+
+void JfrJvmtiAgent::destroy() {
+ if (agent != NULL) {
+ delete agent;
+ agent = NULL;
+ }
+}
+
diff --git a/src/hotspot/share/jfr/instrumentation/jfrJvmtiAgent.hpp b/src/hotspot/share/jfr/instrumentation/jfrJvmtiAgent.hpp
new file mode 100644
index 00000000000..d4ad1fe6da7
--- /dev/null
+++ b/src/hotspot/share/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/hotspot/share/jfr/jfr.cpp b/src/hotspot/share/jfr/jfr.cpp
new file mode 100644
index 00000000000..15db5e86c5d
--- /dev/null
+++ b/src/hotspot/share/jfr/jfr.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.hpp"
+#include "jfr/jfr.hpp"
+#include "jfr/leakprofiler/leakProfiler.hpp"
+#include "jfr/periodic/sampling/jfrThreadSampler.hpp"
+#include "jfr/recorder/service/jfrOptionSet.hpp"
+#include "jfr/recorder/jfrRecorder.hpp"
+#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
+#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
+#include "jfr/support/jfrThreadLocal.hpp"
+#include "runtime/java.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_vm_init() {
+ if (!JfrRecorder::on_vm_init()) {
+ vm_exit_during_initialization("Failure when starting JFR on_vm_init");
+ }
+}
+
+void Jfr::on_vm_start() {
+ if (!JfrRecorder::on_vm_start()) {
+ vm_exit_during_initialization("Failure when starting JFR on_vm_start");
+ }
+}
+
+void Jfr::on_unloading_classes() {
+ if (JfrRecorder::is_created()) {
+ JfrCheckpointManager::write_type_set_for_unloaded_classes();
+ }
+}
+
+void Jfr::on_thread_exit(JavaThread* thread) {
+ if (JfrRecorder::is_recording()) {
+ JfrThreadLocal::on_exit(thread);
+ }
+}
+
+void Jfr::on_thread_destruct(Thread* thread) {
+ if (JfrRecorder::is_created()) {
+ JfrThreadLocal::on_destruct(thread);
+ }
+}
+
+void Jfr::on_vm_shutdown(bool exception_handler) {
+ 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);
+}
+
+bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* tail) {
+ return JfrOptionSet::parse_start_flight_recording_option(option, tail);
+}
+
+bool Jfr::on_flight_recorder_option(const JavaVMOption** option, char* tail) {
+ return JfrOptionSet::parse_flight_recorder_option(option, tail);
+}
+
+Thread* Jfr::sampler_thread() {
+ return JfrThreadSampling::sampler_thread();
+}
diff --git a/src/hotspot/share/jfr/jfr.hpp b/src/hotspot/share/jfr/jfr.hpp
new file mode 100644
index 00000000000..302e8b1eb49
--- /dev/null
+++ b/src/hotspot/share/jfr/jfr.hpp
@@ -0,0 +1,58 @@
+/*
+ * 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_vm_init();
+ static void on_vm_start();
+ static void on_unloading_classes();
+ static void on_thread_exit(JavaThread* thread);
+ static void on_thread_destruct(Thread* thread);
+ static void on_vm_shutdown(bool exception_handler = false);
+ static bool on_start_flight_recording_option(const JavaVMOption** option, char* tail);
+ static bool on_flight_recorder_option(const JavaVMOption** option, char* tail);
+ static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
+ static Thread* sampler_thread();
+};
+
+#endif // SHARE_VM_JFR_JFR_HPP
diff --git a/src/hotspot/share/jfr/jfrEvents.hpp b/src/hotspot/share/jfr/jfrEvents.hpp
new file mode 100644
index 00000000000..d8ea0079c61
--- /dev/null
+++ b/src/hotspot/share/jfr/jfrEvents.hpp
@@ -0,0 +1,35 @@
+/*
+ * 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_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_JFR_JFREVENTS_HPP
diff --git a/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp b/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp
new file mode 100644
index 00000000000..2854c2c30c1
--- /dev/null
+++ b/src/hotspot/share/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.inline.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/hotspot/share/jfr/jni/jfrGetAllEventClasses.hpp b/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.hpp
new file mode 100644
index 00000000000..a94a0b0eb82
--- /dev/null
+++ b/src/hotspot/share/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/hotspot/share/jfr/jni/jfrJavaCall.cpp b/src/hotspot/share/jfr/jni/jfrJavaCall.cpp
new file mode 100644
index 00000000000..77a2f8c89f2
--- /dev/null
+++ b/src/hotspot/share/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_jobject(values(i).get_jobject());
+ break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+}
+
+JfrJavaArguments::JfrJavaArguments(JavaValue* result) : _result(result), _klass(NULL), _name(NULL), _signature(NULL), _array_length(0) {
+ 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(0) {
+ 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(0) {
+ 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/hotspot/share/jfr/jni/jfrJavaCall.hpp b/src/hotspot/share/jfr/jni/jfrJavaCall.hpp
new file mode 100644
index 00000000000..89b2f7b98dd
--- /dev/null
+++ b/src/hotspot/share/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/hotspot/share/jfr/jni/jfrJavaSupport.cpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp
new file mode 100644
index 00000000000..d1478391bda
--- /dev/null
+++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp
@@ -0,0 +1,612 @@
+/*
+ * 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.inline.hpp"
+#include "classfile/modules.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 "logging/log.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/instanceOop.hpp"
+#include "oops/oop.inline.hpp"
+#include "oops/objArrayKlass.hpp"
+#include "oops/objArrayOop.inline.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/fieldDescriptor.hpp"
+#include "runtime/java.hpp"
+#include "runtime/jniHandles.inline.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(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::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) {
+ log_error(jfr, system)("%s",error_msg);
+ }
+ log_error(jfr, system)("%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;
+}
+
+const char* const JDK_JFR_MODULE_NAME = "jdk.jfr";
+const char* const JDK_JFR_PACKAGE_NAME = "jdk/jfr";
+
+static bool is_jdk_jfr_module_in_readability_graph() {
+ Thread* const t = Thread::current();
+ // take one of the packages in the module to be located and query for its definition.
+ TempNewSymbol pkg_sym = SymbolTable::new_symbol(JDK_JFR_PACKAGE_NAME, t);
+ return Modules::is_package_defined(pkg_sym, Handle(), t);
+}
+
+static void print_module_resolution_error(outputStream* stream) {
+ assert(stream != NULL, "invariant");
+ stream->print_cr("%s not found.", JDK_JFR_MODULE_NAME);
+ stream->print_cr("Flight Recorder can not be enabled.");
+ stream->print_cr("To use Flight Recorder, you might need to add" \
+ " \"--add-modules %s\" to the VM command-line options.", JDK_JFR_MODULE_NAME);
+}
+
+bool JfrJavaSupport::is_jdk_jfr_module_available() {
+ return is_jdk_jfr_module_in_readability_graph();
+}
+
+bool JfrJavaSupport::is_jdk_jfr_module_available(outputStream* stream, TRAPS) {
+ if (!JfrJavaSupport::is_jdk_jfr_module_available()) {
+ if (stream != NULL) {
+ print_module_resolution_error(stream);
+ }
+ return false;
+ }
+ return true;
+}
+
+jlong JfrJavaSupport::jfr_thread_id(jobject target_thread) {
+ ThreadsListHandle tlh;
+ JavaThread* native_thread = NULL;
+ (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/hotspot/share/jfr/jni/jfrJavaSupport.hpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp
new file mode 100644
index 00000000000..dc71db5adac
--- /dev/null
+++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp
@@ -0,0 +1,114 @@
+/*
+ * 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 bool is_jdk_jfr_module_available();
+ static bool is_jdk_jfr_module_available(outputStream* stream, 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/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp
new file mode 100644
index 00000000000..9f4e819a9d0
--- /dev/null
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp
@@ -0,0 +1,310 @@
+/*
+ * 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/jfrChunkSizeNotifier.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 "logging/log.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/interfaceSupport.inline.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))
+ JfrChunkSizeNotifier::set_chunk_size_threshold((size_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
+
+
+/*
+ * 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)) {
+ 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);
+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 ? true : false);
+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/hotspot/share/jfr/jni/jfrJniMethod.hpp b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp
new file mode 100644
index 00000000000..87b6e50d3c2
--- /dev/null
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp
@@ -0,0 +1,136 @@
+/*
+ * 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);
+
+jlong JNICALL jfr_add_string_constant(JNIEnv* env, jclass jvm, jlong gen, 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SHARE_VM_JFR_JNI_JFRJNIMETHOD_HPP
diff --git a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp
new file mode 100644
index 00000000000..e737f290293
--- /dev/null
+++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "logging/log.hpp"
+#include "runtime/interfaceSupport.inline.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
+ };
+
+ 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);
+ log_error(jfr, system)("RegisterNatives for JVM class failed!");
+ }
+ env->DeleteLocalRef(jfr_clz);
+ }
+}
diff --git a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.hpp b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.hpp
new file mode 100644
index 00000000000..f0a25f69ccf
--- /dev/null
+++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.hpp
@@ -0,0 +1,39 @@
+/*
+ * 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_JFRJNIMETHODREGISTRATION_HPP
+#define SHARE_VM_JFR_JNI_JFRJNIMETHODREGISTRATION_HPP
+
+#include "jni.h"
+#include "memory/allocation.hpp"
+
+//
+// RegisterNatives for jdk.jfr.internal.JVM
+//
+class JfrJniMethodRegistration : public StackObj {
+ public:
+ JfrJniMethodRegistration(JNIEnv* env);
+};
+
+#endif // SHARE_VM_JFR_JNI_JFRJNIMETHODREGISTRATION_HPP
diff --git a/src/hotspot/share/jfr/jni/jfrUpcalls.cpp b/src/hotspot/share/jfr/jni/jfrUpcalls.cpp
new file mode 100644
index 00000000000..9ac3d0ee214
--- /dev/null
+++ b/src/hotspot/share/jfr/jni/jfrUpcalls.cpp
@@ -0,0 +1,180 @@
+/*
+ * 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 "logging/log.hpp"
+#include "memory/oopFactory.hpp"
+#include "oops/oop.inline.hpp"
+#include "oops/typeArrayKlass.hpp"
+#include "oops/typeArrayOop.inline.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) {
+ log_error(jfr, system)("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);
+ log_error(jfr, system)("%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;
+}
diff --git a/src/hotspot/share/jfr/jni/jfrUpcalls.hpp b/src/hotspot/share/jfr/jni/jfrUpcalls.hpp
new file mode 100644
index 00000000000..1f1a910d680
--- /dev/null
+++ b/src/hotspot/share/jfr/jni/jfrUpcalls.hpp
@@ -0,0 +1,58 @@
+/*
+ * 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);
+};
+
+#endif // SHARE_VM_JFR_JNI_JFRUPCALLS_HPP
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.cpp b/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.cpp
new file mode 100644
index 00000000000..9922729f189
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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/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 "logging/log.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/access.inline.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;
+ log_trace(jfr, system)(
+ "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
+ log_trace(jfr, system)(
+ "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;
+ log_trace(jfr, system)(
+ "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) {
+ assert(_current_parent->distance_to_root() == _current_frontier_level, "invariant");
+ _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");
+
+ const size_t length = _current_parent == NULL ? 1 : _current_parent->distance_to_root() + 2;
+ ResourceMark rm;
+ Edge* const chain = NEW_RESOURCE_ARRAY(Edge, length);
+ size_t idx = 0;
+ chain[idx++] = Edge(NULL, reference);
+ // aggregate from breadth-first search
+ const Edge* current = _current_parent;
+ while (current != NULL) {
+ chain[idx++] = Edge(NULL, current->reference());
+ current = current->parent();
+ }
+ assert(length == idx, "invariant");
+ _edge_store->add_chain(chain, length);
+}
+
+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 = RawAccess<>::oop_load(ref);
+ if (pointee != NULL) {
+ closure_impl(UnifiedOop::encode(ref), pointee);
+ }
+}
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp
new file mode 100644
index 00000000000..25347b16a96
--- /dev/null
+++ b/src/hotspot/share/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"
+#include "oops/oop.hpp"
+
+class BitSet;
+class Edge;
+class EdgeStore;
+class EdgeQueue;
+
+// Class responsible for iterating the heap breadth-first
+class BFSClosure : public ExtendedOopClosure {
+ 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();
+
+ virtual void do_oop(oop* ref);
+ virtual void do_oop(narrowOop* ref);
+};
+
+#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_BFSCLOSURE_HPP
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bitset.cpp b/src/hotspot/share/jfr/leakprofiler/chains/bitset.cpp
new file mode 100644
index 00000000000..67df3d94d52
--- /dev/null
+++ b/src/hotspot/share/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 = BitMapView(map, bits);
+ return true;
+}
+
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bitset.hpp b/src/hotspot/share/jfr/leakprofiler/chains/bitset.hpp
new file mode 100644
index 00000000000..565b2d7e41a
--- /dev/null
+++ b/src/hotspot/share/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;
+ BitMapView _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.par_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/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp
new file mode 100644
index 00000000000..3599ceb1557
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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/dfsClosure.hpp"
+#include "jfr/leakprofiler/chains/edge.hpp"
+#include "jfr/leakprofiler/chains/edgeStore.hpp"
+#include "jfr/leakprofiler/utilities/granularTimer.hpp"
+#include "jfr/leakprofiler/chains/bitset.hpp"
+#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
+#include "jfr/leakprofiler/utilities/rootType.hpp"
+#include "jfr/leakprofiler/chains/rootSetClosure.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/access.inline.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 dfs1;
+ RootSetClosure::process_roots(&dfs1);
+
+ // Depth-first search
+ _max_depth = max_dfs_depth;
+ _ignore_root_set = true;
+ assert(_start_edge == NULL, "invariant");
+ DFSClosure dfs2;
+ RootSetClosure::process_roots(&dfs2);
+}
+
+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 length = _start_edge == NULL ? _depth + 1 :
+ _start_edge->distance_to_root() + 1 + _depth + 1;
+
+ ResourceMark rm;
+ Edge* const chain = NEW_RESOURCE_ARRAY(Edge, length);
+ size_t idx = 0;
+
+ // aggregate from depth-first search
+ const DFSClosure* c = this;
+ while (c != NULL) {
+ chain[idx++] = Edge(NULL, c->reference());
+ c = c->parent();
+ }
+
+ assert(idx == _depth + 1, "invariant");
+
+ // aggregate from breadth-first search
+ const Edge* current = _start_edge;
+ while (current != NULL) {
+ chain[idx++] = Edge(NULL, current->reference());
+ current = current->parent();
+ }
+ assert(idx == length, "invariant");
+ _edge_store->add_chain(chain, length);
+}
+
+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 = RawAccess<>::oop_load(ref);
+ if (pointee != NULL) {
+ closure_impl(UnifiedOop::encode(ref), pointee);
+ }
+}
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp
new file mode 100644
index 00000000000..d1beb088992
--- /dev/null
+++ b/src/hotspot/share/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"
+#include "oops/oop.hpp"
+
+class BitSet;
+class Edge;
+class EdgeStore;
+class EdgeQueue;
+
+// Class responsible for iterating the heap depth-first
+class DFSClosure: public ExtendedOopClosure {
+ 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);
+
+ virtual void do_oop(oop* ref);
+ virtual void do_oop(narrowOop* ref);
+};
+
+#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_DFSCLOSURE_HPP
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/edge.cpp b/src/hotspot/share/jfr/leakprofiler/chains/edge.cpp
new file mode 100644
index 00000000000..9adaa6a5143
--- /dev/null
+++ b/src/hotspot/share/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.inline.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/hotspot/share/trace/traceBackend.hpp b/src/hotspot/share/jfr/leakprofiler/chains/edge.hpp
similarity index 55%
rename from src/hotspot/share/trace/traceBackend.hpp
rename to src/hotspot/share/jfr/leakprofiler/chains/edge.hpp
index 57b80ffda34..1315abd1494 100644
--- a/src/hotspot/share/trace/traceBackend.hpp
+++ b/src/hotspot/share/jfr/leakprofiler/chains/edge.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -21,43 +21,39 @@
* questions.
*
*/
-#ifndef SHARE_VM_TRACE_TRACEBACKEND_HPP
-#define SHARE_VM_TRACE_TRACEBACKEND_HPP
-#include "utilities/macros.hpp"
-#if INCLUDE_TRACE
-#include "runtime/globals.hpp"
-#include "runtime/os.hpp"
-#include "trace/traceTime.hpp"
-#include "tracefiles/traceEventIds.hpp"
+#ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGE_HPP
+#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGE_HPP
-class TraceBackend {
-public:
- static bool enabled(void) {
- return EnableTracing;
+#include "memory/allocation.hpp"
+#include "oops/oopsHierarchy.hpp"
+
+class Edge {
+ private:
+ const Edge* _parent;
+ const oop* _reference;
+ public:
+ Edge();
+ Edge(const Edge* parent, const oop* reference);
+
+ const oop* reference() const {
+ return _reference;
}
-
- static bool is_event_enabled(TraceEventId id) {
- return enabled();
+ const Edge* parent() const {
+ return _parent;
}
-
- static TracingTime time() {
- return os::elapsed_counter();
+ 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;
- static void on_unloading_classes(void) {
+ void* operator new (size_t sz, void* here) {
+ return here;
}
-
};
-class TraceThreadData {
-public:
- TraceThreadData() {}
-};
-
-typedef TraceBackend Tracing;
-
-#else // !INCLUDE_TRACE
-#include "trace/noTraceBackend.hpp"
-#endif // INCLUDE_TRACE
-#endif // SHARE_VM_TRACE_TRACEBACKEND_HPP
+#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_EDGE_HPP
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/edgeQueue.cpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeQueue.cpp
new file mode 100644
index 00000000000..f3abf573fc9
--- /dev/null
+++ b/src/hotspot/share/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/hotspot/share/jfr/leakprofiler/chains/edgeQueue.hpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeQueue.hpp
new file mode 100644
index 00000000000..8211624616a
--- /dev/null
+++ b/src/hotspot/share/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/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp
new file mode 100644
index 00000000000..cab1c70491c
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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/edgeStore.hpp"
+#include "jfr/leakprofiler/chains/edgeUtils.hpp"
+#include "oops/oop.inline.hpp"
+
+RoutableEdge::RoutableEdge() : Edge() {}
+RoutableEdge::RoutableEdge(const Edge* parent, const oop* reference) : Edge(parent, reference),
+ _skip_edge(NULL),
+ _skip_length(0),
+ _processed(false) {}
+
+RoutableEdge::RoutableEdge(const Edge& edge) : Edge(edge),
+ _skip_edge(NULL),
+ _skip_length(0),
+ _processed(false) {}
+
+RoutableEdge::RoutableEdge(const RoutableEdge& edge) : Edge(edge),
+ _skip_edge(edge._skip_edge),
+ _skip_length(edge._skip_length),
+ _processed(edge._processed) {}
+
+void RoutableEdge::operator=(const RoutableEdge& edge) {
+ Edge::operator=(edge);
+ _skip_edge = edge._skip_edge;
+ _skip_length = edge._skip_length;
+ _processed = edge._processed;
+}
+
+size_t RoutableEdge::logical_distance_to_root() const {
+ size_t depth = 0;
+ const RoutableEdge* current = logical_parent();
+ while (current != NULL) {
+ depth++;
+ current = current->logical_parent();
+ }
+ return depth;
+}
+
+traceid EdgeStore::_edge_id_counter = 0;
+
+EdgeStore::EdgeStore() : _edges(NULL) {
+ _edges = new EdgeHashTable(this);
+}
+
+EdgeStore::~EdgeStore() {
+ assert(_edges != NULL, "invariant");
+ delete _edges;
+ _edges = NULL;
+}
+
+const Edge* EdgeStore::get_edge(const Edge* edge) const {
+ assert(edge != NULL, "invariant");
+ EdgeEntry* const entry = _edges->lookup_only(*edge, (uintptr_t)edge->reference());
+ return entry != NULL ? entry->literal_addr() : NULL;
+}
+
+const Edge* EdgeStore::put(const Edge* edge) {
+ assert(edge != NULL, "invariant");
+ const RoutableEdge e = *edge;
+ assert(NULL == _edges->lookup_only(e, (uintptr_t)e.reference()), "invariant");
+ EdgeEntry& entry = _edges->put(e, (uintptr_t)e.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::get_root_id(const Edge* edge) const {
+ assert(edge != NULL, "invariant");
+ const Edge* root = EdgeUtils::root(*edge);
+ assert(root != NULL, "invariant");
+ return get_id(root);
+}
+
+void EdgeStore::add_chain(const Edge* chain, size_t length) {
+ assert(chain != NULL, "invariant");
+ assert(length > 0, "invariant");
+
+ size_t bottom_index = length - 1;
+ const size_t top_index = 0;
+
+ const Edge* stored_parent_edge = NULL;
+
+ // determine level of shared ancestry
+ for (; bottom_index > top_index; --bottom_index) {
+ const Edge* stored_edge = get_edge(&chain[bottom_index]);
+ if (stored_edge != NULL) {
+ stored_parent_edge = stored_edge;
+ continue;
+ }
+ break;
+ }
+
+ // insertion of new Edges
+ for (int i = (int)bottom_index; i >= (int)top_index; --i) {
+ Edge edge(stored_parent_edge, chain[i].reference());
+ stored_parent_edge = put(&edge);
+ }
+
+ const oop sample_object = stored_parent_edge->pointee();
+ assert(sample_object != NULL, "invariant");
+ assert(NULL == sample_object->mark(), "invariant");
+
+ // Install the "top" edge of the chain into the sample object mark oop.
+ // This associates the sample object with its navigable reference chain.
+ sample_object->set_mark(markOop(stored_parent_edge));
+}
+
+bool EdgeStore::is_empty() const {
+ return !_edges->has_entries();
+}
+
+size_t EdgeStore::number_of_entries() const {
+ return _edges->cardinality();
+}
+
+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;
+}
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.hpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.hpp
new file mode 100644
index 00000000000..a504964cdd1
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.hpp
@@ -0,0 +1,119 @@
+/*
+ * 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/utilities/jfrHashtable.hpp"
+#include "jfr/leakprofiler/chains/edge.hpp"
+#include "memory/allocation.hpp"
+
+typedef u8 traceid;
+
+class RoutableEdge : public Edge {
+ private:
+ mutable const RoutableEdge* _skip_edge;
+ mutable size_t _skip_length;
+ mutable bool _processed;
+
+ public:
+ RoutableEdge();
+ RoutableEdge(const Edge* parent, const oop* reference);
+ RoutableEdge(const Edge& edge);
+ RoutableEdge(const RoutableEdge& edge);
+ void operator=(const RoutableEdge& edge);
+
+ const RoutableEdge* skip_edge() const { return _skip_edge; }
+ size_t skip_length() const { return _skip_length; }
+
+ bool is_skip_edge() const { return _skip_edge != NULL; }
+ bool processed() const { return _processed; }
+ bool is_sentinel() const {
+ return _skip_edge == NULL && _skip_length == 1;
+ }
+
+ void set_skip_edge(const RoutableEdge* edge) const {
+ assert(!is_skip_edge(), "invariant");
+ assert(edge != this, "invariant");
+ _skip_edge = edge;
+ }
+
+ void set_skip_length(size_t length) const {
+ _skip_length = length;
+ }
+
+ void set_processed() const {
+ assert(!_processed, "invariant");
+ _processed = true;
+ }
+
+ // true navigation according to physical tree representation
+ const RoutableEdge* physical_parent() const {
+ return static_cast(parent());
+ }
+
+ // logical navigation taking skip levels into account
+ const RoutableEdge* logical_parent() const {
+ return is_skip_edge() ? skip_edge() : physical_parent();
+ }
+
+ size_t logical_distance_to_root() const;
+};
+
+class EdgeStore : public CHeapObj {
+ typedef HashTableHost EdgeHashTable;
+ typedef EdgeHashTable::HashEntry EdgeEntry;
+ template class,
+ typename,
+ size_t>
+ friend class HashTableHost;
+ 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);
+
+ const Edge* get_edge(const Edge* edge) const;
+ const Edge* put(const Edge* edge);
+
+ public:
+ EdgeStore();
+ ~EdgeStore();
+
+ void add_chain(const Edge* chain, size_t length);
+ bool is_empty() const;
+ size_t number_of_entries() const;
+
+ traceid get_id(const Edge* edge) const;
+ traceid get_root_id(const Edge* edge) const;
+
+ template
+ void iterate_edges(T& functor) const { _edges->iterate_value(functor); }
+};
+
+#endif // SHARE_VM_LEAKPROFILER_CHAINS_EDGESTORE_HPP
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp
new file mode 100644
index 00000000000..9074ebbe499
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp
@@ -0,0 +1,312 @@
+/*
+ * 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/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.inline.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;
+}
+
+bool EdgeUtils::is_root(const Edge& edge) {
+ return edge.is_root();
+}
+
+static int field_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");
+ 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 Edge& 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();
+ }
+ return current;
+}
+
+// The number of references associated with the leak node;
+// can be viewed as the leak node "context".
+// Used to provide leak context for a "capped/skipped" reference chain.
+static const size_t leak_context = 100;
+
+// The number of references associated with the root node;
+// can be viewed as the root node "context".
+// Used to provide root context for a "capped/skipped" reference chain.
+static const size_t root_context = 100;
+
+// A limit on the reference chain depth to be serialized,
+static const size_t max_ref_chain_depth = leak_context + root_context;
+
+const RoutableEdge* skip_to(const RoutableEdge& edge, size_t skip_length) {
+ const RoutableEdge* current = &edge;
+ const RoutableEdge* parent = current->physical_parent();
+ size_t seek = 0;
+ while (parent != NULL && seek != skip_length) {
+ seek++;
+ current = parent;
+ parent = parent->physical_parent();
+ }
+ return current;
+}
+
+#ifdef ASSERT
+static void validate_skip_target(const RoutableEdge* skip_target) {
+ assert(skip_target != NULL, "invariant");
+ assert(skip_target->distance_to_root() + 1 == root_context, "invariant");
+ assert(skip_target->is_sentinel(), "invariant");
+}
+
+static void validate_new_skip_edge(const RoutableEdge* new_skip_edge, const RoutableEdge* last_skip_edge, size_t adjustment) {
+ assert(new_skip_edge != NULL, "invariant");
+ assert(new_skip_edge->is_skip_edge(), "invariant");
+ if (last_skip_edge != NULL) {
+ const RoutableEdge* const target = skip_to(*new_skip_edge->logical_parent(), adjustment);
+ validate_skip_target(target->logical_parent());
+ return;
+ }
+ assert(last_skip_edge == NULL, "invariant");
+ // only one level of logical indirection
+ validate_skip_target(new_skip_edge->logical_parent());
+}
+#endif // ASSERT
+
+static void install_logical_route(const RoutableEdge* new_skip_edge, size_t skip_target_distance) {
+ assert(new_skip_edge != NULL, "invariant");
+ assert(!new_skip_edge->is_skip_edge(), "invariant");
+ assert(!new_skip_edge->processed(), "invariant");
+ const RoutableEdge* const skip_target = skip_to(*new_skip_edge, skip_target_distance);
+ assert(skip_target != NULL, "invariant");
+ new_skip_edge->set_skip_edge(skip_target);
+ new_skip_edge->set_skip_length(skip_target_distance);
+ assert(new_skip_edge->is_skip_edge(), "invariant");
+ assert(new_skip_edge->logical_parent() == skip_target, "invariant");
+}
+
+static const RoutableEdge* find_last_skip_edge(const RoutableEdge& edge, size_t& distance) {
+ assert(distance == 0, "invariant");
+ const RoutableEdge* current = &edge;
+ while (current != NULL) {
+ if (current->is_skip_edge() && current->skip_edge()->is_sentinel()) {
+ return current;
+ }
+ current = current->physical_parent();
+ ++distance;
+ }
+ return current;
+}
+
+static void collapse_overlapping_chain(const RoutableEdge& edge,
+ const RoutableEdge* first_processed_edge,
+ size_t first_processed_distance) {
+ assert(first_processed_edge != NULL, "invariant");
+ // first_processed_edge is already processed / written
+ assert(first_processed_edge->processed(), "invariant");
+ assert(first_processed_distance + 1 <= leak_context, "invariant");
+
+ // from this first processed edge, attempt to fetch the last skip edge
+ size_t last_skip_edge_distance = 0;
+ const RoutableEdge* const last_skip_edge = find_last_skip_edge(*first_processed_edge, last_skip_edge_distance);
+ const size_t distance_discovered = first_processed_distance + last_skip_edge_distance + 1;
+
+ if (distance_discovered <= leak_context || (last_skip_edge == NULL && distance_discovered <= max_ref_chain_depth)) {
+ // complete chain can be accommodated without modification
+ return;
+ }
+
+ // backtrack one edge from existing processed edge
+ const RoutableEdge* const new_skip_edge = skip_to(edge, first_processed_distance - 1);
+ assert(new_skip_edge != NULL, "invariant");
+ assert(!new_skip_edge->processed(), "invariant");
+ assert(new_skip_edge->parent() == first_processed_edge, "invariant");
+
+ size_t adjustment = 0;
+ if (last_skip_edge != NULL) {
+ assert(leak_context - 1 > first_processed_distance - 1, "invariant");
+ adjustment = leak_context - first_processed_distance - 1;
+ assert(last_skip_edge_distance + 1 > adjustment, "invariant");
+ install_logical_route(new_skip_edge, last_skip_edge_distance + 1 - adjustment);
+ } else {
+ install_logical_route(new_skip_edge, last_skip_edge_distance + 1 - root_context);
+ new_skip_edge->logical_parent()->set_skip_length(1); // sentinel
+ }
+
+ DEBUG_ONLY(validate_new_skip_edge(new_skip_edge, last_skip_edge, adjustment);)
+}
+
+static void collapse_non_overlapping_chain(const RoutableEdge& edge,
+ const RoutableEdge* first_processed_edge,
+ size_t first_processed_distance) {
+ assert(first_processed_edge != NULL, "invariant");
+ assert(!first_processed_edge->processed(), "invariant");
+ // this implies that the first "processed" edge is the leak context relative "leaf"
+ assert(first_processed_distance + 1 == leak_context, "invariant");
+
+ const size_t distance_to_root = edge.distance_to_root();
+ if (distance_to_root + 1 <= max_ref_chain_depth) {
+ // complete chain can be accommodated without constructing a skip edge
+ return;
+ }
+
+ install_logical_route(first_processed_edge, distance_to_root + 1 - first_processed_distance - root_context);
+ first_processed_edge->logical_parent()->set_skip_length(1); // sentinel
+
+ DEBUG_ONLY(validate_new_skip_edge(first_processed_edge, NULL, 0);)
+}
+
+static const RoutableEdge* processed_edge(const RoutableEdge& edge, size_t& distance) {
+ assert(distance == 0, "invariant");
+ const RoutableEdge* current = &edge;
+ while (current != NULL && distance < leak_context - 1) {
+ if (current->processed()) {
+ return current;
+ }
+ current = current->physical_parent();
+ ++distance;
+ }
+ assert(distance <= leak_context - 1, "invariant");
+ return current;
+}
+
+/*
+ * Some vocabulary:
+ * -----------
+ * "Context" is an interval in the chain, it is associcated with an edge and it signifies a number of connected edges.
+ * "Processed / written" means an edge that has already been serialized.
+ * "Skip edge" is an edge that contains additional information for logical routing purposes.
+ * "Skip target" is an edge used as a destination for a skip edge
+ */
+void EdgeUtils::collapse_chain(const RoutableEdge& edge) {
+ assert(is_leak_edge(edge), "invariant");
+
+ // attempt to locate an already processed edge inside current leak context (if any)
+ size_t first_processed_distance = 0;
+ const RoutableEdge* const first_processed_edge = processed_edge(edge, first_processed_distance);
+ if (first_processed_edge == NULL) {
+ return;
+ }
+
+ if (first_processed_edge->processed()) {
+ collapse_overlapping_chain(edge, first_processed_edge, first_processed_distance);
+ } else {
+ collapse_non_overlapping_chain(edge, first_processed_edge, first_processed_distance);
+ }
+
+ assert(edge.logical_distance_to_root() + 1 <= max_ref_chain_depth, "invariant");
+}
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.hpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.hpp
new file mode 100644
index 00000000000..730b2fdd4db
--- /dev/null
+++ b/src/hotspot/share/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 RoutableEdge;
+class Symbol;
+
+class EdgeUtils : public AllStatic {
+ public:
+ static bool is_leak_edge(const Edge& edge);
+
+ static const Edge* root(const Edge& edge);
+ static bool is_root(const Edge& edge);
+
+ 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);
+
+ static void collapse_chain(const RoutableEdge& edge);
+};
+
+#endif // SHARE_VM_LEAKPROFILER_CHAINS_EDGEUTILS_HPP
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/objectSampleMarker.hpp b/src/hotspot/share/jfr/leakprofiler/chains/objectSampleMarker.hpp
new file mode 100644
index 00000000000..9a97a373fbc
--- /dev/null
+++ b/src/hotspot/share/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/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp b/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp
new file mode 100644
index 00000000000..fab6ada696e
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.cpp
@@ -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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "aot/aotLoader.hpp"
+#include "classfile/classLoaderData.hpp"
+#include "classfile/stringTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "gc/shared/strongRootsScope.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/access.inline.hpp"
+#include "prims/jvmtiExport.hpp"
+#include "runtime/jniHandles.inline.hpp"
+#include "runtime/synchronizer.hpp"
+#include "runtime/thread.hpp"
+#include "services/management.hpp"
+#include "utilities/align.hpp"
+
+RootSetClosure::RootSetClosure(EdgeQueue* edge_queue) :
+ _edge_queue(edge_queue) {
+}
+
+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");
+ const oop pointee = *ref;
+ if (pointee != NULL) {
+ closure_impl(ref, pointee);
+ }
+}
+
+void RootSetClosure::do_oop(narrowOop* ref) {
+ assert(ref != NULL, "invariant");
+ assert(is_aligned(ref, sizeof(narrowOop)), "invariant");
+ const oop pointee = RawAccess<>::oop_load(ref);
+ if (pointee != NULL) {
+ closure_impl(UnifiedOop::encode(ref), pointee);
+ }
+}
+
+void RootSetClosure::closure_impl(const oop* reference, const oop pointee) {
+ if (!_edge_queue->is_full()) {
+ _edge_queue->add(NULL, reference);
+ }
+}
+
+void RootSetClosure::add_to_queue(EdgeQueue* edge_queue) {
+ RootSetClosure rs(edge_queue);
+ process_roots(&rs);
+}
+
+class RootSetClosureMarkScope : public MarkScope {
+};
+
+void RootSetClosure::process_roots(OopClosure* closure) {
+ SaveRestoreCLDClaimBits save_restore_cld_claim_bits;
+ RootSetClosureMarkScope mark_scope;
+
+ CLDToOopClosure cldt_closure(closure);
+ ClassLoaderDataGraph::always_strong_cld_do(&cldt_closure);
+ CodeBlobToOopClosure blobs(closure, false);
+ Threads::oops_do(closure, &blobs);
+ ObjectSynchronizer::oops_do(closure);
+ Universe::oops_do(closure);
+ JNIHandles::oops_do(closure);
+ JvmtiExport::oops_do(closure);
+ SystemDictionary::always_strong_oops_do(closure);
+ Management::oops_do(closure);
+ StringTable::oops_do(closure);
+ AOTLoader::oops_do(closure);
+}
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.hpp
new file mode 100644
index 00000000000..41cb8ba2a4f
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/chains/rootSetClosure.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_CHAINS_ROOTSETCLOSURE_HPP
+#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_ROOTSETCLOSURE_HPP
+
+#include "memory/iterator.hpp"
+#include "oops/oop.hpp"
+
+class EdgeQueue;
+
+class RootSetClosure: public ExtendedOopClosure {
+ private:
+ RootSetClosure(EdgeQueue* edge_queue);
+ EdgeQueue* _edge_queue;
+ void closure_impl(const oop* reference, const oop pointee);
+ public:
+ static void add_to_queue(EdgeQueue* edge_queue);
+ static void process_roots(OopClosure* closure);
+
+ virtual void do_oop(oop* reference);
+ virtual void do_oop(narrowOop* reference);
+};
+
+#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_ROOTSETCLOSURE_HPP
diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp
new file mode 100644
index 00000000000..ab24853463f
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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.inline.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 resume) {
+ assert(class_unload ? SafepointSynchronize::is_at_safepoint() : LeakProfiler::is_suspended(), "invariant");
+
+ if (!writer.has_data()) {
+ if (!class_unload) {
+ LeakProfiler::resume();
+ }
+ assert(LeakProfiler::is_running(), "invariant");
+ return;
+ }
+
+ assert(writer.has_data(), "invariant");
+ const JfrCheckpointBlobHandle h_cp = writer.checkpoint_blob();
+
+ const ObjectSampler* const object_sampler = LeakProfiler::object_sampler();
+ assert(object_sampler != NULL, "invariant");
+
+ ObjectSample* const last = const_cast(object_sampler->last());
+ const ObjectSample* const last_resolved = object_sampler->last_resolved();
+ CheckpointInstall install(h_cp);
+
+ if (class_unload) {
+ if (last != NULL) {
+ // all samples need the class unload information
+ do_samples(last, NULL, install);
+ }
+ assert(LeakProfiler::is_running(), "invariant");
+ return;
+ }
+
+ // only new samples since last resolved checkpoint
+ if (last != last_resolved) {
+ do_samples(last, last_resolved, install);
+ if (resume) {
+ const_cast(object_sampler)->set_last_resolved(last);
+ }
+ }
+ assert(LeakProfiler::is_suspended(), "invariant");
+ if (resume) {
+ LeakProfiler::resume();
+ assert(LeakProfiler::is_running(), "invariant");
+ }
+}
+
+void ObjectSampleCheckpoint::write(const EdgeStore* edge_store, bool emit_all, Thread* thread) {
+ 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 ObjectSampler* const object_sampler = LeakProfiler::object_sampler();
+ assert(object_sampler != NULL, "invariant");
+ const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value();
+ ObjectSample* const last = const_cast(object_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_edges(osw);
+ }
+}
+
+WriteObjectSampleStacktrace::WriteObjectSampleStacktrace(JfrStackTraceRepository& repo) :
+ _stack_trace_repo(repo) {
+}
+
+bool WriteObjectSampleStacktrace::process() {
+ assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ if (!LeakProfiler::is_running()) {
+ return true;
+ }
+ // Suspend the LeakProfiler subsystem
+ // to ensure stable samples even
+ // after we return from the safepoint.
+ LeakProfiler::suspend();
+ assert(!LeakProfiler::is_running(), "invariant");
+ assert(LeakProfiler::is_suspended(), "invariant");
+
+ const ObjectSampler* object_sampler = LeakProfiler::object_sampler();
+ assert(object_sampler != NULL, "invariant");
+ assert(LeakProfiler::is_suspended(), "invariant");
+
+ ObjectSample* const last = const_cast(object_sampler->last());
+ const ObjectSample* const last_resolved = object_sampler->last_resolved();
+ if (last == last_resolved) {
+ assert(LeakProfiler::is_suspended(), "invariant");
+ 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);
+ assert(LeakProfiler::is_suspended(), "invariant");
+ return true;
+ }
+ assert(count > 0, "invariant");
+ writer.write_count((u4)count, count_offset);
+ JfrStackTraceRepository::write_metadata(writer);
+
+ ObjectSampleCheckpoint::install(writer, false, false);
+ assert(LeakProfiler::is_suspended(), "invariant");
+ return true;
+}
+
+int ObjectSampleCheckpoint::mark(ObjectSampleMarker& marker, bool emit_all) {
+ const ObjectSampler* object_sampler = LeakProfiler::object_sampler();
+ 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();
+}
diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp
new file mode 100644
index 00000000000..8735e3aae1c
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.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_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP
+#define SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/exceptions.hpp"
+
+class EdgeStore;
+class JfrStackTraceRepository;
+class JfrCheckpointWriter;
+class ObjectSampleMarker;
+
+class ObjectSampleCheckpoint : AllStatic {
+ public:
+ static void install(JfrCheckpointWriter& writer, bool class_unload, bool resume);
+ static void write(const EdgeStore* edge_store, bool emit_all, Thread* thread);
+ static int mark(ObjectSampleMarker& marker, bool emit_all);
+};
+
+class WriteObjectSampleStacktrace : public StackObj {
+ private:
+ JfrStackTraceRepository& _stack_trace_repo;
+ public:
+ WriteObjectSampleStacktrace(JfrStackTraceRepository& repo);
+ bool process();
+};
+
+#endif // SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP
diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp
new file mode 100644
index 00000000000..916d36f4e6a
--- /dev/null
+++ b/src/hotspot/share/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");
+ const 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_acquire(_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->is_instance_klass()) {
+ 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");
+ const char* tg_name = java_lang_ThreadGroup::name(_object);
+ if (tg_name != NULL) {
+ write_text("Thread Group: ");
+ write_text(tg_name);
+ }
+}
+
+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->is_instance_klass()) {
+ 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/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleDescription.hpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleDescription.hpp
new file mode 100644
index 00000000000..691e2fe551e
--- /dev/null
+++ b/src/hotspot/share/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/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp
new file mode 100644
index 00000000000..bd3319e035c
--- /dev/null
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp
@@ -0,0 +1,615 @@
+/*
+ * 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 "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