changeset 57455:3b2174ed0eb1

8233111: Epoch shift synchronization point for Compiler threads Reviewed-by: egahlin
author mgronlun
date Sat, 21 Dec 2019 12:45:08 +0100
parents 048ef0e2ba9f
children 563fa900fa17
files make/src/classes/build/tools/jfr/GenerateJfrFiles.java src/hotspot/share/jfr/metadata/metadata.xml src/hotspot/share/jfr/metadata/metadata.xsd src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp src/hotspot/share/jfr/support/jfrEpochSynchronization.cpp src/hotspot/share/jfr/support/jfrEpochSynchronization.hpp
diffstat 12 files changed, 174 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/make/src/classes/build/tools/jfr/GenerateJfrFiles.java	Mon Dec 16 14:10:20 2019 +0100
+++ b/make/src/classes/build/tools/jfr/GenerateJfrFiles.java	Sat Dec 21 12:45:08 2019 +0100
@@ -154,6 +154,7 @@
         boolean startTime;
         boolean periodic;
         boolean cutoff;
+        String commitState;
     }
 
     static class FieldElement {
@@ -219,14 +220,15 @@
                 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);
@@ -459,6 +461,7 @@
             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.");
@@ -523,7 +526,7 @@
         }
         out.write("");
         if (!empty) {
-          printWriteData(out, t.fields);
+          printWriteData(out, t.fields, null);
         }
         out.write("};");
         out.write("");
@@ -566,7 +569,7 @@
         }
         out.write("");
         if (!empty) {
-          printWriteData(out, event.fields);
+          printWriteData(out, event.fields, event.commitState);
           out.write("");
         }
         out.write("  using JfrEvent<Event" + event.name + ">::commit; // else commit() is hidden by overloaded versions in this class");
@@ -578,9 +581,13 @@
         out.write("};");
     }
 
-    private static void printWriteData(Printer out, List<FieldElement> fields) {
+    private static void printWriteData(Printer out, List<FieldElement> fields, String commitState) {
         out.write("  template <typename Writer>");
         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);");
--- a/src/hotspot/share/jfr/metadata/metadata.xml	Mon Dec 16 14:10:20 2019 +0100
+++ b/src/hotspot/share/jfr/metadata/metadata.xml	Sat Dec 21 12:45:08 2019 +0100
@@ -464,7 +464,7 @@
     <Field type="ulong" contentType="bytes" name="used" label="Used" />
   </Event>
 
-  <Event name="Compilation" category="Java Virtual Machine, Compiler" label="Compilation" thread="true">
+  <Event name="Compilation" category="Java Virtual Machine, Compiler" label="Compilation" thread="true" commitState="_thread_in_native">
     <Field type="uint" name="compileId" label="Compilation Identifier" relation="CompileId" />
     <Field type="CompilerType" name="compiler" label="Compiler" />
     <Field type="Method" name="method" label="Method" />
@@ -492,7 +492,7 @@
     <Field type="string" name="descriptor" label="Method Descriptor" />
   </Type>
 
-  <Event name="CompilerInlining" category="Java Virtual Machine, Compiler, Optimization" label="Method Inlining" thread="true" startTime="false">
+  <Event name="CompilerInlining" category="Java Virtual Machine, Compiler, Optimization" label="Method Inlining" thread="true" startTime="false" commitState="_thread_in_native">
     <Field type="uint" name="compileId" label="Compilation Identifier" relation="CompileId" />
     <Field type="Method" name="caller" label="Caller Method" />
     <Field type="CalleeMethod" name="callee" struct="true" label="Callee Method" />
--- a/src/hotspot/share/jfr/metadata/metadata.xsd	Mon Dec 16 14:10:20 2019 +0100
+++ b/src/hotspot/share/jfr/metadata/metadata.xsd	Sat Dec 21 12:45:08 2019 +0100
@@ -70,6 +70,7 @@
               <xs:attribute name="stackTrace" type="xs:boolean" use="optional" />
               <xs:attribute name="period" type="periodType" use="optional" />
               <xs:attribute name="cutoff" type="xs:boolean" use="optional" />
+              <xs:attribute name="commitState" type="xs:string" use="optional" />
             </xs:complexType>
           </xs:element>
           <xs:element maxOccurs="unbounded" name="Type">
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp	Mon Dec 16 14:10:20 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp	Sat Dec 21 12:45:08 2019 +0100
@@ -44,9 +44,9 @@
 #include "logging/log.hpp"
 #include "memory/iterator.hpp"
 #include "memory/resourceArea.hpp"
+#include "runtime/atomic.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/mutex.hpp"
-#include "runtime/orderAccess.hpp"
 #include "runtime/os.inline.hpp"
 #include "runtime/safepoint.hpp"
 
@@ -172,7 +172,7 @@
 }
 
 bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const {
-  return _service_thread != thread && _checkpoint_epoch_state != JfrTraceIdEpoch::epoch();
+  return _service_thread != thread && Atomic::load_acquire(&_checkpoint_epoch_state) != JfrTraceIdEpoch::epoch();
 }
 
 static const size_t lease_retry = 10;
