8226511: Implement JFR Event Streaming
Co-authored-by: Erik Gahlin <erik.gahlin@oracle.com> Co-authored-by: Mikhailo Seledtsov <mikhailo.seledtsov@oracle.com> Reviewed-by: egahlin, mseledtsov, mgronlun
This commit is contained in:
parent
061b0a6637
commit
8addc1418a
@ -59,10 +59,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void register_jfr_type_constants() {
|
static void register_jfr_type_constants() {
|
||||||
JfrSerializer::register_serializer(TYPE_G1HEAPREGIONTYPE, false, true,
|
JfrSerializer::register_serializer(TYPE_G1HEAPREGIONTYPE, true,
|
||||||
new G1HeapRegionTypeConstant());
|
new G1HeapRegionTypeConstant());
|
||||||
|
|
||||||
JfrSerializer::register_serializer(TYPE_G1YCTYPE, false, true,
|
JfrSerializer::register_serializer(TYPE_G1YCTYPE, true,
|
||||||
new G1YCTypeConstant());
|
new G1YCTypeConstant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,6 @@ public:
|
|||||||
|
|
||||||
void ShenandoahJFRSupport::register_jfr_type_serializers() {
|
void ShenandoahJFRSupport::register_jfr_type_serializers() {
|
||||||
JfrSerializer::register_serializer(TYPE_SHENANDOAHHEAPREGIONSTATE,
|
JfrSerializer::register_serializer(TYPE_SHENANDOAHHEAPREGIONSTATE,
|
||||||
false,
|
|
||||||
true,
|
true,
|
||||||
new ShenandoahHeapRegionStateConstant());
|
new ShenandoahHeapRegionStateConstant());
|
||||||
}
|
}
|
||||||
|
@ -59,11 +59,9 @@ public:
|
|||||||
|
|
||||||
static void register_jfr_type_serializers() {
|
static void register_jfr_type_serializers() {
|
||||||
JfrSerializer::register_serializer(TYPE_ZSTATISTICSCOUNTERTYPE,
|
JfrSerializer::register_serializer(TYPE_ZSTATISTICSCOUNTERTYPE,
|
||||||
false /* require_safepoint */,
|
|
||||||
true /* permit_cache */,
|
true /* permit_cache */,
|
||||||
new ZStatisticsCounterTypeConstant());
|
new ZStatisticsCounterTypeConstant());
|
||||||
JfrSerializer::register_serializer(TYPE_ZSTATISTICSSAMPLERTYPE,
|
JfrSerializer::register_serializer(TYPE_ZSTATISTICSSAMPLERTYPE,
|
||||||
false /* require_safepoint */,
|
|
||||||
true /* permit_cache */,
|
true /* permit_cache */,
|
||||||
new ZStatisticsSamplerTypeConstant());
|
new ZStatisticsSamplerTypeConstant());
|
||||||
}
|
}
|
||||||
|
@ -349,6 +349,7 @@ JfrStartFlightRecordingDCmd::JfrStartFlightRecordingDCmd(outputStream* output,
|
|||||||
_filename("filename", "Resulting recording filename, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false),
|
_filename("filename", "Resulting recording filename, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false),
|
||||||
_maxage("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"),
|
_maxage("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"),
|
||||||
_maxsize("maxsize", "Maximum amount of bytes to keep (on disk) in (k)B, (M)B or (G)B, e.g. 500M, or 0 for no limit", "MEMORY SIZE", false, "0"),
|
_maxsize("maxsize", "Maximum amount of bytes to keep (on disk) in (k)B, (M)B or (G)B, e.g. 500M, or 0 for no limit", "MEMORY SIZE", false, "0"),
|
||||||
|
_flush_interval("flush-interval", "Minimum time before flushing buffers, measured in (s)econds, e.g. 4 s, or 0 for flushing when a recording ends", "NANOTIME", false, "1s"),
|
||||||
_dump_on_exit("dumponexit", "Dump running recording when JVM shuts down", "BOOLEAN", false),
|
_dump_on_exit("dumponexit", "Dump running recording when JVM shuts down", "BOOLEAN", false),
|
||||||
_path_to_gc_roots("path-to-gc-roots", "Collect path to GC roots", "BOOLEAN", false, "false") {
|
_path_to_gc_roots("path-to-gc-roots", "Collect path to GC roots", "BOOLEAN", false, "false") {
|
||||||
_dcmdparser.add_dcmd_option(&_name);
|
_dcmdparser.add_dcmd_option(&_name);
|
||||||
@ -359,6 +360,7 @@ JfrStartFlightRecordingDCmd::JfrStartFlightRecordingDCmd(outputStream* output,
|
|||||||
_dcmdparser.add_dcmd_option(&_filename);
|
_dcmdparser.add_dcmd_option(&_filename);
|
||||||
_dcmdparser.add_dcmd_option(&_maxage);
|
_dcmdparser.add_dcmd_option(&_maxage);
|
||||||
_dcmdparser.add_dcmd_option(&_maxsize);
|
_dcmdparser.add_dcmd_option(&_maxsize);
|
||||||
|
_dcmdparser.add_dcmd_option(&_flush_interval);
|
||||||
_dcmdparser.add_dcmd_option(&_dump_on_exit);
|
_dcmdparser.add_dcmd_option(&_dump_on_exit);
|
||||||
_dcmdparser.add_dcmd_option(&_path_to_gc_roots);
|
_dcmdparser.add_dcmd_option(&_path_to_gc_roots);
|
||||||
};
|
};
|
||||||
@ -411,6 +413,10 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) {
|
|||||||
maxsize = JfrJavaSupport::new_java_lang_Long(_maxsize.value()._size, CHECK);
|
maxsize = JfrJavaSupport::new_java_lang_Long(_maxsize.value()._size, CHECK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobject flush_interval = NULL;
|
||||||
|
if (_flush_interval.is_set()) {
|
||||||
|
flush_interval = JfrJavaSupport::new_java_lang_Long(_flush_interval.value()._nanotime, CHECK);
|
||||||
|
}
|
||||||
jobject duration = NULL;
|
jobject duration = NULL;
|
||||||
if (_duration.is_set()) {
|
if (_duration.is_set()) {
|
||||||
duration = JfrJavaSupport::new_java_lang_Long(_duration.value()._nanotime, CHECK);
|
duration = JfrJavaSupport::new_java_lang_Long(_duration.value()._nanotime, CHECK);
|
||||||
@ -464,7 +470,7 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) {
|
|||||||
static const char method[] = "execute";
|
static const char method[] = "execute";
|
||||||
static const char signature[] = "(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Long;"
|
static const char signature[] = "(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Long;"
|
||||||
"Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;"
|
"Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;"
|
||||||
"Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;";
|
"Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;";
|
||||||
|
|
||||||
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
|
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
|
||||||
execute_args.set_receiver(h_dcmd_instance);
|
execute_args.set_receiver(h_dcmd_instance);
|
||||||
@ -478,6 +484,7 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) {
|
|||||||
execute_args.push_jobject(filename);
|
execute_args.push_jobject(filename);
|
||||||
execute_args.push_jobject(maxage);
|
execute_args.push_jobject(maxage);
|
||||||
execute_args.push_jobject(maxsize);
|
execute_args.push_jobject(maxsize);
|
||||||
|
execute_args.push_jobject(flush_interval);
|
||||||
execute_args.push_jobject(dump_on_exit);
|
execute_args.push_jobject(dump_on_exit);
|
||||||
execute_args.push_jobject(path_to_gc_roots);
|
execute_args.push_jobject(path_to_gc_roots);
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ class JfrStartFlightRecordingDCmd : public DCmdWithParser {
|
|||||||
DCmdArgument<char*> _filename;
|
DCmdArgument<char*> _filename;
|
||||||
DCmdArgument<NanoTimeArgument> _maxage;
|
DCmdArgument<NanoTimeArgument> _maxage;
|
||||||
DCmdArgument<MemorySizeArgument> _maxsize;
|
DCmdArgument<MemorySizeArgument> _maxsize;
|
||||||
|
DCmdArgument<NanoTimeArgument> _flush_interval;
|
||||||
DCmdArgument<bool> _dump_on_exit;
|
DCmdArgument<bool> _dump_on_exit;
|
||||||
DCmdArgument<bool> _path_to_gc_roots;
|
DCmdArgument<bool> _path_to_gc_roots;
|
||||||
|
|
||||||
|
@ -71,6 +71,18 @@ void Jfr::on_thread_exit(Thread* t) {
|
|||||||
JfrThreadLocal::on_exit(t);
|
JfrThreadLocal::on_exit(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Jfr::exclude_thread(Thread* t) {
|
||||||
|
JfrThreadLocal::exclude(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Jfr::include_thread(Thread* t) {
|
||||||
|
JfrThreadLocal::include(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jfr::is_excluded(Thread* t) {
|
||||||
|
return t != NULL && t->jfr_thread_local()->is_excluded();
|
||||||
|
}
|
||||||
|
|
||||||
void Jfr::on_java_thread_dismantle(JavaThread* jt) {
|
void Jfr::on_java_thread_dismantle(JavaThread* jt) {
|
||||||
if (JfrRecorder::is_recording()) {
|
if (JfrRecorder::is_recording()) {
|
||||||
JfrCheckpointManager::write_thread_checkpoint(jt);
|
JfrCheckpointManager::write_thread_checkpoint(jt);
|
||||||
|
@ -53,6 +53,9 @@ class Jfr : AllStatic {
|
|||||||
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
|
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
|
||||||
static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
|
static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
|
||||||
static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
|
static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
|
||||||
|
static void exclude_thread(Thread* thread);
|
||||||
|
static bool is_excluded(Thread* thread);
|
||||||
|
static void include_thread(Thread* thread);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_JFR_HPP
|
#endif // SHARE_JFR_JFR_HPP
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "jni.h"
|
|
||||||
#include "classfile/javaClasses.inline.hpp"
|
#include "classfile/javaClasses.inline.hpp"
|
||||||
#include "classfile/modules.hpp"
|
#include "classfile/modules.hpp"
|
||||||
#include "classfile/symbolTable.hpp"
|
#include "classfile/symbolTable.hpp"
|
||||||
@ -42,9 +41,11 @@
|
|||||||
#include "runtime/fieldDescriptor.inline.hpp"
|
#include "runtime/fieldDescriptor.inline.hpp"
|
||||||
#include "runtime/java.hpp"
|
#include "runtime/java.hpp"
|
||||||
#include "runtime/jniHandles.inline.hpp"
|
#include "runtime/jniHandles.inline.hpp"
|
||||||
|
#include "runtime/semaphore.inline.hpp"
|
||||||
#include "runtime/synchronizer.hpp"
|
#include "runtime/synchronizer.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
#include "runtime/threadSMR.hpp"
|
#include "runtime/threadSMR.hpp"
|
||||||
|
#include "utilities/growableArray.hpp"
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
void JfrJavaSupport::check_java_thread_in_vm(Thread* t) {
|
void JfrJavaSupport::check_java_thread_in_vm(Thread* t) {
|
||||||
@ -58,6 +59,12 @@ void JfrJavaSupport::check_java_thread_in_native(Thread* t) {
|
|||||||
assert(t->is_Java_thread(), "invariant");
|
assert(t->is_Java_thread(), "invariant");
|
||||||
assert(((JavaThread*)t)->thread_state() == _thread_in_native, "invariant");
|
assert(((JavaThread*)t)->thread_state() == _thread_in_native, "invariant");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void check_new_unstarted_java_thread(Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
assert(t->is_Java_thread(), "invariant");
|
||||||
|
assert(((JavaThread*)t)->thread_state() == _thread_new, "invariant");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -93,6 +100,21 @@ void JfrJavaSupport::destroy_global_jni_handle(jobject handle) {
|
|||||||
JNIHandles::destroy_global(handle);
|
JNIHandles::destroy_global(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jweak JfrJavaSupport::global_weak_jni_handle(const oop obj, Thread* t) {
|
||||||
|
DEBUG_ONLY(check_java_thread_in_vm(t));
|
||||||
|
HandleMark hm(t);
|
||||||
|
return JNIHandles::make_weak_global(Handle(t, obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
jweak JfrJavaSupport::global_weak_jni_handle(const jobject handle, Thread* t) {
|
||||||
|
const oop obj = JNIHandles::resolve(handle);
|
||||||
|
return obj == NULL ? NULL : global_weak_jni_handle(obj, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrJavaSupport::destroy_global_weak_jni_handle(jweak handle) {
|
||||||
|
JNIHandles::destroy_weak_global(handle);
|
||||||
|
}
|
||||||
|
|
||||||
oop JfrJavaSupport::resolve_non_null(jobject obj) {
|
oop JfrJavaSupport::resolve_non_null(jobject obj) {
|
||||||
return JNIHandles::resolve_non_null(obj);
|
return JNIHandles::resolve_non_null(obj);
|
||||||
}
|
}
|
||||||
@ -603,9 +625,149 @@ bool JfrJavaSupport::is_jdk_jfr_module_available(outputStream* stream, TRAPS) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
jlong JfrJavaSupport::jfr_thread_id(jobject target_thread) {
|
class ThreadExclusionListAccess : public StackObj {
|
||||||
|
private:
|
||||||
|
static Semaphore _mutex_semaphore;
|
||||||
|
public:
|
||||||
|
ThreadExclusionListAccess() { _mutex_semaphore.wait(); }
|
||||||
|
~ThreadExclusionListAccess() { _mutex_semaphore.signal(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
Semaphore ThreadExclusionListAccess::_mutex_semaphore(1);
|
||||||
|
static GrowableArray<jweak>* exclusion_list = NULL;
|
||||||
|
|
||||||
|
static bool equals(const jweak excluded_thread, Handle target_thread) {
|
||||||
|
return JfrJavaSupport::resolve_non_null(excluded_thread) == target_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_exclusion_thread_idx(Handle thread) {
|
||||||
|
if (exclusion_list != NULL) {
|
||||||
|
for (int i = 0; i < exclusion_list->length(); ++i) {
|
||||||
|
if (equals(exclusion_list->at(i), thread)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Handle as_handle(jobject thread) {
|
||||||
|
return Handle(Thread::current(), JfrJavaSupport::resolve_non_null(thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool thread_is_not_excluded(Handle thread) {
|
||||||
|
return -1 == find_exclusion_thread_idx(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool thread_is_not_excluded(jobject thread) {
|
||||||
|
return thread_is_not_excluded(as_handle(thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_thread_excluded(jobject thread) {
|
||||||
|
return !thread_is_not_excluded(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
static bool is_thread_excluded(Handle thread) {
|
||||||
|
return !thread_is_not_excluded(thread);
|
||||||
|
}
|
||||||
|
#endif // ASSERT
|
||||||
|
|
||||||
|
static int add_thread_to_exclusion_list(jobject thread) {
|
||||||
|
ThreadExclusionListAccess lock;
|
||||||
|
if (exclusion_list == NULL) {
|
||||||
|
exclusion_list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<jweak>(10, true, mtTracing);
|
||||||
|
}
|
||||||
|
assert(exclusion_list != NULL, "invariant");
|
||||||
|
assert(thread_is_not_excluded(thread), "invariant");
|
||||||
|
jweak ref = JfrJavaSupport::global_weak_jni_handle(thread, Thread::current());
|
||||||
|
const int idx = exclusion_list->append(ref);
|
||||||
|
assert(is_thread_excluded(thread), "invariant");
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_thread_from_exclusion_list(Handle thread) {
|
||||||
|
assert(exclusion_list != NULL, "invariant");
|
||||||
|
assert(is_thread_excluded(thread), "invariant");
|
||||||
|
assert(exclusion_list != NULL, "invariant");
|
||||||
|
const int idx = find_exclusion_thread_idx(thread);
|
||||||
|
assert(idx >= 0, "invariant");
|
||||||
|
assert(idx < exclusion_list->length(), "invariant");
|
||||||
|
JfrJavaSupport::destroy_global_weak_jni_handle(exclusion_list->at(idx));
|
||||||
|
exclusion_list->delete_at(idx);
|
||||||
|
assert(thread_is_not_excluded(thread), "invariant");
|
||||||
|
if (0 == exclusion_list->length()) {
|
||||||
|
delete exclusion_list;
|
||||||
|
exclusion_list = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_thread_from_exclusion_list(jobject thread) {
|
||||||
|
ThreadExclusionListAccess lock;
|
||||||
|
remove_thread_from_exclusion_list(as_handle(thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
// includes removal
|
||||||
|
static bool check_exclusion_state_on_thread_start(JavaThread* jt) {
|
||||||
|
Handle h_obj(jt, jt->threadObj());
|
||||||
|
ThreadExclusionListAccess lock;
|
||||||
|
if (thread_is_not_excluded(h_obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
remove_thread_from_exclusion_list(h_obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
jlong JfrJavaSupport::jfr_thread_id(jobject thread) {
|
||||||
ThreadsListHandle tlh;
|
ThreadsListHandle tlh;
|
||||||
JavaThread* native_thread = NULL;
|
JavaThread* native_thread = NULL;
|
||||||
(void)tlh.cv_internal_thread_to_JavaThread(target_thread, &native_thread, NULL);
|
(void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
|
||||||
return native_thread != NULL ? JFR_THREAD_ID(native_thread) : 0;
|
return native_thread != NULL ? JFR_THREAD_ID(native_thread) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JfrJavaSupport::exclude(jobject thread) {
|
||||||
|
HandleMark hm;
|
||||||
|
ThreadsListHandle tlh;
|
||||||
|
JavaThread* native_thread = NULL;
|
||||||
|
(void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
|
||||||
|
if (native_thread != NULL) {
|
||||||
|
JfrThreadLocal::exclude(native_thread);
|
||||||
|
} else {
|
||||||
|
// not started yet, track the thread oop
|
||||||
|
add_thread_to_exclusion_list(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrJavaSupport::include(jobject thread) {
|
||||||
|
HandleMark hm;
|
||||||
|
ThreadsListHandle tlh;
|
||||||
|
JavaThread* native_thread = NULL;
|
||||||
|
(void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
|
||||||
|
if (native_thread != NULL) {
|
||||||
|
JfrThreadLocal::include(native_thread);
|
||||||
|
} else {
|
||||||
|
// not started yet, untrack the thread oop
|
||||||
|
remove_thread_from_exclusion_list(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JfrJavaSupport::is_excluded(jobject thread) {
|
||||||
|
HandleMark hm;
|
||||||
|
ThreadsListHandle tlh;
|
||||||
|
JavaThread* native_thread = NULL;
|
||||||
|
(void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
|
||||||
|
return native_thread != NULL ? native_thread->jfr_thread_local()->is_excluded() : is_thread_excluded(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrJavaSupport::on_thread_start(Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
assert(Thread::current() == t, "invariant");
|
||||||
|
if (!t->is_Java_thread()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DEBUG_ONLY(check_new_unstarted_java_thread(t);)
|
||||||
|
HandleMark hm;
|
||||||
|
if (check_exclusion_state_on_thread_start((JavaThread*)t)) {
|
||||||
|
JfrThreadLocal::exclude(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -29,18 +29,21 @@
|
|||||||
#include "utilities/exceptions.hpp"
|
#include "utilities/exceptions.hpp"
|
||||||
|
|
||||||
class Klass;
|
class Klass;
|
||||||
class JavaThread;
|
|
||||||
class outputStream;
|
class outputStream;
|
||||||
|
|
||||||
class JfrJavaSupport : public AllStatic {
|
class JfrJavaSupport : public AllStatic {
|
||||||
public:
|
public:
|
||||||
static jobject local_jni_handle(const oop obj, Thread* t);
|
static jobject local_jni_handle(const oop obj, Thread* t);
|
||||||
static jobject local_jni_handle(const jobject handle, Thread* t);
|
static jobject local_jni_handle(const jobject handle, Thread* t);
|
||||||
static void destroy_local_jni_handle(const jobject handle);
|
static void destroy_local_jni_handle(jobject handle);
|
||||||
|
|
||||||
static jobject global_jni_handle(const oop obj, Thread* t);
|
static jobject global_jni_handle(const oop obj, Thread* t);
|
||||||
static jobject global_jni_handle(const jobject handle, Thread* t);
|
static jobject global_jni_handle(const jobject handle, Thread* t);
|
||||||
static void destroy_global_jni_handle(const jobject handle);
|
static void destroy_global_jni_handle(jobject handle);
|
||||||
|
|
||||||
|
static jweak global_weak_jni_handle(const oop obj, Thread* t);
|
||||||
|
static jweak global_weak_jni_handle(const jobject handle, Thread* t);
|
||||||
|
static void destroy_global_weak_jni_handle(jweak handle);
|
||||||
|
|
||||||
static oop resolve_non_null(jobject obj);
|
static oop resolve_non_null(jobject obj);
|
||||||
static void notify_all(jobject obj, TRAPS);
|
static void notify_all(jobject obj, TRAPS);
|
||||||
@ -85,7 +88,11 @@ class JfrJavaSupport : public AllStatic {
|
|||||||
static bool is_jdk_jfr_module_available();
|
static bool is_jdk_jfr_module_available();
|
||||||
static bool is_jdk_jfr_module_available(outputStream* stream, TRAPS);
|
static bool is_jdk_jfr_module_available(outputStream* stream, TRAPS);
|
||||||
|
|
||||||
static jlong jfr_thread_id(jobject target_thread);
|
static jlong jfr_thread_id(jobject thread);
|
||||||
|
static void exclude(jobject thread);
|
||||||
|
static void include(jobject thread);
|
||||||
|
static bool is_excluded(jobject thread);
|
||||||
|
static void on_thread_start(Thread* t);
|
||||||
|
|
||||||
// critical
|
// critical
|
||||||
static void abort(jstring errorMsg, TRAPS);
|
static void abort(jstring errorMsg, TRAPS);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -284,6 +284,10 @@ JVM_ENTRY_NO_ENV(jboolean, jfr_event_writer_flush(JNIEnv* env, jclass cls, jobje
|
|||||||
return JfrJavaEventWriter::flush(writer, used_size, requested_size, thread);
|
return JfrJavaEventWriter::flush(writer, used_size, requested_size, thread);
|
||||||
JVM_END
|
JVM_END
|
||||||
|
|
||||||
|
JVM_ENTRY_NO_ENV(void, jfr_flush(JNIEnv* env, jobject jvm))
|
||||||
|
JfrRepository::flush(thread);
|
||||||
|
JVM_END
|
||||||
|
|
||||||
JVM_ENTRY_NO_ENV(void, jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location))
|
JVM_ENTRY_NO_ENV(void, jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location))
|
||||||
return JfrRepository::set_path(location, thread);
|
return JfrRepository::set_path(location, thread);
|
||||||
JVM_END
|
JVM_END
|
||||||
@ -311,3 +315,20 @@ JVM_END
|
|||||||
JVM_ENTRY_NO_ENV(void, jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean emit_all))
|
JVM_ENTRY_NO_ENV(void, jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean emit_all))
|
||||||
LeakProfiler::emit_events(cutoff_ticks, emit_all == JNI_TRUE);
|
LeakProfiler::emit_events(cutoff_ticks, emit_all == JNI_TRUE);
|
||||||
JVM_END
|
JVM_END
|
||||||
|
|
||||||
|
JVM_ENTRY_NO_ENV(void, jfr_exclude_thread(JNIEnv* env, jobject jvm, jobject t))
|
||||||
|
JfrJavaSupport::exclude(t);
|
||||||
|
JVM_END
|
||||||
|
|
||||||
|
JVM_ENTRY_NO_ENV(void, jfr_include_thread(JNIEnv* env, jobject jvm, jobject t))
|
||||||
|
JfrJavaSupport::include(t);
|
||||||
|
JVM_END
|
||||||
|
|
||||||
|
JVM_ENTRY_NO_ENV(jboolean, jfr_is_thread_excluded(JNIEnv* env, jobject jvm, jobject t))
|
||||||
|
return JfrJavaSupport::is_excluded(t);
|
||||||
|
JVM_END
|
||||||
|
|
||||||
|
JVM_ENTRY_NO_ENV(jlong, jfr_chunk_start_nanos(JNIEnv* env, jobject jvm))
|
||||||
|
return JfrRepository::current_chunk_start_nanos();
|
||||||
|
JVM_END
|
||||||
|
|
||||||
|
@ -113,6 +113,7 @@ jobject JNICALL jfr_new_event_writer(JNIEnv* env, jclass cls);
|
|||||||
|
|
||||||
jboolean JNICALL jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size);
|
jboolean JNICALL jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size);
|
||||||
|
|
||||||
|
void JNICALL jfr_flush(JNIEnv* env, jobject jvm);
|
||||||
void JNICALL jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg);
|
void JNICALL jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg);
|
||||||
|
|
||||||
jlong JNICALL jfr_get_epoch_address(JNIEnv* env, jobject jvm);
|
jlong JNICALL jfr_get_epoch_address(JNIEnv* env, jobject jvm);
|
||||||
@ -131,6 +132,13 @@ void JNICALL jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_
|
|||||||
|
|
||||||
jboolean JNICALL jfr_should_rotate_disk(JNIEnv* env, jobject jvm);
|
jboolean JNICALL jfr_should_rotate_disk(JNIEnv* env, jobject jvm);
|
||||||
|
|
||||||
|
void JNICALL jfr_exclude_thread(JNIEnv* env, jobject jvm, jobject t);
|
||||||
|
|
||||||
|
void JNICALL jfr_include_thread(JNIEnv* env, jobject jvm, jobject t);
|
||||||
|
|
||||||
|
jboolean JNICALL jfr_is_thread_excluded(JNIEnv* env, jobject jvm, jobject t);
|
||||||
|
|
||||||
|
jlong JNICALL jfr_chunk_start_nanos(JNIEnv* env, jobject jvm);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -70,6 +70,7 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) {
|
|||||||
(char*)"getEventWriter", (char*)"()Ljava/lang/Object;", (void*)jfr_get_event_writer,
|
(char*)"getEventWriter", (char*)"()Ljava/lang/Object;", (void*)jfr_get_event_writer,
|
||||||
(char*)"newEventWriter", (char*)"()Ljdk/jfr/internal/EventWriter;", (void*)jfr_new_event_writer,
|
(char*)"newEventWriter", (char*)"()Ljdk/jfr/internal/EventWriter;", (void*)jfr_new_event_writer,
|
||||||
(char*)"flush", (char*)"(Ljdk/jfr/internal/EventWriter;II)Z", (void*)jfr_event_writer_flush,
|
(char*)"flush", (char*)"(Ljdk/jfr/internal/EventWriter;II)Z", (void*)jfr_event_writer_flush,
|
||||||
|
(char*)"flush", (char*)"()V", (void*)jfr_flush,
|
||||||
(char*)"setRepositoryLocation", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_repository_location,
|
(char*)"setRepositoryLocation", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_repository_location,
|
||||||
(char*)"abort", (char*)"(Ljava/lang/String;)V", (void*)jfr_abort,
|
(char*)"abort", (char*)"(Ljava/lang/String;)V", (void*)jfr_abort,
|
||||||
(char*)"getEpochAddress", (char*)"()J",(void*)jfr_get_epoch_address,
|
(char*)"getEpochAddress", (char*)"()J",(void*)jfr_get_epoch_address,
|
||||||
@ -79,7 +80,11 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) {
|
|||||||
(char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count,
|
(char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count,
|
||||||
(char*)"setCutoff", (char*)"(JJ)Z", (void*)jfr_set_cutoff,
|
(char*)"setCutoff", (char*)"(JJ)Z", (void*)jfr_set_cutoff,
|
||||||
(char*)"emitOldObjectSamples", (char*)"(JZ)V", (void*)jfr_emit_old_object_samples,
|
(char*)"emitOldObjectSamples", (char*)"(JZ)V", (void*)jfr_emit_old_object_samples,
|
||||||
(char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk
|
(char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk,
|
||||||
|
(char*)"exclude", (char*)"(Ljava/lang/Thread;)V", (void*)jfr_exclude_thread,
|
||||||
|
(char*)"include", (char*)"(Ljava/lang/Thread;)V", (void*)jfr_include_thread,
|
||||||
|
(char*)"isExcluded", (char*)"(Ljava/lang/Thread;)Z", (void*)jfr_is_thread_excluded,
|
||||||
|
(char*)"getChunkStartNanos", (char*)"()J", (void*)jfr_chunk_start_nanos
|
||||||
};
|
};
|
||||||
|
|
||||||
const size_t method_array_length = sizeof(method) / sizeof(JNINativeMethod);
|
const size_t method_array_length = sizeof(method) / sizeof(JNINativeMethod);
|
||||||
|
@ -259,6 +259,7 @@ void EdgeStore::put_chain(const Edge* chain, size_t length) {
|
|||||||
assert(leak_context_edge->parent() == NULL, "invariant");
|
assert(leak_context_edge->parent() == NULL, "invariant");
|
||||||
|
|
||||||
if (1 == length) {
|
if (1 == length) {
|
||||||
|
store_gc_root_id_in_leak_context_edge(leak_context_edge, leak_context_edge);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
#include "oops/markWord.hpp"
|
#include "oops/markWord.hpp"
|
||||||
#include "oops/oop.inline.hpp"
|
#include "oops/oop.inline.hpp"
|
||||||
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
#include "runtime/vmThread.hpp"
|
#include "runtime/vmThread.hpp"
|
||||||
|
|
||||||
@ -51,8 +52,8 @@ EventEmitter::~EventEmitter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_all) {
|
void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_all) {
|
||||||
|
assert(JfrStream_lock->owned_by_self(), "invariant");
|
||||||
assert(sampler != NULL, "invariant");
|
assert(sampler != NULL, "invariant");
|
||||||
|
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
EdgeStore edge_store;
|
EdgeStore edge_store;
|
||||||
if (cutoff_ticks <= 0) {
|
if (cutoff_ticks <= 0) {
|
||||||
@ -68,6 +69,7 @@ void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t EventEmitter::write_events(ObjectSampler* object_sampler, EdgeStore* edge_store, bool emit_all) {
|
size_t EventEmitter::write_events(ObjectSampler* object_sampler, EdgeStore* edge_store, bool emit_all) {
|
||||||
|
assert_locked_or_safepoint(JfrStream_lock);
|
||||||
assert(_thread == Thread::current(), "invariant");
|
assert(_thread == Thread::current(), "invariant");
|
||||||
assert(_thread->jfr_thread_local() == _jfr_thread_local, "invariant");
|
assert(_thread->jfr_thread_local() == _jfr_thread_local, "invariant");
|
||||||
assert(object_sampler != NULL, "invariant");
|
assert(object_sampler != NULL, "invariant");
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
|
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
|
||||||
#include "jfr/utilities/jfrHashtable.hpp"
|
#include "jfr/utilities/jfrHashtable.hpp"
|
||||||
#include "jfr/utilities/jfrTypes.hpp"
|
#include "jfr/utilities/jfrTypes.hpp"
|
||||||
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "runtime/safepoint.hpp"
|
#include "runtime/safepoint.hpp"
|
||||||
#include "runtime/thread.hpp"
|
#include "runtime/thread.hpp"
|
||||||
#include "utilities/growableArray.hpp"
|
#include "utilities/growableArray.hpp"
|
||||||
@ -271,7 +272,7 @@ void StackTraceBlobInstaller::install(ObjectSample* sample) {
|
|||||||
}
|
}
|
||||||
const JfrStackTrace* const stack_trace = resolve(sample);
|
const JfrStackTrace* const stack_trace = resolve(sample);
|
||||||
DEBUG_ONLY(validate_stack_trace(sample, stack_trace));
|
DEBUG_ONLY(validate_stack_trace(sample, stack_trace));
|
||||||
JfrCheckpointWriter writer(false, true, Thread::current());
|
JfrCheckpointWriter writer;
|
||||||
writer.write_type(TYPE_STACKTRACE);
|
writer.write_type(TYPE_STACKTRACE);
|
||||||
writer.write_count(1);
|
writer.write_count(1);
|
||||||
ObjectSampleCheckpoint::write_stacktrace(stack_trace, writer);
|
ObjectSampleCheckpoint::write_stacktrace(stack_trace, writer);
|
||||||
@ -291,6 +292,7 @@ static void install_stack_traces(const ObjectSampler* sampler, JfrStackTraceRepo
|
|||||||
|
|
||||||
// caller needs ResourceMark
|
// caller needs ResourceMark
|
||||||
void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repo) {
|
void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repo) {
|
||||||
|
assert(JfrStream_lock->owned_by_self(), "invariant");
|
||||||
assert(sampler != NULL, "invariant");
|
assert(sampler != NULL, "invariant");
|
||||||
assert(LeakProfiler::is_running(), "invariant");
|
assert(LeakProfiler::is_running(), "invariant");
|
||||||
install_stack_traces(sampler, stack_trace_repo);
|
install_stack_traces(sampler, stack_trace_repo);
|
||||||
@ -388,7 +390,7 @@ class BlobWriter {
|
|||||||
static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) {
|
static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) {
|
||||||
// sample set is predicated on time of last sweep
|
// sample set is predicated on time of last sweep
|
||||||
const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value();
|
const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value();
|
||||||
JfrCheckpointWriter writer(false, false, thread);
|
JfrCheckpointWriter writer(thread, false);
|
||||||
BlobWriter cbw(sampler, writer, last_sweep);
|
BlobWriter cbw(sampler, writer, last_sweep);
|
||||||
iterate_samples(cbw, true);
|
iterate_samples(cbw, true);
|
||||||
// reset blob write states
|
// reset blob write states
|
||||||
@ -397,13 +399,14 @@ static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thre
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
|
void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
|
||||||
|
assert_locked_or_safepoint(JfrStream_lock);
|
||||||
assert(sampler != NULL, "invariant");
|
assert(sampler != NULL, "invariant");
|
||||||
assert(edge_store != NULL, "invariant");
|
assert(edge_store != NULL, "invariant");
|
||||||
assert(thread != NULL, "invariant");
|
assert(thread != NULL, "invariant");
|
||||||
write_sample_blobs(sampler, emit_all, thread);
|
write_sample_blobs(sampler, emit_all, thread);
|
||||||
// write reference chains
|
// write reference chains
|
||||||
if (!edge_store->is_empty()) {
|
if (!edge_store->is_empty()) {
|
||||||
JfrCheckpointWriter writer(false, true, thread);
|
JfrCheckpointWriter writer(thread);
|
||||||
ObjectSampleWriter osw(writer, edge_store);
|
ObjectSampleWriter osw(writer, edge_store);
|
||||||
edge_store->iterate(osw);
|
edge_store->iterate(osw);
|
||||||
}
|
}
|
||||||
|
@ -355,10 +355,6 @@ int __write_root_description_info__(JfrCheckpointWriter* writer, const void* di)
|
|||||||
|
|
||||||
static traceid get_gc_root_description_info_id(const Edge& edge, traceid id) {
|
static traceid get_gc_root_description_info_id(const Edge& edge, traceid id) {
|
||||||
assert(edge.is_root(), "invariant");
|
assert(edge.is_root(), "invariant");
|
||||||
if (EdgeUtils::is_leak_edge(edge)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root_infos == NULL) {
|
if (root_infos == NULL) {
|
||||||
root_infos = new RootDescriptionInfo();
|
root_infos = new RootDescriptionInfo();
|
||||||
}
|
}
|
||||||
@ -606,8 +602,8 @@ class RootType : public JfrSerializer {
|
|||||||
static void register_serializers() {
|
static void register_serializers() {
|
||||||
static bool is_registered = false;
|
static bool is_registered = false;
|
||||||
if (!is_registered) {
|
if (!is_registered) {
|
||||||
JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType());
|
JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, true, new RootSystemType());
|
||||||
JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType());
|
JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, true, new RootType());
|
||||||
is_registered = true;
|
is_registered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "gc/shared/strongRootsScope.hpp"
|
#include "gc/shared/strongRootsScope.hpp"
|
||||||
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
|
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
|
||||||
#include "jfr/leakprofiler/checkpoint/rootResolver.hpp"
|
#include "jfr/leakprofiler/checkpoint/rootResolver.hpp"
|
||||||
|
#include "jfr/utilities/jfrThreadIterator.hpp"
|
||||||
#include "memory/iterator.hpp"
|
#include "memory/iterator.hpp"
|
||||||
#include "memory/universe.hpp"
|
#include "memory/universe.hpp"
|
||||||
#include "oops/klass.hpp"
|
#include "oops/klass.hpp"
|
||||||
@ -36,7 +37,6 @@
|
|||||||
#include "prims/jvmtiThreadState.hpp"
|
#include "prims/jvmtiThreadState.hpp"
|
||||||
#include "runtime/frame.inline.hpp"
|
#include "runtime/frame.inline.hpp"
|
||||||
#include "runtime/mutexLocker.hpp"
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "runtime/threadSMR.inline.hpp"
|
|
||||||
#include "runtime/vframe_hp.hpp"
|
#include "runtime/vframe_hp.hpp"
|
||||||
#include "services/management.hpp"
|
#include "services/management.hpp"
|
||||||
#include "utilities/growableArray.hpp"
|
#include "utilities/growableArray.hpp"
|
||||||
@ -256,8 +256,9 @@ class ReferenceToThreadRootClosure : public StackObj {
|
|||||||
public:
|
public:
|
||||||
ReferenceToThreadRootClosure(RootCallback& callback) :_callback(callback), _complete(false) {
|
ReferenceToThreadRootClosure(RootCallback& callback) :_callback(callback), _complete(false) {
|
||||||
assert_locked_or_safepoint(Threads_lock);
|
assert_locked_or_safepoint(Threads_lock);
|
||||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
|
JfrJavaThreadIterator iter;
|
||||||
if (do_thread_roots(jt)) {
|
while (iter.has_next()) {
|
||||||
|
if (do_thread_roots(iter.next())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "memory/iterator.hpp"
|
#include "memory/iterator.hpp"
|
||||||
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
#include "runtime/vmThread.hpp"
|
#include "runtime/vmThread.hpp"
|
||||||
|
|
||||||
@ -92,6 +93,7 @@ void LeakProfiler::emit_events(int64_t cutoff_ticks, bool emit_all) {
|
|||||||
if (!is_running()) {
|
if (!is_running()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
MutexLocker lock(JfrStream_lock);
|
||||||
// exclusive access to object sampler instance
|
// exclusive access to object sampler instance
|
||||||
ObjectSampler* const sampler = ObjectSampler::acquire();
|
ObjectSampler* const sampler = ObjectSampler::acquire();
|
||||||
assert(sampler != NULL, "invariant");
|
assert(sampler != NULL, "invariant");
|
||||||
|
@ -110,6 +110,9 @@ static traceid get_thread_id(JavaThread* thread) {
|
|||||||
}
|
}
|
||||||
const JfrThreadLocal* const tl = thread->jfr_thread_local();
|
const JfrThreadLocal* const tl = thread->jfr_thread_local();
|
||||||
assert(tl != NULL, "invariant");
|
assert(tl != NULL, "invariant");
|
||||||
|
if (tl->is_excluded()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (!tl->has_thread_blob()) {
|
if (!tl->has_thread_blob()) {
|
||||||
JfrCheckpointManager::create_thread_blob(thread);
|
JfrCheckpointManager::create_thread_blob(thread);
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,8 @@
|
|||||||
class JfrSerializer : public CHeapObj<mtTracing> {
|
class JfrSerializer : public CHeapObj<mtTracing> {
|
||||||
public:
|
public:
|
||||||
virtual ~JfrSerializer() {}
|
virtual ~JfrSerializer() {}
|
||||||
static bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer);
|
virtual void on_rotation() {}
|
||||||
|
static bool register_serializer(JfrTypeId id, bool permit_cache, JfrSerializer* serializer);
|
||||||
virtual void serialize(JfrCheckpointWriter& writer) = 0;
|
virtual void serialize(JfrCheckpointWriter& writer) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1004,6 +1004,42 @@
|
|||||||
<Field type="string" name="state" label="State" />
|
<Field type="string" name="state" label="State" />
|
||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
|
<Event name="Flush" category="Flight Recorder" label="Flush" thread="false" experimental="true">
|
||||||
|
<Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
|
||||||
|
<Field type="ulong" name="elements" label="Elements Written" />
|
||||||
|
<Field type="ulong" contentType="bytes" name="size" label="Size Written" />
|
||||||
|
</Event>
|
||||||
|
|
||||||
|
<Event name="FlushStorage" category="Flight Recorder" label="Flush Storage" thread="false" experimental="true">
|
||||||
|
<Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
|
||||||
|
<Field type="ulong" name="elements" label="Elements Written" />
|
||||||
|
<Field type="ulong" contentType="bytes" name="size" label="Size Written" />
|
||||||
|
</Event>
|
||||||
|
|
||||||
|
<Event name="FlushStacktrace" category="Flight Recorder" label="Flush Stacktrace" thread="false" experimental="true">
|
||||||
|
<Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
|
||||||
|
<Field type="ulong" name="elements" label="Elements Written" />
|
||||||
|
<Field type="ulong" contentType="bytes" name="size" label="Size Written" />
|
||||||
|
</Event>
|
||||||
|
|
||||||
|
<Event name="FlushStringPool" category="Flight Recorder" label="Flush String Pool" thread="false" experimental="true">
|
||||||
|
<Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
|
||||||
|
<Field type="ulong" name="elements" label="Elements Written" />
|
||||||
|
<Field type="ulong" contentType="bytes" name="size" label="Size Written" />
|
||||||
|
</Event>
|
||||||
|
|
||||||
|
<Event name="FlushMetadata" category="Flight Recorder" label="Flush Metadata" thread="false" experimental="true">
|
||||||
|
<Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
|
||||||
|
<Field type="ulong" name="elements" label="Elements Written" />
|
||||||
|
<Field type="ulong" contentType="bytes" name="size" label="Size Written" />
|
||||||
|
</Event>
|
||||||
|
|
||||||
|
<Event name="FlushTypeSet" category="Flight Recorder" label="Flush Type Set" thread="false" experimental="true">
|
||||||
|
<Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
|
||||||
|
<Field type="ulong" name="elements" label="Elements Written" />
|
||||||
|
<Field type="ulong" contentType="bytes" name="size" label="Size Written" />
|
||||||
|
</Event>
|
||||||
|
|
||||||
<Type name="ZStatisticsCounterType" label="Z Statistics Counter">
|
<Type name="ZStatisticsCounterType" label="Z Statistics Counter">
|
||||||
<Field type="string" name="counter" label="Counter" />
|
<Field type="string" name="counter" label="Counter" />
|
||||||
</Type>
|
</Type>
|
||||||
@ -1184,11 +1220,16 @@
|
|||||||
<Field type="FrameType" name="type" label="Frame Type" />
|
<Field type="FrameType" name="type" label="Frame Type" />
|
||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
|
<Type name="ChunkHeader" label="Chunk Header">
|
||||||
|
<Field type="byte" array="true" name="payload" label="Payload" />
|
||||||
|
</Type>
|
||||||
|
|
||||||
<Relation name="JavaMonitorAddress"/>
|
<Relation name="JavaMonitorAddress"/>
|
||||||
<Relation name="SafepointId"/>
|
<Relation name="SafepointId"/>
|
||||||
<Relation name="GcId"/>
|
<Relation name="GcId"/>
|
||||||
<Relation name="CompileId" />
|
<Relation name="CompileId" />
|
||||||
<Relation name="SweepId"/>
|
<Relation name="SweepId"/>
|
||||||
|
<Relation name="FlushId"/>
|
||||||
|
|
||||||
<XmlType name="Package" parameterType="const PackageEntry*" fieldType="const PackageEntry*"/>
|
<XmlType name="Package" parameterType="const PackageEntry*" fieldType="const PackageEntry*"/>
|
||||||
<XmlType name="Class" javaType="java.lang.Class" parameterType="const Klass*" fieldType="const Klass*"/>
|
<XmlType name="Class" javaType="java.lang.Class" parameterType="const Klass*" fieldType="const Klass*"/>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -39,7 +39,7 @@ struct InterfaceEntry {
|
|||||||
traceid id;
|
traceid id;
|
||||||
uint64_t bytes_in;
|
uint64_t bytes_in;
|
||||||
uint64_t bytes_out;
|
uint64_t bytes_out;
|
||||||
bool in_use;
|
mutable bool written;
|
||||||
};
|
};
|
||||||
|
|
||||||
static GrowableArray<InterfaceEntry>* _interfaces = NULL;
|
static GrowableArray<InterfaceEntry>* _interfaces = NULL;
|
||||||
@ -71,7 +71,7 @@ static InterfaceEntry& new_entry(const NetworkInterface* iface, GrowableArray<In
|
|||||||
entry.id = ++interface_id;
|
entry.id = ++interface_id;
|
||||||
entry.bytes_in = iface->get_bytes_in();
|
entry.bytes_in = iface->get_bytes_in();
|
||||||
entry.bytes_out = iface->get_bytes_out();
|
entry.bytes_out = iface->get_bytes_out();
|
||||||
entry.in_use = false;
|
entry.written = false;
|
||||||
return _interfaces->at(_interfaces->append(entry));
|
return _interfaces->at(_interfaces->append(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +108,39 @@ static uint64_t rate_per_second(uint64_t current, uint64_t old, const JfrTickspa
|
|||||||
return ((current - old) * NANOSECS_PER_SEC) / interval.nanoseconds();
|
return ((current - old) * NANOSECS_PER_SEC) / interval.nanoseconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class JfrNetworkInterfaceName : public JfrSerializer {
|
||||||
|
public:
|
||||||
|
void serialize(JfrCheckpointWriter& writer) {} // we write each constant lazily
|
||||||
|
|
||||||
|
void on_rotation() {
|
||||||
|
for (int i = 0; i < _interfaces->length(); ++i) {
|
||||||
|
const InterfaceEntry& entry = _interfaces->at(i);
|
||||||
|
if (entry.written) {
|
||||||
|
entry.written = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool register_network_interface_name_serializer() {
|
||||||
|
assert(_interfaces != NULL, "invariant");
|
||||||
|
return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME,
|
||||||
|
false, // disallow caching; we want a callback every rotation
|
||||||
|
new JfrNetworkInterfaceName());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_interface_constant(const InterfaceEntry& entry) {
|
||||||
|
if (entry.written) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JfrCheckpointWriter writer;
|
||||||
|
writer.write_type(TYPE_NETWORKINTERFACENAME);
|
||||||
|
writer.write_count(1);
|
||||||
|
writer.write_key(entry.id);
|
||||||
|
writer.write(entry.name);
|
||||||
|
entry.written = true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool get_interfaces(NetworkInterface** network_interfaces) {
|
static bool get_interfaces(NetworkInterface** network_interfaces) {
|
||||||
const int ret_val = JfrOSInterface::network_utilization(network_interfaces);
|
const int ret_val = JfrOSInterface::network_utilization(network_interfaces);
|
||||||
if (ret_val == OS_ERR) {
|
if (ret_val == OS_ERR) {
|
||||||
@ -117,39 +150,6 @@ static bool get_interfaces(NetworkInterface** network_interfaces) {
|
|||||||
return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED;
|
return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
class JfrNetworkInterfaceName : public JfrSerializer {
|
|
||||||
public:
|
|
||||||
void serialize(JfrCheckpointWriter& writer) {
|
|
||||||
assert(_interfaces != NULL, "invariant");
|
|
||||||
const JfrCheckpointContext ctx = writer.context();
|
|
||||||
const intptr_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet
|
|
||||||
int active_interfaces = 0;
|
|
||||||
for (int i = 0; i < _interfaces->length(); ++i) {
|
|
||||||
InterfaceEntry& entry = _interfaces->at(i);
|
|
||||||
if (entry.in_use) {
|
|
||||||
entry.in_use = false;
|
|
||||||
writer.write_key(entry.id);
|
|
||||||
writer.write(entry.name);
|
|
||||||
++active_interfaces;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (active_interfaces == 0) {
|
|
||||||
// nothing to write, restore context
|
|
||||||
writer.set_context(ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
writer.write_count(active_interfaces, count_offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool register_network_interface_name_serializer() {
|
|
||||||
assert(_interfaces != NULL, "invariant");
|
|
||||||
return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME,
|
|
||||||
false, // require safepoint
|
|
||||||
false, // disallow caching; we want a callback every rotation
|
|
||||||
new JfrNetworkInterfaceName());
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrNetworkUtilization::send_events() {
|
void JfrNetworkUtilization::send_events() {
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
NetworkInterface* network_interfaces;
|
NetworkInterface* network_interfaces;
|
||||||
@ -169,7 +169,7 @@ void JfrNetworkUtilization::send_events() {
|
|||||||
const uint64_t read_rate = rate_per_second(current_bytes_in, entry.bytes_in, interval);
|
const uint64_t read_rate = rate_per_second(current_bytes_in, entry.bytes_in, interval);
|
||||||
const uint64_t write_rate = rate_per_second(current_bytes_out, entry.bytes_out, interval);
|
const uint64_t write_rate = rate_per_second(current_bytes_out, entry.bytes_out, interval);
|
||||||
if (read_rate > 0 || write_rate > 0) {
|
if (read_rate > 0 || write_rate > 0) {
|
||||||
entry.in_use = true;
|
write_interface_constant(entry);
|
||||||
EventNetworkUtilization event(UNTIMED);
|
EventNetworkUtilization event(UNTIMED);
|
||||||
event.set_starttime(cur_time);
|
event.set_starttime(cur_time);
|
||||||
event.set_endtime(cur_time);
|
event.set_endtime(cur_time);
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#include "jfr/periodic/jfrNetworkUtilization.hpp"
|
#include "jfr/periodic/jfrNetworkUtilization.hpp"
|
||||||
#include "jfr/recorder/jfrRecorder.hpp"
|
#include "jfr/recorder/jfrRecorder.hpp"
|
||||||
#include "jfr/support/jfrThreadId.hpp"
|
#include "jfr/support/jfrThreadId.hpp"
|
||||||
|
#include "jfr/utilities/jfrThreadIterator.hpp"
|
||||||
#include "jfr/utilities/jfrTime.hpp"
|
#include "jfr/utilities/jfrTime.hpp"
|
||||||
#include "jfrfiles/jfrPeriodic.hpp"
|
#include "jfrfiles/jfrPeriodic.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
@ -56,7 +57,6 @@
|
|||||||
#include "runtime/os.hpp"
|
#include "runtime/os.hpp"
|
||||||
#include "runtime/os_perf.hpp"
|
#include "runtime/os_perf.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
#include "runtime/threadSMR.hpp"
|
|
||||||
#include "runtime/sweeper.hpp"
|
#include "runtime/sweeper.hpp"
|
||||||
#include "runtime/vmThread.hpp"
|
#include "runtime/vmThread.hpp"
|
||||||
#include "services/classLoadingService.hpp"
|
#include "services/classLoadingService.hpp"
|
||||||
@ -410,13 +410,12 @@ TRACE_REQUEST_FUNC(ThreadAllocationStatistics) {
|
|||||||
GrowableArray<jlong> allocated(initial_size);
|
GrowableArray<jlong> allocated(initial_size);
|
||||||
GrowableArray<traceid> thread_ids(initial_size);
|
GrowableArray<traceid> thread_ids(initial_size);
|
||||||
JfrTicks time_stamp = JfrTicks::now();
|
JfrTicks time_stamp = JfrTicks::now();
|
||||||
{
|
JfrJavaThreadIterator iter;
|
||||||
// Collect allocation statistics while holding threads lock
|
while (iter.has_next()) {
|
||||||
MutexLocker ml(Threads_lock);
|
JavaThread* const jt = iter.next();
|
||||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
|
assert(jt != NULL, "invariant");
|
||||||
allocated.append(jt->cooked_allocated_bytes());
|
allocated.append(jt->cooked_allocated_bytes());
|
||||||
thread_ids.append(JFR_THREAD_ID(jt));
|
thread_ids.append(JFR_THREAD_ID(jt));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write allocation statistics to buffer.
|
// Write allocation statistics to buffer.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -28,11 +28,10 @@
|
|||||||
#include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
|
#include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
|
||||||
#include "jfr/support/jfrThreadId.hpp"
|
#include "jfr/support/jfrThreadId.hpp"
|
||||||
#include "jfr/support/jfrThreadLocal.hpp"
|
#include "jfr/support/jfrThreadLocal.hpp"
|
||||||
|
#include "jfr/utilities/jfrThreadIterator.hpp"
|
||||||
#include "jfr/utilities/jfrTime.hpp"
|
#include "jfr/utilities/jfrTime.hpp"
|
||||||
#include "utilities/globalDefinitions.hpp"
|
#include "utilities/globalDefinitions.hpp"
|
||||||
#include "runtime/os.hpp"
|
#include "runtime/os.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
|
||||||
#include "runtime/threadSMR.inline.hpp"
|
|
||||||
|
|
||||||
jlong JfrThreadCPULoadEvent::get_wallclock_time() {
|
jlong JfrThreadCPULoadEvent::get_wallclock_time() {
|
||||||
return os::javaTimeNanos();
|
return os::javaTimeNanos();
|
||||||
@ -115,8 +114,12 @@ void JfrThreadCPULoadEvent::send_events() {
|
|||||||
JfrTicks event_time = JfrTicks::now();
|
JfrTicks event_time = JfrTicks::now();
|
||||||
jlong cur_wallclock_time = JfrThreadCPULoadEvent::get_wallclock_time();
|
jlong cur_wallclock_time = JfrThreadCPULoadEvent::get_wallclock_time();
|
||||||
|
|
||||||
JavaThreadIteratorWithHandle jtiwh;
|
JfrJavaThreadIterator iter;
|
||||||
while (JavaThread* jt = jtiwh.next()) {
|
int number_of_threads = 0;
|
||||||
|
while (iter.has_next()) {
|
||||||
|
JavaThread* const jt = iter.next();
|
||||||
|
assert(jt != NULL, "invariant");
|
||||||
|
++number_of_threads;
|
||||||
EventThreadCPULoad event(UNTIMED);
|
EventThreadCPULoad event(UNTIMED);
|
||||||
if (JfrThreadCPULoadEvent::update_event(event, jt, cur_wallclock_time, processor_count)) {
|
if (JfrThreadCPULoadEvent::update_event(event, jt, cur_wallclock_time, processor_count)) {
|
||||||
event.set_starttime(event_time);
|
event.set_starttime(event_time);
|
||||||
@ -129,7 +132,7 @@ void JfrThreadCPULoadEvent::send_events() {
|
|||||||
event.commit();
|
event.commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log_trace(jfr)("Measured CPU usage for %d threads in %.3f milliseconds", jtiwh.length(),
|
log_trace(jfr)("Measured CPU usage for %d threads in %.3f milliseconds", number_of_threads,
|
||||||
(double)(JfrTicks::now() - event_time).milliseconds());
|
(double)(JfrTicks::now() - event_time).milliseconds());
|
||||||
// Restore this thread's thread id
|
// Restore this thread's thread id
|
||||||
periodic_thread_tl->set_thread_id(periodic_thread_id);
|
periodic_thread_tl->set_thread_id(periodic_thread_id);
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
||||||
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
|
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
|
||||||
#include "jfr/support/jfrThreadId.hpp"
|
#include "jfr/support/jfrThreadId.hpp"
|
||||||
|
#include "jfr/support/jfrThreadLocal.hpp"
|
||||||
#include "jfr/utilities/jfrTime.hpp"
|
#include "jfr/utilities/jfrTime.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "runtime/frame.inline.hpp"
|
#include "runtime/frame.inline.hpp"
|
||||||
@ -352,9 +353,14 @@ static void clear_transition_block(JavaThread* jt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_excluded(JavaThread* thread) {
|
||||||
|
assert(thread != NULL, "invariant");
|
||||||
|
return thread->is_hidden_from_external_view() || thread->in_deopt_handler() || thread->jfr_thread_local()->is_excluded();
|
||||||
|
}
|
||||||
|
|
||||||
bool JfrThreadSampleClosure::do_sample_thread(JavaThread* thread, JfrStackFrame* frames, u4 max_frames, JfrSampleType type) {
|
bool JfrThreadSampleClosure::do_sample_thread(JavaThread* thread, JfrStackFrame* frames, u4 max_frames, JfrSampleType type) {
|
||||||
assert(Threads_lock->owned_by_self(), "Holding the thread table lock.");
|
assert(Threads_lock->owned_by_self(), "Holding the thread table lock.");
|
||||||
if (thread->is_hidden_from_external_view() || thread->in_deopt_handler()) {
|
if (is_excluded(thread)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,11 +37,14 @@
|
|||||||
#include "jfr/recorder/storage/jfrMemorySpace.inline.hpp"
|
#include "jfr/recorder/storage/jfrMemorySpace.inline.hpp"
|
||||||
#include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
|
#include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
|
||||||
#include "jfr/utilities/jfrBigEndian.hpp"
|
#include "jfr/utilities/jfrBigEndian.hpp"
|
||||||
|
#include "jfr/utilities/jfrIterator.hpp"
|
||||||
|
#include "jfr/utilities/jfrThreadIterator.hpp"
|
||||||
#include "jfr/utilities/jfrTypes.hpp"
|
#include "jfr/utilities/jfrTypes.hpp"
|
||||||
|
#include "jfr/writers/jfrJavaEventWriter.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
#include "runtime/handles.inline.hpp"
|
#include "runtime/handles.inline.hpp"
|
||||||
#include "runtime/mutexLocker.hpp"
|
#include "runtime/mutex.hpp"
|
||||||
#include "runtime/orderAccess.hpp"
|
#include "runtime/orderAccess.hpp"
|
||||||
#include "runtime/os.inline.hpp"
|
#include "runtime/os.inline.hpp"
|
||||||
#include "runtime/safepoint.hpp"
|
#include "runtime/safepoint.hpp"
|
||||||
@ -168,7 +171,7 @@ static BufferPtr lease_free(size_t size, JfrCheckpointMspace* mspace, size_t ret
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const {
|
bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const {
|
||||||
return _service_thread != thread && OrderAccess::load_acquire(&_checkpoint_epoch_state) != JfrTraceIdEpoch::epoch();
|
return _service_thread != thread && _checkpoint_epoch_state != JfrTraceIdEpoch::epoch();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const size_t lease_retry = 10;
|
static const size_t lease_retry = 10;
|
||||||
@ -181,12 +184,24 @@ BufferPtr JfrCheckpointManager::lease_buffer(Thread* thread, size_t size /* 0 */
|
|||||||
return lease_free(size, manager._free_list_mspace, lease_retry, thread);
|
return lease_free(size, manager._free_list_mspace, lease_retry, thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JfrCheckpointMspace* JfrCheckpointManager::lookup(BufferPtr old) const {
|
||||||
|
assert(old != NULL, "invariant");
|
||||||
|
return _free_list_mspace->in_free_list(old) ? _free_list_mspace : _epoch_transition_mspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferPtr JfrCheckpointManager::lease_buffer(BufferPtr old, Thread* thread, size_t size /* 0 */) {
|
||||||
|
assert(old != NULL, "invariant");
|
||||||
|
JfrCheckpointMspace* mspace = instance().lookup(old);
|
||||||
|
assert(mspace != NULL, "invariant");
|
||||||
|
return lease_free(size, mspace, lease_retry, thread);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the buffer was a "lease" from the free list, release back.
|
* If the buffer was a lease, release back.
|
||||||
*
|
*
|
||||||
* The buffer is effectively invalidated for the thread post-return,
|
* The buffer is effectively invalidated for the thread post-return,
|
||||||
* and the caller should take means to ensure that it is not referenced.
|
* and the caller should take means to ensure that it is not referenced.
|
||||||
*/
|
*/
|
||||||
static void release(BufferPtr const buffer, Thread* thread) {
|
static void release(BufferPtr const buffer, Thread* thread) {
|
||||||
DEBUG_ONLY(assert_release(buffer);)
|
DEBUG_ONLY(assert_release(buffer);)
|
||||||
buffer->clear_lease();
|
buffer->clear_lease();
|
||||||
@ -202,7 +217,7 @@ BufferPtr JfrCheckpointManager::flush(BufferPtr old, size_t used, size_t request
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
// migration of in-flight information
|
// migration of in-flight information
|
||||||
BufferPtr const new_buffer = lease_buffer(thread, used + requested);
|
BufferPtr const new_buffer = lease_buffer(old, thread, used + requested);
|
||||||
if (new_buffer != NULL) {
|
if (new_buffer != NULL) {
|
||||||
migrate_outstanding_writes(old, new_buffer, used, requested);
|
migrate_outstanding_writes(old, new_buffer, used, requested);
|
||||||
}
|
}
|
||||||
@ -213,8 +228,8 @@ BufferPtr JfrCheckpointManager::flush(BufferPtr old, size_t used, size_t request
|
|||||||
// offsets into the JfrCheckpointEntry
|
// offsets into the JfrCheckpointEntry
|
||||||
static const juint starttime_offset = sizeof(jlong);
|
static const juint starttime_offset = sizeof(jlong);
|
||||||
static const juint duration_offset = starttime_offset + sizeof(jlong);
|
static const juint duration_offset = starttime_offset + sizeof(jlong);
|
||||||
static const juint flushpoint_offset = duration_offset + sizeof(jlong);
|
static const juint checkpoint_type_offset = duration_offset + sizeof(jlong);
|
||||||
static const juint types_offset = flushpoint_offset + sizeof(juint);
|
static const juint types_offset = checkpoint_type_offset + sizeof(juint);
|
||||||
static const juint payload_offset = types_offset + sizeof(juint);
|
static const juint payload_offset = types_offset + sizeof(juint);
|
||||||
|
|
||||||
template <typename Return>
|
template <typename Return>
|
||||||
@ -234,21 +249,21 @@ static jlong duration(const u1* data) {
|
|||||||
return read_data<jlong>(data + duration_offset);
|
return read_data<jlong>(data + duration_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_flushpoint(const u1* data) {
|
static u1 checkpoint_type(const u1* data) {
|
||||||
return read_data<juint>(data + flushpoint_offset) == (juint)1;
|
return read_data<u1>(data + checkpoint_type_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static juint number_of_types(const u1* data) {
|
static juint number_of_types(const u1* data) {
|
||||||
return read_data<juint>(data + types_offset);
|
return read_data<juint>(data + types_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_checkpoint_header(JfrChunkWriter& cw, int64_t offset_prev_cp_event, const u1* data) {
|
static void write_checkpoint_header(JfrChunkWriter& cw, int64_t delta_to_last_checkpoint, const u1* data) {
|
||||||
cw.reserve(sizeof(u4));
|
cw.reserve(sizeof(u4));
|
||||||
cw.write<u8>(EVENT_CHECKPOINT);
|
cw.write<u8>(EVENT_CHECKPOINT);
|
||||||
cw.write(starttime(data));
|
cw.write(starttime(data));
|
||||||
cw.write(duration(data));
|
cw.write(duration(data));
|
||||||
cw.write(offset_prev_cp_event);
|
cw.write(delta_to_last_checkpoint);
|
||||||
cw.write(is_flushpoint(data));
|
cw.write(checkpoint_type(data));
|
||||||
cw.write(number_of_types(data));
|
cw.write(number_of_types(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,9 +276,9 @@ static size_t write_checkpoint_event(JfrChunkWriter& cw, const u1* data) {
|
|||||||
assert(data != NULL, "invariant");
|
assert(data != NULL, "invariant");
|
||||||
const int64_t event_begin = cw.current_offset();
|
const int64_t event_begin = cw.current_offset();
|
||||||
const int64_t last_checkpoint_event = cw.last_checkpoint_offset();
|
const int64_t last_checkpoint_event = cw.last_checkpoint_offset();
|
||||||
const int64_t delta = last_checkpoint_event == 0 ? 0 : last_checkpoint_event - event_begin;
|
const int64_t delta_to_last_checkpoint = last_checkpoint_event == 0 ? 0 : last_checkpoint_event - event_begin;
|
||||||
const int64_t checkpoint_size = total_size(data);
|
const int64_t checkpoint_size = total_size(data);
|
||||||
write_checkpoint_header(cw, delta, data);
|
write_checkpoint_header(cw, delta_to_last_checkpoint, data);
|
||||||
write_checkpoint_content(cw, data, checkpoint_size);
|
write_checkpoint_content(cw, data, checkpoint_size);
|
||||||
const int64_t event_size = cw.current_offset() - event_begin;
|
const int64_t event_size = cw.current_offset() - event_begin;
|
||||||
cw.write_padded_at_offset<u4>(event_size, event_begin);
|
cw.write_padded_at_offset<u4>(event_size, event_begin);
|
||||||
@ -305,13 +320,13 @@ class CheckpointWriteOp {
|
|||||||
typedef CheckpointWriteOp<JfrCheckpointMspace::Type> WriteOperation;
|
typedef CheckpointWriteOp<JfrCheckpointMspace::Type> WriteOperation;
|
||||||
typedef ReleaseOp<JfrCheckpointMspace> CheckpointReleaseOperation;
|
typedef ReleaseOp<JfrCheckpointMspace> CheckpointReleaseOperation;
|
||||||
|
|
||||||
template <template <typename> class WriterHost, template <typename, typename> class CompositeOperation>
|
template <template <typename> class WriterHost, template <typename, typename, typename> class CompositeOperation>
|
||||||
static size_t write_mspace(JfrCheckpointMspace* mspace, JfrChunkWriter& chunkwriter) {
|
static size_t write_mspace(JfrCheckpointMspace* mspace, JfrChunkWriter& chunkwriter) {
|
||||||
assert(mspace != NULL, "invariant");
|
assert(mspace != NULL, "invariant");
|
||||||
WriteOperation wo(chunkwriter);
|
WriteOperation wo(chunkwriter);
|
||||||
WriterHost<WriteOperation> wh(wo);
|
WriterHost<WriteOperation> wh(wo);
|
||||||
CheckpointReleaseOperation cro(mspace, Thread::current(), false);
|
CheckpointReleaseOperation cro(mspace, Thread::current(), false);
|
||||||
CompositeOperation<WriterHost<WriteOperation>, CheckpointReleaseOperation> co(&wh, &cro);
|
CompositeOperation<WriterHost<WriteOperation>, CheckpointReleaseOperation, CompositeOperationAnd> co(&wh, &cro);
|
||||||
assert(mspace->is_full_empty(), "invariant");
|
assert(mspace->is_full_empty(), "invariant");
|
||||||
process_free_list(co, mspace);
|
process_free_list(co, mspace);
|
||||||
return wo.processed();
|
return wo.processed();
|
||||||
@ -333,6 +348,16 @@ size_t JfrCheckpointManager::write_epoch_transition_mspace() {
|
|||||||
return write_mspace<ExclusiveOp, CompositeOperation>(_epoch_transition_mspace, _chunkwriter);
|
return write_mspace<ExclusiveOp, CompositeOperation>(_epoch_transition_mspace, _chunkwriter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef MutexedWriteOp<WriteOperation> FlushOperation;
|
||||||
|
|
||||||
|
size_t JfrCheckpointManager::flush() {
|
||||||
|
WriteOperation wo(_chunkwriter);
|
||||||
|
FlushOperation fo(wo);
|
||||||
|
assert(_free_list_mspace->is_full_empty(), "invariant");
|
||||||
|
process_free_list(fo, _free_list_mspace);
|
||||||
|
return wo.processed();
|
||||||
|
}
|
||||||
|
|
||||||
typedef DiscardOp<DefaultDiscarder<JfrBuffer> > DiscardOperation;
|
typedef DiscardOp<DefaultDiscarder<JfrBuffer> > DiscardOperation;
|
||||||
size_t JfrCheckpointManager::clear() {
|
size_t JfrCheckpointManager::clear() {
|
||||||
JfrTypeSet::clear();
|
JfrTypeSet::clear();
|
||||||
@ -340,44 +365,88 @@ size_t JfrCheckpointManager::clear() {
|
|||||||
process_free_list(discarder, _free_list_mspace);
|
process_free_list(discarder, _free_list_mspace);
|
||||||
process_free_list(discarder, _epoch_transition_mspace);
|
process_free_list(discarder, _epoch_transition_mspace);
|
||||||
synchronize_epoch();
|
synchronize_epoch();
|
||||||
return discarder.processed();
|
return discarder.elements();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrCheckpointManager::write_types() {
|
// Optimization for write_static_type_set() and write_threads() is to write
|
||||||
JfrCheckpointWriter writer(false, true, Thread::current());
|
// directly into the epoch transition mspace because we will immediately
|
||||||
JfrTypeManager::write_types(writer);
|
// serialize and reset this mspace post-write.
|
||||||
|
static JfrBuffer* get_epoch_transition_buffer(JfrCheckpointMspace* mspace, Thread* t) {
|
||||||
|
assert(mspace != NULL, "invariant");
|
||||||
|
JfrBuffer* const buffer = mspace->free_head();
|
||||||
|
assert(buffer != NULL, "invariant");
|
||||||
|
buffer->acquire(t);
|
||||||
|
buffer->set_lease();
|
||||||
|
DEBUG_ONLY(assert_free_lease(buffer);)
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JfrCheckpointManager::is_static_type_set_required() {
|
||||||
|
return JfrTypeManager::has_new_static_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t JfrCheckpointManager::write_static_type_set() {
|
||||||
|
Thread* const t = Thread::current();
|
||||||
|
ResourceMark rm(t);
|
||||||
|
HandleMark hm(t);
|
||||||
|
JfrCheckpointWriter writer(t, get_epoch_transition_buffer(_epoch_transition_mspace, t), STATICS);
|
||||||
|
JfrTypeManager::write_static_types(writer);
|
||||||
return writer.used_size();
|
return writer.used_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrCheckpointManager::write_safepoint_types() {
|
size_t JfrCheckpointManager::write_threads() {
|
||||||
// this is also a "flushpoint"
|
Thread* const t = Thread::current();
|
||||||
JfrCheckpointWriter writer(true, true, Thread::current());
|
ResourceMark rm(t);
|
||||||
JfrTypeManager::write_safepoint_types(writer);
|
HandleMark hm(t);
|
||||||
|
JfrCheckpointWriter writer(t, get_epoch_transition_buffer(_epoch_transition_mspace, t), THREADS);
|
||||||
|
JfrTypeManager::write_threads(writer);
|
||||||
return writer.used_size();
|
return writer.used_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t JfrCheckpointManager::write_static_type_set_and_threads() {
|
||||||
|
write_static_type_set();
|
||||||
|
write_threads();
|
||||||
|
return write_epoch_transition_mspace();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrCheckpointManager::shift_epoch() {
|
||||||
|
debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();)
|
||||||
|
JfrTraceIdEpoch::shift_epoch();
|
||||||
|
assert(current_epoch != JfrTraceIdEpoch::current(), "invariant");
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrCheckpointManager::on_rotation() {
|
||||||
|
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||||
|
JfrTypeManager::on_rotation();
|
||||||
|
notify_threads();
|
||||||
|
}
|
||||||
|
|
||||||
void JfrCheckpointManager::write_type_set() {
|
void JfrCheckpointManager::write_type_set() {
|
||||||
assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
|
assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||||
// can safepoint here
|
if (LeakProfiler::is_running()) {
|
||||||
MutexLocker cld_lock(ClassLoaderDataGraph_lock);
|
|
||||||
MutexLocker module_lock(Module_lock);
|
|
||||||
if (!LeakProfiler::is_running()) {
|
|
||||||
JfrCheckpointWriter writer(true, true, Thread::current());
|
|
||||||
JfrTypeSet::serialize(&writer, NULL, false);
|
|
||||||
} else {
|
|
||||||
Thread* const t = Thread::current();
|
Thread* const t = Thread::current();
|
||||||
JfrCheckpointWriter leakp_writer(false, true, t);
|
// can safepoint here
|
||||||
JfrCheckpointWriter writer(false, true, t);
|
MutexLocker cld_lock(ClassLoaderDataGraph_lock);
|
||||||
JfrTypeSet::serialize(&writer, &leakp_writer, false);
|
MutexLocker module_lock(Module_lock);
|
||||||
|
JfrCheckpointWriter leakp_writer(t);
|
||||||
|
JfrCheckpointWriter writer(t);
|
||||||
|
JfrTypeSet::serialize(&writer, &leakp_writer, false, false);
|
||||||
ObjectSampleCheckpoint::on_type_set(leakp_writer);
|
ObjectSampleCheckpoint::on_type_set(leakp_writer);
|
||||||
|
} else {
|
||||||
|
// can safepoint here
|
||||||
|
MutexLocker cld_lock(ClassLoaderDataGraph_lock);
|
||||||
|
MutexLocker module_lock(Module_lock);
|
||||||
|
JfrCheckpointWriter writer(Thread::current());
|
||||||
|
JfrTypeSet::serialize(&writer, NULL, false, false);
|
||||||
}
|
}
|
||||||
|
write();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrCheckpointManager::write_type_set_for_unloaded_classes() {
|
void JfrCheckpointManager::write_type_set_for_unloaded_classes() {
|
||||||
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
||||||
JfrCheckpointWriter writer(false, true, Thread::current());
|
JfrCheckpointWriter writer(Thread::current());
|
||||||
const JfrCheckpointContext ctx = writer.context();
|
const JfrCheckpointContext ctx = writer.context();
|
||||||
JfrTypeSet::serialize(&writer, NULL, true);
|
JfrTypeSet::serialize(&writer, NULL, true, false);
|
||||||
if (LeakProfiler::is_running()) {
|
if (LeakProfiler::is_running()) {
|
||||||
ObjectSampleCheckpoint::on_type_set_unload(writer);
|
ObjectSampleCheckpoint::on_type_set_unload(writer);
|
||||||
}
|
}
|
||||||
@ -387,16 +456,51 @@ void JfrCheckpointManager::write_type_set_for_unloaded_classes() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrCheckpointManager::create_thread_blob(JavaThread* jt) {
|
bool JfrCheckpointManager::is_type_set_required() {
|
||||||
JfrTypeManager::create_thread_blob(jt);
|
return JfrTraceIdEpoch::has_changed_tag_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrCheckpointManager::write_thread_checkpoint(JavaThread* jt) {
|
size_t JfrCheckpointManager::flush_type_set() {
|
||||||
JfrTypeManager::write_thread_checkpoint(jt);
|
assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||||
|
size_t elements = 0;
|
||||||
|
{
|
||||||
|
JfrCheckpointWriter writer(Thread::current());
|
||||||
|
// can safepoint here
|
||||||
|
MutexLocker cld_lock(ClassLoaderDataGraph_lock);
|
||||||
|
MutexLocker module_lock(Module_lock);
|
||||||
|
elements = JfrTypeSet::serialize(&writer, NULL, false, true);
|
||||||
|
}
|
||||||
|
flush();
|
||||||
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrCheckpointManager::shift_epoch() {
|
void JfrCheckpointManager::flush_static_type_set() {
|
||||||
debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();)
|
flush();
|
||||||
JfrTraceIdEpoch::shift_epoch();
|
}
|
||||||
assert(current_epoch != JfrTraceIdEpoch::current(), "invariant");
|
|
||||||
|
void JfrCheckpointManager::create_thread_blob(Thread* t) {
|
||||||
|
JfrTypeManager::create_thread_blob(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrCheckpointManager::write_thread_checkpoint(Thread* t) {
|
||||||
|
JfrTypeManager::write_thread_checkpoint(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
class JfrNotifyClosure : public ThreadClosure {
|
||||||
|
public:
|
||||||
|
void do_thread(Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
assert(t->is_Java_thread(), "invariant");
|
||||||
|
assert_locked_or_safepoint(Threads_lock);
|
||||||
|
JfrJavaEventWriter::notify((JavaThread*)t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void JfrCheckpointManager::notify_threads() {
|
||||||
|
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||||
|
JfrNotifyClosure tc;
|
||||||
|
JfrJavaThreadIterator iter;
|
||||||
|
while (iter.has_next()) {
|
||||||
|
tc.do_thread(iter.next());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,18 +68,29 @@ class JfrCheckpointManager : public JfrCHeapObj {
|
|||||||
void unlock();
|
void unlock();
|
||||||
DEBUG_ONLY(bool is_locked() const;)
|
DEBUG_ONLY(bool is_locked() const;)
|
||||||
|
|
||||||
|
JfrCheckpointMspace* lookup(Buffer* old) const;
|
||||||
|
bool use_epoch_transition_mspace(const Thread* t) const;
|
||||||
|
size_t write_epoch_transition_mspace();
|
||||||
|
|
||||||
static Buffer* lease_buffer(Thread* t, size_t size = 0);
|
static Buffer* lease_buffer(Thread* t, size_t size = 0);
|
||||||
|
static Buffer* lease_buffer(Buffer* old, Thread* t, size_t size = 0);
|
||||||
static Buffer* flush(Buffer* old, size_t used, size_t requested, Thread* t);
|
static Buffer* flush(Buffer* old, size_t used, size_t requested, Thread* t);
|
||||||
|
|
||||||
size_t clear();
|
size_t clear();
|
||||||
size_t write();
|
size_t write();
|
||||||
size_t write_epoch_transition_mspace();
|
size_t flush();
|
||||||
size_t write_types();
|
|
||||||
size_t write_safepoint_types();
|
bool is_static_type_set_required();
|
||||||
|
size_t write_static_type_set();
|
||||||
|
size_t write_threads();
|
||||||
|
size_t write_static_type_set_and_threads();
|
||||||
|
bool is_type_set_required();
|
||||||
void write_type_set();
|
void write_type_set();
|
||||||
|
static void write_type_set_for_unloaded_classes();
|
||||||
|
|
||||||
void shift_epoch();
|
void shift_epoch();
|
||||||
void synchronize_epoch();
|
void synchronize_epoch();
|
||||||
bool use_epoch_transition_mspace(const Thread* t) const;
|
void notify_threads();
|
||||||
|
|
||||||
JfrCheckpointManager(JfrChunkWriter& cw);
|
JfrCheckpointManager(JfrChunkWriter& cw);
|
||||||
~JfrCheckpointManager();
|
~JfrCheckpointManager();
|
||||||
@ -87,14 +98,17 @@ class JfrCheckpointManager : public JfrCHeapObj {
|
|||||||
static JfrCheckpointManager& instance();
|
static JfrCheckpointManager& instance();
|
||||||
static JfrCheckpointManager* create(JfrChunkWriter& cw);
|
static JfrCheckpointManager* create(JfrChunkWriter& cw);
|
||||||
bool initialize();
|
bool initialize();
|
||||||
|
void on_rotation();
|
||||||
static void destroy();
|
static void destroy();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
size_t flush_type_set();
|
||||||
|
void flush_static_type_set();
|
||||||
|
static void create_thread_blob(Thread* t);
|
||||||
|
static void write_thread_checkpoint(Thread* t);
|
||||||
void register_service_thread(const Thread* t);
|
void register_service_thread(const Thread* t);
|
||||||
static void write_type_set_for_unloaded_classes();
|
|
||||||
static void create_thread_blob(JavaThread* jt);
|
|
||||||
static void write_thread_checkpoint(JavaThread* jt);
|
|
||||||
|
|
||||||
|
friend class Jfr;
|
||||||
friend class JfrRecorder;
|
friend class JfrRecorder;
|
||||||
friend class JfrRecorderService;
|
friend class JfrRecorderService;
|
||||||
friend class JfrCheckpointFlush;
|
friend class JfrCheckpointFlush;
|
||||||
|
@ -31,12 +31,26 @@
|
|||||||
JfrCheckpointFlush::JfrCheckpointFlush(Type* old, size_t used, size_t requested, Thread* t) :
|
JfrCheckpointFlush::JfrCheckpointFlush(Type* old, size_t used, size_t requested, Thread* t) :
|
||||||
_result(JfrCheckpointManager::flush(old, used, requested, t)) {}
|
_result(JfrCheckpointManager::flush(old, used, requested, t)) {}
|
||||||
|
|
||||||
JfrCheckpointWriter::JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread) :
|
JfrCheckpointWriter::JfrCheckpointWriter(JfrCheckpointType type /* GENERIC */) :
|
||||||
JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(thread), thread),
|
JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(Thread::current()), Thread::current()),
|
||||||
_time(JfrTicks::now()),
|
_time(JfrTicks::now()),
|
||||||
_offset(0),
|
_offset(0),
|
||||||
_count(0),
|
_count(0),
|
||||||
_flushpoint(flushpoint),
|
_type(type),
|
||||||
|
_header(true) {
|
||||||
|
assert(this->is_acquired(), "invariant");
|
||||||
|
assert(0 == this->current_offset(), "invariant");
|
||||||
|
if (_header) {
|
||||||
|
reserve(sizeof(JfrCheckpointEntry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JfrCheckpointWriter::JfrCheckpointWriter(Thread* t, bool header /* true */, JfrCheckpointType type /* GENERIC */) :
|
||||||
|
JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(t), t),
|
||||||
|
_time(JfrTicks::now()),
|
||||||
|
_offset(0),
|
||||||
|
_count(0),
|
||||||
|
_type(type),
|
||||||
_header(header) {
|
_header(header) {
|
||||||
assert(this->is_acquired(), "invariant");
|
assert(this->is_acquired(), "invariant");
|
||||||
assert(0 == this->current_offset(), "invariant");
|
assert(0 == this->current_offset(), "invariant");
|
||||||
@ -45,13 +59,27 @@ JfrCheckpointWriter::JfrCheckpointWriter(bool flushpoint, bool header, Thread* t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_checkpoint_header(u1* pos, int64_t size, jlong time, bool flushpoint, u4 type_count) {
|
JfrCheckpointWriter::JfrCheckpointWriter(Thread* t, JfrBuffer* buffer, JfrCheckpointType type /* GENERIC */) :
|
||||||
|
JfrCheckpointWriterBase(buffer, t),
|
||||||
|
_time(JfrTicks::now()),
|
||||||
|
_offset(0),
|
||||||
|
_count(0),
|
||||||
|
_type(type),
|
||||||
|
_header(true) {
|
||||||
|
assert(this->is_acquired(), "invariant");
|
||||||
|
assert(0 == this->current_offset(), "invariant");
|
||||||
|
if (_header) {
|
||||||
|
reserve(sizeof(JfrCheckpointEntry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_checkpoint_header(u1* pos, int64_t size, jlong time, u4 checkpoint_type, u4 type_count) {
|
||||||
assert(pos != NULL, "invariant");
|
assert(pos != NULL, "invariant");
|
||||||
JfrBigEndianWriter be_writer(pos, sizeof(JfrCheckpointEntry));
|
JfrBigEndianWriter be_writer(pos, sizeof(JfrCheckpointEntry));
|
||||||
be_writer.write(size);
|
be_writer.write(size);
|
||||||
be_writer.write(time);
|
be_writer.write(time);
|
||||||
be_writer.write(JfrTicks::now().value() - time);
|
be_writer.write(JfrTicks::now().value() - time);
|
||||||
be_writer.write(flushpoint ? (u4)1 : (u4)0);
|
be_writer.write(checkpoint_type);
|
||||||
be_writer.write(type_count);
|
be_writer.write(type_count);
|
||||||
assert(be_writer.is_valid(), "invariant");
|
assert(be_writer.is_valid(), "invariant");
|
||||||
}
|
}
|
||||||
@ -74,18 +102,10 @@ JfrCheckpointWriter::~JfrCheckpointWriter() {
|
|||||||
assert(this->used_size() > sizeof(JfrCheckpointEntry), "invariant");
|
assert(this->used_size() > sizeof(JfrCheckpointEntry), "invariant");
|
||||||
const int64_t size = this->current_offset();
|
const int64_t size = this->current_offset();
|
||||||
assert(size + this->start_pos() == this->current_pos(), "invariant");
|
assert(size + this->start_pos() == this->current_pos(), "invariant");
|
||||||
write_checkpoint_header(const_cast<u1*>(this->start_pos()), size, _time, is_flushpoint(), count());
|
write_checkpoint_header(const_cast<u1*>(this->start_pos()), size, _time, (u4)_type, count());
|
||||||
release();
|
release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrCheckpointWriter::set_flushpoint(bool flushpoint) {
|
|
||||||
_flushpoint = flushpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JfrCheckpointWriter::is_flushpoint() const {
|
|
||||||
return _flushpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
u4 JfrCheckpointWriter::count() const {
|
u4 JfrCheckpointWriter::count() const {
|
||||||
return _count;
|
return _count;
|
||||||
}
|
}
|
||||||
@ -140,7 +160,7 @@ const u1* JfrCheckpointWriter::session_data(size_t* size, bool move /* false */,
|
|||||||
}
|
}
|
||||||
*size = this->used_size();
|
*size = this->used_size();
|
||||||
assert(this->start_pos() + *size == this->current_pos(), "invariant");
|
assert(this->start_pos() + *size == this->current_pos(), "invariant");
|
||||||
write_checkpoint_header(const_cast<u1*>(this->start_pos()), this->used_offset(), _time, is_flushpoint(), count());
|
write_checkpoint_header(const_cast<u1*>(this->start_pos()), this->used_offset(), _time, (u4)_type, count());
|
||||||
_header = false; // the header was just written
|
_header = false; // the header was just written
|
||||||
if (move) {
|
if (move) {
|
||||||
this->seek(_offset);
|
this->seek(_offset);
|
||||||
|
@ -54,23 +54,24 @@ struct JfrCheckpointContext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class JfrCheckpointWriter : public JfrCheckpointWriterBase {
|
class JfrCheckpointWriter : public JfrCheckpointWriterBase {
|
||||||
|
friend class JfrCheckpointManager;
|
||||||
friend class JfrSerializerRegistration;
|
friend class JfrSerializerRegistration;
|
||||||
private:
|
private:
|
||||||
JfrTicks _time;
|
JfrTicks _time;
|
||||||
int64_t _offset;
|
int64_t _offset;
|
||||||
u4 _count;
|
u4 _count;
|
||||||
bool _flushpoint;
|
JfrCheckpointType _type;
|
||||||
bool _header;
|
bool _header;
|
||||||
|
|
||||||
u4 count() const;
|
u4 count() const;
|
||||||
void set_count(u4 count);
|
void set_count(u4 count);
|
||||||
void increment();
|
void increment();
|
||||||
void set_flushpoint(bool flushpoint);
|
|
||||||
bool is_flushpoint() const;
|
|
||||||
const u1* session_data(size_t* size, bool move = false, const JfrCheckpointContext* ctx = NULL);
|
const u1* session_data(size_t* size, bool move = false, const JfrCheckpointContext* ctx = NULL);
|
||||||
void release();
|
void release();
|
||||||
|
JfrCheckpointWriter(Thread* t, JfrBuffer* buffer, JfrCheckpointType type = GENERIC);
|
||||||
public:
|
public:
|
||||||
JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread);
|
JfrCheckpointWriter(JfrCheckpointType type = GENERIC);
|
||||||
|
JfrCheckpointWriter(Thread* t, bool header = true, JfrCheckpointType mode = GENERIC);
|
||||||
~JfrCheckpointWriter();
|
~JfrCheckpointWriter();
|
||||||
void write_type(JfrTypeId type_id);
|
void write_type(JfrTypeId type_id);
|
||||||
void write_count(u4 nof_entries);
|
void write_count(u4 nof_entries);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -29,61 +29,53 @@
|
|||||||
#include "oops/klass.inline.hpp"
|
#include "oops/klass.inline.hpp"
|
||||||
#include "oops/oop.inline.hpp"
|
#include "oops/oop.inline.hpp"
|
||||||
#include "oops/typeArrayOop.inline.hpp"
|
#include "oops/typeArrayOop.inline.hpp"
|
||||||
#include "runtime/semaphore.hpp"
|
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
|
|
||||||
static jbyteArray _metadata_blob = NULL;
|
static jbyteArray metadata_blob = NULL;
|
||||||
static Semaphore metadata_mutex_semaphore(1);
|
static u8 metadata_id = 0;
|
||||||
|
static u8 last_metadata_id = 0;
|
||||||
|
|
||||||
void JfrMetadataEvent::lock() {
|
static void write_metadata_blob(JfrChunkWriter& chunkwriter) {
|
||||||
metadata_mutex_semaphore.wait();
|
assert(metadata_blob != NULL, "invariant");
|
||||||
|
const typeArrayOop arr = (typeArrayOop)JfrJavaSupport::resolve_non_null(metadata_blob);
|
||||||
|
assert(arr != NULL, "invariant");
|
||||||
|
const int length = arr->length();
|
||||||
|
const Klass* const k = arr->klass();
|
||||||
|
assert(k != NULL && k->is_array_klass(), "invariant");
|
||||||
|
const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast(k);
|
||||||
|
const jbyte* const data_address = arr->byte_at_addr(0);
|
||||||
|
chunkwriter.write_unbuffered(data_address, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrMetadataEvent::unlock() {
|
void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
|
||||||
metadata_mutex_semaphore.signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void write_metadata_blob(JfrChunkWriter& chunkwriter, jbyteArray metadata_blob) {
|
|
||||||
if (metadata_blob != NULL) {
|
|
||||||
const typeArrayOop arr = (typeArrayOop)JfrJavaSupport::resolve_non_null(metadata_blob);
|
|
||||||
assert(arr != NULL, "invariant");
|
|
||||||
const int length = arr->length();
|
|
||||||
const Klass* const k = arr->klass();
|
|
||||||
assert(k != NULL && k->is_array_klass(), "invariant");
|
|
||||||
const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast(k);
|
|
||||||
const jbyte* const data_address = arr->byte_at_addr(0);
|
|
||||||
chunkwriter.write_unbuffered(data_address, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the semaphore is assumed to be locked (was locked previous safepoint)
|
|
||||||
size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter, jlong metadata_offset) {
|
|
||||||
assert(chunkwriter.is_valid(), "invariant");
|
assert(chunkwriter.is_valid(), "invariant");
|
||||||
assert(chunkwriter.current_offset() == metadata_offset, "invariant");
|
if (last_metadata_id == metadata_id && chunkwriter.has_metadata()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// header
|
// header
|
||||||
chunkwriter.reserve(sizeof(u4));
|
const int64_t metadata_offset = chunkwriter.reserve(sizeof(u4));
|
||||||
chunkwriter.write<u8>(EVENT_METADATA); // ID 0
|
chunkwriter.write<u8>(EVENT_METADATA); // ID 0
|
||||||
// time data
|
// time data
|
||||||
chunkwriter.write(JfrTicks::now());
|
chunkwriter.write(JfrTicks::now());
|
||||||
chunkwriter.write((u8)0); // duration
|
chunkwriter.write((u8)0); // duration
|
||||||
chunkwriter.write((u8)0); // metadata id
|
chunkwriter.write(metadata_id); // metadata id
|
||||||
write_metadata_blob(chunkwriter, _metadata_blob); // payload
|
write_metadata_blob(chunkwriter); // payload
|
||||||
unlock(); // open up for java to provide updated metadata
|
|
||||||
// fill in size of metadata descriptor event
|
// fill in size of metadata descriptor event
|
||||||
const jlong size_written = chunkwriter.current_offset() - metadata_offset;
|
const int64_t size_written = chunkwriter.current_offset() - metadata_offset;
|
||||||
chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset);
|
chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset);
|
||||||
return size_written;
|
chunkwriter.set_last_metadata_offset(metadata_offset);
|
||||||
|
last_metadata_id = metadata_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrMetadataEvent::update(jbyteArray metadata) {
|
void JfrMetadataEvent::update(jbyteArray metadata) {
|
||||||
JavaThread* thread = (JavaThread*)Thread::current();
|
JavaThread* thread = (JavaThread*)Thread::current();
|
||||||
assert(thread->is_Java_thread(), "invariant");
|
assert(thread->is_Java_thread(), "invariant");
|
||||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));
|
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));
|
||||||
lock();
|
if (metadata_blob != NULL) {
|
||||||
if (_metadata_blob != NULL) {
|
JfrJavaSupport::destroy_global_jni_handle(metadata_blob);
|
||||||
JfrJavaSupport::destroy_global_jni_handle(_metadata_blob);
|
|
||||||
}
|
}
|
||||||
const oop new_desc_oop = JfrJavaSupport::resolve_non_null(metadata);
|
const oop new_desc_oop = JfrJavaSupport::resolve_non_null(metadata);
|
||||||
_metadata_blob = new_desc_oop != NULL ? (jbyteArray)JfrJavaSupport::global_jni_handle(new_desc_oop, thread) : NULL;
|
assert(new_desc_oop != NULL, "invariant");
|
||||||
unlock();
|
metadata_blob = (jbyteArray)JfrJavaSupport::global_jni_handle(new_desc_oop, thread);
|
||||||
|
++metadata_id;
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,10 @@ class JfrChunkWriter;
|
|||||||
//
|
//
|
||||||
// Metadata is continuously updated in Java as event classes are loaded / unloaded.
|
// Metadata is continuously updated in Java as event classes are loaded / unloaded.
|
||||||
// Using update(), Java stores a binary representation back to native.
|
// Using update(), Java stores a binary representation back to native.
|
||||||
// This is for easy access on chunk finalization as well as having it readily available in the case of fatal error.
|
|
||||||
//
|
//
|
||||||
class JfrMetadataEvent : AllStatic {
|
class JfrMetadataEvent : AllStatic {
|
||||||
public:
|
public:
|
||||||
static void lock();
|
static void write(JfrChunkWriter& writer);
|
||||||
static void unlock();
|
|
||||||
static size_t write(JfrChunkWriter& writer, jlong metadata_offset);
|
|
||||||
static void update(jbyteArray metadata);
|
static void update(jbyteArray metadata);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
|
#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
|
||||||
#include "jfr/utilities/jfrResourceManager.hpp"
|
|
||||||
#include "jfr/utilities/jfrTypes.hpp"
|
#include "jfr/utilities/jfrTypes.hpp"
|
||||||
#include "runtime/handles.inline.hpp"
|
#include "runtime/handles.inline.hpp"
|
||||||
#include "runtime/jniHandles.inline.hpp"
|
#include "runtime/jniHandles.inline.hpp"
|
||||||
@ -33,6 +32,8 @@
|
|||||||
#include "runtime/semaphore.hpp"
|
#include "runtime/semaphore.hpp"
|
||||||
#include "utilities/growableArray.hpp"
|
#include "utilities/growableArray.hpp"
|
||||||
|
|
||||||
|
static const int initial_array_size = 30;
|
||||||
|
|
||||||
class ThreadGroupExclusiveAccess : public StackObj {
|
class ThreadGroupExclusiveAccess : public StackObj {
|
||||||
private:
|
private:
|
||||||
static Semaphore _mutex_semaphore;
|
static Semaphore _mutex_semaphore;
|
||||||
@ -257,12 +258,10 @@ void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group(JfrThreadGroupPointer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JfrThreadGroup::JfrThreadGroup() : _list(NULL) {
|
JfrThreadGroup::JfrThreadGroup() :
|
||||||
_list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(30, true);
|
_list(new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(initial_array_size, true, mtTracing)) {}
|
||||||
}
|
|
||||||
|
|
||||||
JfrThreadGroup::~JfrThreadGroup() {
|
JfrThreadGroup::~JfrThreadGroup() {
|
||||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
|
||||||
if (_list != NULL) {
|
if (_list != NULL) {
|
||||||
for (int i = 0; i < _list->length(); i++) {
|
for (int i = 0; i < _list->length(); i++) {
|
||||||
JfrThreadGroupEntry* e = _list->at(i);
|
JfrThreadGroupEntry* e = _list->at(i);
|
||||||
@ -281,14 +280,11 @@ void JfrThreadGroup::set_instance(JfrThreadGroup* new_instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
traceid JfrThreadGroup::thread_group_id(const JavaThread* jt, Thread* current) {
|
traceid JfrThreadGroup::thread_group_id(const JavaThread* jt, Thread* current) {
|
||||||
ResourceMark rm(current);
|
|
||||||
HandleMark hm(current);
|
|
||||||
JfrThreadGroupsHelper helper(jt, current);
|
JfrThreadGroupsHelper helper(jt, current);
|
||||||
return helper.is_valid() ? thread_group_id_internal(helper) : 0;
|
return helper.is_valid() ? thread_group_id_internal(helper) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
traceid JfrThreadGroup::thread_group_id(JavaThread* const jt) {
|
traceid JfrThreadGroup::thread_group_id(JavaThread* const jt) {
|
||||||
assert(!JfrStream_lock->owned_by_self(), "holding stream lock but should not hold it here");
|
|
||||||
return thread_group_id(jt, jt);
|
return thread_group_id(jt, jt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,9 +392,7 @@ void JfrThreadGroup::serialize(JfrCheckpointWriter& writer) {
|
|||||||
ThreadGroupExclusiveAccess lock;
|
ThreadGroupExclusiveAccess lock;
|
||||||
JfrThreadGroup* tg_instance = instance();
|
JfrThreadGroup* tg_instance = instance();
|
||||||
assert(tg_instance != NULL, "invariant");
|
assert(tg_instance != NULL, "invariant");
|
||||||
ResourceManager<JfrThreadGroup> tg_handle(tg_instance);
|
tg_instance->write_thread_group_entries(writer);
|
||||||
set_instance(NULL);
|
|
||||||
tg_handle->write_thread_group_entries(writer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// for writing a particular thread group
|
// for writing a particular thread group
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,9 +23,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "classfile/javaClasses.inline.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/jfrThreadState.hpp"
|
#include "jfr/recorder/checkpoint/types/jfrThreadState.hpp"
|
||||||
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
||||||
|
#include "jfr/support/jfrThreadLocal.hpp"
|
||||||
#include "jvmtifiles/jvmti.h"
|
#include "jvmtifiles/jvmti.h"
|
||||||
|
#include "runtime/osThread.hpp"
|
||||||
|
#include "runtime/thread.hpp"
|
||||||
|
|
||||||
struct jvmti_thread_state {
|
struct jvmti_thread_state {
|
||||||
u8 id;
|
u8 id;
|
||||||
@ -80,3 +84,47 @@ void JfrThreadState::serialize(JfrCheckpointWriter& writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
traceid JfrThreadId::id(const Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
if (!t->is_Java_thread()) {
|
||||||
|
return os_id(t);
|
||||||
|
}
|
||||||
|
const JavaThread* const jt = (JavaThread*)t;
|
||||||
|
const oop thread_obj = jt->threadObj();
|
||||||
|
return thread_obj != NULL ? java_lang_Thread::thread_id(thread_obj) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
traceid JfrThreadId::os_id(const Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
const OSThread* const os_thread = t->osthread();
|
||||||
|
return os_thread != NULL ? os_thread->thread_id() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
traceid JfrThreadId::jfr_id(const Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
return t->jfr_thread_local()->thread_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
// caller needs ResourceMark
|
||||||
|
const char* get_java_thread_name(const Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
assert(t->is_Java_thread(), "invariant");
|
||||||
|
const JavaThread* const jt = ((JavaThread*)t);
|
||||||
|
const char* name_str = "<no-name - thread name unresolved>";
|
||||||
|
const oop thread_obj = jt->threadObj();
|
||||||
|
if (thread_obj != NULL) {
|
||||||
|
const oop name = java_lang_Thread::name(thread_obj);
|
||||||
|
if (name != NULL) {
|
||||||
|
name_str = java_lang_String::as_utf8_string(name);
|
||||||
|
}
|
||||||
|
} else if (jt->is_attaching_via_jni()) {
|
||||||
|
name_str = "<no-name - thread is attaching>";
|
||||||
|
}
|
||||||
|
assert(name_str != NULL, "unexpected NULL thread name");
|
||||||
|
return name_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* JfrThreadName::name(const Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
return t->is_Java_thread() ? get_java_thread_name(t) : t->name();
|
||||||
|
}
|
||||||
|
@ -28,10 +28,24 @@
|
|||||||
#include "memory/allocation.hpp"
|
#include "memory/allocation.hpp"
|
||||||
|
|
||||||
class JfrCheckpointWriter;
|
class JfrCheckpointWriter;
|
||||||
|
class Thread;
|
||||||
|
|
||||||
class JfrThreadState : public AllStatic {
|
class JfrThreadState : public AllStatic {
|
||||||
public:
|
public:
|
||||||
static void serialize(JfrCheckpointWriter& writer);
|
static void serialize(JfrCheckpointWriter& writer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class JfrThreadId : public AllStatic {
|
||||||
|
public:
|
||||||
|
static traceid id(const Thread* t);
|
||||||
|
static traceid os_id(const Thread* t);
|
||||||
|
static traceid jfr_id(const Thread* t);
|
||||||
|
};
|
||||||
|
|
||||||
|
class JfrThreadName : public AllStatic {
|
||||||
|
public:
|
||||||
|
// Requires a ResourceMark for get_thread_name/as_utf8
|
||||||
|
static const char* name(const Thread* t);
|
||||||
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP
|
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP
|
||||||
|
@ -31,13 +31,14 @@
|
|||||||
#include "gc/shared/gcTrace.hpp"
|
#include "gc/shared/gcTrace.hpp"
|
||||||
#include "gc/shared/gcWhen.hpp"
|
#include "gc/shared/gcWhen.hpp"
|
||||||
#include "jfr/leakprofiler/leakProfiler.hpp"
|
#include "jfr/leakprofiler/leakProfiler.hpp"
|
||||||
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
|
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/jfrType.hpp"
|
#include "jfr/recorder/checkpoint/types/jfrType.hpp"
|
||||||
#include "jfr/recorder/jfrRecorder.hpp"
|
#include "jfr/recorder/jfrRecorder.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
|
#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/jfrThreadState.hpp"
|
#include "jfr/recorder/checkpoint/types/jfrThreadState.hpp"
|
||||||
#include "jfr/support/jfrThreadLocal.hpp"
|
#include "jfr/support/jfrThreadLocal.hpp"
|
||||||
#include "jfr/writers/jfrJavaEventWriter.hpp"
|
#include "jfr/writers/jfrJavaEventWriter.hpp"
|
||||||
|
#include "jfr/utilities/jfrThreadIterator.hpp"
|
||||||
#include "memory/metaspaceGCThresholdUpdater.hpp"
|
#include "memory/metaspaceGCThresholdUpdater.hpp"
|
||||||
#include "memory/referenceType.hpp"
|
#include "memory/referenceType.hpp"
|
||||||
#include "memory/universe.hpp"
|
#include "memory/universe.hpp"
|
||||||
@ -84,27 +85,18 @@ class JfrCheckpointThreadClosure : public ThreadClosure {
|
|||||||
void do_thread(Thread* t);
|
void do_thread(Thread* t);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Requires a ResourceMark for get_thread_name/as_utf8
|
|
||||||
void JfrCheckpointThreadClosure::do_thread(Thread* t) {
|
void JfrCheckpointThreadClosure::do_thread(Thread* t) {
|
||||||
assert(t != NULL, "invariant");
|
assert(t != NULL, "invariant");
|
||||||
assert_locked_or_safepoint(Threads_lock);
|
|
||||||
const JfrThreadLocal* const tl = t->jfr_thread_local();
|
|
||||||
assert(tl != NULL, "invariant");
|
|
||||||
if (tl->is_dead()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
++_count;
|
++_count;
|
||||||
_writer.write_key(tl->thread_id());
|
_writer.write_key(JfrThreadId::jfr_id(t));
|
||||||
_writer.write(t->name());
|
const char* const name = JfrThreadName::name(t);
|
||||||
const OSThread* const os_thread = t->osthread();
|
assert(name != NULL, "invariant");
|
||||||
_writer.write<traceid>(os_thread != NULL ? os_thread->thread_id() : 0);
|
_writer.write(name);
|
||||||
|
_writer.write<traceid>(JfrThreadId::os_id(t));
|
||||||
if (t->is_Java_thread()) {
|
if (t->is_Java_thread()) {
|
||||||
JavaThread* const jt = (JavaThread*)t;
|
_writer.write(name);
|
||||||
_writer.write(jt->name());
|
_writer.write(JfrThreadId::id(t));
|
||||||
_writer.write(java_lang_Thread::thread_id(jt->threadObj()));
|
_writer.write(JfrThreadGroup::thread_group_id((JavaThread*)t, _curthread));
|
||||||
_writer.write(JfrThreadGroup::thread_group_id(jt, _curthread));
|
|
||||||
// since we are iterating threads during a safepoint, also issue notification
|
|
||||||
JfrJavaEventWriter::notify(jt);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_writer.write((const char*)NULL); // java name
|
_writer.write((const char*)NULL); // java name
|
||||||
@ -113,13 +105,18 @@ void JfrCheckpointThreadClosure::do_thread(Thread* t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JfrThreadConstantSet::serialize(JfrCheckpointWriter& writer) {
|
void JfrThreadConstantSet::serialize(JfrCheckpointWriter& writer) {
|
||||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
|
||||||
JfrCheckpointThreadClosure tc(writer);
|
JfrCheckpointThreadClosure tc(writer);
|
||||||
Threads::threads_do(&tc);
|
JfrJavaThreadIterator javathreads;
|
||||||
|
while (javathreads.has_next()) {
|
||||||
|
tc.do_thread(javathreads.next());
|
||||||
|
}
|
||||||
|
JfrNonJavaThreadIterator nonjavathreads;
|
||||||
|
while (nonjavathreads.has_next()) {
|
||||||
|
tc.do_thread(nonjavathreads.next());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrThreadGroupConstant::serialize(JfrCheckpointWriter& writer) {
|
void JfrThreadGroupConstant::serialize(JfrCheckpointWriter& writer) {
|
||||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
|
||||||
JfrThreadGroup::serialize(writer);
|
JfrThreadGroup::serialize(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,19 +275,21 @@ void ThreadStateConstant::serialize(JfrCheckpointWriter& writer) {
|
|||||||
void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) {
|
void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) {
|
||||||
assert(_thread != NULL, "invariant");
|
assert(_thread != NULL, "invariant");
|
||||||
assert(_thread == Thread::current(), "invariant");
|
assert(_thread == Thread::current(), "invariant");
|
||||||
assert(_thread->is_Java_thread(), "invariant");
|
|
||||||
ResourceMark rm(_thread);
|
|
||||||
const oop threadObj = _thread->threadObj();
|
|
||||||
assert(threadObj != NULL, "invariant");
|
|
||||||
const u8 java_lang_thread_id = java_lang_Thread::thread_id(threadObj);
|
|
||||||
const char* const thread_name = _thread->name();
|
|
||||||
const traceid thread_group_id = JfrThreadGroup::thread_group_id(_thread);
|
|
||||||
writer.write_count(1);
|
writer.write_count(1);
|
||||||
writer.write_key(_thread->jfr_thread_local()->thread_id());
|
writer.write_key(JfrThreadId::jfr_id(_thread));
|
||||||
writer.write(thread_name);
|
const char* const name = JfrThreadName::name(_thread);
|
||||||
writer.write((traceid)_thread->osthread()->thread_id());
|
writer.write(name);
|
||||||
writer.write(thread_name);
|
writer.write(JfrThreadId::os_id(_thread));
|
||||||
writer.write(java_lang_thread_id);
|
if (_thread->is_Java_thread()) {
|
||||||
writer.write(thread_group_id);
|
writer.write(name);
|
||||||
JfrThreadGroup::serialize(&writer, thread_group_id);
|
writer.write(JfrThreadId::id(_thread));
|
||||||
|
JavaThread* const jt = (JavaThread*)_thread;
|
||||||
|
const traceid thread_group_id = JfrThreadGroup::thread_group_id(jt, jt);
|
||||||
|
writer.write(thread_group_id);
|
||||||
|
JfrThreadGroup::serialize(&writer, thread_group_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writer.write((const char*)NULL); // java name
|
||||||
|
writer.write((traceid)0); // java thread id
|
||||||
|
writer.write((traceid)0); // java thread group
|
||||||
}
|
}
|
||||||
|
@ -27,16 +27,6 @@
|
|||||||
|
|
||||||
#include "jfr/metadata/jfrSerializer.hpp"
|
#include "jfr/metadata/jfrSerializer.hpp"
|
||||||
|
|
||||||
class JfrThreadConstantSet : public JfrSerializer {
|
|
||||||
public:
|
|
||||||
void serialize(JfrCheckpointWriter& writer);
|
|
||||||
};
|
|
||||||
|
|
||||||
class JfrThreadGroupConstant : public JfrSerializer {
|
|
||||||
public:
|
|
||||||
void serialize(JfrCheckpointWriter& writer);
|
|
||||||
};
|
|
||||||
|
|
||||||
class FlagValueOriginConstant : public JfrSerializer {
|
class FlagValueOriginConstant : public JfrSerializer {
|
||||||
public:
|
public:
|
||||||
void serialize(JfrCheckpointWriter& writer);
|
void serialize(JfrCheckpointWriter& writer);
|
||||||
@ -102,6 +92,16 @@ class VMOperationTypeConstant : public JfrSerializer {
|
|||||||
void serialize(JfrCheckpointWriter& writer);
|
void serialize(JfrCheckpointWriter& writer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class JfrThreadConstantSet : public JfrSerializer {
|
||||||
|
public:
|
||||||
|
void serialize(JfrCheckpointWriter& writer);
|
||||||
|
};
|
||||||
|
|
||||||
|
class JfrThreadGroupConstant : public JfrSerializer {
|
||||||
|
public:
|
||||||
|
void serialize(JfrCheckpointWriter& writer);
|
||||||
|
};
|
||||||
|
|
||||||
class ThreadStateConstant : public JfrSerializer {
|
class ThreadStateConstant : public JfrSerializer {
|
||||||
public:
|
public:
|
||||||
void serialize(JfrCheckpointWriter& writer);
|
void serialize(JfrCheckpointWriter& writer);
|
||||||
@ -109,9 +109,9 @@ class ThreadStateConstant : public JfrSerializer {
|
|||||||
|
|
||||||
class JfrThreadConstant : public JfrSerializer {
|
class JfrThreadConstant : public JfrSerializer {
|
||||||
private:
|
private:
|
||||||
JavaThread* _thread;
|
Thread* _thread;
|
||||||
public:
|
public:
|
||||||
JfrThreadConstant(JavaThread* jt) : _thread(jt) {}
|
JfrThreadConstant(Thread* t) : _thread(t) {}
|
||||||
void serialize(JfrCheckpointWriter& writer);
|
void serialize(JfrCheckpointWriter& writer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/jfrType.hpp"
|
#include "jfr/recorder/checkpoint/types/jfrType.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp"
|
#include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp"
|
||||||
|
#include "jfr/recorder/jfrRecorder.hpp"
|
||||||
#include "jfr/utilities/jfrDoublyLinkedList.hpp"
|
#include "jfr/utilities/jfrDoublyLinkedList.hpp"
|
||||||
#include "jfr/utilities/jfrIterator.hpp"
|
#include "jfr/utilities/jfrIterator.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
@ -73,29 +74,71 @@ class JfrSerializerRegistration : public JfrCHeapObj {
|
|||||||
return _id;
|
return _id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void invoke(JfrCheckpointWriter& writer) const;
|
void on_rotation() const {
|
||||||
|
_serializer->on_rotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke(JfrCheckpointWriter& writer) const {
|
||||||
|
if (_cache.valid()) {
|
||||||
|
writer.increment();
|
||||||
|
_cache->write(writer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const JfrCheckpointContext ctx = writer.context();
|
||||||
|
// serialize the type id before invoking callback
|
||||||
|
writer.write_type(_id);
|
||||||
|
const intptr_t start = writer.current_offset();
|
||||||
|
// invoke the serializer routine
|
||||||
|
_serializer->serialize(writer);
|
||||||
|
if (start == writer.current_offset()) {
|
||||||
|
// the serializer implementation did nothing, rewind to restore
|
||||||
|
writer.set_context(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_permit_cache) {
|
||||||
|
_cache = writer.copy(&ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void JfrSerializerRegistration::invoke(JfrCheckpointWriter& writer) const {
|
static void serialize_threads(JfrCheckpointWriter& writer) {
|
||||||
if (_cache.valid()) {
|
JfrThreadConstantSet thread_set;
|
||||||
writer.increment();
|
writer.write_type(TYPE_THREAD);
|
||||||
_cache->write(writer);
|
thread_set.serialize(writer);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
const JfrCheckpointContext ctx = writer.context();
|
static void serialize_thread_groups(JfrCheckpointWriter& writer) {
|
||||||
// serialize the type id before invoking callback
|
JfrThreadGroupConstant thread_group_set;
|
||||||
writer.write_type(_id);
|
writer.write_type(TYPE_THREADGROUP);
|
||||||
const intptr_t start = writer.current_offset();
|
thread_group_set.serialize(writer);
|
||||||
// invoke the serializer routine
|
}
|
||||||
_serializer->serialize(writer);
|
|
||||||
if (start == writer.current_offset() ) {
|
void JfrTypeManager::write_threads(JfrCheckpointWriter& writer) {
|
||||||
// the serializer implementation did nothing, rewind to restore
|
serialize_threads(writer);
|
||||||
writer.set_context(ctx);
|
serialize_thread_groups(writer);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
if (_permit_cache) {
|
void JfrTypeManager::create_thread_blob(Thread* t) {
|
||||||
_cache = writer.copy(&ctx);
|
assert(t != NULL, "invariant");
|
||||||
}
|
ResourceMark rm(t);
|
||||||
|
HandleMark hm(t);
|
||||||
|
JfrThreadConstant type_thread(t);
|
||||||
|
JfrCheckpointWriter writer(t, true, THREADS);
|
||||||
|
writer.write_type(TYPE_THREAD);
|
||||||
|
type_thread.serialize(writer);
|
||||||
|
// create and install a checkpoint blob
|
||||||
|
t->jfr_thread_local()->set_thread_blob(writer.move());
|
||||||
|
assert(t->jfr_thread_local()->has_thread_blob(), "invariant");
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrTypeManager::write_thread_checkpoint(Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
ResourceMark rm(t);
|
||||||
|
HandleMark hm(t);
|
||||||
|
JfrThreadConstant type_thread(t);
|
||||||
|
JfrCheckpointWriter writer(t, true, THREADS);
|
||||||
|
writer.write_type(TYPE_THREAD);
|
||||||
|
type_thread.serialize(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SerializerRegistrationGuard : public StackObj {
|
class SerializerRegistrationGuard : public StackObj {
|
||||||
@ -115,7 +158,6 @@ Semaphore SerializerRegistrationGuard::_mutex_semaphore(1);
|
|||||||
typedef JfrDoublyLinkedList<JfrSerializerRegistration> List;
|
typedef JfrDoublyLinkedList<JfrSerializerRegistration> List;
|
||||||
typedef StopOnNullIterator<const List> Iterator;
|
typedef StopOnNullIterator<const List> Iterator;
|
||||||
static List types;
|
static List types;
|
||||||
static List safepoint_types;
|
|
||||||
|
|
||||||
void JfrTypeManager::destroy() {
|
void JfrTypeManager::destroy() {
|
||||||
SerializerRegistrationGuard guard;
|
SerializerRegistrationGuard guard;
|
||||||
@ -126,52 +168,15 @@ void JfrTypeManager::destroy() {
|
|||||||
assert(registration != NULL, "invariant");
|
assert(registration != NULL, "invariant");
|
||||||
delete registration;
|
delete registration;
|
||||||
}
|
}
|
||||||
Iterator sp_type_iter(safepoint_types);
|
|
||||||
while (sp_type_iter.has_next()) {
|
|
||||||
registration = safepoint_types.remove(sp_type_iter.next());
|
|
||||||
assert(registration != NULL, "invariant");
|
|
||||||
delete registration;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrTypeManager::write_types(JfrCheckpointWriter& writer) {
|
void JfrTypeManager::on_rotation() {
|
||||||
const Iterator iter(types);
|
const Iterator iter(types);
|
||||||
while (iter.has_next()) {
|
while (iter.has_next()) {
|
||||||
iter.next()->invoke(writer);
|
iter.next()->on_rotation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrTypeManager::write_safepoint_types(JfrCheckpointWriter& writer) {
|
|
||||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
|
||||||
const Iterator iter(safepoint_types);
|
|
||||||
while (iter.has_next()) {
|
|
||||||
iter.next()->invoke(writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrTypeManager::create_thread_blob(JavaThread* jt) {
|
|
||||||
assert(jt != NULL, "invariant");
|
|
||||||
ResourceMark rm(jt);
|
|
||||||
HandleMark hm(jt);
|
|
||||||
JfrThreadConstant type_thread(jt);
|
|
||||||
JfrCheckpointWriter writer(false, true, jt);
|
|
||||||
writer.write_type(TYPE_THREAD);
|
|
||||||
type_thread.serialize(writer);
|
|
||||||
// create and install a checkpoint blob
|
|
||||||
jt->jfr_thread_local()->set_thread_blob(writer.move());
|
|
||||||
assert(jt->jfr_thread_local()->has_thread_blob(), "invariant");
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrTypeManager::write_thread_checkpoint(JavaThread* jt) {
|
|
||||||
assert(jt != NULL, "JavaThread is NULL!");
|
|
||||||
ResourceMark rm(jt);
|
|
||||||
HandleMark hm(jt);
|
|
||||||
JfrThreadConstant type_thread(jt);
|
|
||||||
JfrCheckpointWriter writer(false, true, jt);
|
|
||||||
writer.write_type(TYPE_THREAD);
|
|
||||||
type_thread.serialize(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
static void assert_not_registered_twice(JfrTypeId id, List& list) {
|
static void assert_not_registered_twice(JfrTypeId id, List& list) {
|
||||||
const Iterator iter(list);
|
const Iterator iter(list);
|
||||||
@ -181,52 +186,65 @@ static void assert_not_registered_twice(JfrTypeId id, List& list) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool register_type(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer) {
|
static bool new_registration = false;
|
||||||
|
|
||||||
|
static bool register_static_type(JfrTypeId id, bool permit_cache, JfrSerializer* serializer) {
|
||||||
assert(serializer != NULL, "invariant");
|
assert(serializer != NULL, "invariant");
|
||||||
JfrSerializerRegistration* const registration = new JfrSerializerRegistration(id, permit_cache, serializer);
|
JfrSerializerRegistration* const registration = new JfrSerializerRegistration(id, permit_cache, serializer);
|
||||||
if (registration == NULL) {
|
if (registration == NULL) {
|
||||||
delete serializer;
|
delete serializer;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (require_safepoint) {
|
assert(!types.in_list(registration), "invariant");
|
||||||
assert(!safepoint_types.in_list(registration), "invariant");
|
DEBUG_ONLY(assert_not_registered_twice(id, types);)
|
||||||
DEBUG_ONLY(assert_not_registered_twice(id, safepoint_types);)
|
if (JfrRecorder::is_recording()) {
|
||||||
safepoint_types.prepend(registration);
|
JfrCheckpointWriter writer(STATICS);
|
||||||
} else {
|
registration->invoke(writer);
|
||||||
assert(!types.in_list(registration), "invariant");
|
new_registration = true;
|
||||||
DEBUG_ONLY(assert_not_registered_twice(id, types);)
|
|
||||||
types.prepend(registration);
|
|
||||||
}
|
}
|
||||||
|
types.prepend(registration);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JfrTypeManager::initialize() {
|
bool JfrTypeManager::initialize() {
|
||||||
SerializerRegistrationGuard guard;
|
SerializerRegistrationGuard guard;
|
||||||
|
register_static_type(TYPE_FLAGVALUEORIGIN, true, new FlagValueOriginConstant());
|
||||||
// register non-safepointing type serialization
|
register_static_type(TYPE_INFLATECAUSE, true, new MonitorInflateCauseConstant());
|
||||||
register_type(TYPE_FLAGVALUEORIGIN, false, true, new FlagValueOriginConstant());
|
register_static_type(TYPE_GCCAUSE, true, new GCCauseConstant());
|
||||||
register_type(TYPE_INFLATECAUSE, false, true, new MonitorInflateCauseConstant());
|
register_static_type(TYPE_GCNAME, true, new GCNameConstant());
|
||||||
register_type(TYPE_GCCAUSE, false, true, new GCCauseConstant());
|
register_static_type(TYPE_GCWHEN, true, new GCWhenConstant());
|
||||||
register_type(TYPE_GCNAME, false, true, new GCNameConstant());
|
register_static_type(TYPE_GCTHRESHOLDUPDATER, true, new GCThresholdUpdaterConstant());
|
||||||
register_type(TYPE_GCWHEN, false, true, new GCWhenConstant());
|
register_static_type(TYPE_METADATATYPE, true, new MetadataTypeConstant());
|
||||||
register_type(TYPE_GCTHRESHOLDUPDATER, false, true, new GCThresholdUpdaterConstant());
|
register_static_type(TYPE_METASPACEOBJECTTYPE, true, new MetaspaceObjectTypeConstant());
|
||||||
register_type(TYPE_METADATATYPE, false, true, new MetadataTypeConstant());
|
register_static_type(TYPE_REFERENCETYPE, true, new ReferenceTypeConstant());
|
||||||
register_type(TYPE_METASPACEOBJECTTYPE, false, true, new MetaspaceObjectTypeConstant());
|
register_static_type(TYPE_NARROWOOPMODE, true, new NarrowOopModeConstant());
|
||||||
register_type(TYPE_REFERENCETYPE, false, true, new ReferenceTypeConstant());
|
register_static_type(TYPE_COMPILERPHASETYPE, true, new CompilerPhaseTypeConstant());
|
||||||
register_type(TYPE_NARROWOOPMODE, false, true, new NarrowOopModeConstant());
|
register_static_type(TYPE_CODEBLOBTYPE, true, new CodeBlobTypeConstant());
|
||||||
register_type(TYPE_COMPILERPHASETYPE, false, true, new CompilerPhaseTypeConstant());
|
register_static_type(TYPE_VMOPERATIONTYPE, true, new VMOperationTypeConstant());
|
||||||
register_type(TYPE_CODEBLOBTYPE, false, true, new CodeBlobTypeConstant());
|
register_static_type(TYPE_THREADSTATE, true, new ThreadStateConstant());
|
||||||
register_type(TYPE_VMOPERATIONTYPE, false, true, new VMOperationTypeConstant());
|
|
||||||
register_type(TYPE_THREADSTATE, false, true, new ThreadStateConstant());
|
|
||||||
|
|
||||||
// register safepointing type serialization
|
|
||||||
register_type(TYPE_THREADGROUP, true, false, new JfrThreadGroupConstant());
|
|
||||||
register_type(TYPE_THREAD, true, false, new JfrThreadConstantSet());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// implementation for the static registration function exposed in the JfrSerializer api
|
// implementation for the static registration function exposed in the JfrSerializer api
|
||||||
bool JfrSerializer::register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer) {
|
bool JfrSerializer::register_serializer(JfrTypeId id, bool permit_cache, JfrSerializer* serializer) {
|
||||||
SerializerRegistrationGuard guard;
|
SerializerRegistrationGuard guard;
|
||||||
return register_type(id, require_safepoint, permit_cache, serializer);
|
return register_static_type(id, permit_cache, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JfrTypeManager::has_new_static_type() {
|
||||||
|
if (new_registration) {
|
||||||
|
SerializerRegistrationGuard guard;
|
||||||
|
new_registration = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrTypeManager::write_static_types(JfrCheckpointWriter& writer) {
|
||||||
|
SerializerRegistrationGuard guard;
|
||||||
|
const Iterator iter(types);
|
||||||
|
while (iter.has_next()) {
|
||||||
|
iter.next()->invoke(writer);
|
||||||
|
}
|
||||||
|
new_registration = false;
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,12 @@ class JfrTypeManager : public AllStatic {
|
|||||||
public:
|
public:
|
||||||
static bool initialize();
|
static bool initialize();
|
||||||
static void destroy();
|
static void destroy();
|
||||||
static void write_types(JfrCheckpointWriter& writer);
|
static void on_rotation();
|
||||||
static void write_safepoint_types(JfrCheckpointWriter& writer);
|
static void write_threads(JfrCheckpointWriter& writer);
|
||||||
static void create_thread_blob(JavaThread* jt);
|
static void create_thread_blob(Thread* t);
|
||||||
static void write_thread_checkpoint(JavaThread* jt);
|
static void write_thread_checkpoint(Thread* t);
|
||||||
|
static bool has_new_static_type();
|
||||||
|
static void write_static_types(JfrCheckpointWriter& writer);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP
|
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP
|
||||||
|
@ -73,7 +73,7 @@ static traceid create_symbol_id(traceid artifact_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool current_epoch() {
|
static bool current_epoch() {
|
||||||
return _class_unload;
|
return _class_unload || _flushpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool previous_epoch() {
|
static bool previous_epoch() {
|
||||||
@ -246,7 +246,7 @@ static void do_unloaded_klass(Klass* klass) {
|
|||||||
static void do_klass(Klass* klass) {
|
static void do_klass(Klass* klass) {
|
||||||
assert(klass != NULL, "invariant");
|
assert(klass != NULL, "invariant");
|
||||||
assert(_subsystem_callback != NULL, "invariant");
|
assert(_subsystem_callback != NULL, "invariant");
|
||||||
if (current_epoch()) {
|
if (_flushpoint) {
|
||||||
if (USED_THIS_EPOCH(klass)) {
|
if (USED_THIS_EPOCH(klass)) {
|
||||||
_subsystem_callback->do_artifact(klass);
|
_subsystem_callback->do_artifact(klass);
|
||||||
return;
|
return;
|
||||||
@ -911,10 +911,11 @@ static size_t teardown() {
|
|||||||
return total_count;
|
return total_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload) {
|
static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint) {
|
||||||
_writer = writer;
|
_writer = writer;
|
||||||
_leakp_writer = leakp_writer;
|
_leakp_writer = leakp_writer;
|
||||||
_class_unload = class_unload;
|
_class_unload = class_unload;
|
||||||
|
_flushpoint = flushpoint;
|
||||||
if (_artifacts == NULL) {
|
if (_artifacts == NULL) {
|
||||||
_artifacts = new JfrArtifactSet(class_unload);
|
_artifacts = new JfrArtifactSet(class_unload);
|
||||||
} else {
|
} else {
|
||||||
@ -928,10 +929,10 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer
|
|||||||
/**
|
/**
|
||||||
* Write all "tagged" (in-use) constant artifacts and their dependencies.
|
* Write all "tagged" (in-use) constant artifacts and their dependencies.
|
||||||
*/
|
*/
|
||||||
size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload) {
|
size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint) {
|
||||||
assert(writer != NULL, "invariant");
|
assert(writer != NULL, "invariant");
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
setup(writer, leakp_writer, class_unload);
|
setup(writer, leakp_writer, class_unload, flushpoint);
|
||||||
// write order is important because an individual write step
|
// write order is important because an individual write step
|
||||||
// might tag an artifact to be written in a subsequent step
|
// might tag an artifact to be written in a subsequent step
|
||||||
if (!write_klasses()) {
|
if (!write_klasses()) {
|
||||||
|
@ -32,7 +32,7 @@ class JfrCheckpointWriter;
|
|||||||
class JfrTypeSet : AllStatic {
|
class JfrTypeSet : AllStatic {
|
||||||
public:
|
public:
|
||||||
static void clear();
|
static void clear();
|
||||||
static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload);
|
static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESET_HPP
|
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESET_HPP
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
#include "oops/method.hpp"
|
#include "oops/method.hpp"
|
||||||
#include "oops/oop.inline.hpp"
|
#include "oops/oop.inline.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
#include "runtime/orderAccess.hpp"
|
|
||||||
#include "runtime/vm_version.hpp"
|
#include "runtime/vm_version.hpp"
|
||||||
#include "runtime/jniHandles.inline.hpp"
|
#include "runtime/jniHandles.inline.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
@ -45,7 +44,7 @@ static traceid atomic_inc(traceid volatile* const dest) {
|
|||||||
traceid compare_value;
|
traceid compare_value;
|
||||||
traceid exchange_value;
|
traceid exchange_value;
|
||||||
do {
|
do {
|
||||||
compare_value = OrderAccess::load_acquire(dest);
|
compare_value = *dest;
|
||||||
exchange_value = compare_value + 1;
|
exchange_value = compare_value + 1;
|
||||||
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
|
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
|
||||||
return exchange_value;
|
return exchange_value;
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp"
|
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp"
|
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
|
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp"
|
|
||||||
#include "jfr/support/jfrKlassExtension.hpp"
|
#include "jfr/support/jfrKlassExtension.hpp"
|
||||||
#include "oops/arrayKlass.hpp"
|
#include "oops/arrayKlass.hpp"
|
||||||
#include "oops/klass.hpp"
|
#include "oops/klass.hpp"
|
||||||
@ -60,7 +59,14 @@ inline traceid JfrTraceId::get(const Thread* t) {
|
|||||||
|
|
||||||
inline traceid JfrTraceId::use(const Klass* klass) {
|
inline traceid JfrTraceId::use(const Klass* klass) {
|
||||||
assert(klass != NULL, "invariant");
|
assert(klass != NULL, "invariant");
|
||||||
return set_used_and_get(klass);
|
if (SHOULD_TAG(klass)) {
|
||||||
|
SET_USED_THIS_EPOCH(klass);
|
||||||
|
assert(USED_THIS_EPOCH(klass), "invariant");
|
||||||
|
JfrTraceIdEpoch::set_changed_tag_state();
|
||||||
|
return get(klass);
|
||||||
|
}
|
||||||
|
assert(USED_THIS_EPOCH(klass), "invariant");
|
||||||
|
return TRACE_ID(klass);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline traceid JfrTraceId::use(const Method* method) {
|
inline traceid JfrTraceId::use(const Method* method) {
|
||||||
@ -71,10 +77,16 @@ inline traceid JfrTraceId::use(const Method* method) {
|
|||||||
inline traceid JfrTraceId::use(const Klass* klass, const Method* method) {
|
inline traceid JfrTraceId::use(const Klass* klass, const Method* method) {
|
||||||
assert(klass != NULL, "invariant");
|
assert(klass != NULL, "invariant");
|
||||||
assert(method != NULL, "invariant");
|
assert(method != NULL, "invariant");
|
||||||
SET_METHOD_FLAG_USED_THIS_EPOCH(method);
|
if (SHOULD_TAG_KLASS_METHOD(klass)) {
|
||||||
|
SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass);
|
||||||
SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass);
|
}
|
||||||
assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
|
assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
|
||||||
|
if (METHOD_FLAG_NOT_USED_THIS_EPOCH(method)) {
|
||||||
|
assert(USED_THIS_EPOCH(klass), "invariant");
|
||||||
|
SET_METHOD_FLAG_USED_THIS_EPOCH(method);
|
||||||
|
JfrTraceIdEpoch::set_changed_tag_state();
|
||||||
|
}
|
||||||
|
assert(METHOD_FLAG_USED_THIS_EPOCH(method), "invariant");
|
||||||
return (METHOD_ID(klass, method));
|
return (METHOD_ID(klass, method));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,15 +25,13 @@
|
|||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
|
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
|
||||||
#include "runtime/safepoint.hpp"
|
#include "runtime/safepoint.hpp"
|
||||||
#include "runtime/orderAccess.hpp"
|
|
||||||
|
|
||||||
// Alternating epochs on each rotation allow for concurrent tagging.
|
// Alternating epochs on each rotation allow for concurrent tagging.
|
||||||
// The regular epoch shift happens only during a safepoint.
|
// The epoch shift happens only during a safepoint.
|
||||||
// The fence is there only for the emergency dump case which happens outside of safepoint.
|
|
||||||
bool JfrTraceIdEpoch::_epoch_state = false;
|
bool JfrTraceIdEpoch::_epoch_state = false;
|
||||||
|
bool volatile JfrTraceIdEpoch::_tag_state = false;
|
||||||
|
|
||||||
void JfrTraceIdEpoch::shift_epoch() {
|
void JfrTraceIdEpoch::shift_epoch() {
|
||||||
|
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||||
_epoch_state = !_epoch_state;
|
_epoch_state = !_epoch_state;
|
||||||
if (!SafepointSynchronize::is_at_safepoint()) {
|
|
||||||
OrderAccess::fence();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,18 +25,19 @@
|
|||||||
#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
|
#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
|
||||||
#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
|
#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
|
||||||
|
|
||||||
#include "memory/allocation.hpp"
|
|
||||||
#include "jfr/utilities/jfrTypes.hpp"
|
#include "jfr/utilities/jfrTypes.hpp"
|
||||||
|
#include "memory/allocation.hpp"
|
||||||
|
#include "runtime/orderAccess.hpp"
|
||||||
|
|
||||||
#define USED_BIT 1
|
#define USED_BIT 1
|
||||||
#define METHOD_USED_BIT (USED_BIT << 2)
|
#define METHOD_USED_BIT (USED_BIT << 2)
|
||||||
#define EPOCH_1_SHIFT 0
|
#define EPOCH_1_SHIFT 0
|
||||||
#define EPOCH_2_SHIFT 1
|
#define EPOCH_2_SHIFT 1
|
||||||
#define USED_EPOCH_1_BIT (USED_BIT << EPOCH_1_SHIFT)
|
#define USED_EPOCH_1_BIT (USED_BIT << EPOCH_1_SHIFT)
|
||||||
#define USED_EPOCH_2_BIT (USED_BIT << EPOCH_2_SHIFT)
|
#define USED_EPOCH_2_BIT (USED_BIT << EPOCH_2_SHIFT)
|
||||||
#define METHOD_USED_EPOCH_1_BIT (METHOD_USED_BIT << EPOCH_1_SHIFT)
|
#define METHOD_USED_EPOCH_1_BIT (METHOD_USED_BIT << EPOCH_1_SHIFT)
|
||||||
#define METHOD_USED_EPOCH_2_BIT (METHOD_USED_BIT << EPOCH_2_SHIFT)
|
#define METHOD_USED_EPOCH_2_BIT (METHOD_USED_BIT << EPOCH_2_SHIFT)
|
||||||
#define METHOD_AND_CLASS_IN_USE_BITS (METHOD_USED_BIT | USED_BIT)
|
#define METHOD_AND_CLASS_IN_USE_BITS (METHOD_USED_BIT | USED_BIT)
|
||||||
#define METHOD_AND_CLASS_IN_USE_EPOCH_1_BITS (METHOD_AND_CLASS_IN_USE_BITS << EPOCH_1_SHIFT)
|
#define METHOD_AND_CLASS_IN_USE_EPOCH_1_BITS (METHOD_AND_CLASS_IN_USE_BITS << EPOCH_1_SHIFT)
|
||||||
#define METHOD_AND_CLASS_IN_USE_EPOCH_2_BITS (METHOD_AND_CLASS_IN_USE_BITS << EPOCH_2_SHIFT)
|
#define METHOD_AND_CLASS_IN_USE_EPOCH_2_BITS (METHOD_AND_CLASS_IN_USE_BITS << EPOCH_2_SHIFT)
|
||||||
|
|
||||||
@ -44,6 +45,8 @@ class JfrTraceIdEpoch : AllStatic {
|
|||||||
friend class JfrCheckpointManager;
|
friend class JfrCheckpointManager;
|
||||||
private:
|
private:
|
||||||
static bool _epoch_state;
|
static bool _epoch_state;
|
||||||
|
static bool volatile _tag_state;
|
||||||
|
|
||||||
static void shift_epoch();
|
static void shift_epoch();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -86,6 +89,20 @@ class JfrTraceIdEpoch : AllStatic {
|
|||||||
static traceid method_and_class_in_use_prev_epoch_bits() {
|
static traceid method_and_class_in_use_prev_epoch_bits() {
|
||||||
return _epoch_state ? METHOD_AND_CLASS_IN_USE_EPOCH_1_BITS : METHOD_AND_CLASS_IN_USE_EPOCH_2_BITS;
|
return _epoch_state ? METHOD_AND_CLASS_IN_USE_EPOCH_1_BITS : METHOD_AND_CLASS_IN_USE_EPOCH_2_BITS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool has_changed_tag_state() {
|
||||||
|
if (OrderAccess::load_acquire(&_tag_state)) {
|
||||||
|
OrderAccess::release_store(&_tag_state, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_changed_tag_state() {
|
||||||
|
if (!OrderAccess::load_acquire(&_tag_state)) {
|
||||||
|
OrderAccess::release_store(&_tag_state, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
|
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
|
||||||
|
226
src/hotspot/share/jfr/recorder/repository/jfrChunk.cpp
Normal file
226
src/hotspot/share/jfr/recorder/repository/jfrChunk.cpp
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "precompiled.hpp"
|
||||||
|
#include "jfr/recorder/repository/jfrChunk.hpp"
|
||||||
|
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
||||||
|
#include "jfr/utilities/jfrTime.hpp"
|
||||||
|
#include "jfr/utilities/jfrTimeConverter.hpp"
|
||||||
|
#include "jfr/utilities/jfrTypes.hpp"
|
||||||
|
#include "runtime/os.inline.hpp"
|
||||||
|
|
||||||
|
static const char* const MAGIC = "FLR";
|
||||||
|
static const u2 JFR_VERSION_MAJOR = 2;
|
||||||
|
static const u2 JFR_VERSION_MINOR = 1;
|
||||||
|
|
||||||
|
// strictly monotone
|
||||||
|
static jlong nanos_now() {
|
||||||
|
static jlong last = 0;
|
||||||
|
const jlong now = os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC;
|
||||||
|
if (now > last) {
|
||||||
|
last = now;
|
||||||
|
} else {
|
||||||
|
++last;
|
||||||
|
}
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jlong ticks_now() {
|
||||||
|
return JfrTicks::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
JfrChunk::JfrChunk() :
|
||||||
|
_path(NULL),
|
||||||
|
_start_ticks(0),
|
||||||
|
_previous_start_ticks(invalid_time),
|
||||||
|
_start_nanos(0),
|
||||||
|
_previous_start_nanos(invalid_time),
|
||||||
|
_last_update_nanos(0),
|
||||||
|
_last_checkpoint_offset(0),
|
||||||
|
_last_metadata_offset(0),
|
||||||
|
_generation(1) {}
|
||||||
|
|
||||||
|
JfrChunk::~JfrChunk() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::reset() {
|
||||||
|
if (_path != NULL) {
|
||||||
|
JfrCHeapObj::free(_path, strlen(_path) + 1);
|
||||||
|
_path = NULL;
|
||||||
|
}
|
||||||
|
_last_checkpoint_offset = _last_metadata_offset = 0;
|
||||||
|
_generation = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* JfrChunk::magic() const {
|
||||||
|
return MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
u2 JfrChunk::major_version() const {
|
||||||
|
return JFR_VERSION_MAJOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
u2 JfrChunk::minor_version() const {
|
||||||
|
return JFR_VERSION_MINOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
u2 JfrChunk::capabilities() const {
|
||||||
|
// chunk capabilities, CompressedIntegers etc
|
||||||
|
static bool compressed_integers = JfrOptionSet::compressed_integers();
|
||||||
|
return compressed_integers;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunk::cpu_frequency() const {
|
||||||
|
static const jlong frequency = JfrTime::frequency();
|
||||||
|
return frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::set_last_checkpoint_offset(int64_t offset) {
|
||||||
|
_last_checkpoint_offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunk::last_checkpoint_offset() const {
|
||||||
|
return _last_checkpoint_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunk::start_ticks() const {
|
||||||
|
assert(_start_ticks != 0, "invariant");
|
||||||
|
return _start_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunk::start_nanos() const {
|
||||||
|
assert(_start_nanos != 0, "invariant");
|
||||||
|
return _start_nanos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunk::previous_start_ticks() const {
|
||||||
|
assert(_previous_start_ticks != invalid_time, "invariant");
|
||||||
|
return _previous_start_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunk::previous_start_nanos() const {
|
||||||
|
assert(_previous_start_nanos != invalid_time, "invariant");
|
||||||
|
return _previous_start_nanos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::update_start_ticks() {
|
||||||
|
_start_ticks = ticks_now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::update_start_nanos() {
|
||||||
|
const jlong now = nanos_now();
|
||||||
|
assert(now > _start_nanos, "invariant");
|
||||||
|
assert(now > _last_update_nanos, "invariant");
|
||||||
|
_start_nanos = _last_update_nanos = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::update_current_nanos() {
|
||||||
|
const jlong now = nanos_now();
|
||||||
|
assert(now > _last_update_nanos, "invariant");
|
||||||
|
_last_update_nanos = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::save_current_and_update_start_ticks() {
|
||||||
|
_previous_start_ticks = _start_ticks;
|
||||||
|
update_start_ticks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::save_current_and_update_start_nanos() {
|
||||||
|
_previous_start_nanos = _start_nanos;
|
||||||
|
update_start_nanos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::set_time_stamp() {
|
||||||
|
save_current_and_update_start_nanos();
|
||||||
|
save_current_and_update_start_ticks();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunk::last_chunk_duration() const {
|
||||||
|
assert(_previous_start_nanos != invalid_time, "invariant");
|
||||||
|
return _start_nanos - _previous_start_nanos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* copy_path(const char* path) {
|
||||||
|
assert(path != NULL, "invariant");
|
||||||
|
const size_t path_len = strlen(path);
|
||||||
|
char* new_path = JfrCHeapObj::new_array<char>(path_len + 1);
|
||||||
|
strncpy(new_path, path, path_len + 1);
|
||||||
|
return new_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::set_path(const char* path) {
|
||||||
|
if (_path != NULL) {
|
||||||
|
JfrCHeapObj::free(_path, strlen(_path) + 1);
|
||||||
|
_path = NULL;
|
||||||
|
}
|
||||||
|
if (path != NULL) {
|
||||||
|
_path = copy_path(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* JfrChunk::path() const {
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JfrChunk::is_started() const {
|
||||||
|
return _start_nanos != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JfrChunk::is_finished() const {
|
||||||
|
return 0 == _generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunk::duration() const {
|
||||||
|
assert(_last_update_nanos >= _start_nanos, "invariant");
|
||||||
|
return _last_update_nanos - _start_nanos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunk::last_metadata_offset() const {
|
||||||
|
return _last_metadata_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunk::set_last_metadata_offset(int64_t offset) {
|
||||||
|
assert(offset > _last_metadata_offset, "invariant");
|
||||||
|
_last_metadata_offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JfrChunk::has_metadata() const {
|
||||||
|
return 0 != _last_metadata_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
u1 JfrChunk::generation() const {
|
||||||
|
assert(_generation > 0, "invariant");
|
||||||
|
const u1 this_generation = _generation++;
|
||||||
|
if (GUARD == _generation) {
|
||||||
|
_generation = 1;
|
||||||
|
}
|
||||||
|
return this_generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
u1 JfrChunk::next_generation() const {
|
||||||
|
assert(_generation > 0, "invariant");
|
||||||
|
const u1 next_gen = _generation;
|
||||||
|
return GUARD == next_gen ? 1 : next_gen;
|
||||||
|
}
|
@ -22,38 +22,70 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKSTATE_HPP
|
#ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
|
||||||
#define SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKSTATE_HPP
|
#define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
|
||||||
|
|
||||||
#include "jfr/utilities/jfrAllocation.hpp"
|
#include "jfr/utilities/jfrAllocation.hpp"
|
||||||
#include "jfr/utilities/jfrTypes.hpp"
|
|
||||||
|
|
||||||
class JfrChunkState : public JfrCHeapObj {
|
const u1 COMPLETE = 0;
|
||||||
|
const u1 GUARD = 0xff;
|
||||||
|
const u1 PAD = 0;
|
||||||
|
|
||||||
|
class JfrChunk : public JfrCHeapObj {
|
||||||
friend class JfrChunkWriter;
|
friend class JfrChunkWriter;
|
||||||
|
friend class JfrChunkHeadWriter;
|
||||||
private:
|
private:
|
||||||
char* _path;
|
char* _path;
|
||||||
int64_t _start_ticks;
|
int64_t _start_ticks;
|
||||||
int64_t _start_nanos;
|
|
||||||
int64_t _previous_start_ticks;
|
int64_t _previous_start_ticks;
|
||||||
|
int64_t _start_nanos;
|
||||||
int64_t _previous_start_nanos;
|
int64_t _previous_start_nanos;
|
||||||
|
int64_t _last_update_nanos;
|
||||||
int64_t _last_checkpoint_offset;
|
int64_t _last_checkpoint_offset;
|
||||||
|
int64_t _last_metadata_offset;
|
||||||
|
mutable u1 _generation;
|
||||||
|
|
||||||
|
JfrChunk();
|
||||||
|
~JfrChunk();
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
const char* magic() const;
|
||||||
|
u2 major_version() const;
|
||||||
|
u2 minor_version() const;
|
||||||
|
int64_t cpu_frequency() const;
|
||||||
|
u2 capabilities() const;
|
||||||
|
|
||||||
void update_start_ticks();
|
void update_start_ticks();
|
||||||
void update_start_nanos();
|
void update_start_nanos();
|
||||||
void save_current_and_update_start_ticks();
|
void save_current_and_update_start_ticks();
|
||||||
void save_current_and_update_start_nanos();
|
void save_current_and_update_start_nanos();
|
||||||
|
|
||||||
JfrChunkState();
|
|
||||||
~JfrChunkState();
|
|
||||||
void reset();
|
|
||||||
int64_t last_checkpoint_offset() const;
|
int64_t last_checkpoint_offset() const;
|
||||||
void set_last_checkpoint_offset(int64_t offset);
|
void set_last_checkpoint_offset(int64_t offset);
|
||||||
|
|
||||||
|
int64_t last_metadata_offset() const;
|
||||||
|
void set_last_metadata_offset(int64_t offset);
|
||||||
|
bool has_metadata() const;
|
||||||
|
|
||||||
|
int64_t start_ticks() const;
|
||||||
|
int64_t start_nanos() const;
|
||||||
|
|
||||||
int64_t previous_start_ticks() const;
|
int64_t previous_start_ticks() const;
|
||||||
int64_t previous_start_nanos() const;
|
int64_t previous_start_nanos() const;
|
||||||
int64_t last_chunk_duration() const;
|
int64_t last_chunk_duration() const;
|
||||||
void update_time_to_now();
|
|
||||||
|
void set_time_stamp();
|
||||||
|
void update_current_nanos();
|
||||||
|
|
||||||
void set_path(const char* path);
|
void set_path(const char* path);
|
||||||
const char* path() const;
|
const char* path() const;
|
||||||
|
|
||||||
|
bool is_started() const;
|
||||||
|
bool is_finished() const;
|
||||||
|
|
||||||
|
int64_t duration() const;
|
||||||
|
u1 generation() const;
|
||||||
|
u1 next_generation() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKSTATE_HPP
|
#endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
|
||||||
#include "jfr/dcmd/jfrDcmds.hpp"
|
|
||||||
#include "jfr/recorder/jfrRecorder.hpp"
|
|
||||||
#include "jfr/recorder/repository/jfrChunkState.hpp"
|
|
||||||
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
|
||||||
#include "jfr/utilities/jfrTime.hpp"
|
|
||||||
#include "jfr/utilities/jfrTimeConverter.hpp"
|
|
||||||
#include "logging/log.hpp"
|
|
||||||
#include "runtime/os.inline.hpp"
|
|
||||||
#include "runtime/thread.inline.hpp"
|
|
||||||
|
|
||||||
JfrChunkState::JfrChunkState() :
|
|
||||||
_path(NULL),
|
|
||||||
_start_ticks(0),
|
|
||||||
_start_nanos(0),
|
|
||||||
_previous_start_ticks(0),
|
|
||||||
_previous_start_nanos(0),
|
|
||||||
_last_checkpoint_offset(0) {}
|
|
||||||
|
|
||||||
JfrChunkState::~JfrChunkState() {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrChunkState::reset() {
|
|
||||||
if (_path != NULL) {
|
|
||||||
JfrCHeapObj::free(_path, strlen(_path) + 1);
|
|
||||||
_path = NULL;
|
|
||||||
}
|
|
||||||
set_last_checkpoint_offset(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrChunkState::set_last_checkpoint_offset(int64_t offset) {
|
|
||||||
_last_checkpoint_offset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t JfrChunkState::last_checkpoint_offset() const {
|
|
||||||
return _last_checkpoint_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t JfrChunkState::previous_start_ticks() const {
|
|
||||||
return _previous_start_ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t JfrChunkState::previous_start_nanos() const {
|
|
||||||
return _previous_start_nanos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrChunkState::update_start_ticks() {
|
|
||||||
_start_ticks = JfrTicks::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrChunkState::update_start_nanos() {
|
|
||||||
_start_nanos = os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrChunkState::save_current_and_update_start_ticks() {
|
|
||||||
_previous_start_ticks = _start_ticks;
|
|
||||||
update_start_ticks();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrChunkState::save_current_and_update_start_nanos() {
|
|
||||||
_previous_start_nanos = _start_nanos;
|
|
||||||
update_start_nanos();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrChunkState::update_time_to_now() {
|
|
||||||
save_current_and_update_start_nanos();
|
|
||||||
save_current_and_update_start_ticks();
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t JfrChunkState::last_chunk_duration() const {
|
|
||||||
return _start_nanos - _previous_start_nanos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char* copy_path(const char* path) {
|
|
||||||
assert(path != NULL, "invariant");
|
|
||||||
const size_t path_len = strlen(path);
|
|
||||||
char* new_path = JfrCHeapObj::new_array<char>(path_len + 1);
|
|
||||||
strncpy(new_path, path, path_len + 1);
|
|
||||||
return new_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrChunkState::set_path(const char* path) {
|
|
||||||
assert(JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
if (_path != NULL) {
|
|
||||||
JfrCHeapObj::free(_path, strlen(_path) + 1);
|
|
||||||
_path = NULL;
|
|
||||||
}
|
|
||||||
if (path != NULL) {
|
|
||||||
_path = copy_path(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* JfrChunkState::path() const {
|
|
||||||
return _path;
|
|
||||||
}
|
|
@ -23,82 +23,218 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "jfr/recorder/repository/jfrChunkState.hpp"
|
#include "jfr/recorder/repository/jfrChunk.hpp"
|
||||||
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
||||||
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
|
||||||
#include "jfr/utilities/jfrTime.hpp"
|
#include "jfr/utilities/jfrTime.hpp"
|
||||||
#include "jfr/utilities/jfrTypes.hpp"
|
|
||||||
#include "runtime/mutexLocker.hpp"
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "runtime/os.hpp"
|
|
||||||
#include "runtime/os.inline.hpp"
|
#include "runtime/os.inline.hpp"
|
||||||
|
|
||||||
static const u2 JFR_VERSION_MAJOR = 2;
|
static const int64_t MAGIC_OFFSET = 0;
|
||||||
static const u2 JFR_VERSION_MINOR = 0;
|
static const int64_t MAGIC_LEN = 4;
|
||||||
static const size_t MAGIC_LEN = 4;
|
static const int64_t VERSION_OFFSET = MAGIC_LEN;
|
||||||
static const size_t FILEHEADER_SLOT_SIZE = 8;
|
static const int64_t SIZE_OFFSET = 8;
|
||||||
static const size_t CHUNK_SIZE_OFFSET = 8;
|
static const int64_t SLOT_SIZE = 8;
|
||||||
|
static const int64_t CHECKPOINT_OFFSET = SIZE_OFFSET + SLOT_SIZE;
|
||||||
JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunkstate(NULL) {}
|
static const int64_t METADATA_OFFSET = CHECKPOINT_OFFSET + SLOT_SIZE;
|
||||||
|
static const int64_t START_NANOS_OFFSET = METADATA_OFFSET + SLOT_SIZE;
|
||||||
bool JfrChunkWriter::initialize() {
|
static const int64_t DURATION_NANOS_OFFSET = START_NANOS_OFFSET + SLOT_SIZE;
|
||||||
assert(_chunkstate == NULL, "invariant");
|
static const int64_t START_TICKS_OFFSET = DURATION_NANOS_OFFSET + SLOT_SIZE;
|
||||||
_chunkstate = new JfrChunkState();
|
static const int64_t CPU_FREQUENCY_OFFSET = START_TICKS_OFFSET + SLOT_SIZE;
|
||||||
return _chunkstate != NULL;
|
static const int64_t GENERATION_OFFSET = CPU_FREQUENCY_OFFSET + SLOT_SIZE;
|
||||||
}
|
static const int64_t CAPABILITY_OFFSET = GENERATION_OFFSET + 2;
|
||||||
|
static const int64_t HEADER_SIZE = CAPABILITY_OFFSET + 2;
|
||||||
|
|
||||||
static fio_fd open_chunk(const char* path) {
|
static fio_fd open_chunk(const char* path) {
|
||||||
assert(JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
return path != NULL ? os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE) : invalid_fd;
|
return path != NULL ? os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE) : invalid_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JfrChunkWriter::open() {
|
#ifdef ASSERT
|
||||||
assert(_chunkstate != NULL, "invariant");
|
static void assert_writer_position(JfrChunkWriter* writer, int64_t offset) {
|
||||||
JfrChunkWriterBase::reset(open_chunk(_chunkstate->path()));
|
assert(writer != NULL, "invariant");
|
||||||
const bool is_open = this->has_valid_fd();
|
assert(offset == writer->current_offset(), "invariant");
|
||||||
if (is_open) {
|
}
|
||||||
this->bytes("FLR", MAGIC_LEN);
|
#endif
|
||||||
this->be_write((u2)JFR_VERSION_MAJOR);
|
|
||||||
this->be_write((u2)JFR_VERSION_MINOR);
|
class JfrChunkHeadWriter : public StackObj {
|
||||||
this->reserve(6 * FILEHEADER_SLOT_SIZE);
|
private:
|
||||||
// u8 chunk_size
|
JfrChunkWriter* _writer;
|
||||||
// u8 initial checkpoint offset
|
JfrChunk* _chunk;
|
||||||
// u8 metadata section offset
|
public:
|
||||||
// u8 chunk start nanos
|
void write_magic() {
|
||||||
// u8 chunk duration nanos
|
_writer->bytes(_chunk->magic(), MAGIC_LEN);
|
||||||
// u8 chunk start ticks
|
|
||||||
this->be_write(JfrTime::frequency());
|
|
||||||
// chunk capabilities, CompressedIntegers etc
|
|
||||||
this->be_write((u4)JfrOptionSet::compressed_integers() ? 1 : 0);
|
|
||||||
_chunkstate->reset();
|
|
||||||
}
|
}
|
||||||
return is_open;
|
|
||||||
|
void write_version() {
|
||||||
|
_writer->be_write(_chunk->major_version());
|
||||||
|
_writer->be_write(_chunk->minor_version());
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_size(int64_t size) {
|
||||||
|
_writer->be_write(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_checkpoint() {
|
||||||
|
_writer->be_write(_chunk->last_checkpoint_offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_metadata() {
|
||||||
|
_writer->be_write(_chunk->last_metadata_offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_time(bool finalize) {
|
||||||
|
if (finalize) {
|
||||||
|
_writer->be_write(_chunk->previous_start_nanos());
|
||||||
|
_writer->be_write(_chunk->last_chunk_duration());
|
||||||
|
_writer->be_write(_chunk->previous_start_ticks());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_writer->be_write(_chunk->start_nanos());
|
||||||
|
_writer->be_write(_chunk->duration());
|
||||||
|
_writer->be_write(_chunk->start_ticks());
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_cpu_frequency() {
|
||||||
|
_writer->be_write(_chunk->cpu_frequency());
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_generation(bool finalize) {
|
||||||
|
_writer->be_write(finalize ? COMPLETE : _chunk->generation());
|
||||||
|
_writer->be_write(PAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_next_generation() {
|
||||||
|
_writer->be_write(_chunk->next_generation());
|
||||||
|
_writer->be_write(PAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_guard() {
|
||||||
|
_writer->be_write(GUARD);
|
||||||
|
_writer->be_write(PAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_guard_flush() {
|
||||||
|
write_guard();
|
||||||
|
_writer->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_capabilities() {
|
||||||
|
_writer->be_write(_chunk->capabilities());
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_size_to_generation(int64_t size, bool finalize) {
|
||||||
|
write_size(size);
|
||||||
|
write_checkpoint();
|
||||||
|
write_metadata();
|
||||||
|
write_time(finalize);
|
||||||
|
write_cpu_frequency();
|
||||||
|
write_generation(finalize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush(int64_t size, bool finalize) {
|
||||||
|
assert(_writer->is_valid(), "invariant");
|
||||||
|
assert(_chunk != NULL, "invariant");
|
||||||
|
DEBUG_ONLY(assert_writer_position(_writer, SIZE_OFFSET);)
|
||||||
|
write_size_to_generation(size, finalize);
|
||||||
|
// no need to write capabilities
|
||||||
|
_writer->seek(size); // implicit flush
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize() {
|
||||||
|
assert(_writer->is_valid(), "invariant");
|
||||||
|
assert(_chunk != NULL, "invariant");
|
||||||
|
DEBUG_ONLY(assert_writer_position(_writer, 0);)
|
||||||
|
write_magic();
|
||||||
|
write_version();
|
||||||
|
write_size_to_generation(HEADER_SIZE, false);
|
||||||
|
write_capabilities();
|
||||||
|
DEBUG_ONLY(assert_writer_position(_writer, HEADER_SIZE);)
|
||||||
|
_writer->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
JfrChunkHeadWriter(JfrChunkWriter* writer, int64_t offset, bool guard = true) : _writer(writer), _chunk(writer->_chunk) {
|
||||||
|
assert(_writer != NULL, "invariant");
|
||||||
|
assert(_writer->is_valid(), "invariant");
|
||||||
|
assert(_chunk != NULL, "invariant");
|
||||||
|
if (0 == _writer->current_offset()) {
|
||||||
|
assert(HEADER_SIZE == offset, "invariant");
|
||||||
|
initialize();
|
||||||
|
} else {
|
||||||
|
if (guard) {
|
||||||
|
_writer->seek(GENERATION_OFFSET);
|
||||||
|
write_guard();
|
||||||
|
_writer->seek(offset);
|
||||||
|
} else {
|
||||||
|
_chunk->update_current_nanos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_ONLY(assert_writer_position(_writer, offset);)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int64_t prepare_chunk_header_constant_pool(JfrChunkWriter& cw, int64_t event_offset, bool flushpoint) {
|
||||||
|
const int64_t delta = cw.last_checkpoint_offset() == 0 ? 0 : cw.last_checkpoint_offset() - event_offset;
|
||||||
|
const u4 checkpoint_type = flushpoint ? (u4)(FLUSH | HEADER) : (u4)HEADER;
|
||||||
|
cw.reserve(sizeof(u4));
|
||||||
|
cw.write<u8>(EVENT_CHECKPOINT);
|
||||||
|
cw.write<u8>(JfrTicks::now().value());
|
||||||
|
cw.write<u8>(0); // duration
|
||||||
|
cw.write<u8>(delta); // to previous checkpoint
|
||||||
|
cw.write<u4>(checkpoint_type);
|
||||||
|
cw.write<u4>(1); // pool count
|
||||||
|
cw.write<u8>(TYPE_CHUNKHEADER);
|
||||||
|
cw.write<u4>(1); // count
|
||||||
|
cw.write<u8>(1); // key
|
||||||
|
cw.write<u4>(HEADER_SIZE); // length of byte array
|
||||||
|
return cw.current_offset();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrChunkWriter::close(int64_t metadata_offset) {
|
int64_t JfrChunkWriter::write_chunk_header_checkpoint(bool flushpoint) {
|
||||||
write_header(metadata_offset);
|
assert(this->has_valid_fd(), "invariant");
|
||||||
this->flush();
|
const int64_t event_size_offset = current_offset();
|
||||||
this->close_fd();
|
const int64_t header_content_pos = prepare_chunk_header_constant_pool(*this, event_size_offset, flushpoint);
|
||||||
return (size_t)size_written();
|
JfrChunkHeadWriter head(this, header_content_pos, false);
|
||||||
|
head.write_magic();
|
||||||
|
head.write_version();
|
||||||
|
const int64_t chunk_size_offset = reserve(sizeof(int64_t)); // size to be decided when we are done
|
||||||
|
be_write(event_size_offset); // last checkpoint offset will be this checkpoint
|
||||||
|
head.write_metadata();
|
||||||
|
head.write_time(false);
|
||||||
|
head.write_cpu_frequency();
|
||||||
|
head.write_next_generation();
|
||||||
|
head.write_capabilities();
|
||||||
|
assert(current_offset() - header_content_pos == HEADER_SIZE, "invariant");
|
||||||
|
const u4 checkpoint_size = current_offset() - event_size_offset;
|
||||||
|
write_padded_at_offset<u4>(checkpoint_size, event_size_offset);
|
||||||
|
set_last_checkpoint_offset(event_size_offset);
|
||||||
|
const size_t sz_written = size_written();
|
||||||
|
write_be_at_offset(sz_written, chunk_size_offset);
|
||||||
|
return sz_written;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrChunkWriter::write_header(int64_t metadata_offset) {
|
int64_t JfrChunkWriter::flush_chunk(bool flushpoint) {
|
||||||
assert(this->is_valid(), "invariant");
|
assert(_chunk != NULL, "invariant");
|
||||||
// Chunk size
|
const int64_t sz_written = write_chunk_header_checkpoint(flushpoint);
|
||||||
this->write_be_at_offset(size_written(), CHUNK_SIZE_OFFSET);
|
assert(size_written() == sz_written, "invariant");
|
||||||
// initial checkpoint event offset
|
JfrChunkHeadWriter head(this, SIZE_OFFSET);
|
||||||
this->write_be_at_offset(_chunkstate->last_checkpoint_offset(), CHUNK_SIZE_OFFSET + (1 * FILEHEADER_SLOT_SIZE));
|
head.flush(sz_written, !flushpoint);
|
||||||
// metadata event offset
|
return sz_written;
|
||||||
this->write_be_at_offset(metadata_offset, CHUNK_SIZE_OFFSET + (2 * FILEHEADER_SLOT_SIZE));
|
|
||||||
// start of chunk in nanos since epoch
|
|
||||||
this->write_be_at_offset(_chunkstate->previous_start_nanos(), CHUNK_SIZE_OFFSET + (3 * FILEHEADER_SLOT_SIZE));
|
|
||||||
// duration of chunk in nanos
|
|
||||||
this->write_be_at_offset(_chunkstate->last_chunk_duration(), CHUNK_SIZE_OFFSET + (4 * FILEHEADER_SLOT_SIZE));
|
|
||||||
// start of chunk in ticks
|
|
||||||
this->write_be_at_offset(_chunkstate->previous_start_ticks(), CHUNK_SIZE_OFFSET + (5 * FILEHEADER_SLOT_SIZE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrChunkWriter::set_chunk_path(const char* chunk_path) {
|
JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunk(new JfrChunk()) {}
|
||||||
_chunkstate->set_path(chunk_path);
|
|
||||||
|
JfrChunkWriter::~JfrChunkWriter() {
|
||||||
|
assert(_chunk != NULL, "invariant");
|
||||||
|
delete _chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunkWriter::set_path(const char* path) {
|
||||||
|
assert(_chunk != NULL, "invariant");
|
||||||
|
_chunk->set_path(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrChunkWriter::set_time_stamp() {
|
||||||
|
assert(_chunk != NULL, "invariant");
|
||||||
|
_chunk->set_time_stamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t JfrChunkWriter::size_written() const {
|
int64_t JfrChunkWriter::size_written() const {
|
||||||
@ -106,13 +242,46 @@ int64_t JfrChunkWriter::size_written() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t JfrChunkWriter::last_checkpoint_offset() const {
|
int64_t JfrChunkWriter::last_checkpoint_offset() const {
|
||||||
return _chunkstate->last_checkpoint_offset();
|
assert(_chunk != NULL, "invariant");
|
||||||
|
return _chunk->last_checkpoint_offset();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunkWriter::current_chunk_start_nanos() const {
|
||||||
|
assert(_chunk != NULL, "invariant");
|
||||||
|
return this->is_valid() ? _chunk->start_nanos() : invalid_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrChunkWriter::set_last_checkpoint_offset(int64_t offset) {
|
void JfrChunkWriter::set_last_checkpoint_offset(int64_t offset) {
|
||||||
_chunkstate->set_last_checkpoint_offset(offset);
|
assert(_chunk != NULL, "invariant");
|
||||||
|
_chunk->set_last_checkpoint_offset(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrChunkWriter::time_stamp_chunk_now() {
|
void JfrChunkWriter::set_last_metadata_offset(int64_t offset) {
|
||||||
_chunkstate->update_time_to_now();
|
assert(_chunk != NULL, "invariant");
|
||||||
|
_chunk->set_last_metadata_offset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JfrChunkWriter::has_metadata() const {
|
||||||
|
assert(_chunk != NULL, "invariant");
|
||||||
|
return _chunk->has_metadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JfrChunkWriter::open() {
|
||||||
|
assert(_chunk != NULL, "invariant");
|
||||||
|
JfrChunkWriterBase::reset(open_chunk(_chunk->path()));
|
||||||
|
const bool is_open = this->has_valid_fd();
|
||||||
|
if (is_open) {
|
||||||
|
assert(0 == this->current_offset(), "invariant");
|
||||||
|
_chunk->reset();
|
||||||
|
JfrChunkHeadWriter head(this, HEADER_SIZE);
|
||||||
|
}
|
||||||
|
return is_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t JfrChunkWriter::close() {
|
||||||
|
assert(this->has_valid_fd(), "invariant");
|
||||||
|
const int64_t size_written = flush_chunk(false);
|
||||||
|
this->close_fd();
|
||||||
|
assert(!this->is_valid(), "invariant");
|
||||||
|
return size_written;
|
||||||
}
|
}
|
||||||
|
@ -29,29 +29,36 @@
|
|||||||
#include "jfr/writers/jfrStreamWriterHost.inline.hpp"
|
#include "jfr/writers/jfrStreamWriterHost.inline.hpp"
|
||||||
#include "jfr/writers/jfrWriterHost.inline.hpp"
|
#include "jfr/writers/jfrWriterHost.inline.hpp"
|
||||||
|
|
||||||
typedef MallocAdapter<M> JfrStreamBuffer; // 1 mb buffered writes
|
typedef MallocAdapter<M> JfrChunkBuffer; // 1 mb buffered writes
|
||||||
typedef StreamWriterHost<JfrStreamBuffer, JfrCHeapObj> JfrBufferedStreamWriter;
|
typedef StreamWriterHost<JfrChunkBuffer, JfrCHeapObj> JfrBufferedChunkWriter;
|
||||||
typedef WriterHost<BigEndianEncoder, CompressedIntegerEncoder, JfrBufferedStreamWriter> JfrChunkWriterBase;
|
typedef WriterHost<BigEndianEncoder, CompressedIntegerEncoder, JfrBufferedChunkWriter> JfrChunkWriterBase;
|
||||||
|
|
||||||
class JfrChunkState;
|
class JfrChunk;
|
||||||
|
class JfrChunkHeadWriter;
|
||||||
|
|
||||||
class JfrChunkWriter : public JfrChunkWriterBase {
|
class JfrChunkWriter : public JfrChunkWriterBase {
|
||||||
|
friend class JfrChunkHeadWriter;
|
||||||
friend class JfrRepository;
|
friend class JfrRepository;
|
||||||
private:
|
private:
|
||||||
JfrChunkState* _chunkstate;
|
JfrChunk* _chunk;
|
||||||
|
void set_path(const char* path);
|
||||||
|
int64_t flush_chunk(bool flushpoint);
|
||||||
bool open();
|
bool open();
|
||||||
size_t close(int64_t metadata_offset);
|
int64_t close();
|
||||||
void write_header(int64_t metadata_offset);
|
int64_t current_chunk_start_nanos() const;
|
||||||
void set_chunk_path(const char* chunk_path);
|
int64_t write_chunk_header_checkpoint(bool flushpoint);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JfrChunkWriter();
|
JfrChunkWriter();
|
||||||
bool initialize();
|
~JfrChunkWriter();
|
||||||
|
|
||||||
int64_t size_written() const;
|
int64_t size_written() const;
|
||||||
int64_t last_checkpoint_offset() const;
|
int64_t last_checkpoint_offset() const;
|
||||||
void set_last_checkpoint_offset(int64_t offset);
|
void set_last_checkpoint_offset(int64_t offset);
|
||||||
void time_stamp_chunk_now();
|
void set_last_metadata_offset(int64_t offset);
|
||||||
|
|
||||||
|
bool has_metadata() const;
|
||||||
|
void set_time_stamp();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKWRITER_HPP
|
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKWRITER_HPP
|
||||||
|
@ -248,7 +248,6 @@ static void write_emergency_file(fio_fd emergency_fd, const RepositoryIterator&
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const char* create_emergency_dump_path() {
|
static const char* create_emergency_dump_path() {
|
||||||
assert(JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, JVM_MAXPATHLEN);
|
char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, JVM_MAXPATHLEN);
|
||||||
if (NULL == buffer) {
|
if (NULL == buffer) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -291,7 +290,6 @@ static const char* create_emergency_dump_path() {
|
|||||||
// Caller needs ResourceMark
|
// Caller needs ResourceMark
|
||||||
static const char* create_emergency_chunk_path(const char* repository_path) {
|
static const char* create_emergency_chunk_path(const char* repository_path) {
|
||||||
assert(repository_path != NULL, "invariant");
|
assert(repository_path != NULL, "invariant");
|
||||||
assert(JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
const size_t repository_path_len = strlen(repository_path);
|
const size_t repository_path_len = strlen(repository_path);
|
||||||
// date time
|
// date time
|
||||||
char date_time_buffer[32] = { 0 };
|
char date_time_buffer[32] = { 0 };
|
||||||
@ -307,12 +305,11 @@ static const char* create_emergency_chunk_path(const char* repository_path) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
// append the individual substrings
|
// append the individual substrings
|
||||||
jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_path_len, os::file_separator(), date_time_buffer, chunk_file_jfr_ext);
|
jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_path, os::file_separator(), date_time_buffer, chunk_file_jfr_ext);
|
||||||
return chunk_path;
|
return chunk_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fio_fd emergency_dump_file_descriptor() {
|
static fio_fd emergency_dump_file_descriptor() {
|
||||||
assert(JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
const char* const emergency_dump_path = create_emergency_dump_path();
|
const char* const emergency_dump_path = create_emergency_dump_path();
|
||||||
return emergency_dump_path != NULL ? open_exclusivly(emergency_dump_path) : invalid_fd;
|
return emergency_dump_path != NULL ? open_exclusivly(emergency_dump_path) : invalid_fd;
|
||||||
@ -325,7 +322,6 @@ const char* JfrEmergencyDump::build_dump_path(const char* repository_path) {
|
|||||||
void JfrEmergencyDump::on_vm_error(const char* repository_path) {
|
void JfrEmergencyDump::on_vm_error(const char* repository_path) {
|
||||||
assert(repository_path != NULL, "invariant");
|
assert(repository_path != NULL, "invariant");
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
const fio_fd emergency_fd = emergency_dump_file_descriptor();
|
const fio_fd emergency_fd = emergency_dump_file_descriptor();
|
||||||
if (emergency_fd != invalid_fd) {
|
if (emergency_fd != invalid_fd) {
|
||||||
RepositoryIterator iterator(repository_path, strlen(repository_path));
|
RepositoryIterator iterator(repository_path, strlen(repository_path));
|
||||||
@ -340,17 +336,25 @@ void JfrEmergencyDump::on_vm_error(const char* repository_path) {
|
|||||||
*
|
*
|
||||||
* 1. if the thread state is not "_thread_in_vm", we will quick transition
|
* 1. if the thread state is not "_thread_in_vm", we will quick transition
|
||||||
* it to "_thread_in_vm".
|
* it to "_thread_in_vm".
|
||||||
* 2. the nesting state for both resource and handle areas are unknown,
|
* 2. if the thread is the owner of some critical lock(s), unlock them.
|
||||||
* so we allocate new fresh arenas, discarding the old ones.
|
|
||||||
* 3. if the thread is the owner of some critical lock(s), unlock them.
|
|
||||||
*
|
*
|
||||||
* If we end up deadlocking in the attempt of dumping out jfr data,
|
* If we end up deadlocking in the attempt of dumping out jfr data,
|
||||||
* we rely on the WatcherThread task "is_error_reported()",
|
* we rely on the WatcherThread task "is_error_reported()",
|
||||||
* to exit the VM after a hard-coded timeout.
|
* to exit the VM after a hard-coded timeout (disallow WatcherThread to emergency dump).
|
||||||
* This "safety net" somewhat explains the aggressiveness in this attempt.
|
* This "safety net" somewhat explains the aggressiveness in this attempt.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void prepare_for_emergency_dump(Thread* thread) {
|
static bool prepare_for_emergency_dump() {
|
||||||
|
if (JfrStream_lock->owned_by_self()) {
|
||||||
|
// crashed during jfr rotation, disallow recursion
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Thread* const thread = Thread::current();
|
||||||
|
if (thread->is_Watcher_thread()) {
|
||||||
|
// need WatcherThread as a safeguard against potential deadlocks
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (thread->is_Java_thread()) {
|
if (thread->is_Java_thread()) {
|
||||||
((JavaThread*)thread)->set_thread_state(_thread_in_vm);
|
((JavaThread*)thread)->set_thread_state(_thread_in_vm);
|
||||||
}
|
}
|
||||||
@ -388,7 +392,6 @@ static void prepare_for_emergency_dump(Thread* thread) {
|
|||||||
VMOperationRequest_lock->unlock();
|
VMOperationRequest_lock->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Service_lock->owned_by_self()) {
|
if (Service_lock->owned_by_self()) {
|
||||||
Service_lock->unlock();
|
Service_lock->unlock();
|
||||||
}
|
}
|
||||||
@ -413,13 +416,10 @@ static void prepare_for_emergency_dump(Thread* thread) {
|
|||||||
JfrBuffer_lock->unlock();
|
JfrBuffer_lock->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JfrStream_lock->owned_by_self()) {
|
|
||||||
JfrStream_lock->unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JfrStacktrace_lock->owned_by_self()) {
|
if (JfrStacktrace_lock->owned_by_self()) {
|
||||||
JfrStacktrace_lock->unlock();
|
JfrStacktrace_lock->unlock();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static volatile int jfr_shutdown_lock = 0;
|
static volatile int jfr_shutdown_lock = 0;
|
||||||
@ -429,24 +429,9 @@ static bool guard_reentrancy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) {
|
void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) {
|
||||||
if (!guard_reentrancy()) {
|
if (!(guard_reentrancy() && prepare_for_emergency_dump())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// function made non-reentrant
|
|
||||||
Thread* thread = Thread::current();
|
|
||||||
if (exception_handler) {
|
|
||||||
// we are crashing
|
|
||||||
if (thread->is_Watcher_thread()) {
|
|
||||||
// The Watcher thread runs the periodic thread sampling task.
|
|
||||||
// If it has crashed, it is likely that another thread is
|
|
||||||
// left in a suspended state. This would mean the system
|
|
||||||
// will not be able to ever move to a safepoint. We try
|
|
||||||
// to avoid issuing safepoint operations when attempting
|
|
||||||
// an emergency dump, but a safepoint might be already pending.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
prepare_for_emergency_dump(thread);
|
|
||||||
}
|
|
||||||
EventDumpReason event;
|
EventDumpReason event;
|
||||||
if (event.should_commit()) {
|
if (event.should_commit()) {
|
||||||
event.set_reason(exception_handler ? "Crash" : "Out of Memory");
|
event.set_reason(exception_handler ? "Crash" : "Out of Memory");
|
||||||
@ -458,8 +443,6 @@ void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) {
|
|||||||
LeakProfiler::emit_events(max_jlong, false);
|
LeakProfiler::emit_events(max_jlong, false);
|
||||||
}
|
}
|
||||||
const int messages = MSGBIT(MSG_VM_ERROR);
|
const int messages = MSGBIT(MSG_VM_ERROR);
|
||||||
ResourceMark rm(thread);
|
|
||||||
HandleMark hm(thread);
|
|
||||||
JfrRecorderService service;
|
JfrRecorderService service;
|
||||||
service.rotate(messages);
|
service.rotate(messages);
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,14 @@
|
|||||||
#include "jfr/jfr.hpp"
|
#include "jfr/jfr.hpp"
|
||||||
#include "jfr/jni/jfrJavaSupport.hpp"
|
#include "jfr/jni/jfrJavaSupport.hpp"
|
||||||
#include "jfr/recorder/jfrRecorder.hpp"
|
#include "jfr/recorder/jfrRecorder.hpp"
|
||||||
#include "jfr/recorder/repository/jfrChunkState.hpp"
|
|
||||||
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
||||||
#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
|
#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
|
||||||
#include "jfr/recorder/repository/jfrRepository.hpp"
|
#include "jfr/recorder/repository/jfrRepository.hpp"
|
||||||
#include "jfr/recorder/service/jfrPostBox.hpp"
|
#include "jfr/recorder/service/jfrPostBox.hpp"
|
||||||
|
#include "logging/log.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
#include "runtime/mutex.hpp"
|
#include "runtime/mutex.hpp"
|
||||||
|
#include "runtime/os.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
|
|
||||||
static JfrRepository* _instance = NULL;
|
static JfrRepository* _instance = NULL;
|
||||||
@ -43,11 +44,6 @@ JfrRepository& JfrRepository::instance() {
|
|||||||
|
|
||||||
static JfrChunkWriter* _chunkwriter = NULL;
|
static JfrChunkWriter* _chunkwriter = NULL;
|
||||||
|
|
||||||
static bool initialize_chunkwriter() {
|
|
||||||
assert(_chunkwriter == NULL, "invariant");
|
|
||||||
_chunkwriter = new JfrChunkWriter();
|
|
||||||
return _chunkwriter != NULL && _chunkwriter->initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
JfrChunkWriter& JfrRepository::chunkwriter() {
|
JfrChunkWriter& JfrRepository::chunkwriter() {
|
||||||
return *_chunkwriter;
|
return *_chunkwriter;
|
||||||
@ -56,7 +52,9 @@ JfrChunkWriter& JfrRepository::chunkwriter() {
|
|||||||
JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {}
|
JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {}
|
||||||
|
|
||||||
bool JfrRepository::initialize() {
|
bool JfrRepository::initialize() {
|
||||||
return initialize_chunkwriter();
|
assert(_chunkwriter == NULL, "invariant");
|
||||||
|
_chunkwriter = new JfrChunkWriter();
|
||||||
|
return _chunkwriter != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
JfrRepository::~JfrRepository() {
|
JfrRepository::~JfrRepository() {
|
||||||
@ -84,7 +82,6 @@ void JfrRepository::destroy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JfrRepository::on_vm_error() {
|
void JfrRepository::on_vm_error() {
|
||||||
assert(!JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
if (_path == NULL) {
|
if (_path == NULL) {
|
||||||
// completed already
|
// completed already
|
||||||
return;
|
return;
|
||||||
@ -107,17 +104,21 @@ bool JfrRepository::set_path(const char* path) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRepository::set_chunk_path(const char* path) {
|
|
||||||
assert(JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
chunkwriter().set_chunk_path(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrRepository::notify_on_new_chunk_path() {
|
void JfrRepository::notify_on_new_chunk_path() {
|
||||||
if (Jfr::is_recording()) {
|
if (Jfr::is_recording()) {
|
||||||
|
// rotations are synchronous, block until rotation completes
|
||||||
instance()._post_box.post(MSG_ROTATE);
|
instance()._post_box.post(MSG_ROTATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JfrRepository::set_chunk_path(const char* path) {
|
||||||
|
chunkwriter().set_path(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
jlong JfrRepository::current_chunk_start_nanos() {
|
||||||
|
return chunkwriter().current_chunk_start_nanos();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the file where data should be written.
|
* Sets the file where data should be written.
|
||||||
*
|
*
|
||||||
@ -134,14 +135,11 @@ void JfrRepository::set_chunk_path(jstring path, JavaThread* jt) {
|
|||||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
|
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
|
||||||
ResourceMark rm(jt);
|
ResourceMark rm(jt);
|
||||||
const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt);
|
const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt);
|
||||||
{
|
if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
|
||||||
MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
|
// new output is NULL and current output is NULL
|
||||||
if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
|
return;
|
||||||
// new output is NULL and current output is NULL
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
instance().set_chunk_path(canonical_chunk_path);
|
|
||||||
}
|
}
|
||||||
|
instance().set_chunk_path(canonical_chunk_path);
|
||||||
notify_on_new_chunk_path();
|
notify_on_new_chunk_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,14 +153,28 @@ void JfrRepository::set_path(jstring location, JavaThread* jt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool JfrRepository::open_chunk(bool vm_error /* false */) {
|
bool JfrRepository::open_chunk(bool vm_error /* false */) {
|
||||||
assert(JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
if (vm_error) {
|
if (vm_error) {
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
_chunkwriter->set_chunk_path(JfrEmergencyDump::build_dump_path(_path));
|
_chunkwriter->set_path(JfrEmergencyDump::build_dump_path(_path));
|
||||||
}
|
}
|
||||||
return _chunkwriter->open();
|
return _chunkwriter->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrRepository::close_chunk(int64_t metadata_offset) {
|
size_t JfrRepository::close_chunk() {
|
||||||
return _chunkwriter->close(metadata_offset);
|
return _chunkwriter->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrRepository::flush(JavaThread* jt) {
|
||||||
|
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
|
||||||
|
if (!Jfr::is_recording()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_chunkwriter->is_valid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
instance()._post_box.post(MSG_FLUSHPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t JfrRepository::flush_chunk() {
|
||||||
|
return _chunkwriter->flush_chunk(true);
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,10 @@ class JfrRepository : public JfrCHeapObj {
|
|||||||
bool set_path(const char* path);
|
bool set_path(const char* path);
|
||||||
void set_chunk_path(const char* path);
|
void set_chunk_path(const char* path);
|
||||||
bool open_chunk(bool vm_error = false);
|
bool open_chunk(bool vm_error = false);
|
||||||
size_t close_chunk(int64_t metadata_offset);
|
size_t close_chunk();
|
||||||
|
size_t flush_chunk();
|
||||||
void on_vm_error();
|
void on_vm_error();
|
||||||
|
|
||||||
static void notify_on_new_chunk_path();
|
static void notify_on_new_chunk_path();
|
||||||
static JfrChunkWriter& chunkwriter();
|
static JfrChunkWriter& chunkwriter();
|
||||||
|
|
||||||
@ -68,6 +70,8 @@ class JfrRepository : public JfrCHeapObj {
|
|||||||
public:
|
public:
|
||||||
static void set_path(jstring location, JavaThread* jt);
|
static void set_path(jstring location, JavaThread* jt);
|
||||||
static void set_chunk_path(jstring path, JavaThread* jt);
|
static void set_chunk_path(jstring path, JavaThread* jt);
|
||||||
|
static void flush(JavaThread* jt);
|
||||||
|
static jlong current_chunk_start_nanos();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRREPOSITORY_HPP
|
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRREPOSITORY_HPP
|
||||||
|
@ -26,14 +26,14 @@
|
|||||||
#include "jfr/recorder/service/jfrPostBox.hpp"
|
#include "jfr/recorder/service/jfrPostBox.hpp"
|
||||||
#include "jfr/utilities/jfrTryLock.hpp"
|
#include "jfr/utilities/jfrTryLock.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
#include "runtime/orderAccess.hpp"
|
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
|
|
||||||
#define MSG_IS_SYNCHRONOUS ( (MSGBIT(MSG_ROTATE)) | \
|
#define MSG_IS_SYNCHRONOUS ( (MSGBIT(MSG_ROTATE)) | \
|
||||||
(MSGBIT(MSG_STOP)) | \
|
(MSGBIT(MSG_STOP)) | \
|
||||||
(MSGBIT(MSG_START)) | \
|
(MSGBIT(MSG_START)) | \
|
||||||
(MSGBIT(MSG_CLONE_IN_MEMORY)) | \
|
(MSGBIT(MSG_CLONE_IN_MEMORY)) | \
|
||||||
(MSGBIT(MSG_VM_ERROR)) \
|
(MSGBIT(MSG_VM_ERROR)) | \
|
||||||
|
(MSGBIT(MSG_FLUSHPOINT)) \
|
||||||
)
|
)
|
||||||
|
|
||||||
static JfrPostBox* _instance = NULL;
|
static JfrPostBox* _instance = NULL;
|
||||||
@ -84,7 +84,7 @@ void JfrPostBox::post(JFR_Msg msg) {
|
|||||||
|
|
||||||
void JfrPostBox::deposit(int new_messages) {
|
void JfrPostBox::deposit(int new_messages) {
|
||||||
while (true) {
|
while (true) {
|
||||||
const int current_msgs = OrderAccess::load_acquire(&_messages);
|
const int current_msgs = Atomic::load(&_messages);
|
||||||
// OR the new message
|
// OR the new message
|
||||||
const int exchange_value = current_msgs | new_messages;
|
const int exchange_value = current_msgs | new_messages;
|
||||||
const int result = Atomic::cmpxchg(exchange_value, &_messages, current_msgs);
|
const int result = Atomic::cmpxchg(exchange_value, &_messages, current_msgs);
|
||||||
@ -114,7 +114,7 @@ void JfrPostBox::synchronous_post(int msg) {
|
|||||||
deposit(msg);
|
deposit(msg);
|
||||||
// serial_id is used to check when what we send in has been processed.
|
// serial_id is used to check when what we send in has been processed.
|
||||||
// _msg_read_serial is read under JfrMsg_lock protection.
|
// _msg_read_serial is read under JfrMsg_lock protection.
|
||||||
const uintptr_t serial_id = OrderAccess::load_acquire(&_msg_read_serial) + 1;
|
const uintptr_t serial_id = Atomic::load(&_msg_read_serial) + 1;
|
||||||
msg_lock.notify_all();
|
msg_lock.notify_all();
|
||||||
while (!is_message_processed(serial_id)) {
|
while (!is_message_processed(serial_id)) {
|
||||||
msg_lock.wait();
|
msg_lock.wait();
|
||||||
@ -129,12 +129,12 @@ void JfrPostBox::synchronous_post(int msg) {
|
|||||||
*/
|
*/
|
||||||
bool JfrPostBox::is_message_processed(uintptr_t serial_id) const {
|
bool JfrPostBox::is_message_processed(uintptr_t serial_id) const {
|
||||||
assert(JfrMsg_lock->owned_by_self(), "_msg_handled_serial must be read under JfrMsg_lock protection");
|
assert(JfrMsg_lock->owned_by_self(), "_msg_handled_serial must be read under JfrMsg_lock protection");
|
||||||
return serial_id <= OrderAccess::load_acquire(&_msg_handled_serial);
|
return serial_id <= Atomic::load(&_msg_handled_serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JfrPostBox::is_empty() const {
|
bool JfrPostBox::is_empty() const {
|
||||||
assert(JfrMsg_lock->owned_by_self(), "not holding JfrMsg_lock!");
|
assert(JfrMsg_lock->owned_by_self(), "not holding JfrMsg_lock!");
|
||||||
return OrderAccess::load_acquire(&_messages) == 0;
|
return Atomic::load(&_messages) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int JfrPostBox::collect() {
|
int JfrPostBox::collect() {
|
||||||
|
@ -41,6 +41,7 @@ enum JFR_Msg {
|
|||||||
MSG_SHUTDOWN,
|
MSG_SHUTDOWN,
|
||||||
MSG_VM_ERROR,
|
MSG_VM_ERROR,
|
||||||
MSG_DEADBUFFER,
|
MSG_DEADBUFFER,
|
||||||
|
MSG_FLUSHPOINT,
|
||||||
MSG_NO_OF_MSGS
|
MSG_NO_OF_MSGS
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -53,15 +54,16 @@ enum JFR_Msg {
|
|||||||
* MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2
|
* MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2
|
||||||
* MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4
|
* MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4
|
||||||
* MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8
|
* MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8
|
||||||
* MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 8) == 0x100
|
* MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100
|
||||||
|
* MSG_FLUSHPOINT (10) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0xa) == 0x400
|
||||||
*
|
*
|
||||||
* Asynchronous messages (posting thread returns immediately upon deposit):
|
* Asynchronous messages (posting thread returns immediately upon deposit):
|
||||||
*
|
*
|
||||||
* MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10
|
* MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10
|
||||||
* MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 5) == 0x20
|
* MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20
|
||||||
* MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 6) == 0x40
|
* MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40
|
||||||
* MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 7) == 0x80
|
* MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80
|
||||||
* MSG_DEADBUFFER (9) ; MSGBIT(MSG_DEADBUFFER) == (1 << 9) == 0x200
|
* MSG_DEADBUFFER (9) ; MSGBIT(MSG_DEADBUFFER) == (1 << 0x9) == 0x200
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class JfrPostBox : public JfrCHeapObj {
|
class JfrPostBox : public JfrCHeapObj {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "jfrfiles/jfrEventClasses.hpp"
|
||||||
#include "jfr/jni/jfrJavaSupport.hpp"
|
#include "jfr/jni/jfrJavaSupport.hpp"
|
||||||
#include "jfr/leakprofiler/leakProfiler.hpp"
|
#include "jfr/leakprofiler/leakProfiler.hpp"
|
||||||
#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
|
#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
|
||||||
@ -55,81 +56,102 @@
|
|||||||
#include "runtime/vmOperations.hpp"
|
#include "runtime/vmOperations.hpp"
|
||||||
#include "runtime/vmThread.hpp"
|
#include "runtime/vmThread.hpp"
|
||||||
|
|
||||||
// set data iff *dest == NULL
|
// incremented on each flushpoint
|
||||||
static bool try_set(void* const data, void** dest, bool clear) {
|
static u8 flushpoint_id = 0;
|
||||||
assert(data != NULL, "invariant");
|
|
||||||
const void* const current = OrderAccess::load_acquire(dest);
|
|
||||||
if (current != NULL) {
|
|
||||||
if (current != data) {
|
|
||||||
// already set
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
assert(current == data, "invariant");
|
|
||||||
if (!clear) {
|
|
||||||
// recursion disallowed
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Atomic::cmpxchg(clear ? NULL : data, dest, current) == current;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* rotation_thread = NULL;
|
template <typename E, typename Instance, size_t(Instance::*func)()>
|
||||||
static const int rotation_try_limit = 1000;
|
class Content {
|
||||||
static const int rotation_retry_sleep_millis = 10;
|
|
||||||
|
|
||||||
class RotationLock : public StackObj {
|
|
||||||
private:
|
private:
|
||||||
Thread* const _thread;
|
Instance& _instance;
|
||||||
bool _acquired;
|
u4 _elements;
|
||||||
|
|
||||||
void log(bool recursion) {
|
|
||||||
assert(!_acquired, "invariant");
|
|
||||||
const char* error_msg = NULL;
|
|
||||||
if (recursion) {
|
|
||||||
error_msg = "Unable to issue rotation due to recursive calls.";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
error_msg = "Unable to issue rotation due to wait timeout.";
|
|
||||||
}
|
|
||||||
log_info(jfr)( // For user, should not be "jfr, system"
|
|
||||||
"%s", error_msg);
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
RotationLock(Thread* thread) : _thread(thread), _acquired(false) {
|
typedef E EventType;
|
||||||
assert(_thread != NULL, "invariant");
|
Content(Instance& instance) : _instance(instance), _elements(0) {}
|
||||||
if (_thread == rotation_thread) {
|
bool process() {
|
||||||
// recursion not supported
|
_elements = (u4)(_instance.*func)();
|
||||||
log(true);
|
return true;
|
||||||
return;
|
}
|
||||||
}
|
u4 elements() const { return _elements; }
|
||||||
|
};
|
||||||
|
|
||||||
// limited to not spin indefinitely
|
template <typename Content>
|
||||||
for (int i = 0; i < rotation_try_limit; ++i) {
|
class WriteContent : public StackObj {
|
||||||
if (try_set(_thread, &rotation_thread, false)) {
|
protected:
|
||||||
_acquired = true;
|
const JfrTicks _start_time;
|
||||||
assert(_thread == rotation_thread, "invariant");
|
JfrTicks _end_time;
|
||||||
return;
|
JfrChunkWriter& _cw;
|
||||||
}
|
Content& _content;
|
||||||
if (_thread->is_Java_thread()) {
|
const int64_t _start_offset;
|
||||||
// in order to allow the system to move to a safepoint
|
public:
|
||||||
MutexLocker msg_lock(JfrMsg_lock);
|
typedef typename Content::EventType EventType;
|
||||||
JfrMsg_lock->wait(rotation_retry_sleep_millis);
|
|
||||||
}
|
WriteContent(JfrChunkWriter& cw, Content& content) :
|
||||||
else {
|
_start_time(JfrTicks::now()),
|
||||||
os::naked_short_sleep(rotation_retry_sleep_millis);
|
_end_time(),
|
||||||
}
|
_cw(cw),
|
||||||
}
|
_content(content),
|
||||||
log(false);
|
_start_offset(_cw.current_offset()) {
|
||||||
|
assert(_cw.is_valid(), "invariant");
|
||||||
}
|
}
|
||||||
|
|
||||||
~RotationLock() {
|
bool process() {
|
||||||
assert(_thread != NULL, "invariant");
|
// invocation
|
||||||
if (_acquired) {
|
_content.process();
|
||||||
assert(_thread == rotation_thread, "invariant");
|
_end_time = JfrTicks::now();
|
||||||
while (!try_set(_thread, &rotation_thread, true));
|
return 0 != _content.elements();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const JfrTicks& start_time() const {
|
||||||
|
return _start_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
const JfrTicks& end_time() const {
|
||||||
|
return _end_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t start_offset() const {
|
||||||
|
return _start_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t end_offset() const {
|
||||||
|
return current_offset();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t current_offset() const {
|
||||||
|
return _cw.current_offset();
|
||||||
|
}
|
||||||
|
|
||||||
|
u4 elements() const {
|
||||||
|
return (u4) _content.elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
u4 size() const {
|
||||||
|
return (u4)(end_offset() - start_offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_event_enabled() {
|
||||||
|
return EventType::is_enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 event_id() {
|
||||||
|
return EventType::eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_elements(int64_t offset) {
|
||||||
|
_cw.write_padded_at_offset<u4>(elements(), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_size() {
|
||||||
|
_cw.write_padded_at_offset<u4>(size(), start_offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_last_checkpoint() {
|
||||||
|
_cw.set_last_checkpoint_offset(start_offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
void rewind() {
|
||||||
|
_cw.seek(start_offset());
|
||||||
}
|
}
|
||||||
bool not_acquired() const { return !_acquired; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) {
|
static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) {
|
||||||
@ -138,65 +160,176 @@ static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) {
|
|||||||
cw.reserve(sizeof(u4));
|
cw.reserve(sizeof(u4));
|
||||||
cw.write<u8>(EVENT_CHECKPOINT);
|
cw.write<u8>(EVENT_CHECKPOINT);
|
||||||
cw.write(JfrTicks::now());
|
cw.write(JfrTicks::now());
|
||||||
cw.write((int64_t)0); // duration
|
cw.write<u8>(0); // duration
|
||||||
cw.write(delta_to_last_checkpoint);
|
cw.write(delta_to_last_checkpoint);
|
||||||
cw.write<bool>(false); // flushpoint
|
cw.write<u4>(GENERIC); // checkpoint type
|
||||||
cw.write((u4)1); // nof types in this checkpoint
|
cw.write<u4>(1); // nof types in this checkpoint
|
||||||
cw.write(type_id);
|
cw.write(type_id);
|
||||||
const int64_t number_of_elements_offset = cw.current_offset();
|
return cw.reserve(sizeof(u4));
|
||||||
cw.reserve(sizeof(u4));
|
|
||||||
return number_of_elements_offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContentFunctor>
|
template <typename Content>
|
||||||
class WriteCheckpointEvent : public StackObj {
|
class WriteCheckpointEvent : public WriteContent<Content> {
|
||||||
private:
|
private:
|
||||||
JfrChunkWriter& _cw;
|
const u8 _type_id;
|
||||||
u8 _type_id;
|
|
||||||
ContentFunctor& _content_functor;
|
|
||||||
public:
|
public:
|
||||||
WriteCheckpointEvent(JfrChunkWriter& cw, u8 type_id, ContentFunctor& functor) :
|
WriteCheckpointEvent(JfrChunkWriter& cw, Content& content, u8 type_id) :
|
||||||
_cw(cw),
|
WriteContent<Content>(cw, content), _type_id(type_id) {}
|
||||||
_type_id(type_id),
|
|
||||||
_content_functor(functor) {
|
|
||||||
assert(_cw.is_valid(), "invariant");
|
|
||||||
}
|
|
||||||
bool process() {
|
bool process() {
|
||||||
// current_cp_offset is also offset for the event size header field
|
const int64_t num_elements_offset = write_checkpoint_event_prologue(this->_cw, _type_id);
|
||||||
const int64_t current_cp_offset = _cw.current_offset();
|
if (!WriteContent<Content>::process()) {
|
||||||
const int64_t num_elements_offset = write_checkpoint_event_prologue(_cw, _type_id);
|
|
||||||
// invocation
|
|
||||||
_content_functor.process();
|
|
||||||
const u4 number_of_elements = (u4)_content_functor.processed();
|
|
||||||
if (number_of_elements == 0) {
|
|
||||||
// nothing to do, rewind writer to start
|
// nothing to do, rewind writer to start
|
||||||
_cw.seek(current_cp_offset);
|
this->rewind();
|
||||||
return true;
|
assert(this->current_offset() == this->start_offset(), "invariant");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
assert(number_of_elements > 0, "invariant");
|
assert(this->elements() > 0, "invariant");
|
||||||
assert(_cw.current_offset() > num_elements_offset, "invariant");
|
assert(this->current_offset() > num_elements_offset, "invariant");
|
||||||
_cw.write_padded_at_offset<u4>(number_of_elements, num_elements_offset);
|
this->write_elements(num_elements_offset);
|
||||||
_cw.write_padded_at_offset<u4>((u4)_cw.current_offset() - current_cp_offset, current_cp_offset);
|
this->write_size();
|
||||||
// update writer with last checkpoint position
|
this->set_last_checkpoint();
|
||||||
_cw.set_last_checkpoint_offset(current_cp_offset);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Instance, size_t(Instance::*func)()>
|
template <typename Functor>
|
||||||
class ServiceFunctor {
|
static u4 invoke(Functor& f) {
|
||||||
|
f.process();
|
||||||
|
return f.elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void write_flush_event(Functor& f) {
|
||||||
|
if (Functor::is_event_enabled()) {
|
||||||
|
typename Functor::EventType e(UNTIMED);
|
||||||
|
e.set_starttime(f.start_time());
|
||||||
|
e.set_endtime(f.end_time());
|
||||||
|
e.set_flushId(flushpoint_id);
|
||||||
|
e.set_elements(f.elements());
|
||||||
|
e.set_size(f.size());
|
||||||
|
e.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static u4 invoke_with_flush_event(Functor& f) {
|
||||||
|
const u4 elements = invoke(f);
|
||||||
|
write_flush_event(f);
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
class StackTraceRepository : public StackObj {
|
||||||
private:
|
private:
|
||||||
Instance& _instance;
|
JfrStackTraceRepository& _repo;
|
||||||
size_t _processed;
|
JfrChunkWriter& _cw;
|
||||||
|
size_t _elements;
|
||||||
|
bool _clear;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {}
|
typedef EventFlushStacktrace EventType;
|
||||||
|
StackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) :
|
||||||
|
_repo(repo), _cw(cw), _elements(0), _clear(clear) {}
|
||||||
bool process() {
|
bool process() {
|
||||||
_processed = (_instance.*func)();
|
_elements = _repo.write(_cw, _clear);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
size_t processed() const { return _processed; }
|
size_t elements() const { return _elements; }
|
||||||
|
void reset() { _elements = 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef WriteCheckpointEvent<StackTraceRepository> WriteStackTrace;
|
||||||
|
|
||||||
|
static u4 flush_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter) {
|
||||||
|
StackTraceRepository str(stack_trace_repo, chunkwriter, false);
|
||||||
|
WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE);
|
||||||
|
return invoke_with_flush_event(wst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u4 write_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) {
|
||||||
|
StackTraceRepository str(stack_trace_repo, chunkwriter, clear);
|
||||||
|
WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE);
|
||||||
|
return invoke(wst);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef Content<EventFlushStorage, JfrStorage, &JfrStorage::write> Storage;
|
||||||
|
typedef WriteContent<Storage> WriteStorage;
|
||||||
|
|
||||||
|
static size_t flush_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) {
|
||||||
|
assert(chunkwriter.is_valid(), "invariant");
|
||||||
|
Storage fsf(storage);
|
||||||
|
WriteStorage fs(chunkwriter, fsf);
|
||||||
|
return invoke_with_flush_event(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t write_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) {
|
||||||
|
assert(chunkwriter.is_valid(), "invariant");
|
||||||
|
Storage fsf(storage);
|
||||||
|
WriteStorage fs(chunkwriter, fsf);
|
||||||
|
return invoke(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef Content<EventFlushStringPool, JfrStringPool, &JfrStringPool::write> StringPool;
|
||||||
|
typedef Content<EventFlushStringPool, JfrStringPool, &JfrStringPool::write_at_safepoint> StringPoolSafepoint;
|
||||||
|
typedef WriteCheckpointEvent<StringPool> WriteStringPool;
|
||||||
|
typedef WriteCheckpointEvent<StringPoolSafepoint> WriteStringPoolSafepoint;
|
||||||
|
|
||||||
|
static u4 flush_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
|
||||||
|
StringPool sp(string_pool);
|
||||||
|
WriteStringPool wsp(chunkwriter, sp, TYPE_STRING);
|
||||||
|
return invoke_with_flush_event(wsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u4 write_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
|
||||||
|
StringPool sp(string_pool);
|
||||||
|
WriteStringPool wsp(chunkwriter, sp, TYPE_STRING);
|
||||||
|
return invoke(wsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u4 write_stringpool_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
|
||||||
|
StringPoolSafepoint sps(string_pool);
|
||||||
|
WriteStringPoolSafepoint wsps(chunkwriter, sps, TYPE_STRING);
|
||||||
|
return invoke(wsps);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef Content<EventFlushTypeSet, JfrCheckpointManager, &JfrCheckpointManager::flush_type_set> FlushTypeSetFunctor;
|
||||||
|
typedef WriteContent<FlushTypeSetFunctor> FlushTypeSet;
|
||||||
|
|
||||||
|
static u4 flush_typeset(JfrCheckpointManager& checkpoint_manager, JfrChunkWriter& chunkwriter) {
|
||||||
|
FlushTypeSetFunctor flush_type_set(checkpoint_manager);
|
||||||
|
FlushTypeSet fts(chunkwriter, flush_type_set);
|
||||||
|
return invoke_with_flush_event(fts);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MetadataEvent : public StackObj {
|
||||||
|
private:
|
||||||
|
JfrChunkWriter& _cw;
|
||||||
|
public:
|
||||||
|
typedef EventFlushMetadata EventType;
|
||||||
|
MetadataEvent(JfrChunkWriter& cw) : _cw(cw) {}
|
||||||
|
bool process() {
|
||||||
|
JfrMetadataEvent::write(_cw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
size_t elements() const { return 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef WriteContent<MetadataEvent> WriteMetadata;
|
||||||
|
|
||||||
|
static u4 flush_metadata(JfrChunkWriter& chunkwriter) {
|
||||||
|
assert(chunkwriter.is_valid(), "invariant");
|
||||||
|
MetadataEvent me(chunkwriter);
|
||||||
|
WriteMetadata wm(chunkwriter, me);
|
||||||
|
return invoke_with_flush_event(wm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u4 write_metadata(JfrChunkWriter& chunkwriter) {
|
||||||
|
assert(chunkwriter.is_valid(), "invariant");
|
||||||
|
MetadataEvent me(chunkwriter);
|
||||||
|
WriteMetadata wm(chunkwriter, me);
|
||||||
|
return invoke(wm);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Instance, void(Instance::*func)()>
|
template <typename Instance, void(Instance::*func)()>
|
||||||
class JfrVMOperation : public VM_Operation {
|
class JfrVMOperation : public VM_Operation {
|
||||||
private:
|
private:
|
||||||
@ -208,23 +341,13 @@ class JfrVMOperation : public VM_Operation {
|
|||||||
Mode evaluation_mode() const { return _safepoint; } // default
|
Mode evaluation_mode() const { return _safepoint; } // default
|
||||||
};
|
};
|
||||||
|
|
||||||
class WriteStackTraceRepository : public StackObj {
|
JfrRecorderService::JfrRecorderService() :
|
||||||
private:
|
_checkpoint_manager(JfrCheckpointManager::instance()),
|
||||||
JfrStackTraceRepository& _repo;
|
_chunkwriter(JfrRepository::chunkwriter()),
|
||||||
JfrChunkWriter& _cw;
|
_repository(JfrRepository::instance()),
|
||||||
size_t _elements_processed;
|
_stack_trace_repository(JfrStackTraceRepository::instance()),
|
||||||
bool _clear;
|
_storage(JfrStorage::instance()),
|
||||||
|
_string_pool(JfrStringPool::instance()) {}
|
||||||
public:
|
|
||||||
WriteStackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) :
|
|
||||||
_repo(repo), _cw(cw), _elements_processed(0), _clear(clear) {}
|
|
||||||
bool process() {
|
|
||||||
_elements_processed = _repo.write(_cw, _clear);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
size_t processed() const { return _elements_processed; }
|
|
||||||
void reset() { _elements_processed = 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool recording = false;
|
static bool recording = false;
|
||||||
|
|
||||||
@ -237,19 +360,8 @@ bool JfrRecorderService::is_recording() {
|
|||||||
return recording;
|
return recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
JfrRecorderService::JfrRecorderService() :
|
|
||||||
_checkpoint_manager(JfrCheckpointManager::instance()),
|
|
||||||
_chunkwriter(JfrRepository::chunkwriter()),
|
|
||||||
_repository(JfrRepository::instance()),
|
|
||||||
_stack_trace_repository(JfrStackTraceRepository::instance()),
|
|
||||||
_storage(JfrStorage::instance()),
|
|
||||||
_string_pool(JfrStringPool::instance()) {}
|
|
||||||
|
|
||||||
void JfrRecorderService::start() {
|
void JfrRecorderService::start() {
|
||||||
RotationLock rl(Thread::current());
|
MutexLocker lock(JfrStream_lock);
|
||||||
if (rl.not_acquired()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log_debug(jfr, system)("Request to START recording");
|
log_debug(jfr, system)("Request to START recording");
|
||||||
assert(!is_recording(), "invariant");
|
assert(!is_recording(), "invariant");
|
||||||
clear();
|
clear();
|
||||||
@ -268,9 +380,9 @@ void JfrRecorderService::clear() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::pre_safepoint_clear() {
|
void JfrRecorderService::pre_safepoint_clear() {
|
||||||
_stack_trace_repository.clear();
|
|
||||||
_string_pool.clear();
|
_string_pool.clear();
|
||||||
_storage.clear();
|
_storage.clear();
|
||||||
|
_stack_trace_repository.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::invoke_safepoint_clear() {
|
void JfrRecorderService::invoke_safepoint_clear() {
|
||||||
@ -278,28 +390,28 @@ void JfrRecorderService::invoke_safepoint_clear() {
|
|||||||
VMThread::execute(&safepoint_task);
|
VMThread::execute(&safepoint_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// safepoint clear sequence
|
|
||||||
//
|
|
||||||
// clear stacktrace repository ->
|
|
||||||
// clear string pool ->
|
|
||||||
// clear storage ->
|
|
||||||
// shift epoch ->
|
|
||||||
// update time
|
|
||||||
//
|
|
||||||
void JfrRecorderService::safepoint_clear() {
|
void JfrRecorderService::safepoint_clear() {
|
||||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||||
_stack_trace_repository.clear();
|
|
||||||
_string_pool.clear();
|
_string_pool.clear();
|
||||||
_storage.clear();
|
_storage.clear();
|
||||||
_checkpoint_manager.shift_epoch();
|
_checkpoint_manager.shift_epoch();
|
||||||
_chunkwriter.time_stamp_chunk_now();
|
_chunkwriter.set_time_stamp();
|
||||||
|
_stack_trace_repository.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::post_safepoint_clear() {
|
void JfrRecorderService::post_safepoint_clear() {
|
||||||
_checkpoint_manager.clear();
|
_checkpoint_manager.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JfrRecorderService::open_new_chunk(bool vm_error) {
|
||||||
|
JfrChunkRotation::on_rotation();
|
||||||
|
const bool valid_chunk = _repository.open_chunk(vm_error);
|
||||||
|
_storage.control().set_to_disk(valid_chunk);
|
||||||
|
if (valid_chunk) {
|
||||||
|
_checkpoint_manager.write_static_type_set_and_threads();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void stop() {
|
static void stop() {
|
||||||
assert(JfrRecorderService::is_recording(), "invariant");
|
assert(JfrRecorderService::is_recording(), "invariant");
|
||||||
log_debug(jfr, system)("Recording STOPPED");
|
log_debug(jfr, system)("Recording STOPPED");
|
||||||
@ -307,11 +419,30 @@ static void stop() {
|
|||||||
assert(!JfrRecorderService::is_recording(), "invariant");
|
assert(!JfrRecorderService::is_recording(), "invariant");
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::rotate(int msgs) {
|
void JfrRecorderService::prepare_for_vm_error_rotation() {
|
||||||
RotationLock rl(Thread::current());
|
assert(JfrStream_lock->owned_by_self(), "invariant");
|
||||||
if (rl.not_acquired()) {
|
if (!_chunkwriter.is_valid()) {
|
||||||
return;
|
open_new_chunk(true);
|
||||||
}
|
}
|
||||||
|
_checkpoint_manager.register_service_thread(Thread::current());
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrRecorderService::vm_error_rotation() {
|
||||||
|
assert(JfrStream_lock->owned_by_self(), "invariant");
|
||||||
|
if (_chunkwriter.is_valid()) {
|
||||||
|
Thread* const t = Thread::current();
|
||||||
|
_storage.flush_regular_buffer(t->jfr_thread_local()->native_buffer(), t);
|
||||||
|
invoke_flush();
|
||||||
|
_chunkwriter.set_time_stamp();
|
||||||
|
_repository.close_chunk();
|
||||||
|
assert(!_chunkwriter.is_valid(), "invariant");
|
||||||
|
_repository.on_vm_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrRecorderService::rotate(int msgs) {
|
||||||
|
assert(!JfrStream_lock->owned_by_self(), "invariant");
|
||||||
|
MutexLocker lock(JfrStream_lock);
|
||||||
static bool vm_error = false;
|
static bool vm_error = false;
|
||||||
if (msgs & MSGBIT(MSG_VM_ERROR)) {
|
if (msgs & MSGBIT(MSG_VM_ERROR)) {
|
||||||
vm_error = true;
|
vm_error = true;
|
||||||
@ -329,45 +460,19 @@ void JfrRecorderService::rotate(int msgs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::prepare_for_vm_error_rotation() {
|
|
||||||
if (!_chunkwriter.is_valid()) {
|
|
||||||
open_new_chunk(true);
|
|
||||||
}
|
|
||||||
_checkpoint_manager.register_service_thread(Thread::current());
|
|
||||||
JfrMetadataEvent::lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrRecorderService::open_new_chunk(bool vm_error) {
|
|
||||||
assert(!_chunkwriter.is_valid(), "invariant");
|
|
||||||
assert(!JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
JfrChunkRotation::on_rotation();
|
|
||||||
MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
if (!_repository.open_chunk(vm_error)) {
|
|
||||||
assert(!_chunkwriter.is_valid(), "invariant");
|
|
||||||
_storage.control().set_to_disk(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
assert(_chunkwriter.is_valid(), "invariant");
|
|
||||||
_storage.control().set_to_disk(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrRecorderService::in_memory_rotation() {
|
void JfrRecorderService::in_memory_rotation() {
|
||||||
assert(!_chunkwriter.is_valid(), "invariant");
|
assert(JfrStream_lock->owned_by_self(), "invariant");
|
||||||
// currently running an in-memory recording
|
// currently running an in-memory recording
|
||||||
|
assert(!_storage.control().to_disk(), "invariant");
|
||||||
open_new_chunk();
|
open_new_chunk();
|
||||||
if (_chunkwriter.is_valid()) {
|
if (_chunkwriter.is_valid()) {
|
||||||
// dump all in-memory buffer data to the newly created chunk
|
// dump all in-memory buffer data to the newly created chunk
|
||||||
serialize_storage_from_in_memory_recording();
|
write_storage(_storage, _chunkwriter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::serialize_storage_from_in_memory_recording() {
|
|
||||||
assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!");
|
|
||||||
MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
_storage.write();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrRecorderService::chunk_rotation() {
|
void JfrRecorderService::chunk_rotation() {
|
||||||
|
assert(JfrStream_lock->owned_by_self(), "invariant");
|
||||||
finalize_current_chunk();
|
finalize_current_chunk();
|
||||||
open_new_chunk();
|
open_new_chunk();
|
||||||
}
|
}
|
||||||
@ -375,7 +480,6 @@ void JfrRecorderService::chunk_rotation() {
|
|||||||
void JfrRecorderService::finalize_current_chunk() {
|
void JfrRecorderService::finalize_current_chunk() {
|
||||||
assert(_chunkwriter.is_valid(), "invariant");
|
assert(_chunkwriter.is_valid(), "invariant");
|
||||||
write();
|
write();
|
||||||
assert(!_chunkwriter.is_valid(), "invariant");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::write() {
|
void JfrRecorderService::write() {
|
||||||
@ -386,54 +490,20 @@ void JfrRecorderService::write() {
|
|||||||
post_safepoint_write();
|
post_safepoint_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write> WriteStringPool;
|
|
||||||
typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write_at_safepoint> WriteStringPoolSafepoint;
|
|
||||||
typedef WriteCheckpointEvent<WriteStackTraceRepository> WriteStackTraceCheckpoint;
|
|
||||||
typedef WriteCheckpointEvent<WriteStringPool> WriteStringPoolCheckpoint;
|
|
||||||
typedef WriteCheckpointEvent<WriteStringPoolSafepoint> WriteStringPoolCheckpointSafepoint;
|
|
||||||
|
|
||||||
static void write_stacktrace_checkpoint(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) {
|
|
||||||
WriteStackTraceRepository write_stacktrace_repo(stack_trace_repo, chunkwriter, clear);
|
|
||||||
WriteStackTraceCheckpoint write_stack_trace_checkpoint(chunkwriter, TYPE_STACKTRACE, write_stacktrace_repo);
|
|
||||||
write_stack_trace_checkpoint.process();
|
|
||||||
}
|
|
||||||
static void write_stringpool_checkpoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
|
|
||||||
WriteStringPool write_string_pool(string_pool);
|
|
||||||
WriteStringPoolCheckpoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool);
|
|
||||||
write_string_pool_checkpoint.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void write_stringpool_checkpoint_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
|
|
||||||
WriteStringPoolSafepoint write_string_pool(string_pool);
|
|
||||||
WriteStringPoolCheckpointSafepoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool);
|
|
||||||
write_string_pool_checkpoint.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// pre-safepoint write sequence
|
|
||||||
//
|
|
||||||
// lock stream lock ->
|
|
||||||
// write non-safepoint dependent types ->
|
|
||||||
// write checkpoint epoch transition list->
|
|
||||||
// write stack trace checkpoint ->
|
|
||||||
// write string pool checkpoint ->
|
|
||||||
// write object sample stacktraces ->
|
|
||||||
// write storage ->
|
|
||||||
// release stream lock
|
|
||||||
//
|
|
||||||
void JfrRecorderService::pre_safepoint_write() {
|
void JfrRecorderService::pre_safepoint_write() {
|
||||||
MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
assert(_chunkwriter.is_valid(), "invariant");
|
assert(_chunkwriter.is_valid(), "invariant");
|
||||||
_checkpoint_manager.write_types();
|
|
||||||
_checkpoint_manager.write_epoch_transition_mspace();
|
|
||||||
write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, false);
|
|
||||||
write_stringpool_checkpoint(_string_pool, _chunkwriter);
|
|
||||||
if (LeakProfiler::is_running()) {
|
if (LeakProfiler::is_running()) {
|
||||||
// Exclusive access to the object sampler instance.
|
// Exclusive access to the object sampler instance.
|
||||||
// The sampler is released (unlocked) later in post_safepoint_write.
|
// The sampler is released (unlocked) later in post_safepoint_write.
|
||||||
ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire(), _stack_trace_repository);
|
ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire(), _stack_trace_repository);
|
||||||
}
|
}
|
||||||
_storage.write();
|
if (_string_pool.is_modified()) {
|
||||||
|
write_stringpool(_string_pool, _chunkwriter);
|
||||||
|
}
|
||||||
|
write_storage(_storage, _chunkwriter);
|
||||||
|
if (_stack_trace_repository.is_modified()) {
|
||||||
|
write_stacktrace(_stack_trace_repository, _chunkwriter, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::invoke_safepoint_write() {
|
void JfrRecorderService::invoke_safepoint_write() {
|
||||||
@ -441,50 +511,18 @@ void JfrRecorderService::invoke_safepoint_write() {
|
|||||||
VMThread::execute(&safepoint_task);
|
VMThread::execute(&safepoint_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// safepoint write sequence
|
|
||||||
//
|
|
||||||
// lock stream lock ->
|
|
||||||
// write stacktrace repository ->
|
|
||||||
// write string pool ->
|
|
||||||
// write safepoint dependent types ->
|
|
||||||
// write storage ->
|
|
||||||
// shift_epoch ->
|
|
||||||
// update time ->
|
|
||||||
// lock metadata descriptor ->
|
|
||||||
// release stream lock
|
|
||||||
//
|
|
||||||
void JfrRecorderService::safepoint_write() {
|
void JfrRecorderService::safepoint_write() {
|
||||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||||
MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
|
if (_string_pool.is_modified()) {
|
||||||
write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, true);
|
write_stringpool_safepoint(_string_pool, _chunkwriter);
|
||||||
write_stringpool_checkpoint_safepoint(_string_pool, _chunkwriter);
|
}
|
||||||
_checkpoint_manager.write_safepoint_types();
|
_checkpoint_manager.on_rotation();
|
||||||
_storage.write_at_safepoint();
|
_storage.write_at_safepoint();
|
||||||
_checkpoint_manager.shift_epoch();
|
_checkpoint_manager.shift_epoch();
|
||||||
_chunkwriter.time_stamp_chunk_now();
|
_chunkwriter.set_time_stamp();
|
||||||
JfrMetadataEvent::lock();
|
write_stacktrace(_stack_trace_repository, _chunkwriter, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t write_metadata_event(JfrChunkWriter& chunkwriter) {
|
|
||||||
assert(chunkwriter.is_valid(), "invariant");
|
|
||||||
const int64_t metadata_offset = chunkwriter.current_offset();
|
|
||||||
JfrMetadataEvent::write(chunkwriter, metadata_offset);
|
|
||||||
return metadata_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// post-safepoint write sequence
|
|
||||||
//
|
|
||||||
// write type set ->
|
|
||||||
// release object sampler ->
|
|
||||||
// lock stream lock ->
|
|
||||||
// write checkpoints ->
|
|
||||||
// write metadata event ->
|
|
||||||
// write chunk header ->
|
|
||||||
// close chunk fd ->
|
|
||||||
// release stream lock
|
|
||||||
//
|
|
||||||
void JfrRecorderService::post_safepoint_write() {
|
void JfrRecorderService::post_safepoint_write() {
|
||||||
assert(_chunkwriter.is_valid(), "invariant");
|
assert(_chunkwriter.is_valid(), "invariant");
|
||||||
// During the safepoint tasks just completed, the system transitioned to a new epoch.
|
// During the safepoint tasks just completed, the system transitioned to a new epoch.
|
||||||
@ -497,38 +535,84 @@ void JfrRecorderService::post_safepoint_write() {
|
|||||||
// Note: There is a dependency on write_type_set() above, ensure the release is subsequent.
|
// Note: There is a dependency on write_type_set() above, ensure the release is subsequent.
|
||||||
ObjectSampler::release();
|
ObjectSampler::release();
|
||||||
}
|
}
|
||||||
MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
// serialize any outstanding checkpoint memory
|
|
||||||
_checkpoint_manager.write();
|
|
||||||
// serialize the metadata descriptor event and close out the chunk
|
// serialize the metadata descriptor event and close out the chunk
|
||||||
_repository.close_chunk(write_metadata_event(_chunkwriter));
|
write_metadata(_chunkwriter);
|
||||||
assert(!_chunkwriter.is_valid(), "invariant");
|
_repository.close_chunk();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::vm_error_rotation() {
|
static JfrBuffer* thread_local_buffer(Thread* t) {
|
||||||
if (_chunkwriter.is_valid()) {
|
assert(t != NULL, "invariant");
|
||||||
finalize_current_chunk_on_vm_error();
|
return t->jfr_thread_local()->native_buffer();
|
||||||
assert(!_chunkwriter.is_valid(), "invariant");
|
}
|
||||||
_repository.on_vm_error();
|
|
||||||
|
static void reset_buffer(JfrBuffer* buffer, Thread* t) {
|
||||||
|
assert(buffer != NULL, "invariant");
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
assert(buffer == thread_local_buffer(t), "invariant");
|
||||||
|
buffer->set_pos(const_cast<u1*>(buffer->top()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_thread_local_buffer(Thread* t) {
|
||||||
|
reset_buffer(thread_local_buffer(t), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_thread_local_buffer(JfrChunkWriter& chunkwriter, Thread* t) {
|
||||||
|
JfrBuffer * const buffer = thread_local_buffer(t);
|
||||||
|
assert(buffer != NULL, "invariant");
|
||||||
|
if (!buffer->empty()) {
|
||||||
|
chunkwriter.write_unbuffered(buffer->top(), buffer->pos() - buffer->top());
|
||||||
|
reset_buffer(buffer, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::finalize_current_chunk_on_vm_error() {
|
size_t JfrRecorderService::flush() {
|
||||||
|
assert(JfrStream_lock->owned_by_self(), "invariant");
|
||||||
|
size_t total_elements = flush_metadata(_chunkwriter);
|
||||||
|
const size_t storage_elements = flush_storage(_storage, _chunkwriter);
|
||||||
|
if (0 == storage_elements) {
|
||||||
|
return total_elements;
|
||||||
|
}
|
||||||
|
total_elements += storage_elements;
|
||||||
|
if (_string_pool.is_modified()) {
|
||||||
|
total_elements += flush_stringpool(_string_pool, _chunkwriter);
|
||||||
|
}
|
||||||
|
if (_stack_trace_repository.is_modified()) {
|
||||||
|
total_elements += flush_stacktrace(_stack_trace_repository, _chunkwriter);
|
||||||
|
}
|
||||||
|
if (_checkpoint_manager.is_type_set_required()) {
|
||||||
|
total_elements += flush_typeset(_checkpoint_manager, _chunkwriter);
|
||||||
|
} else if (_checkpoint_manager.is_static_type_set_required()) {
|
||||||
|
// don't tally this, it is only in order to flush the waiting constants
|
||||||
|
_checkpoint_manager.flush_static_type_set();
|
||||||
|
}
|
||||||
|
return total_elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef Content<EventFlush, JfrRecorderService, &JfrRecorderService::flush> FlushFunctor;
|
||||||
|
typedef WriteContent<FlushFunctor> Flush;
|
||||||
|
|
||||||
|
void JfrRecorderService::invoke_flush() {
|
||||||
|
assert(JfrStream_lock->owned_by_self(), "invariant");
|
||||||
assert(_chunkwriter.is_valid(), "invariant");
|
assert(_chunkwriter.is_valid(), "invariant");
|
||||||
pre_safepoint_write();
|
Thread* const t = Thread::current();
|
||||||
// Do not attempt safepoint dependent operations during emergency dump.
|
ResourceMark rm(t);
|
||||||
// Optimistically write tagged artifacts.
|
HandleMark hm(t);
|
||||||
_checkpoint_manager.shift_epoch();
|
++flushpoint_id;
|
||||||
// update time
|
reset_thread_local_buffer(t);
|
||||||
_chunkwriter.time_stamp_chunk_now();
|
FlushFunctor flushpoint(*this);
|
||||||
post_safepoint_write();
|
Flush fl(_chunkwriter, flushpoint);
|
||||||
assert(!_chunkwriter.is_valid(), "invariant");
|
invoke_with_flush_event(fl);
|
||||||
|
write_thread_local_buffer(_chunkwriter, t);
|
||||||
|
_repository.flush_chunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrRecorderService::flushpoint() {
|
||||||
|
MutexLocker lock(JfrStream_lock);
|
||||||
|
invoke_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrRecorderService::process_full_buffers() {
|
void JfrRecorderService::process_full_buffers() {
|
||||||
if (_chunkwriter.is_valid()) {
|
if (_chunkwriter.is_valid()) {
|
||||||
assert(!JfrStream_lock->owned_by_self(), "invariant");
|
|
||||||
MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
_storage.write_full();
|
_storage.write_full();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,11 +46,10 @@ class JfrRecorderService : public StackObj {
|
|||||||
void open_new_chunk(bool vm_error = false);
|
void open_new_chunk(bool vm_error = false);
|
||||||
void chunk_rotation();
|
void chunk_rotation();
|
||||||
void in_memory_rotation();
|
void in_memory_rotation();
|
||||||
void serialize_storage_from_in_memory_recording();
|
|
||||||
void finalize_current_chunk();
|
void finalize_current_chunk();
|
||||||
void finalize_current_chunk_on_vm_error();
|
|
||||||
void prepare_for_vm_error_rotation();
|
void prepare_for_vm_error_rotation();
|
||||||
void vm_error_rotation();
|
void vm_error_rotation();
|
||||||
|
void invoke_flush();
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
void pre_safepoint_clear();
|
void pre_safepoint_clear();
|
||||||
@ -67,7 +66,9 @@ class JfrRecorderService : public StackObj {
|
|||||||
public:
|
public:
|
||||||
JfrRecorderService();
|
JfrRecorderService();
|
||||||
void start();
|
void start();
|
||||||
|
size_t flush();
|
||||||
void rotate(int msgs);
|
void rotate(int msgs);
|
||||||
|
void flushpoint();
|
||||||
void process_full_buffers();
|
void process_full_buffers();
|
||||||
void scavenge();
|
void scavenge();
|
||||||
void evaluate_chunk_size_for_rotation();
|
void evaluate_chunk_size_for_rotation();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,6 +27,7 @@
|
|||||||
#include "classfile/javaClasses.hpp"
|
#include "classfile/javaClasses.hpp"
|
||||||
#include "classfile/symbolTable.hpp"
|
#include "classfile/symbolTable.hpp"
|
||||||
#include "classfile/systemDictionary.hpp"
|
#include "classfile/systemDictionary.hpp"
|
||||||
|
#include "jfr/jfr.hpp"
|
||||||
#include "jfr/jni/jfrJavaSupport.hpp"
|
#include "jfr/jni/jfrJavaSupport.hpp"
|
||||||
#include "jfr/recorder/jfrRecorder.hpp"
|
#include "jfr/recorder/jfrRecorder.hpp"
|
||||||
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
|
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
|
||||||
@ -64,7 +65,6 @@ static Thread* start_thread(instanceHandle thread_oop, ThreadFunction proc, TRAP
|
|||||||
if (allocation_failed) {
|
if (allocation_failed) {
|
||||||
JfrJavaSupport::throw_out_of_memory_error("Unable to create native recording thread for JFR", CHECK_NULL);
|
JfrJavaSupport::throw_out_of_memory_error("Unable to create native recording thread for JFR", CHECK_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::start(new_thread);
|
Thread::start(new_thread);
|
||||||
return new_thread;
|
return new_thread;
|
||||||
}
|
}
|
||||||
@ -98,8 +98,9 @@ bool JfrRecorderThread::start(JfrCheckpointManager* cp_manager, JfrPostBox* post
|
|||||||
instanceHandle h_thread_oop(THREAD, (instanceOop)result.get_jobject());
|
instanceHandle h_thread_oop(THREAD, (instanceOop)result.get_jobject());
|
||||||
assert(h_thread_oop.not_null(), "invariant");
|
assert(h_thread_oop.not_null(), "invariant");
|
||||||
// attempt thread start
|
// attempt thread start
|
||||||
const Thread* const t = start_thread(h_thread_oop, recorderthread_entry,THREAD);
|
Thread* const t = start_thread(h_thread_oop, recorderthread_entry,THREAD);
|
||||||
if (!HAS_PENDING_EXCEPTION) {
|
if (!HAS_PENDING_EXCEPTION) {
|
||||||
|
Jfr::exclude_thread(t);
|
||||||
cp_manager->register_service_thread(t);
|
cp_manager->register_service_thread(t);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ void recorderthread_entry(JavaThread* thread, Thread* unused) {
|
|||||||
#define START (msgs & (MSGBIT(MSG_START)))
|
#define START (msgs & (MSGBIT(MSG_START)))
|
||||||
#define SHUTDOWN (msgs & MSGBIT(MSG_SHUTDOWN))
|
#define SHUTDOWN (msgs & MSGBIT(MSG_SHUTDOWN))
|
||||||
#define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)))
|
#define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)))
|
||||||
|
#define FLUSHPOINT (msgs & (MSGBIT(MSG_FLUSHPOINT)))
|
||||||
#define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER)))
|
#define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER)))
|
||||||
#define SCAVENGE (msgs & (MSGBIT(MSG_DEADBUFFER)))
|
#define SCAVENGE (msgs & (MSGBIT(MSG_DEADBUFFER)))
|
||||||
|
|
||||||
@ -72,6 +73,8 @@ void recorderthread_entry(JavaThread* thread, Thread* unused) {
|
|||||||
service.start();
|
service.start();
|
||||||
} else if (ROTATE) {
|
} else if (ROTATE) {
|
||||||
service.rotate(msgs);
|
service.rotate(msgs);
|
||||||
|
} else if (FLUSHPOINT) {
|
||||||
|
service.flushpoint();
|
||||||
}
|
}
|
||||||
JfrMsg_lock->lock();
|
JfrMsg_lock->lock();
|
||||||
post_box.notify_waiters();
|
post_box.notify_waiters();
|
||||||
@ -90,6 +93,7 @@ void recorderthread_entry(JavaThread* thread, Thread* unused) {
|
|||||||
#undef START
|
#undef START
|
||||||
#undef SHUTDOWN
|
#undef SHUTDOWN
|
||||||
#undef ROTATE
|
#undef ROTATE
|
||||||
|
#undef FLUSHPOINT
|
||||||
#undef PROCESS_FULL_BUFFERS
|
#undef PROCESS_FULL_BUFFERS
|
||||||
#undef SCAVENGE
|
#undef SCAVENGE
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ class JfrFrameType : public JfrSerializer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool JfrStackTraceRepository::initialize() {
|
bool JfrStackTraceRepository::initialize() {
|
||||||
return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType());
|
return JfrSerializer::register_serializer(TYPE_FRAMETYPE, true, new JfrFrameType());
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrStackTraceRepository::destroy() {
|
void JfrStackTraceRepository::destroy() {
|
||||||
@ -71,7 +71,16 @@ void JfrStackTraceRepository::destroy() {
|
|||||||
_instance = NULL;
|
_instance = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) {
|
static traceid last_id = 0;
|
||||||
|
|
||||||
|
bool JfrStackTraceRepository::is_modified() const {
|
||||||
|
return last_id != _next_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) {
|
||||||
|
if (_entries == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
|
MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
|
||||||
assert(_entries > 0, "invariant");
|
assert(_entries > 0, "invariant");
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@ -93,29 +102,10 @@ size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) {
|
|||||||
memset(_table, 0, sizeof(_table));
|
memset(_table, 0, sizeof(_table));
|
||||||
_entries = 0;
|
_entries = 0;
|
||||||
}
|
}
|
||||||
|
last_id = _next_id;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) {
|
|
||||||
return _entries > 0 ? write_impl(sw, clear) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
traceid JfrStackTraceRepository::write(JfrCheckpointWriter& writer, traceid id, unsigned int hash) {
|
|
||||||
assert(JfrStacktrace_lock->owned_by_self(), "invariant");
|
|
||||||
const JfrStackTrace* const trace = lookup(hash, id);
|
|
||||||
assert(trace != NULL, "invariant");
|
|
||||||
assert(trace->hash() == hash, "invariant");
|
|
||||||
assert(trace->id() == id, "invariant");
|
|
||||||
trace->write(writer);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JfrStackTraceRepository::write_metadata(JfrCheckpointWriter& writer) {
|
|
||||||
JfrFrameType fct;
|
|
||||||
writer.write_type(TYPE_FRAMETYPE);
|
|
||||||
fct.serialize(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t JfrStackTraceRepository::clear() {
|
size_t JfrStackTraceRepository::clear() {
|
||||||
MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
|
MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
|
||||||
if (_entries == 0) {
|
if (_entries == 0) {
|
||||||
@ -142,7 +132,7 @@ traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) {
|
|||||||
if (tl->has_cached_stack_trace()) {
|
if (tl->has_cached_stack_trace()) {
|
||||||
return tl->cached_stack_trace_id();
|
return tl->cached_stack_trace_id();
|
||||||
}
|
}
|
||||||
if (!thread->is_Java_thread() || thread->is_hidden_from_external_view()) {
|
if (!thread->is_Java_thread() || thread->is_hidden_from_external_view() || tl->is_excluded()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
JfrStackFrame* frames = tl->stackframes();
|
JfrStackFrame* frames = tl->stackframes();
|
||||||
|
@ -40,7 +40,7 @@ class JfrStackTraceRepository : public JfrCHeapObj {
|
|||||||
friend class ObjectSampleCheckpoint;
|
friend class ObjectSampleCheckpoint;
|
||||||
friend class ObjectSampler;
|
friend class ObjectSampler;
|
||||||
friend class StackTraceBlobInstaller;
|
friend class StackTraceBlobInstaller;
|
||||||
friend class WriteStackTraceRepository;
|
friend class StackTraceRepository;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const u4 TABLE_SIZE = 2053;
|
static const u4 TABLE_SIZE = 2053;
|
||||||
@ -51,19 +51,18 @@ class JfrStackTraceRepository : public JfrCHeapObj {
|
|||||||
JfrStackTraceRepository();
|
JfrStackTraceRepository();
|
||||||
static JfrStackTraceRepository& instance();
|
static JfrStackTraceRepository& instance();
|
||||||
static JfrStackTraceRepository* create();
|
static JfrStackTraceRepository* create();
|
||||||
bool initialize();
|
|
||||||
static void destroy();
|
static void destroy();
|
||||||
|
bool initialize();
|
||||||
|
|
||||||
size_t write_impl(JfrChunkWriter& cw, bool clear);
|
bool is_modified() const;
|
||||||
static void write_metadata(JfrCheckpointWriter& cpw);
|
|
||||||
traceid write(JfrCheckpointWriter& cpw, traceid id, unsigned int hash);
|
|
||||||
size_t write(JfrChunkWriter& cw, bool clear);
|
size_t write(JfrChunkWriter& cw, bool clear);
|
||||||
size_t clear();
|
size_t clear();
|
||||||
|
|
||||||
|
const JfrStackTrace* lookup(unsigned int hash, traceid id) const;
|
||||||
|
|
||||||
traceid add_trace(const JfrStackTrace& stacktrace);
|
traceid add_trace(const JfrStackTrace& stacktrace);
|
||||||
static traceid add(const JfrStackTrace& stacktrace);
|
static traceid add(const JfrStackTrace& stacktrace);
|
||||||
traceid record_for(JavaThread* thread, int skip, JfrStackFrame* frames, u4 max_frames);
|
traceid record_for(JavaThread* thread, int skip, JfrStackFrame* frames, u4 max_frames);
|
||||||
const JfrStackTrace* lookup(unsigned int hash, traceid id) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static traceid record(Thread* thread, int skip = 0);
|
static traceid record(Thread* thread, int skip = 0);
|
||||||
|
@ -54,10 +54,18 @@ bool JfrBuffer::initialize(size_t header_size, size_t size, const void* id /* NU
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrBuffer::reinitialize() {
|
void JfrBuffer::reinitialize(bool exclusion /* false */) {
|
||||||
assert(!lease(), "invariant");
|
assert(!lease(), "invariant");
|
||||||
assert(!transient(), "invariant");
|
assert(!transient(), "invariant");
|
||||||
set_pos(start());
|
set_pos(start());
|
||||||
|
if (exclusion != excluded()) {
|
||||||
|
// update
|
||||||
|
if (exclusion) {
|
||||||
|
set_excluded();
|
||||||
|
} else {
|
||||||
|
clear_excluded();
|
||||||
|
}
|
||||||
|
}
|
||||||
clear_retired();
|
clear_retired();
|
||||||
set_top(start());
|
set_top(start());
|
||||||
}
|
}
|
||||||
@ -80,7 +88,7 @@ size_t JfrBuffer::discard() {
|
|||||||
const u1* JfrBuffer::stable_top() const {
|
const u1* JfrBuffer::stable_top() const {
|
||||||
const u1* current_top;
|
const u1* current_top;
|
||||||
do {
|
do {
|
||||||
current_top = OrderAccess::load_acquire(&_top);
|
current_top = Atomic::load(&_top);
|
||||||
} while (MUTEX_CLAIM == current_top);
|
} while (MUTEX_CLAIM == current_top);
|
||||||
return current_top;
|
return current_top;
|
||||||
}
|
}
|
||||||
@ -107,7 +115,8 @@ void JfrBuffer::set_concurrent_top(const u1* new_top) {
|
|||||||
assert(new_top <= end(), "invariant");
|
assert(new_top <= end(), "invariant");
|
||||||
assert(new_top >= start(), "invariant");
|
assert(new_top >= start(), "invariant");
|
||||||
assert(top() == MUTEX_CLAIM, "invariant");
|
assert(top() == MUTEX_CLAIM, "invariant");
|
||||||
OrderAccess::release_store(&_top, new_top);
|
OrderAccess::storestore();
|
||||||
|
_top = new_top;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrBuffer::unflushed_size() const {
|
size_t JfrBuffer::unflushed_size() const {
|
||||||
@ -118,18 +127,19 @@ void JfrBuffer::acquire(const void* id) {
|
|||||||
assert(id != NULL, "invariant");
|
assert(id != NULL, "invariant");
|
||||||
const void* current_id;
|
const void* current_id;
|
||||||
do {
|
do {
|
||||||
current_id = OrderAccess::load_acquire(&_identity);
|
current_id = Atomic::load(&_identity);
|
||||||
} while (current_id != NULL || Atomic::cmpxchg(id, &_identity, current_id) != current_id);
|
} while (current_id != NULL || Atomic::cmpxchg(id, &_identity, current_id) != current_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JfrBuffer::try_acquire(const void* id) {
|
bool JfrBuffer::try_acquire(const void* id) {
|
||||||
assert(id != NULL, "invariant");
|
assert(id != NULL, "invariant");
|
||||||
const void* const current_id = OrderAccess::load_acquire(&_identity);
|
const void* const current_id = Atomic::load(&_identity);
|
||||||
return current_id == NULL && Atomic::cmpxchg(id, &_identity, current_id) == current_id;
|
return current_id == NULL && Atomic::cmpxchg(id, &_identity, current_id) == current_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrBuffer::release() {
|
void JfrBuffer::release() {
|
||||||
OrderAccess::release_store(&_identity, (const void*)NULL);
|
OrderAccess::storestore();
|
||||||
|
_identity = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JfrBuffer::acquired_by(const void* id) const {
|
bool JfrBuffer::acquired_by(const void* id) const {
|
||||||
@ -186,7 +196,8 @@ void JfrBuffer::concurrent_move_and_reinitialize(JfrBuffer* const to, size_t siz
|
|||||||
enum FLAG {
|
enum FLAG {
|
||||||
RETIRED = 1,
|
RETIRED = 1,
|
||||||
TRANSIENT = 2,
|
TRANSIENT = 2,
|
||||||
LEASE = 4
|
LEASE = 4,
|
||||||
|
EXCLUDED = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
bool JfrBuffer::transient() const {
|
bool JfrBuffer::transient() const {
|
||||||
@ -221,27 +232,35 @@ void JfrBuffer::clear_lease() {
|
|||||||
assert(!lease(), "invariant");
|
assert(!lease(), "invariant");
|
||||||
}
|
}
|
||||||
|
|
||||||
static u2 load_acquire_flags(const u2* const flags) {
|
bool JfrBuffer::excluded() const {
|
||||||
return OrderAccess::load_acquire(flags);
|
return (u1)EXCLUDED == (_flags & (u1)EXCLUDED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void release_store_flags(u2* const flags, u2 new_flags) {
|
void JfrBuffer::set_excluded() {
|
||||||
OrderAccess::release_store(flags, new_flags);
|
_flags |= (u1)EXCLUDED;
|
||||||
|
assert(excluded(), "invariant");
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrBuffer::clear_excluded() {
|
||||||
|
if (excluded()) {
|
||||||
|
OrderAccess::storestore();
|
||||||
|
_flags ^= (u1)EXCLUDED;
|
||||||
|
}
|
||||||
|
assert(!excluded(), "invariant");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JfrBuffer::retired() const {
|
bool JfrBuffer::retired() const {
|
||||||
return (u1)RETIRED == (load_acquire_flags(&_flags) & (u1)RETIRED);
|
return (_flags & (u1)RETIRED) == (u1)RETIRED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrBuffer::set_retired() {
|
void JfrBuffer::set_retired() {
|
||||||
const u2 new_flags = load_acquire_flags(&_flags) | (u1)RETIRED;
|
OrderAccess::storestore();
|
||||||
release_store_flags(&_flags, new_flags);
|
_flags |= (u1)RETIRED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrBuffer::clear_retired() {
|
void JfrBuffer::clear_retired() {
|
||||||
u2 new_flags = load_acquire_flags(&_flags);
|
if (retired()) {
|
||||||
if ((u1)RETIRED == (new_flags & (u1)RETIRED)) {
|
OrderAccess::storestore();
|
||||||
new_flags ^= (u1)RETIRED;
|
_flags ^= (u1)RETIRED;
|
||||||
release_store_flags(&_flags, new_flags);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ class JfrBuffer {
|
|||||||
public:
|
public:
|
||||||
JfrBuffer();
|
JfrBuffer();
|
||||||
bool initialize(size_t header_size, size_t size, const void* id = NULL);
|
bool initialize(size_t header_size, size_t size, const void* id = NULL);
|
||||||
void reinitialize();
|
void reinitialize(bool exclusion = false);
|
||||||
void concurrent_reinitialization();
|
void concurrent_reinitialization();
|
||||||
size_t discard();
|
size_t discard();
|
||||||
JfrBuffer* next() const {
|
JfrBuffer* next() const {
|
||||||
@ -165,12 +165,15 @@ class JfrBuffer {
|
|||||||
bool retired() const;
|
bool retired() const;
|
||||||
void set_retired();
|
void set_retired();
|
||||||
void clear_retired();
|
void clear_retired();
|
||||||
|
|
||||||
|
bool excluded() const;
|
||||||
|
void set_excluded();
|
||||||
|
void clear_excluded();
|
||||||
};
|
};
|
||||||
|
|
||||||
class JfrAgeNode : public JfrBuffer {
|
class JfrAgeNode : public JfrBuffer {
|
||||||
private:
|
private:
|
||||||
JfrBuffer* _retired;
|
JfrBuffer* _retired;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JfrAgeNode() : _retired(NULL) {}
|
JfrAgeNode() : _retired(NULL) {}
|
||||||
void set_retired_buffer(JfrBuffer* retired) {
|
void set_retired_buffer(JfrBuffer* retired) {
|
||||||
|
@ -99,8 +99,8 @@ class JfrMemorySpace : public JfrCHeapObj {
|
|||||||
template <typename IteratorCallback, typename IteratorType>
|
template <typename IteratorCallback, typename IteratorType>
|
||||||
void iterate(IteratorCallback& callback, bool full = true, jfr_iter_direction direction = forward);
|
void iterate(IteratorCallback& callback, bool full = true, jfr_iter_direction direction = forward);
|
||||||
|
|
||||||
debug_only(bool in_full_list(const Type* t) const { return _full.in_list(t); })
|
bool in_full_list(const Type* t) const { return _full.in_list(t); }
|
||||||
debug_only(bool in_free_list(const Type* t) const { return _free.in_list(t); })
|
bool in_free_list(const Type* t) const { return _free.in_list(t); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_HPP
|
#endif // SHARE_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_HPP
|
||||||
|
@ -141,6 +141,7 @@ inline void JfrMemorySpace<T, RetrievalType, Callback>::release_free(T* t) {
|
|||||||
}
|
}
|
||||||
assert(t->empty(), "invariant");
|
assert(t->empty(), "invariant");
|
||||||
assert(!t->retired(), "invariant");
|
assert(!t->retired(), "invariant");
|
||||||
|
assert(!t->excluded(), "invariant");
|
||||||
assert(t->identity() == NULL, "invariant");
|
assert(t->identity() == NULL, "invariant");
|
||||||
if (!should_populate_cache()) {
|
if (!should_populate_cache()) {
|
||||||
remove_free(t);
|
remove_free(t);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "jfr/jfrEvents.hpp"
|
#include "jfr/jfrEvents.hpp"
|
||||||
#include "jfr/jni/jfrJavaSupport.hpp"
|
#include "jfr/jni/jfrJavaSupport.hpp"
|
||||||
#include "jfr/recorder/jfrRecorder.hpp"
|
#include "jfr/recorder/jfrRecorder.hpp"
|
||||||
|
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
|
||||||
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
||||||
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
||||||
#include "jfr/recorder/service/jfrPostBox.hpp"
|
#include "jfr/recorder/service/jfrPostBox.hpp"
|
||||||
@ -253,6 +254,18 @@ bool JfrStorage::flush_regular_buffer(BufferPtr buffer, Thread* thread) {
|
|||||||
assert(buffer->empty(), "invariant");
|
assert(buffer->empty(), "invariant");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buffer->excluded()) {
|
||||||
|
const bool thread_is_excluded = thread->jfr_thread_local()->is_excluded();
|
||||||
|
buffer->reinitialize(thread_is_excluded);
|
||||||
|
assert(buffer->empty(), "invariant");
|
||||||
|
if (!thread_is_excluded) {
|
||||||
|
// state change from exclusion to inclusion requires a thread checkpoint
|
||||||
|
JfrCheckpointManager::write_thread_checkpoint(thread);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
BufferPtr const promotion_buffer = get_promotion_buffer(unflushed_size, _global_mspace, *this, promotion_retry, thread);
|
BufferPtr const promotion_buffer = get_promotion_buffer(unflushed_size, _global_mspace, *this, promotion_retry, thread);
|
||||||
if (promotion_buffer == NULL) {
|
if (promotion_buffer == NULL) {
|
||||||
write_data_loss(buffer, thread);
|
write_data_loss(buffer, thread);
|
||||||
@ -301,7 +314,7 @@ static void handle_registration_failure(BufferPtr buffer) {
|
|||||||
assert(buffer != NULL, "invariant");
|
assert(buffer != NULL, "invariant");
|
||||||
assert(buffer->retired(), "invariant");
|
assert(buffer->retired(), "invariant");
|
||||||
const size_t unflushed_size = buffer->unflushed_size();
|
const size_t unflushed_size = buffer->unflushed_size();
|
||||||
buffer->reinitialize();
|
buffer->concurrent_reinitialization();
|
||||||
log_registration_failure(unflushed_size);
|
log_registration_failure(unflushed_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,6 +483,7 @@ static void assert_flush_large_precondition(ConstBufferPtr cur, const u1* const
|
|||||||
assert(t != NULL, "invariant");
|
assert(t != NULL, "invariant");
|
||||||
assert(cur != NULL, "invariant");
|
assert(cur != NULL, "invariant");
|
||||||
assert(cur->lease(), "invariant");
|
assert(cur->lease(), "invariant");
|
||||||
|
assert(!cur->excluded(), "invariant");
|
||||||
assert(cur_pos != NULL, "invariant");
|
assert(cur_pos != NULL, "invariant");
|
||||||
assert(native ? t->jfr_thread_local()->native_buffer() == cur : t->jfr_thread_local()->java_buffer() == cur, "invariant");
|
assert(native ? t->jfr_thread_local()->native_buffer() == cur : t->jfr_thread_local()->java_buffer() == cur, "invariant");
|
||||||
assert(t->jfr_thread_local()->shelved_buffer() != NULL, "invariant");
|
assert(t->jfr_thread_local()->shelved_buffer() != NULL, "invariant");
|
||||||
@ -496,6 +510,9 @@ BufferPtr JfrStorage::flush_regular(BufferPtr cur, const u1* const cur_pos, size
|
|||||||
// the case for stable thread local buffers; it is not the case for large buffers.
|
// the case for stable thread local buffers; it is not the case for large buffers.
|
||||||
if (!cur->empty()) {
|
if (!cur->empty()) {
|
||||||
flush_regular_buffer(cur, t);
|
flush_regular_buffer(cur, t);
|
||||||
|
if (cur->excluded()) {
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assert(t->jfr_thread_local()->shelved_buffer() == NULL, "invariant");
|
assert(t->jfr_thread_local()->shelved_buffer() == NULL, "invariant");
|
||||||
if (cur->free_size() >= req) {
|
if (cur->free_size() >= req) {
|
||||||
@ -584,28 +601,40 @@ BufferPtr JfrStorage::provision_large(BufferPtr cur, const u1* const cur_pos, si
|
|||||||
typedef UnBufferedWriteToChunk<JfrBuffer> WriteOperation;
|
typedef UnBufferedWriteToChunk<JfrBuffer> WriteOperation;
|
||||||
typedef MutexedWriteOp<WriteOperation> MutexedWriteOperation;
|
typedef MutexedWriteOp<WriteOperation> MutexedWriteOperation;
|
||||||
typedef ConcurrentWriteOp<WriteOperation> ConcurrentWriteOperation;
|
typedef ConcurrentWriteOp<WriteOperation> ConcurrentWriteOperation;
|
||||||
typedef ConcurrentWriteOpExcludeRetired<WriteOperation> ThreadLocalConcurrentWriteOperation;
|
|
||||||
|
typedef Retired<JfrBuffer, true> NonRetired;
|
||||||
|
typedef Excluded<JfrBuffer, true> NonExcluded;
|
||||||
|
typedef CompositeOperation<NonRetired, NonExcluded> BufferPredicate;
|
||||||
|
typedef PredicatedMutexedWriteOp<WriteOperation, BufferPredicate> ThreadLocalMutexedWriteOperation;
|
||||||
|
typedef PredicatedConcurrentWriteOp<WriteOperation, BufferPredicate> ThreadLocalConcurrentWriteOperation;
|
||||||
|
|
||||||
size_t JfrStorage::write() {
|
size_t JfrStorage::write() {
|
||||||
const size_t full_size_processed = write_full();
|
const size_t full_elements = write_full();
|
||||||
WriteOperation wo(_chunkwriter);
|
WriteOperation wo(_chunkwriter);
|
||||||
ThreadLocalConcurrentWriteOperation tlwo(wo);
|
NonRetired nr;
|
||||||
|
NonExcluded ne;
|
||||||
|
BufferPredicate bp(&nr, &ne);
|
||||||
|
ThreadLocalConcurrentWriteOperation tlwo(wo, bp);
|
||||||
process_full_list(tlwo, _thread_local_mspace);
|
process_full_list(tlwo, _thread_local_mspace);
|
||||||
ConcurrentWriteOperation cwo(wo);
|
ConcurrentWriteOperation cwo(wo);
|
||||||
process_free_list(cwo, _global_mspace);
|
process_free_list(cwo, _global_mspace);
|
||||||
return full_size_processed + wo.processed();
|
return full_elements + wo.elements();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrStorage::write_at_safepoint() {
|
size_t JfrStorage::write_at_safepoint() {
|
||||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||||
WriteOperation wo(_chunkwriter);
|
WriteOperation wo(_chunkwriter);
|
||||||
MutexedWriteOperation writer(wo); // mutexed write mode
|
MutexedWriteOperation writer(wo); // mutexed write mode
|
||||||
process_full_list(writer, _thread_local_mspace);
|
NonRetired nr;
|
||||||
|
NonExcluded ne;
|
||||||
|
BufferPredicate bp(&nr, &ne);
|
||||||
|
ThreadLocalMutexedWriteOperation tlmwo(wo, bp);
|
||||||
|
process_full_list(tlmwo, _thread_local_mspace);
|
||||||
assert(_transient_mspace->is_free_empty(), "invariant");
|
assert(_transient_mspace->is_free_empty(), "invariant");
|
||||||
process_full_list(writer, _transient_mspace);
|
process_full_list(writer, _transient_mspace);
|
||||||
assert(_global_mspace->is_full_empty(), "invariant");
|
assert(_global_mspace->is_full_empty(), "invariant");
|
||||||
process_free_list(writer, _global_mspace);
|
process_free_list(writer, _global_mspace);
|
||||||
return wo.processed();
|
return wo.elements();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef DiscardOp<DefaultDiscarder<JfrStorage::Buffer> > DiscardOperation;
|
typedef DiscardOp<DefaultDiscarder<JfrStorage::Buffer> > DiscardOperation;
|
||||||
@ -613,14 +642,14 @@ typedef ReleaseOp<JfrStorageMspace> ReleaseOperation;
|
|||||||
typedef CompositeOperation<MutexedWriteOperation, ReleaseOperation> FullOperation;
|
typedef CompositeOperation<MutexedWriteOperation, ReleaseOperation> FullOperation;
|
||||||
|
|
||||||
size_t JfrStorage::clear() {
|
size_t JfrStorage::clear() {
|
||||||
const size_t full_size_processed = clear_full();
|
const size_t full_elements = clear_full();
|
||||||
DiscardOperation discarder(concurrent); // concurrent discard mode
|
DiscardOperation discarder(concurrent); // concurrent discard mode
|
||||||
process_full_list(discarder, _thread_local_mspace);
|
process_full_list(discarder, _thread_local_mspace);
|
||||||
assert(_transient_mspace->is_free_empty(), "invariant");
|
assert(_transient_mspace->is_free_empty(), "invariant");
|
||||||
process_full_list(discarder, _transient_mspace);
|
process_full_list(discarder, _transient_mspace);
|
||||||
assert(_global_mspace->is_full_empty(), "invariant");
|
assert(_global_mspace->is_full_empty(), "invariant");
|
||||||
process_free_list(discarder, _global_mspace);
|
process_free_list(discarder, _global_mspace);
|
||||||
return full_size_processed + discarder.processed();
|
return full_elements + discarder.elements();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void insert_free_age_nodes(JfrStorageAgeMspace* age_mspace, JfrAgeNode* head, JfrAgeNode* tail, size_t count) {
|
static void insert_free_age_nodes(JfrStorageAgeMspace* age_mspace, JfrAgeNode* head, JfrAgeNode* tail, size_t count) {
|
||||||
@ -711,15 +740,25 @@ size_t JfrStorage::write_full() {
|
|||||||
ReleaseOperation ro(_transient_mspace, thread);
|
ReleaseOperation ro(_transient_mspace, thread);
|
||||||
FullOperation cmd(&writer, &ro);
|
FullOperation cmd(&writer, &ro);
|
||||||
const size_t count = process_full(cmd, control(), _age_mspace);
|
const size_t count = process_full(cmd, control(), _age_mspace);
|
||||||
log(count, writer.processed());
|
if (0 == count) {
|
||||||
return writer.processed();
|
assert(0 == writer.elements(), "invariant");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const size_t size = writer.size();
|
||||||
|
log(count, size);
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrStorage::clear_full() {
|
size_t JfrStorage::clear_full() {
|
||||||
DiscardOperation discarder(mutexed); // a retired buffer implies mutexed access
|
DiscardOperation discarder(mutexed); // a retired buffer implies mutexed access
|
||||||
const size_t count = process_full(discarder, control(), _age_mspace);
|
const size_t count = process_full(discarder, control(), _age_mspace);
|
||||||
log(count, discarder.processed(), true);
|
if (0 == count) {
|
||||||
return discarder.processed();
|
assert(0 == discarder.elements(), "invariant");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const size_t size = discarder.size();
|
||||||
|
log(count, size, true);
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scavenge_log(size_t count, size_t amount, size_t current) {
|
static void scavenge_log(size_t count, size_t amount, size_t current) {
|
||||||
@ -749,6 +788,10 @@ public:
|
|||||||
assert(!t->lease(), "invariant");
|
assert(!t->lease(), "invariant");
|
||||||
++_count;
|
++_count;
|
||||||
_amount += t->total_size();
|
_amount += t->total_size();
|
||||||
|
if (t->excluded()) {
|
||||||
|
t->clear_excluded();
|
||||||
|
}
|
||||||
|
assert(!t->excluded(), "invariant");
|
||||||
t->clear_retired();
|
t->clear_retired();
|
||||||
t->release();
|
t->release();
|
||||||
_control.decrement_dead();
|
_control.decrement_dead();
|
||||||
@ -767,6 +810,11 @@ size_t JfrStorage::scavenge() {
|
|||||||
}
|
}
|
||||||
Scavenger<JfrThreadLocalMspace> scavenger(ctrl, _thread_local_mspace);
|
Scavenger<JfrThreadLocalMspace> scavenger(ctrl, _thread_local_mspace);
|
||||||
process_full_list(scavenger, _thread_local_mspace);
|
process_full_list(scavenger, _thread_local_mspace);
|
||||||
scavenge_log(scavenger.processed(), scavenger.amount(), ctrl.dead_count());
|
const size_t count = scavenger.processed();
|
||||||
return scavenger.processed();
|
if (0 == count) {
|
||||||
|
assert(0 == scavenger.amount(), "invariant");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
scavenge_log(count, scavenger.amount(), ctrl.dead_count());
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,6 @@ class JfrStorage : public JfrCHeapObj {
|
|||||||
|
|
||||||
size_t clear();
|
size_t clear();
|
||||||
size_t clear_full();
|
size_t clear_full();
|
||||||
size_t write();
|
|
||||||
size_t write_full();
|
size_t write_full();
|
||||||
size_t write_at_safepoint();
|
size_t write_at_safepoint();
|
||||||
size_t scavenge();
|
size_t scavenge();
|
||||||
@ -89,6 +88,8 @@ class JfrStorage : public JfrCHeapObj {
|
|||||||
void discard_oldest(Thread* t);
|
void discard_oldest(Thread* t);
|
||||||
static JfrStorageControl& control();
|
static JfrStorageControl& control();
|
||||||
|
|
||||||
|
size_t write();
|
||||||
|
|
||||||
friend class JfrRecorder;
|
friend class JfrRecorder;
|
||||||
friend class JfrRecorderService;
|
friend class JfrRecorderService;
|
||||||
template <typename, template <typename> class, typename>
|
template <typename, template <typename> class, typename>
|
||||||
|
@ -26,14 +26,13 @@
|
|||||||
#include "jfr/recorder/storage/jfrStorageControl.hpp"
|
#include "jfr/recorder/storage/jfrStorageControl.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
#include "runtime/mutexLocker.hpp"
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "runtime/orderAccess.hpp"
|
|
||||||
|
|
||||||
// returns the updated value
|
// returns the updated value
|
||||||
static jlong atomic_add(size_t value, size_t volatile* const dest) {
|
static jlong atomic_add(size_t value, size_t volatile* const dest) {
|
||||||
size_t compare_value;
|
size_t compare_value;
|
||||||
size_t exchange_value;
|
size_t exchange_value;
|
||||||
do {
|
do {
|
||||||
compare_value = OrderAccess::load_acquire(dest);
|
compare_value = *dest;
|
||||||
exchange_value = compare_value + value;
|
exchange_value = compare_value + value;
|
||||||
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
|
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
|
||||||
return exchange_value;
|
return exchange_value;
|
||||||
@ -43,7 +42,7 @@ static jlong atomic_dec(size_t volatile* const dest) {
|
|||||||
size_t compare_value;
|
size_t compare_value;
|
||||||
size_t exchange_value;
|
size_t exchange_value;
|
||||||
do {
|
do {
|
||||||
compare_value = OrderAccess::load_acquire(dest);
|
compare_value = *dest;
|
||||||
assert(compare_value >= 1, "invariant");
|
assert(compare_value >= 1, "invariant");
|
||||||
exchange_value = compare_value - 1;
|
exchange_value = compare_value - 1;
|
||||||
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
|
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
|
||||||
@ -102,7 +101,7 @@ bool JfrStorageControl::should_discard() const {
|
|||||||
// concurrent with accuracy requirement
|
// concurrent with accuracy requirement
|
||||||
|
|
||||||
size_t JfrStorageControl::global_lease_count() const {
|
size_t JfrStorageControl::global_lease_count() const {
|
||||||
return OrderAccess::load_acquire(&_global_lease_count);
|
return Atomic::load(&_global_lease_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrStorageControl::increment_leased() {
|
size_t JfrStorageControl::increment_leased() {
|
||||||
|
@ -31,7 +31,21 @@
|
|||||||
#include "jfr/utilities/jfrTypes.hpp"
|
#include "jfr/utilities/jfrTypes.hpp"
|
||||||
#include "runtime/thread.hpp"
|
#include "runtime/thread.hpp"
|
||||||
|
|
||||||
template <typename Operation, typename NextOperation>
|
class CompositeOperationOr {
|
||||||
|
public:
|
||||||
|
static bool evaluate(bool value) {
|
||||||
|
return !value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CompositeOperationAnd {
|
||||||
|
public:
|
||||||
|
static bool evaluate(bool value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Operation, typename NextOperation, typename TruthFunction = CompositeOperationAnd>
|
||||||
class CompositeOperation {
|
class CompositeOperation {
|
||||||
private:
|
private:
|
||||||
Operation* _op;
|
Operation* _op;
|
||||||
@ -41,11 +55,15 @@ class CompositeOperation {
|
|||||||
assert(_op != NULL, "invariant");
|
assert(_op != NULL, "invariant");
|
||||||
}
|
}
|
||||||
typedef typename Operation::Type Type;
|
typedef typename Operation::Type Type;
|
||||||
bool process(Type* t = NULL) {
|
bool process(Type* t) {
|
||||||
return _next == NULL ? _op->process(t) : _op->process(t) && _next->process(t);
|
const bool op_result = _op->process(t);
|
||||||
|
return _next == NULL ? op_result : TruthFunction::evaluate(op_result) ? _next->process(t) : op_result;
|
||||||
}
|
}
|
||||||
size_t processed() const {
|
size_t elements() const {
|
||||||
return _next == NULL ? _op->processed() : _op->processed() + _next->processed();
|
return _next == NULL ? _op->elements() : _op->elements() + _next->elements();
|
||||||
|
}
|
||||||
|
size_t size() const {
|
||||||
|
return _next == NULL ? _op->size() : _op->size() + _next->size();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -53,43 +71,47 @@ template <typename T>
|
|||||||
class UnBufferedWriteToChunk {
|
class UnBufferedWriteToChunk {
|
||||||
private:
|
private:
|
||||||
JfrChunkWriter& _writer;
|
JfrChunkWriter& _writer;
|
||||||
size_t _processed;
|
size_t _elements;
|
||||||
|
size_t _size;
|
||||||
public:
|
public:
|
||||||
typedef T Type;
|
typedef T Type;
|
||||||
UnBufferedWriteToChunk(JfrChunkWriter& writer) : _writer(writer), _processed(0) {}
|
UnBufferedWriteToChunk(JfrChunkWriter& writer) : _writer(writer), _elements(0), _size(0) {}
|
||||||
bool write(Type* t, const u1* data, size_t size);
|
bool write(Type* t, const u1* data, size_t size);
|
||||||
size_t processed() { return _processed; }
|
size_t elements() const { return _elements; }
|
||||||
|
size_t size() const { return _size; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class DefaultDiscarder {
|
class DefaultDiscarder {
|
||||||
private:
|
private:
|
||||||
size_t _processed;
|
size_t _elements;
|
||||||
|
size_t _size;
|
||||||
public:
|
public:
|
||||||
typedef T Type;
|
typedef T Type;
|
||||||
DefaultDiscarder() : _processed() {}
|
DefaultDiscarder() : _elements(0), _size(0) {}
|
||||||
bool discard(Type* t, const u1* data, size_t size);
|
bool discard(Type* t, const u1* data, size_t size);
|
||||||
size_t processed() const { return _processed; }
|
size_t elements() const { return _elements; }
|
||||||
|
size_t size() const { return _size; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Operation>
|
template <typename T, bool negation>
|
||||||
class ConcurrentWriteOp {
|
class Retired {
|
||||||
private:
|
|
||||||
Operation& _operation;
|
|
||||||
public:
|
public:
|
||||||
typedef typename Operation::Type Type;
|
typedef T Type;
|
||||||
ConcurrentWriteOp(Operation& operation) : _operation(operation) {}
|
bool process(Type* t) {
|
||||||
bool process(Type* t);
|
assert(t != NULL, "invariant");
|
||||||
size_t processed() const { return _operation.processed(); }
|
return negation ? !t->retired() : t->retired();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Operation>
|
template <typename T, bool negation>
|
||||||
class ConcurrentWriteOpExcludeRetired : private ConcurrentWriteOp<Operation> {
|
class Excluded {
|
||||||
public:
|
public:
|
||||||
typedef typename Operation::Type Type;
|
typedef T Type;
|
||||||
ConcurrentWriteOpExcludeRetired(Operation& operation) : ConcurrentWriteOp<Operation>(operation) {}
|
bool process(Type* t) {
|
||||||
bool process(Type* t);
|
assert(t != NULL, "invariant");
|
||||||
size_t processed() const { return ConcurrentWriteOp<Operation>::processed(); }
|
return negation ? !t->excluded() : t->excluded();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Operation>
|
template <typename Operation>
|
||||||
@ -100,7 +122,44 @@ class MutexedWriteOp {
|
|||||||
typedef typename Operation::Type Type;
|
typedef typename Operation::Type Type;
|
||||||
MutexedWriteOp(Operation& operation) : _operation(operation) {}
|
MutexedWriteOp(Operation& operation) : _operation(operation) {}
|
||||||
bool process(Type* t);
|
bool process(Type* t);
|
||||||
size_t processed() const { return _operation.processed(); }
|
size_t elements() const { return _operation.elements(); }
|
||||||
|
size_t size() const { return _operation.size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Operation, typename Predicate>
|
||||||
|
class PredicatedMutexedWriteOp : public MutexedWriteOp<Operation> {
|
||||||
|
private:
|
||||||
|
Predicate& _predicate;
|
||||||
|
public:
|
||||||
|
PredicatedMutexedWriteOp(Operation& operation, Predicate& predicate) :
|
||||||
|
MutexedWriteOp<Operation>(operation), _predicate(predicate) {}
|
||||||
|
bool process(typename Operation::Type* t) {
|
||||||
|
return _predicate.process(t) ? MutexedWriteOp<Operation>::process(t) : true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Operation>
|
||||||
|
class ConcurrentWriteOp {
|
||||||
|
private:
|
||||||
|
Operation& _operation;
|
||||||
|
public:
|
||||||
|
typedef typename Operation::Type Type;
|
||||||
|
ConcurrentWriteOp(Operation& operation) : _operation(operation) {}
|
||||||
|
bool process(Type* t);
|
||||||
|
size_t elements() const { return _operation.elements(); }
|
||||||
|
size_t size() const { return _operation.size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Operation, typename Predicate>
|
||||||
|
class PredicatedConcurrentWriteOp : public ConcurrentWriteOp<Operation> {
|
||||||
|
private:
|
||||||
|
Predicate& _predicate;
|
||||||
|
public:
|
||||||
|
PredicatedConcurrentWriteOp(Operation& operation, Predicate& predicate) :
|
||||||
|
ConcurrentWriteOp<Operation>(operation), _predicate(predicate) {}
|
||||||
|
bool process(typename Operation::Type* t) {
|
||||||
|
return _predicate.process(t) ? ConcurrentWriteOp<Operation>::process(t) : true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Operation>
|
template <typename Operation>
|
||||||
@ -126,7 +185,8 @@ class DiscardOp {
|
|||||||
typedef typename Operation::Type Type;
|
typedef typename Operation::Type Type;
|
||||||
DiscardOp(jfr_operation_mode mode = concurrent) : _operation(), _mode(mode) {}
|
DiscardOp(jfr_operation_mode mode = concurrent) : _operation(), _mode(mode) {}
|
||||||
bool process(Type* t);
|
bool process(Type* t);
|
||||||
size_t processed() const { return _operation.processed(); }
|
size_t elements() const { return _operation.elements(); }
|
||||||
|
size_t size() const { return _operation.size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP
|
#endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP
|
||||||
|
@ -31,13 +31,15 @@
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
inline bool UnBufferedWriteToChunk<T>::write(T* t, const u1* data, size_t size) {
|
inline bool UnBufferedWriteToChunk<T>::write(T* t, const u1* data, size_t size) {
|
||||||
_writer.write_unbuffered(data, size);
|
_writer.write_unbuffered(data, size);
|
||||||
_processed += size;
|
++_elements;
|
||||||
|
_size += size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline bool DefaultDiscarder<T>::discard(T* t, const u1* data, size_t size) {
|
inline bool DefaultDiscarder<T>::discard(T* t, const u1* data, size_t size) {
|
||||||
_processed += size;
|
++_elements;
|
||||||
|
_size += size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,15 +56,6 @@ inline bool ConcurrentWriteOp<Operation>::process(typename Operation::Type* t) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Operation>
|
|
||||||
inline bool ConcurrentWriteOpExcludeRetired<Operation>::process(typename Operation::Type* t) {
|
|
||||||
if (t->retired()) {
|
|
||||||
assert(t->empty(), "invariant");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return ConcurrentWriteOp<Operation>::process(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Operation>
|
template <typename Operation>
|
||||||
inline bool MutexedWriteOp<Operation>::process(typename Operation::Type* t) {
|
inline bool MutexedWriteOp<Operation>::process(typename Operation::Type* t) {
|
||||||
assert(t != NULL, "invariant");
|
assert(t != NULL, "invariant");
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include "jfr/recorder/storage/jfrVirtualMemory.hpp"
|
#include "jfr/recorder/storage/jfrVirtualMemory.hpp"
|
||||||
#include "memory/virtualspace.hpp"
|
#include "memory/virtualspace.hpp"
|
||||||
#include "runtime/globals.hpp"
|
#include "runtime/globals.hpp"
|
||||||
#include "runtime/orderAccess.hpp"
|
|
||||||
#include "runtime/os.hpp"
|
#include "runtime/os.hpp"
|
||||||
#include "services/memTracker.hpp"
|
#include "services/memTracker.hpp"
|
||||||
#include "utilities/globalDefinitions.hpp"
|
#include "utilities/globalDefinitions.hpp"
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
#include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp"
|
#include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp"
|
||||||
#include "jfr/utilities/jfrTypes.hpp"
|
#include "jfr/utilities/jfrTypes.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
|
||||||
#include "runtime/mutexLocker.hpp"
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "runtime/orderAccess.hpp"
|
#include "runtime/orderAccess.hpp"
|
||||||
#include "runtime/safepoint.hpp"
|
#include "runtime/safepoint.hpp"
|
||||||
@ -42,12 +41,42 @@
|
|||||||
typedef JfrStringPool::Buffer* BufferPtr;
|
typedef JfrStringPool::Buffer* BufferPtr;
|
||||||
|
|
||||||
static JfrStringPool* _instance = NULL;
|
static JfrStringPool* _instance = NULL;
|
||||||
|
static uint64_t store_generation = 0;
|
||||||
|
static uint64_t serialized_generation = 0;
|
||||||
|
|
||||||
|
inline void set_generation(uint64_t value, uint64_t* const dest) {
|
||||||
|
assert(dest != NULL, "invariant");
|
||||||
|
OrderAccess::release_store(dest, value);
|
||||||
|
}
|
||||||
|
static void increment_store_generation() {
|
||||||
|
const uint64_t current_serialized = OrderAccess::load_acquire(&serialized_generation);
|
||||||
|
const uint64_t current_stored = OrderAccess::load_acquire(&store_generation);
|
||||||
|
if (current_serialized == current_stored) {
|
||||||
|
set_generation(current_serialized + 1, &store_generation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool increment_serialized_generation() {
|
||||||
|
const uint64_t current_stored = OrderAccess::load_acquire(&store_generation);
|
||||||
|
const uint64_t current_serialized = OrderAccess::load_acquire(&serialized_generation);
|
||||||
|
if (current_stored != current_serialized) {
|
||||||
|
set_generation(current_stored, &serialized_generation);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JfrStringPool::is_modified() {
|
||||||
|
return increment_serialized_generation();
|
||||||
|
}
|
||||||
|
|
||||||
JfrStringPool& JfrStringPool::instance() {
|
JfrStringPool& JfrStringPool::instance() {
|
||||||
return *_instance;
|
return *_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
JfrStringPool* JfrStringPool::create(JfrChunkWriter& cw) {
|
JfrStringPool* JfrStringPool::create(JfrChunkWriter& cw) {
|
||||||
|
store_generation = 0;
|
||||||
|
serialized_generation = 0;
|
||||||
assert(_instance == NULL, "invariant");
|
assert(_instance == NULL, "invariant");
|
||||||
_instance = new JfrStringPool(cw);
|
_instance = new JfrStringPool(cw);
|
||||||
return _instance;
|
return _instance;
|
||||||
@ -131,12 +160,16 @@ BufferPtr JfrStringPool::lease_buffer(Thread* thread, size_t size /* 0 */) {
|
|||||||
bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) {
|
bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) {
|
||||||
assert(jt != NULL, "invariant");
|
assert(jt != NULL, "invariant");
|
||||||
const bool current_epoch = JfrTraceIdEpoch::epoch();
|
const bool current_epoch = JfrTraceIdEpoch::epoch();
|
||||||
if (current_epoch == epoch) {
|
if (current_epoch != epoch) {
|
||||||
|
return current_epoch;
|
||||||
|
}
|
||||||
|
{
|
||||||
JfrStringPoolWriter writer(jt);
|
JfrStringPoolWriter writer(jt);
|
||||||
writer.write(id);
|
writer.write(id);
|
||||||
writer.write(string);
|
writer.write(string);
|
||||||
writer.inc_nof_strings();
|
writer.inc_nof_strings();
|
||||||
}
|
}
|
||||||
|
increment_store_generation();
|
||||||
return current_epoch;
|
return current_epoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +196,10 @@ class StringPoolOp {
|
|||||||
size_t processed() { return _strings_processed; }
|
size_t processed() { return _strings_processed; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Type>
|
template <typename T>
|
||||||
class StringPoolDiscarderStub {
|
class StringPoolDiscarderStub {
|
||||||
public:
|
public:
|
||||||
|
typedef T Type;
|
||||||
bool write(Type* buffer, const u1* data, size_t size) {
|
bool write(Type* buffer, const u1* data, size_t size) {
|
||||||
// stub only, discard happens at higher level
|
// stub only, discard happens at higher level
|
||||||
return true;
|
return true;
|
||||||
@ -197,6 +231,7 @@ size_t JfrStringPool::write_at_safepoint() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t JfrStringPool::clear() {
|
size_t JfrStringPool::clear() {
|
||||||
|
increment_serialized_generation();
|
||||||
DiscardOperation discard_operation;
|
DiscardOperation discard_operation;
|
||||||
ExclusiveDiscardOperation edo(discard_operation);
|
ExclusiveDiscardOperation edo(discard_operation);
|
||||||
StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false);
|
StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false);
|
||||||
|
@ -71,6 +71,7 @@ class JfrStringPool : public JfrCHeapObj {
|
|||||||
static JfrStringPool* create(JfrChunkWriter& cw);
|
static JfrStringPool* create(JfrChunkWriter& cw);
|
||||||
bool initialize();
|
bool initialize();
|
||||||
static void destroy();
|
static void destroy();
|
||||||
|
static bool is_modified();
|
||||||
|
|
||||||
friend class JfrRecorder;
|
friend class JfrRecorder;
|
||||||
friend class JfrRecorderService;
|
friend class JfrRecorderService;
|
||||||
|
@ -55,6 +55,7 @@ JfrThreadLocal::JfrThreadLocal() :
|
|||||||
_stack_trace_hash(0),
|
_stack_trace_hash(0),
|
||||||
_stackdepth(0),
|
_stackdepth(0),
|
||||||
_entering_suspend_flag(0),
|
_entering_suspend_flag(0),
|
||||||
|
_excluded(false),
|
||||||
_dead(false) {}
|
_dead(false) {}
|
||||||
|
|
||||||
u8 JfrThreadLocal::add_data_lost(u8 value) {
|
u8 JfrThreadLocal::add_data_lost(u8 value) {
|
||||||
@ -84,9 +85,13 @@ static void send_java_thread_start_event(JavaThread* jt) {
|
|||||||
void JfrThreadLocal::on_start(Thread* t) {
|
void JfrThreadLocal::on_start(Thread* t) {
|
||||||
assert(t != NULL, "invariant");
|
assert(t != NULL, "invariant");
|
||||||
assert(Thread::current() == t, "invariant");
|
assert(Thread::current() == t, "invariant");
|
||||||
|
JfrJavaSupport::on_thread_start(t);
|
||||||
if (JfrRecorder::is_recording()) {
|
if (JfrRecorder::is_recording()) {
|
||||||
if (t->is_Java_thread()) {
|
if (!t->jfr_thread_local()->is_excluded()) {
|
||||||
send_java_thread_start_event((JavaThread*)t);
|
JfrCheckpointManager::write_thread_checkpoint(t);
|
||||||
|
if (t->is_Java_thread()) {
|
||||||
|
send_java_thread_start_event((JavaThread*)t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,48 +108,68 @@ static void send_java_thread_end_events(traceid id, JavaThread* jt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JfrThreadLocal::release(Thread* t) {
|
||||||
|
if (has_java_event_writer()) {
|
||||||
|
assert(t->is_Java_thread(), "invariant");
|
||||||
|
JfrJavaSupport::destroy_global_jni_handle(java_event_writer());
|
||||||
|
_java_event_writer = NULL;
|
||||||
|
}
|
||||||
|
if (has_native_buffer()) {
|
||||||
|
JfrStorage::release_thread_local(native_buffer(), t);
|
||||||
|
_native_buffer = NULL;
|
||||||
|
}
|
||||||
|
if (has_java_buffer()) {
|
||||||
|
JfrStorage::release_thread_local(java_buffer(), t);
|
||||||
|
_java_buffer = NULL;
|
||||||
|
}
|
||||||
|
if (_stackframes != NULL) {
|
||||||
|
FREE_C_HEAP_ARRAY(JfrStackFrame, _stackframes);
|
||||||
|
_stackframes = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void JfrThreadLocal::release(JfrThreadLocal* tl, Thread* t) {
|
void JfrThreadLocal::release(JfrThreadLocal* tl, Thread* t) {
|
||||||
assert(tl != NULL, "invariant");
|
assert(tl != NULL, "invariant");
|
||||||
assert(t != NULL, "invariant");
|
assert(t != NULL, "invariant");
|
||||||
assert(Thread::current() == t, "invariant");
|
assert(Thread::current() == t, "invariant");
|
||||||
assert(!tl->is_dead(), "invariant");
|
assert(!tl->is_dead(), "invariant");
|
||||||
assert(tl->shelved_buffer() == NULL, "invariant");
|
assert(tl->shelved_buffer() == NULL, "invariant");
|
||||||
if (tl->has_native_buffer()) {
|
|
||||||
JfrStorage::release_thread_local(tl->native_buffer(), t);
|
|
||||||
}
|
|
||||||
if (tl->has_java_buffer()) {
|
|
||||||
JfrStorage::release_thread_local(tl->java_buffer(), t);
|
|
||||||
}
|
|
||||||
if (tl->has_java_event_writer()) {
|
|
||||||
assert(t->is_Java_thread(), "invariant");
|
|
||||||
JfrJavaSupport::destroy_global_jni_handle(tl->java_event_writer());
|
|
||||||
}
|
|
||||||
FREE_C_HEAP_ARRAY(JfrStackFrame, tl->_stackframes);
|
|
||||||
tl->_dead = true;
|
tl->_dead = true;
|
||||||
|
tl->release(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JfrThreadLocal::on_exit(Thread* t) {
|
void JfrThreadLocal::on_exit(Thread* t) {
|
||||||
assert(t != NULL, "invariant");
|
assert(t != NULL, "invariant");
|
||||||
JfrThreadLocal * const tl = t->jfr_thread_local();
|
JfrThreadLocal * const tl = t->jfr_thread_local();
|
||||||
assert(!tl->is_dead(), "invariant");
|
assert(!tl->is_dead(), "invariant");
|
||||||
if (t->is_Java_thread()) {
|
if (JfrRecorder::is_recording()) {
|
||||||
JavaThread* const jt = (JavaThread*)t;
|
if (t->is_Java_thread()) {
|
||||||
ObjectSampleCheckpoint::on_thread_exit(jt);
|
JavaThread* const jt = (JavaThread*)t;
|
||||||
send_java_thread_end_events(tl->thread_id(), jt);
|
ObjectSampleCheckpoint::on_thread_exit(jt);
|
||||||
|
send_java_thread_end_events(tl->thread_id(), jt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
release(tl, Thread::current()); // because it could be that Thread::current() != t
|
release(tl, Thread::current()); // because it could be that Thread::current() != t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JfrBuffer* acquire_buffer(bool excluded) {
|
||||||
|
JfrBuffer* const buffer = JfrStorage::acquire_thread_local(Thread::current());
|
||||||
|
if (buffer != NULL && excluded) {
|
||||||
|
buffer->set_excluded();
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
JfrBuffer* JfrThreadLocal::install_native_buffer() const {
|
JfrBuffer* JfrThreadLocal::install_native_buffer() const {
|
||||||
assert(!has_native_buffer(), "invariant");
|
assert(!has_native_buffer(), "invariant");
|
||||||
_native_buffer = JfrStorage::acquire_thread_local(Thread::current());
|
_native_buffer = acquire_buffer(_excluded);
|
||||||
return _native_buffer;
|
return _native_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
JfrBuffer* JfrThreadLocal::install_java_buffer() const {
|
JfrBuffer* JfrThreadLocal::install_java_buffer() const {
|
||||||
assert(!has_java_buffer(), "invariant");
|
assert(!has_java_buffer(), "invariant");
|
||||||
assert(!has_java_event_writer(), "invariant");
|
assert(!has_java_event_writer(), "invariant");
|
||||||
_java_buffer = JfrStorage::acquire_thread_local(Thread::current());
|
_java_buffer = acquire_buffer(_excluded);
|
||||||
return _java_buffer;
|
return _java_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,6 +187,18 @@ ByteSize JfrThreadLocal::java_event_writer_offset() {
|
|||||||
return in_ByteSize(offset_of(JfrThreadLocal, _java_event_writer));
|
return in_ByteSize(offset_of(JfrThreadLocal, _java_event_writer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JfrThreadLocal::exclude(Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
t->jfr_thread_local()->_excluded = true;
|
||||||
|
t->jfr_thread_local()->release(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JfrThreadLocal::include(Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
t->jfr_thread_local()->_excluded = false;
|
||||||
|
t->jfr_thread_local()->release(t);
|
||||||
|
}
|
||||||
|
|
||||||
u4 JfrThreadLocal::stackdepth() const {
|
u4 JfrThreadLocal::stackdepth() const {
|
||||||
return _stackdepth != 0 ? _stackdepth : (u4)JfrOptionSet::stackdepth();
|
return _stackdepth != 0 ? _stackdepth : (u4)JfrOptionSet::stackdepth();
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,13 @@ class JfrThreadLocal {
|
|||||||
unsigned int _stack_trace_hash;
|
unsigned int _stack_trace_hash;
|
||||||
mutable u4 _stackdepth;
|
mutable u4 _stackdepth;
|
||||||
volatile jint _entering_suspend_flag;
|
volatile jint _entering_suspend_flag;
|
||||||
|
bool _excluded;
|
||||||
bool _dead;
|
bool _dead;
|
||||||
|
|
||||||
JfrBuffer* install_native_buffer() const;
|
JfrBuffer* install_native_buffer() const;
|
||||||
JfrBuffer* install_java_buffer() const;
|
JfrBuffer* install_java_buffer() const;
|
||||||
JfrStackFrame* install_stackframes() const;
|
JfrStackFrame* install_stackframes() const;
|
||||||
|
void release(Thread* t);
|
||||||
static void release(JfrThreadLocal* tl, Thread* t);
|
static void release(JfrThreadLocal* tl, Thread* t);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -203,6 +204,10 @@ class JfrThreadLocal {
|
|||||||
_trace_id = id;
|
_trace_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_excluded() const {
|
||||||
|
return _excluded;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_dead() const {
|
bool is_dead() const {
|
||||||
return _dead;
|
return _dead;
|
||||||
}
|
}
|
||||||
@ -211,6 +216,9 @@ class JfrThreadLocal {
|
|||||||
void set_thread_blob(const JfrBlobHandle& handle);
|
void set_thread_blob(const JfrBlobHandle& handle);
|
||||||
const JfrBlobHandle& thread_blob() const;
|
const JfrBlobHandle& thread_blob() const;
|
||||||
|
|
||||||
|
static void exclude(Thread* t);
|
||||||
|
static void include(Thread* t);
|
||||||
|
|
||||||
static void on_start(Thread* t);
|
static void on_start(Thread* t);
|
||||||
static void on_exit(Thread* t);
|
static void on_exit(Thread* t);
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class JfrTraceFlag {
|
|||||||
|
|
||||||
jbyte* meta_addr() const {
|
jbyte* meta_addr() const {
|
||||||
#ifdef VM_LITTLE_ENDIAN
|
#ifdef VM_LITTLE_ENDIAN
|
||||||
return (jbyte*)(&_flags) + 1;
|
return ((jbyte*)&_flags) + 1;
|
||||||
#else
|
#else
|
||||||
return (jbyte*)&_flags;
|
return (jbyte*)&_flags;
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "memory/allocation.inline.hpp"
|
#include "memory/allocation.inline.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
#include "runtime/orderAccess.hpp"
|
|
||||||
#include "runtime/vm_version.hpp"
|
#include "runtime/vm_version.hpp"
|
||||||
#include "utilities/debug.hpp"
|
#include "utilities/debug.hpp"
|
||||||
#include "utilities/macros.hpp"
|
#include "utilities/macros.hpp"
|
||||||
@ -40,7 +39,7 @@ static jlong atomic_add_jlong(jlong value, jlong volatile* const dest) {
|
|||||||
jlong compare_value;
|
jlong compare_value;
|
||||||
jlong exchange_value;
|
jlong exchange_value;
|
||||||
do {
|
do {
|
||||||
compare_value = OrderAccess::load_acquire(dest);
|
compare_value = *dest;
|
||||||
exchange_value = compare_value + value;
|
exchange_value = compare_value + value;
|
||||||
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
|
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
|
||||||
return exchange_value;
|
return exchange_value;
|
||||||
|
@ -48,8 +48,8 @@ class JfrDoublyLinkedList {
|
|||||||
void prepend(T* const node);
|
void prepend(T* const node);
|
||||||
void append(T* const node);
|
void append(T* const node);
|
||||||
void append_list(T* const head_node, T* const tail_node, size_t count);
|
void append_list(T* const head_node, T* const tail_node, size_t count);
|
||||||
debug_only(bool in_list(const T* const target_node) const;)
|
bool in_list(const T* const target_node) const;
|
||||||
debug_only(bool locate(const T* start_node, const T* const target_node) const;)
|
bool locate(const T* start_node, const T* const target_node) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -153,7 +153,6 @@ T* JfrDoublyLinkedList<T>::clear(bool return_tail /* false */) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ASSERT
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool JfrDoublyLinkedList<T>::locate(const T* node, const T* const target) const {
|
bool JfrDoublyLinkedList<T>::locate(const T* node, const T* const target) const {
|
||||||
assert(target != NULL, "invariant");
|
assert(target != NULL, "invariant");
|
||||||
@ -182,7 +181,6 @@ inline void validate_count_param(T* node, size_t count_param) {
|
|||||||
}
|
}
|
||||||
assert(count_param == count, "invariant");
|
assert(count_param == count, "invariant");
|
||||||
}
|
}
|
||||||
#endif // ASSERT
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void JfrDoublyLinkedList<T>::append_list(T* const head_node, T* const tail_node, size_t count) {
|
void JfrDoublyLinkedList<T>::append_list(T* const head_node, T* const tail_node, size_t count) {
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
JFR_LOG_TAG(jfr, system, bytecode) \
|
JFR_LOG_TAG(jfr, system, bytecode) \
|
||||||
JFR_LOG_TAG(jfr, system, parser) \
|
JFR_LOG_TAG(jfr, system, parser) \
|
||||||
JFR_LOG_TAG(jfr, system, metadata) \
|
JFR_LOG_TAG(jfr, system, metadata) \
|
||||||
|
JFR_LOG_TAG(jfr, system, streaming) \
|
||||||
JFR_LOG_TAG(jfr, metadata) \
|
JFR_LOG_TAG(jfr, metadata) \
|
||||||
JFR_LOG_TAG(jfr, event) \
|
JFR_LOG_TAG(jfr, event) \
|
||||||
JFR_LOG_TAG(jfr, setting) \
|
JFR_LOG_TAG(jfr, setting) \
|
||||||
|
87
src/hotspot/share/jfr/utilities/jfrThreadIterator.cpp
Normal file
87
src/hotspot/share/jfr/utilities/jfrThreadIterator.cpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "precompiled.hpp"
|
||||||
|
#include "jfr/support/jfrThreadLocal.hpp"
|
||||||
|
#include "jfr/utilities/jfrThreadIterator.hpp"
|
||||||
|
#include "runtime/thread.inline.hpp"
|
||||||
|
|
||||||
|
static bool thread_inclusion_predicate(Thread* t) {
|
||||||
|
assert(t != NULL, "invariant");
|
||||||
|
return !t->jfr_thread_local()->is_dead();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool java_thread_inclusion_predicate(JavaThread* jt) {
|
||||||
|
assert(jt != NULL, "invariant");
|
||||||
|
return thread_inclusion_predicate(jt) && jt->thread_state() != _thread_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JavaThread* next_java_thread(JavaThreadIteratorWithHandle& iter) {
|
||||||
|
JavaThread* next = iter.next();
|
||||||
|
while (next != NULL && !java_thread_inclusion_predicate(next)) {
|
||||||
|
next = iter.next();
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NonJavaThread* next_non_java_thread(NonJavaThread::Iterator& iter) {
|
||||||
|
NonJavaThread* next = NULL;
|
||||||
|
while (!iter.end()) {
|
||||||
|
next = iter.current();
|
||||||
|
iter.step();
|
||||||
|
assert(next != NULL, "invariant");
|
||||||
|
if (!thread_inclusion_predicate(next)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
JfrJavaThreadIteratorAdapter::JfrJavaThreadIteratorAdapter() : _iter(), _next(next_java_thread(_iter)) {}
|
||||||
|
|
||||||
|
JavaThread* JfrJavaThreadIteratorAdapter::next() {
|
||||||
|
assert(has_next(), "invariant");
|
||||||
|
Type* const temp = _next;
|
||||||
|
_next = next_java_thread(_iter);
|
||||||
|
assert(temp != _next, "invariant");
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
JfrNonJavaThreadIteratorAdapter::JfrNonJavaThreadIteratorAdapter() : _iter(), _next(next_non_java_thread(_iter)) {}
|
||||||
|
|
||||||
|
bool JfrNonJavaThreadIteratorAdapter::has_next() const {
|
||||||
|
return _next != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NonJavaThread* JfrNonJavaThreadIteratorAdapter::next() {
|
||||||
|
assert(has_next(), "invariant");
|
||||||
|
Type* const temp = _next;
|
||||||
|
_next = next_non_java_thread(_iter);
|
||||||
|
assert(temp != _next, "invariant");
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicit instantiations
|
||||||
|
template class JfrThreadIterator<JfrJavaThreadIteratorAdapter, StackObj>;
|
||||||
|
template class JfrThreadIterator<JfrNonJavaThreadIteratorAdapter, StackObj>;
|
74
src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp
Normal file
74
src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
|
||||||
|
#define SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
|
||||||
|
|
||||||
|
#include "memory/allocation.hpp"
|
||||||
|
#include "runtime/thread.hpp"
|
||||||
|
#include "runtime/threadSMR.hpp"
|
||||||
|
|
||||||
|
template <typename Adapter, typename AP = StackObj>
|
||||||
|
class JfrThreadIterator : public AP {
|
||||||
|
private:
|
||||||
|
Adapter _adapter;
|
||||||
|
public:
|
||||||
|
JfrThreadIterator() : _adapter() {}
|
||||||
|
typename Adapter::Type* next() {
|
||||||
|
assert(has_next(), "invariant");
|
||||||
|
return _adapter.next();
|
||||||
|
}
|
||||||
|
bool has_next() const {
|
||||||
|
return _adapter.has_next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JfrJavaThreadIteratorAdapter {
|
||||||
|
private:
|
||||||
|
JavaThreadIteratorWithHandle _iter;
|
||||||
|
JavaThread* _next;
|
||||||
|
public:
|
||||||
|
typedef JavaThread Type;
|
||||||
|
JfrJavaThreadIteratorAdapter();
|
||||||
|
bool has_next() const {
|
||||||
|
return _next != NULL;
|
||||||
|
}
|
||||||
|
Type* next();
|
||||||
|
};
|
||||||
|
|
||||||
|
class JfrNonJavaThreadIteratorAdapter {
|
||||||
|
private:
|
||||||
|
NonJavaThread::Iterator _iter;
|
||||||
|
NonJavaThread* _next;
|
||||||
|
public:
|
||||||
|
typedef NonJavaThread Type;
|
||||||
|
JfrNonJavaThreadIteratorAdapter();
|
||||||
|
bool has_next() const;
|
||||||
|
Type* next();
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef JfrThreadIterator<JfrJavaThreadIteratorAdapter, StackObj> JfrJavaThreadIterator;
|
||||||
|
typedef JfrThreadIterator<JfrNonJavaThreadIteratorAdapter, StackObj> JfrNonJavaThreadIterator;
|
||||||
|
|
||||||
|
#endif // SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
|
@ -26,11 +26,14 @@
|
|||||||
#define SHARE_JFR_UTILITIES_JFRTYPES_HPP
|
#define SHARE_JFR_UTILITIES_JFRTYPES_HPP
|
||||||
|
|
||||||
#include "jfrfiles/jfrEventIds.hpp"
|
#include "jfrfiles/jfrEventIds.hpp"
|
||||||
|
#include "utilities/globalDefinitions.hpp"
|
||||||
|
|
||||||
typedef u8 traceid;
|
typedef u8 traceid;
|
||||||
typedef int fio_fd;
|
typedef int fio_fd;
|
||||||
|
|
||||||
const int invalid_fd = -1;
|
const int invalid_fd = -1;
|
||||||
const jlong invalid_offset = -1;
|
const jlong invalid_offset = -1;
|
||||||
|
const int64_t invalid_time = -1;
|
||||||
const u4 STACK_DEPTH_DEFAULT = 64;
|
const u4 STACK_DEPTH_DEFAULT = 64;
|
||||||
const u4 MIN_STACK_DEPTH = 1;
|
const u4 MIN_STACK_DEPTH = 1;
|
||||||
const u4 MAX_STACK_DEPTH = 2048;
|
const u4 MAX_STACK_DEPTH = 2048;
|
||||||
@ -48,4 +51,12 @@ enum EventStartTime {
|
|||||||
TIMED
|
TIMED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum JfrCheckpointType {
|
||||||
|
GENERIC,
|
||||||
|
FLUSH,
|
||||||
|
HEADER,
|
||||||
|
STATICS = 4,
|
||||||
|
THREADS = 8
|
||||||
|
};
|
||||||
|
|
||||||
#endif // SHARE_JFR_UTILITIES_JFRTYPES_HPP
|
#endif // SHARE_JFR_UTILITIES_JFRTYPES_HPP
|
||||||
|
@ -32,9 +32,9 @@ class JavaThread;
|
|||||||
class Thread;
|
class Thread;
|
||||||
|
|
||||||
class JfrJavaEventWriter : AllStatic {
|
class JfrJavaEventWriter : AllStatic {
|
||||||
friend class JfrCheckpointThreadClosure;
|
friend class JfrNotifyClosure;
|
||||||
friend class JfrJavaEventWriterNotificationClosure;
|
|
||||||
friend class JfrJavaEventWriterNotifyOperation;
|
friend class JfrJavaEventWriterNotifyOperation;
|
||||||
|
friend class JfrJavaEventWriterNotificationClosure;
|
||||||
friend class JfrRecorder;
|
friend class JfrRecorder;
|
||||||
private:
|
private:
|
||||||
static bool initialize();
|
static bool initialize();
|
||||||
|
@ -82,7 +82,7 @@ class Adapter {
|
|||||||
assert(_thread != NULL, "invariant");
|
assert(_thread != NULL, "invariant");
|
||||||
Flush f(_storage, used, requested, _thread);
|
Flush f(_storage, used, requested, _thread);
|
||||||
_storage = f.result();
|
_storage = f.result();
|
||||||
return _storage != NULL;
|
return _storage != NULL && !_storage->excluded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void release() {
|
void release() {
|
||||||
@ -236,7 +236,8 @@ class NoOwnershipAdapter {
|
|||||||
void release() {}
|
void release() {}
|
||||||
bool flush(size_t used, size_t requested) {
|
bool flush(size_t used, size_t requested) {
|
||||||
// don't flush/expand a buffer that is not our own
|
// don't flush/expand a buffer that is not our own
|
||||||
return false;
|
_pos = _start;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,10 +114,7 @@ void WriterHost<BE, IE, WriterPolicyImpl>::write_utf16(const jchar* value, jint
|
|||||||
template <typename BE, typename IE, typename WriterPolicyImpl >
|
template <typename BE, typename IE, typename WriterPolicyImpl >
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void WriterHost<BE, IE, WriterPolicyImpl>::be_write(T value) {
|
inline void WriterHost<BE, IE, WriterPolicyImpl>::be_write(T value) {
|
||||||
u1* const pos = ensure_size(sizeof(T));
|
be_write(&value, 1);
|
||||||
if (pos) {
|
|
||||||
this->set_current_pos(BE::be_write(&value, 1, pos));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BE, typename IE, typename WriterPolicyImpl >
|
template <typename BE, typename IE, typename WriterPolicyImpl >
|
||||||
|
@ -148,6 +148,7 @@
|
|||||||
LOG_TAG(startuptime) \
|
LOG_TAG(startuptime) \
|
||||||
LOG_TAG(state) \
|
LOG_TAG(state) \
|
||||||
LOG_TAG(stats) \
|
LOG_TAG(stats) \
|
||||||
|
LOG_TAG(streaming) \
|
||||||
LOG_TAG(stringdedup) \
|
LOG_TAG(stringdedup) \
|
||||||
LOG_TAG(stringtable) \
|
LOG_TAG(stringtable) \
|
||||||
LOG_TAG(symboltable) \
|
LOG_TAG(symboltable) \
|
||||||
|
@ -312,7 +312,7 @@ void mutex_init() {
|
|||||||
#if INCLUDE_JFR
|
#if INCLUDE_JFR
|
||||||
def(JfrMsg_lock , PaddedMonitor, leaf, true, _safepoint_check_always);
|
def(JfrMsg_lock , PaddedMonitor, leaf, true, _safepoint_check_always);
|
||||||
def(JfrBuffer_lock , PaddedMutex , leaf, true, _safepoint_check_never);
|
def(JfrBuffer_lock , PaddedMutex , leaf, true, _safepoint_check_never);
|
||||||
def(JfrStream_lock , PaddedMutex , leaf+1, true, _safepoint_check_never); // ensure to rank lower than 'safepoint'
|
def(JfrStream_lock , PaddedMutex , nonleaf + 1, false, _safepoint_check_always);
|
||||||
def(JfrStacktrace_lock , PaddedMutex , special, true, _safepoint_check_never);
|
def(JfrStacktrace_lock , PaddedMutex , special, true, _safepoint_check_never);
|
||||||
def(JfrThreadSampler_lock , PaddedMonitor, leaf, true, _safepoint_check_never);
|
def(JfrThreadSampler_lock , PaddedMonitor, leaf, true, _safepoint_check_never);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -413,6 +413,36 @@ public final class Recording implements Closeable {
|
|||||||
internal.setMaxSize(maxSize);
|
internal.setMaxSize(maxSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines how often events are made available for streaming.
|
||||||
|
*
|
||||||
|
* @param interval the interval at which events are made available for streaming.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code interval} is negative
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if the recording is in the {@code CLOSED} state
|
||||||
|
*
|
||||||
|
* @since 14
|
||||||
|
*/
|
||||||
|
public void setFlushInterval(Duration interval) {
|
||||||
|
Objects.nonNull(interval);
|
||||||
|
if (interval.isNegative()) {
|
||||||
|
throw new IllegalArgumentException("Stream interval can't be negative");
|
||||||
|
}
|
||||||
|
internal.setFlushInterval(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns how often events are made available for streaming purposes.
|
||||||
|
*
|
||||||
|
* @return the flush interval, or {@code null} if no interval has been set
|
||||||
|
*
|
||||||
|
* @since 14
|
||||||
|
*/
|
||||||
|
public Duration getFlushInterval() {
|
||||||
|
return internal.getFlushInterval();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines how far back data is kept in the disk repository.
|
* Determines how far back data is kept in the disk repository.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -1,174 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation. Oracle designates this
|
|
||||||
* particular file as subject to the "Classpath" exception as provided
|
|
||||||
* by Oracle in the LICENSE file that accompanied this code.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package jdk.jfr.consumer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jdk.jfr.EventType;
|
|
||||||
import jdk.jfr.internal.LogLevel;
|
|
||||||
import jdk.jfr.internal.LogTag;
|
|
||||||
import jdk.jfr.internal.Logger;
|
|
||||||
import jdk.jfr.internal.MetadataDescriptor;
|
|
||||||
import jdk.jfr.internal.Type;
|
|
||||||
import jdk.jfr.internal.consumer.ChunkHeader;
|
|
||||||
import jdk.jfr.internal.consumer.RecordingInput;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a chunk.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
final class ChunkParser {
|
|
||||||
private static final long CONSTANT_POOL_TYPE_ID = 1;
|
|
||||||
private final RecordingInput input;
|
|
||||||
private final LongMap<Parser> parsers;
|
|
||||||
private final ChunkHeader chunkHeader;
|
|
||||||
private final long absoluteChunkEnd;
|
|
||||||
private final MetadataDescriptor metadata;
|
|
||||||
private final LongMap<Type> typeMap;
|
|
||||||
private final TimeConverter timeConverter;
|
|
||||||
|
|
||||||
public ChunkParser(RecordingInput input) throws IOException {
|
|
||||||
this(new ChunkHeader(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChunkParser(ChunkHeader header) throws IOException {
|
|
||||||
this.input = header.getInput();
|
|
||||||
this.chunkHeader = header;
|
|
||||||
this.metadata = header.readMetadata();
|
|
||||||
this.absoluteChunkEnd = header.getEnd();
|
|
||||||
this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
|
|
||||||
|
|
||||||
ParserFactory factory = new ParserFactory(metadata, timeConverter);
|
|
||||||
LongMap<ConstantMap> constantPools = factory.getConstantPools();
|
|
||||||
parsers = factory.getParsers();
|
|
||||||
typeMap = factory.getTypeMap();
|
|
||||||
|
|
||||||
fillConstantPools(parsers, constantPools);
|
|
||||||
constantPools.forEach(ConstantMap::setIsResolving);
|
|
||||||
constantPools.forEach(ConstantMap::resolve);
|
|
||||||
constantPools.forEach(ConstantMap::setResolved);
|
|
||||||
|
|
||||||
input.position(chunkHeader.getEventStart());
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecordedEvent readEvent() throws IOException {
|
|
||||||
while (input.position() < absoluteChunkEnd) {
|
|
||||||
long pos = input.position();
|
|
||||||
int size = input.readInt();
|
|
||||||
if (size == 0) {
|
|
||||||
throw new IOException("Event can't have zero size");
|
|
||||||
}
|
|
||||||
long typeId = input.readLong();
|
|
||||||
if (typeId > CONSTANT_POOL_TYPE_ID) { // also skips metadata (id=0)
|
|
||||||
Parser ep = parsers.get(typeId);
|
|
||||||
if (ep instanceof EventParser) {
|
|
||||||
return (RecordedEvent) ep.parse(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input.position(pos + size);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillConstantPools(LongMap<Parser> typeParser, LongMap<ConstantMap> constantPools) throws IOException {
|
|
||||||
long nextCP = chunkHeader.getAbsoluteChunkStart();
|
|
||||||
long deltaToNext = chunkHeader.getConstantPoolPosition();
|
|
||||||
while (deltaToNext != 0) {
|
|
||||||
nextCP += deltaToNext;
|
|
||||||
input.position(nextCP);
|
|
||||||
final long position = nextCP;
|
|
||||||
int size = input.readInt(); // size
|
|
||||||
long typeId = input.readLong();
|
|
||||||
if (typeId != CONSTANT_POOL_TYPE_ID) {
|
|
||||||
throw new IOException("Expected check point event (id = 1) at position " + nextCP + ", but found type id = " + typeId);
|
|
||||||
}
|
|
||||||
input.readLong(); // timestamp
|
|
||||||
input.readLong(); // duration
|
|
||||||
deltaToNext = input.readLong();
|
|
||||||
final long delta = deltaToNext;
|
|
||||||
boolean flush = input.readBoolean();
|
|
||||||
int poolCount = input.readInt();
|
|
||||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
|
|
||||||
return "New constant pool: startPosition=" + position + ", size=" + size + ", deltaToNext=" + delta + ", flush=" + flush + ", poolCount=" + poolCount;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (int i = 0; i < poolCount; i++) {
|
|
||||||
long id = input.readLong(); // type id
|
|
||||||
ConstantMap pool = constantPools.get(id);
|
|
||||||
Type type = typeMap.get(id);
|
|
||||||
if (pool == null) {
|
|
||||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used");
|
|
||||||
if (type == null) {
|
|
||||||
throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]");
|
|
||||||
}
|
|
||||||
pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
|
|
||||||
constantPools.put(type.getId(), pool);
|
|
||||||
}
|
|
||||||
Parser parser = typeParser.get(id);
|
|
||||||
if (parser == null) {
|
|
||||||
throw new IOException("Could not find constant pool type with id = " + id);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
int count = input.readInt();
|
|
||||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> "Constant: " + getName(id) + "[" + count + "]");
|
|
||||||
for (int j = 0; j < count; j++) {
|
|
||||||
long key = input.readLong();
|
|
||||||
Object value = parser.parse(input);
|
|
||||||
pool.put(key, value);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (input.position() != nextCP + size) {
|
|
||||||
throw new IOException("Size of check point event doesn't match content");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getName(long id) {
|
|
||||||
Type type = typeMap.get(id);
|
|
||||||
return type == null ? ("unknown(" + id + ")") : type.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<Type> getTypes() {
|
|
||||||
return metadata.getTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EventType> getEventTypes() {
|
|
||||||
return metadata.getEventTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLastChunk() {
|
|
||||||
return chunkHeader.isLastChunk();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChunkParser nextChunkParser() throws IOException {
|
|
||||||
return new ChunkParser(chunkHeader.nextHeader());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation. Oracle designates this
|
|
||||||
* particular file as subject to the "Classpath" exception as provided
|
|
||||||
* by Oracle in the LICENSE file that accompanied this code.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package jdk.jfr.consumer;
|
|
||||||
|
|
||||||
import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jdk.jfr.EventType;
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
|
||||||
import jdk.jfr.internal.consumer.RecordingInput;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an event and returns a {@link RecordedEvent}.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
final class EventParser extends Parser {
|
|
||||||
private final Parser[] parsers;
|
|
||||||
private final EventType eventType;
|
|
||||||
private final TimeConverter timeConverter;
|
|
||||||
private final boolean hasDuration;
|
|
||||||
private final List<ValueDescriptor> valueDescriptors;
|
|
||||||
|
|
||||||
EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
|
|
||||||
this.timeConverter = timeConverter;
|
|
||||||
this.parsers = parsers;
|
|
||||||
this.eventType = type;
|
|
||||||
this.hasDuration = type.getField(FIELD_DURATION) != null;
|
|
||||||
this.valueDescriptors = type.getFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object parse(RecordingInput input) throws IOException {
|
|
||||||
Object[] values = new Object[parsers.length];
|
|
||||||
for (int i = 0; i < parsers.length; i++) {
|
|
||||||
values[i] = parsers[i].parse(input);
|
|
||||||
}
|
|
||||||
Long startTicks = (Long) values[0];
|
|
||||||
long startTime = timeConverter.convertTimestamp(startTicks);
|
|
||||||
if (hasDuration) {
|
|
||||||
long durationTicks = (Long) values[1];
|
|
||||||
long endTime = timeConverter.convertTimestamp(startTicks + durationTicks);
|
|
||||||
return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter);
|
|
||||||
} else {
|
|
||||||
return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
364
src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java
Normal file
364
src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.consumer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.security.AccessControlContext;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import jdk.jfr.internal.SecuritySupport;
|
||||||
|
import jdk.jfr.internal.Utils;
|
||||||
|
import jdk.jfr.internal.consumer.EventDirectoryStream;
|
||||||
|
import jdk.jfr.internal.consumer.EventFileStream;
|
||||||
|
import jdk.jfr.internal.consumer.FileAccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a stream of events.
|
||||||
|
* <p>
|
||||||
|
* A stream is a sequence of events and the way to interact with a stream is to
|
||||||
|
* register actions. The {@code EventStream} interface is not to be implemented
|
||||||
|
* and future versions of the JDK may prevent this completely.
|
||||||
|
* <p>
|
||||||
|
* To receive a notification when an event arrives, register an action using the
|
||||||
|
* {@link #onEvent(Consumer)} method. To filter the stream for an event with a
|
||||||
|
* specific name, use {@link #onEvent(String, Consumer)} method.
|
||||||
|
* <p>
|
||||||
|
* By default, the same {@code RecordedEvent} object can be used to
|
||||||
|
* represent two or more distinct events. That object can be delivered
|
||||||
|
* multiple times to the same action as well as to other actions. To use an
|
||||||
|
* event object after the action is completed, the
|
||||||
|
* {@link #setReuse(boolean)} method should be set to {@code false} so a
|
||||||
|
* new object is allocated for each event.
|
||||||
|
* <p>
|
||||||
|
* Events are delivered in batches. To receive a notification when a batch is
|
||||||
|
* complete, register an action using the {@link #onFlush(Runnable)} method.
|
||||||
|
* This is an opportunity to aggregate or push data to external systems while
|
||||||
|
* the Java Virtual Machine (JVM) is preparing the next batch.
|
||||||
|
* <p>
|
||||||
|
* Events within a batch are sorted chronologically by their end time.
|
||||||
|
* Well-ordering of events is only maintained for events available to the JVM at
|
||||||
|
* the point of flush, i.e. for the set of events delivered as a unit in a
|
||||||
|
* single batch. Events delivered in a batch could therefore be out-of-order
|
||||||
|
* compared to events delivered in a previous batch, but never out-of-order with
|
||||||
|
* events within the same batch. If ordering is not a concern, sorting can be
|
||||||
|
* disabled using the {@link #setOrdered(boolean)} method.
|
||||||
|
* <p>
|
||||||
|
* To dispatch events to registered actions, the stream must be started. To
|
||||||
|
* start processing in the current thread, invoke the {@link #start()} method.
|
||||||
|
* To process actions asynchronously in a separate thread, invoke the
|
||||||
|
* {@link #startAsync()} method. To await completion of the stream, use the
|
||||||
|
* awaitTermination {@link #awaitTermination()} or the
|
||||||
|
* {@link #awaitTermination(Duration)} method.
|
||||||
|
* <p>
|
||||||
|
* When a stream ends it is automatically closed. To manually stop processing of
|
||||||
|
* events, close the stream by invoking the {@link #close()} method. A stream
|
||||||
|
* can also be automatically closed in exceptional circumstances, for example if
|
||||||
|
* the JVM that is being monitored exits. To receive a notification in any of
|
||||||
|
* these occasions, use the {@link #onClose(Runnable)} method to register an
|
||||||
|
* action.
|
||||||
|
* <p>
|
||||||
|
* If an unexpected exception occurs in an action, it is possible to catch the
|
||||||
|
* exception in an error handler. An error handler can be registered using the
|
||||||
|
* {@link #onError(Runnable)} method. If no error handler is registered, the
|
||||||
|
* default behavior is to print the exception and its backtrace to the standard
|
||||||
|
* error stream.
|
||||||
|
* <p>
|
||||||
|
* The following example shows how an {@code EventStream} can be used to listen
|
||||||
|
* to events on a JVM running Flight Recorder
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <code>
|
||||||
|
* try (var es = EventStream.openRepository()) {
|
||||||
|
* es.onEvent("jdk.CPULoad", event -> {
|
||||||
|
* System.out.println("CPU Load " + event.getEndTime());
|
||||||
|
* System.out.println(" Machine total: " + 100 * event.getFloat("machineTotal") + "%");
|
||||||
|
* System.out.println(" JVM User: " + 100 * event.getFloat("jvmUser") + "%");
|
||||||
|
* System.out.println(" JVM System: " + 100 * event.getFloat("jvmSystem") + "%");
|
||||||
|
* System.out.println();
|
||||||
|
* });
|
||||||
|
* es.onEvent("jdk.GarbageCollection", event -> {
|
||||||
|
* System.out.println("Garbage collection: " + event.getLong("gcId"));
|
||||||
|
* System.out.println(" Cause: " + event.getString("cause"));
|
||||||
|
* System.out.println(" Total pause: " + event.getDuration("sumOfPauses"));
|
||||||
|
* System.out.println(" Longest pause: " + event.getDuration("longestPause"));
|
||||||
|
* System.out.println();
|
||||||
|
* });
|
||||||
|
* es.start();
|
||||||
|
* }
|
||||||
|
* </code>
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* To start recording together with the stream, see {@link RecordingStream}.
|
||||||
|
*
|
||||||
|
* @since 14
|
||||||
|
*/
|
||||||
|
public interface EventStream extends AutoCloseable {
|
||||||
|
/**
|
||||||
|
* Creates a stream from the repository of the current Java Virtual Machine
|
||||||
|
* (JVM).
|
||||||
|
* <p>
|
||||||
|
* By default, the stream starts with the next event flushed by Flight
|
||||||
|
* Recorder.
|
||||||
|
*
|
||||||
|
* @return an event stream, not {@code null}
|
||||||
|
*
|
||||||
|
* @throws IOException if a stream can't be opened, or an I/O error occurs
|
||||||
|
* when trying to access the repository
|
||||||
|
*
|
||||||
|
* @throws SecurityException if a security manager exists and the caller
|
||||||
|
* does not have
|
||||||
|
* {@code FlightRecorderPermission("accessFlightRecorder")}
|
||||||
|
*/
|
||||||
|
public static EventStream openRepository() throws IOException {
|
||||||
|
Utils.checkAccessFlightRecorder();
|
||||||
|
return new EventDirectoryStream(AccessController.getContext(), null, SecuritySupport.PRIVILIGED, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event stream from a disk repository.
|
||||||
|
* <p>
|
||||||
|
* By default, the stream starts with the next event flushed by Flight
|
||||||
|
* Recorder.
|
||||||
|
*
|
||||||
|
* @param directory location of the disk repository, not {@code null}
|
||||||
|
*
|
||||||
|
* @return an event stream, not {@code null}
|
||||||
|
*
|
||||||
|
* @throws IOException if a stream can't be opened, or an I/O error occurs
|
||||||
|
* when trying to access the repository
|
||||||
|
*
|
||||||
|
* @throws SecurityException if a security manager exists and its
|
||||||
|
* {@code checkRead} method denies read access to the directory, or
|
||||||
|
* files in the directory.
|
||||||
|
*/
|
||||||
|
public static EventStream openRepository(Path directory) throws IOException {
|
||||||
|
Objects.nonNull(directory);
|
||||||
|
AccessControlContext acc = AccessController.getContext();
|
||||||
|
return new EventDirectoryStream(acc, directory, FileAccess.UNPRIVILIGED, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event stream from a file.
|
||||||
|
* <p>
|
||||||
|
* By default, the stream starts with the first event in the file.
|
||||||
|
*
|
||||||
|
* @param file location of the file, not {@code null}
|
||||||
|
*
|
||||||
|
* @return an event stream, not {@code null}
|
||||||
|
*
|
||||||
|
* @throws IOException if the file can't be opened, or an I/O error occurs
|
||||||
|
* during reading
|
||||||
|
*
|
||||||
|
* @throws SecurityException if a security manager exists and its
|
||||||
|
* {@code checkRead} method denies read access to the file
|
||||||
|
*/
|
||||||
|
static EventStream openFile(Path file) throws IOException {
|
||||||
|
return new EventFileStream(AccessController.getContext(), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an action to perform on all events in the stream.
|
||||||
|
*
|
||||||
|
* @param action an action to perform on each {@code RecordedEvent}, not
|
||||||
|
* {@code null}
|
||||||
|
*/
|
||||||
|
void onEvent(Consumer<RecordedEvent> action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an action to perform on all events matching a name.
|
||||||
|
*
|
||||||
|
* @param eventName the name of the event, not {@code null}
|
||||||
|
*
|
||||||
|
* @param action an action to perform on each {@code RecordedEvent} matching
|
||||||
|
* the event name, not {@code null}
|
||||||
|
*/
|
||||||
|
void onEvent(String eventName, Consumer<RecordedEvent> action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an action to perform after the stream has been flushed.
|
||||||
|
*
|
||||||
|
* @param action an action to perform after the stream has been
|
||||||
|
* flushed, not {@code null}
|
||||||
|
*/
|
||||||
|
void onFlush(Runnable action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an action to perform if an exception occurs.
|
||||||
|
* <p>
|
||||||
|
* if an action is not registered, an exception stack trace is printed to
|
||||||
|
* standard error.
|
||||||
|
* <p>
|
||||||
|
* Registering an action overrides the default behavior. If multiple actions
|
||||||
|
* have been registered, they are performed in the order of registration.
|
||||||
|
* <p>
|
||||||
|
* If this method itself throws an exception, resulting behavior is
|
||||||
|
* undefined.
|
||||||
|
*
|
||||||
|
* @param action an action to perform if an exception occurs, not
|
||||||
|
* {@code null}
|
||||||
|
*/
|
||||||
|
void onError(Consumer<Throwable> action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an action to perform when the stream is closed.
|
||||||
|
* <p>
|
||||||
|
* If the stream is already closed, the action will be performed immediately
|
||||||
|
* in the current thread.
|
||||||
|
*
|
||||||
|
* @param action an action to perform after the stream is closed, not
|
||||||
|
* {@code null}
|
||||||
|
* @see #close()
|
||||||
|
*/
|
||||||
|
void onClose(Runnable action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases all resources associated with this stream.
|
||||||
|
* <p>
|
||||||
|
* Closing a previously closed stream has no effect.
|
||||||
|
*/
|
||||||
|
void close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters an action.
|
||||||
|
* <p>
|
||||||
|
* If the action has been registered multiple times, all instances are
|
||||||
|
* unregistered.
|
||||||
|
*
|
||||||
|
* @param action the action to unregister, not {@code null}
|
||||||
|
*
|
||||||
|
* @return {@code true} if the action was unregistered, {@code false}
|
||||||
|
* otherwise
|
||||||
|
*
|
||||||
|
* @see #onEvent(Consumer)
|
||||||
|
* @see #onEvent(String, Consumer)
|
||||||
|
* @see #onFlush(Runnable)
|
||||||
|
* @see #onClose(Runnable)
|
||||||
|
* @see #onError(Consumer)
|
||||||
|
*/
|
||||||
|
boolean remove(Object action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies that the event object in an {@link #onEvent(Consumer)} action
|
||||||
|
* can be reused.
|
||||||
|
* <p>
|
||||||
|
* If reuse is set to {@code true), an action should not keep a reference
|
||||||
|
* to the event object after the action has completed.
|
||||||
|
*
|
||||||
|
* @param reuse {@code true} if an event object can be reused, {@code false}
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
void setReuse(boolean reuse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies that events arrives in chronological order, sorted by the time
|
||||||
|
* they were committed to the stream.
|
||||||
|
*
|
||||||
|
* @param ordered if event objects arrive in chronological order to
|
||||||
|
* {@code #onEvent(Consumer)}
|
||||||
|
*/
|
||||||
|
void setOrdered(boolean ordered);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the start time of the stream.
|
||||||
|
* <p>
|
||||||
|
* The start time must be set before starting the stream
|
||||||
|
*
|
||||||
|
* @param startTime the start time, not {@code null}
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if the stream is already started
|
||||||
|
*
|
||||||
|
* @see #start()
|
||||||
|
* @see #startAsync()
|
||||||
|
*/
|
||||||
|
void setStartTime(Instant startTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the end time of the stream.
|
||||||
|
* <p>
|
||||||
|
* The end time must be set before starting the stream.
|
||||||
|
* <p>
|
||||||
|
* At end time, the stream is closed.
|
||||||
|
*
|
||||||
|
* @param endTime the end time, not {@code null}
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if the stream is already started
|
||||||
|
*
|
||||||
|
* @see #start()
|
||||||
|
* @see #startAsync()
|
||||||
|
*/
|
||||||
|
void setEndTime(Instant endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start processing of actions.
|
||||||
|
* <p>
|
||||||
|
* Actions are performed in the current thread.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if the stream is already started or closed
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start asynchronous processing of actions.
|
||||||
|
* <p>
|
||||||
|
* Actions are performed in a single separate thread.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if the stream is already started or closed
|
||||||
|
*/
|
||||||
|
void startAsync();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks until all actions are completed, or the stream is closed, or the
|
||||||
|
* timeout occurs, or the current thread is interrupted, whichever happens
|
||||||
|
* first.
|
||||||
|
*
|
||||||
|
* @param timeout the maximum time to wait, not {@code null}
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if timeout is negative
|
||||||
|
* @throws InterruptedException if interrupted while waiting
|
||||||
|
*
|
||||||
|
* @see #start()
|
||||||
|
* @see #startAsync()
|
||||||
|
* @see Thread#interrupt()
|
||||||
|
*/
|
||||||
|
void awaitTermination(Duration timeout) throws InterruptedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks until all actions are completed, or the stream is closed, or the
|
||||||
|
* current thread is interrupted, whichever happens first.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException if interrupted while waiting
|
||||||
|
*
|
||||||
|
* @see #start()
|
||||||
|
* @see #startAsync()
|
||||||
|
* @see Thread#interrupt()
|
||||||
|
*/
|
||||||
|
void awaitTermination() throws InterruptedException;
|
||||||
|
}
|
@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation. Oracle designates this
|
|
||||||
* particular file as subject to the "Classpath" exception as provided
|
|
||||||
* by Oracle in the LICENSE file that accompanied this code.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package jdk.jfr.consumer;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
|
||||||
import jdk.jfr.internal.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract factory for creating specialized types
|
|
||||||
*/
|
|
||||||
abstract class ObjectFactory<T> {
|
|
||||||
|
|
||||||
final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types.";
|
|
||||||
final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX;
|
|
||||||
final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame";
|
|
||||||
final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame";
|
|
||||||
|
|
||||||
public static ObjectFactory<?> create(Type type, TimeConverter timeConverter) {
|
|
||||||
switch (type.getName()) {
|
|
||||||
case "java.lang.Thread":
|
|
||||||
return RecordedThread.createFactory(type, timeConverter);
|
|
||||||
case TYPE_PREFIX_VERSION_1 + "StackFrame":
|
|
||||||
case TYPE_PREFIX_VERSION_2 + "StackFrame":
|
|
||||||
return RecordedFrame.createFactory(type, timeConverter);
|
|
||||||
case TYPE_PREFIX_VERSION_1 + "Method":
|
|
||||||
case TYPE_PREFIX_VERSION_2 + "Method":
|
|
||||||
return RecordedMethod.createFactory(type, timeConverter);
|
|
||||||
case TYPE_PREFIX_VERSION_1 + "ThreadGroup":
|
|
||||||
case TYPE_PREFIX_VERSION_2 + "ThreadGroup":
|
|
||||||
return RecordedThreadGroup.createFactory(type, timeConverter);
|
|
||||||
case TYPE_PREFIX_VERSION_1 + "StackTrace":
|
|
||||||
case TYPE_PREFIX_VERSION_2 + "StackTrace":
|
|
||||||
return RecordedStackTrace.createFactory(type, timeConverter);
|
|
||||||
case TYPE_PREFIX_VERSION_1 + "ClassLoader":
|
|
||||||
case TYPE_PREFIX_VERSION_2 + "ClassLoader":
|
|
||||||
return RecordedClassLoader.createFactory(type, timeConverter);
|
|
||||||
case "java.lang.Class":
|
|
||||||
return RecordedClass.createFactory(type, timeConverter);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<ValueDescriptor> valueDescriptors;
|
|
||||||
|
|
||||||
ObjectFactory(Type type) {
|
|
||||||
this.valueDescriptors = type.getFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
T createObject(long id, Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (value instanceof Object[]) {
|
|
||||||
return createTyped(valueDescriptors, id, (Object[]) value);
|
|
||||||
}
|
|
||||||
throw new InternalError("Object factory must have struct type");
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract T createTyped(List<ValueDescriptor> valueDescriptors, long id, Object[] values);
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -26,10 +26,8 @@
|
|||||||
package jdk.jfr.consumer;
|
package jdk.jfr.consumer;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
import jdk.jfr.internal.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recorded Java type, such as a class or an interface.
|
* A recorded Java type, such as a class or an interface.
|
||||||
@ -37,21 +35,11 @@ import jdk.jfr.internal.Type;
|
|||||||
* @since 9
|
* @since 9
|
||||||
*/
|
*/
|
||||||
public final class RecordedClass extends RecordedObject {
|
public final class RecordedClass extends RecordedObject {
|
||||||
|
|
||||||
static ObjectFactory<RecordedClass> createFactory(Type type, TimeConverter timeConverter) {
|
|
||||||
return new ObjectFactory<RecordedClass>(type) {
|
|
||||||
@Override
|
|
||||||
RecordedClass createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
|
|
||||||
return new RecordedClass(desc, id, object, timeConverter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private final long uniqueId;
|
private final long uniqueId;
|
||||||
|
|
||||||
// package private
|
// package private
|
||||||
private RecordedClass(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
|
RecordedClass(ObjectContext objectContext, long id, Object[] values) {
|
||||||
super(descriptors, values, timeConverter);
|
super(objectContext, values);
|
||||||
this.uniqueId = id;
|
this.uniqueId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,10 +25,7 @@
|
|||||||
|
|
||||||
package jdk.jfr.consumer;
|
package jdk.jfr.consumer;
|
||||||
|
|
||||||
import java.util.List;
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
|
||||||
import jdk.jfr.internal.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recorded Java class loader.
|
* A recorded Java class loader.
|
||||||
@ -36,21 +33,11 @@ import jdk.jfr.internal.Type;
|
|||||||
* @since 9
|
* @since 9
|
||||||
*/
|
*/
|
||||||
public final class RecordedClassLoader extends RecordedObject {
|
public final class RecordedClassLoader extends RecordedObject {
|
||||||
|
|
||||||
static ObjectFactory<RecordedClassLoader> createFactory(Type type, TimeConverter timeConverter) {
|
|
||||||
return new ObjectFactory<RecordedClassLoader>(type) {
|
|
||||||
@Override
|
|
||||||
RecordedClassLoader createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
|
|
||||||
return new RecordedClassLoader(desc, id, object, timeConverter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private final long uniqueId;
|
private final long uniqueId;
|
||||||
|
|
||||||
// package private
|
// package private
|
||||||
private RecordedClassLoader(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
|
RecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
|
||||||
super(descriptors, values, timeConverter);
|
super(objectContext, values);
|
||||||
this.uniqueId = id;
|
this.uniqueId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -32,6 +32,7 @@ import java.util.List;
|
|||||||
import jdk.jfr.EventType;
|
import jdk.jfr.EventType;
|
||||||
import jdk.jfr.ValueDescriptor;
|
import jdk.jfr.ValueDescriptor;
|
||||||
import jdk.jfr.internal.EventInstrumentation;
|
import jdk.jfr.internal.EventInstrumentation;
|
||||||
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recorded event.
|
* A recorded event.
|
||||||
@ -39,17 +40,14 @@ import jdk.jfr.internal.EventInstrumentation;
|
|||||||
* @since 9
|
* @since 9
|
||||||
*/
|
*/
|
||||||
public final class RecordedEvent extends RecordedObject {
|
public final class RecordedEvent extends RecordedObject {
|
||||||
private final EventType eventType;
|
long startTimeTicks;
|
||||||
private final long startTime;
|
long endTimeTicks;
|
||||||
// package private needed for efficient sorting
|
|
||||||
final long endTime;
|
|
||||||
|
|
||||||
// package private
|
// package private
|
||||||
RecordedEvent(EventType type, List<ValueDescriptor> vds, Object[] values, long startTime, long endTime, TimeConverter timeConverter) {
|
RecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) {
|
||||||
super(vds, values, timeConverter);
|
super(objectContext, values);
|
||||||
this.eventType = type;
|
this.startTimeTicks = startTimeTicks;
|
||||||
this.startTime = startTime;
|
this.endTimeTicks = endTimeTicks;
|
||||||
this.endTime = endTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,7 +76,7 @@ public final class RecordedEvent extends RecordedObject {
|
|||||||
* @return the event type, not {@code null}
|
* @return the event type, not {@code null}
|
||||||
*/
|
*/
|
||||||
public EventType getEventType() {
|
public EventType getEventType() {
|
||||||
return eventType;
|
return objectContext.eventType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +87,7 @@ public final class RecordedEvent extends RecordedObject {
|
|||||||
* @return the start time, not {@code null}
|
* @return the start time, not {@code null}
|
||||||
*/
|
*/
|
||||||
public Instant getStartTime() {
|
public Instant getStartTime() {
|
||||||
return Instant.ofEpochSecond(0, startTime);
|
return Instant.ofEpochSecond(0, getStartTimeNanos());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,7 +98,7 @@ public final class RecordedEvent extends RecordedObject {
|
|||||||
* @return the end time, not {@code null}
|
* @return the end time, not {@code null}
|
||||||
*/
|
*/
|
||||||
public Instant getEndTime() {
|
public Instant getEndTime() {
|
||||||
return Instant.ofEpochSecond(0, endTime);
|
return Instant.ofEpochSecond(0, getEndTimeNanos());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,7 +107,7 @@ public final class RecordedEvent extends RecordedObject {
|
|||||||
* @return the duration in nanoseconds, not {@code null}
|
* @return the duration in nanoseconds, not {@code null}
|
||||||
*/
|
*/
|
||||||
public Duration getDuration() {
|
public Duration getDuration() {
|
||||||
return Duration.ofNanos(endTime - startTime);
|
return Duration.ofNanos(getEndTimeNanos() - getStartTimeNanos());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,6 +117,31 @@ public final class RecordedEvent extends RecordedObject {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<ValueDescriptor> getFields() {
|
public List<ValueDescriptor> getFields() {
|
||||||
return getEventType().getFields();
|
return objectContext.fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Object objectAt(int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
return startTimeTicks;
|
||||||
|
}
|
||||||
|
if (hasDuration()) {
|
||||||
|
if (index == 1) {
|
||||||
|
return endTimeTicks - startTimeTicks;
|
||||||
|
}
|
||||||
|
return objects[index - 2];
|
||||||
|
}
|
||||||
|
return objects[index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasDuration() {
|
||||||
|
return objects.length + 2 == objectContext.fields.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getStartTimeNanos() {
|
||||||
|
return objectContext.convertTimestamp(startTimeTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getEndTimeNanos() {
|
||||||
|
return objectContext.convertTimestamp(endTimeTicks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -26,10 +26,8 @@
|
|||||||
package jdk.jfr.consumer;
|
package jdk.jfr.consumer;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
import jdk.jfr.internal.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recorded frame in a stack trace.
|
* A recorded frame in a stack trace.
|
||||||
@ -37,19 +35,9 @@ import jdk.jfr.internal.Type;
|
|||||||
* @since 9
|
* @since 9
|
||||||
*/
|
*/
|
||||||
public final class RecordedFrame extends RecordedObject {
|
public final class RecordedFrame extends RecordedObject {
|
||||||
|
|
||||||
static ObjectFactory<RecordedFrame> createFactory(Type type, TimeConverter timeConverter) {
|
|
||||||
return new ObjectFactory<RecordedFrame>(type) {
|
|
||||||
@Override
|
|
||||||
RecordedFrame createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
|
|
||||||
return new RecordedFrame(desc, object, timeConverter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// package private
|
// package private
|
||||||
RecordedFrame(List<ValueDescriptor> desc, Object[] objects, TimeConverter timeConverter) {
|
RecordedFrame(ObjectContext objectContext, Object[] values) {
|
||||||
super(desc, objects, timeConverter);
|
super(objectContext, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -26,10 +26,8 @@
|
|||||||
package jdk.jfr.consumer;
|
package jdk.jfr.consumer;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
import jdk.jfr.internal.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recorded method.
|
* A recorded method.
|
||||||
@ -38,17 +36,9 @@ import jdk.jfr.internal.Type;
|
|||||||
*/
|
*/
|
||||||
public final class RecordedMethod extends RecordedObject {
|
public final class RecordedMethod extends RecordedObject {
|
||||||
|
|
||||||
static ObjectFactory<RecordedMethod> createFactory(Type type, TimeConverter timeConverter) {
|
// package private
|
||||||
return new ObjectFactory<RecordedMethod>(type) {
|
RecordedMethod(ObjectContext objectContext, Object[] values) {
|
||||||
@Override
|
super(objectContext, values);
|
||||||
RecordedMethod createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
|
|
||||||
return new RecordedMethod(desc, object, timeConverter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private RecordedMethod(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
|
|
||||||
super(descriptors, objects, timeConverter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,18 +25,24 @@
|
|||||||
|
|
||||||
package jdk.jfr.consumer;
|
package jdk.jfr.consumer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import jdk.jfr.Timespan;
|
import jdk.jfr.Timespan;
|
||||||
import jdk.jfr.Timestamp;
|
import jdk.jfr.Timestamp;
|
||||||
import jdk.jfr.ValueDescriptor;
|
import jdk.jfr.ValueDescriptor;
|
||||||
|
import jdk.jfr.internal.consumer.JdkJfrConsumer;
|
||||||
|
import jdk.jfr.internal.consumer.ObjectFactory;
|
||||||
import jdk.jfr.internal.PrivateAccess;
|
import jdk.jfr.internal.PrivateAccess;
|
||||||
|
import jdk.jfr.internal.Type;
|
||||||
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
import jdk.jfr.internal.tool.PrettyWriter;
|
import jdk.jfr.internal.tool.PrettyWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,6 +57,89 @@ import jdk.jfr.internal.tool.PrettyWriter;
|
|||||||
*/
|
*/
|
||||||
public class RecordedObject {
|
public class RecordedObject {
|
||||||
|
|
||||||
|
static{
|
||||||
|
JdkJfrConsumer access = new JdkJfrConsumer() {
|
||||||
|
public List<Type> readTypes(RecordingFile file) throws IOException {
|
||||||
|
return file.readTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastEventInChunk(RecordingFile file) {
|
||||||
|
return file.isLastEventInChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getOffsetDataTime(RecordedObject event, String name) {
|
||||||
|
return event.getOffsetDateTime(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordedClass newRecordedClass(ObjectContext objectContext, long id, Object[] values) {
|
||||||
|
return new RecordedClass(objectContext, id, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordedClassLoader newRecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
|
||||||
|
return new RecordedClassLoader(objectContext, id, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Comparator<? super RecordedEvent> eventComparator() {
|
||||||
|
return new Comparator<RecordedEvent>() {
|
||||||
|
@Override
|
||||||
|
public int compare(RecordedEvent e1, RecordedEvent e2) {
|
||||||
|
return Long.compare(e1.endTimeTicks, e2.endTimeTicks);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordedStackTrace newRecordedStackTrace(ObjectContext objectContext, Object[] values) {
|
||||||
|
return new RecordedStackTrace(objectContext, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordedThreadGroup newRecordedThreadGroup(ObjectContext objectContext, Object[] values) {
|
||||||
|
return new RecordedThreadGroup(objectContext, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordedFrame newRecordedFrame(ObjectContext objectContext, Object[] values) {
|
||||||
|
return new RecordedFrame(objectContext, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordedThread newRecordedThread(ObjectContext objectContext, long id, Object[] values) {
|
||||||
|
return new RecordedThread(objectContext, id, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordedMethod newRecordedMethod(ObjectContext objectContext, Object[] values) {
|
||||||
|
return new RecordedMethod(objectContext, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecordedEvent newRecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) {
|
||||||
|
return new RecordedEvent(objectContext, values, startTimeTicks, endTimeTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStartTicks(RecordedEvent event, long startTicks) {
|
||||||
|
event.startTimeTicks = startTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEndTicks(RecordedEvent event, long endTicks) {
|
||||||
|
event.endTimeTicks = endTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] eventValues(RecordedEvent event) {
|
||||||
|
return event.objects;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
JdkJfrConsumer.setAccess(access);
|
||||||
|
}
|
||||||
|
|
||||||
private final static class UnsignedValue {
|
private final static class UnsignedValue {
|
||||||
private final Object o;
|
private final Object o;
|
||||||
|
|
||||||
@ -63,15 +152,13 @@ public class RecordedObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Object[] objects;
|
final Object[] objects;
|
||||||
private final List<ValueDescriptor> descriptors;
|
final ObjectContext objectContext;
|
||||||
private final TimeConverter timeConverter;
|
|
||||||
|
|
||||||
// package private, not to be subclassed outside this package
|
// package private, not to be subclassed outside this package
|
||||||
RecordedObject(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
|
RecordedObject(ObjectContext objectContext, Object[] objects) {
|
||||||
this.descriptors = descriptors;
|
this.objectContext = objectContext;
|
||||||
this.objects = objects;
|
this.objects = objects;
|
||||||
this.timeConverter = timeConverter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// package private
|
// package private
|
||||||
@ -101,7 +188,7 @@ public class RecordedObject {
|
|||||||
*/
|
*/
|
||||||
public boolean hasField(String name) {
|
public boolean hasField(String name) {
|
||||||
Objects.requireNonNull(name);
|
Objects.requireNonNull(name);
|
||||||
for (ValueDescriptor v : descriptors) {
|
for (ValueDescriptor v : objectContext.fields) {
|
||||||
if (v.getName().equals(name)) {
|
if (v.getName().equals(name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -109,7 +196,7 @@ public class RecordedObject {
|
|||||||
int dotIndex = name.indexOf(".");
|
int dotIndex = name.indexOf(".");
|
||||||
if (dotIndex > 0) {
|
if (dotIndex > 0) {
|
||||||
String structName = name.substring(0, dotIndex);
|
String structName = name.substring(0, dotIndex);
|
||||||
for (ValueDescriptor v : descriptors) {
|
for (ValueDescriptor v : objectContext.fields) {
|
||||||
if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
|
if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
|
||||||
RecordedObject child = getValue(structName);
|
RecordedObject child = getValue(structName);
|
||||||
if (child != null) {
|
if (child != null) {
|
||||||
@ -169,12 +256,16 @@ public class RecordedObject {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Object objectAt(int index) {
|
||||||
|
return objects[index];
|
||||||
|
}
|
||||||
|
|
||||||
private Object getValue(String name, boolean allowUnsigned) {
|
private Object getValue(String name, boolean allowUnsigned) {
|
||||||
Objects.requireNonNull(name);
|
Objects.requireNonNull(name);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (ValueDescriptor v : descriptors) {
|
for (ValueDescriptor v : objectContext.fields) {
|
||||||
if (name.equals(v.getName())) {
|
if (name.equals(v.getName())) {
|
||||||
Object object = objects[index];
|
Object object = objectAt(index);
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
// error or missing
|
// error or missing
|
||||||
return null;
|
return null;
|
||||||
@ -200,7 +291,7 @@ public class RecordedObject {
|
|||||||
return structifyArray(v, array, 0);
|
return structifyArray(v, array, 0);
|
||||||
}
|
}
|
||||||
// struct
|
// struct
|
||||||
return new RecordedObject(v.getFields(), (Object[]) object, timeConverter);
|
return new RecordedObject(objectContext.getInstance(v), (Object[]) object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
@ -209,7 +300,7 @@ public class RecordedObject {
|
|||||||
int dotIndex = name.indexOf(".");
|
int dotIndex = name.indexOf(".");
|
||||||
if (dotIndex > 0) {
|
if (dotIndex > 0) {
|
||||||
String structName = name.substring(0, dotIndex);
|
String structName = name.substring(0, dotIndex);
|
||||||
for (ValueDescriptor v : descriptors) {
|
for (ValueDescriptor v : objectContext.fields) {
|
||||||
if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
|
if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
|
||||||
RecordedObject child = getValue(structName);
|
RecordedObject child = getValue(structName);
|
||||||
String subName = name.substring(dotIndex + 1);
|
String subName = name.substring(dotIndex + 1);
|
||||||
@ -261,7 +352,7 @@ public class RecordedObject {
|
|||||||
private <T> T getTypedValue(String name, String typeName) {
|
private <T> T getTypedValue(String name, String typeName) {
|
||||||
Objects.requireNonNull(name);
|
Objects.requireNonNull(name);
|
||||||
// Validate name and type first
|
// Validate name and type first
|
||||||
getValueDescriptor(descriptors, name, typeName);
|
getValueDescriptor(objectContext.fields, name, typeName);
|
||||||
return getValue(name);
|
return getValue(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,15 +361,16 @@ public class RecordedObject {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Object[] structArray = new Object[array.length];
|
Object[] structArray = new Object[array.length];
|
||||||
|
ObjectContext objContext = objectContext.getInstance(v);
|
||||||
for (int i = 0; i < structArray.length; i++) {
|
for (int i = 0; i < structArray.length; i++) {
|
||||||
Object arrayElement = array[i];
|
Object arrayElement = array[i];
|
||||||
if (dimension == 0) {
|
if (dimension == 0) {
|
||||||
// No general way to handle structarrays
|
// No general way to handle structarrays
|
||||||
// without invoking ObjectFactory for every instance (which may require id)
|
// without invoking ObjectFactory for every instance (which may require id)
|
||||||
if (isStackFrameType(v.getTypeName())) {
|
if (isStackFrameType(v.getTypeName())) {
|
||||||
structArray[i] = new RecordedFrame(v.getFields(), (Object[]) arrayElement, timeConverter);
|
structArray[i] = new RecordedFrame(objContext, (Object[]) arrayElement);
|
||||||
} else {
|
} else {
|
||||||
structArray[i] = new RecordedObject(v.getFields(), (Object[]) arrayElement, timeConverter);
|
structArray[i] = new RecordedObject(objContext, (Object[]) arrayElement);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1);
|
structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1);
|
||||||
@ -303,7 +395,7 @@ public class RecordedObject {
|
|||||||
* @return the fields, not {@code null}
|
* @return the fields, not {@code null}
|
||||||
*/
|
*/
|
||||||
public List<ValueDescriptor> getFields() {
|
public List<ValueDescriptor> getFields() {
|
||||||
return descriptors;
|
return objectContext.fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -725,7 +817,7 @@ public class RecordedObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Duration getDuration(long timespan, String name) throws InternalError {
|
private Duration getDuration(long timespan, String name) throws InternalError {
|
||||||
ValueDescriptor v = getValueDescriptor(descriptors, name, null);
|
ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
|
||||||
if (timespan == Long.MIN_VALUE) {
|
if (timespan == Long.MIN_VALUE) {
|
||||||
return Duration.ofSeconds(Long.MIN_VALUE, 0);
|
return Duration.ofSeconds(Long.MIN_VALUE, 0);
|
||||||
}
|
}
|
||||||
@ -741,7 +833,7 @@ public class RecordedObject {
|
|||||||
case Timespan.NANOSECONDS:
|
case Timespan.NANOSECONDS:
|
||||||
return Duration.ofNanos(timespan);
|
return Duration.ofNanos(timespan);
|
||||||
case Timespan.TICKS:
|
case Timespan.TICKS:
|
||||||
return Duration.ofNanos(timeConverter.convertTimespan(timespan));
|
return Duration.ofNanos(objectContext.convertTimespan(timespan));
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value());
|
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value());
|
||||||
}
|
}
|
||||||
@ -804,7 +896,7 @@ public class RecordedObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Instant getInstant(long timestamp, String name) {
|
private Instant getInstant(long timestamp, String name) {
|
||||||
ValueDescriptor v = getValueDescriptor(descriptors, name, null);
|
ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
|
||||||
Timestamp ts = v.getAnnotation(Timestamp.class);
|
Timestamp ts = v.getAnnotation(Timestamp.class);
|
||||||
if (ts != null) {
|
if (ts != null) {
|
||||||
if (timestamp == Long.MIN_VALUE) {
|
if (timestamp == Long.MIN_VALUE) {
|
||||||
@ -814,7 +906,7 @@ public class RecordedObject {
|
|||||||
case Timestamp.MILLISECONDS_SINCE_EPOCH:
|
case Timestamp.MILLISECONDS_SINCE_EPOCH:
|
||||||
return Instant.ofEpochMilli(timestamp);
|
return Instant.ofEpochMilli(timestamp);
|
||||||
case Timestamp.TICKS:
|
case Timestamp.TICKS:
|
||||||
return Instant.ofEpochSecond(0, timeConverter.convertTimestamp(timestamp));
|
return Instant.ofEpochSecond(0, objectContext.convertTimestamp(timestamp));
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value());
|
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value());
|
||||||
}
|
}
|
||||||
@ -889,12 +981,12 @@ public class RecordedObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// package private for now. Used by EventWriter
|
// package private for now. Used by EventWriter
|
||||||
OffsetDateTime getOffsetDateTime(String name) {
|
private OffsetDateTime getOffsetDateTime(String name) {
|
||||||
Instant instant = getInstant(name);
|
Instant instant = getInstant(name);
|
||||||
if (instant.equals(Instant.MIN)) {
|
if (instant.equals(Instant.MIN)) {
|
||||||
return OffsetDateTime.MIN;
|
return OffsetDateTime.MIN;
|
||||||
}
|
}
|
||||||
return OffsetDateTime.ofInstant(getInstant(name), timeConverter.getZoneOffset());
|
return OffsetDateTime.ofInstant(getInstant(name), objectContext.getZoneOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
|
private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -29,8 +29,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
import jdk.jfr.internal.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recorded stack trace.
|
* A recorded stack trace.
|
||||||
@ -38,18 +37,9 @@ import jdk.jfr.internal.Type;
|
|||||||
* @since 9
|
* @since 9
|
||||||
*/
|
*/
|
||||||
public final class RecordedStackTrace extends RecordedObject {
|
public final class RecordedStackTrace extends RecordedObject {
|
||||||
|
// package private
|
||||||
static ObjectFactory<RecordedStackTrace> createFactory(Type type, TimeConverter timeConverter) {
|
RecordedStackTrace(ObjectContext objectContext, Object[] values) {
|
||||||
return new ObjectFactory<RecordedStackTrace>(type) {
|
super(objectContext, values);
|
||||||
@Override
|
|
||||||
RecordedStackTrace createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
|
|
||||||
return new RecordedStackTrace(desc, object, timeConverter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private RecordedStackTrace(List<ValueDescriptor> desc, Object[] values, TimeConverter timeConverter) {
|
|
||||||
super(desc, values, timeConverter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,10 +25,7 @@
|
|||||||
|
|
||||||
package jdk.jfr.consumer;
|
package jdk.jfr.consumer;
|
||||||
|
|
||||||
import java.util.List;
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
|
||||||
import jdk.jfr.internal.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recorded thread.
|
* A recorded thread.
|
||||||
@ -36,20 +33,11 @@ import jdk.jfr.internal.Type;
|
|||||||
* @since 9
|
* @since 9
|
||||||
*/
|
*/
|
||||||
public final class RecordedThread extends RecordedObject {
|
public final class RecordedThread extends RecordedObject {
|
||||||
|
|
||||||
static ObjectFactory<RecordedThread> createFactory(Type type, TimeConverter timeConverter) {
|
|
||||||
return new ObjectFactory<RecordedThread>(type) {
|
|
||||||
@Override
|
|
||||||
RecordedThread createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
|
|
||||||
return new RecordedThread(desc, id, object, timeConverter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private final long uniqueId;
|
private final long uniqueId;
|
||||||
|
|
||||||
private RecordedThread(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
|
// package private
|
||||||
super(descriptors, values, timeConverter);
|
RecordedThread(ObjectContext objectContext, long id, Object[] values) {
|
||||||
|
super(objectContext, values);
|
||||||
this.uniqueId = id;
|
this.uniqueId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,10 +25,7 @@
|
|||||||
|
|
||||||
package jdk.jfr.consumer;
|
package jdk.jfr.consumer;
|
||||||
|
|
||||||
import java.util.List;
|
import jdk.jfr.internal.consumer.ObjectContext;
|
||||||
|
|
||||||
import jdk.jfr.ValueDescriptor;
|
|
||||||
import jdk.jfr.internal.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recorded Java thread group.
|
* A recorded Java thread group.
|
||||||
@ -36,18 +33,9 @@ import jdk.jfr.internal.Type;
|
|||||||
* @since 9
|
* @since 9
|
||||||
*/
|
*/
|
||||||
public final class RecordedThreadGroup extends RecordedObject {
|
public final class RecordedThreadGroup extends RecordedObject {
|
||||||
|
// package private
|
||||||
static ObjectFactory<RecordedThreadGroup> createFactory(Type type, TimeConverter timeConverter) {
|
RecordedThreadGroup(ObjectContext objectContext, Object[] values) {
|
||||||
return new ObjectFactory<RecordedThreadGroup>(type) {
|
super(objectContext, values);
|
||||||
@Override
|
|
||||||
RecordedThreadGroup createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
|
|
||||||
return new RecordedThreadGroup(desc, object, timeConverter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private RecordedThreadGroup(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
|
|
||||||
super(descriptors, objects, timeConverter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user