src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp
changeset 58863 c16ac7a2eba4
parent 58132 caa25ab47aca
child 59226 a0f39cc47387
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp	Wed Oct 30 16:14:56 2019 +0100
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp	Wed Oct 30 19:43:52 2019 +0100
@@ -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"
@@ -55,81 +56,102 @@
 #include "runtime/vmOperations.hpp"
 #include "runtime/vmThread.hpp"
 
-// set data iff *dest == NULL
-static bool try_set(void* const data, void** dest, bool clear) {
-  assert(data != NULL, "invariant");
-  const void* const current = OrderAccess::load_acquire(dest);
-  if (current != NULL) {
-    if (current != data) {
-      // already set
-      return false;
-    }
-    assert(current == data, "invariant");
-    if (!clear) {
-      // recursion disallowed
-      return false;
-    }
+// incremented on each flushpoint
+static u8 flushpoint_id = 0;
+
+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;
   }
-  return Atomic::cmpxchg(clear ? NULL : data, dest, current) == current;
-}
-
-static void* rotation_thread = NULL;
-static const int rotation_try_limit = 1000;
-static const int rotation_retry_sleep_millis = 10;
-
-class RotationLock : public StackObj {
- private:
-  Thread* const _thread;
-  bool _acquired;
+  u4 elements() const { return _elements; }
+};
 
-  void log(bool recursion) {
-    assert(!_acquired, "invariant");
-    const char* error_msg = NULL;
-    if (recursion) {
-      error_msg = "Unable to issue rotation due to recursive calls.";
-    }
-    else {
-      error_msg = "Unable to issue rotation due to wait timeout.";
-    }
-    log_info(jfr)( // For user, should not be "jfr, system"
-      "%s", error_msg);
-  }
+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:
-  RotationLock(Thread* thread) : _thread(thread), _acquired(false) {
-    assert(_thread != NULL, "invariant");
-    if (_thread == rotation_thread) {
-      // recursion not supported
-      log(true);
-      return;
-    }
+  typedef typename Content::EventType EventType;
 
-    // limited to not spin indefinitely
-    for (int i = 0; i < rotation_try_limit; ++i) {
-      if (try_set(_thread, &rotation_thread, false)) {
-        _acquired = true;
-        assert(_thread == rotation_thread, "invariant");
-        return;
-      }
-      if (_thread->is_Java_thread()) {
-        // in order to allow the system to move to a safepoint
-        MutexLocker msg_lock(JfrMsg_lock);
-        JfrMsg_lock->wait(rotation_retry_sleep_millis);
-      }
-      else {
-        os::naked_short_sleep(rotation_retry_sleep_millis);
-      }
-    }
-    log(false);
+  WriteContent(JfrChunkWriter& cw, Content& content) :
+    _start_time(JfrTicks::now()),
+    _end_time(),
+    _cw(cw),
+    _content(content),
+    _start_offset(_cw.current_offset()) {
+    assert(_cw.is_valid(), "invariant");
+  }
+
+  bool process() {
+    // invocation
+    _content.process();
+    _end_time = JfrTicks::now();
+    return 0 != _content.elements();
   }
 
-  ~RotationLock() {
-    assert(_thread != NULL, "invariant");
-    if (_acquired) {
-      assert(_thread == rotation_thread, "invariant");
-      while (!try_set(_thread, &rotation_thread, true));
-    }
+  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();
   }
-  bool not_acquired() const { return !_acquired; }
+
+  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) {
@@ -138,65 +160,176 @@
   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 +341,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,19 +360,8 @@
   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()) {
-    return;
-  }
+  MutexLocker lock(JfrStream_lock);
   log_debug(jfr, system)("Request to START recording");
   assert(!is_recording(), "invariant");
   clear();
