--- a/src/hotspot/share/gc/z/zTracer.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/gc/z/zTracer.cpp Fri May 17 16:02:27 2019 +0200
@@ -58,11 +58,9 @@
static void register_jfr_type_serializers() {
JfrSerializer::register_serializer(TYPE_ZSTATISTICSCOUNTERTYPE,
- false /* require_safepoint */,
true /* permit_cache */,
new ZStatisticsCounterTypeConstant());
JfrSerializer::register_serializer(TYPE_ZSTATISTICSSAMPLERTYPE,
- false /* require_safepoint */,
true /* permit_cache */,
new ZStatisticsSamplerTypeConstant());
}
--- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp Fri May 17 16:02:27 2019 +0200
@@ -349,6 +349,7 @@
_filename("filename", "Resulting recording filename, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false),
_maxage("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"),
_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("flush", "Maximum time before flushing buffers, measuare in (s)econds, e.g. 4 s, or 0 for flushing when a recording ends", "NANOTIME", false, "0"),
_dump_on_exit("dumponexit", "Dump running recording when JVM shuts down", "BOOLEAN", false),
_path_to_gc_roots("path-to-gc-roots", "Collect path to GC roots", "BOOLEAN", false, "false") {
_dcmdparser.add_dcmd_option(&_name);
@@ -359,6 +360,7 @@
_dcmdparser.add_dcmd_option(&_filename);
_dcmdparser.add_dcmd_option(&_maxage);
_dcmdparser.add_dcmd_option(&_maxsize);
+ _dcmdparser.add_dcmd_option(&_flush);
_dcmdparser.add_dcmd_option(&_dump_on_exit);
_dcmdparser.add_dcmd_option(&_path_to_gc_roots);
};
@@ -411,6 +413,10 @@
maxsize = JfrJavaSupport::new_java_lang_Long(_maxsize.value()._size, CHECK);
}
+ jobject flush = NULL;
+ if (_flush.is_set()) {
+ flush = JfrJavaSupport::new_java_lang_Long(_flush.value()._nanotime, CHECK);
+ }
jobject duration = NULL;
if (_duration.is_set()) {
duration = JfrJavaSupport::new_java_lang_Long(_duration.value()._nanotime, CHECK);
@@ -458,7 +464,7 @@
static const char method[] = "execute";
static const char signature[] = "(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Long;"
"Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;"
- "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;";
+ "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);
execute_args.set_receiver(h_dcmd_instance);
@@ -472,6 +478,7 @@
execute_args.push_jobject(filename);
execute_args.push_jobject(maxage);
execute_args.push_jobject(maxsize);
+ execute_args.push_jobject(flush);
execute_args.push_jobject(dump_on_exit);
execute_args.push_jobject(path_to_gc_roots);
--- a/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp Fri May 17 16:02:27 2019 +0200
@@ -90,6 +90,7 @@
DCmdArgument<char*> _filename;
DCmdArgument<NanoTimeArgument> _maxage;
DCmdArgument<MemorySizeArgument> _maxsize;
+ DCmdArgument<NanoTimeArgument> _flush;
DCmdArgument<bool> _dump_on_exit;
DCmdArgument<bool> _path_to_gc_roots;
--- a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp Fri May 17 16:02:27 2019 +0200
@@ -44,7 +44,6 @@
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/array.hpp"
-#include "oops/constantPool.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/method.hpp"
#include "prims/jvmtiRedefineClasses.hpp"
--- a/src/hotspot/share/jfr/jfr.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/jfr.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -71,6 +71,18 @@
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) {
if (JfrRecorder::is_recording()) {
JfrCheckpointManager::write_thread_checkpoint(jt);
--- a/src/hotspot/share/jfr/jfr.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/jfr.hpp Fri May 17 16:02:27 2019 +0200
@@ -48,6 +48,9 @@
static void on_unloading_classes();
static void on_thread_start(Thread* thread);
static void on_thread_exit(Thread* thread);
+ static void exclude_thread(Thread* thread);
+ static bool is_excluded(Thread* thread);
+ static void include_thread(Thread* thread);
static void on_java_thread_dismantle(JavaThread* jt);
static void on_vm_shutdown(bool exception_handler = false);
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
--- a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp Fri May 17 16:02:27 2019 +0200
@@ -23,7 +23,6 @@
*/
#include "precompiled.hpp"
-#include "jni.h"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/modules.hpp"
#include "classfile/symbolTable.hpp"
@@ -42,9 +41,11 @@
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/jniHandles.inline.hpp"
+#include "runtime/semaphore.inline.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/threadSMR.hpp"
+#include "utilities/growableArray.hpp"
#ifdef ASSERT
void JfrJavaSupport::check_java_thread_in_vm(Thread* t) {
@@ -58,6 +59,12 @@
assert(t->is_Java_thread(), "invariant");
assert(((JavaThread*)t)->thread_state() == _thread_in_native, "invariant");
}
+
+static void check_new_unstarted_java_thread(Thread* t) {
+ assert(t != NULL, "invariant");
+ assert(t->is_Java_thread(), "invariant");
+ assert(((JavaThread*)t)->thread_state() == _thread_new, "invariant");
+}
#endif
/*
@@ -93,6 +100,21 @@
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) {
return JNIHandles::resolve_non_null(obj);
}
@@ -603,9 +625,149 @@
return true;
}
-jlong JfrJavaSupport::jfr_thread_id(jobject target_thread) {
+class ThreadExclusionListAccess : public StackObj {
+ private:
+ static Semaphore _mutex_semaphore;
+ public:
+ ThreadExclusionListAccess() { _mutex_semaphore.wait(); }
+ ~ThreadExclusionListAccess() { _mutex_semaphore.signal(); }
+};
+
+Semaphore ThreadExclusionListAccess::_mutex_semaphore(1);
+static GrowableArray<jweak>* exclusion_list = NULL;
+
+static bool equals(const jweak excluded_thread, Handle target_thread) {
+ return JfrJavaSupport::resolve_non_null(excluded_thread) == target_thread();
+}
+
+static int find_exclusion_thread_idx(Handle thread) {
+ if (exclusion_list != NULL) {
+ for (int i = 0; i < exclusion_list->length(); ++i) {
+ if (equals(exclusion_list->at(i), thread)) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+static Handle as_handle(jobject thread) {
+ return Handle(Thread::current(), JfrJavaSupport::resolve_non_null(thread));
+}
+
+static bool thread_is_not_excluded(Handle thread) {
+ return -1 == find_exclusion_thread_idx(thread);
+}
+
+static bool thread_is_not_excluded(jobject thread) {
+ return thread_is_not_excluded(as_handle(thread));
+}
+
+static bool is_thread_excluded(jobject thread) {
+ return !thread_is_not_excluded(thread);
+}
+
+#ifdef ASSERT
+static bool is_thread_excluded(Handle thread) {
+ return !thread_is_not_excluded(thread);
+}
+#endif // ASSERT
+
+static int add_thread_to_exclusion_list(jobject thread) {
+ ThreadExclusionListAccess lock;
+ if (exclusion_list == NULL) {
+ exclusion_list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<jweak>(10, true, mtTracing);
+ }
+ assert(exclusion_list != NULL, "invariant");
+ assert(thread_is_not_excluded(thread), "invariant");
+ jweak ref = JfrJavaSupport::global_weak_jni_handle(thread, Thread::current());
+ const int idx = exclusion_list->append(ref);
+ assert(is_thread_excluded(thread), "invariant");
+ return idx;
+}
+
+static void remove_thread_from_exclusion_list(Handle thread) {
+ assert(exclusion_list != NULL, "invariant");
+ assert(is_thread_excluded(thread), "invariant");
+ assert(exclusion_list != NULL, "invariant");
+ const int idx = find_exclusion_thread_idx(thread);
+ assert(idx >= 0, "invariant");
+ assert(idx < exclusion_list->length(), "invariant");
+ JfrJavaSupport::destroy_global_weak_jni_handle(exclusion_list->at(idx));
+ exclusion_list->delete_at(idx);
+ assert(thread_is_not_excluded(thread), "invariant");
+ if (0 == exclusion_list->length()) {
+ delete exclusion_list;
+ exclusion_list = NULL;
+ }
+}
+
+static void remove_thread_from_exclusion_list(jobject thread) {
+ ThreadExclusionListAccess lock;
+ remove_thread_from_exclusion_list(as_handle(thread));
+}
+
+// includes removal
+static bool check_exclusion_state_on_thread_start(JavaThread* jt) {
+ Handle h_obj(jt, jt->threadObj());
+ ThreadExclusionListAccess lock;
+ if (thread_is_not_excluded(h_obj)) {
+ return false;
+ }
+ remove_thread_from_exclusion_list(h_obj);
+ return true;
+}
+
+jlong JfrJavaSupport::jfr_thread_id(jobject thread) {
ThreadsListHandle tlh;
JavaThread* native_thread = NULL;
- (void)tlh.cv_internal_thread_to_JavaThread(target_thread, &native_thread, NULL);
+ (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
return native_thread != NULL ? JFR_THREAD_ID(native_thread) : 0;
}
+
+void JfrJavaSupport::exclude(jobject thread) {
+ HandleMark hm;
+ ThreadsListHandle tlh;
+ JavaThread* native_thread = NULL;
+ (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
+ if (native_thread != NULL) {
+ JfrThreadLocal::exclude(native_thread);
+ } else {
+ // not started yet, track the thread oop
+ add_thread_to_exclusion_list(thread);
+ }
+}
+
+void JfrJavaSupport::include(jobject thread) {
+ HandleMark hm;
+ ThreadsListHandle tlh;
+ JavaThread* native_thread = NULL;
+ (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
+ if (native_thread != NULL) {
+ JfrThreadLocal::include(native_thread);
+ } else {
+ // not started yet, untrack the thread oop
+ remove_thread_from_exclusion_list(thread);
+ }
+}
+
+bool JfrJavaSupport::is_excluded(jobject thread) {
+ HandleMark hm;
+ ThreadsListHandle tlh;
+ JavaThread* native_thread = NULL;
+ (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
+ return native_thread != NULL ? native_thread->jfr_thread_local()->is_excluded() : is_thread_excluded(thread);
+}
+
+void JfrJavaSupport::on_thread_start(Thread* t) {
+ assert(t != NULL, "invariant");
+ assert(Thread::current() == t, "invariant");
+ if (!t->is_Java_thread()) {
+ return;
+ }
+ DEBUG_ONLY(check_new_unstarted_java_thread(t);)
+ HandleMark hm;
+ if (check_exclusion_state_on_thread_start((JavaThread*)t)) {
+ JfrThreadLocal::exclude(t);
+ }
+}
--- a/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp Fri May 17 16:02:27 2019 +0200
@@ -29,18 +29,21 @@
#include "utilities/exceptions.hpp"
class Klass;
-class JavaThread;
class outputStream;
class JfrJavaSupport : public AllStatic {
public:
static jobject local_jni_handle(const oop obj, Thread* t);
static jobject local_jni_handle(const jobject handle, Thread* t);
- static void destroy_local_jni_handle(const jobject handle);
+ static void destroy_local_jni_handle(jobject handle);
static jobject global_jni_handle(const oop obj, Thread* t);
static jobject global_jni_handle(const jobject handle, Thread* t);
- static void destroy_global_jni_handle(const jobject handle);
+ static 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 void notify_all(jobject obj, TRAPS);
@@ -85,7 +88,11 @@
static bool is_jdk_jfr_module_available();
static bool is_jdk_jfr_module_available(outputStream* stream, TRAPS);
- static jlong jfr_thread_id(jobject target_thread);
+ 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
static void abort(jstring errorMsg, TRAPS);
--- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp Fri May 17 16:02:27 2019 +0200
@@ -284,6 +284,10 @@
return JfrJavaEventWriter::flush(writer, used_size, requested_size, thread);
JVM_END
+JVM_ENTRY_NO_ENV(void, jfr_flush(JNIEnv* env, jobject jvm, jboolean include_metadata, jshort flush_counter))
+ JfrRepository::flush(include_metadata == JNI_TRUE, thread);
+JVM_END
+
JVM_ENTRY_NO_ENV(void, jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location))
return JfrRepository::set_path(location, thread);
JVM_END
@@ -311,3 +315,20 @@
JVM_ENTRY_NO_ENV(void, jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean emit_all))
LeakProfiler::emit_events(cutoff_ticks, emit_all == JNI_TRUE);
JVM_END
+
+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
+
--- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp Fri May 17 16:02:27 2019 +0200
@@ -113,6 +113,7 @@
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, jboolean include_metadata, jshort flush_counter);
void JNICALL jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg);
jlong JNICALL jfr_get_epoch_address(JNIEnv* env, jobject jvm);
@@ -131,6 +132,13 @@
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
}
--- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -70,6 +70,7 @@
(char*)"getEventWriter", (char*)"()Ljava/lang/Object;", (void*)jfr_get_event_writer,
(char*)"newEventWriter", (char*)"()Ljdk/jfr/internal/EventWriter;", (void*)jfr_new_event_writer,
(char*)"flush", (char*)"(Ljdk/jfr/internal/EventWriter;II)Z", (void*)jfr_event_writer_flush,
+ (char*)"flush", (char*)"(ZS)V", (void*)jfr_flush,
(char*)"setRepositoryLocation", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_repository_location,
(char*)"abort", (char*)"(Ljava/lang/String;)V", (void*)jfr_abort,
(char*)"getEpochAddress", (char*)"()J",(void*)jfr_get_epoch_address,
@@ -79,7 +80,11 @@
(char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count,
(char*)"setCutoff", (char*)"(JJ)Z", (void*)jfr_set_cutoff,
(char*)"emitOldObjectSamples", (char*)"(JZ)V", (void*)jfr_emit_old_object_samples,
- (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);
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp Fri May 17 16:02:27 2019 +0200
@@ -230,8 +230,8 @@
assert(thread != NULL, "invariant");
static bool types_registered = false;
if (!types_registered) {
- JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType());
- JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType());
+ JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, true, new RootSystemType());
+ JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, true, new RootType());
types_registered = true;
}
const ObjectSampler* const object_sampler = LeakProfiler::object_sampler();
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp Fri May 17 16:02:27 2019 +0200
@@ -29,6 +29,7 @@
#include "gc/shared/strongRootsScope.hpp"
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
#include "jfr/leakprofiler/checkpoint/rootResolver.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "memory/iterator.hpp"
#include "memory/universe.hpp"
#include "oops/klass.hpp"
@@ -37,7 +38,6 @@
#include "prims/jvmtiThreadState.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/mutexLocker.hpp"
-#include "runtime/threadSMR.inline.hpp"
#include "runtime/vframe_hp.hpp"
#include "services/management.hpp"
#include "utilities/growableArray.hpp"
@@ -277,8 +277,9 @@
public:
ReferenceToThreadRootClosure(RootCallback& callback) :_callback(callback), _complete(false) {
assert_locked_or_safepoint(Threads_lock);
- for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
- if (do_thread_roots(jt)) {
+ JfrJavaThreadIterator iter;
+ while (iter.has_next()) {
+ if (do_thread_roots(iter.next())) {
return;
}
}
--- a/src/hotspot/share/jfr/metadata/jfrSerializer.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/metadata/jfrSerializer.hpp Fri May 17 16:02:27 2019 +0200
@@ -70,8 +70,9 @@
class JfrSerializer : public CHeapObj<mtTracing> {
public:
virtual ~JfrSerializer() {}
- static bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer);
- virtual void serialize(JfrCheckpointWriter& writer) = 0;
+ static bool register_serializer(JfrTypeId id, bool permit_cache, JfrSerializer* serializer);
+ virtual void serialize(JfrCheckpointWriter& writer) {}
+ virtual void on_rotation() {}
};
/*
--- a/src/hotspot/share/jfr/metadata/metadata.xml Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/metadata/metadata.xml Fri May 17 16:02:27 2019 +0200
@@ -983,6 +983,42 @@
<Field type="ulong" name="value" label="Value" />
</Event>
+ <Event name="Flush" category="Flight Recorder" label="Flush" thread="false">
+ <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">
+ <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">
+ <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">
+ <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">
+ <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">
+ <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">
<Field type="string" name="counter" label="Counter" />
</Type>
@@ -1168,6 +1204,7 @@
<Relation name="GcId"/>
<Relation name="CompileId" />
<Relation name="SweepId"/>
+ <Relation name="FlushId"/>
<XmlType name="Package" parameterType="const PackageEntry*" fieldType="const PackageEntry*"/>
<XmlType name="Class" javaType="java.lang.Class" parameterType="const Klass*" fieldType="const Klass*"/>
--- a/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,7 @@
uint64_t bytes_in;
uint64_t bytes_out;
bool in_use;
+ bool written;
};
static GrowableArray<InterfaceEntry>* _interfaces = NULL;
@@ -72,6 +73,7 @@
entry.bytes_in = iface->get_bytes_in();
entry.bytes_out = iface->get_bytes_out();
entry.in_use = false;
+ entry.written = false;
return _interfaces->at(_interfaces->append(entry));
}
@@ -117,17 +119,17 @@
return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED;
}
-class JfrNetworkInterfaceName : public JfrSerializer {
- public:
- void serialize(JfrCheckpointWriter& writer) {
+static void write_interface_types(JfrCheckpointWriter& writer) {
assert(_interfaces != NULL, "invariant");
+ writer.write_type(TYPE_NETWORKINTERFACENAME);
const JfrCheckpointContext ctx = writer.context();
const intptr_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet
int active_interfaces = 0;
for (int i = 0; i < _interfaces->length(); ++i) {
InterfaceEntry& entry = _interfaces->at(i);
- if (entry.in_use) {
+ if (entry.in_use && !entry.written) {
entry.in_use = false;
+ entry.written = true;
writer.write_key(entry.id);
writer.write(entry.name);
++active_interfaces;
@@ -140,12 +142,19 @@
}
writer.write_count(active_interfaces, count_offset);
}
+class JfrNetworkInterfaceName : public JfrSerializer {
+ public:
+ void on_rotation() {
+ for (int i = 0; i < _interfaces->length(); ++i) {
+ InterfaceEntry& entry = _interfaces->at(i);
+ entry.written = false;
+ }
+ }
};
static bool register_network_interface_name_serializer() {
assert(_interfaces != NULL, "invariant");
return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME,
- false, // require safepoint
false, // disallow caching; we want a callback every rotation
new JfrNetworkInterfaceName());
}
@@ -161,6 +170,7 @@
const JfrTicks cur_time = JfrTicks::now();
const JfrTickspan interval = last_sample_instant == 0 ? cur_time - cur_time : cur_time - last_sample_instant;
last_sample_instant = cur_time;
+ bool write_type = false;
for (NetworkInterface *cur = network_interfaces; cur != NULL; cur = cur->next()) {
InterfaceEntry& entry = get_entry(cur);
if (interval.value() > 0) {
@@ -177,6 +187,9 @@
event.set_readRate(8 * read_rate);
event.set_writeRate(8 * write_rate);
event.commit();
+ if (!entry.written) {
+ write_type = true;
+ }
}
// update existing entry with new values
entry.bytes_in = current_bytes_in;
@@ -184,6 +197,10 @@
}
}
+ if (write_type) {
+ JfrCheckpointWriter writer(false, true, Thread::current());
+ write_interface_types(writer);
+ }
static bool is_serializer_registered = false;
if (!is_serializer_registered) {
is_serializer_registered = register_network_interface_name_serializer();
--- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Fri May 17 16:02:27 2019 +0200
@@ -45,6 +45,7 @@
#include "jfr/periodic/jfrNetworkUtilization.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/support/jfrThreadId.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "jfrfiles/jfrPeriodic.hpp"
#include "logging/log.hpp"
@@ -57,7 +58,6 @@
#include "runtime/os.hpp"
#include "runtime/os_perf.hpp"
#include "runtime/thread.inline.hpp"
-#include "runtime/threadSMR.hpp"
#include "runtime/sweeper.hpp"
#include "runtime/vmThread.hpp"
#include "services/classLoadingService.hpp"
@@ -416,10 +416,13 @@
GrowableArray<jlong> allocated(initial_size);
GrowableArray<traceid> thread_ids(initial_size);
JfrTicks time_stamp = JfrTicks::now();
+
{
- // Collect allocation statistics while holding threads lock
- MutexLocker ml(Threads_lock);
- for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+ JfrJavaThreadIterator iter;
+ while (iter.has_next()) {
+ JavaThread* const jt = iter.next();
+ assert(jt != NULL, "invariant");
+
allocated.append(jt->cooked_allocated_bytes());
thread_ids.append(JFR_THREAD_ID(jt));
}
--- a/src/hotspot/share/jfr/periodic/jfrThreadCPULoadEvent.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/periodic/jfrThreadCPULoadEvent.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,11 +28,10 @@
#include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
#include "jfr/support/jfrThreadId.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "utilities/globalDefinitions.hpp"
#include "runtime/os.hpp"
-#include "runtime/thread.inline.hpp"
-#include "runtime/threadSMR.inline.hpp"
jlong JfrThreadCPULoadEvent::get_wallclock_time() {
return os::javaTimeNanos();
@@ -115,8 +114,12 @@
JfrTicks event_time = JfrTicks::now();
jlong cur_wallclock_time = JfrThreadCPULoadEvent::get_wallclock_time();
- JavaThreadIteratorWithHandle jtiwh;
- while (JavaThread* jt = jtiwh.next()) {
+ JfrJavaThreadIterator iter;
+ int number_of_threads = 0;
+ while (iter.has_next()) {
+ JavaThread* const jt = iter.next();
+ assert(jt != NULL, "invariant");
+ ++number_of_threads;
EventThreadCPULoad event(UNTIMED);
if (JfrThreadCPULoadEvent::update_event(event, jt, cur_wallclock_time, processor_count)) {
event.set_starttime(event_time);
@@ -129,7 +132,7 @@
event.commit();
}
}
- log_trace(jfr)("Measured CPU usage for %d threads in %.3f milliseconds", jtiwh.length(),
+ log_trace(jfr)("Measured CPU usage for %d threads in %.3f milliseconds", number_of_threads,
(double)(JfrTicks::now() - event_time).milliseconds());
// Restore this thread's thread id
periodic_thread_tl->set_thread_id(periodic_thread_id);
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,9 +34,13 @@
#include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/utilities/jfrBigEndian.hpp"
+#include "jfr/utilities/jfrIterator.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "jfr/utilities/jfrTypes.hpp"
+#include "jfr/writers/jfrJavaEventWriter.hpp"
#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
+#include "runtime/handles.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/os.inline.hpp"
@@ -136,6 +140,8 @@
void JfrCheckpointManager::register_full(BufferPtr t, Thread* thread) {
// nothing here at the moment
+ assert(t != NULL, "invariant");
+ assert(t->acquired_by(thread), "invariant");
assert(t->retired(), "invariant");
}
@@ -271,15 +277,15 @@
static size_t write_checkpoint_event(JfrChunkWriter& cw, const u1* data) {
assert(data != NULL, "invariant");
- const intptr_t previous_checkpoint_event = cw.previous_checkpoint_offset();
- const intptr_t event_begin = cw.current_offset();
- const intptr_t offset_to_previous_checkpoint_event = 0 == previous_checkpoint_event ? 0 : previous_checkpoint_event - event_begin;
- const jlong total_checkpoint_size = total_size(data);
- write_checkpoint_header(cw, offset_to_previous_checkpoint_event, data);
+ const int64_t last_checkpoint_event = cw.last_checkpoint_offset();
+ const int64_t event_begin = cw.current_offset();
+ const int64_t offset_to_last_checkpoint_event = 0 == last_checkpoint_event ? 0 : last_checkpoint_event - event_begin;
+ const int64_t total_checkpoint_size = total_size(data);
+ write_checkpoint_header(cw, offset_to_last_checkpoint_event, data);
write_checkpoint_content(cw, data, total_checkpoint_size - sizeof(JfrCheckpointEntry));
- const jlong checkpoint_event_size = cw.current_offset() - event_begin;
+ const int64_t checkpoint_event_size = cw.current_offset() - event_begin;
cw.write_padded_at_offset<u4>(checkpoint_event_size, event_begin);
- cw.set_previous_checkpoint_offset(event_begin);
+ cw.set_last_checkpoint_offset(event_begin);
return (size_t)total_checkpoint_size;
}
@@ -336,8 +342,36 @@
return processed;
}
+typedef StopOnEmptyIterator<JfrDoublyLinkedList<JfrBuffer> > EmptyIterator;
+
+template <typename Processor>
+static void process_transition_mspace(Processor& processor, JfrCheckpointMspace* mspace) {
+ assert(mspace->is_full_empty(), "invariant");
+ process_free_list_iterator_control<Processor, JfrCheckpointMspace, EmptyIterator>(processor, mspace, forward);
+}
+
+size_t JfrCheckpointManager::flush() {
+ WriteOperation wo(_chunkwriter);
+ MutexedWriteOperation mwo(wo);
+ process_transition_mspace(mwo, _epoch_transition_mspace);
+ assert(_free_list_mspace->is_full_empty(), "invariant");
+ process_free_list(mwo, _free_list_mspace);
+ return wo.processed();
+}
+
+size_t JfrCheckpointManager::write_constants() {
+ write_types();
+ return flush();
+}
+
size_t JfrCheckpointManager::write_epoch_transition_mspace() {
- return write_mspace_exclusive(_epoch_transition_mspace, _chunkwriter);
+ Thread* const thread = Thread::current();
+ WriteOperation wo(_chunkwriter);
+ MutexedWriteOperation mwo(wo);
+ CheckpointReleaseOperation cro(_epoch_transition_mspace, thread, false);
+ CheckpointWriteOperation cpwo(&mwo, &cro);
+ process_transition_mspace(cpwo, _epoch_transition_mspace);
+ return wo.processed();
}
typedef DiscardOp<DefaultDiscarder<JfrBuffer> > DiscardOperation;
@@ -346,20 +380,38 @@
process_free_list(discarder, _free_list_mspace);
process_free_list(discarder, _epoch_transition_mspace);
synchronize_epoch();
- return discarder.processed();
+ return discarder.elements();
}
size_t JfrCheckpointManager::write_types() {
+ ResourceMark rm;
+ HandleMark hm;
JfrCheckpointWriter writer(false, true, Thread::current());
JfrTypeManager::write_types(writer);
return writer.used_size();
}
-size_t JfrCheckpointManager::write_safepoint_types() {
- // this is also a "flushpoint"
- JfrCheckpointWriter writer(true, true, Thread::current());
- JfrTypeManager::write_safepoint_types(writer);
- return writer.used_size();
+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::notify_types_on_rotation() {
+ JfrTypeManager::notify_types_on_rotation();
}
void JfrCheckpointManager::write_type_set() {
@@ -371,10 +423,16 @@
JfrTypeManager::write_type_set_for_unloaded_classes();
}
-void JfrCheckpointManager::create_thread_checkpoint(JavaThread* jt) {
- JfrTypeManager::create_thread_checkpoint(jt);
+size_t JfrCheckpointManager::flush_type_set() {
+ const size_t elements = JfrTypeManager::flush_type_set();
+ flush();
+ return elements;
}
-void JfrCheckpointManager::write_thread_checkpoint(JavaThread* jt) {
- JfrTypeManager::write_thread_checkpoint(jt);
+void JfrCheckpointManager::create_thread_checkpoint(Thread* t) {
+ JfrTypeManager::create_thread_checkpoint(t);
}
+
+void JfrCheckpointManager::write_thread_checkpoint(Thread* t) {
+ JfrTypeManager::write_thread_checkpoint(t);
+}
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp Fri May 17 16:02:27 2019 +0200
@@ -73,13 +73,17 @@
size_t clear();
size_t write();
+ size_t write_constants();
+ size_t flush();
size_t write_epoch_transition_mspace();
size_t write_types();
- size_t write_safepoint_types();
+ size_t write_metadata_event();
void write_type_set();
void shift_epoch();
void synchronize_epoch();
bool use_epoch_transition_mspace(const Thread* t) const;
+ void notify_threads();
+ void notify_types_on_rotation();
JfrCheckpointManager(JfrChunkWriter& cw);
~JfrCheckpointManager();
@@ -91,9 +95,10 @@
public:
void register_service_thread(const Thread* t);
+ size_t flush_type_set();
static void write_type_set_for_unloaded_classes();
- static void create_thread_checkpoint(JavaThread* jt);
- static void write_thread_checkpoint(JavaThread* jt);
+ static void create_thread_checkpoint(Thread* t);
+ static void write_thread_checkpoint(Thread* t);
friend class JfrRecorder;
friend class JfrRecorderService;
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -156,6 +156,7 @@
this->seek(ctx.offset);
set_count(ctx.count);
}
+
bool JfrCheckpointWriter::has_data() const {
return this->used_size() > sizeof(JfrCheckpointEntry);
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,19 +29,11 @@
#include "oops/klass.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
-#include "runtime/semaphore.hpp"
#include "runtime/thread.inline.hpp"
static jbyteArray _metadata_blob = NULL;
-static Semaphore metadata_mutex_semaphore(1);
-
-void JfrMetadataEvent::lock() {
- metadata_mutex_semaphore.wait();
-}
-
-void JfrMetadataEvent::unlock() {
- metadata_mutex_semaphore.signal();
-}
+static u8 metadata_id = 0;
+static u8 last_written_metadata_id = 0;
static void write_metadata_blob(JfrChunkWriter& chunkwriter, jbyteArray metadata_blob) {
if (metadata_blob != NULL) {
@@ -56,34 +48,38 @@
}
}
-// the semaphore is assumed to be locked (was locked previous safepoint)
-size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter, jlong metadata_offset) {
+void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
assert(chunkwriter.is_valid(), "invariant");
+ const jlong metadata_offset = chunkwriter.current_offset();
assert(chunkwriter.current_offset() == metadata_offset, "invariant");
+
+ if (last_written_metadata_id == metadata_id && chunkwriter.has_metadata()) {
+ return;
+ }
+
// header
chunkwriter.reserve(sizeof(u4));
chunkwriter.write<u8>(EVENT_METADATA); // ID 0
// time data
chunkwriter.write(JfrTicks::now());
chunkwriter.write((u8)0); // duration
- chunkwriter.write((u8)0); // metadata id
+ chunkwriter.write(metadata_id); // metadata id
write_metadata_blob(chunkwriter, _metadata_blob); // payload
- unlock(); // open up for java to provide updated metadata
+ last_written_metadata_id = metadata_id;
// fill in size of metadata descriptor event
const jlong size_written = chunkwriter.current_offset() - metadata_offset;
chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset);
- return size_written;
+ chunkwriter.set_last_metadata_offset(metadata_offset);
}
void JfrMetadataEvent::update(jbyteArray metadata) {
JavaThread* thread = (JavaThread*)Thread::current();
assert(thread->is_Java_thread(), "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));
- lock();
if (_metadata_blob != NULL) {
JfrJavaSupport::destroy_global_jni_handle(_metadata_blob);
}
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;
- unlock();
+ ++metadata_id;
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp Fri May 17 16:02:27 2019 +0200
@@ -37,9 +37,7 @@
//
class JfrMetadataEvent : AllStatic {
public:
- static void lock();
- static void unlock();
- static size_t write(JfrChunkWriter& writer, jlong metadata_offset);
+ static void write(JfrChunkWriter& writer);
static void update(jbyteArray metadata);
};
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp Fri May 17 16:02:27 2019 +0200
@@ -25,7 +25,6 @@
#include "precompiled.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
-#include "jfr/utilities/jfrResourceManager.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/jniHandles.inline.hpp"
@@ -33,6 +32,8 @@
#include "runtime/semaphore.hpp"
#include "utilities/growableArray.hpp"
+static const int initial_array_size = 30;
+
class ThreadGroupExclusiveAccess : public StackObj {
private:
static Semaphore _mutex_semaphore;
@@ -257,12 +258,10 @@
}
}
-JfrThreadGroup::JfrThreadGroup() : _list(NULL) {
- _list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(30, true);
-}
+JfrThreadGroup::JfrThreadGroup() :
+ _list(new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(initial_array_size, true, mtTracing)) {}
JfrThreadGroup::~JfrThreadGroup() {
- assert(SafepointSynchronize::is_at_safepoint(), "invariant");
if (_list != NULL) {
for (int i = 0; i < _list->length(); i++) {
JfrThreadGroupEntry* e = _list->at(i);
@@ -281,14 +280,11 @@
}
traceid JfrThreadGroup::thread_group_id(const JavaThread* jt, Thread* current) {
- ResourceMark rm(current);
- HandleMark hm(current);
JfrThreadGroupsHelper helper(jt, current);
return helper.is_valid() ? thread_group_id_internal(helper) : 0;
}
traceid JfrThreadGroup::thread_group_id(JavaThread* const jt) {
- assert(!JfrStream_lock->owned_by_self(), "holding stream lock but should not hold it here");
return thread_group_id(jt, jt);
}
@@ -396,9 +392,7 @@
ThreadGroupExclusiveAccess lock;
JfrThreadGroup* tg_instance = instance();
assert(tg_instance != NULL, "invariant");
- ResourceManager<JfrThreadGroup> tg_handle(tg_instance);
- set_instance(NULL);
- tg_handle->write_thread_group_entries(writer);
+ tg_instance->write_thread_group_entries(writer);
}
// for writing a particular thread group
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -39,7 +39,7 @@
#include "jfr/recorder/checkpoint/types/jfrThreadState.hpp"
#include "jfr/recorder/checkpoint/types/jfrTypeSet.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
-#include "jfr/writers/jfrJavaEventWriter.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "memory/metaspaceGCThresholdUpdater.hpp"
#include "memory/referenceType.hpp"
#include "memory/universe.hpp"
@@ -93,12 +93,10 @@
// Requires a ResourceMark for get_thread_name/as_utf8
void JfrCheckpointThreadClosure::do_thread(Thread* t) {
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;
- }
+ assert(!tl->is_dead(), "invariant");
+ assert(!tl->is_excluded(), "invariant");
++_count;
_writer.write_key(tl->thread_id());
_writer.write(t->name());
@@ -110,7 +108,6 @@
_writer.write(java_lang_Thread::thread_id(jt->threadObj()));
_writer.write(JfrThreadGroup::thread_group_id(jt, _curthread));
// since we are iterating threads during a safepoint, also issue notification
- JfrJavaEventWriter::notify(jt);
return;
}
_writer.write((const char*)NULL); // java name
@@ -119,13 +116,18 @@
}
void JfrThreadConstantSet::serialize(JfrCheckpointWriter& writer) {
- assert(SafepointSynchronize::is_at_safepoint(), "invariant");
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) {
- assert(SafepointSynchronize::is_at_safepoint(), "invariant");
JfrThreadGroup::serialize(writer);
}
@@ -298,16 +300,23 @@
class TypeSetSerialization {
private:
+ size_t _elements;
bool _class_unload;
+ bool _flushpoint;
public:
- explicit TypeSetSerialization(bool class_unload) : _class_unload(class_unload) {}
+ explicit TypeSetSerialization(bool class_unload, bool flushpoint) : _elements(0), _class_unload(class_unload), _flushpoint(flushpoint) {}
void write(JfrCheckpointWriter& writer, JfrCheckpointWriter* leakp_writer) {
- JfrTypeSet::serialize(&writer, leakp_writer, _class_unload);
+ MutexLocker cld_lock(SafepointSynchronize::is_at_safepoint() ? NULL : ClassLoaderDataGraph_lock);
+ MutexLocker lock(SafepointSynchronize::is_at_safepoint() ? NULL : Module_lock);
+ _elements = JfrTypeSet::serialize(&writer, leakp_writer, _class_unload, _flushpoint);
+ }
+ size_t elements() const {
+ return _elements;
}
};
void ClassUnloadTypeSet::serialize(JfrCheckpointWriter& writer) {
- TypeSetSerialization type_set(true);
+ TypeSetSerialization type_set(true, false);
if (LeakProfiler::is_running()) {
JfrCheckpointWriter leakp_writer(false, true, Thread::current());
type_set.write(writer, &leakp_writer);
@@ -317,8 +326,19 @@
type_set.write(writer, NULL);
};
+void FlushTypeSet::serialize(JfrCheckpointWriter& writer) {
+ assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
+ TypeSetSerialization type_set(false, true);
+ type_set.write(writer, NULL);
+ _elements = type_set.elements();
+}
+
+size_t FlushTypeSet::elements() const {
+ return _elements;
+}
+
void TypeSet::serialize(JfrCheckpointWriter& writer) {
- TypeSetSerialization type_set(false);
+ TypeSetSerialization type_set(false, false);
if (LeakProfiler::is_suspended()) {
JfrCheckpointWriter leakp_writer(false, true, Thread::current());
type_set.write(writer, &leakp_writer);
@@ -335,20 +355,23 @@
void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) {
assert(_thread != NULL, "invariant");
assert(_thread == Thread::current(), "invariant");
- assert(_thread->is_Java_thread(), "invariant");
- assert(!_thread->jfr_thread_local()->has_thread_checkpoint(), "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);
+ HandleMark hm(_thread);
const char* const thread_name = _thread->name();
- const traceid thread_group_id = JfrThreadGroup::thread_group_id(_thread);
writer.write_count(1);
writer.write_key(_thread->jfr_thread_local()->thread_id());
writer.write(thread_name);
writer.write((traceid)_thread->osthread()->thread_id());
- writer.write(thread_name);
- writer.write(java_lang_thread_id);
- writer.write(thread_group_id);
- JfrThreadGroup::serialize(&writer, thread_group_id);
+ if (_thread->is_Java_thread()) {
+ JavaThread* const jt = (JavaThread*)_thread;
+ writer.write(jt->name());
+ writer.write(java_lang_Thread::thread_id(jt->threadObj()));
+ 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
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp Fri May 17 16:02:27 2019 +0200
@@ -42,6 +42,12 @@
void serialize(JfrCheckpointWriter& writer);
};
+class FlushTypeSet : public JfrSerializer {
+ size_t _elements;
+ public:
+ void serialize(JfrCheckpointWriter& writer);
+ size_t elements() const;
+};
class FlagValueOriginConstant : public JfrSerializer {
public:
void serialize(JfrCheckpointWriter& writer);
@@ -129,9 +135,9 @@
class JfrThreadConstant : public JfrSerializer {
private:
- JavaThread* _thread;
+ Thread* _thread;
public:
- JfrThreadConstant(JavaThread* jt) : _thread(jt) {}
+ JfrThreadConstant(Thread* t) : _thread(t) {}
void serialize(JfrCheckpointWriter& writer);
};
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp Fri May 17 16:02:27 2019 +0200
@@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
+#include "jfr/jfr.hpp"
#include "jfr/metadata/jfrSerializer.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
#include "jfr/recorder/checkpoint/types/jfrType.hpp"
@@ -72,6 +73,8 @@
}
void invoke(JfrCheckpointWriter& writer) const;
+
+ void on_rotation() const;
};
void JfrSerializerRegistration::invoke(JfrCheckpointWriter& writer) const {
@@ -96,6 +99,10 @@
}
}
+void JfrSerializerRegistration::on_rotation() const {
+ _serializer->on_rotation();
+}
+
class SerializerRegistrationGuard : public StackObj {
private:
static Semaphore _mutex_semaphore;
@@ -113,7 +120,6 @@
typedef JfrDoublyLinkedList<JfrSerializerRegistration> List;
typedef StopOnNullIterator<const List> Iterator;
static List types;
-static List safepoint_types;
void JfrTypeManager::clear() {
SerializerRegistrationGuard guard;
@@ -124,12 +130,6 @@
assert(registration != NULL, "invariant");
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) {
@@ -139,54 +139,53 @@
}
}
-void JfrTypeManager::write_safepoint_types(JfrCheckpointWriter& writer) {
- assert(SafepointSynchronize::is_at_safepoint(), "invariant");
- const Iterator iter(safepoint_types);
+void JfrTypeManager::notify_types_on_rotation() {
+ const Iterator iter(types);
while (iter.has_next()) {
- iter.next()->invoke(writer);
+ iter.next()->on_rotation();
}
}
void JfrTypeManager::write_type_set() {
// can safepoint here because of Module_lock
- MutexLocker cld_lock(SafepointSynchronize::is_at_safepoint() ? NULL : ClassLoaderDataGraph_lock);
- MutexLocker lock(SafepointSynchronize::is_at_safepoint() ? NULL : Module_lock);
-
+ assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
JfrCheckpointWriter writer(true, true, Thread::current());
TypeSet set;
set.serialize(writer);
}
void JfrTypeManager::write_type_set_for_unloaded_classes() {
- assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
+ assert(SafepointSynchronize::is_at_safepoint(), "invariant");
JfrCheckpointWriter writer(false, true, Thread::current());
ClassUnloadTypeSet class_unload_set;
class_unload_set.serialize(writer);
}
-void JfrTypeManager::create_thread_checkpoint(JavaThread* jt) {
- assert(jt != NULL, "invariant");
- JfrThreadConstant type_thread(jt);
- JfrCheckpointWriter writer(false, true, jt);
+size_t JfrTypeManager::flush_type_set() {
+ assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
+ JfrCheckpointWriter writer(true, true, Thread::current());
+ FlushTypeSet flush;
+ flush.serialize(writer);
+ return flush.elements();
+}
+
+void JfrTypeManager::create_thread_checkpoint(Thread* t) {
+ assert(t != NULL, "invariant");
+ JfrThreadConstant type_thread(t);
+ JfrCheckpointWriter writer(false, true, t);
writer.write_type(TYPE_THREAD);
type_thread.serialize(writer);
// create and install a checkpoint blob
- jt->jfr_thread_local()->set_thread_checkpoint(writer.checkpoint_blob());
- assert(jt->jfr_thread_local()->has_thread_checkpoint(), "invariant");
+ t->jfr_thread_local()->set_thread_checkpoint(writer.checkpoint_blob());
+ assert(t->jfr_thread_local()->has_thread_checkpoint(), "invariant");
}
-void JfrTypeManager::write_thread_checkpoint(JavaThread* jt) {
- assert(jt != NULL, "JavaThread is NULL!");
- ResourceMark rm(jt);
- if (jt->jfr_thread_local()->has_thread_checkpoint()) {
- JfrCheckpointWriter writer(false, false, jt);
- jt->jfr_thread_local()->thread_checkpoint()->write(writer);
- } else {
- JfrThreadConstant type_thread(jt);
- JfrCheckpointWriter writer(false, true, jt);
- writer.write_type(TYPE_THREAD);
- type_thread.serialize(writer);
- }
+void JfrTypeManager::write_thread_checkpoint(Thread* t) {
+ assert(t != NULL, "invariant");
+ JfrThreadConstant type_thread(t);
+ JfrCheckpointWriter writer(false, true, t);
+ writer.write_type(TYPE_THREAD);
+ type_thread.serialize(writer);
}
#ifdef ASSERT
@@ -198,22 +197,21 @@
}
#endif
-static bool register_type(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer) {
+static bool register_type(JfrTypeId id, bool permit_cache, JfrSerializer* serializer) {
assert(serializer != NULL, "invariant");
JfrSerializerRegistration* const registration = new JfrSerializerRegistration(id, permit_cache, serializer);
if (registration == NULL) {
delete serializer;
return false;
}
- if (require_safepoint) {
- assert(!safepoint_types.in_list(registration), "invariant");
- DEBUG_ONLY(assert_not_registered_twice(id, safepoint_types);)
- safepoint_types.prepend(registration);
- } else {
- assert(!types.in_list(registration), "invariant");
- DEBUG_ONLY(assert_not_registered_twice(id, types);)
- types.prepend(registration);
+
+ assert(!types.in_list(registration), "invariant");
+ DEBUG_ONLY(assert_not_registered_twice(id, types);)
+ if (Jfr::is_recording()) {
+ JfrCheckpointWriter writer(false, true, Thread::current());
+ registration->invoke(writer);
}
+ types.prepend(registration);
return true;
}
@@ -221,31 +219,30 @@
SerializerRegistrationGuard guard;
// register non-safepointing type serialization
- register_type(TYPE_FLAGVALUEORIGIN, false, true, new FlagValueOriginConstant());
- register_type(TYPE_INFLATECAUSE, false, true, new MonitorInflateCauseConstant());
- register_type(TYPE_GCCAUSE, false, true, new GCCauseConstant());
- register_type(TYPE_GCNAME, false, true, new GCNameConstant());
- register_type(TYPE_GCWHEN, false, true, new GCWhenConstant());
- register_type(TYPE_G1HEAPREGIONTYPE, false, true, new G1HeapRegionTypeConstant());
- register_type(TYPE_GCTHRESHOLDUPDATER, false, true, new GCThresholdUpdaterConstant());
- register_type(TYPE_METADATATYPE, false, true, new MetadataTypeConstant());
- register_type(TYPE_METASPACEOBJECTTYPE, false, true, new MetaspaceObjectTypeConstant());
- register_type(TYPE_G1YCTYPE, false, true, new G1YCTypeConstant());
- register_type(TYPE_REFERENCETYPE, false, true, new ReferenceTypeConstant());
- register_type(TYPE_NARROWOOPMODE, false, true, new NarrowOopModeConstant());
- register_type(TYPE_COMPILERPHASETYPE, false, true, new CompilerPhaseTypeConstant());
- register_type(TYPE_CODEBLOBTYPE, false, true, new CodeBlobTypeConstant());
- register_type(TYPE_VMOPERATIONTYPE, false, true, new VMOperationTypeConstant());
- register_type(TYPE_THREADSTATE, false, true, new ThreadStateConstant());
+ register_type(TYPE_THREADGROUP, false, new JfrThreadGroupConstant());
+ register_type(TYPE_THREAD, false, new JfrThreadConstantSet());
+ register_type(TYPE_FLAGVALUEORIGIN, true, new FlagValueOriginConstant());
+ register_type(TYPE_INFLATECAUSE, true, new MonitorInflateCauseConstant());
+ register_type(TYPE_GCCAUSE, true, new GCCauseConstant());
+ register_type(TYPE_GCNAME, true, new GCNameConstant());
+ register_type(TYPE_GCWHEN, true, new GCWhenConstant());
+ register_type(TYPE_G1HEAPREGIONTYPE, true, new G1HeapRegionTypeConstant());
+ register_type(TYPE_GCTHRESHOLDUPDATER, true, new GCThresholdUpdaterConstant());
+ register_type(TYPE_METADATATYPE, true, new MetadataTypeConstant());
+ register_type(TYPE_METASPACEOBJECTTYPE, true, new MetaspaceObjectTypeConstant());
+ register_type(TYPE_G1YCTYPE, true, new G1YCTypeConstant());
+ register_type(TYPE_REFERENCETYPE, true, new ReferenceTypeConstant());
+ register_type(TYPE_NARROWOOPMODE, true, new NarrowOopModeConstant());
+ register_type(TYPE_COMPILERPHASETYPE, true, new CompilerPhaseTypeConstant());
+ register_type(TYPE_CODEBLOBTYPE, true, new CodeBlobTypeConstant());
+ register_type(TYPE_VMOPERATIONTYPE, true, new VMOperationTypeConstant());
+ register_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;
}
// 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;
- return register_type(id, require_safepoint, permit_cache, serializer);
+ return register_type(id, permit_cache, serializer);
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.hpp Fri May 17 16:02:27 2019 +0200
@@ -34,11 +34,12 @@
static bool initialize();
static void clear();
static void write_types(JfrCheckpointWriter& writer);
- static void write_safepoint_types(JfrCheckpointWriter& writer);
+ static void notify_types_on_rotation();
static void write_type_set();
static void write_type_set_for_unloaded_classes();
- static void create_thread_checkpoint(JavaThread* jt);
- static void write_thread_checkpoint(JavaThread* jt);
+ static size_t flush_type_set();
+ static void create_thread_checkpoint(Thread* t);
+ static void write_thread_checkpoint(Thread* t);
};
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -80,35 +80,35 @@
return cld->is_unsafe_anonymous() ? 0 : TRACE_ID(cld);
}
-static void tag_leakp_klass_artifacts(KlassPtr k, bool class_unload) {
+static void tag_leakp_klass_artifacts(KlassPtr k, bool current_epoch) {
assert(k != NULL, "invariant");
PkgPtr pkg = k->package();
if (pkg != NULL) {
- tag_leakp_artifact(pkg, class_unload);
+ tag_leakp_artifact(pkg, current_epoch);
ModPtr module = pkg->module();
if (module != NULL) {
- tag_leakp_artifact(module, class_unload);
+ tag_leakp_artifact(module, current_epoch);
}
}
CldPtr cld = k->class_loader_data();
assert(cld != NULL, "invariant");
if (!cld->is_unsafe_anonymous()) {
- tag_leakp_artifact(cld, class_unload);
+ tag_leakp_artifact(cld, current_epoch);
}
}
class TagLeakpKlassArtifact {
- bool _class_unload;
+ bool _current_epoch;
public:
- TagLeakpKlassArtifact(bool class_unload) : _class_unload(class_unload) {}
+ TagLeakpKlassArtifact(bool current_epoch) : _current_epoch(current_epoch) {}
bool operator()(KlassPtr klass) {
- if (_class_unload) {
+ if (_current_epoch) {
if (LEAKP_USED_THIS_EPOCH(klass)) {
- tag_leakp_klass_artifacts(klass, _class_unload);
+ tag_leakp_klass_artifacts(klass, _current_epoch);
}
} else {
if (LEAKP_USED_PREV_EPOCH(klass)) {
- tag_leakp_klass_artifacts(klass, _class_unload);
+ tag_leakp_klass_artifacts(klass, _current_epoch);
}
}
return true;
@@ -123,11 +123,10 @@
* The weird naming is an effort to decrease the risk of name clashes.
*/
-int write__artifact__klass(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* k) {
+int write__artifact__klass(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, KlassPtr klass) {
assert(writer != NULL, "invariant");
assert(artifacts != NULL, "invariant");
- assert(k != NULL, "invariant");
- KlassPtr klass = (KlassPtr)k;
+ assert(klass != NULL, "invariant");
traceid pkg_id = 0;
KlassPtr theklass = klass;
if (theklass->is_objArray_klass()) {
@@ -149,17 +148,33 @@
return 1;
}
+int write__artifact__klass__leakp(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* k) {
+ assert(k != NULL, "invariant");
+ KlassPtr klass = (KlassPtr)k;
+ return write__artifact__klass(writer, artifacts, klass);
+}
+
+int write__artifact__klass__serialize(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* k) {
+ assert(k != NULL, "invariant");
+ KlassPtr klass = (KlassPtr)k;
+ int result = write__artifact__klass(writer, artifacts, klass);
+ if (IS_NOT_SERIALIZED(klass)) {
+ SET_SERIALIZED(klass);
+ }
+ assert(IS_SERIALIZED(klass), "invariant");
+ return result;
+}
+
typedef LeakPredicate<KlassPtr> LeakKlassPredicate;
-typedef JfrPredicatedArtifactWriterImplHost<KlassPtr, LeakKlassPredicate, write__artifact__klass> LeakKlassWriterImpl;
+typedef SerializePredicate<KlassPtr> KlassPredicate;
+typedef JfrPredicatedArtifactWriterImplHost<KlassPtr, LeakKlassPredicate, write__artifact__klass__leakp> LeakKlassWriterImpl;
typedef JfrArtifactWriterHost<LeakKlassWriterImpl, TYPE_CLASS> LeakKlassWriter;
-typedef JfrArtifactWriterImplHost<KlassPtr, write__artifact__klass> KlassWriterImpl;
+typedef JfrPredicatedArtifactWriterImplHost<KlassPtr, KlassPredicate, write__artifact__klass__serialize> KlassWriterImpl;
typedef JfrArtifactWriterHost<KlassWriterImpl, TYPE_CLASS> KlassWriter;
-int write__artifact__method(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) {
+int write__artifact__method(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, MethodPtr method) {
assert(writer != NULL, "invariant");
assert(artifacts != NULL, "invariant");
- assert(m != NULL, "invariant");
- MethodPtr method = (MethodPtr)m;
const traceid method_name_symbol_id = artifacts->mark(method->name());
assert(method_name_symbol_id > 0, "invariant");
const traceid method_sig_symbol_id = artifacts->mark(method->signature());
@@ -176,14 +191,33 @@
return 1;
}
-typedef JfrArtifactWriterImplHost<MethodPtr, write__artifact__method> MethodWriterImplTarget;
+int write__artifact__method__leakp(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) {
+ assert(m != NULL, "invariant");
+ MethodPtr method = (MethodPtr)m;
+ return write__artifact__method(writer, artifacts, method);
+}
+
+int write__artifact__method__serialize(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) {
+ assert(m != NULL, "invariant");
+ MethodPtr method = (MethodPtr)m;
+ int result = write__artifact__method(writer, artifacts, method);
+ if (METHOD_NOT_SERIALIZED(method)) {
+ SET_METHOD_SERIALIZED(method);
+ }
+ assert(IS_METHOD_SERIALIZED(method), "invariant");
+ return result;
+}
+
+typedef JfrArtifactWriterImplHost<MethodPtr, write__artifact__method__leakp> LeakpMethodWriterImplTarget;
+typedef JfrArtifactWriterHost<LeakpMethodWriterImplTarget, TYPE_METHOD> LeakpMethodWriterImpl;
+typedef SerializePredicate<MethodPtr> MethodPredicate;
+typedef JfrPredicatedArtifactWriterImplHost<MethodPtr, MethodPredicate, write__artifact__method__serialize> MethodWriterImplTarget;
typedef JfrArtifactWriterHost<MethodWriterImplTarget, TYPE_METHOD> MethodWriterImpl;
-int write__artifact__package(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* p) {
+int write__artifact__package(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, PkgPtr pkg) {
assert(writer != NULL, "invariant");
assert(artifacts != NULL, "invariant");
- assert(p != NULL, "invariant");
- PkgPtr pkg = (PkgPtr)p;
+ assert(pkg != NULL, "invariant");
Symbol* const pkg_name = pkg->name();
const traceid package_name_symbol_id = pkg_name != NULL ? artifacts->mark(pkg_name) : 0;
assert(package_name_symbol_id > 0, "invariant");
@@ -194,17 +228,34 @@
return 1;
}
+int write__artifact__package__leakp(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* p) {
+ assert(p != NULL, "invariant");
+ PkgPtr pkg = (PkgPtr)p;
+ return write__artifact__package(writer, artifacts, pkg);
+}
+
+int write__artifact__package__serialize(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* p) {
+ assert(p != NULL, "invariant");
+ PkgPtr pkg = (PkgPtr)p;
+ int result = write__artifact__package(writer, artifacts, pkg);
+ if (IS_NOT_SERIALIZED(pkg)) {
+ SET_SERIALIZED(pkg);
+ }
+ assert(IS_SERIALIZED(pkg), "invariant");
+ return result;
+}
+
typedef LeakPredicate<PkgPtr> LeakPackagePredicate;
-int _compare_pkg_ptr_(PkgPtr const& lhs, PkgPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; }
-typedef UniquePredicate<PkgPtr, _compare_pkg_ptr_> PackagePredicate;
-typedef JfrPredicatedArtifactWriterImplHost<PkgPtr, LeakPackagePredicate, write__artifact__package> LeakPackageWriterImpl;
-typedef JfrPredicatedArtifactWriterImplHost<PkgPtr, PackagePredicate, write__artifact__package> PackageWriterImpl;
+//int _compare_pkg_ptr_(PkgPtr const& lhs, PkgPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; }
+//typedef UniquePredicate<PkgPtr, _compare_pkg_ptr_> PackagePredicate;
+typedef SerializePredicate<PkgPtr> PackagePredicate;
+typedef JfrPredicatedArtifactWriterImplHost<PkgPtr, LeakPackagePredicate, write__artifact__package__leakp> LeakPackageWriterImpl;
+typedef JfrPredicatedArtifactWriterImplHost<PkgPtr, PackagePredicate, write__artifact__package__serialize> PackageWriterImpl;
typedef JfrArtifactWriterHost<LeakPackageWriterImpl, TYPE_PACKAGE> LeakPackageWriter;
typedef JfrArtifactWriterHost<PackageWriterImpl, TYPE_PACKAGE> PackageWriter;
-int write__artifact__module(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) {
- assert( m != NULL, "invariant");
- ModPtr entry = (ModPtr)m;
+int write__artifact__module(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, ModPtr entry) {
+ assert(entry != NULL, "invariant");
Symbol* const module_name = entry->name();
const traceid module_name_symbol_id = module_name != NULL ? artifacts->mark(module_name) : 0;
Symbol* const module_version = entry->version();
@@ -219,17 +270,41 @@
return 1;
}
+int write__artifact__module__leakp(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) {
+ assert(m != NULL, "invariant");
+ ModPtr entry = (ModPtr)m;
+ return write__artifact__module(writer, artifacts, entry);
+}
+
+int write__artifact__module__serialize(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) {
+ assert(m != NULL, "invariant");
+ ModPtr entry = (ModPtr)m;
+ int result = write__artifact__module(writer, artifacts, entry);
+ CldPtr cld = entry->loader_data();
+ assert(cld != NULL, "invariant");
+ if (IS_NOT_SERIALIZED(cld)) {
+ if (!cld->is_unsafe_anonymous()) {
+ SET_USED_PREV_EPOCH(cld);
+ }
+ }
+ if (IS_NOT_SERIALIZED(entry)) {
+ SET_SERIALIZED(entry);
+ }
+ assert(IS_SERIALIZED(entry), "invariant");
+ return result;
+}
+
typedef LeakPredicate<ModPtr> LeakModulePredicate;
-int _compare_mod_ptr_(ModPtr const& lhs, ModPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; }
-typedef UniquePredicate<ModPtr, _compare_mod_ptr_> ModulePredicate;
-typedef JfrPredicatedArtifactWriterImplHost<ModPtr, LeakModulePredicate, write__artifact__module> LeakModuleWriterImpl;
-typedef JfrPredicatedArtifactWriterImplHost<ModPtr, ModulePredicate, write__artifact__module> ModuleWriterImpl;
+//int _compare_mod_ptr_(ModPtr const& lhs, ModPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; }
+//typedef UniquePredicate<ModPtr, _compare_mod_ptr_> ModulePredicate;
+typedef SerializePredicate<ModPtr> ModulePredicate;
+typedef JfrPredicatedArtifactWriterImplHost<ModPtr, LeakModulePredicate, write__artifact__module__leakp> LeakModuleWriterImpl;
+typedef JfrPredicatedArtifactWriterImplHost<ModPtr, ModulePredicate, write__artifact__module__serialize> ModuleWriterImpl;
typedef JfrArtifactWriterHost<LeakModuleWriterImpl, TYPE_MODULE> LeakModuleWriter;
typedef JfrArtifactWriterHost<ModuleWriterImpl, TYPE_MODULE> ModuleWriter;
-int write__artifact__classloader(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* c) {
- assert(c != NULL, "invariant");
- CldPtr cld = (CldPtr)c;
+int write__artifact__classloader(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, CldPtr cld) {
+ assert(cld != NULL, "invariant");
assert(!cld->is_unsafe_anonymous(), "invariant");
const traceid cld_id = TRACE_ID(cld);
// class loader type
@@ -250,18 +325,40 @@
return 1;
}
-typedef LeakPredicate<CldPtr> LeakCldPredicate;
-int _compare_cld_ptr_(CldPtr const& lhs, CldPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; }
-typedef UniquePredicate<CldPtr, _compare_cld_ptr_> CldPredicate;
-typedef JfrPredicatedArtifactWriterImplHost<CldPtr, LeakCldPredicate, write__artifact__classloader> LeakCldWriterImpl;
-typedef JfrPredicatedArtifactWriterImplHost<CldPtr, CldPredicate, write__artifact__classloader> CldWriterImpl;
+int write__artifact__classloader__leakp(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* c) {
+ assert(c != NULL, "invariant");
+ CldPtr cld = (CldPtr)c;
+ int result = write__artifact__classloader(writer, artifacts, cld);
+ if (IS_NOT_LEAKP_SERIALIZED(cld)) {
+ SET_LEAKP_SERIALIZED(cld);
+ }
+ assert(IS_LEAKP_SERIALIZED(cld), "invariant");
+ return result;
+}
+
+int write__artifact__classloader__serialize(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* c) {
+ assert(c != NULL, "invariant");
+ CldPtr cld = (CldPtr)c;
+ int result = write__artifact__classloader(writer, artifacts, cld);
+ if (IS_NOT_SERIALIZED(cld)) {
+ SET_SERIALIZED(cld);
+ }
+ assert(IS_SERIALIZED(cld), "invariant");
+ return result;
+}
+
+typedef LeakSerializePredicate<CldPtr> LeakCldPredicate;
+//int _compare_cld_ptr_(CldPtr const& lhs, CldPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; }
+//typedef UniquePredicate<CldPtr, _compare_cld_ptr_> CldPredicate;
+typedef SerializePredicate<CldPtr> CldPredicate;
+typedef JfrPredicatedArtifactWriterImplHost<CldPtr, LeakCldPredicate, write__artifact__classloader__leakp> LeakCldWriterImpl;
+typedef JfrPredicatedArtifactWriterImplHost<CldPtr, CldPredicate, write__artifact__classloader__serialize> CldWriterImpl;
typedef JfrArtifactWriterHost<LeakCldWriterImpl, TYPE_CLASSLOADER> LeakCldWriter;
typedef JfrArtifactWriterHost<CldWriterImpl, TYPE_CLASSLOADER> CldWriter;
typedef const JfrSymbolId::SymbolEntry* SymbolEntryPtr;
-static int write__artifact__symbol__entry__(JfrCheckpointWriter* writer,
- SymbolEntryPtr entry) {
+static int write__artifact__symbol__entry__(JfrCheckpointWriter* writer, SymbolEntryPtr entry) {
assert(writer != NULL, "invariant");
assert(entry != NULL, "invariant");
ResourceMark rm;
@@ -336,12 +433,12 @@
typedef KlassPtr Type;
KlassSymbolWriterImpl(JfrCheckpointWriter* writer,
JfrArtifactSet* artifacts,
- bool class_unload) : _writer(writer),
+ bool current_epoch) : _writer(writer),
_artifacts(artifacts),
- _predicate(class_unload),
- _method_used_predicate(class_unload),
- _method_flag_predicate(class_unload),
- _unique_predicate(class_unload) {}
+ _predicate(current_epoch),
+ _method_used_predicate(current_epoch),
+ _method_flag_predicate(current_epoch),
+ _unique_predicate(current_epoch) {}
int operator()(KlassPtr klass) {
assert(klass != NULL, "invariant");
@@ -392,8 +489,7 @@
assert(pkg_name != NULL, "invariant");
SymbolEntryPtr package_symbol = this->_artifacts->map_symbol(pkg_name);
assert(package_symbol != NULL, "invariant");
- return _unique_predicate(package_symbol->id()) ?
- write__artifact__symbol__entry__(this->_writer, package_symbol) : 0;
+ return _unique_predicate(package_symbol->id()) ? write__artifact__symbol__entry__(this->_writer, package_symbol) : 0;
}
template <template <typename> class Predicate>
@@ -495,9 +591,7 @@
MethodUsedPredicate<false> _method_used_predicate;
public:
- ClearKlassAndMethods(bool class_unload) : _clear_klass_tag_bits(class_unload),
- _clear_method_flag(class_unload),
- _method_used_predicate(class_unload) {}
+ ClearKlassAndMethods(bool current_epoch) : _method_used_predicate(current_epoch) {}
bool operator()(KlassPtr klass) {
if (_method_used_predicate(klass)) {
const InstanceKlass* ik = InstanceKlass::cast(klass);
@@ -544,16 +638,17 @@
void JfrTypeSet::write_klass_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
assert(!_artifacts->has_klass_entries(), "invariant");
KlassArtifactRegistrator reg(_artifacts);
- KlassWriter kw(writer, _artifacts, _class_unload);
+ KlassWriter kw(writer, _artifacts, current_epoch());
KlassWriterRegistration kwr(&kw, ®);
if (leakp_writer == NULL) {
KlassCallback callback(&kwr);
_subsystem_callback = &callback;
do_klasses();
+ _artifacts->tally(kw);
return;
}
- TagLeakpKlassArtifact tagging(_class_unload);
- LeakKlassWriter lkw(leakp_writer, _artifacts, _class_unload);
+ TagLeakpKlassArtifact tagging(current_epoch());
+ LeakKlassWriter lkw(leakp_writer, _artifacts, current_epoch());
LeakpKlassArtifactTagging lpkat(&tagging, &lkw);
CompositeKlassWriter ckw(&lpkat, &kw);
CompositeKlassWriterRegistration ckwr(&ckw, ®);
@@ -567,12 +662,18 @@
ClearArtifact<PkgPtr> > PackageWriterWithClear;
typedef CompositeFunctor<PkgPtr,
+ PackageWriter,
+ UnTagArtifact<PkgPtr> > PackageWriterWithUnTag;
+typedef CompositeFunctor<PkgPtr,
LeakPackageWriter,
PackageWriter> CompositePackageWriter;
typedef CompositeFunctor<PkgPtr,
CompositePackageWriter,
ClearArtifact<PkgPtr> > CompositePackageWriterWithClear;
+typedef CompositeFunctor<PkgPtr,
+ CompositePackageWriter,
+ UnTagArtifact<PkgPtr> > CompositePackageWriterWithUnTag;
class PackageFieldSelector {
public:
@@ -587,11 +688,33 @@
PackageWriterWithClear> KlassPackageWriterWithClear;
typedef KlassToFieldEnvelope<PackageFieldSelector,
+ PackageWriterWithUnTag> KlassPackageWriterWithUnTag;
+typedef KlassToFieldEnvelope<PackageFieldSelector, PackageWriter> KlassPackageWriter;
+typedef KlassToFieldEnvelope<PackageFieldSelector, CompositePackageWriter> KlassCompositePackageWriter;
+typedef KlassToFieldEnvelope<PackageFieldSelector,
CompositePackageWriterWithClear> KlassCompositePackageWriterWithClear;
+typedef KlassToFieldEnvelope<PackageFieldSelector,
+ CompositePackageWriterWithUnTag> KlassCompositePackageWriterWithUnTag;
typedef JfrArtifactCallbackHost<PkgPtr, PackageWriterWithClear> PackageCallback;
typedef JfrArtifactCallbackHost<PkgPtr, CompositePackageWriterWithClear> CompositePackageCallback;
+static void write_package_constants_current_epoch(JfrArtifactSet* artifacts, JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
+ assert(artifacts != NULL, "invariant");
+ assert(artifacts->has_klass_entries(), "invariant");
+ PackageWriter pw(writer, artifacts, true);
+ if (leakp_writer == NULL) {
+ KlassPackageWriter kpw(&pw);
+ artifacts->iterate_klasses(kpw);
+ artifacts->tally(pw);
+ } else {
+ LeakPackageWriter lpw(leakp_writer, artifacts, true);
+ CompositePackageWriter cpw(&lpw, &pw);
+ KlassCompositePackageWriter kcpw(&cpw);
+ artifacts->iterate_klasses(kcpw);
+ }
+}
+
/*
* Composite operation
*
@@ -602,22 +725,30 @@
*/
void JfrTypeSet::write_package_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
assert(_artifacts->has_klass_entries(), "invariant");
- ClearArtifact<PkgPtr> clear(_class_unload);
- PackageWriter pw(writer, _artifacts, _class_unload);
+ if (current_epoch()) {
+ write_package_constants_current_epoch(_artifacts, writer, leakp_writer);
+ return;
+ }
+ assert(is_rotating(), "invariant");
+ PackageWriter pw(writer, _artifacts, false);
+ ClearArtifact<PkgPtr> clear;
+ UnTagArtifact<PkgPtr> untag;
if (leakp_writer == NULL) {
+ PackageWriterWithUnTag kpw(&pw, &untag);
+ KlassPackageWriterWithUnTag kpwwut(&kpw);
+ _artifacts->iterate_klasses(kpwwut);
PackageWriterWithClear pwwc(&pw, &clear);
- KlassPackageWriterWithClear kpwwc(&pwwc);
- _artifacts->iterate_klasses(kpwwc);
PackageCallback callback(&pwwc);
_subsystem_callback = &callback;
do_packages();
return;
}
- LeakPackageWriter lpw(leakp_writer, _artifacts, _class_unload);
+ LeakPackageWriter lpw(leakp_writer, _artifacts, false);
CompositePackageWriter cpw(&lpw, &pw);
+ CompositePackageWriterWithUnTag cpwwut(&cpw, &untag);
+ KlassCompositePackageWriterWithUnTag kcpw(&cpwwut);
+ _artifacts->iterate_klasses(kcpw);
CompositePackageWriterWithClear cpwwc(&cpw, &clear);
- KlassCompositePackageWriterWithClear ckpw(&cpwwc);
- _artifacts->iterate_klasses(ckpw);
CompositePackageCallback callback(&cpwwc);
_subsystem_callback = &callback;
do_packages();
@@ -628,12 +759,18 @@
ClearArtifact<ModPtr> > ModuleWriterWithClear;
typedef CompositeFunctor<ModPtr,
+ ModuleWriter,
+ UnTagArtifact<ModPtr> > ModuleWriterWithUnTag;
+typedef CompositeFunctor<ModPtr,
LeakModuleWriter,
ModuleWriter> CompositeModuleWriter;
typedef CompositeFunctor<ModPtr,
CompositeModuleWriter,
ClearArtifact<ModPtr> > CompositeModuleWriterWithClear;
+typedef CompositeFunctor<ModPtr,
+ CompositeModuleWriter,
+ UnTagArtifact<ModPtr> > CompositeModuleWriterWithUnTag;
typedef JfrArtifactCallbackHost<ModPtr, ModuleWriterWithClear> ModuleCallback;
typedef JfrArtifactCallbackHost<ModPtr, CompositeModuleWriterWithClear> CompositeModuleCallback;
@@ -652,8 +789,31 @@
ModuleWriterWithClear> KlassModuleWriterWithClear;
typedef KlassToFieldEnvelope<ModuleFieldSelector,
+ ModuleWriterWithUnTag> KlassModuleWriterWithUnTag;
+typedef KlassToFieldEnvelope<ModuleFieldSelector, ModuleWriter> KlassModuleWriter;
+typedef KlassToFieldEnvelope<ModuleFieldSelector, CompositeModuleWriter> KlassCompositeModuleWriter;
+typedef KlassToFieldEnvelope<ModuleFieldSelector,
CompositeModuleWriterWithClear> KlassCompositeModuleWriterWithClear;
+typedef KlassToFieldEnvelope<ModuleFieldSelector,
+ CompositeModuleWriterWithUnTag> KlassCompositeModuleWriterWithUnTag;
+
+static void write_module_constants_current_epoch(JfrArtifactSet* artifacts, JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
+ assert(artifacts != NULL, "invariant");
+ assert(artifacts->has_klass_entries(), "invariant");
+ ModuleWriter mw(writer, artifacts, true);
+ if (leakp_writer == NULL) {
+ KlassModuleWriter kmw(&mw);
+ artifacts->iterate_klasses(kmw);
+ artifacts->tally(mw);
+ } else {
+ LeakModuleWriter lmw(leakp_writer, artifacts, true);
+ CompositeModuleWriter cmw(&lmw, &mw);
+ KlassCompositeModuleWriter kcmw(&cmw);
+ artifacts->iterate_klasses(kcmw);
+ }
+}
+
/*
* Composite operation
*
@@ -663,30 +823,40 @@
*/
void JfrTypeSet::write_module_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
assert(_artifacts->has_klass_entries(), "invariant");
- ClearArtifact<ModPtr> clear(_class_unload);
- ModuleWriter mw(writer, _artifacts, _class_unload);
+ if (current_epoch()) {
+ write_module_constants_current_epoch(_artifacts, writer, leakp_writer);
+ return;
+ }
+ assert(is_rotating(), "invariant");
+ ClearArtifact<ModPtr> clear;
+ UnTagArtifact<ModPtr> untag;
+ ModuleWriter mw(writer, _artifacts, false);
if (leakp_writer == NULL) {
+ ModuleWriterWithUnTag kpw(&mw, &untag);
+ KlassModuleWriterWithUnTag kmwwut(&kpw);
+ _artifacts->iterate_klasses(kmwwut);
ModuleWriterWithClear mwwc(&mw, &clear);
- KlassModuleWriterWithClear kmwwc(&mwwc);
- _artifacts->iterate_klasses(kmwwc);
ModuleCallback callback(&mwwc);
_subsystem_callback = &callback;
do_modules();
return;
}
- LeakModuleWriter lmw(leakp_writer, _artifacts, _class_unload);
+ LeakModuleWriter lmw(leakp_writer, _artifacts, false);
CompositeModuleWriter cmw(&lmw, &mw);
+ CompositeModuleWriterWithUnTag cmwwut(&cmw, &untag);
+ KlassCompositeModuleWriterWithUnTag kcmw(&cmwwut);
+ _artifacts->iterate_klasses(kcmw);
CompositeModuleWriterWithClear cmwwc(&cmw, &clear);
- KlassCompositeModuleWriterWithClear kmwwc(&cmwwc);
- _artifacts->iterate_klasses(kmwwc);
CompositeModuleCallback callback(&cmwwc);
_subsystem_callback = &callback;
do_modules();
}
typedef CompositeFunctor<CldPtr, CldWriter, ClearArtifact<CldPtr> > CldWriterWithClear;
+typedef CompositeFunctor<CldPtr, CldWriter, UnTagArtifact<CldPtr> > CldWriterWithUnTag;
typedef CompositeFunctor<CldPtr, LeakCldWriter, CldWriter> CompositeCldWriter;
typedef CompositeFunctor<CldPtr, CompositeCldWriter, ClearArtifact<CldPtr> > CompositeCldWriterWithClear;
+typedef CompositeFunctor<CldPtr, CompositeCldWriter, UnTagArtifact<CldPtr> > CompositeCldWriterWithUnTag;
typedef JfrArtifactCallbackHost<CldPtr, CldWriterWithClear> CldCallback;
typedef JfrArtifactCallbackHost<CldPtr, CompositeCldWriterWithClear> CompositeCldCallback;
@@ -700,8 +870,28 @@
}
};
+typedef KlassToFieldEnvelope<CldFieldSelector, CldWriter> KlassCldWriter;
typedef KlassToFieldEnvelope<CldFieldSelector, CldWriterWithClear> KlassCldWriterWithClear;
+typedef KlassToFieldEnvelope<CldFieldSelector, CldWriterWithUnTag> KlassCldWriterWithUnTag;
+typedef KlassToFieldEnvelope<CldFieldSelector, CompositeCldWriter> KlassCompositeCldWriter;
typedef KlassToFieldEnvelope<CldFieldSelector, CompositeCldWriterWithClear> KlassCompositeCldWriterWithClear;
+typedef KlassToFieldEnvelope<CldFieldSelector, CompositeCldWriterWithUnTag> KlassCompositeCldWriterWithUnTag;
+
+static void write_class_loader_constants_current_epoch(JfrArtifactSet* artifacts, JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
+ assert(artifacts != NULL, "invariant");
+ assert(artifacts->has_klass_entries(), "invariant");
+ CldWriter cldw(writer, artifacts, true);
+ if (leakp_writer == NULL) {
+ KlassCldWriter kcw(&cldw);
+ artifacts->iterate_klasses(kcw);
+ artifacts->tally(cldw);
+ } else {
+ LeakCldWriter lcldw(leakp_writer, artifacts, true);
+ CompositeCldWriter ccldw(&lcldw, &cldw);
+ KlassCompositeCldWriter kccldw(&ccldw);
+ artifacts->iterate_klasses(kccldw);
+ }
+}
/*
* Composite operation
@@ -712,22 +902,30 @@
*/
void JfrTypeSet::write_class_loader_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
assert(_artifacts->has_klass_entries(), "invariant");
- ClearArtifact<CldPtr> clear(_class_unload);
- CldWriter cldw(writer, _artifacts, _class_unload);
+ if (current_epoch()) {
+ write_class_loader_constants_current_epoch(_artifacts, writer, leakp_writer);
+ return;
+ }
+ assert(is_rotating(), "invariant");
+ ClearArtifact<CldPtr> clear;
+ UnTagArtifact<CldPtr> untag;
+ CldWriter cldw(writer, _artifacts, false);
if (leakp_writer == NULL) {
+ CldWriterWithUnTag cldwut(&cldw, &untag);
+ KlassCldWriterWithUnTag kcldwut(&cldwut);
+ _artifacts->iterate_klasses(kcldwut);
CldWriterWithClear cldwwc(&cldw, &clear);
- KlassCldWriterWithClear kcldwwc(&cldwwc);
- _artifacts->iterate_klasses(kcldwwc);
CldCallback callback(&cldwwc);
_subsystem_callback = &callback;
do_class_loaders();
return;
}
- LeakCldWriter lcldw(leakp_writer, _artifacts, _class_unload);
+ LeakCldWriter lcldw(leakp_writer, _artifacts, false);
CompositeCldWriter ccldw(&lcldw, &cldw);
+ CompositeCldWriterWithUnTag cldwwut(&ccldw, &untag);
+ KlassCompositeCldWriterWithUnTag kccldw(&cldwwut);
+ _artifacts->iterate_klasses(kccldw);
CompositeCldWriterWithClear ccldwwc(&ccldw, &clear);
- KlassCompositeCldWriterWithClear kcclwwc(&ccldwwc);
- _artifacts->iterate_klasses(kcclwwc);
CompositeCldCallback callback(&ccldwwc);
_subsystem_callback = &callback;
do_class_loaders();
@@ -743,11 +941,11 @@
public:
MethodIteratorHost(JfrCheckpointWriter* writer,
JfrArtifactSet* artifacts,
- bool class_unload,
+ bool current_epoch,
bool skip_header = false) :
- _method_functor(writer, artifacts, class_unload, skip_header),
- _method_used_predicate(class_unload),
- _method_flag_predicate(class_unload) {}
+ _method_functor(writer, artifacts, current_epoch, skip_header),
+ _method_used_predicate(current_epoch),
+ _method_flag_predicate(current_epoch) {}
bool operator()(KlassPtr klass) {
if (_method_used_predicate(klass)) {
@@ -768,7 +966,7 @@
void add(int count) { _method_functor.add(count); }
};
-typedef MethodIteratorHost<true /*leakp */, MethodWriterImpl> LeakMethodWriter;
+typedef MethodIteratorHost<true /*leakp */, LeakpMethodWriterImpl> LeakMethodWriter;
typedef MethodIteratorHost<false, MethodWriterImpl> MethodWriter;
typedef CompositeFunctor<KlassPtr, LeakMethodWriter, MethodWriter> CompositeMethodWriter;
@@ -780,39 +978,56 @@
*/
void JfrTypeSet::write_method_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
assert(_artifacts->has_klass_entries(), "invariant");
- MethodWriter mw(writer, _artifacts, _class_unload);
+ MethodWriter mw(writer, _artifacts, is_not_rotating());
if (leakp_writer == NULL) {
_artifacts->iterate_klasses(mw);
+ _artifacts->tally(mw);
return;
}
- LeakMethodWriter lpmw(leakp_writer, _artifacts, _class_unload);
+ LeakMethodWriter lpmw(leakp_writer, _artifacts, is_not_rotating());
CompositeMethodWriter cmw(&lpmw, &mw);
_artifacts->iterate_klasses(cmw);
}
-static void write_symbols_leakp(JfrCheckpointWriter* leakp_writer, JfrArtifactSet* artifacts, bool class_unload) {
+
+static void write_symbols_leakp(JfrCheckpointWriter* leakp_writer, JfrArtifactSet* artifacts, bool current_epoch) {
assert(leakp_writer != NULL, "invariant");
assert(artifacts != NULL, "invariant");
- LeakKlassSymbolWriter lpksw(leakp_writer, artifacts, class_unload);
+ LeakKlassSymbolWriter lpksw(leakp_writer, artifacts, current_epoch);
artifacts->iterate_klasses(lpksw);
}
-static void write_symbols(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, JfrArtifactSet* artifacts, bool class_unload) {
+
+static void write_symbols(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, JfrArtifactSet* artifacts, bool current_epoch) {
assert(writer != NULL, "invariant");
assert(artifacts != NULL, "invariant");
if (leakp_writer != NULL) {
- write_symbols_leakp(leakp_writer, artifacts, class_unload);
+ write_symbols_leakp(leakp_writer, artifacts, current_epoch);
}
// iterate all registered symbols
- SymbolEntryWriter symbol_writer(writer, artifacts, class_unload);
+ SymbolEntryWriter symbol_writer(writer, artifacts, current_epoch);
artifacts->iterate_symbols(symbol_writer);
- CStringEntryWriter cstring_writer(writer, artifacts, class_unload, true); // skip header
+ CStringEntryWriter cstring_writer(writer, artifacts, current_epoch, true); // skip header
artifacts->iterate_cstrings(cstring_writer);
symbol_writer.add(cstring_writer.count());
+ artifacts->tally(symbol_writer);
}
bool JfrTypeSet::_class_unload = false;
+bool JfrTypeSet::_flushpoint = false;
JfrArtifactSet* JfrTypeSet::_artifacts = NULL;
JfrArtifactClosure* JfrTypeSet::_subsystem_callback = NULL;
+bool JfrTypeSet::is_rotating() {
+ return !(_class_unload || _flushpoint);
+}
+
+bool JfrTypeSet::is_not_rotating() {
+ return !is_rotating();
+}
+
+bool JfrTypeSet::current_epoch() {
+ return is_not_rotating();
+}
+
void JfrTypeSet::write_symbol_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
assert(writer != NULL, "invariant");
assert(_artifacts->has_klass_entries(), "invariant");
@@ -838,12 +1053,23 @@
void JfrTypeSet::do_klass(Klass* klass) {
assert(klass != NULL, "invariant");
assert(_subsystem_callback != NULL, "invariant");
- if (USED_PREV_EPOCH(klass)) { // includes leakp subset
- _subsystem_callback->do_artifact(klass);
- return;
+ if (_flushpoint) {
+ if (USED_THIS_EPOCH(klass)) {
+ _subsystem_callback->do_artifact(klass);
+ return;
+ }
+ } else {
+ if (USED_PREV_EPOCH(klass)) { // includes leakp subset
+ _subsystem_callback->do_artifact(klass);
+ return;
+ }
}
if (klass->is_subclass_of(SystemDictionary::ClassLoader_klass()) || klass == SystemDictionary::Object_klass()) {
- SET_LEAKP_USED_PREV_EPOCH(klass); // tag leakp "safe byte" for subset inclusion
+ if (_flushpoint) {
+ SET_LEAKP_USED_THIS_EPOCH(klass);
+ } else {
+ SET_LEAKP_USED_PREV_EPOCH(klass); // tag leakp "safe byte" for subset inclusion
+ }
_subsystem_callback->do_artifact(klass);
}
}
@@ -856,19 +1082,35 @@
ClassLoaderDataGraph::classes_do(&do_klass);
}
-void JfrTypeSet::do_unloaded_package(PackageEntry* entry) {
- assert(entry != NULL, "invariant");
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_THIS_EPOCH(entry)) { // includes leakp subset
- _subsystem_callback->do_artifact(entry);
+template <typename T>
+static void do_current_epoch_artifact(JfrArtifactClosure* callback, T* value) {
+ assert(callback != NULL, "invariant");
+ assert(value != NULL, "invariant");
+ if (ANY_USED_THIS_EPOCH(value)) { // includes leakp subset
+ callback->do_artifact(value);
}
}
+template <typename T>
+static void do_previous_epoch_artifact(JfrArtifactClosure* callback, T* value) {
+ assert(callback != NULL, "invariant");
+ assert(value != NULL, "invariant");
+ if (ANY_USED_PREV_EPOCH(value)) { // includes leakp subset
+ callback->do_artifact(value);
+ assert(IS_NOT_SERIALIZED(value), "invariant");
+ return;
+ }
+ if (IS_SERIALIZED(value)) {
+ UNSERIALIZE(value);
+ }
+ assert(IS_NOT_SERIALIZED(value), "invariant");
+}
+void JfrTypeSet::do_unloaded_package(PackageEntry* entry) {
+ do_current_epoch_artifact(_subsystem_callback, entry);
+}
+
void JfrTypeSet::do_package(PackageEntry* entry) {
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_PREV_EPOCH(entry)) { // includes leakp subset
- _subsystem_callback->do_artifact(entry);
- }
+ do_previous_epoch_artifact(_subsystem_callback, entry);
}
void JfrTypeSet::do_packages() {
@@ -878,19 +1120,13 @@
}
ClassLoaderDataGraph::packages_do(&do_package);
}
+
void JfrTypeSet::do_unloaded_module(ModuleEntry* entry) {
- assert(entry != NULL, "invariant");
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_THIS_EPOCH(entry)) { // includes leakp subset
- _subsystem_callback->do_artifact(entry);
- }
+ do_current_epoch_artifact(_subsystem_callback, entry);
}
void JfrTypeSet::do_module(ModuleEntry* entry) {
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_PREV_EPOCH(entry)) { // includes leakp subset
- _subsystem_callback->do_artifact(entry);
- }
+ do_previous_epoch_artifact(_subsystem_callback, entry);
}
void JfrTypeSet::do_modules() {
@@ -902,17 +1138,11 @@
}
void JfrTypeSet::do_unloaded_class_loader_data(ClassLoaderData* cld) {
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_THIS_EPOCH(cld)) { // includes leakp subset
- _subsystem_callback->do_artifact(cld);
- }
+ do_current_epoch_artifact(_subsystem_callback, cld);
}
void JfrTypeSet::do_class_loader_data(ClassLoaderData* cld) {
- assert(_subsystem_callback != NULL, "invariant");
- if (ANY_USED_PREV_EPOCH(cld)) { // includes leakp subset
- _subsystem_callback->do_artifact(cld);
- }
+ do_previous_epoch_artifact(_subsystem_callback, cld);
}
class CLDCallback : public CLDClosure {
@@ -921,7 +1151,7 @@
public:
CLDCallback(bool class_unload) : _class_unload(class_unload) {}
void do_cld(ClassLoaderData* cld) {
- assert(cld != NULL, "invariant");
+ assert(cld != NULL, "invariant");
if (cld->is_unsafe_anonymous()) {
return;
}
@@ -942,47 +1172,48 @@
ClassLoaderDataGraph::loaded_cld_do(&cld_cb);
}
-static void clear_artifacts(JfrArtifactSet* artifacts,
- bool class_unload) {
+static void clear_artifacts(JfrArtifactSet* artifacts, bool current_epoch) {
assert(artifacts != NULL, "invariant");
assert(artifacts->has_klass_entries(), "invariant");
// untag
- ClearKlassAndMethods clear(class_unload);
+ ClearKlassAndMethods clear(current_epoch);
artifacts->iterate_klasses(clear);
- artifacts->clear();
}
/**
* Write all "tagged" (in-use) constant artifacts and their dependencies.
*/
-void 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");
ResourceMark rm;
// initialization begin
_class_unload = class_unload;
+ _flushpoint = flushpoint;
++checkpoint_id;
if (_artifacts == NULL) {
- _artifacts = new JfrArtifactSet(class_unload);
- _subsystem_callback = NULL;
+ _artifacts = new JfrArtifactSet(current_epoch());
} else {
- _artifacts->initialize(class_unload);
- _subsystem_callback = NULL;
+ _artifacts->initialize(current_epoch());
}
assert(_artifacts != NULL, "invariant");
assert(!_artifacts->has_klass_entries(), "invariant");
- assert(_subsystem_callback == NULL, "invariant");
// initialization complete
// write order is important because an individual write step
// might tag an artifact to be written in a subsequent step
write_klass_constants(writer, leakp_writer);
- if (_artifacts->has_klass_entries()) {
- write_package_constants(writer, leakp_writer);
- write_module_constants(writer, leakp_writer);
- write_class_loader_constants(writer, leakp_writer);
- write_method_constants(writer, leakp_writer);
- write_symbol_constants(writer, leakp_writer);
+ if (!_artifacts->has_klass_entries()) {
+ return 0;
+ }
+ write_package_constants(writer, leakp_writer);
+ write_module_constants(writer, leakp_writer);
+ write_class_loader_constants(writer, leakp_writer);
+ write_method_constants(writer, leakp_writer);
+ write_symbol_constants(writer, leakp_writer);
+ const size_t total_count = _artifacts->total_count();
+ if (!flushpoint) {
clear_artifacts(_artifacts, class_unload);
}
+ return total_count;
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.hpp Fri May 17 16:02:27 2019 +0200
@@ -44,6 +44,10 @@
static JfrArtifactSet* _artifacts;
static JfrArtifactClosure* _subsystem_callback;
static bool _class_unload;
+ static bool _flushpoint;
+ static bool is_rotating();
+ static bool is_not_rotating();
+ static bool current_epoch();
static void do_klass(Klass* k);
static void do_unloaded_klass(Klass* k);
@@ -67,7 +71,7 @@
static void write_class_loader_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
static void write_method_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
static void write_symbol_constants(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
- static void serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload);
+ static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint);
};
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESET_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -196,20 +196,22 @@
return (uintptr_t)const_cast<Symbol*>(sym)->identity_hash();
}
-JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_id(new JfrSymbolId()),
- _klass_list(NULL),
- _class_unload(class_unload) {
- initialize(class_unload);
+JfrArtifactSet::JfrArtifactSet(bool current_epoch) : _symbol_id(new JfrSymbolId()),
+ _klass_list(NULL),
+ _total_count(0),
+ _current_epoch(current_epoch) {
+ initialize(current_epoch);
assert(_klass_list != NULL, "invariant");
}
static const size_t initial_class_list_size = 200;
-void JfrArtifactSet::initialize(bool class_unload) {
+void JfrArtifactSet::initialize(bool current_epoch) {
assert(_symbol_id != NULL, "invariant");
_symbol_id->initialize();
assert(!_symbol_id->has_entries(), "invariant");
_symbol_id->mark(BOOTSTRAP_LOADER_NAME, 0); // pre-load "bootstrap"
- _class_unload = class_unload;
+ _total_count = 0;
+ _current_epoch = current_epoch;
// resource allocation
_klass_list = new GrowableArray<const Klass*>(initial_class_list_size, false, mtTracing);
}
@@ -269,3 +271,8 @@
assert(_klass_list->find(k) == -1, "invariant");
_klass_list->append(k);
}
+
+size_t JfrArtifactSet::total_count() const {
+ return _total_count;
+}
+
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp Fri May 17 16:02:27 2019 +0200
@@ -76,9 +76,9 @@
};
template <typename T>
-void tag_leakp_artifact(T const& value, bool class_unload) {
+void tag_leakp_artifact(T const& value, bool current_epoch) {
assert(value != NULL, "invariant");
- if (class_unload) {
+ if (current_epoch) {
SET_LEAKP_USED_THIS_EPOCH(value);
assert(LEAKP_USED_THIS_EPOCH(value), "invariant");
} else {
@@ -89,11 +89,11 @@
template <typename T>
class LeakpClearArtifact {
- bool _class_unload;
+ bool _current_epoch;
public:
- LeakpClearArtifact(bool class_unload) : _class_unload(class_unload) {}
+ LeakpClearArtifact(bool current_epoch) : _current_epoch(current_epoch) {}
bool operator()(T const& value) {
- if (_class_unload) {
+ if (_current_epoch) {
if (LEAKP_USED_THIS_EPOCH(value)) {
LEAKP_UNUSE_THIS_EPOCH(value);
}
@@ -107,50 +107,65 @@
};
template <typename T>
+class UnTagArtifact {
+ public:
+ UnTagArtifact() {}
+ bool operator()(T const& value) {
+ if (LEAKP_USED_PREV_EPOCH(value)) {
+ LEAKP_UNUSE_PREV_EPOCH(value);
+ }
+ if (USED_PREV_EPOCH(value)) {
+ UNUSE_PREV_EPOCH(value);
+ }
+ return true;
+ }
+};
+
+template <typename T>
class ClearArtifact {
- bool _class_unload;
public:
- ClearArtifact(bool class_unload) : _class_unload(class_unload) {}
bool operator()(T const& value) {
- if (_class_unload) {
- if (LEAKP_USED_THIS_EPOCH(value)) {
- LEAKP_UNUSE_THIS_EPOCH(value);
- }
- if (USED_THIS_EPOCH(value)) {
- UNUSE_THIS_EPOCH(value);
- }
- if (METHOD_USED_THIS_EPOCH(value)) {
- UNUSE_METHOD_THIS_EPOCH(value);
- }
- } else {
- if (LEAKP_USED_PREV_EPOCH(value)) {
- LEAKP_UNUSE_PREV_EPOCH(value);
- }
- if (USED_PREV_EPOCH(value)) {
- UNUSE_PREV_EPOCH(value);
- }
- if (METHOD_USED_PREV_EPOCH(value)) {
- UNUSE_METHOD_PREV_EPOCH(value);
- }
+ if (LEAKP_USED_PREV_EPOCH(value)) {
+ LEAKP_UNUSE_PREV_EPOCH(value);
+ }
+ if (USED_PREV_EPOCH(value)) {
+ UNUSE_PREV_EPOCH(value);
+ }
+ if (IS_SERIALIZED(value)) {
+ UNSERIALIZE(value);
}
+ assert(IS_NOT_SERIALIZED(value), "invariant");
+ return true;
+ }
+};
+
+template <>
+class ClearArtifact<const Klass*> {
+ public:
+ bool operator()(const Klass* klass) {
+ if (LEAKP_USED_PREV_EPOCH(klass)) {
+ LEAKP_UNUSE_PREV_EPOCH(klass);
+ }
+ if (USED_PREV_EPOCH(klass)) {
+ UNUSE_PREV_EPOCH(klass);
+ }
+ if (METHOD_USED_PREV_EPOCH(klass)) {
+ UNUSE_METHOD_PREV_EPOCH(klass);
+ }
+ if (IS_SERIALIZED(klass)) {
+ UNSERIALIZE(klass);
+ }
+ assert(IS_NOT_SERIALIZED(klass), "invariant");
return true;
}
};
template <>
class ClearArtifact<const Method*> {
- bool _class_unload;
public:
- ClearArtifact(bool class_unload) : _class_unload(class_unload) {}
bool operator()(const Method* method) {
- if (_class_unload) {
- if (METHOD_FLAG_USED_THIS_EPOCH(method)) {
- CLEAR_METHOD_FLAG_USED_THIS_EPOCH(method);
- }
- } else {
- if (METHOD_FLAG_USED_PREV_EPOCH(method)) {
- CLEAR_METHOD_FLAG_USED_PREV_EPOCH(method);
- }
+ if (METHOD_FLAG_USED_PREV_EPOCH(method)) {
+ CLEAR_METHOD_FLAG_USED_PREV_EPOCH(method);
}
return true;
}
@@ -158,21 +173,53 @@
template <typename T>
class LeakPredicate {
- bool _class_unload;
+ bool _current_epoch;
public:
- LeakPredicate(bool class_unload) : _class_unload(class_unload) {}
+ LeakPredicate(bool current_epoch) : _current_epoch(current_epoch) {}
bool operator()(T const& value) {
- return _class_unload ? LEAKP_USED_THIS_EPOCH(value) : LEAKP_USED_PREV_EPOCH(value);
+ return _current_epoch ? LEAKP_USED_THIS_EPOCH(value) : LEAKP_USED_PREV_EPOCH(value);
+ }
+};
+
+template <typename T>
+class LeakSerializePredicate {
+ LeakPredicate<T> _leak_predicate;
+ public:
+ LeakSerializePredicate(bool current_epoch) : _leak_predicate(current_epoch) {}
+ bool operator()(T const& value) {
+ return IS_NOT_LEAKP_SERIALIZED(value) && _leak_predicate(value);
}
};
template <typename T>
class UsedPredicate {
- bool _class_unload;
+ bool _current_epoch;
+ public:
+ UsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {}
+ bool operator()(T const& value) {
+ return _current_epoch ? USED_THIS_EPOCH(value) : USED_PREV_EPOCH(value);
+ }
+};
+
+template <typename T>
+class SerializePredicate {
+ bool _current_epoch;
public:
- UsedPredicate(bool class_unload) : _class_unload(class_unload) {}
+ SerializePredicate(bool current_epoch) : _current_epoch(current_epoch) {}
bool operator()(T const& value) {
- return _class_unload ? USED_THIS_EPOCH(value) : USED_PREV_EPOCH(value);
+ assert(value != NULL, "invariant");
+ return IS_NOT_SERIALIZED(value);
+ }
+};
+
+template <>
+class SerializePredicate<const Method*> {
+ bool _current_epoch;
+public:
+ SerializePredicate(bool current_epoch) : _current_epoch(current_epoch) {}
+ bool operator()(const Method* method) {
+ assert(method != NULL, "invariant");
+ return METHOD_NOT_SERIALIZED(method);
}
};
@@ -194,22 +241,22 @@
};
class MethodFlagPredicate {
- bool _class_unload;
+ bool _current_epoch;
public:
- MethodFlagPredicate(bool class_unload) : _class_unload(class_unload) {}
+ MethodFlagPredicate(bool current_epoch) : _current_epoch(current_epoch) {}
bool operator()(const Method* method) {
- return _class_unload ? METHOD_FLAG_USED_THIS_EPOCH(method) : METHOD_FLAG_USED_PREV_EPOCH(method);
+ return _current_epoch ? METHOD_FLAG_USED_THIS_EPOCH(method) : METHOD_FLAG_USED_PREV_EPOCH(method);
}
};
template <bool leakp>
class MethodUsedPredicate {
- bool _class_unload;
+ bool _current_epoch;
public:
- MethodUsedPredicate(bool class_unload) : _class_unload(class_unload) {}
+ MethodUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {}
bool operator()(const Klass* klass) {
assert(ANY_USED(klass), "invariant");
- if (_class_unload) {
+ if (_current_epoch) {
return leakp ? LEAKP_METHOD_USED_THIS_EPOCH(klass) : METHOD_USED_THIS_EPOCH(klass);
}
return leakp ? LEAKP_METHOD_USED_PREV_EPOCH(klass) : METHOD_USED_PREV_EPOCH(klass);
@@ -313,14 +360,15 @@
private:
JfrSymbolId* _symbol_id;
GrowableArray<const Klass*>* _klass_list;
- bool _class_unload;
+ size_t _total_count;
+ bool _current_epoch;
public:
- JfrArtifactSet(bool class_unload);
+ JfrArtifactSet(bool current_epoch);
~JfrArtifactSet();
// caller needs ResourceMark
- void initialize(bool class_unload);
+ void initialize(bool current_epoch);
void clear();
traceid mark(const Symbol* sym, uintptr_t hash);
@@ -334,7 +382,9 @@
const JfrSymbolId::CStringEntry* map_cstring(uintptr_t hash) const;
bool has_klass_entries() const;
+ bool current_epoch() const { return _current_epoch; }
int entries() const;
+ size_t total_count() const;
void register_klass(const Klass* k);
template <typename Functor>
@@ -355,6 +405,12 @@
void iterate_cstrings(T& functor) {
_symbol_id->iterate_cstrings(functor);
}
+
+ template <typename Writer>
+ void tally(Writer& writer) {
+ _total_count += writer.count();
+ }
+
};
class KlassArtifactRegistrator {
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp Fri May 17 16:02:27 2019 +0200
@@ -41,8 +41,8 @@
public:
JfrArtifactWriterHost(JfrCheckpointWriter* writer,
JfrArtifactSet* artifacts,
- bool class_unload,
- bool skip_header = false) : _impl(writer, artifacts, class_unload),
+ bool current_epoch,
+ bool skip_header = false) : _impl(writer, artifacts, current_epoch),
_writer(writer),
_ctx(writer->context()),
_count(0),
@@ -82,11 +82,11 @@
private:
JfrCheckpointWriter* _writer;
JfrArtifactSet* _artifacts;
- bool _class_unload;
+ bool _current_epoch;
public:
typedef T Type;
- JfrArtifactWriterImplHost(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, bool class_unload) :
- _writer(writer), _artifacts(artifacts), _class_unload(class_unload) {}
+ JfrArtifactWriterImplHost(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, bool current_epoch) :
+ _writer(writer), _artifacts(artifacts), _current_epoch(current_epoch) {}
int operator()(T const& value) {
return op(this->_writer, this->_artifacts, value);
}
@@ -98,8 +98,8 @@
Predicate _predicate;
typedef JfrArtifactWriterImplHost<T, op> Parent;
public:
- JfrPredicatedArtifactWriterImplHost(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, bool class_unload) :
- Parent(writer, artifacts, class_unload), _predicate(class_unload) {}
+ JfrPredicatedArtifactWriterImplHost(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, bool current_epoch) :
+ Parent(writer, artifacts, current_epoch), _predicate(current_epoch) {}
int operator()(T const& value) {
return _predicate(value) ? Parent::operator()(value) : 0;
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp Fri May 17 16:02:27 2019 +0200
@@ -40,17 +40,11 @@
inline void set_bits(jbyte bits, jbyte* const dest) {
assert(dest != NULL, "invariant");
- const jbyte current = OrderAccess::load_acquire(dest);
+ const jbyte current = *dest;
if (bits != (current & bits)) {
- *dest |= bits;
- }
-}
+ *dest = current | bits;
-inline void set_mask(jbyte mask, jbyte* const dest) {
- assert(dest != NULL, "invariant");
- const jbyte current = OrderAccess::load_acquire(dest);
- if (mask != (current & mask)) {
- *dest &= mask;
+ OrderAccess::storestore();
}
}
@@ -82,6 +76,27 @@
} while (true);
}
+inline void set_mask(jbyte mask, jbyte* const dest) {
+ assert(dest != NULL, "invariant");
+ const jbyte current = *dest;
+ if (mask != (current & mask)) {
+ *dest = current & mask;
+ OrderAccess::storestore();
+ }
+}
+inline void set_mask_cas(jbyte mask, jbyte* const dest) {
+ assert(dest != NULL, "invariant");
+ do {
+ const jbyte current = OrderAccess::load_acquire(dest);
+ if (mask == (current & mask)) {
+ return;
+ }
+ const jbyte new_value = current & mask;
+ if (Atomic::cmpxchg(new_value, dest, current) == current) {
+ return;
+ }
+ } while (true);
+}
inline void set_traceid_bits(jbyte bits, traceid* dest) {
set_bits(bits, ((jbyte*)dest) + low_offset);
}
@@ -103,7 +118,7 @@
}
inline void set_leakp_traceid_mask(jbyte mask, traceid* dest) {
- set_mask(mask, ((jbyte*)dest) + leakp_offset);
+ set_mask_cas(mask, ((jbyte*)dest) + leakp_offset);
}
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDBITS_INLINE_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp Fri May 17 16:02:27 2019 +0200
@@ -32,12 +32,19 @@
#define METHOD_USED_BIT (USED_BIT << 2)
#define EPOCH_1_SHIFT 0
#define EPOCH_2_SHIFT 1
+#define SERIALIZED_SHIFT 2
+#define LEAKP_SERIALIZED_SHIFT 3
#define LEAKP_SHIFT 8
#define USED_EPOCH_1_BIT (USED_BIT << EPOCH_1_SHIFT)
#define USED_EPOCH_2_BIT (USED_BIT << EPOCH_2_SHIFT)
#define LEAKP_USED_EPOCH_1_BIT (USED_EPOCH_1_BIT << LEAKP_SHIFT)
#define LEAKP_USED_EPOCH_2_BIT (USED_EPOCH_2_BIT << LEAKP_SHIFT)
+#define SERIALIZED_BIT (USED_BIT << SERIALIZED_SHIFT)
+#define SERIALIZED_TEST_BIT (SERIALIZED_BIT << LEAKP_SHIFT)
+#define LEAKP_SERIALIZED_BIT (USED_BIT << LEAKP_SERIALIZED_SHIFT)
+#define LEAKP_SERIALIZED_TEST_BIT (LEAKP_SERIALIZED_BIT << LEAKP_SHIFT)
+#define METHOD_LEAKP_SERIALIZED_BIT ((USED_BIT << LEAKP_SERIALIZED_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_AND_CLASS_IN_USE_BITS (METHOD_USED_BIT | USED_BIT)
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp Fri May 17 16:02:27 2019 +0200
@@ -48,17 +48,24 @@
// #define JDK_JFR_EVENT_SUBKLASS 16
// #define JDK_JFR_EVENT_KLASS 32
// #define EVENT_HOST_KLASS 64
+// #define EVENT_RESERVED 128
#define IS_JDK_JFR_EVENT_SUBKLASS(ptr) (((ptr)->trace_id() & (JDK_JFR_EVENT_SUBKLASS)) != 0)
+#define IS_SERIALIZED(ptr) (((ptr)->trace_id() & (SERIALIZED_TEST_BIT)) != 0)
+#define IS_NOT_SERIALIZED(ptr) (!IS_SERIALIZED(ptr))
+#define IS_LEAKP_SERIALIZED(ptr) (((ptr)->trace_id() & (LEAKP_SERIALIZED_TEST_BIT)) != 0)
+#define IS_NOT_LEAKP_SERIALIZED(ptr) (!IS_LEAKP_SERIALIZED(ptr))
#define ANY_USED_BITS (USED_EPOCH_2_BIT | \
USED_EPOCH_1_BIT | \
METHOD_USED_EPOCH_2_BIT | \
METHOD_USED_EPOCH_1_BIT | \
+ LEAKP_USED_EPOCH_1_BIT | \
LEAKP_USED_EPOCH_2_BIT | \
- LEAKP_USED_EPOCH_1_BIT)
+ SERIALIZED_TEST_BIT | \
+ LEAKP_SERIALIZED_TEST_BIT)
-#define TRACE_ID_META_BITS (EVENT_HOST_KLASS | JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS | ANY_USED_BITS)
+#define TRACE_ID_META_BITS (EVENT_RESERVED | EVENT_HOST_KLASS | JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS | ANY_USED_BITS)
#define ANY_EVENT (EVENT_HOST_KLASS | JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS)
#define IS_JDK_JFR_EVENT_KLASS(ptr) (((ptr)->trace_id() & JDK_JFR_EVENT_KLASS) != 0)
@@ -76,13 +83,17 @@
#define TRACE_ID_MASKED_PTR(ptr) (TRACE_ID_MASKED((ptr)->trace_id()))
#define TRACE_ID_RAW(ptr) ((ptr)->trace_id())
#define TRACE_ID(ptr) (TRACE_ID_MASKED_PTR(ptr) >> TRACE_ID_SHIFT)
-#define METHOD_ID(kls, meth) (TRACE_ID_MASKED_PTR(kls) | (meth)->method_idnum())
+#define METHOD_ID(kls, meth) (TRACE_ID_MASKED_PTR(kls) | (meth)->orig_method_idnum())
#define SET_TAG(ptr, tag) (set_traceid_bits(tag, (ptr)->trace_id_addr()))
-#define SET_LEAKP_TAG(ptr, tag) (set_leakp_traceid_bits(tag, (ptr)->trace_id_addr()))
#define SET_TAG_CAS(ptr, tag) (set_traceid_bits_cas(tag, (ptr)->trace_id_addr()))
#define SET_LEAKP_TAG_CAS(ptr, tag) (set_leakp_traceid_bits_cas(tag, (ptr)->trace_id_addr()))
+#define SET_LEAKP_TAG(ptr, tag) (SET_LEAKP_TAG_CAS(ptr, tag))
+#define SET_SERIALIZED(ptr) (set_leakp_traceid_bits_cas((jbyte)SERIALIZED_BIT, (ptr)->trace_id_addr()))
+#define SET_LEAKP_SERIALIZED(ptr) (set_leakp_traceid_bits_cas((jbyte)LEAKP_SERIALIZED_BIT, (ptr)->trace_id_addr()))
+#define UNSERIALIZE_MASK (~(SERIALIZED_BIT | LEAKP_SERIALIZED_BIT))
#define IN_USE_THIS_EPOCH_BIT (JfrTraceIdEpoch::in_use_this_epoch_bit())
+#define IN_USE_THIS_EPOCH_UNSERIALIZED_BIT (IN_USE_THIS_EPOCH_BIT | SERIALIZED_TEST_BIT)
#define IN_USE_PREV_EPOCH_BIT (JfrTraceIdEpoch::in_use_prev_epoch_bit())
#define LEAKP_IN_USE_THIS_EPOCH_BIT (JfrTraceIdEpoch::leakp_in_use_this_epoch_bit())
#define LEAKP_IN_USE_PREV_EPOCH_BIT (JfrTraceIdEpoch::leakp_in_use_prev_epoch_bit())
@@ -107,13 +118,15 @@
#define SET_USED_THIS_EPOCH(ptr) (SET_TAG(ptr, IN_USE_THIS_EPOCH_BIT))
#define SET_USED_PREV_EPOCH(ptr) (SET_TAG_CAS(ptr, IN_USE_PREV_EPOCH_BIT))
-#define SET_LEAKP_USED_THIS_EPOCH(ptr) (SET_LEAKP_TAG(ptr, IN_USE_THIS_EPOCH_BIT))
-#define SET_LEAKP_USED_PREV_EPOCH(ptr) (SET_LEAKP_TAG(ptr, IN_USE_PREV_EPOCH_BIT))
+#define SET_LEAKP_USED_THIS_EPOCH(ptr) (SET_LEAKP_TAG_CAS(ptr, IN_USE_THIS_EPOCH_BIT))
+#define SET_LEAKP_USED_PREV_EPOCH(ptr) (SET_LEAKP_TAG_CAS(ptr, IN_USE_PREV_EPOCH_BIT))
#define SET_METHOD_AND_CLASS_USED_THIS_EPOCH(kls) (SET_TAG(kls, METHOD_AND_CLASS_IN_USE_THIS_EPOCH_BITS))
#define USED_THIS_EPOCH(ptr) (((ptr)->trace_id() & IN_USE_THIS_EPOCH_BIT) != 0)
+#define USED_THIS_EPOCH_UNSERIALIZED(ptr) ((USED_THIS_EPOCH(ptr)) && (IS_NOT_SERIALIZED(ptr)))
#define NOT_USED_THIS_EPOCH(ptr) (!USED_THIS_EPOCH(ptr))
#define USED_PREV_EPOCH(ptr) (((ptr)->trace_id() & IN_USE_PREV_EPOCH_BIT) != 0)
+#define USED_PREV_EPOCH_UNSERIALIZED(ptr) ((USED_PREV_EPOCH(ptr)) && (IS_NOT_SERIALIZED(ptr)))
#define NOT_USED_PREV_EPOCH(ptr) (!USED_PREV_EPOCH(ptr))
#define USED_ANY_EPOCH(ptr) (((ptr)->trace_id() & (USED_EPOCH_2_BIT | USED_EPOCH_1_BIT)) != 0)
#define NOT_USED_ANY_EPOCH(ptr) (!USED_ANY_EPOCH(ptr))
@@ -126,8 +139,10 @@
#define LEAKP_NOT_USED_ANY_EPOCH(ptr) (!LEAKP_USED_ANY_EPOCH(ptr))
#define ANY_USED_THIS_EPOCH(ptr) (((ptr)->trace_id() & (LEAKP_IN_USE_THIS_EPOCH_BIT | IN_USE_THIS_EPOCH_BIT)) != 0)
+#define ANY_USED_THIS_EPOCH_UNSERIALIZED(ptr) ((ANY_USED_THIS_EPOCH(ptr)) && (IS_NOT_SERIALIZED(ptr)))
#define ANY_NOT_USED_THIS_EPOCH(ptr) (!ANY_USED_THIS_EPOCH(ptr))
#define ANY_USED_PREV_EPOCH(ptr) (((ptr)->trace_id() & (LEAKP_IN_USE_PREV_EPOCH_BIT | IN_USE_PREV_EPOCH_BIT)) != 0)
+#define ANY_USED_PREV_EPOCH_UNSERIALIZED(ptr) ((ANY_USED_PREV_EPOCH(ptr)) && (IS_NOT_SERIALIZED(ptr)))
#define ANY_NOT_USED_PREV_EPOCH(ptr) (!ANY_USED_PREV_EPOCH(ptr))
#define METHOD_USED_THIS_EPOCH(kls) (((kls)->trace_id() & METHOD_IN_USE_THIS_EPOCH_BIT) != 0)
@@ -166,6 +181,7 @@
#define LEAKP_UNUSE_METHOD_THIS_EPOCH(kls) (set_leakp_traceid_mask(UNUSE_METHOD_THIS_EPOCH_MASK, (kls)->trace_id_addr()))
#define LEAKP_UNUSE_METHOD_PREV_EPOCH(kls) (set_leakp_traceid_mask(UNUSE_METHOD_PREV_EPOCH_MASK, (kls)->trace_id_addr()))
+#define UNSERIALIZE(ptr) (set_leakp_traceid_mask(UNSERIALIZE_MASK, (ptr)->trace_id_addr()))
#define ANY_USED(ptr) (((ptr)->trace_id() & ANY_USED_BITS) != 0)
#define ANY_NOT_USED(ptr) (!ANY_USED(ptr))
@@ -174,14 +190,22 @@
#define UNUSE_METHOD_AND_CLASS_PREV_EPOCH(kls) (set_traceid_mask(UNUSE_METHOD_AND_CLASS_PREV_EPOCH_MASK, (kls)->trace_id_addr()))
#define LEAKP_UNUSE_METHODS_AND_CLASS_PREV_EPOCH(kls) (set_leakp_traceid_mask(UNUSE_METHOD_AND_CLASS_PREV_EPOCH_MASK, (kls)->trace_id_addr()))
+#define IS_METHOD_SERIALIZED(m) ((m)->is_trace_flag_set((jbyte)SERIALIZED_BIT))
+#define METHOD_NOT_SERIALIZED(m) (!IS_METHOD_SERIALIZED(m))
+#define SET_METHOD_SERIALIZED(m) (set_bits_cas((jbyte)SERIALIZED_BIT, (m)->trace_flags_addr()))
+#define UNSERIALIZE_METHOD(m) (clear_bits_cas((jbyte)SERIALIZED_BIT, (m)->trace_flags_addr()))
+#define IS_LEAKP_METHOD_SERIALIZED(m) ((m)->is_trace_flag_set((jbyte)LEAKP_SERIALIZED_BIT))
+#define METHOD_NOT_LEAKP_SERIALIZED(m) (!IS_LEAKP_METHOD_SERIALIZED(m))
+#define SET_LEAKP_METHOD_SERIALIZED(m) (set_bits_cas((jbyte)LEAKP_SERIALIZED_BIT, (m)->trace_flags_addr()))
+#define UNSERIALIZE_LEAKP_METHOD(m) (clear_bits_cas((jbyte)LEAKP_SERIALIZED_BIT, (m)->trace_flags_addr()))
#define METHOD_FLAG_USED_THIS_EPOCH(m) ((m)->is_trace_flag_set((jbyte)JfrTraceIdEpoch::in_use_this_epoch_bit()))
#define METHOD_FLAG_NOT_USED_THIS_EPOCH(m) (!METHOD_FLAG_USED_THIS_EPOCH(m))
-#define SET_METHOD_FLAG_USED_THIS_EPOCH(m) ((m)->set_trace_flag((jbyte)JfrTraceIdEpoch::in_use_this_epoch_bit()))
+#define SET_METHOD_FLAG_USED_THIS_EPOCH(m) (set_bits_cas((jbyte)JfrTraceIdEpoch::in_use_this_epoch_bit(), (m)->trace_flags_addr()))
#define METHOD_FLAG_USED_PREV_EPOCH(m) ((m)->is_trace_flag_set((jbyte)JfrTraceIdEpoch::in_use_prev_epoch_bit()))
#define METHOD_FLAG_NOT_USED_PREV_EPOCH(m) (!METHOD_FLAG_USED_PREV_EPOCH(m))
#define METHOD_FLAG_USED_ANY_EPOCH(m) ((METHOD_FLAG_USED_THIS_EPOCH(m) || METHOD_FLAG_USED_PREV_EPOCH(m)) != 0)
#define METHOD_FLAG_NOT_USED_ANY_EPOCH(m) ((METHOD_FLAG_NOT_USED_THIS_EPOCH(m) && METHOD_FLAG_NOT_USED_PREV_EPOCH(m)) != 0)
-#define CLEAR_METHOD_FLAG_USED_THIS_EPOCH(m) (clear_bits_cas((jbyte)JfrTraceIdEpoch::in_use_this_epoch_bit(), (m)->trace_flags_addr()))
-#define CLEAR_METHOD_FLAG_USED_PREV_EPOCH(m) (clear_bits_cas((jbyte)JfrTraceIdEpoch::in_use_prev_epoch_bit(), (m)->trace_flags_addr()))
+#define CLEAR_METHOD_FLAG_USED_THIS_EPOCH(m) (clear_bits_cas((jbyte)JfrTraceIdEpoch::in_use_this_epoch_bit() | SERIALIZED_BIT, (m)->trace_flags_addr()))
+#define CLEAR_METHOD_FLAG_USED_PREV_EPOCH(m) (clear_bits_cas((jbyte)JfrTraceIdEpoch::in_use_prev_epoch_bit() | SERIALIZED_BIT, (m)->trace_flags_addr()))
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDMACROS_HPP
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp Fri May 17 16:02:27 2019 +0200
@@ -23,10 +23,11 @@
*/
#include "precompiled.hpp"
-#include "jfr/recorder/repository/jfrChunkState.hpp"
+#include "jfr/recorder/repository/jfrChunk.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/utilities/jfrTime.hpp"
+#include "jfr/utilities/jfrTimeConverter.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
@@ -34,85 +35,245 @@
static const u2 JFR_VERSION_MAJOR = 2;
static 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() {
- assert(_chunkstate == NULL, "invariant");
- _chunkstate = new JfrChunkState();
- return _chunkstate != NULL;
-}
+static const int64_t MAGIC_OFFSET = 0;
+static const int64_t MAGIC_LEN = 4;
+static const int64_t VERSION_OFFSET = MAGIC_LEN;
+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 const int64_t RESERVE_SIZE = GENERATION_OFFSET - (4 * SIZE_OFFSET);
+static const int64_t VOLATILE_FIELD_SIZE = SLOT_SIZE * 2;
+
+static const u1 COMPLETE = 0;
+static const u1 GUARD = 0xff;
+static const u1 PAD = 0;
+static const size_t GENERATION_SIZE = sizeof(u2);
+static const size_t HEAD_BUFFER_SIZE = HEADER_SIZE + SLOT_SIZE;
+
+typedef NoOwnershipAdapter JfrHeadBuffer; // stack local array as buffer
+typedef StreamWriterHost<JfrHeadBuffer, StackObj> JfrBufferedHeadWriter;
+typedef WriterHost<BigEndianEncoder, BigEndianEncoder, JfrBufferedHeadWriter> JfrHeadWriterBase;
+
+static uint8_t head_buffer[HEAD_BUFFER_SIZE] = {0};
static fio_fd open_chunk(const char* path) {
- assert(JfrStream_lock->owned_by_self(), "invariant");
return path != NULL ? os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE) : invalid_fd;
}
-bool JfrChunkWriter::open() {
- assert(_chunkstate != NULL, "invariant");
- JfrChunkWriterBase::reset(open_chunk(_chunkstate->path()));
- const bool is_open = this->has_valid_fd();
- if (is_open) {
- this->bytes("FLR", MAGIC_LEN);
- this->be_write((u2)JFR_VERSION_MAJOR);
- this->be_write((u2)JFR_VERSION_MINOR);
- this->reserve(6 * FILEHEADER_SLOT_SIZE);
- // u8 chunk_size
- // u8 initial checkpoint offset
- // u8 metadata section offset
- // u8 chunk start nanos
- // u8 chunk duration nanos
- // u8 chunk start ticks
- this->be_write(JfrTime::frequency());
+class JfrChunkHeadWriter : public StackObj {
+ friend class JfrChunkWriter;
+ private:
+ JfrChunkWriter* _writer;
+ JfrChunk* _chunk;
+
+ void write_magic() {
+ assert(MAGIC_OFFSET == _writer->current_offset(), "invariant");
+ _writer->bytes("FLR", MAGIC_LEN);
+ }
+
+ void write_version() {
+ assert(VERSION_OFFSET == _writer->current_offset(), "invariant");
+ _writer->be_write((u2)JFR_VERSION_MAJOR);
+ _writer->be_write((u2)JFR_VERSION_MINOR);
+ }
+
+ void write_size(int64_t size) {
+ assert(SIZE_OFFSET == _writer->current_offset(), "invariant");
+ _writer->be_write(size);
+ }
+
+ void write_checkpoint() {
+ assert(CHECKPOINT_OFFSET == _writer->current_offset(), "invariant");
+ _writer->be_write(_chunk->last_checkpoint_offset());
+ }
+
+ void write_metadata() {
+ assert(METADATA_OFFSET == _writer->current_offset(), "invariant");
+ _writer->be_write(_chunk->last_metadata_offset());
+ }
+
+ void write_time(bool finalize) {
+ assert(_writer->is_valid(), "invariant");
+ assert(_chunk != NULL, "invariant");
+ assert(START_NANOS_OFFSET == _writer->current_offset(), "invariant");
+ 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() {
+ assert(CPU_FREQUENCY_OFFSET == _writer->current_offset(), "invariant");
+ static const jlong frequency = JfrTime::frequency();
+ _writer->be_write(frequency);
+ }
+
+ void write_capabilities() {
+ assert(CAPABILITY_OFFSET == _writer->current_offset(), "invariant");
// chunk capabilities, CompressedIntegers etc
- this->be_write((u4)JfrOptionSet::compressed_integers() ? 1 : 0);
- _chunkstate->reset();
+ static bool compressed_integers = JfrOptionSet::compressed_integers();
+ _writer->be_write(compressed_integers ? (u2)1 : (u2)0);
+ }
+
+ void write_generation(bool finalize) {
+ assert(GENERATION_OFFSET == _writer->current_offset(), "invariant");
+ _writer->be_write(finalize ? COMPLETE : _chunk->generation());
+ _writer->be_write(PAD);
+ }
+
+ void write_guard() {
+ assert(GENERATION_OFFSET == _writer->current_offset(), "invariant");
+ _writer->be_write(GUARD);
+ _writer->be_write(PAD);
+ }
+
+ void write_guard_flush() {
+ assert(GENERATION_OFFSET == _writer->current_offset(), "invariant");
+ write_guard();
+ _writer->flush();
}
- return is_open;
+
+ void initialize() {
+ assert(_writer->is_valid(), "invariant");
+ assert(_chunk != NULL, "invariant");
+ assert(0 == _writer->current_offset(), "invariant");
+ write_magic();
+ write_version();
+ write_size(HEADER_SIZE);
+ write_checkpoint();
+ write_metadata();
+ write_time(false);
+ write_cpu_frequency();
+ write_generation(false);
+ write_capabilities();
+ assert(HEADER_SIZE == _writer->current_offset(), "invariant");
+ _writer->flush();
+ }
+
+ void flush(int64_t size, bool finalize) {
+ assert(_writer->is_valid(), "invariant");
+ assert(_chunk != NULL, "invariant");
+ assert(SIZE_OFFSET == _writer->current_offset(), "invariant");
+ write_size(size);
+ write_checkpoint();
+ write_metadata();
+ write_time(finalize);
+ write_cpu_frequency();
+ write_generation(finalize);
+ // no need to write capabilities
+ _writer->seek(size); // implicit flush
+ }
+
+ JfrChunkHeadWriter(JfrChunkWriter* writer, int64_t offset) : _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 {
+ _writer->seek(GENERATION_OFFSET);
+ write_guard();
+ _writer->seek(offset);
+ }
+ assert(offset == _writer->current_offset(), "invariant");
+ }
+};
+
+JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunk(new JfrChunk()) {}
+
+JfrChunkWriter::~JfrChunkWriter() {
+ assert(_chunk != NULL, "invariant");
+ delete _chunk;
}
-size_t JfrChunkWriter::close(int64_t metadata_offset) {
- write_header(metadata_offset);
- this->flush();
- this->close_fd();
- return (size_t)size_written();
+void JfrChunkWriter::set_path(const char* path) {
+ assert(_chunk != NULL, "invariant");
+ _chunk->set_path(path);
+}
+
+void JfrChunkWriter::time_stamp_chunk_now() {
+ assert(_chunk != NULL, "invariant");
+ _chunk->update_time_to_now();
}
-void JfrChunkWriter::write_header(int64_t metadata_offset) {
- assert(this->is_valid(), "invariant");
- // Chunk size
- this->write_be_at_offset(size_written(), CHUNK_SIZE_OFFSET);
- // initial checkpoint event offset
- this->write_be_at_offset(_chunkstate->previous_checkpoint_offset(), CHUNK_SIZE_OFFSET + (1 * FILEHEADER_SLOT_SIZE));
- // metadata event offset
- this->write_be_at_offset(metadata_offset, CHUNK_SIZE_OFFSET + (2 * FILEHEADER_SLOT_SIZE));
- // start of chunk in nanos since epoch
- this->write_be_at_offset(_chunkstate->previous_start_nanos(), CHUNK_SIZE_OFFSET + (3 * FILEHEADER_SLOT_SIZE));
- // duration of chunk in nanos
- this->write_be_at_offset(_chunkstate->last_chunk_duration(), CHUNK_SIZE_OFFSET + (4 * FILEHEADER_SLOT_SIZE));
- // start of chunk in ticks
- this->write_be_at_offset(_chunkstate->previous_start_ticks(), CHUNK_SIZE_OFFSET + (5 * FILEHEADER_SLOT_SIZE));
-}
-
-void JfrChunkWriter::set_chunk_path(const char* chunk_path) {
- _chunkstate->set_path(chunk_path);
+int64_t JfrChunkWriter::flushpoint(bool finalize) {
+ assert(_chunk != NULL, "invariant");
+ const int64_t sz_written = size_written();
+ if (!finalize) {
+ _chunk->update();
+ }
+ JfrChunkHeadWriter head(this, SIZE_OFFSET);
+ head.flush(sz_written, finalize);
+ return sz_written;
}
int64_t JfrChunkWriter::size_written() const {
return this->is_valid() ? this->current_offset() : 0;
}
-int64_t JfrChunkWriter::previous_checkpoint_offset() const {
- return _chunkstate->previous_checkpoint_offset();
+int64_t JfrChunkWriter::last_checkpoint_offset() const {
+ 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) {
+ assert(_chunk != NULL, "invariant");
+ _chunk->set_last_checkpoint_offset(offset);
+}
+
+bool JfrChunkWriter::is_initial_flushpoint_for_chunk() const {
+ assert(_chunk != NULL, "invariant");
+ assert(_chunk->is_started(), "invariant");
+ assert(!_chunk->is_finished(), "invariant");
+ return _chunk->is_initial_flush();
}
-void JfrChunkWriter::set_previous_checkpoint_offset(int64_t offset) {
- _chunkstate->set_previous_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() {
- _chunkstate->update_time_to_now();
+bool JfrChunkWriter::open() {
+ assert(_chunk != NULL, "invariant");
+ JfrChunkWriterBase::reset(open_chunk(_chunk->path()));
+ const bool is_open = this->has_valid_fd();
+ if (is_open) {
+ assert(0 == this->current_offset(), "invariant");
+ _chunk->reset();
+ JfrChunkHeadWriter head(this, HEADER_SIZE);
+ }
+ return is_open;
}
+
+int64_t JfrChunkWriter::close() {
+ assert(this->has_valid_fd(), "invariant");
+ const int64_t size_written = flushpoint(true);
+ this->close_fd();
+ assert(!this->is_valid(), "invariant");
+ return size_written;
+}
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.hpp Fri May 17 16:02:27 2019 +0200
@@ -29,28 +29,34 @@
#include "jfr/writers/jfrStreamWriterHost.inline.hpp"
#include "jfr/writers/jfrWriterHost.inline.hpp"
-typedef MallocAdapter<M> JfrStreamBuffer; // 1 mb buffered writes
-typedef StreamWriterHost<JfrStreamBuffer, JfrCHeapObj> JfrBufferedStreamWriter;
-typedef WriterHost<BigEndianEncoder, CompressedIntegerEncoder, JfrBufferedStreamWriter> JfrChunkWriterBase;
+typedef MallocAdapter<M> JfrChunkBuffer; // 1 mb buffered writes
+typedef StreamWriterHost<JfrChunkBuffer, JfrCHeapObj> JfrBufferedChunkWriter;
+typedef WriterHost<BigEndianEncoder, CompressedIntegerEncoder, JfrBufferedChunkWriter> JfrChunkWriterBase;
-class JfrChunkState;
+class JfrChunk;
+class JfrChunkHeadWriter;
class JfrChunkWriter : public JfrChunkWriterBase {
+ friend class JfrChunkHeadWriter;
friend class JfrRepository;
private:
- JfrChunkState* _chunkstate;
-
+ JfrChunk* _chunk;
+ void set_path(const char* path);
+ int64_t flushpoint(bool finalize);
bool open();
- size_t close(int64_t metadata_offset);
- void write_header(int64_t metadata_offset);
- void set_chunk_path(const char* chunk_path);
+ int64_t close();
+ int64_t current_chunk_start_nanos() const;
public:
JfrChunkWriter();
- bool initialize();
+ ~JfrChunkWriter();
+
int64_t size_written() const;
- int64_t previous_checkpoint_offset() const;
- void set_previous_checkpoint_offset(int64_t offset);
+ int64_t last_checkpoint_offset() const;
+ void set_last_checkpoint_offset(int64_t offset);
+ void set_last_metadata_offset(int64_t offset);
+ bool is_initial_flushpoint_for_chunk() const;
+ bool has_metadata() const;
void time_stamp_chunk_now();
};
--- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp Fri May 17 16:02:27 2019 +0200
@@ -248,7 +248,6 @@
}
static const char* create_emergency_dump_path() {
- assert(JfrStream_lock->owned_by_self(), "invariant");
char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, JVM_MAXPATHLEN);
if (NULL == buffer) {
return NULL;
@@ -291,7 +290,6 @@
// Caller needs ResourceMark
static const char* create_emergency_chunk_path(const char* repository_path) {
assert(repository_path != NULL, "invariant");
- assert(JfrStream_lock->owned_by_self(), "invariant");
const size_t repository_path_len = strlen(repository_path);
// date time
char date_time_buffer[32] = { 0 };
@@ -312,7 +310,6 @@
}
static fio_fd emergency_dump_file_descriptor() {
- assert(JfrStream_lock->owned_by_self(), "invariant");
ResourceMark rm;
const char* const emergency_dump_path = create_emergency_dump_path();
return emergency_dump_path != NULL ? open_exclusivly(emergency_dump_path) : invalid_fd;
@@ -325,7 +322,7 @@
void JfrEmergencyDump::on_vm_error(const char* repository_path) {
assert(repository_path != NULL, "invariant");
ResourceMark rm;
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
+
const fio_fd emergency_fd = emergency_dump_file_descriptor();
if (emergency_fd != invalid_fd) {
RepositoryIterator iterator(repository_path, strlen(repository_path));
@@ -409,10 +406,6 @@
JfrBuffer_lock->unlock();
}
- if (JfrStream_lock->owned_by_self()) {
- JfrStream_lock->unlock();
- }
-
if (JfrStacktrace_lock->owned_by_self()) {
JfrStacktrace_lock->unlock();
}
--- a/src/hotspot/share/jfr/recorder/repository/jfrRepository.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/repository/jfrRepository.cpp Fri May 17 16:02:27 2019 +0200
@@ -26,13 +26,14 @@
#include "jfr/jfr.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
-#include "jfr/recorder/repository/jfrChunkState.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
#include "jfr/recorder/repository/jfrRepository.hpp"
#include "jfr/recorder/service/jfrPostBox.hpp"
+#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/mutex.hpp"
+#include "runtime/os.hpp"
#include "runtime/thread.inline.hpp"
static JfrRepository* _instance = NULL;
@@ -43,11 +44,6 @@
static JfrChunkWriter* _chunkwriter = NULL;
-static bool initialize_chunkwriter() {
- assert(_chunkwriter == NULL, "invariant");
- _chunkwriter = new JfrChunkWriter();
- return _chunkwriter != NULL && _chunkwriter->initialize();
-}
JfrChunkWriter& JfrRepository::chunkwriter() {
return *_chunkwriter;
@@ -56,7 +52,9 @@
JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {}
bool JfrRepository::initialize() {
- return initialize_chunkwriter();
+ assert(_chunkwriter == NULL, "invariant");
+ _chunkwriter = new JfrChunkWriter();
+ return _chunkwriter != NULL;
}
JfrRepository::~JfrRepository() {
@@ -84,7 +82,7 @@
}
void JfrRepository::on_vm_error() {
- assert(!JfrStream_lock->owned_by_self(), "invariant");
+
if (_path == NULL) {
// completed already
return;
@@ -107,15 +105,19 @@
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() {
+ if (Jfr::is_recording()) {
+ // rotations are synchronous, block until rotation completes
+ instance()._post_box.post(MSG_ROTATE);
+ }
}
-void JfrRepository::notify_on_new_chunk_path() {
- if (Jfr::is_recording()) {
- 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();
}
/**
@@ -134,14 +136,11 @@
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
ResourceMark rm(jt);
const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt);
- {
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
- if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
- // new output is NULL and current output is NULL
- return;
- }
- instance().set_chunk_path(canonical_chunk_path);
+ if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
+ // new output is NULL and current output is NULL
+ return;
}
+ instance().set_chunk_path(canonical_chunk_path);
notify_on_new_chunk_path();
}
@@ -155,14 +154,26 @@
}
bool JfrRepository::open_chunk(bool vm_error /* false */) {
- assert(JfrStream_lock->owned_by_self(), "invariant");
if (vm_error) {
ResourceMark rm;
- _chunkwriter->set_chunk_path(JfrEmergencyDump::build_dump_path(_path));
+ _chunkwriter->set_path(JfrEmergencyDump::build_dump_path(_path));
}
return _chunkwriter->open();
}
-size_t JfrRepository::close_chunk(int64_t metadata_offset) {
- return _chunkwriter->close(metadata_offset);
+size_t JfrRepository::close_chunk() {
+ return _chunkwriter->close();
}
+
+void JfrRepository::flush(bool metadata, JavaThread* jt) {
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
+ assert(Jfr::is_recording(), "invariant");
+ if (!_chunkwriter->is_valid()) {
+ return;
+ }
+ instance()._post_box.post((metadata || _chunkwriter->is_initial_flushpoint_for_chunk()) ? MSG_FLUSHPOINT_METADATA : MSG_FLUSHPOINT);
+}
+
+size_t JfrRepository::flush_chunk() {
+ return _chunkwriter->flushpoint(false);
+}
--- a/src/hotspot/share/jfr/recorder/repository/jfrRepository.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/repository/jfrRepository.hpp Fri May 17 16:02:27 2019 +0200
@@ -55,8 +55,10 @@
bool set_path(const char* path);
void set_chunk_path(const char* path);
bool open_chunk(bool vm_error = false);
- size_t close_chunk(int64_t metadata_offset);
+ size_t close_chunk();
+ size_t flush_chunk();
void on_vm_error();
+
static void notify_on_new_chunk_path();
static JfrChunkWriter& chunkwriter();
@@ -68,6 +70,8 @@
public:
static void set_path(jstring location, JavaThread* jt);
static void set_chunk_path(jstring path, JavaThread* jt);
+ static void flush(bool metadata, JavaThread* jt);
+ static jlong current_chunk_start_nanos();
};
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRREPOSITORY_HPP
--- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp Fri May 17 16:02:27 2019 +0200
@@ -33,7 +33,9 @@
(MSGBIT(MSG_STOP)) | \
(MSGBIT(MSG_START)) | \
(MSGBIT(MSG_CLONE_IN_MEMORY)) | \
- (MSGBIT(MSG_VM_ERROR)) \
+ (MSGBIT(MSG_VM_ERROR)) | \
+ (MSGBIT(MSG_FLUSHPOINT)) | \
+ (MSGBIT(MSG_FLUSHPOINT_METADATA)) \
)
static JfrPostBox* _instance = NULL;
--- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp Fri May 17 16:02:27 2019 +0200
@@ -41,6 +41,8 @@
MSG_SHUTDOWN,
MSG_VM_ERROR,
MSG_DEADBUFFER,
+ MSG_FLUSHPOINT,
+ MSG_FLUSHPOINT_METADATA,
MSG_NO_OF_MSGS
};
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp Fri May 17 16:02:27 2019 +0200
@@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
+#include "jfrfiles/jfrEventClasses.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
@@ -75,6 +76,9 @@
static const int rotation_try_limit = 1000;
static const int rotation_retry_sleep_millis = 10;
+// incremented on each flushpoint
+static u8 flushpoint_id = 0;
+
class RotationLock : public StackObj {
private:
Thread* const _thread;
@@ -130,70 +134,171 @@
bool not_acquired() const { return !_acquired; }
};
+template <typename E, typename Instance, size_t(Instance::*func)()>
+class ServiceFunctor {
+ private:
+ Instance& _instance;
+ u4 _elements;
+ public:
+ typedef E EventType;
+ ServiceFunctor(Instance& instance) : _instance(instance), _elements(0) {}
+ bool process() {
+ _elements = (u4)(_instance.*func)();
+ return true;
+ }
+ u4 elements() const { return _elements; }
+};
+
+template <typename ContentFunctor>
+class WriteSubsystem : public StackObj {
+ protected:
+ const JfrTicks _start_time;
+ JfrTicks _end_time;
+ JfrChunkWriter& _cw;
+ ContentFunctor& _content_functor;
+ const int64_t _start_offset;
+ public:
+ typedef typename ContentFunctor::EventType EventType;
+
+ WriteSubsystem(JfrChunkWriter& cw, ContentFunctor& functor) :
+ _start_time(JfrTicks::now()),
+ _end_time(),
+ _cw(cw),
+ _content_functor(functor),
+ _start_offset(_cw.current_offset()) {
+ assert(_cw.is_valid(), "invariant");
+ }
+
+ bool process() {
+ // invocation
+ _content_functor.process();
+ _end_time = JfrTicks::now();
+ return 0 != _content_functor.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_functor.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());
+ }
+
+};
+
static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) {
- const int64_t prev_cp_offset = cw.previous_checkpoint_offset();
- const int64_t prev_cp_relative_offset = 0 == prev_cp_offset ? 0 : prev_cp_offset - cw.current_offset();
+ const int64_t last_cp_offset = cw.last_checkpoint_offset();
+ const int64_t last_cp_relative_offset = 0 == last_cp_offset ? 0 : last_cp_offset - cw.current_offset();
cw.reserve(sizeof(u4));
cw.write<u8>(EVENT_CHECKPOINT);
cw.write(JfrTicks::now());
- cw.write((int64_t)0);
- cw.write(prev_cp_relative_offset); // write previous checkpoint offset delta
+ cw.write<int64_t>((int64_t)0);
+ cw.write(last_cp_relative_offset); // write last checkpoint offset delta
cw.write<bool>(false); // flushpoint
- cw.write((u4)1); // nof types in this checkpoint
- cw.write(type_id);
+ cw.write<u4>((u4)1); // nof types in this checkpoint
+ cw.write<u8>(type_id);
const int64_t number_of_elements_offset = cw.current_offset();
cw.reserve(sizeof(u4));
return number_of_elements_offset;
}
template <typename ContentFunctor>
-class WriteCheckpointEvent : public StackObj {
+class WriteSubsystemCheckpointEvent : public WriteSubsystem<ContentFunctor> {
private:
- JfrChunkWriter& _cw;
- u8 _type_id;
- ContentFunctor& _content_functor;
+ const u8 _type_id;
public:
- WriteCheckpointEvent(JfrChunkWriter& cw, u8 type_id, ContentFunctor& functor) :
- _cw(cw),
- _type_id(type_id),
- _content_functor(functor) {
- assert(_cw.is_valid(), "invariant");
- }
+ WriteSubsystemCheckpointEvent(JfrChunkWriter& cw, ContentFunctor& functor, u8 type_id) :
+ WriteSubsystem<ContentFunctor>(cw, functor), _type_id(type_id) {}
+
bool process() {
- // current_cp_offset is also offset for the event size header field
- const int64_t current_cp_offset = _cw.current_offset();
- const int64_t num_elements_offset = write_checkpoint_event_prologue(_cw, _type_id);
- // invocation
- _content_functor.process();
- const u4 number_of_elements = (u4)_content_functor.processed();
- if (number_of_elements == 0) {
+ const int64_t num_elements_offset = write_checkpoint_event_prologue(this->_cw, _type_id);
+ if (!WriteSubsystem<ContentFunctor>::process()) {
// nothing to do, rewind writer to start
- _cw.seek(current_cp_offset);
- return true;
+ this->rewind();
+ assert(this->current_offset() == this->start_offset(), "invariant");
+ return false;
}
- assert(number_of_elements > 0, "invariant");
- assert(_cw.current_offset() > num_elements_offset, "invariant");
- _cw.write_padded_at_offset<u4>(number_of_elements, num_elements_offset);
- _cw.write_padded_at_offset<u4>((u4)_cw.current_offset() - current_cp_offset, current_cp_offset);
- // update writer with last checkpoint position
- _cw.set_previous_checkpoint_offset(current_cp_offset);
+ assert(this->elements() > 0, "invariant");
+ assert(this->current_offset() > num_elements_offset, "invariant");
+ this->write_elements(num_elements_offset);
+ this->write_size();
+ this->set_last_checkpoint();
return true;
}
};
-template <typename Instance, size_t(Instance::*func)()>
-class ServiceFunctor {
- private:
- Instance& _instance;
- size_t _processed;
- public:
- ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {}
- bool process() {
- _processed = (_instance.*func)();
- return true;
+template <typename Functor>
+static void write_flush_event(Functor& f) {
+ if (!Functor::is_event_enabled()) {
+ return;
}
- size_t processed() const { return _processed; }
-};
+ 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(Functor& f) {
+ f.process();
+ return f.elements();
+}
+
+template <typename Functor>
+static u4 invoke_with_flush_event(Functor& f) {
+ const u4 elements = invoke(f);
+ if (elements != 0) {
+ write_flush_event(f);
+ }
+ return elements;
+}
template <typename Instance, void(Instance::*func)()>
class JfrVMOperation : public VM_Operation {
@@ -206,22 +311,36 @@
Mode evaluation_mode() const { return _safepoint; } // default
};
-class WriteStackTraceRepository : public StackObj {
+class FlushStackTraceRepository : public StackObj {
private:
JfrStackTraceRepository& _repo;
JfrChunkWriter& _cw;
- size_t _elements_processed;
+ size_t _elements;
bool _clear;
public:
- WriteStackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) :
- _repo(repo), _cw(cw), _elements_processed(0), _clear(clear) {}
+ typedef EventFlushStacktrace EventType;
+ FlushStackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) :
+ _repo(repo), _cw(cw), _elements(0), _clear(clear) {}
bool process() {
- _elements_processed = _repo.write(_cw, _clear);
+ _elements = _repo.write(_cw, _clear);
return true;
}
- size_t processed() const { return _elements_processed; }
- void reset() { _elements_processed = 0; }
+ size_t elements() const { return _elements; }
+ void reset() { _elements = 0; }
+};
+
+class FlushMetadataEvent : public StackObj {
+ private:
+ JfrChunkWriter& _cw;
+ public:
+ typedef EventFlushMetadata EventType;
+ FlushMetadataEvent(JfrChunkWriter& cw) : _cw(cw) {}
+ bool process() {
+ JfrMetadataEvent::write(_cw);
+ return true;
+ }
+ size_t elements() const { return 1; }
};
static bool recording = false;
@@ -318,8 +437,7 @@
if (msgs & (MSGBIT(MSG_STOP))) {
stop();
}
- // action determined by chunkwriter state
- if (!_chunkwriter.is_valid()) {
+ if (!_storage.control().to_disk()) {
in_memory_rotation();
return;
}
@@ -338,22 +456,17 @@
}
void JfrRecorderService::open_new_chunk(bool vm_error) {
- assert(!_chunkwriter.is_valid(), "invariant");
- assert(!JfrStream_lock->owned_by_self(), "invariant");
JfrChunkRotation::on_rotation();
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
- if (!_repository.open_chunk(vm_error)) {
- assert(!_chunkwriter.is_valid(), "invariant");
- _storage.control().set_to_disk(false);
- return;
+ const bool valid_chunk = _repository.open_chunk(vm_error);
+ _storage.control().set_to_disk(valid_chunk);
+ if (valid_chunk) {
+ _checkpoint_manager.write_constants();
}
- assert(_chunkwriter.is_valid(), "invariant");
- _storage.control().set_to_disk(true);
}
void JfrRecorderService::in_memory_rotation() {
- assert(!_chunkwriter.is_valid(), "invariant");
// currently running an in-memory recording
+ assert(!_storage.control().to_disk(), "invariant");
open_new_chunk();
if (_chunkwriter.is_valid()) {
// dump all in-memory buffer data to the newly created chunk
@@ -362,8 +475,6 @@
}
void JfrRecorderService::serialize_storage_from_in_memory_recording() {
- assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!");
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
_storage.write();
}
@@ -375,7 +486,6 @@
void JfrRecorderService::finalize_current_chunk() {
assert(_chunkwriter.is_valid(), "invariant");
write();
- assert(!_chunkwriter.is_valid(), "invariant");
}
void JfrRecorderService::write() {
@@ -386,48 +496,160 @@
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;
+typedef ServiceFunctor<EventFlushStringPool, JfrStringPool, &JfrStringPool::write> FlushStringPoolFunctor;
+typedef ServiceFunctor<EventFlushStringPool, JfrStringPool, &JfrStringPool::write_at_safepoint> FlushStringPoolSafepointFunctor;
+typedef WriteSubsystemCheckpointEvent<FlushStackTraceRepository> FlushStackTraceCheckpoint;
+typedef WriteSubsystemCheckpointEvent<FlushStringPoolFunctor> FlushStringPoolCheckpoint;
+typedef WriteSubsystemCheckpointEvent<FlushStringPoolSafepointFunctor> FlushStringPoolCheckpointSafepoint;
+
+static u4 flush_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) {
+ FlushStackTraceRepository flush_stacktrace_repo(stack_trace_repo, chunkwriter, clear);
+ FlushStackTraceCheckpoint flush_stack_trace_checkpoint(chunkwriter, flush_stacktrace_repo, TYPE_STACKTRACE);
+ return invoke_with_flush_event(flush_stack_trace_checkpoint);
+}
+
+static u4 flush_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter) {
+ return flush_stacktrace(stack_trace_repo, chunkwriter, false);
+}
+
+static u4 flush_stacktrace_checkpoint(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) {
+ FlushStackTraceRepository flush_stacktrace_repo(stack_trace_repo, chunkwriter, clear);
+ FlushStackTraceCheckpoint flush_stack_trace_checkpoint(chunkwriter, flush_stacktrace_repo, TYPE_STACKTRACE);
+ return invoke(flush_stack_trace_checkpoint);
+}
+
+static u4 flush_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
+ FlushStringPoolFunctor flush_string_pool(string_pool);
+ FlushStringPoolCheckpoint flush_string_pool_checkpoint(chunkwriter, flush_string_pool, TYPE_STRING);
+ return invoke_with_flush_event(flush_string_pool_checkpoint);
+}
-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 u4 flush_stringpool_checkpoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
+ FlushStringPoolFunctor flush_string_pool(string_pool);
+ FlushStringPoolCheckpoint flush_string_pool_checkpoint(chunkwriter, flush_string_pool, TYPE_STRING);
+ return invoke(flush_string_pool_checkpoint);
+}
+
+static u4 flush_stringpool_checkpoint_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
+ FlushStringPoolSafepointFunctor flush_string_pool(string_pool);
+ FlushStringPoolCheckpointSafepoint flush_string_pool_checkpoint(chunkwriter, flush_string_pool, TYPE_STRING);
+ return invoke(flush_string_pool_checkpoint);
+}
+
+typedef ServiceFunctor<EventFlushTypeSet, JfrCheckpointManager, &JfrCheckpointManager::flush_type_set> FlushTypeSetFunctor;
+typedef WriteSubsystem<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);
+}
+
+typedef WriteSubsystem<FlushMetadataEvent> FlushMetadata;
+
+static u4 flush_metadata_event(JfrChunkWriter& chunkwriter) {
+ assert(chunkwriter.is_valid(), "invariant");
+ FlushMetadataEvent fme(chunkwriter);
+ FlushMetadata fm(chunkwriter, fme);
+ return invoke_with_flush_event(fm);
+}
+
+static u4 flush_metadata_event_checkpoint(JfrChunkWriter& chunkwriter) {
+ assert(chunkwriter.is_valid(), "invariant");
+ FlushMetadataEvent wme(chunkwriter);
+ FlushMetadata wm(chunkwriter, wme);
+ return invoke(wm);
}
-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 JfrBuffer* thread_local_buffer() {
+ return Thread::current()->jfr_thread_local()->native_buffer();
+}
+
+static void reset_buffer(JfrBuffer* buffer) {
+ assert(buffer != NULL, "invariant");
+ assert(buffer == thread_local_buffer(), "invariant");
+ buffer->set_pos(const_cast<u1*>(buffer->top()));
+ assert(buffer->empty(), "invariant");
+}
+
+static void reset_thread_local_buffer() {
+ reset_buffer(thread_local_buffer());
+}
+
+static void write_thread_local_buffer(JfrChunkWriter& chunkwriter) {
+ JfrBuffer * const buffer = thread_local_buffer();
+ assert(buffer != NULL, "invariant");
+ if (!buffer->empty()) {
+ chunkwriter.write_unbuffered(buffer->top(), buffer->pos() - buffer->top());
+ reset_buffer(buffer);
+ }
+ assert(buffer->empty(), "invariant");
+}
+
+typedef ServiceFunctor<EventFlushStorage, JfrStorage, &JfrStorage::write> FlushStorageFunctor;
+typedef WriteSubsystem<FlushStorageFunctor> FlushStorage;
+
+static size_t flush_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) {
+ assert(chunkwriter.is_valid(), "invariant");
+ FlushStorageFunctor fsf(storage);
+ FlushStorage fs(chunkwriter, fsf);
+ return invoke_with_flush_event(fs);
}
-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();
+typedef ServiceFunctor<EventFlush, JfrRecorderService, &JfrRecorderService::flush> FlushFunctor;
+typedef WriteSubsystem<FlushFunctor> Flush;
+
+static bool write_metadata_in_flushpoint = false;
+
+size_t JfrRecorderService::flush() {
+ size_t total_elements = 0;
+ if (write_metadata_in_flushpoint) {
+ total_elements = flush_metadata_event(_chunkwriter);
+ }
+ const size_t storage_elements = flush_storage(_storage, _chunkwriter);
+ if (0 == storage_elements) {
+ return total_elements;
+ }
+ total_elements += storage_elements;
+ total_elements += flush_stacktrace(_stack_trace_repository, _chunkwriter);
+ if (_string_pool.modified()) {
+ total_elements += flush_stringpool(_string_pool, _chunkwriter);
+ }
+ total_elements += flush_typeset(_checkpoint_manager, _chunkwriter);
+ return total_elements;
+}
+
+void JfrRecorderService::flush(int msgs) {
+ assert(_chunkwriter.is_valid(), "invariant");
+ ResourceMark rm;
+ HandleMark hm;
+ reset_thread_local_buffer();
+ ++flushpoint_id;
+ write_metadata_in_flushpoint = (msgs & MSGBIT(MSG_FLUSHPOINT_METADATA));
+ FlushFunctor flushpoint(*this);
+ Flush fl(_chunkwriter, flushpoint);
+ invoke_with_flush_event(fl);
+ write_thread_local_buffer(_chunkwriter);
+ _repository.flush_chunk();
}
//
// 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 storage ->
-// release stream lock
+// write checkpoint epoch transition list->
+// write stack trace checkpoint ->
+// write string pool checkpoint ->
+// notify about pending rotation ->
+// write storage
//
void JfrRecorderService::pre_safepoint_write() {
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
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);
+ flush_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, false);
+ if (_string_pool.modified()) {
+ flush_stringpool_checkpoint(_string_pool, _chunkwriter);
+ }
+ _checkpoint_manager.notify_types_on_rotation();
_storage.write();
}
@@ -444,47 +666,34 @@
//
// safepoint write sequence
//
-// lock stream lock ->
-// write object sample stacktraces ->
-// write stacktrace repository ->
-// write string pool ->
-// write safepoint dependent types ->
-// write storage ->
-// shift_epoch ->
-// update time ->
-// lock metadata descriptor ->
-// release stream lock
+// write object sample stacktraces ->
+// write stacktrace repository ->
+// write string pool ->
+// write storage ->
+// notify java threads ->
+// shift_epoch ->
+// update time
//
void JfrRecorderService::safepoint_write() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
write_object_sample_stacktrace(_stack_trace_repository);
- write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, true);
- write_stringpool_checkpoint_safepoint(_string_pool, _chunkwriter);
- _checkpoint_manager.write_safepoint_types();
+ flush_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, true);
+ if (_string_pool.modified()) {
+ flush_stringpool_checkpoint_safepoint(_string_pool, _chunkwriter);
+ }
_storage.write_at_safepoint();
+ _checkpoint_manager.notify_threads();
_checkpoint_manager.shift_epoch();
_chunkwriter.time_stamp_chunk_now();
- JfrMetadataEvent::lock();
-}
-
-static int64_t write_metadata_event(JfrChunkWriter& chunkwriter) {
- assert(chunkwriter.is_valid(), "invariant");
- const int64_t metadata_offset = chunkwriter.current_offset();
- JfrMetadataEvent::write(chunkwriter, metadata_offset);
- return metadata_offset;
}
//
// post-safepoint write sequence
//
-// lock stream lock ->
-// write type set ->
-// write checkpoints ->
-// write metadata event ->
-// write chunk header ->
-// close chunk fd ->
-// release stream lock
+// write type set ->
+// write checkpoints ->
+// write metadata event ->
+// close chunk
//
void JfrRecorderService::post_safepoint_write() {
assert(_chunkwriter.is_valid(), "invariant");
@@ -493,12 +702,11 @@
// already tagged artifacts for the previous epoch. We can accomplish this concurrently
// with threads now tagging artifacts in relation to the new, now updated, epoch and remain outside of a safepoint.
_checkpoint_manager.write_type_set();
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
// serialize any outstanding checkpoint memory
_checkpoint_manager.write();
// serialize the metadata descriptor event and close out the chunk
- _repository.close_chunk(write_metadata_event(_chunkwriter));
- assert(!_chunkwriter.is_valid(), "invariant");
+ flush_metadata_event_checkpoint(_chunkwriter);
+ _repository.close_chunk();
}
void JfrRecorderService::vm_error_rotation() {
@@ -512,7 +720,6 @@
void JfrRecorderService::finalize_current_chunk_on_vm_error() {
assert(_chunkwriter.is_valid(), "invariant");
pre_safepoint_write();
- JfrMetadataEvent::lock();
// Do not attempt safepoint dependent operations during emergency dump.
// Optimistically write tagged artifacts.
_checkpoint_manager.shift_epoch();
@@ -520,13 +727,10 @@
// update time
_chunkwriter.time_stamp_chunk_now();
post_safepoint_write();
- assert(!_chunkwriter.is_valid(), "invariant");
}
void JfrRecorderService::process_full_buffers() {
if (_chunkwriter.is_valid()) {
- assert(!JfrStream_lock->owned_by_self(), "invariant");
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
_storage.write_full();
}
}
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp Fri May 17 16:02:27 2019 +0200
@@ -68,10 +68,12 @@
JfrRecorderService();
void start();
void rotate(int msgs);
+ void flush(int msgs);
void process_full_buffers();
void scavenge();
void evaluate_chunk_size_for_rotation();
static bool is_recording();
+ size_t flush();
};
#endif // SHARE_JFR_RECORDER_SERVICE_JFRRECORDERSERVICE_HPP
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
+#include "jfr/jfr.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
@@ -64,7 +65,6 @@
if (allocation_failed) {
JfrJavaSupport::throw_out_of_memory_error("Unable to create native recording thread for JFR", CHECK_NULL);
}
-
Thread::start(new_thread);
return new_thread;
}
@@ -98,8 +98,9 @@
instanceHandle h_thread_oop(THREAD, (instanceOop)result.get_jobject());
assert(h_thread_oop.not_null(), "invariant");
// 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) {
+ Jfr::exclude_thread(t);
cp_manager->register_service_thread(t);
return true;
}
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp Fri May 17 16:02:27 2019 +0200
@@ -40,6 +40,7 @@
#define START (msgs & (MSGBIT(MSG_START)))
#define SHUTDOWN (msgs & MSGBIT(MSG_SHUTDOWN))
#define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)))
+ #define FLUSHPOINT (msgs & (MSGBIT(MSG_FLUSHPOINT)|MSGBIT(MSG_FLUSHPOINT_METADATA)))
#define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER)))
#define SCAVENGE (msgs & (MSGBIT(MSG_DEADBUFFER)))
@@ -72,6 +73,8 @@
service.start();
} else if (ROTATE) {
service.rotate(msgs);
+ } else if (FLUSHPOINT) {
+ service.flush(msgs);
}
JfrMsg_lock->lock();
post_box.notify_waiters();
@@ -90,6 +93,7 @@
#undef START
#undef SHUTDOWN
#undef ROTATE
+ #undef FLUSHPOINT
#undef PROCESS_FULL_BUFFERS
#undef SCAVENGE
}
--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp Fri May 17 16:02:27 2019 +0200
@@ -119,7 +119,7 @@
};
bool JfrStackTraceRepository::initialize() {
- return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType());
+ return JfrSerializer::register_serializer(TYPE_FRAMETYPE, true, new JfrFrameType());
}
size_t JfrStackTraceRepository::clear() {
@@ -196,7 +196,7 @@
*hash = tl->cached_stack_trace_hash();
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;
}
JfrStackFrame* frames = tl->stackframes();
@@ -367,7 +367,6 @@
assert(_method, "no method pointer");
assert(_line == 0, "already have linenumber");
_line = _method->line_number_from_bci(_bci);
- _method = NULL;
}
void JfrStackTrace::set_frame(u4 frame_pos, JfrStackFrame& frame) {
@@ -446,7 +445,7 @@
const int lineno = method->line_number_from_bci(bci);
// Can we determine if it's inlined?
_hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type);
- _frames[count] = JfrStackFrame(mid, bci, type, lineno);
+ _frames[count] = JfrStackFrame(method, mid, bci, type, lineno);
st.samples_next();
count++;
}
--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp Fri May 17 16:02:27 2019 +0200
@@ -53,8 +53,8 @@
JfrStackFrame(const traceid& id, int bci, int type, const Method* method) :
_method(method), _methodid(id), _line(0), _bci(bci), _type(type) {}
- JfrStackFrame(const traceid& id, int bci, int type, int lineno) :
- _method(NULL), _methodid(id), _line(lineno), _bci(bci), _type(type) {}
+ JfrStackFrame(const Method* method, const traceid& id, int bci, int type, int lineno) :
+ _method(method), _methodid(id), _line(lineno), _bci(bci), _type(type) {}
bool equals(const JfrStackFrame& rhs) const;
void write(JfrChunkWriter& cw) const;
void write(JfrCheckpointWriter& cpw) const;
--- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -54,10 +54,18 @@
return true;
}
-void JfrBuffer::reinitialize() {
+void JfrBuffer::reinitialize(bool exclusion /* false */) {
assert(!lease(), "invariant");
assert(!transient(), "invariant");
set_pos(start());
+ if (exclusion != excluded()) {
+ // update
+ if (exclusion) {
+ set_excluded();
+ } else {
+ clear_excluded();
+ }
+ }
clear_retired();
set_top(start());
}
@@ -136,6 +144,14 @@
_identity = NULL;
}
+bool JfrBuffer::acquired_by(const void* id) const {
+ return identity() == id;
+}
+
+bool JfrBuffer::acquired_by_self() const {
+ return acquired_by(Thread::current());
+}
+
#ifdef ASSERT
static bool validate_to(const JfrBuffer* const to, size_t size) {
assert(to != NULL, "invariant");
@@ -153,10 +169,6 @@
assert(t->top() + size <= t->pos(), "invariant");
return true;
}
-
-bool JfrBuffer::acquired_by_self() const {
- return identity() == Thread::current();
-}
#endif // ASSERT
void JfrBuffer::move(JfrBuffer* const to, size_t size) {
@@ -183,11 +195,11 @@
set_concurrent_top(start());
}
-// flags
enum FLAG {
RETIRED = 1,
TRANSIENT = 2,
- LEASE = 4
+ LEASE = 4,
+ EXCLUDED = 8
};
bool JfrBuffer::transient() const {
@@ -222,6 +234,22 @@
assert(!lease(), "invariant");
}
+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()) {
+ _flags ^= (u1)EXCLUDED;
+ }
+ assert(!excluded(), "invariant");
+}
+
static u2 load_acquire_flags(const u2* const flags) {
return OrderAccess::load_acquire(flags);
}
--- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp Fri May 17 16:02:27 2019 +0200
@@ -57,12 +57,11 @@
u4 _size;
const u1* stable_top() const;
- void clear_flags();
public:
JfrBuffer();
bool initialize(size_t header_size, size_t size, const void* id = NULL);
- void reinitialize();
+ void reinitialize(bool exclusion = false);
void concurrent_reinitialization();
size_t discard();
JfrBuffer* next() const {
@@ -150,6 +149,8 @@
void acquire(const void* id);
bool try_acquire(const void* id);
+ bool acquired_by(const void* id) const;
+ bool acquired_by_self() const;
void release();
void move(JfrBuffer* const to, size_t size);
@@ -167,7 +168,10 @@
void set_retired();
void clear_retired();
- debug_only(bool acquired_by_self() const;)
+ bool excluded() const;
+ void set_excluded();
+ void clear_excluded();
+
};
class JfrAgeNode : public JfrBuffer {
--- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp Fri May 17 16:02:27 2019 +0200
@@ -104,6 +104,7 @@
}
assert(t->empty(), "invariant");
assert(!t->retired(), "invariant");
+ assert(!t->excluded(), "invariant");
assert(t->identity() == NULL, "invariant");
if (!should_populate_cache()) {
remove_free(t);
@@ -346,19 +347,19 @@
template <typename Mspace>
inline bool ReleaseOp<Mspace>::process(typename Mspace::Type* t) {
assert(t != NULL, "invariant");
- if (t->retired() || t->try_acquire(_thread)) {
- if (t->transient()) {
- if (_release_full) {
- mspace_release_full_critical(t, _mspace);
- } else {
- mspace_release_free_critical(t, _mspace);
- }
- return true;
+ // assumes some means of exclusive access to t
+ if (t->transient()) {
+ if (_release_full) {
+ mspace_release_full_critical(t, _mspace);
+ } else {
+ mspace_release_free_critical(t, _mspace);
}
- t->reinitialize();
- assert(t->empty(), "invariant");
- t->release(); // publish
+ return true;
}
+ t->reinitialize();
+ assert(t->empty(), "invariant");
+ assert(!t->retired(), "invariant");
+ t->release(); // publish
return true;
}
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp Fri May 17 16:02:27 2019 +0200
@@ -26,6 +26,7 @@
#include "jfr/jfrEvents.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
+#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/service/jfrPostBox.hpp"
@@ -253,6 +254,18 @@
assert(buffer->empty(), "invariant");
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);
if (promotion_buffer == NULL) {
write_data_loss(buffer, thread);
@@ -339,9 +352,9 @@
void JfrStorage::register_full(BufferPtr buffer, Thread* thread) {
assert(buffer != NULL, "invariant");
assert(buffer->retired(), "invariant");
+ assert(buffer->acquired_by(thread), "invariant");
if (!full_buffer_registration(buffer, _age_mspace, control(), thread)) {
handle_registration_failure(buffer);
- buffer->release();
}
if (control().should_post_buffer_full_message()) {
_post_box.post(MSG_FULLBUFFER);
@@ -376,8 +389,8 @@
}
}
assert(buffer->empty(), "invariant");
+ assert(buffer->identity() != NULL, "invariant");
control().increment_dead();
- buffer->release();
buffer->set_retired();
}
@@ -465,6 +478,7 @@
assert(t != NULL, "invariant");
assert(cur != NULL, "invariant");
assert(cur->lease(), "invariant");
+ assert(!cur->excluded(), "invariant");
assert(cur_pos != NULL, "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");
@@ -491,6 +505,9 @@
// the case for stable thread local buffers; it is not the case for large buffers.
if (!cur->empty()) {
flush_regular_buffer(cur, t);
+ if (cur->excluded()) {
+ return cur;
+ }
}
assert(t->jfr_thread_local()->shelved_buffer() == NULL, "invariant");
if (cur->free_size() >= req) {
@@ -582,13 +599,13 @@
typedef ConcurrentWriteOpExcludeRetired<WriteOperation> ThreadLocalConcurrentWriteOperation;
size_t JfrStorage::write() {
- const size_t full_size_processed = write_full();
+ const size_t full_elements = write_full();
WriteOperation wo(_chunkwriter);
ThreadLocalConcurrentWriteOperation tlwo(wo);
process_full_list(tlwo, _thread_local_mspace);
ConcurrentWriteOperation cwo(wo);
process_free_list(cwo, _global_mspace);
- return full_size_processed + wo.processed();
+ return full_elements + wo.elements();
}
size_t JfrStorage::write_at_safepoint() {
@@ -600,7 +617,7 @@
process_full_list(writer, _transient_mspace);
assert(_global_mspace->is_full_empty(), "invariant");
process_free_list(writer, _global_mspace);
- return wo.processed();
+ return wo.elements();
}
typedef DiscardOp<DefaultDiscarder<JfrStorage::Buffer> > DiscardOperation;
@@ -608,14 +625,14 @@
typedef CompositeOperation<MutexedWriteOperation, ReleaseOperation> FullOperation;
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
process_full_list(discarder, _thread_local_mspace);
assert(_transient_mspace->is_free_empty(), "invariant");
process_full_list(discarder, _transient_mspace);
assert(_global_mspace->is_full_empty(), "invariant");
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) {
@@ -687,10 +704,9 @@
static void log(size_t count, size_t amount, bool clear = false) {
if (log_is_enabled(Debug, jfr, system)) {
- if (count > 0) {
+ assert(count > 0, "invariant");
log_debug(jfr, system)("%s " SIZE_FORMAT " full buffer(s) of " SIZE_FORMAT" B of data%s",
clear ? "Discarded" : "Wrote", count, amount, clear ? "." : " to chunk.");
- }
}
}
@@ -706,15 +722,25 @@
ReleaseOperation ro(_transient_mspace, thread);
FullOperation cmd(&writer, &ro);
const size_t count = process_full(cmd, control(), _age_mspace);
- log(count, writer.processed());
- return writer.processed();
+ if (0 == count) {
+ assert(0 == writer.elements(), "invariant");
+ return 0;
+ }
+ const size_t size = writer.size();
+ log(count, size);
+ return count;
}
size_t JfrStorage::clear_full() {
DiscardOperation discarder(mutexed); // a retired buffer implies mutexed access
const size_t count = process_full(discarder, control(), _age_mspace);
- log(count, discarder.processed(), true);
- return discarder.processed();
+ if (0 == count) {
+ 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) {
@@ -738,13 +764,18 @@
Scavenger(JfrStorageControl& control, Mspace* mspace) : _control(control), _mspace(mspace), _count(0), _amount(0) {}
bool process(Type* t) {
if (t->retired()) {
+ assert(t->identity() != NULL, "invariant");
+ assert(t->empty(), "invariant");
assert(!t->transient(), "invariant");
assert(!t->lease(), "invariant");
- assert(t->empty(), "invariant");
- assert(t->identity() == NULL, "invariant");
++_count;
_amount += t->total_size();
+ if (t->excluded()) {
+ t->clear_excluded();
+ }
+ assert(!t->excluded(), "invariant");
t->clear_retired();
+ t->release();
_control.decrement_dead();
mspace_release_full_critical(t, _mspace);
}
@@ -761,6 +792,11 @@
}
Scavenger<JfrThreadLocalMspace> scavenger(ctrl, _thread_local_mspace);
process_full_list(scavenger, _thread_local_mspace);
- scavenge_log(scavenger.processed(), scavenger.amount(), ctrl.dead_count());
- return scavenger.processed();
+ const size_t count = scavenger.processed();
+ if (0 == count) {
+ assert(0 == scavenger.amount(), "invariant");
+ return 0;
+ }
+ scavenge_log(count, scavenger.amount(), ctrl.dead_count());
+ return count;
}
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp Fri May 17 16:02:27 2019 +0200
@@ -68,7 +68,6 @@
size_t clear();
size_t clear_full();
- size_t write();
size_t write_full();
size_t write_at_safepoint();
size_t scavenge();
@@ -89,6 +88,8 @@
void discard_oldest(Thread* t);
static JfrStorageControl& control();
+ size_t write();
+
friend class JfrRecorder;
friend class JfrRecorderService;
template <typename, template <typename> class, typename>
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.hpp Fri May 17 16:02:27 2019 +0200
@@ -44,8 +44,11 @@
bool process(Type* t = NULL) {
return _next == NULL ? _op->process(t) : _op->process(t) && _next->process(t);
}
- size_t processed() const {
- return _next == NULL ? _op->processed() : _op->processed() + _next->processed();
+ size_t elements() const {
+ return _next == NULL ? _op->elements() : _op->elements() + _next->elements();
+ }
+ size_t size() const {
+ return _next == NULL ? _op->size() : _op->size() + _next->size();
}
};
@@ -53,23 +56,27 @@
class UnBufferedWriteToChunk {
private:
JfrChunkWriter& _writer;
- size_t _processed;
+ size_t _elements;
+ size_t _size;
public:
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);
- size_t processed() { return _processed; }
+ size_t elements() const { return _elements; }
+ size_t size() const { return _size; }
};
template <typename T>
class DefaultDiscarder {
private:
- size_t _processed;
+ size_t _elements;
+ size_t _size;
public:
typedef T Type;
- DefaultDiscarder() : _processed() {}
+ DefaultDiscarder() : _elements(0), _size(0) {}
bool discard(Type* t, const u1* data, size_t size);
- size_t processed() const { return _processed; }
+ size_t elements() const { return _elements; }
+ size_t size() const { return _size; }
};
template <typename Operation>
@@ -80,7 +87,8 @@
typedef typename Operation::Type Type;
ConcurrentWriteOp(Operation& operation) : _operation(operation) {}
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>
@@ -89,10 +97,10 @@
typedef typename Operation::Type Type;
ConcurrentWriteOpExcludeRetired(Operation& operation) : ConcurrentWriteOp<Operation>(operation) {}
bool process(Type* t);
- size_t processed() const { return ConcurrentWriteOp<Operation>::processed(); }
+ size_t elements() const { return ConcurrentWriteOp<Operation>::elements();}
+ size_t size() const { return ConcurrentWriteOp<Operation>::size(); }
};
-
template <typename Operation>
class MutexedWriteOp {
private:
@@ -101,7 +109,17 @@
typedef typename Operation::Type Type;
MutexedWriteOp(Operation& operation) : _operation(operation) {}
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>
+class ExclusiveOp : public MutexedWriteOp<Operation> {
+ public:
+ typedef typename Operation::Type Type;
+ ExclusiveOp(Operation& operation) : MutexedWriteOp<Operation>(operation) {}
+ bool process(Type* t);
+ size_t size() const { return MutexedWriteOp<Operation>::size(); }
};
enum jfr_operation_mode {
@@ -118,7 +136,8 @@
typedef typename Operation::Type Type;
DiscardOp(jfr_operation_mode mode = concurrent) : _operation(), _mode(mode) {}
bool process(Type* t);
- size_t processed() const { return _operation.processed(); }
+ size_t elements() const { return _operation.elements(); }
+ size_t size() const { return _operation.size(); }
};
#endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp Fri May 17 16:02:27 2019 +0200
@@ -26,17 +26,20 @@
#define SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_INLINE_HPP
#include "jfr/recorder/storage/jfrStorageUtils.hpp"
+#include "runtime/thread.inline.hpp"
template <typename T>
inline bool UnBufferedWriteToChunk<T>::write(T* t, const u1* data, size_t size) {
_writer.write_unbuffered(data, size);
- _processed += size;
+ ++_elements;
+ _size += size;
return true;
}
template <typename T>
inline bool DefaultDiscarder<T>::discard(T* t, const u1* data, size_t size) {
- _processed += size;
+ ++_elements;
+ _size += size;
return true;
}
@@ -44,7 +47,7 @@
inline bool ConcurrentWriteOp<Operation>::process(typename Operation::Type* t) {
const u1* const current_top = t->concurrent_top();
const size_t unflushed_size = t->pos() - current_top;
- if (unflushed_size == 0) {
+ if (unflushed_size == 0 || t->excluded()) {
t->set_concurrent_top(current_top);
return true;
}
@@ -67,7 +70,7 @@
assert(t != NULL, "invariant");
const u1* const current_top = t->top();
const size_t unflushed_size = t->pos() - current_top;
- if (unflushed_size == 0) {
+ if (unflushed_size == 0 || t->excluded()) {
return true;
}
const bool result = _operation.write(t, current_top, unflushed_size);
@@ -75,6 +78,28 @@
return result;
}
+template <typename Type>
+static void retired_sensitive_acquire(Type* t) {
+ assert(t != NULL, "invariant");
+ if (t->retired()) {
+ return;
+ }
+ Thread* const thread = Thread::current();
+ while (!t->try_acquire(thread)) {
+ if (t->retired()) {
+ return;
+ }
+ }
+}
+
+template <typename Operation>
+inline bool ExclusiveOp<Operation>::process(typename Operation::Type* t) {
+ retired_sensitive_acquire(t);
+ assert(t->acquired_by_self() || t->retired(), "invariant");
+ // User is required to ensure proper release of the acquisition
+ return MutexedWriteOp<Operation>::process(t);
+}
+
template <typename Operation>
inline bool DiscardOp<Operation>::process(typename Operation::Type* t) {
assert(t != NULL, "invariant");
--- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,7 +33,6 @@
#include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "logging/log.hpp"
-#include "runtime/atomic.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/safepoint.hpp"
@@ -42,12 +41,32 @@
typedef JfrStringPool::Buffer* BufferPtr;
static JfrStringPool* _instance = NULL;
+static uint64_t store_generation = 0;
+static uint64_t serialized_generation = 0;
+inline void set_value(uint64_t value, uint64_t* const dest) {
+ assert(dest != NULL, "invariant");
+ const uint64_t current = OrderAccess::load_acquire(dest);
+ if (value != current) {
+ OrderAccess::release_store(dest, value);
+ }
+}
+static void inc_store_generation() {
+ set_value(OrderAccess::load_acquire(&serialized_generation) + 1, &store_generation);
+}
+static void set_serialized_generation() {
+ set_value(OrderAccess::load_acquire(&store_generation), &serialized_generation);
+}
+bool JfrStringPool::modified() {
+ return serialized_generation != OrderAccess::load_acquire(&store_generation);
+}
JfrStringPool& JfrStringPool::instance() {
return *_instance;
}
JfrStringPool* JfrStringPool::create(JfrChunkWriter& cw) {
+ store_generation = 0;
+ serialized_generation = 0;
assert(_instance == NULL, "invariant");
_instance = new JfrStringPool(cw);
return _instance;
@@ -136,97 +155,83 @@
writer.write(id);
writer.write(string);
writer.inc_nof_strings();
+ inc_store_generation();
}
return current_epoch;
}
-class StringPoolWriteOp {
+template <template <typename> class Operation>
+class StringPoolOp {
public:
typedef JfrStringPoolBuffer Type;
private:
- UnBufferedWriteToChunk<Type> _writer;
+ Operation<Type> _op;
Thread* _thread;
size_t _strings_processed;
public:
- StringPoolWriteOp(JfrChunkWriter& writer, Thread* thread) : _writer(writer), _thread(thread), _strings_processed(0) {}
+ StringPoolOp() : _op(), _thread(Thread::current()), _strings_processed(0) {}
+ StringPoolOp(JfrChunkWriter& writer, Thread* thread) : _op(writer), _thread(thread), _strings_processed(0) {}
bool write(Type* buffer, const u1* data, size_t size) {
- buffer->acquire(_thread); // blocking
+ assert(buffer->acquired_by(_thread) || buffer->retired(), "invariant");
const uint64_t nof_strings_used = buffer->string_count();
assert(nof_strings_used > 0, "invariant");
buffer->set_string_top(buffer->string_top() + nof_strings_used);
// "size processed" for string pool buffers is the number of processed string elements
_strings_processed += nof_strings_used;
- const bool ret = _writer.write(buffer, data, size);
- buffer->release();
- return ret;
+ return _op.write(buffer, data, size);
}
size_t processed() { return _strings_processed; }
};
-typedef StringPoolWriteOp WriteOperation;
-typedef ConcurrentWriteOp<WriteOperation> ConcurrentWriteOperation;
+template <typename Type>
+class StringPoolDiscarderStub {
+ public:
+ bool write(Type* buffer, const u1* data, size_t size) {
+ // stub only, discard happens at higher level
+ return true;
+ }
+};
+
+typedef StringPoolOp<UnBufferedWriteToChunk> WriteOperation;
+typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation;
+typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation;
+typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation;
+typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation;
+typedef CompositeOperation<ExclusiveWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation;
+typedef CompositeOperation<ExclusiveDiscardOperation, StringPoolReleaseOperation> StringPoolDiscardOperation;
size_t JfrStringPool::write() {
+ set_serialized_generation();
Thread* const thread = Thread::current();
WriteOperation wo(_chunkwriter, thread);
- ConcurrentWriteOperation cwo(wo);
- assert(_free_list_mspace->is_full_empty(), "invariant");
- process_free_list(cwo, _free_list_mspace);
- return wo.processed();
-}
-
-typedef MutexedWriteOp<WriteOperation> MutexedWriteOperation;
-typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation;
-typedef CompositeOperation<MutexedWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation;
-
-size_t JfrStringPool::write_at_safepoint() {
- assert(SafepointSynchronize::is_at_safepoint(), "invariant");
- Thread* const thread = Thread::current();
- WriteOperation wo(_chunkwriter, thread);
- MutexedWriteOperation mwo(wo);
+ ExclusiveWriteOperation ewo(wo);
StringPoolReleaseOperation spro(_free_list_mspace, thread, false);
- StringPoolWriteOperation spwo(&mwo, &spro);
+ StringPoolWriteOperation spwo(&ewo, &spro);
assert(_free_list_mspace->is_full_empty(), "invariant");
process_free_list(spwo, _free_list_mspace);
return wo.processed();
}
-class StringPoolBufferDiscarder {
- private:
- Thread* _thread;
- size_t _processed;
- public:
- typedef JfrStringPoolBuffer Type;
- StringPoolBufferDiscarder() : _thread(Thread::current()), _processed(0) {}
- bool process(Type* buffer) {
- buffer->acquire(_thread); // serialized access
- const u1* const current_top = buffer->top();
- const size_t unflushed_size = buffer->pos() - current_top;
- if (unflushed_size == 0) {
- assert(buffer->string_count() == 0, "invariant");
- buffer->release();
- return true;
- }
- buffer->set_top(current_top + unflushed_size);
- const uint64_t nof_strings_used = buffer->string_count();
- buffer->set_string_top(buffer->string_top() + nof_strings_used);
- // "size processed" for string pool buffers is the number of string elements
- _processed += (size_t)nof_strings_used;
- buffer->release();
- return true;
- }
- size_t processed() const { return _processed; }
-};
+size_t JfrStringPool::write_at_safepoint() {
+ assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ return write();
+}
size_t JfrStringPool::clear() {
- StringPoolBufferDiscarder discard_operation;
+ set_serialized_generation();
+ DiscardOperation discard_operation;
+ ExclusiveDiscardOperation edo(discard_operation);
+ StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false);
+ StringPoolDiscardOperation spdo(&edo, &spro);
assert(_free_list_mspace->is_full_empty(), "invariant");
- process_free_list(discard_operation, _free_list_mspace);
+ process_free_list(spdo, _free_list_mspace);
return discard_operation.processed();
}
void JfrStringPool::register_full(BufferPtr t, Thread* thread) {
// nothing here at the moment
+ assert(t != NULL, "invariant");
+ assert(t->acquired_by(thread), "invariant");
assert(t->retired(), "invariant");
}
--- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp Fri May 17 16:02:27 2019 +0200
@@ -71,6 +71,7 @@
static JfrStringPool* create(JfrChunkWriter& cw);
bool initialize();
static void destroy();
+ static bool modified();
friend class JfrRecorder;
friend class JfrRecorderService;
--- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPoolBuffer.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPoolBuffer.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,11 +29,9 @@
void JfrStringPoolBuffer::reinitialize() {
assert(acquired_by_self() || retired(), "invariant");
- concurrent_top();
- set_pos((start()));
set_string_pos(0);
set_string_top(0);
- set_concurrent_top(start());
+ JfrBuffer::reinitialize();
}
uint64_t JfrStringPoolBuffer::string_pos() const {
@@ -57,7 +55,7 @@
}
void JfrStringPoolBuffer::increment(uint64_t value) {
- assert(acquired_by_self() || retired(), "invariant");
+ assert(acquired_by_self(), "invariant");
++_string_count_pos;
}
--- a/src/hotspot/share/jfr/support/jfrKlassExtension.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/support/jfrKlassExtension.hpp Fri May 17 16:02:27 2019 +0200
@@ -36,6 +36,7 @@
#define JDK_JFR_EVENT_SUBKLASS 16
#define JDK_JFR_EVENT_KLASS 32
#define EVENT_HOST_KLASS 64
+#define EVENT_RESERVED 128
#define IS_EVENT_KLASS(ptr) (((ptr)->trace_id() & (JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS)) != 0)
#define ON_KLASS_CREATION(k, p, t) if (IS_EVENT_KLASS(k)) JfrEventClassTransformer::on_klass_creation(k, p, t)
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -55,6 +55,7 @@
_stack_trace_hash(0),
_stackdepth(0),
_entering_suspend_flag(0),
+ _excluded(false),
_dead(false) {}
u8 JfrThreadLocal::add_data_lost(u8 value) {
@@ -84,9 +85,13 @@
void JfrThreadLocal::on_start(Thread* t) {
assert(t != NULL, "invariant");
assert(Thread::current() == t, "invariant");
+ JfrJavaSupport::on_thread_start(t);
if (JfrRecorder::is_recording()) {
- if (t->is_Java_thread()) {
- send_java_thread_start_event((JavaThread*)t);
+ if (!t->jfr_thread_local()->is_excluded()) {
+ JfrCheckpointManager::write_thread_checkpoint(t);
+ if (t->is_Java_thread()) {
+ send_java_thread_start_event((JavaThread*)t);
+ }
}
}
}
@@ -107,20 +112,22 @@
assert(Thread::current() == t, "invariant");
assert(!tl->is_dead(), "invariant");
assert(tl->shelved_buffer() == NULL, "invariant");
+ tl->_dead = true;
+ if (tl->has_java_event_writer()) {
+ assert(t->is_Java_thread(), "invariant");
+ const jobject event_writer = tl->java_event_writer();
+ tl->set_java_event_writer(NULL);
+ JfrJavaSupport::destroy_global_jni_handle(event_writer);
+ }
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);
}
- tl->_dead = true;
}
void JfrThreadLocal::on_exit(Thread* t) {
@@ -128,23 +135,31 @@
JfrThreadLocal * const tl = t->jfr_thread_local();
assert(!tl->is_dead(), "invariant");
if (JfrRecorder::is_recording()) {
- if (t->is_Java_thread()) {
+ if (t->is_Java_thread() && !tl->is_excluded()) {
send_java_thread_end_events(tl->thread_id(), (JavaThread*)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 {
assert(!has_native_buffer(), "invariant");
- _native_buffer = JfrStorage::acquire_thread_local(Thread::current());
+ _native_buffer = acquire_buffer(_excluded);
return _native_buffer;
}
JfrBuffer* JfrThreadLocal::install_java_buffer() const {
assert(!has_java_buffer(), "invariant");
assert(!has_java_event_writer(), "invariant");
- _java_buffer = JfrStorage::acquire_thread_local(Thread::current());
+ _java_buffer = acquire_buffer(_excluded);
return _java_buffer;
}
@@ -163,3 +178,13 @@
ByteSize JfrThreadLocal::java_event_writer_offset() {
return in_ByteSize(offset_of(JfrThreadLocal, _java_event_writer));
}
+
+void JfrThreadLocal::exclude(Thread* t) {
+ assert(t != NULL, "invariant");
+ t->jfr_thread_local()->_excluded = true;
+}
+
+void JfrThreadLocal::include(Thread* t) {
+ assert(t != NULL, "invariant");
+ t->jfr_thread_local()->_excluded = false;
+}
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp Fri May 17 16:02:27 2019 +0200
@@ -50,6 +50,7 @@
unsigned int _stack_trace_hash;
mutable u4 _stackdepth;
volatile jint _entering_suspend_flag;
+ bool _excluded;
bool _dead;
JfrBuffer* install_native_buffer() const;
@@ -205,6 +206,10 @@
_trace_id = id;
}
+ bool is_excluded() const {
+ return _excluded;
+ }
+
bool is_dead() const {
return _dead;
}
@@ -215,6 +220,8 @@
static void on_start(Thread* t);
static void on_exit(Thread* t);
+ static void exclude(Thread* t);
+ static void include(Thread* t);
// Code generation
static ByteSize trace_id_offset();
--- a/src/hotspot/share/jfr/utilities/jfrIterator.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/utilities/jfrIterator.hpp Fri May 17 16:02:27 2019 +0200
@@ -40,6 +40,14 @@
}
};
+template <typename Node>
+class StopOnEmptyCondition : public AllStatic {
+public:
+ static bool has_next(const Node* node) {
+ return node != NULL && !node->empty();
+ }
+};
+
template <typename List, template <typename> class ContinuationPredicate>
class Navigator {
public:
@@ -83,6 +91,12 @@
NavigatorStopOnNull(List& list, jfr_iter_direction direction = forward) : Navigator<List, StopOnNullCondition>(list, direction) {}
};
+template <typename List>
+class NavigatorStopOnEmpty : public Navigator<List, StopOnEmptyCondition> {
+public:
+ NavigatorStopOnEmpty(List& list, jfr_iter_direction direction = forward) : Navigator<List, StopOnEmptyCondition>(list, direction) {}
+};
+
template<typename List, template <typename> class Navigator, typename AP = StackObj>
class IteratorHost : public AP {
private:
@@ -104,4 +118,10 @@
StopOnNullIterator(List& list, jfr_iter_direction direction = forward) : IteratorHost<List, NavigatorStopOnNull, AP>(list, direction) {}
};
+template<typename List, typename AP = StackObj>
+class StopOnEmptyIterator : public IteratorHost<List, NavigatorStopOnEmpty, AP> {
+public:
+ StopOnEmptyIterator(List& list, jfr_iter_direction direction = forward) : IteratorHost<List, NavigatorStopOnEmpty, AP>(list, direction) {}
+};
+
#endif // SHARE_JFR_UTILITIES_JFRITERATOR_HPP
--- a/src/hotspot/share/jfr/utilities/jfrLogTagSets.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/utilities/jfrLogTagSets.hpp Fri May 17 16:02:27 2019 +0200
@@ -53,6 +53,7 @@
JFR_LOG_TAG(jfr, system, bytecode) \
JFR_LOG_TAG(jfr, system, parser) \
JFR_LOG_TAG(jfr, system, metadata) \
+ JFR_LOG_TAG(jfr, system, streaming) \
JFR_LOG_TAG(jfr, metadata) \
JFR_LOG_TAG(jfr, event) \
JFR_LOG_TAG(jfr, setting) \
--- a/src/hotspot/share/jfr/utilities/jfrTypes.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/utilities/jfrTypes.hpp Fri May 17 16:02:27 2019 +0200
@@ -33,6 +33,7 @@
typedef int fio_fd;
const int invalid_fd = -1;
const jlong invalid_offset = -1;
+const int64_t invalid_time = -1;
const u4 STACK_DEPTH_DEFAULT = 64;
const u4 MIN_STACK_DEPTH = 1;
const u4 MAX_STACK_DEPTH = 2048;
--- a/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp Fri May 17 16:02:27 2019 +0200
@@ -32,7 +32,7 @@
class Thread;
class JfrJavaEventWriter : AllStatic {
- friend class JfrCheckpointThreadClosure;
+ friend class JfrNotifyClosure;
friend class JfrJavaEventWriterNotifyOperation;
friend class JfrJavaEventWriterNotificationClosure;
private:
--- a/src/hotspot/share/jfr/writers/jfrStorageAdapter.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/writers/jfrStorageAdapter.hpp Fri May 17 16:02:27 2019 +0200
@@ -82,7 +82,7 @@
assert(_thread != NULL, "invariant");
Flush f(_storage, used, requested, _thread);
_storage = f.result();
- return _storage != NULL;
+ return _storage != NULL && !_storage->excluded();
}
void release() {
@@ -236,7 +236,8 @@
void release() {}
bool flush(size_t used, size_t requested) {
// don't flush/expand a buffer that is not our own
- return false;
+ _pos = _start;
+ return true;
}
};
--- a/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp Fri May 17 16:02:27 2019 +0200
@@ -170,7 +170,7 @@
}
if (this->available_size() < requested + size_safety_cushion) {
if (!this->accommodate(this->used_size(), requested + size_safety_cushion)) {
- this->cancel();
+ assert(!this->is_valid(), "invariant");
return NULL;
}
}
--- a/src/hotspot/share/logging/logTag.hpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/logging/logTag.hpp Fri May 17 16:02:27 2019 +0200
@@ -147,6 +147,7 @@
LOG_TAG(startuptime) \
LOG_TAG(state) \
LOG_TAG(stats) \
+ LOG_TAG(streaming) \
LOG_TAG(stringdedup) \
LOG_TAG(stringtable) \
LOG_TAG(symboltable) \
--- a/src/hotspot/share/runtime/thread.cpp Fri May 17 15:53:21 2019 +0200
+++ b/src/hotspot/share/runtime/thread.cpp Fri May 17 16:02:27 2019 +0200
@@ -3075,17 +3075,6 @@
// if vm exit occurs during initialization). These cases can all be accounted
// for such that this method never returns NULL.
const char* JavaThread::get_thread_name() const {
-#ifdef ASSERT
- // early safepoints can hit while current thread does not yet have TLS
- if (!SafepointSynchronize::is_at_safepoint()) {
- Thread *cur = Thread::current();
- if (!(cur->is_Java_thread() && cur == this)) {
- // Current JavaThreads are allowed to get their own name without
- // the Threads_lock.
- assert_locked_or_safepoint(Threads_lock);
- }
- }
-#endif // ASSERT
return get_thread_name_string();
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/Recording.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/Recording.java Fri May 17 16:02:27 2019 +0200
@@ -414,6 +414,32 @@
}
/**
+ * Determines how often events are made available for streaming.
+ *
+ * @param interval the interval at which events are made available for streaming.
+ *
+ * @throws IllegalArgumentException if <code>interval</code> is negative
+ *
+ * @throws IllegalStateException if the recording is in the {@code CLOSED} state
+ */
+ public void setFlushInterval(Duration interval) {
+ Objects.nonNull(interval);
+ if (interval.isNegative()) {
+ throw new IllegalArgumentException("Stream interval can't be negative");
+ }
+ internal.setFlushInterval(interval);
+ }
+
+ /**
+ * Returns how often events are made available for streaming purposes.
+ *
+ * @return the desired stream interval, or {@code null} if no interval has been set
+ */
+ public Duration getFlushInterval() {
+ return internal.getFlushInterval();
+ }
+
+ /**
* Determines how far back data is kept in the disk repository.
* <p>
* To control the amount of recording data stored on disk, the maximum length of
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ChunkParser.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/ChunkParser.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
import java.io.IOException;
import java.util.Collection;
import java.util.List;
+import java.util.StringJoiner;
import jdk.jfr.EventType;
import jdk.jfr.internal.LogLevel;
@@ -35,7 +36,9 @@
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type;
+import jdk.jfr.internal.Utils;
import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.Parser;
import jdk.jfr.internal.consumer.RecordingInput;
/**
@@ -45,38 +48,127 @@
final class ChunkParser {
private static final long CONSTANT_POOL_TYPE_ID = 1;
private final RecordingInput input;
- private final LongMap<Parser> parsers;
private final ChunkHeader chunkHeader;
- private final long absoluteChunkEnd;
private final MetadataDescriptor metadata;
- private final LongMap<Type> typeMap;
private final TimeConverter timeConverter;
+ private final MetadataDescriptor previousMetadata;
+ private final long pollInterval;
+ private final LongMap<ConstantLookup> constantLookups;
+
+ private LongMap<Type> typeMap;
+ private LongMap<Parser> parsers;
+ private boolean chunkFinished;
+ private InternalEventFilter eventFilter = InternalEventFilter.ACCEPT_ALL;
public ChunkParser(RecordingInput input) throws IOException {
- this(new ChunkHeader(input));
+ this(new ChunkHeader(input), null, 500);
}
- private ChunkParser(ChunkHeader header) throws IOException {
+ private ChunkParser(ChunkHeader header, ChunkParser previous, long pollInterval) throws IOException {
this.input = header.getInput();
this.chunkHeader = header;
- this.metadata = header.readMetadata();
- this.absoluteChunkEnd = header.getEnd();
+ if (previous == null) {
+ this.pollInterval = 500;
+ this.constantLookups = new LongMap<>();
+ this.previousMetadata = null;
+ } else {
+ this.constantLookups = previous.constantLookups;
+ this.previousMetadata = previous.metadata;
+ this.pollInterval = previous.pollInterval;
+ }
+ this.metadata = header.readMetadata(previousMetadata);
this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
-
- ParserFactory factory = new ParserFactory(metadata, timeConverter);
- LongMap<ConstantMap> constantPools = factory.getConstantPools();
- parsers = factory.getParsers();
- typeMap = factory.getTypeMap();
-
- fillConstantPools(parsers, constantPools);
- constantPools.forEach(ConstantMap::setIsResolving);
- constantPools.forEach(ConstantMap::resolve);
- constantPools.forEach(ConstantMap::setResolved);
+ if (metadata != previousMetadata) {
+ ParserFactory factory = new ParserFactory(metadata, constantLookups, timeConverter);
+ parsers = factory.getParsers();
+ typeMap = factory.getTypeMap();
+ } else {
+ parsers = previous.parsers;
+ typeMap = previous.typeMap;
+ }
+ constantLookups.forEach(c -> c.newPool());
+ fillConstantPools(0);
+ constantLookups.forEach(c -> c.getLatestPool().setResolving());
+ constantLookups.forEach(c -> c.getLatestPool().resolve());
+ constantLookups.forEach(c -> c.getLatestPool().setResolved());
input.position(chunkHeader.getEventStart());
}
+ public void setParserFilter(InternalEventFilter filter) {
+ // Disable low level filter, since it doesn't work
+ // when a psrser is shared
+ // this.eventFilter = filter;
+ // updateParserFilters();
+ }
+
+ public InternalEventFilter getEventFilter() {
+ return this.eventFilter;
+ }
+
+ private void updateParserFilters() {
+ parsers.forEach(p -> {
+ if (p instanceof EventParser) {
+ EventParser ep = (EventParser) p;
+ long threshold = eventFilter.getThreshold(ep.getEventType().getName());
+ if (threshold >= 0) {
+ ep.setEnabled(true);
+ ep.setThreshold(timeConverter.convertDurationNanos(threshold));
+ } else {
+ ep.setThreshold(-1L);
+ }
+ }
+ });
+ }
+
+ /**
+ * Reads an event and returns null when segment or chunk ends.
+ */
+ public RecordedEvent readStreamingEvent(boolean awaitNewEvents) throws IOException {
+ long absoluteChunkEnd = chunkHeader.getEnd();
+ while (true) {
+ RecordedEvent event = readEvent();
+ if (event != null) {
+ return event;
+ }
+ if (!awaitNewEvents) {
+ return null;
+ }
+ long lastValid = absoluteChunkEnd;
+ long metadataPoistion = chunkHeader.getMetataPosition();
+ long contantPosition = chunkHeader.getConstantPoolPosition();
+ chunkFinished = awaitUpdatedHeader(absoluteChunkEnd);
+ if (chunkFinished) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "At chunk end");
+ return null;
+ }
+ absoluteChunkEnd = chunkHeader.getEnd();
+ // Read metadata and constant pools for the next segment
+ if (chunkHeader.getMetataPosition() != metadataPoistion) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found new metadata in chunk. Rebuilding types and parsers");
+ MetadataDescriptor metadata = chunkHeader.readMetadata(previousMetadata);
+ ParserFactory factory = new ParserFactory(metadata, constantLookups, timeConverter);
+ parsers = factory.getParsers();
+ typeMap = factory.getTypeMap();
+ updateParserFilters();
+ }
+ if (contantPosition != chunkHeader.getConstantPoolPosition()) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found new constant pool data. Filling up pools with new values");
+ constantLookups.forEach(c -> c.getLatestPool().setAllResolved(false));
+ fillConstantPools(contantPosition + chunkHeader.getAbsoluteChunkStart());
+ constantLookups.forEach(c -> c.getLatestPool().setResolving());
+ constantLookups.forEach(c -> c.getLatestPool().resolve());
+ constantLookups.forEach(c -> c.getLatestPool().setResolved());
+ }
+ input.position(lastValid);
+ }
+ }
+
+ /**
+ * Reads an event and returns null when the chunk ends
+ */
public RecordedEvent readEvent() throws IOException {
+ long absoluteChunkEnd = chunkHeader.getEnd();
while (input.position() < absoluteChunkEnd) {
long pos = input.position();
int size = input.readInt();
@@ -84,10 +176,16 @@
throw new IOException("Event can't have zero size");
}
long typeId = input.readLong();
- if (typeId > CONSTANT_POOL_TYPE_ID) { // also skips metadata (id=0)
- Parser ep = parsers.get(typeId);
- if (ep instanceof EventParser) {
- return (RecordedEvent) ep.parse(input);
+ // Skip metadata and constant pool events (id = 0, id = 1)
+ if (typeId > CONSTANT_POOL_TYPE_ID) {
+ Parser p = parsers.get(typeId);
+ if (p instanceof EventParser) {
+ EventParser ep = (EventParser) p;
+ RecordedEvent event = ep.parse(input);
+ if (event != null) {
+ input.position(pos + size);
+ return event;
+ }
}
}
input.position(pos + size);
@@ -95,62 +193,132 @@
return null;
}
- private void fillConstantPools(LongMap<Parser> typeParser, LongMap<ConstantMap> constantPools) throws IOException {
- long nextCP = chunkHeader.getAbsoluteChunkStart();
- long deltaToNext = chunkHeader.getConstantPoolPosition();
- while (deltaToNext != 0) {
- nextCP += deltaToNext;
- input.position(nextCP);
- final long position = nextCP;
+ private boolean awaitUpdatedHeader(long absoluteChunkEnd) throws IOException {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Waiting for more data (streaming). Read so far: " + chunkHeader.getChunkSize() + " bytes");
+ while (true) {
+ chunkHeader.refresh();
+ if (absoluteChunkEnd != chunkHeader.getEnd()) {
+ return false;
+ }
+ if (chunkHeader.isFinished()) {
+ return true;
+ }
+ Utils.takeNap(pollInterval);
+ }
+ }
+
+ private void fillConstantPools(long abortCP) throws IOException {
+ long thisCP = chunkHeader.getConstantPoolPosition() + chunkHeader.getAbsoluteChunkStart();
+ long lastCP = -1;
+ long delta = -1;
+ boolean log = Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE);
+ while (thisCP != abortCP && delta != 0) {
+ input.position(thisCP);
+ lastCP = thisCP;
int size = input.readInt(); // size
long typeId = input.readLong();
if (typeId != CONSTANT_POOL_TYPE_ID) {
- throw new IOException("Expected check point event (id = 1) at position " + nextCP + ", but found type id = " + typeId);
+ throw new IOException("Expected check point event (id = 1) at position " + lastCP + ", but found type id = " + typeId);
}
input.readLong(); // timestamp
input.readLong(); // duration
- deltaToNext = input.readLong();
- final long delta = deltaToNext;
+ delta = input.readLong();
+ thisCP += delta;
boolean flush = input.readBoolean();
int poolCount = input.readInt();
+ final long logLastCP = lastCP;
+ final long logDelta = delta;
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
- return "New constant pool: startPosition=" + position + ", size=" + size + ", deltaToNext=" + delta + ", flush=" + flush + ", poolCount=" + poolCount;
+ return "New constant pool: startPosition=" + logLastCP + ", size=" + size + ", deltaToNext=" + logDelta + ", flush=" + flush + ", poolCount=" + poolCount;
});
-
for (int i = 0; i < poolCount; i++) {
long id = input.readLong(); // type id
- ConstantMap pool = constantPools.get(id);
+ ConstantLookup lookup = constantLookups.get(id);
Type type = typeMap.get(id);
- if (pool == null) {
+ if (lookup == null) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used");
if (type == null) {
- throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]");
+ throw new IOException(
+ "Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + lastCP + ", " + lastCP + size + "]");
}
- pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
- constantPools.put(type.getId(), pool);
+ ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+ constantLookups.put(type.getId(), new ConstantLookup(pool, type));
}
- Parser parser = typeParser.get(id);
+ Parser parser = parsers.get(id);
if (parser == null) {
throw new IOException("Could not find constant pool type with id = " + id);
}
try {
int count = input.readInt();
- Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> "Constant: " + getName(id) + "[" + count + "]");
+ if (count == 0) {
+ throw new InternalError("Pool " + type.getName() + " must contain at least one element ");
+ }
+ if (log) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Constant Pool " + i + ": " + type.getName());
+ }
for (int j = 0; j < count; j++) {
long key = input.readLong();
- Object value = parser.parse(input);
- pool.put(key, value);
+// Object resolved = lookup.getCurrent(key);
+// Disable cache Object resolved = lookup.getResolved(key);
+// if (resolved == null) {
+ Object v = parser.parse(input);
+ logConstant(key, v, false);
+ lookup.getLatestPool().put(key, v);
+// } else {
+// parser.skip(input);
+// logConstant(key, resolved, true);
+// Disable cache lookup.getLatestPool().putResolved(key, resolved);
+// }
}
} catch (Exception e) {
- throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]", e);
+ throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + lastCP + ", " + lastCP + size + "]",
+ e);
}
}
- if (input.position() != nextCP + size) {
+ if (input.position() != lastCP + size) {
throw new IOException("Size of check point event doesn't match content");
}
}
}
+ private void logConstant(long key, Object v, boolean preresolved) {
+ if (!Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE)) {
+ return;
+ }
+ String valueText;
+ if (v.getClass().isArray()) {
+ Object[] array = (Object[]) v;
+ StringJoiner sj = new StringJoiner(", ", "{", "}");
+ for (int i = 0; i < array.length; i++) {
+ sj.add(textify(array[i]));
+ }
+ valueText = sj.toString();
+ } else {
+ valueText = textify(v);
+ }
+ String suffix = preresolved ? " (presolved)" :"";
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Constant: " + key + " = " + valueText + suffix);
+ }
+
+ private String textify(Object o) {
+ if (o == null) { // should not happen
+ return "null";
+ }
+ if (o instanceof String) {
+ return "\"" + String.valueOf(o) + "\"";
+ }
+ if (o instanceof RecordedObject) {
+ return o.getClass().getName();
+ }
+ if (o.getClass().isArray()) {
+ Object[] array = (Object[]) o;
+ if (array.length > 0) {
+ return textify(array[0]) + "[]"; // can it be recursive?
+ }
+ }
+ return String.valueOf(o);
+ }
+
private String getName(long id) {
Type type = typeMap.get(id);
return type == null ? ("unknown(" + id + ")") : type.getName();
@@ -164,11 +332,15 @@
return metadata.getEventTypes();
}
- public boolean isLastChunk() {
+ public boolean isLastChunk() throws IOException {
return chunkHeader.isLastChunk();
}
public ChunkParser nextChunkParser() throws IOException {
- return new ChunkParser(chunkHeader.nextHeader());
+ return new ChunkParser(chunkHeader.nextHeader(), this, pollInterval);
+ }
+
+ public boolean isChunkFinished() {
+ return chunkFinished;
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ConstantMap.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/ConstantMap.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,9 +25,6 @@
package jdk.jfr.consumer;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Holds mapping between a set of keys and their corresponding object.
*
@@ -35,6 +32,15 @@
* {@link ObjectFactory} can be supplied which will instantiate a typed object.
*/
final class ConstantMap {
+
+ private static final int RESOLUTION_FINISHED = 0;
+ private static final int RESOLUTION_STARTED = 1;
+ public static final ConstantMap EMPTY = new ConstantMap();
+
+ // A temporary placeholder, so objects can
+ // reference themselves (directly, or indirectly),
+ // when making a transition from numeric id references
+ // to normal Java references.
private final static class Reference {
private final long key;
private final ConstantMap pool;
@@ -47,18 +53,28 @@
Object resolve() {
return pool.get(key);
}
+
+ public String toString() {
+ return "ref: " + pool.name + "[" + key + "]";
+ }
}
- private final ObjectFactory<?> factory;
+ final ObjectFactory<?> factory;
+ final String name;
+
private final LongMap<Object> objects;
- private LongMap<Boolean> isResolving;
+ private boolean resolving;
private boolean allResolved;
- private String name;
+
+ private ConstantMap() {
+ this(null, "<empty>");
+ allResolved = true;
+ }
ConstantMap(ObjectFactory<?> factory, String name) {
this.name = name;
- this.objects = new LongMap<>();
+ this.objects = new LongMap<>(2);
this.factory = factory;
}
@@ -68,26 +84,42 @@
return objects.get(id);
}
// referenced from a pool, deal with this later
- if (isResolving == null) {
+ if (!resolving) {
return new Reference(this, id);
}
- Boolean beingResolved = isResolving.get(id);
+ // should always have a value
+ Object value = objects.get(id);
+ if (value == null) {
+ // unless is 0 which is used to represent null
+ if (id == 0) {
+ return null;
+ }
+ throw new InternalError("Missing object id=" + id + " in pool " + name + ". All ids should reference object");
+ }
- // we are resolved (but not the whole pool)
- if (Boolean.FALSE.equals(beingResolved)) {
- return objects.get(id);
+ // id is resolved (but not the whole pool)
+ if (objects.isSetId(id, RESOLUTION_FINISHED)) {
+ return value;
}
// resolving ourself, abort to avoid infinite recursion
- if (Boolean.TRUE.equals(beingResolved)) {
+ if (objects.isSetId(id, RESOLUTION_STARTED)) {
return null;
}
- // resolve me!
- isResolving.put(id, Boolean.TRUE);
- Object resolved = resolve(objects.get(id));
- isResolving.put(id, Boolean.FALSE);
+ // mark id as STARTED if we should
+ // come back during object resolution
+ objects.setId(id, RESOLUTION_STARTED);
+
+ // resolve object!
+ Object resolved = resolve(value);
+
+ // mark id as FINISHED
+ objects.setId(id, RESOLUTION_FINISHED);
+
+ // if a factory exists, convert to RecordedThread.
+ // RecordedClass etc. and store back results
if (factory != null) {
Object factorized = factory.createObject(id, resolved);
objects.put(id, factorized);
@@ -105,7 +137,8 @@
if (o != null && o.getClass().isArray()) {
final Object[] array = (Object[]) o;
for (int i = 0; i < array.length; i++) {
- array[i] = resolve(array[i]);
+ Object element = array[i];
+ array[i] = resolve(element);
}
return array;
}
@@ -113,27 +146,44 @@
}
public void resolve() {
- List<Long> keyList = new ArrayList<>();
- objects.keys().forEachRemaining(keyList::add);
- for (Long l : keyList) {
- get(l);
- }
+ objects.forEachKey(k -> get(k));
}
public void put(long key, Object value) {
objects.put(key, value);
}
- public void setIsResolving() {
- isResolving = new LongMap<>();
+ public void setResolving() {
+ resolving = true;
+ allResolved = false;
}
public void setResolved() {
allResolved = true;
- isResolving = null; // pool finished, release memory
+ resolving = false;
}
public String getName() {
return name;
}
+
+ public Object getResolved(long id) {
+ return objects.get(id);
+ }
+
+ public void putResolved(long id, Object object) {
+ objects.setId(id, RESOLUTION_FINISHED);
+ objects.put(id, object);
+ }
+
+ public void setAllResolved(boolean allResolved) {
+ this.allResolved = allResolved;
+ }
+
+ public boolean isResolved(long id) {
+ if (objects.hasKey(id)) {
+ return objects.isSetId(id, RESOLUTION_FINISHED);
+ }
+ return false;
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.consumer.Parser;
import jdk.jfr.internal.consumer.RecordingInput;
/**
@@ -44,29 +45,68 @@
private final TimeConverter timeConverter;
private final boolean hasDuration;
private final List<ValueDescriptor> valueDescriptors;
+ private final int startIndex;
+ private long thresholdTicks = -1;
+ private boolean enabled = true;
EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
this.timeConverter = timeConverter;
this.parsers = parsers;
this.eventType = type;
this.hasDuration = type.getField(FIELD_DURATION) != null;
+ this.startIndex = hasDuration ? 2 : 1;
this.valueDescriptors = type.getFields();
}
+ public EventType getEventType() {
+ return eventType;
+ }
+
+ public void setThreshold(long thresholdTicks) {
+ this.thresholdTicks = thresholdTicks;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public RecordedEvent parse(RecordingInput input) throws IOException {
+ if (enabled) {
+ long startTicks = input.readLong();
+ long durationTicks = 0;
+ if (hasDuration) {
+ durationTicks = input.readLong();
+ if (durationTicks < thresholdTicks) {
+ return null;
+ }
+ }
+ Object[] values = new Object[parsers.length];
+ for (int i = startIndex; i < parsers.length; i++) {
+ values[i] = parsers[i].parse(input);
+ }
+ values[0] = startTicks;
+ if (hasDuration) {
+ values[1] = Long.valueOf(durationTicks);
+ }
+ long startTime = timeConverter.convertTimestamp(startTicks);
+ if (hasDuration) {
+ long endTime = timeConverter.convertTimestamp(startTicks + durationTicks);
+ return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter);
+ } else {
+ return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter);
+ }
+ }
+ return null;
+
+ }
+
@Override
- public Object parse(RecordingInput input) throws IOException {
- Object[] values = new Object[parsers.length];
- for (int i = 0; i < parsers.length; i++) {
- values[i] = parsers[i].parse(input);
- }
- Long startTicks = (Long) values[0];
- long startTime = timeConverter.convertTimestamp(startTicks);
- if (hasDuration) {
- long durationTicks = (Long) values[1];
- long endTime = timeConverter.convertTimestamp(startTicks + durationTicks);
- return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter);
- } else {
- return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter);
- }
+ public void skip(RecordingInput input) throws IOException {
+ throw new InternalError("Should not call this method. More efficent to read event size and skip ahead");
}
+
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/LongMap.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/LongMap.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 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.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,37 +25,232 @@
package jdk.jfr.consumer;
-import java.util.HashMap;
-import java.util.Iterator;
+import java.util.BitSet;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+@SuppressWarnings("unchecked")
+final class LongMap<T> {
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+ private static final long[] EMPTY_KEYS = new long[0];
+ private static final Object[] EMPTY_OBJECTS = new Object[0];
+ private static final int DEFAULT_SIZE = 32;
+ private static final Object NULL_OBJECT = new Object();
+
+ private final int bitCount;
+ private BitSet bitSet;
+ private long[] keys = EMPTY_KEYS;
+ private T[] objects = (T[]) EMPTY_OBJECTS;
+ private int count;
+ private int shift;
+
+ public LongMap() {
+ this.bitCount = 0;
+ }
+
+ public LongMap(int markBits) {
+ this.bitCount = markBits;
+ this.bitSet = new BitSet();
+ }
+
+ // Should be 2^n
+ private void initialize(int capacity) {
+ keys = new long[capacity];
+ objects = (T[]) new Object[capacity];
+ shift = 64 - (31 - Integer.numberOfLeadingZeros(capacity));
+ }
+
+ public void claimBits() {
+ // flip last bit back and forth to make bitset expand to max size
+ int lastBit = bitSetIndex(objects.length - 1, bitCount -1);
+ bitSet.flip(lastBit);
+ bitSet.flip(lastBit);
+ }
-/**
- * Commonly used data structure for looking up objects given an id (long value)
- *
- * TODO: Implement without using Map and Long objects, to minimize allocation
- *
- * @param <T>
- */
-final class LongMap<T> implements Iterable<T> {
- private final HashMap<Long, T> map;
+ public void setId(long id, int bitIndex) {
+ int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
+ bitSet.set(bitSetIndex, true);
+ }
+
+ public void clearId(long id, int bitIndex) {
+ int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
+ bitSet.set(bitSetIndex, false);
+ }
+
+ public boolean isSetId(long id, int bitIndex) {
+ int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
+ return bitSet.get(bitSetIndex);
+ }
+
+ private int bitSetIndex(int tableIndex, int bitIndex) {
+ return bitCount * tableIndex + bitIndex;
+ }
- LongMap() {
- map = new HashMap<>(101);
+ private int tableIndexOf(long id) {
+ int index = index(id);
+ while (true) {
+ if (objects[index] == null) {
+ throw new InternalError("Unknown id");
+ }
+ if (keys[index] == id) {
+ return index;
+ }
+ index++;
+ if (index == keys.length) {
+ index = 0;
+ }
+ }
+ }
+
+ public boolean hasKey(long id) {
+ int index = index(id);
+ while (true) {
+ if (objects[index] == null) {
+ return false;
+ }
+ if (keys[index] == id) {
+ return true;
+ }
+ index++;
+ if (index == keys.length) {
+ index = 0;
+ }
+ }
}
- void put(long id, T object) {
- map.put(id, object);
- }
-
- T get(long id) {
- return map.get(id);
+ public void expand(int size) {
+ int l = 4 * size / 3;
+ if (l <= keys.length) {
+ return;
+ }
+ int n = tableSizeFor(l);
+ LongMap<T> temp = new LongMap<>(bitCount);
+ temp.initialize(n);
+ // Optimization, avoid growing while copying bits
+ if (bitCount > 0 && !bitSet.isEmpty()) {
+ temp.claimBits();
+ claimBits();
+ }
+ for (int tIndex = 0; tIndex < keys.length; tIndex++) {
+ T o = objects[tIndex];
+ if (o != null) {
+ long key = keys[tIndex];
+ temp.put(key, o);
+ if (bitCount != 0) {
+ for (int bIndex = 0; bIndex < bitCount; bIndex++) {
+ boolean bitValue = isSetId(key, bIndex);
+ if (bitValue) {
+ temp.setId(key, bIndex);
+ }
+ }
+ }
+ }
+ }
+ keys = temp.keys;
+ objects = temp.objects;
+ shift = temp.shift;
+ bitSet = temp.bitSet;
}
- @Override
- public Iterator<T> iterator() {
- return map.values().iterator();
+ public void put(long id, T object) {
+ if (keys == EMPTY_KEYS) {
+ // Lazy initialization
+ initialize(DEFAULT_SIZE);
+ }
+ if (count > 3 * keys.length / 4) {
+ expand(2 * keys.length);
+ }
+ if (object == null) {
+ object = (T) NULL_OBJECT;
+ }
+
+ int index = index(id);
+ // probe for empty slot
+ while (true) {
+ if (objects[index] == null) {
+ keys[index] = id;
+ objects[index] = object;
+ count++;
+ return;
+ }
+ // if it already exists, replace
+ if (keys[index] == id) {
+ objects[index] = object;
+ return;
+ }
+ index++;
+ if (index == keys.length) {
+ index = 0;
+ }
+ }
+ }
+ public T getAt(int tableIndex) {
+ T o = objects[tableIndex];
+ return o == NULL_OBJECT ? null : o;
}
- Iterator<Long> keys() {
- return map.keySet().iterator();
+ public T get(long id) {
+ if (keys == EMPTY_KEYS) {
+ return null;
+ }
+ int index = index(id);
+ while (true) {
+ if (objects[index] == null) {
+ return null;
+ }
+ if (keys[index] == id) {
+ return getAt(index);
+ }
+ index++;
+ if (index == keys.length) {
+ index = 0;
+ }
+ }
+ }
+
+ private int index(long id) {
+// int hash = (int) (id ^ (id >>> 32));
+// return hash & (keys.length - 1);
+ return (int) ((id * -7046029254386353131L) >>> shift);
+ }
+
+ // Copied from HashMap::tableSizeFor
+ private static final int tableSizeFor(int cap) {
+ int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+
+ public void forEachKey(LongConsumer keyTraverser) {
+ for (int i = 0; i < keys.length; i++) {
+ if (objects[i] != null) {
+ keyTraverser.accept(keys[i]);
+ }
+ }
+ }
+
+ public void forEach(Consumer<T> consumer) {
+ for (int i = 0; i < keys.length; i++) {
+ T o = objects[i];
+ if (o != null) {
+ consumer.accept(o);
+ }
+ }
+ }
+
+ public int size() {
+ return count;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < objects.length; i++) {
+ sb.append(i);
+ sb.append(": id=");
+ sb.append(keys[i]);
+ sb.append(" ");
+ sb.append(objects[i]);
+ sb.append("\n");
+ }
+ return sb.toString();
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectFactory.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectFactory.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -78,7 +78,7 @@
if (value instanceof Object[]) {
return createTyped(valueDescriptors, id, (Object[]) value);
}
- throw new InternalError("Object factory must have struct type");
+ throw new InternalError("Object factory must have struct type. Type was " + value.getClass().getName());
}
abstract T createTyped(List<ValueDescriptor> valueDescriptors, long id, Object[] values);
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ParserFactory.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/ParserFactory.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,13 +28,13 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.Parser;
import jdk.jfr.internal.consumer.RecordingInput;
/**
@@ -44,19 +44,21 @@
private final LongMap<Parser> parsers = new LongMap<>();
private final TimeConverter timeConverter;
private final LongMap<Type> types = new LongMap<>();
- private final LongMap<ConstantMap> constantPools;
+ private final LongMap<ConstantLookup> constantLookups;
- public ParserFactory(MetadataDescriptor metadata, TimeConverter timeConverter) throws IOException {
- this.constantPools = new LongMap<>();
+ public ParserFactory(MetadataDescriptor metadata, LongMap<ConstantLookup> constantLookups, TimeConverter timeConverter) throws IOException {
+ this.constantLookups = constantLookups;
this.timeConverter = timeConverter;
for (Type t : metadata.getTypes()) {
types.put(t.getId(), t);
}
- for (Type t : types) {
+ List<Type> typeList = new ArrayList<>();
+ types.forEach(typeList::add);
+ for (Type t : typeList) {
if (!t.getFields().isEmpty()) { // Avoid primitives
- CompositeParser cp = createCompositeParser(t);
+ CompositeParser cp = createCompositeParser(t, false);
if (t.isSimpleType()) { // Reduce to nested parser
- parsers.put(t.getId(), cp.parsers[0]);
+ parsers.put(t.getId(), cp.parsers[0]);
}
}
@@ -71,10 +73,6 @@
return parsers;
}
- public LongMap<ConstantMap> getConstantPools() {
- return constantPools;
- }
-
public LongMap<Type> getTypeMap() {
return types;
}
@@ -82,17 +80,17 @@
private EventParser createEventParser(EventType eventType) throws IOException {
List<Parser> parsers = new ArrayList<Parser>();
for (ValueDescriptor f : eventType.getFields()) {
- parsers.add(createParser(f));
+ parsers.add(createParser(f, true));
}
return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0]));
}
- private Parser createParser(ValueDescriptor v) throws IOException {
+ private Parser createParser(ValueDescriptor v, boolean event) throws IOException {
boolean constantPool = PrivateAccess.getInstance().isConstantPool(v);
if (v.isArray()) {
Type valueType = PrivateAccess.getInstance().getType(v);
ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null);
- return new ArrayParser(createParser(element));
+ return new ArrayParser(createParser(element, event));
}
long id = v.getTypeId();
Type type = types.get(id);
@@ -100,25 +98,29 @@
throw new IOException("Type '" + v.getTypeName() + "' is not defined");
}
if (constantPool) {
- ConstantMap pool = constantPools.get(id);
- if (pool == null) {
- pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
- constantPools.put(id, pool);
+ ConstantLookup lookup = constantLookups.get(id);
+ if (lookup == null) {
+ ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+ lookup = new ConstantLookup(pool, type);
+ constantLookups.put(id, lookup);
}
- return new ConstantMapValueParser(pool);
+ if (event) {
+ return new EventValueConstantParser(lookup);
+ }
+ return new ConstantValueParser(lookup);
}
Parser parser = parsers.get(id);
if (parser == null) {
if (!v.getFields().isEmpty()) {
- return createCompositeParser(type);
+ return createCompositeParser(type, event);
} else {
- return registerParserType(type, createPrimitiveParser(type));
+ return registerParserType(type, createPrimitiveParser(type, constantPool));
}
}
return parser;
}
- private Parser createPrimitiveParser(Type type) throws IOException {
+ private Parser createPrimitiveParser(Type type, boolean event) throws IOException {
switch (type.getName()) {
case "int":
return new IntegerParser();
@@ -138,8 +140,9 @@
return new ByteParser();
case "java.lang.String":
ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
- constantPools.put(type.getId(), pool);
- return new StringParser(pool);
+ ConstantLookup lookup = new ConstantLookup(pool, type);
+ constantLookups.put(type.getId(), lookup);
+ return new StringParser(lookup, event);
default:
throw new IOException("Unknown primitive type " + type.getName());
}
@@ -155,7 +158,7 @@
return parser;
}
- private CompositeParser createCompositeParser(Type type) throws IOException {
+ private CompositeParser createCompositeParser(Type type, boolean event) throws IOException {
List<ValueDescriptor> vds = type.getFields();
Parser[] parsers = new Parser[vds.size()];
CompositeParser composite = new CompositeParser(parsers);
@@ -164,7 +167,7 @@
int index = 0;
for (ValueDescriptor vd : vds) {
- parsers[index++] = createParser(vd);
+ parsers[index++] = createParser(vd, event);
}
return composite;
}
@@ -174,6 +177,11 @@
public Object parse(RecordingInput input) throws IOException {
return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
}
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(1);
+ }
}
private static final class ByteParser extends Parser {
@@ -181,19 +189,50 @@
public Object parse(RecordingInput input) throws IOException {
return Byte.valueOf(input.readByte());
}
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(1);
+ }
}
private static final class LongParser extends Parser {
+ private Object lastLongObject = Long.valueOf(0);
+ private long last = 0;
+
@Override
public Object parse(RecordingInput input) throws IOException {
- return Long.valueOf(input.readLong());
+ long l = input.readLong();
+ if (l != last) {
+ last = l;
+ lastLongObject = Long.valueOf(l);
+ }
+ return lastLongObject;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readLong();
}
}
private static final class IntegerParser extends Parser {
+ private Integer lastIntegergObject = Integer.valueOf(0);
+ private int last = 0;
+
@Override
public Object parse(RecordingInput input) throws IOException {
- return Integer.valueOf(input.readInt());
+ int i = input.readInt();
+ if (i != last) {
+ last = i;
+ lastIntegergObject = Integer.valueOf(i);
+ }
+ return lastIntegergObject;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readInt();
}
}
@@ -202,6 +241,11 @@
public Object parse(RecordingInput input) throws IOException {
return Short.valueOf(input.readShort());
}
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readShort();
+ }
}
private static final class CharacterParser extends Parser {
@@ -209,6 +253,11 @@
public Object parse(RecordingInput input) throws IOException {
return Character.valueOf(input.readChar());
}
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readChar();
+ }
}
private static final class FloatParser extends Parser {
@@ -216,6 +265,11 @@
public Object parse(RecordingInput input) throws IOException {
return Float.valueOf(input.readFloat());
}
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(Float.SIZE);
+ }
}
private static final class DoubleParser extends Parser {
@@ -223,33 +277,10 @@
public Object parse(RecordingInput input) throws IOException {
return Double.valueOf(input.readDouble());
}
- }
-
- private static final class StringParser extends Parser {
- private final ConstantMap stringConstantMap;
- private String last;
-
- StringParser(ConstantMap stringConstantMap) {
- this.stringConstantMap = stringConstantMap;
- }
@Override
- public Object parse(RecordingInput input) throws IOException {
- String s = parseEncodedString(input);
- if (!Objects.equals(s, last)) {
- last = s;
- }
- return last;
- }
-
- private String parseEncodedString(RecordingInput input) throws IOException {
- byte encoding = input.readByte();
- if (encoding == RecordingInput.STRING_ENCODING_CONSTANT_POOL) {
- long id = input.readLong();
- return (String) stringConstantMap.get(id);
- } else {
- return input.readEncodedString(encoding);
- }
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(Double.SIZE);
}
}
@@ -269,9 +300,17 @@
}
return array;
}
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ final int size = input.readInt();
+ for (int i = 0; i < size; i++) {
+ elementParser.skip(input);
+ }
+ }
}
- private final static class CompositeParser extends Parser {
+ final static class CompositeParser extends Parser {
private final Parser[] parsers;
public CompositeParser(Parser[] valueParsers) {
@@ -286,18 +325,54 @@
}
return values;
}
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ for (int i = 0; i < parsers.length; i++) {
+ parsers[i].skip(input);
+ }
+ }
}
- private static final class ConstantMapValueParser extends Parser {
- private final ConstantMap pool;
-
- ConstantMapValueParser(ConstantMap pool) {
- this.pool = pool;
+ public static final class EventValueConstantParser extends Parser {
+ private final ConstantLookup lookup;
+ private Object lastValue = 0;
+ private long lastKey = -1;
+ EventValueConstantParser(ConstantLookup lookup) {
+ this.lookup = lookup;
}
@Override
public Object parse(RecordingInput input) throws IOException {
- return pool.get(input.readLong());
+ long key = input.readLong();
+ if (key == lastKey) {
+ return lastValue;
+ }
+ lastKey = key;
+ lastValue = lookup.getCurrentResolved(key);
+ return lastValue;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readLong();
+ }
+ }
+
+ public static final class ConstantValueParser extends Parser {
+ private final ConstantLookup lookup;
+ ConstantValueParser(ConstantLookup lookup) {
+ this.lookup = lookup;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return lookup.getCurrent(input.readLong());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readLong();
}
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,11 +25,13 @@
package jdk.jfr.consumer;
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -37,6 +39,9 @@
import jdk.jfr.Timestamp;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.Parser;
+import jdk.jfr.internal.consumer.RecordingInternals;
import jdk.jfr.internal.tool.PrettyWriter;
/**
@@ -51,6 +56,33 @@
*/
public class RecordedObject {
+ static{
+ RecordingInternals.INSTANCE = new RecordingInternals() {
+ public List<Type> readTypes(RecordingFile file) throws IOException {
+ return file.readTypes();
+ }
+
+ public boolean isLastEventInChunk(RecordingFile file) {
+ return file.isLastEventInChunk;
+ }
+
+ @Override
+ public Object getOffsetDataTime(RecordedObject event, String name) {
+ return event.getOffsetDateTime(name);
+ }
+
+ @Override
+ public void sort(List<RecordedEvent> events) {
+ Collections.sort(events, (e1, e2) -> Long.compare(e1.endTime, e2.endTime));
+ }
+
+ @Override
+ public Parser newStringParser() {
+ return new StringParser(null, false);
+ }
+ };
+ }
+
private final static class UnsignedValue {
private final Object o;
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,7 +32,6 @@
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -41,7 +40,6 @@
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.RecordingInput;
-import jdk.jfr.internal.consumer.RecordingInternals;
/**
* A recording file.
@@ -62,29 +60,8 @@
* @since 9
*/
public final class RecordingFile implements Closeable {
- static{
- RecordingInternals.INSTANCE = new RecordingInternals() {
- public List<Type> readTypes(RecordingFile file) throws IOException {
- return file.readTypes();
- }
- public boolean isLastEventInChunk(RecordingFile file) {
- return file.isLastEventInChunk;
- }
-
- @Override
- public Object getOffsetDataTime(RecordedObject event, String name) {
- return event.getOffsetDateTime(name);
- }
-
- @Override
- public void sort(List<RecordedEvent> events) {
- Collections.sort(events, (e1, e2) -> Long.compare(e1.endTime, e2.endTime));
- }
- };
- }
-
- private boolean isLastEventInChunk;
+ boolean isLastEventInChunk;
private final File file;
private RecordingInput input;
private ChunkParser chunkParser;
@@ -154,14 +131,15 @@
*/
public List<EventType> readEventTypes() throws IOException {
ensureOpen();
+ MetadataDescriptor previous = null;
List<EventType> types = new ArrayList<>();
HashSet<Long> foundIds = new HashSet<>();
try (RecordingInput ri = new RecordingInput(file)) {
ChunkHeader ch = new ChunkHeader(ri);
- aggregateEventTypeForChunk(ch, types, foundIds);
+ aggregateEventTypeForChunk(ch, null, types, foundIds);
while (!ch.isLastChunk()) {
ch = ch.nextHeader();
- aggregateEventTypeForChunk(ch, types, foundIds);
+ previous = aggregateEventTypeForChunk(ch, previous, types, foundIds);
}
}
return types;
@@ -169,37 +147,41 @@
List<Type> readTypes() throws IOException {
ensureOpen();
+ MetadataDescriptor previous = null;
List<Type> types = new ArrayList<>();
HashSet<Long> foundIds = new HashSet<>();
try (RecordingInput ri = new RecordingInput(file)) {
ChunkHeader ch = new ChunkHeader(ri);
- aggregateTypeForChunk(ch, types, foundIds);
+ ch.awaitFinished();
+ aggregateTypeForChunk(ch, null, types, foundIds);
while (!ch.isLastChunk()) {
ch = ch.nextHeader();
- aggregateTypeForChunk(ch, types, foundIds);
+ previous = aggregateTypeForChunk(ch, previous, types, foundIds);
}
}
return types;
}
- private void aggregateTypeForChunk(ChunkHeader ch, List<Type> types, HashSet<Long> foundIds) throws IOException {
- MetadataDescriptor m = ch.readMetadata();
+ private MetadataDescriptor aggregateTypeForChunk(ChunkHeader ch, MetadataDescriptor previous, List<Type> types, HashSet<Long> foundIds) throws IOException {
+ MetadataDescriptor m = ch.readMetadata(previous);
for (Type t : m.getTypes()) {
if (!foundIds.contains(t.getId())) {
types.add(t);
foundIds.add(t.getId());
}
}
+ return m;
}
- private static void aggregateEventTypeForChunk(ChunkHeader ch, List<EventType> types, HashSet<Long> foundIds) throws IOException {
- MetadataDescriptor m = ch.readMetadata();
+ private static MetadataDescriptor aggregateEventTypeForChunk(ChunkHeader ch, MetadataDescriptor previous, List<EventType> types, HashSet<Long> foundIds) throws IOException {
+ MetadataDescriptor m = ch.readMetadata(previous);
for (EventType t : m.getEventTypes()) {
if (!foundIds.contains(t.getId())) {
types.add(t);
foundIds.add(t.getId());
}
}
+ return m;
}
/**
@@ -246,6 +228,11 @@
}
}
+ // package protected
+ File getFile() {
+ return file;
+ }
+
// either sets next to an event or sets eof to true
private void findNext() throws IOException {
while (nextEvent == null) {
@@ -266,4 +253,6 @@
throw new IOException("Stream Closed");
}
}
+
+
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/TimeConverter.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/TimeConverter.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -69,4 +69,8 @@
public ZoneOffset getZoneOffset() {
return zoneOffet;
}
+
+ public long convertDurationNanos(long durationNanos) {
+ return (long) (durationNanos * divisor);
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveRecordingEvent.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveRecordingEvent.java Fri May 17 16:02:27 2019 +0200
@@ -53,6 +53,10 @@
@Timespan(Timespan.MILLISECONDS)
public long maxAge;
+ @Label("Flush Interval")
+ @Timespan(Timespan.MILLISECONDS)
+ public long flushInterval;
+
@Label("Max Size")
@DataAmount
public long maxSize;
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriter.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriter.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
package jdk.jfr.internal;
import jdk.internal.misc.Unsafe;
-import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringEncoding;
/**
* Class must reside in a package with package restriction.
@@ -115,18 +115,18 @@
public void putString(String s, StringPool pool) {
if (s == null) {
- putByte(RecordingInput.STRING_ENCODING_NULL);
+ putByte(StringEncoding.STRING_ENCODING_NULL);
return;
}
int length = s.length();
if (length == 0) {
- putByte(RecordingInput.STRING_ENCODING_EMPTY_STRING);
+ putByte(StringEncoding.STRING_ENCODING_EMPTY_STRING);
return;
}
if (length > StringPool.MIN_LIMIT && length < StringPool.MAX_LIMIT) {
long l = StringPool.addString(s);
if (l > 0) {
- putByte(RecordingInput.STRING_ENCODING_CONSTANT_POOL);
+ putByte(StringEncoding.STRING_ENCODING_CONSTANT_POOL);
putLong(l);
return;
}
@@ -138,7 +138,7 @@
private void putStringValue(String s) {
int length = s.length();
if (isValidForSize(1 + 5 + 3 * length)) {
- putUncheckedByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // 1 byte
+ putUncheckedByte(StringEncoding.STRING_ENCODING_CHAR_ARRAY); // 1 byte
putUncheckedInt(length); // max 5 bytes
for (int i = 0; i < length; i++) {
putUncheckedChar(s.charAt(i)); // max 3 bytes
@@ -197,11 +197,7 @@
if (currentPosition + requestedSize > maxPosition) {
flushOnEnd = flush(usedSize(), requestedSize);
// retry
- if (currentPosition + requestedSize > maxPosition) {
- Logger.log(LogTag.JFR_SYSTEM,
- LogLevel.WARN, () ->
- "Unable to commit. Requested size " + requestedSize + " too large");
- valid = false;
+ if (!valid) {
return false;
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -270,7 +270,6 @@
*
* @param file the file where data should be written, or null if it should
* not be copied out (in memory).
- *
* @throws IOException
*/
public native void setOutput(String file);
@@ -460,6 +459,20 @@
public static native boolean flush(EventWriter writer, int uncommittedSize, int requestedSize);
/**
+ * Flushes all thread buffers to disk and the constant pool data needed to read
+ * them.
+ * <p>
+ * When the method returns, the chunk header should be updated with valid
+ * pointers to the metadata event, last check point event, correct file size and
+ * the generation id.
+ *
+ * @param includeMetadata {@code true} if metadata event should be written, {@code false}
+ * otherwise
+ * @param flushCounter the (flushCounter + 1= should be written to the chunk header, by default the
+ * value in the chunk header should be {@code 1} and set {@code 0} once the chunk is complete.
+ */
+ public native void flush(boolean includeMetadata, short flushCounter);
+ /**
* Sets the location of the disk repository, to be used at an emergency
* dump.
*
@@ -523,4 +536,30 @@
* @return if it is time to perform a chunk rotation
*/
public native boolean shouldRotateDisk();
+
+ /**
+ * Exclude a thread from the jfr system
+ *
+ */
+ public native void exclude(Thread thread);
+
+ /**
+ * Include a thread back into the jfr system
+ *
+ */
+ public native void include(Thread thread);
+
+ /**
+ * Test if a thread ius currently excluded from the jfr system.
+ *
+ * @return is thread currently excluded
+ */
+ public native boolean isExcluded(Thread thread);
+
+ /**
+ * Get the start time in nanos from the header of the current chunk
+ *
+ *@return start time of the recording in nanos, -1 in case of in-memory
+ */
+ public native long getChunkStartNanos();
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/LogTag.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/LogTag.java Fri May 17 16:02:27 2019 +0200
@@ -63,21 +63,25 @@
*/
JFR_SYSTEM_METADATA(6),
/**
+ * Covers streaming (for Hotspot developers)
+ */
+ JFR_SYSTEM_STREAMING(7),
+ /**
* Covers metadata for Java user (for Hotspot developers)
*/
- JFR_METADATA(7),
+ JFR_METADATA(8),
/**
* Covers events (for users of the JDK)
*/
- JFR_EVENT(8),
+ JFR_EVENT(9),
/**
* Covers setting (for users of the JDK)
*/
- JFR_SETTING(9),
+ JFR_SETTING(10),
/**
* Covers usage of jcmd with JFR
*/
- JFR_DCMD(10);
+ JFR_DCMD(11);
/* set from native side */
volatile int tagSetLevel = 100; // prevent logging if JVM log system has not been initialized
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataDescriptor.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataDescriptor.java Fri May 17 16:02:27 2019 +0200
@@ -25,7 +25,6 @@
package jdk.jfr.internal;
-import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,6 +34,7 @@
import java.util.TimeZone;
import jdk.jfr.EventType;
+import jdk.jfr.internal.consumer.RecordingInput;
/**
* Metadata about a chunk
@@ -214,6 +214,7 @@
long gmtOffset;
String locale;
Element root;
+ public long metadataId;
// package private
MetadataDescriptor() {
@@ -252,7 +253,7 @@
return locale;
}
- public static MetadataDescriptor read(DataInput input) throws IOException {
+ public static MetadataDescriptor read(RecordingInput input) throws IOException {
MetadataReader r = new MetadataReader(input);
return r.getDescriptor();
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java Fri May 17 16:02:27 2019 +0200
@@ -49,6 +49,9 @@
import jdk.jfr.SettingDescriptor;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.MetadataDescriptor.Element;
+import jdk.jfr.internal.consumer.Parser;
+import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.RecordingInternals;
/**
* Parses metadata.
@@ -61,12 +64,13 @@
private final MetadataDescriptor descriptor;
private final Map<Long, Type> types = new HashMap<>();
- public MetadataReader(DataInput input) throws IOException {
+ public MetadataReader(RecordingInput input) throws IOException {
this.input = input;
int size = input.readInt();
this.pool = new ArrayList<>(size);
+ Parser p = RecordingInternals.instance().newStringParser();
for (int i = 0; i < size; i++) {
- this.pool.add(input.readUTF());
+ this.pool.add((String) p.parse(input));
}
descriptor = new MetadataDescriptor();
Element root = createElement();
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -61,6 +61,8 @@
private boolean staleMetadata = true;
private boolean unregistered;
private long lastUnloaded = -1;
+ private boolean flushMetadata;
+ private short flushCounter = 0;
public MetadataRepository() {
initializeJVMEventTypes();
@@ -142,6 +144,7 @@
typeLibrary.addType(handler.getPlatformEventType());
if (jvm.isRecording()) {
storeDescriptorInJVM(); // needed for emergency dump
+ flushMetadata = true;
settingsManager.setEventControl(handler.getEventControl());
settingsManager.updateRetransform(Collections.singletonList((eventClass)));
} else {
@@ -255,12 +258,12 @@
staleMetadata = true;
}
- // Lock around setOutput ensures that other threads dosn't
- // emit event after setOutput and unregister the event class, before a call
+ // Lock around setOutput ensures that other threads don't
+ // emit events after setOutput and unregister the event class, before a call
// to storeDescriptorInJVM
- synchronized void setOutput(String filename) {
+ synchronized void setOutput(String filename) {
jvm.setOutput(filename);
-
+ flushMetadata = false;
unregisterUnloaded();
if (unregistered) {
staleMetadata = typeLibrary.clearUnregistered();
@@ -269,6 +272,7 @@
if (staleMetadata) {
storeDescriptorInJVM();
}
+ flushCounter = 0;
}
private void unregisterUnloaded() {
@@ -307,4 +311,11 @@
throw new InternalError("Mirror class must have annotation " + MirrorEvent.class.getName());
}
+ public synchronized void flush() {
+ jvm.flush(flushMetadata || flushCounter == 0, ++flushCounter);
+ if (flushCounter == Short.MAX_VALUE) {
+ flushCounter = 0;
+ }
+ this.flushMetadata = false;
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataWriter.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataWriter.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -53,7 +53,7 @@
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.MetadataDescriptor.Attribute;
import jdk.jfr.internal.MetadataDescriptor.Element;
-import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringEncoding;
/**
* Class responsible for converting a list of types into a format that can be
@@ -94,10 +94,10 @@
private void writeString(DataOutput out, String s) throws IOException {
if (s == null ) {
- out.writeByte(RecordingInput.STRING_ENCODING_NULL);
+ out.writeByte(StringEncoding.STRING_ENCODING_NULL);
return;
}
- out.writeByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // encoding UTF-16
+ out.writeByte(StringEncoding.STRING_ENCODING_CHAR_ARRAY); // encoding UTF-16
int length = s.length();
writeInt(out, length);
for (int i = 0; i < length; i++) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -69,7 +69,7 @@
super(name, Type.SUPER_TYPE_EVENT, id);
this.dynamicSettings = dynamicSettings;
this.isJVM = Type.isDefinedByJVM(id);
- this.isMethodSampling = name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample");
+ this.isMethodSampling = isJVM && (name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample"));
this.isJDK = isJDK;
this.stackTraceOffset = stackTraceOffset(name, isJDK);
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,6 @@
import static jdk.jfr.internal.LogTag.JFR;
import static jdk.jfr.internal.LogTag.JFR_SYSTEM;
-import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.time.Duration;
@@ -96,6 +95,7 @@
Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> {
result.add(new Timer("JFR Recording Scheduler", true));
});
+ jvm.exclude(t);
t.start();
t.join();
return result.get(0);
@@ -204,7 +204,7 @@
repository.clear();
}
- synchronized void start(PlatformRecording recording) {
+ synchronized long start(PlatformRecording recording) {
// State can only be NEW or DELAYED because of previous checks
Instant now = Instant.now();
recording.setStartTime(now);
@@ -215,14 +215,17 @@
}
boolean toDisk = recording.isToDisk();
boolean beginPhysical = true;
+ long streamInterval = recording.getStreamIntervalMillis();
for (PlatformRecording s : getRecordings()) {
if (s.getState() == RecordingState.RUNNING) {
beginPhysical = false;
if (s.isToDisk()) {
toDisk = true;
}
+ streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis());
}
}
+ long startNanos = -1;
if (beginPhysical) {
RepositoryChunk newChunk = null;
if (toDisk) {
@@ -232,7 +235,8 @@
MetadataRepository.getInstance().setOutput(null);
}
currentChunk = newChunk;
- jvm.beginRecording_();
+ jvm.beginRecording_();
+ startNanos = jvm.getChunkStartNanos();
recording.setState(RecordingState.RUNNING);
updateSettings();
writeMetaEvents();
@@ -241,7 +245,8 @@
if (toDisk) {
newChunk = repository.newChunk(now);
RequestEngine.doChunkEnd();
- MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
+ MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
+ startNanos = jvm.getChunkStartNanos();
}
recording.setState(RecordingState.RUNNING);
updateSettings();
@@ -251,8 +256,12 @@
}
currentChunk = newChunk;
}
+ if (toDisk) {
+ RequestEngine.setFlushInterval(streamInterval);
+ }
+ RequestEngine.doChunkBegin();
- RequestEngine.doChunkBegin();
+ return startNanos;
}
synchronized void stop(PlatformRecording recording) {
@@ -267,6 +276,7 @@
Instant now = Instant.now();
boolean toDisk = false;
boolean endPhysical = true;
+ long streamInterval = Long.MAX_VALUE;
for (PlatformRecording s : getRecordings()) {
RecordingState rs = s.getState();
if (s != recording && RecordingState.RUNNING == rs) {
@@ -274,6 +284,7 @@
if (s.isToDisk()) {
toDisk = true;
}
+ streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis());
}
}
OldObjectSample.emit(recording);
@@ -309,6 +320,13 @@
currentChunk = newChunk;
RequestEngine.doChunkBegin();
}
+
+ if (toDisk) {
+ RequestEngine.setFlushInterval(streamInterval);
+ } else {
+ RequestEngine.setFlushInterval(Long.MAX_VALUE);
+ }
+
recording.setState(RecordingState.STOPPED);
}
@@ -395,6 +413,7 @@
r.appendChunk(chunk);
}
}
+ FilePurger.purge();
}
private void writeMetaEvents() {
@@ -415,6 +434,8 @@
event.maxSize = size == null ? Long.MAX_VALUE : size;
Instant start = r.getStartTime();
event.recordingStart = start == null ? Long.MAX_VALUE : start.toEpochMilli();
+ Duration fi = r.getFlushInterval();
+ event.flushInterval = fi == null ? Long.MAX_VALUE : fi.toMillis();
event.commit();
}
}
@@ -448,7 +469,7 @@
JVM.FILE_DELTA_CHANGE.wait(duration < 10 ? 10 : duration);
}
} catch (InterruptedException e) {
- e.printStackTrace();
+ // Ignore
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -84,6 +84,7 @@
private TimerTask startTask;
private AccessControlContext noDestinationDumpOnExitAccessControlContext;
private boolean shuoldWriteActiveRecordingEvent = true;
+ private Duration flushInterval;
PlatformRecording(PlatformRecorder recorder, long id) {
// Typically the access control context is taken
@@ -98,9 +99,10 @@
this.name = String.valueOf(id);
}
- public void start() {
+ public long start() {
RecordingState oldState;
RecordingState newState;
+ long startNanos = -1;
synchronized (recorder) {
oldState = getState();
if (!Utils.isBefore(state, RecordingState.RUNNING)) {
@@ -111,7 +113,7 @@
startTask = null;
startTime = null;
}
- recorder.start(this);
+ startNanos = recorder.start(this);
Logger.log(LogTag.JFR, LogLevel.INFO, () -> {
// Only print non-default values so it easy to see
// which options were added
@@ -143,6 +145,8 @@
newState = getState();
}
notifyIfStateChanged(oldState, newState);
+
+ return startNanos;
}
public boolean stop(String reason) {
@@ -775,4 +779,28 @@
public SafePath getDumpOnExitDirectory() {
return this.dumpOnExitDirectory;
}
+
+ public void setFlushInterval(Duration interval) {
+ synchronized (recorder) {
+ if (getState() == RecordingState.CLOSED) {
+ throw new IllegalStateException("Can't set stream interval when recording is closed");
+ }
+ this.flushInterval = interval;
+ }
+ }
+
+ public Duration getFlushInterval() {
+ synchronized (recorder) {
+ return flushInterval;
+ }
+ }
+
+ public long getStreamIntervalMillis() {
+ synchronized (recorder) {
+ if (flushInterval != null) {
+ return flushInterval.toMillis();
+ }
+ return Long.MAX_VALUE;
+ }
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Repository.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Repository.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,6 +43,7 @@
public final static DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter
.ofPattern("yyyy_MM_dd_HH_mm_ss");
+ private static final String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
private final Set<SafePath> cleanupDirectories = new HashSet<>();
private SafePath baseLocation;
@@ -55,7 +56,7 @@
return instance;
}
- public synchronized void setBasePath(SafePath baseLocation) throws Exception {
+ public synchronized void setBasePath(SafePath baseLocation) throws IOException {
// Probe to see if repository can be created, needed for fail fast
// during JVM startup or JFR.configure
this.repository = createRepository(baseLocation);
@@ -69,7 +70,7 @@
this.baseLocation = baseLocation;
}
- synchronized void ensureRepository() throws Exception {
+ public synchronized void ensureRepository() throws IOException {
if (baseLocation == null) {
setBasePath(SecuritySupport.JAVA_IO_TMPDIR);
}
@@ -91,7 +92,7 @@
}
}
- private static SafePath createRepository(SafePath basePath) throws Exception {
+ private static SafePath createRepository(SafePath basePath) throws IOException {
SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath);
SafePath f = null;
@@ -108,13 +109,14 @@
}
if (i == MAX_REPO_CREATION_RETRIES) {
- throw new Exception("Unable to create JFR repository directory using base location (" + basePath + ")");
+ throw new IOException("Unable to create JFR repository directory using base location (" + basePath + ")");
}
SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f);
+ SecuritySupport.setProperty(JFR_REPOSITORY_LOCATION_PROPERTY, canonicalRepositoryPath.toString());
return canonicalRepositoryPath;
}
- private static SafePath createRealBasePath(SafePath safePath) throws Exception {
+ private static SafePath createRealBasePath(SafePath safePath) throws IOException {
if (SecuritySupport.exists(safePath)) {
if (!SecuritySupport.isWritable(safePath)) {
throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable");
@@ -154,7 +156,7 @@
SecuritySupport.clearDirectory(p);
Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p);
} catch (IOException e) {
- Logger.log(LogTag.JFR, LogLevel.ERROR, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
+ Logger.log(LogTag.JFR, LogLevel.INFO, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
}
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -63,10 +63,10 @@
LocalDateTime.ofInstant(startTime, z.getZone()));
this.startTime = startTime;
this.repositoryPath = path;
- this.unFinishedFile = findFileName(repositoryPath, fileName, ".part");
+ this.unFinishedFile = findFileName(repositoryPath, fileName, ".jfr");
this.file = findFileName(repositoryPath, fileName, ".jfr");
this.unFinishedRAF = SecuritySupport.createRandomAccessFile(unFinishedFile);
- SecuritySupport.touch(file);
+ // SecuritySupport.touch(file);
}
private static SafePath findFileName(SafePath directory, String name, String extension) throws Exception {
@@ -105,8 +105,6 @@
private static long finish(SafePath unFinishedFile, SafePath file) throws IOException {
Objects.requireNonNull(unFinishedFile);
Objects.requireNonNull(file);
- SecuritySupport.delete(file);
- SecuritySupport.moveReplace(unFinishedFile, file);
return SecuritySupport.getFileSize(file);
}
@@ -123,9 +121,11 @@
SecuritySupport.delete(f);
Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " deleted");
} catch (IOException e) {
- Logger.log(LogTag.JFR, LogLevel.ERROR, () -> "Repository chunk " + f + " could not be deleted: " + e.getMessage());
+ // Probably happens because file is being streamed
+ // on Windows where files in use can't be removed.
+ Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " could not be deleted: " + e.getMessage());
if (f != null) {
- SecuritySupport.deleteOnExit(f);
+ FilePurger.add(f);
}
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -96,6 +97,8 @@
private final static List<RequestHook> entries = new CopyOnWriteArrayList<>();
private static long lastTimeMillis;
+ private static long flushInterval = Long.MAX_VALUE;
+ private static long streamDelta;
public static void addHook(AccessControlContext acc, PlatformEventType type, Runnable hook) {
Objects.requireNonNull(acc);
@@ -209,7 +212,9 @@
lastTimeMillis = now;
return 0;
}
- for (RequestHook he : entries) {
+ Iterator<RequestHook> hookIterator = entries.iterator();
+ while(hookIterator.hasNext()) {
+ RequestHook he = hookIterator.next();
long left = 0;
PlatformEventType es = he.type;
// Not enabled, skip.
@@ -228,7 +233,6 @@
// for wait > period
r_delta = 0;
he.execute();
- ;
}
// calculate time left
@@ -250,7 +254,36 @@
min = left;
}
}
+ // Flush should happen after all periodic events has been emitted
+ // Repeat of the above algorithm, but using the stream interval.
+ if (flushInterval != Long.MAX_VALUE) {
+ long r_period = flushInterval;
+ long r_delta = streamDelta;
+ r_delta += delta;
+ if (r_delta >= r_period) {
+ r_delta = 0;
+ MetadataRepository.getInstance().flush();
+ }
+ long left = (r_period - r_delta);
+ if (left < 0) {
+ left = 0;
+ }
+ streamDelta = r_delta;
+ if (min == 0 || left < min) {
+ min = left;
+ }
+ }
+
lastTimeMillis = now;
return min;
}
+
+ public static void setFlushInterval(long interval) {
+ flushInterval = interval;
+ if (interval < flushInterval) {
+ synchronized (JVM.FILE_DELTA_CHANGE) {
+ JVM.FILE_DELTA_CHANGE.notifyAll();
+ }
+ }
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -290,6 +290,10 @@
doPrivileged(() -> MetadataRepository.getInstance().registerMirror(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
}
+ public static void setProperty(String propertyName, String value) {
+ doPrivileged(() -> System.setProperty(propertyName, value), new PropertyPermission(propertyName, "write"));
+ }
+
static boolean getBooleanProperty(String propertyName) {
return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read"));
}
@@ -330,7 +334,7 @@
doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner()));
}
- static SafePath toRealPath(SafePath safePath) throws Exception {
+ static SafePath toRealPath(SafePath safePath) throws IOException {
return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath()));
}
@@ -408,7 +412,7 @@
}
static Class<?> defineClass(Class<?> lookupClass, byte[] bytes) {
- return AccessController.doPrivileged(new PrivilegedAction<>() {
+ return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
@Override
public Class<?> run() {
try {
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -595,4 +595,10 @@
String idText = recording == null ? "" : "-id-" + Long.toString(recording.getId());
return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr";
}
+ public static void takeNap(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ }
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,48 +25,61 @@
package jdk.jfr.internal.consumer;
-import java.io.DataInput;
import java.io.IOException;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.MetadataDescriptor;
+import jdk.jfr.internal.Utils;
public final class ChunkHeader {
+ public static final long HEADER_SIZE = 68;
+ public static final byte MARKED_FOR_DELETION = (byte) 255;
+ private static final byte UPDATING_CHUNK_HEADER = (byte) 255;
+ private static final long CHUNK_SIZE_POSITION = 8;
+ private static final long DURATION_NANOS_POSITION = 40;
+ private static final long FILE_STATE_POSITION = 64;
private static final long METADATA_TYPE_ID = 0;
private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' };
private final short major;
private final short minor;
- private final long chunkSize;
private final long chunkStartTicks;
private final long ticksPerSecond;
private final long chunkStartNanos;
- private final long metadataPosition;
- // private final long absoluteInitialConstantPoolPosition;
- private final long absoluteChunkEnd;
- private final long absoluteEventStart;
private final long absoluteChunkStart;
- private final boolean lastChunk;
private final RecordingInput input;
- private final long durationNanos;
private final long id;
+ private long absoluteEventStart;
+ private long chunkSize;
private long constantPoolPosition;
+ private long metadataPosition;
+ private long durationNanos;
+ private long absoluteChunkEnd;
+ private boolean isFinished;
+ private boolean finished;
public ChunkHeader(RecordingInput input) throws IOException {
this(input, 0, 0);
}
private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException {
+ this.absoluteChunkStart = absoluteChunkStart;
+ this.absoluteEventStart = absoluteChunkStart + HEADER_SIZE;
+ if (input.getFileSize() < HEADER_SIZE) {
+ throw new IOException("Not a complete Chunk header");
+ }
+ input.setValidSize(absoluteChunkStart + HEADER_SIZE);
input.position(absoluteChunkStart);
if (input.position() >= input.size()) {
- throw new IOException("Chunk contains no data");
+ throw new IOException("Chunk contains no data");
}
verifyMagic(input);
this.input = input;
this.id = id;
- Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id);
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: " + id);
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: file=" + input.getFilename());
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart);
major = input.readRawShort();
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major);
@@ -75,11 +88,11 @@
if (major != 1 && major != 2) {
throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK.");
}
- chunkSize = input.readRawLong();
+ input.readRawLong(); // chunk size
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
- this.constantPoolPosition = input.readRawLong();
+ input.readRawLong(); // constant pool position
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
- metadataPosition = input.readRawLong();
+ input.readRawLong(); // metadata position
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
chunkStartNanos = input.readRawLong(); // nanos since epoch
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos);
@@ -91,21 +104,97 @@
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond);
input.readRawInt(); // features, not used
- // set up boundaries
- this.absoluteChunkStart = absoluteChunkStart;
- absoluteChunkEnd = absoluteChunkStart + chunkSize;
- lastChunk = input.size() == absoluteChunkEnd;
- absoluteEventStart = input.position();
+ refresh();
+ input.position(absoluteEventStart);
+ }
+ public void refresh() throws IOException {
+ while (true) {
+ byte fileState1;
+ input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+ while ((fileState1 = input.readPhysicalByte()) == UPDATING_CHUNK_HEADER) {
+ Utils.takeNap(3);
+ input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+ }
+ input.positionPhysical(absoluteChunkStart + CHUNK_SIZE_POSITION);
+ long chunkSize = input.readPhysicalLong();
+ long constantPoolPosition = input.readPhysicalLong();
+ long metadataPosition = input.readPhysicalLong();
+ input.positionPhysical(absoluteChunkStart + DURATION_NANOS_POSITION);
+ long durationNanos = input.readPhysicalLong();
+ input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+ byte fileState2 = input.readPhysicalByte();
+ if (fileState1 == fileState2) { // valid header
+ finished = fileState1 == 0;
+ if (constantPoolPosition != 0 && metadataPosition != 0) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Setting input size to " + (absoluteChunkStart + chunkSize));
+ if (finished) {
+ // This assumes that the whole recording
+ // is finished if the first chunk is.
+ // This is a limitation we may want to
+ // remove, but greatly improves performance as
+ // data can be read across chunk boundaries
+ // of multi-chunk files and only once.
+ input.setValidSize(input.getFileSize());
+ } else {
+ input.setValidSize(absoluteChunkStart + chunkSize);
+ }
+ this.chunkSize = chunkSize;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
+ this.constantPoolPosition = constantPoolPosition;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
+ this.metadataPosition = metadataPosition;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
+ this.durationNanos = durationNanos;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos =" + durationNanos);
+ isFinished = fileState2 == 0;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: generation=" + fileState2);
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: finished=" + isFinished);
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: fileSize=" + input.size());
+ absoluteChunkEnd = absoluteChunkStart + chunkSize;
+ return;
+ }
+ }
+ }
+ }
- // read metadata
- input.position(absoluteEventStart);
+ public void awaitFinished() throws IOException {
+ if (finished) {
+ return;
+ }
+ long pos = input.position();
+ try {
+ input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+ while (true) {
+ byte filestate = input.readPhysicalByte();
+ if (filestate ==0) {
+ finished = true;
+ return;
+ }
+ Utils.takeNap(3);
+ }
+ } finally {
+ input.position(pos);
+ }
+ }
+
+ public boolean isLastChunk() throws IOException {
+ awaitFinished();
+ // streaming files only have one chunk
+ return input.getFileSize() == absoluteChunkEnd;
+ }
+
+ public boolean isFinished() throws IOException {
+ return isFinished;
}
public ChunkHeader nextHeader() throws IOException {
return new ChunkHeader(input, absoluteChunkEnd, id + 1);
}
+ public MetadataDescriptor readMetadata() throws IOException {
+ return readMetadata(null);
+ }
- public MetadataDescriptor readMetadata() throws IOException {
+ public MetadataDescriptor readMetadata(MetadataDescriptor previous) throws IOException {
input.position(absoluteChunkStart + metadataPosition);
input.readInt(); // size
long id = input.readLong(); // event type id
@@ -115,15 +204,15 @@
input.readLong(); // start time
input.readLong(); // duration
long metadataId = input.readLong();
- Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId);
- // No need to read if metadataId == lastMetadataId, but we
- // do it for verification purposes.
- return MetadataDescriptor.read(input);
+ if (previous != null && metadataId == previous.metadataId) {
+ return previous;
+ }
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "New metadata id = " + metadataId);
+ MetadataDescriptor m = MetadataDescriptor.read(input);
+ m.metadataId = metadataId;
+ return m;
}
- public boolean isLastChunk() {
- return lastChunk;
- }
public short getMajor() {
return major;
@@ -137,13 +226,22 @@
return absoluteChunkStart;
}
+ public long getAbsoluteEventStart() {
+ return absoluteEventStart;
+ }
public long getConstantPoolPosition() {
return constantPoolPosition;
}
+ public long getMetataPosition() {
+ return metadataPosition;
+ }
public long getStartTicks() {
return chunkStartTicks;
}
+ public long getChunkSize() {
+ return chunkSize;
+ }
public double getTicksPerSecond() {
return ticksPerSecond;
@@ -169,7 +267,7 @@
return input;
}
- private static void verifyMagic(DataInput input) throws IOException {
+ private static void verifyMagic(RecordingInput input) throws IOException {
for (byte c : FILE_MAGIC) {
if (input.readByte() != c) {
throw new IOException("Not a Flight Recorder file");
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,36 +30,28 @@
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
-import java.nio.charset.Charset;
public final class RecordingInput implements DataInput, AutoCloseable {
- public static final byte STRING_ENCODING_NULL = 0;
- public static final byte STRING_ENCODING_EMPTY_STRING = 1;
- public static final byte STRING_ENCODING_CONSTANT_POOL = 2;
- public static final byte STRING_ENCODING_UTF8_BYTE_ARRAY = 3;
- public static final byte STRING_ENCODING_CHAR_ARRAY = 4;
- public static final byte STRING_ENCODING_LATIN1_BYTE_ARRAY = 5;
private final static int DEFAULT_BLOCK_SIZE = 16 * 1024 * 1024;
- private final static Charset UTF8 = Charset.forName("UTF-8");
- private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
private static final class Block {
private byte[] bytes = new byte[0];
private long blockPosition;
-
+ private int size;
boolean contains(long position) {
- return position >= blockPosition && position < blockPosition + bytes.length;
+ return position >= blockPosition && position < blockPosition + size;
}
public void read(RandomAccessFile file, int amount) throws IOException {
blockPosition = file.getFilePointer();
// reuse byte array, if possible
- if (amount != bytes.length) {
+ if (amount > bytes.length) {
bytes = new byte[amount];
}
- file.readFully(bytes);
+ this.size = amount;
+ file.readFully(bytes, 0 , amount);
}
public byte get(long position) {
@@ -68,24 +60,34 @@
}
private final RandomAccessFile file;
- private final long size;
+ private final String filename;
private Block currentBlock = new Block();
private Block previousBlock = new Block();
private long position;
private final int blockSize;
+ private long size = -1; // Fail fast if setSize(...) has not been called before parsing
- private RecordingInput(File f, int blockSize) throws IOException {
- this.size = f.length();
+ public RecordingInput(File f, int blockSize) throws IOException {
this.blockSize = blockSize;
+ this.filename = f.getAbsolutePath().toString();
this.file = new RandomAccessFile(f, "r");
- if (size < 8) {
- throw new IOException("Not a valid Flight Recorder file. File length is only " + size + " bytes.");
+ if (f.length() < 8) {
+ throw new IOException("Not a valid Flight Recorder file. File length is only " + f.length() + " bytes.");
}
}
public RecordingInput(File f) throws IOException {
this(f, DEFAULT_BLOCK_SIZE);
}
+ public void positionPhysical(long position) throws IOException {
+ file.seek(position);
+ }
+ public final byte readPhysicalByte() throws IOException {
+ return file.readByte();
+ }
+ public long readPhysicalLong() throws IOException {
+ return file.readLong();
+ }
@Override
public final byte readByte() throws IOException {
@@ -150,20 +152,20 @@
return ((b7 & 0xFFL)) + ((b6 & 0xFFL) << 8) + ((b5 & 0xFFL) << 16) + ((b4 & 0xFFL) << 24) + ((b3 & 0xFFL) << 32) + ((b2 & 0xFFL) << 40) + ((b1 & 0xFFL) << 48) + (((long) b0) << 56);
}
- public final long position() throws IOException {
+ public final long position() {
return position;
}
public final void position(long newPosition) throws IOException {
if (!currentBlock.contains(newPosition)) {
if (!previousBlock.contains(newPosition)) {
- if (newPosition > size()) {
- throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size() + " bytes.");
+ if (newPosition > size) {
+ throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size + " bytes.");
}
long blockStart = trimToFileSize(calculateBlockStart(newPosition));
file.seek(blockStart);
// trim amount to file size
- long amount = Math.min(size() - blockStart, blockSize);
+ long amount = Math.min(size - blockStart, blockSize);
previousBlock.read(file, (int) amount);
}
// swap previous and current
@@ -191,7 +193,7 @@
return newPosition - blockSize / 2;
}
- public final long size() throws IOException {
+ public final long size() {
return size;
}
@@ -245,34 +247,7 @@
// 4, means ""
@Override
public String readUTF() throws IOException {
- return readEncodedString(readByte());
- }
-
- public String readEncodedString(byte encoding) throws IOException {
- if (encoding == STRING_ENCODING_NULL) {
- return null;
- }
- if (encoding == STRING_ENCODING_EMPTY_STRING) {
- return "";
- }
- int size = readInt();
- if (encoding == STRING_ENCODING_CHAR_ARRAY) {
- char[] c = new char[size];
- for (int i = 0; i < size; i++) {
- c[i] = readChar();
- }
- return new String(c);
- }
- byte[] bytes = new byte[size];
- readFully(bytes); // TODO: optimize, check size, and copy only if needed
- if (encoding == STRING_ENCODING_UTF8_BYTE_ARRAY) {
- return new String(bytes, UTF8);
- }
-
- if (encoding == STRING_ENCODING_LATIN1_BYTE_ARRAY) {
- return new String(bytes, LATIN1);
- }
- throw new IOException("Unknown string encoding " + encoding);
+ throw new UnsupportedOperationException("Use StringParser");
}
@Override
@@ -336,4 +311,19 @@
int b8 = readByte(); // read last byte raw
return ret + (((long) (b8 & 0XFF)) << 56);
}
+
+ public void setValidSize(long size) {
+ if (size > this.size) {
+ this.size = size;
+ }
+ }
+
+ public long getFileSize() throws IOException {
+ return file.length();
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInternals.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInternals.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,19 @@
public abstract class RecordingInternals {
+ public static RecordingInternals instance() {
+ if (INSTANCE == null) {
+ // Force initialization
+ try {
+ Class<?> c = RecordedObject.class;
+ Class.forName(c.getName(), true, c.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ new InternalError("shuld not happen");
+ }
+ }
+ return INSTANCE;
+ }
+
public static RecordingInternals INSTANCE;
public abstract boolean isLastEventInChunk(RecordingFile file);
@@ -44,4 +57,5 @@
public abstract void sort(List<RecordedEvent> events);
+ public abstract Parser newStringParser();
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java Fri May 17 16:02:27 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -80,7 +80,7 @@
* @throws DCmdException if recording could not be started
*/
@SuppressWarnings("resource")
- public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
+ public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Long flush, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name +
", settings=" + Arrays.asList(settings) +
@@ -89,6 +89,7 @@
", disk=" + disk+
", filename=" + path +
", maxage=" + maxAge +
+ ", flush=" + flush +
", maxsize=" + maxSize +
", dumponexit =" + dumpOnExit +
", path-to-gc-roots=" + pathToGcRoots);
@@ -131,6 +132,12 @@
}
}
+ if (flush != null) {
+ if (Boolean.FALSE.equals(disk)) {
+ throw new DCmdException("Flush can only be set for recordings that are to disk.");
+ }
+ }
+
if (!FlightRecorder.isInitialized() && delay == null) {
initializeWithForcedInstrumentation(s);
}
@@ -143,6 +150,7 @@
if (disk != null) {
recording.setToDisk(disk.booleanValue());
}
+
recording.setSettings(s);
SafePath safePath = null;
@@ -172,6 +180,10 @@
recording.setMaxAge(Duration.ofNanos(maxAge));
}
+ if (flush != null) {
+ recording.setFlushInterval(Duration.ofNanos(flush));
+ }
+
if (maxSize != null) {
recording.setMaxSize(maxSize);
}
--- a/src/jdk.jfr/share/conf/jfr/default.jfc Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/conf/jfr/default.jfc Fri May 17 16:02:27 2019 +0200
@@ -661,6 +661,36 @@
<setting name="enabled">true</setting>
</event>
+ <event name="jdk.Flush">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStorage">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStacktrace">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStringPool">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushMetadata">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushTypeSet">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
<event name="jdk.DataLoss">
<setting name="enabled">true</setting>
</event>
--- a/src/jdk.jfr/share/conf/jfr/profile.jfc Fri May 17 15:53:21 2019 +0200
+++ b/src/jdk.jfr/share/conf/jfr/profile.jfc Fri May 17 16:02:27 2019 +0200
@@ -661,6 +661,36 @@
<setting name="enabled">true</setting>
</event>
+ <event name="jdk.Flush">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStorage">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStacktrace">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStringPool">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushMetadata">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushTypeSet">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
<event name="jdk.DataLoss">
<setting name="enabled">true</setting>
</event>
--- a/test/hotspot/gtest/jfr/test_networkUtilization.cpp Fri May 17 15:53:21 2019 +0200
+++ b/test/hotspot/gtest/jfr/test_networkUtilization.cpp Fri May 17 16:02:27 2019 +0200
@@ -78,6 +78,9 @@
void write_key(traceid id) {
current = id;
}
+ void write_type(JfrTypeId id) {}
+ MockJfrCheckpointWriter() {}
+ MockJfrCheckpointWriter(bool dummy1, bool dummy2, void* dummy3) {}
void write(const char* data) {
ids[current] = data;
}
@@ -89,12 +92,12 @@
public:
static MockJfrSerializer* current;
- static bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, MockJfrSerializer* serializer) {
+ static bool register_serializer(JfrTypeId id, bool permit_cache, MockJfrSerializer* serializer) {
current = serializer;
return true;
}
- virtual void serialize(MockJfrCheckpointWriter& writer) = 0;
+ virtual void serialize(MockJfrCheckpointWriter& writer) {}
};
MockJfrSerializer* MockJfrSerializer::current;
--- a/test/hotspot/gtest/jfr/test_threadCpuLoad.cpp Fri May 17 15:53:21 2019 +0200
+++ b/test/hotspot/gtest/jfr/test_threadCpuLoad.cpp Fri May 17 16:02:27 2019 +0200
@@ -36,11 +36,10 @@
#include "jfr/jfrEvents.hpp"
#include "jfr/support/jfrThreadId.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "utilities/globalDefinitions.hpp"
#include "runtime/os.hpp"
-#include "runtime/thread.inline.hpp"
-#include "runtime/threadSMR.inline.hpp"
#include "unittest.hpp"
@@ -81,11 +80,18 @@
MockJavaThread() : ::JavaThread() {}
};
- class MockJavaThreadIteratorWithHandle
+ class MockJfrJavaThreadIterator
{
public:
MockJavaThread* next() { return NULL; }
- int length() { return 0; }
+ bool has_next() const { return false; }
+ };
+
+ class MockJfrJavaThreadIteratorAdapter
+ {
+ public:
+ MockJavaThread* next() { return NULL; }
+ bool has_next() const { return false; }
};
// Reincluding source files in the anonymous namespace unfortunately seems to
@@ -97,7 +103,8 @@
#define os MockOs
#define EventThreadCPULoad MockEventThreadCPULoad
#define JavaThread MockJavaThread
-#define JavaThreadIteratorWithHandle MockJavaThreadIteratorWithHandle
+#define JfrJavaThreadIterator MockJfrJavaThreadIterator
+#define JfrJavaThreadIteratorAdapter MockJfrJavaThreadIteratorAdapter
#include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
#include "jfr/periodic/jfrThreadCPULoadEvent.cpp"
@@ -105,7 +112,8 @@
#undef os
#undef EventThreadCPULoad
#undef JavaThread
-#undef JavaThreadIteratorWithHandle
+#define JfrJavaThreadIterator MockJfrJavaThreadIterator
+#define JfrJavaThreadIteratorAdapter MockJfrJavaThreadIteratorAdapter
} // anonymous namespace
--- a/test/jdk/jdk/jfr/api/consumer/TestReadTwice.java Fri May 17 15:53:21 2019 +0200
+++ b/test/jdk/jdk/jfr/api/consumer/TestReadTwice.java Fri May 17 16:02:27 2019 +0200
@@ -26,7 +26,6 @@
package jdk.jfr.api.consumer;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
--- a/test/jdk/jdk/jfr/api/consumer/TestRecordingFile.java Fri May 17 15:53:21 2019 +0200
+++ b/test/jdk/jdk/jfr/api/consumer/TestRecordingFile.java Fri May 17 16:02:27 2019 +0200
@@ -55,7 +55,7 @@
* @key jfr
* @requires vm.hasJFR
* @library /test/lib
- * @run main/othervm jdk.jfr.api.consumer.TestRecordingFile
+ * @run main/othervm -Xlog:jfr*=info jdk.jfr.api.consumer.TestRecordingFile
*/
public class TestRecordingFile {
@@ -210,12 +210,12 @@
assertHasEventType(types, "Event2");
assertMissingEventType(types, "Event3");
}
- try (RecordingFile f = new RecordingFile(twoEventTypes)) {
+ try (RecordingFile f = new RecordingFile(threeEventTypes)) {
List<EventType> types = f.readEventTypes();
assertUniqueEventTypes(types);
assertHasEventType(types, "Event1");
assertHasEventType(types, "Event2");
- assertMissingEventType(types, "Event3");
+ assertHasEventType(types, "Event3");
}
}
--- a/test/jdk/jdk/jfr/api/consumer/TestRecordingInternals.java Fri May 17 15:53:21 2019 +0200
+++ b/test/jdk/jdk/jfr/api/consumer/TestRecordingInternals.java Fri May 17 16:02:27 2019 +0200
@@ -60,6 +60,7 @@
Asserts.assertEquals(id.toString(), rt.getThreadGroup().getName(), "Thread group name should match id");
Asserts.assertEquals(id, Integer.valueOf(i), "Chunks out of order");
i++;
+ System.out.println(i + " OK ");
}
}
}
--- a/test/lib/jdk/test/lib/jfr/EventNames.java Fri May 17 15:53:21 2019 +0200
+++ b/test/lib/jdk/test/lib/jfr/EventNames.java Fri May 17 16:02:27 2019 +0200
@@ -187,6 +187,12 @@
public final static String CPUTimeStampCounter = PREFIX + "CPUTimeStampCounter";
public final static String ActiveRecording = PREFIX + "ActiveRecording";
public final static String ActiveSetting = PREFIX + "ActiveSetting";
+ public static final String Flush = PREFIX + "Flush";
+ public static final String FlushStringPool = PREFIX + "FlushStringPool";
+ public static final String FlushStacktrace = PREFIX + "FlushStacktrace";
+ public static final String FlushStorage = PREFIX + "FlushStorage";
+ public static final String FlushMetadata = PREFIX + "FlushMetadata";
+ public static final String FlushTypeSet = PREFIX + "FlushTypeSet";
public static boolean isGcEvent(EventType et) {
return et.getCategoryNames().contains(GC_CATEGORY);