From 48997f54c9c910048f665a9970ec00fdb678c33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Gr=C3=B6nlund?= Date: Mon, 17 Jun 2024 14:22:29 +0000 Subject: [PATCH] 8326715: ZGC: RunThese24H fails with ExitCode 139 during shutdown Reviewed-by: egahlin Backport-of: cdf22b13204456b589349500bef0e9d48af44e83 --- .../checkpoint/objectSampleCheckpoint.cpp | 113 ++++++---------- .../checkpoint/objectSampleCheckpoint.hpp | 5 +- .../leakprofiler/sampling/objectSample.hpp | 4 +- .../checkpoint/jfrCheckpointManager.cpp | 28 ++-- .../checkpoint/jfrCheckpointWriter.hpp | 1 + .../storage/jfrReferenceCountedStorage.cpp | 79 ++++++++++++ .../storage/jfrReferenceCountedStorage.hpp | 68 ++++++++++ .../jfr/support/jfrDeprecationEventWriter.cpp | 31 ++++- .../jfr/support/jfrDeprecationEventWriter.hpp | 13 +- .../jfr/support/jfrDeprecationManager.cpp | 122 ++++++++++-------- .../jfr/support/jfrDeprecationManager.hpp | 13 +- 11 files changed, 313 insertions(+), 164 deletions(-) create mode 100644 src/hotspot/share/jfr/recorder/storage/jfrReferenceCountedStorage.cpp create mode 100644 src/hotspot/share/jfr/recorder/storage/jfrReferenceCountedStorage.hpp diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp index 2412454f6aa..b7f9b733c0f 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp @@ -36,6 +36,7 @@ #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp" #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" +#include "jfr/recorder/storage/jfrReferenceCountedStorage.hpp" #include "jfr/support/jfrKlassUnloading.hpp" #include "jfr/support/jfrMethodLookup.hpp" #include "jfr/utilities/jfrHashtable.hpp" @@ -272,11 +273,30 @@ static void install_stack_traces(const ObjectSampler* sampler) { iterate_samples(installer); } +// Resets the blob write states from the previous epoch. +static void reset_blob_write_state(const ObjectSampler* sampler, JavaThread* jt) { + assert(sampler != nullptr, "invariant"); + const ObjectSample* sample = sampler->last_resolved(); + while (sample != nullptr) { + if (sample->has_stacktrace()) { + sample->stacktrace()->reset_write_state(); + } + if (sample->has_thread()) { + sample->thread()->reset_write_state(); + } + if (sample->has_type_set()) { + sample->type_set()->reset_write_state(); + } + sample = sample->next(); + } +} + void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler) { assert(sampler != nullptr, "invariant"); assert(LeakProfiler::is_running(), "invariant"); JavaThread* const thread = JavaThread::current(); DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(thread);) + reset_blob_write_state(sampler, thread); if (!ObjectSampler::has_unresolved_entry()) { return; } @@ -326,38 +346,34 @@ void ObjectSampleCheckpoint::write_stacktrace(const JfrStackTrace* trace, JfrChe } } -static void write_blob(const JfrBlobHandle& blob, JfrCheckpointWriter& writer, bool reset) { - if (reset) { - blob->reset_write_state(); - return; - } +static void write_blob(const JfrBlobHandle& blob, JfrCheckpointWriter& writer) { blob->exclusive_write(writer); } -static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) { +static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { if (sample->has_type_set()) { - write_blob(sample->type_set(), writer, reset); + write_blob(sample->type_set(), writer); } } -static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) { +static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { assert(sample->has_thread(), "invariant"); if (sample->is_virtual_thread() || has_thread_exited(sample->thread_id())) { - write_blob(sample->thread(), writer, reset); + write_blob(sample->thread(), writer); } } -static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) { +static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { if (sample->has_stacktrace()) { - write_blob(sample->stacktrace(), writer, reset); + write_blob(sample->stacktrace(), writer); } } -static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) { +static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer) { assert(sample != nullptr, "invariant"); - write_stacktrace_blob(sample, writer, reset); - write_thread_blob(sample, writer, reset); - write_type_set_blob(sample, writer, reset); + write_stacktrace_blob(sample, writer); + write_thread_blob(sample, writer); + write_type_set_blob(sample, writer); } class BlobWriter { @@ -365,18 +381,14 @@ class BlobWriter { const ObjectSampler* _sampler; JfrCheckpointWriter& _writer; const jlong _last_sweep; - bool _reset; public: BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) : - _sampler(sampler), _writer(writer), _last_sweep(last_sweep), _reset(false) {} + _sampler(sampler), _writer(writer), _last_sweep(last_sweep) {} void sample_do(ObjectSample* sample) { if (sample->is_alive_and_older_than(_last_sweep)) { - write_blobs(sample, _writer, _reset); + write_blobs(sample, _writer); } } - void set_reset() { - _reset = true; - } }; static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) { @@ -385,9 +397,6 @@ static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thre JfrCheckpointWriter writer(thread, false); BlobWriter cbw(sampler, writer, last_sweep); iterate_samples(cbw, true); - // reset blob write states - cbw.set_reset(); - iterate_samples(cbw, true); } void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) { @@ -403,67 +412,17 @@ void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge } } -// A linked list of saved type set blobs for the epoch. -// The link consist of a reference counted handle. -static JfrBlobHandle saved_type_set_blobs; - -static void release_state_for_previous_epoch() { - // decrements the reference count and the list is reinitialized - saved_type_set_blobs = JfrBlobHandle(); -} - -class BlobInstaller { - public: - ~BlobInstaller() { - release_state_for_previous_epoch(); - } - void sample_do(ObjectSample* sample) { - if (!sample->is_dead()) { - sample->set_type_set(saved_type_set_blobs); - } - } -}; - -static void install_type_set_blobs() { - if (saved_type_set_blobs.valid()) { - BlobInstaller installer; - iterate_samples(installer); - } -} - -static void save_type_set_blob(JfrCheckpointWriter& writer) { - assert(writer.has_data(), "invariant"); - const JfrBlobHandle blob = writer.copy(); - if (saved_type_set_blobs.valid()) { - saved_type_set_blobs->set_next(blob); - } else { - saved_type_set_blobs = blob; - } -} - // This routine has exclusive access to the sampler instance on entry. -void ObjectSampleCheckpoint::on_type_set(JfrCheckpointWriter& writer) { +void ObjectSampleCheckpoint::on_type_set(JavaThread* jt) { assert(LeakProfiler::is_running(), "invariant"); DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(JavaThread::current());) assert(ClassLoaderDataGraph_lock->owned_by_self(), "invariant"); if (!ObjectSampler::has_unresolved_entry()) { return; } - const ObjectSample* const last = ObjectSampler::sampler()->last(); + ObjectSample* const last = ObjectSampler::sampler()->last(); assert(last != nullptr, "invariant"); assert(last != ObjectSampler::sampler()->last_resolved(), "invariant"); - if (writer.has_data()) { - save_type_set_blob(writer); - } - install_type_set_blobs(); + JfrReferenceCountedStorage::install(last, ObjectSampler::sampler()->last_resolved()); ObjectSampler::sampler()->set_last_resolved(last); } - -// This routine does NOT have exclusive access to the sampler instance on entry. -void ObjectSampleCheckpoint::on_type_set_unload(JfrCheckpointWriter& writer) { - assert(LeakProfiler::is_running(), "invariant"); - assert_locked_or_safepoint(ClassLoaderDataGraph_lock); - if (writer.has_data() && ObjectSampler::has_unresolved_entry()) { - save_type_set_blob(writer); - } -} diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp index eb887f2db9a..bf1965b13b5 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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 @@ -50,8 +50,7 @@ class ObjectSampleCheckpoint : AllStatic { static void write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread); static void clear(); public: - static void on_type_set(JfrCheckpointWriter& writer); - static void on_type_set_unload(JfrCheckpointWriter& writer); + static void on_type_set(JavaThread* jt); static void on_thread_exit(traceid tid); static void on_rotation(const ObjectSampler* sampler); }; diff --git a/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp b/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp index c202ba8d8aa..214de827d03 100644 --- a/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp +++ b/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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 @@ -233,7 +233,7 @@ class ObjectSample : public JfrCHeapObj { return _type_set.valid(); } - void set_type_set(const JfrBlobHandle& ref) { + void install_type_set(const JfrBlobHandle& ref) { if (_type_set != ref) { if (_type_set.valid()) { _type_set->set_next(ref); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp index 0aed916702e..a2e887e71a3 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, 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 @@ -37,6 +37,7 @@ #include "jfr/recorder/service/jfrOptionSet.hpp" #include "jfr/recorder/storage/jfrEpochStorage.inline.hpp" #include "jfr/recorder/storage/jfrMemorySpace.inline.hpp" +#include "jfr/recorder/storage/jfrReferenceCountedStorage.hpp" #include "jfr/recorder/storage/jfrStorageUtils.inline.hpp" #include "jfr/recorder/stringpool/jfrStringPool.hpp" #include "jfr/support/jfrDeprecationManager.hpp" @@ -589,12 +590,14 @@ void JfrCheckpointManager::clear_type_set() { MutexLocker module_lock(Module_lock); JfrTypeSet::clear(&writer, &leakp_writer); } - JfrDeprecationManager::on_type_set(leakp_writer, nullptr, thread); - // We placed a blob in the Deprecated subsystem by moving the information - // from the leakp writer. For the real writer, the data will not be - // committed, because the JFR system is yet to be started. - // Therefore, the writer is cancelled before its destructor is run, - // to avoid writing unnecessary information into the checkpoint system. + JfrAddRefCountedBlob add_blob(leakp_writer); + JfrDeprecationManager::on_type_set(nullptr, thread); + // We installed a blob in the JfrReferenceCountedStorage subsystem + // by moving the information from the leakp writer. + // For the real writer, the data will not be committed, + // because the JFR system is yet to be started. + // Therefore, we cancel the writer before its destructor is run + // to avoid writing invalid information into the checkpoint system. writer.cancel(); } @@ -613,11 +616,11 @@ void JfrCheckpointManager::write_type_set() { MutexLocker module_lock(thread, Module_lock); JfrTypeSet::serialize(&writer, &leakp_writer, false, false); } + JfrAddRefCountedBlob add_blob(leakp_writer); if (LeakProfiler::is_running()) { - ObjectSampleCheckpoint::on_type_set(leakp_writer); + ObjectSampleCheckpoint::on_type_set(thread); } - // Place this call after ObjectSampleCheckpoint::on_type_set. - JfrDeprecationManager::on_type_set(leakp_writer, _chunkwriter, thread); + JfrDeprecationManager::on_type_set(_chunkwriter, thread); } write(); } @@ -626,10 +629,7 @@ void JfrCheckpointManager::on_unloading_classes() { assert_locked_or_safepoint(ClassLoaderDataGraph_lock); JfrCheckpointWriter writer(Thread::current()); JfrTypeSet::on_unloading_classes(&writer); - if (LeakProfiler::is_running()) { - ObjectSampleCheckpoint::on_type_set_unload(writer); - } - JfrDeprecationManager::on_type_set_unload(writer); + JfrAddRefCountedBlob add_blob(writer, false /* move */, false /* reset */); } static size_t flush_type_set(Thread* thread) { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp index a8ec2fd70f5..aaebc8f3e7e 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp @@ -54,6 +54,7 @@ struct JfrCheckpointContext { }; class JfrCheckpointWriter : public JfrCheckpointWriterBase { + friend class JfrAddRefCountedBlob; friend class JfrCheckpointManager; friend class JfrDeprecationManager; friend class JfrSerializerRegistration; diff --git a/src/hotspot/share/jfr/recorder/storage/jfrReferenceCountedStorage.cpp b/src/hotspot/share/jfr/recorder/storage/jfrReferenceCountedStorage.cpp new file mode 100644 index 00000000000..ac652905c5b --- /dev/null +++ b/src/hotspot/share/jfr/recorder/storage/jfrReferenceCountedStorage.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, 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/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/recorder/storage/jfrReferenceCountedStorage.hpp" +#include "jfr/support/jfrDeprecationManager.hpp" + +// Currently only two subsystems use type set blobs. Save a blob only if either has an unresolved entry. +static inline bool save_blob_predicate() { + return JfrDeprecationManager::has_unresolved_entry() || ObjectSampler::has_unresolved_entry(); +} + +JfrAddRefCountedBlob::JfrAddRefCountedBlob(JfrCheckpointWriter& writer, bool move /* true */, bool reset /* true */) : _reset(reset) { + if (writer.has_data()) { + if (save_blob_predicate()) { + JfrReferenceCountedStorage::save_blob(writer, move); + } else if (move) { + writer.cancel(); + } + } + DEBUG_ONLY(if (reset) JfrReferenceCountedStorage::set_scope();) +} + +JfrAddRefCountedBlob::~JfrAddRefCountedBlob() { + if (_reset) { + JfrReferenceCountedStorage::reset(); + } +} + +JfrBlobHandle JfrReferenceCountedStorage::_type_sets = JfrBlobHandle(); +DEBUG_ONLY(bool JfrReferenceCountedStorage::_scope = false;) + +void JfrReferenceCountedStorage::save_blob(JfrCheckpointWriter& writer, bool move /* false */) { + assert(writer.has_data(), "invariant"); + const JfrBlobHandle blob = move ? writer.move() : writer.copy(); + if (_type_sets.valid()) { + _type_sets->set_next(blob); + return; + } + _type_sets = blob; +} + +void JfrReferenceCountedStorage::reset() { + assert(_scope, "invariant"); + if (_type_sets.valid()) { + _type_sets = JfrBlobHandle(); + } + DEBUG_ONLY(_scope = false;) +} + +#ifdef ASSERT +void JfrReferenceCountedStorage::set_scope() { + assert(!_scope, "invariant"); + _scope = true; +} +#endif diff --git a/src/hotspot/share/jfr/recorder/storage/jfrReferenceCountedStorage.hpp b/src/hotspot/share/jfr/recorder/storage/jfrReferenceCountedStorage.hpp new file mode 100644 index 00000000000..6d32e1b0ead --- /dev/null +++ b/src/hotspot/share/jfr/recorder/storage/jfrReferenceCountedStorage.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_JFR_RECORDER_STORAGE_JFRREFERENCECOUNTEDSTORAGE_HPP +#define SHARE_JFR_RECORDER_STORAGE_JFRREFERENCECOUNTEDSTORAGE_HPP + +#include "jfr/utilities/jfrBlob.hpp" +#include "memory/allocation.hpp" +#include "utilities/macros.hpp" + +class JfrCheckpointWriter; + +// RAII helper class for adding blobs to the storage. +class JfrAddRefCountedBlob : public StackObj { + private: + bool _reset; + public: + JfrAddRefCountedBlob(JfrCheckpointWriter& writer, bool move = true, bool reset = true); + ~JfrAddRefCountedBlob(); +}; + +// The debug aid 'scope' implies the proper RAII save construct is placed on stack. +// This is a necessary condition for installing reference counted storage to nodes. +class JfrReferenceCountedStorage : AllStatic { + friend class JfrAddRefCountedBlob; + private: + static JfrBlobHandle _type_sets; // linked-list of blob handles saved during epoch. + DEBUG_ONLY(static bool _scope;) + + static void save_blob(JfrCheckpointWriter& writer, bool move = false); + static void reset(); + DEBUG_ONLY(static void set_scope();) + + public: + template + static void install(T* node, const T* end) { + assert(_scope, "invariant"); + if (_type_sets.valid()) { + while (node != end) { + node->install_type_set(_type_sets); + node = node->next(); + } + } + } +}; + +#endif // SHARE_JFR_RECORDER_STORAGE_JFRREFERENCECOUNTEDSTORAGE_HPP diff --git a/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp b/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp index 24cdbdc1611..4aa61bdddff 100644 --- a/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp +++ b/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2023, 2024, 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 @@ -116,8 +116,8 @@ bool JfrDeprecatedStackTraceWriter::process(const JfrDeprecatedEdge* edge) { return true; } -JfrDeprecatedEventWriter::JfrDeprecatedEventWriter(JfrChunkWriter& cw, bool stacktrace) : - _now(JfrTicks::now()),_cw(cw), _for_removal(only_for_removal()), _stacktrace(stacktrace), _did_write(false) {} +JfrDeprecatedEventWriter::JfrDeprecatedEventWriter(JfrChunkWriter& cw, JfrCheckpointWriter& tsw, bool stacktrace) : + _now(JfrTicks::now()),_cw(cw), _tsw(tsw), _for_removal(only_for_removal()), _stacktrace(stacktrace) {} static size_t calculate_event_size(const JfrDeprecatedEdge* edge, JfrChunkWriter& cw, const JfrTicks& now, bool stacktrace) { assert(edge != nullptr, "invariant"); @@ -141,14 +141,31 @@ static void write_event(const JfrDeprecatedEdge* edge, JfrChunkWriter& cw, const cw.write(edge->for_removal()); } +static void write_type_set(const JfrDeprecatedEdge* edge, JfrCheckpointWriter& tsw) { + if (!edge->has_type_set()) { + return; + } + edge->type_set()->exclusive_write(tsw); +} + bool JfrDeprecatedEventWriter::process(const JfrDeprecatedEdge* edge) { assert(edge != nullptr, "invariant"); if (_for_removal && !edge->for_removal()) { return true; } - write_event(edge, _cw,_now, _stacktrace); - if (!_did_write) { - _did_write = true; - } + write_event(edge, _cw, _now, _stacktrace); + write_type_set(edge, _tsw); return true; } + +JfrDeprecatedEventClear::JfrDeprecatedEventClear() {} + +bool JfrDeprecatedEventClear::process(const JfrDeprecatedEdge* edge) { + assert(edge != nullptr, "invariant"); + if (!edge->has_type_set()) { + return true; + } + edge->type_set()->reset_write_state(); + return true; +} + diff --git a/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.hpp b/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.hpp index 77699c94979..1d73d4dc3a3 100644 --- a/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.hpp +++ b/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2023, 2024, 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 @@ -56,12 +56,17 @@ class JfrDeprecatedEventWriter : public StackObj { private: JfrTicks _now; JfrChunkWriter& _cw; + JfrCheckpointWriter& _tsw; bool _for_removal; bool _stacktrace; - bool _did_write; public: - JfrDeprecatedEventWriter(JfrChunkWriter& cw, bool stacktrace); - bool did_write() const { return _did_write; } + JfrDeprecatedEventWriter(JfrChunkWriter& cw, JfrCheckpointWriter& tsw, bool stacktrace); + bool process(const JfrDeprecatedEdge* edge); +}; + +class JfrDeprecatedEventClear : public StackObj { + public: + JfrDeprecatedEventClear(); bool process(const JfrDeprecatedEdge* edge); }; diff --git a/src/hotspot/share/jfr/support/jfrDeprecationManager.cpp b/src/hotspot/share/jfr/support/jfrDeprecationManager.cpp index 5f5c87d239c..8969b787a1c 100644 --- a/src/hotspot/share/jfr/support/jfrDeprecationManager.cpp +++ b/src/hotspot/share/jfr/support/jfrDeprecationManager.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2023, 2024, 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 @@ -32,6 +32,7 @@ #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" #include "jfr/recorder/repository/jfrChunkWriter.hpp" #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" +#include "jfr/recorder/storage/jfrReferenceCountedStorage.hpp" #include "jfr/support/jfrDeprecationEventWriter.hpp" #include "jfr/support/jfrDeprecationManager.hpp" #include "jfr/support/jfrKlassUnloading.hpp" @@ -66,6 +67,7 @@ static inline traceid load_traceid(const Method* method) { JfrDeprecatedEdge::JfrDeprecatedEdge(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* jt) : _invocation_time(JfrTicks::now()), _stacktrace(), + _type_set(), _next(nullptr), _deprecated_ik(method->method_holder()), _deprecated_methodid(load_traceid(method)), @@ -94,11 +96,25 @@ const JfrBlobHandle& JfrDeprecatedEdge::stacktrace() const { return _stacktrace; } +bool JfrDeprecatedEdge::has_type_set() const { + return _type_set.valid(); +} + +const JfrBlobHandle& JfrDeprecatedEdge::type_set() const { + assert(has_type_set(), "invariant"); + return _type_set; +} + +void JfrDeprecatedEdge::install_type_set(const JfrBlobHandle& type_set) { + assert(!has_type_set(), "invariant"); + _type_set = type_set; +} + typedef JfrLinkedList DeprecatedEdgeList; static DeprecatedEdgeList _list; // Newly constructed edges are concurrently added to this list. static DeprecatedEdgeList _pending_list; // During epoch rotation (safepoint) entries in _list are moved onto _pending_list -static DeprecatedEdgeList _resolved_list; // Fully resolved edges (event and stacktrace blobs). +static DeprecatedEdgeList _resolved_list; // Fully resolved edges (event, stacktrace and typeset blobs). static JfrDeprecatedEdge* allocate_edge(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* jt) { DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt);) @@ -225,10 +241,6 @@ static void transfer_list() { } } -void JfrDeprecationManager::on_level_setting_update(int64_t new_level) { - JfrDeprecatedEventWriterState::on_level_setting_update(new_level); -} - void JfrDeprecationManager::on_safepoint_clear() { assert(!_enqueue_klasses, "invariant"); // We are now starting JFR, so begin enqueuing tagged klasses. @@ -270,6 +282,23 @@ static void add_to_leakp_set(const JfrDeprecatedEdge* edge) { static DeprecatedEdgeList::NodePtr _pending_head = nullptr; static DeprecatedEdgeList::NodePtr _pending_tail = nullptr; +inline DeprecatedEdgeList::NodePtr pending_head() { + return Atomic::load(&_pending_head); +} + +// The test for a pending head can be read concurrently from a thread doing class unloading. +inline static bool has_pending_head() { + return pending_head() != nullptr; +} + +inline static bool no_pending_head() { + return !has_pending_head(); +} + +inline static void set_pending_head(DeprecatedEdgeList::NodePtr head) { + Atomic::store(&_pending_head, head); +} + class PendingListProcessor { private: JfrCheckpointWriter& _writer; @@ -281,66 +310,57 @@ class PendingListProcessor { JfrDeprecatedStackTraceWriter::install_stacktrace_blob(edge, _writer, _jt); assert(edge->has_stacktrace(), "invariant"); add_to_leakp_set(edge); - if (_pending_head == nullptr) { - _pending_head = edge; + if (no_pending_head()) { + set_pending_head(edge); } _pending_tail = edge; return true; } }; -void JfrDeprecationManager::prepare_type_set(JavaThread* jt) { - _pending_head = nullptr; +// Resets the pending head and tail. +// Resets blob write states for nodes on the resolved list, dirtied in the previous epoch. +static void reset_type_set_blobs() { + set_pending_head(nullptr); _pending_tail = nullptr; + if (_resolved_list.is_nonempty()) { + JfrDeprecatedEventClear clear; + _resolved_list.iterate(clear); + } +} + +void JfrDeprecationManager::prepare_type_set(JavaThread* jt) { + reset_type_set_blobs(); if (_pending_list.is_nonempty()) { JfrKlassUnloading::sort(true); JfrCheckpointWriter writer(true /* prev epoch */, jt, false /* header */); PendingListProcessor plp(writer, jt); _pending_list.iterate(plp); - assert(_pending_head != nullptr, "invariant"); + assert(has_pending_head(), "invariant"); assert(_pending_tail != nullptr, "invariant"); assert(_pending_tail->next() == nullptr, "invariant"); // Excise already resolved edges to link them. _pending_tail->set_next(_resolved_list.cut()); // Re-insertion. - _resolved_list.add_list(_pending_head); + _resolved_list.add_list(pending_head()); _pending_list.clear(); } assert(_pending_list.is_empty(), "invariant"); } -// A linked-list of blob handles. -static JfrBlobHandle type_set_blobs; - -static inline void write_type_set_blobs(JfrCheckpointWriter& writer) { - type_set_blobs->write(writer); -} - -static void save_type_set_blob(JfrCheckpointWriter& writer, bool copy = false) { - assert(writer.has_data(), "invariant"); - const JfrBlobHandle blob = copy ? writer.copy() : writer.move(); - if (type_set_blobs.valid()) { - type_set_blobs->set_next(blob); - } else { - type_set_blobs = blob; - } -} - -void JfrDeprecationManager::on_type_set_unload(JfrCheckpointWriter& writer) { - if (writer.has_data()) { - save_type_set_blob(writer, true); - } +bool JfrDeprecationManager::has_unresolved_entry() { + return _list.is_nonempty() || has_pending_head() || _pending_list.is_nonempty(); } static inline bool has_stacktrace() { return JfrEventSetting::has_stacktrace(JfrDeprecatedInvocationEvent); } -static inline bool write_events(JfrChunkWriter& cw) { +static inline void write_events(JfrChunkWriter& cw, Thread* thread, bool on_error) { assert(_resolved_list.is_nonempty(), "invariant"); - JfrDeprecatedEventWriter ebw(cw, has_stacktrace()); + JfrCheckpointWriter type_set_writer(!on_error, thread, false); + JfrDeprecatedEventWriter ebw(cw, type_set_writer, has_stacktrace()); _resolved_list.iterate(ebw); - return ebw.did_write(); } static inline void write_stacktraces(JfrChunkWriter& cw) { @@ -349,34 +369,30 @@ static inline void write_stacktraces(JfrChunkWriter& cw) { _resolved_list.iterate(scw); } -static inline void write_type_sets(Thread* thread, bool on_error) { - JfrCheckpointWriter writer(!on_error, thread, false); - write_type_set_blobs(writer); -} - -// First, we consolidate all stacktrace blobs into a single TYPE_STACKTRACE checkpoint and serialize it to the chunk. -// Secondly, we serialize all events to the chunk. -// Thirdly, the type set blobs are written into the JfrCheckpoint system, to be serialized to the chunk -// just after we return from here. +// First, we consolidate all stack trace blobs into a single TYPE_STACKTRACE checkpoint +// and serialize it to the chunk. Then, all events are serialized, and unique type set blobs +// written into the JfrCheckpoint system to be serialized to the chunk upon return. void JfrDeprecationManager::write_edges(JfrChunkWriter& cw, Thread* thread, bool on_error /* false */) { if (_resolved_list.is_nonempty() && JfrEventSetting::is_enabled(JfrDeprecatedInvocationEvent)) { if (has_stacktrace()) { write_stacktraces(cw); } - if (write_events(cw)) { - write_type_sets(thread, on_error); - } + write_events(cw, thread, on_error); } } -void JfrDeprecationManager::on_type_set(JfrCheckpointWriter& writer, JfrChunkWriter* cw, Thread* thread) { +void JfrDeprecationManager::on_type_set(JfrChunkWriter* cw, Thread* thread) { assert(_pending_list.is_empty(), "invariant"); - if (_pending_head != nullptr) { - save_type_set_blob(writer); - } else { - writer.cancel(); + if (has_pending_head()) { + assert(_pending_tail != nullptr, "invariant"); + // Install type set blobs for the pending, i.e. unresolved nodes. + JfrReferenceCountedStorage::install(pending_head(), _pending_tail->next()); } if (cw != nullptr) { write_edges(*cw, thread); } } + +void JfrDeprecationManager::on_level_setting_update(int64_t new_level) { + JfrDeprecatedEventWriterState::on_level_setting_update(new_level); +} diff --git a/src/hotspot/share/jfr/support/jfrDeprecationManager.hpp b/src/hotspot/share/jfr/support/jfrDeprecationManager.hpp index a1948357491..d156679521a 100644 --- a/src/hotspot/share/jfr/support/jfrDeprecationManager.hpp +++ b/src/hotspot/share/jfr/support/jfrDeprecationManager.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2023, 2024, 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 @@ -42,6 +42,7 @@ class JfrDeprecatedEdge : public CHeapObj { private: JfrTicks _invocation_time; JfrBlobHandle _stacktrace; + JfrBlobHandle _type_set; JfrDeprecatedEdge* _next; InstanceKlass* _deprecated_ik; traceid _deprecated_methodid; @@ -58,7 +59,7 @@ class JfrDeprecatedEdge : public CHeapObj { public: JfrDeprecatedEdge(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* jt); - const JfrDeprecatedEdge* next() const { return _next; } + JfrDeprecatedEdge* next() const { return _next; } void set_next(JfrDeprecatedEdge* edge) { _next = edge; } bool has_event() const; @@ -68,6 +69,10 @@ class JfrDeprecatedEdge : public CHeapObj { const JfrBlobHandle& stacktrace() const; void install_stacktrace_blob(JavaThread* jt); + bool has_type_set() const; + const JfrBlobHandle& type_set() const; + void install_type_set(const JfrBlobHandle& type_set); + const InstanceKlass* deprecated_ik() const { return _deprecated_ik; } traceid deprecated_methodid() const { return _deprecated_methodid; } @@ -89,11 +94,11 @@ class JfrDeprecationManager : AllStatic { static void on_safepoint_write(); static void on_recorder_stop(); static void prepare_type_set(JavaThread* jt); - static void on_type_set(JfrCheckpointWriter& writer, JfrChunkWriter* cw, Thread* thread); - static void on_type_set_unload(JfrCheckpointWriter& writer); + static void on_type_set(JfrChunkWriter* cw, Thread* thread); static void write_edges(JfrChunkWriter& cw, Thread* thread, bool on_error = false); static void on_link(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* thread); static void on_level_setting_update(int64_t new_level); + static bool has_unresolved_entry(); }; #endif // SHARE_JFR_SUPPORT_JFRDEPRECATIONMANAGER_HPP