From 0bb1ab9e51d3419d2eb1f6c7e9bd56ef102ba29f Mon Sep 17 00:00:00 2001 From: Denghui Dong Date: Wed, 22 Jul 2020 13:06:15 +0800 Subject: [PATCH] Revert "Revert "[JFR] add support for opto object allocations sampling"" This reverts commit 6585eb9ceefe7395469fdf618514149b368bc34b. --- make/windows/makefiles/vm.make | 3 + src/share/vm/gc_interface/allocTracer.hpp | 11 +- .../vm/gc_interface/allocTracer.inline.hpp | 128 +++++++++++++++++ src/share/vm/gc_interface/collectedHeap.hpp | 10 ++ .../vm/gc_interface/collectedHeap.inline.hpp | 12 +- src/share/vm/jfr/dcmd/jfrDcmds.cpp | 26 +++- src/share/vm/jfr/dcmd/jfrDcmds.hpp | 10 ++ src/share/vm/jfr/jni/jfrJniMethod.cpp | 20 ++- src/share/vm/jfr/jni/jfrJniMethod.hpp | 4 + .../vm/jfr/jni/jfrJniMethodRegistration.cpp | 2 + .../leakprofiler/sampling/objectSampler.cpp | 2 +- src/share/vm/jfr/metadata/metadata.xml | 12 ++ .../vm/jfr/objectprofiler/objectProfiler.cpp | 90 ++++++++++++ .../vm/jfr/objectprofiler/objectProfiler.hpp | 43 ++++++ .../recorder/checkpoint/types/jfrTypeSet.cpp | 10 ++ src/share/vm/jfr/recorder/jfrEventSetting.cpp | 9 ++ src/share/vm/jfr/recorder/jfrEventSetting.hpp | 2 + .../vm/jfr/recorder/service/jfrEvent.hpp | 3 +- .../vm/jfr/recorder/service/jfrOptionSet.cpp | 46 +++++++ .../vm/jfr/recorder/service/jfrOptionSet.hpp | 6 + .../stacktrace/jfrStackTraceRepository.cpp | 62 +++++++-- .../stacktrace/jfrStackTraceRepository.hpp | 19 ++- src/share/vm/jfr/support/jfrFlush.cpp | 4 +- src/share/vm/jfr/support/jfrFlush.hpp | 6 +- .../vm/jfr/support/jfrStackTraceMark.cpp | 11 +- src/share/vm/jfr/support/jfrThreadLocal.cpp | 6 +- src/share/vm/jfr/support/jfrThreadLocal.hpp | 72 ++++++++++ .../vm/jfr/support/jfrTraceIdExtension.hpp | 20 +++ src/share/vm/opto/macro.cpp | 130 +++++++++++++++++- src/share/vm/opto/macro.hpp | 8 ++ src/share/vm/opto/runtime.cpp | 46 +++++++ src/share/vm/opto/runtime.hpp | 5 + src/share/vm/precompiled/precompiled.hpp | 2 + src/share/vm/runtime/vframe.hpp | 2 + 34 files changed, 806 insertions(+), 36 deletions(-) create mode 100644 src/share/vm/gc_interface/allocTracer.inline.hpp create mode 100644 src/share/vm/jfr/objectprofiler/objectProfiler.cpp create mode 100644 src/share/vm/jfr/objectprofiler/objectProfiler.hpp diff --git a/make/windows/makefiles/vm.make b/make/windows/makefiles/vm.make index 1394dd92f..8dbd6dc9b 100644 --- a/make/windows/makefiles/vm.make +++ b/make/windows/makefiles/vm.make @@ -468,6 +468,9 @@ arguments.obj: $(WorkSpace)\src\share\vm\runtime\arguments.cpp {$(COMMONSRC)\share\vm\jfr\writers}.cpp.obj:: $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< +{$(COMMONSRC)\share\vm\jfr\objectprofiler}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + default:: _build_pch_file.obj: diff --git a/src/share/vm/gc_interface/allocTracer.hpp b/src/share/vm/gc_interface/allocTracer.hpp index ac565a247..b6a183263 100644 --- a/src/share/vm/gc_interface/allocTracer.hpp +++ b/src/share/vm/gc_interface/allocTracer.hpp @@ -25,14 +25,23 @@ #ifndef SHARE_VM_GC_INTERFACE_ALLOCTRACER_HPP #define SHARE_VM_GC_INTERFACE_ALLOCTRACER_HPP -#include "memory/allocation.hpp" +#include "gc_implementation/shared/gcId.hpp" #include "runtime/handles.hpp" +#include "utilities/globalDefinitions.hpp" class AllocTracer : AllStatic { + private: + static void send_opto_array_allocation_event(KlassHandle klass, oop obj,size_t alloc_size, Thread* thread); + static void send_opto_instance_allocation_event(KlassHandle klass, oop obj, Thread* thread); + public: static void send_allocation_outside_tlab_event(KlassHandle klass, HeapWord* obj, size_t alloc_size, Thread* thread); static void send_allocation_in_new_tlab_event(KlassHandle klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, Thread* thread); static void send_allocation_requiring_gc_event(size_t size, const GCId& gcId); + static void opto_slow_allocation_enter(bool is_array, Thread* thread); + static void opto_slow_allocation_leave(bool is_array, Thread* thread); + static void send_slow_allocation_event(KlassHandle klass, oop obj,size_t alloc_size, Thread* thread); + static void send_opto_fast_allocation_event(KlassHandle klass, oop obj, size_t alloc_size, Thread* thread); }; #endif /* SHARE_VM_GC_INTERFACE_ALLOCTRACER_HPP */ diff --git a/src/share/vm/gc_interface/allocTracer.inline.hpp b/src/share/vm/gc_interface/allocTracer.inline.hpp new file mode 100644 index 000000000..db10c29b4 --- /dev/null +++ b/src/share/vm/gc_interface/allocTracer.inline.hpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * 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. Alibaba 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. + * + */ +#ifndef SHARE_VM_GC_INTERFACE_ALLOCTRACER_INLINE_HPP +#define SHARE_VM_GC_INTERFACE_ALLOCTRACER_INLINE_HPP + +#include "gc_implementation/shared/gcId.hpp" +#include "jfr/jfrEvents.hpp" +#include "runtime/handles.hpp" +#include "utilities/globalDefinitions.hpp" +#include "gc_interface/allocTracer.hpp" +#if INCLUDE_JFR +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/objectprofiler/objectProfiler.hpp" +#endif // INCLUDE_JFR + +inline void AllocTracer::opto_slow_allocation_enter(bool is_array, Thread* thread) { +#if INCLUDE_JFR + if (JfrOptionSet::sample_object_allocations() && + ObjectProfiler::enabled()) { + assert(thread != NULL, "Invariant"); + assert(thread->is_Java_thread(), "Invariant"); + thread->jfr_thread_local()->incr_alloc_count(1); + if (is_array) { + thread->jfr_thread_local()->set_cached_event_id(JfrOptoArrayObjectAllocationEvent); + } else { + thread->jfr_thread_local()->set_cached_event_id(JfrOptoInstanceObjectAllocationEvent); + } + } +#endif // INCLUDE_JFR +} + +inline void AllocTracer::opto_slow_allocation_leave(bool is_array, Thread* thread) { +#if INCLUDE_JFR +#ifndef PRODUCT + if (JfrOptionSet::sample_object_allocations()) { + JfrThreadLocal* jfr_thread_local = thread->jfr_thread_local(); + assert(!jfr_thread_local->has_cached_event_id(), "Invariant"); + assert(jfr_thread_local->alloc_count_until_sample() >= jfr_thread_local->alloc_count(), "Invariant"); + } +#endif +#endif // INCLUDE_JFR +} + +inline void AllocTracer::send_opto_array_allocation_event(KlassHandle klass, oop obj, size_t alloc_size, Thread* thread) { + EventOptoArrayObjectAllocation event; + if (event.should_commit()) { + event.set_objectClass(klass()); + event.set_address(cast_from_oop(obj)); + event.set_allocationSize(alloc_size); + event.commit(); + } +} + +inline void AllocTracer::send_opto_instance_allocation_event(KlassHandle klass, oop obj, Thread* thread) { + EventOptoInstanceObjectAllocation event; + if (event.should_commit()) { + event.set_objectClass(klass()); + event.set_address(cast_from_oop(obj)); + event.commit(); + } +} + +inline void AllocTracer::send_slow_allocation_event(KlassHandle klass, oop obj, size_t alloc_size, Thread* thread) { +#if INCLUDE_JFR + if (JfrOptionSet::sample_object_allocations()) { + assert(thread != NULL, "Illegal parameter: thread is NULL"); + assert(thread == Thread::current(), "Invariant"); + if (thread->jfr_thread_local()->has_cached_event_id()) { + assert(thread->is_Java_thread(), "Only allow to be called from java thread"); + jlong alloc_count = thread->jfr_thread_local()->alloc_count(); + jlong alloc_count_until_sample = thread->jfr_thread_local()->alloc_count_until_sample(); + assert(alloc_count > 0 || alloc_count <= alloc_count_until_sample, "Invariant"); + if (alloc_count == alloc_count_until_sample) { + JfrEventId event_id = thread->jfr_thread_local()->cached_event_id(); + if (event_id ==JfrOptoArrayObjectAllocationEvent) { + send_opto_array_allocation_event(klass, obj, alloc_size, thread); + } else if(event_id == JfrOptoInstanceObjectAllocationEvent) { + send_opto_instance_allocation_event(klass, obj, thread); + } else { + ShouldNotReachHere(); + } + jlong interval = JfrOptionSet::object_allocations_sampling_interval(); + thread->jfr_thread_local()->incr_alloc_count_until_sample(interval); + } + thread->jfr_thread_local()->clear_cached_event_id(); + } + } +#endif // INCLUDE_JFR +} + +inline void AllocTracer::send_opto_fast_allocation_event(KlassHandle klass, oop obj, size_t alloc_size, Thread* thread) { +#if INCLUDE_JFR + assert(JfrOptionSet::sample_object_allocations(), "Invariant"); + assert(thread != NULL, "Invariant"); + assert(thread->is_Java_thread(), "Invariant"); + assert(!thread->jfr_thread_local()->has_cached_event_id(), "Invariant"); + + Klass* k = klass(); + if (k->oop_is_array()) { + send_opto_array_allocation_event(klass, obj, alloc_size, thread); + } else { + send_opto_instance_allocation_event(klass, obj, thread); + } + jlong interval = JfrOptionSet::object_allocations_sampling_interval(); + thread->jfr_thread_local()->incr_alloc_count_until_sample(interval); +#endif // INCLUDE_JFR +} + +#endif /* SHARE_VM_GC_INTERFACE_ALLOCTRACER_INLINE_HPP */ diff --git a/src/share/vm/gc_interface/collectedHeap.hpp b/src/share/vm/gc_interface/collectedHeap.hpp index b53536568..5ee4f8818 100644 --- a/src/share/vm/gc_interface/collectedHeap.hpp +++ b/src/share/vm/gc_interface/collectedHeap.hpp @@ -26,6 +26,7 @@ #define SHARE_VM_GC_INTERFACE_COLLECTEDHEAP_HPP #include "gc_interface/gcCause.hpp" +#include "gc_interface/allocTracer.hpp" #include "gc_implementation/shared/gcWhen.hpp" #include "memory/allocation.hpp" #include "memory/barrierSet.hpp" @@ -323,6 +324,15 @@ class CollectedHeap : public CHeapObj { inline static void check_array_size(int size, int length, TRAPS); public: + // Implicit Jfr inline methods. + static void trace_slow_allocation(KlassHandle klass, oop obj, size_t alloc_size, Thread* thread) { + AllocTracer::send_slow_allocation_event(klass, obj, alloc_size, thread); + } + + static void trace_allocation_outside_tlab(KlassHandle klass, HeapWord* obj, size_t alloc_size, Thread* thread) { + AllocTracer::send_allocation_outside_tlab_event(klass, obj, alloc_size, thread); + } + inline static void post_allocation_install_obj_klass(KlassHandle klass, oop obj); diff --git a/src/share/vm/gc_interface/collectedHeap.inline.hpp b/src/share/vm/gc_interface/collectedHeap.inline.hpp index b8c1817e3..5b0bf5bef 100644 --- a/src/share/vm/gc_interface/collectedHeap.inline.hpp +++ b/src/share/vm/gc_interface/collectedHeap.inline.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_VM_GC_INTERFACE_COLLECTEDHEAP_INLINE_HPP #define SHARE_VM_GC_INTERFACE_COLLECTEDHEAP_INLINE_HPP -#include "gc_interface/allocTracer.hpp" #include "gc_interface/collectedHeap.hpp" #include "memory/threadLocalAllocBuffer.inline.hpp" #include "memory/universe.hpp" @@ -69,7 +68,7 @@ void CollectedHeap::post_allocation_install_obj_klass(KlassHandle klass, "missing klass"); } -// Support for jvmti and dtrace +// Support for jvmti, jfr and dtrace inline void post_allocation_notify(KlassHandle klass, oop obj, int size) { // support low memory notifications (no-op if not enabled) LowMemoryDetector::detect_low_memory_for_collected_pools(); @@ -83,6 +82,9 @@ inline void post_allocation_notify(KlassHandle klass, oop obj, int size) { SharedRuntime::dtrace_object_alloc(obj, size); } } + + // support for jfr + CollectedHeap::trace_slow_allocation(klass, obj, size * HeapWordSize, Thread::current()); } void CollectedHeap::post_allocation_setup_obj(KlassHandle klass, @@ -91,7 +93,7 @@ void CollectedHeap::post_allocation_setup_obj(KlassHandle klass, post_allocation_setup_common(klass, obj); assert(Universe::is_bootstrapping() || !((oop)obj)->is_array(), "must not be an array"); - // notify jvmti and dtrace + // notify jvmti, jfr and dtrace post_allocation_notify(klass, (oop)obj, size); } @@ -106,7 +108,7 @@ void CollectedHeap::post_allocation_setup_array(KlassHandle klass, post_allocation_setup_common(klass, obj); oop new_obj = (oop)obj; assert(new_obj->is_array(), "must be an array"); - // notify jvmti and dtrace (must be after length is set for dtrace) + // notify jvmti, jfr and dtrace (must be after length is set for dtrace) post_allocation_notify(klass, new_obj, new_obj->size()); } @@ -140,7 +142,7 @@ HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t si "Unexpected exception, will result in uninitialized storage"); THREAD->incr_allocated_bytes(size * HeapWordSize); - AllocTracer::send_allocation_outside_tlab_event(klass, result, size * HeapWordSize, Thread::current()); + CollectedHeap::trace_allocation_outside_tlab(klass, result, size * HeapWordSize, THREAD); return result; } diff --git a/src/share/vm/jfr/dcmd/jfrDcmds.cpp b/src/share/vm/jfr/dcmd/jfrDcmds.cpp index 167405e39..77687e84b 100644 --- a/src/share/vm/jfr/dcmd/jfrDcmds.cpp +++ b/src/share/vm/jfr/dcmd/jfrDcmds.cpp @@ -544,6 +544,7 @@ void JfrStopFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { JfrConfigureFlightRecorderDCmd::JfrConfigureFlightRecorderDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), + _on_vm_start(false), _repository_path("repositorypath", "Path to repository,.e.g \\\"My Repository\\\"", "STRING", false, NULL), _dump_path("dumppath", "Path to dump,.e.g \\\"My Dump path\\\"", "STRING", false, NULL), _stack_depth("stackdepth", "Stack Depth", "JULONG", false, "64"), @@ -552,7 +553,9 @@ JfrConfigureFlightRecorderDCmd::JfrConfigureFlightRecorderDCmd(outputStream* out _thread_buffer_size("thread_buffer_size", "Size of a thread buffer", "MEMORY SIZE", false, "8k"), _memory_size("memorysize", "Overall memory size, ", "MEMORY SIZE", false, "10m"), _max_chunk_size("maxchunksize", "Size of an individual disk chunk", "MEMORY SIZE", false, "12m"), - _sample_threads("samplethreads", "Activate Thread sampling", "BOOLEAN", false, "true") { + _sample_threads("samplethreads", "Activate Thread sampling", "BOOLEAN", false, "true"), + _sample_object_allocations("sampleobjectallocations","object allocations sampling enable / disable", "BOOLEAN", false, "false"), + _object_allocations_sampling_interval("objectallocationssamplinginterval", "object allocations sampling interval", "JLONG", false, "1024") { _dcmdparser.add_dcmd_option(&_repository_path); _dcmdparser.add_dcmd_option(&_dump_path); _dcmdparser.add_dcmd_option(&_stack_depth); @@ -562,6 +565,8 @@ JfrConfigureFlightRecorderDCmd::JfrConfigureFlightRecorderDCmd(outputStream* out _dcmdparser.add_dcmd_option(&_memory_size); _dcmdparser.add_dcmd_option(&_max_chunk_size); _dcmdparser.add_dcmd_option(&_sample_threads); + _dcmdparser.add_dcmd_option(&_sample_object_allocations); + _dcmdparser.add_dcmd_option(&_object_allocations_sampling_interval); }; int JfrConfigureFlightRecorderDCmd::num_arguments() { @@ -592,6 +597,8 @@ void JfrConfigureFlightRecorderDCmd::execute(DCmdSource source, TRAPS) { Handle h_dcmd_instance(THREAD, dcmd); assert(h_dcmd_instance.not_null(), "invariant"); + jobject on_vm_start = JfrJavaSupport::new_java_lang_Boolean(_on_vm_start, CHECK); + jstring repository_path = NULL; if (_repository_path.is_set() && _repository_path.value() != NULL) { repository_path = JfrJavaSupport::new_string(_repository_path.value(), CHECK); @@ -637,16 +644,27 @@ void JfrConfigureFlightRecorderDCmd::execute(DCmdSource source, TRAPS) { sample_threads = JfrJavaSupport::new_java_lang_Boolean(_sample_threads.value(), CHECK); } + jobject sample_object_allocations = NULL; + if (_sample_object_allocations.is_set()) { + sample_object_allocations = JfrJavaSupport::new_java_lang_Boolean(_sample_object_allocations.value(), CHECK); + } + + jobject object_allocations_sampling_interval = NULL; + if (_object_allocations_sampling_interval.is_set()) { + object_allocations_sampling_interval = JfrJavaSupport::new_java_lang_Long(_object_allocations_sampling_interval.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;" + static const char signature[] = "(Ljava/lang/Boolean;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;"; + "Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Long;)Ljava/lang/String;"; JfrJavaArguments execute_args(&result, klass, method, signature, CHECK); execute_args.set_receiver(h_dcmd_instance); // params + execute_args.push_jobject(on_vm_start); execute_args.push_jobject(repository_path); execute_args.push_jobject(dump_path); execute_args.push_jobject(stack_depth); @@ -656,6 +674,8 @@ void JfrConfigureFlightRecorderDCmd::execute(DCmdSource source, TRAPS) { execute_args.push_jobject(memory_size); execute_args.push_jobject(max_chunk_size); execute_args.push_jobject(sample_threads); + execute_args.push_jobject(sample_object_allocations); + execute_args.push_jobject(object_allocations_sampling_interval); JfrJavaSupport::call_virtual(&execute_args, THREAD); handle_dcmd_result(output(), (oop)result.get_jobject(), source, THREAD); diff --git a/src/share/vm/jfr/dcmd/jfrDcmds.hpp b/src/share/vm/jfr/dcmd/jfrDcmds.hpp index f6de5d68d..425a07875 100644 --- a/src/share/vm/jfr/dcmd/jfrDcmds.hpp +++ b/src/share/vm/jfr/dcmd/jfrDcmds.hpp @@ -140,6 +140,11 @@ class JfrRuntimeOptions; class JfrConfigureFlightRecorderDCmd : public DCmdWithParser { friend class JfrOptionSet; + private: + bool _on_vm_start; + void set_on_vm_start(bool on_vm_start) { + _on_vm_start = on_vm_start; + } protected: DCmdArgument _repository_path; DCmdArgument _dump_path; @@ -150,6 +155,8 @@ class JfrConfigureFlightRecorderDCmd : public DCmdWithParser { DCmdArgument _memory_size; DCmdArgument _max_chunk_size; DCmdArgument _sample_threads; + DCmdArgument _sample_object_allocations; + DCmdArgument _object_allocations_sampling_interval; public: JfrConfigureFlightRecorderDCmd(outputStream* output, bool heap); @@ -168,6 +175,9 @@ class JfrConfigureFlightRecorderDCmd : public DCmdWithParser { } static int num_arguments(); virtual void execute(DCmdSource source, TRAPS); + bool on_vm_start() const { + return _on_vm_start; + } }; class JfrUnlockCommercialFeaturesDCmd : public DCmd { diff --git a/src/share/vm/jfr/jni/jfrJniMethod.cpp b/src/share/vm/jfr/jni/jfrJniMethod.cpp index 688c77cb7..c02271b65 100644 --- a/src/share/vm/jfr/jni/jfrJniMethod.cpp +++ b/src/share/vm/jfr/jni/jfrJniMethod.cpp @@ -44,6 +44,7 @@ #include "jfr/instrumentation/jfrEventClassTransformer.hpp" #include "jfr/instrumentation/jfrJvmtiAgent.hpp" #include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/objectprofiler/objectProfiler.hpp" #include "jfr/utilities/jfrJavaLog.hpp" #include "jfr/utilities/jfrTimeConverter.hpp" #include "jfr/utilities/jfrTime.hpp" @@ -110,6 +111,15 @@ NO_TRANSITION(void, jfr_set_enabled(JNIEnv* env, jobject jvm, jlong event_type_i LeakProfiler::stop(); } } + if (EventOptoInstanceObjectAllocation::eventId == event_type_id || + EventOptoArrayObjectAllocation::eventId == event_type_id) { + ThreadInVMfromNative transition(JavaThread::thread_from_jni_environment(env)); + if (JNI_TRUE == enabled) { + ObjectProfiler::start(event_type_id); + } else { + ObjectProfiler::stop(event_type_id); + } + } NO_TRANSITION_END NO_TRANSITION(void, jfr_set_file_notification(JNIEnv* env, jobject jvm, jlong threshold)) @@ -144,6 +154,14 @@ NO_TRANSITION(void, jfr_set_memory_size(JNIEnv* env, jobject jvm, jlong size)) JfrOptionSet::set_memory_size(size); NO_TRANSITION_END +NO_TRANSITION(void, jfr_set_sample_object_allocations(JNIEnv* env, jobject jvm, jboolean sampleAllocations)) + JfrOptionSet::set_sample_object_allocations(sampleAllocations); +NO_TRANSITION_END + +NO_TRANSITION(void, jfr_set_object_allocations_sampling_interval(JNIEnv* env, jobject jvm, jlong interval)) + JfrOptionSet::set_object_allocations_sampling_interval(interval); +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 @@ -233,7 +251,7 @@ JVM_ENTRY_NO_ENV(jlong, jfr_class_id(JNIEnv* env, jclass jvm, jclass jc)) JVM_END JVM_ENTRY_NO_ENV(jlong, jfr_stacktrace_id(JNIEnv* env, jobject jvm, jint skip)) - return JfrStackTraceRepository::record(thread, skip); + return JfrStackTraceRepository::record(thread, skip, WALK_BY_DEFAULT); JVM_END JVM_ENTRY_NO_ENV(void, jfr_log(JNIEnv* env, jobject jvm, jint tag_set, jint level, jstring message)) diff --git a/src/share/vm/jfr/jni/jfrJniMethod.hpp b/src/share/vm/jfr/jni/jfrJniMethod.hpp index 7b5394a2c..4a9a3eb5f 100644 --- a/src/share/vm/jfr/jni/jfrJniMethod.hpp +++ b/src/share/vm/jfr/jni/jfrJniMethod.hpp @@ -93,6 +93,10 @@ 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_set_sample_object_allocations(JNIEnv* env, jobject jvm, jboolean sampleAllocations); + +void JNICALL jfr_set_object_allocations_sampling_interval(JNIEnv* env, jobject jvm, jlong interval); + void JNICALL jfr_store_metadata_descriptor(JNIEnv* env, jobject jvm, jbyteArray descriptor); jlong JNICALL jfr_id_for_thread(JNIEnv* env, jobject jvm, jobject t); diff --git a/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp b/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp index eff29fe0c..cedb941a1 100644 --- a/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp +++ b/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp @@ -62,6 +62,8 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (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*)"setSampleObjectAllocations", (char*)"(Z)V",(void*)jfr_set_sample_object_allocations, + (char*)"setObjectAllocationsSamplingInterval", (char*)"(J)V",(void*)jfr_set_object_allocations_sampling_interval, (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, diff --git a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp index 277927215..90f78bad9 100644 --- a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp +++ b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp @@ -122,7 +122,7 @@ void ObjectSampler::fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* threa assert(stacktrace != NULL, "invariant"); assert(thread != NULL, "invariant"); if (JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) { - JfrStackTraceRepository::fill_stacktrace_for(thread, stacktrace, 0); + JfrStackTraceRepository::fill_stacktrace_for(thread, stacktrace, 0, WALK_BY_DEFAULT); } } diff --git a/src/share/vm/jfr/metadata/metadata.xml b/src/share/vm/jfr/metadata/metadata.xml index 12c3c638b..8d97a420c 100644 --- a/src/share/vm/jfr/metadata/metadata.xml +++ b/src/share/vm/jfr/metadata/metadata.xml @@ -575,6 +575,17 @@ + + + + + + + + + + + @@ -949,6 +960,7 @@ + diff --git a/src/share/vm/jfr/objectprofiler/objectProfiler.cpp b/src/share/vm/jfr/objectprofiler/objectProfiler.cpp new file mode 100644 index 000000000..993909817 --- /dev/null +++ b/src/share/vm/jfr/objectprofiler/objectProfiler.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * 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. Alibaba 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. + * + */ +#include "precompiled.hpp" +#include "runtime/vmThread.hpp" +#include "jfr/objectprofiler/objectProfiler.hpp" +#include "jfr/utilities/jfrTryLock.hpp" +#include "jfrfiles/jfrEventClasses.hpp" + +volatile jint ObjectProfiler::_enabled = JNI_FALSE; +bool ObjectProfiler::_sample_instance_obj_alloc = false; +bool ObjectProfiler::_sample_array_obj_alloc = false; +#ifndef PRODUCT +volatile int ObjectProfiler::_try_lock = 0; +#endif + +void ObjectProfiler::start(jlong event_id) { +#ifndef PRODUCT + JfrTryLock try_lock(&_try_lock); + assert(try_lock.has_lock(), "Not allow contention"); +#endif + if (EventOptoInstanceObjectAllocation::eventId == event_id) { + if (!_sample_instance_obj_alloc) { + _sample_instance_obj_alloc = true; + } + } else if (EventOptoArrayObjectAllocation::eventId == event_id) { + if (!_sample_array_obj_alloc) { + _sample_array_obj_alloc = true; + } + } else { + ShouldNotReachHere(); + } + if (enabled() == JNI_TRUE) { + return; + } + OrderAccess::release_store((volatile jint*)&_enabled, JNI_TRUE); +} + +void ObjectProfiler::stop(jlong event_id) { +#ifndef PRODUCT + JfrTryLock try_lock(&_try_lock); + assert(try_lock.has_lock(), "Not allow contention"); +#endif + if (enabled() == JNI_FALSE) { + assert(!_sample_array_obj_alloc && !_sample_instance_obj_alloc, "Invariant"); + return; + } + if (EventOptoInstanceObjectAllocation::eventId == event_id) { + if (_sample_instance_obj_alloc) { + _sample_instance_obj_alloc = false; + } + } else if (EventOptoArrayObjectAllocation::eventId == event_id) { + if (_sample_array_obj_alloc) { + _sample_array_obj_alloc = false; + } + } else { + ShouldNotReachHere(); + } + bool should_enable = _sample_array_obj_alloc || _sample_instance_obj_alloc; + if (should_enable) { + return; + } + OrderAccess::release_store(&_enabled, JNI_FALSE); +} + +jint ObjectProfiler::enabled() { + return OrderAccess::load_acquire((volatile jint*)&_enabled); +} + +void* ObjectProfiler::enabled_flag_address() { + return (void*)&_enabled; +} diff --git a/src/share/vm/jfr/objectprofiler/objectProfiler.hpp b/src/share/vm/jfr/objectprofiler/objectProfiler.hpp new file mode 100644 index 000000000..a2fbe47b2 --- /dev/null +++ b/src/share/vm/jfr/objectprofiler/objectProfiler.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * 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. Alibaba 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. + * + */ +#ifndef SHARE_VM_JFR_OBJECTROFILER_OBJECTPROFILER_HPP +#define SHARE_VM_JFR_OBJECTROFILER_OBJECTPROFILER_HPP + +#include "prims/jni.h" + +class ObjectProfiler : public AllStatic { + private: + static volatile jint _enabled; + static bool _sample_instance_obj_alloc; + static bool _sample_array_obj_alloc; +#ifndef PRODUCT + static volatile int _try_lock; +#endif + + public: + static void start(jlong event_id); + static void stop(jlong event_id); + static jint enabled(); + static void* enabled_flag_address(); +}; + +#endif // SHARE_VM_JFR_OBJECTROFILER_OBJECTPROFILER_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 877acca9f..05f430c94 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -148,6 +148,16 @@ int write__artifact__klass(JfrCheckpointWriter* writer, JfrArtifactSet* artifact writer->write((traceid)CREATE_SYMBOL_ID(symbol_id)); writer->write(pkg_id); writer->write((s4)klass->access_flags().get_flags()); + if (klass->oop_is_array()) { + // The object array size can not be determined statically from klass. + // It is determined by the elements length in object layout. + // So we put a place holder here to make the event parser ignore it. + writer->write((s4)ARRAY_OBJECT_SIZE_PLACE_HOLDER); + } else { + assert(klass->oop_is_instance(), "invariant"); + jint instanceSize = ((InstanceKlass*) klass)->size_helper() * HeapWordSize; + writer->write((s4)instanceSize); + } return 1; } diff --git a/src/share/vm/jfr/recorder/jfrEventSetting.cpp b/src/share/vm/jfr/recorder/jfrEventSetting.cpp index b7ed48b07..2b2fe71fd 100644 --- a/src/share/vm/jfr/recorder/jfrEventSetting.cpp +++ b/src/share/vm/jfr/recorder/jfrEventSetting.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "jfr/recorder/jfrEventSetting.inline.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" JfrNativeSettings JfrEventSetting::_jvm_event_settings; @@ -53,6 +54,14 @@ void JfrEventSetting::set_enabled(jlong id, bool enabled) { setting(event_id).enabled = enabled; } +StackWalkMode JfrEventSetting::stack_walk_mode(JfrEventId event_id) { + if (event_id == JfrOptoArrayObjectAllocationEvent || + event_id == JfrOptoInstanceObjectAllocationEvent) { + return WALK_BY_CURRENT_FRAME; + } + return WALK_BY_DEFAULT; +} + #ifdef ASSERT bool JfrEventSetting::bounds_check_event(jlong id) { if ((unsigned)id < NUM_RESERVED_EVENTS || (unsigned)id >= MaxJfrEventId) { diff --git a/src/share/vm/jfr/recorder/jfrEventSetting.hpp b/src/share/vm/jfr/recorder/jfrEventSetting.hpp index a8e507cac..5407604bc 100644 --- a/src/share/vm/jfr/recorder/jfrEventSetting.hpp +++ b/src/share/vm/jfr/recorder/jfrEventSetting.hpp @@ -28,6 +28,7 @@ #include "jni.h" #include "jfr/utilities/jfrAllocation.hpp" #include "jfrfiles/jfrEventControl.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" // // Native event settings as an associative array using the event id as key. @@ -46,6 +47,7 @@ class JfrEventSetting : AllStatic { static jlong threshold(JfrEventId event_id); static bool set_cutoff(jlong event_id, jlong cutoff_ticks); static jlong cutoff(JfrEventId event_id); + static StackWalkMode stack_walk_mode(JfrEventId event_id); DEBUG_ONLY(static bool bounds_check_event(jlong id);) }; diff --git a/src/share/vm/jfr/recorder/service/jfrEvent.hpp b/src/share/vm/jfr/recorder/service/jfrEvent.hpp index 38ef13a5b..1257b7cb3 100644 --- a/src/share/vm/jfr/recorder/service/jfrEvent.hpp +++ b/src/share/vm/jfr/recorder/service/jfrEvent.hpp @@ -26,6 +26,7 @@ #define SHARE_VM_JFR_RECORDER_SERVICE_JFREVENT_HPP #include "jfr/recorder/jfrEventSetting.inline.hpp" +#include "jfr/recorder/jfrEventSetting.hpp" #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" #include "jfr/utilities/jfrTime.hpp" #include "jfr/utilities/jfrTypes.hpp" @@ -176,7 +177,7 @@ class JfrEvent { if (tl->has_cached_stack_trace()) { writer.write(tl->cached_stack_trace_id()); } else { - writer.write(JfrStackTraceRepository::record(event_thread)); + writer.write(JfrStackTraceRepository::record(event_thread, 0, JfrEventSetting::stack_walk_mode(T::eventId))); } } else { writer.write(0); diff --git a/src/share/vm/jfr/recorder/service/jfrOptionSet.cpp b/src/share/vm/jfr/recorder/service/jfrOptionSet.cpp index 5c9fa7510..f4b58caeb 100644 --- a/src/share/vm/jfr/recorder/service/jfrOptionSet.cpp +++ b/src/share/vm/jfr/recorder/service/jfrOptionSet.cpp @@ -35,6 +35,7 @@ #include "services/diagnosticFramework.hpp" #include "utilities/growableArray.hpp" #include "utilities/ostream.hpp" +#include "utilities/debug.hpp" struct ObsoleteOption { const char* name; @@ -162,6 +163,22 @@ bool JfrOptionSet::allow_event_retransforms() { return allow_retransforms() && (DumpSharedSpaces || can_retransform()); } +bool JfrOptionSet::sample_object_allocations() { + return _sample_object_allocations == JNI_TRUE; +} + +void JfrOptionSet::set_sample_object_allocations(jboolean value) { + _sample_object_allocations = value; +} + +jlong JfrOptionSet::object_allocations_sampling_interval() { + return _object_allocations_sampling_interval; +} + +void JfrOptionSet::set_object_allocations_sampling_interval(jlong value) { + _object_allocations_sampling_interval = value; +} + // default options for the dcmd parser const char* const default_repository = NULL; const char* const default_global_buffer_size = "512k"; @@ -174,6 +191,9 @@ const char* const default_stack_depth = "64"; const char* const default_retransform = "true"; const char* const default_old_object_queue_size = "256"; DEBUG_ONLY(const char* const default_sample_protection = "false";) +const char* const default_sample_object_allocations = "false"; +// the unit of this value is not time but quantity +const char* const default_object_allocations_sampling_interval = "1024"; // statics static DCmdArgument _dcmd_repository( @@ -255,6 +275,20 @@ static DCmdArgument _dcmd_retransform( true, default_retransform); +static DCmdArgument _dcmd_sampleobjectallocations( + "sampleobjectallocations", + "If object allocations should be sampled (by default false)", + "BOOLEAN", + false, + default_sample_object_allocations); + +static DCmdArgument _dcmd_objectallocationssamplinginterval( + "objectallocationssamplinginterval", + "object allocations sampling interval (by default 1024)", + "JLONG", + false, + default_object_allocations_sampling_interval); + static DCmdParser _parser; static void register_parser_options() { @@ -269,6 +303,8 @@ static void register_parser_options() { _parser.add_dcmd_option(&_dcmd_retransform); _parser.add_dcmd_option(&_dcmd_old_object_queue_size); DEBUG_ONLY(_parser.add_dcmd_option(&_dcmd_sample_protection);) + _parser.add_dcmd_option(&_dcmd_sampleobjectallocations); + _parser.add_dcmd_option(&_dcmd_objectallocationssamplinginterval); } static bool parse_flight_recorder_options_internal(TRAPS) { @@ -314,6 +350,9 @@ jboolean JfrOptionSet::_sample_protection = JNI_FALSE; #else jboolean JfrOptionSet::_sample_protection = JNI_TRUE; #endif +volatile jboolean JfrOptionSet::_sample_object_allocations = JNI_FALSE; +// the unit of this value is not time but quantity +volatile jlong JfrOptionSet::_object_allocations_sampling_interval = 1024; bool JfrOptionSet::initialize(Thread* thread) { register_parser_options(); @@ -335,6 +374,7 @@ bool JfrOptionSet::configure(TRAPS) { bufferedStream st; // delegate to DCmd execution JfrConfigureFlightRecorderDCmd configure(&st, false); + configure.set_on_vm_start(true); configure._repository_path.set_is_set(_dcmd_repository.is_set()); char* repo = _dcmd_repository.value(); if (repo != NULL) { @@ -368,6 +408,12 @@ bool JfrOptionSet::configure(TRAPS) { configure._sample_threads.set_is_set(_dcmd_sample_threads.is_set()); configure._sample_threads.set_value(_dcmd_sample_threads.value()); + configure._sample_object_allocations.set_is_set(_dcmd_sampleobjectallocations.is_set()); + configure._sample_object_allocations.set_value(_dcmd_sampleobjectallocations.value()); + + configure._object_allocations_sampling_interval.set_is_set(_dcmd_objectallocationssamplinginterval.is_set()); + configure._object_allocations_sampling_interval.set_value(_dcmd_objectallocationssamplinginterval.value()); + configure.execute(DCmd_Source_Internal, THREAD); if (HAS_PENDING_EXCEPTION) { diff --git a/src/share/vm/jfr/recorder/service/jfrOptionSet.hpp b/src/share/vm/jfr/recorder/service/jfrOptionSet.hpp index d7097eeed..7de4ef48a 100644 --- a/src/share/vm/jfr/recorder/service/jfrOptionSet.hpp +++ b/src/share/vm/jfr/recorder/service/jfrOptionSet.hpp @@ -48,6 +48,8 @@ class JfrOptionSet : public AllStatic { static jboolean _sample_threads; static jboolean _retransform; static jboolean _sample_protection; + static volatile jboolean _sample_object_allocations; + static volatile jlong _object_allocations_sampling_interval; static bool initialize(Thread* thread); static bool configure(TRAPS); @@ -77,6 +79,10 @@ class JfrOptionSet : public AllStatic { static bool allow_event_retransforms(); static bool sample_protection(); DEBUG_ONLY(static void set_sample_protection(jboolean protection);) + static bool sample_object_allocations(); + static void set_sample_object_allocations(jboolean value); + static jlong object_allocations_sampling_interval(); + static void set_object_allocations_sampling_interval(jlong value); static bool parse_flight_recorder_option(const JavaVMOption** option, char* delimiter); static bool parse_start_flight_recording_option(const JavaVMOption** option, char* delimiter); diff --git a/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp b/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp index 57fa598fb..ce7a5098b 100644 --- a/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp +++ b/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp @@ -173,7 +173,7 @@ traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) { return tid; } -traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) { +traceid JfrStackTraceRepository::record(Thread* thread, int skip, StackWalkMode mode) { assert(thread == Thread::current(), "invariant"); JfrThreadLocal* const tl = thread->jfr_thread_local(); assert(tl != NULL, "invariant"); @@ -190,12 +190,12 @@ traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) { } assert(frames != NULL, "invariant"); assert(tl->stackframes() == frames, "invariant"); - return instance().record_for((JavaThread*)thread, skip,frames, tl->stackdepth()); + return instance().record_for((JavaThread*)thread, skip, mode, frames, tl->stackdepth()); } -traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrStackFrame *frames, u4 max_frames) { +traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, StackWalkMode mode, JfrStackFrame *frames, u4 max_frames) { JfrStackTrace stacktrace(frames, max_frames); - return stacktrace.record_safe(thread, skip) ? add(stacktrace) : 0; + return stacktrace.record_safe(thread, skip, false, mode) ? add(stacktrace) : 0; } traceid JfrStackTraceRepository::add(const JfrStackTrace* stacktrace, JavaThread* thread) { @@ -205,7 +205,7 @@ traceid JfrStackTraceRepository::add(const JfrStackTrace* stacktrace, JavaThread return add(*stacktrace); } -bool JfrStackTraceRepository::fill_stacktrace_for(JavaThread* thread, JfrStackTrace* stacktrace, int skip) { +bool JfrStackTraceRepository::fill_stacktrace_for(JavaThread* thread, JfrStackTrace* stacktrace, int skip, StackWalkMode mode) { assert(thread == Thread::current(), "invariant"); assert(stacktrace != NULL, "invariant"); JfrThreadLocal* const tl = thread->jfr_thread_local(); @@ -215,7 +215,7 @@ bool JfrStackTraceRepository::fill_stacktrace_for(JavaThread* thread, JfrStackTr stacktrace->set_hash(cached_stacktrace_hash); return true; } - return stacktrace->record_safe(thread, skip, true); + return stacktrace->record_safe(thread, skip, true, mode); } size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) { @@ -363,16 +363,45 @@ void JfrStackTrace::resolve_linenos() const { _lineno = true; } -bool JfrStackTrace::record_safe(JavaThread* thread, int skip, bool leakp /* false */) { +bool JfrStackTrace::record_safe(JavaThread* thread, int skip, bool leakp, StackWalkMode mode) { assert(SafepointSynchronize::safepoint_safe(thread, thread->thread_state()) || thread == Thread::current(), "Thread stack needs to be walkable"); - vframeStream vfs(thread); + + bool success = false; + switch(mode) { + case WALK_BY_DEFAULT: + { + vframeStream vfs(thread); + success = fill_in(vfs, skip, leakp, mode); + break; + } + case WALK_BY_CURRENT_FRAME: + { + vframeStream vfs(thread, os::current_frame()); + success = fill_in(vfs, skip, leakp, mode); + break; + } + default: + ShouldNotReachHere(); + } + return success; +} + +bool JfrStackTrace::fill_in(vframeStream& vfs, int skip, bool leakp, StackWalkMode mode) { u4 count = 0; _reached_root = true; + // Indicates whether the top frame is visited in this frames iteration. + // Top frame bci may be invalid and fill_in() will fix the top frame bci in a conservative way. + bool top_frame_visited = false; for(int i = 0; i < skip; i++) { if (vfs.at_end()) { break; } + // The top frame is in skip list. + // Mark top_frame_visited to avoid unnecessary top frame bci fixing. + if (!top_frame_visited) { + top_frame_visited = true; + } vfs.next(); } @@ -387,8 +416,25 @@ bool JfrStackTrace::record_safe(JavaThread* thread, int skip, bool leakp /* fals int bci = 0; if (method->is_native()) { type = JfrStackFrame::FRAME_NATIVE; + // The top frame is in native. + // Mark top_frame_visited to avoid unnecessary top frame bci fixing. + if (!top_frame_visited) { + top_frame_visited = true; + } } else { bci = vfs.bci(); + // Hit the top frame and fix bci here. + if (!top_frame_visited) { + if (mode == WALK_BY_CURRENT_FRAME) { + // Only fix opto fast path allocation. + // All fast path allocations do not have cached event id. + if (!vfs.thread_ref()->jfr_thread_local()->has_cached_event_id()) { + assert(vfs.thread_ref()->jfr_thread_local()->has_cached_top_frame_bci(), "Invariant"); + bci = vfs.thread_ref()->jfr_thread_local()->cached_top_frame_bci(); + } + } + top_frame_visited = true; + } } // Can we determine if it's inlined? _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type); diff --git a/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp b/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp index 6a95e171f..ad5589ac3 100644 --- a/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp +++ b/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp @@ -33,6 +33,7 @@ class JavaThread; class JfrCheckpointWriter; class JfrChunkWriter; class Method; +class vframeStream; class JfrStackFrame { private: @@ -61,6 +62,15 @@ class JfrStackFrame { void resolve_lineno() const; }; +enum StackWalkMode { + // walk stack by vframeStream vfs(thread). + WALK_BY_DEFAULT = 0, + // walk stack by vframeStream vfs(thread, os::current_frame()). + // It is only used in JIT runtime leaf call. In JIT runtime leaf call, + // last_java_sp is not maintained and WALK_BY_DEFAULT can not walk stack. + WALK_BY_CURRENT_FRAME +}; + class JfrStackTrace : public StackObj { friend class JfrStackTraceRepository; private: @@ -72,6 +82,7 @@ class JfrStackTrace : public StackObj { bool _reached_root; mutable bool _lineno; + bool fill_in(vframeStream& vfs, int skip, bool leakp, StackWalkMode mode); public: JfrStackTrace(JfrStackFrame* frames, u4 max_frames) : _frames(frames), _id(0), @@ -81,7 +92,7 @@ class JfrStackTrace : public StackObj { _max_frames(max_frames), _lineno(false) {} bool record_thread(JavaThread& thread, frame& frame); - bool record_safe(JavaThread* thread, int skip, bool leakp = false); + bool record_safe(JavaThread* thread, int skip, bool leakp, StackWalkMode stack_walk_mode); void resolve_linenos() const; void set_nr_of_frames(u4 nr_of_frames) { _nr_of_frames = nr_of_frames; } void set_hash(unsigned int hash) { _hash = hash; } @@ -131,13 +142,13 @@ class JfrStackTraceRepository : public JfrCHeapObj { traceid add_trace(const JfrStackTrace& stacktrace); static traceid add(const JfrStackTrace* stacktrace, JavaThread* thread); - traceid record_for(JavaThread* thread, int skip, JfrStackFrame* frames, u4 max_frames); + traceid record_for(JavaThread* thread, int skip, StackWalkMode mode, JfrStackFrame* frames, u4 max_frames); size_t write_impl(JfrChunkWriter& cw, bool clear); const StackTrace* resolve_entry(unsigned int hash, traceid id) const; static void write_metadata(JfrCheckpointWriter& cpw); - static bool fill_stacktrace_for(JavaThread* thread, JfrStackTrace* stacktrace, int skip); + static bool fill_stacktrace_for(JavaThread* thread, JfrStackTrace* stacktrace, int skip, StackWalkMode mode); JfrStackTraceRepository(); static JfrStackTraceRepository* create(); @@ -148,7 +159,7 @@ class JfrStackTraceRepository : public JfrCHeapObj { public: static traceid add(const JfrStackTrace& stacktrace); - static traceid record(Thread* thread, int skip = 0); + static traceid record(Thread* thread, int skip, StackWalkMode mode); traceid write(JfrCheckpointWriter& cpw, traceid id, unsigned int hash); size_t write(JfrChunkWriter& cw, bool clear); size_t clear(); diff --git a/src/share/vm/jfr/support/jfrFlush.cpp b/src/share/vm/jfr/support/jfrFlush.cpp index 3f1402fc4..2dca3ce42 100644 --- a/src/share/vm/jfr/support/jfrFlush.cpp +++ b/src/share/vm/jfr/support/jfrFlush.cpp @@ -70,12 +70,12 @@ void jfr_conditional_flush(JfrEventId id, size_t size, Thread* t) { } } -bool jfr_save_stacktrace(Thread* t) { +bool jfr_save_stacktrace(Thread* t, StackWalkMode mode) { JfrThreadLocal* const tl = t->jfr_thread_local(); if (tl->has_cached_stack_trace()) { return false; // no ownership } - tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(t)); + tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(t, 0, mode)); return true; } diff --git a/src/share/vm/jfr/support/jfrFlush.hpp b/src/share/vm/jfr/support/jfrFlush.hpp index 62c4bc84f..394aee70c 100644 --- a/src/share/vm/jfr/support/jfrFlush.hpp +++ b/src/share/vm/jfr/support/jfrFlush.hpp @@ -27,6 +27,8 @@ #include "jfr/recorder/storage/jfrBuffer.hpp" #include "jfr/utilities/jfrTypes.hpp" +#include "jfr/recorder/jfrEventSetting.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" #include "memory/allocation.hpp" class Thread; @@ -43,7 +45,7 @@ class JfrFlush : public StackObj { void jfr_conditional_flush(JfrEventId id, size_t size, Thread* t); bool jfr_is_event_enabled(JfrEventId id); bool jfr_has_stacktrace_enabled(JfrEventId id); -bool jfr_save_stacktrace(Thread* t); +bool jfr_save_stacktrace(Thread* t, StackWalkMode mode); void jfr_clear_stacktrace(Thread* t); template @@ -66,7 +68,7 @@ class JfrConditionalFlushWithStacktrace : public JfrConditionalFlush { public: JfrConditionalFlushWithStacktrace(Thread* t) : JfrConditionalFlush(t), _t(t), _owner(false) { if (this->_enabled && Event::has_stacktrace() && jfr_has_stacktrace_enabled(Event::eventId)) { - _owner = jfr_save_stacktrace(t); + _owner = jfr_save_stacktrace(t, JfrEventSetting::stack_walk_mode(Event::eventId)); } } ~JfrConditionalFlushWithStacktrace() { diff --git a/src/share/vm/jfr/support/jfrStackTraceMark.cpp b/src/share/vm/jfr/support/jfrStackTraceMark.cpp index b7a2f1d64..2fa3610e7 100644 --- a/src/share/vm/jfr/support/jfrStackTraceMark.cpp +++ b/src/share/vm/jfr/support/jfrStackTraceMark.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "jfr/recorder/jfrEventSetting.inline.hpp" +#include "jfr/recorder/jfrEventSetting.hpp" #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" #include "jfr/support/jfrStackTraceMark.hpp" #include "jfr/support/jfrThreadLocal.hpp" @@ -35,7 +36,7 @@ JfrStackTraceMark::JfrStackTraceMark() : _t(Thread::current()), _previous_id(0), _previous_id = tl->cached_stack_trace_id(); _previous_hash = tl->cached_stack_trace_hash(); } - tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(Thread::current())); + tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(Thread::current(), 0, WALK_BY_DEFAULT)); } JfrStackTraceMark::JfrStackTraceMark(Thread* t) : _t(t), _previous_id(0), _previous_hash(0) { @@ -44,7 +45,7 @@ JfrStackTraceMark::JfrStackTraceMark(Thread* t) : _t(t), _previous_id(0), _previ _previous_id = tl->cached_stack_trace_id(); _previous_hash = tl->cached_stack_trace_hash(); } - tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(t)); + tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(t, 0, WALK_BY_DEFAULT)); } JfrStackTraceMark::JfrStackTraceMark(JfrEventId eventId) : _t(NULL), _previous_id(0), _previous_hash(0) { @@ -55,7 +56,8 @@ JfrStackTraceMark::JfrStackTraceMark(JfrEventId eventId) : _t(NULL), _previous_i _previous_id = tl->cached_stack_trace_id(); _previous_hash = tl->cached_stack_trace_hash(); } - tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(_t)); + StackWalkMode mode = JfrEventSetting::stack_walk_mode(eventId); + tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(_t, 0, mode)); } } @@ -67,7 +69,8 @@ JfrStackTraceMark::JfrStackTraceMark(JfrEventId eventId, Thread* t) : _t(NULL), _previous_id = tl->cached_stack_trace_id(); _previous_hash = tl->cached_stack_trace_hash(); } - tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(_t)); + StackWalkMode mode = JfrEventSetting::stack_walk_mode(eventId); + tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(_t, 0, mode)); } } diff --git a/src/share/vm/jfr/support/jfrThreadLocal.cpp b/src/share/vm/jfr/support/jfrThreadLocal.cpp index 7a7fde319..6f1c53f6c 100644 --- a/src/share/vm/jfr/support/jfrThreadLocal.cpp +++ b/src/share/vm/jfr/support/jfrThreadLocal.cpp @@ -55,7 +55,11 @@ JfrThreadLocal::JfrThreadLocal() : _stack_trace_hash(0), _stackdepth(0), _entering_suspend_flag(0), - _dead(false) {} + _dead(false), + _cached_top_frame_bci(max_jint), + _alloc_count(0), + _alloc_count_until_sample(1), + _cached_event_id(MaxJfrEventId) {} u8 JfrThreadLocal::add_data_lost(u8 value) { _data_lost += value; diff --git a/src/share/vm/jfr/support/jfrThreadLocal.hpp b/src/share/vm/jfr/support/jfrThreadLocal.hpp index 93fa41969..7acba01f3 100644 --- a/src/share/vm/jfr/support/jfrThreadLocal.hpp +++ b/src/share/vm/jfr/support/jfrThreadLocal.hpp @@ -51,6 +51,27 @@ class JfrThreadLocal { mutable u4 _stackdepth; volatile jint _entering_suspend_flag; bool _dead; + // Jfr callstack collection relies on vframeStream. + // But the bci of top frame can not be determined by vframeStream in some scenarios. + // For example, in the opto CallLeafNode runtime call of + // OptoRuntime::jfr_fast_object_alloc_C, the top frame bci + // returned by vframeStream is always invalid. This is largely due to the oopmap that + // is not correctly granted ( refer to PhaseMacroExpand::expand_allocate_common to get more details ). + // The opto fast path object allocation tracing occurs in the opto CallLeafNode, + // which has been broken by invalid top frame bci. + // To fix this, we get the top frame bci in opto compilation phase + // and pass it as parameter to runtime call. Our implementation will replace the invalid top + // frame bci with cached_top_frame_bci. + jint _cached_top_frame_bci; + jlong _alloc_count; + jlong _alloc_count_until_sample; + // This field is used to help to distinguish the object allocation request source. + // For example, for object allocation slow path, we trace it in CollectedHeap::obj_allocate. + // But in CollectedHeap::obj_allocate, it is impossible to determine where the allocation request + // is from, which could be from c1, opto, or even interpreter. + // We save this infomation in _event_id, which later can be retrieved in + // CollecetedHeap::obj_allocate to identify the real allocation request source. + JfrEventId _cached_event_id; JfrBuffer* install_native_buffer() const; JfrBuffer* install_java_buffer() const; @@ -207,6 +228,54 @@ class JfrThreadLocal { return _dead; } + void set_cached_top_frame_bci(jint bci) { + _cached_top_frame_bci = bci; + } + + bool has_cached_top_frame_bci() const { + return _cached_top_frame_bci != max_jint; + } + + jint cached_top_frame_bci() const { + return _cached_top_frame_bci; + } + + void clear_cached_top_frame_bci() { + _cached_top_frame_bci = max_jint; + } + + jlong alloc_count() const { + return _alloc_count; + } + + void incr_alloc_count(jlong delta) { + _alloc_count += delta; + } + + jlong alloc_count_until_sample() const { + return _alloc_count_until_sample; + } + + void incr_alloc_count_until_sample(jlong delta) { + _alloc_count_until_sample += delta; + } + + void set_cached_event_id(JfrEventId event_id) { + _cached_event_id = event_id; + } + + JfrEventId cached_event_id() const { + return _cached_event_id; + } + + bool has_cached_event_id() const { + return _cached_event_id != MaxJfrEventId; + } + + void clear_cached_event_id() { + _cached_event_id = MaxJfrEventId; + } + bool has_thread_checkpoint() const; void set_thread_checkpoint(const JfrCheckpointBlobHandle& handle); const JfrCheckpointBlobHandle& thread_checkpoint() const; @@ -217,6 +286,9 @@ class JfrThreadLocal { // Code generation static ByteSize trace_id_offset(); static ByteSize java_event_writer_offset(); + + TRACE_DEFINE_THREAD_ALLOC_COUNT_UNTIL_SAMPLE_OFFSET; + TRACE_DEFINE_THREAD_ALLOC_COUNT_OFFSET; }; #endif // SHARE_VM_JFR_SUPPORT_JFRTHREADLOCAL_HPP diff --git a/src/share/vm/jfr/support/jfrTraceIdExtension.hpp b/src/share/vm/jfr/support/jfrTraceIdExtension.hpp index e4d479f59..c46a893bd 100644 --- a/src/share/vm/jfr/support/jfrTraceIdExtension.hpp +++ b/src/share/vm/jfr/support/jfrTraceIdExtension.hpp @@ -78,4 +78,24 @@ class JfrTraceFlag { return _trace_flags.flags_addr(); \ } +#define ARRAY_OBJECT_SIZE_PLACE_HOLDER 0x1111baba + +#define TRACE_OPTO_SLOW_ALLOCATION_ENTER(is_array, thread) \ + AllocTracer::opto_slow_allocation_enter(is_array, thread) + +#define TRACE_OPTO_SLOW_ALLOCATION_LEAVE(is_array, thread) \ + AllocTracer::opto_slow_allocation_leave(is_array, thread) + +#define TRACE_SLOW_ALLOCATION(klass, obj, alloc_size, thread) \ + AllocTracer::send_slow_allocation_event(klass, obj, alloc_size, thread) + +#define TRACE_DEFINE_THREAD_ALLOC_COUNT_OFFSET \ + static ByteSize alloc_count_offset() { return in_ByteSize(offset_of(JfrThreadLocal, _alloc_count)); } +#define TRACE_THREAD_ALLOC_COUNT_OFFSET \ + (JfrThreadLocal::alloc_count_offset() + Thread::jfr_thread_local_offset()) +#define TRACE_DEFINE_THREAD_ALLOC_COUNT_UNTIL_SAMPLE_OFFSET \ + static ByteSize alloc_count_until_sample_offset() { return in_ByteSize(offset_of(JfrThreadLocal, _alloc_count_until_sample)); } +#define TRACE_THREAD_ALLOC_COUNT_UNTIL_SAMPLE_OFFSET \ + (JfrThreadLocal::alloc_count_until_sample_offset() + Thread::jfr_thread_local_offset()) + #endif // SHARE_VM_JFR_SUPPORT_JFRTRACEIDEXTENSION_HPP diff --git a/src/share/vm/opto/macro.cpp b/src/share/vm/opto/macro.cpp index cf2ba77d5..f53cd74ba 100644 --- a/src/share/vm/opto/macro.cpp +++ b/src/share/vm/opto/macro.cpp @@ -27,6 +27,7 @@ #include "libadt/vectset.hpp" #include "opto/addnode.hpp" #include "opto/callnode.hpp" +#include "opto/divnode.hpp" #include "opto/cfgnode.hpp" #include "opto/compile.hpp" #include "opto/connode.hpp" @@ -41,6 +42,7 @@ #include "opto/subnode.hpp" #include "opto/type.hpp" #include "runtime/sharedRuntime.hpp" +#include "jfr/objectprofiler/objectProfiler.hpp" // @@ -1087,15 +1089,21 @@ void PhaseMacroExpand::set_eden_pointers(Node* &eden_top_adr, Node* &eden_end_ad } } - -Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) { +Node* PhaseMacroExpand::load(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt, MemNode::MemOrd mo) { Node* adr = basic_plus_adr(base, offset); const TypePtr* adr_type = adr->bottom_type()->is_ptr(); - Node* value = LoadNode::make(_igvn, ctl, mem, adr, adr_type, value_type, bt, MemNode::unordered); + Node* value = LoadNode::make(_igvn, ctl, mem, adr, adr_type, value_type, bt, mo); transform_later(value); return value; } +Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) { + return load(ctl, mem, base, offset, value_type, bt, MemNode::unordered); +} + +Node* PhaseMacroExpand::make_load_acquire(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) { + return load(ctl, mem, base, offset, value_type, bt, MemNode::acquire); +} Node* PhaseMacroExpand::make_store(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) { Node* adr = basic_plus_adr(base, offset); @@ -1434,6 +1442,12 @@ void PhaseMacroExpand::expand_allocate_common( } } +#if INCLUDE_JFR + if (JfrOptionSet::sample_object_allocations()) { + jfr_sample_fast_object_allocation(alloc, fast_oop, fast_oop_ctrl, fast_oop_rawmem); + } +#endif // INCLUDE_JFR + if (C->env()->dtrace_extended_probes()) { // Slow-path call int size = TypeFunc::Parms + 2; @@ -1619,6 +1633,116 @@ void PhaseMacroExpand::expand_allocate_common( // This completes all paths into the result merge point } +#if INCLUDE_JFR +static jint bottom_java_frame_bci(JVMState* state) { + assert(state != NULL, "Invariant"); + + JVMState* last = NULL; + JVMState* current = state; + while (current != NULL) { + last = current; + current = current->caller(); + } + return last->bci(); +} + +// +// Pseudo code: +// +// int alloc_sample_enabled = *(int *)ObjectProfiler::enabled_flag_address(); +// if (alloc_sample_enabled) { +// long alloc_count = thread->jfr_thread_local()->alloc_count(); +// long alloc_count_new = alloc_count + 1; +// thread->jfr_thread_local()->set_alloc_count(alloc_count_new); +// long alloc_count_until_sample = thread->jfr_thread_local()->alloc_count_until_sample(); +// if (alloc_count_until_sample == alloc_count_new) { +// jfr_fast_object_alloc_C(obj, thread); +// } +// } +void PhaseMacroExpand::jfr_sample_fast_object_allocation( + AllocateNode* alloc, Node* fast_oop, + Node*& fast_oop_ctrl, Node*& fast_oop_rawmem) { + Node* tls = transform_later(new (C) ThreadLocalNode()); + + Node* alloc_sample_enabled_addr = transform_later(ConPNode::make(C, (address) ObjectProfiler::enabled_flag_address())); + Node* alloc_sample_enabled = make_load_acquire(fast_oop_ctrl, fast_oop_rawmem, alloc_sample_enabled_addr, 0, TypeInt::INT, T_INT); + Node* alloc_sample_enabled_cmp = transform_later(new (C) CmpINode(alloc_sample_enabled, intcon(1))); + Node* alloc_sample_enabled_bool = transform_later(new (C) BoolNode(alloc_sample_enabled_cmp, BoolTest::eq)); + IfNode* alloc_sample_enabled_if = (IfNode*)transform_later(new (C) IfNode(fast_oop_ctrl, alloc_sample_enabled_bool, PROB_MIN, COUNT_UNKNOWN)); + Node* alloc_sample_enabled_ctrl = transform_later(new (C) IfTrueNode(alloc_sample_enabled_if)); + Node* alloc_sample_enabled_mem = fast_oop_rawmem; + Node* alloc_sample_disabled_ctrl = transform_later(new (C) IfFalseNode(alloc_sample_enabled_if)); + Node* alloc_sample_disabled_mem = fast_oop_rawmem; + Node* alloc_sample_enabled_region = transform_later(new (C) RegionNode(3)); + Node* alloc_sample_enabled_region_phi_mem = transform_later(new (C) PhiNode(alloc_sample_enabled_region, Type::MEMORY, TypeRawPtr::BOTTOM)); + enum { enabled_idx = 1, disabled_idx = 2 }; + + // if _enabled then + { + const int alloc_count_offset = in_bytes(TRACE_THREAD_ALLOC_COUNT_OFFSET); + Node* alloc_count = make_load(alloc_sample_enabled_ctrl, alloc_sample_enabled_mem, tls, alloc_count_offset, TypeLong::LONG, T_LONG); + Node* alloc_count_new = transform_later(new (C) AddLNode(alloc_count, longcon(1))); + alloc_sample_enabled_mem = make_store(alloc_sample_enabled_ctrl, alloc_sample_enabled_mem, tls, alloc_count_offset, alloc_count_new, T_LONG); + const int alloc_count_until_sample_offset = in_bytes(TRACE_THREAD_ALLOC_COUNT_UNTIL_SAMPLE_OFFSET); + Node* alloc_count_until_sample = make_load(alloc_sample_enabled_ctrl, alloc_sample_enabled_mem, tls, alloc_count_until_sample_offset, TypeLong::LONG, T_LONG); + Node* alloc_count_until_sample_cmp = transform_later(new (C) CmpLNode(alloc_count_until_sample, alloc_count_new)); + Node* alloc_sample_hit_bool = transform_later(new (C) BoolNode(alloc_count_until_sample_cmp, BoolTest::eq)); + IfNode* alloc_sample_hit_if = (IfNode*)transform_later(new (C) IfNode(alloc_sample_enabled_ctrl, alloc_sample_hit_bool, PROB_MIN, COUNT_UNKNOWN)); + Node* alloc_sample_hit_ctrl = transform_later(new (C) IfTrueNode(alloc_sample_hit_if)); + Node* alloc_sample_hit_mem = alloc_sample_enabled_mem; + Node* alloc_sample_miss_ctrl = transform_later(new (C) IfFalseNode(alloc_sample_hit_if)); + Node* alloc_sample_miss_mem = alloc_sample_enabled_mem; + Node* alloc_sample_hit_region = transform_later(new (C) RegionNode(3)); + Node* alloc_sample_hit_region_phi_mem = transform_later(new (C) PhiNode(alloc_sample_hit_region, Type::MEMORY, TypeRawPtr::BOTTOM)); + + // if sample_hit then + { + CallLeafNode *call = new (C) CallLeafNode(OptoRuntime::jfr_fast_object_alloc_Type(), + CAST_FROM_FN_PTR(address, OptoRuntime::jfr_fast_object_alloc_C), + "jfr_fast_object_alloc_C", + TypeRawPtr::BOTTOM); + call->init_req(TypeFunc::Parms+0, fast_oop); + call->init_req(TypeFunc::Parms+1, intcon(bottom_java_frame_bci(alloc->jvms()))); + call->init_req(TypeFunc::Parms+2, tls); + call->init_req(TypeFunc::Control, alloc_sample_hit_ctrl); + call->init_req(TypeFunc::I_O , top()); + call->init_req(TypeFunc::Memory , alloc_sample_hit_mem); + call->init_req(TypeFunc::ReturnAdr, alloc->in(TypeFunc::ReturnAdr)); + call->init_req(TypeFunc::FramePtr, alloc->in(TypeFunc::FramePtr)); + transform_later(call); + alloc_sample_hit_ctrl = new (C) ProjNode(call,TypeFunc::Control); + transform_later(alloc_sample_hit_ctrl); + alloc_sample_hit_mem = new (C) ProjNode(call,TypeFunc::Memory); + transform_later(alloc_sample_hit_mem); + + alloc_sample_hit_region->init_req(enabled_idx, alloc_sample_hit_ctrl); + alloc_sample_hit_region_phi_mem->init_req(enabled_idx, alloc_sample_hit_mem); + } + + { + alloc_sample_hit_region->init_req(disabled_idx, alloc_sample_miss_ctrl); + alloc_sample_hit_region_phi_mem->init_req(disabled_idx, alloc_sample_miss_mem); + } + + { + alloc_sample_enabled_ctrl = alloc_sample_hit_region; + alloc_sample_enabled_mem = alloc_sample_hit_region_phi_mem; + } + alloc_sample_enabled_region->init_req(enabled_idx, alloc_sample_enabled_ctrl); + alloc_sample_enabled_region_phi_mem->init_req(enabled_idx, alloc_sample_enabled_mem); + } + + { + alloc_sample_enabled_region->init_req(disabled_idx, alloc_sample_disabled_ctrl); + alloc_sample_enabled_region_phi_mem->init_req(disabled_idx, alloc_sample_disabled_mem); + } + + { + fast_oop_ctrl = alloc_sample_enabled_region; + fast_oop_rawmem = alloc_sample_enabled_region_phi_mem; + } +} +#endif // INCLUDE_JFR // Helper for PhaseMacroExpand::expand_allocate_common. // Initializes the newly-allocated storage. diff --git a/src/share/vm/opto/macro.hpp b/src/share/vm/opto/macro.hpp index 0efe988ab..9268cf524 100644 --- a/src/share/vm/opto/macro.hpp +++ b/src/share/vm/opto/macro.hpp @@ -61,8 +61,12 @@ private: return n; } void set_eden_pointers(Node* &eden_top_adr, Node* &eden_end_adr); + Node* load( Node* ctl, Node* mem, Node* base, int offset, + const Type* value_type, BasicType bt, MemNode::MemOrd mo); Node* make_load( Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt); + Node* make_load_acquire( Node* ctl, Node* mem, Node* base, int offset, + const Type* value_type, BasicType bt); Node* make_store(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt); @@ -119,6 +123,10 @@ private: Node* old_eden_top, Node* new_eden_top, Node* length); + //JFR tracing + void jfr_sample_fast_object_allocation(AllocateNode* alloc, Node* fast_oop, + Node*& fast_oop_ctrl, Node*& fast_oop_rawmem); + public: PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn), _has_locks(false) { _igvn.set_delay_transform(true); diff --git a/src/share/vm/opto/runtime.cpp b/src/share/vm/opto/runtime.cpp index 4562dbcd5..8fd4dc9b5 100644 --- a/src/share/vm/opto/runtime.cpp +++ b/src/share/vm/opto/runtime.cpp @@ -68,6 +68,7 @@ #include "runtime/vframe_hp.hpp" #include "utilities/copy.hpp" #include "utilities/preserveException.hpp" +#include "gc_interface/allocTracer.inline.hpp" #if defined AD_MD_HPP # include AD_MD_HPP #elif defined TARGET_ARCH_MODEL_x86_32 @@ -260,8 +261,10 @@ JRT_BLOCK_ENTRY(void, OptoRuntime::new_instance_C(Klass* klass, JavaThread* thre if (!HAS_PENDING_EXCEPTION) { // Scavenge and allocate an instance. Handle holder(THREAD, klass->klass_holder()); // keep the klass alive + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_ENTER(false, THREAD);) oop result = InstanceKlass::cast(klass)->allocate_instance(THREAD); thread->set_vm_result(result); + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_LEAVE(false, THREAD);) // Pass oops back through thread local storage. Our apparent type to Java // is that we return an oop, but we can block on exit from this routine and @@ -290,6 +293,7 @@ JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_C(Klass* array_type, int len, JavaT // Scavenge and allocate an instance. oop result; + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);) if (array_type->oop_is_typeArray()) { // The oopFactory likes to work with the element type. // (We could bypass the oopFactory, since it doesn't add much value.) @@ -303,6 +307,7 @@ JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_C(Klass* array_type, int len, JavaT Klass* elem_type = ObjArrayKlass::cast(array_type)->element_klass(); result = oopFactory::new_objArray(elem_type, len, THREAD); } + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);) // Pass oops back through thread local storage. Our apparent type to Java // is that we return an oop, but we can block on exit from this routine and @@ -329,10 +334,12 @@ JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_nozero_C(Klass* array_type, int len // Scavenge and allocate an instance. oop result; + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);) assert(array_type->oop_is_typeArray(), "should be called only for type array"); // The oopFactory likes to work with the element type. BasicType elem_type = TypeArrayKlass::cast(array_type)->element_type(); result = oopFactory::new_typeArray_nozero(elem_type, len, THREAD); + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);) // Pass oops back through thread local storage. Our apparent type to Java // is that we return an oop, but we can block on exit from this routine and @@ -379,7 +386,9 @@ JRT_ENTRY(void, OptoRuntime::multianewarray2_C(Klass* elem_type, int len1, int l dims[0] = len1; dims[1] = len2; Handle holder(THREAD, elem_type->klass_holder()); // keep the klass alive + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);) oop obj = ArrayKlass::cast(elem_type)->multi_allocate(2, dims, THREAD); + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);) deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); thread->set_vm_result(obj); JRT_END @@ -396,7 +405,9 @@ JRT_ENTRY(void, OptoRuntime::multianewarray3_C(Klass* elem_type, int len1, int l dims[1] = len2; dims[2] = len3; Handle holder(THREAD, elem_type->klass_holder()); // keep the klass alive + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);) oop obj = ArrayKlass::cast(elem_type)->multi_allocate(3, dims, THREAD); + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);) deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); thread->set_vm_result(obj); JRT_END @@ -414,7 +425,9 @@ JRT_ENTRY(void, OptoRuntime::multianewarray4_C(Klass* elem_type, int len1, int l dims[2] = len3; dims[3] = len4; Handle holder(THREAD, elem_type->klass_holder()); // keep the klass alive + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);) oop obj = ArrayKlass::cast(elem_type)->multi_allocate(4, dims, THREAD); + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);) deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); thread->set_vm_result(obj); JRT_END @@ -451,7 +464,9 @@ JRT_ENTRY(void, OptoRuntime::multianewarrayN_C(Klass* elem_type, arrayOopDesc* d Copy::conjoint_jints_atomic(j_dims, c_dims, len); Handle holder(THREAD, elem_type->klass_holder()); // keep the klass alive + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);) oop obj = ArrayKlass::cast(elem_type)->multi_allocate(len, c_dims, THREAD); + JFR_ONLY(TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);) deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION); thread->set_vm_result(obj); JRT_END @@ -1677,3 +1692,34 @@ JRT_LEAF(void, OptoRuntime::zap_dead_native_locals_C(JavaThread* thread)) JRT_END # endif + +#if INCLUDE_JFR +//----------------------------------------------------------------------------- +// JFR support. +const TypeFunc *OptoRuntime::jfr_fast_object_alloc_Type() { + const Type **fields = TypeTuple::fields(3); + fields[TypeFunc::Parms+0] = TypeRawPtr::BOTTOM; // newly allocated object + fields[TypeFunc::Parms+1] = TypeInt::INT; // bci + fields[TypeFunc::Parms+2] = TypeRawPtr::BOTTOM; // tls + + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+3, fields); + + // create result type (range) + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = TypeRawPtr::BOTTOM; // returned oop + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1, fields); + + return TypeFunc::make(domain, range); +} + +void OptoRuntime::jfr_fast_object_alloc_C(oopDesc* obj, jint top_frame_bci, JavaThread* thread) { + KlassHandle kh(thread, obj->klass()); + assert(obj != NULL, "invariant"); + assert(obj->klass() != NULL, "invariant"); + thread->jfr_thread_local()->set_cached_top_frame_bci(top_frame_bci); + AllocTracer::send_opto_fast_allocation_event(kh, obj, obj->size() * HeapWordSize, thread); + thread->jfr_thread_local()->clear_cached_top_frame_bci(); + thread->set_vm_result(obj); +} +#endif // INCLUDE_JFR diff --git a/src/share/vm/opto/runtime.hpp b/src/share/vm/opto/runtime.hpp index 58c6bd501..48aea577e 100644 --- a/src/share/vm/opto/runtime.hpp +++ b/src/share/vm/opto/runtime.hpp @@ -179,6 +179,8 @@ public: static void complete_monitor_locking_C(oopDesc* obj, BasicLock* lock, JavaThread* thread); static void complete_monitor_unlocking_C(oopDesc* obj, BasicLock* lock); + // JFR support + static void jfr_fast_object_alloc_C(oopDesc* obj, jint bci, JavaThread* thread); private: // Implicit exception support @@ -339,6 +341,9 @@ private: static const TypeFunc* zap_dead_locals_Type(); # endif + // JFR support + static const TypeFunc* jfr_fast_object_alloc_Type(); + private: static NamedCounter * volatile _named_counters; diff --git a/src/share/vm/precompiled/precompiled.hpp b/src/share/vm/precompiled/precompiled.hpp index d7409fca2..986025843 100644 --- a/src/share/vm/precompiled/precompiled.hpp +++ b/src/share/vm/precompiled/precompiled.hpp @@ -97,6 +97,8 @@ # include "gc_interface/collectedHeap.hpp" # include "gc_interface/collectedHeap.inline.hpp" # include "gc_interface/gcCause.hpp" +# include "gc_interface/allocTracer.hpp" +# include "gc_interface/allocTracer.inline.hpp" # include "interpreter/abstractInterpreter.hpp" # include "interpreter/bytecode.hpp" # include "interpreter/bytecodeHistogram.hpp" diff --git a/src/share/vm/runtime/vframe.hpp b/src/share/vm/runtime/vframe.hpp index 2adaf841f..9ef651d01 100644 --- a/src/share/vm/runtime/vframe.hpp +++ b/src/share/vm/runtime/vframe.hpp @@ -367,6 +367,8 @@ class vframeStream : public vframeStreamCommon { } } + Thread *& thread_ref() { return (Thread *&)_thread; } + // top_frame may not be at safepoint, start with sender vframeStream(JavaThread* thread, frame top_frame, bool stop_at_java_call_stub = false); }; -- GitLab