--- old/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp 2021-07-05 14:05:58.445128960 +0000 +++ new/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp 2021-07-05 14:05:58.385128068 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, 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 @@ -140,7 +140,9 @@ } ~StackTraceWrite() { assert(JfrStacktrace_lock->owned_by_self(), "invariant"); + // TODO: or after the unlock ? JfrStacktrace_lock->unlock(); + JfrStackTraceRepository::clear_leak_profiler(); } void sample_do(ObjectSample* sample) { @@ -148,7 +150,8 @@ if (!sample->is_dead()) { if (sample->has_stack_trace()) { JfrTraceId::use(sample->klass(), true); - _stack_trace_repo.write(_writer, sample->stack_trace_id(), sample->stack_trace_hash()); + traceid t = JfrStackTraceRepository::write_for_leak_profiler(_writer, sample->stack_trace_id(), sample->stack_trace_hash()); + tty->print_cr("ObjectSampleCheckpoint.cpp| Write %ld stack trace id | traceid %ld", sample->stack_trace_id(), t); ++_count; } } @@ -278,6 +281,7 @@ StackTraceWrite stack_trace_write(_stack_trace_repo, writer); // JfrStacktrace_lock do_samples(last, last_resolved, stack_trace_write); count = stack_trace_write.count(); + tty->print_cr("ObjectSampleCheckpoint.cpp| Write %d stack traces", count); } if (count == 0) { writer.set_context(ctx); --- old/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp 2021-07-05 14:05:58.761133659 +0000 +++ new/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp 2021-07-05 14:05:58.701132766 +0000 @@ -128,15 +128,39 @@ // We were successful in acquiring the try lock and have been selected for adding a sample. // Go ahead with installing our previously taken stacktrace into the stacktrace repository. -// + +// TODO: record_for_leak_profiler replaces this function because it's doing +// the JfrStackTraceRepository::add in leak_profile instance and set_cached_stack_trace_id. + traceid ObjectSampler::stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread) { assert(stacktrace != NULL, "invariant"); assert(stacktrace->hash() != 0, "invariant"); - const traceid stacktrace_id = JfrStackTraceRepository::add(stacktrace, thread); + const traceid stacktrace_id = JfrStackTraceRepository::add_for_leak_profiler(stacktrace, thread); thread->jfr_thread_local()->set_cached_stack_trace_id(stacktrace_id, stacktrace->hash()); return stacktrace_id; } +class RecordStackTrace { + private: + JavaThread* _jt; + JfrStackTrace* _stacktrace; + bool _enabled; + public: + RecordStackTrace(JavaThread* jt, JfrStackTrace* st) : _jt(jt), + _stacktrace(st), + _enabled(JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) { + if (_enabled) { + //tty->print_cr("objectSampler.cpp | ", st->) + JfrStackTraceRepository::record_for_leak_profiler(jt, st); + } + } + ~RecordStackTrace() { + if (_enabled) { + _jt->jfr_thread_local()->clear_cached_stack_trace(); + } + } +}; + void ObjectSampler::sample(HeapWord* obj, size_t allocated, JavaThread* thread) { assert(thread != NULL, "invariant"); assert(is_created(), "invariant"); @@ -145,9 +169,12 @@ if (thread_id == 0) { return; } + const JfrThreadLocal* const tl = thread->jfr_thread_local(); JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth()); fill_stacktrace(&stacktrace, thread); + + RecordStackTrace rst(thread, &stacktrace); // try enter critical section JfrTryLock tryLock(&_lock); @@ -156,11 +183,11 @@ return; } - instance().add(obj, allocated, thread_id, &stacktrace, thread); + instance().add(obj, allocated, thread_id/*, &stacktrace*/, thread); } -void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread) { - assert(stacktrace != NULL, "invariant"); +void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id/*, JfrStackTrace* stacktrace*/, JavaThread* thread) { + //assert(stacktrace != NULL, "invariant"); assert(thread_id != 0, "invariant"); assert(thread != NULL, "invariant"); assert(thread->jfr_thread_local()->has_thread_checkpoint(), "invariant"); @@ -189,11 +216,21 @@ sample->set_thread_id(thread_id); sample->set_thread_checkpoint(thread->jfr_thread_local()->thread_checkpoint()); + const JfrThreadLocal* const tl = thread->jfr_thread_local(); + const unsigned int stacktrace_hash = tl->cached_stack_trace_hash(); + if (stacktrace_hash != 0) { + //tty->print_cr("objectSampler.cpp | Set sample stack trace id(%ld)/hash(%d)", tl->cached_stack_trace_id(), stacktrace_hash); + sample->set_stack_trace_id(tl->cached_stack_trace_id()); + sample->set_stack_trace_hash(stacktrace_hash); + } + /* const unsigned int stacktrace_hash = stacktrace->hash(); if (stacktrace_hash != 0) { + // Only place where stacktrace_id() is used + // stacktrace_id() sample->set_stack_trace_id(stacktrace_id(stacktrace, thread)); sample->set_stack_trace_hash(stacktrace_hash); - } + }*/ sample->set_span(allocated); sample->set_object((oop)obj); --- old/src/share/vm/jfr/leakprofiler/sampling/objectSampler.hpp 2021-07-05 14:05:59.065138178 +0000 +++ new/src/share/vm/jfr/leakprofiler/sampling/objectSampler.hpp 2021-07-05 14:05:59.005137286 +0000 @@ -77,7 +77,7 @@ // Sampling static void sample(HeapWord* object, size_t size, JavaThread* thread); - void add(HeapWord* object, size_t size, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread); + void add(HeapWord* object, size_t size, traceid thread_id/*, JfrStackTrace* stacktrace*/, JavaThread* thread); void scavenge(); void remove_dead(ObjectSample* sample); --- old/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp 2021-07-05 14:05:59.361142580 +0000 +++ new/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp 2021-07-05 14:05:59.297141628 +0000 @@ -267,7 +267,8 @@ } void JfrRecorderService::pre_safepoint_clear() { - _stack_trace_repository.clear(); + //_stack_trace_repository.clear(); + JfrStackTraceRepository::clear(); _string_pool.clear(); _storage.clear(); } @@ -288,7 +289,8 @@ // void JfrRecorderService::safepoint_clear() { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); - _stack_trace_repository.clear(); + //_stack_trace_repository.clear(); + JfrStackTraceRepository::clear(); _string_pool.clear(); _storage.clear(); _checkpoint_manager.shift_epoch(); @@ -442,6 +444,7 @@ ObjectSampler* const sampler = ObjectSampler::acquire(); assert(sampler != NULL, "invariant"); write_object_sample_stacktrace(sampler, _stack_trace_repository); + //write_object_sample_stacktrace(sampler, JfrStackTraceRepository::leak_profiler_instance()); } _storage.write(); } @@ -473,6 +476,7 @@ _storage.write_at_safepoint(); _checkpoint_manager.shift_epoch(); _chunkwriter.time_stamp_chunk_now(); + JfrStackTraceRepository::clear_leak_profiler(); JfrMetadataEvent::lock(); } --- old/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp 2021-07-05 14:05:59.665147100 +0000 +++ new/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp 2021-07-05 14:05:59.601146148 +0000 @@ -83,26 +83,42 @@ } static JfrStackTraceRepository* _instance = NULL; +static JfrStackTraceRepository* _leak_profiler_instance = NULL; +static traceid _next_id = 0; JfrStackTraceRepository& JfrStackTraceRepository::instance() { + assert(_instance != NULL, "invariant"); return *_instance; } +JfrStackTraceRepository& JfrStackTraceRepository::leak_profiler_instance() { + assert(_leak_profiler_instance != NULL, "invariant"); + return *_leak_profiler_instance; +} + JfrStackTraceRepository* JfrStackTraceRepository::create() { assert(_instance == NULL, "invariant"); + assert(_leak_profiler_instance == NULL, "invariant"); + _leak_profiler_instance = new JfrStackTraceRepository(); + if (_leak_profiler_instance == NULL) { + return NULL; + } _instance = new JfrStackTraceRepository(); return _instance; } void JfrStackTraceRepository::destroy() { - assert(_instance != NULL, "invarinat"); + assert(_instance != NULL, "invariant"); delete _instance; _instance = NULL; + delete _leak_profiler_instance; + _leak_profiler_instance = NULL; } -JfrStackTraceRepository::JfrStackTraceRepository() : _next_id(0), _entries(0) { +JfrStackTraceRepository::JfrStackTraceRepository() : _entries(0) { memset(_table, 0, sizeof(_table)); } + class JfrFrameType : public JfrSerializer { public: void serialize(JfrCheckpointWriter& writer) { @@ -122,22 +138,22 @@ return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType()); } -size_t JfrStackTraceRepository::clear() { +size_t JfrStackTraceRepository::clear(JfrStackTraceRepository& repo) { MutexLockerEx lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); - if (_entries == 0) { + if (repo._entries == 0) { return 0; } for (u4 i = 0; i < TABLE_SIZE; ++i) { - JfrStackTraceRepository::StackTrace* stacktrace = _table[i]; + JfrStackTraceRepository::StackTrace* stacktrace = repo._table[i]; while (stacktrace != NULL) { JfrStackTraceRepository::StackTrace* next = stacktrace->next(); delete stacktrace; stacktrace = next; } } - memset(_table, 0, sizeof(_table)); - const size_t processed = _entries; - _entries = 0; + memset(repo._table, 0, sizeof(repo._table)); + const size_t processed = repo._entries; + repo._entries = 0; return processed; } @@ -163,16 +179,39 @@ return id; } -traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) { - traceid tid = instance().add_trace(stacktrace); +traceid JfrStackTraceRepository::add(JfrStackTraceRepository& repo, const JfrStackTrace& stacktrace) { + traceid tid = repo.add_trace(stacktrace); if (tid == 0) { stacktrace.resolve_linenos(); - tid = instance().add_trace(stacktrace); + tid = repo.add_trace(stacktrace); } assert(tid != 0, "invariant"); return tid; } +traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) { + return add(instance(), stacktrace); +} + +void JfrStackTraceRepository::record_for_leak_profiler(JavaThread* thread, JfrStackTrace* stacktrace, int skip /* 0 */) { + //assert(stacktrace != NULL, "invariant"); + //assert(stacktrace->hash() != 0, "invariant"); + //const traceid stacktrace_id = JfrStackTraceRepository::add_for_leak_profiler(stacktrace, thread); + //thread->jfr_thread_local()->set_cached_stack_trace_id(stacktrace_id, stacktrace->hash()); + //return stacktrace_id; + + assert(thread != NULL, "invariant"); + JfrThreadLocal* const tl = thread->jfr_thread_local(); + assert(tl != NULL, "invariant"); + assert(!tl->has_cached_stack_trace(), "invariant"); + const unsigned int hash = stacktrace->hash(); + if (hash != 0) { + traceid t = add(leak_profiler_instance(), *stacktrace); + //tty->print_cr("jfrStackTraceRepository.cpp | Add stack traceid %ld", t); + tl->set_cached_stack_trace_id(t, hash); + } +} + traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) { assert(thread == Thread::current(), "invariant"); JfrThreadLocal* const tl = thread->jfr_thread_local(); @@ -198,11 +237,11 @@ return stacktrace.record_safe(thread, skip) ? add(stacktrace) : 0; } -traceid JfrStackTraceRepository::add(const JfrStackTrace* stacktrace, JavaThread* thread) { +traceid JfrStackTraceRepository::add_for_leak_profiler(const JfrStackTrace* stacktrace, JavaThread* thread) { assert(stacktrace != NULL, "invariant"); assert(thread != NULL, "invariant"); assert(stacktrace->hash() != 0, "invariant"); - return add(*stacktrace); + return add(leak_profiler_instance(), *stacktrace); } bool JfrStackTraceRepository::fill_stacktrace_for(JavaThread* thread, JfrStackTrace* stacktrace, int skip) { @@ -247,9 +286,9 @@ return _entries > 0 ? write_impl(sw, clear) : 0; } -traceid JfrStackTraceRepository::write(JfrCheckpointWriter& writer, traceid id, unsigned int hash) { +traceid JfrStackTraceRepository::write_for_leak_profiler(JfrCheckpointWriter& writer, traceid id, unsigned int hash) { assert(JfrStacktrace_lock->owned_by_self(), "invariant"); - const StackTrace* const trace = resolve_entry(hash, id); + const StackTrace* const trace = lookup_for_leak_profiler(hash, id); assert(trace != NULL, "invariant"); assert(trace->hash() == hash, "invariant"); assert(trace->id() == id, "invariant"); @@ -309,6 +348,15 @@ write_stacktrace(cpw, _id, _reached_root, _nr_of_frames, _frames); } +void JfrStackTraceRepository::clear_leak_profiler() { + clear(leak_profiler_instance()); +} + +size_t JfrStackTraceRepository::clear() { + clear_leak_profiler(); + return clear(instance()); +} + // JfrStackFrame bool JfrStackFrame::equals(const JfrStackFrame& rhs) const { @@ -332,9 +380,9 @@ } // invariant is that the entry to be resolved actually exists in the table -const JfrStackTraceRepository::StackTrace* JfrStackTraceRepository::resolve_entry(unsigned int hash, traceid id) const { +const JfrStackTraceRepository::StackTrace* JfrStackTraceRepository::lookup_for_leak_profiler(unsigned int hash, traceid id) { const size_t index = (hash % TABLE_SIZE); - const StackTrace* trace = _table[index]; + const StackTrace* trace = leak_profiler_instance()._table[index]; while (trace != NULL && trace->id() != id) { trace = trace->next(); } --- old/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp 2021-07-05 14:05:59.965151560 +0000 +++ new/src/share/vm/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp 2021-07-05 14:05:59.905150668 +0000 @@ -96,6 +96,8 @@ friend class JfrRecorder; friend class JfrRecorderService; friend class ObjectSampler; + friend class RecordStackTrace; + friend class StackTraceWrite; friend class WriteObjectSampleStacktrace; class StackTrace : public JfrCHeapObj { @@ -126,15 +128,17 @@ private: static const u4 TABLE_SIZE = 2053; StackTrace* _table[TABLE_SIZE]; - traceid _next_id; + u4 _last_entries; u4 _entries; traceid add_trace(const JfrStackTrace& stacktrace); - static traceid add(const JfrStackTrace* stacktrace, JavaThread* thread); + static traceid add_for_leak_profiler(const JfrStackTrace* stacktrace, JavaThread* thread); traceid record_for(JavaThread* thread, int skip, JfrStackFrame* frames, u4 max_frames); size_t write_impl(JfrChunkWriter& cw, bool clear); - const StackTrace* resolve_entry(unsigned int hash, traceid id) const; + static const StackTrace* lookup_for_leak_profiler(unsigned int hash, traceid id); + static void record_for_leak_profiler(JavaThread* thread, JfrStackTrace* stacktrace, int skip = 0); + static void clear_leak_profiler(); static void write_metadata(JfrCheckpointWriter& cpw); static bool fill_stacktrace_for(JavaThread* thread, JfrStackTrace* stacktrace, int skip); @@ -147,11 +151,14 @@ static JfrStackTraceRepository& instance(); public: + static JfrStackTraceRepository& leak_profiler_instance(); + static traceid add(JfrStackTraceRepository& repo, const JfrStackTrace& stacktrace); static traceid add(const JfrStackTrace& stacktrace); static traceid record(Thread* thread, int skip = 0); - traceid write(JfrCheckpointWriter& cpw, traceid id, unsigned int hash); + static traceid write_for_leak_profiler(JfrCheckpointWriter& cpw, traceid id, unsigned int hash); size_t write(JfrChunkWriter& cw, bool clear); - size_t clear(); + static size_t clear(); + static size_t clear(JfrStackTraceRepository& repo); }; #endif // SHARE_VM_JFR_RECORDER_STACKTRACE_JFRSTACKTRACEREPOSITORY_HPP --- old/src/share/vm/jfr/support/jfrAllocationTracer.cpp 2021-07-05 14:06:00.269156080 +0000 +++ new/src/share/vm/jfr/support/jfrAllocationTracer.cpp 2021-07-05 14:06:00.209155188 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2016, 2021, 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 @@ -28,16 +28,9 @@ #include "jfr/support/jfrThreadLocal.hpp" #include "runtime/thread.hpp" -JfrAllocationTracer::JfrAllocationTracer(HeapWord* obj, size_t alloc_size, Thread* thread) : _tl(NULL) { +JfrAllocationTracer::JfrAllocationTracer(HeapWord* obj, size_t alloc_size, Thread* thread) { if (LeakProfiler::is_running()) { assert(thread->is_Java_thread(), "invariant"); - _tl = thread->jfr_thread_local(); LeakProfiler::sample(obj, alloc_size, (JavaThread*)thread); } } - -JfrAllocationTracer::~JfrAllocationTracer() { - if (_tl != NULL) { - _tl->clear_cached_stack_trace(); - } -} --- old/src/share/vm/jfr/support/jfrAllocationTracer.hpp 2021-07-05 14:06:00.565160481 +0000 +++ new/src/share/vm/jfr/support/jfrAllocationTracer.hpp 2021-07-05 14:06:00.505159589 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2018, 2021, 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 @@ -27,14 +27,9 @@ #include "memory/allocation.hpp" -class JfrThreadLocal; - class JfrAllocationTracer : public StackObj { - private: - JfrThreadLocal* _tl; public: JfrAllocationTracer(HeapWord* obj, size_t alloc_size, Thread* thread); - ~JfrAllocationTracer(); }; #endif // SHARE_VM_JFR_SUPPORT_JFRALLOCATIONTRACER_HPP --- /dev/null 2021-05-05 21:17:04.324000000 +0000 +++ new/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp.orig 2021-07-05 14:06:00.933165953 +0000 @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2017, 2018, 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/jfrEvents.hpp" +#include "jfr/leakprofiler/sampling/objectSample.hpp" +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/leakprofiler/sampling/sampleList.hpp" +#include "jfr/leakprofiler/sampling/samplePriorityQueue.hpp" +#include "jfr/recorder/jfrEventSetting.inline.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" +#include "jfr/support/jfrThreadLocal.hpp" +#include "jfr/utilities/jfrTryLock.hpp" +#include "memory/universe.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" +#include "runtime/orderAccess.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/thread.hpp" + +static ObjectSampler* _instance = NULL; + +static ObjectSampler& instance() { + assert(_instance != NULL, "invariant"); + return *_instance; +} + +ObjectSampler::ObjectSampler(size_t size) : + _priority_queue(new SamplePriorityQueue(size)), + _list(new SampleList(size)), + _last_sweep(JfrTicks::now()), + _total_allocated(0), + _threshold(0), + _size(size), + _dead_samples(false) {} + +ObjectSampler::~ObjectSampler() { + delete _priority_queue; + _priority_queue = NULL; + delete _list; + _list = NULL; +} + +bool ObjectSampler::create(size_t size) { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + assert(_instance == NULL, "invariant"); + _instance = new ObjectSampler(size); + return _instance != NULL; +} + +bool ObjectSampler::is_created() { + return _instance != NULL; +} + +ObjectSampler* ObjectSampler::sampler() { + assert(is_created(), "invariant"); + return _instance; +} + +void ObjectSampler::destroy() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + if (_instance != NULL) { + ObjectSampler* const sampler = _instance; + _instance = NULL; + delete sampler; + } +} + +static volatile int _lock = 0; + +ObjectSampler* ObjectSampler::acquire() { + assert(is_created(), "invariant"); + while (Atomic::cmpxchg(1, &_lock, 0) == 1) {} + return _instance; +} + +void ObjectSampler::release() { + assert(is_created(), "invariant"); + OrderAccess::fence(); + _lock = 0; +} + +static traceid get_thread_id(JavaThread* thread) { + assert(thread != NULL, "invariant"); + if (thread->threadObj() == NULL) { + return 0; + } + const JfrThreadLocal* const tl = thread->jfr_thread_local(); + assert(tl != NULL, "invariant"); + if (!tl->has_thread_checkpoint()) { + JfrCheckpointManager::create_thread_checkpoint(thread); + } + assert(tl->has_thread_checkpoint(), "invariant"); + return tl->thread_id(); +} + +// Populates the thread local stack frames, but does not add them +// to the stacktrace repository (...yet, see stacktrace_id() below) +// +void ObjectSampler::fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* thread) { + assert(stacktrace != NULL, "invariant"); + assert(thread != NULL, "invariant"); + if (JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) { + JfrStackTraceRepository::fill_stacktrace_for(thread, stacktrace, 0); + } +} + +// We were successful in acquiring the try lock and have been selected for adding a sample. +// Go ahead with installing our previously taken stacktrace into the stacktrace repository. + +// TODO: record_for_leak_profiler replaces this function because it's doing +// the JfrStackTraceRepository::add in leak_profile instance and set_cached_stack_trace_id. + +traceid ObjectSampler::stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread) { + assert(stacktrace != NULL, "invariant"); + assert(stacktrace->hash() != 0, "invariant"); + const traceid stacktrace_id = JfrStackTraceRepository::add_for_leak_profiler(stacktrace, thread); + thread->jfr_thread_local()->set_cached_stack_trace_id(stacktrace_id, stacktrace->hash()); + return stacktrace_id; +} + +class RecordStackTrace { + private: + JavaThread* _jt; + JfrStackTrace* _stacktrace; + bool _enabled; + public: + RecordStackTrace(JavaThread* jt, JfrStackTrace* st) : _jt(jt), + _stacktrace(st), + _enabled(JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) { + if (_enabled) { + //tty->print_cr("objectSampler.cpp | ", st->); + JfrStackTraceRepository::record_for_leak_profiler(jt, st); + } + } + ~RecordStackTrace() { + if (_enabled) { + _jt->jfr_thread_local()->clear_cached_stack_trace(); + } + } +}; + +void ObjectSampler::sample(HeapWord* obj, size_t allocated, JavaThread* thread) { + assert(thread != NULL, "invariant"); + assert(is_created(), "invariant"); + + const traceid thread_id = get_thread_id(thread); + if (thread_id == 0) { + return; + } + + const JfrThreadLocal* const tl = thread->jfr_thread_local(); + JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth()); + fill_stacktrace(&stacktrace, thread); + + RecordStackTrace rst(thread, &stacktrace); + + // try enter critical section + JfrTryLock tryLock(&_lock); + if (!tryLock.has_lock()) { + if (LogJFR && Verbose) tty->print_cr("Skipping old object sample due to lock contention"); + return; + } + + instance().add(obj, allocated, thread_id/*, &stacktrace*/, thread); +} + +void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id/*, JfrStackTrace* stacktrace*/, JavaThread* thread) { + //assert(stacktrace != NULL, "invariant"); + assert(thread_id != 0, "invariant"); + assert(thread != NULL, "invariant"); + assert(thread->jfr_thread_local()->has_thread_checkpoint(), "invariant"); + + if (_dead_samples) { + scavenge(); + assert(!_dead_samples, "invariant"); + } + + _total_allocated += allocated; + const size_t span = _total_allocated - _priority_queue->total(); + ObjectSample* sample; + if ((size_t)_priority_queue->count() == _size) { + assert(_list->count() == _size, "invariant"); + const ObjectSample* peek = _priority_queue->peek(); + if (peek->span() > span) { + // quick reject, will not fit + return; + } + sample = _list->reuse(_priority_queue->pop()); + } else { + sample = _list->get(); + } + + assert(sample != NULL, "invariant"); + sample->set_thread_id(thread_id); + sample->set_thread_checkpoint(thread->jfr_thread_local()->thread_checkpoint()); + + const JfrThreadLocal* const tl = thread->jfr_thread_local(); + const unsigned int stacktrace_hash = tl->cached_stack_trace_hash(); + if (stacktrace_hash != 0) { + tty->print_cr("objectSampler.cpp | Set sample stack trace id(%ld)/hash(%d)", tl->cached_stack_trace_id(), stacktrace_hash); + sample->set_stack_trace_id(tl->cached_stack_trace_id()); + sample->set_stack_trace_hash(stacktrace_hash); + } + /* + const unsigned int stacktrace_hash = stacktrace->hash(); + if (stacktrace_hash != 0) { + // Only place where stacktrace_id() is used + // stacktrace_id() + sample->set_stack_trace_id(stacktrace_id(stacktrace, thread)); + sample->set_stack_trace_hash(stacktrace_hash); + }*/ + + sample->set_span(allocated); + sample->set_object((oop)obj); + sample->set_allocated(allocated); + sample->set_allocation_time(JfrTicks::now()); + sample->set_heap_used_at_last_gc(Universe::get_heap_used_at_last_gc()); + _priority_queue->push(sample); +} + +void ObjectSampler::scavenge() { + ObjectSample* current = _list->last(); + while (current != NULL) { + ObjectSample* next = current->next(); + if (current->is_dead()) { + remove_dead(current); + } + current = next; + } + _dead_samples = false; +} + +void ObjectSampler::remove_dead(ObjectSample* sample) { + assert(sample != NULL, "invariant"); + assert(sample->is_dead(), "invariant"); + ObjectSample* const previous = sample->prev(); + // push span on to previous + if (previous != NULL) { + _priority_queue->remove(previous); + previous->add_span(sample->span()); + _priority_queue->push(previous); + } + _priority_queue->remove(sample); + _list->release(sample); +} + +void ObjectSampler::oops_do(BoolObjectClosure* is_alive, OopClosure* f) { + assert(is_created(), "invariant"); + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + ObjectSampler& sampler = instance(); + ObjectSample* current = sampler._list->last(); + while (current != NULL) { + ObjectSample* next = current->next(); + if (!current->is_dead()) { + if (is_alive->do_object_b(current->object())) { + // The weakly referenced object is alive, update pointer + f->do_oop(const_cast(current->object_addr())); + } else { + current->set_dead(); + sampler._dead_samples = true; + } + } + current = next; + } + sampler._last_sweep = JfrTicks::now(); +} + +const ObjectSample* ObjectSampler::last() const { + return _list->last(); +} + +const ObjectSample* ObjectSampler::first() const { + return _list->first(); +} + +const ObjectSample* ObjectSampler::last_resolved() const { + return _list->last_resolved(); +} + +void ObjectSampler::set_last_resolved(const ObjectSample* sample) { + _list->set_last_resolved(sample); +} + +int ObjectSampler::item_count() const { + return _priority_queue->count(); +} + +const ObjectSample* ObjectSampler::item_at(int index) const { + return _priority_queue->item_at(index); +} + +ObjectSample* ObjectSampler::item_at(int index) { + return const_cast( + const_cast(this)->item_at(index) + ); +} + +const JfrTicks& ObjectSampler::last_sweep() const { + return _last_sweep; +} --- /dev/null 2021-05-05 21:17:04.324000000 +0000 +++ new/src/share/vm/jfr/recorder/service/jfrRecorderService.cpp.orig 2021-07-05 14:06:01.213170115 +0000 @@ -0,0 +1,555 @@ +/* + * Copyright (c) 2016, 2018, 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/jni/jfrJavaSupport.hpp" +#include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" +#include "jfr/recorder/checkpoint/jfrMetadataEvent.hpp" +#include "jfr/recorder/repository/jfrChunkRotation.hpp" +#include "jfr/recorder/repository/jfrChunkWriter.hpp" +#include "jfr/recorder/repository/jfrRepository.hpp" +#include "jfr/recorder/service/jfrPostBox.hpp" +#include "jfr/recorder/service/jfrRecorderService.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" +#include "jfr/recorder/storage/jfrStorage.hpp" +#include "jfr/recorder/storage/jfrStorageControl.hpp" +#include "jfr/recorder/stringpool/jfrStringPool.hpp" +#include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "jfr/writers/jfrJavaEventWriter.hpp" +#include "jfr/utilities/jfrTypes.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" +#include "runtime/vm_operations.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"); + void* const current = OrderAccess::load_ptr_acquire(dest); + if (current != NULL) { + if (current != data) { + // already set + return false; + } + assert(current == data, "invariant"); + if (!clear) { + // recursion disallowed + return false; + } + } + return Atomic::cmpxchg_ptr(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; + + 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."; + } + if (LogJFR) tty->print_cr( // For user, should not be "jfr, system" + "%s", error_msg); + } + public: + RotationLock(Thread* thread) : _thread(thread), _acquired(false) { + assert(_thread != NULL, "invariant"); + if (_thread == rotation_thread) { + // recursion not supported + log(true); + return; + } + + // 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 + MutexLockerEx msg_lock(JfrMsg_lock); + JfrMsg_lock->wait(false, rotation_retry_sleep_millis); + } + else { + os::naked_short_sleep(rotation_retry_sleep_millis); + } + } + log(false); + } + + ~RotationLock() { + assert(_thread != NULL, "invariant"); + if (_acquired) { + assert(_thread == rotation_thread, "invariant"); + while (!try_set(_thread, &rotation_thread, true)); + } + } + bool not_acquired() const { return !_acquired; } +}; + +static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) { + const int64_t prev_cp_offset = cw.previous_checkpoint_offset(); + const int64_t prev_cp_relative_offset = 0 == prev_cp_offset ? 0 : prev_cp_offset - cw.current_offset(); + cw.reserve(sizeof(u4)); + cw.write(EVENT_CHECKPOINT); + cw.write(JfrTicks::now()); + cw.write((int64_t)0); + cw.write(prev_cp_relative_offset); // write previous checkpoint offset delta + cw.write(false); // flushpoint + 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; +} + +template +class WriteCheckpointEvent : public StackObj { + private: + JfrChunkWriter& _cw; + u8 _type_id; + ContentFunctor& _content_functor; + public: + WriteCheckpointEvent(JfrChunkWriter& cw, u8 type_id, ContentFunctor& functor) : + _cw(cw), + _type_id(type_id), + _content_functor(functor) { + assert(_cw.is_valid(), "invariant"); + } + 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) { + // nothing to do, rewind writer to start + _cw.seek(current_cp_offset); + return true; + } + 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_previous_checkpoint_offset(current_cp_offset); + return true; + } +}; + +template +class ServiceFunctor { + private: + Instance& _instance; + size_t _processed; + public: + ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {} + bool process() { + _processed = (_instance.*func)(); + return true; + } + size_t processed() const { return _processed; } +}; + +template +class JfrVMOperation : public VM_Operation { + private: + Instance& _instance; + public: + JfrVMOperation(Instance& instance) : _instance(instance) {} + void doit() { (_instance.*func)(); } + VMOp_Type type() const { return VMOp_JFRCheckpoint; } + 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; } +}; + +static bool recording = false; + +static void set_recording_state(bool is_recording) { + OrderAccess::storestore(); + recording = is_recording; +} + +bool JfrRecorderService::is_recording() { + return recording; +} + +JfrRecorderService::JfrRecorderService() : + _checkpoint_manager(JfrCheckpointManager::instance()), + _chunkwriter(JfrRepository::chunkwriter()), + _repository(JfrRepository::instance()), + _storage(JfrStorage::instance()), + _stack_trace_repository(JfrStackTraceRepository::instance()), + _string_pool(JfrStringPool::instance()) {} + +void JfrRecorderService::start() { + RotationLock rl(Thread::current()); + if (rl.not_acquired()) { + return; + } + if (LogJFR) tty->print_cr("Request to START recording"); + assert(!is_recording(), "invariant"); + clear(); + set_recording_state(true); + assert(is_recording(), "invariant"); + open_new_chunk(); + if (LogJFR) tty->print_cr("Recording STARTED"); +} + +void JfrRecorderService::clear() { + ResourceMark rm; + HandleMark hm; + pre_safepoint_clear(); + invoke_safepoint_clear(); + post_safepoint_clear(); +} + +void JfrRecorderService::pre_safepoint_clear() { + //_stack_trace_repository.clear(); + JfrStackTraceRepository::clear(); + _string_pool.clear(); + _storage.clear(); +} + +void JfrRecorderService::invoke_safepoint_clear() { + JfrVMOperation safepoint_task(*this); + 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(); + JfrStackTraceRepository::clear(); + _string_pool.clear(); + _storage.clear(); + _checkpoint_manager.shift_epoch(); + _chunkwriter.time_stamp_chunk_now(); +} + +void JfrRecorderService::post_safepoint_clear() { + _checkpoint_manager.clear(); +} + +static void stop() { + assert(JfrRecorderService::is_recording(), "invariant"); + if (LogJFR) tty->print_cr("Recording STOPPED"); + set_recording_state(false); + assert(!JfrRecorderService::is_recording(), "invariant"); +} + +void JfrRecorderService::rotate(int msgs) { + RotationLock rl(Thread::current()); + if (rl.not_acquired()) { + return; + } + static bool vm_error = false; + if (msgs & MSGBIT(MSG_VM_ERROR)) { + vm_error = true; + prepare_for_vm_error_rotation(); + } + if (msgs & (MSGBIT(MSG_STOP))) { + stop(); + } + // action determined by chunkwriter state + if (!_chunkwriter.is_valid()) { + in_memory_rotation(); + return; + } + if (vm_error) { + vm_error_rotation(); + return; + } + chunk_rotation(); +} + +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(); + MutexLockerEx 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"); + // currently running an in-memory recording + open_new_chunk(); + if (_chunkwriter.is_valid()) { + // dump all in-memory buffer data to the newly created chunk + serialize_storage_from_in_memory_recording(); + } +} + +void JfrRecorderService::serialize_storage_from_in_memory_recording() { + assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!"); + MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); + _storage.write(); +} + +void JfrRecorderService::chunk_rotation() { + finalize_current_chunk(); + open_new_chunk(); +} + +void JfrRecorderService::finalize_current_chunk() { + assert(_chunkwriter.is_valid(), "invariant"); + write(); + assert(!_chunkwriter.is_valid(), "invariant"); +} + +void JfrRecorderService::write() { + ResourceMark rm; + HandleMark hm; + pre_safepoint_write(); + invoke_safepoint_write(); + 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_object_sample_stacktrace(ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repository) { + WriteObjectSampleStacktrace object_sample_stacktrace(sampler, stack_trace_repository); + object_sample_stacktrace.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() { + MutexLockerEx 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. + ObjectSampler* const sampler = ObjectSampler::acquire(); + assert(sampler != NULL, "invariant"); + //write_object_sample_stacktrace(sampler, _stack_trace_repository); + write_object_sample_stacktrace(sampler, JfrStackTraceRepository::leak_profiler_instance()); + } + _storage.write(); +} + +void JfrRecorderService::invoke_safepoint_write() { + JfrVMOperation safepoint_task(*this); + 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"); + MutexLockerEx 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(); + _storage.write_at_safepoint(); + _checkpoint_manager.shift_epoch(); + _chunkwriter.time_stamp_chunk_now(); + JfrStackTraceRepository::clear_leak_profiler(); + 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; +} + +// +// 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. + // Type tagging is epoch relative which entails we are able to write out the + // already tagged artifacts for the previous epoch. We can accomplish this concurrently + // with threads now tagging artifacts in relation to the new, now updated, epoch and remain outside of a safepoint. + _checkpoint_manager.write_type_set(); + if (LeakProfiler::is_running()) { + // The object sampler instance was exclusively acquired and locked in pre_safepoint_write. + // Note: There is a dependency on write_type_set() above, ensure the release is subsequent. + ObjectSampler::release(); + } MutexLockerEx 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"); +} + +void JfrRecorderService::vm_error_rotation() { + if (_chunkwriter.is_valid()) { + finalize_current_chunk_on_vm_error(); + assert(!_chunkwriter.is_valid(), "invariant"); + _repository.on_vm_error(); + } +} + +void JfrRecorderService::finalize_current_chunk_on_vm_error() { + 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"); +} + +void JfrRecorderService::process_full_buffers() { + if (_chunkwriter.is_valid()) { + assert(!JfrStream_lock->owned_by_self(), "invariant"); + MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); + _storage.write_full(); + } +} + +void JfrRecorderService::scavenge() { + _storage.scavenge(); +} + +void JfrRecorderService::evaluate_chunk_size_for_rotation() { + JfrChunkRotation::evaluate(_chunkwriter); +}