提交 79ae032e 编写于 作者: D Denghui Dong 提交者: D-D-H

[Backport] 8226511: Implement JFR Event Streaming

Summary:

Test Plan: jdk/jfr

Reviewed-by: yuleil

Issue: https://github.com/alibaba/dragonwell8/issues/112
上级 e6305e1b
...@@ -345,6 +345,7 @@ JfrStartFlightRecordingDCmd::JfrStartFlightRecordingDCmd(outputStream* output, ...@@ -345,6 +345,7 @@ JfrStartFlightRecordingDCmd::JfrStartFlightRecordingDCmd(outputStream* output,
_disk("disk", "Recording should be persisted to disk", "BOOLEAN", false), _disk("disk", "Recording should be persisted to disk", "BOOLEAN", false),
_maxage("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"), _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);
...@@ -355,6 +356,7 @@ JfrStartFlightRecordingDCmd::JfrStartFlightRecordingDCmd(outputStream* output, ...@@ -355,6 +356,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);
}; };
...@@ -407,6 +409,10 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { ...@@ -407,6 +409,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);
...@@ -460,7 +466,7 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { ...@@ -460,7 +466,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);
...@@ -474,6 +480,7 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { ...@@ -474,6 +480,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 { ...@@ -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;
......
...@@ -78,6 +78,18 @@ void Jfr::on_thread_exit(Thread* t) { ...@@ -78,6 +78,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);
......
...@@ -56,6 +56,9 @@ class Jfr : AllStatic { ...@@ -56,6 +56,9 @@ class Jfr : AllStatic {
static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f); static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
static void weak_oops_do(OopClosure* f); static void weak_oops_do(OopClosure* f);
static Thread* sampler_thread(); static Thread* sampler_thread();
static void exclude_thread(Thread* thread);
static bool is_excluded(Thread* thread);
static void include_thread(Thread* thread);
}; };
#endif // SHARE_VM_JFR_JFR_HPP #endif // SHARE_VM_JFR_JFR_HPP
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
*/ */
#include "precompiled.hpp" #include "precompiled.hpp"
#include "jni.h"
#include "classfile/javaClasses.hpp" #include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp" #include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp" #include "classfile/systemDictionary.hpp"
...@@ -40,9 +39,11 @@ ...@@ -40,9 +39,11 @@
#include "runtime/fieldDescriptor.hpp" #include "runtime/fieldDescriptor.hpp"
#include "runtime/java.hpp" #include "runtime/java.hpp"
#include "runtime/jniHandles.hpp" #include "runtime/jniHandles.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) {
...@@ -56,6 +57,14 @@ void JfrJavaSupport::check_java_thread_in_native(Thread* t) { ...@@ -56,6 +57,14 @@ 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");
// The start-up process of JFR is different from that of upstream,
// so the following line needs to be commented out
// assert(((JavaThread*)t)->thread_state() == _thread_new, "invariant");
}
#endif #endif
/* /*
...@@ -91,6 +100,21 @@ void JfrJavaSupport::destroy_global_jni_handle(const jobject handle) { ...@@ -91,6 +100,21 @@ void JfrJavaSupport::destroy_global_jni_handle(const 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);
} }
...@@ -574,14 +598,141 @@ JfrJavaSupport::CAUSE JfrJavaSupport::cause() { ...@@ -574,14 +598,141 @@ JfrJavaSupport::CAUSE JfrJavaSupport::cause() {
return _cause; return _cause;
} }
// XXX class ThreadExclusionListAccess : public StackObj {
//const char* const JDK_JFR_MODULE_NAME = "jdk.jfr"; private:
//const char* const JDK_JFR_PACKAGE_NAME = "jdk/jfr"; 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));
}
jlong JfrJavaSupport::jfr_thread_id(jobject target_thread) { // includes removal
// ThreadsListHandle tlh; static bool check_exclusion_state_on_thread_start(JavaThread* jt) {
// XXX is it correct and safe? Handle h_obj(jt, jt->threadObj());
JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(target_thread)); ThreadExclusionListAccess lock;
// (void)tlh.cv_internal_thread_to_JavaThread(target_thread, &native_thread, NULL); 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) {
JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(thread));
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;
JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(thread));
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;
JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(thread));
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;
JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(thread));
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 @@ ...@@ -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);
...@@ -83,7 +86,11 @@ class JfrJavaSupport : public AllStatic { ...@@ -83,7 +86,11 @@ class JfrJavaSupport : public AllStatic {
static void throw_class_format_error(const char* message, TRAPS); static void throw_class_format_error(const char* message, TRAPS);
static void throw_runtime_exception(const char* message, TRAPS); static void throw_runtime_exception(const char* message, 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);
......
/* /*
* 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
...@@ -303,6 +303,10 @@ JVM_ENTRY_NO_ENV(jboolean, jfr_event_writer_flush(JNIEnv* env, jclass cls, jobje ...@@ -303,6 +303,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
...@@ -330,3 +334,20 @@ JVM_END ...@@ -330,3 +334,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
...@@ -117,6 +117,7 @@ jobject JNICALL jfr_new_event_writer(JNIEnv* env, jclass cls); ...@@ -117,6 +117,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);
...@@ -135,6 +136,13 @@ void JNICALL jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ ...@@ -135,6 +136,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
} }
......
/* /*
* 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
...@@ -72,6 +72,7 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { ...@@ -72,6 +72,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,
...@@ -81,7 +82,11 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { ...@@ -81,7 +82,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) { ...@@ -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;
} }
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "oops/markOop.hpp" #include "oops/markOop.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"
...@@ -50,8 +51,8 @@ EventEmitter::~EventEmitter() { ...@@ -50,8 +51,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) {
...@@ -67,6 +68,7 @@ void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_ ...@@ -67,6 +68,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 @@ ...@@ -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/semaphore.hpp" #include "runtime/semaphore.hpp"
#include "runtime/thread.hpp" #include "runtime/thread.hpp"
...@@ -272,7 +273,7 @@ void StackTraceBlobInstaller::install(ObjectSample* sample) { ...@@ -272,7 +273,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);
...@@ -292,6 +293,7 @@ static void install_stack_traces(const ObjectSampler* sampler, JfrStackTraceRepo ...@@ -292,6 +293,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);
...@@ -389,7 +391,7 @@ class BlobWriter { ...@@ -389,7 +391,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
...@@ -398,13 +400,14 @@ static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thre ...@@ -398,13 +400,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) ...@@ -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 { ...@@ -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;
} }
} }
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
//#include "gc_interface/strongRootsScope.hpp" //#include "gc_interface/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 "oops/klass.hpp" #include "oops/klass.hpp"
#include "oops/oop.hpp" #include "oops/oop.hpp"
...@@ -253,8 +254,9 @@ class ReferenceToThreadRootClosure : public StackObj { ...@@ -253,8 +254,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 (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { JfrJavaThreadIterator iter;
if (do_thread_roots(thread)) { while (iter.has_next()) {
if (do_thread_roots(iter.next())) {
return; return;
} }
} }
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "jfr/leakprofiler/sampling/objectSampler.hpp" #include "jfr/leakprofiler/sampling/objectSampler.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp" #include "jfr/recorder/service/jfrOptionSet.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"
...@@ -81,6 +82,7 @@ void LeakProfiler::emit_events(int64_t cutoff_ticks, bool emit_all) { ...@@ -81,6 +82,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");
......
...@@ -108,6 +108,9 @@ static traceid get_thread_id(JavaThread* thread) { ...@@ -108,6 +108,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 @@ ...@@ -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;
}; };
......
...@@ -154,7 +154,7 @@ ...@@ -154,7 +154,7 @@
<Field type="string" name="newValue" label="New Value" /> <Field type="string" name="newValue" label="New Value" />
<Field type="FlagValueOrigin" name="origin" label="Origin" /> <Field type="FlagValueOrigin" name="origin" label="Origin" />
</Event> </Event>
<Type name="VirtualSpace"> <Type name="VirtualSpace">
<Field type="ulong" contentType="address" name="start" label="Start Address" description="Start address of the virtual space" /> <Field type="ulong" contentType="address" name="start" label="Start Address" description="Start address of the virtual space" />
<Field type="ulong" contentType="address" name="committedEnd" label="Committed End Address" description="End address of the committed memory for the virtual space" /> <Field type="ulong" contentType="address" name="committedEnd" label="Committed End Address" description="End address of the committed memory for the virtual space" />
...@@ -162,27 +162,27 @@ ...@@ -162,27 +162,27 @@
<Field type="ulong" contentType="address" name="reservedEnd" label="Reserved End Address" description="End address of the reserved memory for the virtual space" /> <Field type="ulong" contentType="address" name="reservedEnd" label="Reserved End Address" description="End address of the reserved memory for the virtual space" />
<Field type="ulong" contentType="bytes" name="reservedSize" label="Reserved Size" description="Size of the reserved memory for the virtual space" /> <Field type="ulong" contentType="bytes" name="reservedSize" label="Reserved Size" description="Size of the reserved memory for the virtual space" />
</Type> </Type>
<Type name="ObjectSpace"> <Type name="ObjectSpace">
<Field type="ulong" contentType="address" name="start" label="Start Address" description="Start address of the space" /> <Field type="ulong" contentType="address" name="start" label="Start Address" description="Start address of the space" />
<Field type="ulong" contentType="address" name="end" label="End Address" description="End address of the space" /> <Field type="ulong" contentType="address" name="end" label="End Address" description="End address of the space" />
<Field type="ulong" contentType="bytes" name="used" label="Used" description="Bytes allocated by objects in the space" /> <Field type="ulong" contentType="bytes" name="used" label="Used" description="Bytes allocated by objects in the space" />
<Field type="ulong" contentType="bytes" name="size" label="Size" description="Size of the space" /> <Field type="ulong" contentType="bytes" name="size" label="Size" description="Size of the space" />
</Type> </Type>
<Event name="GCHeapSummary" category="Java Virtual Machine, GC, Heap" label="Heap Summary" startTime="false"> <Event name="GCHeapSummary" category="Java Virtual Machine, GC, Heap" label="Heap Summary" startTime="false">
<Field type="uint" name="gcId" label="GC Identifier" relation="GcId" /> <Field type="uint" name="gcId" label="GC Identifier" relation="GcId" />
<Field type="GCWhen" name="when" label="When" /> <Field type="GCWhen" name="when" label="When" />
<Field type="VirtualSpace" struct="true" name="heapSpace" label="Heap Space" /> <Field type="VirtualSpace" struct="true" name="heapSpace" label="Heap Space" />
<Field type="ulong" contentType="bytes" name="heapUsed" label="Heap Used" description="Bytes allocated by objects in the heap" /> <Field type="ulong" contentType="bytes" name="heapUsed" label="Heap Used" description="Bytes allocated by objects in the heap" />
</Event> </Event>
<Type name="MetaspaceSizes"> <Type name="MetaspaceSizes">
<Field type="ulong" contentType="bytes" name="committed" label="Committed" description="Committed memory for this space" /> <Field type="ulong" contentType="bytes" name="committed" label="Committed" description="Committed memory for this space" />
<Field type="ulong" contentType="bytes" name="used" label="Used" description="Bytes allocated by objects in the space" /> <Field type="ulong" contentType="bytes" name="used" label="Used" description="Bytes allocated by objects in the space" />
<Field type="ulong" contentType="bytes" name="reserved" label="Reserved" description="Reserved memory for this space" /> <Field type="ulong" contentType="bytes" name="reserved" label="Reserved" description="Reserved memory for this space" />
</Type> </Type>
<Event name="MetaspaceSummary" category="Java Virtual Machine, GC, Heap" label="Metaspace Summary" startTime="false"> <Event name="MetaspaceSummary" category="Java Virtual Machine, GC, Heap" label="Metaspace Summary" startTime="false">
<Field type="uint" name="gcId" label="GC Identifier" relation="GcId" /> <Field type="uint" name="gcId" label="GC Identifier" relation="GcId" />
<Field type="GCWhen" name="when" label="When" /> <Field type="GCWhen" name="when" label="When" />
...@@ -477,7 +477,7 @@ ...@@ -477,7 +477,7 @@
<Field type="string" name="failureMessage" label="Failure Message" /> <Field type="string" name="failureMessage" label="Failure Message" />
<Field type="uint" name="compileId" label="Compilation Identifier" relation="CompileId" /> <Field type="uint" name="compileId" label="Compilation Identifier" relation="CompileId" />
</Event> </Event>
<Type name="CalleeMethod"> <Type name="CalleeMethod">
<Field type="string" name="type" label="Class" /> <Field type="string" name="type" label="Class" />
<Field type="string" name="name" label="Method Name" /> <Field type="string" name="name" label="Method Name" />
...@@ -595,21 +595,21 @@ ...@@ -595,21 +595,21 @@
<Field type="OldObjectGcRoot" name="root" label="GC Root" /> <Field type="OldObjectGcRoot" name="root" label="GC Root" />
</Event> </Event>
<Event name="DumpReason" category="Flight Recorder" label="Recording Reason" <Event name="DumpReason" category="Flight Recorder" label="Recording Reason"
description="Who requested the recording and why" description="Who requested the recording and why"
startTime="false"> startTime="false">
<Field type="string" name="reason" label="Reason" description="Reason for writing recording data to disk" /> <Field type="string" name="reason" label="Reason" description="Reason for writing recording data to disk" />
<Field type="int" name="recordingId" label="Recording Id" description="Id of the recording that triggered the dump, or -1 if it was not related to a recording" /> <Field type="int" name="recordingId" label="Recording Id" description="Id of the recording that triggered the dump, or -1 if it was not related to a recording" />
</Event> </Event>
<Event name="DataLoss" category="Flight Recorder" label="Data Loss" <Event name="DataLoss" category="Flight Recorder" label="Data Loss"
description="Data could not be copied out from a buffer, typically because of contention" description="Data could not be copied out from a buffer, typically because of contention"
startTime="false"> startTime="false">
<Field type="ulong" contentType="bytes" name="amount" label="Amount" description="Amount lost data" /> <Field type="ulong" contentType="bytes" name="amount" label="Amount" description="Amount lost data" />
<Field type="ulong" contentType="bytes" name="total" label="Total" description="Total lost amount for thread" /> <Field type="ulong" contentType="bytes" name="total" label="Total" description="Total lost amount for thread" />
</Event> </Event>
<Event name="JVMInformation" category="Java Virtual Machine" label="JVM Information" <Event name="JVMInformation" category="Java Virtual Machine" label="JVM Information"
description="Description of JVM and the Java application" description="Description of JVM and the Java application"
period="endChunk"> period="endChunk">
<Field type="string" name="jvmName" label="JVM Name" /> <Field type="string" name="jvmName" label="JVM Name" />
...@@ -930,6 +930,42 @@ ...@@ -930,6 +930,42 @@
<Field type="ulong" name="value" label="Value" /> <Field type="ulong" name="value" label="Value" />
</Event> </Event>
<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>
...@@ -1112,34 +1148,40 @@ ...@@ -1112,34 +1148,40 @@
<Field type="int" name="bytecodeIndex" label="Bytecode Index" /> <Field type="int" name="bytecodeIndex" label="Bytecode Index" />
<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*"/>
<XmlType name="ClassLoader" parameterType="const ClassLoaderData*" fieldType="const ClassLoaderData*"/> <XmlType name="Module" parameterType="const ModuleEntry*" fieldType="const ModuleEntry*"/>
<XmlType name="Method" parameterType="const Method*" fieldType="const Method*"/> <XmlType name="ClassLoader" parameterType="const ClassLoaderData*" fieldType="const ClassLoaderData*"/>
<XmlType name="Thread" javaType="java.lang.Thread" parameterType="u8" fieldType="u8"/> <XmlType name="Method" parameterType="const Method*" fieldType="const Method*"/>
<XmlType name="Tickspan" contentType="tickspan" javaType="long" parameterType="const Tickspan&amp;" fieldType="Tickspan"/> <XmlType name="Thread" javaType="java.lang.Thread" parameterType="u8" fieldType="u8"/>
<XmlType name="Ticks" contentType="tickstamp" javaType="long" parameterType="const Ticks&amp;" fieldType="Ticks"/> <XmlType name="Tickspan" contentType="tickspan" javaType="long" parameterType="const Tickspan&amp;" fieldType="Tickspan"/>
<XmlType name="ulong" javaType="long" unsigned="true" parameterType="u8" fieldType="u8"/> <XmlType name="Ticks" contentType="tickstamp" javaType="long" parameterType="const Ticks&amp;" fieldType="Ticks"/>
<XmlType name="uint" javaType="int" unsigned="true" parameterType="unsigned" fieldType="unsigned"/> <XmlType name="ulong" javaType="long" unsigned="true" parameterType="u8" fieldType="u8"/>
<XmlType name="ushort" javaType="short" unsigned="true" parameterType="u2" fieldType="u2"/> <XmlType name="uint" javaType="int" unsigned="true" parameterType="unsigned" fieldType="unsigned"/>
<XmlType name="ubyte" javaType="byte" unsigned="true" parameterType="u1" fieldType="u1"/> <XmlType name="ushort" javaType="short" unsigned="true" parameterType="u2" fieldType="u2"/>
<XmlType name="long" javaType="long" parameterType="s8" fieldType="s8"/> <XmlType name="ubyte" javaType="byte" unsigned="true" parameterType="u1" fieldType="u1"/>
<XmlType name="int" javaType="int" parameterType="s4" fieldType="s4"/> <XmlType name="long" javaType="long" parameterType="s8" fieldType="s8"/>
<XmlType name="short" javaType="short" parameterType="s2" fieldType="s2"/> <XmlType name="int" javaType="int" parameterType="s4" fieldType="s4"/>
<XmlType name="byte" javaType="byte" parameterType="s1" fieldType="s1"/> <XmlType name="short" javaType="short" parameterType="s2" fieldType="s2"/>
<XmlType name="double" javaType="double" parameterType="double" fieldType="double"/> <XmlType name="byte" javaType="byte" parameterType="s1" fieldType="s1"/>
<XmlType name="float" javaType="float" parameterType="float" fieldType="float"/> <XmlType name="double" javaType="double" parameterType="double" fieldType="double"/>
<XmlType name="boolean" javaType="boolean" parameterType="bool" fieldType="bool"/> <XmlType name="float" javaType="float" parameterType="float" fieldType="float"/>
<XmlType name="char" javaType="char" parameterType="char" fieldType="char"/> <XmlType name="boolean" javaType="boolean" parameterType="bool" fieldType="bool"/>
<XmlType name="string" javaType="java.lang.String" parameterType="const char*" fieldType="const char*"/> <XmlType name="char" javaType="char" parameterType="char" fieldType="char"/>
<XmlType name="string" javaType="java.lang.String" parameterType="const char*" fieldType="const char*"/>
<XmlContentType name="bytes" annotation="jdk.jfr.DataAmount(BYTES)" /> <XmlContentType name="bytes" annotation="jdk.jfr.DataAmount(BYTES)" />
<XmlContentType name="tickstamp" annotation="jdk.jfr.Timestamp(TICKS)" /> <XmlContentType name="tickstamp" annotation="jdk.jfr.Timestamp(TICKS)" />
<XmlContentType name="epochmillis" annotation="jdk.jfr.Timestamp(MILLISECONDS_SINCE_EPOCH)" /> <XmlContentType name="epochmillis" annotation="jdk.jfr.Timestamp(MILLISECONDS_SINCE_EPOCH)" />
...@@ -1151,5 +1193,5 @@ ...@@ -1151,5 +1193,5 @@
<XmlContentType name="hertz" annotation="jdk.jfr.Frequency" /> <XmlContentType name="hertz" annotation="jdk.jfr.Frequency" />
<XmlContentType name="bytes-per-second" annotation="jdk.jfr.DataAmount(BYTES), jdk.jfr.Frequency" /> <XmlContentType name="bytes-per-second" annotation="jdk.jfr.DataAmount(BYTES), jdk.jfr.Frequency" />
<XmlContentType name="bits-per-second" annotation="jdk.jfr.DataAmount(BITS), jdk.jfr.Frequency" /> <XmlContentType name="bits-per-second" annotation="jdk.jfr.DataAmount(BITS), jdk.jfr.Frequency" />
</Metadata> </Metadata>
/* /*
* 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
...@@ -38,7 +38,7 @@ struct InterfaceEntry { ...@@ -38,7 +38,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;
...@@ -70,7 +70,7 @@ static InterfaceEntry& new_entry(const NetworkInterface* iface, GrowableArray<In ...@@ -70,7 +70,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));
} }
...@@ -107,46 +107,46 @@ static uint64_t rate_per_second(uint64_t current, uint64_t old, const JfrTickspa ...@@ -107,46 +107,46 @@ 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();
} }
static bool get_interfaces(NetworkInterface** network_interfaces) {
const int ret_val = JfrOSInterface::network_utilization(network_interfaces);
if (ret_val == OS_ERR) {
if (LogJFR) tty->print_cr("Unable to generate network utilization events");
return false;
}
return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED;
}
class JfrNetworkInterfaceName : public JfrSerializer { class JfrNetworkInterfaceName : public JfrSerializer {
public: public:
void serialize(JfrCheckpointWriter& writer) { void serialize(JfrCheckpointWriter& writer) {} // we write each constant lazily
assert(_interfaces != NULL, "invariant");
const JfrCheckpointContext ctx = writer.context(); void on_rotation() {
const intptr_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet for (int i = 0; i < _interfaces->length(); ++i) {
int active_interfaces = 0; const InterfaceEntry& entry = _interfaces->at(i);
for (int i = 0; i < _interfaces->length(); ++i) { if (entry.written) {
InterfaceEntry& entry = _interfaces->at(i); entry.written = false;
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() { static bool register_network_interface_name_serializer() {
assert(_interfaces != NULL, "invariant"); assert(_interfaces != NULL, "invariant");
return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME, return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME,
false, // require safepoint false, // disallow caching; we want a callback every rotation
false, // disallow caching; we want a callback every rotation new JfrNetworkInterfaceName());
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) {
const int ret_val = JfrOSInterface::network_utilization(network_interfaces);
if (ret_val == OS_ERR) {
if (LogJFR) tty->print_cr("Unable to generate network utilization events");
return false;
}
return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED;
} }
void JfrNetworkUtilization::send_events() { void JfrNetworkUtilization::send_events() {
...@@ -168,7 +168,7 @@ void JfrNetworkUtilization::send_events() { ...@@ -168,7 +168,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);
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,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 "memory/heapInspection.hpp" #include "memory/heapInspection.hpp"
...@@ -393,13 +394,12 @@ TRACE_REQUEST_FUNC(ThreadAllocationStatistics) { ...@@ -393,13 +394,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()) {
MutexLockerEx ml(Threads_lock); JavaThread* const jt = iter.next();
for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { assert(jt != NULL, "invariant");
allocated.append(thread->cooked_allocated_bytes()); allocated.append(jt->cooked_allocated_bytes());
thread_ids.append(JFR_THREAD_ID(thread)); thread_ids.append(JFR_THREAD_ID(jt));
}
} }
// Write allocation statistics to buffer. // Write allocation statistics to buffer.
......
/* /*
* 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
......
...@@ -30,6 +30,7 @@ ...@@ -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 "runtime/frame.inline.hpp" #include "runtime/frame.inline.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
...@@ -353,9 +354,14 @@ static void clear_transition_block(JavaThread* jt) { ...@@ -353,9 +354,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,10 +37,13 @@ ...@@ -37,10 +37,13 @@
#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 "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.inline.hpp" #include "runtime/orderAccess.inline.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "runtime/safepoint.hpp" #include "runtime/safepoint.hpp"
...@@ -167,7 +170,7 @@ static BufferPtr lease_free(size_t size, JfrCheckpointMspace* mspace, size_t ret ...@@ -167,7 +170,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((u1*)&_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;
...@@ -180,12 +183,24 @@ BufferPtr JfrCheckpointManager::lease_buffer(Thread* thread, size_t size /* 0 */ ...@@ -180,12 +183,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();
...@@ -201,7 +216,7 @@ BufferPtr JfrCheckpointManager::flush(BufferPtr old, size_t used, size_t request ...@@ -201,7 +216,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);
} }
...@@ -212,8 +227,8 @@ BufferPtr JfrCheckpointManager::flush(BufferPtr old, size_t used, size_t request ...@@ -212,8 +227,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>
...@@ -233,21 +248,21 @@ static jlong duration(const u1* data) { ...@@ -233,21 +248,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));
} }
...@@ -260,9 +275,9 @@ static size_t write_checkpoint_event(JfrChunkWriter& cw, const u1* data) { ...@@ -260,9 +275,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);
...@@ -333,6 +348,16 @@ size_t JfrCheckpointManager::write_epoch_transition_mspace() { ...@@ -333,6 +348,16 @@ size_t JfrCheckpointManager::write_epoch_transition_mspace() {
return write_mspace_exclusive(_epoch_transition_mspace, _chunkwriter); return write_mspace_exclusive(_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,45 +365,121 @@ size_t JfrCheckpointManager::clear() { ...@@ -340,45 +365,121 @@ 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::flush_static_type_set() {
flush();
}
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());
}
}
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()) {
// TODO: CLDG lock does not exist in 11 - what shall we do?
// MutexLocker cld_lock(ClassLoaderDataGraph_lock);
MutexLocker package_table_lock(PackageTable_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);
MutexLocker package_table_lock(PackageTable_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);
MutexLocker package_table_lock(PackageTable_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(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
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);
} }
...@@ -388,16 +489,20 @@ void JfrCheckpointManager::write_type_set_for_unloaded_classes() { ...@@ -388,16 +489,20 @@ 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) {
JfrTypeManager::write_thread_checkpoint(jt);
} }
void JfrCheckpointManager::shift_epoch() { size_t JfrCheckpointManager::flush_type_set() {
debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();) assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
JfrTraceIdEpoch::shift_epoch(); size_t elements = 0;
assert(current_epoch != JfrTraceIdEpoch::current(), "invariant"); {
JfrCheckpointWriter writer(Thread::current());
// can safepoint here
// MutexLocker cld_lock(ClassLoaderDataGraph_lock);
MutexLocker package_table_lock(PackageTable_lock);
elements = JfrTypeSet::serialize(&writer, NULL, false, true);
}
flush();
return elements;
} }
...@@ -68,18 +68,29 @@ class JfrCheckpointManager : public JfrCHeapObj { ...@@ -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 { ...@@ -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 @@ ...@@ -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 ...@@ -45,13 +59,27 @@ JfrCheckpointWriter::JfrCheckpointWriter(bool flushpoint, bool header, Thread* t
} }
} }
static void write_checkpoint_header(u1* pos, jlong size, jlong time, bool flushpoint, juint 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 ? (juint)1 : (juint)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() { ...@@ -74,18 +102,10 @@ JfrCheckpointWriter::~JfrCheckpointWriter() {
assert(this->used_size() > sizeof(JfrCheckpointEntry), "invariant"); assert(this->used_size() > sizeof(JfrCheckpointEntry), "invariant");
const jlong size = this->current_offset(); const jlong 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;
}
juint JfrCheckpointWriter::count() const { juint JfrCheckpointWriter::count() const {
return _count; return _count;
} }
...@@ -140,7 +160,7 @@ const u1* JfrCheckpointWriter::session_data(size_t* size, bool move /* false */, ...@@ -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 { ...@@ -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;
jlong _offset; jlong _offset;
juint _count; juint _count;
bool _flushpoint; JfrCheckpointType _type;
bool _header; bool _header;
juint count() const; juint count() const;
void set_count(juint count); void set_count(juint 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);
......
/* /*
* 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 @@ ...@@ -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.hpp" #include "oops/typeArrayOop.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->oop_is_array(), "invariant");
const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast((Klass*)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();
Klass* const k = arr->klass();
assert(k != NULL && k->oop_is_array(), "invariant");
const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast(k);
const jbyte* const data_address = arr->byte_at_addr(0);
chunkwriter.write_unbuffered(data_address, length);
}
}
// the semaphore is assumed to be locked (was locked previous safepoint)
size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter, jlong metadata_offset) {
assert(chunkwriter.is_valid(), "invariant"); assert(chunkwriter.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; ...@@ -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 @@ ...@@ -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.hpp" #include "runtime/jniHandles.hpp"
...@@ -33,6 +32,8 @@ ...@@ -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 ...@@ -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) { ...@@ -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* jt) { traceid JfrThreadGroup::thread_group_id(JavaThread* jt) {
assert(!JfrStream_lock->owned_by_self(), "holding stream lock but should not hold it here");
return thread_group_id(jt, jt); return thread_group_id(jt, jt);
} }
...@@ -398,9 +394,7 @@ void JfrThreadGroup::serialize(JfrCheckpointWriter& writer) { ...@@ -398,9 +394,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
......
/* /*
* 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 @@ ...@@ -23,9 +23,13 @@
*/ */
#include "precompiled.hpp" #include "precompiled.hpp"
#include "classfile/javaClasses.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) { ...@@ -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 @@ ...@@ -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_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP #endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP
...@@ -31,13 +31,14 @@ ...@@ -31,13 +31,14 @@
#include "gc_implementation/shared/gcTrace.hpp" #include "gc_implementation/shared/gcTrace.hpp"
#include "gc_implementation/shared/gcWhen.hpp" #include "gc_implementation/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"
...@@ -86,27 +87,18 @@ class JfrCheckpointThreadClosure : public ThreadClosure { ...@@ -86,27 +87,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
...@@ -115,13 +107,18 @@ void JfrCheckpointThreadClosure::do_thread(Thread* t) { ...@@ -115,13 +107,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);
} }
...@@ -298,19 +295,21 @@ void ThreadStateConstant::serialize(JfrCheckpointWriter& writer) { ...@@ -298,19 +295,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 @@ ...@@ -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);
...@@ -112,6 +102,16 @@ class VMOperationTypeConstant : public JfrSerializer { ...@@ -112,6 +102,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);
...@@ -119,9 +119,9 @@ class ThreadStateConstant : public JfrSerializer { ...@@ -119,9 +119,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 @@ ...@@ -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 { ...@@ -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); ...@@ -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() { ...@@ -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,22 +186,23 @@ static void assert_not_registered_twice(JfrTypeId id, List& list) { ...@@ -181,22 +186,23 @@ 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;
} }
...@@ -204,31 +210,46 @@ bool JfrTypeManager::initialize() { ...@@ -204,31 +210,46 @@ bool JfrTypeManager::initialize() {
SerializerRegistrationGuard guard; SerializerRegistrationGuard guard;
// register non-safepointing type serialization // register non-safepointing type serialization
register_type(TYPE_FLAGVALUEORIGIN, false, true, new FlagValueOriginConstant()); register_static_type(TYPE_FLAGVALUEORIGIN, true, new FlagValueOriginConstant());
register_type(TYPE_INFLATECAUSE, false, true, new MonitorInflateCauseConstant()); register_static_type(TYPE_INFLATECAUSE, true, new MonitorInflateCauseConstant());
register_type(TYPE_GCCAUSE, false, true, new GCCauseConstant()); register_static_type(TYPE_GCCAUSE, true, new GCCauseConstant());
register_type(TYPE_GCNAME, false, true, new GCNameConstant()); register_static_type(TYPE_GCNAME, true, new GCNameConstant());
register_type(TYPE_GCWHEN, false, true, new GCWhenConstant()); register_static_type(TYPE_GCWHEN, true, new GCWhenConstant());
register_type(TYPE_G1HEAPREGIONTYPE, false, true, new G1HeapRegionTypeConstant()); register_static_type(TYPE_G1HEAPREGIONTYPE, true, new G1HeapRegionTypeConstant());
register_type(TYPE_GCTHRESHOLDUPDATER, false, true, new GCThresholdUpdaterConstant()); register_static_type(TYPE_GCTHRESHOLDUPDATER, true, new GCThresholdUpdaterConstant());
register_type(TYPE_METADATATYPE, false, true, new MetadataTypeConstant()); register_static_type(TYPE_METADATATYPE, true, new MetadataTypeConstant());
register_type(TYPE_METASPACEOBJECTTYPE, false, true, new MetaspaceObjectTypeConstant()); register_static_type(TYPE_METASPACEOBJECTTYPE, true, new MetaspaceObjectTypeConstant());
register_type(TYPE_G1YCTYPE, false, true, new G1YCTypeConstant()); register_static_type(TYPE_G1YCTYPE, true, new G1YCTypeConstant());
register_type(TYPE_REFERENCETYPE, false, true, new ReferenceTypeConstant()); register_static_type(TYPE_REFERENCETYPE, true, new ReferenceTypeConstant());
register_type(TYPE_NARROWOOPMODE, false, true, new NarrowOopModeConstant()); register_static_type(TYPE_NARROWOOPMODE, true, new NarrowOopModeConstant());
register_type(TYPE_COMPILERPHASETYPE, false, true, new CompilerPhaseTypeConstant()); register_static_type(TYPE_COMPILERPHASETYPE, true, new CompilerPhaseTypeConstant());
register_type(TYPE_CODEBLOBTYPE, false, true, new CodeBlobTypeConstant()); register_static_type(TYPE_CODEBLOBTYPE, true, new CodeBlobTypeConstant());
register_type(TYPE_VMOPERATIONTYPE, false, true, new VMOperationTypeConstant()); register_static_type(TYPE_VMOPERATIONTYPE, true, new VMOperationTypeConstant());
register_type(TYPE_THREADSTATE, false, true, new ThreadStateConstant()); register_static_type(TYPE_THREADSTATE, 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 { ...@@ -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_VM_JFR_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP #endif // SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP
...@@ -72,7 +72,7 @@ static traceid create_symbol_id(traceid artifact_id) { ...@@ -72,7 +72,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() {
...@@ -109,14 +109,14 @@ inline uintptr_t package_name_hash(const char *s) { ...@@ -109,14 +109,14 @@ inline uintptr_t package_name_hash(const char *s) {
return val; return val;
} }
static traceid package_id(KlassPtr klass, JfrArtifactSet* artifacts) { static traceid package_id(KlassPtr klass, JfrArtifactSet* artifacts, bool leakp) {
assert(klass != NULL, "invariant"); assert(klass != NULL, "invariant");
char* klass_name = klass->name()->as_C_string(); // uses ResourceMark declared in JfrTypeSet::serialize() char* klass_name = klass->name()->as_C_string(); // uses ResourceMark declared in JfrTypeSet::serialize()
const char* pkg_name = ClassLoader::package_from_name(klass_name, NULL); const char* pkg_name = ClassLoader::package_from_name(klass_name, NULL);
if (pkg_name == NULL) { if (pkg_name == NULL) {
return 0; return 0;
} }
return CREATE_PACKAGE_ID(artifacts->markPackage(pkg_name, package_name_hash(pkg_name))); return CREATE_PACKAGE_ID(artifacts->markPackage(pkg_name, package_name_hash(pkg_name), leakp));
} }
static traceid method_id(KlassPtr klass, MethodPtr method) { static traceid method_id(KlassPtr klass, MethodPtr method) {
...@@ -178,7 +178,7 @@ static int write_klass(JfrCheckpointWriter* writer, KlassPtr klass, bool leakp) ...@@ -178,7 +178,7 @@ static int write_klass(JfrCheckpointWriter* writer, KlassPtr klass, bool leakp)
theklass = obj_arr_klass->bottom_klass(); theklass = obj_arr_klass->bottom_klass();
} }
if (theklass->oop_is_instance()) { if (theklass->oop_is_instance()) {
pkg_id = package_id(theklass, _artifacts); pkg_id = package_id(theklass, _artifacts, leakp);
} else { } else {
assert(theklass->oop_is_typeArray(), "invariant"); assert(theklass->oop_is_typeArray(), "invariant");
} }
...@@ -245,7 +245,7 @@ static void do_unloaded_klass(Klass* klass) { ...@@ -245,7 +245,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;
...@@ -316,7 +316,14 @@ static bool write_klasses() { ...@@ -316,7 +316,14 @@ static bool write_klasses() {
return true; return true;
} }
int write__artifact__package(JfrCheckpointWriter* writer, const void* p) { template <>
void set_serialized<JfrSymbolId::CStringEntry>(CStringEntryPtr ptr) {
assert(ptr != NULL, "invariant");
ptr->set_serialized();
assert(ptr->is_serialized(), "invariant");
}
int write__package(JfrCheckpointWriter* writer, const void* p) {
assert(writer != NULL, "invariant"); assert(writer != NULL, "invariant");
assert(_artifacts != NULL, "invariant"); assert(_artifacts != NULL, "invariant");
assert(p != NULL, "invariant"); assert(p != NULL, "invariant");
...@@ -327,17 +334,45 @@ int write__artifact__package(JfrCheckpointWriter* writer, const void* p) { ...@@ -327,17 +334,45 @@ int write__artifact__package(JfrCheckpointWriter* writer, const void* p) {
writer->write((traceid)CREATE_PACKAGE_ID(entry->id())); writer->write((traceid)CREATE_PACKAGE_ID(entry->id()));
writer->write((traceid)CREATE_SYMBOL_ID(package_name_symbol_id)); writer->write((traceid)CREATE_SYMBOL_ID(package_name_symbol_id));
writer->write((bool)true); // exported writer->write((bool)true); // exported
set_serialized(entry);
return 1;
}
int write__package__leakp(JfrCheckpointWriter* writer, const void* p) {
assert(writer != NULL, "invariant");
assert(_artifacts != NULL, "invariant");
assert(p != NULL, "invariant");
CStringEntryPtr entry = (CStringEntryPtr)p;
const traceid package_name_symbol_id = _artifacts->mark(package_name_hash(entry->value()), entry->value(), true);
assert(package_name_symbol_id > 0, "invariant");
writer->write((traceid)CREATE_PACKAGE_ID(entry->id()));
writer->write((traceid)CREATE_SYMBOL_ID(package_name_symbol_id));
writer->write((bool)true); // exported
return 1; return 1;
} }
typedef JfrTypeWriterImplHost<CStringEntryPtr, write__artifact__package> PackageEntryWriterImpl; typedef SymbolPredicate<CStringEntryPtr, false> PackageCStringPredicate;
typedef JfrTypeWriterHost<PackageEntryWriterImpl, TYPE_PACKAGE> PackageEntryWriter; typedef JfrPredicatedTypeWriterImplHost<CStringEntryPtr, PackageCStringPredicate, write__package> PackageCStringEntryWriterImpl;
typedef JfrTypeWriterHost<PackageCStringEntryWriterImpl, TYPE_PACKAGE> PackageCStringEntryWriter;
typedef SymbolPredicate<CStringEntryPtr, true> LeakPackageCStringPredicate;
typedef JfrPredicatedTypeWriterImplHost<CStringEntryPtr, LeakPackageCStringPredicate, write__package__leakp> LeakPackageCStringEntryWriterImpl;
typedef JfrTypeWriterHost<LeakPackageCStringEntryWriterImpl, TYPE_PACKAGE> LeakPackageCStringEntryWriter;
typedef CompositeFunctor<CStringEntryPtr, LeakPackageCStringEntryWriter, PackageCStringEntryWriter> CompositePackageCStringWriter;
void write_packages() { void write_packages() {
// below jdk9 there is no oop for packages, so nothing to do with leakp_writer if (_leakp_writer != NULL) {
// just write packages PackageCStringEntryWriter pcsw(_writer, _class_unload);
PackageEntryWriter pw(_writer, _class_unload); LeakPackageCStringEntryWriter lpcsw(_leakp_writer, _class_unload);
_artifacts->iterate_packages(pw); CompositePackageCStringWriter cpcsw(&lpcsw, &pcsw);
_artifacts->iterate_packages(cpcsw);
_artifacts->tally(pcsw);
} else {
PackageCStringEntryWriter pcsw(_writer, _class_unload);
_artifacts->iterate_packages(pcsw);
_artifacts->tally(pcsw);
}
} }
template <typename T> template <typename T>
...@@ -636,13 +671,6 @@ void set_serialized<JfrSymbolId::SymbolEntry>(SymbolEntryPtr ptr) { ...@@ -636,13 +671,6 @@ void set_serialized<JfrSymbolId::SymbolEntry>(SymbolEntryPtr ptr) {
assert(ptr->is_serialized(), "invariant"); assert(ptr->is_serialized(), "invariant");
} }
template <>
void set_serialized<JfrSymbolId::CStringEntry>(CStringEntryPtr ptr) {
assert(ptr != NULL, "invariant");
ptr->set_serialized();
assert(ptr->is_serialized(), "invariant");
}
static int write_symbol(JfrCheckpointWriter* writer, SymbolEntryPtr entry, bool leakp) { static int write_symbol(JfrCheckpointWriter* writer, SymbolEntryPtr entry, bool leakp) {
assert(writer != NULL, "invariant"); assert(writer != NULL, "invariant");
assert(entry != NULL, "invariant"); assert(entry != NULL, "invariant");
...@@ -754,10 +782,11 @@ static size_t teardown() { ...@@ -754,10 +782,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 {
...@@ -771,10 +800,10 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer ...@@ -771,10 +800,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; ...@@ -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_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESET_HPP #endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESET_HPP
...@@ -175,7 +175,7 @@ traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) { ...@@ -175,7 +175,7 @@ traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) {
return entry.id(); return entry.id();
} }
traceid JfrSymbolId::markPackage(const char* name, uintptr_t hash) { traceid JfrSymbolId::markPackage(const char* name, uintptr_t hash, bool leakp) {
assert(name != NULL, "invariant"); assert(name != NULL, "invariant");
assert(_pkg_table != NULL, "invariant"); assert(_pkg_table != NULL, "invariant");
_cstring_query = name; _cstring_query = name;
...@@ -183,6 +183,9 @@ traceid JfrSymbolId::markPackage(const char* name, uintptr_t hash) { ...@@ -183,6 +183,9 @@ traceid JfrSymbolId::markPackage(const char* name, uintptr_t hash) {
if (_class_unload) { if (_class_unload) {
entry.set_unloading(); entry.set_unloading();
} }
if (leakp) {
entry.set_leakp();
}
return entry.id(); return entry.id();
} }
...@@ -308,8 +311,8 @@ traceid JfrArtifactSet::mark(const Klass* klass, bool leakp) { ...@@ -308,8 +311,8 @@ traceid JfrArtifactSet::mark(const Klass* klass, bool leakp) {
return _symbol_id->mark(klass, leakp); return _symbol_id->mark(klass, leakp);
} }
traceid JfrArtifactSet::markPackage(const char* const name, uintptr_t hash) { traceid JfrArtifactSet::markPackage(const char* const name, uintptr_t hash, bool leakp) {
return _symbol_id->markPackage(name, hash); return _symbol_id->markPackage(name, hash, leakp);
} }
traceid JfrArtifactSet::mark(const Symbol* symbol, bool leakp) { traceid JfrArtifactSet::mark(const Symbol* symbol, bool leakp) {
......
...@@ -256,7 +256,7 @@ class JfrSymbolId : public JfrCHeapObj { ...@@ -256,7 +256,7 @@ class JfrSymbolId : public JfrCHeapObj {
void clear(); void clear();
void set_class_unload(bool class_unload); void set_class_unload(bool class_unload);
traceid markPackage(const char* name, uintptr_t hash); traceid markPackage(const char* name, uintptr_t hash, bool leakp);
template <typename T> template <typename T>
void iterate_packages(T& functor) { void iterate_packages(T& functor) {
...@@ -318,7 +318,7 @@ class JfrArtifactSet : public JfrCHeapObj { ...@@ -318,7 +318,7 @@ class JfrArtifactSet : public JfrCHeapObj {
traceid mark_unsafe_anonymous_klass_name(const Klass* klass, bool leakp); traceid mark_unsafe_anonymous_klass_name(const Klass* klass, bool leakp);
traceid bootstrap_name(bool leakp); traceid bootstrap_name(bool leakp);
traceid markPackage(const char* const name, uintptr_t hash); traceid markPackage(const char* const name, uintptr_t hash, bool leakp);
const JfrSymbolId::SymbolEntry* map_symbol(const Symbol* symbol) const; const JfrSymbolId::SymbolEntry* map_symbol(const Symbol* symbol) const;
const JfrSymbolId::SymbolEntry* map_symbol(uintptr_t hash) const; const JfrSymbolId::SymbolEntry* map_symbol(uintptr_t hash) const;
......
...@@ -33,7 +33,6 @@ ...@@ -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.inline.hpp" #include "runtime/atomic.inline.hpp"
#include "runtime/orderAccess.inline.hpp"
#include "runtime/vm_version.hpp" #include "runtime/vm_version.hpp"
#include "runtime/jniHandles.hpp" #include "runtime/jniHandles.hpp"
#include "runtime/thread.inline.hpp" #include "runtime/thread.inline.hpp"
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,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"
...@@ -58,7 +57,14 @@ inline traceid JfrTraceId::get(const Thread* t) { ...@@ -58,7 +57,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) {
...@@ -69,10 +75,16 @@ inline traceid JfrTraceId::use(const Method* method) { ...@@ -69,10 +75,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));
} }
......
/* /*
* 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 @@ ...@@ -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 @@ ...@@ -25,18 +25,19 @@
#ifndef SHARE_VM_JFR_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP #ifndef SHARE_VM_JFR_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
#define SHARE_VM_JFR_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP #define SHARE_VM_JFR_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 { ...@@ -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 { ...@@ -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 ((bool)OrderAccess::load_acquire((volatile jubyte*)&_tag_state)) {
OrderAccess::release_store((volatile jubyte*)&_tag_state, (jubyte)false);
return true;
}
return false;
}
static void set_changed_tag_state() {
if (!(bool)OrderAccess::load_acquire((volatile jubyte*)&_tag_state)) {
OrderAccess::release_store((volatile jubyte*)&_tag_state, (jubyte)true);
}
}
}; };
#endif // SHARE_VM_JFR_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP #endif // SHARE_VM_JFR_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
...@@ -23,75 +23,142 @@ ...@@ -23,75 +23,142 @@
*/ */
#include "precompiled.hpp" #include "precompiled.hpp"
#include "jfr/dcmd/jfrDcmds.hpp" #include "jfr/recorder/repository/jfrChunk.hpp"
#include "jfr/recorder/jfrRecorder.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/repository/jfrChunkState.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/utilities/jfrTime.hpp" #include "jfr/utilities/jfrTime.hpp"
#include "jfr/utilities/jfrTimeConverter.hpp" #include "jfr/utilities/jfrTimeConverter.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "runtime/thread.inline.hpp"
JfrChunkState::JfrChunkState() : 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), _path(NULL),
_start_ticks(0), _start_ticks(0),
_previous_start_ticks(invalid_time),
_start_nanos(0), _start_nanos(0),
_previous_start_ticks(0), _previous_start_nanos(invalid_time),
_previous_start_nanos(0), _last_update_nanos(0),
_last_checkpoint_offset(0) {} _last_checkpoint_offset(0),
_last_metadata_offset(0),
_generation(1) {}
JfrChunkState::~JfrChunkState() { JfrChunk::~JfrChunk() {
reset(); reset();
} }
void JfrChunkState::reset() { void JfrChunk::reset() {
if (_path != NULL) { if (_path != NULL) {
JfrCHeapObj::free(_path, strlen(_path) + 1); JfrCHeapObj::free(_path, strlen(_path) + 1);
_path = NULL; _path = NULL;
} }
set_last_checkpoint_offset(0); _last_checkpoint_offset = _last_metadata_offset = 0;
_generation = 1;
} }
void JfrChunkState::set_last_checkpoint_offset(int64_t offset) { 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; _last_checkpoint_offset = offset;
} }
int64_t JfrChunkState::last_checkpoint_offset() const { int64_t JfrChunk::last_checkpoint_offset() const {
return _last_checkpoint_offset; return _last_checkpoint_offset;
} }
jlong JfrChunkState::previous_start_ticks() const { 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; return _previous_start_ticks;
} }
jlong JfrChunkState::previous_start_nanos() const { int64_t JfrChunk::previous_start_nanos() const {
assert(_previous_start_nanos != invalid_time, "invariant");
return _previous_start_nanos; return _previous_start_nanos;
} }
void JfrChunkState::update_start_ticks() { void JfrChunk::update_start_ticks() {
_start_ticks = JfrTicks::now(); _start_ticks = ticks_now();
} }
void JfrChunkState::update_start_nanos() { void JfrChunk::update_start_nanos() {
_start_nanos = (jlong)(os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC); const jlong now = nanos_now();
assert(now > _start_nanos, "invariant");
assert(now > _last_update_nanos, "invariant");
_start_nanos = _last_update_nanos = now;
} }
void JfrChunkState::save_current_and_update_start_ticks() { 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; _previous_start_ticks = _start_ticks;
update_start_ticks(); update_start_ticks();
} }
void JfrChunkState::save_current_and_update_start_nanos() { void JfrChunk::save_current_and_update_start_nanos() {
_previous_start_nanos = _start_nanos; _previous_start_nanos = _start_nanos;
update_start_nanos(); update_start_nanos();
} }
void JfrChunkState::update_time_to_now() { void JfrChunk::set_time_stamp() {
save_current_and_update_start_nanos(); save_current_and_update_start_nanos();
save_current_and_update_start_ticks(); save_current_and_update_start_ticks();
} }
jlong JfrChunkState::last_chunk_duration() const { int64_t JfrChunk::last_chunk_duration() const {
assert(_previous_start_nanos != invalid_time, "invariant");
return _start_nanos - _previous_start_nanos; return _start_nanos - _previous_start_nanos;
} }
...@@ -103,8 +170,7 @@ static char* copy_path(const char* path) { ...@@ -103,8 +170,7 @@ static char* copy_path(const char* path) {
return new_path; return new_path;
} }
void JfrChunkState::set_path(const char* path) { void JfrChunk::set_path(const char* path) {
assert(JfrStream_lock->owned_by_self(), "invariant");
if (_path != NULL) { if (_path != NULL) {
JfrCHeapObj::free(_path, strlen(_path) + 1); JfrCHeapObj::free(_path, strlen(_path) + 1);
_path = NULL; _path = NULL;
...@@ -114,6 +180,47 @@ void JfrChunkState::set_path(const char* path) { ...@@ -114,6 +180,47 @@ void JfrChunkState::set_path(const char* path) {
} }
} }
const char* JfrChunkState::path() const { const char* JfrChunk::path() const {
return _path; 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;
}
/* /*
* 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
...@@ -22,39 +22,70 @@ ...@@ -22,39 +22,70 @@
* *
*/ */
#ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNKSTATE_HPP #ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
#define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNKSTATE_HPP #define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
#include "jni.h"
#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;
jlong _start_ticks; int64_t _start_ticks;
jlong _start_nanos; int64_t _previous_start_ticks;
jlong _previous_start_ticks; int64_t _start_nanos;
jlong _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);
jlong previous_start_ticks() const;
jlong previous_start_nanos() const; int64_t last_metadata_offset() const;
jlong last_chunk_duration() const; void set_last_metadata_offset(int64_t offset);
void update_time_to_now(); bool has_metadata() const;
int64_t start_ticks() const;
int64_t start_nanos() const;
int64_t previous_start_ticks() const;
int64_t previous_start_nanos() const;
int64_t last_chunk_duration() const;
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_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNKSTATE_HPP #endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
...@@ -23,83 +23,219 @@ ...@@ -23,83 +23,219 @@
*/ */
#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.hpp"
#include "runtime/os.hpp"
const u2 JFR_VERSION_MAJOR = 2;
const u2 JFR_VERSION_MINOR = 0;
static const size_t MAGIC_LEN = 4;
static const size_t FILEHEADER_SLOT_SIZE = 8;
static const size_t CHUNK_SIZE_OFFSET = 8;
JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunkstate(NULL) {}
bool JfrChunkWriter::initialize() { static const int64_t MAGIC_OFFSET = 0;
assert(_chunkstate == NULL, "invariant"); static const int64_t MAGIC_LEN = 4;
_chunkstate = new JfrChunkState(); static const int64_t VERSION_OFFSET = MAGIC_LEN;
return _chunkstate != NULL; static const int64_t SIZE_OFFSET = 8;
} static const int64_t SLOT_SIZE = 8;
static const int64_t CHECKPOINT_OFFSET = SIZE_OFFSET + SLOT_SIZE;
static const int64_t METADATA_OFFSET = CHECKPOINT_OFFSET + SLOT_SIZE;
static const int64_t START_NANOS_OFFSET = METADATA_OFFSET + SLOT_SIZE;
static const int64_t DURATION_NANOS_OFFSET = START_NANOS_OFFSET + SLOT_SIZE;
static const int64_t START_TICKS_OFFSET = DURATION_NANOS_OFFSET + SLOT_SIZE;
static const int64_t CPU_FREQUENCY_OFFSET = START_TICKS_OFFSET + SLOT_SIZE;
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"); 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(intptr_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_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(intptr_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((jlong)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((jlong)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();
} }
intptr_t JfrChunkWriter::size_written() const { intptr_t JfrChunkWriter::size_written() const {
...@@ -107,13 +243,46 @@ intptr_t JfrChunkWriter::size_written() const { ...@@ -107,13 +243,46 @@ intptr_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::set_last_metadata_offset(int64_t offset) {
assert(_chunk != NULL, "invariant");
_chunk->set_last_metadata_offset(offset);
}
bool JfrChunkWriter::has_metadata() const {
assert(_chunk != NULL, "invariant");
return _chunk->has_metadata();
} }
void JfrChunkWriter::time_stamp_chunk_now() { bool JfrChunkWriter::open() {
_chunkstate->update_time_to_now(); 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 @@ ...@@ -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(intptr_t metadata_offset); int64_t close();
void write_header(intptr_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();
intptr_t size_written() const; intptr_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_VM_JFR_RECORDER_REPOSITORY_JFRCHUNKWRITER_HPP #endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRCHUNKWRITER_HPP
...@@ -246,7 +246,6 @@ static void write_emergency_file(fio_fd emergency_fd, const RepositoryIterator& ...@@ -246,7 +246,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;
...@@ -288,7 +287,6 @@ static const char* create_emergency_dump_path() { ...@@ -288,7 +287,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 };
...@@ -304,12 +302,11 @@ static const char* create_emergency_chunk_path(const char* repository_path) { ...@@ -304,12 +302,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;
...@@ -322,7 +319,6 @@ const char* JfrEmergencyDump::build_dump_path(const char* repository_path) { ...@@ -322,7 +319,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;
MutexLockerEx 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));
...@@ -337,17 +333,25 @@ void JfrEmergencyDump::on_vm_error(const char* repository_path) { ...@@ -337,17 +333,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);
} }
...@@ -386,7 +390,6 @@ static void prepare_for_emergency_dump(Thread* thread) { ...@@ -386,7 +390,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();
} }
...@@ -407,13 +410,10 @@ static void prepare_for_emergency_dump(Thread* thread) { ...@@ -407,13 +410,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;
...@@ -423,24 +423,9 @@ static bool guard_reentrancy() { ...@@ -423,24 +423,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");
...@@ -452,8 +437,6 @@ void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) { ...@@ -452,8 +437,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,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#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"
...@@ -44,11 +43,6 @@ JfrRepository& JfrRepository::instance() { ...@@ -44,11 +43,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;
...@@ -57,7 +51,9 @@ JfrChunkWriter& JfrRepository::chunkwriter() { ...@@ -57,7 +51,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() {
...@@ -85,7 +81,6 @@ void JfrRepository::destroy() { ...@@ -85,7 +81,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;
...@@ -108,17 +103,21 @@ bool JfrRepository::set_path(const char* path) { ...@@ -108,17 +103,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.
* *
...@@ -135,14 +134,11 @@ void JfrRepository::set_chunk_path(jstring path, JavaThread* jt) { ...@@ -135,14 +134,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()) {
MutexLockerEx 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();
} }
...@@ -156,14 +152,28 @@ void JfrRepository::set_path(jstring location, JavaThread* jt) { ...@@ -156,14 +152,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(jlong 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 { ...@@ -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(jlong 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 { ...@@ -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_VM_JFR_RECORDER_REPOSITORY_JFRREPOSITORY_HPP #endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRREPOSITORY_HPP
...@@ -33,7 +33,8 @@ ...@@ -33,7 +33,8 @@
(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;
......
...@@ -41,6 +41,7 @@ enum JFR_Msg { ...@@ -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 { ...@@ -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 @@ ...@@ -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"
...@@ -54,148 +55,281 @@ ...@@ -54,148 +55,281 @@
#include "runtime/vm_operations.hpp" #include "runtime/vm_operations.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");
void* const current = OrderAccess::load_ptr_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_ptr(clear ? NULL : data, dest, current) == current;
}
static void* rotation_thread = NULL;
static const int rotation_try_limit = 1000;
static const int rotation_retry_sleep_millis = 10;
class RotationLock : public StackObj { template <typename E, typename Instance, size_t(Instance::*func)()>
class Content {
private: private:
Thread* const _thread; Instance& _instance;
bool _acquired; u4 _elements;
public:
void log(bool recursion) { typedef E EventType;
assert(!_acquired, "invariant"); Content(Instance& instance) : _instance(instance), _elements(0) {}
const char* error_msg = NULL; bool process() {
if (recursion) { _elements = (u4)(_instance.*func)();
error_msg = "Unable to issue rotation due to recursive calls."; return true;
}
else {
error_msg = "Unable to issue rotation due to wait timeout.";
}
if (LogJFR) tty->print_cr( // For user, should not be "jfr, system"
"%s", error_msg);
} }
u4 elements() const { return _elements; }
};
template <typename Content>
class WriteContent : public StackObj {
protected:
const JfrTicks _start_time;
JfrTicks _end_time;
JfrChunkWriter& _cw;
Content& _content;
const int64_t _start_offset;
public: public:
RotationLock(Thread* thread) : _thread(thread), _acquired(false) { typedef typename Content::EventType EventType;
assert(_thread != NULL, "invariant");
if (_thread == rotation_thread) {
// recursion not supported
log(true);
return;
}
// limited to not spin indefinitely WriteContent(JfrChunkWriter& cw, Content& content) :
for (int i = 0; i < rotation_try_limit; ++i) { _start_time(JfrTicks::now()),
if (try_set(_thread, &rotation_thread, false)) { _end_time(),
_acquired = true; _cw(cw),
assert(_thread == rotation_thread, "invariant"); _content(content),
return; _start_offset(_cw.current_offset()) {
} assert(_cw.is_valid(), "invariant");
if (_thread->is_Java_thread()) {
// in order to allow the system to move to a safepoint
MutexLockerEx msg_lock(JfrMsg_lock);
JfrMsg_lock->wait(false, rotation_retry_sleep_millis);
}
else {
os::naked_short_sleep(rotation_retry_sleep_millis);
}
}
log(false);
} }
~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 intptr_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) { static intptr_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) {
const int64_t last_cp_offset = cw.last_checkpoint_offset(); const int64_t last_cp_offset = cw.last_checkpoint_offset();
const int64_t delta_to_last_checkpoint = 0 == last_cp_offset ? 0 : last_cp_offset - cw.current_offset(); const int64_t delta_to_last_checkpoint = 0 == last_cp_offset ? 0 : last_cp_offset - cw.current_offset();
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>((u4)1); // nof types in this checkpoint cw.write<u4>(1); // nof types in this checkpoint
cw.write<u8>(type_id); cw.write<u8>(type_id);
const intptr_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 intptr_t current_cp_offset = _cw.current_offset(); if (!WriteContent<Content>::process()) {
const intptr_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:
typedef EventFlushStacktrace EventType;
StackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) :
_repo(repo), _cw(cw), _elements(0), _clear(clear) {}
bool process() {
_elements = _repo.write(_cw, _clear);
return true;
}
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: public:
ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {} typedef EventFlushMetadata EventType;
MetadataEvent(JfrChunkWriter& cw) : _cw(cw) {}
bool process() { bool process() {
_processed = (_instance.*func)(); JfrMetadataEvent::write(_cw);
return true; return true;
} }
size_t processed() const { return _processed; } 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:
...@@ -207,23 +341,13 @@ class JfrVMOperation : public VM_Operation { ...@@ -207,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;
...@@ -236,19 +360,8 @@ bool JfrRecorderService::is_recording() { ...@@ -236,19 +360,8 @@ bool JfrRecorderService::is_recording() {
return recording; return recording;
} }
JfrRecorderService::JfrRecorderService() :
_checkpoint_manager(JfrCheckpointManager::instance()),
_chunkwriter(JfrRepository::chunkwriter()),
_repository(JfrRepository::instance()),
_storage(JfrStorage::instance()),
_stack_trace_repository(JfrStackTraceRepository::instance()),
_string_pool(JfrStringPool::instance()) {}
void JfrRecorderService::start() { void JfrRecorderService::start() {
RotationLock rl(Thread::current()); MutexLocker lock(JfrStream_lock);
if (rl.not_acquired()) {
return;
}
if (LogJFR) tty->print_cr("Request to START recording"); if (LogJFR) tty->print_cr("Request to START recording");
assert(!is_recording(), "invariant"); assert(!is_recording(), "invariant");
clear(); clear();
...@@ -267,9 +380,9 @@ void JfrRecorderService::clear() { ...@@ -267,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() {
...@@ -277,28 +390,28 @@ void JfrRecorderService::invoke_safepoint_clear() { ...@@ -277,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");
if (LogJFR) tty->print_cr("Recording STOPPED"); if (LogJFR) tty->print_cr("Recording STOPPED");
...@@ -306,11 +419,30 @@ static void stop() { ...@@ -306,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;
...@@ -328,45 +460,19 @@ void JfrRecorderService::rotate(int msgs) { ...@@ -328,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();
MutexLockerEx 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!");
MutexLockerEx 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();
} }
...@@ -374,7 +480,6 @@ void JfrRecorderService::chunk_rotation() { ...@@ -374,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() {
...@@ -385,54 +490,20 @@ void JfrRecorderService::write() { ...@@ -385,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() {
MutexLockerEx 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() {
...@@ -440,50 +511,18 @@ void JfrRecorderService::invoke_safepoint_write() { ...@@ -440,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");
MutexLockerEx 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 jlong write_metadata_event(JfrChunkWriter& chunkwriter) {
assert(chunkwriter.is_valid(), "invariant");
const jlong 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.
...@@ -495,38 +534,85 @@ void JfrRecorderService::post_safepoint_write() { ...@@ -495,38 +534,85 @@ void JfrRecorderService::post_safepoint_write() {
// The object sampler instance was exclusively acquired and locked in pre_safepoint_write. // The object sampler instance was exclusively acquired and locked in pre_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();
} MutexLockerEx 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");
MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
_storage.write_full(); _storage.write_full();
} }
} }
......
...@@ -46,11 +46,10 @@ class JfrRecorderService : public StackObj { ...@@ -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 { ...@@ -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();
......
/* /*
* 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 @@ ...@@ -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"
...@@ -63,7 +64,6 @@ static Thread* start_thread(instanceHandle thread_oop, ThreadFunction proc, TRAP ...@@ -63,7 +64,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;
} }
...@@ -97,8 +97,9 @@ bool JfrRecorderThread::start(JfrCheckpointManager* cp_manager, JfrPostBox* post ...@@ -97,8 +97,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;
} }
......
...@@ -39,6 +39,7 @@ void recorderthread_entry(JavaThread* thread, Thread* unused) { ...@@ -39,6 +39,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)))
...@@ -71,6 +72,8 @@ void recorderthread_entry(JavaThread* thread, Thread* unused) { ...@@ -71,6 +72,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();
...@@ -89,6 +92,7 @@ void recorderthread_entry(JavaThread* thread, Thread* unused) { ...@@ -89,6 +92,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 { ...@@ -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() { ...@@ -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;
}
MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx 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) { ...@@ -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() {
MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx 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, StackWalkMode ...@@ -142,7 +132,7 @@ traceid JfrStackTraceRepository::record(Thread* thread, int skip, StackWalkMode
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();
......
...@@ -41,7 +41,7 @@ class JfrStackTraceRepository : public JfrCHeapObj { ...@@ -41,7 +41,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;
...@@ -52,19 +52,18 @@ class JfrStackTraceRepository : public JfrCHeapObj { ...@@ -52,19 +52,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, StackWalkMode mode, JfrStackFrame* frames, u4 max_frames); traceid record_for(JavaThread* thread, int skip, StackWalkMode mode, JfrStackFrame* frames, u4 max_frames);
const JfrStackTrace* lookup(unsigned int hash, traceid id) const;
public: public:
static traceid record(Thread* thread, int skip, StackWalkMode mode); static traceid record(Thread* thread, int skip, StackWalkMode mode);
......
...@@ -55,10 +55,18 @@ bool JfrBuffer::initialize(size_t header_size, size_t size, const void* id /* NU ...@@ -55,10 +55,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());
} }
...@@ -191,7 +199,8 @@ void JfrBuffer::concurrent_move_and_reinitialize(JfrBuffer* const to, size_t siz ...@@ -191,7 +199,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 {
...@@ -250,3 +259,20 @@ void JfrBuffer::clear_retired() { ...@@ -250,3 +259,20 @@ void JfrBuffer::clear_retired() {
release_store_flags(&_flags, new_flags); release_store_flags(&_flags, new_flags);
} }
} }
bool JfrBuffer::excluded() const {
return (u1)EXCLUDED == (_flags & (u1)EXCLUDED);
}
void JfrBuffer::set_excluded() {
_flags |= (u1)EXCLUDED;
assert(excluded(), "invariant");
}
void JfrBuffer::clear_excluded() {
if (excluded()) {
OrderAccess::storestore();
_flags ^= (u1)EXCLUDED;
}
assert(!excluded(), "invariant");
}
...@@ -61,7 +61,7 @@ class JfrBuffer { ...@@ -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 {
...@@ -167,6 +167,11 @@ class JfrBuffer { ...@@ -167,6 +167,11 @@ 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 {
......
...@@ -99,8 +99,8 @@ class JfrMemorySpace : public JfrCHeapObj { ...@@ -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_VM_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_HPP #endif // SHARE_VM_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_HPP
...@@ -141,6 +141,7 @@ inline void JfrMemorySpace<T, RetrievalType, Callback>::release_free(T* t) { ...@@ -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 @@ ...@@ -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"
...@@ -252,6 +253,18 @@ bool JfrStorage::flush_regular_buffer(BufferPtr buffer, Thread* thread) { ...@@ -252,6 +253,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);
...@@ -302,7 +315,7 @@ static void handle_registration_failure(BufferPtr buffer) { ...@@ -302,7 +315,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);
} }
...@@ -464,6 +477,7 @@ static void assert_flush_large_precondition(ConstBufferPtr cur, const u1* const ...@@ -464,6 +477,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");
...@@ -490,6 +504,9 @@ BufferPtr JfrStorage::flush_regular(BufferPtr cur, const u1* const cur_pos, size ...@@ -490,6 +504,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) {
...@@ -578,28 +595,40 @@ BufferPtr JfrStorage::provision_large(BufferPtr cur, const u1* const cur_pos, si ...@@ -578,28 +595,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;
...@@ -607,14 +636,14 @@ typedef ReleaseOp<JfrStorageMspace> ReleaseOperation; ...@@ -607,14 +636,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) {
...@@ -703,15 +732,25 @@ size_t JfrStorage::write_full() { ...@@ -703,15 +732,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) {
...@@ -739,6 +778,10 @@ public: ...@@ -739,6 +778,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();
...@@ -757,6 +800,11 @@ size_t JfrStorage::scavenge() { ...@@ -757,6 +800,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 { ...@@ -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 { ...@@ -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>
......
...@@ -33,7 +33,7 @@ static jlong atomic_add(size_t value, size_t volatile* const dest) { ...@@ -33,7 +33,7 @@ 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_ptr_acquire((intptr_t*)dest); compare_value = *dest;
exchange_value = compare_value + value; exchange_value = compare_value + value;
} while ((unsigned long)Atomic::cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value) != compare_value); } while ((unsigned long)Atomic::cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value) != compare_value);
return exchange_value; return exchange_value;
...@@ -43,7 +43,7 @@ static jlong atomic_dec(size_t volatile* const dest) { ...@@ -43,7 +43,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_ptr_acquire((intptr_t*)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 ((unsigned long)Atomic::cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value) != compare_value); } while ((unsigned long)Atomic::cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value) != compare_value);
......
...@@ -31,7 +31,21 @@ ...@@ -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 { ...@@ -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,54 +71,95 @@ template <typename T> ...@@ -53,54 +71,95 @@ 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 T, bool negation>
class Retired {
public:
typedef T Type;
bool process(Type* t) {
assert(t != NULL, "invariant");
return negation ? !t->retired() : t->retired();
}
};
template <typename T, bool negation>
class Excluded {
public:
typedef T Type;
bool process(Type* t) {
assert(t != NULL, "invariant");
return negation ? !t->excluded() : t->excluded();
}
}; };
template <typename Operation> template <typename Operation>
class ConcurrentWriteOp { class MutexedWriteOp {
private: private:
Operation& _operation; Operation& _operation;
public: public:
typedef typename Operation::Type Type; typedef typename Operation::Type Type;
ConcurrentWriteOp(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> template <typename Operation, typename Predicate>
class ConcurrentWriteOpExcludeRetired : private ConcurrentWriteOp<Operation> { class PredicatedMutexedWriteOp : public MutexedWriteOp<Operation> {
private:
Predicate& _predicate;
public: public:
typedef typename Operation::Type Type; PredicatedMutexedWriteOp(Operation& operation, Predicate& predicate) :
ConcurrentWriteOpExcludeRetired(Operation& operation) : ConcurrentWriteOp<Operation>(operation) {} MutexedWriteOp<Operation>(operation), _predicate(predicate) {}
bool process(Type* t); bool process(typename Operation::Type* t) {
size_t processed() const { return ConcurrentWriteOp<Operation>::processed(); } return _predicate.process(t) ? MutexedWriteOp<Operation>::process(t) : true;
}
}; };
template <typename Operation> template <typename Operation>
class MutexedWriteOp { class ConcurrentWriteOp {
private: private:
Operation& _operation; Operation& _operation;
public: public:
typedef typename Operation::Type Type; typedef typename Operation::Type Type;
MutexedWriteOp(Operation& operation) : _operation(operation) {} ConcurrentWriteOp(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 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 { ...@@ -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_VM_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP #endif // SHARE_VM_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP
...@@ -31,13 +31,15 @@ ...@@ -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) { ...@@ -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");
......
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
#include "jfr/recorder/stringpool/jfrStringPool.hpp" #include "jfr/recorder/stringpool/jfrStringPool.hpp"
#include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp" #include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp"
#include "jfr/utilities/jfrTypes.hpp" #include "jfr/utilities/jfrTypes.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"
...@@ -41,12 +40,42 @@ ...@@ -41,12 +40,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;
...@@ -130,12 +159,16 @@ BufferPtr JfrStringPool::lease_buffer(Thread* thread, size_t size /* 0 */) { ...@@ -130,12 +159,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() != 0); const bool current_epoch = (JfrTraceIdEpoch::epoch() != 0);
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;
} }
...@@ -196,6 +229,7 @@ size_t JfrStringPool::write_at_safepoint() { ...@@ -196,6 +229,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 { ...@@ -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() : ...@@ -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),
_cached_top_frame_bci(max_jint), _cached_top_frame_bci(max_jint),
_alloc_count(0), _alloc_count(0),
...@@ -88,9 +89,13 @@ static void send_java_thread_start_event(JavaThread* jt) { ...@@ -88,9 +89,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);
}
} }
} }
} }
...@@ -107,50 +112,68 @@ static void send_java_thread_end_events(traceid id, JavaThread* jt) { ...@@ -107,50 +112,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, mtTracing);
_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());
}
if (tl->_stackframes != NULL) {
FREE_C_HEAP_ARRAY(JfrStackFrame, tl->_stackframes, mtTracing);
}
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;
} }
...@@ -168,6 +191,18 @@ ByteSize JfrThreadLocal::java_event_writer_offset() { ...@@ -168,6 +191,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,6 +50,7 @@ class JfrThreadLocal { ...@@ -50,6 +50,7 @@ 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;
// Jfr callstack collection relies on vframeStream. // Jfr callstack collection relies on vframeStream.
// But the bci of top frame can not be determined by vframeStream in some scenarios. // But the bci of top frame can not be determined by vframeStream in some scenarios.
...@@ -76,7 +77,7 @@ class JfrThreadLocal { ...@@ -76,7 +77,7 @@ class JfrThreadLocal {
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:
...@@ -224,6 +225,10 @@ class JfrThreadLocal { ...@@ -224,6 +225,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;
} }
...@@ -280,6 +285,9 @@ class JfrThreadLocal { ...@@ -280,6 +285,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);
......
...@@ -61,8 +61,13 @@ class JfrTraceFlag { ...@@ -61,8 +61,13 @@ class JfrTraceFlag {
jbyte* flags_addr() const { jbyte* flags_addr() const {
return (jbyte*)&_flags; return (jbyte*)&_flags;
} }
jbyte* meta_addr() const { jbyte* meta_addr() const {
#ifdef VM_LITTLE_ENDIAN
return ((jbyte*)&_flags) + 1; return ((jbyte*)&_flags) + 1;
#else
return (jbyte*)&_flags;
#endif
} }
}; };
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#include "jfr/utilities/jfrTypes.hpp" #include "jfr/utilities/jfrTypes.hpp"
#include "memory/allocation.inline.hpp" #include "memory/allocation.inline.hpp"
#include "runtime/atomic.inline.hpp" #include "runtime/atomic.inline.hpp"
#include "runtime/orderAccess.inline.hpp"
#include "runtime/vm_version.hpp" #include "runtime/vm_version.hpp"
#include "runtime/mutexLocker.hpp" #include "runtime/mutexLocker.hpp"
#include "utilities/debug.hpp" #include "utilities/debug.hpp"
...@@ -45,7 +44,7 @@ jlong atomic_add_jlong(jlong value, jlong volatile* const dest) { ...@@ -45,7 +44,7 @@ jlong atomic_add_jlong(jlong value, jlong volatile* const dest) {
} }
#endif #endif
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 { ...@@ -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 */) { ...@@ -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) { ...@@ -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) {
......
/*
* 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(JavaThread* cur) {
JavaThread* next = cur->next();
while (next != NULL && !java_thread_inclusion_predicate(next)) {
next = next->next();
}
return next;
}
/**
static Thread* next_non_java_thread(Thread* cur) {
Thread* next = cur->next();
while (next != NULL && !thread_inclusion_predicate(next)) {
next = next->next();
}
return next;
}
*/
JfrJavaThreadIteratorAdapter::JfrJavaThreadIteratorAdapter() {
_next = Threads::first();
while (_next != NULL && !java_thread_inclusion_predicate(_next)) {
_next = _next->next();
}
}
JavaThread* JfrJavaThreadIteratorAdapter::next() {
assert(has_next(), "invariant");
Type* const temp = _next;
_next = next_java_thread(_next);
assert(temp != _next, "invariant");
return temp;
}
JfrNonJavaThreadIteratorAdapter::JfrNonJavaThreadIteratorAdapter() {
}
bool JfrNonJavaThreadIteratorAdapter::has_next() const {
return false;
}
Thread* JfrNonJavaThreadIteratorAdapter::next() {
return NULL;
}
// explicit instantiations
template class JfrThreadIterator<JfrJavaThreadIteratorAdapter, StackObj>;
template class JfrThreadIterator<JfrNonJavaThreadIteratorAdapter, StackObj>;
/*
* 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:
JavaThread* _next;
public:
typedef JavaThread Type;
JfrJavaThreadIteratorAdapter();
bool has_next() const {
return _next != NULL;
}
Type* next();
};
class JfrNonJavaThreadIteratorAdapter {
public:
typedef Thread 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 @@ ...@@ -26,11 +26,14 @@
#define SHARE_VM_JFR_UTILITIES_JFRTYPES_HPP #define SHARE_VM_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;
...@@ -50,4 +53,12 @@ enum EventStartTime { ...@@ -50,4 +53,12 @@ enum EventStartTime {
jlong atomic_add_jlong(jlong value, jlong volatile* const dest); jlong atomic_add_jlong(jlong value, jlong volatile* const dest);
enum JfrCheckpointType {
GENERIC,
FLUSH,
HEADER,
STATICS = 4,
THREADS = 8
};
#endif // SHARE_VM_JFR_UTILITIES_JFRTYPES_HPP #endif // SHARE_VM_JFR_UTILITIES_JFRTYPES_HPP
...@@ -32,9 +32,9 @@ class JavaThread; ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 >
......
...@@ -334,7 +334,7 @@ void mutex_init() { ...@@ -334,7 +334,7 @@ void mutex_init() {
def(JfrMsg_lock , Monitor, leaf, true); def(JfrMsg_lock , Monitor, leaf, true);
def(JfrBuffer_lock , Mutex, leaf, true); def(JfrBuffer_lock , Mutex, leaf, true);
def(JfrThreadGroups_lock , Mutex, leaf, true); def(JfrThreadGroups_lock , Mutex, leaf, true);
def(JfrStream_lock , Mutex, nonleaf, true); def(JfrStream_lock , Mutex, nonleaf + 1, false);
def(JfrStacktrace_lock , Mutex, special, true); def(JfrStacktrace_lock , Mutex, special, true);
#ifndef SUPPORTS_NATIVE_CX8 #ifndef SUPPORTS_NATIVE_CX8
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
#ifndef SHARE_VM_RUNTIME_SEMAPHORE_INLINE_HPP #ifndef SHARE_VM_RUNTIME_SEMAPHORE_INLINE_HPP
#define SHARE_VM_RUNTIME_SEMAPHORE_INLINE_HPP #define SHARE_VM_RUNTIME_SEMAPHORE_INLINE_HPP
#include "runtime/interfaceSupport.inline.hpp" #include "runtime/interfaceSupport.hpp"
#include "runtime/semaphore.hpp" #include "runtime/semaphore.hpp"
#include "runtime/thread.inline.hpp" #include "runtime/thread.inline.hpp"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册