提交 3471dab8 编写于 作者: 卓昂 提交者: D-D-H

[JFR] add support for opto object allocations sampling

Summary: add support for opto object allocations sampling

Test Plan: test/jdk/jdk/jfr/event/objectsprofiling/TestOptoObjectAllocationsSampling.java

Reviewers: kuaiwei, luchsh, sanhong

Issue: #7
上级 02618803
......@@ -22,7 +22,6 @@
*
*/
#include "precompiled.hpp"
#include "gc/shared/allocTracer.hpp"
#include "jfr/jfrEvents.hpp"
#include "runtime/handles.hpp"
......
......@@ -25,14 +25,22 @@
#ifndef SHARE_VM_GC_SHARED_ALLOCTRACER_HPP
#define SHARE_VM_GC_SHARED_ALLOCTRACER_HPP
#include "memory/allocation.hpp"
#include "runtime/handles.hpp"
#include "gc/shared/gcId.hpp"
#include "utilities/globalDefinitions.hpp"
class AllocTracer : AllStatic {
private:
static void send_opto_array_allocation_event(Klass* klass, oop obj,size_t alloc_size, Thread* thread);
static void send_opto_instance_allocation_event(Klass* klass, oop obj, Thread* thread);
public:
static void send_allocation_outside_tlab(Klass* klass, HeapWord* obj, size_t alloc_size, Thread* thread);
static void send_allocation_in_new_tlab(Klass* klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, Thread* thread);
static void send_allocation_requiring_gc_event(size_t size, uint 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(Klass* klass, oop obj,size_t alloc_size, Thread* thread);
static void send_opto_fast_allocation_event(Klass* klass, oop obj, size_t alloc_size, Thread* thread);
};
#endif // SHARE_VM_GC_SHARED_ALLOCTRACER_HPP
/*
* 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/shared/gcId.hpp"
#include "runtime/handles.hpp"
#include "utilities/globalDefinitions.hpp"
#include "gc/shared/allocTracer.hpp"
#include "jfr/jfrEvents.hpp"
typedef uintptr_t TraceAddress;
inline void AllocTracer::opto_slow_allocation_enter(bool is_array, Thread* thread) {
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);
}
}
}
inline void AllocTracer::opto_slow_allocation_leave(bool is_array, Thread* thread) {
#ifndef PRODUCT
if (JfrOptionSet::sample_object_allocations() && ObjectProfiler::enabled()) {
JfrThreadLocal* tl = thread->jfr_thread_local();
assert(!tl->has_cached_event_id(), "Invariant");
assert(tl->alloc_count_until_sample() >= tl ->alloc_count(), "Invariant");
}
#endif
}
inline void AllocTracer::send_opto_array_allocation_event(Klass* klass, oop obj, size_t alloc_size, Thread* thread) {
EventOptoArrayObjectAllocation event;
if (event.should_commit()) {
event.set_objectClass(klass);
event.set_address((TraceAddress)obj);
event.set_allocationSize(alloc_size);
event.commit();
}
}
inline void AllocTracer::send_opto_instance_allocation_event(Klass* klass, oop obj, Thread* thread) {
EventOptoInstanceObjectAllocation event;
if (event.should_commit()) {
event.set_objectClass(klass);
event.set_address((TraceAddress)obj);
event.commit();
}
}
inline void AllocTracer::send_slow_allocation_event(Klass* klass, oop obj, size_t alloc_size, Thread* thread) {
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();
}
}
}
inline void AllocTracer::send_opto_fast_allocation_event(Klass* klass, oop obj, size_t alloc_size, Thread* thread) {
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");
if (klass->is_array_klass()) {
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 /* SHARE_VM_GC_INTERFACE_ALLOCTRACER_INLINE_HPP */
......@@ -27,6 +27,7 @@
#include "gc/shared/gcCause.hpp"
#include "gc/shared/gcWhen.hpp"
#include "gc/shared/allocTracer.hpp"
#include "memory/allocation.hpp"
#include "runtime/handles.hpp"
#include "runtime/perfData.hpp"
......@@ -181,6 +182,15 @@ class CollectedHeap : public CHeapObj<mtInternal> {
Z
};
// Implicit Jfr inline methods.
static void trace_slow_allocation(Klass* 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(Klass* klass, HeapWord* obj, size_t alloc_size, Thread* thread) {
AllocTracer::send_allocation_outside_tlab(klass, obj, alloc_size, thread);
}
static inline size_t filler_array_max_size() {
return _filler_array_max_size;
}
......
......@@ -28,6 +28,8 @@
#include "gc/shared/collectedHeap.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/align.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/objectprofiler/objectProfiler.hpp"
inline HeapWord* CollectedHeap::align_allocation_or_fail(HeapWord* addr,
HeapWord* end,
......
......@@ -58,6 +58,7 @@ class MemAllocator::Allocation: StackObj {
void notify_allocation_jvmti_sampler();
void notify_allocation_low_memory_detector();
void notify_allocation_jfr_sampler();
void notify_allocation_jfr_object_profiling();
void notify_allocation_dtrace_sampler();
void check_for_bad_heap_word_value() const;
#ifdef ASSERT
......@@ -250,6 +251,11 @@ void MemAllocator::Allocation::notify_allocation_jfr_sampler() {
}
}
void MemAllocator::Allocation::notify_allocation_jfr_object_profiling() {
size_t size_in_bytes = _allocator._word_size * HeapWordSize;
CollectedHeap::trace_slow_allocation(_allocator._klass, obj(), size_in_bytes, _thread);
}
void MemAllocator::Allocation::notify_allocation_dtrace_sampler() {
if (DTraceAllocProbes) {
// support for Dtrace object alloc event (no-op most of the time)
......@@ -264,6 +270,7 @@ void MemAllocator::Allocation::notify_allocation_dtrace_sampler() {
void MemAllocator::Allocation::notify_allocation() {
notify_allocation_low_memory_detector();
notify_allocation_jfr_sampler();
notify_allocation_jfr_object_profiling();
notify_allocation_dtrace_sampler();
notify_allocation_jvmti_sampler();
}
......
......@@ -542,6 +542,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"),
......@@ -550,7 +551,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);
......@@ -560,6 +563,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() {
......@@ -590,6 +595,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);
......@@ -635,16 +642,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);
......@@ -654,6 +672,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);
......
......@@ -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<char*> _repository_path;
DCmdArgument<char*> _dump_path;
......@@ -150,6 +155,8 @@ class JfrConfigureFlightRecorderDCmd : public DCmdWithParser {
DCmdArgument<MemorySizeArgument> _memory_size;
DCmdArgument<MemorySizeArgument> _max_chunk_size;
DCmdArgument<bool> _sample_threads;
DCmdArgument<bool> _sample_object_allocations;
DCmdArgument<jlong> _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;
}
};
bool register_jfr_dcmds();
......
......@@ -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"
......@@ -111,6 +112,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))
......@@ -145,6 +155,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
......@@ -232,7 +250,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))
......
......@@ -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);
......
......@@ -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,
......
......@@ -70,7 +70,7 @@ void ObjectSampler::add(HeapWord* obj, size_t allocated, JavaThread* thread) {
traceid stack_trace_id = 0;
unsigned int stack_trace_hash = 0;
if (JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) {
stack_trace_id = JfrStackTraceRepository::record(thread, 0, &stack_trace_hash);
stack_trace_id = JfrStackTraceRepository::record(thread, 0, WALK_BY_DEFAULT, &stack_trace_hash);
thread->jfr_thread_local()->set_cached_stack_trace_id(stack_trace_id, stack_trace_hash);
}
......
......@@ -918,6 +918,17 @@
<Field type="ulong" name="value" label="Value" />
</Event>
<Event name="OptoInstanceObjectAllocation" category="Java Virtual Machine, Runtime" label="Opto instance object allocation" description="Allocation by Opto jitted method" thread="true" stackTrace="true" startTime="false">
<Field type="Class" name="objectClass" label="Object Class" description="Class of allocated instance object"/>
<Field type="ulong" contentType="address" name="address" label="Opto Instance Object Allocation Address" description="Address of allocated instance object"/>
</Event>
<Event name="OptoArrayObjectAllocation" category="Java Virtual Machine, Runtime" label="Opto array object allocation" description="Array Allocation by Opto jitted method" thread="true" stackTrace="true" startTime="false">
<Field type="Class" name="objectClass" label="Object Class" description="Class of allocated array object"/>
<Field type="ulong" contentType="address" name="address" label="Opto Array Object Allocation Address" description="Address of allocated instance object"/>
<Field type="ulong" name="allocationSize" label="Object Size" description="The Array Object Size" />
</Event>
<Type name="ZStatisticsCounterType" label="Z Statistics Counter">
<Field type="string" name="counter" label="Counter" />
</Type>
......@@ -948,6 +959,7 @@
<Field type="Symbol" name="name" label="Name" />
<Field type="Package" name="package" label="Package" />
<Field type="int" name="modifiers" label="Access Modifiers" />
<Field type="int" name="objectSize" label="Object Size"/>
</Type>
<Type name="ClassLoader" label="Java Class Loader">
......
/*
* 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 "runtime/vmThread.hpp"
#include "jfr/objectprofiler/objectProfiler.hpp"
#include "jfr/utilities/jfrTryLock.hpp"
#include "jfr/jfrEvents.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;
}
/*
* 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 "jni.h"
#define ARRAY_OBJECT_SIZE_PLACE_HOLDER 0x1111baba
#if INCLUDE_JFR
#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_LOCAL_OFFSET_JFR)
#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_LOCAL_OFFSET_JFR)
#else
#define TRACE_OPTO_SLOW_ALLOCATION_ENTER(is_array, thread)
#define TRACE_OPTO_SLOW_ALLOCATION_LEAVE(is_array, thread)
#define TRACE_SLOW_ALLOCATION(klass, obj, alloc_size, thread)
#define TRACE_DEFINE_THREAD_ALLOC_COUNT_UNTIL_SAMPLE_OFFSET
#define TRACE_THREAD_ALLOC_COUNT_UNTIL_SAMPLE_OFFSET
#define TRACE_DEFINE_THREAD_ALLOC_COUNT_OFFSET
#define TRACE_THREAD_ALLOC_COUNT_OFFSET
#endif
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
......@@ -39,6 +39,7 @@
#include "jfr/recorder/storage/jfrBuffer.hpp"
#include "jfr/utilities/jfrHashtable.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "jfr/objectprofiler/objectProfiler.hpp"
#include "memory/iterator.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceKlass.hpp"
......@@ -146,6 +147,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->is_array_klass()) {
// 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->is_instance_klass(), "invariant");
jint instanceSize = ((InstanceKlass*) klass)->size_helper() * HeapWordSize;
writer->write((s4)instanceSize);
}
return 1;
}
......
......@@ -23,7 +23,9 @@
*/
#include "precompiled.hpp"
#include "jfrfiles/jfrEventIds.hpp"
#include "jfr/recorder/jfrEventSetting.inline.hpp"
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
JfrNativeSettings JfrEventSetting::_jvm_event_settings;
......@@ -53,6 +55,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) {
......
......@@ -27,6 +27,7 @@
#include "jni.h"
#include "jfr/utilities/jfrAllocation.hpp"
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
#include "jfrfiles/jfrEventControl.hpp"
//
......@@ -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);)
};
......
......@@ -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<traceid>(0);
......
......@@ -37,6 +37,7 @@
#include "services/diagnosticFramework.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/ostream.hpp"
#include "utilities/debug.hpp"
struct ObsoleteOption {
const char* name;
......@@ -160,6 +161,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";
......@@ -172,6 +189,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<char*> _dcmd_repository(
......@@ -253,6 +273,20 @@ static DCmdArgument<bool> _dcmd_retransform(
true,
default_retransform);
static DCmdArgument<bool> _dcmd_sampleobjectallocations(
"sampleobjectallocations",
"If object allocations should be sampled (by default false)",
"BOOLEAN",
false,
default_sample_object_allocations);
static DCmdArgument<jlong> _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() {
......@@ -267,6 +301,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) {
......@@ -312,6 +348,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();
......@@ -333,6 +372,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) {
......@@ -366,6 +406,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) {
......
......@@ -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);
......
......@@ -167,7 +167,7 @@ traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) {
return instance().add_trace(stacktrace);
}
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");
......@@ -184,10 +184,10 @@ 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(Thread* thread, int skip, unsigned int* hash) {
traceid JfrStackTraceRepository::record(Thread* thread, int skip, StackWalkMode mode, unsigned int* hash) {
assert(thread == Thread::current(), "invariant");
JfrThreadLocal* const tl = thread->jfr_thread_local();
assert(tl != NULL, "invariant");
......@@ -206,12 +206,12 @@ traceid JfrStackTraceRepository::record(Thread* thread, int skip, unsigned int*
}
assert(frames != NULL, "invariant");
assert(tl->stackframes() == frames, "invariant");
return instance().record_for((JavaThread*)thread, skip, frames, tl->stackdepth(), hash);
return instance().record_for((JavaThread*)thread, skip, mode, frames, tl->stackdepth(), hash);
}
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);
if (!stacktrace.record_safe(thread, skip)) {
if (!stacktrace.record_safe(thread, skip, false, mode)) {
return 0;
}
traceid tid = add(stacktrace);
......@@ -222,10 +222,10 @@ traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrSta
return tid;
}
traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrStackFrame *frames, u4 max_frames, unsigned int* hash) {
traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, StackWalkMode mode, JfrStackFrame *frames, u4 max_frames, unsigned int* hash) {
assert(hash != NULL && *hash == 0, "invariant");
JfrStackTrace stacktrace(frames, max_frames);
if (!stacktrace.record_safe(thread, skip, true)) {
if (!stacktrace.record_safe(thread, skip, true, mode)) {
return 0;
}
traceid tid = add(stacktrace);
......@@ -382,16 +382,45 @@ void JfrStackTrace::resolve_linenos() {
_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();
}
......@@ -406,8 +435,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);
......
......@@ -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();
};
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,8 @@ class JfrStackTrace : public StackObj {
bool _reached_root;
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 +93,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();
void set_nr_of_frames(u4 nr_of_frames) { _nr_of_frames = nr_of_frames; }
void set_hash(unsigned int hash) { _hash = hash; }
......@@ -129,8 +141,8 @@ class JfrStackTraceRepository : public JfrCHeapObj {
u4 _entries;
size_t write_impl(JfrChunkWriter& cw, bool clear);
traceid record_for(JavaThread* thread, int skip, JfrStackFrame* frames, u4 max_frames);
traceid record_for(JavaThread* thread, int skip, JfrStackFrame* frames, u4 max_frames, unsigned int* hash);
traceid record_for(JavaThread* thread, int skip, StackWalkMode mode, JfrStackFrame* frames, u4 max_frames);
traceid record_for(JavaThread* thread, int skip, StackWalkMode mode, JfrStackFrame* frames, u4 max_frames, unsigned int* hash);
traceid add_trace(const JfrStackTrace& stacktrace);
const StackTrace* resolve_entry(unsigned int hash, traceid id) const;
......@@ -143,8 +155,8 @@ class JfrStackTraceRepository : public JfrCHeapObj {
bool initialize();
static void destroy();
static traceid add(const JfrStackTrace& stacktrace);
static traceid record(Thread* thread, int skip = 0);
static traceid record(Thread* thread, int skip, unsigned int* hash);
static traceid record(Thread* thread, int skip, StackWalkMode mode);
static traceid record(Thread* thread, int skip, StackWalkMode mode, unsigned int* hash);
traceid write(JfrCheckpointWriter& cpw, traceid id, unsigned int hash);
size_t write(JfrChunkWriter& cw, bool clear);
size_t clear();
......
......@@ -71,12 +71,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;
}
......
......@@ -26,6 +26,8 @@
#define SHARE_VM_JFR_SUPPORT_JFRFLUSH_HPP
#include "jfr/recorder/storage/jfrBuffer.hpp"
#include "jfr/recorder/jfrEventSetting.hpp"
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "memory/allocation.hpp"
......@@ -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 <typename Event>
......@@ -64,7 +66,7 @@ class JfrConditionalFlushWithStacktrace : public JfrConditionalFlush<Event> {
public:
JfrConditionalFlushWithStacktrace(Thread* t) : JfrConditionalFlush<Event>(t), _t(t), _owner(false) {
if (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() {
......
......@@ -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));
}
}
......
......@@ -53,7 +53,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;
......
......@@ -26,6 +26,7 @@
#define SHARE_VM_JFR_SUPPORT_JFRTHREADLOCAL_HPP
#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp"
#include "jfr/objectprofiler/objectProfiler.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "utilities/sizes.hpp"
......@@ -51,6 +52,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;
......@@ -193,6 +215,54 @@ class JfrThreadLocal {
_wallclock_time = wallclock_time;
}
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;
}
traceid trace_id() const {
return _trace_id;
}
......@@ -227,6 +297,9 @@ class JfrThreadLocal {
static ByteSize java_event_writer_offset() {
return in_ByteSize(offset_of(JfrThreadLocal, _java_event_writer));
}
TRACE_DEFINE_THREAD_ALLOC_COUNT_UNTIL_SAMPLE_OFFSET;
TRACE_DEFINE_THREAD_ALLOC_COUNT_OFFSET;
};
#endif // SHARE_VM_JFR_SUPPORT_JFRTHREADLOCAL_HPP
......@@ -27,6 +27,7 @@
#include "gc/shared/collectedHeap.inline.hpp"
#include "libadt/vectset.hpp"
#include "opto/addnode.hpp"
#include "opto/divnode.hpp"
#include "opto/arraycopynode.hpp"
#include "opto/callnode.hpp"
#include "opto/castnode.hpp"
......@@ -50,6 +51,7 @@
#if INCLUDE_G1GC
#include "gc/g1/g1ThreadLocalData.hpp"
#endif // INCLUDE_G1GC
#include "jfr/objectprofiler/objectProfiler.hpp"
//
......@@ -1161,14 +1163,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);
......@@ -1520,6 +1529,10 @@ void PhaseMacroExpand::expand_allocate_common(
}
}
if (JfrOptionSet::sample_object_allocations()) {
jfr_sample_fast_object_allocation(alloc, fast_oop, fast_oop_ctrl, fast_oop_rawmem);
}
if (C->env()->dtrace_extended_probes()) {
// Slow-path call
int size = TypeFunc::Parms + 2;
......@@ -1705,6 +1718,115 @@ void PhaseMacroExpand::expand_allocate_common(
// This completes all paths into the result merge point
}
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->trace_data()->alloc_count();
// long alloc_count_new = alloc_count + 1;
// thread->trace_data()->set_alloc_count(alloc_count_new);
// long alloc_count_until_sample = thread->trace_data()->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 ThreadLocalNode());
Node* alloc_sample_enabled_addr = transform_later(ConPNode::make((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 CmpINode(alloc_sample_enabled, intcon(1)));
Node* alloc_sample_enabled_bool = transform_later(new BoolNode(alloc_sample_enabled_cmp, BoolTest::eq));
IfNode* alloc_sample_enabled_if = (IfNode*)transform_later(new IfNode(fast_oop_ctrl, alloc_sample_enabled_bool, PROB_MIN, COUNT_UNKNOWN));
Node* alloc_sample_enabled_ctrl = transform_later(new IfTrueNode(alloc_sample_enabled_if));
Node* alloc_sample_enabled_mem = fast_oop_rawmem;
Node* alloc_sample_disabled_ctrl = transform_later(new IfFalseNode(alloc_sample_enabled_if));
Node* alloc_sample_disabled_mem = fast_oop_rawmem;
Node* alloc_sample_enabled_region = transform_later(new RegionNode(3));
Node* alloc_sample_enabled_region_phi_mem = transform_later(new 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 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 CmpLNode(alloc_count_until_sample, alloc_count_new));
Node* alloc_sample_hit_bool = transform_later(new BoolNode(alloc_count_until_sample_cmp, BoolTest::eq));
IfNode* alloc_sample_hit_if = (IfNode*)transform_later(new IfNode(alloc_sample_enabled_ctrl, alloc_sample_hit_bool, PROB_MIN, COUNT_UNKNOWN));
Node* alloc_sample_hit_ctrl = transform_later(new IfTrueNode(alloc_sample_hit_if));
Node* alloc_sample_hit_mem = alloc_sample_enabled_mem;
Node* alloc_sample_miss_ctrl = transform_later(new IfFalseNode(alloc_sample_hit_if));
Node* alloc_sample_miss_mem = alloc_sample_enabled_mem;
Node* alloc_sample_hit_region = transform_later(new RegionNode(3));
Node* alloc_sample_hit_region_phi_mem = transform_later(new PhiNode(alloc_sample_hit_region, Type::MEMORY, TypeRawPtr::BOTTOM));
// if sample_hit then
{
CallLeafNode *call = new 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 ProjNode(call,TypeFunc::Control);
transform_later(alloc_sample_hit_ctrl);
alloc_sample_hit_mem = new 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;
}
}
// Helper for PhaseMacroExpand::expand_allocate_common.
// Initializes the newly-allocated storage.
......
......@@ -58,8 +58,12 @@ public:
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);
......@@ -198,6 +202,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);
Node* make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type *ftype, AllocateNode *alloc);
public:
......
......@@ -73,6 +73,7 @@
#include "runtime/vframe_hp.hpp"
#include "utilities/copy.hpp"
#include "utilities/preserveException.hpp"
#include "gc/shared/allocTracer.inline.hpp"
// For debugging purposes:
......@@ -212,8 +213,11 @@ 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
TRACE_OPTO_SLOW_ALLOCATION_ENTER(false, THREAD);
oop result = InstanceKlass::cast(klass)->allocate_instance(THREAD);
thread->set_vm_result(result);
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
......@@ -240,6 +244,7 @@ JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_C(Klass* array_type, int len, JavaT
// Scavenge and allocate an instance.
oop result;
TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);
if (array_type->is_typeArray_klass()) {
// The oopFactory likes to work with the element type.
// (We could bypass the oopFactory, since it doesn't add much value.)
......@@ -253,6 +258,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);
}
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
......@@ -277,10 +283,12 @@ JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_nozero_C(Klass* array_type, int len
// Scavenge and allocate an instance.
oop result;
TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);
assert(array_type->is_typeArray_klass(), "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);
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
......@@ -326,7 +334,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
TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);
oop obj = ArrayKlass::cast(elem_type)->multi_allocate(2, dims, THREAD);
TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);
deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION);
thread->set_vm_result(obj);
JRT_END
......@@ -343,7 +353,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
TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);
oop obj = ArrayKlass::cast(elem_type)->multi_allocate(3, dims, THREAD);
TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);
deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION);
thread->set_vm_result(obj);
JRT_END
......@@ -361,7 +373,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
TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);
oop obj = ArrayKlass::cast(elem_type)->multi_allocate(4, dims, THREAD);
TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);
deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION);
thread->set_vm_result(obj);
JRT_END
......@@ -380,7 +394,9 @@ JRT_ENTRY(void, OptoRuntime::multianewarray5_C(Klass* elem_type, int len1, int l
dims[3] = len4;
dims[4] = len5;
Handle holder(THREAD, elem_type->klass_holder()); // keep the klass alive
TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);
oop obj = ArrayKlass::cast(elem_type)->multi_allocate(5, dims, THREAD);
TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);
deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION);
thread->set_vm_result(obj);
JRT_END
......@@ -398,7 +414,9 @@ JRT_ENTRY(void, OptoRuntime::multianewarrayN_C(Klass* elem_type, arrayOopDesc* d
c_dims, len);
Handle holder(THREAD, elem_type->klass_holder()); // keep the klass alive
TRACE_OPTO_SLOW_ALLOCATION_ENTER(true, THREAD);
oop obj = ArrayKlass::cast(elem_type)->multi_allocate(len, c_dims, THREAD);
TRACE_OPTO_SLOW_ALLOCATION_LEAVE(true, THREAD);
deoptimize_caller_frame(thread, HAS_PENDING_EXCEPTION);
thread->set_vm_result(obj);
JRT_END
......@@ -1658,3 +1676,30 @@ static void trace_exception(outputStream* st, oop exception_oop, address excepti
st->print_raw_cr(tempst.as_string());
}
// 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) {
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(obj->klass(), obj, obj->size() * HeapWordSize, thread);
thread->jfr_thread_local()->clear_cached_top_frame_bci();
thread->set_vm_result(obj);
}
......@@ -177,6 +177,8 @@ public:
static void monitor_notify_C(oopDesc* obj, JavaThread* thread);
static void monitor_notifyAll_C(oopDesc* obj, JavaThread* thread);
// JFR support
static void jfr_fast_object_alloc_C(oopDesc* obj, jint bci, JavaThread* thread);
private:
// Implicit exception support
......@@ -318,6 +320,9 @@ private:
static const TypeFunc* dtrace_method_entry_exit_Type();
static const TypeFunc* dtrace_object_alloc_Type();
// JFR support
static const TypeFunc* jfr_fast_object_alloc_Type();
private:
static NamedCounter * volatile _named_counters;
......
......@@ -93,6 +93,8 @@
# include "gc/shared/collectorCounters.hpp"
# include "gc/shared/collectorPolicy.hpp"
# include "gc/shared/gcCause.hpp"
# include "gc/shared/allocTracer.hpp"
# include "gc/shared/allocTracer.inline.hpp"
# include "gc/shared/gcLocker.hpp"
# include "gc/shared/gcStats.hpp"
# include "gc/shared/gcUtil.hpp"
......
......@@ -346,6 +346,10 @@ class vframeStream : public vframeStreamCommon {
// top_frame may not be at safepoint, start with sender
vframeStream(JavaThread* thread, frame top_frame, bool stop_at_java_call_stub = false);
Thread *& thread_ref() {
return (Thread *&)_thread;
}
};
#endif // SHARE_VM_RUNTIME_VFRAME_HPP
/*
* 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.
*
*/
package jdk.jfr;
/**
* A constant class for JFR Event, contains the names of some events used by jdk.
*/
public class EventNames {
private final static String PREFIX = "jdk.";
public final static String OptoInstanceObjectAllocation = PREFIX + "OptoInstanceObjectAllocation";
public final static String OptoArrayObjectAllocation = PREFIX + "OptoArrayObjectAllocation";
}
......@@ -673,4 +673,7 @@ public final class Recording implements Closeable {
internal.setSetting(id, value);
}
public boolean isRecorderEnabled(String eventName) {
return internal.isRecorderEnabled(eventName);
}
}
......@@ -100,4 +100,27 @@ public final class RecordedClass extends RecordedObject {
public long getId() {
return uniqueId;
}
/**
* Returns the object size for the class.
* <p>
* The object size for instance class is accurate. But for the array class, it
* is a magic code 0x1111baba. The array object size can not be determined statically
* from the JVM klass information because array object size is affected by
* actual element length.
*
* @return the object size (instance object), or magic code 0x1111baba (array object)
*/
public int getObjectSize() {
return getTyped("objectSize", Integer.class, -1);
}
/**
* Checks whether the class is for instance or array.
*
* @return true or false
*/
public boolean isArray() {
return getName().startsWith("[");
}
}
......@@ -292,6 +292,20 @@ public final class JVM {
*/
public native void setSampleThreads(boolean sampleThreads) throws IllegalStateException;
/**
* Turn on/off objects allocations sampling.
*
* @param sampleAllocations true if object allocations should be sampled, false otherwise.
*/
public native void setSampleObjectAllocations(boolean sampleAllocations);
/**
* Set object allocations sampling interval.
*
* @param interval
*/
public native void setObjectAllocationsSamplingInterval(long interval) throws IllegalArgumentException;
/**
* Turn on/off compressed integers.
*
......
......@@ -49,6 +49,8 @@ public final class Options {
private static final boolean DEFAULT_SAMPLE_THREADS = true;
private static final long DEFAULT_MAX_CHUNK_SIZE = 12 * 1024 * 1024;
private static final SafePath DEFAULT_DUMP_PATH = SecuritySupport.USER_HOME;
private static final boolean DEFAULT_SAMPLE_OBJECT_ALLOCATIONS = false;
private static final long DEFAULT_OBJECT_ALLOCATIONS_SAMPLING_INTERVAL = 1024;
private static long memorySize;
private static long globalBufferSize;
......@@ -58,6 +60,8 @@ public final class Options {
private static boolean sampleThreads;
private static long maxChunkSize;
private static SafePath dumpPath;
private static boolean sampleObjectAllocations;
private static long objectAllocationsSamplingInterval;
static {
final long pageSize = Unsafe.getUnsafe().pageSize();
......@@ -139,6 +143,27 @@ public final class Options {
return sampleThreads;
}
public static synchronized void setSampleObjectAllocations(Boolean sample) {
jvm.setSampleObjectAllocations(sample);
sampleObjectAllocations = sample;
}
public static synchronized boolean getSampleObjectAllocations() {
return sampleObjectAllocations;
}
public static synchronized void setObjectAllocationsSamplingInterval(Long interval) {
if (interval <= 0) {
throw new IllegalArgumentException("interval should be greater than 0");
}
jvm.setObjectAllocationsSamplingInterval(interval);
objectAllocationsSamplingInterval = interval;
}
public static synchronized long getObjectAllocationsSamplingInterval() {
return objectAllocationsSamplingInterval;
}
private static synchronized void reset() {
setMaxChunkSize(DEFAULT_MAX_CHUNK_SIZE);
setMemorySize(DEFAULT_MEMORY_SIZE);
......@@ -148,6 +173,8 @@ public final class Options {
setSampleThreads(DEFAULT_SAMPLE_THREADS);
setStackDepth(DEFAULT_STACK_DEPTH);
setThreadBufferSize(DEFAULT_THREAD_BUFFER_SIZE);
setSampleObjectAllocations(DEFAULT_SAMPLE_OBJECT_ALLOCATIONS);
setObjectAllocationsSamplingInterval(DEFAULT_OBJECT_ALLOCATIONS_SAMPLING_INTERVAL);
}
static synchronized long getWaitInterval() {
......
......@@ -550,4 +550,8 @@ public final class PlatformRecorder {
target.setStopTime(endTime);
target.setInternalDuration(Duration.between(startTime, endTime));
}
public boolean isEnabled(String eventName) {
return MetadataRepository.getInstance().isEnabled(eventName);
}
}
......@@ -774,4 +774,8 @@ public final class PlatformRecording implements AutoCloseable {
public SafePath getDumpOnExitDirectory() {
return this.dumpOnExitDirectory;
}
public boolean isRecorderEnabled(String eventName) {
return recorder.isEnabled(eventName);
}
}
......@@ -43,6 +43,7 @@ final class DCmdConfigure extends AbstractDCmd {
/**
* Execute JFR.configure.
*
* @param onVMStart if cmd is invoked at the moment when vm is started
* @param repositoryPath the path
* @param dumpPath path to dump to on fatal error (oom)
* @param stackDepth depth of stack traces
......@@ -51,6 +52,8 @@ final class DCmdConfigure extends AbstractDCmd {
* @param threadBufferSize size of thread buffer for events
* @param maxChunkSize threshold at which a new chunk is created in the disk repository
* @param sampleThreads if thread sampling should be enabled
* @param sampleObjectAllocations if object allocations sampling should be enbaled
* @param objectAllocationsSamplingInterval interval of object allocations samplings
*
* @return result
......@@ -59,6 +62,7 @@ final class DCmdConfigure extends AbstractDCmd {
*/
public String execute
(
Boolean onVMStart,
String repositoryPath,
String dumpPath,
Integer stackDepth,
......@@ -67,9 +71,18 @@ final class DCmdConfigure extends AbstractDCmd {
Long threadBufferSize,
Long memorySize,
Long maxChunkSize,
Boolean sampleThreads
Boolean sampleThreads,
Boolean sampleObjectAllocations,
Long objectAllocationsSamplingInterval
) throws DCmdException {
// Check parameters correctness
if (!onVMStart) {
if (sampleObjectAllocations != null || objectAllocationsSamplingInterval != null) {
Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not change sampleObjectAllocations and objectAllocationsSamplingInterval during application's running");
return getResult();
}
}
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdConfigure: repositorypath=" + repositoryPath +
", dumppath=" + dumpPath +
......@@ -79,7 +92,9 @@ final class DCmdConfigure extends AbstractDCmd {
", thread_buffer_size" + threadBufferSize +
", memorysize" + memorySize +
", maxchunksize=" + maxChunkSize +
", samplethreads" + sampleThreads);
", samplethreads" + sampleThreads +
", sampleObjectAllocations=" + sampleObjectAllocations +
", objectAllocationsSamplingInterval=" + objectAllocationsSamplingInterval);
}
......@@ -152,6 +167,20 @@ final class DCmdConfigure extends AbstractDCmd {
updated = true;
}
if (objectAllocationsSamplingInterval != null) {
Options.setObjectAllocationsSamplingInterval(objectAllocationsSamplingInterval);
Logger.log(LogTag.JFR, LogLevel.INFO, "object allocations sampling interval set to " + objectAllocationsSamplingInterval);
printObjectAllocationsSamplingInterval();
updated = true;
}
if (sampleObjectAllocations != null) {
Options.setSampleObjectAllocations(sampleObjectAllocations);
Logger.log(LogTag.JFR, LogLevel.INFO, "Sample object allocations set to " + sampleObjectAllocations);
printSampleObjectAllocations();
updated = true;
}
if (!updated) {
println("Current configuration:");
println();
......@@ -163,6 +192,8 @@ final class DCmdConfigure extends AbstractDCmd {
printMemorySize();
printMaxChunkSize();
printSampleThreads();
printSampleObjectAllocations();
printObjectAllocationsSamplingInterval();
}
return getResult();
}
......@@ -214,4 +245,12 @@ final class DCmdConfigure extends AbstractDCmd {
printBytes(Options.getMaxChunkSize());
println();
}
private void printSampleObjectAllocations() {
println("Sample object allocations: " + Options.getSampleObjectAllocations());
}
private void printObjectAllocationsSamplingInterval() {
println("objects allocations sampling interval: " + Options.getObjectAllocationsSamplingInterval());
}
}
......@@ -35,9 +35,11 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import jdk.jfr.EventNames;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Recording;
import jdk.jfr.internal.JVM;
import jdk.jfr.internal.Options;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
......@@ -111,7 +113,7 @@ final class DCmdStart extends AbstractDCmd {
try {
s.putAll(JFC.createKnown(configName).getSettings());
} catch (IOException | ParseException e) {
throw new DCmdException("Could not parse setting " + settings[0], e);
throw new DCmdException("Could not parse setting " + configName, e);
}
}
......@@ -146,6 +148,14 @@ final class DCmdStart extends AbstractDCmd {
recording.setSettings(s);
SafePath safePath = null;
if (recording.isRecorderEnabled(EventNames.OptoInstanceObjectAllocation) ||
recording.isRecorderEnabled(EventNames.OptoArrayObjectAllocation)) {
if (!Options.getSampleObjectAllocations()) {
println("Please add -XX:FlightRecorderOptions=sampleobjectallocations=true in JVM options.");
return getResult();
}
}
if (path != null) {
try {
if (dumpOnExit == null) {
......
/*
* 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.
*
*/
package jfr.event.objectsprofiling;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.TimeUnit;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedStackTrace;
import jdk.jfr.consumer.RecordedFrame;
import sun.hotspot.WhiteBox;
import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.EventNames;
import jdk.test.lib.jfr.Events;
import jdk.test.lib.Utils;
/*
* @test TestOptoObjectAllocationsSampling
* @library /test/lib
*
* @build sun.hotspot.WhiteBox
* @build ClassFileInstaller
*
* @compile -g TestOptoObjectAllocationsSampling.java
*
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-TieredCompilation
* -XX:FlightRecorderOptions=sampleobjectallocations=true,objectallocationssamplinginterval=1
* jfr.event.objectsprofiling.TestOptoObjectAllocationsSampling
*/
public class TestOptoObjectAllocationsSampling {
private static final WhiteBox WB;
private static final Method OPTO_METHOD;
private static final int OPTO_METHOD_INVOKE_COUNT = 10;
private static final String OPTO_METHOD_NAME = "fireOptoAllocations";
private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
private static final int RECORDED_ARRAY_CLASS_OBJECT_SIZE_MAGIC_CODE = 0x1111baba;
static {
try {
WB = WhiteBox.getWhiteBox();
Class<?> thisClass = TestOptoObjectAllocationsSampling.class;
OPTO_METHOD = thisClass.getMethod(OPTO_METHOD_NAME, Integer.TYPE, Byte.TYPE);
} catch (Exception e) {
Asserts.fail(e.getMessage());
throw new ExceptionInInitializerError(e);
}
}
private static class InstanceObject {
private byte content;
InstanceObject(byte content) {
this.content = content;
}
public String toString() {
return "InstanceObject ( " + content + " )";
}
}
public static Object array;
public static Object instance;
public static void fireOptoAllocations(int arrayLength, byte b) {
array = new InstanceObject[arrayLength];
instance = new InstanceObject(b);
}
private static void ensureOptoMethod() throws Exception {
int initialCompLevel = WB.getMethodCompilationLevel(OPTO_METHOD);
if (initialCompLevel != COMP_LEVEL_FULL_OPTIMIZATION) {
WB.enqueueMethodForCompilation(OPTO_METHOD, COMP_LEVEL_FULL_OPTIMIZATION);
}
Utils.waitForCondition(() -> COMP_LEVEL_FULL_OPTIMIZATION == WB.getMethodCompilationLevel(OPTO_METHOD), 30_000L);
System.out.format("%s is already compiled at full optimization compile level\n", OPTO_METHOD_NAME);
}
private static void assertOptoMethod() throws Exception {
int compLevel = WB.getMethodCompilationLevel(OPTO_METHOD);
Asserts.assertTrue(compLevel == COMP_LEVEL_FULL_OPTIMIZATION);
}
private static void runForLong(int arg1, byte arg2) {
for (int i = 0; i < 1000_000; i++) {
try {
ensureOptoMethod();
fireOptoAllocations(arg1, arg2);
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
}
}
}
public static void main(String[] args) throws Exception {
int arg1 = 3*1024;
byte arg2 = (byte)0x66;
// Run warm code to prevent deoptimizaiton.
// It will deoptimize if it runs without this warmup step.
fireOptoAllocations(arg1, arg2);
ensureOptoMethod();
assertOptoMethod();
try (Recording recording = new Recording()) {
recording.enable(EventNames.OptoArrayObjectAllocation);
recording.enable(EventNames.OptoInstanceObjectAllocation);
recording.start();
// fireOptoAllocationsMethod would be deoptimized after jfr recording is started.
// The root cause is not clear.
// Invoke ensureOptoMethod blindly to enforce it in level 4 again.
ensureOptoMethod();
for (int i = 0; i < OPTO_METHOD_INVOKE_COUNT; i++) {
assertOptoMethod();
fireOptoAllocations(arg1, arg2);
WB.youngGC();
}
recording.stop();
List<RecordedEvent> events = Events.fromRecording(recording);
int countOfInstanceObject = 0;
int countOfArrayObject = 0;
final String instanceObjectClassName = InstanceObject.class.getName();
final String arrayObjectClassName = InstanceObject[].class.getName();
for (RecordedEvent event : events) {
RecordedStackTrace stackTrace = event.getStackTrace();
Asserts.assertTrue(stackTrace != null);
List<RecordedFrame> frames = stackTrace.getFrames();
Asserts.assertTrue(frames != null && frames.size() > 0);
RecordedFrame topFrame = frames.get(0);
Asserts.assertTrue(event.hasField("objectClass"));
RecordedClass clazz = event.getValue("objectClass");
String className = clazz.getName();
Asserts.assertTrue(event.getStackTrace().getFrames().size() > 0);
int objectSize = clazz.getObjectSize();
System.out.format("Allocation Object Class Name: %s, Object Size: %x, topFrame: %s\n", className, objectSize, topFrame.getMethod());
if (className.equals(instanceObjectClassName)) {
Asserts.assertTrue(!clazz.isArray());
Asserts.assertTrue(objectSize > 0);
Asserts.assertTrue(topFrame.getLineNumber() > 0);
Asserts.assertTrue(topFrame.getBytecodeIndex() > 0);
countOfInstanceObject++;
} else if (className.equals(arrayObjectClassName)) {
Asserts.assertTrue(clazz.isArray());
Asserts.assertTrue(objectSize == RECORDED_ARRAY_CLASS_OBJECT_SIZE_MAGIC_CODE);
countOfArrayObject++;
Asserts.assertTrue(topFrame.getLineNumber() > 0);
Asserts.assertTrue(topFrame.getBytecodeIndex() > 0);
}
}
System.out.format("Total Event Count: %d, EventOptoInstanceObjectAllocaiton Count: %d, EventOptoArrayObjectAllocation Count: %d\n", events.size(), countOfInstanceObject, countOfArrayObject);
Asserts.assertTrue(countOfArrayObject == countOfInstanceObject);
Asserts.assertTrue(countOfArrayObject == OPTO_METHOD_INVOKE_COUNT);
Asserts.assertTrue(events.size() >= (countOfInstanceObject + countOfArrayObject));
}
}
}
......@@ -179,6 +179,8 @@ public class EventNames {
public final static String CPUTimeStampCounter = PREFIX + "CPUTimeStampCounter";
public final static String ActiveRecording = PREFIX + "ActiveRecording";
public final static String ActiveSetting = PREFIX + "ActiveSetting";
public final static String OptoInstanceObjectAllocation = PREFIX + "OptoInstanceObjectAllocation";
public final static String OptoArrayObjectAllocation = PREFIX + "OptoArrayObjectAllocation";
public static boolean isGcEvent(EventType et) {
return et.getCategoryNames().contains(GC_CATEGORY);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册