8326715: ZGC: RunThese24H fails with ExitCode 139 during shutdown
Reviewed-by: egahlin
This commit is contained in:
parent
ef7923e127
commit
cdf22b1320
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -54,6 +54,7 @@ struct JfrCheckpointContext {
|
||||
};
|
||||
|
||||
class JfrCheckpointWriter : public JfrCheckpointWriterBase {
|
||||
friend class JfrAddRefCountedBlob;
|
||||
friend class JfrCheckpointManager;
|
||||
friend class JfrDeprecationManager;
|
||||
friend class JfrSerializerRegistration;
|
||||
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user