@@ -268,9 +380,9 @@
 }
 
 void JfrRecorderService::pre_safepoint_clear() {
-  _stack_trace_repository.clear();
   _string_pool.clear();
   _storage.clear();
+  _stack_trace_repository.clear();
 }
 
 void JfrRecorderService::invoke_safepoint_clear() {
@@ -278,28 +390,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();
+  _stack_trace_repository.clear();
 }
 
 void JfrRecorderService::post_safepoint_clear() {
   _checkpoint_manager.clear();
 }
 
+void JfrRecorderService::open_new_chunk(bool vm_error) {
+  JfrChunkRotation::on_rotation();
+  const bool valid_chunk = _repository.open_chunk(vm_error);
+  _storage.control().set_to_disk(valid_chunk);
+  if (valid_chunk) {
+    _checkpoint_manager.write_static_type_set_and_threads();
+  }
+}
+
 static void stop() {
   assert(JfrRecorderService::is_recording(), "invariant");
   log_debug(jfr, system)("Recording STOPPED");
@@ -307,11 +419,30 @@
   assert(!JfrRecorderService::is_recording(), "invariant");
 }
 
+void JfrRecorderService::prepare_for_vm_error_rotation() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
+  if (!_chunkwriter.is_valid()) {
+    open_new_chunk(true);
+  }
+  _checkpoint_manager.register_service_thread(Thread::current());
+}
+
+void JfrRecorderService::vm_error_rotation() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
+  if (_chunkwriter.is_valid()) {
+    Thread* const t = Thread::current();
+    _storage.flush_regular_buffer(t->jfr_thread_local()->native_buffer(), t);
+    invoke_flush();
+    _chunkwriter.set_time_stamp();
+    _repository.close_chunk();
+    assert(!_chunkwriter.is_valid(), "invariant");
+    _repository.on_vm_error();
+  }
+}
+
 void JfrRecorderService::rotate(int msgs) {
-  RotationLock rl(Thread::current());
-  if (rl.not_acquired()) {
-    return;
-  }
+  assert(!JfrStream_lock->owned_by_self(), "invariant");
+  MutexLocker lock(JfrStream_lock);
   static bool vm_error = false;
   if (msgs & MSGBIT(MSG_VM_ERROR)) {
     vm_error = true;
@@ -329,45 +460,19 @@
   }
 }
 
-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");
+  assert(JfrStream_lock->owned_by_self(), "invariant");
   // currently running an in-memory recording
+  assert(!_storage.control().to_disk(), "invariant");
   open_new_chunk();
   if (_chunkwriter.is_valid()) {
     // dump all in-memory buffer data to the newly created chunk
-    serialize_storage_from_in_memory_recording();
+    write_storage(_storage, _chunkwriter);
   }
 }
 
-void JfrRecorderService::serialize_storage_from_in_memory_recording() {
-  assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!");
-  MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
-  _storage.write();
-}
-
 void JfrRecorderService::chunk_rotation() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
   finalize_current_chunk();
   open_new_chunk();
 }
@@ -375,7 +480,6 @@
 void JfrRecorderService::finalize_current_chunk() {
   assert(_chunkwriter.is_valid(), "invariant");
   write();
-  assert(!_chunkwriter.is_valid(), "invariant");
 }
 
 void JfrRecorderService::write() {
@@ -386,54 +490,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 (LeakProfiler::is_running()) {
     // Exclusive access to the object sampler instance.
     // The sampler is released (unlocked) later in post_safepoint_write.
     ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire(), _stack_trace_repository);
   }
-  _storage.write();
+  if (_string_pool.is_modified()) {
+    write_stringpool(_string_pool, _chunkwriter);
+  }
+  write_storage(_storage, _chunkwriter);
+  if (_stack_trace_repository.is_modified()) {
+    write_stacktrace(_stack_trace_repository, _chunkwriter, false);
+  }
 }
 
 void JfrRecorderService::invoke_safepoint_write() {
@@ -441,50 +511,18 @@
   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();
+  if (_string_pool.is_modified()) {
+    write_stringpool_safepoint(_string_pool, _chunkwriter);
+  }
+  _checkpoint_manager.on_rotation();
   _storage.write_at_safepoint();
   _checkpoint_manager.shift_epoch();
-  _chunkwriter.time_stamp_chunk_now();
-  JfrMetadataEvent::lock();
-}
-
-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;
+  _chunkwriter.set_time_stamp();
+  write_stacktrace(_stack_trace_repository, _chunkwriter, true);
 }
 
