apetushkov@9858: /* apetushkov@9858: * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. apetushkov@9858: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. apetushkov@9858: * apetushkov@9858: * This code is free software; you can redistribute it and/or modify it apetushkov@9858: * under the terms of the GNU General Public License version 2 only, as apetushkov@9858: * published by the Free Software Foundation. apetushkov@9858: * apetushkov@9858: * This code is distributed in the hope that it will be useful, but WITHOUT apetushkov@9858: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or apetushkov@9858: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License apetushkov@9858: * version 2 for more details (a copy is included in the LICENSE file that apetushkov@9858: * accompanied this code). apetushkov@9858: * apetushkov@9858: * You should have received a copy of the GNU General Public License version apetushkov@9858: * 2 along with this work; if not, write to the Free Software Foundation, apetushkov@9858: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. apetushkov@9858: * apetushkov@9858: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA apetushkov@9858: * or visit www.oracle.com if you need additional information or have any apetushkov@9858: * questions. apetushkov@9858: * apetushkov@9858: */ apetushkov@9858: apetushkov@9858: #include "precompiled.hpp" apetushkov@9858: #include "jfr/jni/jfrJavaSupport.hpp" ddong@9885: #include "jfr/leakprofiler/leakProfiler.hpp" apetushkov@9858: #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" ddong@9885: #include "jfr/leakprofiler/sampling/objectSampler.hpp" apetushkov@9858: #include "jfr/recorder/jfrRecorder.hpp" apetushkov@9858: #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" apetushkov@9858: #include "jfr/recorder/checkpoint/jfrMetadataEvent.hpp" mgronlun@9875: #include "jfr/recorder/repository/jfrChunkRotation.hpp" apetushkov@9858: #include "jfr/recorder/repository/jfrChunkWriter.hpp" apetushkov@9858: #include "jfr/recorder/repository/jfrRepository.hpp" apetushkov@9858: #include "jfr/recorder/service/jfrPostBox.hpp" apetushkov@9858: #include "jfr/recorder/service/jfrRecorderService.hpp" apetushkov@9858: #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" apetushkov@9858: #include "jfr/recorder/storage/jfrStorage.hpp" apetushkov@9858: #include "jfr/recorder/storage/jfrStorageControl.hpp" apetushkov@9858: #include "jfr/recorder/stringpool/jfrStringPool.hpp" apetushkov@9858: #include "jfr/utilities/jfrAllocation.hpp" apetushkov@9858: #include "jfr/utilities/jfrTime.hpp" apetushkov@9858: #include "jfr/writers/jfrJavaEventWriter.hpp" apetushkov@9858: #include "jfr/utilities/jfrTypes.hpp" apetushkov@9858: #include "memory/resourceArea.hpp" apetushkov@9858: #include "runtime/atomic.hpp" apetushkov@9858: #include "runtime/handles.inline.hpp" apetushkov@9858: #include "runtime/mutexLocker.hpp" apetushkov@9858: #include "runtime/orderAccess.hpp" apetushkov@9858: #include "runtime/os.hpp" apetushkov@9858: #include "runtime/safepoint.hpp" apetushkov@9858: #include "runtime/thread.inline.hpp" apetushkov@9858: #include "runtime/vm_operations.hpp" apetushkov@9858: #include "runtime/vmThread.hpp" apetushkov@9858: apetushkov@9858: // set data iff *dest == NULL apetushkov@9858: static bool try_set(void* const data, void** dest, bool clear) { apetushkov@9858: assert(data != NULL, "invariant"); apetushkov@9858: void* const current = OrderAccess::load_ptr_acquire(dest); apetushkov@9858: if (current != NULL) { apetushkov@9858: if (current != data) { apetushkov@9858: // already set apetushkov@9858: return false; apetushkov@9858: } apetushkov@9858: assert(current == data, "invariant"); apetushkov@9858: if (!clear) { apetushkov@9858: // recursion disallowed apetushkov@9858: return false; apetushkov@9858: } apetushkov@9858: } apetushkov@9858: return Atomic::cmpxchg_ptr(clear ? NULL : data, dest, current) == current; apetushkov@9858: } apetushkov@9858: apetushkov@9858: static void* rotation_thread = NULL; apetushkov@9858: static const int rotation_try_limit = 1000; apetushkov@9858: static const int rotation_retry_sleep_millis = 10; apetushkov@9858: apetushkov@9858: class RotationLock : public StackObj { apetushkov@9858: private: apetushkov@9858: Thread* const _thread; apetushkov@9858: bool _acquired; apetushkov@9858: apetushkov@9858: void log(bool recursion) { apetushkov@9858: assert(!_acquired, "invariant"); apetushkov@9858: const char* error_msg = NULL; apetushkov@9858: if (recursion) { apetushkov@9858: error_msg = "Unable to issue rotation due to recursive calls."; apetushkov@9858: } apetushkov@9858: else { apetushkov@9858: error_msg = "Unable to issue rotation due to wait timeout."; apetushkov@9858: } apetushkov@9858: if (LogJFR) tty->print_cr( // For user, should not be "jfr, system" apetushkov@9858: "%s", error_msg); apetushkov@9858: } apetushkov@9858: public: apetushkov@9858: RotationLock(Thread* thread) : _thread(thread), _acquired(false) { apetushkov@9858: assert(_thread != NULL, "invariant"); apetushkov@9858: if (_thread == rotation_thread) { apetushkov@9858: // recursion not supported apetushkov@9858: log(true); apetushkov@9858: return; apetushkov@9858: } apetushkov@9858: apetushkov@9858: // limited to not spin indefinitely apetushkov@9858: for (int i = 0; i < rotation_try_limit; ++i) { apetushkov@9858: if (try_set(_thread, &rotation_thread, false)) { apetushkov@9858: _acquired = true; apetushkov@9858: assert(_thread == rotation_thread, "invariant"); apetushkov@9858: return; apetushkov@9858: } apetushkov@9858: if (_thread->is_Java_thread()) { apetushkov@9858: // in order to allow the system to move to a safepoint apetushkov@9858: MutexLockerEx msg_lock(JfrMsg_lock); apetushkov@9858: JfrMsg_lock->wait(false, rotation_retry_sleep_millis); apetushkov@9858: } apetushkov@9858: else { apetushkov@9858: os::naked_short_sleep(rotation_retry_sleep_millis); apetushkov@9858: } apetushkov@9858: } apetushkov@9858: log(false); apetushkov@9858: } apetushkov@9858: apetushkov@9858: ~RotationLock() { apetushkov@9858: assert(_thread != NULL, "invariant"); apetushkov@9858: if (_acquired) { apetushkov@9858: assert(_thread == rotation_thread, "invariant"); apetushkov@9858: while (!try_set(_thread, &rotation_thread, true)); apetushkov@9858: } apetushkov@9858: } apetushkov@9858: bool not_acquired() const { return !_acquired; } apetushkov@9858: }; apetushkov@9858: apetushkov@9858: static intptr_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) { apetushkov@9858: const intptr_t prev_cp_offset = cw.previous_checkpoint_offset(); apetushkov@9858: const intptr_t prev_cp_relative_offset = 0 == prev_cp_offset ? 0 : prev_cp_offset - cw.current_offset(); apetushkov@9858: cw.reserve(sizeof(u4)); apetushkov@9858: cw.write(EVENT_CHECKPOINT); apetushkov@9858: cw.write(JfrTicks::now()); apetushkov@9858: cw.write((jlong)0); apetushkov@9858: cw.write((jlong)prev_cp_relative_offset); // write previous checkpoint offset delta apetushkov@9858: cw.write(false); // flushpoint apetushkov@9858: cw.write((u4)1); // nof types in this checkpoint apetushkov@9858: cw.write(type_id); apetushkov@9858: const intptr_t number_of_elements_offset = cw.current_offset(); apetushkov@9858: cw.reserve(sizeof(u4)); apetushkov@9858: return number_of_elements_offset; apetushkov@9858: } apetushkov@9858: apetushkov@9858: template apetushkov@9858: class WriteCheckpointEvent : public StackObj { apetushkov@9858: private: apetushkov@9858: JfrChunkWriter& _cw; apetushkov@9858: u8 _type_id; apetushkov@9858: ContentFunctor& _content_functor; apetushkov@9858: public: apetushkov@9858: WriteCheckpointEvent(JfrChunkWriter& cw, u8 type_id, ContentFunctor& functor) : apetushkov@9858: _cw(cw), apetushkov@9858: _type_id(type_id), apetushkov@9858: _content_functor(functor) { apetushkov@9858: assert(_cw.is_valid(), "invariant"); apetushkov@9858: } apetushkov@9858: bool process() { apetushkov@9858: // current_cp_offset is also offset for the event size header field apetushkov@9858: const intptr_t current_cp_offset = _cw.current_offset(); apetushkov@9858: const intptr_t num_elements_offset = write_checkpoint_event_prologue(_cw, _type_id); apetushkov@9858: // invocation apetushkov@9858: _content_functor.process(); apetushkov@9858: const u4 number_of_elements = (u4)_content_functor.processed(); apetushkov@9858: if (number_of_elements == 0) { apetushkov@9858: // nothing to do, rewind writer to start apetushkov@9858: _cw.seek(current_cp_offset); apetushkov@9858: return true; apetushkov@9858: } apetushkov@9858: assert(number_of_elements > 0, "invariant"); apetushkov@9858: assert(_cw.current_offset() > num_elements_offset, "invariant"); apetushkov@9858: _cw.write_padded_at_offset(number_of_elements, num_elements_offset); apetushkov@9858: _cw.write_padded_at_offset((u4)_cw.current_offset() - current_cp_offset, current_cp_offset); apetushkov@9858: // update writer with last checkpoint position apetushkov@9858: _cw.set_previous_checkpoint_offset(current_cp_offset); apetushkov@9858: return true; apetushkov@9858: } apetushkov@9858: }; apetushkov@9858: apetushkov@9858: template apetushkov@9858: class ServiceFunctor { apetushkov@9858: private: apetushkov@9858: Instance& _instance; apetushkov@9858: size_t _processed; apetushkov@9858: public: apetushkov@9858: ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {} apetushkov@9858: bool process() { apetushkov@9858: _processed = (_instance.*func)(); apetushkov@9858: return true; apetushkov@9858: } apetushkov@9858: size_t processed() const { return _processed; } apetushkov@9858: }; apetushkov@9858: apetushkov@9858: template apetushkov@9858: class JfrVMOperation : public VM_Operation { apetushkov@9858: private: apetushkov@9858: Instance& _instance; apetushkov@9858: public: apetushkov@9858: JfrVMOperation(Instance& instance) : _instance(instance) {} apetushkov@9858: void doit() { (_instance.*func)(); } apetushkov@9858: VMOp_Type type() const { return VMOp_JFRCheckpoint; } apetushkov@9858: Mode evaluation_mode() const { return _safepoint; } // default apetushkov@9858: }; apetushkov@9858: apetushkov@9858: class WriteStackTraceRepository : public StackObj { apetushkov@9858: private: apetushkov@9858: JfrStackTraceRepository& _repo; apetushkov@9858: JfrChunkWriter& _cw; apetushkov@9858: size_t _elements_processed; apetushkov@9858: bool _clear; apetushkov@9858: apetushkov@9858: public: apetushkov@9858: WriteStackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) : apetushkov@9858: _repo(repo), _cw(cw), _elements_processed(0), _clear(clear) {} apetushkov@9858: bool process() { apetushkov@9858: _elements_processed = _repo.write(_cw, _clear); apetushkov@9858: return true; apetushkov@9858: } apetushkov@9858: size_t processed() const { return _elements_processed; } apetushkov@9858: void reset() { _elements_processed = 0; } apetushkov@9858: }; apetushkov@9858: apetushkov@9858: static bool recording = false; apetushkov@9858: apetushkov@9858: static void set_recording_state(bool is_recording) { apetushkov@9858: OrderAccess::storestore(); apetushkov@9858: recording = is_recording; apetushkov@9858: } apetushkov@9858: apetushkov@9858: bool JfrRecorderService::is_recording() { apetushkov@9858: return recording; apetushkov@9858: } apetushkov@9858: apetushkov@9858: JfrRecorderService::JfrRecorderService() : apetushkov@9858: _checkpoint_manager(JfrCheckpointManager::instance()), apetushkov@9858: _chunkwriter(JfrRepository::chunkwriter()), apetushkov@9858: _repository(JfrRepository::instance()), apetushkov@9858: _storage(JfrStorage::instance()), apetushkov@9858: _stack_trace_repository(JfrStackTraceRepository::instance()), apetushkov@9858: _string_pool(JfrStringPool::instance()) {} apetushkov@9858: apetushkov@9858: void JfrRecorderService::start() { apetushkov@9858: RotationLock rl(Thread::current()); apetushkov@9858: if (rl.not_acquired()) { apetushkov@9858: return; apetushkov@9858: } apetushkov@9858: if (LogJFR) tty->print_cr("Request to START recording"); apetushkov@9858: assert(!is_recording(), "invariant"); apetushkov@9858: clear(); apetushkov@9858: set_recording_state(true); apetushkov@9858: assert(is_recording(), "invariant"); apetushkov@9858: open_new_chunk(); apetushkov@9858: if (LogJFR) tty->print_cr("Recording STARTED"); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::clear() { apetushkov@9858: ResourceMark rm; apetushkov@9858: HandleMark hm; apetushkov@9858: pre_safepoint_clear(); apetushkov@9858: invoke_safepoint_clear(); apetushkov@9858: post_safepoint_clear(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::pre_safepoint_clear() { apetushkov@9858: _stack_trace_repository.clear(); apetushkov@9858: _string_pool.clear(); apetushkov@9858: _storage.clear(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::invoke_safepoint_clear() { apetushkov@9858: JfrVMOperation safepoint_task(*this); apetushkov@9858: VMThread::execute(&safepoint_task); apetushkov@9858: } apetushkov@9858: apetushkov@9858: // apetushkov@9858: // safepoint clear sequence apetushkov@9858: // apetushkov@9858: // clear stacktrace repository -> apetushkov@9858: // clear string pool -> apetushkov@9858: // clear storage -> apetushkov@9858: // shift epoch -> apetushkov@9858: // update time apetushkov@9858: // apetushkov@9858: void JfrRecorderService::safepoint_clear() { apetushkov@9858: assert(SafepointSynchronize::is_at_safepoint(), "invariant"); apetushkov@9858: _stack_trace_repository.clear(); apetushkov@9858: _string_pool.clear(); apetushkov@9858: _storage.clear(); apetushkov@9858: _checkpoint_manager.shift_epoch(); apetushkov@9858: _chunkwriter.time_stamp_chunk_now(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::post_safepoint_clear() { apetushkov@9858: _checkpoint_manager.clear(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: static void stop() { apetushkov@9858: assert(JfrRecorderService::is_recording(), "invariant"); apetushkov@9858: if (LogJFR) tty->print_cr("Recording STOPPED"); apetushkov@9858: set_recording_state(false); apetushkov@9858: assert(!JfrRecorderService::is_recording(), "invariant"); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::rotate(int msgs) { apetushkov@9858: RotationLock rl(Thread::current()); apetushkov@9858: if (rl.not_acquired()) { apetushkov@9858: return; apetushkov@9858: } apetushkov@9858: static bool vm_error = false; apetushkov@9858: if (msgs & MSGBIT(MSG_VM_ERROR)) { apetushkov@9858: vm_error = true; apetushkov@9858: prepare_for_vm_error_rotation(); apetushkov@9858: } apetushkov@9858: if (msgs & (MSGBIT(MSG_STOP))) { apetushkov@9858: stop(); apetushkov@9858: } apetushkov@9858: // action determined by chunkwriter state apetushkov@9858: if (!_chunkwriter.is_valid()) { apetushkov@9858: in_memory_rotation(); apetushkov@9858: return; apetushkov@9858: } apetushkov@9858: if (vm_error) { apetushkov@9858: vm_error_rotation(); apetushkov@9858: return; apetushkov@9858: } apetushkov@9858: chunk_rotation(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::prepare_for_vm_error_rotation() { apetushkov@9858: if (!_chunkwriter.is_valid()) { apetushkov@9858: open_new_chunk(true); apetushkov@9858: } apetushkov@9858: _checkpoint_manager.register_service_thread(Thread::current()); ddong@9885: JfrMetadataEvent::lock(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::open_new_chunk(bool vm_error) { apetushkov@9858: assert(!_chunkwriter.is_valid(), "invariant"); apetushkov@9858: assert(!JfrStream_lock->owned_by_self(), "invariant"); mgronlun@9875: JfrChunkRotation::on_rotation(); apetushkov@9858: MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); apetushkov@9858: if (!_repository.open_chunk(vm_error)) { apetushkov@9858: assert(!_chunkwriter.is_valid(), "invariant"); apetushkov@9858: _storage.control().set_to_disk(false); apetushkov@9858: return; apetushkov@9858: } apetushkov@9858: assert(_chunkwriter.is_valid(), "invariant"); apetushkov@9858: _storage.control().set_to_disk(true); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::in_memory_rotation() { apetushkov@9858: assert(!_chunkwriter.is_valid(), "invariant"); apetushkov@9858: // currently running an in-memory recording apetushkov@9858: open_new_chunk(); apetushkov@9858: if (_chunkwriter.is_valid()) { apetushkov@9858: // dump all in-memory buffer data to the newly created chunk apetushkov@9858: serialize_storage_from_in_memory_recording(); apetushkov@9858: } apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::serialize_storage_from_in_memory_recording() { apetushkov@9858: assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!"); apetushkov@9858: MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); apetushkov@9858: _storage.write(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::chunk_rotation() { apetushkov@9858: finalize_current_chunk(); apetushkov@9858: open_new_chunk(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::finalize_current_chunk() { apetushkov@9858: assert(_chunkwriter.is_valid(), "invariant"); apetushkov@9858: write(); apetushkov@9858: assert(!_chunkwriter.is_valid(), "invariant"); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::write() { apetushkov@9858: ResourceMark rm; apetushkov@9858: HandleMark hm; apetushkov@9858: pre_safepoint_write(); apetushkov@9858: invoke_safepoint_write(); apetushkov@9858: post_safepoint_write(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: typedef ServiceFunctor WriteStringPool; apetushkov@9858: typedef ServiceFunctor WriteStringPoolSafepoint; apetushkov@9858: typedef WriteCheckpointEvent WriteStackTraceCheckpoint; apetushkov@9858: typedef WriteCheckpointEvent WriteStringPoolCheckpoint; apetushkov@9858: typedef WriteCheckpointEvent WriteStringPoolCheckpointSafepoint; apetushkov@9858: apetushkov@9858: static void write_stacktrace_checkpoint(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) { apetushkov@9858: WriteStackTraceRepository write_stacktrace_repo(stack_trace_repo, chunkwriter, clear); apetushkov@9858: WriteStackTraceCheckpoint write_stack_trace_checkpoint(chunkwriter, TYPE_STACKTRACE, write_stacktrace_repo); apetushkov@9858: write_stack_trace_checkpoint.process(); apetushkov@9858: } apetushkov@9858: ddong@9885: static void write_object_sample_stacktrace(ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repository) { ddong@9885: WriteObjectSampleStacktrace object_sample_stacktrace(sampler, stack_trace_repository); ddong@9885: object_sample_stacktrace.process(); ddong@9885: } ddong@9885: apetushkov@9858: static void write_stringpool_checkpoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { apetushkov@9858: WriteStringPool write_string_pool(string_pool); apetushkov@9858: WriteStringPoolCheckpoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool); apetushkov@9858: write_string_pool_checkpoint.process(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: static void write_stringpool_checkpoint_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { apetushkov@9858: WriteStringPoolSafepoint write_string_pool(string_pool); apetushkov@9858: WriteStringPoolCheckpointSafepoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool); apetushkov@9858: write_string_pool_checkpoint.process(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: // apetushkov@9858: // pre-safepoint write sequence apetushkov@9858: // apetushkov@9858: // lock stream lock -> apetushkov@9858: // write non-safepoint dependent types -> apetushkov@9858: // write checkpoint epoch transition list-> apetushkov@9858: // write stack trace checkpoint -> apetushkov@9858: // write string pool checkpoint -> ddong@9885: // write object sample stacktraces -> ddong@9885: // write storage -> ddong@9885: // release stream lock apetushkov@9858: // apetushkov@9858: void JfrRecorderService::pre_safepoint_write() { apetushkov@9858: MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); apetushkov@9858: assert(_chunkwriter.is_valid(), "invariant"); apetushkov@9858: _checkpoint_manager.write_types(); apetushkov@9858: _checkpoint_manager.write_epoch_transition_mspace(); apetushkov@9858: write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, false); apetushkov@9858: write_stringpool_checkpoint(_string_pool, _chunkwriter); ddong@9885: if (LeakProfiler::is_running()) { ddong@9885: // Exclusive access to the object sampler instance. ddong@9885: // The sampler is released (unlocked) later in post_safepoint_write. ddong@9885: ObjectSampler* const sampler = ObjectSampler::acquire(); ddong@9885: assert(sampler != NULL, "invariant"); ddong@9885: write_object_sample_stacktrace(sampler, _stack_trace_repository); ddong@9885: } apetushkov@9858: _storage.write(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::invoke_safepoint_write() { apetushkov@9858: JfrVMOperation safepoint_task(*this); apetushkov@9858: VMThread::execute(&safepoint_task); apetushkov@9858: } apetushkov@9858: apetushkov@9858: // apetushkov@9858: // safepoint write sequence apetushkov@9858: // apetushkov@9858: // lock stream lock -> apetushkov@9858: // write stacktrace repository -> apetushkov@9858: // write string pool -> apetushkov@9858: // write safepoint dependent types -> apetushkov@9858: // write storage -> apetushkov@9858: // shift_epoch -> apetushkov@9858: // update time -> apetushkov@9858: // lock metadata descriptor -> apetushkov@9858: // release stream lock apetushkov@9858: // apetushkov@9858: void JfrRecorderService::safepoint_write() { apetushkov@9858: assert(SafepointSynchronize::is_at_safepoint(), "invariant"); apetushkov@9858: MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); apetushkov@9858: write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, true); apetushkov@9858: write_stringpool_checkpoint_safepoint(_string_pool, _chunkwriter); apetushkov@9858: _checkpoint_manager.write_safepoint_types(); apetushkov@9858: _storage.write_at_safepoint(); apetushkov@9858: _checkpoint_manager.shift_epoch(); apetushkov@9858: _chunkwriter.time_stamp_chunk_now(); apetushkov@9858: JfrMetadataEvent::lock(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: static jlong write_metadata_event(JfrChunkWriter& chunkwriter) { apetushkov@9858: assert(chunkwriter.is_valid(), "invariant"); apetushkov@9858: const jlong metadata_offset = chunkwriter.current_offset(); apetushkov@9858: JfrMetadataEvent::write(chunkwriter, metadata_offset); apetushkov@9858: return metadata_offset; apetushkov@9858: } apetushkov@9858: apetushkov@9858: // apetushkov@9858: // post-safepoint write sequence apetushkov@9858: // ddong@9885: // write type set -> ddong@9885: // release object sampler -> ddong@9885: // lock stream lock -> ddong@9885: // write checkpoints -> ddong@9885: // write metadata event -> ddong@9885: // write chunk header -> ddong@9885: // close chunk fd -> ddong@9885: // release stream lock apetushkov@9858: // apetushkov@9858: void JfrRecorderService::post_safepoint_write() { apetushkov@9858: assert(_chunkwriter.is_valid(), "invariant"); apetushkov@9858: // During the safepoint tasks just completed, the system transitioned to a new epoch. apetushkov@9858: // Type tagging is epoch relative which entails we are able to write out the apetushkov@9858: // already tagged artifacts for the previous epoch. We can accomplish this concurrently apetushkov@9858: // with threads now tagging artifacts in relation to the new, now updated, epoch and remain outside of a safepoint. apetushkov@9858: _checkpoint_manager.write_type_set(); ddong@9885: if (LeakProfiler::is_running()) { ddong@9885: // The object sampler instance was exclusively acquired and locked in pre_safepoint_write. ddong@9885: // Note: There is a dependency on write_type_set() above, ensure the release is subsequent. ddong@9885: ObjectSampler::release(); ddong@9885: } MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); apetushkov@9858: // serialize any outstanding checkpoint memory apetushkov@9858: _checkpoint_manager.write(); apetushkov@9858: // serialize the metadata descriptor event and close out the chunk apetushkov@9858: _repository.close_chunk(write_metadata_event(_chunkwriter)); apetushkov@9858: assert(!_chunkwriter.is_valid(), "invariant"); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::vm_error_rotation() { apetushkov@9858: if (_chunkwriter.is_valid()) { apetushkov@9858: finalize_current_chunk_on_vm_error(); apetushkov@9858: assert(!_chunkwriter.is_valid(), "invariant"); apetushkov@9858: _repository.on_vm_error(); apetushkov@9858: } apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::finalize_current_chunk_on_vm_error() { apetushkov@9858: assert(_chunkwriter.is_valid(), "invariant"); apetushkov@9858: pre_safepoint_write(); apetushkov@9858: // Do not attempt safepoint dependent operations during emergency dump. apetushkov@9858: // Optimistically write tagged artifacts. apetushkov@9858: _checkpoint_manager.shift_epoch(); apetushkov@9858: // update time apetushkov@9858: _chunkwriter.time_stamp_chunk_now(); apetushkov@9858: post_safepoint_write(); apetushkov@9858: assert(!_chunkwriter.is_valid(), "invariant"); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::process_full_buffers() { apetushkov@9858: if (_chunkwriter.is_valid()) { apetushkov@9858: assert(!JfrStream_lock->owned_by_self(), "invariant"); apetushkov@9858: MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); apetushkov@9858: _storage.write_full(); apetushkov@9858: } apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::scavenge() { apetushkov@9858: _storage.scavenge(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void JfrRecorderService::evaluate_chunk_size_for_rotation() { mgronlun@9875: JfrChunkRotation::evaluate(_chunkwriter); apetushkov@9858: }