8326715: ZGC: RunThese24H fails with ExitCode 139 during shutdown

Reviewed-by: egahlin
This commit is contained in:
Markus Grönlund 2024-06-17 12:57:09 +00:00
parent ef7923e127
commit cdf22b1320
11 changed files with 313 additions and 164 deletions

View File

@ -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);
}
}

View File

@ -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);
};

View File

@ -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);

View File

@ -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) {

View File

@ -54,6 +54,7 @@ struct JfrCheckpointContext {
};
class JfrCheckpointWriter : public JfrCheckpointWriterBase {
friend class JfrAddRefCountedBlob;
friend class JfrCheckpointManager;
friend class JfrDeprecationManager;
friend class JfrSerializerRegistration;

View File

@ -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

View File

@ -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 <typename T>
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

View File

@ -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;
}

View File

@ -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);
};

View File

@ -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<JfrDeprecatedEdge> 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);
}

View File

@ -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<mtTracing> {
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<mtTracing> {
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<mtTracing> {
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