diff --git a/src/share/vm/jfr/GenerateJfrFiles.java b/src/share/vm/jfr/GenerateJfrFiles.java
index ac3b5b6d5b30ee2b6ed399d1ca1c40f3bdfa7fe3..66fdd9b3af22b818cdb67e9c6dd4a97e05d611c3 100644
--- a/src/share/vm/jfr/GenerateJfrFiles.java
+++ b/src/share/vm/jfr/GenerateJfrFiles.java
@@ -222,6 +222,7 @@ public class GenerateJfrFiles {
boolean startTime;
boolean periodic;
boolean cutoff;
+ String commitState;
}
static class FieldElement {
@@ -287,14 +288,15 @@ public class GenerateJfrFiles {
currentType.name = attributes.getValue("name");
break;
case "Event":
- EventElement eventtType = new EventElement();
- eventtType.name = attributes.getValue("name");
- eventtType.thread = getBoolean(attributes, "thread", false);
- eventtType.stackTrace = getBoolean(attributes, "stackTrace", false);
- eventtType.startTime = getBoolean(attributes, "startTime", true);
- eventtType.periodic = attributes.getValue("period") != null;
- eventtType.cutoff = getBoolean(attributes, "cutoff", false);
- currentType = eventtType;
+ EventElement eventType = new EventElement();
+ eventType.name = attributes.getValue("name");
+ eventType.thread = getBoolean(attributes, "thread", false);
+ eventType.stackTrace = getBoolean(attributes, "stackTrace", false);
+ eventType.startTime = getBoolean(attributes, "startTime", true);
+ eventType.periodic = attributes.getValue("period") != null;
+ eventType.cutoff = getBoolean(attributes, "cutoff", false);
+ eventType.commitState = attributes.getValue("commitState");
+ currentType = eventType;
break;
case "Field":
currentField = new FieldElement(metadata);
@@ -527,6 +529,7 @@ public class GenerateJfrFiles {
out.write("#include \"utilities/ticks.hpp\"");
out.write("#if INCLUDE_JFR");
out.write("#include \"jfr/recorder/service/jfrEvent.hpp\"");
+ out.write("#include \"jfr/support/jfrEpochSynchronization.hpp\"");
out.write("/*");
out.write(" * Each event class has an assert member function verify() which is invoked");
out.write(" * just before the engine writes the event and its fields to the data stream.");
@@ -591,7 +594,7 @@ public class GenerateJfrFiles {
}
out.write("");
if (!empty) {
- printWriteData(out, t.fields);
+ printWriteData(out, t.fields, null);
}
out.write("};");
out.write("");
@@ -634,7 +637,7 @@ public class GenerateJfrFiles {
}
out.write("");
if (!empty) {
- printWriteData(out, event.fields);
+ printWriteData(out, event.fields, event.commitState);
out.write("");
}
out.write(" using JfrEvent::commit; // else commit() is hidden by overloaded versions in this class");
@@ -646,9 +649,13 @@ public class GenerateJfrFiles {
out.write("};");
}
- private static void printWriteData(Printer out, List fields) {
+ private static void printWriteData(Printer out, List fields, String commitState) {
out.write(" template ");
out.write(" void writeData(Writer& w) {");
+ if (("_thread_in_native").equals(commitState)) {
+ out.write(" // explicit epoch synchronization check");
+ out.write(" JfrEpochSynchronization sync;");
+ }
for (FieldElement field : fields) {
if (field.struct) {
out.write(" _" + field.name + ".writeData(w);");
diff --git a/src/share/vm/jfr/metadata/metadata.xml b/src/share/vm/jfr/metadata/metadata.xml
index 26115aed613cdb315f384c93d20ed117e50f5efc..1bb5a306caccc801a455f2ae9a1d5b608e506eda 100644
--- a/src/share/vm/jfr/metadata/metadata.xml
+++ b/src/share/vm/jfr/metadata/metadata.xml
@@ -457,7 +457,7 @@
-
+
@@ -484,7 +484,7 @@
-
+
diff --git a/src/share/vm/jfr/metadata/metadata.xsd b/src/share/vm/jfr/metadata/metadata.xsd
index 4bcb9675354727068382f261622249cd0d983809..47f593cad31e4df18a3d71cce32651924aa0fe97 100644
--- a/src/share/vm/jfr/metadata/metadata.xsd
+++ b/src/share/vm/jfr/metadata/metadata.xsd
@@ -70,6 +70,7 @@
+
@@ -119,4 +120,4 @@
-
\ No newline at end of file
+
diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp
index 1eb7eccfe3fe8090638f3acc6e37d6daa278920e..158bbab6d2679365b6c5c18de9436bf9bf40d6db 100644
--- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp
+++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp
@@ -170,7 +170,7 @@ static BufferPtr lease_free(size_t size, JfrCheckpointMspace* mspace, size_t ret
}
bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const {
- return _service_thread != thread && _checkpoint_epoch_state != JfrTraceIdEpoch::epoch();
+ return _service_thread != thread && OrderAccess::load_acquire((volatile jubyte*)&_checkpoint_epoch_state) != JfrTraceIdEpoch::epoch();
}
static const size_t lease_retry = 10;
@@ -332,7 +332,19 @@ static size_t write_mspace_exclusive(JfrCheckpointMspace* mspace, JfrChunkWriter
return wo.processed();
}
-void JfrCheckpointManager::synchronize_epoch() {
+void JfrCheckpointManager::begin_epoch_shift() {
+ assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ JfrTraceIdEpoch::begin_epoch_shift();
+}
+
+void JfrCheckpointManager::end_epoch_shift() {
+ assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();)
+ JfrTraceIdEpoch::end_epoch_shift();
+ assert(current_epoch != JfrTraceIdEpoch::current(), "invariant");
+}
+
+void JfrCheckpointManager::synchronize_checkpoint_manager_with_current_epoch() {
assert(_checkpoint_epoch_state != JfrTraceIdEpoch::epoch(), "invariant");
OrderAccess::storestore();
_checkpoint_epoch_state = JfrTraceIdEpoch::epoch();
@@ -340,7 +352,7 @@ void JfrCheckpointManager::synchronize_epoch() {
size_t JfrCheckpointManager::write() {
const size_t processed = write_mspace_exclusive(_free_list_mspace, _chunkwriter);
- synchronize_epoch();
+ synchronize_checkpoint_manager_with_current_epoch();
return processed;
}
@@ -364,7 +376,7 @@ size_t JfrCheckpointManager::clear() {
DiscardOperation discarder(mutexed); // mutexed discard mode
process_free_list(discarder, _free_list_mspace);
process_free_list(discarder, _epoch_transition_mspace);
- synchronize_epoch();
+ synchronize_checkpoint_manager_with_current_epoch();
return discarder.elements();
}
@@ -409,12 +421,6 @@ size_t JfrCheckpointManager::write_static_type_set_and_threads() {
return write_epoch_transition_mspace();
}
-void JfrCheckpointManager::shift_epoch() {
- debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();)
- JfrTraceIdEpoch::shift_epoch();
- assert(current_epoch != JfrTraceIdEpoch::current(), "invariant");
-}
-
void JfrCheckpointManager::flush_static_type_set() {
flush();
}
diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp
index 5392f145eaedc282fdebeb28d7c7aeb11aa47c6d..5941a807fede648181769e2055e2aaf7c8936083 100644
--- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp
+++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp
@@ -88,8 +88,10 @@ class JfrCheckpointManager : public JfrCHeapObj {
void write_type_set();
static void write_type_set_for_unloaded_classes();
- void shift_epoch();
- void synchronize_epoch();
+ void begin_epoch_shift();
+ void end_epoch_shift();
+ void synchronize_checkpoint_manager_with_current_epoch();
+
void notify_threads();
JfrCheckpointManager(JfrChunkWriter& cw);
diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp
index d9ee53cd035d41ac121dc479b5060b19780d910a..d762a3a6b5b7f5fb8134014cd8c4afa072102f0d 100644
--- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp
+++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp
@@ -302,7 +302,7 @@ static bool write_klasses() {
_subsystem_callback = &callback;
do_klasses();
} else {
- LeakKlassWriter lkw(_leakp_writer, _artifacts, _class_unload);
+ LeakKlassWriter lkw(_leakp_writer, _class_unload);
CompositeKlassWriter ckw(&lkw, &kw);
CompositeKlassWriterRegistration ckwr(&ckw, ®);
CompositeKlassCallback callback(&ckwr);
diff --git a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp
index 5e5a6e67dc4d2463f523fe6d7aa6d0de792bf977..81649caa9ef73abdf0695cdccfa1848cd91f2cd0 100644
--- a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp
+++ b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp
@@ -75,15 +75,12 @@ inline traceid JfrTraceId::use(const Method* method) {
inline traceid JfrTraceId::use(const Klass* klass, const Method* method) {
assert(klass != NULL, "invariant");
assert(method != NULL, "invariant");
- 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_AND_CLASS_USED_THIS_EPOCH(klass);
SET_METHOD_FLAG_USED_THIS_EPOCH(method);
JfrTraceIdEpoch::set_changed_tag_state();
}
+ assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
assert(METHOD_FLAG_USED_THIS_EPOCH(method), "invariant");
return (METHOD_ID(klass, method));
}
diff --git a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp
index aa38d30eb1556838916c1cf215b6df2b1c673f1a..66f05828d6c04f59031bbcf2f5bc4d30c36ef774 100644
--- a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp
+++ b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp
@@ -26,12 +26,20 @@
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
#include "runtime/safepoint.hpp"
-// Alternating epochs on each rotation allow for concurrent tagging.
-// The epoch shift happens only during a safepoint.
bool JfrTraceIdEpoch::_epoch_state = false;
-bool volatile JfrTraceIdEpoch::_tag_state = false;
+bool JfrTraceIdEpoch::_synchronizing = false;
+volatile bool JfrTraceIdEpoch::_changed_tag_state = false;
-void JfrTraceIdEpoch::shift_epoch() {
+void JfrTraceIdEpoch::begin_epoch_shift() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ _synchronizing = true;
+ OrderAccess::fence();
+}
+
+void JfrTraceIdEpoch::end_epoch_shift() {
+ assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ assert(_synchronizing, "invariant");
_epoch_state = !_epoch_state;
+ OrderAccess::storestore();
+ _synchronizing = false;
}
diff --git a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp
index 9ab83d6d2d50f980d37565dc6fcfdaf69b45bde2..e2e4de61c355b5b4a870a42c4fb905b155d9a11b 100644
--- a/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp
+++ b/src/share/vm/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp
@@ -41,13 +41,33 @@
#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)
+ // Epoch alternation on each rotation allow for concurrent tagging.
+ // The epoch shift happens only during a safepoint.
+ //
+ // _synchronizing is a transition state, the purpose of which is to
+ // have JavaThreads that run _thread_in_native (i.e. Compiler threads)
+ // respect the current epoch shift in-progress during a safepoint.
+ //
+ // _changed_tag_state == true signals an incremental modification to artifact tagging
+ // (klasses, methods, CLDs, etc), used to request collection of artifacts.
+ //
class JfrTraceIdEpoch : AllStatic {
friend class JfrCheckpointManager;
private:
static bool _epoch_state;
- static bool volatile _tag_state;
+ static bool _synchronizing;
+ static volatile bool _changed_tag_state;
- static void shift_epoch();
+ static void begin_epoch_shift();
+ static void end_epoch_shift();
+
+ static bool changed_tag_state() {
+ return (bool)OrderAccess::load_acquire((volatile jubyte*)&_changed_tag_state);
+ }
+
+ static void set_tag_state(bool value) {
+ OrderAccess::release_store((volatile jubyte*)&_changed_tag_state, (jubyte)value);
+ }
public:
static u1 epoch() {
@@ -66,6 +86,10 @@ class JfrTraceIdEpoch : AllStatic {
return _epoch_state ? (u1)0 : (u1)1;
}
+ static bool is_synchronizing() {
+ return (bool)OrderAccess::load_acquire((volatile jubyte*)&_synchronizing);
+ }
+
static traceid in_use_this_epoch_bit() {
return _epoch_state ? USED_EPOCH_2_BIT : USED_EPOCH_1_BIT;
}
@@ -91,16 +115,16 @@ class JfrTraceIdEpoch : AllStatic {
}
static bool has_changed_tag_state() {
- if ((bool)OrderAccess::load_acquire((volatile jubyte*)&_tag_state)) {
- OrderAccess::release_store((volatile jubyte*)&_tag_state, (jubyte)false);
+ if (changed_tag_state()) {
+ set_tag_state(false);
return true;
}
return false;
}
static void set_changed_tag_state() {
- if (!(bool)OrderAccess::load_acquire((volatile jubyte*)&_tag_state)) {
- OrderAccess::release_store((volatile jubyte*)&_tag_state, (jubyte)true);
+ if (!changed_tag_state()) {
+ set_tag_state(true);
}
}
};
diff --git a/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp b/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp
index 2c983a318620ab0ccac2f854bf1f70d0be32eab5..0ff99b153616413ff9a0299cb3e21df4d10ece97 100644
--- a/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp
+++ b/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp
@@ -392,11 +392,12 @@ void JfrRecorderService::invoke_safepoint_clear() {
void JfrRecorderService::safepoint_clear() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ _checkpoint_manager.begin_epoch_shift();
_string_pool.clear();
+ _stack_trace_repository.clear();
_storage.clear();
- _checkpoint_manager.shift_epoch();
_chunkwriter.set_time_stamp();
- _stack_trace_repository.clear();
+ _checkpoint_manager.end_epoch_shift();
}
void JfrRecorderService::post_safepoint_clear() {
@@ -514,14 +515,15 @@ void JfrRecorderService::invoke_safepoint_write() {
void JfrRecorderService::safepoint_write() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+ _checkpoint_manager.begin_epoch_shift();
if (_string_pool.is_modified()) {
write_stringpool_safepoint(_string_pool, _chunkwriter);
}
_checkpoint_manager.on_rotation();
+ write_stacktrace(_stack_trace_repository, _chunkwriter, true);
_storage.write_at_safepoint();
- _checkpoint_manager.shift_epoch();
_chunkwriter.set_time_stamp();
- write_stacktrace(_stack_trace_repository, _chunkwriter, true);
+ _checkpoint_manager.end_epoch_shift();
}
void JfrRecorderService::post_safepoint_write() {
diff --git a/src/share/vm/jfr/support/jfrEpochSynchronization.cpp b/src/share/vm/jfr/support/jfrEpochSynchronization.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c2ff6711e690ef45a28ea5b781ae04ca522a3cc7
--- /dev/null
+++ b/src/share/vm/jfr/support/jfrEpochSynchronization.cpp
@@ -0,0 +1,42 @@
+/*
+* 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/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
+#include "jfr/support/jfrEpochSynchronization.hpp"
+#include "runtime/interfaceSupport.hpp"
+#include "runtime/thread.inline.hpp"
+
+JfrEpochSynchronization::JfrEpochSynchronization() {
+ if (JfrTraceIdEpoch::is_synchronizing()) {
+ // only at a safepoint
+ Thread* thread = Thread::current();
+ assert(thread != NULL, "invariant");
+ assert(thread->is_Java_thread(), "invariant");
+ JavaThread* const jt = (JavaThread*)thread;
+ assert(jt->thread_state() == _thread_in_native, "invariant");
+ // use ordinary transition to have the thread block and await the new epoch
+ ThreadInVMfromNative transition(jt);
+ }
+}
diff --git a/src/share/vm/jfr/support/jfrEpochSynchronization.hpp b/src/share/vm/jfr/support/jfrEpochSynchronization.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7dd35912119849d80199e85d46c6194ce6924dfd
--- /dev/null
+++ b/src/share/vm/jfr/support/jfrEpochSynchronization.hpp
@@ -0,0 +1,37 @@
+/*
+* 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_JFR_SUPPORT_JFREPOCHSYNCHRONIZATION_HPP
+#define SHARE_JFR_SUPPORT_JFREPOCHSYNCHRONIZATION_HPP
+
+/*
+ * JavaThreads running _thread_in_native (Compiler threads) must synchronize
+ * with the upcoming epoch in case there is an epoch shift in-progress.
+ */
+class JfrEpochSynchronization {
+ public:
+ JfrEpochSynchronization();
+};
+
+#endif // SHARE_JFR_SUPPORT_JFREPOCHSYNCHRONIZATION_HPP