From 051d372da1716f34602edfc523301c7916b8bf54 Mon Sep 17 00:00:00 2001 From: Denghui Dong Date: Mon, 2 Mar 2020 12:25:41 +0800 Subject: [PATCH] [Backport] 8233111: Epoch shift synchronization point for Compiler threads Summary: Test Plan: jdk/jfr Reviewed-by: yuleil Issue: https://github.com/alibaba/dragonwell8/issues/112 --- src/share/vm/jfr/GenerateJfrFiles.java | 29 ++++++++----- src/share/vm/jfr/metadata/metadata.xml | 4 +- src/share/vm/jfr/metadata/metadata.xsd | 3 +- .../checkpoint/jfrCheckpointManager.cpp | 26 +++++++----- .../checkpoint/jfrCheckpointManager.hpp | 6 ++- .../recorder/checkpoint/types/jfrTypeSet.cpp | 2 +- .../types/traceid/jfrTraceId.inline.hpp | 7 +--- .../types/traceid/jfrTraceIdEpoch.cpp | 16 +++++-- .../types/traceid/jfrTraceIdEpoch.hpp | 36 +++++++++++++--- .../recorder/service/jfrRecorderService.cpp | 10 +++-- .../jfr/support/jfrEpochSynchronization.cpp | 42 +++++++++++++++++++ .../jfr/support/jfrEpochSynchronization.hpp | 37 ++++++++++++++++ 12 files changed, 172 insertions(+), 46 deletions(-) create mode 100644 src/share/vm/jfr/support/jfrEpochSynchronization.cpp create mode 100644 src/share/vm/jfr/support/jfrEpochSynchronization.hpp diff --git a/src/share/vm/jfr/GenerateJfrFiles.java b/src/share/vm/jfr/GenerateJfrFiles.java index ac3b5b6d5..66fdd9b3a 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 26115aed6..1bb5a306c 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 4bcb96753..47f593cad 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 1eb7eccfe..158bbab6d 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 5392f145e..5941a807f 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 d9ee53cd0..d762a3a6b 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 5e5a6e67d..81649caa9 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 aa38d30eb..66f05828d 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 9ab83d6d2..e2e4de61c 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 2c983a318..0ff99b153 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 000000000..c2ff6711e --- /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 000000000..7dd359121 --- /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 -- GitLab