@@ -333,7 +333,19 @@
   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();
@@ -341,7 +353,7 @@
 
 size_t JfrCheckpointManager::write() {
   const size_t processed = write_mspace<MutexedWriteOp, CompositeOperation>(_free_list_mspace, _chunkwriter);
-  synchronize_epoch();
+  synchronize_checkpoint_manager_with_current_epoch();
   return processed;
 }
 
@@ -365,7 +377,7 @@
   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();
 }
 
@@ -410,12 +422,6 @@
   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();
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp	Mon Dec 16 14:10:20 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp	Sat Dec 21 12:45:08 2019 +0100
@@ -88,8 +88,10 @@
   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);
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp	Mon Dec 16 14:10:20 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp	Sat Dec 21 12:45:08 2019 +0100
@@ -303,7 +303,7 @@
     _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, &reg);
     CompositeKlassCallback callback(&ckwr);
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp	Mon Dec 16 14:10:20 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp	Sat Dec 21 12:45:08 2019 +0100
@@ -61,6 +61,7 @@
 }
 
 inline traceid JfrTraceId::use(const Klass* klass) {
+  assert(klass != NULL, "invariant");
   return set_used_and_get(klass);
 }
 
@@ -71,15 +72,12 @@
 inline traceid JfrTraceId::use(const Klass* klass, const Method* method) {
   assert(klass != NULL, "invariant");
   assert(method != NULL, "invariant");
-  if (SHOULD_TAG_KLASS_METHOD(klass)) {
+  if (METHOD_FLAG_NOT_USED_THIS_EPOCH(method)) {
     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_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
   assert(METHOD_FLAG_USED_THIS_EPOCH(method), "invariant");
   return (METHOD_ID(klass, method));
 }
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp	Mon Dec 16 14:10:20 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp	Sat Dec 21 12:45:08 2019 +0100
@@ -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;
 }
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp	Mon Dec 16 14:10:20 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp	Sat Dec 21 12:45:08 2019 +0100
@@ -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 Atomic::load_acquire(&_changed_tag_state);
+  }
+
+  static void set_tag_state(bool value) {
+    Atomic::release_store(&_changed_tag_state, value);
+  }
 
  public:
   static bool epoch() {
@@ -66,6 +86,10 @@
     return _epoch_state ? (u1)0 : (u1)1;
   }
 
+  static bool is_synchronizing() {
+    return Atomic::load_acquire(&_synchronizing);
+  }
+
   static traceid in_use_this_epoch_bit() {
     return _epoch_state ? USED_EPOCH_2_BIT : USED_EPOCH_1_BIT;
   }
@@ -91,16 +115,16 @@
   }
 
   static bool has_changed_tag_state() {
-    if (Atomic::load_acquire(&_tag_state)) {
-      Atomic::release_store(&_tag_state, false);
+    if (changed_tag_state()) {
+      set_tag_state(false);
       return true;
     }
     return false;
   }
 
   static void set_changed_tag_state() {
-    if (!Atomic::load_acquire(&_tag_state)) {
-      Atomic::release_store(&_tag_state, true);
+    if (!changed_tag_state()) {
+      set_tag_state(true);
     }
   }
 };
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp	Mon Dec 16 14:10:20 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp	Sat Dec 21 12:45:08 2019 +0100
@@ -46,9 +46,9 @@
 #include "jfr/utilities/jfrTypes.hpp"
 #include "logging/log.hpp"
 #include "memory/resourceArea.hpp"
+#include "runtime/atomic.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/mutexLocker.hpp"
-#include "runtime/orderAccess.hpp"
 #include "runtime/os.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/thread.inline.hpp"
@@ -390,11 +390,12 @@
 
 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() {
@@ -559,14 +560,15 @@
   assert(SafepointSynchronize::is_at_safepoint(), "invariant");
   assert(is_rotation_safepoint_pending(), "invariant");
   set_rotation_safepoint_pending(false);
+  _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() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/support/jfrEpochSynchronization.cpp	Sat Dec 21 12:45:08 2019 +0100
@@ -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.inline.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);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/support/jfrEpochSynchronization.hpp	Sat Dec 21 12:45:08 2019 +0100
@@ -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