-//
-// 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 +535,84 @@
     // 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(Thread* t) {
+  assert(t != NULL, "invariant");
+  return t->jfr_thread_local()->native_buffer();
 }
 
-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 void reset_buffer(JfrBuffer* buffer, Thread* t) {
+  assert(buffer != NULL, "invariant");
+  assert(t != NULL, "invariant");
+  assert(buffer == thread_local_buffer(t), "invariant");
+  buffer->set_pos(const_cast<u1*>(buffer->top()));
+}
+
+static void reset_thread_local_buffer(Thread* t) {
+  reset_buffer(thread_local_buffer(t), t);
+}
+
+static void write_thread_local_buffer(JfrChunkWriter& chunkwriter, Thread* t) {
+  JfrBuffer * const buffer = thread_local_buffer(t);
+  assert(buffer != NULL, "invariant");
+  if (!buffer->empty()) {
+    chunkwriter.write_unbuffered(buffer->top(), buffer->pos() - buffer->top());
+    reset_buffer(buffer, t);
   }
 }
 
-void JfrRecorderService::finalize_current_chunk_on_vm_error() {
+size_t JfrRecorderService::flush() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
+  size_t total_elements = flush_metadata(_chunkwriter);
+  const size_t storage_elements = flush_storage(_storage, _chunkwriter);
+  if (0 == storage_elements) {
+    return total_elements;
+  }
+  total_elements += storage_elements;
+  if (_string_pool.is_modified()) {
+    total_elements += flush_stringpool(_string_pool, _chunkwriter);
+  }
+  if (_stack_trace_repository.is_modified()) {
+    total_elements += flush_stacktrace(_stack_trace_repository, _chunkwriter);
+  }
+  if (_checkpoint_manager.is_type_set_required()) {
+    total_elements += flush_typeset(_checkpoint_manager, _chunkwriter);
+  } else if (_checkpoint_manager.is_static_type_set_required()) {
+    // don't tally this, it is only in order to flush the waiting constants
+    _checkpoint_manager.flush_static_type_set();
+  }
+  return total_elements;
+}
+
+typedef Content<EventFlush, JfrRecorderService, &JfrRecorderService::flush> FlushFunctor;
+typedef WriteContent<FlushFunctor> Flush;
+
+void JfrRecorderService::invoke_flush() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
   assert(_chunkwriter.is_valid(), "invariant");
-  pre_safepoint_write();
-  // Do not attempt safepoint dependent operations during emergency dump.
-  // Optimistically write tagged artifacts.
-  _checkpoint_manager.shift_epoch();
-  // update time
-  _chunkwriter.time_stamp_chunk_now();
-  post_safepoint_write();
-  assert(!_chunkwriter.is_valid(), "invariant");
+  Thread* const t = Thread::current();
+  ResourceMark rm(t);
+  HandleMark hm(t);
+  ++flushpoint_id;
+  reset_thread_local_buffer(t);
+  FlushFunctor flushpoint(*this);
+  Flush fl(_chunkwriter, flushpoint);
+  invoke_with_flush_event(fl);
+  write_thread_local_buffer(_chunkwriter, t);
+  _repository.flush_chunk();
+}
+
+void JfrRecorderService::flushpoint() {
+  MutexLocker lock(JfrStream_lock);
+  invoke_flush();
 }
 
 void JfrRecorderService::process_full_buffers() {
   if (_chunkwriter.is_valid()) {
-    assert(!JfrStream_lock->owned_by_self(), "invariant");
-    MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
     _storage.write_full();
   }
 }