diff -r 2c3cc4b01880 -r c16ac7a2eba4 src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp --- 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 +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 +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(elements(), offset); + } + + void write_size() { + _cw.write_padded_at_offset(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(EVENT_CHECKPOINT); cw.write(JfrTicks::now()); - cw.write((int64_t)0); // duration + cw.write(0); // duration cw.write(delta_to_last_checkpoint); - cw.write(false); // flushpoint - cw.write((u4)1); // nof types in this checkpoint + cw.write(GENERIC); // checkpoint type + cw.write(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 -class WriteCheckpointEvent : public StackObj { +template +class WriteCheckpointEvent : public WriteContent { 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(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::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(number_of_elements, num_elements_offset); - _cw.write_padded_at_offset((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 -class ServiceFunctor { +template +static u4 invoke(Functor& f) { + f.process(); + return f.elements(); +} + +template +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 +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 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 Storage; +typedef WriteContent 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 StringPool; +typedef Content StringPoolSafepoint; +typedef WriteCheckpointEvent WriteStringPool; +typedef WriteCheckpointEvent 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 FlushTypeSetFunctor; +typedef WriteContent 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 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 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 WriteStringPool; -typedef ServiceFunctor WriteStringPoolSafepoint; -typedef WriteCheckpointEvent WriteStackTraceCheckpoint; -typedef WriteCheckpointEvent WriteStringPoolCheckpoint; -typedef WriteCheckpointEvent 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(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 FlushFunctor; +typedef WriteContent 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(); } }