Skip to content

Commit

Permalink
8326715: ZGC: RunThese24H fails with ExitCode 139 during shutdown
Browse files Browse the repository at this point in the history
Reviewed-by: egahlin
  • Loading branch information
Markus Grönlund committed Jun 17, 2024
1 parent ef7923e commit cdf22b1
Show file tree
Hide file tree
Showing 11 changed files with 312 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -326,57 +346,49 @@ 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 {
private:
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) {
Expand All @@ -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) {
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
};
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
Expand Down
28 changes: 14 additions & 14 deletions src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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();
}

Expand All @@ -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();
}
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct JfrCheckpointContext {
};

class JfrCheckpointWriter : public JfrCheckpointWriterBase {
friend class JfrAddRefCountedBlob;
friend class JfrCheckpointManager;
friend class JfrDeprecationManager;
friend class JfrSerializerRegistration;
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Loading

1 comment on commit cdf22b1

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.