--- a/src/hotspot/share/gc/g1/g1Trace.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1Trace.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -59,10 +59,10 @@
};
static void register_jfr_type_constants() {
- JfrSerializer::register_serializer(TYPE_G1HEAPREGIONTYPE, false, true,
+ JfrSerializer::register_serializer(TYPE_G1HEAPREGIONTYPE, true,
new G1HeapRegionTypeConstant());
- JfrSerializer::register_serializer(TYPE_G1YCTYPE, false, true,
+ JfrSerializer::register_serializer(TYPE_G1YCTYPE, true,
new G1YCTypeConstant());
}
--- a/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -47,7 +47,6 @@
void ShenandoahJFRSupport::register_jfr_type_serializers() {
JfrSerializer::register_serializer(TYPE_SHENANDOAHHEAPREGIONSTATE,
- false,
true,
new ShenandoahHeapRegionStateConstant());
}
--- a/src/hotspot/share/gc/z/zTracer.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/gc/z/zTracer.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -58,11 +58,9 @@
static void register_jfr_type_serializers() {
JfrSerializer::register_serializer(TYPE_ZSTATISTICSCOUNTERTYPE,
- false /* require_safepoint */,
true /* permit_cache */,
new ZStatisticsCounterTypeConstant());
JfrSerializer::register_serializer(TYPE_ZSTATISTICSSAMPLERTYPE,
- false /* require_safepoint */,
true /* permit_cache */,
new ZStatisticsSamplerTypeConstant());
}
--- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -349,6 +349,7 @@
_filename("filename", "Resulting recording filename, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false),
_maxage("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"),
_maxsize("maxsize", "Maximum amount of bytes to keep (on disk) in (k)B, (M)B or (G)B, e.g. 500M, or 0 for no limit", "MEMORY SIZE", false, "0"),
+ _flush_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);
@@ -359,6 +360,7 @@
_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);
};
@@ -411,6 +413,10 @@
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);
@@ -464,7 +470,7 @@
static const char method[] = "execute";
static const char signature[] = "(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Long;"
"Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;"
- "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;";
+ "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
@@ -478,6 +484,7 @@
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);
--- a/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -90,6 +90,7 @@
DCmdArgument<char*> _filename;
DCmdArgument<NanoTimeArgument> _maxage;
DCmdArgument<MemorySizeArgument> _maxsize;
+ DCmdArgument<NanoTimeArgument> _flush_interval;
DCmdArgument<bool> _dump_on_exit;
DCmdArgument<bool> _path_to_gc_roots;
--- a/src/hotspot/share/jfr/jfr.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/jfr.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -71,6 +71,18 @@
JfrThreadLocal::on_exit(t);
}
+void Jfr::exclude_thread(const Thread* t) {
+ JfrThreadLocal::exclude(t);
+}
+
+void Jfr::include_thread(const Thread* t) {
+ JfrThreadLocal::include(t);
+}
+
+bool Jfr::is_excluded(const Thread* t) {
+ return t != NULL && t->jfr_thread_local()->is_excluded();
+}
+
void Jfr::on_java_thread_dismantle(JavaThread* jt) {
if (JfrRecorder::is_recording()) {
JfrCheckpointManager::write_thread_checkpoint(jt);
--- a/src/hotspot/share/jfr/jfr.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/jfr.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -48,6 +48,9 @@
static void on_unloading_classes();
static void on_thread_start(Thread* thread);
static void on_thread_exit(Thread* thread);
+ static void exclude_thread(const Thread* thread);
+ static bool is_excluded(const Thread* thread);
+ static void include_thread(const Thread* thread);
static void on_java_thread_dismantle(JavaThread* jt);
static void on_vm_shutdown(bool exception_handler = false);
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
--- a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -23,7 +23,6 @@
*/
#include "precompiled.hpp"
-#include "jni.h"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/modules.hpp"
#include "classfile/symbolTable.hpp"
@@ -42,9 +41,11 @@
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/jniHandles.inline.hpp"
+#include "runtime/semaphore.inline.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/threadSMR.hpp"
+#include "utilities/growableArray.hpp"
#ifdef ASSERT
void JfrJavaSupport::check_java_thread_in_vm(Thread* t) {
@@ -58,6 +59,12 @@
assert(t->is_Java_thread(), "invariant");
assert(((JavaThread*)t)->thread_state() == _thread_in_native, "invariant");
}
+
+static void check_new_unstarted_java_thread(Thread* t) {
+ assert(t != NULL, "invariant");
+ assert(t->is_Java_thread(), "invariant");
+ assert(((JavaThread*)t)->thread_state() == _thread_new, "invariant");
+}
#endif
/*
@@ -93,6 +100,21 @@
JNIHandles::destroy_global(handle);
}
+jweak JfrJavaSupport::global_weak_jni_handle(const oop obj, Thread* t) {
+ DEBUG_ONLY(check_java_thread_in_vm(t));
+ HandleMark hm(t);
+ return JNIHandles::make_weak_global(Handle(t, obj));
+}
+
+jweak JfrJavaSupport::global_weak_jni_handle(const jobject handle, Thread* t) {
+ const oop obj = JNIHandles::resolve(handle);
+ return obj == NULL ? NULL : global_weak_jni_handle(obj, t);
+}
+
+void JfrJavaSupport::destroy_global_weak_jni_handle(jweak handle) {
+ JNIHandles::destroy_weak_global(handle);
+}
+
oop JfrJavaSupport::resolve_non_null(jobject obj) {
return JNIHandles::resolve_non_null(obj);
}
@@ -603,9 +625,149 @@
return true;
}
-jlong JfrJavaSupport::jfr_thread_id(jobject target_thread) {
+class ThreadExclusionListAccess : public StackObj {
+ private:
+ static Semaphore _mutex_semaphore;
+ public:
+ ThreadExclusionListAccess() { _mutex_semaphore.wait(); }
+ ~ThreadExclusionListAccess() { _mutex_semaphore.signal(); }
+};
+
+Semaphore ThreadExclusionListAccess::_mutex_semaphore(1);
+static GrowableArray<jweak>* exclusion_list = NULL;
+
+static bool equals(const jweak excluded_thread, Handle target_thread) {
+ return JfrJavaSupport::resolve_non_null(excluded_thread) == target_thread();
+}
+
+static int find_exclusion_thread_idx(Handle thread) {
+ if (exclusion_list != NULL) {
+ for (int i = 0; i < exclusion_list->length(); ++i) {
+ if (equals(exclusion_list->at(i), thread)) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+static Handle as_handle(jobject thread) {
+ return Handle(Thread::current(), JfrJavaSupport::resolve_non_null(thread));
+}
+
+static bool thread_is_not_excluded(Handle thread) {
+ return -1 == find_exclusion_thread_idx(thread);
+}
+
+static bool thread_is_not_excluded(jobject thread) {
+ return thread_is_not_excluded(as_handle(thread));
+}
+
+static bool is_thread_excluded(jobject thread) {
+ return !thread_is_not_excluded(thread);
+}
+
+#ifdef ASSERT
+static bool is_thread_excluded(Handle thread) {
+ return !thread_is_not_excluded(thread);
+}
+#endif // ASSERT
+
+static int add_thread_to_exclusion_list(jobject thread) {
+ ThreadExclusionListAccess lock;
+ if (exclusion_list == NULL) {
+ exclusion_list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<jweak>(10, true, mtTracing);
+ }
+ assert(exclusion_list != NULL, "invariant");
+ assert(thread_is_not_excluded(thread), "invariant");
+ jweak ref = JfrJavaSupport::global_weak_jni_handle(thread, Thread::current());
+ const int idx = exclusion_list->append(ref);
+ assert(is_thread_excluded(thread), "invariant");
+ return idx;
+}
+
+static void remove_thread_from_exclusion_list(Handle thread) {
+ assert(exclusion_list != NULL, "invariant");
+ assert(is_thread_excluded(thread), "invariant");
+ assert(exclusion_list != NULL, "invariant");
+ const int idx = find_exclusion_thread_idx(thread);
+ assert(idx >= 0, "invariant");
+ assert(idx < exclusion_list->length(), "invariant");
+ JfrJavaSupport::destroy_global_weak_jni_handle(exclusion_list->at(idx));
+ exclusion_list->delete_at(idx);
+ assert(thread_is_not_excluded(thread), "invariant");
+ if (0 == exclusion_list->length()) {
+ delete exclusion_list;
+ exclusion_list = NULL;
+ }
+}
+
+static void remove_thread_from_exclusion_list(jobject thread) {
+ ThreadExclusionListAccess lock;
+ remove_thread_from_exclusion_list(as_handle(thread));
+}
+
+// includes removal
+static bool check_exclusion_state_on_thread_start(JavaThread* jt) {
+ Handle h_obj(jt, jt->threadObj());
+ ThreadExclusionListAccess lock;
+ if (thread_is_not_excluded(h_obj)) {
+ return false;
+ }
+ remove_thread_from_exclusion_list(h_obj);
+ return true;
+}
+
+jlong JfrJavaSupport::jfr_thread_id(jobject thread) {
ThreadsListHandle tlh;
JavaThread* native_thread = NULL;
- (void)tlh.cv_internal_thread_to_JavaThread(target_thread, &native_thread, NULL);
+ (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
return native_thread != NULL ? JFR_THREAD_ID(native_thread) : 0;
}
+
+void JfrJavaSupport::exclude(jobject thread) {
+ HandleMark hm;
+ ThreadsListHandle tlh;
+ JavaThread* native_thread = NULL;
+ (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
+ if (native_thread != NULL) {
+ JfrThreadLocal::exclude(native_thread);
+ } else {
+ // not started yet, track the thread oop
+ add_thread_to_exclusion_list(thread);
+ }
+}
+
+void JfrJavaSupport::include(jobject thread) {
+ HandleMark hm;
+ ThreadsListHandle tlh;
+ JavaThread* native_thread = NULL;
+ (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
+ if (native_thread != NULL) {
+ JfrThreadLocal::include(native_thread);
+ } else {
+ // not started yet, untrack the thread oop
+ remove_thread_from_exclusion_list(thread);
+ }
+}
+
+bool JfrJavaSupport::is_excluded(jobject thread) {
+ HandleMark hm;
+ ThreadsListHandle tlh;
+ JavaThread* native_thread = NULL;
+ (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
+ return native_thread != NULL ? native_thread->jfr_thread_local()->is_excluded() : is_thread_excluded(thread);
+}
+
+void JfrJavaSupport::on_thread_start(Thread* t) {
+ assert(t != NULL, "invariant");
+ assert(Thread::current() == t, "invariant");
+ if (!t->is_Java_thread()) {
+ return;
+ }
+ DEBUG_ONLY(check_new_unstarted_java_thread(t);)
+ HandleMark hm;
+ if (check_exclusion_state_on_thread_start((JavaThread*)t)) {
+ JfrThreadLocal::exclude(t);
+ }
+}
--- a/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -29,18 +29,21 @@
#include "utilities/exceptions.hpp"
class Klass;
-class JavaThread;
class outputStream;
class JfrJavaSupport : public AllStatic {
public:
static jobject local_jni_handle(const oop obj, Thread* t);
static jobject local_jni_handle(const jobject handle, Thread* t);
- static void destroy_local_jni_handle(const jobject handle);
+ static void destroy_local_jni_handle(jobject handle);
static jobject global_jni_handle(const oop obj, Thread* t);
static jobject global_jni_handle(const jobject handle, Thread* t);
- static void destroy_global_jni_handle(const jobject handle);
+ static void destroy_global_jni_handle(jobject handle);
+
+ static jweak global_weak_jni_handle(const oop obj, Thread* t);
+ static jweak global_weak_jni_handle(const jobject handle, Thread* t);
+ static void destroy_global_weak_jni_handle(jweak handle);
static oop resolve_non_null(jobject obj);
static void notify_all(jobject obj, TRAPS);
@@ -85,7 +88,11 @@
static bool is_jdk_jfr_module_available();
static bool is_jdk_jfr_module_available(outputStream* stream, TRAPS);
- static jlong jfr_thread_id(jobject target_thread);
+ static jlong jfr_thread_id(jobject thread);
+ static void exclude(jobject thread);
+ static void include(jobject thread);
+ static bool is_excluded(jobject thread);
+ static void on_thread_start(Thread* t);
// critical
static void abort(jstring errorMsg, TRAPS);
--- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -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
@@ -284,6 +284,10 @@
return JfrJavaEventWriter::flush(writer, used_size, requested_size, thread);
JVM_END
+JVM_ENTRY_NO_ENV(void, jfr_flush(JNIEnv* env, jobject jvm, jboolean include_metadata))
+ JfrRepository::flush(include_metadata == JNI_TRUE, thread);
+JVM_END
+
JVM_ENTRY_NO_ENV(void, jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location))
return JfrRepository::set_path(location, thread);
JVM_END
@@ -311,3 +315,20 @@
JVM_ENTRY_NO_ENV(void, jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean emit_all))
LeakProfiler::emit_events(cutoff_ticks, emit_all == JNI_TRUE);
JVM_END
+
+JVM_ENTRY_NO_ENV(void, jfr_exclude_thread(JNIEnv* env, jobject jvm, jobject t))
+ JfrJavaSupport::exclude(t);
+JVM_END
+
+JVM_ENTRY_NO_ENV(void, jfr_include_thread(JNIEnv* env, jobject jvm, jobject t))
+ JfrJavaSupport::include(t);
+JVM_END
+
+JVM_ENTRY_NO_ENV(jboolean, jfr_is_thread_excluded(JNIEnv* env, jobject jvm, jobject t))
+ return JfrJavaSupport::is_excluded(t);
+JVM_END
+
+JVM_ENTRY_NO_ENV(jlong, jfr_chunk_start_nanos(JNIEnv* env, jobject jvm))
+ return JfrRepository::current_chunk_start_nanos();
+JVM_END
+
--- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -113,6 +113,7 @@
jboolean JNICALL jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size);
+void JNICALL jfr_flush(JNIEnv* env, jobject jvm, jboolean include_metadata);
void JNICALL jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg);
jlong JNICALL jfr_get_epoch_address(JNIEnv* env, jobject jvm);
@@ -131,6 +132,13 @@
jboolean JNICALL jfr_should_rotate_disk(JNIEnv* env, jobject jvm);
+void JNICALL jfr_exclude_thread(JNIEnv* env, jobject jvm, jobject t);
+
+void JNICALL jfr_include_thread(JNIEnv* env, jobject jvm, jobject t);
+
+jboolean JNICALL jfr_is_thread_excluded(JNIEnv* env, jobject jvm, jobject t);
+
+jlong JNICALL jfr_chunk_start_nanos(JNIEnv* env, jobject jvm);
#ifdef __cplusplus
}
--- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -70,6 +70,7 @@
(char*)"getEventWriter", (char*)"()Ljava/lang/Object;", (void*)jfr_get_event_writer,
(char*)"newEventWriter", (char*)"()Ljdk/jfr/internal/EventWriter;", (void*)jfr_new_event_writer,
(char*)"flush", (char*)"(Ljdk/jfr/internal/EventWriter;II)Z", (void*)jfr_event_writer_flush,
+ (char*)"flush", (char*)"(Z)V", (void*)jfr_flush,
(char*)"setRepositoryLocation", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_repository_location,
(char*)"abort", (char*)"(Ljava/lang/String;)V", (void*)jfr_abort,
(char*)"getEpochAddress", (char*)"()J",(void*)jfr_get_epoch_address,
@@ -79,7 +80,11 @@
(char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count,
(char*)"setCutoff", (char*)"(JJ)Z", (void*)jfr_set_cutoff,
(char*)"emitOldObjectSamples", (char*)"(JZ)V", (void*)jfr_emit_old_object_samples,
- (char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk
+ (char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk,
+ (char*)"exclude", (char*)"(Ljava/lang/Thread;)V", (void*)jfr_exclude_thread,
+ (char*)"include", (char*)"(Ljava/lang/Thread;)V", (void*)jfr_include_thread,
+ (char*)"isExcluded", (char*)"(Ljava/lang/Thread;)Z", (void*)jfr_is_thread_excluded,
+ (char*)"getChunkStartNanos", (char*)"()J", (void*)jfr_chunk_start_nanos
};
const size_t method_array_length = sizeof(method) / sizeof(JNINativeMethod);
--- a/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -259,6 +259,7 @@
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;
}
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -271,7 +271,7 @@
}
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);
@@ -388,7 +388,7 @@
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
@@ -403,7 +403,7 @@
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);
}
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -355,10 +355,6 @@
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 @@
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;
}
}
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -29,6 +29,7 @@
#include "gc/shared/strongRootsScope.hpp"
#include "jfr/leakprofiler/utilities/unifiedOop.hpp"
#include "jfr/leakprofiler/checkpoint/rootResolver.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "memory/iterator.hpp"
#include "memory/universe.hpp"
#include "oops/klass.hpp"
@@ -36,7 +37,6 @@
#include "prims/jvmtiThreadState.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/mutexLocker.hpp"
-#include "runtime/threadSMR.inline.hpp"
#include "runtime/vframe_hp.hpp"
#include "services/management.hpp"
#include "utilities/growableArray.hpp"
@@ -256,8 +256,9 @@
public:
ReferenceToThreadRootClosure(RootCallback& callback) :_callback(callback), _complete(false) {
assert_locked_or_safepoint(Threads_lock);
- for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
- if (do_thread_roots(jt)) {
+ JfrJavaThreadIterator iter;
+ while (iter.has_next()) {
+ if (do_thread_roots(iter.next())) {
return;
}
}
--- a/src/hotspot/share/jfr/leakprofiler/sampling/objectSampler.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/leakprofiler/sampling/objectSampler.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -110,6 +110,9 @@
}
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);
}
--- a/src/hotspot/share/jfr/metadata/jfrSerializer.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/metadata/jfrSerializer.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -70,7 +70,8 @@
class JfrSerializer : public CHeapObj<mtTracing> {
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;
};
--- a/src/hotspot/share/jfr/metadata/metadata.xml Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/metadata/metadata.xml Wed Oct 09 23:22:56 2019 +0200
@@ -154,7 +154,7 @@
<Field type="string" name="newValue" label="New Value" />
<Field type="FlagValueOrigin" name="origin" label="Origin" />
</Event>
-
+
<Type name="VirtualSpace">
<Field type="ulong" contentType="address" name="start" label="Start Address" description="Start address of the virtual space" />
<Field type="ulong" contentType="address" name="committedEnd" label="Committed End Address" description="End address of the committed memory for the virtual space" />
@@ -162,27 +162,27 @@
<Field type="ulong" contentType="address" name="reservedEnd" label="Reserved End Address" description="End address of the reserved memory for the virtual space" />
<Field type="ulong" contentType="bytes" name="reservedSize" label="Reserved Size" description="Size of the reserved memory for the virtual space" />
</Type>
-
+
<Type name="ObjectSpace">
<Field type="ulong" contentType="address" name="start" label="Start Address" description="Start address of the space" />
<Field type="ulong" contentType="address" name="end" label="End Address" description="End address of the space" />
<Field type="ulong" contentType="bytes" name="used" label="Used" description="Bytes allocated by objects in the space" />
<Field type="ulong" contentType="bytes" name="size" label="Size" description="Size of the space" />
</Type>
-
+
<Event name="GCHeapSummary" category="Java Virtual Machine, GC, Heap" label="Heap Summary" startTime="false">
<Field type="uint" name="gcId" label="GC Identifier" relation="GcId" />
<Field type="GCWhen" name="when" label="When" />
<Field type="VirtualSpace" struct="true" name="heapSpace" label="Heap Space" />
<Field type="ulong" contentType="bytes" name="heapUsed" label="Heap Used" description="Bytes allocated by objects in the heap" />
</Event>
-
+
<Type name="MetaspaceSizes">
<Field type="ulong" contentType="bytes" name="committed" label="Committed" description="Committed memory for this space" />
<Field type="ulong" contentType="bytes" name="used" label="Used" description="Bytes allocated by objects in the space" />
<Field type="ulong" contentType="bytes" name="reserved" label="Reserved" description="Reserved memory for this space" />
</Type>
-
+
<Event name="MetaspaceSummary" category="Java Virtual Machine, GC, Heap" label="Metaspace Summary" startTime="false">
<Field type="uint" name="gcId" label="GC Identifier" relation="GcId" />
<Field type="GCWhen" name="when" label="When" />
@@ -442,7 +442,7 @@
<Field type="uint" name="gcWorkerId" label="GC Worker Identifier" />
<Field type="string" name="name" label="Name" />
</Event>
-
+
<Event name="AllocationRequiringGC" category="Java Virtual Machine, GC, Detailed" label="Allocation Requiring GC" thread="true" stackTrace="true"
startTime="false">
<Field type="uint" name="gcId" label="Pending GC Identifier" relation="GcId" />
@@ -484,7 +484,7 @@
<Field type="string" name="failureMessage" label="Failure Message" />
<Field type="uint" name="compileId" label="Compilation Identifier" relation="CompileId" />
</Event>
-
+
<Type name="CalleeMethod">
<Field type="string" name="type" label="Class" />
<Field type="string" name="name" label="Method Name" />
@@ -585,21 +585,21 @@
<Field type="OldObjectGcRoot" name="root" label="GC Root" />
</Event>
- <Event name="DumpReason" category="Flight Recorder" label="Recording Reason"
- description="Who requested the recording and why"
+ <Event name="DumpReason" category="Flight Recorder" label="Recording Reason"
+ description="Who requested the recording and why"
startTime="false">
<Field type="string" name="reason" label="Reason" description="Reason for writing recording data to disk" />
<Field type="int" name="recordingId" label="Recording Id" description="Id of the recording that triggered the dump, or -1 if it was not related to a recording" />
</Event>
- <Event name="DataLoss" category="Flight Recorder" label="Data Loss"
+ <Event name="DataLoss" category="Flight Recorder" label="Data Loss"
description="Data could not be copied out from a buffer, typically because of contention"
startTime="false">
<Field type="ulong" contentType="bytes" name="amount" label="Amount" description="Amount lost data" />
<Field type="ulong" contentType="bytes" name="total" label="Total" description="Total lost amount for thread" />
</Event>
- <Event name="JVMInformation" category="Java Virtual Machine" label="JVM Information"
+ <Event name="JVMInformation" category="Java Virtual Machine" label="JVM Information"
description="Description of JVM and the Java application"
period="endChunk">
<Field type="string" name="jvmName" label="JVM Name" />
@@ -1004,6 +1004,42 @@
<Field type="string" name="state" label="State" />
</Type>
+ <Event name="Flush" category="Flight Recorder" label="Flush" thread="false" experimental="true">
+ <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+ <Field type="ulong" name="elements" label="Elements Written" />
+ <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+ </Event>
+
+ <Event name="FlushStorage" category="Flight Recorder" label="Flush Storage" thread="false" experimental="true">
+ <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+ <Field type="ulong" name="elements" label="Elements Written" />
+ <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+ </Event>
+
+ <Event name="FlushStacktrace" category="Flight Recorder" label="Flush Stacktrace" thread="false" experimental="true">
+ <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+ <Field type="ulong" name="elements" label="Elements Written" />
+ <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+ </Event>
+
+ <Event name="FlushStringPool" category="Flight Recorder" label="Flush String Pool" thread="false" experimental="true">
+ <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+ <Field type="ulong" name="elements" label="Elements Written" />
+ <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+ </Event>
+
+ <Event name="FlushMetadata" category="Flight Recorder" label="Flush Metadata" thread="false" experimental="true">
+ <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+ <Field type="ulong" name="elements" label="Elements Written" />
+ <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+ </Event>
+
+ <Event name="FlushTypeSet" category="Flight Recorder" label="Flush Type Set" thread="false" experimental="true">
+ <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+ <Field type="ulong" name="elements" label="Elements Written" />
+ <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+ </Event>
+
<Type name="ZStatisticsCounterType" label="Z Statistics Counter">
<Field type="string" name="counter" label="Counter" />
</Type>
@@ -1183,35 +1219,40 @@
<Field type="int" name="bytecodeIndex" label="Bytecode Index" />
<Field type="FrameType" name="type" label="Frame Type" />
</Type>
-
+
+ <Type name="ChunkHeader" label="Chunk Header">
+ <Field type="byte" array="true" name="payload" label="Payload" />
+ </Type>
+
<Relation name="JavaMonitorAddress"/>
<Relation name="SafepointId"/>
<Relation name="GcId"/>
<Relation name="CompileId" />
<Relation name="SweepId"/>
-
- <XmlType name="Package" parameterType="const PackageEntry*" fieldType="const PackageEntry*"/>
- <XmlType name="Class" javaType="java.lang.Class" parameterType="const Klass*" fieldType="const Klass*"/>
- <XmlType name="Module" parameterType="const ModuleEntry*" fieldType="const ModuleEntry*"/>
- <XmlType name="ClassLoader" parameterType="const ClassLoaderData*" fieldType="const ClassLoaderData*"/>
- <XmlType name="Method" parameterType="const Method*" fieldType="const Method*"/>
- <XmlType name="Thread" javaType="java.lang.Thread" parameterType="u8" fieldType="u8"/>
- <XmlType name="Tickspan" contentType="tickspan" javaType="long" parameterType="const Tickspan&" fieldType="Tickspan"/>
- <XmlType name="Ticks" contentType="tickstamp" javaType="long" parameterType="const Ticks&" fieldType="Ticks"/>
- <XmlType name="ulong" javaType="long" unsigned="true" parameterType="u8" fieldType="u8"/>
- <XmlType name="uint" javaType="int" unsigned="true" parameterType="unsigned" fieldType="unsigned"/>
- <XmlType name="ushort" javaType="short" unsigned="true" parameterType="u2" fieldType="u2"/>
- <XmlType name="ubyte" javaType="byte" unsigned="true" parameterType="u1" fieldType="u1"/>
- <XmlType name="long" javaType="long" parameterType="s8" fieldType="s8"/>
- <XmlType name="int" javaType="int" parameterType="s4" fieldType="s4"/>
- <XmlType name="short" javaType="short" parameterType="s2" fieldType="s2"/>
- <XmlType name="byte" javaType="byte" parameterType="s1" fieldType="s1"/>
- <XmlType name="double" javaType="double" parameterType="double" fieldType="double"/>
- <XmlType name="float" javaType="float" parameterType="float" fieldType="float"/>
- <XmlType name="boolean" javaType="boolean" parameterType="bool" fieldType="bool"/>
- <XmlType name="char" javaType="char" parameterType="char" fieldType="char"/>
- <XmlType name="string" javaType="java.lang.String" parameterType="const char*" fieldType="const char*"/>
-
+ <Relation name="FlushId"/>
+
+ <XmlType name="Package" parameterType="const PackageEntry*" fieldType="const PackageEntry*"/>
+ <XmlType name="Class" javaType="java.lang.Class" parameterType="const Klass*" fieldType="const Klass*"/>
+ <XmlType name="Module" parameterType="const ModuleEntry*" fieldType="const ModuleEntry*"/>
+ <XmlType name="ClassLoader" parameterType="const ClassLoaderData*" fieldType="const ClassLoaderData*"/>
+ <XmlType name="Method" parameterType="const Method*" fieldType="const Method*"/>
+ <XmlType name="Thread" javaType="java.lang.Thread" parameterType="u8" fieldType="u8"/>
+ <XmlType name="Tickspan" contentType="tickspan" javaType="long" parameterType="const Tickspan&" fieldType="Tickspan"/>
+ <XmlType name="Ticks" contentType="tickstamp" javaType="long" parameterType="const Ticks&" fieldType="Ticks"/>
+ <XmlType name="ulong" javaType="long" unsigned="true" parameterType="u8" fieldType="u8"/>
+ <XmlType name="uint" javaType="int" unsigned="true" parameterType="unsigned" fieldType="unsigned"/>
+ <XmlType name="ushort" javaType="short" unsigned="true" parameterType="u2" fieldType="u2"/>
+ <XmlType name="ubyte" javaType="byte" unsigned="true" parameterType="u1" fieldType="u1"/>
+ <XmlType name="long" javaType="long" parameterType="s8" fieldType="s8"/>
+ <XmlType name="int" javaType="int" parameterType="s4" fieldType="s4"/>
+ <XmlType name="short" javaType="short" parameterType="s2" fieldType="s2"/>
+ <XmlType name="byte" javaType="byte" parameterType="s1" fieldType="s1"/>
+ <XmlType name="double" javaType="double" parameterType="double" fieldType="double"/>
+ <XmlType name="float" javaType="float" parameterType="float" fieldType="float"/>
+ <XmlType name="boolean" javaType="boolean" parameterType="bool" fieldType="bool"/>
+ <XmlType name="char" javaType="char" parameterType="char" fieldType="char"/>
+ <XmlType name="string" javaType="java.lang.String" parameterType="const char*" fieldType="const char*"/>
+
<XmlContentType name="bytes" annotation="jdk.jfr.DataAmount(BYTES)" />
<XmlContentType name="tickstamp" annotation="jdk.jfr.Timestamp(TICKS)" />
<XmlContentType name="epochmillis" annotation="jdk.jfr.Timestamp(MILLISECONDS_SINCE_EPOCH)" />
@@ -1223,5 +1264,5 @@
<XmlContentType name="hertz" annotation="jdk.jfr.Frequency" />
<XmlContentType name="bytes-per-second" annotation="jdk.jfr.DataAmount(BYTES), jdk.jfr.Frequency" />
<XmlContentType name="bits-per-second" annotation="jdk.jfr.DataAmount(BITS), jdk.jfr.Frequency" />
-
+
</Metadata>
--- a/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -39,7 +39,7 @@
traceid id;
uint64_t bytes_in;
uint64_t bytes_out;
- bool in_use;
+ mutable bool written;
};
static GrowableArray<InterfaceEntry>* _interfaces = NULL;
@@ -71,7 +71,7 @@
entry.id = ++interface_id;
entry.bytes_in = iface->get_bytes_in();
entry.bytes_out = iface->get_bytes_out();
- entry.in_use = false;
+ entry.written = false;
return _interfaces->at(_interfaces->append(entry));
}
@@ -108,6 +108,39 @@
return ((current - old) * NANOSECS_PER_SEC) / interval.nanoseconds();
}
+class JfrNetworkInterfaceName : public JfrSerializer {
+ public:
+ void serialize(JfrCheckpointWriter& writer) {} // we write each constant lazily
+
+ void on_rotation() {
+ for (int i = 0; i < _interfaces->length(); ++i) {
+ const InterfaceEntry& entry = _interfaces->at(i);
+ if (entry.written) {
+ entry.written = false;
+ }
+ }
+ }
+};
+
+static bool register_network_interface_name_serializer() {
+ assert(_interfaces != NULL, "invariant");
+ return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME,
+ false, // disallow caching; we want a callback every rotation
+ new JfrNetworkInterfaceName());
+}
+
+static void write_interface_constant(const InterfaceEntry& entry) {
+ if (entry.written) {
+ return;
+ }
+ JfrCheckpointWriter writer;
+ writer.write_type(TYPE_NETWORKINTERFACENAME);
+ writer.write_count(1);
+ writer.write_key(entry.id);
+ writer.write(entry.name);
+ entry.written = true;
+}
+
static bool get_interfaces(NetworkInterface** network_interfaces) {
const int ret_val = JfrOSInterface::network_utilization(network_interfaces);
if (ret_val == OS_ERR) {
@@ -117,39 +150,6 @@
return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED;
}
-class JfrNetworkInterfaceName : public JfrSerializer {
- public:
- void serialize(JfrCheckpointWriter& writer) {
- assert(_interfaces != NULL, "invariant");
- const JfrCheckpointContext ctx = writer.context();
- const intptr_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet
- int active_interfaces = 0;
- for (int i = 0; i < _interfaces->length(); ++i) {
- InterfaceEntry& entry = _interfaces->at(i);
- if (entry.in_use) {
- entry.in_use = false;
- writer.write_key(entry.id);
- writer.write(entry.name);
- ++active_interfaces;
- }
- }
- if (active_interfaces == 0) {
- // nothing to write, restore context
- writer.set_context(ctx);
- return;
- }
- writer.write_count(active_interfaces, count_offset);
- }
-};
-
-static bool register_network_interface_name_serializer() {
- assert(_interfaces != NULL, "invariant");
- return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME,
- false, // require safepoint
- false, // disallow caching; we want a callback every rotation
- new JfrNetworkInterfaceName());
-}
-
void JfrNetworkUtilization::send_events() {
ResourceMark rm;
NetworkInterface* network_interfaces;
@@ -169,7 +169,7 @@
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);
--- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -44,6 +44,7 @@
#include "jfr/periodic/jfrNetworkUtilization.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/support/jfrThreadId.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "jfrfiles/jfrPeriodic.hpp"
#include "logging/log.hpp"
@@ -56,7 +57,6 @@
#include "runtime/os.hpp"
#include "runtime/os_perf.hpp"
#include "runtime/thread.inline.hpp"
-#include "runtime/threadSMR.hpp"
#include "runtime/sweeper.hpp"
#include "runtime/vmThread.hpp"
#include "services/classLoadingService.hpp"
@@ -410,13 +410,12 @@
GrowableArray<jlong> allocated(initial_size);
GrowableArray<traceid> thread_ids(initial_size);
JfrTicks time_stamp = JfrTicks::now();
- {
- // Collect allocation statistics while holding threads lock
- MutexLocker ml(Threads_lock);
- for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
- allocated.append(jt->cooked_allocated_bytes());
- thread_ids.append(JFR_THREAD_ID(jt));
- }
+ 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.
--- a/src/hotspot/share/jfr/periodic/jfrThreadCPULoadEvent.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/periodic/jfrThreadCPULoadEvent.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,11 +28,10 @@
#include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
#include "jfr/support/jfrThreadId.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "utilities/globalDefinitions.hpp"
#include "runtime/os.hpp"
-#include "runtime/thread.inline.hpp"
-#include "runtime/threadSMR.inline.hpp"
jlong JfrThreadCPULoadEvent::get_wallclock_time() {
return os::javaTimeNanos();
@@ -115,8 +114,12 @@
JfrTicks event_time = JfrTicks::now();
jlong cur_wallclock_time = JfrThreadCPULoadEvent::get_wallclock_time();
- JavaThreadIteratorWithHandle jtiwh;
- while (JavaThread* jt = jtiwh.next()) {
+ JfrJavaThreadIterator iter;
+ int number_of_threads = 0;
+ while (iter.has_next()) {
+ JavaThread* const jt = iter.next();
+ assert(jt != NULL, "invariant");
+ ++number_of_threads;
EventThreadCPULoad event(UNTIMED);
if (JfrThreadCPULoadEvent::update_event(event, jt, cur_wallclock_time, processor_count)) {
event.set_starttime(event_time);
@@ -129,7 +132,7 @@
event.commit();
}
}
- log_trace(jfr)("Measured CPU usage for %d threads in %.3f milliseconds", jtiwh.length(),
+ log_trace(jfr)("Measured CPU usage for %d threads in %.3f milliseconds", number_of_threads,
(double)(JfrTicks::now() - event_time).milliseconds());
// Restore this thread's thread id
periodic_thread_tl->set_thread_id(periodic_thread_id);
--- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -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 "logging/log.hpp"
#include "runtime/frame.inline.hpp"
@@ -352,9 +353,14 @@
}
}
+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;
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -34,11 +34,14 @@
#include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/utilities/jfrBigEndian.hpp"
+#include "jfr/utilities/jfrIterator.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "jfr/utilities/jfrTypes.hpp"
+#include "jfr/writers/jfrJavaEventWriter.hpp"
#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/handles.inline.hpp"
-#include "runtime/mutexLocker.hpp"
+#include "runtime/mutex.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/os.inline.hpp"
#include "runtime/safepoint.hpp"
@@ -81,7 +84,7 @@
if (_lock != NULL) {
delete _lock;
}
- JfrTypeManager::clear();
+ JfrTypeManager::destroy();
}
static const size_t unlimited_mspace_size = 0;
@@ -165,7 +168,7 @@
}
bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const {
- return _service_thread != thread && OrderAccess::load_acquire(&_checkpoint_epoch_state) != JfrTraceIdEpoch::epoch();
+ return _service_thread != thread && _checkpoint_epoch_state != JfrTraceIdEpoch::epoch();
}
static const size_t lease_retry = 10;
@@ -178,12 +181,24 @@
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();
@@ -199,7 +214,7 @@
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);
}
@@ -210,8 +225,8 @@
// 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 <typename Return>
@@ -231,21 +246,21 @@
return read_data<jlong>(data + duration_offset);
}
-static bool is_flushpoint(const u1* data) {
- return read_data<juint>(data + flushpoint_offset) == (juint)1;
+static u1 checkpoint_type(const u1* data) {
+ return read_data<u1>(data + checkpoint_type_offset);
}
static juint number_of_types(const u1* data) {
return read_data<juint>(data + types_offset);
}
-static void write_checkpoint_header(JfrChunkWriter& cw, int64_t offset_prev_cp_event, const u1* data) {
+static void write_checkpoint_header(JfrChunkWriter& cw, int64_t delta_to_last_checkpoint, const u1* data) {
cw.reserve(sizeof(u4));
cw.write<u8>(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));
}
@@ -258,9 +273,9 @@
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<u4>(event_size, event_begin);
@@ -302,13 +317,13 @@
typedef CheckpointWriteOp<JfrCheckpointMspace::Type> WriteOperation;
typedef ReleaseOp<JfrCheckpointMspace> CheckpointReleaseOperation;
-template <template <typename> class WriterHost, template <typename, typename> class CompositeOperation>
+template <template <typename> class WriterHost, template <typename, typename, typename> class CompositeOperation>
static size_t write_mspace(JfrCheckpointMspace* mspace, JfrChunkWriter& chunkwriter) {
assert(mspace != NULL, "invariant");
WriteOperation wo(chunkwriter);
WriterHost<WriteOperation> wh(wo);
CheckpointReleaseOperation cro(mspace, Thread::current(), false);
- CompositeOperation<WriterHost<WriteOperation>, CheckpointReleaseOperation> co(&wh, &cro);
+ CompositeOperation<WriterHost<WriteOperation>, CheckpointReleaseOperation, CompositeOperationAnd> co(&wh, &cro);
assert(mspace->is_full_empty(), "invariant");
process_free_list(co, mspace);
return wo.processed();
@@ -330,47 +345,126 @@
return write_mspace<ExclusiveOp, CompositeOperation>(_epoch_transition_mspace, _chunkwriter);
}
+typedef MutexedWriteOp<WriteOperation> FlushOperation;
+
+size_t JfrCheckpointManager::flush() {
+ WriteOperation wo(_chunkwriter);
+ FlushOperation fo(wo);
+ assert(_free_list_mspace->is_full_empty(), "invariant");
+ process_free_list(fo, _free_list_mspace);
+ return wo.processed();
+}
+
typedef DiscardOp<DefaultDiscarder<JfrBuffer> > DiscardOperation;
size_t JfrCheckpointManager::clear() {
+ JfrTypeManager::clear();
DiscardOperation discarder(mutexed); // mutexed discard mode
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::on_rotation() {
+ assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ JfrTypeManager::on_rotation();
+ notify_threads();
+ shift_epoch();
+}
+
void JfrCheckpointManager::write_type_set() {
JfrTypeManager::write_type_set();
+ write();
}
void JfrCheckpointManager::write_type_set_for_unloaded_classes() {
- assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
JfrTypeManager::write_type_set_for_unloaded_classes();
}
-void JfrCheckpointManager::create_thread_blob(JavaThread* jt) {
- JfrTypeManager::create_thread_blob(jt);
+bool JfrCheckpointManager::is_type_set_required() {
+ return JfrTraceIdEpoch::has_changed_tag_state();
+}
+
+size_t JfrCheckpointManager::flush_type_set() {
+ const size_t elements = JfrTypeManager::flush_type_set();
+ flush();
+ return elements;
+}
+
+void JfrCheckpointManager::flush_static_type_set() {
+ flush();
+}
+
+void JfrCheckpointManager::create_thread_blob(Thread* t) {
+ JfrTypeManager::create_thread_blob(t);
}
-void JfrCheckpointManager::write_thread_checkpoint(JavaThread* jt) {
- JfrTypeManager::write_thread_checkpoint(jt);
+void JfrCheckpointManager::write_thread_checkpoint(Thread* t) {
+ JfrTypeManager::write_thread_checkpoint(t);
}
-void JfrCheckpointManager::shift_epoch() {
- debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();)
- JfrTraceIdEpoch::shift_epoch();
- assert(current_epoch != JfrTraceIdEpoch::current(), "invariant");
+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());
+ }
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -68,18 +68,29 @@
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 @@
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;
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -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 @@
}
}
-static void write_checkpoint_header(u1* pos, int64_t size, jlong time, bool flushpoint, u4 type_count) {
+JfrCheckpointWriter::JfrCheckpointWriter(Thread* t, JfrBuffer* buffer, JfrCheckpointType type /* GENERIC */) :
+ JfrCheckpointWriterBase(buffer, t),
+ _time(JfrTicks::now()),
+ _offset(0),
+ _count(0),
+ _type(type),
+ _header(true) {
+ assert(this->is_acquired(), "invariant");
+ assert(0 == this->current_offset(), "invariant");
+ if (_header) {
+ reserve(sizeof(JfrCheckpointEntry));
+ }
+}
+
+static void write_checkpoint_header(u1* pos, int64_t size, jlong time, u4 checkpoint_type, u4 type_count) {
assert(pos != NULL, "invariant");
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 ? (u4)1 : (u4)0);
+ be_writer.write(checkpoint_type);
be_writer.write(type_count);
assert(be_writer.is_valid(), "invariant");
}
@@ -74,18 +102,10 @@
assert(this->used_size() > sizeof(JfrCheckpointEntry), "invariant");
const int64_t size = this->current_offset();
assert(size + this->start_pos() == this->current_pos(), "invariant");
- write_checkpoint_header(const_cast<u1*>(this->start_pos()), size, _time, is_flushpoint(), count());
+ write_checkpoint_header(const_cast<u1*>(this->start_pos()), size, _time, (u4)_type, count());
release();
}
-void JfrCheckpointWriter::set_flushpoint(bool flushpoint) {
- _flushpoint = flushpoint;
-}
-
-bool JfrCheckpointWriter::is_flushpoint() const {
- return _flushpoint;
-}
-
u4 JfrCheckpointWriter::count() const {
return _count;
}
@@ -140,7 +160,7 @@
}
*size = this->used_size();
assert(this->start_pos() + *size == this->current_pos(), "invariant");
- write_checkpoint_header(const_cast<u1*>(this->start_pos()), this->used_offset(), _time, is_flushpoint(), count());
+ write_checkpoint_header(const_cast<u1*>(this->start_pos()), this->used_offset(), _time, (u4)_type, count());
_header = false; // the header was just written
if (move) {
this->seek(_offset);
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -54,23 +54,24 @@
};
class JfrCheckpointWriter : public JfrCheckpointWriterBase {
+ friend class JfrCheckpointManager;
friend class JfrSerializerRegistration;
private:
JfrTicks _time;
int64_t _offset;
u4 _count;
- bool _flushpoint;
+ JfrCheckpointType _type;
bool _header;
u4 count() const;
void set_count(u4 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);
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,61 +29,53 @@
#include "oops/klass.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
-#include "runtime/semaphore.hpp"
#include "runtime/thread.inline.hpp"
-static jbyteArray _metadata_blob = NULL;
-static Semaphore metadata_mutex_semaphore(1);
+static jbyteArray metadata_blob = NULL;
+static u8 metadata_id = 0;
+static u8 last_written_metadata_id = 0;
-void JfrMetadataEvent::lock() {
- metadata_mutex_semaphore.wait();
-}
-
-void JfrMetadataEvent::unlock() {
- metadata_mutex_semaphore.signal();
+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->is_array_klass(), "invariant");
+ const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast(k);
+ const jbyte* const data_address = arr->byte_at_addr(0);
+ chunkwriter.write_unbuffered(data_address, length);
}
-static void write_metadata_blob(JfrChunkWriter& chunkwriter, jbyteArray metadata_blob) {
- if (metadata_blob != NULL) {
- const typeArrayOop arr = (typeArrayOop)JfrJavaSupport::resolve_non_null(metadata_blob);
- assert(arr != NULL, "invariant");
- const int length = arr->length();
- const Klass* const k = arr->klass();
- assert(k != NULL && k->is_array_klass(), "invariant");
- const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast(k);
- const jbyte* const data_address = arr->byte_at_addr(0);
- chunkwriter.write_unbuffered(data_address, length);
+void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
+ assert(chunkwriter.is_valid(), "invariant");
+ if (last_written_metadata_id == metadata_id && chunkwriter.has_metadata()) {
+ return;
}
-}
-
-// the semaphore is assumed to be locked (was locked previous safepoint)
-size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter, jlong metadata_offset) {
- assert(chunkwriter.is_valid(), "invariant");
- assert(chunkwriter.current_offset() == metadata_offset, "invariant");
// header
- chunkwriter.reserve(sizeof(u4));
+ const int64_t metadata_offset = chunkwriter.reserve(sizeof(u4));
chunkwriter.write<u8>(EVENT_METADATA); // ID 0
// time data
chunkwriter.write(JfrTicks::now());
chunkwriter.write((u8)0); // duration
- chunkwriter.write((u8)0); // metadata id
- 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
+ last_written_metadata_id = metadata_id;
// 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);
}
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;
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -33,13 +33,10 @@
//
// 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);
};
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -25,7 +25,6 @@
#include "precompiled.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
-#include "jfr/utilities/jfrResourceManager.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/jniHandles.inline.hpp"
@@ -33,6 +32,8 @@
#include "runtime/semaphore.hpp"
#include "utilities/growableArray.hpp"
+static const int initial_array_size = 30;
+
class ThreadGroupExclusiveAccess : public StackObj {
private:
static Semaphore _mutex_semaphore;
@@ -257,12 +258,10 @@
}
}
-JfrThreadGroup::JfrThreadGroup() : _list(NULL) {
- _list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(30, true);
-}
+JfrThreadGroup::JfrThreadGroup() :
+ _list(new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(initial_array_size, true, mtTracing)) {}
JfrThreadGroup::~JfrThreadGroup() {
- assert(SafepointSynchronize::is_at_safepoint(), "invariant");
if (_list != NULL) {
for (int i = 0; i < _list->length(); i++) {
JfrThreadGroupEntry* e = _list->at(i);
@@ -281,14 +280,11 @@
}
traceid JfrThreadGroup::thread_group_id(const JavaThread* jt, Thread* current) {
- ResourceMark rm(current);
- HandleMark hm(current);
JfrThreadGroupsHelper helper(jt, current);
return helper.is_valid() ? thread_group_id_internal(helper) : 0;
}
traceid JfrThreadGroup::thread_group_id(JavaThread* const jt) {
- assert(!JfrStream_lock->owned_by_self(), "holding stream lock but should not hold it here");
return thread_group_id(jt, jt);
}
@@ -396,9 +392,7 @@
ThreadGroupExclusiveAccess lock;
JfrThreadGroup* tg_instance = instance();
assert(tg_instance != NULL, "invariant");
- ResourceManager<JfrThreadGroup> tg_handle(tg_instance);
- set_instance(NULL);
- tg_handle->write_thread_group_entries(writer);
+ tg_instance->write_thread_group_entries(writer);
}
// for writing a particular thread group
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,9 +23,13 @@
*/
#include "precompiled.hpp"
+#include "classfile/javaClasses.inline.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 @@
}
}
+traceid JfrThreadId::id(const Thread* t) {
+ assert(t != NULL, "invariant");
+ if (!t->is_Java_thread()) {
+ return os_id(t);
+ }
+ const JavaThread* const jt = (JavaThread*)t;
+ const oop thread_obj = jt->threadObj();
+ return thread_obj != NULL ? java_lang_Thread::thread_id(thread_obj) : 0;
+}
+
+traceid JfrThreadId::os_id(const Thread* t) {
+ assert(t != NULL, "invariant");
+ const OSThread* const os_thread = t->osthread();
+ return os_thread != NULL ? os_thread->thread_id() : 0;
+}
+
+traceid JfrThreadId::jfr_id(const Thread* t) {
+ assert(t != NULL, "invariant");
+ return t->jfr_thread_local()->thread_id();
+}
+
+// caller needs ResourceMark
+const char* get_java_thread_name(const Thread* t) {
+ assert(t != NULL, "invariant");
+ assert(t->is_Java_thread(), "invariant");
+ const JavaThread* const jt = ((JavaThread*)t);
+ const char* name_str = "<no-name - thread name unresolved>";
+ const oop thread_obj = jt->threadObj();
+ if (thread_obj != NULL) {
+ const oop name = java_lang_Thread::name(thread_obj);
+ if (name != NULL) {
+ name_str = java_lang_String::as_utf8_string(name);
+ }
+ } else if (jt->is_attaching_via_jni()) {
+ name_str = "<no-name - thread is attaching>";
+ }
+ assert(name_str != NULL, "unexpected NULL thread name");
+ return name_str;
+}
+
+const char* JfrThreadName::name(const Thread* t) {
+ assert(t != NULL, "invariant");
+ return t->is_Java_thread() ? get_java_thread_name(t) : t->name();
+}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -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_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -37,8 +37,7 @@
#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
#include "jfr/recorder/checkpoint/types/jfrThreadState.hpp"
#include "jfr/recorder/checkpoint/types/jfrTypeSet.hpp"
-#include "jfr/support/jfrThreadLocal.hpp"
-#include "jfr/writers/jfrJavaEventWriter.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "memory/metaspaceGCThresholdUpdater.hpp"
#include "memory/referenceType.hpp"
#include "memory/universe.hpp"
@@ -85,27 +84,18 @@
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<traceid>(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<traceid>(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
@@ -114,13 +104,18 @@
}
void JfrThreadConstantSet::serialize(JfrCheckpointWriter& writer) {
- assert(SafepointSynchronize::is_at_safepoint(), "invariant");
JfrCheckpointThreadClosure tc(writer);
- Threads::threads_do(&tc);
+ JfrJavaThreadIterator javathreads;
+ while (javathreads.has_next()) {
+ tc.do_thread(javathreads.next());
+ }
+ JfrNonJavaThreadIterator nonjavathreads;
+ while (nonjavathreads.has_next()) {
+ tc.do_thread(nonjavathreads.next());
+ }
}
void JfrThreadGroupConstant::serialize(JfrCheckpointWriter& writer) {
- assert(SafepointSynchronize::is_at_safepoint(), "invariant");
JfrThreadGroup::serialize(writer);
}
@@ -274,25 +269,50 @@
class TypeSetSerialization {
private:
JfrCheckpointWriter* _leakp_writer;
- bool _class_unload;
+ size_t _elements;
public:
- TypeSetSerialization(bool class_unload, JfrCheckpointWriter* leakp_writer = NULL) :
- _leakp_writer(leakp_writer), _class_unload(class_unload) {}
- void write(JfrCheckpointWriter& writer) {
- JfrTypeSet::serialize(&writer, _leakp_writer, _class_unload);
+ TypeSetSerialization(JfrCheckpointWriter* leakp_writer = NULL) :
+ _leakp_writer(leakp_writer), _elements(0) {}
+ void write(JfrCheckpointWriter& writer, bool class_unload, bool flushpoint) {
+ assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
+ assert_locked_or_safepoint(Module_lock);
+ _elements = JfrTypeSet::serialize(&writer, _leakp_writer, class_unload, flushpoint);
+ }
+ size_t elements() const {
+ return _elements;
}
};
void ClassUnloadTypeSet::serialize(JfrCheckpointWriter& writer) {
- TypeSetSerialization type_set(true);
- type_set.write(writer);
+ TypeSetSerialization type_set;
+ type_set.write(writer, true, false);
};
+void FlushTypeSet::serialize(JfrCheckpointWriter& writer) {
+ assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
+ MutexLocker cld_lock(ClassLoaderDataGraph_lock);
+ MutexLocker module_lock(Module_lock);
+ TypeSetSerialization type_set;
+ type_set.write(writer, false, true);
+ _elements = type_set.elements();
+}
+
+size_t FlushTypeSet::elements() const {
+ return _elements;
+}
+
TypeSet::TypeSet(JfrCheckpointWriter* leakp_writer) : _leakp_writer(leakp_writer) {}
+void TypeSet::clear() {
+ JfrTypeSet::clear();
+}
+
void TypeSet::serialize(JfrCheckpointWriter& writer) {
- TypeSetSerialization type_set(false, _leakp_writer);
- type_set.write(writer);
+ assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
+ MutexLocker cld_lock(ClassLoaderDataGraph_lock);
+ MutexLocker module_lock(Module_lock);
+ TypeSetSerialization type_set(_leakp_writer);
+ type_set.write(writer, false, false);
};
void ThreadStateConstant::serialize(JfrCheckpointWriter& writer) {
@@ -302,19 +322,21 @@
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
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -27,14 +27,13 @@
#include "jfr/metadata/jfrSerializer.hpp"
-class JfrThreadConstantSet : public JfrSerializer {
+class TypeSet : public JfrSerializer {
+ private:
+ JfrCheckpointWriter* _leakp_writer;
public:
+ explicit TypeSet(JfrCheckpointWriter* leakp_writer = NULL);
void serialize(JfrCheckpointWriter& writer);
-};
-
-class JfrThreadGroupConstant : public JfrSerializer {
- public:
- void serialize(JfrCheckpointWriter& writer);
+ void clear();
};
class ClassUnloadTypeSet : public JfrSerializer {
@@ -42,6 +41,13 @@
void serialize(JfrCheckpointWriter& writer);
};
+class FlushTypeSet : public JfrSerializer {
+ size_t _elements;
+ public:
+ void serialize(JfrCheckpointWriter& writer);
+ size_t elements() const;
+};
+
class FlagValueOriginConstant : public JfrSerializer {
public:
void serialize(JfrCheckpointWriter& writer);
@@ -107,11 +113,13 @@
void serialize(JfrCheckpointWriter& writer);
};
-class TypeSet : public JfrSerializer {
- private:
- JfrCheckpointWriter* _leakp_writer;
+class JfrThreadConstantSet : public JfrSerializer {
public:
- explicit TypeSet(JfrCheckpointWriter* leakp_writer = NULL);
+ void serialize(JfrCheckpointWriter& writer);
+};
+
+class JfrThreadGroupConstant : public JfrSerializer {
+ public:
void serialize(JfrCheckpointWriter& writer);
};
@@ -122,9 +130,9 @@
class JfrThreadConstant : public JfrSerializer {
private:
- JavaThread* _thread;
+ Thread* _thread;
public:
- JfrThreadConstant(JavaThread* jt) : _thread(jt) {}
+ JfrThreadConstant(Thread* t) : _thread(t) {}
void serialize(JfrCheckpointWriter& writer);
};
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -34,7 +34,6 @@
#include "jfr/utilities/jfrIterator.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/handles.inline.hpp"
-#include "runtime/safepoint.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/exceptions.hpp"
#include "runtime/semaphore.hpp"
@@ -76,28 +75,105 @@
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);
+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);
+}
+
+size_t JfrTypeManager::flush_type_set() {
+ JfrCheckpointWriter writer;
+ FlushTypeSet flush;
+ flush.serialize(writer);
+ return flush.elements();
+}
+
+void JfrTypeManager::write_type_set() {
+ if (!LeakProfiler::is_running()) {
+ JfrCheckpointWriter writer;
+ TypeSet set;
+ set.serialize(writer);
return;
}
+ JfrCheckpointWriter leakp_writer;
+ JfrCheckpointWriter writer;
+ TypeSet set(&leakp_writer);
+ set.serialize(writer);
+ ObjectSampleCheckpoint::on_type_set(leakp_writer);
+}
+
+void JfrTypeManager::write_type_set_for_unloaded_classes() {
+ JfrCheckpointWriter writer;
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
+ ClassUnloadTypeSet class_unload_set;
+ class_unload_set.serialize(writer);
+ if (LeakProfiler::is_running()) {
+ ObjectSampleCheckpoint::on_type_set_unload(writer);
+ }
+ if (!Jfr::is_recording()) {
+ // discard anything written
writer.set_context(ctx);
- return;
- }
- if (_permit_cache) {
- _cache = writer.copy(&ctx);
}
}
@@ -118,9 +194,8 @@
typedef JfrDoublyLinkedList<JfrSerializerRegistration> List;
typedef StopOnNullIterator<const List> Iterator;
static List types;
-static List safepoint_types;
-void JfrTypeManager::clear() {
+void JfrTypeManager::destroy() {
SerializerRegistrationGuard guard;
Iterator iter(types);
JfrSerializerRegistration* registration;
@@ -129,85 +204,20 @@
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) {
- 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);
- }
}
-void JfrTypeManager::write_type_set() {
- assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
- // can safepoint here
- MutexLocker cld_lock(ClassLoaderDataGraph_lock);
- MutexLocker module_lock(Module_lock);
- if (!LeakProfiler::is_running()) {
- JfrCheckpointWriter writer(true, true, Thread::current());
- TypeSet set;
- set.serialize(writer);
- return;
- }
- JfrCheckpointWriter leakp_writer(false, true, Thread::current());
- JfrCheckpointWriter writer(false, true, Thread::current());
- TypeSet set(&leakp_writer);
- set.serialize(writer);
- ObjectSampleCheckpoint::on_type_set(leakp_writer);
+void JfrTypeManager::clear() {
+ TypeSet type_set;
+ type_set.clear();
}
-void JfrTypeManager::write_type_set_for_unloaded_classes() {
- assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
- JfrCheckpointWriter writer(false, true, Thread::current());
- const JfrCheckpointContext ctx = writer.context();
- ClassUnloadTypeSet class_unload_set;
- class_unload_set.serialize(writer);
- if (LeakProfiler::is_running()) {
- ObjectSampleCheckpoint::on_type_set_unload(writer);
- }
- if (!Jfr::is_recording()) {
- // discard anything written
- writer.set_context(ctx);
+void JfrTypeManager::on_rotation() {
+ const Iterator iter(types);
+ while (iter.has_next()) {
+ 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);
@@ -217,52 +227,65 @@
}
#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 (Jfr::is_recording()) {
+ JfrCheckpointWriter writer(STATICS);
+ registration->invoke(writer);
+ new_registration = true;
}
+ types.prepend(registration);
return true;
}
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_GCTHRESHOLDUPDATER, false, true, new GCThresholdUpdaterConstant());
- register_type(TYPE_METADATATYPE, false, true, new MetadataTypeConstant());
- register_type(TYPE_METASPACEOBJECTTYPE, false, true, new MetaspaceObjectTypeConstant());
- 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_GCTHRESHOLDUPDATER, true, new GCThresholdUpdaterConstant());
+ register_static_type(TYPE_METADATATYPE, true, new MetadataTypeConstant());
+ register_static_type(TYPE_METASPACEOBJECTTYPE, true, new MetaspaceObjectTypeConstant());
+ 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;
+}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -32,13 +32,17 @@
class JfrTypeManager : public AllStatic {
public:
static bool initialize();
+ static void destroy();
static void clear();
- static void write_types(JfrCheckpointWriter& writer);
- static void write_safepoint_types(JfrCheckpointWriter& writer);
+ 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);
+ static size_t flush_type_set();
static void write_type_set();
static void write_type_set_for_unloaded_classes();
- static void create_thread_blob(JavaThread* jt);
- static void write_thread_checkpoint(JavaThread* jt);
};
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -72,7 +72,7 @@
}
static bool current_epoch() {
- return _class_unload;
+ return _class_unload || _flushpoint;
}
static bool previous_epoch() {
@@ -204,9 +204,14 @@
return write_klass(writer, klass, true);
}
+static bool is_implied(const Klass* klass) {
+ assert(klass != NULL, "invariant");
+ return klass->is_subclass_of(SystemDictionary::ClassLoader_klass()) || klass == SystemDictionary::Object_klass();
+}
+
static void do_implied(Klass* klass) {
assert(klass != NULL, "invariant");
- if (klass->is_subclass_of(SystemDictionary::ClassLoader_klass()) || klass == SystemDictionary::Object_klass()) {
+ if (is_implied(klass)) {
if (_leakp_writer != NULL) {
SET_LEAKP(klass);
}
@@ -231,7 +236,7 @@
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;
@@ -259,6 +264,16 @@
typedef CompositeFunctor<KlassPtr, KlassWriter, KlassArtifactRegistrator> KlassWriterRegistration;
typedef JfrArtifactCallbackHost<KlassPtr, KlassWriterRegistration> KlassCallback;
+template <>
+class LeakPredicate<const Klass*> {
+public:
+ LeakPredicate(bool class_unload) {}
+ bool operator()(const Klass* klass) {
+ assert(klass != NULL, "invariant");
+ return IS_LEAKP(klass) || is_implied(klass);
+ }
+};
+
typedef LeakPredicate<KlassPtr> LeakKlassPredicate;
typedef JfrPredicatedTypeWriterImplHost<KlassPtr, LeakKlassPredicate, write__klass__leakp> LeakKlassWriterImpl;
typedef JfrTypeWriterHost<LeakKlassWriterImpl, TYPE_CLASS> LeakKlassWriter;
@@ -809,6 +824,12 @@
_artifacts->tally(sw);
}
+static bool clear_artifacts = false;
+
+void JfrTypeSet::clear() {
+ clear_artifacts = true;
+}
+
typedef Wrapper<KlassPtr, ClearArtifact> ClearKlassBits;
typedef Wrapper<MethodPtr, ClearArtifact> ClearMethodFlag;
typedef MethodIteratorHost<ClearMethodFlag, ClearKlassBits, false> ClearKlassAndMethods;
@@ -820,21 +841,23 @@
assert(_writer != NULL, "invariant");
ClearKlassAndMethods clear(_writer);
_artifacts->iterate_klasses(clear);
- _artifacts->clear();
+ JfrTypeSet::clear();
++checkpoint_id;
}
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 {
- _artifacts->initialize(class_unload);
+ _artifacts->initialize(class_unload, clear_artifacts);
}
+ clear_artifacts = false;
assert(_artifacts != NULL, "invariant");
assert(!_artifacts->has_klass_entries(), "invariant");
}
@@ -842,10 +865,10 @@
/**
* 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()) {
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -31,7 +31,8 @@
class JfrTypeSet : AllStatic {
public:
- static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload);
+ static void clear();
+ static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint);
};
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESET_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -35,6 +35,8 @@
_cstring_table(new CStringTable(this)),
_sym_list(NULL),
_cstring_list(NULL),
+ _sym_query(NULL),
+ _cstring_query(NULL),
_symbol_id_counter(1),
_class_unload(false) {
assert(_sym_table != NULL, "invariant");
@@ -66,9 +68,11 @@
assert(!_cstring_table->has_entries(), "invariant");
_sym_list = NULL;
- _cstring_list = NULL;
_symbol_id_counter = 1;
+ _sym_query = NULL;
+ _cstring_query = NULL;
+
assert(bootstrap != NULL, "invariant");
bootstrap->reset();
_cstring_list = bootstrap;
@@ -88,10 +92,10 @@
}
bool JfrSymbolId::on_equals(uintptr_t hash, const SymbolEntry* entry) {
- // query might be NULL
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
- return true;
+ assert(_sym_query != NULL, "invariant");
+ return _sym_query == entry->literal();
}
void JfrSymbolId::on_unlink(const SymbolEntry* entry) {
@@ -99,18 +103,36 @@
const_cast<Symbol*>(entry->literal())->decrement_refcount();
}
+static const char* resource_to_cstring(const char* resource_str) {
+ assert(resource_str != NULL, "invariant");
+ const size_t length = strlen(resource_str);
+ char* const c_string = JfrCHeapObj::new_array<char>(length + 1);
+ assert(c_string != NULL, "invariant");
+ strncpy(c_string, resource_str, length + 1);
+ return c_string;
+}
+
void JfrSymbolId::on_link(const CStringEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->id() == 0, "invariant");
entry->set_id(++_symbol_id_counter);
+ const_cast<CStringEntry*>(entry)->set_literal(resource_to_cstring(entry->literal()));
entry->set_list_next(_cstring_list);
_cstring_list = entry;
}
+static bool string_compare(const char* query, const char* candidate) {
+ assert(query != NULL, "invariant");
+ assert(candidate != NULL, "invariant");
+ const size_t length = strlen(query);
+ return strncmp(query, candidate, length) == 0;
+}
+
bool JfrSymbolId::on_equals(uintptr_t hash, const CStringEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
- return true;
+ assert(_cstring_query != NULL, "invariant");
+ return string_compare(_cstring_query, entry->literal());
}
void JfrSymbolId::on_unlink(const CStringEntry* entry) {
@@ -131,16 +153,10 @@
return mark((uintptr_t)symbol->identity_hash(), symbol, leakp);
}
-static unsigned int last_symbol_hash = 0;
-static traceid last_symbol_id = 0;
-
traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) {
assert(data != NULL, "invariant");
assert(_sym_table != NULL, "invariant");
- if (hash == last_symbol_hash) {
- assert(last_symbol_id != 0, "invariant");
- return last_symbol_id;
- }
+ _sym_query = data;
const SymbolEntry& entry = _sym_table->lookup_put(hash, data);
if (_class_unload) {
entry.set_unloading();
@@ -148,21 +164,13 @@
if (leakp) {
entry.set_leakp();
}
- last_symbol_hash = hash;
- last_symbol_id = entry.id();
- return last_symbol_id;
+ return entry.id();
}
-static unsigned int last_cstring_hash = 0;
-static traceid last_cstring_id = 0;
-
traceid JfrSymbolId::mark(uintptr_t hash, const char* str, bool leakp) {
assert(str != NULL, "invariant");
assert(_cstring_table != NULL, "invariant");
- if (hash == last_cstring_hash) {
- assert(last_cstring_id != 0, "invariant");
- return last_cstring_id;
- }
+ _cstring_query = str;
const CStringEntry& entry = _cstring_table->lookup_put(hash, str);
if (_class_unload) {
entry.set_unloading();
@@ -170,9 +178,7 @@
if (leakp) {
entry.set_leakp();
}
- last_cstring_hash = hash;
- last_cstring_id = entry.id();
- return last_cstring_id;
+ return entry.id();
}
/*
@@ -202,7 +208,7 @@
sprintf(hash_buf, "/" UINTX_FORMAT, hash);
const size_t hash_len = strlen(hash_buf);
const size_t result_len = ik->name()->utf8_length();
- anonymous_symbol = JfrCHeapObj::new_array<char>(result_len + hash_len + 1);
+ anonymous_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1);
ik->name()->as_klass_external_name(anonymous_symbol, (int)result_len + 1);
assert(strlen(anonymous_symbol) == result_len, "invariant");
strcpy(anonymous_symbol + result_len, hash_buf);
@@ -215,21 +221,12 @@
return k->is_instance_klass() && ((const InstanceKlass*)k)->is_unsafe_anonymous();
}
-static unsigned int last_anonymous_hash = 0;
-static traceid last_anonymous_id = 0;
-
traceid JfrSymbolId::mark_unsafe_anonymous_klass_name(const InstanceKlass* ik, bool leakp) {
assert(ik != NULL, "invariant");
assert(ik->is_unsafe_anonymous(), "invariant");
const uintptr_t hash = unsafe_anonymous_klass_name_hash(ik);
- if (hash == last_anonymous_hash) {
- assert(last_anonymous_id != 0, "invariant");
- return last_anonymous_id;
- }
- last_anonymous_hash = hash;
- const CStringEntry* const entry = _cstring_table->lookup_only(hash);
- last_anonymous_id = entry != NULL ? entry->id() : mark(hash, create_unsafe_anonymous_klass_symbol(ik, hash), leakp);
- return last_anonymous_id;
+ const char* const anonymous_klass_symbol = create_unsafe_anonymous_klass_symbol(ik, hash);
+ return mark(hash, anonymous_klass_symbol, leakp);
}
traceid JfrSymbolId::mark(const Klass* k, bool leakp) {
@@ -249,23 +246,20 @@
return symbol_id;
}
-static void reset_symbol_caches() {
- last_anonymous_hash = 0;
- last_symbol_hash = 0;
- last_cstring_hash = 0;
-}
-
JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_id(new JfrSymbolId()),
- _klass_list(NULL),
- _total_count(0) {
+ _klass_list(NULL),
+ _total_count(0) {
initialize(class_unload);
assert(_klass_list != NULL, "invariant");
}
static const size_t initial_class_list_size = 200;
-void JfrArtifactSet::initialize(bool class_unload) {
+void JfrArtifactSet::initialize(bool class_unload, bool clear /* false */) {
assert(_symbol_id != NULL, "invariant");
+ if (clear) {
+ _symbol_id->clear();
+ }
_symbol_id->set_class_unload(class_unload);
_total_count = 0;
// resource allocation
@@ -273,13 +267,8 @@
}
JfrArtifactSet::~JfrArtifactSet() {
- clear();
+ _symbol_id->clear();
delete _symbol_id;
-}
-
-void JfrArtifactSet::clear() {
- reset_symbol_caches();
- _symbol_id->clear();
// _klass_list will be cleared by a ResourceMark
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -222,6 +222,8 @@
CStringTable* _cstring_table;
const SymbolEntry* _sym_list;
const CStringEntry* _cstring_list;
+ const Symbol* _sym_query;
+ const char* _cstring_query;
traceid _symbol_id_counter;
bool _class_unload;
@@ -300,9 +302,7 @@
~JfrArtifactSet();
// caller needs ResourceMark
- void initialize(bool class_unload);
- void clear();
-
+ void initialize(bool class_unload, bool clear = false);
traceid mark(uintptr_t hash, const Symbol* sym, bool leakp);
traceid mark(const Klass* klass, bool leakp);
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -33,7 +33,6 @@
#include "oops/method.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
-#include "runtime/orderAccess.hpp"
#include "runtime/vm_version.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/thread.inline.hpp"
@@ -45,7 +44,7 @@
traceid compare_value;
traceid exchange_value;
do {
- compare_value = OrderAccess::load_acquire(dest);
+ compare_value = *dest;
exchange_value = compare_value + 1;
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
return exchange_value;
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -31,7 +31,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"
@@ -60,7 +59,14 @@
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) {
@@ -71,10 +77,16 @@
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));
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -42,8 +42,10 @@
assert(dest != NULL, "invariant");
if (bits != (*dest & bits)) {
*dest |= bits;
- OrderAccess::storestore();
+ OrderAccess::storeload();
+ return;
}
+ OrderAccess::loadload();
}
inline jbyte traceid_and(jbyte current, jbyte bits) {
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,8 @@
// The regular epoch shift happens only during a safepoint.
// The fence is there only for the emergency dump case which happens outside of safepoint.
bool JfrTraceIdEpoch::_epoch_state = false;
+bool volatile JfrTraceIdEpoch::_tag_state = false;
+
void JfrTraceIdEpoch::shift_epoch() {
_epoch_state = !_epoch_state;
if (!SafepointSynchronize::is_at_safepoint()) {
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -25,18 +25,19 @@
#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
+#include "jfr/utilities/jfrTypes.hpp"
#include "memory/allocation.hpp"
-#include "jfr/utilities/jfrTypes.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 @@
friend class JfrCheckpointManager;
private:
static bool _epoch_state;
+ static bool volatile _tag_state;
+
static void shift_epoch();
public:
@@ -86,6 +89,20 @@
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 (OrderAccess::load_acquire(&_tag_state)) {
+ OrderAccess::release_store(&_tag_state, false);
+ return true;
+ }
+ return false;
+ }
+
+ static void set_changed_tag_state() {
+ if (!OrderAccess::load_acquire(&_tag_state)) {
+ OrderAccess::release_store(&_tag_state, true);
+ }
+ }
};
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunk.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jfr/recorder/repository/jfrChunk.hpp"
+#include "jfr/recorder/service/jfrOptionSet.hpp"
+#include "jfr/utilities/jfrTime.hpp"
+#include "jfr/utilities/jfrTimeConverter.hpp"
+#include "jfr/utilities/jfrTypes.hpp"
+#include "runtime/os.inline.hpp"
+
+static const char* const MAGIC = "FLR";
+static const u2 JFR_VERSION_MAJOR = 2;
+static const u2 JFR_VERSION_MINOR = 0;
+
+static jlong nanos_now() {
+ return os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC;
+}
+
+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() {
+ _start_nanos = _last_update_nanos = nanos_now();
+}
+
+void JfrChunk::update_current_nanos() {
+ _last_update_nanos = nanos_now();
+}
+
+void JfrChunk::save_current_and_update_start_ticks() {
+ _previous_start_ticks = _start_ticks;
+ update_start_ticks();
+}
+
+void JfrChunk::save_current_and_update_start_nanos() {
+ _previous_start_nanos = _start_nanos;
+ update_start_nanos();
+}
+
+void JfrChunk::set_time_stamp() {
+ save_current_and_update_start_nanos();
+ save_current_and_update_start_ticks();
+}
+
+int64_t JfrChunk::last_chunk_duration() const {
+ assert(_previous_start_nanos != invalid_time, "invariant");
+ return _start_nanos - _previous_start_nanos;
+}
+
+static char* copy_path(const char* path) {
+ assert(path != NULL, "invariant");
+ const size_t path_len = strlen(path);
+ char* new_path = JfrCHeapObj::new_array<char>(path_len + 1);
+ strncpy(new_path, path, path_len + 1);
+ return new_path;
+}
+
+void JfrChunk::set_path(const char* path) {
+ if (_path != NULL) {
+ JfrCHeapObj::free(_path, strlen(_path) + 1);
+ _path = NULL;
+ }
+ if (path != NULL) {
+ _path = copy_path(path);
+ }
+}
+
+const char* JfrChunk::path() const {
+ return _path;
+}
+
+bool JfrChunk::is_started() const {
+ return _start_nanos != 0;
+}
+
+bool JfrChunk::is_finished() const {
+ return 0 == _generation;
+}
+
+int64_t JfrChunk::duration() const {
+ assert(_last_update_nanos >= _start_nanos, "invariant");
+ return _last_update_nanos - _start_nanos;
+}
+
+int64_t JfrChunk::last_metadata_offset() const {
+ return _last_metadata_offset;
+}
+
+void JfrChunk::set_last_metadata_offset(int64_t offset) {
+ assert(offset > _last_metadata_offset, "invariant");
+ _last_metadata_offset = offset;
+}
+
+bool JfrChunk::has_metadata() const {
+ return 0 != _last_metadata_offset;
+}
+
+u1 JfrChunk::generation() const {
+ assert(_generation > 0, "invariant");
+ const u1 this_generation = _generation++;
+ if (GUARD == _generation) {
+ _generation = 1;
+ }
+ return this_generation;
+}
+
+u1 JfrChunk::next_generation() const {
+ assert(_generation > 0, "invariant");
+ const u1 next_gen = _generation;
+ return GUARD == next_gen ? 1 : next_gen;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunk.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
+#define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
+
+#include "jfr/utilities/jfrAllocation.hpp"
+
+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;
+ 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();
+
+ int64_t last_checkpoint_offset() const;
+ void set_last_checkpoint_offset(int64_t offset);
+
+ int64_t last_metadata_offset() const;
+ void set_last_metadata_offset(int64_t offset);
+ bool has_metadata() const;
+
+ int64_t start_ticks() const;
+ int64_t start_nanos() const;
+
+ int64_t previous_start_ticks() const;
+ int64_t previous_start_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_JFRRCHUNK_HPP
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkState.cpp Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,120 +0,0 @@
-/*
- * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#include "precompiled.hpp"
-#include "jfr/dcmd/jfrDcmds.hpp"
-#include "jfr/recorder/jfrRecorder.hpp"
-#include "jfr/recorder/repository/jfrChunkState.hpp"
-#include "jfr/recorder/repository/jfrChunkWriter.hpp"
-#include "jfr/utilities/jfrTime.hpp"
-#include "jfr/utilities/jfrTimeConverter.hpp"
-#include "logging/log.hpp"
-#include "runtime/os.inline.hpp"
-#include "runtime/thread.inline.hpp"
-
-JfrChunkState::JfrChunkState() :
- _path(NULL),
- _start_ticks(0),
- _start_nanos(0),
- _previous_start_ticks(0),
- _previous_start_nanos(0),
- _last_checkpoint_offset(0) {}
-
-JfrChunkState::~JfrChunkState() {
- reset();
-}
-
-void JfrChunkState::reset() {
- if (_path != NULL) {
- JfrCHeapObj::free(_path, strlen(_path) + 1);
- _path = NULL;
- }
- set_last_checkpoint_offset(0);
-}
-
-void JfrChunkState::set_last_checkpoint_offset(int64_t offset) {
- _last_checkpoint_offset = offset;
-}
-
-int64_t JfrChunkState::last_checkpoint_offset() const {
- return _last_checkpoint_offset;
-}
-
-int64_t JfrChunkState::previous_start_ticks() const {
- return _previous_start_ticks;
-}
-
-int64_t JfrChunkState::previous_start_nanos() const {
- return _previous_start_nanos;
-}
-
-void JfrChunkState::update_start_ticks() {
- _start_ticks = JfrTicks::now();
-}
-
-void JfrChunkState::update_start_nanos() {
- _start_nanos = os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC;
-}
-
-void JfrChunkState::save_current_and_update_start_ticks() {
- _previous_start_ticks = _start_ticks;
- update_start_ticks();
-}
-
-void JfrChunkState::save_current_and_update_start_nanos() {
- _previous_start_nanos = _start_nanos;
- update_start_nanos();
-}
-
-void JfrChunkState::update_time_to_now() {
- save_current_and_update_start_nanos();
- save_current_and_update_start_ticks();
-}
-
-int64_t JfrChunkState::last_chunk_duration() const {
- return _start_nanos - _previous_start_nanos;
-}
-
-static char* copy_path(const char* path) {
- assert(path != NULL, "invariant");
- const size_t path_len = strlen(path);
- char* new_path = JfrCHeapObj::new_array<char>(path_len + 1);
- strncpy(new_path, path, path_len + 1);
- return new_path;
-}
-
-void JfrChunkState::set_path(const char* path) {
- assert(JfrStream_lock->owned_by_self(), "invariant");
- if (_path != NULL) {
- JfrCHeapObj::free(_path, strlen(_path) + 1);
- _path = NULL;
- }
- if (path != NULL) {
- _path = copy_path(path);
- }
-}
-
-const char* JfrChunkState::path() const {
- return _path;
-}
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkState.hpp Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +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.
- *
- */
-
-#ifndef SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKSTATE_HPP
-#define SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKSTATE_HPP
-
-#include "jfr/utilities/jfrAllocation.hpp"
-#include "jfr/utilities/jfrTypes.hpp"
-
-class JfrChunkState : public JfrCHeapObj {
- friend class JfrChunkWriter;
- private:
- char* _path;
- int64_t _start_ticks;
- int64_t _start_nanos;
- int64_t _previous_start_ticks;
- int64_t _previous_start_nanos;
- int64_t _last_checkpoint_offset;
-
- 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);
- int64_t previous_start_ticks() const;
- int64_t previous_start_nanos() const;
- int64_t last_chunk_duration() const;
- void update_time_to_now();
- void set_path(const char* path);
- const char* path() const;
-};
-
-#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKSTATE_HPP
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -23,82 +23,218 @@
*/
#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.inline.hpp"
-static const u2 JFR_VERSION_MAJOR = 2;
-static const u2 JFR_VERSION_MINOR = 0;
-static const size_t MAGIC_LEN = 4;
-static const size_t FILEHEADER_SLOT_SIZE = 8;
-static const size_t CHUNK_SIZE_OFFSET = 8;
-
-JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunkstate(NULL) {}
-
-bool JfrChunkWriter::initialize() {
- assert(_chunkstate == NULL, "invariant");
- _chunkstate = new JfrChunkState();
- return _chunkstate != NULL;
-}
+static const int64_t MAGIC_OFFSET = 0;
+static const int64_t MAGIC_LEN = 4;
+static const int64_t VERSION_OFFSET = MAGIC_LEN;
+static const int64_t SIZE_OFFSET = 8;
+static const int64_t SLOT_SIZE = 8;
+static const int64_t CHECKPOINT_OFFSET = SIZE_OFFSET + SLOT_SIZE;
+static const int64_t METADATA_OFFSET = CHECKPOINT_OFFSET + SLOT_SIZE;
+static const int64_t START_NANOS_OFFSET = METADATA_OFFSET + SLOT_SIZE;
+static const int64_t DURATION_NANOS_OFFSET = START_NANOS_OFFSET + SLOT_SIZE;
+static const int64_t START_TICKS_OFFSET = DURATION_NANOS_OFFSET + SLOT_SIZE;
+static const int64_t CPU_FREQUENCY_OFFSET = START_TICKS_OFFSET + SLOT_SIZE;
+static const int64_t GENERATION_OFFSET = CPU_FREQUENCY_OFFSET + SLOT_SIZE;
+static const int64_t CAPABILITY_OFFSET = GENERATION_OFFSET + 2;
+static const int64_t HEADER_SIZE = CAPABILITY_OFFSET + 2;
static 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);
+ }
+
+ 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();
}
- return is_open;
+
+ void write_capabilities() {
+ _writer->be_write(_chunk->capabilities());
+ }
+
+ void write_size_to_generation(int64_t size, bool finalize) {
+ write_size(size);
+ write_checkpoint();
+ write_metadata();
+ write_time(finalize);
+ write_cpu_frequency();
+ write_generation(finalize);
+ }
+
+ void flush(int64_t size, bool finalize) {
+ assert(_writer->is_valid(), "invariant");
+ assert(_chunk != NULL, "invariant");
+ DEBUG_ONLY(assert_writer_position(_writer, SIZE_OFFSET);)
+ write_size_to_generation(size, finalize);
+ // no need to write capabilities
+ _writer->seek(size); // implicit flush
+ }
+
+ void initialize() {
+ assert(_writer->is_valid(), "invariant");
+ assert(_chunk != NULL, "invariant");
+ DEBUG_ONLY(assert_writer_position(_writer, 0);)
+ write_magic();
+ write_version();
+ write_size_to_generation(HEADER_SIZE, false);
+ write_capabilities();
+ DEBUG_ONLY(assert_writer_position(_writer, HEADER_SIZE);)
+ _writer->flush();
+ }
+
+ JfrChunkHeadWriter(JfrChunkWriter* writer, int64_t offset, bool guard = true) : _writer(writer), _chunk(writer->_chunk) {
+ assert(_writer != NULL, "invariant");
+ assert(_writer->is_valid(), "invariant");
+ assert(_chunk != NULL, "invariant");
+ if (0 == _writer->current_offset()) {
+ assert(HEADER_SIZE == offset, "invariant");
+ initialize();
+ } else {
+ if (guard) {
+ _writer->seek(GENERATION_OFFSET);
+ write_guard();
+ _writer->seek(offset);
+ } else {
+ _chunk->update_current_nanos();
+ }
+ }
+ DEBUG_ONLY(assert_writer_position(_writer, offset);)
+ }
+};
+
+static int64_t prepare_chunk_header_constant_pool(JfrChunkWriter& cw, int64_t event_offset, bool flushpoint) {
+ const int64_t delta = cw.last_checkpoint_offset() == 0 ? 0 : cw.last_checkpoint_offset() - event_offset;
+ const u4 checkpoint_type = flushpoint ? (u4)(FLUSH | HEADER) : (u4)HEADER;
+ cw.reserve(sizeof(u4));
+ cw.write<u8>(EVENT_CHECKPOINT);
+ cw.write<u8>(JfrTicks::now().value());
+ cw.write<u8>(0); // duration
+ cw.write<u8>(delta); // to previous checkpoint
+ cw.write<u4>(checkpoint_type);
+ cw.write<u4>(1); // pool count
+ cw.write<u8>(TYPE_CHUNKHEADER);
+ cw.write<u4>(1); // count
+ cw.write<u8>(1); // key
+ cw.write<u4>(HEADER_SIZE); // length of byte array
+ return cw.current_offset();
}
-size_t JfrChunkWriter::close(int64_t metadata_offset) {
- write_header(metadata_offset);
- this->flush();
- this->close_fd();
- return (size_t)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<u4>(checkpoint_size, event_size_offset);
+ set_last_checkpoint_offset(event_size_offset);
+ const size_t sz_written = size_written();
+ write_be_at_offset(sz_written, chunk_size_offset);
+ return sz_written;
}
-void JfrChunkWriter::write_header(int64_t metadata_offset) {
- assert(this->is_valid(), "invariant");
- // Chunk size
- this->write_be_at_offset(size_written(), CHUNK_SIZE_OFFSET);
- // initial checkpoint event offset
- this->write_be_at_offset(_chunkstate->last_checkpoint_offset(), CHUNK_SIZE_OFFSET + (1 * FILEHEADER_SLOT_SIZE));
- // metadata event offset
- this->write_be_at_offset(metadata_offset, CHUNK_SIZE_OFFSET + (2 * FILEHEADER_SLOT_SIZE));
- // start of chunk in nanos since epoch
- this->write_be_at_offset(_chunkstate->previous_start_nanos(), CHUNK_SIZE_OFFSET + (3 * FILEHEADER_SLOT_SIZE));
- // duration of chunk in nanos
- this->write_be_at_offset(_chunkstate->last_chunk_duration(), CHUNK_SIZE_OFFSET + (4 * FILEHEADER_SLOT_SIZE));
- // start of chunk in ticks
- this->write_be_at_offset(_chunkstate->previous_start_ticks(), CHUNK_SIZE_OFFSET + (5 * FILEHEADER_SLOT_SIZE));
+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();
}
int64_t JfrChunkWriter::size_written() const {
@@ -106,13 +242,46 @@
}
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;
+}
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -29,29 +29,36 @@
#include "jfr/writers/jfrStreamWriterHost.inline.hpp"
#include "jfr/writers/jfrWriterHost.inline.hpp"
-typedef MallocAdapter<M> JfrStreamBuffer; // 1 mb buffered writes
-typedef StreamWriterHost<JfrStreamBuffer, JfrCHeapObj> JfrBufferedStreamWriter;
-typedef WriterHost<BigEndianEncoder, CompressedIntegerEncoder, JfrBufferedStreamWriter> JfrChunkWriterBase;
+typedef MallocAdapter<M> JfrChunkBuffer; // 1 mb buffered writes
+typedef StreamWriterHost<JfrChunkBuffer, JfrCHeapObj> JfrBufferedChunkWriter;
+typedef WriterHost<BigEndianEncoder, CompressedIntegerEncoder, JfrBufferedChunkWriter> JfrChunkWriterBase;
-class JfrChunkState;
+class JfrChunk;
+class JfrChunkHeadWriter;
class JfrChunkWriter : public JfrChunkWriterBase {
+ friend class JfrChunkHeadWriter;
friend class JfrRepository;
private:
- JfrChunkState* _chunkstate;
-
+ JfrChunk* _chunk;
+ void set_path(const char* path);
+ int64_t flush_chunk(bool flushpoint);
bool open();
- size_t close(int64_t metadata_offset);
- void write_header(int64_t metadata_offset);
- void set_chunk_path(const char* chunk_path);
+ int64_t close();
+ int64_t current_chunk_start_nanos() const;
+ int64_t write_chunk_header_checkpoint(bool flushpoint);
public:
JfrChunkWriter();
- bool initialize();
+ ~JfrChunkWriter();
+
int64_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_JFR_RECORDER_REPOSITORY_JFRCHUNKWRITER_HPP
--- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -248,7 +248,6 @@
}
static const char* create_emergency_dump_path() {
- assert(JfrStream_lock->owned_by_self(), "invariant");
char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, JVM_MAXPATHLEN);
if (NULL == buffer) {
return NULL;
@@ -291,7 +290,6 @@
// Caller needs ResourceMark
static const char* create_emergency_chunk_path(const char* repository_path) {
assert(repository_path != NULL, "invariant");
- assert(JfrStream_lock->owned_by_self(), "invariant");
const size_t repository_path_len = strlen(repository_path);
// date time
char date_time_buffer[32] = { 0 };
@@ -307,12 +305,11 @@
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;
@@ -325,7 +322,7 @@
void JfrEmergencyDump::on_vm_error(const char* repository_path) {
assert(repository_path != NULL, "invariant");
ResourceMark rm;
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
+
const fio_fd emergency_fd = emergency_dump_file_descriptor();
if (emergency_fd != invalid_fd) {
RepositoryIterator iterator(repository_path, strlen(repository_path));
@@ -413,10 +410,6 @@
JfrBuffer_lock->unlock();
}
- if (JfrStream_lock->owned_by_self()) {
- JfrStream_lock->unlock();
- }
-
if (JfrStacktrace_lock->owned_by_self()) {
JfrStacktrace_lock->unlock();
}
--- a/src/hotspot/share/jfr/recorder/repository/jfrRepository.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/repository/jfrRepository.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -26,13 +26,14 @@
#include "jfr/jfr.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
-#include "jfr/recorder/repository/jfrChunkState.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
#include "jfr/recorder/repository/jfrRepository.hpp"
#include "jfr/recorder/service/jfrPostBox.hpp"
+#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/mutex.hpp"
+#include "runtime/os.hpp"
#include "runtime/thread.inline.hpp"
static JfrRepository* _instance = NULL;
@@ -43,11 +44,6 @@
static JfrChunkWriter* _chunkwriter = NULL;
-static bool initialize_chunkwriter() {
- assert(_chunkwriter == NULL, "invariant");
- _chunkwriter = new JfrChunkWriter();
- return _chunkwriter != NULL && _chunkwriter->initialize();
-}
JfrChunkWriter& JfrRepository::chunkwriter() {
return *_chunkwriter;
@@ -56,7 +52,9 @@
JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {}
bool JfrRepository::initialize() {
- return initialize_chunkwriter();
+ assert(_chunkwriter == NULL, "invariant");
+ _chunkwriter = new JfrChunkWriter();
+ return _chunkwriter != NULL;
}
JfrRepository::~JfrRepository() {
@@ -84,7 +82,7 @@
}
void JfrRepository::on_vm_error() {
- assert(!JfrStream_lock->owned_by_self(), "invariant");
+
if (_path == NULL) {
// completed already
return;
@@ -107,15 +105,19 @@
return true;
}
-void JfrRepository::set_chunk_path(const char* path) {
- assert(JfrStream_lock->owned_by_self(), "invariant");
- chunkwriter().set_chunk_path(path);
+void JfrRepository::notify_on_new_chunk_path() {
+ if (Jfr::is_recording()) {
+ // rotations are synchronous, block until rotation completes
+ instance()._post_box.post(MSG_ROTATE);
+ }
}
-void JfrRepository::notify_on_new_chunk_path() {
- if (Jfr::is_recording()) {
- instance()._post_box.post(MSG_ROTATE);
- }
+void JfrRepository::set_chunk_path(const char* path) {
+ chunkwriter().set_path(path);
+}
+
+jlong JfrRepository::current_chunk_start_nanos() {
+ return chunkwriter().current_chunk_start_nanos();
}
/**
@@ -134,14 +136,11 @@
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
ResourceMark rm(jt);
const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt);
- {
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
- if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
- // new output is NULL and current output is NULL
- return;
- }
- instance().set_chunk_path(canonical_chunk_path);
+ if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
+ // new output is NULL and current output is NULL
+ return;
}
+ instance().set_chunk_path(canonical_chunk_path);
notify_on_new_chunk_path();
}
@@ -155,14 +154,28 @@
}
bool JfrRepository::open_chunk(bool vm_error /* false */) {
- assert(JfrStream_lock->owned_by_self(), "invariant");
if (vm_error) {
ResourceMark rm;
- _chunkwriter->set_chunk_path(JfrEmergencyDump::build_dump_path(_path));
+ _chunkwriter->set_path(JfrEmergencyDump::build_dump_path(_path));
}
return _chunkwriter->open();
}
-size_t JfrRepository::close_chunk(int64_t metadata_offset) {
- return _chunkwriter->close(metadata_offset);
+size_t JfrRepository::close_chunk() {
+ return _chunkwriter->close();
}
+
+void JfrRepository::flush(bool metadata, JavaThread* jt) {
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
+ if (!Jfr::is_recording()) {
+ return;
+ }
+ if (!_chunkwriter->is_valid()) {
+ return;
+ }
+ instance()._post_box.post((metadata || !_chunkwriter->has_metadata()) ? MSG_FLUSHPOINT_METADATA : MSG_FLUSHPOINT);
+}
+
+size_t JfrRepository::flush_chunk() {
+ return _chunkwriter->flush_chunk(true);
+}
--- a/src/hotspot/share/jfr/recorder/repository/jfrRepository.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/repository/jfrRepository.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -55,8 +55,10 @@
bool set_path(const char* path);
void set_chunk_path(const char* path);
bool open_chunk(bool vm_error = false);
- size_t close_chunk(int64_t metadata_offset);
+ size_t close_chunk();
+ size_t flush_chunk();
void on_vm_error();
+
static void notify_on_new_chunk_path();
static JfrChunkWriter& chunkwriter();
@@ -68,6 +70,8 @@
public:
static void set_path(jstring location, JavaThread* jt);
static void set_chunk_path(jstring path, JavaThread* jt);
+ static void flush(bool metadata, JavaThread* jt);
+ static jlong current_chunk_start_nanos();
};
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRREPOSITORY_HPP
--- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -26,14 +26,15 @@
#include "jfr/recorder/service/jfrPostBox.hpp"
#include "jfr/utilities/jfrTryLock.hpp"
#include "runtime/atomic.hpp"
-#include "runtime/orderAccess.hpp"
#include "runtime/thread.inline.hpp"
#define MSG_IS_SYNCHRONOUS ( (MSGBIT(MSG_ROTATE)) | \
(MSGBIT(MSG_STOP)) | \
(MSGBIT(MSG_START)) | \
(MSGBIT(MSG_CLONE_IN_MEMORY)) | \
- (MSGBIT(MSG_VM_ERROR)) \
+ (MSGBIT(MSG_VM_ERROR)) | \
+ (MSGBIT(MSG_FLUSHPOINT)) | \
+ (MSGBIT(MSG_FLUSHPOINT_METADATA)) \
)
static JfrPostBox* _instance = NULL;
@@ -84,7 +85,7 @@
void JfrPostBox::deposit(int new_messages) {
while (true) {
- const int current_msgs = OrderAccess::load_acquire(&_messages);
+ const int current_msgs = Atomic::load(&_messages);
// OR the new message
const int exchange_value = current_msgs | new_messages;
const int result = Atomic::cmpxchg(exchange_value, &_messages, current_msgs);
@@ -114,7 +115,7 @@
deposit(msg);
// serial_id is used to check when what we send in has been processed.
// _msg_read_serial is read under JfrMsg_lock protection.
- const uintptr_t serial_id = OrderAccess::load_acquire(&_msg_read_serial) + 1;
+ const uintptr_t serial_id = Atomic::load(&_msg_read_serial) + 1;
msg_lock.notify_all();
while (!is_message_processed(serial_id)) {
msg_lock.wait();
@@ -129,12 +130,12 @@
*/
bool JfrPostBox::is_message_processed(uintptr_t serial_id) const {
assert(JfrMsg_lock->owned_by_self(), "_msg_handled_serial must be read under JfrMsg_lock protection");
- return serial_id <= OrderAccess::load_acquire(&_msg_handled_serial);
+ return serial_id <= Atomic::load(&_msg_handled_serial);
}
bool JfrPostBox::is_empty() const {
assert(JfrMsg_lock->owned_by_self(), "not holding JfrMsg_lock!");
- return OrderAccess::load_acquire(&_messages) == 0;
+ return Atomic::load(&_messages) == 0;
}
int JfrPostBox::collect() {
--- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -41,6 +41,8 @@
MSG_SHUTDOWN,
MSG_VM_ERROR,
MSG_DEADBUFFER,
+ MSG_FLUSHPOINT,
+ MSG_FLUSHPOINT_METADATA,
MSG_NO_OF_MSGS
};
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -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"
@@ -58,7 +59,7 @@
// set data iff *dest == NULL
static bool try_set(void* const data, void** dest, bool clear) {
assert(data != NULL, "invariant");
- const void* const current = OrderAccess::load_acquire(dest);
+ const void* const current = *dest;
if (current != NULL) {
if (current != data) {
// already set
@@ -77,6 +78,9 @@
static const int rotation_try_limit = 1000;
static const int rotation_retry_sleep_millis = 10;
+// incremented on each flushpoint
+static u8 flushpoint_id = 0;
+
class RotationLock : public StackObj {
private:
Thread* const _thread;
@@ -132,71 +136,277 @@
bool not_acquired() const { return !_acquired; }
};
+template <typename E, typename Instance, size_t(Instance::*func)()>
+class Content {
+ private:
+ 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 <typename Content>
+class WriteContent : public StackObj {
+ protected:
+ const JfrTicks _start_time;
+ JfrTicks _end_time;
+ JfrChunkWriter& _cw;
+ Content& _content;
+ const int64_t _start_offset;
+ public:
+ typedef typename Content::EventType EventType;
+
+ 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");
+ }
+
+ 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<u4>(elements(), offset);
+ }
+
+ void write_size() {
+ _cw.write_padded_at_offset<u4>(size(), start_offset());
+ }
+
+ void set_last_checkpoint() {
+ _cw.set_last_checkpoint_offset(start_offset());
+ }
+
+ void rewind() {
+ _cw.seek(start_offset());
+ }
+};
+
static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) {
const int64_t 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<u8>(EVENT_CHECKPOINT);
cw.write(JfrTicks::now());
- cw.write((int64_t)0); // duration
+ cw.write<u8>(0); // duration
cw.write(delta_to_last_checkpoint);
- cw.write<bool>(false); // flushpoint
- cw.write((u4)1); // nof types in this checkpoint
+ cw.write<u4>(GENERIC); // checkpoint type
+ cw.write<u4>(1); // nof types in this checkpoint
cw.write(type_id);
- const int64_t number_of_elements_offset = cw.current_offset();
- cw.reserve(sizeof(u4));
- return number_of_elements_offset;
+ return cw.reserve(sizeof(u4));
}
-template <typename ContentFunctor>
-class WriteCheckpointEvent : public StackObj {
+template <typename Content>
+class WriteCheckpointEvent : public WriteContent<Content> {
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<Content>(cw, content), _type_id(type_id) {}
+
bool process() {
- // current_cp_offset is also offset for the event size header field
- const int64_t current_cp_offset = _cw.current_offset();
- const int64_t num_elements_offset = write_checkpoint_event_prologue(_cw, _type_id);
- // invocation
- _content_functor.process();
- const u4 number_of_elements = (u4)_content_functor.processed();
- if (number_of_elements == 0) {
+ const int64_t num_elements_offset = write_checkpoint_event_prologue(this->_cw, _type_id);
+ if (!WriteContent<Content>::process()) {
// nothing to do, rewind writer to start
- _cw.seek(current_cp_offset);
- return true;
+ this->rewind();
+ assert(this->current_offset() == this->start_offset(), "invariant");
+ return false;
}
- assert(number_of_elements > 0, "invariant");
- assert(_cw.current_offset() > num_elements_offset, "invariant");
- _cw.write_padded_at_offset<u4>(number_of_elements, num_elements_offset);
- _cw.write_padded_at_offset<u4>((u4)_cw.current_offset() - current_cp_offset, current_cp_offset);
- // update writer with last checkpoint position
- _cw.set_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 <typename Instance, size_t(Instance::*func)()>
-class ServiceFunctor {
+template <typename Functor>
+static u4 invoke(Functor& f) {
+ f.process();
+ return f.elements();
+}
+
+template <typename Functor>
+static void write_flush_event(Functor& f) {
+ if (Functor::is_event_enabled()) {
+ typename Functor::EventType e(UNTIMED);
+ e.set_starttime(f.start_time());
+ e.set_endtime(f.end_time());
+ e.set_flushId(flushpoint_id);
+ e.set_elements(f.elements());
+ e.set_size(f.size());
+ e.commit();
+ }
+}
+
+template <typename Functor>
+static u4 invoke_with_flush_event(Functor& f) {
+ const u4 elements = invoke(f);
+ write_flush_event(f);
+ return elements;
+}
+
+class StackTraceRepository : public StackObj {
private:
- Instance& _instance;
- size_t _processed;
+ JfrStackTraceRepository& _repo;
+ JfrChunkWriter& _cw;
+ size_t _elements;
+ bool _clear;
+
public:
- ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {}
+ typedef EventFlushStacktrace EventType;
+ StackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) :
+ _repo(repo), _cw(cw), _elements(0), _clear(clear) {}
bool process() {
- _processed = (_instance.*func)();
+ _elements = _repo.write(_cw, _clear);
return true;
}
- size_t processed() const { return _processed; }
+ size_t elements() const { return _elements; }
+ void reset() { _elements = 0; }
};
+typedef WriteCheckpointEvent<StackTraceRepository> WriteStackTrace;
+
+static u4 flush_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter) {
+ StackTraceRepository str(stack_trace_repo, chunkwriter, false);
+ WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE);
+ return invoke_with_flush_event(wst);
+}
+
+static u4 write_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) {
+ StackTraceRepository str(stack_trace_repo, chunkwriter, clear);
+ WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE);
+ return invoke(wst);
+}
+
+typedef Content<EventFlushStorage, JfrStorage, &JfrStorage::write> Storage;
+typedef WriteContent<Storage> WriteStorage;
+
+static size_t flush_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) {
+ assert(chunkwriter.is_valid(), "invariant");
+ Storage fsf(storage);
+ WriteStorage fs(chunkwriter, fsf);
+ return invoke_with_flush_event(fs);
+}
+
+static size_t write_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) {
+ assert(chunkwriter.is_valid(), "invariant");
+ Storage fsf(storage);
+ WriteStorage fs(chunkwriter, fsf);
+ return invoke(fs);
+}
+
+typedef Content<EventFlushStringPool, JfrStringPool, &JfrStringPool::write> StringPool;
+typedef Content<EventFlushStringPool, JfrStringPool, &JfrStringPool::write_at_safepoint> StringPoolSafepoint;
+typedef WriteCheckpointEvent<StringPool> WriteStringPool;
+typedef WriteCheckpointEvent<StringPoolSafepoint> WriteStringPoolSafepoint;
+
+static u4 flush_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
+ StringPool sp(string_pool);
+ WriteStringPool wsp(chunkwriter, sp, TYPE_STRING);
+ return invoke_with_flush_event(wsp);
+}
+
+static u4 write_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
+ StringPool sp(string_pool);
+ WriteStringPool wsp(chunkwriter, sp, TYPE_STRING);
+ return invoke(wsp);
+}
+
+static u4 write_stringpool_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
+ StringPoolSafepoint sps(string_pool);
+ WriteStringPoolSafepoint wsps(chunkwriter, sps, TYPE_STRING);
+ return invoke(wsps);
+}
+
+typedef Content<EventFlushTypeSet, JfrCheckpointManager, &JfrCheckpointManager::flush_type_set> FlushTypeSetFunctor;
+typedef WriteContent<FlushTypeSetFunctor> FlushTypeSet;
+
+static u4 flush_typeset(JfrCheckpointManager& checkpoint_manager, JfrChunkWriter& chunkwriter) {
+ FlushTypeSetFunctor flush_type_set(checkpoint_manager);
+ FlushTypeSet fts(chunkwriter, flush_type_set);
+ return invoke_with_flush_event(fts);
+}
+
+class MetadataEvent : public StackObj {
+ private:
+ JfrChunkWriter& _cw;
+ public:
+ typedef EventFlushMetadata EventType;
+ MetadataEvent(JfrChunkWriter& cw) : _cw(cw) {}
+ bool process() {
+ JfrMetadataEvent::write(_cw);
+ return true;
+ }
+ size_t elements() const { return 1; }
+};
+
+typedef WriteContent<MetadataEvent> WriteMetadata;
+
+static u4 flush_metadata(JfrChunkWriter& chunkwriter) {
+ assert(chunkwriter.is_valid(), "invariant");
+ MetadataEvent me(chunkwriter);
+ WriteMetadata wm(chunkwriter, me);
+ return invoke_with_flush_event(wm);
+}
+
+static u4 write_metadata(JfrChunkWriter& chunkwriter) {
+ assert(chunkwriter.is_valid(), "invariant");
+ MetadataEvent me(chunkwriter);
+ WriteMetadata wm(chunkwriter, me);
+ return invoke(wm);
+}
+
template <typename Instance, void(Instance::*func)()>
class JfrVMOperation : public VM_Operation {
private:
@@ -208,23 +418,13 @@
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;
@@ -237,14 +437,6 @@
return recording;
}
-JfrRecorderService::JfrRecorderService() :
- _checkpoint_manager(JfrCheckpointManager::instance()),
- _chunkwriter(JfrRepository::chunkwriter()),
- _repository(JfrRepository::instance()),
- _stack_trace_repository(JfrStackTraceRepository::instance()),
- _storage(JfrStorage::instance()),
- _string_pool(JfrStringPool::instance()) {}
-
void JfrRecorderService::start() {
RotationLock rl(Thread::current());
if (rl.not_acquired()) {
@@ -278,28 +470,28 @@
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();
}
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");
log_debug(jfr, system)("Recording STOPPED");
@@ -307,6 +499,27 @@
assert(!JfrRecorderService::is_recording(), "invariant");
}
+void JfrRecorderService::prepare_for_vm_error_rotation() {
+ if (!_chunkwriter.is_valid()) {
+ open_new_chunk(true);
+ }
+ _checkpoint_manager.register_service_thread(Thread::current());
+}
+
+void JfrRecorderService::vm_error_rotation() {
+ if (_chunkwriter.is_valid()) {
+ pre_safepoint_write();
+ // Do not attempt safepoint dependent operations during emergency dump.
+ // Optimistically write tagged artifacts.
+ _checkpoint_manager.shift_epoch();
+ // update time
+ _chunkwriter.set_time_stamp();
+ post_safepoint_write();
+ assert(!_chunkwriter.is_valid(), "invariant");
+ _repository.on_vm_error();
+ }
+}
+
void JfrRecorderService::rotate(int msgs) {
RotationLock rl(Thread::current());
if (rl.not_acquired()) {
@@ -329,44 +542,16 @@
}
}
-void JfrRecorderService::prepare_for_vm_error_rotation() {
- if (!_chunkwriter.is_valid()) {
- open_new_chunk(true);
- }
- _checkpoint_manager.register_service_thread(Thread::current());
- JfrMetadataEvent::lock();
-}
-
-void JfrRecorderService::open_new_chunk(bool vm_error) {
- assert(!_chunkwriter.is_valid(), "invariant");
- assert(!JfrStream_lock->owned_by_self(), "invariant");
- JfrChunkRotation::on_rotation();
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
- if (!_repository.open_chunk(vm_error)) {
- assert(!_chunkwriter.is_valid(), "invariant");
- _storage.control().set_to_disk(false);
- return;
- }
- assert(_chunkwriter.is_valid(), "invariant");
- _storage.control().set_to_disk(true);
-}
-
void JfrRecorderService::in_memory_rotation() {
- assert(!_chunkwriter.is_valid(), "invariant");
// currently running an in-memory recording
+ assert(!_storage.control().to_disk(), "invariant");
open_new_chunk();
if (_chunkwriter.is_valid()) {
// dump all in-memory buffer data to the newly created chunk
- serialize_storage_from_in_memory_recording();
+ write_storage(_storage, _chunkwriter);
}
}
-void JfrRecorderService::serialize_storage_from_in_memory_recording() {
- assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!");
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
- _storage.write();
-}
-
void JfrRecorderService::chunk_rotation() {
finalize_current_chunk();
open_new_chunk();
@@ -375,7 +560,6 @@
void JfrRecorderService::finalize_current_chunk() {
assert(_chunkwriter.is_valid(), "invariant");
write();
- assert(!_chunkwriter.is_valid(), "invariant");
}
void JfrRecorderService::write() {
@@ -386,54 +570,20 @@
post_safepoint_write();
}
-typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write> WriteStringPool;
-typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write_at_safepoint> WriteStringPoolSafepoint;
-typedef WriteCheckpointEvent<WriteStackTraceRepository> WriteStackTraceCheckpoint;
-typedef WriteCheckpointEvent<WriteStringPool> WriteStringPoolCheckpoint;
-typedef WriteCheckpointEvent<WriteStringPoolSafepoint> WriteStringPoolCheckpointSafepoint;
-
-static void write_stacktrace_checkpoint(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) {
- WriteStackTraceRepository write_stacktrace_repo(stack_trace_repo, chunkwriter, clear);
- WriteStackTraceCheckpoint write_stack_trace_checkpoint(chunkwriter, TYPE_STACKTRACE, write_stacktrace_repo);
- write_stack_trace_checkpoint.process();
-}
-static void write_stringpool_checkpoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
- WriteStringPool write_string_pool(string_pool);
- WriteStringPoolCheckpoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool);
- write_string_pool_checkpoint.process();
-}
-
-static void write_stringpool_checkpoint_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
- WriteStringPoolSafepoint write_string_pool(string_pool);
- WriteStringPoolCheckpointSafepoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool);
- write_string_pool_checkpoint.process();
-}
-
-//
-// pre-safepoint write sequence
-//
-// lock stream lock ->
-// write non-safepoint dependent types ->
-// write checkpoint epoch transition list->
-// write stack trace checkpoint ->
-// write string pool checkpoint ->
-// write object sample stacktraces ->
-// write storage ->
-// release stream lock
-//
void JfrRecorderService::pre_safepoint_write() {
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
assert(_chunkwriter.is_valid(), "invariant");
- _checkpoint_manager.write_types();
- _checkpoint_manager.write_epoch_transition_mspace();
- write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, false);
- write_stringpool_checkpoint(_string_pool, _chunkwriter);
+ if (_stack_trace_repository.is_modified()) {
+ write_stacktrace(_stack_trace_repository, _chunkwriter, false);
+ }
+ if (_string_pool.is_modified()) {
+ write_stringpool(_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();
+ write_storage(_storage, _chunkwriter);
}
void JfrRecorderService::invoke_safepoint_write() {
@@ -441,50 +591,17 @@
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");
- MutexLocker 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();
+ write_stacktrace(_stack_trace_repository, _chunkwriter, true);
+ if (_string_pool.is_modified()) {
+ write_stringpool_safepoint(_string_pool, _chunkwriter);
+ }
_storage.write_at_safepoint();
- _checkpoint_manager.shift_epoch();
- _chunkwriter.time_stamp_chunk_now();
- JfrMetadataEvent::lock();
+ _checkpoint_manager.on_rotation();
+ _chunkwriter.set_time_stamp();
}
-static int64_t write_metadata_event(JfrChunkWriter& chunkwriter) {
- assert(chunkwriter.is_valid(), "invariant");
- const int64_t metadata_offset = chunkwriter.current_offset();
- JfrMetadataEvent::write(chunkwriter, metadata_offset);
- return metadata_offset;
-}
-
-//
-// post-safepoint write sequence
-//
-// write type set ->
-// release object sampler ->
-// lock stream lock ->
-// write checkpoints ->
-// write metadata event ->
-// write chunk header ->
-// close chunk fd ->
-// release stream lock
-//
void JfrRecorderService::post_safepoint_write() {
assert(_chunkwriter.is_valid(), "invariant");
// During the safepoint tasks just completed, the system transitioned to a new epoch.
@@ -497,38 +614,82 @@
// Note: There is a dependency on write_type_set() above, ensure the release is subsequent.
ObjectSampler::release();
}
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
- // serialize any outstanding checkpoint memory
- _checkpoint_manager.write();
// serialize the metadata descriptor event and close out the chunk
- _repository.close_chunk(write_metadata_event(_chunkwriter));
- assert(!_chunkwriter.is_valid(), "invariant");
+ write_metadata(_chunkwriter);
+ _repository.close_chunk();
+}
+
+static JfrBuffer* thread_local_buffer() {
+ return Thread::current()->jfr_thread_local()->native_buffer();
+}
+
+static void reset_buffer(JfrBuffer* buffer) {
+ assert(buffer != NULL, "invariant");
+ assert(buffer == thread_local_buffer(), "invariant");
+ buffer->set_pos(const_cast<u1*>(buffer->top()));
+ assert(buffer->empty(), "invariant");
+}
+
+static void reset_thread_local_buffer() {
+ reset_buffer(thread_local_buffer());
+}
+
+static void write_thread_local_buffer(JfrChunkWriter& chunkwriter) {
+ JfrBuffer * const buffer = thread_local_buffer();
+ assert(buffer != NULL, "invariant");
+ if (!buffer->empty()) {
+ chunkwriter.write_unbuffered(buffer->top(), buffer->pos() - buffer->top());
+ reset_buffer(buffer);
+ }
+ assert(buffer->empty(), "invariant");
}
-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 bool write_metadata_in_flushpoint = false;
+
+size_t JfrRecorderService::flush() {
+ size_t total_elements = 0;
+ if (write_metadata_in_flushpoint) {
+ 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 (_stack_trace_repository.is_modified()) {
+ total_elements += flush_stacktrace(_stack_trace_repository, _chunkwriter);
+ }
+ if (_string_pool.is_modified()) {
+ total_elements += flush_stringpool(_string_pool, _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;
}
-void JfrRecorderService::finalize_current_chunk_on_vm_error() {
+typedef Content<EventFlush, JfrRecorderService, &JfrRecorderService::flush> FlushFunctor;
+typedef WriteContent<FlushFunctor> Flush;
+
+void JfrRecorderService::flush(int msgs) {
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");
+ ResourceMark rm;
+ HandleMark hm;
+ write_metadata_in_flushpoint = (msgs & MSGBIT(MSG_FLUSHPOINT_METADATA));
+ ++flushpoint_id;
+ reset_thread_local_buffer();
+ FlushFunctor flushpoint(*this);
+ Flush fl(_chunkwriter, flushpoint);
+ invoke_with_flush_event(fl);
+ write_thread_local_buffer(_chunkwriter);
+ _repository.flush_chunk();
}
void JfrRecorderService::process_full_buffers() {
if (_chunkwriter.is_valid()) {
- assert(!JfrStream_lock->owned_by_self(), "invariant");
- MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
_storage.write_full();
}
}
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -46,9 +46,7 @@
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();
@@ -68,10 +66,12 @@
JfrRecorderService();
void start();
void rotate(int msgs);
+ void flush(int msgs);
void process_full_buffers();
void scavenge();
void evaluate_chunk_size_for_rotation();
static bool is_recording();
+ size_t flush();
};
#endif // SHARE_JFR_RECORDER_SERVICE_JFRRECORDERSERVICE_HPP
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
+#include "jfr/jfr.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
@@ -64,7 +65,6 @@
if (allocation_failed) {
JfrJavaSupport::throw_out_of_memory_error("Unable to create native recording thread for JFR", CHECK_NULL);
}
-
Thread::start(new_thread);
return new_thread;
}
@@ -100,6 +100,7 @@
// attempt thread start
const Thread* const t = start_thread(h_thread_oop, recorderthread_entry,THREAD);
if (!HAS_PENDING_EXCEPTION) {
+ Jfr::exclude_thread(t);
cp_manager->register_service_thread(t);
return true;
}
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -40,6 +40,7 @@
#define START (msgs & (MSGBIT(MSG_START)))
#define SHUTDOWN (msgs & MSGBIT(MSG_SHUTDOWN))
#define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)))
+ #define FLUSHPOINT (msgs & (MSGBIT(MSG_FLUSHPOINT)|MSGBIT(MSG_FLUSHPOINT_METADATA)))
#define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER)))
#define SCAVENGE (msgs & (MSGBIT(MSG_DEADBUFFER)))
@@ -72,6 +73,8 @@
service.start();
} else if (ROTATE) {
service.rotate(msgs);
+ } else if (FLUSHPOINT) {
+ service.flush(msgs);
}
JfrMsg_lock->lock();
post_box.notify_waiters();
@@ -90,6 +93,7 @@
#undef START
#undef SHUTDOWN
#undef ROTATE
+ #undef FLUSHPOINT
#undef PROCESS_FULL_BUFFERS
#undef SCAVENGE
}
--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -62,7 +62,7 @@
};
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 @@
_instance = NULL;
}
-size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) {
+static traceid last_id = 0;
+
+bool JfrStackTraceRepository::is_modified() const {
+ return last_id != _next_id;
+}
+
+size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) {
+ if (_entries == 0) {
+ return 0;
+ }
MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
assert(_entries > 0, "invariant");
int count = 0;
@@ -93,29 +102,10 @@
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() {
MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
if (_entries == 0) {
@@ -142,7 +132,7 @@
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();
--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -40,7 +40,7 @@
friend class ObjectSampleCheckpoint;
friend class ObjectSampler;
friend class StackTraceBlobInstaller;
- friend class WriteStackTraceRepository;
+ friend class StackTraceRepository;
private:
static const u4 TABLE_SIZE = 2053;
@@ -51,19 +51,18 @@
JfrStackTraceRepository();
static JfrStackTraceRepository& instance();
static JfrStackTraceRepository* create();
+ static void destroy();
bool initialize();
- static void destroy();
- 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, JfrStackFrame* frames, u4 max_frames);
- const JfrStackTrace* lookup(unsigned int hash, traceid id) const;
public:
static traceid record(Thread* thread, int skip = 0);
--- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -54,10 +54,18 @@
return true;
}
-void JfrBuffer::reinitialize() {
+void JfrBuffer::reinitialize(bool exclusion /* false */) {
assert(!lease(), "invariant");
assert(!transient(), "invariant");
set_pos(start());
+ if (exclusion != excluded()) {
+ // update
+ if (exclusion) {
+ set_excluded();
+ } else {
+ clear_excluded();
+ }
+ }
clear_retired();
set_top(start());
}
@@ -80,7 +88,7 @@
const u1* JfrBuffer::stable_top() const {
const u1* current_top;
do {
- current_top = OrderAccess::load_acquire(&_top);
+ current_top = Atomic::load(&_top);
} while (MUTEX_CLAIM == current_top);
return current_top;
}
@@ -107,7 +115,8 @@
assert(new_top <= end(), "invariant");
assert(new_top >= start(), "invariant");
assert(top() == MUTEX_CLAIM, "invariant");
- OrderAccess::release_store(&_top, new_top);
+ OrderAccess::storestore();
+ _top = new_top;
}
size_t JfrBuffer::unflushed_size() const {
@@ -118,18 +127,19 @@
assert(id != NULL, "invariant");
const void* current_id;
do {
- current_id = OrderAccess::load_acquire(&_identity);
+ current_id = Atomic::load(&_identity);
} while (current_id != NULL || Atomic::cmpxchg(id, &_identity, current_id) != current_id);
}
bool JfrBuffer::try_acquire(const void* id) {
assert(id != NULL, "invariant");
- const void* const current_id = OrderAccess::load_acquire(&_identity);
+ const void* const current_id = Atomic::load(&_identity);
return current_id == NULL && Atomic::cmpxchg(id, &_identity, current_id) == current_id;
}
void JfrBuffer::release() {
- OrderAccess::release_store(&_identity, (const void*)NULL);
+ OrderAccess::storestore();
+ _identity = NULL;
}
bool JfrBuffer::acquired_by(const void* id) const {
@@ -186,7 +196,8 @@
enum FLAG {
RETIRED = 1,
TRANSIENT = 2,
- LEASE = 4
+ LEASE = 4,
+ EXCLUDED = 8
};
bool JfrBuffer::transient() const {
@@ -221,27 +232,35 @@
assert(!lease(), "invariant");
}
-static u2 load_acquire_flags(const u2* const flags) {
- return OrderAccess::load_acquire(flags);
+bool JfrBuffer::excluded() const {
+ return (u1)EXCLUDED == (_flags & (u1)EXCLUDED);
}
-static void release_store_flags(u2* const flags, u2 new_flags) {
- OrderAccess::release_store(flags, new_flags);
+void JfrBuffer::set_excluded() {
+ _flags |= (u1)EXCLUDED;
+ assert(excluded(), "invariant");
+}
+
+void JfrBuffer::clear_excluded() {
+ if (excluded()) {
+ OrderAccess::storestore();
+ _flags ^= (u1)EXCLUDED;
+ }
+ assert(!excluded(), "invariant");
}
bool JfrBuffer::retired() const {
- return (u1)RETIRED == (load_acquire_flags(&_flags) & (u1)RETIRED);
+ return (_flags & (u1)RETIRED) == (u1)RETIRED;
}
void JfrBuffer::set_retired() {
- const u2 new_flags = load_acquire_flags(&_flags) | (u1)RETIRED;
- release_store_flags(&_flags, new_flags);
+ OrderAccess::storestore();
+ _flags |= (u1)RETIRED;
}
void JfrBuffer::clear_retired() {
- u2 new_flags = load_acquire_flags(&_flags);
- if ((u1)RETIRED == (new_flags & (u1)RETIRED)) {
- new_flags ^= (u1)RETIRED;
- release_store_flags(&_flags, new_flags);
+ if (retired()) {
+ OrderAccess::storestore();
+ _flags ^= (u1)RETIRED;
}
}
--- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -61,7 +61,7 @@
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 {
@@ -165,12 +165,15 @@
bool retired() const;
void set_retired();
void clear_retired();
+
+ bool excluded() const;
+ void set_excluded();
+ void clear_excluded();
};
class JfrAgeNode : public JfrBuffer {
private:
JfrBuffer* _retired;
-
public:
JfrAgeNode() : _retired(NULL) {}
void set_retired_buffer(JfrBuffer* retired) {
--- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -99,8 +99,8 @@
template <typename IteratorCallback, typename IteratorType>
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_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_HPP
--- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -141,6 +141,7 @@
}
assert(t->empty(), "invariant");
assert(!t->retired(), "invariant");
+ assert(!t->excluded(), "invariant");
assert(t->identity() == NULL, "invariant");
if (!should_populate_cache()) {
remove_free(t);
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -26,6 +26,7 @@
#include "jfr/jfrEvents.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
+#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/service/jfrPostBox.hpp"
@@ -253,6 +254,18 @@
assert(buffer->empty(), "invariant");
return true;
}
+
+ if (buffer->excluded()) {
+ const bool thread_is_excluded = thread->jfr_thread_local()->is_excluded();
+ buffer->reinitialize(thread_is_excluded);
+ assert(buffer->empty(), "invariant");
+ if (!thread_is_excluded) {
+ // state change from exclusion to inclusion requires a thread checkpoint
+ JfrCheckpointManager::write_thread_checkpoint(thread);
+ }
+ return true;
+ }
+
BufferPtr const promotion_buffer = get_promotion_buffer(unflushed_size, _global_mspace, *this, promotion_retry, thread);
if (promotion_buffer == NULL) {
write_data_loss(buffer, thread);
@@ -301,7 +314,7 @@
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);
}
@@ -470,6 +483,7 @@
assert(t != NULL, "invariant");
assert(cur != NULL, "invariant");
assert(cur->lease(), "invariant");
+ assert(!cur->excluded(), "invariant");
assert(cur_pos != NULL, "invariant");
assert(native ? t->jfr_thread_local()->native_buffer() == cur : t->jfr_thread_local()->java_buffer() == cur, "invariant");
assert(t->jfr_thread_local()->shelved_buffer() != NULL, "invariant");
@@ -496,6 +510,9 @@
// the case for stable thread local buffers; it is not the case for large buffers.
if (!cur->empty()) {
flush_regular_buffer(cur, t);
+ if (cur->excluded()) {
+ return cur;
+ }
}
assert(t->jfr_thread_local()->shelved_buffer() == NULL, "invariant");
if (cur->free_size() >= req) {
@@ -584,28 +601,40 @@
typedef UnBufferedWriteToChunk<JfrBuffer> WriteOperation;
typedef MutexedWriteOp<WriteOperation> MutexedWriteOperation;
typedef ConcurrentWriteOp<WriteOperation> ConcurrentWriteOperation;
-typedef ConcurrentWriteOpExcludeRetired<WriteOperation> ThreadLocalConcurrentWriteOperation;
+
+typedef Retired<JfrBuffer, true> NonRetired;
+typedef Excluded<JfrBuffer, true> NonExcluded;
+typedef CompositeOperation<NonRetired, NonExcluded> BufferPredicate;
+typedef PredicatedMutexedWriteOp<WriteOperation, BufferPredicate> ThreadLocalMutexedWriteOperation;
+typedef PredicatedConcurrentWriteOp<WriteOperation, BufferPredicate> ThreadLocalConcurrentWriteOperation;
size_t JfrStorage::write() {
- 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<DefaultDiscarder<JfrStorage::Buffer> > DiscardOperation;
@@ -613,14 +642,14 @@
typedef CompositeOperation<MutexedWriteOperation, ReleaseOperation> FullOperation;
size_t JfrStorage::clear() {
- const size_t full_size_processed = clear_full();
+ const size_t full_elements = clear_full();
DiscardOperation discarder(concurrent); // concurrent discard mode
process_full_list(discarder, _thread_local_mspace);
assert(_transient_mspace->is_free_empty(), "invariant");
process_full_list(discarder, _transient_mspace);
assert(_global_mspace->is_full_empty(), "invariant");
process_free_list(discarder, _global_mspace);
- return full_size_processed + discarder.processed();
+ return full_elements + discarder.elements();
}
static void insert_free_age_nodes(JfrStorageAgeMspace* age_mspace, JfrAgeNode* head, JfrAgeNode* tail, size_t count) {
@@ -711,15 +740,25 @@
ReleaseOperation ro(_transient_mspace, thread);
FullOperation cmd(&writer, &ro);
const size_t count = process_full(cmd, control(), _age_mspace);
- log(count, writer.processed());
- return writer.processed();
+ if (0 == count) {
+ assert(0 == writer.elements(), "invariant");
+ return 0;
+ }
+ const size_t size = writer.size();
+ log(count, size);
+ return count;
}
size_t JfrStorage::clear_full() {
DiscardOperation discarder(mutexed); // a retired buffer implies mutexed access
const size_t count = process_full(discarder, control(), _age_mspace);
- log(count, discarder.processed(), true);
- return discarder.processed();
+ if (0 == count) {
+ assert(0 == discarder.elements(), "invariant");
+ return 0;
+ }
+ const size_t size = discarder.size();
+ log(count, size, true);
+ return count;
}
static void scavenge_log(size_t count, size_t amount, size_t current) {
@@ -749,6 +788,10 @@
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();
@@ -767,6 +810,11 @@
}
Scavenger<JfrThreadLocalMspace> scavenger(ctrl, _thread_local_mspace);
process_full_list(scavenger, _thread_local_mspace);
- scavenge_log(scavenger.processed(), scavenger.amount(), ctrl.dead_count());
- return scavenger.processed();
+ const size_t count = scavenger.processed();
+ if (0 == count) {
+ assert(0 == scavenger.amount(), "invariant");
+ return 0;
+ }
+ scavenge_log(count, scavenger.amount(), ctrl.dead_count());
+ return count;
}
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -68,7 +68,6 @@
size_t clear();
size_t clear_full();
- size_t write();
size_t write_full();
size_t write_at_safepoint();
size_t scavenge();
@@ -89,6 +88,8 @@
void discard_oldest(Thread* t);
static JfrStorageControl& control();
+ size_t write();
+
friend class JfrRecorder;
friend class JfrRecorderService;
template <typename, template <typename> class, typename>
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorageControl.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageControl.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -26,14 +26,13 @@
#include "jfr/recorder/storage/jfrStorageControl.hpp"
#include "runtime/atomic.hpp"
#include "runtime/mutexLocker.hpp"
-#include "runtime/orderAccess.hpp"
// returns the updated value
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_acquire(dest);
+ compare_value = *dest;
exchange_value = compare_value + value;
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
return exchange_value;
@@ -43,7 +42,7 @@
size_t compare_value;
size_t exchange_value;
do {
- compare_value = OrderAccess::load_acquire(dest);
+ compare_value = *dest;
assert(compare_value >= 1, "invariant");
exchange_value = compare_value - 1;
} while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
@@ -102,7 +101,7 @@
// concurrent with accuracy requirement
size_t JfrStorageControl::global_lease_count() const {
- return OrderAccess::load_acquire(&_global_lease_count);
+ return Atomic::load(&_global_lease_count);
}
size_t JfrStorageControl::increment_leased() {
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -31,7 +31,21 @@
#include "jfr/utilities/jfrTypes.hpp"
#include "runtime/thread.hpp"
-template <typename Operation, typename NextOperation>
+class CompositeOperationOr {
+ public:
+ static bool evaluate(bool value) {
+ return !value;
+ }
+};
+
+class CompositeOperationAnd {
+ public:
+ static bool evaluate(bool value) {
+ return value;
+ }
+};
+
+template <typename Operation, typename NextOperation, typename TruthFunction = CompositeOperationAnd>
class CompositeOperation {
private:
Operation* _op;
@@ -41,11 +55,15 @@
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,23 +71,71 @@
class UnBufferedWriteToChunk {
private:
JfrChunkWriter& _writer;
- size_t _processed;
+ size_t _elements;
+ size_t _size;
public:
typedef T Type;
- UnBufferedWriteToChunk(JfrChunkWriter& writer) : _writer(writer), _processed(0) {}
+ UnBufferedWriteToChunk(JfrChunkWriter& writer) : _writer(writer), _elements(0), _size(0) {}
bool write(Type* t, const u1* data, size_t size);
- size_t processed() { return _processed; }
+ size_t elements() const { return _elements; }
+ size_t size() const { return _size; }
};
template <typename T>
class DefaultDiscarder {
private:
- size_t _processed;
+ size_t _elements;
+ size_t _size;
+ public:
+ typedef T Type;
+ DefaultDiscarder() : _elements(0), _size(0) {}
+ bool discard(Type* t, const u1* data, size_t size);
+ size_t elements() const { return _elements; }
+ size_t size() const { return _size; }
+};
+
+template <typename T, bool negation>
+class Retired {
+ public:
+ typedef T Type;
+ bool process(Type* t) {
+ assert(t != NULL, "invariant");
+ return negation ? !t->retired() : t->retired();
+ }
+};
+
+template <typename T, bool negation>
+class Excluded {
public:
typedef T Type;
- DefaultDiscarder() : _processed() {}
- bool discard(Type* t, const u1* data, size_t size);
- size_t processed() const { return _processed; }
+ bool process(Type* t) {
+ assert(t != NULL, "invariant");
+ return negation ? !t->excluded() : t->excluded();
+ }
+};
+
+template <typename Operation>
+class MutexedWriteOp {
+ private:
+ Operation& _operation;
+ public:
+ typedef typename Operation::Type Type;
+ MutexedWriteOp(Operation& operation) : _operation(operation) {}
+ bool process(Type* t);
+ size_t elements() const { return _operation.elements(); }
+ size_t size() const { return _operation.size(); }
+};
+
+template <typename Operation, typename Predicate>
+class PredicatedMutexedWriteOp : public MutexedWriteOp<Operation> {
+ private:
+ Predicate& _predicate;
+ public:
+ PredicatedMutexedWriteOp(Operation& operation, Predicate& predicate) :
+ MutexedWriteOp<Operation>(operation), _predicate(predicate) {}
+ bool process(typename Operation::Type* t) {
+ return _predicate.process(t) ? MutexedWriteOp<Operation>::process(t) : true;
+ }
};
template <typename Operation>
@@ -80,27 +146,20 @@
typedef typename Operation::Type Type;
ConcurrentWriteOp(Operation& operation) : _operation(operation) {}
bool process(Type* t);
- size_t processed() const { return _operation.processed(); }
+ size_t elements() const { return _operation.elements(); }
+ size_t size() const { return _operation.size(); }
};
-template <typename Operation>
-class ConcurrentWriteOpExcludeRetired : private ConcurrentWriteOp<Operation> {
+template <typename Operation, typename Predicate>
+class PredicatedConcurrentWriteOp : public ConcurrentWriteOp<Operation> {
+ private:
+ Predicate& _predicate;
public:
- typedef typename Operation::Type Type;
- ConcurrentWriteOpExcludeRetired(Operation& operation) : ConcurrentWriteOp<Operation>(operation) {}
- bool process(Type* t);
- size_t processed() const { return ConcurrentWriteOp<Operation>::processed(); }
-};
-
-template <typename Operation>
-class MutexedWriteOp {
- private:
- Operation& _operation;
- public:
- typedef typename Operation::Type Type;
- MutexedWriteOp(Operation& operation) : _operation(operation) {}
- bool process(Type* t);
- size_t processed() const { return _operation.processed(); }
+ PredicatedConcurrentWriteOp(Operation& operation, Predicate& predicate) :
+ ConcurrentWriteOp<Operation>(operation), _predicate(predicate) {}
+ bool process(typename Operation::Type* t) {
+ return _predicate.process(t) ? ConcurrentWriteOp<Operation>::process(t) : true;
+ }
};
template <typename Operation>
@@ -126,7 +185,8 @@
typedef typename Operation::Type Type;
DiscardOp(jfr_operation_mode mode = concurrent) : _operation(), _mode(mode) {}
bool process(Type* t);
- size_t processed() const { return _operation.processed(); }
+ size_t elements() const { return _operation.elements(); }
+ size_t size() const { return _operation.size(); }
};
#endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -31,13 +31,15 @@
template <typename T>
inline bool UnBufferedWriteToChunk<T>::write(T* t, const u1* data, size_t size) {
_writer.write_unbuffered(data, size);
- _processed += size;
+ ++_elements;
+ _size += size;
return true;
}
template <typename T>
inline bool DefaultDiscarder<T>::discard(T* t, const u1* data, size_t size) {
- _processed += size;
+ ++_elements;
+ _size += size;
return true;
}
@@ -55,15 +57,6 @@
}
template <typename Operation>
-inline bool ConcurrentWriteOpExcludeRetired<Operation>::process(typename Operation::Type* t) {
- if (t->retired()) {
- assert(t->empty(), "invariant");
- return true;
- }
- return ConcurrentWriteOp<Operation>::process(t);
-}
-
-template <typename Operation>
inline bool MutexedWriteOp<Operation>::process(typename Operation::Type* t) {
assert(t != NULL, "invariant");
const u1* const current_top = t->top();
--- a/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -26,7 +26,6 @@
#include "jfr/recorder/storage/jfrVirtualMemory.hpp"
#include "memory/virtualspace.hpp"
#include "runtime/globals.hpp"
-#include "runtime/orderAccess.hpp"
#include "runtime/os.hpp"
#include "services/memTracker.hpp"
#include "utilities/globalDefinitions.hpp"
--- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -33,7 +33,6 @@
#include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "logging/log.hpp"
-#include "runtime/atomic.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/safepoint.hpp"
@@ -42,12 +41,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;
@@ -131,12 +160,16 @@
bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) {
assert(jt != NULL, "invariant");
const bool current_epoch = JfrTraceIdEpoch::epoch();
- 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;
}
@@ -163,9 +196,10 @@
size_t processed() { return _strings_processed; }
};
-template <typename Type>
+template <typename T>
class StringPoolDiscarderStub {
public:
+ typedef T Type;
bool write(Type* buffer, const u1* data, size_t size) {
// stub only, discard happens at higher level
return true;
@@ -197,6 +231,7 @@
}
size_t JfrStringPool::clear() {
+ increment_serialized_generation();
DiscardOperation discard_operation;
ExclusiveDiscardOperation edo(discard_operation);
StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false);
--- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -71,6 +71,7 @@
static JfrStringPool* create(JfrChunkWriter& cw);
bool initialize();
static void destroy();
+ static bool is_modified();
friend class JfrRecorder;
friend class JfrRecorderService;
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -55,6 +55,7 @@
_stack_trace_hash(0),
_stackdepth(0),
_entering_suspend_flag(0),
+ _excluded(false),
_dead(false) {}
u8 JfrThreadLocal::add_data_lost(u8 value) {
@@ -84,9 +85,13 @@
void JfrThreadLocal::on_start(Thread* t) {
assert(t != NULL, "invariant");
assert(Thread::current() == t, "invariant");
+ JfrJavaSupport::on_thread_start(t);
if (JfrRecorder::is_recording()) {
- if (t->is_Java_thread()) {
- send_java_thread_start_event((JavaThread*)t);
+ if (!t->jfr_thread_local()->is_excluded()) {
+ JfrCheckpointManager::write_thread_checkpoint(t);
+ if (t->is_Java_thread()) {
+ send_java_thread_start_event((JavaThread*)t);
+ }
}
}
}
@@ -109,42 +114,54 @@
assert(Thread::current() == t, "invariant");
assert(!tl->is_dead(), "invariant");
assert(tl->shelved_buffer() == NULL, "invariant");
+ tl->_dead = true;
+ if (tl->has_java_event_writer()) {
+ assert(t->is_Java_thread(), "invariant");
+ const jobject event_writer = tl->java_event_writer();
+ tl->set_java_event_writer(NULL);
+ JfrJavaSupport::destroy_global_jni_handle(event_writer);
+ }
if (tl->has_native_buffer()) {
JfrStorage::release_thread_local(tl->native_buffer(), t);
}
if (tl->has_java_buffer()) {
JfrStorage::release_thread_local(tl->java_buffer(), t);
}
- if (tl->has_java_event_writer()) {
- assert(t->is_Java_thread(), "invariant");
- JfrJavaSupport::destroy_global_jni_handle(tl->java_event_writer());
- }
FREE_C_HEAP_ARRAY(JfrStackFrame, tl->_stackframes);
- tl->_dead = true;
}
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;
}
@@ -162,6 +179,16 @@
return in_ByteSize(offset_of(JfrThreadLocal, _java_event_writer));
}
+void JfrThreadLocal::exclude(const Thread* t) {
+ assert(t != NULL, "invariant");
+ t->jfr_thread_local()->_excluded = true;
+}
+
+void JfrThreadLocal::include(const Thread* t) {
+ assert(t != NULL, "invariant");
+ t->jfr_thread_local()->_excluded = false;
+}
+
u4 JfrThreadLocal::stackdepth() const {
return _stackdepth != 0 ? _stackdepth : (u4)JfrOptionSet::stackdepth();
}
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -50,6 +50,7 @@
unsigned int _stack_trace_hash;
mutable u4 _stackdepth;
volatile jint _entering_suspend_flag;
+ bool _excluded;
bool _dead;
JfrBuffer* install_native_buffer() const;
@@ -203,6 +204,10 @@
_trace_id = id;
}
+ bool is_excluded() const {
+ return _excluded;
+ }
+
bool is_dead() const {
return _dead;
}
@@ -211,6 +216,9 @@
void set_thread_blob(const JfrBlobHandle& handle);
const JfrBlobHandle& thread_blob() const;
+ static void exclude(const Thread* t);
+ static void include(const Thread* t);
+
static void on_start(Thread* t);
static void on_exit(Thread* t);
--- a/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -69,7 +69,7 @@
jbyte* meta_addr() const {
#ifdef VM_LITTLE_ENDIAN
- return (jbyte*)(&_flags) + 1;
+ return ((jbyte*)&_flags) + 1;
#else
return (jbyte*)&_flags;
#endif
--- a/src/hotspot/share/jfr/utilities/jfrAllocation.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/utilities/jfrAllocation.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -28,7 +28,6 @@
#include "logging/log.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/atomic.hpp"
-#include "runtime/orderAccess.hpp"
#include "runtime/vm_version.hpp"
#include "utilities/debug.hpp"
#include "utilities/macros.hpp"
@@ -40,7 +39,7 @@
jlong compare_value;
jlong exchange_value;
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;
--- a/src/hotspot/share/jfr/utilities/jfrDoublyLinkedList.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/utilities/jfrDoublyLinkedList.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -48,8 +48,8 @@
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 <typename T>
@@ -153,7 +153,6 @@
return node;
}
-#ifdef ASSERT
template <typename T>
bool JfrDoublyLinkedList<T>::locate(const T* node, const T* const target) const {
assert(target != NULL, "invariant");
@@ -182,7 +181,6 @@
}
assert(count_param == count, "invariant");
}
-#endif // ASSERT
template <typename T>
void JfrDoublyLinkedList<T>::append_list(T* const head_node, T* const tail_node, size_t count) {
--- a/src/hotspot/share/jfr/utilities/jfrLogTagSets.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/utilities/jfrLogTagSets.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -53,6 +53,7 @@
JFR_LOG_TAG(jfr, system, bytecode) \
JFR_LOG_TAG(jfr, system, parser) \
JFR_LOG_TAG(jfr, system, metadata) \
+ JFR_LOG_TAG(jfr, system, streaming) \
JFR_LOG_TAG(jfr, metadata) \
JFR_LOG_TAG(jfr, event) \
JFR_LOG_TAG(jfr, setting) \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrThreadIterator.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jfr/support/jfrThreadLocal.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
+#include "runtime/thread.inline.hpp"
+
+static bool thread_inclusion_predicate(Thread* t) {
+ assert(t != NULL, "invariant");
+ return !t->jfr_thread_local()->is_dead();
+}
+
+static bool java_thread_inclusion_predicate(JavaThread* jt) {
+ assert(jt != NULL, "invariant");
+ return thread_inclusion_predicate(jt) && jt->thread_state() != _thread_new;
+}
+
+static JavaThread* next_java_thread(JavaThreadIteratorWithHandle& iter) {
+ JavaThread* next = iter.next();
+ while (next != NULL && !java_thread_inclusion_predicate(next)) {
+ next = iter.next();
+ }
+ return next;
+}
+
+static NonJavaThread* next_non_java_thread(NonJavaThread::Iterator& iter) {
+ NonJavaThread* next = NULL;
+ while (!iter.end()) {
+ next = iter.current();
+ iter.step();
+ assert(next != NULL, "invariant");
+ if (!thread_inclusion_predicate(next)) {
+ continue;
+ }
+ }
+ return next;
+}
+
+JfrJavaThreadIteratorAdapter::JfrJavaThreadIteratorAdapter() : _iter(), _next(next_java_thread(_iter)) {}
+
+JavaThread* JfrJavaThreadIteratorAdapter::next() {
+ assert(has_next(), "invariant");
+ Type* const temp = _next;
+ _next = next_java_thread(_iter);
+ assert(temp != _next, "invariant");
+ return temp;
+}
+
+JfrNonJavaThreadIteratorAdapter::JfrNonJavaThreadIteratorAdapter() : _iter(), _next(next_non_java_thread(_iter)) {}
+
+bool JfrNonJavaThreadIteratorAdapter::has_next() const {
+ return _next != NULL;
+}
+
+NonJavaThread* JfrNonJavaThreadIteratorAdapter::next() {
+ assert(has_next(), "invariant");
+ Type* const temp = _next;
+ _next = next_non_java_thread(_iter);
+ assert(temp != _next, "invariant");
+ return temp;
+}
+
+// explicit instantiations
+template class JfrThreadIterator<JfrJavaThreadIteratorAdapter, StackObj>;
+template class JfrThreadIterator<JfrNonJavaThreadIteratorAdapter, StackObj>;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
+#define SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
+
+#include "memory/allocation.hpp"
+#include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
+
+template <typename Adapter, typename AP = StackObj>
+class JfrThreadIterator : public AP {
+ private:
+ Adapter _adapter;
+ public:
+ JfrThreadIterator() : _adapter() {}
+ typename Adapter::Type* next() {
+ assert(has_next(), "invariant");
+ return _adapter.next();
+ }
+ bool has_next() const {
+ return _adapter.has_next();
+ }
+};
+
+class JfrJavaThreadIteratorAdapter {
+ private:
+ JavaThreadIteratorWithHandle _iter;
+ JavaThread* _next;
+ public:
+ typedef JavaThread Type;
+ JfrJavaThreadIteratorAdapter();
+ bool has_next() const {
+ return _next != NULL;
+ }
+ Type* next();
+};
+
+class JfrNonJavaThreadIteratorAdapter {
+ private:
+ NonJavaThread::Iterator _iter;
+ NonJavaThread* _next;
+ public:
+ typedef NonJavaThread Type;
+ JfrNonJavaThreadIteratorAdapter();
+ bool has_next() const;
+ Type* next();
+};
+
+typedef JfrThreadIterator<JfrJavaThreadIteratorAdapter, StackObj> JfrJavaThreadIterator;
+typedef JfrThreadIterator<JfrNonJavaThreadIteratorAdapter, StackObj> JfrNonJavaThreadIterator;
+
+#endif // SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
--- a/src/hotspot/share/jfr/utilities/jfrTypes.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/utilities/jfrTypes.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -26,11 +26,14 @@
#define SHARE_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;
@@ -48,4 +51,12 @@
TIMED
};
+enum JfrCheckpointType {
+ GENERIC,
+ FLUSH,
+ HEADER,
+ STATICS = 4,
+ THREADS = 8
+};
+
#endif // SHARE_JFR_UTILITIES_JFRTYPES_HPP
--- a/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -32,9 +32,9 @@
class Thread;
class JfrJavaEventWriter : AllStatic {
- friend class JfrCheckpointThreadClosure;
+ friend class JfrNotifyClosure;
+ friend class JfrJavaEventWriterNotifyOperation;
friend class JfrJavaEventWriterNotificationClosure;
- friend class JfrJavaEventWriterNotifyOperation;
friend class JfrRecorder;
private:
static bool initialize();
--- a/src/hotspot/share/jfr/writers/jfrStorageAdapter.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/writers/jfrStorageAdapter.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -82,7 +82,7 @@
assert(_thread != NULL, "invariant");
Flush f(_storage, used, requested, _thread);
_storage = f.result();
- return _storage != NULL;
+ return _storage != NULL && !_storage->excluded();
}
void release() {
@@ -236,7 +236,8 @@
void release() {}
bool flush(size_t used, size_t requested) {
// don't flush/expand a buffer that is not our own
- return false;
+ _pos = _start;
+ return true;
}
};
--- a/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -114,10 +114,7 @@
template <typename BE, typename IE, typename WriterPolicyImpl >
template <typename T>
inline void WriterHost<BE, IE, WriterPolicyImpl>::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 <typename BE, typename IE, typename WriterPolicyImpl >
--- a/src/hotspot/share/logging/logTag.hpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/hotspot/share/logging/logTag.hpp Wed Oct 09 23:22:56 2019 +0200
@@ -148,6 +148,7 @@
LOG_TAG(startuptime) \
LOG_TAG(state) \
LOG_TAG(stats) \
+ LOG_TAG(streaming) \
LOG_TAG(stringdedup) \
LOG_TAG(stringtable) \
LOG_TAG(symboltable) \
--- a/src/java.desktop/windows/native/common/awt/systemscale/systemScale.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/src/java.desktop/windows/native/common/awt/systemscale/systemScale.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -93,7 +93,7 @@
__uuidof(ID2D1Factory), NULL,
&m_pDirect2dFactory);
if (res == S_OK) {
- m_pDirect2dFactory->GetDesktopDpi(dpiX, dpiY);
+ // m_pDirect2dFactory->GetDesktopDpi(dpiX, dpiY);
m_pDirect2dFactory->Release();
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/Recording.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/Recording.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -413,6 +413,36 @@
internal.setMaxSize(maxSize);
}
+ /**
+ * Determines how often events are made available for streaming.
+ *
+ * @param interval the interval at which events are made available for streaming.
+ *
+ * @throws IllegalArgumentException if {@code interval} is negative
+ *
+ * @throws IllegalStateException if the recording is in the {@code CLOSED} state
+ *
+ * @since 14
+ */
+ public void setFlushInterval(Duration interval) {
+ Objects.nonNull(interval);
+ if (interval.isNegative()) {
+ throw new IllegalArgumentException("Stream interval can't be negative");
+ }
+ internal.setFlushInterval(interval);
+ }
+
+ /**
+ * Returns how often events are made available for streaming purposes.
+ *
+ * @return the flush interval, or {@code null} if no interval has been set
+ *
+ * @since 14
+ */
+ public Duration getFlushInterval() {
+ return internal.getFlushInterval();
+ }
+
/**
* Determines how far back data is kept in the disk repository.
* <p>
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ChunkParser.java Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.consumer;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-
-import jdk.jfr.EventType;
-import jdk.jfr.internal.LogLevel;
-import jdk.jfr.internal.LogTag;
-import jdk.jfr.internal.Logger;
-import jdk.jfr.internal.MetadataDescriptor;
-import jdk.jfr.internal.Type;
-import jdk.jfr.internal.consumer.ChunkHeader;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Parses a chunk.
- *
- */
-final class ChunkParser {
- private static final long CONSTANT_POOL_TYPE_ID = 1;
- private final RecordingInput input;
- private final LongMap<Parser> parsers;
- private final ChunkHeader chunkHeader;
- private final long absoluteChunkEnd;
- private final MetadataDescriptor metadata;
- private final LongMap<Type> typeMap;
- private final TimeConverter timeConverter;
-
- public ChunkParser(RecordingInput input) throws IOException {
- this(new ChunkHeader(input));
- }
-
- private ChunkParser(ChunkHeader header) throws IOException {
- this.input = header.getInput();
- this.chunkHeader = header;
- this.metadata = header.readMetadata();
- this.absoluteChunkEnd = header.getEnd();
- this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
-
- ParserFactory factory = new ParserFactory(metadata, timeConverter);
- LongMap<ConstantMap> constantPools = factory.getConstantPools();
- parsers = factory.getParsers();
- typeMap = factory.getTypeMap();
-
- fillConstantPools(parsers, constantPools);
- constantPools.forEach(ConstantMap::setIsResolving);
- constantPools.forEach(ConstantMap::resolve);
- constantPools.forEach(ConstantMap::setResolved);
-
- input.position(chunkHeader.getEventStart());
- }
-
- public RecordedEvent readEvent() throws IOException {
- while (input.position() < absoluteChunkEnd) {
- long pos = input.position();
- int size = input.readInt();
- if (size == 0) {
- throw new IOException("Event can't have zero size");
- }
- long typeId = input.readLong();
- if (typeId > CONSTANT_POOL_TYPE_ID) { // also skips metadata (id=0)
- Parser ep = parsers.get(typeId);
- if (ep instanceof EventParser) {
- return (RecordedEvent) ep.parse(input);
- }
- }
- input.position(pos + size);
- }
- return null;
- }
-
- private void fillConstantPools(LongMap<Parser> typeParser, LongMap<ConstantMap> constantPools) throws IOException {
- long nextCP = chunkHeader.getAbsoluteChunkStart();
- long deltaToNext = chunkHeader.getConstantPoolPosition();
- while (deltaToNext != 0) {
- nextCP += deltaToNext;
- input.position(nextCP);
- final long position = nextCP;
- int size = input.readInt(); // size
- long typeId = input.readLong();
- if (typeId != CONSTANT_POOL_TYPE_ID) {
- throw new IOException("Expected check point event (id = 1) at position " + nextCP + ", but found type id = " + typeId);
- }
- input.readLong(); // timestamp
- input.readLong(); // duration
- deltaToNext = input.readLong();
- final long delta = deltaToNext;
- boolean flush = input.readBoolean();
- int poolCount = input.readInt();
- Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
- return "New constant pool: startPosition=" + position + ", size=" + size + ", deltaToNext=" + delta + ", flush=" + flush + ", poolCount=" + poolCount;
- });
-
- for (int i = 0; i < poolCount; i++) {
- long id = input.readLong(); // type id
- ConstantMap pool = constantPools.get(id);
- Type type = typeMap.get(id);
- if (pool == null) {
- Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used");
- if (type == null) {
- throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]");
- }
- pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
- constantPools.put(type.getId(), pool);
- }
- Parser parser = typeParser.get(id);
- if (parser == null) {
- throw new IOException("Could not find constant pool type with id = " + id);
- }
- try {
- int count = input.readInt();
- Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> "Constant: " + getName(id) + "[" + count + "]");
- for (int j = 0; j < count; j++) {
- long key = input.readLong();
- Object value = parser.parse(input);
- pool.put(key, value);
- }
- } catch (Exception e) {
- throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]", e);
- }
- }
- if (input.position() != nextCP + size) {
- throw new IOException("Size of check point event doesn't match content");
- }
- }
- }
-
- private String getName(long id) {
- Type type = typeMap.get(id);
- return type == null ? ("unknown(" + id + ")") : type.getName();
- }
-
- public Collection<Type> getTypes() {
- return metadata.getTypes();
- }
-
- public List<EventType> getEventTypes() {
- return metadata.getEventTypes();
- }
-
- public boolean isLastChunk() {
- return chunkHeader.isLastChunk();
- }
-
- public ChunkParser nextChunkParser() throws IOException {
- return new ChunkParser(chunkHeader.nextHeader());
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ConstantMap.java Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.consumer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Holds mapping between a set of keys and their corresponding object.
- *
- * If the type is a known type, i.e. {@link RecordedThread}, an
- * {@link ObjectFactory} can be supplied which will instantiate a typed object.
- */
-final class ConstantMap {
- private final static class Reference {
- private final long key;
- private final ConstantMap pool;
-
- Reference(ConstantMap pool, long key) {
- this.pool = pool;
- this.key = key;
- }
-
- Object resolve() {
- return pool.get(key);
- }
- }
-
- private final ObjectFactory<?> factory;
- private final LongMap<Object> objects;
-
- private LongMap<Boolean> isResolving;
- private boolean allResolved;
- private String name;
-
- ConstantMap(ObjectFactory<?> factory, String name) {
- this.name = name;
- this.objects = new LongMap<>();
- this.factory = factory;
- }
-
- Object get(long id) {
- // fast path, all objects in pool resolved
- if (allResolved) {
- return objects.get(id);
- }
- // referenced from a pool, deal with this later
- if (isResolving == null) {
- return new Reference(this, id);
- }
-
- Boolean beingResolved = isResolving.get(id);
-
- // we are resolved (but not the whole pool)
- if (Boolean.FALSE.equals(beingResolved)) {
- return objects.get(id);
- }
-
- // resolving ourself, abort to avoid infinite recursion
- if (Boolean.TRUE.equals(beingResolved)) {
- return null;
- }
-
- // resolve me!
- isResolving.put(id, Boolean.TRUE);
- Object resolved = resolve(objects.get(id));
- isResolving.put(id, Boolean.FALSE);
- if (factory != null) {
- Object factorized = factory.createObject(id, resolved);
- objects.put(id, factorized);
- return factorized;
- } else {
- objects.put(id, resolved);
- return resolved;
- }
- }
-
- private static Object resolve(Object o) {
- if (o instanceof Reference) {
- return resolve(((Reference) o).resolve());
- }
- if (o != null && o.getClass().isArray()) {
- final Object[] array = (Object[]) o;
- for (int i = 0; i < array.length; i++) {
- array[i] = resolve(array[i]);
- }
- return array;
- }
- return o;
- }
-
- public void resolve() {
- List<Long> keyList = new ArrayList<>();
- objects.keys().forEachRemaining(keyList::add);
- for (Long l : keyList) {
- get(l);
- }
- }
-
- public void put(long key, Object value) {
- objects.put(key, value);
- }
-
- public void setIsResolving() {
- isResolving = new LongMap<>();
- }
-
- public void setResolved() {
- allResolved = true;
- isResolving = null; // pool finished, release memory
- }
-
- public String getName() {
- return name;
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.consumer;
-
-import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION;
-
-import java.io.IOException;
-import java.util.List;
-
-import jdk.jfr.EventType;
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Parses an event and returns a {@link RecordedEvent}.
- *
- */
-final class EventParser extends Parser {
- private final Parser[] parsers;
- private final EventType eventType;
- private final TimeConverter timeConverter;
- private final boolean hasDuration;
- private final List<ValueDescriptor> valueDescriptors;
-
- EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
- this.timeConverter = timeConverter;
- this.parsers = parsers;
- this.eventType = type;
- this.hasDuration = type.getField(FIELD_DURATION) != null;
- this.valueDescriptors = type.getFields();
- }
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- Object[] values = new Object[parsers.length];
- for (int i = 0; i < parsers.length; i++) {
- values[i] = parsers[i].parse(input);
- }
- Long startTicks = (Long) values[0];
- long startTime = timeConverter.convertTimestamp(startTicks);
- if (hasDuration) {
- long durationTicks = (Long) values[1];
- long endTime = timeConverter.convertTimestamp(startTicks + durationTicks);
- return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter);
- } else {
- return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter);
- }
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+import jdk.jfr.internal.SecuritySupport;
+import jdk.jfr.internal.Utils;
+import jdk.jfr.internal.consumer.EventDirectoryStream;
+import jdk.jfr.internal.consumer.EventFileStream;
+import jdk.jfr.internal.consumer.FileAccess;
+
+/**
+ * Represents a stream of events.
+ * <p>
+ * A stream is a sequence of events and the way to interact with a stream is to
+ * register actions. The {@code EventStream} interface is not to be implemented
+ * and future versions of the JDK may prevent this completely.
+ * <p>
+ * To receive a notification when an event arrives, register an action using the
+ * {@link #onEvent(Consumer)} method. To filter the stream for an event with a
+ * specific name, use {@link #onEvent(String, Consumer)} method.
+ * <p>
+ * By default, the same {@code RecordedEvent} object can be used to
+ * represent two or more distinct events. That object can be delivered
+ * multiple times to the same action as well as to other actions. To use an
+ * event object after the action is completed, the
+ * {@link #setReuse(boolean)} method should be set to {@code false} so a
+ * new object is allocated for each event.
+ * <p>
+ * Events are delivered in batches. To receive a notification when a batch is
+ * complete, register an action using the {@link #onFlush(Runnable)} method.
+ * This is an opportunity to aggregate or push data to external systems while
+ * the Java Virtual Machine (JVM) is preparing the next batch.
+ * <p>
+ * Events within a batch are sorted chronologically by their end time.
+ * Well-ordering of events is only maintained for events available to the JVM at
+ * the point of flush, i.e. for the set of events delivered as a unit in a
+ * single batch. Events delivered in a batch could therefore be out-of-order
+ * compared to events delivered in a previous batch, but never out-of-order with
+ * events within the same batch. If ordering is not a concern, sorting can be
+ * disabled using the {@link #setOrdered(boolean)} method.
+ * <p>
+ * To dispatch events to registered actions, the stream must be started. To
+ * start processing in the current thread, invoke the {@link #start()} method.
+ * To process actions asynchronously in a separate thread, invoke the
+ * {@link #startAsync()} method. To await completion of the stream, use the
+ * awaitTermination {@link #awaitTermination()} or the
+ * {@link #awaitTermination(Duration)} method.
+ * <p>
+ * When a stream ends it is automatically closed. To manually stop processing of
+ * events, close the stream by invoking the {@link #close()} method. A stream
+ * can also be automatically closed in exceptional circumstances, for example if
+ * the JVM that is being monitored exits. To receive a notification in any of
+ * these occasions, use the {@link #onClose(Runnable)} method to register an
+ * action.
+ * <p>
+ * If an unexpected exception occurs in an action, it is possible to catch the
+ * exception in an error handler. An error handler can be registered using the
+ * {@link #onError(Runnable)} method. If no error handler is registered, the
+ * default behavior is to print the exception and its backtrace to the standard
+ * error stream.
+ * <p>
+ * The following example shows how an {@code EventStream} can be used to listen
+ * to events on a JVM running Flight Recorder
+ *
+ * <pre>
+ * <code>
+ * try (var es = EventStream.openRepository()) {
+ * es.onEvent("jdk.CPULoad", event -> {
+ * System.out.println("CPU Load " + event.getEndTime());
+ * System.out.println(" Machine total: " + 100 * event.getFloat("machineTotal") + "%");
+ * System.out.println(" JVM User: " + 100 * event.getFloat("jvmUser") + "%");
+ * System.out.println(" JVM System: " + 100 * event.getFloat("jvmSystem") + "%");
+ * System.out.println();
+ * });
+ * es.onEvent("jdk.GarbageCollection", event -> {
+ * System.out.println("Garbage collection: " + event.getLong("gcId"));
+ * System.out.println(" Cause: " + event.getString("cause"));
+ * System.out.println(" Total pause: " + event.getDuration("sumOfPauses"));
+ * System.out.println(" Longest pause: " + event.getDuration("longestPause"));
+ * System.out.println();
+ * });
+ * es.start();
+ * }
+ * </code>
+ * </pre>
+ * <p>
+ * To start recording together with the stream, see {@link RecordingStream}.
+ *
+ * @since 14
+ */
+public interface EventStream extends AutoCloseable {
+ /**
+ * Creates a stream from the repository of the current Java Virtual Machine
+ * (JVM).
+ * <p>
+ * By default, the stream starts with the next event flushed by Flight
+ * Recorder.
+ *
+ * @return an event stream, not {@code null}
+ *
+ * @throws IOException if a stream can't be opened, or an I/O error occurs
+ * when trying to access the repository
+ *
+ * @throws SecurityException if a security manager exists and the caller
+ * does not have
+ * {@code FlightRecorderPermission("accessFlightRecorder")}
+ */
+ public static EventStream openRepository() throws IOException {
+ Utils.checkAccessFlightRecorder();
+ return new EventDirectoryStream(AccessController.getContext(), null, SecuritySupport.PRIVILIGED, false);
+ }
+
+ /**
+ * Creates an event stream from a disk repository.
+ * <p>
+ * By default, the stream starts with the next event flushed by Flight
+ * Recorder.
+ *
+ * @param directory location of the disk repository, not {@code null}
+ *
+ * @return an event stream, not {@code null}
+ *
+ * @throws IOException if a stream can't be opened, or an I/O error occurs
+ * when trying to access the repository
+ *
+ * @throws SecurityException if a security manager exists and its
+ * {@code checkRead} method denies read access to the directory, or
+ * files in the directory.
+ */
+ public static EventStream openRepository(Path directory) throws IOException {
+ Objects.nonNull(directory);
+ AccessControlContext acc = AccessController.getContext();
+ return new EventDirectoryStream(acc, directory, FileAccess.UNPRIVILIGED, false);
+ }
+
+ /**
+ * Creates an event stream from a file.
+ * <p>
+ * By default, the stream starts with the first event in the file.
+ *
+ * @param file location of the file, not {@code null}
+ *
+ * @return an event stream, not {@code null}
+ *
+ * @throws IOException if the file can't be opened, or an I/O error occurs
+ * during reading
+ *
+ * @throws SecurityException if a security manager exists and its
+ * {@code checkRead} method denies read access to the file
+ */
+ static EventStream openFile(Path file) throws IOException {
+ return new EventFileStream(AccessController.getContext(), file);
+ }
+
+ /**
+ * Registers an action to perform on all events in the stream.
+ *
+ * @param action an action to perform on each {@code RecordedEvent}, not
+ * {@code null}
+ */
+ void onEvent(Consumer<RecordedEvent> action);
+
+ /**
+ * Registers an action to perform on all events matching a name.
+ *
+ * @param eventName the name of the event, not {@code null}
+ *
+ * @param action an action to perform on each {@code RecordedEvent} matching
+ * the event name, not {@code null}
+ */
+ void onEvent(String eventName, Consumer<RecordedEvent> action);
+
+ /**
+ * Registers an action to perform after the stream has been flushed.
+ *
+ * @param action an action to perform after the stream has been
+ * flushed, not {@code null}
+ */
+ void onFlush(Runnable action);
+
+ /**
+ * Registers an action to perform if an exception occurs.
+ * <p>
+ * if an action is not registered, an exception stack trace is printed to
+ * standard error.
+ * <p>
+ * Registering an action overrides the default behavior. If multiple actions
+ * have been registered, they are performed in the order of registration.
+ * <p>
+ * If this method itself throws an exception, resulting behavior is
+ * undefined.
+ *
+ * @param action an action to perform if an exception occurs, not
+ * {@code null}
+ */
+ void onError(Consumer<Throwable> action);
+
+ /**
+ * Registers an action to perform when the stream is closed.
+ * <p>
+ * If the stream is already closed, the action will be performed immediately
+ * in the current thread.
+ *
+ * @param action an action to perform after the stream is closed, not
+ * {@code null}
+ * @see #close()
+ */
+ void onClose(Runnable action);
+
+ /**
+ * Releases all resources associated with this stream.
+ * <p>
+ * Closing a previously closed stream has no effect.
+ */
+ void close();
+
+ /**
+ * Unregisters an action.
+ * <p>
+ * If the action has been registered multiple times, all instances are
+ * unregistered.
+ *
+ * @param action the action to unregister, not {@code null}
+ *
+ * @return {@code true} if the action was unregistered, {@code false}
+ * otherwise
+ *
+ * @see #onEvent(Consumer)
+ * @see #onEvent(String, Consumer)
+ * @see #onFlush(Runnable)
+ * @see #onClose(Runnable)
+ * @see #onError(Consumer)
+ */
+ boolean remove(Object action);
+
+ /**
+ * Specifies that the event object in an {@link #onEvent(Consumer)} action
+ * can be reused.
+ * <p>
+ * If reuse is set to {@code true), an action should not keep a reference
+ * to the event object after the action has completed.
+ *
+ * @param reuse {@code true} if an event object can be reused, {@code false}
+ * otherwise
+ */
+ void setReuse(boolean reuse);
+
+ /**
+ * Specifies that events arrives in chronological order, sorted by the time
+ * they were committed to the stream.
+ *
+ * @param ordered if event objects arrive in chronological order to
+ * {@code #onEvent(Consumer)}
+ */
+ void setOrdered(boolean ordered);
+
+ /**
+ * Specifies the start time of the stream.
+ * <p>
+ * The start time must be set before starting the stream
+ *
+ * @param startTime the start time, not {@code null}
+ *
+ * @throws IllegalStateException if the stream is already started
+ *
+ * @see #start()
+ * @see #startAsync()
+ */
+ void setStartTime(Instant startTime);
+
+ /**
+ * Specifies the end time of the stream.
+ * <p>
+ * The end time must be set before starting the stream.
+ * <p>
+ * At end time, the stream is closed.
+ *
+ * @param endTime the end time, not {@code null}
+ *
+ * @throws IllegalStateException if the stream is already started
+ *
+ * @see #start()
+ * @see #startAsync()
+ */
+ void setEndTime(Instant endTime);
+
+ /**
+ * Start processing of actions.
+ * <p>
+ * Actions are performed in the current thread.
+ *
+ * @throws IllegalStateException if the stream is already started or closed
+ */
+ void start();
+
+ /**
+ * Start asynchronous processing of actions.
+ * <p>
+ * Actions are performed in a single separate thread.
+ *
+ * @throws IllegalStateException if the stream is already started or closed
+ */
+ void startAsync();
+
+ /**
+ * Blocks until all actions are completed, or the stream is closed, or the
+ * timeout occurs, or the current thread is interrupted, whichever happens
+ * first.
+ *
+ * @param timeout the maximum time to wait, not {@code null}
+ *
+ * @throws IllegalArgumentException if timeout is negative
+ * @throws InterruptedException if interrupted while waiting
+ *
+ * @see #start()
+ * @see #startAsync()
+ * @see Thread#interrupt()
+ */
+ void awaitTermination(Duration timeout) throws InterruptedException;
+
+ /**
+ * Blocks until all actions are completed, or the stream is closed, or the
+ * current thread is interrupted, whichever happens first.
+ *
+ * @throws InterruptedException if interrupted while waiting
+ *
+ * @see #start()
+ * @see #startAsync()
+ * @see Thread#interrupt()
+ */
+ void awaitTermination() throws InterruptedException;
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/LongMap.java Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.consumer;
-
-import java.util.HashMap;
-import java.util.Iterator;
-
-/**
- * Commonly used data structure for looking up objects given an id (long value)
- *
- * TODO: Implement without using Map and Long objects, to minimize allocation
- *
- * @param <T>
- */
-final class LongMap<T> implements Iterable<T> {
- private final HashMap<Long, T> map;
-
- LongMap() {
- map = new HashMap<>(101);
- }
-
- void put(long id, T object) {
- map.put(id, object);
- }
-
- T get(long id) {
- return map.get(id);
- }
-
- @Override
- public Iterator<T> iterator() {
- return map.values().iterator();
- }
-
- Iterator<Long> keys() {
- return map.keySet().iterator();
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectFactory.java Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.consumer;
-
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
-
-/**
- * Abstract factory for creating specialized types
- */
-abstract class ObjectFactory<T> {
-
- final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types.";
- final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX;
- final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame";
- final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame";
-
- public static ObjectFactory<?> create(Type type, TimeConverter timeConverter) {
- switch (type.getName()) {
- case "java.lang.Thread":
- return RecordedThread.createFactory(type, timeConverter);
- case TYPE_PREFIX_VERSION_1 + "StackFrame":
- case TYPE_PREFIX_VERSION_2 + "StackFrame":
- return RecordedFrame.createFactory(type, timeConverter);
- case TYPE_PREFIX_VERSION_1 + "Method":
- case TYPE_PREFIX_VERSION_2 + "Method":
- return RecordedMethod.createFactory(type, timeConverter);
- case TYPE_PREFIX_VERSION_1 + "ThreadGroup":
- case TYPE_PREFIX_VERSION_2 + "ThreadGroup":
- return RecordedThreadGroup.createFactory(type, timeConverter);
- case TYPE_PREFIX_VERSION_1 + "StackTrace":
- case TYPE_PREFIX_VERSION_2 + "StackTrace":
- return RecordedStackTrace.createFactory(type, timeConverter);
- case TYPE_PREFIX_VERSION_1 + "ClassLoader":
- case TYPE_PREFIX_VERSION_2 + "ClassLoader":
- return RecordedClassLoader.createFactory(type, timeConverter);
- case "java.lang.Class":
- return RecordedClass.createFactory(type, timeConverter);
- }
- return null;
- }
-
- private final List<ValueDescriptor> valueDescriptors;
-
- ObjectFactory(Type type) {
- this.valueDescriptors = type.getFields();
- }
-
- T createObject(long id, Object value) {
- if (value == null) {
- return null;
- }
- if (value instanceof Object[]) {
- return createTyped(valueDescriptors, id, (Object[]) value);
- }
- throw new InternalError("Object factory must have struct type");
- }
-
- abstract T createTyped(List<ValueDescriptor> valueDescriptors, long id, Object[] values);
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/Parser.java Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.consumer;
-
-import java.io.IOException;
-
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Base class for parsing data from a {@link RecordingInput}.
- */
-abstract class Parser {
- /**
- * Parses data from a {@link RecordingInput} and return an object.
- *
- * @param input input to read from
- * @return an object
- * @throws IOException if operation couldn't be completed due to I/O
- * problems
- */
- abstract Object parse(RecordingInput input) throws IOException;
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ParserFactory.java Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,303 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.consumer;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import jdk.jfr.EventType;
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.MetadataDescriptor;
-import jdk.jfr.internal.PrivateAccess;
-import jdk.jfr.internal.Type;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Class that create parsers suitable for reading events and constant pools
- */
-final class ParserFactory {
- private final LongMap<Parser> parsers = new LongMap<>();
- private final TimeConverter timeConverter;
- private final LongMap<Type> types = new LongMap<>();
- private final LongMap<ConstantMap> constantPools;
-
- public ParserFactory(MetadataDescriptor metadata, TimeConverter timeConverter) throws IOException {
- this.constantPools = new LongMap<>();
- this.timeConverter = timeConverter;
- for (Type t : metadata.getTypes()) {
- types.put(t.getId(), t);
- }
- for (Type t : types) {
- if (!t.getFields().isEmpty()) { // Avoid primitives
- CompositeParser cp = createCompositeParser(t);
- if (t.isSimpleType()) { // Reduce to nested parser
- parsers.put(t.getId(), cp.parsers[0]);
- }
-
- }
- }
- // Override event types with event parsers
- for (EventType t : metadata.getEventTypes()) {
- parsers.put(t.getId(), createEventParser(t));
- }
- }
-
- public LongMap<Parser> getParsers() {
- return parsers;
- }
-
- public LongMap<ConstantMap> getConstantPools() {
- return constantPools;
- }
-
- public LongMap<Type> getTypeMap() {
- return types;
- }
-
- private EventParser createEventParser(EventType eventType) throws IOException {
- List<Parser> parsers = new ArrayList<Parser>();
- for (ValueDescriptor f : eventType.getFields()) {
- parsers.add(createParser(f));
- }
- return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0]));
- }
-
- private Parser createParser(ValueDescriptor v) throws IOException {
- boolean constantPool = PrivateAccess.getInstance().isConstantPool(v);
- if (v.isArray()) {
- Type valueType = PrivateAccess.getInstance().getType(v);
- ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null);
- return new ArrayParser(createParser(element));
- }
- long id = v.getTypeId();
- Type type = types.get(id);
- if (type == null) {
- throw new IOException("Type '" + v.getTypeName() + "' is not defined");
- }
- if (constantPool) {
- ConstantMap pool = constantPools.get(id);
- if (pool == null) {
- pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
- constantPools.put(id, pool);
- }
- return new ConstantMapValueParser(pool);
- }
- Parser parser = parsers.get(id);
- if (parser == null) {
- if (!v.getFields().isEmpty()) {
- return createCompositeParser(type);
- } else {
- return registerParserType(type, createPrimitiveParser(type));
- }
- }
- return parser;
- }
-
- private Parser createPrimitiveParser(Type type) throws IOException {
- switch (type.getName()) {
- case "int":
- return new IntegerParser();
- case "long":
- return new LongParser();
- case "float":
- return new FloatParser();
- case "double":
- return new DoubleParser();
- case "char":
- return new CharacterParser();
- case "boolean":
- return new BooleanParser();
- case "short":
- return new ShortParser();
- case "byte":
- return new ByteParser();
- case "java.lang.String":
- ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
- constantPools.put(type.getId(), pool);
- return new StringParser(pool);
- default:
- throw new IOException("Unknown primitive type " + type.getName());
- }
- }
-
- private Parser registerParserType(Type t, Parser parser) {
- Parser p = parsers.get(t.getId());
- // check if parser exists (known type)
- if (p != null) {
- return p;
- }
- parsers.put(t.getId(), parser);
- return parser;
- }
-
- private CompositeParser createCompositeParser(Type type) throws IOException {
- List<ValueDescriptor> vds = type.getFields();
- Parser[] parsers = new Parser[vds.size()];
- CompositeParser composite = new CompositeParser(parsers);
- // need to pre-register so recursive types can be handled
- registerParserType(type, composite);
-
- int index = 0;
- for (ValueDescriptor vd : vds) {
- parsers[index++] = createParser(vd);
- }
- return composite;
- }
-
- private static final class BooleanParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
- }
- }
-
- private static final class ByteParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Byte.valueOf(input.readByte());
- }
- }
-
- private static final class LongParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Long.valueOf(input.readLong());
- }
- }
-
- private static final class IntegerParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Integer.valueOf(input.readInt());
- }
- }
-
- private static final class ShortParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Short.valueOf(input.readShort());
- }
- }
-
- private static final class CharacterParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Character.valueOf(input.readChar());
- }
- }
-
- private static final class FloatParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Float.valueOf(input.readFloat());
- }
- }
-
- private static final class DoubleParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Double.valueOf(input.readDouble());
- }
- }
-
- private static final class StringParser extends Parser {
- private final ConstantMap stringConstantMap;
- private String last;
-
- StringParser(ConstantMap stringConstantMap) {
- this.stringConstantMap = stringConstantMap;
- }
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- String s = parseEncodedString(input);
- if (!Objects.equals(s, last)) {
- last = s;
- }
- return last;
- }
-
- private String parseEncodedString(RecordingInput input) throws IOException {
- byte encoding = input.readByte();
- if (encoding == RecordingInput.STRING_ENCODING_CONSTANT_POOL) {
- long id = input.readLong();
- return (String) stringConstantMap.get(id);
- } else {
- return input.readEncodedString(encoding);
- }
- }
- }
-
- private final static class ArrayParser extends Parser {
- private final Parser elementParser;
-
- public ArrayParser(Parser elementParser) {
- this.elementParser = elementParser;
- }
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- final int size = input.readInt();
- final Object[] array = new Object[size];
- for (int i = 0; i < size; i++) {
- array[i] = elementParser.parse(input);
- }
- return array;
- }
- }
-
- private final static class CompositeParser extends Parser {
- private final Parser[] parsers;
-
- public CompositeParser(Parser[] valueParsers) {
- this.parsers = valueParsers;
- }
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- final Object[] values = new Object[parsers.length];
- for (int i = 0; i < values.length; i++) {
- values[i] = parsers[i].parse(input);
- }
- return values;
- }
- }
-
- private static final class ConstantMapValueParser extends Parser {
- private final ConstantMap pool;
-
- ConstantMapValueParser(ConstantMap pool) {
- this.pool = pool;
- }
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return pool.get(input.readLong());
- }
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClass.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClass.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,10 +26,8 @@
package jdk.jfr.consumer;
import java.lang.reflect.Modifier;
-import java.util.List;
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded Java type, such as a class or an interface.
@@ -37,21 +35,11 @@
* @since 9
*/
public final class RecordedClass extends RecordedObject {
-
- static ObjectFactory<RecordedClass> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedClass>(type) {
- @Override
- RecordedClass createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
- return new RecordedClass(desc, id, object, timeConverter);
- }
- };
- }
-
private final long uniqueId;
// package private
- private RecordedClass(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
- super(descriptors, values, timeConverter);
+ RecordedClass(ObjectContext objectContext, long id, Object[] values) {
+ super(objectContext, values);
this.uniqueId = id;
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClassLoader.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClassLoader.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,10 +25,7 @@
package jdk.jfr.consumer;
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded Java class loader.
@@ -36,21 +33,11 @@
* @since 9
*/
public final class RecordedClassLoader extends RecordedObject {
-
- static ObjectFactory<RecordedClassLoader> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedClassLoader>(type) {
- @Override
- RecordedClassLoader createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
- return new RecordedClassLoader(desc, id, object, timeConverter);
- }
- };
- }
-
private final long uniqueId;
// package private
- private RecordedClassLoader(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
- super(descriptors, values, timeConverter);
+ RecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
+ super(objectContext, values);
this.uniqueId = id;
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.EventInstrumentation;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded event.
@@ -39,17 +40,14 @@
* @since 9
*/
public final class RecordedEvent extends RecordedObject {
- private final EventType eventType;
- private final long startTime;
- // package private needed for efficient sorting
- final long endTime;
+ long startTimeTicks;
+ long endTimeTicks;
// package private
- RecordedEvent(EventType type, List<ValueDescriptor> vds, Object[] values, long startTime, long endTime, TimeConverter timeConverter) {
- super(vds, values, timeConverter);
- this.eventType = type;
- this.startTime = startTime;
- this.endTime = endTime;
+ RecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) {
+ super(objectContext, values);
+ this.startTimeTicks = startTimeTicks;
+ this.endTimeTicks = endTimeTicks;
}
/**
@@ -78,7 +76,7 @@
* @return the event type, not {@code null}
*/
public EventType getEventType() {
- return eventType;
+ return objectContext.eventType;
}
/**
@@ -89,7 +87,7 @@
* @return the start time, not {@code null}
*/
public Instant getStartTime() {
- return Instant.ofEpochSecond(0, startTime);
+ return Instant.ofEpochSecond(0, getStartTimeNanos());
}
/**
@@ -100,7 +98,7 @@
* @return the end time, not {@code null}
*/
public Instant getEndTime() {
- return Instant.ofEpochSecond(0, endTime);
+ return Instant.ofEpochSecond(0, getEndTimeNanos());
}
/**
@@ -109,7 +107,7 @@
* @return the duration in nanoseconds, not {@code null}
*/
public Duration getDuration() {
- return Duration.ofNanos(endTime - startTime);
+ return Duration.ofNanos(getEndTimeNanos() - getStartTimeNanos());
}
/**
@@ -119,6 +117,31 @@
*/
@Override
public List<ValueDescriptor> getFields() {
- return getEventType().getFields();
+ return objectContext.fields;
+ }
+
+ protected final Object objectAt(int index) {
+ if (index == 0) {
+ return startTimeTicks;
+ }
+ if (hasDuration()) {
+ if (index == 1) {
+ return endTimeTicks - startTimeTicks;
+ }
+ return objects[index - 2];
+ }
+ return objects[index - 1];
+ }
+
+ private boolean hasDuration() {
+ return objects.length + 2 == objectContext.fields.size();
+ }
+
+ private long getStartTimeNanos() {
+ return objectContext.convertTimestamp(startTimeTicks);
+ }
+
+ private long getEndTimeNanos() {
+ return objectContext.convertTimestamp(endTimeTicks);
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedFrame.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedFrame.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,10 +26,8 @@
package jdk.jfr.consumer;
import java.lang.reflect.Modifier;
-import java.util.List;
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded frame in a stack trace.
@@ -37,19 +35,9 @@
* @since 9
*/
public final class RecordedFrame extends RecordedObject {
-
- static ObjectFactory<RecordedFrame> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedFrame>(type) {
- @Override
- RecordedFrame createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
- return new RecordedFrame(desc, object, timeConverter);
- }
- };
- }
-
// package private
- RecordedFrame(List<ValueDescriptor> desc, Object[] objects, TimeConverter timeConverter) {
- super(desc, objects, timeConverter);
+ RecordedFrame(ObjectContext objectContext, Object[] values) {
+ super(objectContext, values);
}
/**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,10 +26,8 @@
package jdk.jfr.consumer;
import java.lang.reflect.Modifier;
-import java.util.List;
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded method.
@@ -38,17 +36,9 @@
*/
public final class RecordedMethod extends RecordedObject {
- static ObjectFactory<RecordedMethod> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedMethod>(type) {
- @Override
- RecordedMethod createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
- return new RecordedMethod(desc, object, timeConverter);
- }
- };
- }
-
- private RecordedMethod(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
- super(descriptors, objects, timeConverter);
+ // package private
+ RecordedMethod(ObjectContext objectContext, Object[] values) {
+ super(objectContext, values);
}
/**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,18 +25,24 @@
package jdk.jfr.consumer;
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
+import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import jdk.jfr.Timespan;
import jdk.jfr.Timestamp;
import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.consumer.JdkJfrConsumer;
+import jdk.jfr.internal.consumer.ObjectFactory;
import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.internal.tool.PrettyWriter;
/**
@@ -51,6 +57,89 @@
*/
public class RecordedObject {
+ static{
+ JdkJfrConsumer access = new JdkJfrConsumer() {
+ public List<Type> readTypes(RecordingFile file) throws IOException {
+ return file.readTypes();
+ }
+
+ public boolean isLastEventInChunk(RecordingFile file) {
+ return file.isLastEventInChunk();
+ }
+
+ @Override
+ public Object getOffsetDataTime(RecordedObject event, String name) {
+ return event.getOffsetDateTime(name);
+ }
+
+ @Override
+ public RecordedClass newRecordedClass(ObjectContext objectContext, long id, Object[] values) {
+ return new RecordedClass(objectContext, id, values);
+ }
+
+ @Override
+ public RecordedClassLoader newRecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
+ return new RecordedClassLoader(objectContext, id, values);
+ }
+
+ @Override
+ public Comparator<? super RecordedEvent> eventComparator() {
+ return new Comparator<RecordedEvent>() {
+ @Override
+ public int compare(RecordedEvent e1, RecordedEvent e2) {
+ return Long.compare(e1.endTimeTicks, e2.endTimeTicks);
+ }
+ };
+ }
+
+ @Override
+ public RecordedStackTrace newRecordedStackTrace(ObjectContext objectContext, Object[] values) {
+ return new RecordedStackTrace(objectContext, values);
+ }
+
+ @Override
+ public RecordedThreadGroup newRecordedThreadGroup(ObjectContext objectContext, Object[] values) {
+ return new RecordedThreadGroup(objectContext, values);
+ }
+
+ @Override
+ public RecordedFrame newRecordedFrame(ObjectContext objectContext, Object[] values) {
+ return new RecordedFrame(objectContext, values);
+ }
+
+ @Override
+ public RecordedThread newRecordedThread(ObjectContext objectContext, long id, Object[] values) {
+ return new RecordedThread(objectContext, id, values);
+ }
+
+ @Override
+ public RecordedMethod newRecordedMethod(ObjectContext objectContext, Object[] values) {
+ return new RecordedMethod(objectContext, values);
+ }
+
+ @Override
+ public RecordedEvent newRecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) {
+ return new RecordedEvent(objectContext, values, startTimeTicks, endTimeTicks);
+ }
+
+ @Override
+ public void setStartTicks(RecordedEvent event, long startTicks) {
+ event.startTimeTicks = startTicks;
+ }
+
+ @Override
+ public void setEndTicks(RecordedEvent event, long endTicks) {
+ event.endTimeTicks = endTicks;
+ }
+
+ @Override
+ public Object[] eventValues(RecordedEvent event) {
+ return event.objects;
+ }
+ };
+ JdkJfrConsumer.setAccess(access);
+ }
+
private final static class UnsignedValue {
private final Object o;
@@ -63,15 +152,13 @@
}
}
- private final Object[] objects;
- private final List<ValueDescriptor> descriptors;
- private final TimeConverter timeConverter;
+ final Object[] objects;
+ final ObjectContext objectContext;
// package private, not to be subclassed outside this package
- RecordedObject(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
- this.descriptors = descriptors;
+ RecordedObject(ObjectContext objectContext, Object[] objects) {
+ this.objectContext = objectContext;
this.objects = objects;
- this.timeConverter = timeConverter;
}
// package private
@@ -101,7 +188,7 @@
*/
public boolean hasField(String name) {
Objects.requireNonNull(name);
- for (ValueDescriptor v : descriptors) {
+ for (ValueDescriptor v : objectContext.fields) {
if (v.getName().equals(name)) {
return true;
}
@@ -109,7 +196,7 @@
int dotIndex = name.indexOf(".");
if (dotIndex > 0) {
String structName = name.substring(0, dotIndex);
- for (ValueDescriptor v : descriptors) {
+ for (ValueDescriptor v : objectContext.fields) {
if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
RecordedObject child = getValue(structName);
if (child != null) {
@@ -169,12 +256,16 @@
return t;
}
+ protected Object objectAt(int index) {
+ return objects[index];
+ }
+
private Object getValue(String name, boolean allowUnsigned) {
Objects.requireNonNull(name);
int index = 0;
- for (ValueDescriptor v : descriptors) {
+ for (ValueDescriptor v : objectContext.fields) {
if (name.equals(v.getName())) {
- Object object = objects[index];
+ Object object = objectAt(index);
if (object == null) {
// error or missing
return null;
@@ -200,7 +291,7 @@
return structifyArray(v, array, 0);
}
// struct
- return new RecordedObject(v.getFields(), (Object[]) object, timeConverter);
+ return new RecordedObject(objectContext.getInstance(v), (Object[]) object);
}
}
index++;
@@ -209,7 +300,7 @@
int dotIndex = name.indexOf(".");
if (dotIndex > 0) {
String structName = name.substring(0, dotIndex);
- for (ValueDescriptor v : descriptors) {
+ for (ValueDescriptor v : objectContext.fields) {
if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
RecordedObject child = getValue(structName);
String subName = name.substring(dotIndex + 1);
@@ -261,7 +352,7 @@
private <T> T getTypedValue(String name, String typeName) {
Objects.requireNonNull(name);
// Validate name and type first
- getValueDescriptor(descriptors, name, typeName);
+ getValueDescriptor(objectContext.fields, name, typeName);
return getValue(name);
}
@@ -270,15 +361,16 @@
return null;
}
Object[] structArray = new Object[array.length];
+ ObjectContext objContext = objectContext.getInstance(v);
for (int i = 0; i < structArray.length; i++) {
Object arrayElement = array[i];
if (dimension == 0) {
// No general way to handle structarrays
// without invoking ObjectFactory for every instance (which may require id)
if (isStackFrameType(v.getTypeName())) {
- structArray[i] = new RecordedFrame(v.getFields(), (Object[]) arrayElement, timeConverter);
+ structArray[i] = new RecordedFrame(objContext, (Object[]) arrayElement);
} else {
- structArray[i] = new RecordedObject(v.getFields(), (Object[]) arrayElement, timeConverter);
+ structArray[i] = new RecordedObject(objContext, (Object[]) arrayElement);
}
} else {
structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1);
@@ -303,7 +395,7 @@
* @return the fields, not {@code null}
*/
public List<ValueDescriptor> getFields() {
- return descriptors;
+ return objectContext.fields;
}
/**
@@ -725,7 +817,7 @@
}
private Duration getDuration(long timespan, String name) throws InternalError {
- ValueDescriptor v = getValueDescriptor(descriptors, name, null);
+ ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
if (timespan == Long.MIN_VALUE) {
return Duration.ofSeconds(Long.MIN_VALUE, 0);
}
@@ -741,7 +833,7 @@
case Timespan.NANOSECONDS:
return Duration.ofNanos(timespan);
case Timespan.TICKS:
- return Duration.ofNanos(timeConverter.convertTimespan(timespan));
+ return Duration.ofNanos(objectContext.convertTimespan(timespan));
}
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value());
}
@@ -804,7 +896,7 @@
}
private Instant getInstant(long timestamp, String name) {
- ValueDescriptor v = getValueDescriptor(descriptors, name, null);
+ ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
Timestamp ts = v.getAnnotation(Timestamp.class);
if (ts != null) {
if (timestamp == Long.MIN_VALUE) {
@@ -814,7 +906,7 @@
case Timestamp.MILLISECONDS_SINCE_EPOCH:
return Instant.ofEpochMilli(timestamp);
case Timestamp.TICKS:
- return Instant.ofEpochSecond(0, timeConverter.convertTimestamp(timestamp));
+ return Instant.ofEpochSecond(0, objectContext.convertTimestamp(timestamp));
}
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value());
}
@@ -889,12 +981,12 @@
}
// package private for now. Used by EventWriter
- OffsetDateTime getOffsetDateTime(String name) {
+ private OffsetDateTime getOffsetDateTime(String name) {
Instant instant = getInstant(name);
if (instant.equals(Instant.MIN)) {
return OffsetDateTime.MIN;
}
- return OffsetDateTime.ofInstant(getInstant(name), timeConverter.getZoneOffset());
+ return OffsetDateTime.ofInstant(getInstant(name), objectContext.getZoneOffset());
}
private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedStackTrace.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedStackTrace.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,8 +29,7 @@
import java.util.Collections;
import java.util.List;
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded stack trace.
@@ -38,18 +37,9 @@
* @since 9
*/
public final class RecordedStackTrace extends RecordedObject {
-
- static ObjectFactory<RecordedStackTrace> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedStackTrace>(type) {
- @Override
- RecordedStackTrace createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
- return new RecordedStackTrace(desc, object, timeConverter);
- }
- };
- }
-
- private RecordedStackTrace(List<ValueDescriptor> desc, Object[] values, TimeConverter timeConverter) {
- super(desc, values, timeConverter);
+ // package private
+ RecordedStackTrace(ObjectContext objectContext, Object[] values) {
+ super(objectContext, values);
}
/**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,10 +25,7 @@
package jdk.jfr.consumer;
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded thread.
@@ -36,20 +33,11 @@
* @since 9
*/
public final class RecordedThread extends RecordedObject {
-
- static ObjectFactory<RecordedThread> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedThread>(type) {
- @Override
- RecordedThread createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
- return new RecordedThread(desc, id, object, timeConverter);
- }
- };
- }
-
private final long uniqueId;
- private RecordedThread(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
- super(descriptors, values, timeConverter);
+ // package private
+ RecordedThread(ObjectContext objectContext, long id, Object[] values) {
+ super(objectContext, values);
this.uniqueId = id;
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,10 +25,7 @@
package jdk.jfr.consumer;
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded Java thread group.
@@ -36,18 +33,9 @@
* @since 9
*/
public final class RecordedThreadGroup extends RecordedObject {
-
- static ObjectFactory<RecordedThreadGroup> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedThreadGroup>(type) {
- @Override
- RecordedThreadGroup createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
- return new RecordedThreadGroup(desc, object, timeConverter);
- }
- };
- }
-
- private RecordedThreadGroup(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
- super(descriptors, objects, timeConverter);
+ // package private
+ RecordedThreadGroup(ObjectContext objectContext, Object[] values) {
+ super(objectContext, values);
}
/**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,7 +32,6 @@
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -40,8 +39,9 @@
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.ChunkParser;
+import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput;
-import jdk.jfr.internal.consumer.RecordingInternals;
/**
* A recording file.
@@ -62,27 +62,6 @@
* @since 9
*/
public final class RecordingFile implements Closeable {
- static{
- RecordingInternals.INSTANCE = new RecordingInternals() {
- public List<Type> readTypes(RecordingFile file) throws IOException {
- return file.readTypes();
- }
-
- public boolean isLastEventInChunk(RecordingFile file) {
- return file.isLastEventInChunk;
- }
-
- @Override
- public Object getOffsetDataTime(RecordedObject event, String name) {
- return event.getOffsetDateTime(name);
- }
-
- @Override
- public void sort(List<RecordedEvent> events) {
- Collections.sort(events, (e1, e2) -> Long.compare(e1.endTime, e2.endTime));
- }
- };
- }
private boolean isLastEventInChunk;
private final File file;
@@ -104,7 +83,7 @@
*/
public RecordingFile(Path file) throws IOException {
this.file = file.toFile();
- this.input = new RecordingInput(this.file);
+ this.input = new RecordingInput(this.file, FileAccess.UNPRIVILIGED);
findNext();
}
@@ -154,14 +133,15 @@
*/
public List<EventType> readEventTypes() throws IOException {
ensureOpen();
+ MetadataDescriptor previous = null;
List<EventType> types = new ArrayList<>();
HashSet<Long> foundIds = new HashSet<>();
- try (RecordingInput ri = new RecordingInput(file)) {
+ try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILIGED)) {
ChunkHeader ch = new ChunkHeader(ri);
- aggregateEventTypeForChunk(ch, types, foundIds);
+ aggregateEventTypeForChunk(ch, null, types, foundIds);
while (!ch.isLastChunk()) {
ch = ch.nextHeader();
- aggregateEventTypeForChunk(ch, types, foundIds);
+ previous = aggregateEventTypeForChunk(ch, previous, types, foundIds);
}
}
return types;
@@ -169,37 +149,41 @@
List<Type> readTypes() throws IOException {
ensureOpen();
+ MetadataDescriptor previous = null;
List<Type> types = new ArrayList<>();
HashSet<Long> foundIds = new HashSet<>();
- try (RecordingInput ri = new RecordingInput(file)) {
+ try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILIGED)) {
ChunkHeader ch = new ChunkHeader(ri);
- aggregateTypeForChunk(ch, types, foundIds);
+ ch.awaitFinished();
+ aggregateTypeForChunk(ch, null, types, foundIds);
while (!ch.isLastChunk()) {
ch = ch.nextHeader();
- aggregateTypeForChunk(ch, types, foundIds);
+ previous = aggregateTypeForChunk(ch, previous, types, foundIds);
}
}
return types;
}
- private void aggregateTypeForChunk(ChunkHeader ch, List<Type> types, HashSet<Long> foundIds) throws IOException {
- MetadataDescriptor m = ch.readMetadata();
+ private MetadataDescriptor aggregateTypeForChunk(ChunkHeader ch, MetadataDescriptor previous, List<Type> types, HashSet<Long> foundIds) throws IOException {
+ MetadataDescriptor m = ch.readMetadata(previous);
for (Type t : m.getTypes()) {
if (!foundIds.contains(t.getId())) {
types.add(t);
foundIds.add(t.getId());
}
}
+ return m;
}
- private static void aggregateEventTypeForChunk(ChunkHeader ch, List<EventType> types, HashSet<Long> foundIds) throws IOException {
- MetadataDescriptor m = ch.readMetadata();
+ private static MetadataDescriptor aggregateEventTypeForChunk(ChunkHeader ch, MetadataDescriptor previous, List<EventType> types, HashSet<Long> foundIds) throws IOException {
+ MetadataDescriptor m = ch.readMetadata(previous);
for (EventType t : m.getEventTypes()) {
if (!foundIds.contains(t.getId())) {
types.add(t);
foundIds.add(t.getId());
}
}
+ return m;
}
/**
@@ -246,6 +230,17 @@
}
}
+ // package protected
+ File getFile() {
+ return file;
+ }
+
+ // package protected
+ boolean isLastEventInChunk() {
+ return isLastEventInChunk;
+ }
+
+
// either sets next to an event or sets eof to true
private void findNext() throws IOException {
while (nextEvent == null) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import jdk.jfr.Configuration;
+import jdk.jfr.Event;
+import jdk.jfr.EventSettings;
+import jdk.jfr.EventType;
+import jdk.jfr.Recording;
+import jdk.jfr.internal.PlatformRecording;
+import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.SecuritySupport;
+import jdk.jfr.internal.Utils;
+import jdk.jfr.internal.consumer.EventDirectoryStream;
+
+/**
+ * A recording stream produces events from the current JVM (Java Virtual
+ * Machine).
+ * <p>
+ * The following example shows how to record events using the default
+ * configuration and print the Garbage Collection, CPU Load and JVM Information
+ * event to standard out.
+ * <pre>
+ * <code>
+ * Configuration c = Configuration.getConfiguration("default");
+ * try (var rs = new RecordingStream(c)) {
+ * rs.onEvent("jdk.GarbageCollection", System.out::println);
+ * rs.onEvent("jdk.CPULoad", System.out::println);
+ * rs.onEvent("jdk.JVMInformation", System.out::println);
+ * rs.start();
+ * }
+ * }
+ * </code>
+ * </pre>
+ *
+ * @since 14
+ */
+public final class RecordingStream implements AutoCloseable, EventStream {
+
+ private final Recording recording;
+ private final EventDirectoryStream directoryStream;
+
+ /**
+ * Creates an event stream for the current JVM (Java Virtual Machine).
+ *
+ * @throws IllegalStateException if Flight Recorder can't be created (for
+ * example, if the Java Virtual Machine (JVM) lacks Flight Recorder
+ * support, or if the file repository can't be created or accessed)
+ *
+ * @throws SecurityException if a security manager exists and the caller
+ * does not have
+ * {@code FlightRecorderPermission("accessFlightRecorder")}
+ */
+ public RecordingStream() {
+ Utils.checkAccessFlightRecorder();
+ AccessControlContext acc = AccessController.getContext();
+ this.recording = new Recording();
+ this.recording.setFlushInterval(Duration.ofMillis(1000));
+ try {
+ this.directoryStream = new EventDirectoryStream(acc, null, SecuritySupport.PRIVILIGED, true);
+ } catch (IOException ioe) {
+ this.recording.close();
+ throw new IllegalStateException(ioe.getMessage());
+ }
+ }
+
+ /**
+ * Creates a recording stream using settings from a configuration.
+ * <p>
+ * The following example shows how to create a recording stream that uses a
+ * predefined configuration.
+ *
+ * <pre>
+ * <code>
+ * var c = Configuration.getConfiguration("default");
+ * try (var rs = new RecordingStream(c)) {
+ * rs.onEvent(System.out::println);
+ * rs.start();
+ * }
+ * </code>
+ * </pre>
+ *
+ * @param configuration configuration that contains the settings to use,
+ * not {@code null}
+ *
+ * @throws IllegalStateException if Flight Recorder can't be created (for
+ * example, if the Java Virtual Machine (JVM) lacks Flight Recorder
+ * support, or if the file repository can't be created or accessed)
+ *
+ * @throws SecurityException if a security manager is used and
+ * FlightRecorderPermission "accessFlightRecorder" is not set.
+ *
+ * @see Configuration
+ */
+ public RecordingStream(Configuration configuration) {
+ this();
+ recording.setSettings(configuration.getSettings());
+ }
+
+ /**
+ * Enables the event with the specified name.
+ * <p>
+ * If multiple events have the same name (for example, the same class is
+ * loaded in different class loaders), then all events that match the name
+ * are enabled. To enable a specific class, use the {@link #enable(Class)}
+ * method or a {@code String} representation of the event type ID.
+ *
+ * @param name the settings for the event, not {@code null}
+ *
+ * @return an event setting for further configuration, not {@code null}
+ *
+ * @see EventType
+ */
+ public EventSettings enable(String name) {
+ return recording.enable(name);
+ }
+
+ /**
+ * Replaces all settings for this recording stream.
+ * <p>
+ * The following example records 20 seconds using the "default" configuration
+ * and then changes settings to the "profile" configuration.
+ *
+ * <pre>
+ * <code>
+ * Configuration defaultConfiguration = Configuration.getConfiguration("default");
+ * Configuration profileConfiguration = Configuration.getConfiguration("profile");
+ * try (var rs = new RecordingStream(defaultConfiguration) {
+ * rs.onEvent(System.out::println);
+ * rs.startAsync();
+ * Thread.sleep(20_000);
+ * rs.setSettings(profileConfiguration.getSettings());
+ * Thread.sleep(20_000);
+ * }
+ * </code>
+ * </pre>
+ *
+ * @param settings the settings to set, not {@code null}
+ *
+ * @see Recording#setSettings(Map)
+ */
+ public void setSettings(Map<String, String> settings) {
+ recording.setSettings(settings);
+ };
+
+ /**
+ * Enables event.
+ *
+ * @param eventClass the event to enable, not {@code null}
+ *
+ * @throws IllegalArgumentException if {@code eventClass} is an abstract
+ * class or not a subclass of {@link Event}
+ *
+ * @return an event setting for further configuration, not {@code null}
+ */
+ public EventSettings enable(Class<? extends Event> eventClass) {
+ return recording.enable(eventClass);
+ }
+
+ /**
+ * Disables event with the specified name.
+ * <p>
+ * If multiple events with same name (for example, the same class is loaded
+ * in different class loaders), then all events that match the name are
+ * disabled. To disable a specific class, use the {@link #disable(Class)}
+ * method or a {@code String} representation of the event type ID.
+ *
+ * @param name the settings for the event, not {@code null}
+ *
+ * @return an event setting for further configuration, not {@code null}
+ *
+ */
+ public EventSettings disable(String name) {
+ return recording.disable(name);
+ }
+
+ /**
+ * Disables event.
+ *
+ * @param eventClass the event to enable, not {@code null}
+ *
+ * @throws IllegalArgumentException if {@code eventClass} is an abstract
+ * class or not a subclass of {@link Event}
+ *
+ * @return an event setting for further configuration, not {@code null}
+ *
+ */
+ public EventSettings disable(Class<? extends Event> eventClass) {
+ return recording.disable(eventClass);
+ }
+
+ /**
+ * Determines how far back data is kept for the stream.
+ * <p>
+ * To control the amount of recording data stored on disk, the maximum
+ * length of time to retain the data can be specified. Data stored on disk
+ * that is older than the specified length of time is removed by the Java
+ * Virtual Machine (JVM).
+ * <p>
+ * If neither maximum limit or the maximum age is set, the size of the
+ * recording may grow indefinitely if events are on
+ *
+ * @param maxAge the length of time that data is kept, or {@code null} if
+ * infinite
+ *
+ * @throws IllegalArgumentException if {@code maxAge} is negative
+ *
+ * @throws IllegalStateException if the recording is in the {@code CLOSED}
+ * state
+ */
+ public void setMaxAge(Duration maxAge) {
+ recording.setMaxAge(maxAge);
+ }
+
+ /**
+ * Determines how much data is kept for the stream.
+ * <p>
+ * To control the amount of recording data that is stored on disk, the
+ * maximum amount of data to retain can be specified. When the maximum limit
+ * is exceeded, the Java Virtual Machine (JVM) removes the oldest chunk to
+ * make room for a more recent chunk.
+ * <p>
+ * If neither maximum limit or the maximum age is set, the size of the
+ * recording may grow indefinitely.
+ * <p>
+ * The size is measured in bytes.
+ *
+ * @param maxSize the amount of data to retain, {@code 0} if infinite
+ *
+ * @throws IllegalArgumentException if {@code maxSize} is negative
+ *
+ * @throws IllegalStateException if the recording is in {@code CLOSED} state
+ */
+ public void setMaxSize(long maxSize) {
+ recording.setMaxSize(maxSize);
+ }
+
+ /**
+ * Determines how often events are made available for streaming.
+ *
+ * @param interval the interval at which events are made available to the
+ * stream, no {@code null}
+ *
+ * @throws IllegalArgumentException if {@code interval} is negative
+ *
+ * @throws IllegalStateException if the stream is closed
+ */
+ public void setFlushInterval(Duration interval) {
+ recording.setFlushInterval(interval);
+ }
+
+ @Override
+ public void setReuse(boolean reuse) {
+ directoryStream.setReuse(reuse);
+ }
+
+ @Override
+ public void setOrdered(boolean ordered) {
+ directoryStream.setOrdered(ordered);
+ }
+
+ @Override
+ public void setStartTime(Instant startTime) {
+ directoryStream.setStartTime(startTime);
+ }
+
+ @Override
+ public void setEndTime(Instant endTime) {
+ directoryStream.setStartTime(endTime);
+ }
+
+ @Override
+ public void onEvent(String eventName, Consumer<RecordedEvent> action) {
+ directoryStream.onEvent(eventName, action);
+ }
+
+ @Override
+ public void onEvent(Consumer<RecordedEvent> action) {
+ directoryStream.onEvent(action);
+ }
+
+ @Override
+ public void onFlush(Runnable action) {
+ directoryStream.onFlush(action);
+ }
+
+ @Override
+ public void onClose(Runnable action) {
+ directoryStream.onClose(action);
+ }
+
+ @Override
+ public void onError(Consumer<Throwable> action) {
+ directoryStream.onError(action);
+ }
+
+ @Override
+ public void close() {
+ recording.close();
+ directoryStream.close();
+ }
+
+ @Override
+ public boolean remove(Object action) {
+ return directoryStream.remove(action);
+ }
+
+ @Override
+ public void start() {
+ PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
+ long startNanos = pr.start();
+ directoryStream.start(startNanos);
+ }
+
+ @Override
+ public void startAsync() {
+ PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
+ long startNanos = pr.start();
+ directoryStream.startAsync(startNanos);
+ }
+
+ @Override
+ public void awaitTermination(Duration timeout) throws InterruptedException {
+ directoryStream.awaitTermination(timeout);
+ }
+
+ @Override
+ public void awaitTermination() throws InterruptedException {
+ directoryStream.awaitTermination();
+ }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/TimeConverter.java Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jfr.consumer;
-
-import java.time.DateTimeException;
-import java.time.ZoneOffset;
-
-import jdk.jfr.internal.LogLevel;
-import jdk.jfr.internal.LogTag;
-import jdk.jfr.internal.Logger;
-import jdk.jfr.internal.consumer.ChunkHeader;
-
-/**
- * Converts ticks to nanoseconds
- */
-final class TimeConverter {
- private final long startTicks;
- private final long startNanos;
- private final double divisor;
- private final ZoneOffset zoneOffet;
-
- TimeConverter(ChunkHeader chunkHeader, int rawOffset) {
- this.startTicks = chunkHeader.getStartTicks();
- this.startNanos = chunkHeader.getStartNanos();
- this.divisor = chunkHeader.getTicksPerSecond() / 1000_000_000L;
- this.zoneOffet = zoneOfSet(rawOffset);
- }
-
- private ZoneOffset zoneOfSet(int rawOffset) {
- try {
- return ZoneOffset.ofTotalSeconds(rawOffset / 1000);
- } catch (DateTimeException dte) {
- Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Could not create ZoneOffset from raw offset " + rawOffset);
- }
- return ZoneOffset.UTC;
- }
-
- public long convertTimestamp(long ticks) {
- return startNanos + (long) ((ticks - startTicks) / divisor);
- }
-
- public long convertTimespan(long ticks) {
- return (long) (ticks / divisor);
- }
-
- public ZoneOffset getZoneOffset() {
- return zoneOffet;
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveRecordingEvent.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveRecordingEvent.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,13 @@
@StackTrace(false)
public final class ActiveRecordingEvent extends AbstractJDKEvent {
+ public static final ThreadLocal<ActiveRecordingEvent> EVENT = new ThreadLocal<ActiveRecordingEvent>() {
+ @Override
+ protected ActiveRecordingEvent initialValue() {
+ return new ActiveRecordingEvent();
+ }
+ };
+
@Label("Id")
public long id;
@@ -53,6 +60,10 @@
@Timespan(Timespan.MILLISECONDS)
public long maxAge;
+ @Label("Flush Interval")
+ @Timespan(Timespan.MILLISECONDS)
+ public long flushInterval;
+
@Label("Max Size")
@DataAmount
public long maxSize;
--- a/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveSettingEvent.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveSettingEvent.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,13 @@
@StackTrace(false)
public final class ActiveSettingEvent extends AbstractJDKEvent {
+ public static final ThreadLocal<ActiveSettingEvent> EVENT = new ThreadLocal<ActiveSettingEvent>() {
+ @Override
+ protected ActiveSettingEvent initialValue() {
+ return new ActiveSettingEvent();
+ }
+ };
+
@Label("Event Id")
public long id;
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,11 +32,7 @@
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
import jdk.internal.module.Modules;
import jdk.jfr.AnnotationElement;
@@ -59,7 +55,14 @@
// holds SettingControl instances that need to be released
// when a class is unloaded (to avoid memory leaks).
public final class EventControl {
-
+ final static class NamedControl {
+ public final String name;
+ public final Control control;
+ NamedControl(String name, Control control) {
+ this.name = name;
+ this.control = control;
+ }
+ }
static final String FIELD_SETTING_PREFIX = "setting";
private static final Type TYPE_ENABLED = TypeLibrary.createType(EnabledSetting.class);
private static final Type TYPE_THRESHOLD = TypeLibrary.createType(ThresholdSetting.class);
@@ -67,24 +70,24 @@
private static final Type TYPE_PERIOD = TypeLibrary.createType(PeriodSetting.class);
private static final Type TYPE_CUTOFF = TypeLibrary.createType(CutoffSetting.class);
- private final List<SettingInfo> settingInfos = new ArrayList<>();
- private final Map<String, Control> eventControls = new HashMap<>(5);
+ private final ArrayList<SettingInfo> settingInfos = new ArrayList<>();
+ private final ArrayList<NamedControl> namedControls = new ArrayList<>(5);
private final PlatformEventType type;
private final String idName;
EventControl(PlatformEventType eventType) {
- eventControls.put(Enabled.NAME, defineEnabled(eventType));
+ addControl(Enabled.NAME, defineEnabled(eventType));
if (eventType.hasDuration()) {
- eventControls.put(Threshold.NAME, defineThreshold(eventType));
+ addControl(Threshold.NAME, defineThreshold(eventType));
}
if (eventType.hasStackTrace()) {
- eventControls.put(StackTrace.NAME, defineStackTrace(eventType));
+ addControl(StackTrace.NAME, defineStackTrace(eventType));
}
if (eventType.hasPeriod()) {
- eventControls.put(Period.NAME, definePeriod(eventType));
+ addControl(Period.NAME, definePeriod(eventType));
}
if (eventType.hasCutoff()) {
- eventControls.put(Cutoff.NAME, defineCutoff(eventType));
+ addControl(Cutoff.NAME, defineCutoff(eventType));
}
ArrayList<AnnotationElement> aes = new ArrayList<>(eventType.getAnnotationElements());
@@ -99,6 +102,19 @@
this.idName = String.valueOf(eventType.getId());
}
+ private boolean hasControl(String name) {
+ for (NamedControl nc : namedControls) {
+ if (name.equals(nc.name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void addControl(String name, Control control) {
+ namedControls.add(new NamedControl(name, control));
+ }
+
static void remove(PlatformEventType type, List<AnnotationElement> aes, Class<? extends java.lang.annotation.Annotation> clazz) {
long id = Type.getTypeId(clazz);
for (AnnotationElement a : type.getAnnotationElements()) {
@@ -131,7 +147,8 @@
if (n != null) {
name = n.value();
}
- if (!eventControls.containsKey(name)) {
+
+ if (!hasControl(name)) {
defineSetting((Class<? extends SettingControl>) settingClass, m, type, name);
}
}
@@ -163,7 +180,7 @@
}
}
aes.trimToSize();
- eventControls.put(settingName, si.settingControl);
+ addControl(settingName, si.settingControl);
eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, settingName, defaultValue, aes));
settingInfos.add(si);
}
@@ -247,9 +264,9 @@
}
void disable() {
- for (Control c : eventControls.values()) {
- if (c instanceof EnabledSetting) {
- c.setValueSafe("false");
+ for (NamedControl nc : namedControls) {
+ if (nc.control instanceof EnabledSetting) {
+ nc.control.setValueSafe("false");
return;
}
}
@@ -259,24 +276,23 @@
if (!type.isRegistered()) {
return;
}
- for (Map.Entry<String, Control> entry : eventControls.entrySet()) {
- Control c = entry.getValue();
- if (Utils.isSettingVisible(c, type.hasEventHook())) {
- String value = c.getLastValue();
+ ActiveSettingEvent event = ActiveSettingEvent.EVENT.get();
+ for (NamedControl nc : namedControls) {
+ if (Utils.isSettingVisible(nc.control, type.hasEventHook())) {
+ String value = nc.control.getLastValue();
if (value == null) {
- value = c.getDefaultValue();
+ value = nc.control.getDefaultValue();
}
- ActiveSettingEvent ase = new ActiveSettingEvent();
- ase.id = type.getId();
- ase.name = entry.getKey();
- ase.value = value;
- ase.commit();
+ event.id = type.getId();
+ event.name = nc.name;
+ event.value = value;
+ event.commit();
}
}
}
- public Set<Entry<String, Control>> getEntries() {
- return eventControls.entrySet();
+ public ArrayList<NamedControl> getNamedControls() {
+ return namedControls;
}
public PlatformEventType getEventType() {
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -363,74 +363,72 @@
methodVisitor.visitMaxs(0, 0);
});
- // MyEvent#commit() - Java event writer
updateMethod(METHOD_COMMIT, methodVisitor -> {
- // if (!isEnable()) {
- // return;
- // }
- methodVisitor.visitCode();
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false);
- Label l0 = new Label();
- methodVisitor.visitJumpInsn(Opcodes.IFNE, l0);
- methodVisitor.visitInsn(Opcodes.RETURN);
- methodVisitor.visitLabel(l0);
- methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
- // if (startTime == 0) {
- // startTime = EventWriter.timestamp();
- // } else {
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
- methodVisitor.visitInsn(Opcodes.LCONST_0);
- methodVisitor.visitInsn(Opcodes.LCMP);
- Label durationalEvent = new Label();
- methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent);
+ // if (!isEnable()) {
+ // return;
+ // }
+ methodVisitor.visitCode();
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false);
+ Label l0 = new Label();
+ methodVisitor.visitJumpInsn(Opcodes.IFNE, l0);
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ methodVisitor.visitLabel(l0);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ // if (startTime == 0) {
+ // startTime = EventWriter.timestamp();
+ // } else {
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+ methodVisitor.visitInsn(Opcodes.LCONST_0);
+ methodVisitor.visitInsn(Opcodes.LCMP);
+ Label durationalEvent = new Label();
+ methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
+ methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+ Label commit = new Label();
+ methodVisitor.visitJumpInsn(Opcodes.GOTO, commit);
+ // if (duration == 0) {
+ // duration = EventWriter.timestamp() - startTime;
+ // }
+ // }
+ methodVisitor.visitLabel(durationalEvent);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
+ methodVisitor.visitInsn(Opcodes.LCONST_0);
+ methodVisitor.visitInsn(Opcodes.LCMP);
+ methodVisitor.visitJumpInsn(Opcodes.IFNE, commit);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+ methodVisitor.visitInsn(Opcodes.LSUB);
+ methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
+ methodVisitor.visitLabel(commit);
+ // if (shouldCommit()) {
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false);
+ Label end = new Label();
+ // eventHandler.write(...);
+ // }
+ methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
+ getEventHandler(methodVisitor);
+
+ methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
+ for (FieldInfo fi : fieldInfos) {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(),
- METHOD_TIME_STAMP.getDescriptor(), false);
- methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
- Label commit = new Label();
- methodVisitor.visitJumpInsn(Opcodes.GOTO, commit);
- // if (duration == 0) {
- // duration = EventWriter.timestamp() - startTime;
- // }
- // }
- methodVisitor.visitLabel(durationalEvent);
- methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
- methodVisitor.visitInsn(Opcodes.LCONST_0);
- methodVisitor.visitInsn(Opcodes.LCMP);
- methodVisitor.visitJumpInsn(Opcodes.IFNE, commit);
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
- methodVisitor.visitInsn(Opcodes.LSUB);
- methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
- methodVisitor.visitLabel(commit);
- // if (shouldCommit()) {
- methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false);
- Label end = new Label();
- // eventHandler.write(...);
- // }
- methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
- getEventHandler(methodVisitor);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor);
+ }
- methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
- for (FieldInfo fi : fieldInfos) {
- methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
- methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor);
- }
-
- methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false);
- methodVisitor.visitLabel(end);
- methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
- methodVisitor.visitInsn(Opcodes.RETURN);
- methodVisitor.visitEnd();
- });
+ methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false);
+ methodVisitor.visitLabel(end);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ methodVisitor.visitEnd();
+ });
// MyEvent#shouldCommit()
updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> {
@@ -469,6 +467,7 @@
});
}
+
private void getEventHandler(MethodVisitor methodVisitor) {
if (untypedEventHandler) {
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_OBJECT.getDescriptor());
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriter.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriter.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
package jdk.jfr.internal;
import jdk.internal.misc.Unsafe;
-import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringParser;
/**
* Class must reside in a package with package restriction.
@@ -115,18 +115,18 @@
public void putString(String s, StringPool pool) {
if (s == null) {
- putByte(RecordingInput.STRING_ENCODING_NULL);
+ putByte(StringParser.Encoding.NULL.byteValue());
return;
}
int length = s.length();
if (length == 0) {
- putByte(RecordingInput.STRING_ENCODING_EMPTY_STRING);
+ putByte(StringParser.Encoding.EMPTY_STRING.byteValue());
return;
}
if (length > StringPool.MIN_LIMIT && length < StringPool.MAX_LIMIT) {
long l = StringPool.addString(s);
if (l > 0) {
- putByte(RecordingInput.STRING_ENCODING_CONSTANT_POOL);
+ putByte(StringParser.Encoding.CONSTANT_POOL.byteValue());
putLong(l);
return;
}
@@ -138,7 +138,7 @@
private void putStringValue(String s) {
int length = s.length();
if (isValidForSize(1 + 5 + 3 * length)) {
- putUncheckedByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // 1 byte
+ putUncheckedByte(StringParser.Encoding.CHAR_ARRAY.byteValue()); // 1 byte
putUncheckedInt(length); // max 5 bytes
for (int i = 0; i < length; i++) {
putUncheckedChar(s.charAt(i)); // max 3 bytes
@@ -197,11 +197,7 @@
if (currentPosition + requestedSize > maxPosition) {
flushOnEnd = flush(usedSize(), requestedSize);
// retry
- if (currentPosition + requestedSize > maxPosition) {
- Logger.log(LogTag.JFR_SYSTEM,
- LogLevel.WARN, () ->
- "Unable to commit. Requested size " + requestedSize + " too large");
- valid = false;
+ if (!valid) {
return false;
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/FilePurger.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import jdk.jfr.internal.SecuritySupport.SafePath;
+
+// This class keeps track of files that can't be deleted
+// so they can a later staged be removed.
+final class FilePurger {
+
+ private final static Set<SafePath> paths = new LinkedHashSet<>();
+
+ public synchronized static void add(SafePath p) {
+ paths.add(p);
+ if (paths.size() > 1000) {
+ removeOldest();
+ }
+ }
+
+ public synchronized static void purge() {
+ if (paths.isEmpty()) {
+ return;
+ }
+
+ for (SafePath p : new ArrayList<>(paths)) {
+ if (delete(p)) {
+ paths.remove(p);
+ }
+ }
+ }
+
+ private static void removeOldest() {
+ SafePath oldest = paths.iterator().next();
+ paths.remove(oldest);
+ }
+
+ private static boolean delete(SafePath p) {
+ try {
+ SecuritySupport.delete(p);
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -270,7 +270,6 @@
*
* @param file the file where data should be written, or null if it should
* not be copied out (in memory).
- *
* @throws IOException
*/
public native void setOutput(String file);
@@ -460,6 +459,18 @@
public static native boolean flush(EventWriter writer, int uncommittedSize, int requestedSize);
/**
+ * Flushes all thread buffers to disk and the constant pool data needed to read
+ * them.
+ * <p>
+ * When the method returns, the chunk header should be updated with valid
+ * pointers to the metadata event, last check point event, correct file size and
+ * the generation id.
+ *
+ * @param includeMetadata {@code true} if metadata event should be written, {@code false}
+ * otherwise
+ */
+ public native void flush(boolean includeMetadata);
+ /**
* Sets the location of the disk repository, to be used at an emergency
* dump.
*
@@ -523,4 +534,30 @@
* @return if it is time to perform a chunk rotation
*/
public native boolean shouldRotateDisk();
+
+ /**
+ * Exclude a thread from the jfr system
+ *
+ */
+ public native void exclude(Thread thread);
+
+ /**
+ * Include a thread back into the jfr system
+ *
+ */
+ public native void include(Thread thread);
+
+ /**
+ * Test if a thread ius currently excluded from the jfr system.
+ *
+ * @return is thread currently excluded
+ */
+ public native boolean isExcluded(Thread thread);
+
+ /**
+ * Get the start time in nanos from the header of the current chunk
+ *
+ *@return start time of the recording in nanos, -1 in case of in-memory
+ */
+ public native long getChunkStartNanos();
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/LogTag.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/LogTag.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -63,21 +63,25 @@
*/
JFR_SYSTEM_METADATA(6),
/**
+ * Covers streaming (for Hotspot developers)
+ */
+ JFR_SYSTEM_STREAMING(7),
+ /**
* Covers metadata for Java user (for Hotspot developers)
*/
- JFR_METADATA(7),
+ JFR_METADATA(8),
/**
* Covers events (for users of the JDK)
*/
- JFR_EVENT(8),
+ JFR_EVENT(9),
/**
* Covers setting (for users of the JDK)
*/
- JFR_SETTING(9),
+ JFR_SETTING(10),
/**
* Covers usage of jcmd with JFR
*/
- JFR_DCMD(10);
+ JFR_DCMD(11);
/* set from native side */
volatile int tagSetLevel = 100; // prevent logging if JVM log system has not been initialized
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.util.BitSet;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+@SuppressWarnings("unchecked")
+public final class LongMap<T> {
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+ private static final long[] EMPTY_KEYS = new long[0];
+ private static final Object[] EMPTY_OBJECTS = new Object[0];
+ private static final int DEFAULT_SIZE = 32;
+ private static final Object NULL_OBJECT = new Object();
+
+ private final int bitCount;
+ private BitSet bitSet;
+ private long[] keys = EMPTY_KEYS;
+ private T[] objects = (T[]) EMPTY_OBJECTS;
+ private int count;
+ private int shift;
+
+ public LongMap() {
+ this.bitCount = 0;
+ }
+
+ public LongMap(int markBits) {
+ this.bitCount = markBits;
+ this.bitSet = new BitSet();
+ }
+
+ // Should be 2^n
+ private void initialize(int capacity) {
+ keys = new long[capacity];
+ objects = (T[]) new Object[capacity];
+ shift = 64 - (31 - Integer.numberOfLeadingZeros(capacity));
+ }
+
+ public void claimBits() {
+ // flip last bit back and forth to make bitset expand to max size
+ int lastBit = bitSetIndex(objects.length - 1, bitCount -1);
+ bitSet.flip(lastBit);
+ bitSet.flip(lastBit);
+ }
+
+ public void setId(long id, int bitIndex) {
+ int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
+ bitSet.set(bitSetIndex, true);
+ }
+
+ public void clearId(long id, int bitIndex) {
+ int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
+ bitSet.set(bitSetIndex, false);
+ }
+
+ public boolean isSetId(long id, int bitIndex) {
+ int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
+ return bitSet.get(bitSetIndex);
+ }
+
+ private int bitSetIndex(int tableIndex, int bitIndex) {
+ return bitCount * tableIndex + bitIndex;
+ }
+
+ private int tableIndexOf(long id) {
+ int index = index(id);
+ while (true) {
+ if (objects[index] == null) {
+ throw new InternalError("Unknown id");
+ }
+ if (keys[index] == id) {
+ return index;
+ }
+ index++;
+ if (index == keys.length) {
+ index = 0;
+ }
+ }
+ }
+
+ public boolean hasKey(long id) {
+ int index = index(id);
+ while (true) {
+ if (objects[index] == null) {
+ return false;
+ }
+ if (keys[index] == id) {
+ return true;
+ }
+ index++;
+ if (index == keys.length) {
+ index = 0;
+ }
+ }
+ }
+
+ public void expand(int size) {
+ int l = 4 * size / 3;
+ if (l <= keys.length) {
+ return;
+ }
+ int n = tableSizeFor(l);
+ LongMap<T> temp = new LongMap<>(bitCount);
+ temp.initialize(n);
+ // Optimization, avoid growing while copying bits
+ if (bitCount > 0 && !bitSet.isEmpty()) {
+ temp.claimBits();
+ claimBits();
+ }
+ for (int tIndex = 0; tIndex < keys.length; tIndex++) {
+ T o = objects[tIndex];
+ if (o != null) {
+ long key = keys[tIndex];
+ temp.put(key, o);
+ if (bitCount != 0) {
+ for (int bIndex = 0; bIndex < bitCount; bIndex++) {
+ boolean bitValue = isSetId(key, bIndex);
+ if (bitValue) {
+ temp.setId(key, bIndex);
+ }
+ }
+ }
+ }
+ }
+ keys = temp.keys;
+ objects = temp.objects;
+ shift = temp.shift;
+ bitSet = temp.bitSet;
+ }
+
+ public void put(long id, T object) {
+ if (keys == EMPTY_KEYS) {
+ // Lazy initialization
+ initialize(DEFAULT_SIZE);
+ }
+ if (count > 3 * keys.length / 4) {
+ expand(2 * keys.length);
+ }
+ if (object == null) {
+ object = (T) NULL_OBJECT;
+ }
+
+ int index = index(id);
+ // probe for empty slot
+ while (true) {
+ if (objects[index] == null) {
+ keys[index] = id;
+ objects[index] = object;
+ count++;
+ return;
+ }
+ // if it already exists, replace
+ if (keys[index] == id) {
+ objects[index] = object;
+ return;
+ }
+ index++;
+ if (index == keys.length) {
+ index = 0;
+ }
+ }
+ }
+ public T getAt(int tableIndex) {
+ T o = objects[tableIndex];
+ return o == NULL_OBJECT ? null : o;
+ }
+
+ public T get(long id) {
+ if (keys == EMPTY_KEYS) {
+ return null;
+ }
+ int index = index(id);
+ while (true) {
+ if (objects[index] == null) {
+ return null;
+ }
+ if (keys[index] == id) {
+ return getAt(index);
+ }
+ index++;
+ if (index == keys.length) {
+ index = 0;
+ }
+ }
+ }
+
+ private int index(long id) {
+ return (int) ((id * -7046029254386353131L) >>> shift);
+ }
+
+ // Copied from HashMap::tableSizeFor
+ private static final int tableSizeFor(int cap) {
+ int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+
+ public void forEachKey(LongConsumer keyTraverser) {
+ for (int i = 0; i < keys.length; i++) {
+ if (objects[i] != null) {
+ keyTraverser.accept(keys[i]);
+ }
+ }
+ }
+
+ public void forEach(Consumer<T> consumer) {
+ for (int i = 0; i < keys.length; i++) {
+ T o = objects[i];
+ if (o != null) {
+ consumer.accept(o);
+ }
+ }
+ }
+
+ public int size() {
+ return count;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < objects.length; i++) {
+ sb.append(i);
+ sb.append(": id=");
+ sb.append(keys[i]);
+ sb.append(" ");
+ sb.append(objects[i]);
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataDescriptor.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataDescriptor.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,6 @@
package jdk.jfr.internal;
-import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,6 +34,7 @@
import java.util.TimeZone;
import jdk.jfr.EventType;
+import jdk.jfr.internal.consumer.RecordingInput;
/**
* Metadata about a chunk
@@ -214,6 +214,7 @@
long gmtOffset;
String locale;
Element root;
+ public long metadataId;
// package private
MetadataDescriptor() {
@@ -252,7 +253,7 @@
return locale;
}
- public static MetadataDescriptor read(DataInput input) throws IOException {
+ public static MetadataDescriptor read(RecordingInput input) throws IOException {
MetadataReader r = new MetadataReader(input);
return r.getDescriptor();
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,8 @@
import jdk.jfr.SettingDescriptor;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.MetadataDescriptor.Element;
+import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringParser;
/**
* Parses metadata.
@@ -61,12 +63,13 @@
private final MetadataDescriptor descriptor;
private final Map<Long, Type> types = new HashMap<>();
- public MetadataReader(DataInput input) throws IOException {
+ public MetadataReader(RecordingInput input) throws IOException {
this.input = input;
int size = input.readInt();
this.pool = new ArrayList<>(size);
+ StringParser p = new StringParser(null, false);
for (int i = 0; i < size; i++) {
- this.pool.add(input.readUTF());
+ this.pool.add((String) p.parse(input));
}
descriptor = new MetadataDescriptor();
Element root = createElement();
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -46,6 +46,7 @@
import jdk.jfr.Threshold;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.RequestEngine.RequestHook;
+import jdk.jfr.internal.consumer.RepositoryFiles;
import jdk.jfr.internal.handlers.EventHandler;
public final class MetadataRepository {
@@ -61,6 +62,7 @@
private boolean staleMetadata = true;
private boolean unregistered;
private long lastUnloaded = -1;
+ private boolean flushMetadata;
public MetadataRepository() {
initializeJVMEventTypes();
@@ -142,6 +144,7 @@
typeLibrary.addType(handler.getPlatformEventType());
if (jvm.isRecording()) {
storeDescriptorInJVM(); // needed for emergency dump
+ flushMetadata = true;
settingsManager.setEventControl(handler.getEventControl());
settingsManager.updateRetransform(Collections.singletonList((eventClass)));
} else {
@@ -207,10 +210,14 @@
}
public synchronized List<EventControl> getEventControls() {
- List<EventControl> controls = new ArrayList<>();
+ List<Class<? extends jdk.internal.event.Event>> eventClasses = jvm.getAllEventClasses();
+ ArrayList<EventControl> controls = new ArrayList<>(eventClasses.size() + nativeControls.size());
controls.addAll(nativeControls);
- for (EventHandler eh : getEventHandlers()) {
- controls.add(eh.getEventControl());
+ for (Class<? extends jdk.internal.event.Event> clazz : eventClasses) {
+ EventHandler eh = Utils.getHandler(clazz);
+ if (eh != null) {
+ controls.add(eh.getEventControl());
+ }
}
return controls;
}
@@ -255,12 +262,15 @@
staleMetadata = true;
}
- // Lock around setOutput ensures that other threads dosn't
- // emit event after setOutput and unregister the event class, before a call
+ // Lock around setOutput ensures that other threads don't
+ // emit events after setOutput and unregister the event class, before a call
// to storeDescriptorInJVM
- synchronized void setOutput(String filename) {
+ synchronized void setOutput(String filename) {
jvm.setOutput(filename);
-
+ if (filename != null) {
+ RepositoryFiles.notifyNewFile();
+ }
+ flushMetadata = false;
unregisterUnloaded();
if (unregistered) {
staleMetadata = typeLibrary.clearUnregistered();
@@ -307,4 +317,12 @@
throw new InternalError("Mirror class must have annotation " + MirrorEvent.class.getName());
}
+ public synchronized void flush() {
+ jvm.flush(flushMetadata);
+ this.flushMetadata = false;
+ }
+
+
+
+
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataWriter.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataWriter.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -53,7 +53,7 @@
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.MetadataDescriptor.Attribute;
import jdk.jfr.internal.MetadataDescriptor.Element;
-import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringParser;
/**
* Class responsible for converting a list of types into a format that can be
@@ -94,10 +94,10 @@
private void writeString(DataOutput out, String s) throws IOException {
if (s == null ) {
- out.writeByte(RecordingInput.STRING_ENCODING_NULL);
+ out.writeByte(StringParser.Encoding.NULL.byteValue());
return;
}
- out.writeByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // encoding UTF-16
+ out.writeByte(StringParser.Encoding.CHAR_ARRAY.byteValue()); // encoding UTF-16
int length = s.length();
writeInt(out, length);
for (int i = 0; i < length; i++) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -69,7 +69,7 @@
super(name, Type.SUPER_TYPE_EVENT, id);
this.dynamicSettings = dynamicSettings;
this.isJVM = Type.isDefinedByJVM(id);
- this.isMethodSampling = name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample");
+ this.isMethodSampling = isJVM && (name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample"));
this.isJDK = isJDK;
this.stackTraceOffset = stackTraceOffset(name, isJDK);
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,6 @@
import static jdk.jfr.internal.LogTag.JFR;
import static jdk.jfr.internal.LogTag.JFR_SYSTEM;
-import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.time.Duration;
@@ -59,6 +58,7 @@
public final class PlatformRecorder {
+
private final List<PlatformRecording> recordings = new ArrayList<>();
private final static List<SecureRecorderListener> changeListeners = new ArrayList<>();
private final Repository repository;
@@ -96,6 +96,7 @@
Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> {
result.add(new Timer("JFR Recording Scheduler", true));
});
+ jvm.exclude(t);
t.start();
t.join();
return result.get(0);
@@ -204,7 +205,7 @@
repository.clear();
}
- synchronized void start(PlatformRecording recording) {
+ synchronized long start(PlatformRecording recording) {
// State can only be NEW or DELAYED because of previous checks
Instant now = Instant.now();
recording.setStartTime(now);
@@ -215,14 +216,17 @@
}
boolean toDisk = recording.isToDisk();
boolean beginPhysical = true;
+ long streamInterval = recording.getStreamIntervalMillis();
for (PlatformRecording s : getRecordings()) {
if (s.getState() == RecordingState.RUNNING) {
beginPhysical = false;
if (s.isToDisk()) {
toDisk = true;
}
+ streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis());
}
}
+ long startNanos = -1;
if (beginPhysical) {
RepositoryChunk newChunk = null;
if (toDisk) {
@@ -232,7 +236,8 @@
MetadataRepository.getInstance().setOutput(null);
}
currentChunk = newChunk;
- jvm.beginRecording_();
+ jvm.beginRecording_();
+ startNanos = jvm.getChunkStartNanos();
recording.setState(RecordingState.RUNNING);
updateSettings();
writeMetaEvents();
@@ -241,7 +246,8 @@
if (toDisk) {
newChunk = repository.newChunk(now);
RequestEngine.doChunkEnd();
- MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
+ MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
+ startNanos = jvm.getChunkStartNanos();
}
recording.setState(RecordingState.RUNNING);
updateSettings();
@@ -251,8 +257,12 @@
}
currentChunk = newChunk;
}
+ if (toDisk) {
+ RequestEngine.setFlushInterval(streamInterval);
+ }
+ RequestEngine.doChunkBegin();
- RequestEngine.doChunkBegin();
+ return startNanos;
}
synchronized void stop(PlatformRecording recording) {
@@ -267,6 +277,7 @@
Instant now = Instant.now();
boolean toDisk = false;
boolean endPhysical = true;
+ long streamInterval = Long.MAX_VALUE;
for (PlatformRecording s : getRecordings()) {
RecordingState rs = s.getState();
if (s != recording && RecordingState.RUNNING == rs) {
@@ -274,6 +285,7 @@
if (s.isToDisk()) {
toDisk = true;
}
+ streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis());
}
}
OldObjectSample.emit(recording);
@@ -309,6 +321,13 @@
currentChunk = newChunk;
RequestEngine.doChunkBegin();
}
+
+ if (toDisk) {
+ RequestEngine.setFlushInterval(streamInterval);
+ } else {
+ RequestEngine.setFlushInterval(Long.MAX_VALUE);
+ }
+
recording.setState(RecordingState.STOPPED);
}
@@ -338,6 +357,18 @@
MetadataRepository.getInstance().setSettings(list);
}
+ public synchronized void rotateIfRecordingToDisk() {
+ boolean disk = false;
+ for (PlatformRecording s : getRecordings()) {
+ if (RecordingState.RUNNING == s.getState() && s.isToDisk()) {
+ disk = true;
+ }
+ }
+ if (disk) {
+ rotateDisk();
+ }
+ }
+
synchronized void rotateDisk() {
Instant now = Instant.now();
RepositoryChunk newChunk = repository.newChunk(now);
@@ -395,14 +426,14 @@
r.appendChunk(chunk);
}
}
+ FilePurger.purge();
}
private void writeMetaEvents() {
-
if (activeRecordingEvent.isEnabled()) {
+ ActiveRecordingEvent event = ActiveRecordingEvent.EVENT.get();
for (PlatformRecording r : getRecordings()) {
if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) {
- ActiveRecordingEvent event = new ActiveRecordingEvent();
event.id = r.getId();
event.name = r.getName();
WriteableUserPath p = r.getDestination();
@@ -415,6 +446,8 @@
event.maxSize = size == null ? Long.MAX_VALUE : size;
Instant start = r.getStartTime();
event.recordingStart = start == null ? Long.MAX_VALUE : start.toEpochMilli();
+ Duration fi = r.getFlushInterval();
+ event.flushInterval = fi == null ? Long.MAX_VALUE : fi.toMillis();
event.commit();
}
}
@@ -448,7 +481,7 @@
JVM.FILE_DELTA_CHANGE.wait(duration < 10 ? 10 : duration);
}
} catch (InterruptedException e) {
- e.printStackTrace();
+ // Ignore
}
}
@@ -550,4 +583,7 @@
target.setStopTime(endTime);
target.setInternalDuration(Duration.between(startTime, endTime));
}
+
+
+
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -84,6 +84,7 @@
private TimerTask startTask;
private AccessControlContext noDestinationDumpOnExitAccessControlContext;
private boolean shuoldWriteActiveRecordingEvent = true;
+ private Duration flushInterval = Duration.ofSeconds(1);
PlatformRecording(PlatformRecorder recorder, long id) {
// Typically the access control context is taken
@@ -98,9 +99,10 @@
this.name = String.valueOf(id);
}
- public void start() {
+ public long start() {
RecordingState oldState;
RecordingState newState;
+ long startNanos = -1;
synchronized (recorder) {
oldState = getState();
if (!Utils.isBefore(state, RecordingState.RUNNING)) {
@@ -111,7 +113,7 @@
startTask = null;
startTime = null;
}
- recorder.start(this);
+ startNanos = recorder.start(this);
Logger.log(LogTag.JFR, LogLevel.INFO, () -> {
// Only print non-default values so it easy to see
// which options were added
@@ -143,6 +145,8 @@
newState = getState();
}
notifyIfStateChanged(oldState, newState);
+
+ return startNanos;
}
public boolean stop(String reason) {
@@ -780,4 +784,28 @@
public SafePath getDumpOnExitDirectory() {
return this.dumpOnExitDirectory;
}
+
+ public void setFlushInterval(Duration interval) {
+ synchronized (recorder) {
+ if (getState() == RecordingState.CLOSED) {
+ throw new IllegalStateException("Can't set stream interval when recording is closed");
+ }
+ this.flushInterval = interval;
+ }
+ }
+
+ public Duration getFlushInterval() {
+ synchronized (recorder) {
+ return flushInterval;
+ }
+ }
+
+ public long getStreamIntervalMillis() {
+ synchronized (recorder) {
+ if (flushInterval != null) {
+ return flushInterval.toMillis();
+ }
+ return Long.MAX_VALUE;
+ }
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Repository.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Repository.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,6 +43,7 @@
public final static DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter
.ofPattern("yyyy_MM_dd_HH_mm_ss");
+ private static final String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
private final Set<SafePath> cleanupDirectories = new HashSet<>();
private SafePath baseLocation;
@@ -55,8 +56,7 @@
return instance;
}
- public synchronized void setBasePath(SafePath baseLocation) throws Exception {
-
+ public synchronized void setBasePath(SafePath baseLocation) throws IOException {
if(baseLocation.equals(this.baseLocation)) {
Logger.log(LogTag.JFR, LogLevel.INFO, "Same base repository path " + baseLocation.toString() + " is set");
return;
@@ -74,7 +74,7 @@
this.baseLocation = baseLocation;
}
- synchronized void ensureRepository() throws Exception {
+ public synchronized void ensureRepository() throws IOException {
if (baseLocation == null) {
setBasePath(SecuritySupport.JAVA_IO_TMPDIR);
}
@@ -96,7 +96,7 @@
}
}
- private static SafePath createRepository(SafePath basePath) throws Exception {
+ private static SafePath createRepository(SafePath basePath) throws IOException {
SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath);
SafePath f = null;
@@ -113,13 +113,14 @@
}
if (i == MAX_REPO_CREATION_RETRIES) {
- throw new Exception("Unable to create JFR repository directory using base location (" + basePath + ")");
+ throw new IOException("Unable to create JFR repository directory using base location (" + basePath + ")");
}
SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f);
+ SecuritySupport.setProperty(JFR_REPOSITORY_LOCATION_PROPERTY, canonicalRepositoryPath.toString());
return canonicalRepositoryPath;
}
- private static SafePath createRealBasePath(SafePath safePath) throws Exception {
+ private static SafePath createRealBasePath(SafePath safePath) throws IOException {
if (SecuritySupport.exists(safePath)) {
if (!SecuritySupport.isWritable(safePath)) {
throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable");
@@ -159,7 +160,7 @@
SecuritySupport.clearDirectory(p);
Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p);
} catch (IOException e) {
- Logger.log(LogTag.JFR, LogLevel.ERROR, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
+ Logger.log(LogTag.JFR, LogLevel.INFO, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
}
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -63,10 +63,10 @@
LocalDateTime.ofInstant(startTime, z.getZone()));
this.startTime = startTime;
this.repositoryPath = path;
- this.unFinishedFile = findFileName(repositoryPath, fileName, ".part");
+ this.unFinishedFile = findFileName(repositoryPath, fileName, ".jfr");
this.file = findFileName(repositoryPath, fileName, ".jfr");
this.unFinishedRAF = SecuritySupport.createRandomAccessFile(unFinishedFile);
- SecuritySupport.touch(file);
+ // SecuritySupport.touch(file);
}
private static SafePath findFileName(SafePath directory, String name, String extension) throws Exception {
@@ -105,8 +105,6 @@
private static long finish(SafePath unFinishedFile, SafePath file) throws IOException {
Objects.requireNonNull(unFinishedFile);
Objects.requireNonNull(file);
- SecuritySupport.delete(file);
- SecuritySupport.moveReplace(unFinishedFile, file);
return SecuritySupport.getFileSize(file);
}
@@ -123,9 +121,11 @@
SecuritySupport.delete(f);
Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " deleted");
} catch (IOException e) {
- Logger.log(LogTag.JFR, LogLevel.ERROR, () -> "Repository chunk " + f + " could not be deleted: " + e.getMessage());
+ // Probably happens because file is being streamed
+ // on Windows where files in use can't be removed.
+ Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " could not be deleted: " + e.getMessage());
if (f != null) {
- SecuritySupport.deleteOnExit(f);
+ FilePurger.add(f);
}
}
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -96,6 +97,8 @@
private final static List<RequestHook> entries = new CopyOnWriteArrayList<>();
private static long lastTimeMillis;
+ private static long flushInterval = Long.MAX_VALUE;
+ private static long streamDelta;
public static void addHook(AccessControlContext acc, PlatformEventType type, Runnable hook) {
Objects.requireNonNull(acc);
@@ -209,7 +212,9 @@
lastTimeMillis = now;
return 0;
}
- for (RequestHook he : entries) {
+ Iterator<RequestHook> hookIterator = entries.iterator();
+ while(hookIterator.hasNext()) {
+ RequestHook he = hookIterator.next();
long left = 0;
PlatformEventType es = he.type;
// Not enabled, skip.
@@ -228,7 +233,6 @@
// for wait > period
r_delta = 0;
he.execute();
- ;
}
// calculate time left
@@ -250,7 +254,39 @@
min = left;
}
}
+ // Flush should happen after all periodic events has been emitted
+ // Repeat of the above algorithm, but using the stream interval.
+ if (flushInterval != Long.MAX_VALUE) {
+ long r_period = flushInterval;
+ long r_delta = streamDelta;
+ r_delta += delta;
+ if (r_delta >= r_period) {
+ r_delta = 0;
+ MetadataRepository.getInstance().flush();
+ Utils.notifyFlush();
+ }
+ long left = (r_period - r_delta);
+ if (left < 0) {
+ left = 0;
+ }
+ streamDelta = r_delta;
+ if (min == 0 || left < min) {
+ min = left;
+ }
+ }
+
lastTimeMillis = now;
return min;
}
+
+ static void setFlushInterval(long millis) {
+ // Don't accept shorter interval than 1 s.
+ long interval = millis < 1000 ? 1000 : millis;
+ flushInterval = interval;
+ if (interval < flushInterval) {
+ synchronized (JVM.FILE_DELTA_CHANGE) {
+ JVM.FILE_DELTA_CHANGE.notifyAll();
+ }
+ }
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,6 +38,7 @@
import java.lang.reflect.ReflectPermission;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
+import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -65,6 +66,7 @@
import jdk.jfr.FlightRecorderListener;
import jdk.jfr.FlightRecorderPermission;
import jdk.jfr.Recording;
+import jdk.jfr.internal.consumer.FileAccess;
/**
* Contains JFR code that does
@@ -75,7 +77,7 @@
private final static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private final static Module JFR_MODULE = Event.class.getModule();
public final static SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr");
-
+ public final static FileAccess PRIVILIGED = new Privileged();
static final SafePath USER_HOME = getPathInProperty("user.home", null);
static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null);
@@ -150,7 +152,7 @@
* a malicious provider.
*
*/
- public static final class SafePath {
+ public static final class SafePath implements Comparable<SafePath> {
private final Path path;
private final String text;
@@ -168,11 +170,20 @@
return path;
}
+ public File toFile() {
+ return path.toFile();
+ }
+
public String toString() {
return text;
}
@Override
+ public int compareTo(SafePath that) {
+ return that.text.compareTo(this.text);
+ }
+
+ @Override
public boolean equals(Object other) {
if(other != null && other instanceof SafePath){
return this.toPath().equals(((SafePath) other).toPath());
@@ -303,6 +314,10 @@
doPrivileged(() -> MetadataRepository.getInstance().registerMirror(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
}
+ public static void setProperty(String propertyName, String value) {
+ doPrivileged(() -> System.setProperty(propertyName, value), new PropertyPermission(propertyName, "write"));
+ }
+
static boolean getBooleanProperty(String propertyName) {
return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read"));
}
@@ -343,7 +358,7 @@
doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner()));
}
- static SafePath toRealPath(SafePath safePath) throws Exception {
+ static SafePath toRealPath(SafePath safePath) throws IOException {
return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath()));
}
@@ -369,7 +384,8 @@
}
public static boolean exists(SafePath safePath) throws IOException {
- return doPrivilegedIOWithReturn(() -> Files.exists(safePath.toPath()));
+ // Files.exist(path) is allocation intensive
+ return doPrivilegedIOWithReturn(() -> safePath.toPath().toFile().exists());
}
public static boolean isDirectory(SafePath safePath) throws IOException {
@@ -421,7 +437,7 @@
}
static Class<?> defineClass(Class<?> lookupClass, byte[] bytes) {
- return AccessController.doPrivileged(new PrivilegedAction<>() {
+ return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
@Override
public Class<?> run() {
try {
@@ -433,7 +449,7 @@
});
}
- static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) {
+ public static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) {
return doPrivilegedWithReturn(() -> new Thread(runnable, threadName), new Permission[0]);
}
@@ -444,4 +460,32 @@
public static SafePath getAbsolutePath(SafePath path) throws IOException {
return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath())));
}
+
+ private final static class Privileged extends FileAccess {
+ @Override
+ public RandomAccessFile openRAF(File f, String mode) throws IOException {
+ return doPrivilegedIOWithReturn( () -> new RandomAccessFile(f, mode));
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(Path directory) throws IOException {
+ return doPrivilegedIOWithReturn( () -> Files.newDirectoryStream(directory));
+ }
+
+ @Override
+ public String getAbsolutePath(File f) throws IOException {
+ return doPrivilegedIOWithReturn( () ->f.getAbsolutePath());
+ }
+ @Override
+ public long length(File f) throws IOException {
+ return doPrivilegedIOWithReturn( () ->f.length());
+ }
+
+ @Override
+ public long fileSize(Path p) throws IOException {
+ return doPrivilegedIOWithReturn( () ->Files.size(p));
+ }
+ }
+
+
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,10 +33,10 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.StringJoiner;
+import jdk.jfr.internal.EventControl.NamedControl;
import jdk.jfr.internal.handlers.EventHandler;
final class SettingsManager {
@@ -213,18 +213,21 @@
void setEventControl(EventControl ec) {
InternalSetting is = getInternalSetting(ec);
- Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {");
- for (Entry<String, Control> entry : ec.getEntries()) {
+ boolean shouldLog = Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO);
+ if (shouldLog) {
+ Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {");
+ }
+ for (NamedControl nc: ec.getNamedControls()) {
Set<String> values = null;
- String settingName = entry.getKey();
+ String settingName = nc.name;
if (is != null) {
values = is.getValues(settingName);
}
- Control control = entry.getValue();
+ Control control = nc.control;
if (values != null) {
control.apply(values);
String after = control.getLastValue();
- if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) {
+ if (shouldLog) {
if (Utils.isSettingVisible(control, ec.getEventType().hasEventHook())) {
if (values.size() > 1) {
StringJoiner sj = new StringJoiner(", ", "{", "}");
@@ -241,14 +244,16 @@
}
} else {
control.setDefault();
- if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) {
+ if (shouldLog) {
String message = " " + settingName + "=\"" + control.getLastValue() + "\"";
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message);
}
}
}
ec.writeActiveSettingEvent();
- Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}");
+ if (shouldLog) {
+ Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}");
+ }
}
private InternalSetting getInternalSetting(EventControl ec) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,6 +43,7 @@
import java.lang.reflect.Modifier;
import java.nio.file.Path;
import java.time.Duration;
+import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
@@ -65,17 +66,17 @@
public final class Utils {
+ private static final Object flushObject = new Object();
private static final String INFINITY = "infinity";
-
- private static Boolean SAVE_GENERATED;
-
public static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events";
public static final String INSTRUMENT_PACKAGE_NAME = "jdk.jfr.internal.instrument";
public static final String HANDLERS_PACKAGE_NAME = "jdk.jfr.internal.handlers";
public static final String REGISTER_EVENT = "registerEvent";
public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder";
+ private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
- private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
+ private static Boolean SAVE_GENERATED;
+
public static void checkAccessFlightRecorder() throws SecurityException {
SecurityManager sm = System.getSecurityManager();
@@ -595,4 +596,33 @@
String idText = recording == null ? "" : "-id-" + Long.toString(recording.getId());
return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr";
}
+
+ public static void takeNap(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ // ok
+ }
+ }
+
+ public static void notifyFlush() {
+ synchronized (flushObject) {
+ flushObject.notifyAll();
+ }
+ }
+
+ public static void waitFlush(long timeOut) {
+ synchronized (flushObject) {
+ try {
+ flushObject.wait(timeOut);
+ } catch (InterruptedException e) {
+ // OK
+ }
+ }
+
+ }
+
+ public static long timeToNanos(Instant timestamp) {
+ return timestamp.getEpochSecond() * 1_000_000_000L + timestamp.getNano();
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.JVM;
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.SecuritySupport;
+
+/*
+ * Purpose of this class is to simplify the implementation of
+ * an event stream.
+ */
+abstract class AbstractEventStream implements EventStream {
+ private final static AtomicLong counter = new AtomicLong(1);
+
+ private final Object terminated = new Object();
+ private final boolean active;
+ private final Runnable flushOperation = () -> dispatcher().runFlushActions();
+ private final AccessControlContext accessControllerContext;
+ private final StreamConfiguration configuration = new StreamConfiguration();
+
+ private volatile Thread thread;
+ private Dispatcher dispatcher;
+
+ private volatile boolean closed;
+
+ AbstractEventStream(AccessControlContext acc, boolean active) throws IOException {
+ this.accessControllerContext = Objects.requireNonNull(acc);
+ this.active = active;
+ }
+
+ @Override
+ abstract public void start();
+
+ @Override
+ abstract public void startAsync();
+
+ @Override
+ abstract public void close();
+
+ protected final Dispatcher dispatcher() {
+ if (configuration.hasChanged()) {
+ synchronized (configuration) {
+ dispatcher = new Dispatcher(configuration);
+ }
+ }
+ return dispatcher;
+ }
+
+ @Override
+ public final void setOrdered(boolean ordered) {
+ configuration.setOrdered(ordered);
+ }
+
+ @Override
+ public final void setReuse(boolean reuse) {
+ configuration.setReuse(reuse);
+ }
+
+ @Override
+ public final void setStartTime(Instant startTime) {
+ Objects.nonNull(startTime);
+ synchronized (configuration) {
+ if (configuration.started) {
+ throw new IllegalStateException("Stream is already started");
+ }
+ if (startTime.isBefore(Instant.EPOCH)) {
+ startTime = Instant.EPOCH;
+ }
+ configuration.setStartTime(startTime);
+ }
+ }
+
+ @Override
+ public final void setEndTime(Instant endTime) {
+ Objects.requireNonNull(endTime);
+ synchronized (configuration) {
+ if (configuration.started) {
+ throw new IllegalStateException("Stream is already started");
+ }
+ configuration.setEndTime(endTime);
+ }
+ }
+
+ @Override
+ public final void onEvent(Consumer<RecordedEvent> action) {
+ Objects.requireNonNull(action);
+ configuration.addEventAction(action);
+ }
+
+ @Override
+ public final void onEvent(String eventName, Consumer<RecordedEvent> action) {
+ Objects.requireNonNull(eventName);
+ Objects.requireNonNull(action);
+ configuration.addEventAction(eventName, action);
+ }
+
+ @Override
+ public final void onFlush(Runnable action) {
+ Objects.requireNonNull(action);
+ configuration.addFlushAction(action);
+ }
+
+ @Override
+ public final void onClose(Runnable action) {
+ Objects.requireNonNull(action);
+ configuration.addCloseAction(action);
+ }
+
+ @Override
+ public final void onError(Consumer<Throwable> action) {
+ Objects.requireNonNull(action);
+ configuration.addErrorAction(action);
+ }
+
+ @Override
+ public final boolean remove(Object action) {
+ Objects.requireNonNull(action);
+ return configuration.remove(action);
+ }
+
+ @Override
+ public final void awaitTermination() throws InterruptedException {
+ awaitTermination(Duration.ofMillis(0));
+ }
+
+ @Override
+ public final void awaitTermination(Duration timeout) throws InterruptedException {
+ Objects.requireNonNull(timeout);
+ if (timeout.isNegative()) {
+ throw new IllegalArgumentException("timeout value is negative");
+ }
+
+ long base = System.currentTimeMillis();
+ long now = 0;
+
+ long millis;
+ try {
+ millis = Math.multiplyExact(timeout.getSeconds(), 1000);
+ } catch (ArithmeticException a) {
+ millis = Long.MAX_VALUE;
+ }
+ int nanos = timeout.toNanosPart();
+ if (nanos == 0 && millis == 0) {
+ synchronized (terminated) {
+ while (!isClosed()) {
+ terminated.wait(0);
+ }
+ }
+ } else {
+ while (!isClosed()) {
+ long delay = millis - now;
+ if (delay <= 0) {
+ break;
+ }
+ synchronized (terminated) {
+ terminated.wait(delay, nanos);
+ }
+ now = System.currentTimeMillis() - base;
+ }
+ }
+ }
+
+ protected abstract void process() throws IOException;
+
+ protected final void setClosed(boolean closed) {
+ this.closed = closed;
+ }
+
+ protected final boolean isClosed() {
+ return closed;
+ }
+
+ public final void startAsync(long startNanos) {
+ startInternal(startNanos);
+ Runnable r = () -> run(accessControllerContext);
+ thread = SecuritySupport.createThreadWitNoPermissions(nextThreadName(), r);
+ thread.start();
+ }
+
+ public final void start(long startNanos) {
+ startInternal(startNanos);
+ thread = Thread.currentThread();
+ run(accessControllerContext);
+ }
+
+ protected final Runnable getFlushOperation() {
+ return flushOperation;
+ }
+
+ private void startInternal(long startNanos) {
+ synchronized (configuration) {
+ if (configuration.started) {
+ throw new IllegalStateException("Event stream can only be started once");
+ }
+ if (active) {
+ configuration.setStartNanos(startNanos);
+ }
+ configuration.setStarted(true);
+ }
+ }
+
+ private void execute() {
+ try {
+ process();
+ } catch (IOException ioe) {
+ // This can happen if a chunk file is removed, or
+ // a file is access that has been closed
+ // This is "normal" behavior for streaming and the
+ // stream will be closed when this happens.
+ } finally {
+ Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "Execution of stream ended.");
+ try {
+ close();
+ } finally {
+ synchronized (terminated) {
+ terminated.notifyAll();
+ }
+ }
+ }
+ }
+
+ private void run(AccessControlContext accessControlContext) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ execute();
+ return null;
+ }
+ }, accessControlContext);
+ }
+
+ private String nextThreadName() {
+ counter.incrementAndGet();
+ return "JFR Event Stream " + counter;
+ }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,48 +25,60 @@
package jdk.jfr.internal.consumer;
-import java.io.DataInput;
import java.io.IOException;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.MetadataDescriptor;
+import jdk.jfr.internal.Utils;
public final class ChunkHeader {
+ private static final long HEADER_SIZE = 68;
+ private static final byte UPDATING_CHUNK_HEADER = (byte) 255;
+ private static final long CHUNK_SIZE_POSITION = 8;
+ private static final long DURATION_NANOS_POSITION = 40;
+ private static final long FILE_STATE_POSITION = 64;
private static final long METADATA_TYPE_ID = 0;
private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' };
private final short major;
private final short minor;
- private final long chunkSize;
private final long chunkStartTicks;
private final long ticksPerSecond;
private final long chunkStartNanos;
- private final long metadataPosition;
- // private final long absoluteInitialConstantPoolPosition;
- private final long absoluteChunkEnd;
- private final long absoluteEventStart;
private final long absoluteChunkStart;
- private final boolean lastChunk;
private final RecordingInput input;
- private final long durationNanos;
private final long id;
- private long constantPoolPosition;
+ private long absoluteEventStart;
+ private long chunkSize = 0;
+ private long constantPoolPosition = 0;
+ private long metadataPosition = 0;
+ private long durationNanos;
+ private long absoluteChunkEnd;
+ private boolean isFinished;
+ private boolean finished;
public ChunkHeader(RecordingInput input) throws IOException {
this(input, 0, 0);
}
private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException {
+ this.absoluteChunkStart = absoluteChunkStart;
+ this.absoluteEventStart = absoluteChunkStart + HEADER_SIZE;
+ if (input.getFileSize() < HEADER_SIZE) {
+ throw new IOException("Not a complete Chunk header");
+ }
+ input.setValidSize(absoluteChunkStart + HEADER_SIZE);
input.position(absoluteChunkStart);
if (input.position() >= input.size()) {
- throw new IOException("Chunk contains no data");
+ throw new IOException("Chunk contains no data");
}
verifyMagic(input);
this.input = input;
this.id = id;
- Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id);
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: " + id);
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: file=" + input.getFilename());
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart);
major = input.readRawShort();
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major);
@@ -75,11 +87,11 @@
if (major != 1 && major != 2) {
throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK.");
}
- chunkSize = input.readRawLong();
+ input.readRawLong(); // chunk size
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
- this.constantPoolPosition = input.readRawLong();
+ input.readRawLong(); // constant pool position
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
- metadataPosition = input.readRawLong();
+ input.readRawLong(); // metadata position
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
chunkStartNanos = input.readRawLong(); // nanos since epoch
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos);
@@ -91,21 +103,98 @@
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond);
input.readRawInt(); // features, not used
- // set up boundaries
- this.absoluteChunkStart = absoluteChunkStart;
- absoluteChunkEnd = absoluteChunkStart + chunkSize;
- lastChunk = input.size() == absoluteChunkEnd;
- absoluteEventStart = input.position();
+ refresh();
+ input.position(absoluteEventStart);
+ }
- // read metadata
- input.position(absoluteEventStart);
+ void refresh() throws IOException {
+ while (true) {
+ byte fileState1;
+ input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+ while ((fileState1 = input.readPhysicalByte()) == UPDATING_CHUNK_HEADER) {
+ Utils.takeNap(1);
+ input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+ }
+ input.positionPhysical(absoluteChunkStart + CHUNK_SIZE_POSITION);
+ long chunkSize = input.readPhysicalLong();
+ long constantPoolPosition = input.readPhysicalLong();
+ long metadataPosition = input.readPhysicalLong();
+ input.positionPhysical(absoluteChunkStart + DURATION_NANOS_POSITION);
+ long durationNanos = input.readPhysicalLong();
+ input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+ byte fileState2 = input.readPhysicalByte();
+ if (fileState1 == fileState2) { // valid header
+ finished = fileState1 == 0;
+ if (metadataPosition != 0) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Setting input size to " + (absoluteChunkStart + chunkSize));
+ if (finished) {
+ // This assumes that the whole recording
+ // is finished if the first chunk is.
+ // This is a limitation we may want to
+ // remove, but greatly improves performance as
+ // data can be read across chunk boundaries
+ // of multi-chunk files and only once.
+ input.setValidSize(input.getFileSize());
+ } else {
+ input.setValidSize(absoluteChunkStart + chunkSize);
+ }
+ this.chunkSize = chunkSize;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
+ this.constantPoolPosition = constantPoolPosition;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
+ this.metadataPosition = metadataPosition;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
+ this.durationNanos = durationNanos;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos =" + durationNanos);
+ isFinished = fileState2 == 0;
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: generation=" + fileState2);
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: finished=" + isFinished);
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: fileSize=" + input.size());
+ absoluteChunkEnd = absoluteChunkStart + chunkSize;
+ return;
+ }
+ }
+ }
+ }
+
+ public void awaitFinished() throws IOException {
+ if (finished) {
+ return;
+ }
+ long pos = input.position();
+ try {
+ input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+ while (true) {
+ byte filestate = input.readPhysicalByte();
+ if (filestate == 0) {
+ finished = true;
+ return;
+ }
+ Utils.takeNap(1);
+ }
+ } finally {
+ input.position(pos);
+ }
+ }
+
+ public boolean isLastChunk() throws IOException {
+ awaitFinished();
+ // streaming files only have one chunk
+ return input.getFileSize() == absoluteChunkEnd;
+ }
+
+ public boolean isFinished() throws IOException {
+ return isFinished;
}
public ChunkHeader nextHeader() throws IOException {
return new ChunkHeader(input, absoluteChunkEnd, id + 1);
}
+ public MetadataDescriptor readMetadata() throws IOException {
+ return readMetadata(null);
+ }
- public MetadataDescriptor readMetadata() throws IOException {
+ public MetadataDescriptor readMetadata(MetadataDescriptor previous) throws IOException {
input.position(absoluteChunkStart + metadataPosition);
input.readInt(); // size
long id = input.readLong(); // event type id
@@ -115,15 +204,15 @@
input.readLong(); // start time
input.readLong(); // duration
long metadataId = input.readLong();
- Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId);
- // No need to read if metadataId == lastMetadataId, but we
- // do it for verification purposes.
- return MetadataDescriptor.read(input);
+ if (previous != null && metadataId == previous.metadataId) {
+ return previous;
+ }
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "New metadata id = " + metadataId);
+ MetadataDescriptor m = MetadataDescriptor.read(input);
+ m.metadataId = metadataId;
+ return m;
}
- public boolean isLastChunk() {
- return lastChunk;
- }
public short getMajor() {
return major;
@@ -137,13 +226,22 @@
return absoluteChunkStart;
}
+ public long getAbsoluteEventStart() {
+ return absoluteEventStart;
+ }
public long getConstantPoolPosition() {
return constantPoolPosition;
}
+ public long getMetataPosition() {
+ return metadataPosition;
+ }
public long getStartTicks() {
return chunkStartTicks;
}
+ public long getChunkSize() {
+ return chunkSize;
+ }
public double getTicksPerSecond() {
return ticksPerSecond;
@@ -169,7 +267,7 @@
return input;
}
- private static void verifyMagic(DataInput input) throws IOException {
+ private static void verifyMagic(RecordingInput input) throws IOException {
for (byte c : FILE_MAGIC) {
if (input.readByte() != c) {
throw new IOException("Not a Flight Recorder file");
@@ -181,4 +279,7 @@
return absoluteEventStart;
}
+ static long headerSize() {
+ return HEADER_SIZE;
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkParser.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,445 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.StringJoiner;
+
+import jdk.jfr.EventType;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordedObject;
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.LongMap;
+import jdk.jfr.internal.MetadataDescriptor;
+import jdk.jfr.internal.Type;
+import jdk.jfr.internal.Utils;
+
+/**
+ * Parses a chunk.
+ *
+ */
+public final class ChunkParser {
+
+ static final class ParserConfiguration {
+ private final boolean reuse;
+ private final boolean ordered;
+ private final ParserFilter eventFilter;
+
+ long filterStart;
+ long filterEnd;
+
+ ParserConfiguration(long filterStart, long filterEnd, boolean reuse, boolean ordered, ParserFilter filter) {
+ this.filterStart = filterStart;
+ this.filterEnd = filterEnd;
+ this.reuse = reuse;
+ this.ordered = ordered;
+ this.eventFilter = filter;
+ }
+
+ public ParserConfiguration() {
+ this(0, Long.MAX_VALUE, false, false, ParserFilter.ACCEPT_ALL);
+ }
+
+ public boolean isOrdered() {
+ return ordered;
+ }
+ }
+
+ private enum CheckPointType {
+ // Checkpoint that finishes a flush segment
+ FLUSH(1),
+ // Checkpoint contains chunk header information in the first pool
+ CHUNK_HEADER(2),
+ // Checkpoint contains only statics that will not change from chunk to chunk
+ STATICS(4),
+ // Checkpoint contains thread related information
+ THREAD(8);
+ private final int mask;
+ private CheckPointType(int mask) {
+ this.mask = mask;
+ }
+
+ private boolean is(int flags) {
+ return (mask & flags) != 0;
+ }
+ }
+
+ private static final long CONSTANT_POOL_TYPE_ID = 1;
+ private static final String CHUNKHEADER = "jdk.types.ChunkHeader";
+ private final RecordingInput input;
+ private final ChunkHeader chunkHeader;
+ private final MetadataDescriptor metadata;
+ private final TimeConverter timeConverter;
+ private final MetadataDescriptor previousMetadata;
+ private final LongMap<ConstantLookup> constantLookups;
+
+ private LongMap<Type> typeMap;
+ private LongMap<Parser> parsers;
+ private boolean chunkFinished;
+
+ private Runnable flushOperation;
+ private ParserConfiguration configuration;
+
+ public ChunkParser(RecordingInput input) throws IOException {
+ this(input, new ParserConfiguration());
+ }
+
+ ChunkParser(RecordingInput input, ParserConfiguration pc) throws IOException {
+ this(new ChunkHeader(input), null, pc);
+ }
+
+ private ChunkParser(ChunkParser previous) throws IOException {
+ this(new ChunkHeader(previous.input), previous, new ParserConfiguration());
+ }
+
+ private ChunkParser(ChunkHeader header, ChunkParser previous, ParserConfiguration pc) throws IOException {
+ this.configuration = pc;
+ this.input = header.getInput();
+ this.chunkHeader = header;
+ if (previous == null) {
+ this.constantLookups = new LongMap<>();
+ this.previousMetadata = null;
+ } else {
+ this.constantLookups = previous.constantLookups;
+ this.previousMetadata = previous.metadata;
+ this.configuration = previous.configuration;
+ }
+ this.metadata = header.readMetadata(previousMetadata);
+ this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
+ if (metadata != previousMetadata) {
+ ParserFactory factory = new ParserFactory(metadata, constantLookups, timeConverter);
+ parsers = factory.getParsers();
+ typeMap = factory.getTypeMap();
+ updateConfiguration();
+ } else {
+ parsers = previous.parsers;
+ typeMap = previous.typeMap;
+ }
+ constantLookups.forEach(c -> c.newPool());
+ fillConstantPools(0);
+ constantLookups.forEach(c -> c.getLatestPool().setResolving());
+ constantLookups.forEach(c -> c.getLatestPool().resolve());
+ constantLookups.forEach(c -> c.getLatestPool().setResolved());
+
+ input.position(chunkHeader.getEventStart());
+ }
+
+ public ChunkParser nextChunkParser() throws IOException {
+ return new ChunkParser(chunkHeader.nextHeader(), this, configuration);
+ }
+
+ private void updateConfiguration() {
+ updateConfiguration(configuration, false);
+ }
+
+ void updateConfiguration(ParserConfiguration configuration, boolean resetEventCache) {
+ this.configuration = configuration;
+ parsers.forEach(p -> {
+ if (p instanceof EventParser) {
+ EventParser ep = (EventParser) p;
+ if (resetEventCache) {
+ ep.resetCache();
+ }
+ String name = ep.getEventType().getName();
+ ep.setOrdered(configuration.ordered);
+ ep.setReuse(configuration.reuse);
+ ep.setFilterStart(configuration.filterStart);
+ ep.setFilterEnd(configuration.filterEnd);
+ long threshold = configuration.eventFilter.getThreshold(name);
+ if (threshold >= 0) {
+ ep.setEnabled(true);
+ ep.setThresholdNanos(threshold);
+ } else {
+ ep.setEnabled(false);
+ ep.setThresholdNanos(Long.MAX_VALUE);
+ }
+ }
+ });
+ }
+
+ /**
+ * Reads an event and returns null when segment or chunk ends.
+ *
+ * @param awaitNewEvents wait for new data.
+ */
+ RecordedEvent readStreamingEvent(boolean awaitNewEvents) throws IOException {
+ long absoluteChunkEnd = chunkHeader.getEnd();
+ while (true) {
+ RecordedEvent event = readEvent();
+ if (event != null) {
+ return event;
+ }
+ if (!awaitNewEvents) {
+ return null;
+ }
+ long lastValid = absoluteChunkEnd;
+ long metadataPoistion = chunkHeader.getMetataPosition();
+ long contantPosition = chunkHeader.getConstantPoolPosition();
+ chunkFinished = awaitUpdatedHeader(absoluteChunkEnd);
+ if (chunkFinished) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "At chunk end");
+ return null;
+ }
+ absoluteChunkEnd = chunkHeader.getEnd();
+ // Read metadata and constant pools for the next segment
+ if (chunkHeader.getMetataPosition() != metadataPoistion) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found new metadata in chunk. Rebuilding types and parsers");
+ MetadataDescriptor metadata = chunkHeader.readMetadata(previousMetadata);
+ ParserFactory factory = new ParserFactory(metadata, constantLookups, timeConverter);
+ parsers = factory.getParsers();
+ typeMap = factory.getTypeMap();
+ updateConfiguration();;
+ }
+ if (contantPosition != chunkHeader.getConstantPoolPosition()) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found new constant pool data. Filling up pools with new values");
+ constantLookups.forEach(c -> c.getLatestPool().setAllResolved(false));
+ fillConstantPools(contantPosition + chunkHeader.getAbsoluteChunkStart());
+ constantLookups.forEach(c -> c.getLatestPool().setResolving());
+ constantLookups.forEach(c -> c.getLatestPool().resolve());
+ constantLookups.forEach(c -> c.getLatestPool().setResolved());
+ }
+ input.position(lastValid);
+ }
+ }
+
+ /**
+ * Reads an event and returns null when the chunk ends
+ */
+ public RecordedEvent readEvent() throws IOException {
+ long absoluteChunkEnd = chunkHeader.getEnd();
+ while (input.position() < absoluteChunkEnd) {
+ long pos = input.position();
+ int size = input.readInt();
+ if (size == 0) {
+ throw new IOException("Event can't have zero size");
+ }
+ long typeId = input.readLong();
+
+ if (typeId != 0) { // Not metadata event
+ Parser p = parsers.get(typeId);
+ if (p instanceof EventParser) {
+ EventParser ep = (EventParser) p;
+ RecordedEvent event = ep.parse(input);
+ if (event != null) {
+ input.position(pos + size);
+ return event;
+ }
+ }
+ if (typeId == 1 && flushOperation != null) { // checkpoint event
+ parseCheckpoint();
+ }
+ }
+ input.position(pos + size);
+ }
+ return null;
+ }
+
+ private void parseCheckpoint() throws IOException {
+ // Content has been parsed previously. This
+ // is to trigger flush
+ input.readLong(); // timestamp
+ input.readLong(); // duration
+ input.readLong(); // delta
+ byte typeFlags = input.readByte();
+ if (CheckPointType.FLUSH.is(typeFlags)) {
+ flushOperation.run();
+ }
+ }
+
+ private boolean awaitUpdatedHeader(long absoluteChunkEnd) throws IOException {
+ if (Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO)) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Waiting for more data (streaming). Read so far: " + chunkHeader.getChunkSize() + " bytes");
+ }
+ while (true) {
+ chunkHeader.refresh();
+ if (absoluteChunkEnd != chunkHeader.getEnd()) {
+ return false;
+ }
+ if (chunkHeader.isFinished()) {
+ return true;
+ }
+ Utils.waitFlush(1000);
+ }
+ }
+
+ private void fillConstantPools(long abortCP) throws IOException {
+ long thisCP = chunkHeader.getConstantPoolPosition() + chunkHeader.getAbsoluteChunkStart();
+ long lastCP = -1;
+ long delta = -1;
+ boolean logTrace = Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE);
+ while (thisCP != abortCP && delta != 0) {
+ input.position(thisCP);
+ lastCP = thisCP;
+ int size = input.readInt(); // size
+ long typeId = input.readLong();
+ if (typeId != CONSTANT_POOL_TYPE_ID) {
+ throw new IOException("Expected check point event (id = 1) at position " + lastCP + ", but found type id = " + typeId);
+ }
+ input.readLong(); // timestamp
+ input.readLong(); // duration
+ delta = input.readLong();
+ thisCP += delta;
+ boolean flush = input.readBoolean();
+ int poolCount = input.readInt();
+ final long logLastCP = lastCP;
+ final long logDelta = delta;
+ if (logTrace) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
+ return "New constant pool: startPosition=" + logLastCP + ", size=" + size + ", deltaToNext=" + logDelta + ", flush=" + flush + ", poolCount=" + poolCount;
+ });
+ }
+ for (int i = 0; i < poolCount; i++) {
+ long id = input.readLong(); // type id
+ ConstantLookup lookup = constantLookups.get(id);
+ Type type = typeMap.get(id);
+ if (lookup == null) {
+ if (type == null) {
+ throw new IOException(
+ "Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + lastCP + ", " + lastCP + size + "]");
+ }
+ if (type.getName() != CHUNKHEADER) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used");
+ }
+ ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+ lookup = new ConstantLookup(pool, type);
+ constantLookups.put(type.getId(), lookup);
+ }
+ Parser parser = parsers.get(id);
+ if (parser == null) {
+ throw new IOException("Could not find constant pool type with id = " + id);
+ }
+ try {
+ int count = input.readInt();
+ if (count == 0) {
+ throw new InternalError("Pool " + type.getName() + " must contain at least one element ");
+ }
+ if (logTrace) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Constant Pool " + i + ": " + type.getName());
+ }
+ for (int j = 0; j < count; j++) {
+ long key = input.readLong();
+ Object resolved = lookup.getPreviousResolved(key);
+ if (resolved == null) {
+ Object v = parser.parse(input);
+ logConstant(key, v, false);
+ lookup.getLatestPool().put(key, v);
+ } else {
+ parser.skip(input);
+ logConstant(key, resolved, true);
+ lookup.getLatestPool().putResolved(key, resolved);
+ }
+ }
+ } catch (Exception e) {
+ throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + lastCP + ", " + lastCP + size + "]",
+ e);
+ }
+ }
+ if (input.position() != lastCP + size) {
+ throw new IOException("Size of check point event doesn't match content");
+ }
+ }
+ }
+
+ private void logConstant(long key, Object v, boolean preresolved) {
+ if (!Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE)) {
+ return;
+ }
+ String valueText;
+ if (v.getClass().isArray()) {
+ Object[] array = (Object[]) v;
+ StringJoiner sj = new StringJoiner(", ", "{", "}");
+ for (int i = 0; i < array.length; i++) {
+ sj.add(textify(array[i]));
+ }
+ valueText = sj.toString();
+ } else {
+ valueText = textify(v);
+ }
+ String suffix = preresolved ? " (presolved)" :"";
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Constant: " + key + " = " + valueText + suffix);
+ }
+
+ private String textify(Object o) {
+ if (o == null) { // should not happen
+ return "null";
+ }
+ if (o instanceof String) {
+ return "\"" + String.valueOf(o) + "\"";
+ }
+ if (o instanceof RecordedObject) {
+ return o.getClass().getName();
+ }
+ if (o.getClass().isArray()) {
+ Object[] array = (Object[]) o;
+ if (array.length > 0) {
+ return textify(array[0]) + "[]"; // can it be recursive?
+ }
+ }
+ return String.valueOf(o);
+ }
+
+ private String getName(long id) {
+ Type type = typeMap.get(id);
+ return type == null ? ("unknown(" + id + ")") : type.getName();
+ }
+
+ public Collection<Type> getTypes() {
+ return metadata.getTypes();
+ }
+
+ public List<EventType> getEventTypes() {
+ return metadata.getEventTypes();
+ }
+
+ public boolean isLastChunk() throws IOException {
+ return chunkHeader.isLastChunk();
+ }
+
+ ChunkParser newChunkParser() throws IOException {
+ return new ChunkParser(this);
+ }
+
+ public boolean isChunkFinished() {
+ return chunkFinished;
+ }
+
+ public void setFlushOperation(Runnable flushOperation) {
+ this.flushOperation = flushOperation;
+ }
+
+ public long getChunkDuration() {
+ return chunkHeader.getDurationNanos();
+ }
+
+ public long getStartNanos() {
+ return chunkHeader.getStartNanos();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantLookup.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import jdk.jfr.internal.Type;
+
+final class ConstantLookup {
+ private final Type type;
+ private ConstantMap current;
+ private ConstantMap previous = ConstantMap.EMPTY;
+
+ ConstantLookup(ConstantMap current, Type type) {
+ this.current = current;
+ this.type = type;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public ConstantMap getLatestPool() {
+ return current;
+ }
+
+ public void newPool() {
+ previous = current;
+ current = new ConstantMap(current.factory, current.name);
+ }
+
+ public Object getPreviousResolved(long key) {
+ return previous.getResolved(key);
+ }
+
+ public Object getCurrentResolved(long key) {
+ return current.getResolved(key);
+ }
+
+ public Object getCurrent(long key) {
+ return current.get(key);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantMap.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,184 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import jdk.jfr.internal.LongMap;
+
+/**
+ * Holds mapping between a set of keys and their corresponding object.
+ *
+ * If the type is a known type, i.e. {@link RecordedThread}, an
+ * {@link ObjectFactory} can be supplied which will instantiate a typed object.
+ */
+final class ConstantMap {
+
+ private static final int RESOLUTION_FINISHED = 0;
+ private static final int RESOLUTION_STARTED = 1;
+ public static final ConstantMap EMPTY = new ConstantMap();
+
+ // A temporary placeholder, so objects can
+ // reference themselves (directly, or indirectly),
+ // when making a transition from numeric id references
+ // to normal Java references.
+ private final static class Reference {
+ private final long key;
+ private final ConstantMap pool;
+
+ Reference(ConstantMap pool, long key) {
+ this.pool = pool;
+ this.key = key;
+ }
+
+ Object resolve() {
+ return pool.get(key);
+ }
+
+ public String toString() {
+ return "ref: " + pool.name + "[" + key + "]";
+ }
+ }
+
+ final ObjectFactory<?> factory;
+ final String name;
+
+ private final LongMap<Object> objects;
+
+ private boolean resolving;
+ private boolean allResolved;
+
+ private ConstantMap() {
+ this(null, "<empty>");
+ allResolved = true;
+ }
+
+ ConstantMap(ObjectFactory<?> factory, String name) {
+ this.name = name;
+ this.objects = new LongMap<>(2);
+ this.factory = factory;
+ }
+
+ Object get(long id) {
+ // fast path, all objects in pool resolved
+ if (allResolved) {
+ return objects.get(id);
+ }
+ // referenced from a pool, deal with this later
+ if (!resolving) {
+ return new Reference(this, id);
+ }
+
+ // should always have a value
+ Object value = objects.get(id);
+ if (value == null) {
+ // unless is 0 which is used to represent null
+ if (id == 0) {
+ return null;
+ }
+ throw new InternalError("Missing object id=" + id + " in pool " + name + ". All ids should reference object");
+ }
+
+ // id is resolved (but not the whole pool)
+ if (objects.isSetId(id, RESOLUTION_FINISHED)) {
+ return value;
+ }
+
+ // resolving ourself, abort to avoid infinite recursion
+ if (objects.isSetId(id, RESOLUTION_STARTED)) {
+ return null;
+ }
+
+ // mark id as STARTED if we should
+ // come back during object resolution
+ objects.setId(id, RESOLUTION_STARTED);
+
+ // resolve object!
+ Object resolved = resolve(value);
+
+ // mark id as FINISHED
+ objects.setId(id, RESOLUTION_FINISHED);
+
+ // if a factory exists, convert to RecordedThread.
+ // RecordedClass etc. and store back results
+ if (factory != null) {
+ Object factorized = factory.createObject(id, resolved);
+ objects.put(id, factorized);
+ return factorized;
+ } else {
+ objects.put(id, resolved);
+ return resolved;
+ }
+ }
+
+ private static Object resolve(Object o) {
+ if (o instanceof Reference) {
+ return resolve(((Reference) o).resolve());
+ }
+ if (o != null && o.getClass().isArray()) {
+ final Object[] array = (Object[]) o;
+ for (int i = 0; i < array.length; i++) {
+ Object element = array[i];
+ array[i] = resolve(element);
+ }
+ return array;
+ }
+ return o;
+ }
+
+ public void resolve() {
+ objects.forEachKey(k -> get(k));
+ }
+
+ public void put(long key, Object value) {
+ objects.put(key, value);
+ }
+
+ public void setResolving() {
+ resolving = true;
+ allResolved = false;
+ }
+
+ public void setResolved() {
+ allResolved = true;
+ resolving = false;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Object getResolved(long id) {
+ return objects.get(id);
+ }
+
+ public void putResolved(long id, Object object) {
+ objects.put(id, object);
+ objects.setId(id, RESOLUTION_FINISHED);
+ }
+
+ public void setAllResolved(boolean allResolved) {
+ this.allResolved = allResolved;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/Dispatcher.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import jdk.jfr.EventType;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.LongMap;
+import jdk.jfr.internal.consumer.ChunkParser.ParserConfiguration;
+
+final class Dispatcher {
+
+ final static class EventDispatcher {
+ private final static EventDispatcher[] NO_DISPATCHERS = new EventDispatcher[0];
+
+ private final String eventName;
+ private final Consumer<RecordedEvent> action;
+
+ public EventDispatcher(String eventName, Consumer<RecordedEvent> action) {
+ this.eventName = eventName;
+ this.action = action;
+ }
+
+ private void offer(RecordedEvent event) {
+ action.accept(event);
+ }
+
+ private boolean accepts(EventType eventType) {
+ return (eventName == null || eventType.getName().equals(eventName));
+ }
+
+ public Consumer<RecordedEvent> getAction() {
+ return action;
+ }
+ }
+
+ private final Consumer<Throwable>[] errorActions;
+ private final Runnable[] flushActions;
+ private final Runnable[] closeActions;
+ private final EventDispatcher[] dispatchers;
+ private final LongMap<EventDispatcher[]> dispatcherLookup = new LongMap<>();
+ final ParserConfiguration parserConfiguration;
+ final Instant startTime;
+ final Instant endTime;
+ final long startNanos;
+ final long endNanos;
+
+ // Cache
+ private EventType cacheEventType;
+ private EventDispatcher[] cacheDispatchers;
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public Dispatcher(StreamConfiguration c) {
+ this.flushActions = c.flushActions.toArray(new Runnable[0]);
+ this.closeActions = c.closeActions.toArray(new Runnable[0]);
+ this.errorActions = c.errorActions.toArray(new Consumer[0]);
+ this.dispatchers = c.eventActions.toArray(new EventDispatcher[0]);
+ this.parserConfiguration = new ParserConfiguration(0, Long.MAX_VALUE, c.reuse, c.ordered, buildFilter(dispatchers));
+ this.startTime = c.startTime;
+ this.endTime = c.endTime;
+ this.startNanos = c.startNanos;
+ this.endNanos = c.endNanos;
+ }
+
+ public void runFlushActions() {
+ Runnable[] flushActions = this.flushActions;
+ for (int i = 0; i < flushActions.length; i++) {
+ try {
+ flushActions[i].run();
+ } catch (Exception e) {
+ handleError(e);
+ }
+ }
+ }
+
+ public void runCloseActions() {
+ Runnable[] closeActions = this.closeActions;
+ for (int i = 0; i < closeActions.length; i++) {
+ try {
+ closeActions[i].run();
+ } catch (Exception e) {
+ handleError(e);
+ }
+ }
+ }
+
+ private static ParserFilter buildFilter(EventDispatcher[] dispatchers) {
+ ParserFilter ef = new ParserFilter();
+ for (EventDispatcher ed : dispatchers) {
+ String name = ed.eventName;
+ if (name == null) {
+ return ParserFilter.ACCEPT_ALL;
+ }
+ ef.setThreshold(name, 0);
+ }
+ return ef;
+ }
+
+ void dispatch(RecordedEvent event) {
+ EventType type = event.getEventType();
+ EventDispatcher[] dispatchers = null;
+ if (type == cacheEventType) {
+ dispatchers = cacheDispatchers;
+ } else {
+ dispatchers = dispatcherLookup.get(type.getId());
+ if (dispatchers == null) {
+ List<EventDispatcher> list = new ArrayList<>();
+ for (EventDispatcher e : this.dispatchers) {
+ if (e.accepts(type)) {
+ list.add(e);
+ }
+ }
+ dispatchers = list.isEmpty() ? EventDispatcher.NO_DISPATCHERS : list.toArray(new EventDispatcher[0]);
+ dispatcherLookup.put(type.getId(), dispatchers);
+ }
+ cacheDispatchers = dispatchers;
+ }
+ // Expected behavior if exception occurs in onEvent:
+ //
+ // Synchronous:
+ // - User has added onError action:
+ // Catch exception, call onError and continue with next event
+ // Let Errors propagate to caller of EventStream::start
+ // - Default action
+ // Catch exception, e.printStackTrace() and continue with next event
+ // Let Errors propagate to caller of EventStream::start
+ //
+ // Asynchronous
+ // - User has added onError action
+ // Catch exception, call onError and continue with next event
+ // Let Errors propagate, shutdown thread and stream
+ // - Default action
+ // Catch exception, e.printStackTrace() and continue with next event
+ // Let Errors propagate and shutdown thread and stream
+ //
+ for (int i = 0; i < dispatchers.length; i++) {
+ try {
+ dispatchers[i].offer(event);
+ } catch (Exception e) {
+ handleError(e);
+ }
+ }
+ }
+
+ private void handleError(Throwable e) {
+ Consumer<?>[] consumers = this.errorActions;
+ if (consumers.length == 0) {
+ defaultErrorHandler(e);
+ return;
+ }
+ for (int i = 0; i < consumers.length; i++) {
+ @SuppressWarnings("unchecked")
+ Consumer<Throwable> consumer = (Consumer<Throwable>) consumers[i];
+ consumer.accept(e);
+ }
+ }
+
+ private void defaultErrorHandler(Throwable e) {
+ e.printStackTrace();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.AccessControlContext;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
+
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.JVM;
+import jdk.jfr.internal.Utils;
+import jdk.jfr.internal.consumer.ChunkParser.ParserConfiguration;
+
+/**
+ * Implementation of an {@code EventStream}} that operates against a directory
+ * with chunk files.
+ *
+ */
+public final class EventDirectoryStream extends AbstractEventStream {
+
+ private final static Comparator<? super RecordedEvent> EVENT_COMPARATOR = JdkJfrConsumer.instance().eventComparator();
+
+ private final RepositoryFiles repositoryFiles;
+ private final boolean active;
+ private final FileAccess fileAccess;
+
+ private ChunkParser currentParser;
+ private long currentChunkStartNanos;
+ private RecordedEvent[] sortedCache;
+
+ public EventDirectoryStream(AccessControlContext acc, Path p, FileAccess fileAccess, boolean active) throws IOException {
+ super(acc, active);
+ this.fileAccess = Objects.requireNonNull(fileAccess);
+ this.active = active;
+ this.repositoryFiles = new RepositoryFiles(fileAccess, p);
+ }
+
+ @Override
+ public void close() {
+ setClosed(true);
+ dispatcher().runCloseActions();
+ repositoryFiles.close();
+ }
+
+ @Override
+ public void start() {
+ start(Utils.timeToNanos(Instant.now()));
+ }
+
+ @Override
+ public void startAsync() {
+ startAsync(Utils.timeToNanos(Instant.now()));
+ }
+
+ @Override
+ protected void process() throws IOException {
+ try {
+ JVM.getJVM().exclude(Thread.currentThread());
+ processRecursionSafe();
+ } finally {
+ JVM.getJVM().include(Thread.currentThread());
+ }
+ }
+
+ protected void processRecursionSafe() throws IOException {
+ Dispatcher disp = dispatcher();
+
+ Path path;
+ boolean validStartTime = active || disp.startTime != null;
+ if (validStartTime) {
+ path = repositoryFiles.firstPath(disp.startNanos);
+ } else {
+ path = repositoryFiles.lastPath();
+ }
+ if (path == null) { // closed
+ return;
+ }
+ currentChunkStartNanos = repositoryFiles.getTimestamp(path);
+ try (RecordingInput input = new RecordingInput(path.toFile(), fileAccess)) {
+ currentParser = new ChunkParser(input, disp.parserConfiguration);
+ long segmentStart = currentParser.getStartNanos() + currentParser.getChunkDuration();
+ long filterStart = validStartTime ? disp.startNanos : segmentStart;
+ long filterEnd = disp.endTime != null ? disp.endNanos: Long.MAX_VALUE;
+
+ while (!isClosed()) {
+ boolean awaitnewEvent = false;
+ while (!isClosed() && !currentParser.isChunkFinished()) {
+ disp = dispatcher();
+ ParserConfiguration pc = disp.parserConfiguration;
+ pc.filterStart = filterStart;
+ pc.filterEnd = filterEnd;
+ currentParser.updateConfiguration(pc, true);
+ currentParser.setFlushOperation(getFlushOperation());
+ if (pc.isOrdered()) {
+ awaitnewEvent = processOrdered(disp, awaitnewEvent);
+ } else {
+ awaitnewEvent = processUnordered(disp, awaitnewEvent);
+ }
+ if (currentParser.getStartNanos() + currentParser.getChunkDuration() > filterEnd) {
+ close();
+ return;
+ }
+ }
+
+ if (isClosed()) {
+ return;
+ }
+ long durationNanos = currentParser.getChunkDuration();
+ path = repositoryFiles.nextPath(currentChunkStartNanos + durationNanos);
+ if (path == null) {
+ return; // stream closed
+ }
+ currentChunkStartNanos = repositoryFiles.getTimestamp(path);
+ input.setFile(path);
+ currentParser = currentParser.newChunkParser();
+ // TODO: Optimization. No need filter when we reach new chunk
+ // Could set start = 0;
+ }
+ }
+ }
+
+ private boolean processOrdered(Dispatcher c, boolean awaitNewEvents) throws IOException {
+ if (sortedCache == null) {
+ sortedCache = new RecordedEvent[100_000];
+ }
+ int index = 0;
+ while (true) {
+ RecordedEvent e = currentParser.readStreamingEvent(awaitNewEvents);
+ if (e == null) {
+ // wait for new event with next call to
+ // readStreamingEvent()
+ awaitNewEvents = true;
+ break;
+ }
+ awaitNewEvents = false;
+ if (index == sortedCache.length) {
+ sortedCache = Arrays.copyOf(sortedCache, sortedCache.length * 2);
+ }
+ sortedCache[index++] = e;
+ }
+
+ // no events found
+ if (index == 0 && currentParser.isChunkFinished()) {
+ return awaitNewEvents;
+ }
+ // at least 2 events, sort them
+ if (index > 1) {
+ Arrays.sort(sortedCache, 0, index, EVENT_COMPARATOR);
+ }
+ for (int i = 0; i < index; i++) {
+ c.dispatch(sortedCache[i]);
+ }
+ return awaitNewEvents;
+ }
+
+ private boolean processUnordered(Dispatcher c, boolean awaitNewEvents) throws IOException {
+ while (true) {
+ RecordedEvent e = currentParser.readStreamingEvent(awaitNewEvents);
+ if (e == null) {
+ return true;
+ } else {
+ c.dispatch(e);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.AccessControlContext;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
+
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.consumer.Dispatcher;
+import jdk.jfr.internal.consumer.FileAccess;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Implementation of an event stream that operates against a recording file.
+ *
+ */
+public final class EventFileStream extends AbstractEventStream {
+ private final static Comparator<? super RecordedEvent> EVENT_COMPARATOR = JdkJfrConsumer.instance().eventComparator();
+
+ private final RecordingInput input;
+
+ private ChunkParser currentParser;
+ private RecordedEvent[] cacheSorted;
+
+ public EventFileStream(AccessControlContext acc, Path path) throws IOException {
+ super(acc, false);
+ Objects.requireNonNull(path);
+ this.input = new RecordingInput(path.toFile(), FileAccess.UNPRIVILIGED);
+ }
+
+ @Override
+ public void start() {
+ start(0);
+ }
+
+ @Override
+ public void startAsync() {
+ startAsync(0);
+ }
+
+ @Override
+ public void close() {
+ setClosed(true);
+ dispatcher().runCloseActions();
+ try {
+ input.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ @Override
+ protected void process() throws IOException {
+ Dispatcher disp = dispatcher();
+ long start = 0;
+ long end = Long.MAX_VALUE;
+ if (disp.startTime != null) {
+ start = disp.startNanos;
+ }
+ if (disp.endTime != null) {
+ end = disp.endNanos;
+ }
+
+ currentParser = new ChunkParser(input, disp.parserConfiguration);
+ while (!isClosed()) {
+ if (currentParser.getStartNanos() > end) {
+ close();
+ return;
+ }
+ disp = dispatcher();
+ disp.parserConfiguration.filterStart = start;
+ disp.parserConfiguration.filterEnd = end;
+ currentParser.updateConfiguration(disp.parserConfiguration, true);
+ currentParser.setFlushOperation(getFlushOperation());
+ if (disp.parserConfiguration.isOrdered()) {
+ processOrdered(disp);
+ } else {
+ processUnordered(disp);
+ }
+ if (isClosed() || currentParser.isLastChunk()) {
+ return;
+ }
+ currentParser = currentParser.nextChunkParser();
+ }
+ }
+
+ private void processOrdered(Dispatcher c) throws IOException {
+ if (cacheSorted == null) {
+ cacheSorted = new RecordedEvent[10_000];
+ }
+ RecordedEvent event;
+ int index = 0;
+ while (true) {
+ event = currentParser.readEvent();
+ if (event == null) {
+ Arrays.sort(cacheSorted, 0, index, EVENT_COMPARATOR);
+ for (int i = 0; i < index; i++) {
+ c.dispatch(cacheSorted[i]);
+ }
+ return;
+ }
+ if (index == cacheSorted.length) {
+ RecordedEvent[] tmp = cacheSorted;
+ cacheSorted = new RecordedEvent[2 * tmp.length];
+ System.arraycopy(tmp, 0, cacheSorted, 0, tmp.length);
+ }
+ cacheSorted[index++] = event;
+ }
+ }
+
+ private void processUnordered(Dispatcher c) throws IOException {
+ while (!isClosed()) {
+ RecordedEvent event = currentParser.readEvent();
+ if (event == null) {
+ return;
+ }
+ c.dispatch(event);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventParser.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,198 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION;
+
+import java.io.IOException;
+import java.util.List;
+
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.consumer.Parser;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Parses an event and returns a {@link RecordedEvent}.
+ *
+ */
+final class EventParser extends Parser {
+
+ private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
+
+ private final Parser[] parsers;
+ private final EventType eventType;
+ private final TimeConverter timeConverter;
+ private final boolean hasDuration;
+ private final List<ValueDescriptor> valueDescriptors;
+ private final int startIndex;
+ private final int length;
+ private final RecordedEvent unorderedEvent;
+ private final ObjectContext objectContext;
+
+ private RecordedEvent[] cached;
+ private int cacheIndex;
+
+ private boolean enabled = true;
+ private boolean ordered;
+ private long filterStart;
+ private long filterEnd = Long.MAX_VALUE;
+ private long thresholdNanos = -1;
+
+ EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
+ this.timeConverter = timeConverter;
+ this.parsers = parsers;
+ this.eventType = type;
+ this.hasDuration = type.getField(FIELD_DURATION) != null;
+ this.startIndex = hasDuration ? 2 : 1;
+ this.length = parsers.length - startIndex;
+ this.valueDescriptors = type.getFields();
+ this.objectContext = new ObjectContext(type, valueDescriptors, timeConverter);
+ this.unorderedEvent = PRIVATE_ACCESS.newRecordedEvent(objectContext, new Object[length], 0L, 0L);
+ }
+
+ private RecordedEvent cachedEvent() {
+ if (ordered) {
+ if (cacheIndex == cached.length) {
+ RecordedEvent[] old = cached;
+ cached = new RecordedEvent[cached.length * 2];
+ System.arraycopy(old, 0, cached, 0, old.length);
+ }
+ RecordedEvent event = cached[cacheIndex];
+ if (event == null) {
+ event = PRIVATE_ACCESS.newRecordedEvent(objectContext, new Object[length], 0L, 0L);
+ cached[cacheIndex] = event;
+ }
+ cacheIndex++;
+ return event;
+ } else {
+ return unorderedEvent;
+ }
+ }
+
+ public EventType getEventType() {
+ return eventType;
+ }
+
+ public void setThresholdNanos(long thresholdNanos) {
+ this.thresholdNanos = thresholdNanos;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public RecordedEvent parse(RecordingInput input) throws IOException {
+ if (!enabled) {
+ return null;
+ }
+
+ long startTicks = input.readLong();
+ long endTicks = startTicks;
+ if (hasDuration) {
+ long durationTicks = input.readLong();
+ if (thresholdNanos > 0L) {
+ if (timeConverter.convertTimespan(durationTicks) < thresholdNanos) {
+ return null;
+ }
+ }
+ endTicks += durationTicks;
+ }
+ if (filterStart != 0L || filterEnd != Long.MAX_VALUE) {
+ long eventEnd = timeConverter.convertTimestamp(endTicks);
+ if (eventEnd < filterStart) {
+ return null;
+ }
+ if (eventEnd > filterEnd) {
+ return null;
+ }
+ }
+
+ if (cached != null) {
+ RecordedEvent event = cachedEvent();
+ JdkJfrConsumer access = PRIVATE_ACCESS;
+ access.setStartTicks(event, startTicks);
+ access.setEndTicks(event, endTicks);
+ Object[] values = access.eventValues(event);
+ for (int i = 0; i < values.length; i++) {
+ values[i] = parsers[startIndex + i].parse(input);
+ }
+ return event;
+ }
+
+ Object[] values = new Object[length];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = parsers[startIndex + i].parse(input);
+ }
+ return PRIVATE_ACCESS.newRecordedEvent(objectContext, values, startTicks, endTicks);
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ throw new InternalError("Should not call this method. More efficent to read event size and skip ahead");
+ }
+
+ public void resetCache() {
+ cacheIndex = 0;
+ }
+
+ private boolean hasReuse() {
+ return cached != null;
+ }
+
+ public void setReuse(boolean reuse) {
+ if (reuse == hasReuse()) {
+ return;
+ }
+ if (reuse) {
+ cached = new RecordedEvent[2];
+ cacheIndex = 0;
+ } else {
+ cached = null;
+ }
+ }
+
+ public void setFilterStart(long filterStart) {
+ this.filterStart = filterStart;
+ }
+
+ public void setFilterEnd(long filterEnd) {
+ this.filterEnd = filterEnd;
+ }
+
+ public void setOrdered(boolean ordered) {
+ if (this.ordered == ordered) {
+ return;
+ }
+ this.ordered = ordered;
+ this.cacheIndex = 0;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/FileAccess.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+// Protected by modular boundaries.
+public abstract class FileAccess {
+ public final static FileAccess UNPRIVILIGED = new UnPriviliged();
+
+ public abstract RandomAccessFile openRAF(File f, String mode) throws IOException;
+
+ public abstract DirectoryStream<Path> newDirectoryStream(Path repository) throws IOException;
+
+ public abstract String getAbsolutePath(File f) throws IOException;
+
+ public abstract long length(File f) throws IOException;
+
+ public abstract long fileSize(Path p) throws IOException;
+
+ private static class UnPriviliged extends FileAccess {
+ @Override
+ public RandomAccessFile openRAF(File f, String mode) throws IOException {
+ return new RandomAccessFile(f, mode);
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(Path dir) throws IOException {
+ return Files.newDirectoryStream(dir);
+ }
+
+ @Override
+ public String getAbsolutePath(File f) throws IOException {
+ return f.getAbsolutePath();
+ }
+
+ @Override
+ public long length(File f) throws IOException {
+ return f.length();
+ }
+
+ @Override
+ public long fileSize(Path p) throws IOException {
+ return Files.size(p);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/JdkJfrConsumer.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.List;
+
+import jdk.jfr.consumer.RecordedClass;
+import jdk.jfr.consumer.RecordedClassLoader;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordedFrame;
+import jdk.jfr.consumer.RecordedMethod;
+import jdk.jfr.consumer.RecordedObject;
+import jdk.jfr.consumer.RecordedStackTrace;
+import jdk.jfr.consumer.RecordedThread;
+import jdk.jfr.consumer.RecordedThreadGroup;
+import jdk.jfr.consumer.RecordingFile;
+import jdk.jfr.internal.Type;
+/*
+ * Purpose of this class is to give package private access to
+ * the jdk.jfr.consumer package
+ */
+public abstract class JdkJfrConsumer {
+
+ private static JdkJfrConsumer instance;
+
+ // Initialization will trigger setAccess being called
+ private static void forceInitializetion() {
+ try {
+ Class<?> c = RecordedObject.class;
+ Class.forName(c.getName(), true, c.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw new InternalError("Should not happen");
+ }
+ }
+
+ public static void setAccess(JdkJfrConsumer access) {
+ instance = access;
+ }
+
+ public static JdkJfrConsumer instance() {
+ if (instance == null) {
+ forceInitializetion();
+ }
+ return instance;
+ }
+
+ public abstract List<Type> readTypes(RecordingFile file) throws IOException;
+
+ public abstract boolean isLastEventInChunk(RecordingFile file);
+
+ public abstract Object getOffsetDataTime(RecordedObject event, String name);
+
+ public abstract RecordedClass newRecordedClass(ObjectContext objectContext, long id, Object[] values);
+
+ public abstract RecordedClassLoader newRecordedClassLoader(ObjectContext objectContext, long id, Object[] values);
+
+ public abstract RecordedStackTrace newRecordedStackTrace(ObjectContext objectContext, Object[] values);
+
+ public abstract RecordedThreadGroup newRecordedThreadGroup(ObjectContext objectContext, Object[] values);
+
+ public abstract RecordedFrame newRecordedFrame(ObjectContext objectContext, Object[] values);
+
+ public abstract RecordedThread newRecordedThread(ObjectContext objectContext, long id, Object[] values);
+
+ public abstract RecordedMethod newRecordedMethod(ObjectContext objectContext, Object[] values);
+
+ public abstract RecordedEvent newRecordedEvent(ObjectContext objectContext, Object[] objects, long l, long m);
+
+ public abstract Comparator<? super RecordedEvent> eventComparator();
+
+ public abstract void setStartTicks(RecordedEvent event, long startTicks);
+
+ public abstract void setEndTicks(RecordedEvent event, long endTicks);
+
+ public abstract Object[] eventValues(RecordedEvent event);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ObjectContext.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.time.ZoneId;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+
+public final class ObjectContext {
+ private final Map<ValueDescriptor, ObjectContext> contextLookup;
+ private final TimeConverter timeConverter;
+
+ public final EventType eventType;
+ public final List<ValueDescriptor> fields;
+
+ ObjectContext(EventType eventType, List<ValueDescriptor> fields, TimeConverter timeConverter) {
+ this.contextLookup = new HashMap<>();
+ this.eventType = eventType;
+ this.fields = fields;
+ this.timeConverter = timeConverter;
+ }
+
+ private ObjectContext(ObjectContext parent, ValueDescriptor descriptor) {
+ this.eventType = parent.eventType;
+ this.contextLookup = parent.contextLookup;
+ this.timeConverter = parent.timeConverter;
+ this.fields = descriptor.getFields();
+ }
+
+ public ObjectContext getInstance(ValueDescriptor descriptor) {
+ ObjectContext context = contextLookup.get(descriptor);
+ if (context == null) {
+ context = new ObjectContext(this, descriptor);
+ contextLookup.put(descriptor, context);
+ }
+ return context;
+ }
+
+ public long convertTimestamp(long ticks) {
+ return timeConverter.convertTimestamp(ticks);
+ }
+
+ public long convertTimespan(long ticks) {
+ return timeConverter.convertTimespan(ticks);
+ }
+
+ public ZoneId getZoneOffset() {
+ return timeConverter.getZoneOffset();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ObjectFactory.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,153 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import jdk.jfr.consumer.RecordedClass;
+import jdk.jfr.consumer.RecordedClassLoader;
+import jdk.jfr.consumer.RecordedFrame;
+import jdk.jfr.consumer.RecordedMethod;
+import jdk.jfr.consumer.RecordedStackTrace;
+import jdk.jfr.consumer.RecordedThread;
+import jdk.jfr.consumer.RecordedThreadGroup;
+import jdk.jfr.internal.Type;
+
+/**
+ * Abstract factory for creating specialized types
+ */
+public abstract class ObjectFactory<T> {
+ private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
+
+ private final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types.";
+ private final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX;
+ public final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame";
+ public final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame";
+
+ static ObjectFactory<?> create(Type type, TimeConverter timeConverter) {
+ switch (type.getName()) {
+ case "java.lang.Thread":
+ return createThreadFactory(type, timeConverter);
+ case TYPE_PREFIX_VERSION_1 + "StackFrame":
+ case TYPE_PREFIX_VERSION_2 + "StackFrame":
+ return createFrameFactory(type, timeConverter);
+ case TYPE_PREFIX_VERSION_1 + "Method":
+ case TYPE_PREFIX_VERSION_2 + "Method":
+ return createMethodFactory(type, timeConverter);
+ case TYPE_PREFIX_VERSION_1 + "ThreadGroup":
+ case TYPE_PREFIX_VERSION_2 + "ThreadGroup":
+ return createdThreadGroupFactory(type, timeConverter);
+ case TYPE_PREFIX_VERSION_1 + "StackTrace":
+ case TYPE_PREFIX_VERSION_2 + "StackTrace":
+ return createStackTraceFactory(type, timeConverter);
+ case TYPE_PREFIX_VERSION_1 + "ClassLoader":
+ case TYPE_PREFIX_VERSION_2 + "ClassLoader":
+ return createClassLoaderFactory(type, timeConverter);
+ case "java.lang.Class":
+ return createClassFactory(type, timeConverter);
+ }
+ return null;
+ }
+
+ private static ObjectFactory<RecordedClass> createClassFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory<RecordedClass>(type, timeConverter) {
+ @Override
+ RecordedClass createTyped(ObjectContext objectContext, long id, Object[] values) {
+ return PRIVATE_ACCESS.newRecordedClass(objectContext, id, values);
+ }
+ };
+ }
+
+ private static ObjectFactory<?> createClassLoaderFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory<RecordedClassLoader>(type, timeConverter) {
+ @Override
+ RecordedClassLoader createTyped(ObjectContext objectContext, long id, Object[] values) {
+ return PRIVATE_ACCESS.newRecordedClassLoader(objectContext, id, values);
+ }
+ };
+ }
+
+ private static ObjectFactory<RecordedStackTrace> createStackTraceFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory<RecordedStackTrace>(type, timeConverter) {
+ @Override
+ RecordedStackTrace createTyped(ObjectContext objectContext, long id, Object[] values) {
+ return PRIVATE_ACCESS.newRecordedStackTrace(objectContext, values);
+ }
+ };
+ }
+
+ private static ObjectFactory<RecordedThreadGroup> createdThreadGroupFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory<RecordedThreadGroup>(type, timeConverter) {
+ @Override
+ RecordedThreadGroup createTyped(ObjectContext objectContext, long id, Object[] values) {
+ return PRIVATE_ACCESS.newRecordedThreadGroup(objectContext, values);
+ }
+ };
+ }
+
+ private static ObjectFactory<RecordedMethod> createMethodFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory<RecordedMethod>(type, timeConverter) {
+ @Override
+ RecordedMethod createTyped(ObjectContext objectContext, long id, Object[] values) {
+ return PRIVATE_ACCESS.newRecordedMethod(objectContext, values);
+ }
+ };
+ }
+
+ private static ObjectFactory<RecordedFrame> createFrameFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory<RecordedFrame>(type, timeConverter) {
+ @Override
+ RecordedFrame createTyped(ObjectContext objectContext, long id, Object[] values) {
+ return PRIVATE_ACCESS.newRecordedFrame(objectContext, values);
+ }
+ };
+ }
+
+ private static ObjectFactory<RecordedThread> createThreadFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory<RecordedThread>(type, timeConverter) {
+ @Override
+ RecordedThread createTyped(ObjectContext objectContext, long id, Object[] values) {
+ return PRIVATE_ACCESS.newRecordedThread(objectContext, id, values);
+ }
+ };
+ }
+
+ private final ObjectContext objectContext;
+
+ private ObjectFactory(Type type, TimeConverter timeConverter) {
+ this.objectContext = new ObjectContext(null, type.getFields(), timeConverter);
+ }
+
+ T createObject(long id, Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Object[]) {
+ return createTyped(objectContext, id, (Object[]) value);
+ }
+ throw new InternalError("Object factory must have struct type. Type was " + value.getClass().getName());
+ }
+
+ abstract T createTyped(ObjectContext objectContextm, long id, Object[] values);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/Parser.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,52 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+
+/**
+ * Base class for parsing data from a {@link RecordingInput}.
+ */
+abstract class Parser {
+ /**
+ * Parses data from a {@link RecordingInput} and return an object.
+ *
+ * @param input input to read from
+ * @return an object
+ * @throws IOException if operation couldn't be completed due to I/O
+ * problems
+ */
+ public abstract Object parse(RecordingInput input) throws IOException;
+
+ /**
+ * Skips data that would usually be by parsed the {@code #parse(RecordingInput)} method.
+ *
+ * @param input input to read from
+ * @throws IOException if operation couldn't be completed due to I/O
+ * problems
+ */
+ public abstract void skip(RecordingInput input) throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,382 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.LongMap;
+import jdk.jfr.internal.MetadataDescriptor;
+import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.Parser;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Class that create parsers suitable for reading events and constant pools
+ */
+final class ParserFactory {
+ private final LongMap<Parser> parsers = new LongMap<>();
+ private final TimeConverter timeConverter;
+ private final LongMap<Type> types = new LongMap<>();
+ private final LongMap<ConstantLookup> constantLookups;
+
+ public ParserFactory(MetadataDescriptor metadata, LongMap<ConstantLookup> constantLookups, TimeConverter timeConverter) throws IOException {
+ this.constantLookups = constantLookups;
+ this.timeConverter = timeConverter;
+ for (Type t : metadata.getTypes()) {
+ types.put(t.getId(), t);
+ }
+ // Add to separate list
+ // so createCompositeParser can throw
+ // IOException outside lambda
+ List<Type> typeList = new ArrayList<>();
+ types.forEach(typeList::add);
+ for (Type t : typeList) {
+ if (!t.getFields().isEmpty()) { // Avoid primitives
+ CompositeParser cp = createCompositeParser(t, false);
+ if (t.isSimpleType()) { // Reduce to nested parser
+ parsers.put(t.getId(), cp.parsers[0]);
+ }
+ }
+ }
+ // Override event types with event parsers
+ for (EventType t : metadata.getEventTypes()) {
+ parsers.put(t.getId(), createEventParser(t));
+ }
+ }
+
+ public LongMap<Parser> getParsers() {
+ return parsers;
+ }
+
+ public LongMap<Type> getTypeMap() {
+ return types;
+ }
+
+ private EventParser createEventParser(EventType eventType) throws IOException {
+ List<Parser> parsers = new ArrayList<Parser>();
+ for (ValueDescriptor f : eventType.getFields()) {
+ parsers.add(createParser(f, true));
+ }
+ return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0]));
+ }
+
+ private Parser createParser(ValueDescriptor v, boolean event) throws IOException {
+ boolean constantPool = PrivateAccess.getInstance().isConstantPool(v);
+ if (v.isArray()) {
+ Type valueType = PrivateAccess.getInstance().getType(v);
+ ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null);
+ return new ArrayParser(createParser(element, event));
+ }
+ long id = v.getTypeId();
+ Type type = types.get(id);
+ if (type == null) {
+ throw new IOException("Type '" + v.getTypeName() + "' is not defined");
+ }
+ if (constantPool) {
+ ConstantLookup lookup = constantLookups.get(id);
+ if (lookup == null) {
+ ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+ lookup = new ConstantLookup(pool, type);
+ constantLookups.put(id, lookup);
+ }
+ if (event) {
+ return new EventValueConstantParser(lookup);
+ }
+ return new ConstantValueParser(lookup);
+ }
+ Parser parser = parsers.get(id);
+ if (parser == null) {
+ if (!v.getFields().isEmpty()) {
+ return createCompositeParser(type, event);
+ } else {
+ return registerParserType(type, createPrimitiveParser(type, constantPool));
+ }
+ }
+ return parser;
+ }
+
+ private Parser createPrimitiveParser(Type type, boolean event) throws IOException {
+ switch (type.getName()) {
+ case "int":
+ return new IntegerParser();
+ case "long":
+ return new LongParser();
+ case "float":
+ return new FloatParser();
+ case "double":
+ return new DoubleParser();
+ case "char":
+ return new CharacterParser();
+ case "boolean":
+ return new BooleanParser();
+ case "short":
+ return new ShortParser();
+ case "byte":
+ return new ByteParser();
+ case "java.lang.String":
+ ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+ ConstantLookup lookup = new ConstantLookup(pool, type);
+ constantLookups.put(type.getId(), lookup);
+ return new StringParser(lookup, event);
+ default:
+ throw new IOException("Unknown primitive type " + type.getName());
+ }
+ }
+
+ private Parser registerParserType(Type t, Parser parser) {
+ Parser p = parsers.get(t.getId());
+ // check if parser exists (known type)
+ if (p != null) {
+ return p;
+ }
+ parsers.put(t.getId(), parser);
+ return parser;
+ }
+
+ private CompositeParser createCompositeParser(Type type, boolean event) throws IOException {
+ List<ValueDescriptor> vds = type.getFields();
+ Parser[] parsers = new Parser[vds.size()];
+ CompositeParser composite = new CompositeParser(parsers);
+ // need to pre-register so recursive types can be handled
+ registerParserType(type, composite);
+
+ int index = 0;
+ for (ValueDescriptor vd : vds) {
+ parsers[index++] = createParser(vd, event);
+ }
+ return composite;
+ }
+
+ private static final class BooleanParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(1);
+ }
+ }
+
+ private static final class ByteParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Byte.valueOf(input.readByte());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(1);
+ }
+ }
+
+ private static final class LongParser extends Parser {
+ private Object lastLongObject = Long.valueOf(0);
+ private long last = 0;
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ long l = input.readLong();
+ if (l == last) {
+ return lastLongObject;
+ }
+ last = l;
+ lastLongObject = Long.valueOf(l);
+ return lastLongObject;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readLong();
+ }
+ }
+
+ private static final class IntegerParser extends Parser {
+ private Integer lastIntegergObject = Integer.valueOf(0);
+ private int last = 0;
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ int i = input.readInt();
+ if (i != last) {
+ last = i;
+ lastIntegergObject = Integer.valueOf(i);
+ }
+ return lastIntegergObject;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readInt();
+ }
+ }
+
+ private static final class ShortParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Short.valueOf(input.readShort());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readShort();
+ }
+ }
+
+ private static final class CharacterParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Character.valueOf(input.readChar());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readChar();
+ }
+ }
+
+ private static final class FloatParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Float.valueOf(input.readFloat());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(Float.SIZE);
+ }
+ }
+
+ private static final class DoubleParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Double.valueOf(input.readDouble());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(Double.SIZE);
+ }
+ }
+
+ private final static class ArrayParser extends Parser {
+ private final Parser elementParser;
+
+ public ArrayParser(Parser elementParser) {
+ this.elementParser = elementParser;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ final int size = input.readInt();
+ final Object[] array = new Object[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = elementParser.parse(input);
+ }
+ return array;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ final int size = input.readInt();
+ for (int i = 0; i < size; i++) {
+ elementParser.skip(input);
+ }
+ }
+ }
+
+ private final static class CompositeParser extends Parser {
+ private final Parser[] parsers;
+
+ public CompositeParser(Parser[] valueParsers) {
+ this.parsers = valueParsers;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ final Object[] values = new Object[parsers.length];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = parsers[i].parse(input);
+ }
+ return values;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ for (int i = 0; i < parsers.length; i++) {
+ parsers[i].skip(input);
+ }
+ }
+ }
+
+ private static final class EventValueConstantParser extends Parser {
+ private final ConstantLookup lookup;
+ private Object lastValue = 0;
+ private long lastKey = -1;
+ EventValueConstantParser(ConstantLookup lookup) {
+ this.lookup = lookup;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ long key = input.readLong();
+ if (key == lastKey) {
+ return lastValue;
+ }
+ lastKey = key;
+ lastValue = lookup.getCurrentResolved(key);
+ return lastValue;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readLong();
+ }
+ }
+
+ private static final class ConstantValueParser extends Parser {
+ private final ConstantLookup lookup;
+ ConstantValueParser(ConstantLookup lookup) {
+ this.lookup = lookup;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return lookup.getCurrent(input.readLong());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readLong();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFilter.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
+
+final class ParserFilter {
+ public static final ParserFilter ACCEPT_ALL = new ParserFilter(true, Map.of());
+
+ private final Map<String, Long> thresholds;
+ private final boolean acceptAll;
+
+ public ParserFilter() {
+ this(false, new HashMap<>());
+ }
+
+ private ParserFilter(boolean acceptAll, Map<String, Long> thresholds) {
+ this.acceptAll = acceptAll;
+ this.thresholds = thresholds;
+ }
+
+ public void setThreshold(String eventName, long nanos) {
+ Long l = thresholds.get(eventName);
+ if (l != null) {
+ l = Math.min(l, nanos);
+ } else {
+ l = nanos;
+ }
+ thresholds.put(eventName, l);
+ }
+
+ public long getThreshold(String eventName) {
+ if (acceptAll) {
+ return 0L;
+ }
+ Long l = thresholds.get(eventName);
+ if (l != null) {
+ return l;
+ }
+ return -1;
+ }
+
+ public String toString() {
+ if (acceptAll) {
+ return "ACCEPT ALL";
+ }
+
+ StringJoiner sb = new StringJoiner(", ");
+ for (String key : thresholds.keySet().toArray(new String[0])) {
+ Long value = thresholds.get(key);
+ sb.add(key + " = " + value);
+ }
+ return sb.toString();
+ }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,61 +30,82 @@
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
-import java.nio.charset.Charset;
+import java.nio.file.Path;
public final class RecordingInput implements DataInput, AutoCloseable {
- public static final byte STRING_ENCODING_NULL = 0;
- public static final byte STRING_ENCODING_EMPTY_STRING = 1;
- public static final byte STRING_ENCODING_CONSTANT_POOL = 2;
- public static final byte STRING_ENCODING_UTF8_BYTE_ARRAY = 3;
- public static final byte STRING_ENCODING_CHAR_ARRAY = 4;
- public static final byte STRING_ENCODING_LATIN1_BYTE_ARRAY = 5;
-
- private final static int DEFAULT_BLOCK_SIZE = 16 * 1024 * 1024;
- private final static Charset UTF8 = Charset.forName("UTF-8");
- private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
+ private final static int DEFAULT_BLOCK_SIZE = 64_000;
private static final class Block {
private byte[] bytes = new byte[0];
private long blockPosition;
+ private long blockPositionEnd;
boolean contains(long position) {
- return position >= blockPosition && position < blockPosition + bytes.length;
+ return position >= blockPosition && position < blockPositionEnd;
}
public void read(RandomAccessFile file, int amount) throws IOException {
blockPosition = file.getFilePointer();
// reuse byte array, if possible
- if (amount != bytes.length) {
+ if (amount > bytes.length) {
bytes = new byte[amount];
}
- file.readFully(bytes);
+ this.blockPositionEnd = blockPosition + amount;
+ file.readFully(bytes, 0, amount);
}
public byte get(long position) {
return bytes[(int) (position - blockPosition)];
}
+
+ public void reset() {
+ blockPosition = 0;
+ blockPositionEnd = 0;
+ }
}
-
- private final RandomAccessFile file;
- private final long size;
+ private final int blockSize;
+ private final FileAccess fileAccess;
+ private RandomAccessFile file;
+ private String filename;
private Block currentBlock = new Block();
private Block previousBlock = new Block();
private long position;
- private final int blockSize;
+ private long size = -1; // Fail fast if setSize(...) has not been called
+ // before parsing
+
+ RecordingInput(File f, FileAccess fileAccess, int blockSize) throws IOException {
+ this.blockSize = blockSize;
+ this.fileAccess = fileAccess;
+ initialize(f);
+ }
- private RecordingInput(File f, int blockSize) throws IOException {
- this.size = f.length();
- this.blockSize = blockSize;
- this.file = new RandomAccessFile(f, "r");
- if (size < 8) {
- throw new IOException("Not a valid Flight Recorder file. File length is only " + size + " bytes.");
+ private void initialize(File f) throws IOException {
+ this.filename = fileAccess.getAbsolutePath(f);
+ this.file = fileAccess.openRAF(f, "r");
+ this.position = 0;
+ this.size = -1;
+ this.currentBlock.reset();
+ previousBlock.reset();
+ if (fileAccess.length(f) < 8) {
+ throw new IOException("Not a valid Flight Recorder file. File length is only " + f.length() + " bytes.");
}
}
- public RecordingInput(File f) throws IOException {
- this(f, DEFAULT_BLOCK_SIZE);
+ public RecordingInput(File f, FileAccess fileAccess) throws IOException {
+ this(f, fileAccess, DEFAULT_BLOCK_SIZE);
+ }
+
+ void positionPhysical(long position) throws IOException {
+ file.seek(position);
+ }
+
+ byte readPhysicalByte() throws IOException {
+ return file.readByte();
+ }
+
+ long readPhysicalLong() throws IOException {
+ return file.readLong();
}
@Override
@@ -109,7 +130,7 @@
readFully(dst, 0, dst.length);
}
- public final short readRawShort() throws IOException {
+ short readRawShort() throws IOException {
// copied from java.io.Bits
byte b0 = readByte();
byte b1 = readByte();
@@ -117,18 +138,18 @@
}
@Override
- public final double readDouble() throws IOException {
+ public double readDouble() throws IOException {
// copied from java.io.Bits
return Double.longBitsToDouble(readRawLong());
}
@Override
- public final float readFloat() throws IOException {
+ public float readFloat() throws IOException {
// copied from java.io.Bits
return Float.intBitsToFloat(readRawInt());
}
- public final int readRawInt() throws IOException {
+ int readRawInt() throws IOException {
// copied from java.io.Bits
byte b0 = readByte();
byte b1 = readByte();
@@ -137,7 +158,7 @@
return ((b3 & 0xFF)) + ((b2 & 0xFF) << 8) + ((b1 & 0xFF) << 16) + ((b0) << 24);
}
- public final long readRawLong() throws IOException {
+ long readRawLong() throws IOException {
// copied from java.io.Bits
byte b0 = readByte();
byte b1 = readByte();
@@ -150,20 +171,20 @@
return ((b7 & 0xFFL)) + ((b6 & 0xFFL) << 8) + ((b5 & 0xFFL) << 16) + ((b4 & 0xFFL) << 24) + ((b3 & 0xFFL) << 32) + ((b2 & 0xFFL) << 40) + ((b1 & 0xFFL) << 48) + (((long) b0) << 56);
}
- public final long position() throws IOException {
+ public final long position() {
return position;
}
public final void position(long newPosition) throws IOException {
if (!currentBlock.contains(newPosition)) {
if (!previousBlock.contains(newPosition)) {
- if (newPosition > size()) {
- throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size() + " bytes.");
+ if (newPosition > size) {
+ throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size + " bytes.");
}
long blockStart = trimToFileSize(calculateBlockStart(newPosition));
file.seek(blockStart);
// trim amount to file size
- long amount = Math.min(size() - blockStart, blockSize);
+ long amount = Math.min(size - blockStart, blockSize);
previousBlock.read(file, (int) amount);
}
// swap previous and current
@@ -191,11 +212,12 @@
return newPosition - blockSize / 2;
}
- public final long size() throws IOException {
+ long size() {
return size;
}
- public final void close() throws IOException {
+ @Override
+ public void close() throws IOException {
file.close();
}
@@ -245,34 +267,7 @@
// 4, means ""
@Override
public String readUTF() throws IOException {
- return readEncodedString(readByte());
- }
-
- public String readEncodedString(byte encoding) throws IOException {
- if (encoding == STRING_ENCODING_NULL) {
- return null;
- }
- if (encoding == STRING_ENCODING_EMPTY_STRING) {
- return "";
- }
- int size = readInt();
- if (encoding == STRING_ENCODING_CHAR_ARRAY) {
- char[] c = new char[size];
- for (int i = 0; i < size; i++) {
- c[i] = readChar();
- }
- return new String(c);
- }
- byte[] bytes = new byte[size];
- readFully(bytes); // TODO: optimize, check size, and copy only if needed
- if (encoding == STRING_ENCODING_UTF8_BYTE_ARRAY) {
- return new String(bytes, UTF8);
- }
-
- if (encoding == STRING_ENCODING_LATIN1_BYTE_ARRAY) {
- return new String(bytes, LATIN1);
- }
- throw new IOException("Unknown string encoding " + encoding);
+ throw new UnsupportedOperationException("Use StringParser");
}
@Override
@@ -292,48 +287,152 @@
@Override
public long readLong() throws IOException {
- // can be optimized by branching checks, but will do for now
+ final byte[] bytes = currentBlock.bytes;
+ final int index = (int) (position - currentBlock.blockPosition);
+
+ if (index + 8 < bytes.length && index >= 0) {
+ byte b0 = bytes[index];
+ long ret = (b0 & 0x7FL);
+ if (b0 >= 0) {
+ position += 1;
+ return ret;
+ }
+ int b1 = bytes[index + 1];
+ ret += (b1 & 0x7FL) << 7;
+ if (b1 >= 0) {
+ position += 2;
+ return ret;
+ }
+ int b2 = bytes[index + 2];
+ ret += (b2 & 0x7FL) << 14;
+ if (b2 >= 0) {
+ position += 3;
+ return ret;
+ }
+ int b3 = bytes[index + 3];
+ ret += (b3 & 0x7FL) << 21;
+ if (b3 >= 0) {
+ position += 4;
+ return ret;
+ }
+ int b4 = bytes[index + 4];
+ ret += (b4 & 0x7FL) << 28;
+ if (b4 >= 0) {
+ position += 5;
+ return ret;
+ }
+ int b5 = bytes[index + 5];
+ ret += (b5 & 0x7FL) << 35;
+ if (b5 >= 0) {
+ position += 6;
+ return ret;
+ }
+ int b6 = bytes[index + 6];
+ ret += (b6 & 0x7FL) << 42;
+ if (b6 >= 0) {
+ position += 7;
+ return ret;
+ }
+ int b7 = bytes[index + 7];
+ ret += (b7 & 0x7FL) << 49;
+ if (b7 >= 0) {
+ position += 8;
+ return ret;
+ }
+ int b8 = bytes[index + 8];// read last byte raw
+ position += 9;
+ return ret + (((long) (b8 & 0XFF)) << 56);
+ } else {
+ return readLongSlow();
+ }
+ }
+
+ private long readLongSlow() throws IOException {
byte b0 = readByte();
long ret = (b0 & 0x7FL);
if (b0 >= 0) {
return ret;
}
+
int b1 = readByte();
ret += (b1 & 0x7FL) << 7;
if (b1 >= 0) {
return ret;
}
+
int b2 = readByte();
ret += (b2 & 0x7FL) << 14;
if (b2 >= 0) {
return ret;
}
+
int b3 = readByte();
ret += (b3 & 0x7FL) << 21;
if (b3 >= 0) {
return ret;
}
+
int b4 = readByte();
ret += (b4 & 0x7FL) << 28;
if (b4 >= 0) {
return ret;
}
+
int b5 = readByte();
ret += (b5 & 0x7FL) << 35;
if (b5 >= 0) {
return ret;
}
+
int b6 = readByte();
ret += (b6 & 0x7FL) << 42;
if (b6 >= 0) {
return ret;
}
+
int b7 = readByte();
ret += (b7 & 0x7FL) << 49;
if (b7 >= 0) {
return ret;
+
}
+
int b8 = readByte(); // read last byte raw
return ret + (((long) (b8 & 0XFF)) << 56);
}
+
+ public void setValidSize(long size) {
+ if (size > this.size) {
+ this.size = size;
+ }
+ }
+
+ public long getFileSize() throws IOException {
+ return file.length();
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ // Purpose of this method is to reuse block cache from a
+ // previous RecordingInput
+ public void setFile(Path path) throws IOException {
+ try {
+ file.close();
+ } catch (IOException e) {
+ // perhaps deleted
+ }
+ file = null;
+ initialize(path.toFile());
+ }
+/*
+
+
+
+
+
+ *
+ *
+ */
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInternals.java Wed Oct 09 12:21:28 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package jdk.jfr.internal.consumer;
-
-import java.io.IOException;
-import java.util.List;
-
-import jdk.jfr.consumer.RecordedEvent;
-import jdk.jfr.consumer.RecordedObject;
-import jdk.jfr.consumer.RecordingFile;
-import jdk.jfr.internal.Type;
-
-public abstract class RecordingInternals {
-
- public static RecordingInternals INSTANCE;
-
- public abstract boolean isLastEventInChunk(RecordingFile file);
-
- public abstract Object getOffsetDataTime(RecordedObject event, String name);
-
- public abstract List<Type> readTypes(RecordingFile file) throws IOException;
-
- public abstract void sort(List<RecordedEvent> events);
-
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.Repository;
+import jdk.jfr.internal.SecuritySupport.SafePath;
+
+public final class RepositoryFiles {
+ private static final Object WAIT_OBJECT = new Object();
+ public static void notifyNewFile() {
+ synchronized (WAIT_OBJECT) {
+ WAIT_OBJECT.notifyAll();
+ }
+ }
+
+ private final FileAccess fileAccess;
+ private final NavigableMap<Long, Path> pathSet = new TreeMap<>();
+ private final Map<Path, Long> pathLookup = new HashMap<>();
+ private final Path repository;
+ private final Object waitObject;
+
+ private volatile boolean closed;
+
+ RepositoryFiles(FileAccess fileAccess, Path repository) {
+ this.repository = repository;
+ this.fileAccess = fileAccess;
+ this.waitObject = repository == null ? WAIT_OBJECT : new Object();
+ }
+
+ long getTimestamp(Path p) {
+ return pathLookup.get(p);
+ }
+
+ Path lastPath() {
+ if (waitForPaths()) {
+ return pathSet.lastEntry().getValue();
+ }
+ return null; // closed
+ }
+
+ Path firstPath(long startTimeNanos) {
+ if (waitForPaths()) {
+ // Pick closest chunk before timestamp
+ Long time = pathSet.floorKey(startTimeNanos);
+ if (time != null) {
+ startTimeNanos = time;
+ }
+ return path(startTimeNanos);
+ }
+ return null; // closed
+ }
+
+ private boolean waitForPaths() {
+ while (!closed) {
+ try {
+ if (updatePaths()) {
+ break;
+ }
+ } catch (IOException e) {
+ Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "IOException during repository file scan " + e.getMessage());
+ // This can happen if a chunk is being removed
+ // between the file was discovered and an instance
+ // was accessed, or if new file has been written yet
+ // Just ignore, and retry later.
+ }
+ nap();
+ }
+ return !closed;
+ }
+
+ Path nextPath(long startTimeNanos) {
+ return path(startTimeNanos);
+ }
+
+ private Path path(long timestamp) {
+ if (closed) {
+ return null;
+ }
+ while (true) {
+ SortedMap<Long, Path> after = pathSet.tailMap(timestamp);
+ if (!after.isEmpty()) {
+ Path path = after.get(after.firstKey());
+ if (Logger.shouldLog(LogTag.JFR_SYSTEM_STREAMING, LogLevel.TRACE)) {
+ Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.TRACE, "Return path " + path + " for start time nanos " + timestamp);
+ }
+ return path;
+ }
+ if (!waitForPaths()) {
+ return null; // closed
+ }
+ }
+ }
+
+ private void nap() {
+ try {
+ synchronized (waitObject) {
+ waitObject.wait(1000);
+ }
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
+ private boolean updatePaths() throws IOException {
+ boolean foundNew = false;
+ Path repoPath = repository;
+ if (repoPath == null) {
+ // Always get the latest repository if 'jcmd JFR.configure
+ // repositorypath=...' has been executed
+ SafePath sf = Repository.getRepository().getRepositoryPath();
+ if (sf == null) {
+ return false; // not initialized
+ }
+ repoPath = sf.toPath();
+ }
+
+ try (DirectoryStream<Path> dirStream = fileAccess.newDirectoryStream(repoPath)) {
+ List<Path> added = new ArrayList<>();
+ Set<Path> current = new HashSet<>();
+ for (Path p : dirStream) {
+ if (!pathLookup.containsKey(p)) {
+ String s = p.toString();
+ if (s.endsWith(".jfr")) {
+ added.add(p);
+ Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "New file found: " + p.toAbsolutePath());
+ }
+ current.add(p);
+ }
+ }
+ List<Path> removed = new ArrayList<>();
+ for (Path p : pathLookup.keySet()) {
+ if (!current.contains(p)) {
+ removed.add(p);
+ }
+ }
+
+ for (Path remove : removed) {
+ Long time = pathLookup.get(remove);
+ pathSet.remove(time);
+ pathLookup.remove(remove);
+ }
+ Collections.sort(added, (p1, p2) -> p1.compareTo(p2));
+ for (Path p : added) {
+ // Only add files that have a complete header
+ // as the JVM may be in progress writing the file
+ long size = fileAccess.fileSize(p);
+ if (size >= ChunkHeader.headerSize()) {
+ long startNanos = readStartTime(p);
+ pathSet.put(startNanos, p);
+ pathLookup.put(p, startNanos);
+ foundNew = true;
+ }
+ }
+ return foundNew;
+ }
+ }
+
+ private long readStartTime(Path p) throws IOException {
+ try (RecordingInput in = new RecordingInput(p.toFile(), fileAccess, 100)) {
+ ChunkHeader c = new ChunkHeader(in);
+ return c.getStartNanos();
+ }
+ }
+
+ void close() {
+ synchronized (waitObject) {
+ this.closed = true;
+ waitObject.notify();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StreamConfiguration.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.Utils;
+import jdk.jfr.internal.consumer.Dispatcher.EventDispatcher;
+
+final class StreamConfiguration {
+ final List<Runnable> closeActions = new ArrayList<>();
+ final List<Runnable> flushActions = new ArrayList<>();
+ final List<EventDispatcher> eventActions = new ArrayList<>();
+ final List<Consumer<Throwable>> errorActions = new ArrayList<>();
+
+ boolean reuse = true;
+ boolean ordered = true;
+ Instant startTime = null;
+ Instant endTime = null;
+ boolean started = false;
+ long startNanos = 0;
+ long endNanos = Long.MAX_VALUE;
+
+ private volatile boolean changed = true;
+
+ public synchronized boolean remove(Object action) {
+ boolean removed = false;
+ removed |= flushActions.removeIf(e -> e == action);
+ removed |= closeActions.removeIf(e -> e == action);
+ removed |= errorActions.removeIf(e -> e == action);
+ removed |= eventActions.removeIf(e -> e.getAction() == action);
+ if (removed) {
+ changed = true;
+ }
+ return removed;
+ }
+
+ public synchronized void addEventAction(String name, Consumer<RecordedEvent> consumer) {
+ eventActions.add(new EventDispatcher(name, consumer));
+ changed = true;
+ }
+
+ public void addEventAction(Consumer<RecordedEvent> action) {
+ addEventAction(null, action);
+ }
+
+ public synchronized void addFlushAction(Runnable action) {
+ flushActions.add(action);
+ changed = true;
+ }
+
+ public synchronized void addCloseAction(Runnable action) {
+ closeActions.add(action);
+ changed = true;
+ }
+
+ public synchronized void addErrorAction(Consumer<Throwable> action) {
+ errorActions.add(action);
+ changed = true;
+ }
+
+ public synchronized void setReuse(boolean reuse) {
+ this.reuse = reuse;
+ changed = true;
+ }
+
+ public synchronized void setOrdered(boolean ordered) {
+ this.ordered = ordered;
+ changed = true;
+ }
+
+ public synchronized void setEndTime(Instant endTime) {
+ this.endTime = endTime;
+ this.endNanos = Utils.timeToNanos(endTime);
+ changed = true;
+ }
+
+ public synchronized void setStartTime(Instant startTime) {
+ this.startTime = startTime;
+ this.startNanos = Utils.timeToNanos(startTime);
+ changed = true;
+ }
+
+ public synchronized void setStartNanos(long startNanos) {
+ this.startNanos = startNanos;
+ changed = true;
+ }
+
+ public synchronized void setStarted(boolean started) {
+ this.started = started;
+ changed = true;
+ }
+
+ public boolean hasChanged() {
+ return changed;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+public final class StringParser extends Parser {
+
+ public enum Encoding {
+ NULL(0),
+ EMPTY_STRING(1),
+ CONSTANT_POOL(2),
+ UT8_BYTE_ARRAY(3),
+ CHAR_ARRAY(4),
+ LATIN1_BYTE_ARRAY(5);
+
+ private byte byteValue;
+
+ private Encoding(int byteValue) {
+ this.byteValue = (byte) byteValue;
+ }
+
+ public byte byteValue() {
+ return byteValue;
+ }
+
+ public boolean is(byte value) {
+ return value == byteValue;
+ }
+
+ }
+ private final static Charset UTF8 = Charset.forName("UTF-8");
+ private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
+
+ private final static class CharsetParser extends Parser {
+ private final Charset charset;
+ private int lastSize;
+ private byte[] buffer = new byte[16];
+ private String lastString;
+
+ CharsetParser(Charset charset) {
+ this.charset = charset;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ int size = input.readInt();
+ ensureSize(size);
+ if (lastSize == size) {
+ boolean equalsLastString = true;
+ for (int i = 0; i < size; i++) {
+ // TODO: No need to read byte per byte
+ byte b = input.readByte();
+ if (buffer[i] != b) {
+ equalsLastString = false;
+ buffer[i] = b;
+ }
+ }
+ if (equalsLastString) {
+ return lastString;
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ buffer[i] = input.readByte();
+ }
+ }
+ lastString = new String(buffer, 0, size, charset);
+ lastSize = size;
+ return lastString;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ int size = input.readInt();
+ input.skipBytes(size);
+ }
+
+ private void ensureSize(int size) {
+ if (buffer.length < size) {
+ buffer = new byte[size];
+ }
+ }
+ }
+
+ private final static class CharArrayParser extends Parser {
+ private char[] buffer = new char[16];
+ private int lastSize = -1;
+ private String lastString = null;
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ int size = input.readInt();
+ ensureSize(size);
+ if (lastSize == size) {
+ boolean equalsLastString = true;
+ for (int i = 0; i < size; i++) {
+ char c = input.readChar();
+ if (buffer[i] != c) {
+ equalsLastString = false;
+ buffer[i] = c;
+ }
+ }
+ if (equalsLastString) {
+ return lastString;
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ buffer[i] = input.readChar();
+ }
+ }
+ lastString = new String(buffer, 0, size);
+ lastSize = size;
+ return lastString;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ int size = input.readInt();
+ for (int i = 0; i < size; i++) {
+ input.readChar();
+ }
+ }
+
+ private void ensureSize(int size) {
+ if (buffer.length < size) {
+ buffer = new char[size];
+ }
+ }
+ }
+
+ private final ConstantLookup stringLookup;
+ private final CharArrayParser charArrayParser = new CharArrayParser();
+ private final CharsetParser utf8parser = new CharsetParser(UTF8);
+ private final CharsetParser latin1parser = new CharsetParser(LATIN1);
+ private final boolean event;
+
+ public StringParser(ConstantLookup stringLookup, boolean event) {
+ this.stringLookup = stringLookup;
+ this.event = event;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ byte encoding = input.readByte();
+ if (Encoding.CONSTANT_POOL.is(encoding)) {
+ long key = input.readLong();
+ if (event) {
+ return stringLookup.getCurrentResolved(key);
+ } else {
+ return stringLookup.getCurrent(key);
+ }
+ }
+ if (Encoding.NULL.is(encoding)) {
+ return null;
+ }
+ if (Encoding.EMPTY_STRING.is(encoding)) {
+ return "";
+ }
+ if (Encoding.CHAR_ARRAY.is(encoding)) {
+ return charArrayParser.parse(input);
+ }
+ if (Encoding.UT8_BYTE_ARRAY.is(encoding)) {
+ return utf8parser.parse(input);
+ }
+ if (Encoding.LATIN1_BYTE_ARRAY.is(encoding)) {
+ return latin1parser.parse(input);
+ }
+ throw new IOException("Unknown string encoding " + encoding);
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ byte encoding = input.readByte();
+ if (Encoding.CONSTANT_POOL.is(encoding)) {
+ input.readLong();
+ return;
+ }
+ if (Encoding.EMPTY_STRING.is(encoding)) {
+ return;
+ }
+ if (Encoding.NULL.is(encoding)) {
+ return;
+ }
+ if (Encoding.CHAR_ARRAY.is(encoding)) {
+ charArrayParser.skip(input);
+ return;
+ }
+ if (Encoding.UT8_BYTE_ARRAY.is(encoding)) {
+ utf8parser.skip(input);
+ return;
+ }
+ if (Encoding.LATIN1_BYTE_ARRAY.is(encoding)) {
+ latin1parser.skip(input);
+ return;
+ }
+ throw new IOException("Unknown string encoding " + encoding);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/TimeConverter.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,72 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal.consumer;
+
+import java.time.DateTimeException;
+import java.time.ZoneOffset;
+
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.consumer.ChunkHeader;
+
+/**
+ * Converts ticks to nanoseconds
+ */
+final class TimeConverter {
+ private final long startTicks;
+ private final long startNanos;
+ private final double divisor;
+ private final ZoneOffset zoneOffet;
+
+ TimeConverter(ChunkHeader chunkHeader, int rawOffset) {
+ this.startTicks = chunkHeader.getStartTicks();
+ this.startNanos = chunkHeader.getStartNanos();
+ this.divisor = chunkHeader.getTicksPerSecond() / 1000_000_000L;
+ this.zoneOffet = zoneOfSet(rawOffset);
+ }
+
+ public long convertTimestamp(long ticks) {
+ return startNanos + (long) ((ticks - startTicks) / divisor);
+ }
+
+ public long convertTimespan(long ticks) {
+ return (long) (ticks / divisor);
+ }
+
+ public ZoneOffset getZoneOffset() {
+ return zoneOffet;
+ }
+
+ private ZoneOffset zoneOfSet(int rawOffset) {
+ try {
+ return ZoneOffset.ofTotalSeconds(rawOffset / 1000);
+ } catch (DateTimeException dte) {
+ Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Could not create ZoneOffset from raw offset " + rawOffset);
+ }
+ return ZoneOffset.UTC;
+ }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,10 +27,12 @@
+import jdk.jfr.FlightRecorder;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.Options;
+import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.Repository;
import jdk.jfr.internal.SecuritySupport.SafePath;
@@ -89,6 +91,9 @@
SafePath s = new SafePath(repositoryPath);
Repository.getRepository().setBasePath(s);
Logger.log(LogTag.JFR, LogLevel.INFO, "Base repository path set to " + repositoryPath);
+ if (FlightRecorder.isInitialized()) {
+ PrivateAccess.getInstance().getPlatformRecorder().rotateIfRecordingToDisk();;
+ }
} catch (Exception e) {
throw new DCmdException("Could not use " + repositoryPath + " as repository. " + e.getMessage(), e);
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -81,7 +81,7 @@
* @throws DCmdException if recording could not be started
*/
@SuppressWarnings("resource")
- public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
+ public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Long flush, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name +
", settings=" + Arrays.asList(settings) +
@@ -90,6 +90,7 @@
", disk=" + disk+
", filename=" + path +
", maxage=" + maxAge +
+ ", flush=" + flush +
", maxsize=" + maxSize +
", dumponexit =" + dumpOnExit +
", path-to-gc-roots=" + pathToGcRoots);
@@ -136,6 +137,12 @@
}
}
+ if (flush != null) {
+ if (Boolean.FALSE.equals(disk)) {
+ throw new DCmdException("Flush can only be set for recordings that are to disk.");
+ }
+ }
+
if (!FlightRecorder.isInitialized() && delay == null) {
initializeWithForcedInstrumentation(s);
}
@@ -148,6 +155,7 @@
if (disk != null) {
recording.setToDisk(disk.booleanValue());
}
+
recording.setSettings(s);
SafePath safePath = null;
@@ -177,6 +185,10 @@
recording.setMaxAge(Duration.ofNanos(maxAge));
}
+ if (flush != null) {
+ recording.setFlushInterval(Duration.ofNanos(flush));
+ }
+
if (maxSize != null) {
recording.setMaxSize(maxSize);
}
@@ -222,6 +234,7 @@
print("Use jcmd " + getPid() + " JFR." + cmd + " " + recordingspecifier + " " + fileOption + "to copy recording data to file.");
println();
}
+
return getResult();
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -41,6 +41,7 @@
import java.util.List;
import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput;
final class Disassemble extends Command {
@@ -163,7 +164,7 @@
}
private List<Long> findChunkSizes(Path p) throws IOException {
- try (RecordingInput input = new RecordingInput(p.toFile())) {
+ try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILIGED)) {
List<Long> sizes = new ArrayList<>();
ChunkHeader ch = new ChunkHeader(input);
sizes.add(ch.getSize());
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java Wed Oct 09 23:22:56 2019 +0200
@@ -30,6 +30,7 @@
import java.io.PrintWriter;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -42,7 +43,7 @@
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.consumer.RecordingFile;
-import jdk.jfr.internal.consumer.RecordingInternals;
+import jdk.jfr.internal.consumer.JdkJfrConsumer;
abstract class EventPrintWriter extends StructuredWriter {
@@ -52,6 +53,7 @@
protected static final String STACK_TRACE_FIELD = "stackTrace";
protected static final String EVENT_THREAD_FIELD = "eventThread";
+ private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
private Predicate<EventType> eventFilter = x -> true;
private int stackDepth;
@@ -74,8 +76,8 @@
if (acceptEvent(event)) {
events.add(event);
}
- if (RecordingInternals.INSTANCE.isLastEventInChunk(file)) {
- RecordingInternals.INSTANCE.sort(events);
+ if (PRIVATE_ACCESS.isLastEventInChunk(file)) {
+ Collections.sort(events, PRIVATE_ACCESS.eventComparator());
print(events);
events.clear();
}
@@ -121,7 +123,7 @@
case TIMESPAN:
return object.getDuration(v.getName());
case TIMESTAMP:
- return RecordingInternals.INSTANCE.getOffsetDataTime(object, v.getName());
+ return PRIVATE_ACCESS.getOffsetDataTime(object, v.getName());
default:
return object.getValue(v.getName());
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java Wed Oct 09 23:22:56 2019 +0200
@@ -35,10 +35,12 @@
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.internal.Type;
-import jdk.jfr.internal.consumer.RecordingInternals;
+import jdk.jfr.internal.consumer.JdkJfrConsumer;
final class Metadata extends Command {
+ private final static JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
+
private static class TypeComparator implements Comparator<Type> {
@Override
@@ -89,6 +91,7 @@
}
}
+
@Override
public String getName() {
return "metadata";
@@ -125,7 +128,7 @@
PrettyWriter prettyWriter = new PrettyWriter(pw);
prettyWriter.setShowIds(showIds);
try (RecordingFile rf = new RecordingFile(file)) {
- List<Type> types = RecordingInternals.INSTANCE.readTypes(rf);
+ List<Type> types = PRIVATE_ACCESS.readTypes(rf);
Collections.sort(types, new TypeComparator());
for (Type type : types) {
prettyWriter.printType(type);
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Summary.java Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Summary.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,6 +42,7 @@
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput;
final class Summary extends Command {
@@ -91,7 +92,7 @@
long totalDuration = 0;
long chunks = 0;
- try (RecordingInput input = new RecordingInput(p.toFile())) {
+ try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILIGED)) {
ChunkHeader first = new ChunkHeader(input);
ChunkHeader ch = first;
String eventPrefix = Type.EVENT_NAME_PREFIX;
--- a/src/jdk.jfr/share/conf/jfr/default.jfc Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/conf/jfr/default.jfc Wed Oct 09 23:22:56 2019 +0200
@@ -406,7 +406,7 @@
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
-
+
<event name="jdk.G1BasicIHOP">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
@@ -670,6 +670,36 @@
<setting name="enabled">true</setting>
</event>
+ <event name="jdk.Flush">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStorage">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStacktrace">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStringPool">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushMetadata">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushTypeSet">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
<event name="jdk.DataLoss">
<setting name="enabled">true</setting>
</event>
--- a/src/jdk.jfr/share/conf/jfr/profile.jfc Wed Oct 09 12:21:28 2019 -0700
+++ b/src/jdk.jfr/share/conf/jfr/profile.jfc Wed Oct 09 23:22:56 2019 +0200
@@ -406,7 +406,7 @@
<setting name="enabled" control="gc-enabled-normal">true</setting>
<setting name="threshold">0 ms</setting>
</event>
-
+
<event name="jdk.G1BasicIHOP">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
@@ -670,6 +670,36 @@
<setting name="enabled">true</setting>
</event>
+ <event name="jdk.Flush">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStorage">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStacktrace">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushStringPool">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushMetadata">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
+ <event name="jdk.FlushTypeSet">
+ <setting name="enabled">true</setting>
+ <setting name="threshold">0 ns</setting>
+ </event>
+
<event name="jdk.DataLoss">
<setting name="enabled">true</setting>
</event>
--- a/test/hotspot/gtest/jfr/test_networkUtilization.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/test/hotspot/gtest/jfr/test_networkUtilization.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -51,7 +51,7 @@
namespace {
class MockFastUnorderedElapsedCounterSource : public ::FastUnorderedElapsedCounterSource {
- public:
+ public:
static jlong current_ticks;
static Type now() {
return current_ticks;
@@ -65,7 +65,7 @@
typedef TimeInterval<CounterRepresentation, MockFastUnorderedElapsedCounterSource> MockJfrTickspan;
class MockJfrCheckpointWriter {
- public:
+ public:
traceid current;
std::map<traceid, std::string> ids;
@@ -78,44 +78,97 @@
void write_key(traceid id) {
current = id;
}
- void write(const char* data) {
- ids[current] = data;
- }
+ void write_type(JfrTypeId id) {}
+ MockJfrCheckpointWriter() {}
+ void write(const char* data) {}
void set_context(const JfrCheckpointContext ctx) { }
- void write_count(u4 nof_entries, jlong offset) { }
+ void write_count(u4 nof_entries) { }
};
class MockJfrSerializer {
- public:
- static MockJfrSerializer* current;
-
- static bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, MockJfrSerializer* serializer) {
- current = serializer;
+ public:
+ static bool register_serializer(JfrTypeId id, bool permit_cache, MockJfrSerializer* serializer) {
return true;
}
+ virtual void on_rotation() {}
+ virtual void serialize(MockJfrCheckpointWriter& writer) {}
+ };
- virtual void serialize(MockJfrCheckpointWriter& writer) = 0;
+ struct MockNetworkInterface {
+ std::string name;
+ uint64_t bytes_in;
+ uint64_t bytes_out;
+ traceid id;
+ MockNetworkInterface(std::string name, uint64_t bytes_in, uint64_t bytes_out, traceid id) :
+ name(name), bytes_in(bytes_in), bytes_out(bytes_out), id(id) {}
+
+ bool operator==(const MockNetworkInterface& rhs) const {
+ return name == rhs.name;
+ }
+ };
+
+ class NetworkInterface : public ::NetworkInterface {
+ public:
+ NetworkInterface(const char* name, uint64_t bytes_in, uint64_t bytes_out, NetworkInterface* next) :
+ ::NetworkInterface(name, bytes_in, bytes_out, next) {}
+ NetworkInterface* next(void) const {
+ return reinterpret_cast<NetworkInterface*>(::NetworkInterface::next());
+ }
};
- MockJfrSerializer* MockJfrSerializer::current;
+ class MockJfrOSInterface {
+ static std::list<MockNetworkInterface> _interfaces;
+ public:
+ MockJfrOSInterface() {}
+ static int network_utilization(NetworkInterface** network_interfaces) {
+ *network_interfaces = NULL;
+ for (std::list<MockNetworkInterface>::const_iterator i = _interfaces.begin();
+ i != _interfaces.end();
+ ++i) {
+ NetworkInterface* cur = new NetworkInterface(i->name.c_str(), i->bytes_in, i->bytes_out, *network_interfaces);
+ *network_interfaces = cur;
+ }
+ return OS_OK;
+ }
+ static MockNetworkInterface& add_interface(const std::string& name, traceid id) {
+ MockNetworkInterface iface(name, 0, 0, id);
+ _interfaces.push_front(iface);
+ return _interfaces.front();
+ }
+ static void remove_interface(const MockNetworkInterface& iface) {
+ _interfaces.remove(iface);
+ }
+ static void clear_interfaces() {
+ _interfaces.clear();
+ }
+ static const MockNetworkInterface& get_interface(traceid id) {
+ std::list<MockNetworkInterface>::const_iterator i = _interfaces.begin();
+ for (; i != _interfaces.end(); ++i) {
+ if (i->id == id) {
+ break;
+ }
+ }
+ return *i;
+ }
+ };
- class MockEventNetworkUtilization : public ::EventNetworkUtilization
- {
- public:
+ std::list<MockNetworkInterface> MockJfrOSInterface::_interfaces;
+
+ class MockEventNetworkUtilization : public ::EventNetworkUtilization {
+ public:
std::string iface;
s8 readRate;
s8 writeRate;
static std::vector<MockEventNetworkUtilization> committed;
MockJfrCheckpointWriter writer;
- public:
+ public:
MockEventNetworkUtilization(EventStartTime timing=TIMED) :
- ::EventNetworkUtilization(timing) {
- }
+ ::EventNetworkUtilization(timing) {}
void set_networkInterface(traceid new_value) {
- MockJfrSerializer::current->serialize(writer);
- iface = writer.ids[new_value];
+ const MockNetworkInterface& entry = MockJfrOSInterface::get_interface(new_value);
+ iface = entry.name;
}
void set_readRate(s8 new_value) {
readRate = new_value;
@@ -129,7 +182,6 @@
}
void set_starttime(const MockJfrTicks& time) {}
-
void set_endtime(const MockJfrTicks& time) {}
static const MockEventNetworkUtilization& get_committed(const std::string& name) {
@@ -149,62 +201,6 @@
jlong MockFastUnorderedElapsedCounterSource::current_ticks;
- struct MockNetworkInterface {
- std::string name;
- uint64_t bytes_in;
- uint64_t bytes_out;
- MockNetworkInterface(std::string name, uint64_t bytes_in, uint64_t bytes_out)
- : name(name),
- bytes_in(bytes_in),
- bytes_out(bytes_out) {
-
- }
- bool operator==(const MockNetworkInterface& rhs) const {
- return name == rhs.name;
- }
- };
-
- class NetworkInterface : public ::NetworkInterface {
- public:
- NetworkInterface(const char* name, uint64_t bytes_in, uint64_t bytes_out, NetworkInterface* next)
- : ::NetworkInterface(name, bytes_in, bytes_out, next) {
- }
- NetworkInterface* next(void) const {
- return reinterpret_cast<NetworkInterface*>(::NetworkInterface::next());
- }
- };
-
- class MockJfrOSInterface {
- static std::list<MockNetworkInterface> _interfaces;
-
- public:
- MockJfrOSInterface() {
- }
- static int network_utilization(NetworkInterface** network_interfaces) {
- *network_interfaces = NULL;
- for (std::list<MockNetworkInterface>::const_iterator i = _interfaces.begin();
- i != _interfaces.end();
- ++i) {
- NetworkInterface* cur = new NetworkInterface(i->name.c_str(), i->bytes_in, i->bytes_out, *network_interfaces);
- *network_interfaces = cur;
- }
- return OS_OK;
- }
- static MockNetworkInterface& add_interface(const std::string& name) {
- MockNetworkInterface iface(name, 0, 0);
- _interfaces.push_back(iface);
- return _interfaces.back();
- }
- static void remove_interface(const MockNetworkInterface& iface) {
- _interfaces.remove(iface);
- }
- static void clear_interfaces() {
- _interfaces.clear();
- }
- };
-
- std::list<MockNetworkInterface> MockJfrOSInterface::_interfaces;
-
// Reincluding source files in the anonymous namespace unfortunately seems to
// behave strangely with precompiled headers (only when using gcc though)
#ifndef DONT_USE_PRECOMPILED_HEADER
@@ -248,7 +244,7 @@
TEST_VM_F(JfrTestNetworkUtilization, RequestFunctionBasic) {
- MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
+ MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0", 1);
JfrNetworkUtilization::send_events();
ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());
@@ -265,9 +261,9 @@
TEST_VM_F(JfrTestNetworkUtilization, RequestFunctionMultiple) {
- MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
- MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1");
- MockNetworkInterface& ppp0 = MockJfrOSInterface::add_interface("ppp0");
+ MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0", 2);
+ MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1", 3);
+ MockNetworkInterface& ppp0 = MockJfrOSInterface::add_interface("ppp0", 4);
JfrNetworkUtilization::send_events();
ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());
@@ -296,8 +292,8 @@
}
TEST_VM_F(JfrTestNetworkUtilization, InterfaceRemoved) {
- MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
- MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1");
+ MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0", 5);
+ MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1", 6);
JfrNetworkUtilization::send_events();
ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());
@@ -333,7 +329,7 @@
}
TEST_VM_F(JfrTestNetworkUtilization, InterfaceReset) {
- MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
+ MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0", 7);
JfrNetworkUtilization::send_events();
ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());
--- a/test/hotspot/gtest/jfr/test_threadCpuLoad.cpp Wed Oct 09 12:21:28 2019 -0700
+++ b/test/hotspot/gtest/jfr/test_threadCpuLoad.cpp Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,11 +36,10 @@
#include "jfr/jfrEvents.hpp"
#include "jfr/support/jfrThreadId.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "utilities/globalDefinitions.hpp"
#include "runtime/os.hpp"
-#include "runtime/thread.inline.hpp"
-#include "runtime/threadSMR.inline.hpp"
#include "unittest.hpp"
@@ -81,11 +80,18 @@
MockJavaThread() : ::JavaThread() {}
};
- class MockJavaThreadIteratorWithHandle
+ class MockJfrJavaThreadIterator
{
public:
MockJavaThread* next() { return NULL; }
- int length() { return 0; }
+ bool has_next() const { return false; }
+ };
+
+ class MockJfrJavaThreadIteratorAdapter
+ {
+ public:
+ MockJavaThread* next() { return NULL; }
+ bool has_next() const { return false; }
};
// Reincluding source files in the anonymous namespace unfortunately seems to
@@ -97,7 +103,8 @@
#define os MockOs
#define EventThreadCPULoad MockEventThreadCPULoad
#define JavaThread MockJavaThread
-#define JavaThreadIteratorWithHandle MockJavaThreadIteratorWithHandle
+#define JfrJavaThreadIterator MockJfrJavaThreadIterator
+#define JfrJavaThreadIteratorAdapter MockJfrJavaThreadIteratorAdapter
#include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
#include "jfr/periodic/jfrThreadCPULoadEvent.cpp"
@@ -105,7 +112,8 @@
#undef os
#undef EventThreadCPULoad
#undef JavaThread
-#undef JavaThreadIteratorWithHandle
+#define JfrJavaThreadIterator MockJfrJavaThreadIterator
+#define JfrJavaThreadIteratorAdapter MockJfrJavaThreadIteratorAdapter
} // anonymous namespace
--- a/test/jdk/jdk/jfr/api/consumer/TestReadTwice.java Wed Oct 09 12:21:28 2019 -0700
+++ b/test/jdk/jdk/jfr/api/consumer/TestReadTwice.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
package jdk.jfr.api.consumer;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
--- a/test/jdk/jdk/jfr/api/consumer/TestRecordingFile.java Wed Oct 09 12:21:28 2019 -0700
+++ b/test/jdk/jdk/jfr/api/consumer/TestRecordingFile.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -55,7 +55,7 @@
* @key jfr
* @requires vm.hasJFR
* @library /test/lib
- * @run main/othervm jdk.jfr.api.consumer.TestRecordingFile
+ * @run main/othervm -Xlog:jfr*=info jdk.jfr.api.consumer.TestRecordingFile
*/
public class TestRecordingFile {
@@ -210,12 +210,12 @@
assertHasEventType(types, "Event2");
assertMissingEventType(types, "Event3");
}
- try (RecordingFile f = new RecordingFile(twoEventTypes)) {
+ try (RecordingFile f = new RecordingFile(threeEventTypes)) {
List<EventType> types = f.readEventTypes();
assertUniqueEventTypes(types);
assertHasEventType(types, "Event1");
assertHasEventType(types, "Event2");
- assertMissingEventType(types, "Event3");
+ assertHasEventType(types, "Event3");
}
}
--- a/test/jdk/jdk/jfr/api/consumer/TestRecordingInternals.java Wed Oct 09 12:21:28 2019 -0700
+++ b/test/jdk/jdk/jfr/api/consumer/TestRecordingInternals.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -60,6 +60,7 @@
Asserts.assertEquals(id.toString(), rt.getThreadGroup().getName(), "Thread group name should match id");
Asserts.assertEquals(id, Integer.valueOf(i), "Chunks out of order");
i++;
+ System.out.println(i + " OK ");
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/filestream/TestMultipleChunk.java Wed Oct 09 23:22:56 2019 +0200
@@ -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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.filestream;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.atomic.AtomicLong;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents from a multichunked file
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.filestream.TestMultipleChunk
+ */
+public class TestMultipleChunk {
+
+ static class SnakeEvent extends Event {
+ int id;
+ }
+
+ public static void main(String... args) throws Exception {
+ Path path = Paths.get("./using-file.jfr");
+ try (Recording r1 = new Recording()) {
+ r1.start();
+ emitSnakeEvent(1);
+ emitSnakeEvent(2);
+ emitSnakeEvent(3);
+ // Force a chunk rotation
+ try (Recording r2 = new Recording()) {
+ r2.start();
+ emitSnakeEvent(4);
+ emitSnakeEvent(5);
+ emitSnakeEvent(6);
+ r2.stop();
+ }
+ r1.stop();
+ r1.dump(path);
+ AtomicLong counter = new AtomicLong();
+ try (EventStream es = EventStream.openFile(path)) {
+ es.onEvent(e -> {
+ counter.incrementAndGet();
+ });
+ es.start();
+ if (counter.get() != 6) {
+ throw new Exception("Expected 6 event, but got " + counter.get());
+ }
+ }
+ }
+ }
+
+ static void emitSnakeEvent(int id) {
+ SnakeEvent e = new SnakeEvent();
+ e.id = id;
+ e.commit();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/filestream/TestOrdered.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.filestream;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Test EventStream::setOrdered(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.filestream.TestOrdered
+ */
+public class TestOrdered {
+
+ static class OrderedEvent extends Event {
+ }
+
+ static class Emitter extends Thread {
+ private final CyclicBarrier barrier;
+
+ public Emitter(CyclicBarrier barrier) {
+ this.barrier = barrier;
+ }
+
+ @Override
+ public void run() {
+ OrderedEvent e1 = new OrderedEvent();
+ e1.commit();
+ try {
+ barrier.await();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new Error("Unexpected exception in barrier");
+ }
+ OrderedEvent e2 = new OrderedEvent();
+ e2.commit();
+ }
+ }
+
+ private static final int THREAD_COUNT = 4;
+ private static final boolean[] BOOLEAN_STATES = { false, true };
+
+ public static void main(String... args) throws Exception {
+ Path p = makeUnorderedRecording();
+
+ testSetOrderedTrue(p);
+ testSetOrderedFalse(p);
+ }
+
+ private static void testSetOrderedTrue(Path p) throws Exception {
+ for (boolean reuse : BOOLEAN_STATES) {
+ AtomicReference<Instant> timestamp = new AtomicReference<>(Instant.MIN);
+ try (EventStream es = EventStream.openFile(p)) {
+ es.setReuse(reuse);
+ es.setOrdered(true);
+ es.onEvent(e -> {
+ Instant endTime = e.getEndTime();
+ if (endTime.isBefore(timestamp.get())) {
+ throw new Error("Events are not ordered! Reuse = " + reuse);
+ }
+ timestamp.set(endTime);
+ });
+ es.start();
+ }
+ }
+ }
+
+ private static void testSetOrderedFalse(Path p) throws Exception {
+ for (boolean reuse : BOOLEAN_STATES) {
+ AtomicReference<Instant> timestamp = new AtomicReference<>(Instant.MIN);
+ AtomicBoolean unoreded = new AtomicBoolean(false);
+ try (EventStream es = EventStream.openFile(p)) {
+ es.setReuse(reuse);
+ es.setOrdered(false);
+ es.onEvent(e -> {
+ Instant endTime = e.getEndTime();
+ if (endTime.isBefore(timestamp.get())) {
+ unoreded.set(true);
+ es.close();
+ }
+ timestamp.set(endTime);
+ });
+ es.start();
+ if (!unoreded.get()) {
+ throw new Exception("Expected at least some events to be out of order! Reuse = " + reuse);
+ }
+ }
+ }
+ }
+
+ private static Path makeUnorderedRecording() throws Exception {
+ CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
+
+ try (Recording r = new Recording()) {
+ r.start();
+ List<Emitter> emitters = new ArrayList<>();
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ Emitter e = new Emitter(barrier);
+ e.start();
+ emitters.add(e);
+ }
+ // Thread buffers should now have one event each
+ barrier.await();
+ // Add another event to each thread buffer, so
+ // events are bound to come out of order when they
+ // are flushed
+ for (Emitter e : emitters) {
+ e.join();
+ }
+ r.stop();
+ Path p = Files.createTempFile("recording", ".jfr");
+ r.dump(p);
+ return p;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/filestream/TestReuse.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.filestream;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordedEvent;
+
+/**
+ * @test
+ * @summary Test EventStream::setReuse(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.filestream.TestReuse
+ */
+public class TestReuse {
+
+ static class ReuseEvent extends Event {
+ }
+
+ private static final boolean[] BOOLEAN_STATES = { false, true };
+
+ public static void main(String... args) throws Exception {
+ Path p = makeRecording();
+
+ testSetReuseTrue(p);
+ testSetReuseFalse(p);
+ }
+
+ private static void testSetReuseFalse(Path p) throws Exception {
+ for (boolean ordered : BOOLEAN_STATES) {
+ AtomicBoolean fail = new AtomicBoolean(false);
+ Map<RecordedEvent, RecordedEvent> identity = new IdentityHashMap<>();
+ try (EventStream es = EventStream.openFile(p)) {
+ es.setOrdered(ordered);
+ es.setReuse(false);
+ es.onEvent(e -> {
+ if (identity.containsKey(e)) {
+ fail.set(true);
+ es.close();
+ }
+ identity.put(e, e);
+ });
+ es.start();
+ }
+ if (fail.get()) {
+ throw new Exception("Unexpected reuse! Ordered = " + ordered);
+ }
+
+ }
+ }
+
+ private static void testSetReuseTrue(Path p) throws Exception {
+ for (boolean ordered : BOOLEAN_STATES) {
+ AtomicBoolean success = new AtomicBoolean(false);
+ Map<RecordedEvent, RecordedEvent> events = new IdentityHashMap<>();
+ try (EventStream es = EventStream.openFile(p)) {
+ es.setOrdered(ordered);
+ es.setReuse(true);
+ es.onEvent(e -> {
+ if(events.containsKey(e)) {
+ success.set(true);;
+ es.close();
+ }
+ events.put(e,e);
+ });
+ es.start();
+ }
+ if (!success.get()) {
+ throw new Exception("No reuse! Ordered = " + ordered);
+ }
+ }
+
+ }
+
+ private static Path makeRecording() throws IOException {
+ try (Recording r = new Recording()) {
+ r.start();
+ for (int i = 0; i < 5; i++) {
+ ReuseEvent e = new ReuseEvent();
+ e.commit();
+ }
+ Recording rotation = new Recording();
+ rotation.start();
+ for (int i = 0; i < 5; i++) {
+ ReuseEvent e = new ReuseEvent();
+ e.commit();
+ }
+ r.stop();
+ rotation.close();
+ Path p = Files.createTempFile("recording", ".jfr");
+ r.dump(p);
+ return p;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestAwaitTermination.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test RecordingStream::awaitTermination(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestAwaitTermination
+ */
+public class TestAwaitTermination {
+
+ public static void main(String... args) throws Exception {
+ testAwaitClose();
+ testAwaitTimeOut();
+ }
+
+ private static void testAwaitClose() throws InterruptedException, ExecutionException {
+ try (RecordingStream r = new RecordingStream()) {
+ r.startAsync();
+ var c = CompletableFuture.runAsync(() -> {
+ try {
+ r.awaitTermination();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ });
+ r.close();
+ c.get();
+ }
+ }
+
+ private static void testAwaitTimeOut() throws InterruptedException, ExecutionException {
+ try (RecordingStream r = new RecordingStream()) {
+ r.startAsync();
+ var c = CompletableFuture.runAsync(() -> {
+ try {
+ r.awaitTermination(Duration.ofMillis(10));
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ });
+ c.get();
+ r.close();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestClose.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicLong;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::close()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestClose
+ */
+public class TestClose {
+
+ private static class CloseEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testCloseUnstarted();
+ testCloseStarted();
+ testCloseTwice();
+ testCloseStreaming();
+ testCloseMySelf();
+ }
+
+ private static void testCloseMySelf() throws Exception {
+ log("Entering testCloseMySelf()");
+ CountDownLatch l1 = new CountDownLatch(1);
+ CountDownLatch l2 = new CountDownLatch(1);
+ RecordingStream r = new RecordingStream();
+ r.onEvent(e -> {
+ try {
+ l1.await();
+ r.close();
+ l2.countDown();
+ } catch (InterruptedException ie) {
+ throw new Error(ie);
+ }
+ });
+ r.startAsync();
+ CloseEvent c = new CloseEvent();
+ c.commit();
+ l1.countDown();
+ l2.await();
+ log("Leaving testCloseMySelf()");
+ }
+
+ private static void testCloseStreaming() throws Exception {
+ log("Entering testCloseStreaming()");
+ CountDownLatch streaming = new CountDownLatch(1);
+ RecordingStream r = new RecordingStream();
+ AtomicLong count = new AtomicLong();
+ r.onEvent(e -> {
+ if (count.incrementAndGet() > 100) {
+ streaming.countDown();
+ }
+ });
+ r.startAsync();
+ var streamingLoop = CompletableFuture.runAsync(() -> {
+ while (true) {
+ CloseEvent c = new CloseEvent();
+ c.commit();
+ }
+ });
+ streaming.await();
+ r.close();
+ streamingLoop.cancel(true);
+ log("Leaving testCloseStreaming()");
+ }
+
+ private static void testCloseStarted() {
+ log("Entering testCloseStarted()");
+ RecordingStream r = new RecordingStream();
+ r.startAsync();
+ r.close();
+ log("Leaving testCloseStarted()");
+ }
+
+ private static void testCloseUnstarted() {
+ log("Entering testCloseUnstarted()");
+ RecordingStream r = new RecordingStream();
+ r.close();
+ log("Leaving testCloseUnstarted()");
+ }
+
+ private static void testCloseTwice() {
+ log("Entering testCloseTwice()");
+ RecordingStream r = new RecordingStream();
+ r.startAsync();
+ r.close();
+ r.close();
+ log("Leaving testCloseTwice()");
+ }
+
+ private static void log(String msg) {
+ System.out.println(msg);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestConstructor.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Configuration;
+import jdk.jfr.Enabled;
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::RecordingStream()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestConstructor
+ */
+public class TestConstructor {
+
+ public static void main(String... args) throws Exception {
+ testEmpty();
+ testConfiguration();
+ }
+
+ private static void testConfiguration() throws Exception {
+ CountDownLatch jvmInformation = new CountDownLatch(1);
+ Configuration c = Configuration.getConfiguration("default");
+ if (!c.getSettings().containsKey(EventNames.JVMInformation + "#" + Enabled.NAME)) {
+ throw new Exception("Expected default configuration to contain enabled " + EventNames.JVMInformation);
+ }
+ RecordingStream r = new RecordingStream(c);
+ r.onEvent("jdk.JVMInformation", e -> {
+ jvmInformation.countDown();
+ });
+ r.startAsync();
+ jvmInformation.await();
+ r.close();
+ }
+
+ private static void testEmpty() {
+ RecordingStream r = new RecordingStream();
+ r.close();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestDisable.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::disable(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestDisable
+ */
+public class TestDisable {
+
+ private static class DisabledEvent extends Event {
+ }
+
+ private static class EnabledEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testDisableWithClass();
+ testDisableWithEventName();
+ }
+
+ private static void testDisableWithEventName() {
+ test(r -> r.disable(DisabledEvent.class.getName()));
+ }
+
+ private static void testDisableWithClass() {
+ test(r -> r.disable(DisabledEvent.class));
+ }
+
+ private static void test(Consumer<RecordingStream> disablement) {
+ CountDownLatch twoEvent = new CountDownLatch(2);
+ AtomicBoolean fail = new AtomicBoolean(false);
+ try(RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ if (e.getEventType().getName().equals(DisabledEvent.class.getName())) {
+ fail.set(true);
+ }
+ twoEvent.countDown();
+ });
+ disablement.accept(r);
+ r.startAsync();
+ EnabledEvent e1 = new EnabledEvent();
+ e1.commit();
+ DisabledEvent d1 = new DisabledEvent();
+ d1.commit();
+ EnabledEvent e2 = new EnabledEvent();
+ e2.commit();
+ try {
+ twoEvent.await();
+ } catch (InterruptedException ie) {
+ throw new RuntimeException("Unexpexpected interruption of thread", ie);
+ }
+ if (fail.get()) {
+ throw new RuntimeException("Should not receive a disabled event");
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestEnable.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
+
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::enable(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestEnable
+ */
+public class TestEnable {
+
+ @Enabled(false)
+ private static class EnabledEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testEnableWithClass();
+ testEnableWithEventName();
+ }
+
+ private static void testEnableWithEventName() {
+ test(r -> r.enable(EnabledEvent.class.getName()));
+ }
+
+ private static void testEnableWithClass() {
+ test(r -> r.enable(EnabledEvent.class));
+ }
+
+ private static void test(Consumer<RecordingStream> enablement) {
+ CountDownLatch event = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ event.countDown();
+ });
+ enablement.accept(r);
+ r.startAsync();
+ EnabledEvent e = new EnabledEvent();
+ e.commit();
+ try {
+ event.await();
+ } catch (InterruptedException ie) {
+ throw new RuntimeException("Unexpected interruption of latch", ie);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestMaxAge.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::setMaxAge(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestMaxAge
+ */
+public class TestMaxAge {
+
+ public static void main(String... args) throws Exception {
+ Duration testDuration = Duration.ofMillis(1234567);
+ try (RecordingStream r = new RecordingStream()) {
+ r.setMaxAge(testDuration);
+ r.enable(EventNames.ActiveRecording);
+ r.onEvent(e -> {
+ System.out.println(e);
+ Duration d = e.getDuration("maxAge");
+ System.out.println(d.toMillis());
+ if (testDuration.equals(d)) {
+ r.close();
+ return;
+ }
+ System.out.println("Max age not set, was " + d.toMillis() + " ms , but expected " + testDuration.toMillis() + " ms");
+ });
+ r.start();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onClose(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnClose
+ */
+public class TestOnClose {
+
+ private static class CloseEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testOnCloseNull();
+ testOnClosedUnstarted();
+ testOnClosedStarted();
+ }
+
+ private static void testOnCloseNull() {
+ try (RecordingStream rs = new RecordingStream()) {
+ try {
+ rs.onClose(null);
+ throw new AssertionError("Expected NullPointerException from onClose(null");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ }
+ }
+
+ private static void testOnClosedStarted() throws InterruptedException {
+ AtomicBoolean onClose = new AtomicBoolean(false);
+ CountDownLatch event = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ event.countDown();
+ });
+ r.onClose(() -> {
+ onClose.set(true);
+ });
+ r.startAsync();
+ CloseEvent c = new CloseEvent();
+ c.commit();
+ event.await();
+ }
+ if (!onClose.get()) {
+ throw new AssertionError("OnClose was not called");
+ }
+ }
+
+ private static void testOnClosedUnstarted() {
+ AtomicBoolean onClose = new AtomicBoolean(false);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onClose(() -> {
+ onClose.set(true);
+ });
+ }
+ if (!onClose.get()) {
+ throw new AssertionError("OnClose was not called");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnErrorAsync.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.api.consumer.recordingstream.TestUtils.TestError;
+import jdk.jfr.api.consumer.recordingstream.TestUtils.TestException;
+import jdk.jfr.api.consumer.security.TestStreamingRemote.TestEvent;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onError(...) when using
+ * RecordingStream:startAsync
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnErrorAsync
+ */
+public class TestOnErrorAsync {
+ public static void main(String... args) throws Exception {
+ testDefaultError();
+ testCustomError();
+ testDefaultException();
+ testCustomException();
+ testOnFlushSanity();
+ testOnCloseSanity();
+ }
+
+ private static void testDefaultError() throws Exception {
+ AtomicBoolean closed = new AtomicBoolean();
+ CountDownLatch receivedError = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ TestError error = new TestError();
+ TestUtils.installUncaughtException(receivedError, error);
+ throw error; // closes stream
+ });
+ r.onClose(() -> {
+ closed.set(true);
+ });
+ r.startAsync();
+ TestEvent e = new TestEvent();
+ e.commit();
+ r.awaitTermination();
+ receivedError.await();
+ if (!closed.get()) {
+ throw new Exception("Expected stream to be closed");
+ }
+ }
+ }
+
+ private static void testCustomError() throws Exception {
+ AtomicBoolean onError = new AtomicBoolean();
+ AtomicBoolean closed = new AtomicBoolean();
+ CountDownLatch receivedError = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ TestError error = new TestError();
+ TestUtils.installUncaughtException(receivedError, error);
+ throw error; // closes stream
+ });
+ r.onError(e -> {
+ onError.set(true);
+ });
+ r.onClose(() -> {
+ closed.set(true);
+ });
+ r.startAsync();
+ TestEvent e = new TestEvent();
+ e.commit();
+ r.awaitTermination();
+ receivedError.await();
+ if (onError.get()) {
+ throw new Exception("onError handler should not be invoked on java.lang.Error.");
+ }
+ if (!closed.get()) {
+ throw new Exception("Expected stream to be closed");
+ }
+ }
+ }
+
+ private static void testDefaultException() throws Exception {
+ TestException exception = new TestException();
+ AtomicBoolean closed = new AtomicBoolean();
+ AtomicInteger counter = new AtomicInteger();
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ if (counter.incrementAndGet() == 2) {
+ r.close();
+ return;
+ }
+ TestUtils.throwUnchecked(exception);
+ });
+ r.onClose(() -> {
+ closed.set(true);
+ });
+ r.startAsync();
+ TestEvent e1 = new TestEvent();
+ e1.commit();
+ TestEvent e2 = new TestEvent();
+ e2.commit();
+ r.awaitTermination();
+ if (!exception.isPrinted()) {
+ throw new Exception("Expected stack trace from Exception to be printed");
+ }
+ if (!closed.get()) {
+ throw new Exception("Expected stream to be closed");
+ }
+ }
+ }
+
+ private static void testCustomException() throws Exception {
+ TestException exception = new TestException();
+ AtomicBoolean closed = new AtomicBoolean();
+ AtomicBoolean received = new AtomicBoolean();
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ TestUtils.throwUnchecked(exception);
+ });
+ r.onError(t -> {
+ received.set(t == exception);
+ r.close();
+ });
+ r.onClose(() -> {
+ closed.set(true);
+ });
+ r.startAsync();
+ TestEvent event = new TestEvent();
+ event.commit();
+ r.awaitTermination();
+ if (!received.get()) {
+ throw new Exception("Did not receive expected exception in onError(...)");
+ }
+ if (exception.isPrinted()) {
+ throw new Exception("Expected stack trace from Exception NOT to be printed");
+ }
+ if (!closed.get()) {
+ throw new Exception("Expected stream to be closed");
+ }
+ }
+ }
+
+ private static void testOnFlushSanity() throws Exception {
+ TestException exception = new TestException();
+ CountDownLatch received = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onFlush(() -> {
+ TestUtils.throwUnchecked(exception);
+ });
+ r.onError(t -> {
+ if (t == exception) {
+ received.countDown();
+ }
+ });
+ r.startAsync();
+ received.await();
+ }
+ }
+
+ private static void testOnCloseSanity() throws Exception {
+ TestException exception = new TestException();
+ CountDownLatch received = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onFlush(() -> {
+ r.close(); // will trigger onClose
+ });
+ r.onClose(() -> {
+ TestUtils.throwUnchecked(exception); // will trigger onError
+ });
+ r.onError(t -> {
+ if (t == exception) {
+ received.countDown();
+ }
+ });
+ r.startAsync();
+ received.await();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnErrorSync.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.api.consumer.recordingstream.TestUtils.TestError;
+import jdk.jfr.api.consumer.recordingstream.TestUtils.TestException;
+import jdk.jfr.api.consumer.security.TestStreamingRemote.TestEvent;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onError(...) when using RecordingStream:start
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnErrorSync
+ */
+public class TestOnErrorSync {
+ public static void main(String... args) throws Exception {
+ testDefaultError();
+ testCustomError();
+ testDefaultException();
+ testCustomException();
+ testOnFlushSanity();
+ testOnCloseSanity();
+ }
+
+ private static void testDefaultError() throws Exception {
+ TestError error = new TestError();
+ AtomicBoolean closed = new AtomicBoolean();
+ Timer t = newEventEmitter();
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ throw error; // closes stream
+ });
+ r.onClose(() -> {
+ closed.set(true);
+ });
+ try {
+ r.start();
+ throw new Exception("Expected TestError to be thrown");
+ } catch (TestError te) {
+ // as expected
+ }
+ if (!closed.get()) {
+ throw new Exception("Expected stream to be closed");
+ }
+ } finally {
+ t.cancel();
+ }
+ }
+
+ private static void testCustomError() throws Exception {
+ TestError error = new TestError();
+ AtomicBoolean onError = new AtomicBoolean();
+ AtomicBoolean closed = new AtomicBoolean();
+ Timer t = newEventEmitter();
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ throw error; // closes stream
+ });
+ r.onError(e -> {
+ onError.set(true);
+ });
+ r.onClose(() -> {
+ closed.set(true);
+ });
+ try {
+ r.start();
+ throw new Exception("Expected TestError to be thrown");
+ } catch (TestError terror) {
+ // as expected
+ }
+ if (onError.get()) {
+ throw new Exception("Expected onError(...) NOT to be invoked");
+ }
+ if (!closed.get()) {
+ throw new Exception("Expected stream to be closed");
+ }
+ } finally {
+ t.cancel();
+ }
+ }
+
+ private static void testDefaultException() throws Exception {
+ TestException exception = new TestException();
+ AtomicInteger counter = new AtomicInteger();
+ AtomicBoolean closed = new AtomicBoolean();
+ Timer t = newEventEmitter();
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ if (counter.incrementAndGet() == 2) {
+ // Only close if we get a second event after an exception
+ r.close();
+ return;
+ }
+ TestUtils.throwUnchecked(exception);
+ });
+ r.onClose(() -> {
+ closed.set(true);
+ });
+ try {
+ r.start();
+ } catch (Exception e) {
+ throw new Exception("Unexpected exception thrown from start()", e);
+ }
+ if (!exception.isPrinted()) {
+ throw new Exception("Expected stack trace from Exception to be printed");
+ }
+ if (!closed.get()) {
+ throw new Exception("Expected stream to be closed");
+ }
+ } finally {
+ t.cancel();
+ }
+ }
+
+ private static void testCustomException() throws Exception {
+ TestException exception = new TestException();
+ AtomicInteger counter = new AtomicInteger();
+ AtomicBoolean onError = new AtomicBoolean();
+ AtomicBoolean closed = new AtomicBoolean();
+ AtomicBoolean received = new AtomicBoolean();
+ Timer t = newEventEmitter();
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ if (counter.incrementAndGet() == 2) {
+ // Only close if we get a second event after an exception
+ r.close();
+ return;
+ }
+ TestUtils.throwUnchecked(exception);
+ });
+ r.onError(e -> {
+ received.set(e == exception);
+ onError.set(true);
+ });
+ r.onClose(() -> {
+ closed.set(true);
+ });
+ try {
+ r.start();
+ } catch (Exception e) {
+ throw new Exception("Unexpected exception thrown from start()", e);
+ }
+ if (!received.get()) {
+ throw new Exception("Did not receive expected exception in onError(...)");
+ }
+ if (exception.isPrinted()) {
+ throw new Exception("Expected stack trace from Exception NOT to be printed");
+ }
+ if (!onError.get()) {
+ throw new Exception("Expected OnError(...) to be invoked");
+ }
+ if (!closed.get()) {
+ throw new Exception("Expected stream to be closed");
+ }
+ } finally {
+ t.cancel();
+ }
+ }
+
+ private static void testOnFlushSanity() throws Exception {
+ TestException exception = new TestException();
+ AtomicBoolean received = new AtomicBoolean();
+ try (RecordingStream r = new RecordingStream()) {
+ r.onFlush(() -> {
+ TestUtils.throwUnchecked(exception);
+ });
+ r.onError(t -> {
+ received.set(t == exception);
+ r.close();
+ });
+ r.start();
+ if (!received.get()) {
+ throw new Exception("Expected exception in OnFlush to propagate to onError");
+ }
+ }
+ }
+
+ private static void testOnCloseSanity() throws Exception {
+ TestException exception = new TestException();
+ AtomicBoolean received = new AtomicBoolean();
+ try (RecordingStream r = new RecordingStream()) {
+ r.onFlush(() -> {
+ r.close(); // will trigger onClose
+ });
+ r.onClose(() -> {
+ TestUtils.throwUnchecked(exception); // will trigger onError
+ });
+ r.onError(t -> {
+ received.set(t == exception);
+ });
+ r.start();
+ if (!received.get()) {
+ throw new Exception("Expected exception in OnFlush to propagate to onError");
+ }
+ }
+ }
+
+ private static Timer newEventEmitter() {
+ Timer timer = new Timer();
+ TimerTask task = new TimerTask() {
+ @Override
+ public void run() {
+ TestEvent event = new TestEvent();
+ event.commit();
+ }
+ };
+ timer.schedule(task, 0, 100);
+ return timer;
+ }
+
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnEvent.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onEvent(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnEvent
+ */
+public class TestOnEvent {
+
+ @Name("A")
+ static class EventA extends Event {
+ }
+
+ @Name("A")
+ static class EventAlsoA extends Event {
+ }
+
+ @Name("C")
+ static class EventC extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testOnEventNull();
+ testOnEvent();
+ testNamedEvent();
+ testTwoEventWithSameName();
+ }
+
+ private static void testOnEventNull() {
+ log("Entering testOnEventNull()");
+ try (RecordingStream rs = new RecordingStream()) {
+ try {
+ rs.onEvent(null);
+ throw new AssertionError("Expected NullPointerException from onEvent(null)");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ try {
+ rs.onEvent("A", null);
+ throw new AssertionError("Expected NullPointerException from onEvent(\"A\", null)");
+
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ try {
+ String s = null;
+ rs.onEvent(s, null);
+ throw new AssertionError("Expected NullPointerException from onEvent(null, null)");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ }
+ log("Leaving testOnEventNull()");
+ }
+
+ private static void testTwoEventWithSameName() throws Exception {
+ log("Entering testTwoEventWithSameName()");
+ CountDownLatch eventA = new CountDownLatch(2);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent("A", e -> {
+ System.out.println("testTwoEventWithSameName" + e);
+ eventA.countDown();
+ });
+ r.startAsync();
+ EventA a1 = new EventA();
+ a1.commit();
+ EventAlsoA a2 = new EventAlsoA();
+ a2.commit();
+ eventA.await();
+ }
+ log("Leaving testTwoEventWithSameName()");
+ }
+
+ private static void testNamedEvent() throws Exception {
+ log("Entering testNamedEvent()");
+ try (RecordingStream r = new RecordingStream()) {
+ CountDownLatch eventA = new CountDownLatch(1);
+ CountDownLatch eventC = new CountDownLatch(1);
+ r.onEvent("A", e -> {
+ System.out.println("TestNamedEvent:" + e);
+ if (e.getEventType().getName().equals("A")) {
+ eventA.countDown();
+ }
+ });
+ r.onEvent("C", e -> {
+ System.out.println("TestNamedEvent:" + e);
+ if (e.getEventType().getName().equals("C")) {
+ eventC.countDown();
+ }
+ });
+
+ r.startAsync();
+ EventA a = new EventA();
+ a.commit();
+ EventC c = new EventC();
+ c.commit();
+ eventA.await();
+ eventC.await();
+ }
+ log("Leaving testNamedEvent()");
+ }
+
+ private static void testOnEvent() throws Exception {
+ log("Entering testOnEvent()");
+ try (RecordingStream r = new RecordingStream()) {
+ CountDownLatch event = new CountDownLatch(1);
+ r.onEvent(e -> {
+ event.countDown();
+ });
+ r.startAsync();
+ EventA a = new EventA();
+ a.commit();
+ event.await();
+ }
+ log("Leaving testOnEvent()");
+ }
+
+ private static void log(String msg) {
+ System.out.println(msg);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnFlush.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onFlush(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnFlush
+ */
+public class TestOnFlush {
+
+ static class OneEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testOnFlushNull();
+ testOneEvent();
+ testNoEvent();
+ }
+
+ private static void testOnFlushNull() {
+ log("Entering testOnFlushNull()");
+ try (RecordingStream rs = new RecordingStream()) {
+ try {
+ rs.onFlush(null);
+ throw new AssertionError("Expected NullPointerException from onFlush(null");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ }
+ log("Leaving testOnFlushNull()");
+ }
+
+ private static void testNoEvent() throws Exception {
+ log("Entering testNoEvent()");
+ CountDownLatch flush = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onFlush(() -> {
+ flush.countDown();
+ });
+ r.startAsync();
+ flush.await();
+ }
+ log("Leaving testNoEvent()");
+ }
+
+ private static void testOneEvent() throws InterruptedException {
+ log("Entering testOneEvent()");
+ CountDownLatch flush = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ // ignore event
+ });
+ r.onFlush(() -> {
+ flush.countDown();
+ });
+ r.startAsync();
+ OneEvent e = new OneEvent();
+ e.commit();
+ flush.await();
+ }
+ log("Leaving testOneEvent()");
+ }
+
+ private static void log(String msg) {
+ System.out.println(msg);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestRecursive.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.Events;
+
+/**
+ * @test
+ * @summary Tests that events are not emitted in handlers
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestRecursive
+ */
+public class TestRecursive {
+
+ public static class NotRecorded extends Event {
+ }
+
+ public static class Recorded extends Event {
+ }
+
+ public static class Provoker extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testSync();
+ testAsync();
+ }
+
+ private static void emit(AtomicBoolean stop) {
+ Runnable r = () -> {
+ while (!stop.get()) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ Provoker e = new Provoker();
+ e.commit();
+ }
+ };
+ Thread t = new Thread(r);
+ t.start();
+ }
+
+ private static void testSync() throws Exception {
+ try (Recording r = new Recording()) {
+ r.start();
+ AtomicBoolean stop = new AtomicBoolean(false);
+ emit(stop);
+ try (RecordingStream rs = new RecordingStream()) {
+ Recorded e1 = new Recorded();
+ e1.commit();
+ rs.onEvent(e -> {
+ if (!stop.get()) {
+ System.out.println("Emitting NotRecorded event");
+ NotRecorded event = new NotRecorded();
+ event.commit();
+ System.out.println("Stopping event provoker");
+ stop.set(true);
+ System.out.println("Closing recording stream");
+ rs.close();
+ return;
+ }
+ });
+ rs.start();
+ Recorded e2 = new Recorded();
+ e2.commit();
+ }
+ r.stop();
+ List<RecordedEvent> events = Events.fromRecording(r);
+ System.out.println(events);
+ if (count(events, NotRecorded.class) != 0) {
+ throw new Exception("Expected 0 NotRecorded events");
+ }
+ if (count(events, Recorded.class) == 2) {
+ throw new Exception("Expected 2 Recorded events");
+ }
+ }
+ }
+
+ private static int count(List<RecordedEvent> events, Class<?> eventClass) {
+ int count = 0;
+ for (RecordedEvent e : events) {
+ if (e.getEventType().getName().equals(eventClass.getName())) {
+ count++;
+ }
+ }
+ System.out.println(count);
+ return count;
+ }
+
+ private static void testAsync() throws InterruptedException, Exception {
+ CountDownLatch latchOne = new CountDownLatch(1);
+ CountDownLatch latchTwo = new CountDownLatch(2);
+ AtomicBoolean fail = new AtomicBoolean();
+ try (RecordingStream r = new RecordingStream()) {
+ r.onEvent(e -> {
+ System.out.println(e);
+ NotRecorded event = new NotRecorded();
+ event.commit();
+ if (e.getEventType().getName().equals(Recorded.class.getName())) {
+ latchOne.countDown();
+ latchTwo.countDown();
+ }
+ if (e.getEventType().getName().equals(NotRecorded.class.getName())) {
+ fail.set(true);
+ }
+ });
+ r.startAsync();
+ Recorded e1 = new Recorded();
+ e1.commit();
+ latchOne.await();
+ Recorded e2 = new Recorded();
+ e2.commit();
+ latchTwo.await();
+ if (fail.get()) {
+ throw new Exception("Unexpected event found");
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestRemove.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStrream::remove(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestRemove
+ */
+public class TestRemove {
+
+ static class RemoveEvent extends Event {
+
+ }
+
+ public static void main(String... args) throws Exception {
+ testRemoveNull();
+ testRemoveOnFlush();
+ testRemoveOnClose();
+ testRemoveOnEvent();
+ }
+
+ private static void testRemoveNull() {
+ log("Entering testRemoveNull()");
+ try (RecordingStream rs = new RecordingStream()) {
+ try {
+ rs.remove(null);
+ throw new AssertionError("Expected NullPointerException from remove(null");
+ } catch (NullPointerException npe) {
+ // OK; as expected
+ }
+ }
+ log("Leaving testRemoveNull()");
+ }
+
+ private static void testRemoveOnEvent() throws Exception {
+ log("Entering testRemoveOnEvent()");
+ try (RecordingStream rs = new RecordingStream()) {
+ AtomicInteger counter = new AtomicInteger(0);
+ CountDownLatch events = new CountDownLatch(2);
+ Consumer<RecordedEvent> c1 = e -> {
+ counter.incrementAndGet();
+ };
+
+ Consumer<RecordedEvent> c2 = e -> {
+ events.countDown();
+ };
+ rs.onEvent(c1);
+ rs.onEvent(c2);
+
+ rs.remove(c1);
+ rs.startAsync();
+ RemoveEvent r1 = new RemoveEvent();
+ r1.commit();
+ RemoveEvent r2 = new RemoveEvent();
+ r2.commit();
+ events.await();
+ if (counter.get() > 0) {
+ throw new AssertionError("OnEvent handler not removed!");
+ }
+ }
+ log("Leaving testRemoveOnEvent()");
+ }
+
+ private static void testRemoveOnClose() {
+ log("Entering testRemoveOnClose()");
+ try (RecordingStream rs = new RecordingStream()) {
+ AtomicBoolean onClose = new AtomicBoolean(false);
+ Runnable r = () -> {
+ onClose.set(true);
+ };
+ rs.onClose(r);
+ rs.remove(r);
+ rs.close();
+ if (onClose.get()) {
+ throw new AssertionError("onClose handler not removed!");
+ }
+ }
+ log("Leaving testRemoveOnClose()");
+ }
+
+ private static void testRemoveOnFlush() throws Exception {
+ log("Entering testRemoveOnFlush()");
+ try (RecordingStream rs = new RecordingStream()) {
+ AtomicInteger flushCount = new AtomicInteger(2);
+ AtomicBoolean removeExecuted = new AtomicBoolean(false);
+ Runnable onFlush1 = () -> {
+ removeExecuted.set(true);
+ };
+ Runnable onFlush2 = () -> {
+ flushCount.incrementAndGet();
+ };
+
+ rs.onFlush(onFlush1);
+ rs.onFlush(onFlush2);
+ rs.remove(onFlush1);
+ rs.startAsync();
+ while (flushCount.get() < 2) {
+ RemoveEvent r = new RemoveEvent();
+ r.commit();
+ Thread.sleep(100);
+ }
+
+ if (removeExecuted.get()) {
+ throw new AssertionError("onFlush handler not removed!");
+ }
+ }
+ log("Leaving testRemoveOnFlush()");
+ }
+
+ private static void log(String msg) {
+ System.out.println(msg);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetEndTime.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.Recording;
+import jdk.jfr.StackTrace;
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingFile;
+
+/**
+ * @test
+ * @summary Tests EventStream::setEndTime
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetEndTime
+ */
+public final class TestSetEndTime {
+
+ @Name("Mark")
+ @StackTrace(false)
+ public final static class Mark extends Event {
+ public boolean before;
+ }
+
+ public static void main(String... args) throws Exception {
+ try (Recording r = new Recording()) {
+ r.setFlushInterval(Duration.ofSeconds(1));
+ r.start();
+ Mark event1 = new Mark();
+ event1.begin(); // start time
+ event1.before = true;
+ advanceClock();
+ event1.commit();
+
+ Mark event2 = new Mark();
+ event2.begin(); // end time
+ advanceClock();
+ Thread.sleep(100);
+ event2.before = false;
+ event2.commit();
+
+ Path p = Paths.get("recording.jfr");
+ r.dump(p);
+ Instant start = null;
+ Instant end = null;
+ for (RecordedEvent e : RecordingFile.readAllEvents(p)) {
+ if (e.getBoolean("before")) {
+ start = e.getStartTime();
+ System.out.println("Start: " + start);
+ }
+ if (!e.getBoolean("before")) {
+ end = e.getStartTime();
+ System.out.println("End : " + end);
+ }
+ }
+ System.out.println("===================");
+ AtomicBoolean error = new AtomicBoolean(true);
+ try (EventStream d = EventStream.openRepository()) {
+ d.setStartTime(start);
+ d.setEndTime(end);
+ d.onEvent(e -> {
+ System.out.println(e);
+ System.out.println("Event:");
+ System.out.println(e.getStartTime());
+ System.out.println(e.getEndTime());
+ System.out.println(e.getBoolean("before"));
+ System.out.println();
+ boolean before = e.getBoolean("before");
+ if (before) {
+ error.set(false);
+ } else {
+ error.set(true);
+ }
+ });
+ d.start();
+ if (error.get()) {
+ throw new Exception("Found unexpected event!");
+ }
+ }
+ }
+ }
+
+ private static void advanceClock() {
+ // Wait for some clock movement with
+ // java.time clock resolution.
+ Instant now = Instant.now();
+ while (Instant.now().equals(now)) {
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetFlushInterval.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::setFlushInterval
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetFlushInterval
+ */
+public class TestSetFlushInterval {
+
+ public static void main(String... args) throws Exception {
+ Duration expectedDuration = Duration.ofMillis(1001);
+ try (RecordingStream r = new RecordingStream()) {
+ r.setFlushInterval(expectedDuration);
+ r.enable(EventNames.ActiveRecording);
+ r.onEvent(e -> {
+ System.out.println(e);
+ Duration duration = e.getDuration("flushInterval");
+ if (expectedDuration.equals(duration)) {
+ System.out.println("Closing recording");
+ r.close();
+ return;
+ }
+ System.out.println("Flush interval not set, was " + duration +
+ ", but expected " + expectedDuration);
+ });
+ r.start();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetMaxAge.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStrream::setMaxAge
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetMaxAge
+ */
+public class TestSetMaxAge {
+
+ public static void main(String... args) throws Exception {
+ Duration expecteddAge = Duration.ofMillis(123456789);
+ try (RecordingStream r = new RecordingStream()) {
+ r.setMaxAge(expecteddAge);
+ r.enable(EventNames.ActiveRecording);
+ r.onEvent(e -> {
+ System.out.println(e);
+ Duration age = e.getDuration("maxAge");
+ if (expecteddAge.equals(age)) {
+ r.close();
+ return;
+ }
+ System.out.println("Max age not set, was " + age + ", but expected " + expecteddAge);
+ });
+ r.start();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetMaxSize.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+* @test
+* @summary Tests RecordingStrream::setMaxSize
+* @key jfr
+* @requires vm.hasJFR
+* @library /test/lib
+* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetMaxSize
+*/
+public class TestSetMaxSize {
+
+ public static void main(String... args) throws Exception {
+ long testSize = 123456789;
+ try (RecordingStream r = new RecordingStream()) {
+ r.setMaxSize(123456789);
+ r.enable(EventNames.ActiveRecording);
+ r.onEvent(e -> {
+ System.out.println(e);
+ long size= e.getLong("maxSize");
+ if (size == testSize) {
+ r.close();
+ return;
+ }
+ System.out.println("Max size not set, was " + size + ", but expected " + testSize);
+ });
+ r.start();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetSettings.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::setSettings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetSettings
+ */
+public final class TestSetSettings {
+
+ @Name("LateBloomer")
+ private final static class LateBloomer extends Event {
+ }
+
+ private static CountDownLatch lateBloomer = new CountDownLatch(1);
+
+ public static void main(String... args) throws Exception {
+ try (RecordingStream r = new RecordingStream()) {
+ r.startAsync();
+ Map<String, String> settings = new HashMap<String, String>();
+ settings.put("LateBloomer#enabled", "true");
+ r.setSettings(settings);
+ r.onEvent("LateBloomer", e -> {
+ lateBloomer.countDown();
+ });
+ LateBloomer event = new LateBloomer();
+ event.commit();
+ lateBloomer.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetStartTime.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.Recording;
+import jdk.jfr.StackTrace;
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Tests EventStream::setStartTime
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetStartTime
+ */
+public final class TestSetStartTime {
+
+ @Name("Mark")
+ @StackTrace(false)
+ public final static class Mark extends Event {
+ public boolean before;
+ }
+
+ public static void main(String... args) throws Exception {
+ try (Recording r = new Recording()) {
+ r.setFlushInterval(Duration.ofSeconds(1));
+ r.start();
+ Mark event1 = new Mark();
+ event1.before = true;
+ event1.commit();
+ Thread.sleep(2000);
+ Instant now = Instant.now();
+ System.out.println("Instant.now() = " + now);
+ Thread.sleep(2000);
+ Mark event2 = new Mark();
+ event2.before = false;
+ event2.commit();
+ AtomicBoolean error = new AtomicBoolean();
+ try (EventStream d = EventStream.openRepository()) {
+ d.setStartTime(now);
+ d.onEvent(e -> {
+ System.out.println(e);
+ boolean early = e.getBoolean("before");
+ if (early) {
+ error.set(true);
+ } else {
+ // OK, as expected
+ d.close();
+ }
+ });
+ d.start();
+ if (error.get()) {
+ throw new Exception("Found unexpected event!");
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestStart.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::start()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestStart
+ */
+public class TestStart {
+ static class StartEvent extends Event {
+ }
+ static class EventProducer extends Thread {
+ private final Object lock = new Object();
+ private boolean killed = false;
+ public void run() {
+ while (true) {
+ StartEvent s = new StartEvent();
+ s.commit();
+ synchronized (lock) {
+ try {
+ lock.wait(10);
+ if (killed) {
+ return; // end thread
+ }
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ }
+ public void kill() {
+ synchronized (lock) {
+ this.killed = true;
+ lock.notifyAll();
+ }
+ }
+ }
+
+ public static void main(String... args) throws Exception {
+ testStart();
+ testStartOnEvent();
+ testStartTwice();
+ testStartClosed();
+ }
+
+ private static void testStartTwice() throws Exception {
+ log("Entering testStartTwice()");
+ CountDownLatch started = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ EventProducer t = new EventProducer();
+ t.start();
+ CompletableFuture.runAsync(() -> {
+ rs.start();
+ });
+ rs.onEvent(e -> {
+ if (started.getCount() > 0) {
+ started.countDown();
+ }
+ });
+ started.await();
+ t.kill();
+ try {
+ rs.start();
+ throw new AssertionError("Expected IllegalStateException if started twice");
+ } catch (IllegalStateException ise) {
+ // OK, as expected
+ }
+ }
+ log("Leaving testStartTwice()");
+ }
+
+ static void testStart() throws Exception {
+ log("Entering testStart()");
+ CountDownLatch started = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(e -> {
+ started.countDown();
+ });
+ EventProducer t = new EventProducer();
+ t.start();
+ CompletableFuture.runAsync(() -> {
+ rs.start();
+ });
+ started.await();
+ t.kill();
+ }
+ log("Leaving testStart()");
+ }
+
+ static void testStartOnEvent() throws Exception {
+ log("Entering testStartOnEvent()");
+ AtomicBoolean ISE = new AtomicBoolean(false);
+ CountDownLatch startedTwice = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(e -> {
+ try {
+ rs.start(); // must not deadlock
+ } catch (IllegalStateException ise) {
+ if (!ISE.get()) {
+ ISE.set(true);
+ startedTwice.countDown();
+ }
+ }
+ });
+ EventProducer t = new EventProducer();
+ t.start();
+ CompletableFuture.runAsync(() -> {
+ rs.start();
+ });
+ startedTwice.await();
+ t.kill();
+ if (!ISE.get()) {
+ throw new AssertionError("Expected IllegalStateException");
+ }
+ }
+ log("Leaving testStartOnEvent()");
+ }
+
+ static void testStartClosed() {
+ log("Entering testStartClosed()");
+ RecordingStream rs = new RecordingStream();
+ rs.close();
+ try {
+ rs.start();
+ throw new AssertionError("Expected IllegalStateException");
+ } catch (IllegalStateException ise) {
+ // OK, as expected.
+ }
+ log("Leaving testStartClosed()");
+ }
+
+ private static void log(String msg) {
+ System.out.println(msg);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestStartAsync.java Wed Oct 09 23:22:56 2019 +0200
@@ -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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::startAsync()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestStartAsync
+ */
+public class TestStartAsync {
+ static class StartEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ testStart();
+ testStartTwice();
+ testStartClosed();
+ }
+
+ private static void testStartTwice() throws Exception {
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.startAsync();
+ try {
+ rs.startAsync();
+ throw new AssertionError("Expected IllegalStateException if started twice");
+ } catch (IllegalStateException ise) {
+ // OK, as expected
+ }
+ }
+ }
+
+ static void testStart() throws Exception {
+ CountDownLatch started = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(e -> {
+ started.countDown();
+ });
+ rs.startAsync();
+ StartEvent e = new StartEvent();
+ e.commit();
+ started.await();
+ }
+ }
+
+ static void testStartClosed() {
+ RecordingStream rs = new RecordingStream();
+ rs.close();
+ try {
+ rs.startAsync();
+ throw new AssertionError("Expected IllegalStateException");
+ } catch (IllegalStateException ise) {
+ // OK, as expected.
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestUtils.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+public class TestUtils {
+
+ public static final class TestError extends Error {
+ private static final long serialVersionUID = 1L;
+ }
+
+ public static final class TestException extends Exception {
+ private static final long serialVersionUID = 1L;
+ private volatile boolean printed;
+
+ @Override
+ public void printStackTrace() {
+ super.printStackTrace();
+ printed = true;
+ }
+
+ public boolean isPrinted() {
+ return printed;
+ }
+ }
+
+ // Can throw checked exception as unchecked.
+ @SuppressWarnings("unchecked")
+ public static <T extends Throwable> void throwUnchecked(Throwable e) throws T {
+ throw (T) e;
+ }
+
+ public static void installUncaughtException(CountDownLatch receivedError, Throwable expected) {
+ Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> {
+ if (throwable == expected) {
+ System.out.println("Received uncaught exception " + expected.getClass());
+ receivedError.countDown();
+ }
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/DriverRecordingDumper.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.security;
+
+import java.nio.file.Paths;
+
+import jdk.jfr.Recording;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * Driver that dumps a recording with a JVMInformation event
+ *
+ * Usage:
+ *
+ * @run driver jdk.jfr.api.consumer.security.RecordingDumper <filename>
+ */
+public class DriverRecordingDumper {
+ public static void main(String... args) throws Exception {
+ try (Recording r = new Recording()) {
+ r.enable(EventNames.JVMInformation);
+ r.start();
+ r.stop();
+ r.dump(Paths.get(args[0]));
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestMissingPermission.java Wed Oct 09 23:22:56 2019 +0200
@@ -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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.security;
+
+import java.io.IOException;
+
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests that streaming doesn't work if
+ * FlightRecordingPermission("accessFlightRecorder") is missing
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy
+ * jdk.jfr.api.consumer.security.TestMissingPermission
+ */
+public class TestMissingPermission {
+
+ public static void main(String... args) throws Exception {
+ testOpenRepository();
+ testRecordingStream();
+ }
+
+ private static void testRecordingStream() throws IOException {
+ try {
+ try (EventStream es = EventStream.openRepository()) {
+ throw new AssertionError("Should not be able to create EventStream without FlightRecorderPermission");
+ }
+ } catch (SecurityException se) {
+ // OK, as expected
+ }
+ }
+
+ private static void testOpenRepository() throws IOException {
+ try {
+ try (RecordingStream es = new RecordingStream()) {
+ throw new AssertionError("Should not be able to create RecordingStream without FlightRecorderPermission");
+ }
+ } catch (SecurityException se) {
+ // OK, as expected
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestRecordingFile.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.security;
+
+import java.nio.file.Paths;
+
+import jdk.jfr.consumer.RecordingFile;
+
+/**
+ * @test
+ * @summary Test that a recording file can't be opened without permissions
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run driver jdk.jfr.api.consumer.security.DriverRecordingDumper
+ * test-recording-file.jfr
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy
+ * jdk.jfr.api.consumer.security.TestRecordingFile
+ * test-recording-file.jfr
+ */
+public class TestRecordingFile {
+ public static void main(String... args) throws Exception {
+ try {
+ RecordingFile.readAllEvents(Paths.get(args[0]));
+ throw new AssertionError("Expected SecurityException");
+ } catch (SecurityException se) {
+ // OK, as expected
+ return;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestRecordingStream.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.security;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests that a RecordingStream works using only
+ * FlightRecordingPermission("accessFlightRecorder")
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=local-streaming.policy
+ * jdk.jfr.api.consumer.security.TestStreamingLocal
+ */
+public class TestRecordingStream {
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ try (RecordingStream r = new RecordingStream()) {
+ // Enable JVM event, no write permission needed
+ r.enable(EventNames.JVMInformation);
+ r.setStartTime(Instant.EPOCH);
+ r.onEvent(EventNames.JVMInformation, e -> {
+ latch.countDown();
+ });
+ r.startAsync();
+ latch.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingFile.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.security;
+
+import java.nio.file.Paths;
+
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Test that an event file stream can't be opened without permissions
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run driver jdk.jfr.api.consumer.security.DriverRecordingDumper
+ * test-streaming-file.jfr
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy
+ * jdk.jfr.api.consumer.security.TestStreamingFile
+ * test-streaming-file.jfr
+ */
+public class TestStreamingFile {
+
+ public static void main(String... args) throws Exception {
+ try (EventStream es = EventStream.openFile(Paths.get(args[0]))) {
+ throw new AssertionError("Expected SecurityException");
+ } catch (SecurityException se) {
+ // OK, as expected
+ return;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingLocal.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.security;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests that local streaming works using only
+ * FlightRecordingPermission("accessFlightRecorder")
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=local-streaming.policy
+ * jdk.jfr.api.consumer.security.TestStreamingLocal
+ */
+public class TestStreamingLocal {
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ try (Recording r = new Recording()) {
+ // Enable JVM event, no write permission needed
+ r.enable(EventNames.JVMInformation);
+ r.start();
+ r.stop();
+ try (EventStream es = EventStream.openRepository()) {
+ es.setStartTime(Instant.EPOCH);
+ es.onEvent("jdk.JVMInformation", e -> {
+ latch.countDown();
+ });
+ es.startAsync();
+ latch.await();
+ }
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingRemote.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.security;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.time.Instant;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+/**
+ * @test
+ * @summary Test that a stream can be opened against a remote repository using
+ * only file permission
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm jdk.jfr.api.consumer.security.TestStreamingRemote
+ */
+public class TestStreamingRemote {
+
+ private static final String SUCCESS = "Success!";
+
+ public static class TestEvent extends Event {
+ }
+
+ public static class Test {
+ public static void main(String... args) throws Exception {
+ Path repo = Paths.get(args[0]);
+ System.out.println("Repository: " + repo);
+ try (EventStream es = EventStream.openRepository(repo)) {
+ es.setStartTime(Instant.EPOCH);
+ es.onEvent(e -> {
+ System.out.println(SUCCESS);
+ es.close();
+ });
+ es.start();
+ }
+ }
+ }
+
+ public static void main(String... args) throws Exception {
+ try (Recording r = new Recording()) {
+ r.setFlushInterval(Duration.ofSeconds(1));
+ r.start();
+ String repository = System.getProperty("jdk.jfr.repository");
+ Path policy = createPolicyFile(repository);
+ TestEvent e = new TestEvent();
+ e.commit();
+ String[] c = new String[4];
+ c[0] = "-Djava.security.manager";
+ c[1] = "-Djava.security.policy=" + escapeBackslashes(policy.toString());
+ c[2] = Test.class.getName();
+ c[3] = repository;
+ OutputAnalyzer oa = ProcessTools.executeTestJvm(c);
+ oa.shouldContain(SUCCESS);
+ }
+ }
+
+ private static Path createPolicyFile(String repository) throws IOException {
+ Path policy = Paths.get("permission.policy").toAbsolutePath();
+ try (PrintWriter pw = new PrintWriter(policy.toFile())) {
+ pw.println("grant {");
+ // All the files and directories the contained in path
+ String dir = escapeBackslashes(repository);
+ String contents = escapeBackslashes(repository + File.separatorChar + "-");
+ pw.println(" permission java.io.FilePermission \"" + dir + "\", \"read\";");
+ pw.println(" permission java.io.FilePermission \"" + contents + "\", \"read\";");
+ pw.println("};");
+ pw.println();
+ }
+ System.out.println("Permission file: " + policy);
+ for (String line : Files.readAllLines(policy)) {
+ System.out.println(line);
+ }
+ System.out.println();
+ return policy;
+ }
+
+ // Needed for Windows
+ private static String escapeBackslashes(String text) {
+ return text.replace("\\", "\\\\");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/local-streaming.policy Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,4 @@
+// Minimum policy to stream locally
+grant {
+permission jdk.jfr.FlightRecorderPermission "accessFlightRecorder";
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/no-permission.policy Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,3 @@
+// No permission
+grant {
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestChunkGap.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Tests that a stream can gracefully handle chunk being removed in the
+ * middle
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFilledChunks
+ */
+public class TestChunkGap {
+
+ static class StartEvent extends Event {
+ }
+
+ static class TestGapEvent extends Event {
+ }
+
+ static class EndEvent extends Event {
+ }
+
+ static long count;
+
+ public static void main(String... args) throws Exception {
+
+ CountDownLatch gap = new CountDownLatch(1);
+ try (EventStream s = EventStream.openRepository()) {
+ try (Recording r1 = new Recording()) {
+ s.setStartTime(Instant.EPOCH);
+ s.onEvent(e -> {
+ System.out.println(e);
+ try {
+ gap.await();
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ count++;
+ if (e.getEventType().getName().equals(EndEvent.class.getName())) {
+ s.close();
+ }
+ });
+ s.startAsync();
+
+ r1.enable(StartEvent.class);
+ r1.start();
+ StartEvent event1 = new StartEvent();
+ event1.commit();
+ r1.stop();
+
+ // create chunk that is removed
+ try (Recording r2 = new Recording()) {
+ r2.enable(TestGapEvent.class);
+ r2.start();
+ TestGapEvent event2 = new TestGapEvent();
+ event2.commit();
+ r2.stop();
+ }
+ gap.countDown();
+ try (Recording r3 = new Recording()) {
+ r3.enable(EndEvent.class);
+ r3.start();
+ EndEvent event3 = new EndEvent();
+ event3.commit();
+ r3.stop();
+
+ s.awaitTermination();
+ if (count != 2) {
+ throw new AssertionError("Expected 2 event, but got " + count);
+ }
+ }
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestEmptyChunks.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test that it is possible to iterate over chunk without normal events
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestEmptyChunks
+ */
+public class TestEmptyChunks {
+ static class EndEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch end = new CountDownLatch(1);
+ try (RecordingStream es = new RecordingStream()) {
+ es.onEvent(EndEvent.class.getName(), e -> {
+ end.countDown();
+ });
+ es.startAsync();
+ Recording r1 = new Recording();
+ r1.start();
+ System.out.println("Chunk 1 started");
+ Recording r2 = new Recording();
+ r2.start();
+ System.out.println("Chunk 2 started");
+ Recording r3 = new Recording();
+ r3.start();
+ System.out.println("Chunk 3 started");
+ r2.stop();
+ System.out.println("Chunk 4 started");
+ r3.stop();
+ System.out.println("Chunk 5 started");
+ EndEvent e = new EndEvent();
+ e.commit();
+ end.await();
+ r1.stop();
+ System.out.println("Chunk 5 ended");
+ r1.close();
+ r2.close();
+ r3.close();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestEnableEvents.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents from specified event
+ * settings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestEnableEvents
+ */
+public class TestEnableEvents {
+
+ @Enabled(false)
+ static class HorseEvent extends Event {
+ }
+
+ @Enabled(false)
+ static class ElephantEvent extends Event {
+ }
+
+ @Enabled(false)
+ static class TigerEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch elephantLatch = new CountDownLatch(1);
+ CountDownLatch tigerLatch = new CountDownLatch(1);
+ CountDownLatch horseLatch = new CountDownLatch(1);
+
+ FlightRecorder.addPeriodicEvent(ElephantEvent.class, () -> {
+ HorseEvent ze = new HorseEvent();
+ ze.commit();
+ });
+
+ try (RecordingStream s = new RecordingStream()) {
+ s.enable(HorseEvent.class.getName()).withPeriod(Duration.ofMillis(50));
+ s.enable(TigerEvent.class.getName());
+ s.enable(ElephantEvent.class.getName());
+ s.onEvent(TigerEvent.class.getName(), e -> {
+ System.out.println("Event: " + e.getEventType().getName());
+ System.out.println("Found tiger!");
+ tigerLatch.countDown();
+ });
+ s.onEvent(HorseEvent.class.getName(), e -> {
+ System.out.println("Found horse!");
+ horseLatch.countDown();
+ });
+ s.onEvent(ElephantEvent.class.getName(), e -> {
+ System.out.println("Found elelphant!");
+ elephantLatch.countDown();
+ });
+ s.startAsync();
+ TigerEvent te = new TigerEvent();
+ te.commit();
+ ElephantEvent ee = new ElephantEvent();
+ ee.commit();
+ elephantLatch.await();
+ horseLatch.await();
+ tigerLatch.await();
+ }
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestEventRegistration.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Registered;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test that it is possible to register new metadata in a chunk
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestEventRegistration
+ */
+public class TestEventRegistration {
+ @Registered(false)
+ static class StreamEvent1 extends Event {
+ }
+
+ @Registered(false)
+ static class StreamEvent2 extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+
+ CountDownLatch s1Latch = new CountDownLatch(1);
+ CountDownLatch s2Latch = new CountDownLatch(1);
+ try (RecordingStream es = new RecordingStream()) {
+ es.onEvent(StreamEvent1.class.getName(), e -> {
+ s1Latch.countDown();
+ });
+ es.onEvent(StreamEvent2.class.getName(), e -> {
+ s2Latch.countDown();
+ });
+ es.startAsync();
+ System.out.println("Registering " + StreamEvent1.class.getName());
+ FlightRecorder.register(StreamEvent1.class);
+ StreamEvent1 s1 = new StreamEvent1();
+ s1.commit();
+ System.out.println(StreamEvent1.class.getName() + " commited");
+ System.out.println("Awaiting latch for " + StreamEvent1.class.getName());
+ s1Latch.await();
+ System.out.println();
+ System.out.println("Registering " + StreamEvent2.class.getName());
+ FlightRecorder.register(StreamEvent2.class);
+ StreamEvent2 s2 = new StreamEvent2();
+ s2.commit();
+ System.out.println(StreamEvent2.class.getName() + " commited");
+ System.out.println("Awaiting latch for " + StreamEvent2.class.getName());
+ s2Latch.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test that it is possible to iterate over chunk with normal events
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFilledChunks
+ */
+public class TestFilledChunks {
+
+ static class FillEvent extends Event {
+ String message;
+ int value;
+ int id;
+ }
+
+ static class EndEvent extends Event {
+ }
+
+ // Will generate about 100 MB of data, or 8-9 chunks
+ private static final int EVENT_COUNT = 5_000_000;
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch end = new CountDownLatch(1);
+ AtomicInteger idCounter = new AtomicInteger();
+ try (RecordingStream es = new RecordingStream()) {
+ es.onEvent(EndEvent.class.getName(), e -> end.countDown());
+ es.onEvent(FillEvent.class.getName(), e -> {
+ idCounter.incrementAndGet();
+// if (id != expected) {
+// throw new Error("Expected id " + expected + ", but got " + id);
+// }
+ });
+ es.startAsync();
+ long seed = System.currentTimeMillis();
+ System.out.println("Random seed: " + seed);
+ Random r = new Random(seed);
+ for (int i = 1; i < EVENT_COUNT; i++) {
+ FillEvent f = new FillEvent();
+ f.message = i %2 == 0 ? "ko" : "kak";
+ f.value = r.nextInt(10000);
+ f.id = i;
+ f.commit();
+ if (i % 1_000_000 == 0) {
+ System.out.println("Emitted " + i + " events");
+ }
+ }
+ System.out.println("Awaiting end event");
+ Thread.sleep(1_000);
+ for (int i = 1; i < EVENT_COUNT; i++) {
+ EndEvent e = new EndEvent();
+ e.commit();
+ }
+ end.await();
+
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestFiltering.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to filter a stream for an event
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFiltering
+ */
+public class TestFiltering {
+
+ static class SnakeEvent extends Event {
+ int id;
+ }
+
+ static class EelEvent extends Event {
+ int id;
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch l = new CountDownLatch(1);
+ String eventName = SnakeEvent.class.getName();
+ AtomicInteger idCounter = new AtomicInteger(-1);
+ try (RecordingStream e = new RecordingStream()) {
+ e.onEvent(eventName, event -> {
+ if (!event.getEventType().getName().equals(eventName)) {
+ throw new InternalError("Unexpected event " + e);
+ }
+ if (event.getInt("id") != idCounter.incrementAndGet()) {
+ throw new InternalError("Incorrect id");
+ }
+ if (idCounter.get() == 99) {
+ l.countDown();
+ }
+ });
+ e.startAsync();
+ for (int i = 0; i < 100; i++) {
+ SnakeEvent se = new SnakeEvent();
+ se.id = i;
+ se.commit();
+
+ EelEvent ee = new EelEvent();
+ ee.id = i;
+ ee.commit();
+ }
+ l.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestLatestEvent.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that a stream from a repository starts at the latest event
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestLatestEvent
+ */
+public class TestLatestEvent {
+
+ @Name("Chunk")
+ static class ChunkEvent extends Event {
+ boolean end;
+ }
+
+ public static void main(String... args) throws Exception {
+ try (RecordingStream r = new RecordingStream()) {
+ r.startAsync();
+ // Create chunks with events in the repository
+ for (int i = 0; i < 5; i++) {
+ try (Recording r1 = new Recording()) {
+ r1.start();
+ ChunkEvent e = new ChunkEvent();
+ e.end = false;
+ e.commit();
+ r1.stop();
+ }
+ }
+ CountDownLatch endEventRecevied = new CountDownLatch(1);
+ CountDownLatch emitEvent = new CountDownLatch(1);
+ try (EventStream s = EventStream.openRepository()) {
+ s.onEvent("Chunk", e -> {
+ if (e.getBoolean("end")) {
+ endEventRecevied.countDown();
+ return;
+ }
+ System.out.println("Stream should start at latest event:");
+ System.out.println(e);
+ });
+
+ ChunkEvent e1 = new ChunkEvent();
+ e1.end = false;
+ e1.commit();
+ s.startAsync();
+ s.onFlush(() -> {
+ emitEvent.countDown();
+ });
+ emitEvent.await();
+ ChunkEvent e2 = new ChunkEvent();
+ e2.end = true;
+ e2.commit();
+
+ endEventRecevied.await();
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRecordingBefore.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to start a stream when there are
+ * already chunk in the repository
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestRecordingBefore
+ */
+public class TestRecordingBefore {
+
+ static class SnakeEvent extends Event {
+ int id;
+ }
+
+ public static void main(String... args) throws Exception {
+
+ try (Recording r1 = new Recording()) {
+ r1.start();
+ emitSnakeEvent(1);
+ emitSnakeEvent(2);
+ emitSnakeEvent(3);
+ // Force a chunk rotation
+ try (Recording r2 = new Recording()) {
+ r2.start();
+ emitSnakeEvent(4);
+ emitSnakeEvent(5);
+ emitSnakeEvent(6);
+ r2.stop();
+ }
+ r1.stop();
+ // Two chunks should now exist in the repository
+ AtomicBoolean fail = new AtomicBoolean(false);
+ CountDownLatch lastEvent = new CountDownLatch(1);
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.onEvent(e -> {
+ long id = e.getLong("id");
+ if (id < 7) {
+ System.out.println("Found unexpected id " + id);
+ fail.set(true);
+ }
+ if (id == 9) {
+ lastEvent.countDown();
+ }
+ });
+ rs.startAsync();
+ emitSnakeEvent(7);
+ emitSnakeEvent(8);
+ emitSnakeEvent(9);
+ lastEvent.await();
+ if (fail.get()) {
+ throw new Exception("Found events from a previous recording");
+ }
+ }
+ }
+ }
+
+ static void emitSnakeEvent(int id) {
+ SnakeEvent e = new SnakeEvent();
+ e.id = id;
+ e.commit();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRemovedChunks.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests that a stream can gracefully handle chunk being removed
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestRemovedChunks
+ */
+public class TestRemovedChunks {
+ private final static CountDownLatch parkLatch = new CountDownLatch(1);
+ private final static CountDownLatch removalLatch = new CountDownLatch(1);
+ private final static CountDownLatch IFeelFineLatch = new CountDownLatch(1);
+
+ static class DataEvent extends Event {
+ double double1;
+ double double2;
+ double double3;
+ double double4;
+ double double5;
+ }
+
+ static class ParkStream extends Event {
+ }
+
+ static class IFeelFine extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+
+ try (RecordingStream s = new RecordingStream()) {
+ s.setMaxSize(5_000_000);
+ s.onEvent(ParkStream.class.getName(), e -> {
+ parkLatch.countDown();
+ await(removalLatch);
+
+ });
+ s.onEvent(IFeelFine.class.getName(), e -> {
+ IFeelFineLatch.countDown();
+ });
+ s.startAsync();
+ // Fill first chunk with data
+ emitData(1_000_000);
+ // Park stream
+ ParkStream ps = new ParkStream();
+ ps.commit();
+ await(parkLatch);
+ // Rotate and emit data that exceeds maxSize
+ for (int i = 0; i< 10;i++) {
+ try (Recording r = new Recording()) {
+ r.start();
+ emitData(1_000_000);
+ }
+ }
+ // Emit final event
+ IFeelFine i = new IFeelFine();
+ i.commit();
+ // Wake up parked stream
+ removalLatch.countDown();
+ // Await event things gone bad
+ await(IFeelFineLatch);
+ }
+ }
+
+ private static void await(CountDownLatch latch) throws Error {
+ try {
+ latch.await();
+ } catch (InterruptedException e1) {
+ throw new Error("Latch interupted");
+ }
+ }
+
+ private static void emitData(int amount) throws InterruptedException {
+ int count = 0;
+ while (amount > 0) {
+ DataEvent de = new DataEvent();
+ // 5 doubles are 40 bytes bytes
+ // and event size, event type, thread,
+ // start time, duration and stack trace about 15 bytes
+ de.double1 = 0.0;
+ de.double2 = 1.0;
+ de.double3 = 2.0;
+ de.double4 = 3.0;
+ de.double5 = 4.0;
+ de.commit();
+ amount -= 55;
+ count++;
+ //
+ if (count % 100_000 == 0) {
+ Thread.sleep(10);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRepositoryMigration.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.jcmd.JcmdHelper;
+
+/**
+ * @test
+ * @summary Verifies that is possible to stream from a repository that is being
+ * moved.
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestRepositoryMigration
+ */
+public class TestRepositoryMigration {
+ static class MigrationEvent extends Event {
+ int id;
+ }
+
+ public static void main(String... args) throws Exception {
+ Path newRepository = Paths.get("new-repository");
+ CountDownLatch event1 = new CountDownLatch(1);
+ CountDownLatch event2 = new CountDownLatch(1);
+
+ try (EventStream es = EventStream.openRepository()) {
+ es.setStartTime(Instant.EPOCH);
+ es.onEvent(e -> {
+ System.out.println(e);
+ if (e.getInt("id") == 1) {
+ event1.countDown();
+ }
+ if (e.getInt("id") == 2) {
+ event2.countDown();
+ }
+ });
+ es.startAsync();
+ System.out.println("Started es.startAsync()");
+
+ try (Recording r = new Recording()) {
+ r.setFlushInterval(Duration.ofSeconds(1));
+ r.start();
+ // Chunk in default repository
+ MigrationEvent e1 = new MigrationEvent();
+ e1.id = 1;
+ e1.commit();
+ event1.await();
+ System.out.println("Passed the event1.await()");
+ JcmdHelper.jcmd("JFR.configure", "repositorypath=" + newRepository.toAbsolutePath());
+ // Chunk in new repository
+ MigrationEvent e2 = new MigrationEvent();
+ e2.id = 2;
+ e2.commit();
+ r.stop();
+ event2.await();
+ System.out.println("Passed the event2.await()");
+ // Verify that it happened in new repository
+ if (!Files.exists(newRepository)) {
+ throw new AssertionError("Could not find repository " + newRepository);
+ }
+ System.out.println("Listing contents in new repository:");
+ boolean empty = true;
+ for (Path p : Files.newDirectoryStream(newRepository)) {
+ System.out.println(p.toAbsolutePath());
+ empty = false;
+ }
+ System.out.println();
+ if (empty) {
+ throw new AssertionError("Could not find contents in new repository location " + newRepository);
+ }
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRepositoryProperty.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Properties;
+
+import com.sun.tools.attach.AttachNotSupportedException;
+import com.sun.tools.attach.VirtualMachine;
+
+import jdk.jfr.Recording;
+import jdk.test.lib.dcmd.CommandExecutor;
+import jdk.test.lib.dcmd.PidJcmdExecutor;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to access JFR repository from a system
+ * property
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @modules jdk.attach
+ * jdk.jfr
+ * @run main/othervm -Djdk.attach.allowAttachSelf=true jdk.jfr.api.consumer.streaming.TestRepositoryProperty
+ */
+public class TestRepositoryProperty {
+
+ private final static String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
+
+ public static void main(String... args) throws Exception {
+ testBeforeInitialization();
+ testAfterInitialization();
+ testFromAgent();
+ testAfterChange();
+ }
+
+ private static void testFromAgent() throws AttachNotSupportedException, IOException {
+ String pidText = String.valueOf(ProcessHandle.current().pid());
+ VirtualMachine vm = VirtualMachine.attach(pidText);
+ Properties p = vm.getSystemProperties();
+ String location = (String) p.get(JFR_REPOSITORY_LOCATION_PROPERTY);
+ if (location == null) {
+ throw new AssertionError("Could not find repository path in agent properties");
+ }
+ Path path = Path.of(location);
+ if (!Files.isDirectory(path)) {
+ throw new AssertionError("Repository path doesn't point to directory");
+ }
+ }
+
+ private static void testAfterChange() {
+ Path newRepository = Path.of(".").toAbsolutePath();
+
+ String cmd = "JFR.configure repository=" + newRepository.toString();
+ CommandExecutor executor = new PidJcmdExecutor();
+ executor.execute(cmd);
+ String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
+ if (newRepository.toString().equals(location)) {
+ throw new AssertionError("Repository path not updated after it has been changed");
+ }
+ }
+
+ private static void testAfterInitialization() {
+ try (Recording r = new Recording()) {
+ r.start();
+ String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
+ if (location == null) {
+ throw new AssertionError("Repository path should exist before JFR is initialized");
+ }
+ System.out.println("repository=" + location);
+ Path p = Path.of(location);
+ if (!Files.isDirectory(p)) {
+ throw new AssertionError("Repository path doesn't point to directory");
+ }
+ }
+
+ }
+
+ private static void testBeforeInitialization() {
+ String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
+ if (location != null) {
+ throw new AssertionError("Repository path should not exist before JFR is initialized");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Name;
+import jdk.jfr.Period;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents of ongoing
+ * recordings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+streaming=trace
+ * jdk.jfr.api.consumer.streaming.TestStartMultiChunk
+ */
+public class TestStartMultiChunk {
+
+ @Period("10 s")
+ @Name("Zebra")
+ static class ZebraEvent extends Event {
+ }
+
+ @Name("Cat")
+ static class CatEvent extends Event {
+ }
+
+ @Name("Dog")
+ static class DogEvent extends Event {
+ }
+
+ @Name("Mouse")
+ static class MouseEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch dogLatch = new CountDownLatch(1);
+ CountDownLatch catLatch = new CountDownLatch(1);
+ CountDownLatch mouseLatch = new CountDownLatch(1);
+ CountDownLatch zebraLatch = new CountDownLatch(3);
+
+ FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> {
+ ZebraEvent ze = new ZebraEvent();
+ ze.commit();
+ System.out.println("Zebra emitted");
+ });
+
+ try (RecordingStream s = new RecordingStream()) {
+ s.onEvent("Cat", e -> {
+ System.out.println("Found cat!");
+ catLatch.countDown();
+ });
+ s.onEvent("Dog", e -> {
+ System.out.println("Found dog!");
+ dogLatch.countDown();
+ });
+ s.onEvent("Zebra", e -> {
+ System.out.println("Found zebra!");
+ zebraLatch.countDown();
+ });
+ s.onEvent("Mouse", e -> {
+ System.out.println("Found mouse!");
+ mouseLatch.countDown();
+ });
+ s.startAsync();
+ System.out.println("Stream recoding started");
+
+ try (Recording r1 = new Recording()) {
+ r1.start();
+ System.out.println("r1.start()");
+ MouseEvent me = new MouseEvent();
+ me.commit();
+ System.out.println("Mouse emitted");
+ mouseLatch.await();
+ try (Recording r2 = new Recording()) { // force chunk rotation
+ // in stream
+ r2.start();
+ System.out.println("r2.start()");
+ DogEvent de = new DogEvent();
+ de.commit();
+ System.out.println("Dog emitted");
+ dogLatch.await();
+ CatEvent ce = new CatEvent();
+ ce.commit();
+ System.out.println("Cat emitted");
+ catLatch.await();
+ zebraLatch.await();
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartSingleChunk.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Period;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents of ongoing
+ * recordings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+streaming=trace
+ * jdk.jfr.api.consumer.streaming.TestStartSingleChunk
+ */
+public class TestStartSingleChunk {
+
+ @Period("500 ms")
+ static class ElkEvent extends Event {
+ }
+
+ static class FrogEvent extends Event {
+ }
+
+ static class LionEvent extends Event {
+ }
+
+ public static void main(String... args) throws Exception {
+ CountDownLatch frogLatch = new CountDownLatch(1);
+ CountDownLatch lionLatch = new CountDownLatch(1);
+ CountDownLatch elkLatch = new CountDownLatch(3);
+
+ FlightRecorder.addPeriodicEvent(ElkEvent.class, () -> {
+ ElkEvent ee = new ElkEvent();
+ ee.commit();
+ });
+ try (RecordingStream s = new RecordingStream()) {
+ s.onEvent(ElkEvent.class.getName(), e -> {
+ System.out.println("Found elk!");
+ elkLatch.countDown();
+ });
+ s.onEvent(LionEvent.class.getName(), e -> {
+ System.out.println("Found lion!");
+ lionLatch.countDown();
+ });
+ s.onEvent(FrogEvent.class.getName(), e -> {
+ System.out.println("Found frog!");
+ frogLatch.countDown();
+ });
+ s.startAsync();
+ FrogEvent fe = new FrogEvent();
+ fe.commit();
+
+ LionEvent le = new LionEvent();
+ le.commit();
+
+ frogLatch.await();
+ lionLatch.await();
+ elkLatch.await();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestUnstarted.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.consumer.streaming;
+
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to open a stream when a repository doesn't
+ * exists
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestUnstarted
+ */
+public class TestUnstarted {
+ public static void main(String... args) throws Exception {
+ try (EventStream es = EventStream.openRepository()) {
+ es.onEvent(e -> {
+ // ignore
+ });
+ es.startAsync();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/event/TestEventDuration.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.api.event;
+
+import java.util.List;
+
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.test.lib.jfr.Events;
+import jdk.test.lib.jfr.SimpleEvent;
+
+/**
+ * @test
+ * @summary Tests that a duration is recorded.
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.event.TestEventDuration
+ */
+public class TestEventDuration {
+
+ public static int counter;
+
+ public static void main(String[] args) throws Exception {
+
+ try(Recording r = new Recording()) {
+ r.start();
+ SimpleEvent e = new SimpleEvent();
+ e.begin();
+ for (int i = 0; i < 10_000;i++) {
+ counter+=i;
+ }
+ e.end();
+ e.commit();
+
+ r.stop();
+ List<RecordedEvent> events = Events.fromRecording(r);
+ if (events.get(0).getDuration().toNanos() < 1) {
+ throw new AssertionError("Expected a duration");
+ }
+ }
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/recording/time/TestSetFlushInterval.java Wed Oct 09 23:22:56 2019 +0200
@@ -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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.api.recording.time;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.test.lib.Asserts;
+
+/**
+ * @test
+ * @key jfr
+ * @summary Test Recording::SetFlushInterval(...) and
+ * Recording::getFlushInterval()
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.recording.time.TestSetFlushInterval
+ */
+
+public class TestSetFlushInterval {
+
+ public static void main(String[] args) throws Throwable {
+ testSetGet();
+ testSetNull();
+ testFlush();
+ }
+
+ static void testFlush() throws Exception {
+ CountDownLatch flush = new CountDownLatch(1);
+ try (EventStream es = EventStream.openRepository()) {
+ es.onFlush(() -> {
+ flush.countDown();
+ });
+ es.startAsync();
+ try (Recording r = new Recording()) {
+ r.setFlushInterval(Duration.ofSeconds(1));
+ r.start();
+ flush.await();
+ }
+ }
+ }
+
+ static void testSetNull() {
+ try (Recording r = new Recording()) {
+ r.setFlushInterval(null);
+ Asserts.fail("Expected NullPointerException");
+ } catch (NullPointerException npe) {
+ // as expected
+ }
+ }
+
+ static void testSetGet() {
+ try (Recording r = new Recording()) {
+ Duration a = Duration.ofNanos(21378461289374646L);
+ r.setFlushInterval(a);
+ Duration b = r.getFlushInterval();
+ Asserts.assertEQ(a, b);
+ }
+ }
+
+}
--- a/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java Wed Oct 09 12:21:28 2019 -0700
+++ b/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java Wed Oct 09 23:22:56 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -81,6 +81,13 @@
Arrays.asList("DumpReason")
);
+ // Experimental events
+ private static final Set<String> experimentalEvents = new HashSet<>(
+ Arrays.asList(
+ "Flush", "FlushStorage", "FlushStacktrace",
+ "FlushStringPool", "FlushMetadata", "FlushTypeSet")
+ );
+
public static void main(String[] args) throws Exception {
for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) {
@@ -138,6 +145,10 @@
}
}
+ // remove experimental events from eventsFromEventNamesClass since jfrEventTypes
+ // excludes experimental events
+ eventsFromEventNamesClass.removeAll(experimentalEvents);
+
if (!jfrEventTypes.equals(eventsFromEventNamesClass)) {
String exceptionMsg = "Events declared in jdk.test.lib.jfr.EventNames differ " +
"from events returned by FlightRecorder.getEventTypes()";
--- a/test/jdk/jdk/jfr/event/oldobject/TestLargeRootSet.java Wed Oct 09 12:21:28 2019 -0700
+++ b/test/jdk/jdk/jfr/event/oldobject/TestLargeRootSet.java Wed Oct 09 23:22:56 2019 +0200
@@ -25,16 +25,20 @@
package jdk.jfr.event.oldobject;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Vector;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordedFrame;
+import jdk.jfr.consumer.RecordedMethod;
import jdk.jfr.consumer.RecordedObject;
+import jdk.jfr.consumer.RecordedStackTrace;
import jdk.jfr.internal.test.WhiteBox;
-import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.EventNames;
import jdk.test.lib.jfr.Events;
@@ -50,12 +54,12 @@
public class TestLargeRootSet {
private static final int THREAD_COUNT = 50;
+ public static Vector<StackObject[]> temporaries = new Vector<>(OldObjects.MIN_SIZE);
private static class RootThread extends Thread {
private final CyclicBarrier barrier;
private int maxDepth = OldObjects.MIN_SIZE / THREAD_COUNT;
- public List<StackObject[]> temporaries = new ArrayList<>(maxDepth);
RootThread(CyclicBarrier cb) {
this.barrier = cb;
@@ -91,36 +95,71 @@
public static void main(String[] args) throws Exception {
WhiteBox.setWriteAllObjectSamples(true);
-
- List<RootThread> threads = new ArrayList<>();
- try (Recording r = new Recording()) {
- r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity");
- r.start();
- CyclicBarrier cb = new CyclicBarrier(THREAD_COUNT + 1);
- for (int i = 0; i < THREAD_COUNT; i++) {
- RootThread t = new RootThread(cb);
- t.start();
- if (i % 10 == 0) {
- // Give threads some breathing room before starting next batch
- Thread.sleep(100);
+ int attempt = 1;
+ while (true) {
+ System.out.println();
+ System.out.println();
+ System.out.println("ATTEMPT: " + attempt);
+ System.out.println("====================================");
+ List<RootThread> threads = new ArrayList<>();
+ try (Recording r = new Recording()) {
+ r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity");
+ r.start();
+ CyclicBarrier cb = new CyclicBarrier(THREAD_COUNT + 1);
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ RootThread t = new RootThread(cb);
+ t.start();
+ if (i % 10 == 0) {
+ // Give threads some breathing room before starting next batch
+ Thread.sleep(100);
+ }
+ threads.add(t);
}
- threads.add(t);
- }
- cb.await();
- System.gc();
- r.stop();
- cb.await();
- List<RecordedEvent> events = Events.fromRecording(r);
- Events.hasEvents(events);
- for (RecordedEvent e : events) {
- RecordedObject ro = e.getValue("object");
- RecordedClass rc = ro.getValue("type");
- System.out.println(rc.getName());
- if (rc.getName().equals(StackObject[].class.getName())) {
- return; // ok
+ cb.await();
+ System.gc();
+ r.stop();
+ cb.await();
+ List<RecordedEvent> events = Events.fromRecording(r);
+ Events.hasEvents(events);
+ int sample = 0;
+ for (RecordedEvent e : events) {
+ RecordedObject ro = e.getValue("object");
+ RecordedClass rc = ro.getValue("type");
+ System.out.println("Sample: " + sample);
+ System.out.println(" - allocationTime: " + e.getInstant("allocationTime"));
+ System.out.println(" - type: " + rc.getName());
+ RecordedObject root = e.getValue("root");
+ if (root != null) {
+ System.out.println(" - root:");
+ System.out.println(" - description: " + root.getValue("description"));
+ System.out.println(" - system: " + root.getValue("system"));
+ System.out.println(" - type: " + root.getValue("type"));
+ } else {
+ System.out.println(" - root: N/A");
+ }
+ RecordedStackTrace stack = e.getStackTrace();
+ if (stack != null) {
+ System.out.println(" - stack:");
+ int frameCount = 0;
+ for (RecordedFrame frame: stack.getFrames()) {
+ RecordedMethod m = frame.getMethod();
+ System.out.println(" " + m.getType().getName() + "." + m.getName() +"(...)");
+ frameCount++;
+ if (frameCount == 10) {
+ break;
+ }
+ }
+ } else {
+ System.out.println(" - stack: N/A");
+ }
+ System.out.println();
+ if (rc.getName().equals(StackObject[].class.getName())) {
+ return; // ok
+ }
+ sample++;
}
}
- Asserts.fail("Could not find root object");
+ attempt++;
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/runtime/TestFlush.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.event.runtime;
+
+import java.util.concurrent.CountDownLatch;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Period;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+import jdk.jfr.consumer.RecordedEvent;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Verifies at the metalevel that stream contents are written to ongoing recordings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+streaming=trace jdk.jfr.event.runtime.TestFlush
+ */
+public class TestFlush {
+ private static boolean flushEventAck = false;
+
+ @Period("2 s")
+ static class ZebraEvent extends Event {
+ }
+ static class CatEvent extends Event {
+ }
+ static class DogEvent extends Event {
+ }
+ static class MouseEvent extends Event {
+ }
+
+ public static void main(String... args) throws InterruptedException {
+ CountDownLatch dogLatch = new CountDownLatch(1);
+ CountDownLatch catLatch = new CountDownLatch(1);
+ CountDownLatch mouseLatch = new CountDownLatch(1);
+ CountDownLatch zebraLatch = new CountDownLatch(3);
+
+ FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> {
+ ZebraEvent ze = new ZebraEvent();
+ ze.commit();
+ });
+
+ try (RecordingStream rs = new RecordingStream()) {
+ rs.enable(EventNames.Flush);
+ rs.enable(EventNames.FlushStorage);
+ rs.enable(EventNames.FlushStacktrace);
+ rs.enable(EventNames.FlushStringPool);
+ rs.enable(EventNames.FlushMetadata);
+ rs.enable(EventNames.FlushTypeSet);
+ rs.onEvent(e -> {
+ switch (e.getEventType().getName()) {
+ case EventNames.Flush:
+ flushEventAck = true;
+ case EventNames.FlushStorage:
+ case EventNames.FlushStacktrace:
+ case EventNames.FlushStringPool:
+ case EventNames.FlushMetadata:
+ case EventNames.FlushTypeSet:
+ validateFlushEvent(e);
+ return;
+ }
+ if (e.getEventType().getName().equals(CatEvent.class.getName())) {
+ System.out.println("Found cat!");
+ catLatch.countDown();
+ return;
+ }
+ if (e.getEventType().getName().equals(DogEvent.class.getName())) {
+ System.out.println("Found dog!");
+ dogLatch.countDown();
+ return;
+ }
+ if (e.getEventType().getName().equals(ZebraEvent.class.getName())) {
+ System.out.println("Found zebra!");
+ zebraLatch.countDown();
+ return;
+ }
+ if (e.getEventType().getName().equals(MouseEvent.class.getName())) {
+ System.out.println("Found mouse!");
+ mouseLatch.countDown();
+ return;
+ }
+ System.out.println("Unexpected event: " + e.getEventType().getName());
+ });
+
+ rs.startAsync();
+
+ try (Recording r1 = new Recording()) {
+ r1.start();
+ MouseEvent me = new MouseEvent();
+ me.commit();
+ System.out.println("Mouse emitted");
+ mouseLatch.await();
+ try (Recording r2 = new Recording()) { // force chunk rotation in stream
+ r2.start();
+ DogEvent de = new DogEvent();
+ de.commit();
+ System.out.println("Dog emitted");
+ dogLatch.await();
+ CatEvent ce = new CatEvent();
+ ce.commit();
+ System.out.println("Cat emitted");
+ catLatch.await();
+ zebraLatch.await();
+ acknowledgeFlushEvent();
+ }
+ }
+ }
+ }
+
+ private static void printEvent(RecordedEvent re) {
+ System.out.println(re.getEventType().getName());
+ System.out.println(re.getStartTime().toEpochMilli());
+ System.out.println(re.getEndTime().toEpochMilli());
+ }
+
+ private static void printFlushEvent(RecordedEvent re) {
+ printEvent(re);
+ System.out.println("flushID: " + (long) re.getValue("flushId"));
+ System.out.println("elements: " + (long) re.getValue("elements"));
+ System.out.println("size: " + (long) re.getValue("size"));
+ }
+
+ private static void validateFlushEvent(RecordedEvent re) {
+ printFlushEvent(re);
+ Asserts.assertTrue(re.getEventType().getName().contains("Flush"), "invalid Event type");
+ Asserts.assertGT((long) re.getValue("flushId"), 0L, "Invalid flush ID");
+ Asserts.assertGT((long) re.getValue("elements"), 0L, "No elements");
+ Asserts.assertGT((long) re.getValue("size"), 0L, "Empty size");
+ }
+
+ private static void acknowledgeFlushEvent() {
+ Asserts.assertTrue(flushEventAck, "No Flush event");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/jcmd/TestJcmdStartFlushInterval.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.jcmd;
+
+import java.time.Duration;
+
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Recording;
+
+/**
+ * @test
+ * @summary Start a recording with a flush interval
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm jdk.jfr.jcmd.TestJcmdStartReadOnlyFile
+ */
+public class TestJcmdStartFlushInterval {
+
+ public static void main(String[] args) throws Exception {
+ JcmdHelper.jcmd("JFR.start","flush-interval=1s");
+ for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) {
+ Duration d = r.getFlushInterval();
+ if (d.equals(Duration.ofSeconds(1))) {
+ return; //OK
+ } else {
+ throw new Exception("Unexpected flush-interval=" + d);
+ }
+ }
+ throw new Exception("No recording found");
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/jvm/TestThreadExclusion.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.jvm;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.JVM;
+import jdk.jfr.Recording;
+
+import jdk.test.lib.jfr.EventNames;
+import jdk.test.lib.jfr.Events;
+
+import static jdk.test.lib.Asserts.assertTrue;
+
+/**
+ * @test
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @modules jdk.jfr/jdk.jfr.internal
+ * @run main/othervm jdk.jfr.jvm.TestThreadExclusion
+ */
+
+/**
+ * Starts and stops a number of threads in order.
+ * Verifies that events are in the same order.
+ */
+public class TestThreadExclusion {
+ private final static String EVENT_NAME_THREAD_START = EventNames.ThreadStart;
+ private final static String EVENT_NAME_THREAD_END = EventNames.ThreadEnd;
+ private static final String THREAD_NAME_PREFIX = "TestThread-";
+ private static JVM jvm;
+
+ public static void main(String[] args) throws Throwable {
+ // Test Java Thread Start event
+ Recording recording = new Recording();
+ recording.enable(EVENT_NAME_THREAD_START).withThreshold(Duration.ofMillis(0));
+ recording.enable(EVENT_NAME_THREAD_END).withThreshold(Duration.ofMillis(0));
+ recording.start();
+ LatchedThread[] threads = startThreads();
+ long[] javaThreadIds = getJavaThreadIds(threads);
+ stopThreads(threads);
+ recording.stop();
+ List<RecordedEvent> events = Events.fromRecording(recording);
+ verifyThreadExclusion(events, javaThreadIds);
+ }
+
+ private static void verifyThreadExclusion(List<RecordedEvent> events, long[] javaThreadIds) throws Exception {
+ for (RecordedEvent event : events) {
+ System.out.println("Event:" + event);
+ final long eventJavaThreadId = event.getThread().getJavaThreadId();
+ for (int i = 0; i < javaThreadIds.length; ++i) {
+ if (eventJavaThreadId == javaThreadIds[i]) {
+ throw new Exception("Event " + event.getEventType().getName() + " has a thread id " + eventJavaThreadId + " that should have been excluded");
+ }
+ }
+ }
+ }
+
+ private static LatchedThread[] startThreads() {
+ LatchedThread threads[] = new LatchedThread[10];
+ ThreadGroup threadGroup = new ThreadGroup("TestThreadGroup");
+ jvm = JVM.getJVM();
+ for (int i = 0; i < threads.length; i++) {
+ threads[i] = new LatchedThread(threadGroup, THREAD_NAME_PREFIX + i);
+ jvm.exclude(threads[i]);
+ threads[i].startThread();
+ System.out.println("Started thread id=" + threads[i].getId());
+ }
+ return threads;
+ }
+
+ private static long[] getJavaThreadIds(LatchedThread[] threads) {
+ long[] javaThreadIds = new long[threads.length];
+ for (int i = 0; i < threads.length; ++i) {
+ javaThreadIds[i] = threads[i].getId();
+ }
+ return javaThreadIds;
+ }
+
+ private static void stopThreads(LatchedThread[] threads) {
+ for (LatchedThread thread : threads) {
+ assertTrue(jvm.isExcluded(thread), "Thread " + thread + "should be excluded");
+ thread.stopThread();
+ while (thread.isAlive()) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private static class LatchedThread extends Thread {
+ private final CountDownLatch start = new CountDownLatch(1);
+ private final CountDownLatch stop = new CountDownLatch(1);
+
+ public LatchedThread(ThreadGroup threadGroup, String name) {
+ super(threadGroup, name);
+ }
+
+ public void run() {
+ start.countDown();
+ try {
+ stop.await();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void startThread() {
+ this.start();
+ try {
+ start.await();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void stopThread() {
+ stop.countDown();
+ }
+ }
+}
--- a/test/jdk/jdk/jfr/jvm/TestUnsupportedVM.java Wed Oct 09 12:21:28 2019 -0700
+++ b/test/jdk/jdk/jfr/jvm/TestUnsupportedVM.java Wed Oct 09 23:22:56 2019 +0200
@@ -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
@@ -30,8 +30,10 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
import jdk.jfr.AnnotationElement;
import jdk.jfr.Configuration;
@@ -48,6 +50,7 @@
import jdk.jfr.RecordingState;
import jdk.jfr.SettingControl;
import jdk.jfr.ValueDescriptor;
+import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedFrame;
@@ -57,6 +60,7 @@
import jdk.jfr.consumer.RecordedThread;
import jdk.jfr.consumer.RecordedThreadGroup;
import jdk.jfr.consumer.RecordingFile;
+import jdk.jfr.consumer.RecordingStream;
import jdk.management.jfr.ConfigurationInfo;
import jdk.management.jfr.EventTypeInfo;
import jdk.management.jfr.FlightRecorderMXBean;
@@ -106,9 +110,11 @@
RecordingState.class,
SettingControl.class,
SettingDescriptorInfo.class,
- ValueDescriptor.class
+ ValueDescriptor.class,
+ EventStream.class,
+ RecordingStream.class
};
- // * @run main/othervm -Dprepare-recording=true jdk.jfr.jvm.TestUnsupportedVM
+
@Label("My Event")
@Description("My fine event")
static class MyEvent extends Event {
@@ -125,7 +131,7 @@
return;
}
- System.out.println("jdk.jfr.unsupportedvm=" + System.getProperty("jdk.jfr.unsupportedvm"));
+ System.out.println("jfr.unsupported.vm=" + System.getProperty("jfr.unsupported.vm"));
// Class FlightRecorder
if (FlightRecorder.isAvailable()) {
throw new AssertionError("JFR should not be available on an unsupported VM");
@@ -136,6 +142,7 @@
}
assertIllegalStateException(() -> FlightRecorder.getFlightRecorder());
+ assertIllegalStateException(() -> new RecordingStream());
assertSwallow(() -> FlightRecorder.addListener(new FlightRecorderListener() {}));
assertSwallow(() -> FlightRecorder.removeListener(new FlightRecorderListener() {}));
assertSwallow(() -> FlightRecorder.register(MyEvent.class));
@@ -174,8 +181,39 @@
// Only run this part of tests if we are on VM
// that can produce a recording file
if (Files.exists(RECORDING_FILE)) {
+ boolean firstFileEvent = true;
for(RecordedEvent re : RecordingFile.readAllEvents(RECORDING_FILE)) {
- System.out.println(re);
+ // Print one event
+ if (firstFileEvent) {
+ System.out.println(re);
+ firstFileEvent = false;
+ }
+ }
+ AtomicBoolean firstStreamEvent = new AtomicBoolean(true);
+ try (EventStream es = EventStream.openFile(RECORDING_FILE)) {
+ es.onEvent(e -> {
+ // Print one event
+ if (firstStreamEvent.get()) {
+ try {
+ System.out.println(e);
+ firstStreamEvent.set(false);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ });
+ es.start();
+ if (firstStreamEvent.get()) {
+ throw new AssertionError("Didn't print streaming event");
+ }
+ }
+
+ try (EventStream es = EventStream.openRepository()) {
+ es.onEvent(e -> {
+ System.out.println(e);
+ });
+ es.startAsync();
+ es.awaitTermination(Duration.ofMillis(10));
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/startupargs/TestFlushInterval.java Wed Oct 09 23:22:56 2019 +0200
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.startupargs;
+
+import java.time.Duration;
+
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Recording;
+
+/**
+ * @test
+ * @summary Start a recording with a flush interval
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm -XX:StartFlightRecording=flush-interval=1s jdk.jfr.startupargs.TestFlushInterval
+ */
+public class TestFlushInterval {
+
+ public static void main(String[] args) throws Exception {
+ for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) {
+ Duration d = r.getFlushInterval();
+ if (d.equals(Duration.ofSeconds(1))) {
+ return; //OK
+ } else {
+ throw new Exception("Unexpected flush-interval " + d);
+ }
+ }
+ throw new Exception("No recording found");
+ }
+
+}
--- a/test/lib/jdk/test/lib/jfr/EventNames.java Wed Oct 09 12:21:28 2019 -0700
+++ b/test/lib/jdk/test/lib/jfr/EventNames.java Wed Oct 09 23:22:56 2019 +0200
@@ -189,6 +189,12 @@
public final static String CPUTimeStampCounter = PREFIX + "CPUTimeStampCounter";
public final static String ActiveRecording = PREFIX + "ActiveRecording";
public final static String ActiveSetting = PREFIX + "ActiveSetting";
+ public static final String Flush = PREFIX + "Flush";
+ public static final String FlushStringPool = PREFIX + "FlushStringPool";
+ public static final String FlushStacktrace = PREFIX + "FlushStacktrace";
+ public static final String FlushStorage = PREFIX + "FlushStorage";
+ public static final String FlushMetadata = PREFIX + "FlushMetadata";
+ public static final String FlushTypeSet = PREFIX + "FlushTypeSet";
public static boolean isGcEvent(EventType et) {
return et.getCategoryNames().contains(GC_CATEGORY);