diff --git a/src/share/vm/jfr/dcmd/jfrDcmds.cpp b/src/share/vm/jfr/dcmd/jfrDcmds.cpp index 77687e84bed84fe650c9b5153d565c9573e43df8..e0c11da18bc2e522264e45b0b25d448f1709bcdd 100644 --- a/src/share/vm/jfr/dcmd/jfrDcmds.cpp +++ b/src/share/vm/jfr/dcmd/jfrDcmds.cpp @@ -345,6 +345,7 @@ JfrStartFlightRecordingDCmd::JfrStartFlightRecordingDCmd(outputStream* output, _disk("disk", "Recording should be persisted to disk", "BOOLEAN", false), _maxage("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"), _maxsize("maxsize", "Maximum amount of bytes to keep (on disk) in (k)B, (M)B or (G)B, e.g. 500M, or 0 for no limit", "MEMORY SIZE", false, "0"), + _flush_interval("flush-interval", "Minimum time before flushing buffers, measured in (s)econds, e.g. 4 s, or 0 for flushing when a recording ends", "NANOTIME", false, "1s"), _dump_on_exit("dumponexit", "Dump running recording when JVM shuts down", "BOOLEAN", false), _path_to_gc_roots("path-to-gc-roots", "Collect path to GC roots", "BOOLEAN", false, "false") { _dcmdparser.add_dcmd_option(&_name); @@ -355,6 +356,7 @@ JfrStartFlightRecordingDCmd::JfrStartFlightRecordingDCmd(outputStream* output, _dcmdparser.add_dcmd_option(&_filename); _dcmdparser.add_dcmd_option(&_maxage); _dcmdparser.add_dcmd_option(&_maxsize); + _dcmdparser.add_dcmd_option(&_flush_interval); _dcmdparser.add_dcmd_option(&_dump_on_exit); _dcmdparser.add_dcmd_option(&_path_to_gc_roots); }; @@ -407,6 +409,10 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { maxsize = JfrJavaSupport::new_java_lang_Long(_maxsize.value()._size, CHECK); } + jobject flush_interval = NULL; + if (_flush_interval.is_set()) { + flush_interval = JfrJavaSupport::new_java_lang_Long(_flush_interval.value()._nanotime, CHECK); + } jobject duration = NULL; if (_duration.is_set()) { duration = JfrJavaSupport::new_java_lang_Long(_duration.value()._nanotime, CHECK); @@ -460,7 +466,7 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { 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); @@ -474,6 +480,7 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) { execute_args.push_jobject(filename); execute_args.push_jobject(maxage); execute_args.push_jobject(maxsize); + execute_args.push_jobject(flush_interval); execute_args.push_jobject(dump_on_exit); execute_args.push_jobject(path_to_gc_roots); diff --git a/src/share/vm/jfr/dcmd/jfrDcmds.hpp b/src/share/vm/jfr/dcmd/jfrDcmds.hpp index 425a07875e136b3a09a5e064d4541e0572923ec2..953a9f60d38f734e3c1f2ed6906460d3025c2741 100644 --- a/src/share/vm/jfr/dcmd/jfrDcmds.hpp +++ b/src/share/vm/jfr/dcmd/jfrDcmds.hpp @@ -90,6 +90,7 @@ class JfrStartFlightRecordingDCmd : public DCmdWithParser { DCmdArgument _filename; DCmdArgument _maxage; DCmdArgument _maxsize; + DCmdArgument _flush_interval; DCmdArgument _dump_on_exit; DCmdArgument _path_to_gc_roots; diff --git a/src/share/vm/jfr/jfr.cpp b/src/share/vm/jfr/jfr.cpp index 06d3b79837e86ca737ae477a43c9b111aa6135be..b5905d1e688234b371b959bbce16809eeaf5e8a8 100644 --- a/src/share/vm/jfr/jfr.cpp +++ b/src/share/vm/jfr/jfr.cpp @@ -78,6 +78,18 @@ void Jfr::on_thread_exit(Thread* t) { JfrThreadLocal::on_exit(t); } +void Jfr::exclude_thread(Thread* t) { + JfrThreadLocal::exclude(t); +} + +void Jfr::include_thread(Thread* t) { + JfrThreadLocal::include(t); +} + +bool Jfr::is_excluded(Thread* t) { + return t != NULL && t->jfr_thread_local()->is_excluded(); +} + void Jfr::on_java_thread_dismantle(JavaThread* jt) { if (JfrRecorder::is_recording()) { JfrCheckpointManager::write_thread_checkpoint(jt); diff --git a/src/share/vm/jfr/jfr.hpp b/src/share/vm/jfr/jfr.hpp index a09d7ef7c6c17d75d2838bc39a5d8534b47c4de2..04e0cfb6518617256a0672247e09c71f7eae64f6 100644 --- a/src/share/vm/jfr/jfr.hpp +++ b/src/share/vm/jfr/jfr.hpp @@ -56,6 +56,9 @@ class Jfr : AllStatic { static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f); static void weak_oops_do(OopClosure* f); static Thread* sampler_thread(); + static void exclude_thread(Thread* thread); + static bool is_excluded(Thread* thread); + static void include_thread(Thread* thread); }; #endif // SHARE_VM_JFR_JFR_HPP diff --git a/src/share/vm/jfr/jni/jfrJavaSupport.cpp b/src/share/vm/jfr/jni/jfrJavaSupport.cpp index f3232b56ff5e05cd64929bc2898e3e193c552b50..db7511c5f02e0edc28213be007778845fde774d1 100644 --- a/src/share/vm/jfr/jni/jfrJavaSupport.cpp +++ b/src/share/vm/jfr/jni/jfrJavaSupport.cpp @@ -23,7 +23,6 @@ */ #include "precompiled.hpp" -#include "jni.h" #include "classfile/javaClasses.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" @@ -40,9 +39,11 @@ #include "runtime/fieldDescriptor.hpp" #include "runtime/java.hpp" #include "runtime/jniHandles.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) { @@ -56,6 +57,14 @@ void JfrJavaSupport::check_java_thread_in_native(Thread* t) { 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"); + // The start-up process of JFR is different from that of upstream, + // so the following line needs to be commented out + // assert(((JavaThread*)t)->thread_state() == _thread_new, "invariant"); +} #endif /* @@ -91,6 +100,21 @@ void JfrJavaSupport::destroy_global_jni_handle(const jobject handle) { JNIHandles::destroy_global(handle); } +jweak JfrJavaSupport::global_weak_jni_handle(const oop obj, Thread* t) { + DEBUG_ONLY(check_java_thread_in_vm(t)); + HandleMark hm(t); + return JNIHandles::make_weak_global(Handle(t, obj)); +} + +jweak JfrJavaSupport::global_weak_jni_handle(const jobject handle, Thread* t) { + const oop obj = JNIHandles::resolve(handle); + return obj == NULL ? NULL : global_weak_jni_handle(obj, t); +} + +void JfrJavaSupport::destroy_global_weak_jni_handle(jweak handle) { + JNIHandles::destroy_weak_global(handle); +} + oop JfrJavaSupport::resolve_non_null(jobject obj) { return JNIHandles::resolve_non_null(obj); } @@ -574,14 +598,141 @@ JfrJavaSupport::CAUSE JfrJavaSupport::cause() { return _cause; } -// XXX -//const char* const JDK_JFR_MODULE_NAME = "jdk.jfr"; -//const char* const JDK_JFR_PACKAGE_NAME = "jdk/jfr"; +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* 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(10, true, mtTracing); + } + assert(exclusion_list != NULL, "invariant"); + assert(thread_is_not_excluded(thread), "invariant"); + jweak ref = JfrJavaSupport::global_weak_jni_handle(thread, Thread::current()); + const int idx = exclusion_list->append(ref); + assert(is_thread_excluded(thread), "invariant"); + return idx; +} + +static void remove_thread_from_exclusion_list(Handle thread) { + assert(exclusion_list != NULL, "invariant"); + assert(is_thread_excluded(thread), "invariant"); + assert(exclusion_list != NULL, "invariant"); + const int idx = find_exclusion_thread_idx(thread); + assert(idx >= 0, "invariant"); + assert(idx < exclusion_list->length(), "invariant"); + JfrJavaSupport::destroy_global_weak_jni_handle(exclusion_list->at(idx)); + exclusion_list->delete_at(idx); + assert(thread_is_not_excluded(thread), "invariant"); + if (0 == exclusion_list->length()) { + delete exclusion_list; + exclusion_list = NULL; + } +} + +static void remove_thread_from_exclusion_list(jobject thread) { + ThreadExclusionListAccess lock; + remove_thread_from_exclusion_list(as_handle(thread)); +} -jlong JfrJavaSupport::jfr_thread_id(jobject target_thread) { -// ThreadsListHandle tlh; - // XXX is it correct and safe? - JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(target_thread)); -// (void)tlh.cv_internal_thread_to_JavaThread(target_thread, &native_thread, NULL); +// 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) { + JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(thread)); return native_thread != NULL ? JFR_THREAD_ID(native_thread) : 0; } + +void JfrJavaSupport::exclude(jobject thread) { + HandleMark hm; + JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(thread)); + if (native_thread != NULL) { + JfrThreadLocal::exclude(native_thread); + } else { + // not started yet, track the thread oop + add_thread_to_exclusion_list(thread); + } +} + +void JfrJavaSupport::include(jobject thread) { + HandleMark hm; + JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(thread)); + if (native_thread != NULL) { + JfrThreadLocal::include(native_thread); + } else { + // not started yet, untrack the thread oop + remove_thread_from_exclusion_list(thread); + } +} + +bool JfrJavaSupport::is_excluded(jobject thread) { + HandleMark hm; + JavaThread* native_thread = java_lang_Thread::thread(JNIHandles::resolve_non_null(thread)); + return native_thread != NULL ? native_thread->jfr_thread_local()->is_excluded() : is_thread_excluded(thread); +} + +void JfrJavaSupport::on_thread_start(Thread* t) { + assert(t != NULL, "invariant"); + assert(Thread::current() == t, "invariant"); + if (!t->is_Java_thread()) { + return; + } + DEBUG_ONLY(check_new_unstarted_java_thread(t);) + HandleMark hm; + if (check_exclusion_state_on_thread_start((JavaThread*)t)) { + JfrThreadLocal::exclude(t); + } +} diff --git a/src/share/vm/jfr/jni/jfrJavaSupport.hpp b/src/share/vm/jfr/jni/jfrJavaSupport.hpp index d08c4b96e1b1a219c3e939fd2f40b333cebf1603..c837ec386ada5fb0afb8a4a0696984b0c58fafd7 100644 --- a/src/share/vm/jfr/jni/jfrJavaSupport.hpp +++ b/src/share/vm/jfr/jni/jfrJavaSupport.hpp @@ -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); @@ -83,7 +86,11 @@ class JfrJavaSupport : public AllStatic { static void throw_class_format_error(const char* message, TRAPS); static void throw_runtime_exception(const char* message, TRAPS); - static jlong jfr_thread_id(jobject target_thread); + static jlong jfr_thread_id(jobject thread); + static void exclude(jobject thread); + static void include(jobject thread); + static bool is_excluded(jobject thread); + static void on_thread_start(Thread* t); // critical static void abort(jstring errorMsg, TRAPS); diff --git a/src/share/vm/jfr/jni/jfrJniMethod.cpp b/src/share/vm/jfr/jni/jfrJniMethod.cpp index c02271b6525a39b47d573ccff1c525bcea71ce02..30e5f5d590bce9af461ba9cb47fd935dfe222489 100644 --- a/src/share/vm/jfr/jni/jfrJniMethod.cpp +++ b/src/share/vm/jfr/jni/jfrJniMethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -303,6 +303,10 @@ JVM_ENTRY_NO_ENV(jboolean, jfr_event_writer_flush(JNIEnv* env, jclass cls, jobje return JfrJavaEventWriter::flush(writer, used_size, requested_size, thread); JVM_END +JVM_ENTRY_NO_ENV(void, jfr_flush(JNIEnv* env, jobject jvm)) + JfrRepository::flush(thread); +JVM_END + JVM_ENTRY_NO_ENV(void, jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location)) return JfrRepository::set_path(location, thread); JVM_END @@ -330,3 +334,20 @@ JVM_END JVM_ENTRY_NO_ENV(void, jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean emit_all)) 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 + diff --git a/src/share/vm/jfr/jni/jfrJniMethod.hpp b/src/share/vm/jfr/jni/jfrJniMethod.hpp index 4a9a3eb5f582dce7a91457e9f01db9116160d448..a9a56ee2c8cff2854dff6ad1ab6dc923e220efc0 100644 --- a/src/share/vm/jfr/jni/jfrJniMethod.hpp +++ b/src/share/vm/jfr/jni/jfrJniMethod.hpp @@ -117,6 +117,7 @@ jobject JNICALL jfr_new_event_writer(JNIEnv* env, jclass cls); jboolean JNICALL jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size); +void JNICALL jfr_flush(JNIEnv* env, jobject jvm); void JNICALL jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg); jlong JNICALL jfr_get_epoch_address(JNIEnv* env, jobject jvm); @@ -135,6 +136,13 @@ void JNICALL jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ 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 } diff --git a/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp b/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp index cedb941a1fa0f0057ec14f0f6a4016dd4cb61d18..e8ce2480c0b42c6dc75319db20a08329d40b5954 100644 --- a/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp +++ b/src/share/vm/jfr/jni/jfrJniMethodRegistration.cpp @@ -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 @@ -72,6 +72,7 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (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*)"()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, @@ -81,7 +82,11 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (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); diff --git a/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp b/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp index ad549f4864a77f25b65b389241b726c4b1a24b2d..67854ef3fa5fab0d7926df0380984517b681e1f9 100644 --- a/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp +++ b/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp @@ -259,6 +259,7 @@ void EdgeStore::put_chain(const Edge* chain, size_t length) { assert(leak_context_edge->parent() == NULL, "invariant"); if (1 == length) { + store_gc_root_id_in_leak_context_edge(leak_context_edge, leak_context_edge); return; } diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.cpp index ff58db7dd4b8501d5029657ac128bdc40a649efb..6a6bc87c4352fc94a0525b2dbeb436ac309e852a 100644 --- a/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.cpp +++ b/src/share/vm/jfr/leakprofiler/checkpoint/eventEmitter.cpp @@ -33,6 +33,7 @@ #include "memory/resourceArea.hpp" #include "oops/markOop.hpp" #include "oops/oop.inline.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/thread.inline.hpp" #include "runtime/vmThread.hpp" @@ -50,8 +51,8 @@ EventEmitter::~EventEmitter() { } void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_all) { + assert(JfrStream_lock->owned_by_self(), "invariant"); assert(sampler != NULL, "invariant"); - ResourceMark rm; EdgeStore edge_store; if (cutoff_ticks <= 0) { @@ -67,6 +68,7 @@ void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_ } size_t EventEmitter::write_events(ObjectSampler* object_sampler, EdgeStore* edge_store, bool emit_all) { + assert_locked_or_safepoint(JfrStream_lock); assert(_thread == Thread::current(), "invariant"); assert(_thread->jfr_thread_local() == _jfr_thread_local, "invariant"); assert(object_sampler != NULL, "invariant"); diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp index 6c38cc67772e8d061144b4e0ae2f1512eedfdb08..835950fd0caf244d9ced20d58fa0b0c87fef8529 100644 --- a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp @@ -37,6 +37,7 @@ #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" #include "jfr/utilities/jfrHashtable.hpp" #include "jfr/utilities/jfrTypes.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" #include "runtime/semaphore.hpp" #include "runtime/thread.hpp" @@ -272,7 +273,7 @@ void StackTraceBlobInstaller::install(ObjectSample* sample) { } const JfrStackTrace* const stack_trace = resolve(sample); DEBUG_ONLY(validate_stack_trace(sample, stack_trace)); - JfrCheckpointWriter writer(false, true, Thread::current()); + JfrCheckpointWriter writer; writer.write_type(TYPE_STACKTRACE); writer.write_count(1); ObjectSampleCheckpoint::write_stacktrace(stack_trace, writer); @@ -292,6 +293,7 @@ static void install_stack_traces(const ObjectSampler* sampler, JfrStackTraceRepo // caller needs ResourceMark void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repo) { + assert(JfrStream_lock->owned_by_self(), "invariant"); assert(sampler != NULL, "invariant"); assert(LeakProfiler::is_running(), "invariant"); install_stack_traces(sampler, stack_trace_repo); @@ -389,7 +391,7 @@ class BlobWriter { static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) { // sample set is predicated on time of last sweep const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value(); - JfrCheckpointWriter writer(false, false, thread); + JfrCheckpointWriter writer(thread, false); BlobWriter cbw(sampler, writer, last_sweep); iterate_samples(cbw, true); // reset blob write states @@ -398,13 +400,14 @@ static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thre } void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) { + assert_locked_or_safepoint(JfrStream_lock); assert(sampler != NULL, "invariant"); assert(edge_store != NULL, "invariant"); assert(thread != NULL, "invariant"); write_sample_blobs(sampler, emit_all, thread); // write reference chains if (!edge_store->is_empty()) { - JfrCheckpointWriter writer(false, true, thread); + JfrCheckpointWriter writer(thread); ObjectSampleWriter osw(writer, edge_store); edge_store->iterate(osw); } diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp index 3cfd560116ce4d283ec60d37ca1ff0748c69e2d3..bcad2855eff5357c24f438d59cfb05750bf98ee3 100644 --- a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp @@ -355,10 +355,6 @@ int __write_root_description_info__(JfrCheckpointWriter* writer, const void* di) static traceid get_gc_root_description_info_id(const Edge& edge, traceid id) { assert(edge.is_root(), "invariant"); - if (EdgeUtils::is_leak_edge(edge)) { - return 0; - } - if (root_infos == NULL) { root_infos = new RootDescriptionInfo(); } @@ -606,8 +602,8 @@ class RootType : public JfrSerializer { static void register_serializers() { static bool is_registered = false; if (!is_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()); is_registered = true; } } diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp index e3a56afd0c525714a3fd787696e4a3e6bd6e3353..083443153b5cc1e90ace5c0a7a905edc321972b6 100644 --- a/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp +++ b/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp @@ -27,6 +27,7 @@ //#include "gc_interface/strongRootsScope.hpp" #include "jfr/leakprofiler/utilities/unifiedOop.hpp" #include "jfr/leakprofiler/checkpoint/rootResolver.hpp" +#include "jfr/utilities/jfrThreadIterator.hpp" #include "memory/iterator.hpp" #include "oops/klass.hpp" #include "oops/oop.hpp" @@ -253,8 +254,9 @@ class ReferenceToThreadRootClosure : public StackObj { public: ReferenceToThreadRootClosure(RootCallback& callback) :_callback(callback), _complete(false) { assert_locked_or_safepoint(Threads_lock); - for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { - if (do_thread_roots(thread)) { + JfrJavaThreadIterator iter; + while (iter.has_next()) { + if (do_thread_roots(iter.next())) { return; } } diff --git a/src/share/vm/jfr/leakprofiler/leakProfiler.cpp b/src/share/vm/jfr/leakprofiler/leakProfiler.cpp index 46ad79aa6eabf33de879d4ef10d84f87503b1992..822c78035a97c8462d05011dbe70933bd8af1175 100644 --- a/src/share/vm/jfr/leakprofiler/leakProfiler.cpp +++ b/src/share/vm/jfr/leakprofiler/leakProfiler.cpp @@ -30,6 +30,7 @@ #include "jfr/leakprofiler/sampling/objectSampler.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp" #include "memory/iterator.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/thread.inline.hpp" #include "runtime/vmThread.hpp" @@ -81,6 +82,7 @@ void LeakProfiler::emit_events(int64_t cutoff_ticks, bool emit_all) { if (!is_running()) { return; } + MutexLocker lock(JfrStream_lock); // exclusive access to object sampler instance ObjectSampler* const sampler = ObjectSampler::acquire(); assert(sampler != NULL, "invariant"); diff --git a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp index 89653a6f6e04997803ae6faddcd7b5cc30078b69..3137e20ff4e5b804585c630b120a22281464eb31 100644 --- a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp +++ b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp @@ -108,6 +108,9 @@ static traceid get_thread_id(JavaThread* thread) { } const JfrThreadLocal* const tl = thread->jfr_thread_local(); assert(tl != NULL, "invariant"); + if (tl->is_excluded()) { + return 0; + } if (!tl->has_thread_blob()) { JfrCheckpointManager::create_thread_blob(thread); } diff --git a/src/share/vm/jfr/metadata/jfrSerializer.hpp b/src/share/vm/jfr/metadata/jfrSerializer.hpp index f5a4d7d9f2475f7fe9277d31bc37b13db9644507..2cada944663783eec98f97d4af9ccbb4d520f37d 100644 --- a/src/share/vm/jfr/metadata/jfrSerializer.hpp +++ b/src/share/vm/jfr/metadata/jfrSerializer.hpp @@ -70,7 +70,8 @@ class JfrSerializer : public CHeapObj { public: virtual ~JfrSerializer() {} - static bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer); + virtual void on_rotation() {} + static bool register_serializer(JfrTypeId id, bool permit_cache, JfrSerializer* serializer); virtual void serialize(JfrCheckpointWriter& writer) = 0; }; diff --git a/src/share/vm/jfr/metadata/metadata.xml b/src/share/vm/jfr/metadata/metadata.xml index 8d97a420c9b01c05a0256b2bc9dd00ba1831b8ba..26115aed613cdb315f384c93d20ed117e50f5efc 100644 --- a/src/share/vm/jfr/metadata/metadata.xml +++ b/src/share/vm/jfr/metadata/metadata.xml @@ -154,7 +154,7 @@ - + @@ -162,27 +162,27 @@ - + - + - + - + @@ -477,7 +477,7 @@ - + @@ -595,21 +595,21 @@ - - - @@ -930,6 +930,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1112,34 +1148,40 @@ + + + + - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -1151,5 +1193,5 @@ - + diff --git a/src/share/vm/jfr/periodic/jfrNetworkUtilization.cpp b/src/share/vm/jfr/periodic/jfrNetworkUtilization.cpp index 6a7c0d84f3606d369e55dd8a36313ac04ce5958e..38bc40045b1505b867fec94a2d0e6776bd397510 100644 --- a/src/share/vm/jfr/periodic/jfrNetworkUtilization.cpp +++ b/src/share/vm/jfr/periodic/jfrNetworkUtilization.cpp @@ -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 @@ -38,7 +38,7 @@ struct InterfaceEntry { traceid id; uint64_t bytes_in; uint64_t bytes_out; - bool in_use; + mutable bool written; }; static GrowableArray* _interfaces = NULL; @@ -70,7 +70,7 @@ static InterfaceEntry& new_entry(const NetworkInterface* iface, GrowableArrayget_bytes_in(); entry.bytes_out = iface->get_bytes_out(); - entry.in_use = false; + entry.written = false; return _interfaces->at(_interfaces->append(entry)); } @@ -107,46 +107,46 @@ static uint64_t rate_per_second(uint64_t current, uint64_t old, const JfrTickspa return ((current - old) * NANOSECS_PER_SEC) / interval.nanoseconds(); } -static bool get_interfaces(NetworkInterface** network_interfaces) { - const int ret_val = JfrOSInterface::network_utilization(network_interfaces); - if (ret_val == OS_ERR) { - if (LogJFR) tty->print_cr("Unable to generate network utilization events"); - return false; - } - return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED; -} - class JfrNetworkInterfaceName : public JfrSerializer { public: - void serialize(JfrCheckpointWriter& writer) { - assert(_interfaces != NULL, "invariant"); - const JfrCheckpointContext ctx = writer.context(); - const intptr_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet - int active_interfaces = 0; - for (int i = 0; i < _interfaces->length(); ++i) { - InterfaceEntry& entry = _interfaces->at(i); - if (entry.in_use) { - entry.in_use = false; - writer.write_key(entry.id); - writer.write(entry.name); - ++active_interfaces; - } - } - if (active_interfaces == 0) { - // nothing to write, restore context - writer.set_context(ctx); - return; - } - writer.write_count(active_interfaces, count_offset); - } + void serialize(JfrCheckpointWriter& writer) {} // we write each constant lazily + + void on_rotation() { + for (int i = 0; i < _interfaces->length(); ++i) { + const InterfaceEntry& entry = _interfaces->at(i); + if (entry.written) { + entry.written = false; + } + } + } }; static bool register_network_interface_name_serializer() { assert(_interfaces != NULL, "invariant"); return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME, - false, // require safepoint - false, // disallow caching; we want a callback every rotation - new JfrNetworkInterfaceName()); + false, // disallow caching; we want a callback every rotation + new JfrNetworkInterfaceName()); +} + +static void write_interface_constant(const InterfaceEntry& entry) { + if (entry.written) { + return; + } + JfrCheckpointWriter writer; + writer.write_type(TYPE_NETWORKINTERFACENAME); + writer.write_count(1); + writer.write_key(entry.id); + writer.write(entry.name); + entry.written = true; +} + +static bool get_interfaces(NetworkInterface** network_interfaces) { + const int ret_val = JfrOSInterface::network_utilization(network_interfaces); + if (ret_val == OS_ERR) { + if (LogJFR) tty->print_cr("Unable to generate network utilization events"); + return false; + } + return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED; } void JfrNetworkUtilization::send_events() { @@ -168,7 +168,7 @@ void JfrNetworkUtilization::send_events() { const uint64_t read_rate = rate_per_second(current_bytes_in, entry.bytes_in, interval); const uint64_t write_rate = rate_per_second(current_bytes_out, entry.bytes_out, interval); if (read_rate > 0 || write_rate > 0) { - entry.in_use = true; + write_interface_constant(entry); EventNetworkUtilization event(UNTIMED); event.set_starttime(cur_time); event.set_endtime(cur_time); diff --git a/src/share/vm/jfr/periodic/jfrPeriodic.cpp b/src/share/vm/jfr/periodic/jfrPeriodic.cpp index 92f516361dcb3271ebcfab37291d4fe7ac80c80c..e3add031dfd2f725b88f0a9ff25d6515b8838058 100644 --- a/src/share/vm/jfr/periodic/jfrPeriodic.cpp +++ b/src/share/vm/jfr/periodic/jfrPeriodic.cpp @@ -40,6 +40,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 "memory/heapInspection.hpp" @@ -393,13 +394,12 @@ TRACE_REQUEST_FUNC(ThreadAllocationStatistics) { GrowableArray allocated(initial_size); GrowableArray thread_ids(initial_size); JfrTicks time_stamp = JfrTicks::now(); - { - // Collect allocation statistics while holding threads lock - MutexLockerEx ml(Threads_lock); - for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { - allocated.append(thread->cooked_allocated_bytes()); - thread_ids.append(JFR_THREAD_ID(thread)); - } + 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)); } // Write allocation statistics to buffer. diff --git a/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.cpp b/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.cpp index 2d80ceca90f9daf851fd56704eaf705de5c8ab57..b075ecbc50dbc3e083f9bbe51fe9d3a8c170fdda 100644 --- a/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.cpp +++ b/src/share/vm/jfr/periodic/jfrThreadCPULoadEvent.cpp @@ -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 diff --git a/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp b/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp index 969ce8ff6d61a0da63cff4bb9766151c74ff6602..7e7a9fe85e01d661044ae9f04cc320ccaff4646a 100644 --- a/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp +++ b/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp @@ -30,6 +30,7 @@ #include "jfr/recorder/service/jfrOptionSet.hpp" #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" #include "jfr/support/jfrThreadId.hpp" +#include "jfr/support/jfrThreadLocal.hpp" #include "jfr/utilities/jfrTime.hpp" #include "runtime/frame.inline.hpp" #include "runtime/os.hpp" @@ -353,9 +354,14 @@ static void clear_transition_block(JavaThread* jt) { } } +static bool is_excluded(JavaThread* thread) { + assert(thread != NULL, "invariant"); + return thread->is_hidden_from_external_view() || thread->in_deopt_handler() || thread->jfr_thread_local()->is_excluded(); +} + bool JfrThreadSampleClosure::do_sample_thread(JavaThread* thread, JfrStackFrame* frames, u4 max_frames, JfrSampleType type) { assert(Threads_lock->owned_by_self(), "Holding the thread table lock."); - if (thread->is_hidden_from_external_view() || thread->in_deopt_handler()) { + if (is_excluded(thread)) { return false; } diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp index c28dfb81b336e5d7655b912f1ea65b95f94fa7f0..4e98a4e8bed69d93144b9e738d1912b022ef12ea 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp @@ -37,10 +37,13 @@ #include "jfr/recorder/storage/jfrMemorySpace.inline.hpp" #include "jfr/recorder/storage/jfrStorageUtils.inline.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 "memory/resourceArea.hpp" #include "runtime/handles.inline.hpp" -#include "runtime/mutexLocker.hpp" +#include "runtime/mutex.hpp" #include "runtime/orderAccess.inline.hpp" #include "runtime/os.hpp" #include "runtime/safepoint.hpp" @@ -167,7 +170,7 @@ static BufferPtr lease_free(size_t size, JfrCheckpointMspace* mspace, size_t ret } bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const { - return _service_thread != thread && OrderAccess::load_acquire((u1*)&_checkpoint_epoch_state) != JfrTraceIdEpoch::epoch(); + return _service_thread != thread && _checkpoint_epoch_state != JfrTraceIdEpoch::epoch(); } static const size_t lease_retry = 10; @@ -180,12 +183,24 @@ BufferPtr JfrCheckpointManager::lease_buffer(Thread* thread, size_t size /* 0 */ return lease_free(size, manager._free_list_mspace, lease_retry, thread); } +JfrCheckpointMspace* JfrCheckpointManager::lookup(BufferPtr old) const { + assert(old != NULL, "invariant"); + return _free_list_mspace->in_free_list(old) ? _free_list_mspace : _epoch_transition_mspace; +} + +BufferPtr JfrCheckpointManager::lease_buffer(BufferPtr old, Thread* thread, size_t size /* 0 */) { + assert(old != NULL, "invariant"); + JfrCheckpointMspace* mspace = instance().lookup(old); + assert(mspace != NULL, "invariant"); + return lease_free(size, mspace, lease_retry, thread); +} + /* -* If the buffer was a "lease" from the free list, release back. -* -* The buffer is effectively invalidated for the thread post-return, -* and the caller should take means to ensure that it is not referenced. -*/ + * If the buffer was a lease, release back. + * + * The buffer is effectively invalidated for the thread post-return, + * and the caller should take means to ensure that it is not referenced. + */ static void release(BufferPtr const buffer, Thread* thread) { DEBUG_ONLY(assert_release(buffer);) buffer->clear_lease(); @@ -201,7 +216,7 @@ BufferPtr JfrCheckpointManager::flush(BufferPtr old, size_t used, size_t request return NULL; } // migration of in-flight information - BufferPtr const new_buffer = lease_buffer(thread, used + requested); + BufferPtr const new_buffer = lease_buffer(old, thread, used + requested); if (new_buffer != NULL) { migrate_outstanding_writes(old, new_buffer, used, requested); } @@ -212,8 +227,8 @@ BufferPtr JfrCheckpointManager::flush(BufferPtr old, size_t used, size_t request // offsets into the JfrCheckpointEntry static const juint starttime_offset = sizeof(jlong); static const juint duration_offset = starttime_offset + sizeof(jlong); -static const juint flushpoint_offset = duration_offset + sizeof(jlong); -static const juint types_offset = flushpoint_offset + sizeof(juint); +static const juint checkpoint_type_offset = duration_offset + sizeof(jlong); +static const juint types_offset = checkpoint_type_offset + sizeof(juint); static const juint payload_offset = types_offset + sizeof(juint); template @@ -233,21 +248,21 @@ static jlong duration(const u1* data) { return read_data(data + duration_offset); } -static bool is_flushpoint(const u1* data) { - return read_data(data + flushpoint_offset) == (juint)1; +static u1 checkpoint_type(const u1* data) { + return read_data(data + checkpoint_type_offset); } static juint number_of_types(const u1* data) { return read_data(data + types_offset); } -static void write_checkpoint_header(JfrChunkWriter& cw, int64_t offset_prev_cp_event, const u1* data) { +static void write_checkpoint_header(JfrChunkWriter& cw, int64_t delta_to_last_checkpoint, const u1* data) { cw.reserve(sizeof(u4)); cw.write(EVENT_CHECKPOINT); cw.write(starttime(data)); cw.write(duration(data)); - cw.write(offset_prev_cp_event); - cw.write(is_flushpoint(data)); + cw.write(delta_to_last_checkpoint); + cw.write(checkpoint_type(data)); cw.write(number_of_types(data)); } @@ -260,9 +275,9 @@ static size_t write_checkpoint_event(JfrChunkWriter& cw, const u1* data) { assert(data != NULL, "invariant"); const int64_t event_begin = cw.current_offset(); const int64_t last_checkpoint_event = cw.last_checkpoint_offset(); - const int64_t delta = last_checkpoint_event == 0 ? 0 : last_checkpoint_event - event_begin; + const int64_t delta_to_last_checkpoint = last_checkpoint_event == 0 ? 0 : last_checkpoint_event - event_begin; const int64_t checkpoint_size = total_size(data); - write_checkpoint_header(cw, delta, data); + write_checkpoint_header(cw, delta_to_last_checkpoint, data); write_checkpoint_content(cw, data, checkpoint_size); const int64_t event_size = cw.current_offset() - event_begin; cw.write_padded_at_offset(event_size, event_begin); @@ -333,6 +348,16 @@ size_t JfrCheckpointManager::write_epoch_transition_mspace() { return write_mspace_exclusive(_epoch_transition_mspace, _chunkwriter); } +typedef MutexedWriteOp FlushOperation; + +size_t JfrCheckpointManager::flush() { + WriteOperation wo(_chunkwriter); + FlushOperation fo(wo); + assert(_free_list_mspace->is_full_empty(), "invariant"); + process_free_list(fo, _free_list_mspace); + return wo.processed(); +} + typedef DiscardOp > DiscardOperation; size_t JfrCheckpointManager::clear() { JfrTypeSet::clear(); @@ -340,45 +365,121 @@ size_t JfrCheckpointManager::clear() { 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() { - JfrCheckpointWriter writer(false, true, Thread::current()); - JfrTypeManager::write_types(writer); +// Optimization for write_static_type_set() and write_threads() is to write +// directly into the epoch transition mspace because we will immediately +// serialize and reset this mspace post-write. +static JfrBuffer* get_epoch_transition_buffer(JfrCheckpointMspace* mspace, Thread* t) { + assert(mspace != NULL, "invariant"); + JfrBuffer* const buffer = mspace->free_head(); + assert(buffer != NULL, "invariant"); + buffer->acquire(t); + buffer->set_lease(); + DEBUG_ONLY(assert_free_lease(buffer);) + return buffer; +} + +bool JfrCheckpointManager::is_static_type_set_required() { + return JfrTypeManager::has_new_static_type(); +} + +size_t JfrCheckpointManager::write_static_type_set() { + Thread* const t = Thread::current(); + ResourceMark rm(t); + HandleMark hm(t); + JfrCheckpointWriter writer(t, get_epoch_transition_buffer(_epoch_transition_mspace, t), STATICS); + JfrTypeManager::write_static_types(writer); return writer.used_size(); } -size_t JfrCheckpointManager::write_safepoint_types() { - // this is also a "flushpoint" - JfrCheckpointWriter writer(true, true, Thread::current()); - JfrTypeManager::write_safepoint_types(writer); +size_t JfrCheckpointManager::write_threads() { + Thread* const t = Thread::current(); + ResourceMark rm(t); + HandleMark hm(t); + JfrCheckpointWriter writer(t, get_epoch_transition_buffer(_epoch_transition_mspace, t), THREADS); + JfrTypeManager::write_threads(writer); return writer.used_size(); } +size_t JfrCheckpointManager::write_static_type_set_and_threads() { + write_static_type_set(); + write_threads(); + return write_epoch_transition_mspace(); +} + +void JfrCheckpointManager::shift_epoch() { + debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();) + JfrTraceIdEpoch::shift_epoch(); + assert(current_epoch != JfrTraceIdEpoch::current(), "invariant"); +} + +void JfrCheckpointManager::flush_static_type_set() { + flush(); +} + +void JfrCheckpointManager::create_thread_blob(Thread* t) { + JfrTypeManager::create_thread_blob(t); +} + +void JfrCheckpointManager::write_thread_checkpoint(Thread* t) { + JfrTypeManager::write_thread_checkpoint(t); +} + +class JfrNotifyClosure : public ThreadClosure { + public: + void do_thread(Thread* t) { + assert(t != NULL, "invariant"); + assert(t->is_Java_thread(), "invariant"); + assert_locked_or_safepoint(Threads_lock); + JfrJavaEventWriter::notify((JavaThread*)t); + } +}; + +void JfrCheckpointManager::notify_threads() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + JfrNotifyClosure tc; + JfrJavaThreadIterator iter; + while (iter.has_next()) { + tc.do_thread(iter.next()); + } +} + +void JfrCheckpointManager::on_rotation() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + JfrTypeManager::on_rotation(); + notify_threads(); +} + void JfrCheckpointManager::write_type_set() { assert(!SafepointSynchronize::is_at_safepoint(), "invariant"); - // can safepoint here - // TODO: CLDG lock does not exist in 11 - what shall we do? - // MutexLocker cld_lock(ClassLoaderDataGraph_lock); - MutexLocker package_table_lock(PackageTable_lock); - if (!LeakProfiler::is_running()) { - JfrCheckpointWriter writer(true, true, Thread::current()); - JfrTypeSet::serialize(&writer, NULL, false); - } else { + if (LeakProfiler::is_running()) { Thread* const t = Thread::current(); - JfrCheckpointWriter leakp_writer(false, true, t); - JfrCheckpointWriter writer(false, true, t); - JfrTypeSet::serialize(&writer, &leakp_writer, false); + // can safepoint here + // MutexLocker cld_lock(ClassLoaderDataGraph_lock); + // MutexLocker module_lock(Module_lock); + MutexLocker package_table_lock(PackageTable_lock); + JfrCheckpointWriter leakp_writer(t); + JfrCheckpointWriter writer(t); + JfrTypeSet::serialize(&writer, &leakp_writer, false, false); ObjectSampleCheckpoint::on_type_set(leakp_writer); + } else { + // can safepoint here + // MutexLocker cld_lock(ClassLoaderDataGraph_lock); + // MutexLocker module_lock(Module_lock); + MutexLocker package_table_lock(PackageTable_lock); + JfrCheckpointWriter writer(Thread::current()); + JfrTypeSet::serialize(&writer, NULL, false, false); } + write(); } void JfrCheckpointManager::write_type_set_for_unloaded_classes() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); - JfrCheckpointWriter writer(false, true, Thread::current()); + JfrCheckpointWriter writer(Thread::current()); const JfrCheckpointContext ctx = writer.context(); - JfrTypeSet::serialize(&writer, NULL, true); + JfrTypeSet::serialize(&writer, NULL, true, false); if (LeakProfiler::is_running()) { ObjectSampleCheckpoint::on_type_set_unload(writer); } @@ -388,16 +489,20 @@ void JfrCheckpointManager::write_type_set_for_unloaded_classes() { } } -void JfrCheckpointManager::create_thread_blob(JavaThread* jt) { - JfrTypeManager::create_thread_blob(jt); -} - -void JfrCheckpointManager::write_thread_checkpoint(JavaThread* jt) { - JfrTypeManager::write_thread_checkpoint(jt); +bool JfrCheckpointManager::is_type_set_required() { + return JfrTraceIdEpoch::has_changed_tag_state(); } -void JfrCheckpointManager::shift_epoch() { - debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();) - JfrTraceIdEpoch::shift_epoch(); - assert(current_epoch != JfrTraceIdEpoch::current(), "invariant"); +size_t JfrCheckpointManager::flush_type_set() { + assert(!SafepointSynchronize::is_at_safepoint(), "invariant"); + size_t elements = 0; + { + JfrCheckpointWriter writer(Thread::current()); + // can safepoint here + // MutexLocker cld_lock(ClassLoaderDataGraph_lock); + MutexLocker package_table_lock(PackageTable_lock); + elements = JfrTypeSet::serialize(&writer, NULL, false, true); + } + flush(); + return elements; } diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp index f90cdc9af38c21450925d713a773deac36953b80..5392f145eaedc282fdebeb28d7c7aeb11aa47c6d 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp @@ -68,18 +68,29 @@ class JfrCheckpointManager : public JfrCHeapObj { void unlock(); DEBUG_ONLY(bool is_locked() const;) + JfrCheckpointMspace* lookup(Buffer* old) const; + bool use_epoch_transition_mspace(const Thread* t) const; + size_t write_epoch_transition_mspace(); + static Buffer* lease_buffer(Thread* t, size_t size = 0); + static Buffer* lease_buffer(Buffer* old, Thread* t, size_t size = 0); static Buffer* flush(Buffer* old, size_t used, size_t requested, Thread* t); size_t clear(); size_t write(); - size_t write_epoch_transition_mspace(); - size_t write_types(); - size_t write_safepoint_types(); + size_t flush(); + + bool is_static_type_set_required(); + size_t write_static_type_set(); + size_t write_threads(); + size_t write_static_type_set_and_threads(); + bool is_type_set_required(); void write_type_set(); + static void write_type_set_for_unloaded_classes(); + void shift_epoch(); void synchronize_epoch(); - bool use_epoch_transition_mspace(const Thread* t) const; + void notify_threads(); JfrCheckpointManager(JfrChunkWriter& cw); ~JfrCheckpointManager(); @@ -87,14 +98,17 @@ class JfrCheckpointManager : public JfrCHeapObj { static JfrCheckpointManager& instance(); static JfrCheckpointManager* create(JfrChunkWriter& cw); bool initialize(); + void on_rotation(); static void destroy(); public: + size_t flush_type_set(); + void flush_static_type_set(); + static void create_thread_blob(Thread* t); + static void write_thread_checkpoint(Thread* t); void register_service_thread(const Thread* t); - static void write_type_set_for_unloaded_classes(); - static void create_thread_blob(JavaThread* jt); - static void write_thread_checkpoint(JavaThread* jt); + friend class Jfr; friend class JfrRecorder; friend class JfrRecorderService; friend class JfrCheckpointFlush; diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp index fb2bd1bb0c1e1ecb913856065ddad46c7501ef88..19ae7684bb316590a2847a961688a25c7fe48426 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp @@ -31,12 +31,26 @@ JfrCheckpointFlush::JfrCheckpointFlush(Type* old, size_t used, size_t requested, Thread* t) : _result(JfrCheckpointManager::flush(old, used, requested, t)) {} -JfrCheckpointWriter::JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread) : - JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(thread), thread), +JfrCheckpointWriter::JfrCheckpointWriter(JfrCheckpointType type /* GENERIC */) : + JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(Thread::current()), Thread::current()), _time(JfrTicks::now()), _offset(0), _count(0), - _flushpoint(flushpoint), + _type(type), + _header(true) { + assert(this->is_acquired(), "invariant"); + assert(0 == this->current_offset(), "invariant"); + if (_header) { + reserve(sizeof(JfrCheckpointEntry)); + } +} + +JfrCheckpointWriter::JfrCheckpointWriter(Thread* t, bool header /* true */, JfrCheckpointType type /* GENERIC */) : + JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(t), t), + _time(JfrTicks::now()), + _offset(0), + _count(0), + _type(type), _header(header) { assert(this->is_acquired(), "invariant"); assert(0 == this->current_offset(), "invariant"); @@ -45,13 +59,27 @@ JfrCheckpointWriter::JfrCheckpointWriter(bool flushpoint, bool header, Thread* t } } -static void write_checkpoint_header(u1* pos, jlong size, jlong time, bool flushpoint, juint type_count) { +JfrCheckpointWriter::JfrCheckpointWriter(Thread* t, JfrBuffer* buffer, JfrCheckpointType type /* GENERIC */) : + JfrCheckpointWriterBase(buffer, t), + _time(JfrTicks::now()), + _offset(0), + _count(0), + _type(type), + _header(true) { + assert(this->is_acquired(), "invariant"); + assert(0 == this->current_offset(), "invariant"); + if (_header) { + reserve(sizeof(JfrCheckpointEntry)); + } +} + +static void write_checkpoint_header(u1* pos, int64_t size, jlong time, u4 checkpoint_type, u4 type_count) { assert(pos != NULL, "invariant"); JfrBigEndianWriter be_writer(pos, sizeof(JfrCheckpointEntry)); be_writer.write(size); be_writer.write(time); be_writer.write(JfrTicks::now().value() - time); - be_writer.write(flushpoint ? (juint)1 : (juint)0); + be_writer.write(checkpoint_type); be_writer.write(type_count); assert(be_writer.is_valid(), "invariant"); } @@ -74,18 +102,10 @@ JfrCheckpointWriter::~JfrCheckpointWriter() { assert(this->used_size() > sizeof(JfrCheckpointEntry), "invariant"); const jlong size = this->current_offset(); assert(size + this->start_pos() == this->current_pos(), "invariant"); - write_checkpoint_header(const_cast(this->start_pos()), size, _time, is_flushpoint(), count()); + write_checkpoint_header(const_cast(this->start_pos()), size, _time, (u4)_type, count()); release(); } -void JfrCheckpointWriter::set_flushpoint(bool flushpoint) { - _flushpoint = flushpoint; -} - -bool JfrCheckpointWriter::is_flushpoint() const { - return _flushpoint; -} - juint JfrCheckpointWriter::count() const { return _count; } @@ -140,7 +160,7 @@ const u1* JfrCheckpointWriter::session_data(size_t* size, bool move /* false */, } *size = this->used_size(); assert(this->start_pos() + *size == this->current_pos(), "invariant"); - write_checkpoint_header(const_cast(this->start_pos()), this->used_offset(), _time, is_flushpoint(), count()); + write_checkpoint_header(const_cast(this->start_pos()), this->used_offset(), _time, (u4)_type, count()); _header = false; // the header was just written if (move) { this->seek(_offset); diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp index efdfefd14732650f9a3d5b3f2cab03710c06438b..cd9754947ba2e3a57e321f18f87c7ce8dc6d8c60 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp @@ -54,23 +54,24 @@ struct JfrCheckpointContext { }; class JfrCheckpointWriter : public JfrCheckpointWriterBase { + friend class JfrCheckpointManager; friend class JfrSerializerRegistration; private: JfrTicks _time; jlong _offset; juint _count; - bool _flushpoint; + JfrCheckpointType _type; bool _header; juint count() const; void set_count(juint count); void increment(); - void set_flushpoint(bool flushpoint); - bool is_flushpoint() const; const u1* session_data(size_t* size, bool move = false, const JfrCheckpointContext* ctx = NULL); void release(); + JfrCheckpointWriter(Thread* t, JfrBuffer* buffer, JfrCheckpointType type = GENERIC); public: - JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread); + JfrCheckpointWriter(JfrCheckpointType type = GENERIC); + JfrCheckpointWriter(Thread* t, bool header = true, JfrCheckpointType mode = GENERIC); ~JfrCheckpointWriter(); void write_type(JfrTypeId type_id); void write_count(u4 nof_entries); diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.cpp index 6c4d72f4d6870a1130b7ff1e5cc852e6e9df3de4..6732f4e7e896d7a24eeede22aeba3a9537a2791f 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.cpp @@ -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,61 +29,53 @@ #include "oops/klass.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.hpp" -#include "runtime/semaphore.hpp" #include "runtime/thread.inline.hpp" -static jbyteArray _metadata_blob = NULL; -static Semaphore metadata_mutex_semaphore(1); +static jbyteArray metadata_blob = NULL; +static u8 metadata_id = 0; +static u8 last_metadata_id = 0; -void JfrMetadataEvent::lock() { - metadata_mutex_semaphore.wait(); +static void write_metadata_blob(JfrChunkWriter& chunkwriter) { + assert(metadata_blob != NULL, "invariant"); + const typeArrayOop arr = (typeArrayOop)JfrJavaSupport::resolve_non_null(metadata_blob); + assert(arr != NULL, "invariant"); + const int length = arr->length(); + const Klass* const k = arr->klass(); + assert(k != NULL && k->oop_is_array(), "invariant"); + const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast((Klass*)k); + const jbyte* const data_address = arr->byte_at_addr(0); + chunkwriter.write_unbuffered(data_address, length); } -void JfrMetadataEvent::unlock() { - metadata_mutex_semaphore.signal(); -} - -static void write_metadata_blob(JfrChunkWriter& chunkwriter, jbyteArray metadata_blob) { - if (metadata_blob != NULL) { - const typeArrayOop arr = (typeArrayOop)JfrJavaSupport::resolve_non_null(metadata_blob); - assert(arr != NULL, "invariant"); - const int length = arr->length(); - Klass* const k = arr->klass(); - assert(k != NULL && k->oop_is_array(), "invariant"); - const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast(k); - const jbyte* const data_address = arr->byte_at_addr(0); - chunkwriter.write_unbuffered(data_address, length); - } -} - -// the semaphore is assumed to be locked (was locked previous safepoint) -size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter, jlong metadata_offset) { +void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { assert(chunkwriter.is_valid(), "invariant"); - assert(chunkwriter.current_offset() == metadata_offset, "invariant"); + if (last_metadata_id == metadata_id && chunkwriter.has_metadata()) { + return; + } // header - chunkwriter.reserve(sizeof(u4)); + const int64_t metadata_offset = chunkwriter.reserve(sizeof(u4)); chunkwriter.write(EVENT_METADATA); // ID 0 // time data chunkwriter.write(JfrTicks::now()); chunkwriter.write((u8)0); // duration - chunkwriter.write((u8)0); // metadata id - write_metadata_blob(chunkwriter, _metadata_blob); // payload - unlock(); // open up for java to provide updated metadata + chunkwriter.write(metadata_id); // metadata id + write_metadata_blob(chunkwriter); // payload // fill in size of metadata descriptor event - const jlong size_written = chunkwriter.current_offset() - metadata_offset; + const int64_t size_written = chunkwriter.current_offset() - metadata_offset; chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset); - return size_written; + chunkwriter.set_last_metadata_offset(metadata_offset); + last_metadata_id = metadata_id; } 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); + 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(); + assert(new_desc_oop != NULL, "invariant"); + metadata_blob = (jbyteArray)JfrJavaSupport::global_jni_handle(new_desc_oop, thread); + ++metadata_id; } diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.hpp index 09845069df63d942e1ef7adf8cccd96d24dfd482..15554c1d28002f801f0365b70ee2cb2e886ad0d2 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrMetadataEvent.hpp @@ -33,13 +33,10 @@ class JfrChunkWriter; // // Metadata is continuously updated in Java as event classes are loaded / unloaded. // Using update(), Java stores a binary representation back to native. -// This is for easy access on chunk finalization as well as having it readily available in the case of fatal error. // class JfrMetadataEvent : AllStatic { 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); }; diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp index d090a1891dbbb3158050e25b2dea22386ea10ef2..048757afbd169b9a8111d3d08c54ea79ed5581c8 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp @@ -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.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 @@ void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group(JfrThreadGroupPointer } } -JfrThreadGroup::JfrThreadGroup() : _list(NULL) { - _list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray(30, true); -} +JfrThreadGroup::JfrThreadGroup() : + _list(new (ResourceObj::C_HEAP, mtTracing) GrowableArray(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 @@ void JfrThreadGroup::set_instance(JfrThreadGroup* new_instance) { } 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* jt) { - assert(!JfrStream_lock->owned_by_self(), "holding stream lock but should not hold it here"); return thread_group_id(jt, jt); } @@ -398,9 +394,7 @@ void JfrThreadGroup::serialize(JfrCheckpointWriter& writer) { ThreadGroupExclusiveAccess lock; JfrThreadGroup* tg_instance = instance(); assert(tg_instance != NULL, "invariant"); - ResourceManager 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 diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.cpp index 50e213b3ce5d3ae10a274b4a78be5a3556f4e36a..31b2416d2b9c65861d505b8e82c3c7552f609360 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.cpp @@ -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 @@ -23,9 +23,13 @@ */ #include "precompiled.hpp" +#include "classfile/javaClasses.hpp" #include "jfr/recorder/checkpoint/types/jfrThreadState.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/support/jfrThreadLocal.hpp" #include "jvmtifiles/jvmti.h" +#include "runtime/osThread.hpp" +#include "runtime/thread.hpp" struct jvmti_thread_state { u8 id; @@ -80,3 +84,47 @@ void JfrThreadState::serialize(JfrCheckpointWriter& writer) { } } +traceid JfrThreadId::id(const Thread* t) { + assert(t != NULL, "invariant"); + if (!t->is_Java_thread()) { + return os_id(t); + } + const JavaThread* const jt = (JavaThread*)t; + const oop thread_obj = jt->threadObj(); + return thread_obj != NULL ? java_lang_Thread::thread_id(thread_obj) : 0; +} + +traceid JfrThreadId::os_id(const Thread* t) { + assert(t != NULL, "invariant"); + const OSThread* const os_thread = t->osthread(); + return os_thread != NULL ? os_thread->thread_id() : 0; +} + +traceid JfrThreadId::jfr_id(const Thread* t) { + assert(t != NULL, "invariant"); + return t->jfr_thread_local()->thread_id(); +} + +// caller needs ResourceMark +const char* get_java_thread_name(const Thread* t) { + assert(t != NULL, "invariant"); + assert(t->is_Java_thread(), "invariant"); + const JavaThread* const jt = ((JavaThread*)t); + const char* name_str = ""; + const oop thread_obj = jt->threadObj(); + if (thread_obj != NULL) { + const oop name = java_lang_Thread::name(thread_obj); + if (name != NULL) { + name_str = java_lang_String::as_utf8_string(name); + } + } else if (jt->is_attaching_via_jni()) { + name_str = ""; + } + assert(name_str != NULL, "unexpected NULL thread name"); + return name_str; +} + +const char* JfrThreadName::name(const Thread* t) { + assert(t != NULL, "invariant"); + return t->is_Java_thread() ? get_java_thread_name(t) : t->name(); +} diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.hpp index bcedaa7949e082c9521785268977ce71c4644483..9fd241193053d56fb017a1f8a7599568fc6c74cc 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadState.hpp @@ -28,10 +28,24 @@ #include "memory/allocation.hpp" class JfrCheckpointWriter; +class Thread; class JfrThreadState : public AllStatic { public: static void serialize(JfrCheckpointWriter& writer); }; +class JfrThreadId : public AllStatic { +public: + static traceid id(const Thread* t); + static traceid os_id(const Thread* t); + static traceid jfr_id(const Thread* t); +}; + +class JfrThreadName : public AllStatic { + public: + // Requires a ResourceMark for get_thread_name/as_utf8 + static const char* name(const Thread* t); +}; + #endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp index f717ae6fbf66b4ce19a35aa308f739d32166aca6..2079fcb28c0c2ccef3bb6a34aa22040221545781 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp @@ -31,13 +31,14 @@ #include "gc_implementation/shared/gcTrace.hpp" #include "gc_implementation/shared/gcWhen.hpp" #include "jfr/leakprofiler/leakProfiler.hpp" -#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" #include "jfr/recorder/checkpoint/types/jfrType.hpp" #include "jfr/recorder/jfrRecorder.hpp" #include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp" #include "jfr/recorder/checkpoint/types/jfrThreadState.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" @@ -86,27 +87,18 @@ class JfrCheckpointThreadClosure : public ThreadClosure { void do_thread(Thread* t); }; -// 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; - } ++_count; - _writer.write_key(tl->thread_id()); - _writer.write(t->name()); - const OSThread* const os_thread = t->osthread(); - _writer.write(os_thread != NULL ? os_thread->thread_id() : 0); + _writer.write_key(JfrThreadId::jfr_id(t)); + const char* const name = JfrThreadName::name(t); + assert(name != NULL, "invariant"); + _writer.write(name); + _writer.write(JfrThreadId::os_id(t)); if (t->is_Java_thread()) { - JavaThread* const jt = (JavaThread*)t; - _writer.write(jt->name()); - _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); + _writer.write(name); + _writer.write(JfrThreadId::id(t)); + _writer.write(JfrThreadGroup::thread_group_id((JavaThread*)t, _curthread)); return; } _writer.write((const char*)NULL); // java name @@ -115,13 +107,18 @@ void JfrCheckpointThreadClosure::do_thread(Thread* t) { } 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,19 +295,21 @@ void ThreadStateConstant::serialize(JfrCheckpointWriter& writer) { void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) { assert(_thread != NULL, "invariant"); assert(_thread == Thread::current(), "invariant"); - assert(_thread->is_Java_thread(), "invariant"); - ResourceMark rm(_thread); - const oop threadObj = _thread->threadObj(); - assert(threadObj != NULL, "invariant"); - const u8 java_lang_thread_id = java_lang_Thread::thread_id(threadObj); - const char* const thread_name = _thread->name(); - const traceid thread_group_id = JfrThreadGroup::thread_group_id(_thread); writer.write_count(1); - writer.write_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); + writer.write_key(JfrThreadId::jfr_id(_thread)); + const char* const name = JfrThreadName::name(_thread); + writer.write(name); + writer.write(JfrThreadId::os_id(_thread)); + if (_thread->is_Java_thread()) { + writer.write(name); + writer.write(JfrThreadId::id(_thread)); + JavaThread* const jt = (JavaThread*)_thread; + const traceid thread_group_id = JfrThreadGroup::thread_group_id(jt, jt); + writer.write(thread_group_id); + JfrThreadGroup::serialize(&writer, thread_group_id); + return; + } + writer.write((const char*)NULL); // java name + writer.write((traceid)0); // java thread id + writer.write((traceid)0); // java thread group } diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp index cddbc43aaa31ec2b2f21db2ea4abe0e10038286a..b13720ec9e98379e7eeaa427a01d670965d18331 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp @@ -27,16 +27,6 @@ #include "jfr/metadata/jfrSerializer.hpp" -class JfrThreadConstantSet : public JfrSerializer { - public: - void serialize(JfrCheckpointWriter& writer); -}; - -class JfrThreadGroupConstant : public JfrSerializer { - public: - void serialize(JfrCheckpointWriter& writer); -}; - class FlagValueOriginConstant : public JfrSerializer { public: void serialize(JfrCheckpointWriter& writer); @@ -112,6 +102,16 @@ class VMOperationTypeConstant : public JfrSerializer { void serialize(JfrCheckpointWriter& writer); }; +class JfrThreadConstantSet : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + +class JfrThreadGroupConstant : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer); +}; + class ThreadStateConstant : public JfrSerializer { public: void serialize(JfrCheckpointWriter& writer); @@ -119,9 +119,9 @@ class ThreadStateConstant : public JfrSerializer { class JfrThreadConstant : public JfrSerializer { private: - JavaThread* _thread; + Thread* _thread; public: - JfrThreadConstant(JavaThread* jt) : _thread(jt) {} + JfrThreadConstant(Thread* t) : _thread(t) {} void serialize(JfrCheckpointWriter& writer); }; diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp index 135491dd986a36dac8e4e37f758f6e4f333c3a57..84d02cb27a6f2bd0603161efc9ad454eb7e25b26 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp @@ -27,6 +27,7 @@ #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" #include "jfr/recorder/checkpoint/types/jfrType.hpp" #include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp" +#include "jfr/recorder/jfrRecorder.hpp" #include "jfr/utilities/jfrDoublyLinkedList.hpp" #include "jfr/utilities/jfrIterator.hpp" #include "memory/resourceArea.hpp" @@ -73,29 +74,71 @@ class JfrSerializerRegistration : public JfrCHeapObj { return _id; } - void invoke(JfrCheckpointWriter& writer) const; + void on_rotation() const { + _serializer->on_rotation(); + } + + void invoke(JfrCheckpointWriter& writer) const { + if (_cache.valid()) { + writer.increment(); + _cache->write(writer); + return; + } + const JfrCheckpointContext ctx = writer.context(); + // serialize the type id before invoking callback + writer.write_type(_id); + const intptr_t start = writer.current_offset(); + // invoke the serializer routine + _serializer->serialize(writer); + if (start == writer.current_offset()) { + // the serializer implementation did nothing, rewind to restore + writer.set_context(ctx); + return; + } + if (_permit_cache) { + _cache = writer.copy(&ctx); + } + } }; -void JfrSerializerRegistration::invoke(JfrCheckpointWriter& writer) const { - if (_cache.valid()) { - writer.increment(); - _cache->write(writer); - return; - } - const JfrCheckpointContext ctx = writer.context(); - // serialize the type id before invoking callback - writer.write_type(_id); - const intptr_t start = writer.current_offset(); - // invoke the serializer routine - _serializer->serialize(writer); - if (start == writer.current_offset() ) { - // the serializer implementation did nothing, rewind to restore - writer.set_context(ctx); - return; - } - if (_permit_cache) { - _cache = writer.copy(&ctx); - } +static void serialize_threads(JfrCheckpointWriter& writer) { + JfrThreadConstantSet thread_set; + writer.write_type(TYPE_THREAD); + thread_set.serialize(writer); +} + +static void serialize_thread_groups(JfrCheckpointWriter& writer) { + JfrThreadGroupConstant thread_group_set; + writer.write_type(TYPE_THREADGROUP); + thread_group_set.serialize(writer); +} + +void JfrTypeManager::write_threads(JfrCheckpointWriter& writer) { + serialize_threads(writer); + serialize_thread_groups(writer); +} + +void JfrTypeManager::create_thread_blob(Thread* t) { + assert(t != NULL, "invariant"); + ResourceMark rm(t); + HandleMark hm(t); + JfrThreadConstant type_thread(t); + JfrCheckpointWriter writer(t, true, THREADS); + writer.write_type(TYPE_THREAD); + type_thread.serialize(writer); + // create and install a checkpoint blob + t->jfr_thread_local()->set_thread_blob(writer.move()); + assert(t->jfr_thread_local()->has_thread_blob(), "invariant"); +} + +void JfrTypeManager::write_thread_checkpoint(Thread* t) { + assert(t != NULL, "invariant"); + ResourceMark rm(t); + HandleMark hm(t); + JfrThreadConstant type_thread(t); + JfrCheckpointWriter writer(t, true, THREADS); + writer.write_type(TYPE_THREAD); + type_thread.serialize(writer); } class SerializerRegistrationGuard : public StackObj { @@ -115,7 +158,6 @@ Semaphore SerializerRegistrationGuard::_mutex_semaphore(1); typedef JfrDoublyLinkedList List; typedef StopOnNullIterator Iterator; static List types; -static List safepoint_types; void JfrTypeManager::destroy() { SerializerRegistrationGuard guard; @@ -126,52 +168,15 @@ void JfrTypeManager::destroy() { 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) { +void JfrTypeManager::on_rotation() { const Iterator iter(types); while (iter.has_next()) { - iter.next()->invoke(writer); - } -} - -void JfrTypeManager::write_safepoint_types(JfrCheckpointWriter& writer) { - assert(SafepointSynchronize::is_at_safepoint(), "invariant"); - const Iterator iter(safepoint_types); - while (iter.has_next()) { - iter.next()->invoke(writer); + iter.next()->on_rotation(); } } -void JfrTypeManager::create_thread_blob(JavaThread* jt) { - assert(jt != NULL, "invariant"); - ResourceMark rm(jt); - HandleMark hm(jt); - JfrThreadConstant type_thread(jt); - JfrCheckpointWriter writer(false, true, jt); - writer.write_type(TYPE_THREAD); - type_thread.serialize(writer); - // create and install a checkpoint blob - jt->jfr_thread_local()->set_thread_blob(writer.move()); - assert(jt->jfr_thread_local()->has_thread_blob(), "invariant"); -} - -void JfrTypeManager::write_thread_checkpoint(JavaThread* jt) { - assert(jt != NULL, "JavaThread is NULL!"); - ResourceMark rm(jt); - HandleMark hm(jt); - JfrThreadConstant type_thread(jt); - JfrCheckpointWriter writer(false, true, jt); - writer.write_type(TYPE_THREAD); - type_thread.serialize(writer); -} - #ifdef ASSERT static void assert_not_registered_twice(JfrTypeId id, List& list) { const Iterator iter(list); @@ -181,22 +186,23 @@ static void assert_not_registered_twice(JfrTypeId id, List& list) { } #endif -static bool register_type(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer) { +static bool new_registration = false; + +static bool register_static_type(JfrTypeId id, bool permit_cache, JfrSerializer* serializer) { assert(serializer != NULL, "invariant"); 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 (JfrRecorder::is_recording()) { + JfrCheckpointWriter writer(STATICS); + registration->invoke(writer); + new_registration = true; } + types.prepend(registration); return true; } @@ -204,31 +210,46 @@ bool JfrTypeManager::initialize() { 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 safepointing type serialization - register_type(TYPE_THREADGROUP, true, false, new JfrThreadGroupConstant()); - register_type(TYPE_THREAD, true, false, new JfrThreadConstantSet()); + register_static_type(TYPE_FLAGVALUEORIGIN, true, new FlagValueOriginConstant()); + register_static_type(TYPE_INFLATECAUSE, true, new MonitorInflateCauseConstant()); + register_static_type(TYPE_GCCAUSE, true, new GCCauseConstant()); + register_static_type(TYPE_GCNAME, true, new GCNameConstant()); + register_static_type(TYPE_GCWHEN, true, new GCWhenConstant()); + register_static_type(TYPE_G1HEAPREGIONTYPE, true, new G1HeapRegionTypeConstant()); + register_static_type(TYPE_GCTHRESHOLDUPDATER, true, new GCThresholdUpdaterConstant()); + register_static_type(TYPE_METADATATYPE, true, new MetadataTypeConstant()); + register_static_type(TYPE_METASPACEOBJECTTYPE, true, new MetaspaceObjectTypeConstant()); + register_static_type(TYPE_G1YCTYPE, true, new G1YCTypeConstant()); + register_static_type(TYPE_REFERENCETYPE, true, new ReferenceTypeConstant()); + register_static_type(TYPE_NARROWOOPMODE, true, new NarrowOopModeConstant()); + register_static_type(TYPE_COMPILERPHASETYPE, true, new CompilerPhaseTypeConstant()); + register_static_type(TYPE_CODEBLOBTYPE, true, new CodeBlobTypeConstant()); + register_static_type(TYPE_VMOPERATIONTYPE, true, new VMOperationTypeConstant()); + register_static_type(TYPE_THREADSTATE, true, new ThreadStateConstant()); + 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_static_type(id, permit_cache, serializer); +} + +bool JfrTypeManager::has_new_static_type() { + if (new_registration) { + SerializerRegistrationGuard guard; + new_registration = false; + return true; + } + return false; +} + +void JfrTypeManager::write_static_types(JfrCheckpointWriter& writer) { + SerializerRegistrationGuard guard; + const Iterator iter(types); + while (iter.has_next()) { + iter.next()->invoke(writer); + } + new_registration = false; } diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp index 50b01024bebe771bb68bd0f455518ceeb1f10af7..0f2aa2bcd1379feae56c69cd40b671f6a5197360 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp @@ -33,10 +33,12 @@ class JfrTypeManager : public AllStatic { public: static bool initialize(); static void destroy(); - static void write_types(JfrCheckpointWriter& writer); - static void write_safepoint_types(JfrCheckpointWriter& writer); - static void create_thread_blob(JavaThread* jt); - static void write_thread_checkpoint(JavaThread* jt); + static void on_rotation(); + static void write_threads(JfrCheckpointWriter& writer); + static void create_thread_blob(Thread* t); + static void write_thread_checkpoint(Thread* t); + static bool has_new_static_type(); + static void write_static_types(JfrCheckpointWriter& writer); }; #endif // SHARE_VM_JFR_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index ac88cda0553c0dcc23f1b4f7c7b8cea95a971cb1..d9ee53cd035d41ac121dc479b5060b19780d910a 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -72,7 +72,7 @@ static traceid create_symbol_id(traceid artifact_id) { } static bool current_epoch() { - return _class_unload; + return _class_unload || _flushpoint; } static bool previous_epoch() { @@ -109,14 +109,14 @@ inline uintptr_t package_name_hash(const char *s) { return val; } -static traceid package_id(KlassPtr klass, JfrArtifactSet* artifacts) { +static traceid package_id(KlassPtr klass, JfrArtifactSet* artifacts, bool leakp) { assert(klass != NULL, "invariant"); char* klass_name = klass->name()->as_C_string(); // uses ResourceMark declared in JfrTypeSet::serialize() const char* pkg_name = ClassLoader::package_from_name(klass_name, NULL); if (pkg_name == NULL) { return 0; } - return CREATE_PACKAGE_ID(artifacts->markPackage(pkg_name, package_name_hash(pkg_name))); + return CREATE_PACKAGE_ID(artifacts->markPackage(pkg_name, package_name_hash(pkg_name), leakp)); } static traceid method_id(KlassPtr klass, MethodPtr method) { @@ -178,7 +178,7 @@ static int write_klass(JfrCheckpointWriter* writer, KlassPtr klass, bool leakp) theklass = obj_arr_klass->bottom_klass(); } if (theklass->oop_is_instance()) { - pkg_id = package_id(theklass, _artifacts); + pkg_id = package_id(theklass, _artifacts, leakp); } else { assert(theklass->oop_is_typeArray(), "invariant"); } @@ -245,7 +245,7 @@ static void do_unloaded_klass(Klass* klass) { static void do_klass(Klass* klass) { assert(klass != NULL, "invariant"); assert(_subsystem_callback != NULL, "invariant"); - if (current_epoch()) { + if (_flushpoint) { if (USED_THIS_EPOCH(klass)) { _subsystem_callback->do_artifact(klass); return; @@ -316,7 +316,14 @@ static bool write_klasses() { return true; } -int write__artifact__package(JfrCheckpointWriter* writer, const void* p) { +template <> +void set_serialized(CStringEntryPtr ptr) { + assert(ptr != NULL, "invariant"); + ptr->set_serialized(); + assert(ptr->is_serialized(), "invariant"); +} + +int write__package(JfrCheckpointWriter* writer, const void* p) { assert(writer != NULL, "invariant"); assert(_artifacts != NULL, "invariant"); assert(p != NULL, "invariant"); @@ -327,17 +334,45 @@ int write__artifact__package(JfrCheckpointWriter* writer, const void* p) { writer->write((traceid)CREATE_PACKAGE_ID(entry->id())); writer->write((traceid)CREATE_SYMBOL_ID(package_name_symbol_id)); writer->write((bool)true); // exported + set_serialized(entry); + return 1; +} + +int write__package__leakp(JfrCheckpointWriter* writer, const void* p) { + assert(writer != NULL, "invariant"); + assert(_artifacts != NULL, "invariant"); + assert(p != NULL, "invariant"); + + CStringEntryPtr entry = (CStringEntryPtr)p; + const traceid package_name_symbol_id = _artifacts->mark(package_name_hash(entry->value()), entry->value(), true); + assert(package_name_symbol_id > 0, "invariant"); + writer->write((traceid)CREATE_PACKAGE_ID(entry->id())); + writer->write((traceid)CREATE_SYMBOL_ID(package_name_symbol_id)); + writer->write((bool)true); // exported return 1; } -typedef JfrTypeWriterImplHost PackageEntryWriterImpl; -typedef JfrTypeWriterHost PackageEntryWriter; +typedef SymbolPredicate PackageCStringPredicate; +typedef JfrPredicatedTypeWriterImplHost PackageCStringEntryWriterImpl; +typedef JfrTypeWriterHost PackageCStringEntryWriter; + +typedef SymbolPredicate LeakPackageCStringPredicate; +typedef JfrPredicatedTypeWriterImplHost LeakPackageCStringEntryWriterImpl; +typedef JfrTypeWriterHost LeakPackageCStringEntryWriter; +typedef CompositeFunctor CompositePackageCStringWriter; void write_packages() { - // below jdk9 there is no oop for packages, so nothing to do with leakp_writer - // just write packages - PackageEntryWriter pw(_writer, _class_unload); - _artifacts->iterate_packages(pw); + if (_leakp_writer != NULL) { + PackageCStringEntryWriter pcsw(_writer, _class_unload); + LeakPackageCStringEntryWriter lpcsw(_leakp_writer, _class_unload); + CompositePackageCStringWriter cpcsw(&lpcsw, &pcsw); + _artifacts->iterate_packages(cpcsw); + _artifacts->tally(pcsw); + } else { + PackageCStringEntryWriter pcsw(_writer, _class_unload); + _artifacts->iterate_packages(pcsw); + _artifacts->tally(pcsw); + } } template @@ -636,13 +671,6 @@ void set_serialized(SymbolEntryPtr ptr) { assert(ptr->is_serialized(), "invariant"); } -template <> -void set_serialized(CStringEntryPtr ptr) { - assert(ptr != NULL, "invariant"); - ptr->set_serialized(); - assert(ptr->is_serialized(), "invariant"); -} - static int write_symbol(JfrCheckpointWriter* writer, SymbolEntryPtr entry, bool leakp) { assert(writer != NULL, "invariant"); assert(entry != NULL, "invariant"); @@ -754,10 +782,11 @@ static size_t teardown() { return total_count; } -static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload) { +static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint) { _writer = writer; _leakp_writer = leakp_writer; _class_unload = class_unload; + _flushpoint = flushpoint; if (_artifacts == NULL) { _artifacts = new JfrArtifactSet(class_unload); } else { @@ -771,10 +800,10 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer /** * Write all "tagged" (in-use) constant artifacts and their dependencies. */ -size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload) { +size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint) { assert(writer != NULL, "invariant"); ResourceMark rm; - setup(writer, leakp_writer, class_unload); + setup(writer, leakp_writer, class_unload, flushpoint); // write order is important because an individual write step // might tag an artifact to be written in a subsequent step if (!write_klasses()) { diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.hpp index 949380271f74eb5c47e2d6d9cd64560e9ca6b0a0..4d097d731fe4b18909e18e6d1b69a77865cc0cfb 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.hpp @@ -32,7 +32,7 @@ class JfrCheckpointWriter; class JfrTypeSet : AllStatic { public: static void clear(); - static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload); + static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint); }; #endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESET_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp index 4e0318d2c43947f5e3e883dcc43c58902f9e7cb4..4c122ed613e2564a92ee8a124a2dce8079a59c8a 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp @@ -175,7 +175,7 @@ traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) { return entry.id(); } -traceid JfrSymbolId::markPackage(const char* name, uintptr_t hash) { +traceid JfrSymbolId::markPackage(const char* name, uintptr_t hash, bool leakp) { assert(name != NULL, "invariant"); assert(_pkg_table != NULL, "invariant"); _cstring_query = name; @@ -183,6 +183,9 @@ traceid JfrSymbolId::markPackage(const char* name, uintptr_t hash) { if (_class_unload) { entry.set_unloading(); } + if (leakp) { + entry.set_leakp(); + } return entry.id(); } @@ -308,8 +311,8 @@ traceid JfrArtifactSet::mark(const Klass* klass, bool leakp) { return _symbol_id->mark(klass, leakp); } -traceid JfrArtifactSet::markPackage(const char* const name, uintptr_t hash) { - return _symbol_id->markPackage(name, hash); +traceid JfrArtifactSet::markPackage(const char* const name, uintptr_t hash, bool leakp) { + return _symbol_id->markPackage(name, hash, leakp); } traceid JfrArtifactSet::mark(const Symbol* symbol, bool leakp) { diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp index 44fe244d69fe82cec748849448a1479830bae062..328835852a20bf3bd20ccd6d44fde16fc4ab3290 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp @@ -256,7 +256,7 @@ class JfrSymbolId : public JfrCHeapObj { void clear(); void set_class_unload(bool class_unload); - traceid markPackage(const char* name, uintptr_t hash); + traceid markPackage(const char* name, uintptr_t hash, bool leakp); template void iterate_packages(T& functor) { @@ -318,7 +318,7 @@ class JfrArtifactSet : public JfrCHeapObj { traceid mark_unsafe_anonymous_klass_name(const Klass* klass, bool leakp); traceid bootstrap_name(bool leakp); - traceid markPackage(const char* const name, uintptr_t hash); + traceid markPackage(const char* const name, uintptr_t hash, bool leakp); const JfrSymbolId::SymbolEntry* map_symbol(const Symbol* symbol) const; const JfrSymbolId::SymbolEntry* map_symbol(uintptr_t hash) const; diff --git a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp index 666f434942b8340a02f22330f9a37cd4d894306f..407e65911999093769a466eb3e8c9ba30958484b 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp @@ -33,7 +33,6 @@ #include "oops/method.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.inline.hpp" -#include "runtime/orderAccess.inline.hpp" #include "runtime/vm_version.hpp" #include "runtime/jniHandles.hpp" #include "runtime/thread.inline.hpp" diff --git a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp index c087048c8022c314df0847a7a50a987c10fcb5ce..5e5a6e67dc4d2463f523fe6d7aa6d0de792bf977 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp @@ -29,7 +29,6 @@ #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp" -#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp" #include "jfr/support/jfrKlassExtension.hpp" #include "oops/arrayKlass.hpp" #include "oops/klass.hpp" @@ -58,7 +57,14 @@ inline traceid JfrTraceId::get(const Thread* t) { inline traceid JfrTraceId::use(const Klass* klass) { assert(klass != NULL, "invariant"); - return set_used_and_get(klass); + if (SHOULD_TAG(klass)) { + SET_USED_THIS_EPOCH(klass); + assert(USED_THIS_EPOCH(klass), "invariant"); + JfrTraceIdEpoch::set_changed_tag_state(); + return get(klass); + } + assert(USED_THIS_EPOCH(klass), "invariant"); + return TRACE_ID(klass); } inline traceid JfrTraceId::use(const Method* method) { @@ -69,10 +75,16 @@ inline traceid JfrTraceId::use(const Method* method) { inline traceid JfrTraceId::use(const Klass* klass, const Method* method) { assert(klass != NULL, "invariant"); assert(method != NULL, "invariant"); - SET_METHOD_FLAG_USED_THIS_EPOCH(method); - - SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass); + if (SHOULD_TAG_KLASS_METHOD(klass)) { + SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass); + } assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant"); + if (METHOD_FLAG_NOT_USED_THIS_EPOCH(method)) { + assert(USED_THIS_EPOCH(klass), "invariant"); + SET_METHOD_FLAG_USED_THIS_EPOCH(method); + JfrTraceIdEpoch::set_changed_tag_state(); + } + assert(METHOD_FLAG_USED_THIS_EPOCH(method), "invariant"); return (METHOD_ID(klass, method)); } diff --git a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp index 80c4b0ac1759a3a95a76c9d84f559bc6c880d297..aa38d30eb1556838916c1cf215b6df2b1c673f1a 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp @@ -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,15 +25,13 @@ #include "precompiled.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp" #include "runtime/safepoint.hpp" -#include "runtime/orderAccess.hpp" // Alternating epochs on each rotation allow for concurrent tagging. -// The regular epoch shift happens only during a safepoint. -// The fence is there only for the emergency dump case which happens outside of safepoint. +// The epoch shift happens only during a safepoint. bool JfrTraceIdEpoch::_epoch_state = false; +bool volatile JfrTraceIdEpoch::_tag_state = false; + void JfrTraceIdEpoch::shift_epoch() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); _epoch_state = !_epoch_state; - if (!SafepointSynchronize::is_at_safepoint()) { - OrderAccess::fence(); - } } diff --git a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp index 1374521d2a423fffb9ac47db42becfd11ebafdac..9ab83d6d2d50f980d37565dc6fcfdaf69b45bde2 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp @@ -25,18 +25,19 @@ #ifndef SHARE_VM_JFR_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP #define SHARE_VM_JFR_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP -#include "memory/allocation.hpp" #include "jfr/utilities/jfrTypes.hpp" +#include "memory/allocation.hpp" +#include "runtime/orderAccess.hpp" -#define USED_BIT 1 -#define METHOD_USED_BIT (USED_BIT << 2) -#define EPOCH_1_SHIFT 0 -#define EPOCH_2_SHIFT 1 -#define USED_EPOCH_1_BIT (USED_BIT << EPOCH_1_SHIFT) -#define USED_EPOCH_2_BIT (USED_BIT << EPOCH_2_SHIFT) -#define METHOD_USED_EPOCH_1_BIT (METHOD_USED_BIT << EPOCH_1_SHIFT) -#define METHOD_USED_EPOCH_2_BIT (METHOD_USED_BIT << EPOCH_2_SHIFT) -#define METHOD_AND_CLASS_IN_USE_BITS (METHOD_USED_BIT | USED_BIT) +#define USED_BIT 1 +#define METHOD_USED_BIT (USED_BIT << 2) +#define EPOCH_1_SHIFT 0 +#define EPOCH_2_SHIFT 1 +#define USED_EPOCH_1_BIT (USED_BIT << EPOCH_1_SHIFT) +#define USED_EPOCH_2_BIT (USED_BIT << EPOCH_2_SHIFT) +#define METHOD_USED_EPOCH_1_BIT (METHOD_USED_BIT << EPOCH_1_SHIFT) +#define METHOD_USED_EPOCH_2_BIT (METHOD_USED_BIT << EPOCH_2_SHIFT) +#define METHOD_AND_CLASS_IN_USE_BITS (METHOD_USED_BIT | USED_BIT) #define METHOD_AND_CLASS_IN_USE_EPOCH_1_BITS (METHOD_AND_CLASS_IN_USE_BITS << EPOCH_1_SHIFT) #define METHOD_AND_CLASS_IN_USE_EPOCH_2_BITS (METHOD_AND_CLASS_IN_USE_BITS << EPOCH_2_SHIFT) @@ -44,6 +45,8 @@ class JfrTraceIdEpoch : AllStatic { friend class JfrCheckpointManager; private: static bool _epoch_state; + static bool volatile _tag_state; + static void shift_epoch(); public: @@ -86,6 +89,20 @@ class JfrTraceIdEpoch : AllStatic { static traceid method_and_class_in_use_prev_epoch_bits() { return _epoch_state ? METHOD_AND_CLASS_IN_USE_EPOCH_1_BITS : METHOD_AND_CLASS_IN_USE_EPOCH_2_BITS; } + + static bool has_changed_tag_state() { + if ((bool)OrderAccess::load_acquire((volatile jubyte*)&_tag_state)) { + OrderAccess::release_store((volatile jubyte*)&_tag_state, (jubyte)false); + return true; + } + return false; + } + + static void set_changed_tag_state() { + if (!(bool)OrderAccess::load_acquire((volatile jubyte*)&_tag_state)) { + OrderAccess::release_store((volatile jubyte*)&_tag_state, (jubyte)true); + } + } }; #endif // SHARE_VM_JFR_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP diff --git a/src/share/vm/jfr/recorder/repository/jfrChunk.cpp b/src/share/vm/jfr/recorder/repository/jfrChunk.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fddeaf9047149381a3dc322dc23873f868b9daa9 --- /dev/null +++ b/src/share/vm/jfr/recorder/repository/jfrChunk.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "jfr/recorder/repository/jfrChunk.hpp" +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "jfr/utilities/jfrTimeConverter.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "runtime/os.hpp" + +static const char* const MAGIC = "FLR"; +static const u2 JFR_VERSION_MAJOR = 2; +static const u2 JFR_VERSION_MINOR = 1; + +// strictly monotone +static jlong nanos_now() { + static jlong last = 0; + const jlong now = os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC; + if (now > last) { + last = now; + } else { + ++last; + } + return last; +} + +static jlong ticks_now() { + return JfrTicks::now(); +} + +JfrChunk::JfrChunk() : + _path(NULL), + _start_ticks(0), + _previous_start_ticks(invalid_time), + _start_nanos(0), + _previous_start_nanos(invalid_time), + _last_update_nanos(0), + _last_checkpoint_offset(0), + _last_metadata_offset(0), + _generation(1) {} + +JfrChunk::~JfrChunk() { + reset(); +} + +void JfrChunk::reset() { + if (_path != NULL) { + JfrCHeapObj::free(_path, strlen(_path) + 1); + _path = NULL; + } + _last_checkpoint_offset = _last_metadata_offset = 0; + _generation = 1; +} + +const char* JfrChunk::magic() const { + return MAGIC; +} + +u2 JfrChunk::major_version() const { + return JFR_VERSION_MAJOR; +} + +u2 JfrChunk::minor_version() const { + return JFR_VERSION_MINOR; +} + +u2 JfrChunk::capabilities() const { + // chunk capabilities, CompressedIntegers etc + static bool compressed_integers = JfrOptionSet::compressed_integers(); + return compressed_integers; +} + +int64_t JfrChunk::cpu_frequency() const { + static const jlong frequency = JfrTime::frequency(); + return frequency; +} + +void JfrChunk::set_last_checkpoint_offset(int64_t offset) { + _last_checkpoint_offset = offset; +} + +int64_t JfrChunk::last_checkpoint_offset() const { + return _last_checkpoint_offset; +} + +int64_t JfrChunk::start_ticks() const { + assert(_start_ticks != 0, "invariant"); + return _start_ticks; +} + +int64_t JfrChunk::start_nanos() const { + assert(_start_nanos != 0, "invariant"); + return _start_nanos; +} + +int64_t JfrChunk::previous_start_ticks() const { + assert(_previous_start_ticks != invalid_time, "invariant"); + return _previous_start_ticks; +} + +int64_t JfrChunk::previous_start_nanos() const { + assert(_previous_start_nanos != invalid_time, "invariant"); + return _previous_start_nanos; +} + +void JfrChunk::update_start_ticks() { + _start_ticks = ticks_now(); +} + +void JfrChunk::update_start_nanos() { + const jlong now = nanos_now(); + assert(now > _start_nanos, "invariant"); + assert(now > _last_update_nanos, "invariant"); + _start_nanos = _last_update_nanos = now; +} + +void JfrChunk::update_current_nanos() { + const jlong now = nanos_now(); + assert(now > _last_update_nanos, "invariant"); + _last_update_nanos = now; +} + +void JfrChunk::save_current_and_update_start_ticks() { + _previous_start_ticks = _start_ticks; + update_start_ticks(); +} + +void JfrChunk::save_current_and_update_start_nanos() { + _previous_start_nanos = _start_nanos; + update_start_nanos(); +} + +void JfrChunk::set_time_stamp() { + save_current_and_update_start_nanos(); + save_current_and_update_start_ticks(); +} + +int64_t JfrChunk::last_chunk_duration() const { + assert(_previous_start_nanos != invalid_time, "invariant"); + return _start_nanos - _previous_start_nanos; +} + +static char* copy_path(const char* path) { + assert(path != NULL, "invariant"); + const size_t path_len = strlen(path); + char* new_path = JfrCHeapObj::new_array(path_len + 1); + strncpy(new_path, path, path_len + 1); + return new_path; +} + +void JfrChunk::set_path(const char* path) { + if (_path != NULL) { + JfrCHeapObj::free(_path, strlen(_path) + 1); + _path = NULL; + } + if (path != NULL) { + _path = copy_path(path); + } +} + +const char* JfrChunk::path() const { + return _path; +} + +bool JfrChunk::is_started() const { + return _start_nanos != 0; +} + +bool JfrChunk::is_finished() const { + return 0 == _generation; +} + +int64_t JfrChunk::duration() const { + assert(_last_update_nanos >= _start_nanos, "invariant"); + return _last_update_nanos - _start_nanos; +} + +int64_t JfrChunk::last_metadata_offset() const { + return _last_metadata_offset; +} + +void JfrChunk::set_last_metadata_offset(int64_t offset) { + assert(offset > _last_metadata_offset, "invariant"); + _last_metadata_offset = offset; +} + +bool JfrChunk::has_metadata() const { + return 0 != _last_metadata_offset; +} + +u1 JfrChunk::generation() const { + assert(_generation > 0, "invariant"); + const u1 this_generation = _generation++; + if (GUARD == _generation) { + _generation = 1; + } + return this_generation; +} + +u1 JfrChunk::next_generation() const { + assert(_generation > 0, "invariant"); + const u1 next_gen = _generation; + return GUARD == next_gen ? 1 : next_gen; +} diff --git a/src/share/vm/jfr/recorder/repository/jfrChunkState.hpp b/src/share/vm/jfr/recorder/repository/jfrChunk.hpp similarity index 53% rename from src/share/vm/jfr/recorder/repository/jfrChunkState.hpp rename to src/share/vm/jfr/recorder/repository/jfrChunk.hpp index 0d3dfa1bd557cd125652a98be66034763817eeae..26073b9aa3bdb1e4f2eaa50039372e6d25f9e47d 100644 --- a/src/share/vm/jfr/recorder/repository/jfrChunkState.hpp +++ b/src/share/vm/jfr/recorder/repository/jfrChunk.hpp @@ -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 @@ -22,39 +22,70 @@ * */ -#ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNKSTATE_HPP -#define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNKSTATE_HPP +#ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP +#define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP -#include "jni.h" #include "jfr/utilities/jfrAllocation.hpp" -#include "jfr/utilities/jfrTypes.hpp" -class JfrChunkState : public JfrCHeapObj { +const u1 COMPLETE = 0; +const u1 GUARD = 0xff; +const u1 PAD = 0; + +class JfrChunk : public JfrCHeapObj { friend class JfrChunkWriter; + friend class JfrChunkHeadWriter; private: char* _path; - jlong _start_ticks; - jlong _start_nanos; - jlong _previous_start_ticks; - jlong _previous_start_nanos; + int64_t _start_ticks; + int64_t _previous_start_ticks; + int64_t _start_nanos; + int64_t _previous_start_nanos; + int64_t _last_update_nanos; int64_t _last_checkpoint_offset; + int64_t _last_metadata_offset; + mutable u1 _generation; + + JfrChunk(); + ~JfrChunk(); + void reset(); + + const char* magic() const; + u2 major_version() const; + u2 minor_version() const; + int64_t cpu_frequency() const; + u2 capabilities() const; void update_start_ticks(); void update_start_nanos(); void save_current_and_update_start_ticks(); void save_current_and_update_start_nanos(); - JfrChunkState(); - ~JfrChunkState(); - void reset(); int64_t last_checkpoint_offset() const; void set_last_checkpoint_offset(int64_t offset); - jlong previous_start_ticks() const; - jlong previous_start_nanos() const; - jlong last_chunk_duration() const; - void update_time_to_now(); + + int64_t last_metadata_offset() const; + void set_last_metadata_offset(int64_t offset); + bool has_metadata() const; + + int64_t start_ticks() const; + int64_t start_nanos() const; + + int64_t previous_start_ticks() const; + int64_t previous_start_nanos() const; + int64_t last_chunk_duration() const; + + void set_time_stamp(); + void update_current_nanos(); + void set_path(const char* path); const char* path() const; + + bool is_started() const; + bool is_finished() const; + + int64_t duration() const; + u1 generation() const; + u1 next_generation() const; }; -#endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNKSTATE_HPP +#endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP diff --git a/src/share/vm/jfr/recorder/repository/jfrChunkState.cpp b/src/share/vm/jfr/recorder/repository/jfrChunkState.cpp deleted file mode 100644 index f48716d33f0d7727d523d5e46849e30f074088d1..0000000000000000000000000000000000000000 --- a/src/share/vm/jfr/recorder/repository/jfrChunkState.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "jfr/dcmd/jfrDcmds.hpp" -#include "jfr/recorder/jfrRecorder.hpp" -#include "jfr/recorder/repository/jfrChunkState.hpp" -#include "jfr/recorder/repository/jfrChunkWriter.hpp" -#include "jfr/utilities/jfrTime.hpp" -#include "jfr/utilities/jfrTimeConverter.hpp" -#include "runtime/os.hpp" -#include "runtime/thread.inline.hpp" - -JfrChunkState::JfrChunkState() : - _path(NULL), - _start_ticks(0), - _start_nanos(0), - _previous_start_ticks(0), - _previous_start_nanos(0), - _last_checkpoint_offset(0) {} - -JfrChunkState::~JfrChunkState() { - reset(); -} - -void JfrChunkState::reset() { - if (_path != NULL) { - JfrCHeapObj::free(_path, strlen(_path) + 1); - _path = NULL; - } - set_last_checkpoint_offset(0); -} - -void JfrChunkState::set_last_checkpoint_offset(int64_t offset) { - _last_checkpoint_offset = offset; -} - -int64_t JfrChunkState::last_checkpoint_offset() const { - return _last_checkpoint_offset; -} - -jlong JfrChunkState::previous_start_ticks() const { - return _previous_start_ticks; -} - -jlong JfrChunkState::previous_start_nanos() const { - return _previous_start_nanos; -} - -void JfrChunkState::update_start_ticks() { - _start_ticks = JfrTicks::now(); -} - -void JfrChunkState::update_start_nanos() { - _start_nanos = (jlong)(os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC); -} - -void JfrChunkState::save_current_and_update_start_ticks() { - _previous_start_ticks = _start_ticks; - update_start_ticks(); -} - -void JfrChunkState::save_current_and_update_start_nanos() { - _previous_start_nanos = _start_nanos; - update_start_nanos(); -} - -void JfrChunkState::update_time_to_now() { - save_current_and_update_start_nanos(); - save_current_and_update_start_ticks(); -} - -jlong JfrChunkState::last_chunk_duration() const { - return _start_nanos - _previous_start_nanos; -} - -static char* copy_path(const char* path) { - assert(path != NULL, "invariant"); - const size_t path_len = strlen(path); - char* new_path = JfrCHeapObj::new_array(path_len + 1); - strncpy(new_path, path, path_len + 1); - return new_path; -} - -void JfrChunkState::set_path(const char* path) { - assert(JfrStream_lock->owned_by_self(), "invariant"); - if (_path != NULL) { - JfrCHeapObj::free(_path, strlen(_path) + 1); - _path = NULL; - } - if (path != NULL) { - _path = copy_path(path); - } -} - -const char* JfrChunkState::path() const { - return _path; -} diff --git a/src/share/vm/jfr/recorder/repository/jfrChunkWriter.cpp b/src/share/vm/jfr/recorder/repository/jfrChunkWriter.cpp index 928404d0e0d9d40993b529a4656f6acd0b000de9..9f10a7211e9e124d114a8177fef02cef5f06493d 100644 --- a/src/share/vm/jfr/recorder/repository/jfrChunkWriter.cpp +++ b/src/share/vm/jfr/recorder/repository/jfrChunkWriter.cpp @@ -23,83 +23,219 @@ */ #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/jfrTypes.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" -#include "runtime/os.hpp" - -const u2 JFR_VERSION_MAJOR = 2; -const u2 JFR_VERSION_MINOR = 0; - -static const size_t MAGIC_LEN = 4; -static const size_t FILEHEADER_SLOT_SIZE = 8; -static const size_t CHUNK_SIZE_OFFSET = 8; - -JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunkstate(NULL) {} -bool JfrChunkWriter::initialize() { - 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 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()); - // chunk capabilities, CompressedIntegers etc - this->be_write((u4)JfrOptionSet::compressed_integers() ? 1 : 0); - _chunkstate->reset(); +#ifdef ASSERT +static void assert_writer_position(JfrChunkWriter* writer, int64_t offset) { + assert(writer != NULL, "invariant"); + assert(offset == writer->current_offset(), "invariant"); +} +#endif + +class JfrChunkHeadWriter : public StackObj { + private: + JfrChunkWriter* _writer; + JfrChunk* _chunk; + public: + void write_magic() { + _writer->bytes(_chunk->magic(), MAGIC_LEN); } - return is_open; + + void write_version() { + _writer->be_write(_chunk->major_version()); + _writer->be_write(_chunk->minor_version()); + } + + void write_size(int64_t size) { + _writer->be_write(size); + } + + void write_checkpoint() { + _writer->be_write(_chunk->last_checkpoint_offset()); + } + + void write_metadata() { + _writer->be_write(_chunk->last_metadata_offset()); + } + + void write_time(bool finalize) { + if (finalize) { + _writer->be_write(_chunk->previous_start_nanos()); + _writer->be_write(_chunk->last_chunk_duration()); + _writer->be_write(_chunk->previous_start_ticks()); + return; + } + _writer->be_write(_chunk->start_nanos()); + _writer->be_write(_chunk->duration()); + _writer->be_write(_chunk->start_ticks()); + } + + void write_cpu_frequency() { + _writer->be_write(_chunk->cpu_frequency()); + } + + void write_generation(bool finalize) { + _writer->be_write(finalize ? COMPLETE : _chunk->generation()); + _writer->be_write(PAD); + } + + void write_next_generation() { + _writer->be_write(_chunk->next_generation()); + _writer->be_write(PAD); + } + + void write_guard() { + _writer->be_write(GUARD); + _writer->be_write(PAD); + } + + void write_guard_flush() { + write_guard(); + _writer->flush(); + } + + void write_capabilities() { + _writer->be_write(_chunk->capabilities()); + } + + void write_size_to_generation(int64_t size, bool finalize) { + write_size(size); + write_checkpoint(); + write_metadata(); + write_time(finalize); + write_cpu_frequency(); + write_generation(finalize); + } + + void flush(int64_t size, bool finalize) { + assert(_writer->is_valid(), "invariant"); + assert(_chunk != NULL, "invariant"); + DEBUG_ONLY(assert_writer_position(_writer, SIZE_OFFSET);) + write_size_to_generation(size, finalize); + // no need to write capabilities + _writer->seek(size); // implicit flush + } + + void initialize() { + assert(_writer->is_valid(), "invariant"); + assert(_chunk != NULL, "invariant"); + DEBUG_ONLY(assert_writer_position(_writer, 0);) + write_magic(); + write_version(); + write_size_to_generation(HEADER_SIZE, false); + write_capabilities(); + DEBUG_ONLY(assert_writer_position(_writer, HEADER_SIZE);) + _writer->flush(); + } + + JfrChunkHeadWriter(JfrChunkWriter* writer, int64_t offset, bool guard = true) : _writer(writer), _chunk(writer->_chunk) { + assert(_writer != NULL, "invariant"); + assert(_writer->is_valid(), "invariant"); + assert(_chunk != NULL, "invariant"); + if (0 == _writer->current_offset()) { + assert(HEADER_SIZE == offset, "invariant"); + initialize(); + } else { + if (guard) { + _writer->seek(GENERATION_OFFSET); + write_guard(); + _writer->seek(offset); + } else { + _chunk->update_current_nanos(); + } + } + DEBUG_ONLY(assert_writer_position(_writer, offset);) + } +}; + +static int64_t prepare_chunk_header_constant_pool(JfrChunkWriter& cw, int64_t event_offset, bool flushpoint) { + const int64_t delta = cw.last_checkpoint_offset() == 0 ? 0 : cw.last_checkpoint_offset() - event_offset; + const u4 checkpoint_type = flushpoint ? (u4)(FLUSH | HEADER) : (u4)HEADER; + cw.reserve(sizeof(u4)); + cw.write(EVENT_CHECKPOINT); + cw.write(JfrTicks::now().value()); + cw.write(0); // duration + cw.write(delta); // to previous checkpoint + cw.write(checkpoint_type); + cw.write(1); // pool count + cw.write(TYPE_CHUNKHEADER); + cw.write(1); // count + cw.write(1); // key + cw.write(HEADER_SIZE); // length of byte array + return cw.current_offset(); } -size_t JfrChunkWriter::close(intptr_t metadata_offset) { - write_header(metadata_offset); - this->flush(); - this->close_fd(); - return size_written(); +int64_t JfrChunkWriter::write_chunk_header_checkpoint(bool flushpoint) { + assert(this->has_valid_fd(), "invariant"); + const int64_t event_size_offset = current_offset(); + const int64_t header_content_pos = prepare_chunk_header_constant_pool(*this, event_size_offset, flushpoint); + JfrChunkHeadWriter head(this, header_content_pos, false); + head.write_magic(); + head.write_version(); + const int64_t chunk_size_offset = reserve(sizeof(int64_t)); // size to be decided when we are done + be_write(event_size_offset); // last checkpoint offset will be this checkpoint + head.write_metadata(); + head.write_time(false); + head.write_cpu_frequency(); + head.write_next_generation(); + head.write_capabilities(); + assert(current_offset() - header_content_pos == HEADER_SIZE, "invariant"); + const u4 checkpoint_size = current_offset() - event_size_offset; + write_padded_at_offset(checkpoint_size, event_size_offset); + set_last_checkpoint_offset(event_size_offset); + const size_t sz_written = size_written(); + write_be_at_offset(sz_written, chunk_size_offset); + return sz_written; } -void JfrChunkWriter::write_header(intptr_t metadata_offset) { - assert(this->is_valid(), "invariant"); - // Chunk size - this->write_be_at_offset((jlong)size_written(), CHUNK_SIZE_OFFSET); - // initial checkpoint event offset - this->write_be_at_offset(_chunkstate->last_checkpoint_offset(), CHUNK_SIZE_OFFSET + (1 * FILEHEADER_SLOT_SIZE)); - // metadata event offset - this->write_be_at_offset((jlong)metadata_offset, CHUNK_SIZE_OFFSET + (2 * FILEHEADER_SLOT_SIZE)); - // start of chunk in nanos since epoch - this->write_be_at_offset(_chunkstate->previous_start_nanos(), CHUNK_SIZE_OFFSET + (3 * FILEHEADER_SLOT_SIZE)); - // duration of chunk in nanos - this->write_be_at_offset(_chunkstate->last_chunk_duration(), CHUNK_SIZE_OFFSET + (4 * FILEHEADER_SLOT_SIZE)); - // start of chunk in ticks - this->write_be_at_offset(_chunkstate->previous_start_ticks(), CHUNK_SIZE_OFFSET + (5 * FILEHEADER_SLOT_SIZE)); +int64_t JfrChunkWriter::flush_chunk(bool flushpoint) { + assert(_chunk != NULL, "invariant"); + const int64_t sz_written = write_chunk_header_checkpoint(flushpoint); + assert(size_written() == sz_written, "invariant"); + JfrChunkHeadWriter head(this, SIZE_OFFSET); + head.flush(sz_written, !flushpoint); + return sz_written; } -void JfrChunkWriter::set_chunk_path(const char* chunk_path) { - _chunkstate->set_path(chunk_path); +JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunk(new JfrChunk()) {} + +JfrChunkWriter::~JfrChunkWriter() { + assert(_chunk != NULL, "invariant"); + delete _chunk; +} + +void JfrChunkWriter::set_path(const char* path) { + assert(_chunk != NULL, "invariant"); + _chunk->set_path(path); +} + +void JfrChunkWriter::set_time_stamp() { + assert(_chunk != NULL, "invariant"); + _chunk->set_time_stamp(); } intptr_t JfrChunkWriter::size_written() const { @@ -107,13 +243,46 @@ intptr_t JfrChunkWriter::size_written() const { } int64_t JfrChunkWriter::last_checkpoint_offset() const { - return _chunkstate->last_checkpoint_offset(); + assert(_chunk != NULL, "invariant"); + return _chunk->last_checkpoint_offset(); +} + +int64_t JfrChunkWriter::current_chunk_start_nanos() const { + assert(_chunk != NULL, "invariant"); + return this->is_valid() ? _chunk->start_nanos() : invalid_time; } void JfrChunkWriter::set_last_checkpoint_offset(int64_t offset) { - _chunkstate->set_last_checkpoint_offset(offset); + assert(_chunk != NULL, "invariant"); + _chunk->set_last_checkpoint_offset(offset); +} + +void JfrChunkWriter::set_last_metadata_offset(int64_t offset) { + assert(_chunk != NULL, "invariant"); + _chunk->set_last_metadata_offset(offset); +} + +bool JfrChunkWriter::has_metadata() const { + assert(_chunk != NULL, "invariant"); + return _chunk->has_metadata(); } -void JfrChunkWriter::time_stamp_chunk_now() { - _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 = flush_chunk(false); + this->close_fd(); + assert(!this->is_valid(), "invariant"); + return size_written; } diff --git a/src/share/vm/jfr/recorder/repository/jfrChunkWriter.hpp b/src/share/vm/jfr/recorder/repository/jfrChunkWriter.hpp index c26a17143d5759accf86274d8681c2026fb645ed..707f995b7afcebde33f1ddef7ff1a56690b250a9 100644 --- a/src/share/vm/jfr/recorder/repository/jfrChunkWriter.hpp +++ b/src/share/vm/jfr/recorder/repository/jfrChunkWriter.hpp @@ -29,29 +29,36 @@ #include "jfr/writers/jfrStreamWriterHost.inline.hpp" #include "jfr/writers/jfrWriterHost.inline.hpp" -typedef MallocAdapter JfrStreamBuffer; // 1 mb buffered writes -typedef StreamWriterHost JfrBufferedStreamWriter; -typedef WriterHost JfrChunkWriterBase; +typedef MallocAdapter JfrChunkBuffer; // 1 mb buffered writes +typedef StreamWriterHost JfrBufferedChunkWriter; +typedef WriterHost 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 flush_chunk(bool flushpoint); bool open(); - size_t close(intptr_t metadata_offset); - void write_header(intptr_t metadata_offset); - void set_chunk_path(const char* chunk_path); + int64_t close(); + int64_t current_chunk_start_nanos() const; + int64_t write_chunk_header_checkpoint(bool flushpoint); public: JfrChunkWriter(); - bool initialize(); + ~JfrChunkWriter(); + intptr_t size_written() const; int64_t last_checkpoint_offset() const; void set_last_checkpoint_offset(int64_t offset); - void time_stamp_chunk_now(); + void set_last_metadata_offset(int64_t offset); + + bool has_metadata() const; + void set_time_stamp(); }; #endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRCHUNKWRITER_HPP diff --git a/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.cpp index dd5ba88e123ae625e51932ee1b1a39ef1087e7d7..d76c66f268d7a1d7a3dd64b4b01575fcd4178f1a 100644 --- a/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.cpp @@ -246,7 +246,6 @@ static void write_emergency_file(fio_fd emergency_fd, const RepositoryIterator& } 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; @@ -288,7 +287,6 @@ static const char* create_emergency_dump_path() { // 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 }; @@ -304,12 +302,11 @@ static const char* create_emergency_chunk_path(const char* repository_path) { return NULL; } // append the individual substrings - jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_path_len, os::file_separator(), date_time_buffer, chunk_file_jfr_ext); + jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_path, os::file_separator(), date_time_buffer, chunk_file_jfr_ext); return chunk_path; } 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; @@ -322,7 +319,6 @@ const char* JfrEmergencyDump::build_dump_path(const char* repository_path) { void JfrEmergencyDump::on_vm_error(const char* repository_path) { assert(repository_path != NULL, "invariant"); ResourceMark rm; - MutexLockerEx 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)); @@ -337,17 +333,25 @@ void JfrEmergencyDump::on_vm_error(const char* repository_path) { * * 1. if the thread state is not "_thread_in_vm", we will quick transition * it to "_thread_in_vm". -* 2. the nesting state for both resource and handle areas are unknown, -* so we allocate new fresh arenas, discarding the old ones. -* 3. if the thread is the owner of some critical lock(s), unlock them. +* 2. if the thread is the owner of some critical lock(s), unlock them. * * If we end up deadlocking in the attempt of dumping out jfr data, * we rely on the WatcherThread task "is_error_reported()", -* to exit the VM after a hard-coded timeout. +* to exit the VM after a hard-coded timeout (disallow WatcherThread to emergency dump). * This "safety net" somewhat explains the aggressiveness in this attempt. * */ -static void prepare_for_emergency_dump(Thread* thread) { +static bool prepare_for_emergency_dump() { + if (JfrStream_lock->owned_by_self()) { + // crashed during jfr rotation, disallow recursion + return false; + } + Thread* const thread = Thread::current(); + if (thread->is_Watcher_thread()) { + // need WatcherThread as a safeguard against potential deadlocks + return false; + } + if (thread->is_Java_thread()) { ((JavaThread*)thread)->set_thread_state(_thread_in_vm); } @@ -386,7 +390,6 @@ static void prepare_for_emergency_dump(Thread* thread) { VMOperationRequest_lock->unlock(); } - if (Service_lock->owned_by_self()) { Service_lock->unlock(); } @@ -407,13 +410,10 @@ static void prepare_for_emergency_dump(Thread* thread) { JfrBuffer_lock->unlock(); } - if (JfrStream_lock->owned_by_self()) { - JfrStream_lock->unlock(); - } - if (JfrStacktrace_lock->owned_by_self()) { JfrStacktrace_lock->unlock(); } + return true; } static volatile int jfr_shutdown_lock = 0; @@ -423,24 +423,9 @@ static bool guard_reentrancy() { } void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) { - if (!guard_reentrancy()) { + if (!(guard_reentrancy() && prepare_for_emergency_dump())) { return; } - // function made non-reentrant - Thread* thread = Thread::current(); - if (exception_handler) { - // we are crashing - if (thread->is_Watcher_thread()) { - // The Watcher thread runs the periodic thread sampling task. - // If it has crashed, it is likely that another thread is - // left in a suspended state. This would mean the system - // will not be able to ever move to a safepoint. We try - // to avoid issuing safepoint operations when attempting - // an emergency dump, but a safepoint might be already pending. - return; - } - prepare_for_emergency_dump(thread); - } EventDumpReason event; if (event.should_commit()) { event.set_reason(exception_handler ? "Crash" : "Out of Memory"); @@ -452,8 +437,6 @@ void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) { LeakProfiler::emit_events(max_jlong, false); } const int messages = MSGBIT(MSG_VM_ERROR); - ResourceMark rm(thread); - HandleMark hm(thread); JfrRecorderService service; service.rotate(messages); } diff --git a/src/share/vm/jfr/recorder/repository/jfrRepository.cpp b/src/share/vm/jfr/recorder/repository/jfrRepository.cpp index cf0022f4573cef536213c719eddcdde49f892c73..ebb1486b426991cfec90764e2be8893da5f968dc 100644 --- a/src/share/vm/jfr/recorder/repository/jfrRepository.cpp +++ b/src/share/vm/jfr/recorder/repository/jfrRepository.cpp @@ -26,7 +26,6 @@ #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" @@ -44,11 +43,6 @@ JfrRepository& JfrRepository::instance() { 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; @@ -57,7 +51,9 @@ JfrChunkWriter& JfrRepository::chunkwriter() { 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() { @@ -85,7 +81,6 @@ void JfrRepository::destroy() { } void JfrRepository::on_vm_error() { - assert(!JfrStream_lock->owned_by_self(), "invariant"); if (_path == NULL) { // completed already return; @@ -108,17 +103,21 @@ bool JfrRepository::set_path(const char* path) { 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::set_chunk_path(const char* path) { + chunkwriter().set_path(path); +} + +jlong JfrRepository::current_chunk_start_nanos() { + return chunkwriter().current_chunk_start_nanos(); +} + /** * Sets the file where data should be written. * @@ -135,14 +134,11 @@ void JfrRepository::set_chunk_path(jstring path, JavaThread* jt) { DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); ResourceMark rm(jt); const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt); - { - MutexLockerEx 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(); } @@ -156,14 +152,28 @@ void JfrRepository::set_path(jstring location, JavaThread* jt) { } 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(jlong metadata_offset) { - return _chunkwriter->close(metadata_offset); +size_t JfrRepository::close_chunk() { + return _chunkwriter->close(); +} + +void JfrRepository::flush(JavaThread* jt) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); + if (!Jfr::is_recording()) { + return; + } + if (!_chunkwriter->is_valid()) { + return; + } + instance()._post_box.post(MSG_FLUSHPOINT); +} + +size_t JfrRepository::flush_chunk() { + return _chunkwriter->flush_chunk(true); } diff --git a/src/share/vm/jfr/recorder/repository/jfrRepository.hpp b/src/share/vm/jfr/recorder/repository/jfrRepository.hpp index 6b98df4f246631bbdf6b6a524b18a1ceadcc7af5..8c1b0edb23062d0dc457b9648c1a8b10abb411f9 100644 --- a/src/share/vm/jfr/recorder/repository/jfrRepository.hpp +++ b/src/share/vm/jfr/recorder/repository/jfrRepository.hpp @@ -55,8 +55,10 @@ class JfrRepository : public JfrCHeapObj { bool set_path(const char* path); void set_chunk_path(const char* path); bool open_chunk(bool vm_error = false); - size_t close_chunk(jlong metadata_offset); + size_t close_chunk(); + size_t flush_chunk(); void on_vm_error(); + static void notify_on_new_chunk_path(); static JfrChunkWriter& chunkwriter(); @@ -68,6 +70,8 @@ class JfrRepository : public JfrCHeapObj { public: static void set_path(jstring location, JavaThread* jt); static void set_chunk_path(jstring path, JavaThread* jt); + static void flush(JavaThread* jt); + static jlong current_chunk_start_nanos(); }; #endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRREPOSITORY_HPP diff --git a/src/share/vm/jfr/recorder/service/jfrPostBox.cpp b/src/share/vm/jfr/recorder/service/jfrPostBox.cpp index 88ec660588d19ef9ff8ad67142fbbe1e4050d621..cab3db4721550fb71210187ce99037f67f63823b 100644 --- a/src/share/vm/jfr/recorder/service/jfrPostBox.cpp +++ b/src/share/vm/jfr/recorder/service/jfrPostBox.cpp @@ -33,7 +33,8 @@ (MSGBIT(MSG_STOP)) | \ (MSGBIT(MSG_START)) | \ (MSGBIT(MSG_CLONE_IN_MEMORY)) | \ - (MSGBIT(MSG_VM_ERROR)) \ + (MSGBIT(MSG_VM_ERROR)) | \ + (MSGBIT(MSG_FLUSHPOINT)) \ ) static JfrPostBox* _instance = NULL; diff --git a/src/share/vm/jfr/recorder/service/jfrPostBox.hpp b/src/share/vm/jfr/recorder/service/jfrPostBox.hpp index e17cd220465cf481f59b1b54c87743f1d92b04ba..6f30dbfca6a081bff02a90ef854726e7716ced30 100644 --- a/src/share/vm/jfr/recorder/service/jfrPostBox.hpp +++ b/src/share/vm/jfr/recorder/service/jfrPostBox.hpp @@ -41,6 +41,7 @@ enum JFR_Msg { MSG_SHUTDOWN, MSG_VM_ERROR, MSG_DEADBUFFER, + MSG_FLUSHPOINT, MSG_NO_OF_MSGS }; @@ -53,15 +54,16 @@ enum JFR_Msg { * MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2 * MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4 * MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8 - * MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 8) == 0x100 + * MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100 + * MSG_FLUSHPOINT (10) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0xa) == 0x400 * * Asynchronous messages (posting thread returns immediately upon deposit): * * MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10 - * MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 5) == 0x20 - * MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 6) == 0x40 - * MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 7) == 0x80 - * MSG_DEADBUFFER (9) ; MSGBIT(MSG_DEADBUFFER) == (1 << 9) == 0x200 + * MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20 + * MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40 + * MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80 + * MSG_DEADBUFFER (9) ; MSGBIT(MSG_DEADBUFFER) == (1 << 0x9) == 0x200 */ class JfrPostBox : public JfrCHeapObj { diff --git a/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp b/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp index ce5ae9677682a6b1c0c023941079f2486e967539..63189a5267c43ccceda5b5beda82b1d1f900d92c 100644 --- a/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "jfrfiles/jfrEventClasses.hpp" #include "jfr/jni/jfrJavaSupport.hpp" #include "jfr/leakprofiler/leakProfiler.hpp" #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" @@ -54,148 +55,281 @@ #include "runtime/vm_operations.hpp" #include "runtime/vmThread.hpp" -// set data iff *dest == NULL -static bool try_set(void* const data, void** dest, bool clear) { - assert(data != NULL, "invariant"); - void* const current = OrderAccess::load_ptr_acquire(dest); - if (current != NULL) { - if (current != data) { - // already set - return false; - } - assert(current == data, "invariant"); - if (!clear) { - // recursion disallowed - return false; - } - } - return Atomic::cmpxchg_ptr(clear ? NULL : data, dest, current) == current; -} - -static void* rotation_thread = NULL; -static const int rotation_try_limit = 1000; -static const int rotation_retry_sleep_millis = 10; +// incremented on each flushpoint +static u8 flushpoint_id = 0; -class RotationLock : public StackObj { +template +class Content { private: - Thread* const _thread; - bool _acquired; - - void log(bool recursion) { - assert(!_acquired, "invariant"); - const char* error_msg = NULL; - if (recursion) { - error_msg = "Unable to issue rotation due to recursive calls."; - } - else { - error_msg = "Unable to issue rotation due to wait timeout."; - } - if (LogJFR) tty->print_cr( // For user, should not be "jfr, system" - "%s", error_msg); + Instance& _instance; + u4 _elements; + public: + typedef E EventType; + Content(Instance& instance) : _instance(instance), _elements(0) {} + bool process() { + _elements = (u4)(_instance.*func)(); + return true; } + u4 elements() const { return _elements; } +}; + +template +class WriteContent : public StackObj { + protected: + const JfrTicks _start_time; + JfrTicks _end_time; + JfrChunkWriter& _cw; + Content& _content; + const int64_t _start_offset; public: - RotationLock(Thread* thread) : _thread(thread), _acquired(false) { - assert(_thread != NULL, "invariant"); - if (_thread == rotation_thread) { - // recursion not supported - log(true); - return; - } + typedef typename Content::EventType EventType; - // limited to not spin indefinitely - for (int i = 0; i < rotation_try_limit; ++i) { - if (try_set(_thread, &rotation_thread, false)) { - _acquired = true; - assert(_thread == rotation_thread, "invariant"); - return; - } - if (_thread->is_Java_thread()) { - // in order to allow the system to move to a safepoint - MutexLockerEx msg_lock(JfrMsg_lock); - JfrMsg_lock->wait(false, rotation_retry_sleep_millis); - } - else { - os::naked_short_sleep(rotation_retry_sleep_millis); - } - } - log(false); + WriteContent(JfrChunkWriter& cw, Content& content) : + _start_time(JfrTicks::now()), + _end_time(), + _cw(cw), + _content(content), + _start_offset(_cw.current_offset()) { + assert(_cw.is_valid(), "invariant"); } - ~RotationLock() { - assert(_thread != NULL, "invariant"); - if (_acquired) { - assert(_thread == rotation_thread, "invariant"); - while (!try_set(_thread, &rotation_thread, true)); - } + bool process() { + // invocation + _content.process(); + _end_time = JfrTicks::now(); + return 0 != _content.elements(); + } + + const JfrTicks& start_time() const { + return _start_time; + } + + const JfrTicks& end_time() const { + return _end_time; + } + + int64_t start_offset() const { + return _start_offset; + } + + int64_t end_offset() const { + return current_offset(); + } + + int64_t current_offset() const { + return _cw.current_offset(); + } + + u4 elements() const { + return (u4) _content.elements(); + } + + u4 size() const { + return (u4)(end_offset() - start_offset()); + } + + static bool is_event_enabled() { + return EventType::is_enabled(); + } + + static u8 event_id() { + return EventType::eventId; + } + + void write_elements(int64_t offset) { + _cw.write_padded_at_offset(elements(), offset); + } + + void write_size() { + _cw.write_padded_at_offset(size(), start_offset()); + } + + void set_last_checkpoint() { + _cw.set_last_checkpoint_offset(start_offset()); + } + + void rewind() { + _cw.seek(start_offset()); } - bool not_acquired() const { return !_acquired; } }; + static intptr_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) { const int64_t last_cp_offset = cw.last_checkpoint_offset(); const int64_t delta_to_last_checkpoint = 0 == last_cp_offset ? 0 : last_cp_offset - cw.current_offset(); cw.reserve(sizeof(u4)); cw.write(EVENT_CHECKPOINT); cw.write(JfrTicks::now()); - cw.write((int64_t)0); // duration + cw.write(0); // duration cw.write(delta_to_last_checkpoint); - cw.write(false); // flushpoint - cw.write((u4)1); // nof types in this checkpoint + cw.write(GENERIC); // checkpoint type + cw.write(1); // nof types in this checkpoint cw.write(type_id); - const intptr_t number_of_elements_offset = cw.current_offset(); - cw.reserve(sizeof(u4)); - return number_of_elements_offset; + return cw.reserve(sizeof(u4)); } -template -class WriteCheckpointEvent : public StackObj { +template +class WriteCheckpointEvent : public WriteContent { 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"); - } + WriteCheckpointEvent(JfrChunkWriter& cw, Content& content, u8 type_id) : + WriteContent(cw, content), _type_id(type_id) {} + bool process() { - // current_cp_offset is also offset for the event size header field - const intptr_t current_cp_offset = _cw.current_offset(); - const intptr_t num_elements_offset = write_checkpoint_event_prologue(_cw, _type_id); - // invocation - _content_functor.process(); - const u4 number_of_elements = (u4)_content_functor.processed(); - if (number_of_elements == 0) { + const int64_t num_elements_offset = write_checkpoint_event_prologue(this->_cw, _type_id); + if (!WriteContent::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(number_of_elements, num_elements_offset); - _cw.write_padded_at_offset((u4)_cw.current_offset() - current_cp_offset, current_cp_offset); - // update writer with last checkpoint position - _cw.set_last_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 -class ServiceFunctor { +template +static u4 invoke(Functor& f) { + f.process(); + return f.elements(); +} + +template +static void write_flush_event(Functor& f) { + if (Functor::is_event_enabled()) { + typename Functor::EventType e(UNTIMED); + e.set_starttime(f.start_time()); + e.set_endtime(f.end_time()); + e.set_flushId(flushpoint_id); + e.set_elements(f.elements()); + e.set_size(f.size()); + e.commit(); + } +} + +template +static u4 invoke_with_flush_event(Functor& f) { + const u4 elements = invoke(f); + write_flush_event(f); + return elements; +} + +class StackTraceRepository : public StackObj { private: - Instance& _instance; - size_t _processed; + JfrStackTraceRepository& _repo; + JfrChunkWriter& _cw; + size_t _elements; + bool _clear; + + public: + typedef EventFlushStacktrace EventType; + StackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) : + _repo(repo), _cw(cw), _elements(0), _clear(clear) {} + bool process() { + _elements = _repo.write(_cw, _clear); + return true; + } + size_t elements() const { return _elements; } + void reset() { _elements = 0; } +}; + +typedef WriteCheckpointEvent WriteStackTrace; + +static u4 flush_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter) { + StackTraceRepository str(stack_trace_repo, chunkwriter, false); + WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE); + return invoke_with_flush_event(wst); +} + +static u4 write_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) { + StackTraceRepository str(stack_trace_repo, chunkwriter, clear); + WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE); + return invoke(wst); +} + +typedef Content Storage; +typedef WriteContent WriteStorage; + +static size_t flush_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) { + assert(chunkwriter.is_valid(), "invariant"); + Storage fsf(storage); + WriteStorage fs(chunkwriter, fsf); + return invoke_with_flush_event(fs); +} + +static size_t write_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) { + assert(chunkwriter.is_valid(), "invariant"); + Storage fsf(storage); + WriteStorage fs(chunkwriter, fsf); + return invoke(fs); +} + +typedef Content StringPool; +typedef Content StringPoolSafepoint; +typedef WriteCheckpointEvent WriteStringPool; +typedef WriteCheckpointEvent WriteStringPoolSafepoint; + +static u4 flush_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { + StringPool sp(string_pool); + WriteStringPool wsp(chunkwriter, sp, TYPE_STRING); + return invoke_with_flush_event(wsp); +} + +static u4 write_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { + StringPool sp(string_pool); + WriteStringPool wsp(chunkwriter, sp, TYPE_STRING); + return invoke(wsp); +} + +static u4 write_stringpool_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { + StringPoolSafepoint sps(string_pool); + WriteStringPoolSafepoint wsps(chunkwriter, sps, TYPE_STRING); + return invoke(wsps); +} + +typedef Content FlushTypeSetFunctor; +typedef WriteContent FlushTypeSet; + +static u4 flush_typeset(JfrCheckpointManager& checkpoint_manager, JfrChunkWriter& chunkwriter) { + FlushTypeSetFunctor flush_type_set(checkpoint_manager); + FlushTypeSet fts(chunkwriter, flush_type_set); + return invoke_with_flush_event(fts); +} + +class MetadataEvent : public StackObj { + private: + JfrChunkWriter& _cw; public: - ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {} + typedef EventFlushMetadata EventType; + MetadataEvent(JfrChunkWriter& cw) : _cw(cw) {} bool process() { - _processed = (_instance.*func)(); + JfrMetadataEvent::write(_cw); return true; } - size_t processed() const { return _processed; } + size_t elements() const { return 1; } }; +typedef WriteContent WriteMetadata; + +static u4 flush_metadata(JfrChunkWriter& chunkwriter) { + assert(chunkwriter.is_valid(), "invariant"); + MetadataEvent me(chunkwriter); + WriteMetadata wm(chunkwriter, me); + return invoke_with_flush_event(wm); +} + +static u4 write_metadata(JfrChunkWriter& chunkwriter) { + assert(chunkwriter.is_valid(), "invariant"); + MetadataEvent me(chunkwriter); + WriteMetadata wm(chunkwriter, me); + return invoke(wm); +} + template class JfrVMOperation : public VM_Operation { private: @@ -207,23 +341,13 @@ class JfrVMOperation : public VM_Operation { Mode evaluation_mode() const { return _safepoint; } // default }; -class WriteStackTraceRepository : public StackObj { - private: - JfrStackTraceRepository& _repo; - JfrChunkWriter& _cw; - size_t _elements_processed; - bool _clear; - - public: - WriteStackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) : - _repo(repo), _cw(cw), _elements_processed(0), _clear(clear) {} - bool process() { - _elements_processed = _repo.write(_cw, _clear); - return true; - } - size_t processed() const { return _elements_processed; } - void reset() { _elements_processed = 0; } -}; +JfrRecorderService::JfrRecorderService() : + _checkpoint_manager(JfrCheckpointManager::instance()), + _chunkwriter(JfrRepository::chunkwriter()), + _repository(JfrRepository::instance()), + _stack_trace_repository(JfrStackTraceRepository::instance()), + _storage(JfrStorage::instance()), + _string_pool(JfrStringPool::instance()) {} static bool recording = false; @@ -236,19 +360,8 @@ bool JfrRecorderService::is_recording() { return recording; } -JfrRecorderService::JfrRecorderService() : - _checkpoint_manager(JfrCheckpointManager::instance()), - _chunkwriter(JfrRepository::chunkwriter()), - _repository(JfrRepository::instance()), - _storage(JfrStorage::instance()), - _stack_trace_repository(JfrStackTraceRepository::instance()), - _string_pool(JfrStringPool::instance()) {} - void JfrRecorderService::start() { - RotationLock rl(Thread::current()); - if (rl.not_acquired()) { - return; - } + MutexLocker lock(JfrStream_lock); if (LogJFR) tty->print_cr("Request to START recording"); assert(!is_recording(), "invariant"); clear(); @@ -267,9 +380,9 @@ void JfrRecorderService::clear() { } void JfrRecorderService::pre_safepoint_clear() { - _stack_trace_repository.clear(); _string_pool.clear(); _storage.clear(); + _stack_trace_repository.clear(); } void JfrRecorderService::invoke_safepoint_clear() { @@ -277,28 +390,28 @@ void JfrRecorderService::invoke_safepoint_clear() { VMThread::execute(&safepoint_task); } -// -// safepoint clear sequence -// -// clear stacktrace repository -> -// clear string pool -> -// clear storage -> -// shift epoch -> -// update time -// void JfrRecorderService::safepoint_clear() { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); - _stack_trace_repository.clear(); _string_pool.clear(); _storage.clear(); _checkpoint_manager.shift_epoch(); - _chunkwriter.time_stamp_chunk_now(); + _chunkwriter.set_time_stamp(); + _stack_trace_repository.clear(); } void JfrRecorderService::post_safepoint_clear() { _checkpoint_manager.clear(); } +void JfrRecorderService::open_new_chunk(bool vm_error) { + JfrChunkRotation::on_rotation(); + const bool valid_chunk = _repository.open_chunk(vm_error); + _storage.control().set_to_disk(valid_chunk); + if (valid_chunk) { + _checkpoint_manager.write_static_type_set_and_threads(); + } +} + static void stop() { assert(JfrRecorderService::is_recording(), "invariant"); if (LogJFR) tty->print_cr("Recording STOPPED"); @@ -306,11 +419,30 @@ static void stop() { assert(!JfrRecorderService::is_recording(), "invariant"); } -void JfrRecorderService::rotate(int msgs) { - RotationLock rl(Thread::current()); - if (rl.not_acquired()) { - return; +void JfrRecorderService::prepare_for_vm_error_rotation() { + assert(JfrStream_lock->owned_by_self(), "invariant"); + if (!_chunkwriter.is_valid()) { + open_new_chunk(true); + } + _checkpoint_manager.register_service_thread(Thread::current()); +} + +void JfrRecorderService::vm_error_rotation() { + assert(JfrStream_lock->owned_by_self(), "invariant"); + if (_chunkwriter.is_valid()) { + Thread* const t = Thread::current(); + _storage.flush_regular_buffer(t->jfr_thread_local()->native_buffer(), t); + invoke_flush(); + _chunkwriter.set_time_stamp(); + _repository.close_chunk(); + assert(!_chunkwriter.is_valid(), "invariant"); + _repository.on_vm_error(); } +} + +void JfrRecorderService::rotate(int msgs) { + assert(!JfrStream_lock->owned_by_self(), "invariant"); + MutexLocker lock(JfrStream_lock); static bool vm_error = false; if (msgs & MSGBIT(MSG_VM_ERROR)) { vm_error = true; @@ -328,45 +460,19 @@ void JfrRecorderService::rotate(int msgs) { } } -void JfrRecorderService::prepare_for_vm_error_rotation() { - if (!_chunkwriter.is_valid()) { - open_new_chunk(true); - } - _checkpoint_manager.register_service_thread(Thread::current()); - JfrMetadataEvent::lock(); -} - -void JfrRecorderService::open_new_chunk(bool vm_error) { - assert(!_chunkwriter.is_valid(), "invariant"); - assert(!JfrStream_lock->owned_by_self(), "invariant"); - JfrChunkRotation::on_rotation(); - MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); - if (!_repository.open_chunk(vm_error)) { - assert(!_chunkwriter.is_valid(), "invariant"); - _storage.control().set_to_disk(false); - return; - } - assert(_chunkwriter.is_valid(), "invariant"); - _storage.control().set_to_disk(true); -} - void JfrRecorderService::in_memory_rotation() { - assert(!_chunkwriter.is_valid(), "invariant"); + assert(JfrStream_lock->owned_by_self(), "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 - serialize_storage_from_in_memory_recording(); + write_storage(_storage, _chunkwriter); } } -void JfrRecorderService::serialize_storage_from_in_memory_recording() { - assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!"); - MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); - _storage.write(); -} - void JfrRecorderService::chunk_rotation() { + assert(JfrStream_lock->owned_by_self(), "invariant"); finalize_current_chunk(); open_new_chunk(); } @@ -374,7 +480,6 @@ void JfrRecorderService::chunk_rotation() { void JfrRecorderService::finalize_current_chunk() { assert(_chunkwriter.is_valid(), "invariant"); write(); - assert(!_chunkwriter.is_valid(), "invariant"); } void JfrRecorderService::write() { @@ -385,54 +490,20 @@ void JfrRecorderService::write() { post_safepoint_write(); } -typedef ServiceFunctor WriteStringPool; -typedef ServiceFunctor WriteStringPoolSafepoint; -typedef WriteCheckpointEvent WriteStackTraceCheckpoint; -typedef WriteCheckpointEvent WriteStringPoolCheckpoint; -typedef WriteCheckpointEvent WriteStringPoolCheckpointSafepoint; - -static void write_stacktrace_checkpoint(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) { - WriteStackTraceRepository write_stacktrace_repo(stack_trace_repo, chunkwriter, clear); - WriteStackTraceCheckpoint write_stack_trace_checkpoint(chunkwriter, TYPE_STACKTRACE, write_stacktrace_repo); - write_stack_trace_checkpoint.process(); -} -static void write_stringpool_checkpoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { - WriteStringPool write_string_pool(string_pool); - WriteStringPoolCheckpoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool); - write_string_pool_checkpoint.process(); -} - -static void write_stringpool_checkpoint_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { - WriteStringPoolSafepoint write_string_pool(string_pool); - WriteStringPoolCheckpointSafepoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool); - write_string_pool_checkpoint.process(); -} - -// -// pre-safepoint write sequence -// -// lock stream lock -> -// write non-safepoint dependent types -> -// write checkpoint epoch transition list-> -// write stack trace checkpoint -> -// write string pool checkpoint -> -// write object sample stacktraces -> -// write storage -> -// release stream lock -// void JfrRecorderService::pre_safepoint_write() { - MutexLockerEx 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); if (LeakProfiler::is_running()) { // Exclusive access to the object sampler instance. // The sampler is released (unlocked) later in post_safepoint_write. ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire(), _stack_trace_repository); } - _storage.write(); + if (_string_pool.is_modified()) { + write_stringpool(_string_pool, _chunkwriter); + } + write_storage(_storage, _chunkwriter); + if (_stack_trace_repository.is_modified()) { + write_stacktrace(_stack_trace_repository, _chunkwriter, false); + } } void JfrRecorderService::invoke_safepoint_write() { @@ -440,50 +511,18 @@ void JfrRecorderService::invoke_safepoint_write() { VMThread::execute(&safepoint_task); } -// -// safepoint write sequence -// -// lock stream lock -> -// write stacktrace repository -> -// write string pool -> -// write safepoint dependent types -> -// write storage -> -// shift_epoch -> -// update time -> -// lock metadata descriptor -> -// release stream lock -// void JfrRecorderService::safepoint_write() { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); - MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); - write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, true); - write_stringpool_checkpoint_safepoint(_string_pool, _chunkwriter); - _checkpoint_manager.write_safepoint_types(); + if (_string_pool.is_modified()) { + write_stringpool_safepoint(_string_pool, _chunkwriter); + } + _checkpoint_manager.on_rotation(); _storage.write_at_safepoint(); _checkpoint_manager.shift_epoch(); - _chunkwriter.time_stamp_chunk_now(); - JfrMetadataEvent::lock(); + _chunkwriter.set_time_stamp(); + write_stacktrace(_stack_trace_repository, _chunkwriter, true); } -static jlong write_metadata_event(JfrChunkWriter& chunkwriter) { - assert(chunkwriter.is_valid(), "invariant"); - const jlong metadata_offset = chunkwriter.current_offset(); - JfrMetadataEvent::write(chunkwriter, metadata_offset); - return metadata_offset; -} - -// -// post-safepoint write sequence -// -// write type set -> -// release object sampler -> -// lock stream lock -> -// write checkpoints -> -// write metadata event -> -// write chunk header -> -// close chunk fd -> -// release stream lock -// void JfrRecorderService::post_safepoint_write() { assert(_chunkwriter.is_valid(), "invariant"); // During the safepoint tasks just completed, the system transitioned to a new epoch. @@ -495,38 +534,85 @@ void JfrRecorderService::post_safepoint_write() { // The object sampler instance was exclusively acquired and locked in pre_safepoint_write. // Note: There is a dependency on write_type_set() above, ensure the release is subsequent. ObjectSampler::release(); - } MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); - // serialize any outstanding checkpoint memory - _checkpoint_manager.write(); + } // serialize the metadata descriptor event and close out the chunk - _repository.close_chunk(write_metadata_event(_chunkwriter)); - assert(!_chunkwriter.is_valid(), "invariant"); + write_metadata(_chunkwriter); + _repository.close_chunk(); } -void JfrRecorderService::vm_error_rotation() { - if (_chunkwriter.is_valid()) { - finalize_current_chunk_on_vm_error(); - assert(!_chunkwriter.is_valid(), "invariant"); - _repository.on_vm_error(); +static JfrBuffer* thread_local_buffer(Thread* t) { + assert(t != NULL, "invariant"); + return t->jfr_thread_local()->native_buffer(); +} + +static void reset_buffer(JfrBuffer* buffer, Thread* t) { + assert(buffer != NULL, "invariant"); + assert(t != NULL, "invariant"); + assert(buffer == thread_local_buffer(t), "invariant"); + buffer->set_pos(const_cast(buffer->top())); +} + +static void reset_thread_local_buffer(Thread* t) { + reset_buffer(thread_local_buffer(t), t); +} + +static void write_thread_local_buffer(JfrChunkWriter& chunkwriter, Thread* t) { + JfrBuffer * const buffer = thread_local_buffer(t); + assert(buffer != NULL, "invariant"); + if (!buffer->empty()) { + chunkwriter.write_unbuffered(buffer->top(), buffer->pos() - buffer->top()); + reset_buffer(buffer, t); } } -void JfrRecorderService::finalize_current_chunk_on_vm_error() { +size_t JfrRecorderService::flush() { + assert(JfrStream_lock->owned_by_self(), "invariant"); + size_t total_elements = flush_metadata(_chunkwriter); + const size_t storage_elements = flush_storage(_storage, _chunkwriter); + if (0 == storage_elements) { + return total_elements; + } + total_elements += storage_elements; + if (_string_pool.is_modified()) { + total_elements += flush_stringpool(_string_pool, _chunkwriter); + } + if (_stack_trace_repository.is_modified()) { + total_elements += flush_stacktrace(_stack_trace_repository, _chunkwriter); + } + if (_checkpoint_manager.is_type_set_required()) { + total_elements += flush_typeset(_checkpoint_manager, _chunkwriter); + } else if (_checkpoint_manager.is_static_type_set_required()) { + // don't tally this, it is only in order to flush the waiting constants + _checkpoint_manager.flush_static_type_set(); + } + return total_elements; +} + +typedef Content FlushFunctor; +typedef WriteContent Flush; + +void JfrRecorderService::invoke_flush() { + assert(JfrStream_lock->owned_by_self(), "invariant"); assert(_chunkwriter.is_valid(), "invariant"); - pre_safepoint_write(); - // Do not attempt safepoint dependent operations during emergency dump. - // Optimistically write tagged artifacts. - _checkpoint_manager.shift_epoch(); - // update time - _chunkwriter.time_stamp_chunk_now(); - post_safepoint_write(); - assert(!_chunkwriter.is_valid(), "invariant"); + Thread* const t = Thread::current(); + ResourceMark rm(t); + HandleMark hm(t); + ++flushpoint_id; + reset_thread_local_buffer(t); + FlushFunctor flushpoint(*this); + Flush fl(_chunkwriter, flushpoint); + invoke_with_flush_event(fl); + write_thread_local_buffer(_chunkwriter, t); + _repository.flush_chunk(); +} + +void JfrRecorderService::flushpoint() { + MutexLocker lock(JfrStream_lock); + invoke_flush(); } void JfrRecorderService::process_full_buffers() { if (_chunkwriter.is_valid()) { - assert(!JfrStream_lock->owned_by_self(), "invariant"); - MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); _storage.write_full(); } } diff --git a/src/share/vm/jfr/recorder/service/jfrRecorderService.hpp b/src/share/vm/jfr/recorder/service/jfrRecorderService.hpp index 2cccf5b6f7d95de5224c0208bf0b3ff3bf65fdfe..d51444f3b8298303ea47056bba7af7ef23d0418f 100644 --- a/src/share/vm/jfr/recorder/service/jfrRecorderService.hpp +++ b/src/share/vm/jfr/recorder/service/jfrRecorderService.hpp @@ -46,11 +46,10 @@ class JfrRecorderService : public StackObj { void open_new_chunk(bool vm_error = false); void chunk_rotation(); void in_memory_rotation(); - void serialize_storage_from_in_memory_recording(); void finalize_current_chunk(); - void finalize_current_chunk_on_vm_error(); void prepare_for_vm_error_rotation(); void vm_error_rotation(); + void invoke_flush(); void clear(); void pre_safepoint_clear(); @@ -67,7 +66,9 @@ class JfrRecorderService : public StackObj { public: JfrRecorderService(); void start(); + size_t flush(); void rotate(int msgs); + void flushpoint(); void process_full_buffers(); void scavenge(); void evaluate_chunk_size_for_rotation(); diff --git a/src/share/vm/jfr/recorder/service/jfrRecorderThread.cpp b/src/share/vm/jfr/recorder/service/jfrRecorderThread.cpp index f6feb467467d3749620624a5ab9bea71842ac653..71470831823cbbe5c42d33c17a78fd18988ae41e 100644 --- a/src/share/vm/jfr/recorder/service/jfrRecorderThread.cpp +++ b/src/share/vm/jfr/recorder/service/jfrRecorderThread.cpp @@ -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" @@ -63,7 +64,6 @@ static Thread* start_thread(instanceHandle thread_oop, ThreadFunction proc, TRAP 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; } @@ -97,8 +97,9 @@ bool JfrRecorderThread::start(JfrCheckpointManager* cp_manager, JfrPostBox* post 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; } diff --git a/src/share/vm/jfr/recorder/service/jfrRecorderThreadLoop.cpp b/src/share/vm/jfr/recorder/service/jfrRecorderThreadLoop.cpp index fa766e2d6f2d001bdbed2b7297bf0f1e2a615de4..8f7723a64840cf7124a0826c67221bf9bde7d926 100644 --- a/src/share/vm/jfr/recorder/service/jfrRecorderThreadLoop.cpp +++ b/src/share/vm/jfr/recorder/service/jfrRecorderThreadLoop.cpp @@ -39,6 +39,7 @@ void recorderthread_entry(JavaThread* thread, Thread* unused) { #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))) #define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER))) #define SCAVENGE (msgs & (MSGBIT(MSG_DEADBUFFER))) @@ -71,6 +72,8 @@ void recorderthread_entry(JavaThread* thread, Thread* unused) { service.start(); } else if (ROTATE) { service.rotate(msgs); + } else if (FLUSHPOINT) { + service.flushpoint(); } JfrMsg_lock->lock(); post_box.notify_waiters(); @@ -89,6 +92,7 @@ void recorderthread_entry(JavaThread* thread, Thread* unused) { #undef START #undef SHUTDOWN #undef ROTATE + #undef FLUSHPOINT #undef PROCESS_FULL_BUFFERS #undef SCAVENGE } diff --git a/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp b/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp index f33ff914687863a543b59049bb0ecd2df8f25e15..1c24ecad3e7cd974c5df113798100b3e0aad7d12 100644 --- a/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp +++ b/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp @@ -62,7 +62,7 @@ class JfrFrameType : public JfrSerializer { }; bool JfrStackTraceRepository::initialize() { - return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType()); + return JfrSerializer::register_serializer(TYPE_FRAMETYPE, true, new JfrFrameType()); } void JfrStackTraceRepository::destroy() { @@ -71,7 +71,16 @@ void JfrStackTraceRepository::destroy() { _instance = NULL; } -size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) { +static traceid last_id = 0; + +bool JfrStackTraceRepository::is_modified() const { + return last_id != _next_id; +} + +size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) { + if (_entries == 0) { + return 0; + } MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); assert(_entries > 0, "invariant"); int count = 0; @@ -93,29 +102,10 @@ size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) { memset(_table, 0, sizeof(_table)); _entries = 0; } + last_id = _next_id; return count; } -size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) { - return _entries > 0 ? write_impl(sw, clear) : 0; -} - -traceid JfrStackTraceRepository::write(JfrCheckpointWriter& writer, traceid id, unsigned int hash) { - assert(JfrStacktrace_lock->owned_by_self(), "invariant"); - const JfrStackTrace* const trace = lookup(hash, id); - assert(trace != NULL, "invariant"); - assert(trace->hash() == hash, "invariant"); - assert(trace->id() == id, "invariant"); - trace->write(writer); - return id; -} - -void JfrStackTraceRepository::write_metadata(JfrCheckpointWriter& writer) { - JfrFrameType fct; - writer.write_type(TYPE_FRAMETYPE); - fct.serialize(writer); -} - size_t JfrStackTraceRepository::clear() { MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); if (_entries == 0) { @@ -142,7 +132,7 @@ traceid JfrStackTraceRepository::record(Thread* thread, int skip, StackWalkMode if (tl->has_cached_stack_trace()) { 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(); diff --git a/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp b/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp index f17b92b79c378e4b66d306b2c3b6bfc6cf4bbec7..e9a9d62b6accb580726b890521c902bb65ce7fde 100644 --- a/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp +++ b/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp @@ -41,7 +41,7 @@ class JfrStackTraceRepository : public JfrCHeapObj { friend class ObjectSampleCheckpoint; friend class ObjectSampler; friend class StackTraceBlobInstaller; - friend class WriteStackTraceRepository; + friend class StackTraceRepository; private: static const u4 TABLE_SIZE = 2053; @@ -52,19 +52,18 @@ class JfrStackTraceRepository : public JfrCHeapObj { JfrStackTraceRepository(); static JfrStackTraceRepository& instance(); static JfrStackTraceRepository* create(); - bool initialize(); static void destroy(); + bool initialize(); - size_t write_impl(JfrChunkWriter& cw, bool clear); - static void write_metadata(JfrCheckpointWriter& cpw); - traceid write(JfrCheckpointWriter& cpw, traceid id, unsigned int hash); + bool is_modified() const; size_t write(JfrChunkWriter& cw, bool clear); size_t clear(); + const JfrStackTrace* lookup(unsigned int hash, traceid id) const; + traceid add_trace(const JfrStackTrace& stacktrace); static traceid add(const JfrStackTrace& stacktrace); traceid record_for(JavaThread* thread, int skip, StackWalkMode mode, JfrStackFrame* frames, u4 max_frames); - const JfrStackTrace* lookup(unsigned int hash, traceid id) const; public: static traceid record(Thread* thread, int skip, StackWalkMode mode); diff --git a/src/share/vm/jfr/recorder/storage/jfrBuffer.cpp b/src/share/vm/jfr/recorder/storage/jfrBuffer.cpp index 120ea38c44943da10f34450870835d9d6dec948c..02ab10243a9ec7959a2c0ad157f4db8bd7c9e5ca 100644 --- a/src/share/vm/jfr/recorder/storage/jfrBuffer.cpp +++ b/src/share/vm/jfr/recorder/storage/jfrBuffer.cpp @@ -55,10 +55,18 @@ bool JfrBuffer::initialize(size_t header_size, size_t size, const void* id /* NU 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()); } @@ -191,7 +199,8 @@ void JfrBuffer::concurrent_move_and_reinitialize(JfrBuffer* const to, size_t siz enum FLAG { RETIRED = 1, TRANSIENT = 2, - LEASE = 4 + LEASE = 4, + EXCLUDED = 8 }; bool JfrBuffer::transient() const { @@ -250,3 +259,20 @@ void JfrBuffer::clear_retired() { release_store_flags(&_flags, new_flags); } } + +bool JfrBuffer::excluded() const { + return (u1)EXCLUDED == (_flags & (u1)EXCLUDED); +} + +void JfrBuffer::set_excluded() { + _flags |= (u1)EXCLUDED; + assert(excluded(), "invariant"); +} + +void JfrBuffer::clear_excluded() { + if (excluded()) { + OrderAccess::storestore(); + _flags ^= (u1)EXCLUDED; + } + assert(!excluded(), "invariant"); +} diff --git a/src/share/vm/jfr/recorder/storage/jfrBuffer.hpp b/src/share/vm/jfr/recorder/storage/jfrBuffer.hpp index f97d50fabae801623b25e4a762fc534afb029662..bee0bbe354090685a6ec79204acabdcb4bbe0a4d 100644 --- a/src/share/vm/jfr/recorder/storage/jfrBuffer.hpp +++ b/src/share/vm/jfr/recorder/storage/jfrBuffer.hpp @@ -61,7 +61,7 @@ class JfrBuffer { 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 { @@ -167,6 +167,11 @@ class JfrBuffer { bool retired() const; void set_retired(); void clear_retired(); + + + bool excluded() const; + void set_excluded(); + void clear_excluded(); }; class JfrAgeNode : public JfrBuffer { diff --git a/src/share/vm/jfr/recorder/storage/jfrMemorySpace.hpp b/src/share/vm/jfr/recorder/storage/jfrMemorySpace.hpp index b7aebed901c926901b3966ddb320de876f8dfe0c..ec4b8d5fb9166d1bc20d7f85ed7d77bd7944796b 100644 --- a/src/share/vm/jfr/recorder/storage/jfrMemorySpace.hpp +++ b/src/share/vm/jfr/recorder/storage/jfrMemorySpace.hpp @@ -99,8 +99,8 @@ class JfrMemorySpace : public JfrCHeapObj { template void iterate(IteratorCallback& callback, bool full = true, jfr_iter_direction direction = forward); - debug_only(bool in_full_list(const Type* t) const { return _full.in_list(t); }) - debug_only(bool in_free_list(const Type* t) const { return _free.in_list(t); }) + bool in_full_list(const Type* t) const { return _full.in_list(t); } + bool in_free_list(const Type* t) const { return _free.in_list(t); } }; #endif // SHARE_VM_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_HPP diff --git a/src/share/vm/jfr/recorder/storage/jfrMemorySpace.inline.hpp b/src/share/vm/jfr/recorder/storage/jfrMemorySpace.inline.hpp index a55cee2d14b74d29f66952ac0743d28f3665b481..20285d7f6588da83fd64b1a76191243bdfe183c8 100644 --- a/src/share/vm/jfr/recorder/storage/jfrMemorySpace.inline.hpp +++ b/src/share/vm/jfr/recorder/storage/jfrMemorySpace.inline.hpp @@ -141,6 +141,7 @@ inline void JfrMemorySpace::release_free(T* t) { } assert(t->empty(), "invariant"); assert(!t->retired(), "invariant"); + assert(!t->excluded(), "invariant"); assert(t->identity() == NULL, "invariant"); if (!should_populate_cache()) { remove_free(t); diff --git a/src/share/vm/jfr/recorder/storage/jfrStorage.cpp b/src/share/vm/jfr/recorder/storage/jfrStorage.cpp index 23a106702a686ee676eab003fcbb7d541e5c6c1d..dff2b63b1777895803ee6c09b1b3f33ddcb80d6f 100644 --- a/src/share/vm/jfr/recorder/storage/jfrStorage.cpp +++ b/src/share/vm/jfr/recorder/storage/jfrStorage.cpp @@ -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" @@ -252,6 +253,18 @@ bool JfrStorage::flush_regular_buffer(BufferPtr buffer, Thread* thread) { 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); @@ -302,7 +315,7 @@ static void handle_registration_failure(BufferPtr buffer) { assert(buffer != NULL, "invariant"); assert(buffer->retired(), "invariant"); const size_t unflushed_size = buffer->unflushed_size(); - buffer->reinitialize(); + buffer->concurrent_reinitialization(); log_registration_failure(unflushed_size); } @@ -464,6 +477,7 @@ static void assert_flush_large_precondition(ConstBufferPtr cur, const u1* const 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"); @@ -490,6 +504,9 @@ BufferPtr JfrStorage::flush_regular(BufferPtr cur, const u1* const cur_pos, size // the case for stable thread local buffers; it is not the case for large buffers. 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) { @@ -578,28 +595,40 @@ BufferPtr JfrStorage::provision_large(BufferPtr cur, const u1* const cur_pos, si typedef UnBufferedWriteToChunk WriteOperation; typedef MutexedWriteOp MutexedWriteOperation; typedef ConcurrentWriteOp ConcurrentWriteOperation; -typedef ConcurrentWriteOpExcludeRetired ThreadLocalConcurrentWriteOperation; + +typedef Retired NonRetired; +typedef Excluded NonExcluded; +typedef CompositeOperation BufferPredicate; +typedef PredicatedMutexedWriteOp ThreadLocalMutexedWriteOperation; +typedef PredicatedConcurrentWriteOp 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); + NonRetired nr; + NonExcluded ne; + BufferPredicate bp(&nr, &ne); + ThreadLocalConcurrentWriteOperation tlwo(wo, bp); 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() { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); WriteOperation wo(_chunkwriter); MutexedWriteOperation writer(wo); // mutexed write mode - process_full_list(writer, _thread_local_mspace); + NonRetired nr; + NonExcluded ne; + BufferPredicate bp(&nr, &ne); + ThreadLocalMutexedWriteOperation tlmwo(wo, bp); + process_full_list(tlmwo, _thread_local_mspace); assert(_transient_mspace->is_free_empty(), "invariant"); 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 > DiscardOperation; @@ -607,14 +636,14 @@ typedef ReleaseOp ReleaseOperation; typedef CompositeOperation 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) { @@ -703,15 +732,25 @@ size_t JfrStorage::write_full() { 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) { @@ -739,6 +778,10 @@ public: assert(!t->lease(), "invariant"); ++_count; _amount += t->total_size(); + if (t->excluded()) { + t->clear_excluded(); + } + assert(!t->excluded(), "invariant"); t->clear_retired(); t->release(); _control.decrement_dead(); @@ -757,6 +800,11 @@ size_t JfrStorage::scavenge() { } Scavenger 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; } diff --git a/src/share/vm/jfr/recorder/storage/jfrStorage.hpp b/src/share/vm/jfr/recorder/storage/jfrStorage.hpp index 90e0c90f65f2862e734148c489e1e851abe888d8..fb489a4c9c782cf787bad96420c3501ccb31490c 100644 --- a/src/share/vm/jfr/recorder/storage/jfrStorage.hpp +++ b/src/share/vm/jfr/recorder/storage/jfrStorage.hpp @@ -68,7 +68,6 @@ class JfrStorage : public JfrCHeapObj { 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 @@ class JfrStorage : public JfrCHeapObj { void discard_oldest(Thread* t); static JfrStorageControl& control(); + size_t write(); + friend class JfrRecorder; friend class JfrRecorderService; template class, typename> diff --git a/src/share/vm/jfr/recorder/storage/jfrStorageControl.cpp b/src/share/vm/jfr/recorder/storage/jfrStorageControl.cpp index 0a5db43c002751bfa8a563550e0a6671b0f77294..3a4591681e2648711014519249084ee639953403 100644 --- a/src/share/vm/jfr/recorder/storage/jfrStorageControl.cpp +++ b/src/share/vm/jfr/recorder/storage/jfrStorageControl.cpp @@ -33,7 +33,7 @@ static jlong atomic_add(size_t value, size_t volatile* const dest) { size_t compare_value; size_t exchange_value; do { - compare_value = OrderAccess::load_ptr_acquire((intptr_t*)dest); + compare_value = *dest; exchange_value = compare_value + value; } while ((unsigned long)Atomic::cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value) != compare_value); return exchange_value; @@ -43,7 +43,7 @@ static jlong atomic_dec(size_t volatile* const dest) { size_t compare_value; size_t exchange_value; do { - compare_value = OrderAccess::load_ptr_acquire((intptr_t*)dest); + compare_value = *dest; assert(compare_value >= 1, "invariant"); exchange_value = compare_value - 1; } while ((unsigned long)Atomic::cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value) != compare_value); diff --git a/src/share/vm/jfr/recorder/storage/jfrStorageUtils.hpp b/src/share/vm/jfr/recorder/storage/jfrStorageUtils.hpp index 1f4428da56ea7820b79a9dd0e9bcbc8a1bd486ad..bec28dcfc08fe8df70347682a3c9458dfb317b0a 100644 --- a/src/share/vm/jfr/recorder/storage/jfrStorageUtils.hpp +++ b/src/share/vm/jfr/recorder/storage/jfrStorageUtils.hpp @@ -31,7 +31,21 @@ #include "jfr/utilities/jfrTypes.hpp" #include "runtime/thread.hpp" -template +class CompositeOperationOr { + public: + static bool evaluate(bool value) { + return !value; + } +}; + +class CompositeOperationAnd { + public: + static bool evaluate(bool value) { + return value; + } +}; + +template class CompositeOperation { private: Operation* _op; @@ -41,11 +55,15 @@ class CompositeOperation { assert(_op != NULL, "invariant"); } typedef typename Operation::Type Type; - bool process(Type* t = NULL) { - return _next == NULL ? _op->process(t) : _op->process(t) && _next->process(t); + bool process(Type* t) { + const bool op_result = _op->process(t); + return _next == NULL ? op_result : TruthFunction::evaluate(op_result) ? _next->process(t) : op_result; } - size_t processed() const { - 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,54 +71,95 @@ template 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 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 +class Retired { + public: + typedef T Type; + bool process(Type* t) { + assert(t != NULL, "invariant"); + return negation ? !t->retired() : t->retired(); + } +}; + +template +class Excluded { + public: + typedef T Type; + bool process(Type* t) { + assert(t != NULL, "invariant"); + return negation ? !t->excluded() : t->excluded(); + } }; template -class ConcurrentWriteOp { +class MutexedWriteOp { private: Operation& _operation; public: typedef typename Operation::Type Type; - ConcurrentWriteOp(Operation& operation) : _operation(operation) {} + 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 -class ConcurrentWriteOpExcludeRetired : private ConcurrentWriteOp { +template +class PredicatedMutexedWriteOp : public MutexedWriteOp { + private: + Predicate& _predicate; public: - typedef typename Operation::Type Type; - ConcurrentWriteOpExcludeRetired(Operation& operation) : ConcurrentWriteOp(operation) {} - bool process(Type* t); - size_t processed() const { return ConcurrentWriteOp::processed(); } + PredicatedMutexedWriteOp(Operation& operation, Predicate& predicate) : + MutexedWriteOp(operation), _predicate(predicate) {} + bool process(typename Operation::Type* t) { + return _predicate.process(t) ? MutexedWriteOp::process(t) : true; + } }; template -class MutexedWriteOp { +class ConcurrentWriteOp { private: Operation& _operation; public: typedef typename Operation::Type Type; - MutexedWriteOp(Operation& operation) : _operation(operation) {} + 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 +class PredicatedConcurrentWriteOp : public ConcurrentWriteOp { + private: + Predicate& _predicate; + public: + PredicatedConcurrentWriteOp(Operation& operation, Predicate& predicate) : + ConcurrentWriteOp(operation), _predicate(predicate) {} + bool process(typename Operation::Type* t) { + return _predicate.process(t) ? ConcurrentWriteOp::process(t) : true; + } }; template @@ -126,7 +185,8 @@ class DiscardOp { 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_VM_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP diff --git a/src/share/vm/jfr/recorder/storage/jfrStorageUtils.inline.hpp b/src/share/vm/jfr/recorder/storage/jfrStorageUtils.inline.hpp index 620a9f2614be33c978100f1bca2a399ddd81f9b8..919a1930dc2c8494728888be6c887cf781976acb 100644 --- a/src/share/vm/jfr/recorder/storage/jfrStorageUtils.inline.hpp +++ b/src/share/vm/jfr/recorder/storage/jfrStorageUtils.inline.hpp @@ -31,13 +31,15 @@ template inline bool UnBufferedWriteToChunk::write(T* t, const u1* data, size_t size) { _writer.write_unbuffered(data, size); - _processed += size; + ++_elements; + _size += size; return true; } template inline bool DefaultDiscarder::discard(T* t, const u1* data, size_t size) { - _processed += size; + ++_elements; + _size += size; return true; } @@ -54,15 +56,6 @@ inline bool ConcurrentWriteOp::process(typename Operation::Type* t) { return result; } -template -inline bool ConcurrentWriteOpExcludeRetired::process(typename Operation::Type* t) { - if (t->retired()) { - assert(t->empty(), "invariant"); - return true; - } - return ConcurrentWriteOp::process(t); -} - template inline bool MutexedWriteOp::process(typename Operation::Type* t) { assert(t != NULL, "invariant"); diff --git a/src/share/vm/jfr/recorder/stringpool/jfrStringPool.cpp b/src/share/vm/jfr/recorder/stringpool/jfrStringPool.cpp index 105089b050d1ce584b6b01cdcd4310e124f48148..cca2272438d7d7db2cae3015e0868737f180d1a9 100644 --- a/src/share/vm/jfr/recorder/stringpool/jfrStringPool.cpp +++ b/src/share/vm/jfr/recorder/stringpool/jfrStringPool.cpp @@ -32,7 +32,6 @@ #include "jfr/recorder/stringpool/jfrStringPool.hpp" #include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp" #include "jfr/utilities/jfrTypes.hpp" -#include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/orderAccess.hpp" #include "runtime/safepoint.hpp" @@ -41,12 +40,42 @@ typedef JfrStringPool::Buffer* BufferPtr; static JfrStringPool* _instance = NULL; +static uint64_t store_generation = 0; +static uint64_t serialized_generation = 0; + +inline void set_generation(uint64_t value, uint64_t* const dest) { + assert(dest != NULL, "invariant"); + OrderAccess::release_store(dest, value); +} +static void increment_store_generation() { + const uint64_t current_serialized = OrderAccess::load_acquire(&serialized_generation); + const uint64_t current_stored = OrderAccess::load_acquire(&store_generation); + if (current_serialized == current_stored) { + set_generation(current_serialized + 1, &store_generation); + } +} + +static bool increment_serialized_generation() { + const uint64_t current_stored = OrderAccess::load_acquire(&store_generation); + const uint64_t current_serialized = OrderAccess::load_acquire(&serialized_generation); + if (current_stored != current_serialized) { + set_generation(current_stored, &serialized_generation); + return true; + } + return false; +} + +bool JfrStringPool::is_modified() { + return increment_serialized_generation(); +} JfrStringPool& JfrStringPool::instance() { return *_instance; } JfrStringPool* JfrStringPool::create(JfrChunkWriter& cw) { + store_generation = 0; + serialized_generation = 0; assert(_instance == NULL, "invariant"); _instance = new JfrStringPool(cw); return _instance; @@ -130,12 +159,16 @@ BufferPtr JfrStringPool::lease_buffer(Thread* thread, size_t size /* 0 */) { bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) { assert(jt != NULL, "invariant"); const bool current_epoch = (JfrTraceIdEpoch::epoch() != 0); - if (current_epoch == epoch) { + if (current_epoch != epoch) { + return current_epoch; + } + { JfrStringPoolWriter writer(jt); writer.write(id); writer.write(string); writer.inc_nof_strings(); } + increment_store_generation(); return current_epoch; } @@ -196,6 +229,7 @@ size_t JfrStringPool::write_at_safepoint() { } size_t JfrStringPool::clear() { + increment_serialized_generation(); DiscardOperation discard_operation; ExclusiveDiscardOperation edo(discard_operation); StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false); diff --git a/src/share/vm/jfr/recorder/stringpool/jfrStringPool.hpp b/src/share/vm/jfr/recorder/stringpool/jfrStringPool.hpp index 84c522174eff7d9f98c82691416919007d363754..fa27eb8e65cc26873201f729cec6d4377f719d4a 100644 --- a/src/share/vm/jfr/recorder/stringpool/jfrStringPool.hpp +++ b/src/share/vm/jfr/recorder/stringpool/jfrStringPool.hpp @@ -71,6 +71,7 @@ class JfrStringPool : public JfrCHeapObj { static JfrStringPool* create(JfrChunkWriter& cw); bool initialize(); static void destroy(); + static bool is_modified(); friend class JfrRecorder; friend class JfrRecorderService; diff --git a/src/share/vm/jfr/support/jfrThreadLocal.cpp b/src/share/vm/jfr/support/jfrThreadLocal.cpp index 0d36e33f731bea88f392c5516f95376a2d21f17e..8d6ea9557296c36ae896d27dd0e8ce23a858a1e6 100644 --- a/src/share/vm/jfr/support/jfrThreadLocal.cpp +++ b/src/share/vm/jfr/support/jfrThreadLocal.cpp @@ -55,6 +55,7 @@ JfrThreadLocal::JfrThreadLocal() : _stack_trace_hash(0), _stackdepth(0), _entering_suspend_flag(0), + _excluded(false), _dead(false), _cached_top_frame_bci(max_jint), _alloc_count(0), @@ -88,9 +89,13 @@ static void send_java_thread_start_event(JavaThread* jt) { 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,50 +112,68 @@ static void send_java_thread_end_events(traceid id, JavaThread* jt) { } } +void JfrThreadLocal::release(Thread* t) { + if (has_java_event_writer()) { + assert(t->is_Java_thread(), "invariant"); + JfrJavaSupport::destroy_global_jni_handle(java_event_writer()); + _java_event_writer = NULL; + } + if (has_native_buffer()) { + JfrStorage::release_thread_local(native_buffer(), t); + _native_buffer = NULL; + } + if (has_java_buffer()) { + JfrStorage::release_thread_local(java_buffer(), t); + _java_buffer = NULL; + } + if (_stackframes != NULL) { + FREE_C_HEAP_ARRAY(JfrStackFrame, _stackframes, mtTracing); + _stackframes = NULL; + } +} + void JfrThreadLocal::release(JfrThreadLocal* tl, Thread* t) { assert(tl != NULL, "invariant"); assert(t != NULL, "invariant"); assert(Thread::current() == t, "invariant"); assert(!tl->is_dead(), "invariant"); assert(tl->shelved_buffer() == NULL, "invariant"); - if (tl->has_native_buffer()) { - JfrStorage::release_thread_local(tl->native_buffer(), t); - } - if (tl->has_java_buffer()) { - JfrStorage::release_thread_local(tl->java_buffer(), t); - } - if (tl->has_java_event_writer()) { - assert(t->is_Java_thread(), "invariant"); - JfrJavaSupport::destroy_global_jni_handle(tl->java_event_writer()); - } - if (tl->_stackframes != NULL) { - FREE_C_HEAP_ARRAY(JfrStackFrame, tl->_stackframes, mtTracing); - } tl->_dead = true; + tl->release(t); } void JfrThreadLocal::on_exit(Thread* t) { assert(t != NULL, "invariant"); JfrThreadLocal * const tl = t->jfr_thread_local(); assert(!tl->is_dead(), "invariant"); - if (t->is_Java_thread()) { - JavaThread* const jt = (JavaThread*)t; - ObjectSampleCheckpoint::on_thread_exit(jt); - send_java_thread_end_events(tl->thread_id(), jt); + if (JfrRecorder::is_recording()) { + if (t->is_Java_thread()) { + JavaThread* const jt = (JavaThread*)t; + ObjectSampleCheckpoint::on_thread_exit(jt); + send_java_thread_end_events(tl->thread_id(), jt); + } } release(tl, Thread::current()); // because it could be that Thread::current() != t } +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; } @@ -168,6 +191,18 @@ 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; + t->jfr_thread_local()->release(t); +} + +void JfrThreadLocal::include(Thread* t) { + assert(t != NULL, "invariant"); + t->jfr_thread_local()->_excluded = false; + t->jfr_thread_local()->release(t); +} + u4 JfrThreadLocal::stackdepth() const { return _stackdepth != 0 ? _stackdepth : (u4)JfrOptionSet::stackdepth(); } diff --git a/src/share/vm/jfr/support/jfrThreadLocal.hpp b/src/share/vm/jfr/support/jfrThreadLocal.hpp index 2684880ea7a770fb599c37b7912a45bc51484390..3c5292805c05d0742c91319a77aba2b5608a852e 100644 --- a/src/share/vm/jfr/support/jfrThreadLocal.hpp +++ b/src/share/vm/jfr/support/jfrThreadLocal.hpp @@ -50,6 +50,7 @@ class JfrThreadLocal { unsigned int _stack_trace_hash; mutable u4 _stackdepth; volatile jint _entering_suspend_flag; + bool _excluded; bool _dead; // Jfr callstack collection relies on vframeStream. // But the bci of top frame can not be determined by vframeStream in some scenarios. @@ -76,7 +77,7 @@ class JfrThreadLocal { JfrBuffer* install_native_buffer() const; JfrBuffer* install_java_buffer() const; JfrStackFrame* install_stackframes() const; - + void release(Thread* t); static void release(JfrThreadLocal* tl, Thread* t); public: @@ -224,6 +225,10 @@ class JfrThreadLocal { _trace_id = id; } + bool is_excluded() const { + return _excluded; + } + bool is_dead() const { return _dead; } @@ -280,6 +285,9 @@ class JfrThreadLocal { void set_thread_blob(const JfrBlobHandle& handle); const JfrBlobHandle& thread_blob() const; + static void exclude(Thread* t); + static void include(Thread* t); + static void on_start(Thread* t); static void on_exit(Thread* t); diff --git a/src/share/vm/jfr/support/jfrTraceIdExtension.hpp b/src/share/vm/jfr/support/jfrTraceIdExtension.hpp index 4464a94cd6cb898c6303559cde6c94027c6b39b7..05b5d6cf045e08a88530056e93d6233f086d24c3 100644 --- a/src/share/vm/jfr/support/jfrTraceIdExtension.hpp +++ b/src/share/vm/jfr/support/jfrTraceIdExtension.hpp @@ -61,8 +61,13 @@ class JfrTraceFlag { jbyte* flags_addr() const { return (jbyte*)&_flags; } + jbyte* meta_addr() const { +#ifdef VM_LITTLE_ENDIAN return ((jbyte*)&_flags) + 1; +#else + return (jbyte*)&_flags; +#endif } }; diff --git a/src/share/vm/jfr/utilities/jfrAllocation.cpp b/src/share/vm/jfr/utilities/jfrAllocation.cpp index 2238bec5f6347eed2b330c09476acf7b0bd963b7..34521030fa119fc324d11d33c8cb588c8319ab0d 100644 --- a/src/share/vm/jfr/utilities/jfrAllocation.cpp +++ b/src/share/vm/jfr/utilities/jfrAllocation.cpp @@ -28,7 +28,6 @@ #include "jfr/utilities/jfrTypes.hpp" #include "memory/allocation.inline.hpp" #include "runtime/atomic.inline.hpp" -#include "runtime/orderAccess.inline.hpp" #include "runtime/vm_version.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/debug.hpp" @@ -45,7 +44,7 @@ jlong atomic_add_jlong(jlong value, jlong volatile* const dest) { } #endif do { - compare_value = OrderAccess::load_acquire(dest); + compare_value = *dest; exchange_value = compare_value + value; } while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value); return exchange_value; diff --git a/src/share/vm/jfr/utilities/jfrDoublyLinkedList.hpp b/src/share/vm/jfr/utilities/jfrDoublyLinkedList.hpp index 8ecc326ff76a084151335f417e5aaba153abda5f..94e398acc72fdbfcacb65fd4a7b455b61b8b2688 100644 --- a/src/share/vm/jfr/utilities/jfrDoublyLinkedList.hpp +++ b/src/share/vm/jfr/utilities/jfrDoublyLinkedList.hpp @@ -48,8 +48,8 @@ class JfrDoublyLinkedList { void prepend(T* const node); void append(T* const node); void append_list(T* const head_node, T* const tail_node, size_t count); - debug_only(bool in_list(const T* const target_node) const;) - debug_only(bool locate(const T* start_node, const T* const target_node) const;) + bool in_list(const T* const target_node) const; + bool locate(const T* start_node, const T* const target_node) const; }; template @@ -153,7 +153,6 @@ T* JfrDoublyLinkedList::clear(bool return_tail /* false */) { return node; } -#ifdef ASSERT template bool JfrDoublyLinkedList::locate(const T* node, const T* const target) const { assert(target != NULL, "invariant"); @@ -182,7 +181,6 @@ inline void validate_count_param(T* node, size_t count_param) { } assert(count_param == count, "invariant"); } -#endif // ASSERT template void JfrDoublyLinkedList::append_list(T* const head_node, T* const tail_node, size_t count) { diff --git a/src/share/vm/jfr/utilities/jfrThreadIterator.cpp b/src/share/vm/jfr/utilities/jfrThreadIterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4a50a9b6a67db087a5db6119bcab6d6ac46d1ad4 --- /dev/null +++ b/src/share/vm/jfr/utilities/jfrThreadIterator.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "jfr/support/jfrThreadLocal.hpp" +#include "jfr/utilities/jfrThreadIterator.hpp" +#include "runtime/thread.inline.hpp" + +static bool thread_inclusion_predicate(Thread* t) { + assert(t != NULL, "invariant"); + return !t->jfr_thread_local()->is_dead(); +} + +static bool java_thread_inclusion_predicate(JavaThread* jt) { + assert(jt != NULL, "invariant"); + return thread_inclusion_predicate(jt) && jt->thread_state() != _thread_new; +} + +static JavaThread* next_java_thread(JavaThread* cur) { + JavaThread* next = cur->next(); + while (next != NULL && !java_thread_inclusion_predicate(next)) { + next = next->next(); + } + return next; +} + +/** +static Thread* next_non_java_thread(Thread* cur) { + Thread* next = cur->next(); + while (next != NULL && !thread_inclusion_predicate(next)) { + next = next->next(); + } + return next; +} +*/ + +JfrJavaThreadIteratorAdapter::JfrJavaThreadIteratorAdapter() { + _next = Threads::first(); + while (_next != NULL && !java_thread_inclusion_predicate(_next)) { + _next = _next->next(); + } +} + +JavaThread* JfrJavaThreadIteratorAdapter::next() { + assert(has_next(), "invariant"); + Type* const temp = _next; + _next = next_java_thread(_next); + assert(temp != _next, "invariant"); + return temp; +} + +JfrNonJavaThreadIteratorAdapter::JfrNonJavaThreadIteratorAdapter() { +} + +bool JfrNonJavaThreadIteratorAdapter::has_next() const { + return false; +} + +Thread* JfrNonJavaThreadIteratorAdapter::next() { + return NULL; +} + +// explicit instantiations +template class JfrThreadIterator; +template class JfrThreadIterator; diff --git a/src/share/vm/jfr/utilities/jfrThreadIterator.hpp b/src/share/vm/jfr/utilities/jfrThreadIterator.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e89cbf2eddd483cfe6cfaad531f36abaea5c32bc --- /dev/null +++ b/src/share/vm/jfr/utilities/jfrThreadIterator.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP +#define SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP + +#include "memory/allocation.hpp" +#include "runtime/thread.hpp" +// #include "runtime/threadSMR.hpp" + +template +class JfrThreadIterator : public AP { + private: + Adapter _adapter; + public: + JfrThreadIterator() : _adapter() {} + typename Adapter::Type* next() { + assert(has_next(), "invariant"); + return _adapter.next(); + } + bool has_next() const { + return _adapter.has_next(); + } +}; + +class JfrJavaThreadIteratorAdapter { + private: + JavaThread* _next; + public: + typedef JavaThread Type; + JfrJavaThreadIteratorAdapter(); + bool has_next() const { + return _next != NULL; + } + Type* next(); +}; + +class JfrNonJavaThreadIteratorAdapter { + public: + typedef Thread Type; + JfrNonJavaThreadIteratorAdapter(); + bool has_next() const; + Type* next(); +}; + +typedef JfrThreadIterator JfrJavaThreadIterator; +typedef JfrThreadIterator JfrNonJavaThreadIterator; + +#endif // SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP diff --git a/src/share/vm/jfr/utilities/jfrTypes.hpp b/src/share/vm/jfr/utilities/jfrTypes.hpp index da14db517f43f0d8fa08377df61dc4bfed39e9b2..91bf7464e0199fa0bc5f281b137fa835417d1f98 100644 --- a/src/share/vm/jfr/utilities/jfrTypes.hpp +++ b/src/share/vm/jfr/utilities/jfrTypes.hpp @@ -26,11 +26,14 @@ #define SHARE_VM_JFR_UTILITIES_JFRTYPES_HPP #include "jfrfiles/jfrEventIds.hpp" +#include "utilities/globalDefinitions.hpp" typedef u8 traceid; 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; @@ -50,4 +53,12 @@ enum EventStartTime { jlong atomic_add_jlong(jlong value, jlong volatile* const dest); +enum JfrCheckpointType { + GENERIC, + FLUSH, + HEADER, + STATICS = 4, + THREADS = 8 +}; + #endif // SHARE_VM_JFR_UTILITIES_JFRTYPES_HPP diff --git a/src/share/vm/jfr/writers/jfrJavaEventWriter.hpp b/src/share/vm/jfr/writers/jfrJavaEventWriter.hpp index 9e521f417a5b85f92cb668e3e1b7ece88b50c128..3dd3770e12318588a0e494c608a4eb6332d3f975 100644 --- a/src/share/vm/jfr/writers/jfrJavaEventWriter.hpp +++ b/src/share/vm/jfr/writers/jfrJavaEventWriter.hpp @@ -32,9 +32,9 @@ class JavaThread; class Thread; class JfrJavaEventWriter : AllStatic { - friend class JfrCheckpointThreadClosure; - friend class JfrJavaEventWriterNotificationClosure; + friend class JfrNotifyClosure; friend class JfrJavaEventWriterNotifyOperation; + friend class JfrJavaEventWriterNotificationClosure; friend class JfrRecorder; private: static bool initialize(); diff --git a/src/share/vm/jfr/writers/jfrStorageAdapter.hpp b/src/share/vm/jfr/writers/jfrStorageAdapter.hpp index 1a47f5bb24bbc50ee32ef6b66c80c3a9a7528e47..24e7431aeaa947f11037f9955a7022580f32b832 100644 --- a/src/share/vm/jfr/writers/jfrStorageAdapter.hpp +++ b/src/share/vm/jfr/writers/jfrStorageAdapter.hpp @@ -82,7 +82,7 @@ class Adapter { 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 @@ class NoOwnershipAdapter { 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; } }; diff --git a/src/share/vm/jfr/writers/jfrWriterHost.inline.hpp b/src/share/vm/jfr/writers/jfrWriterHost.inline.hpp index 240632acc322b1e1269d0ea4f46459bfebc16452..cc97ddb4890f5f6e640f82703ebd269919dff155 100644 --- a/src/share/vm/jfr/writers/jfrWriterHost.inline.hpp +++ b/src/share/vm/jfr/writers/jfrWriterHost.inline.hpp @@ -114,10 +114,7 @@ void WriterHost::write_utf16(const jchar* value, jint template template inline void WriterHost::be_write(T value) { - u1* const pos = ensure_size(sizeof(T)); - if (pos) { - this->set_current_pos(BE::be_write(&value, 1, pos)); - } + be_write(&value, 1); } template diff --git a/src/share/vm/runtime/mutexLocker.cpp b/src/share/vm/runtime/mutexLocker.cpp index 7cb2f08c415c828e6df4f33c96b37c6b4f2aa9d6..3c24e8386ba28738a2c6b046657f7c7af008570b 100644 --- a/src/share/vm/runtime/mutexLocker.cpp +++ b/src/share/vm/runtime/mutexLocker.cpp @@ -334,7 +334,7 @@ void mutex_init() { def(JfrMsg_lock , Monitor, leaf, true); def(JfrBuffer_lock , Mutex, leaf, true); def(JfrThreadGroups_lock , Mutex, leaf, true); - def(JfrStream_lock , Mutex, nonleaf, true); + def(JfrStream_lock , Mutex, nonleaf + 1, false); def(JfrStacktrace_lock , Mutex, special, true); #ifndef SUPPORTS_NATIVE_CX8 diff --git a/src/share/vm/runtime/semaphore.inline.hpp b/src/share/vm/runtime/semaphore.inline.hpp index e1cd4c9f6e7b8df78322c8b1c67900db57a5f014..458bfca8014ebf2e3f8a9c9f46afeb910ea70ca5 100644 --- a/src/share/vm/runtime/semaphore.inline.hpp +++ b/src/share/vm/runtime/semaphore.inline.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_VM_RUNTIME_SEMAPHORE_INLINE_HPP #define SHARE_VM_RUNTIME_SEMAPHORE_INLINE_HPP -#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/interfaceSupport.hpp" #include "runtime/semaphore.hpp" #include "runtime/thread.inline.hpp"