8356587: Missing object ID X in pool jdk.types.Method

Reviewed-by: egahlin
Backport-of: a34994476e8f4783c9f5a83a9c3db63ad605b323
This commit is contained in:
Markus Grönlund 2025-07-29 11:40:55 +00:00
parent 3db8262445
commit 9fe2aa59ff
24 changed files with 203 additions and 175 deletions

View File

@ -132,14 +132,14 @@ InstanceKlass* JfrClassTransformer::create_new_instance_klass(InstanceKlass* ik,
}
// Redefining / retransforming?
const Klass* JfrClassTransformer::find_existing_klass(const InstanceKlass* ik, JavaThread* thread) {
const InstanceKlass* JfrClassTransformer::find_existing_klass(const InstanceKlass* ik, JavaThread* thread) {
assert(ik != nullptr, "invariant");
assert(thread != nullptr, "invariant");
JvmtiThreadState* const state = thread->jvmti_thread_state();
return state != nullptr ? klass_being_redefined(ik, state) : nullptr;
}
const Klass* JfrClassTransformer::klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state) {
const InstanceKlass* JfrClassTransformer::klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state) {
assert(ik != nullptr, "invariant");
assert(state != nullptr, "invariant");
const GrowableArray<Klass*>* const redef_klasses = state->get_classes_being_redefined();
@ -149,9 +149,10 @@ const Klass* JfrClassTransformer::klass_being_redefined(const InstanceKlass* ik,
for (int i = 0; i < redef_klasses->length(); ++i) {
const Klass* const existing_klass = redef_klasses->at(i);
assert(existing_klass != nullptr, "invariant");
assert(existing_klass->is_instance_klass(), "invariant");
if (ik->name() == existing_klass->name() && ik->class_loader_data() == existing_klass->class_loader_data()) {
// 'ik' is a scratch klass. Return the klass being redefined.
return existing_klass;
return InstanceKlass::cast(existing_klass);
}
}
return nullptr;

View File

@ -38,10 +38,10 @@ class InstanceKlass;
class JfrClassTransformer : AllStatic {
private:
static InstanceKlass* create_new_instance_klass(InstanceKlass* ik, ClassFileStream* stream, TRAPS);
static const Klass* klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state);
static const InstanceKlass* klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state);
public:
static const Klass* find_existing_klass(const InstanceKlass* ik, JavaThread* thread);
static const InstanceKlass* find_existing_klass(const InstanceKlass* ik, JavaThread* thread);
static InstanceKlass* create_instance_klass(InstanceKlass*& ik, ClassFileStream* stream, bool is_initial_load, JavaThread* thread);
static void copy_traceid(const InstanceKlass* ik, const InstanceKlass* new_ik);
static void transfer_cached_class_file_data(InstanceKlass* ik, InstanceKlass* new_ik, const ClassFileParser& parser, JavaThread* thread);

View File

@ -36,6 +36,7 @@
#include "jfr/support/jfrResolution.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
#include "jfr/support/methodtracer/jfrMethodTracer.hpp"
#include "jfr/support/methodtracer/jfrTraceTagging.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/instanceKlass.inline.hpp"
#include "oops/klass.hpp"
@ -88,12 +89,10 @@ void Jfr::on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS)
}
}
void Jfr::on_klass_redefinition(const InstanceKlass* ik, Thread* thread) {
assert(JfrMethodTracer::in_use(), "invariant");
JfrMethodTracer::on_klass_redefinition(ik, thread);
void Jfr::on_klass_redefinition(const InstanceKlass* ik, const InstanceKlass* scratch_klass) {
JfrTraceTagging::on_klass_redefinition(ik, scratch_klass);
}
bool Jfr::is_excluded(Thread* t) {
return JfrJavaSupport::is_excluded(t);
}

View File

@ -61,7 +61,7 @@ class Jfr : AllStatic {
static void include_thread(Thread* thread);
static void exclude_thread(Thread* thread);
static void on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS);
static void on_klass_redefinition(const InstanceKlass* ik, Thread* thread);
static void on_klass_redefinition(const InstanceKlass* ik, const InstanceKlass* scratch_klass);
static void on_thread_start(Thread* thread);
static void on_thread_exit(Thread* thread);
static void on_resolution(const CallInfo& info, TRAPS);

View File

@ -533,8 +533,9 @@ static void clear_method_tracer_klasses() {
static void do_unloading_klass(Klass* klass) {
assert(klass != nullptr, "invariant");
assert(_subsystem_callback != nullptr, "invariant");
if (klass->is_instance_klass() && InstanceKlass::cast(klass)->is_scratch_class()) {
return;
if (!used(klass) && klass->is_instance_klass() && InstanceKlass::cast(klass)->is_scratch_class()) {
SET_TRANSIENT(klass);
assert(used(klass), "invariant");
}
if (JfrKlassUnloading::on_unload(klass)) {
if (JfrTraceId::has_sticky_bit(klass)) {

View File

@ -152,7 +152,7 @@ public:
if (!klass->is_instance_klass()) {
return false;
}
return _current_epoch ? METHOD_USED_THIS_EPOCH(klass) : METHOD_USED_PREVIOUS_EPOCH(klass);
return _current_epoch ? USED_THIS_EPOCH(klass) : USED_PREVIOUS_EPOCH(klass);
}
};

View File

@ -30,6 +30,7 @@
#include "memory/allStatic.hpp"
class ClassLoaderData;
class InstanceKlass;
class Klass;
class Method;
class ModuleEntry;
@ -86,7 +87,6 @@ class JfrTraceId : public AllStatic {
// through load barrier
static traceid load(const Klass* klass);
static traceid load_previous_epoch(const Klass* klass);
static traceid load(jclass jc, bool raw = false);
static traceid load(const Method* method);
static traceid load(const Klass* klass, const Method* method);
@ -146,10 +146,8 @@ class JfrTraceId : public AllStatic {
static void set_sticky_bit(const Method* method);
static void clear_sticky_bit(const Klass* k);
static void clear_sticky_bit(const Method* method);
static bool has_timing_bit(const Klass* k);
static void set_timing_bit(const Klass* k);
static void clear_timing_bit(const Klass* k);
static bool has_timing_bit(const InstanceKlass* scratch_klass);
static void set_timing_bit(const InstanceKlass* scratch_klass);
};
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEID_HPP

View File

@ -32,6 +32,7 @@
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp"
#include "jfr/support/jfrKlassExtension.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/klass.hpp"
#include "runtime/javaThread.inline.hpp"
#include "runtime/mutexLocker.hpp"
@ -81,10 +82,6 @@ inline traceid JfrTraceId::load_leakp_previous_epoch(const Klass* klass, const M
return JfrTraceIdLoadBarrier::load_leakp_previous_epoch(klass, method);
}
inline traceid JfrTraceId::load_previous_epoch(const Klass* klass) {
return JfrTraceIdLoadBarrier::load_previous_epoch(klass);
}
template <typename T>
inline traceid raw_load(const T* t) {
assert(t != nullptr, "invariant");
@ -198,6 +195,7 @@ inline void JfrTraceId::set_sticky_bit(const Method* method) {
assert(method != nullptr, "invariant");
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
assert(!has_sticky_bit(method), "invariant");
assert(!method->is_old(), "invariant");
SET_METHOD_STICKY_BIT(method);
assert(has_sticky_bit(method), "invariant");
}
@ -205,30 +203,22 @@ inline void JfrTraceId::set_sticky_bit(const Method* method) {
inline void JfrTraceId::clear_sticky_bit(const Method* method) {
assert(method != nullptr, "invarriant");
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
assert(!method->is_old(), "invariant");
assert(JfrTraceId::has_sticky_bit(method), "invariant");
CLEAR_STICKY_BIT_METHOD(method);
assert(!JfrTraceId::has_sticky_bit(method), "invariant");
}
inline bool JfrTraceId::has_timing_bit(const Klass* k) {
assert(k != nullptr, "invariant");
return HAS_TIMING_BIT(k);
inline bool JfrTraceId::has_timing_bit(const InstanceKlass* scratch_klass) {
assert(scratch_klass != nullptr, "invariant");
return HAS_TIMING_BIT(scratch_klass);
}
inline void JfrTraceId::set_timing_bit(const Klass* k) {
assert(k != nullptr, "invariant");
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
assert(!has_timing_bit(k), "invariant");
SET_TIMING_BIT(k);
assert(has_timing_bit(k), "invariant");
}
inline void JfrTraceId::clear_timing_bit(const Klass* k) {
assert(k != nullptr, "invarriant");
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
assert(JfrTraceId::has_timing_bit(k), "invariant");
CLEAR_TIMING_BIT(k);
assert(!JfrTraceId::has_timing_bit(k), "invariant");
inline void JfrTraceId::set_timing_bit(const InstanceKlass* scratch_klass) {
assert(scratch_klass != nullptr, "invariant");
assert(!has_timing_bit(scratch_klass), "invariant");
SET_TIMING_BIT(scratch_klass);
assert(has_timing_bit(scratch_klass), "invariant");
}
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEID_INLINE_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -78,7 +78,7 @@ inline uint8_t* traceid_meta_byte(const T* ptr) {
template <>
inline uint8_t* traceid_meta_byte<Method>(const Method* ptr) {
assert(ptr != nullptr, "invariant");
return ptr->trace_meta_addr();
return ptr->trace_flags_meta_addr();
}
inline uint8_t traceid_and(uint8_t bits, uint8_t current) {

View File

@ -86,6 +86,27 @@ inline traceid JfrTraceIdLoadBarrier::load(const Klass* klass) {
return TRACE_ID(klass);
}
inline const Method* latest_version(const Klass* klass, const Method* method) {
assert(klass != nullptr, "invariant");
assert(method != nullptr, "invariant");
assert(klass == method->method_holder(), "invariant");
assert(method->is_old(), "invariant");
const InstanceKlass* const ik = InstanceKlass::cast(klass);
assert(ik->has_been_redefined(), "invariant");
const Method* const latest_version = ik->method_with_orig_idnum(method->orig_method_idnum());
if (latest_version == nullptr) {
assert(AllowRedefinitionToAddDeleteMethods, "invariant");
// method has been removed. Return old version.
return method;
}
assert(latest_version != nullptr, "invariant");
assert(latest_version != method, "invariant");
assert(!latest_version->is_old(), "invariant");
assert(latest_version->orig_method_idnum() == method->orig_method_idnum(), "invariant");
assert(latest_version->name() == method->name() && latest_version->signature() == method->signature(), "invariant");
return latest_version;
}
inline traceid JfrTraceIdLoadBarrier::load(const Method* method) {
return load(method->method_holder(), method);
}
@ -93,6 +114,9 @@ inline traceid JfrTraceIdLoadBarrier::load(const Method* method) {
inline traceid JfrTraceIdLoadBarrier::load(const Klass* klass, const Method* method) {
assert(klass != nullptr, "invariant");
assert(method != nullptr, "invariant");
if (method->is_old()) {
method = latest_version(klass, method);
}
if (should_tag(method)) {
SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass);
SET_METHOD_FLAG_USED_THIS_EPOCH(method);
@ -111,6 +135,9 @@ inline traceid JfrTraceIdLoadBarrier::load_no_enqueue(const Method* method) {
inline traceid JfrTraceIdLoadBarrier::load_no_enqueue(const Klass* klass, const Method* method) {
assert(klass != nullptr, "invariant");
assert(method != nullptr, "invariant");
if (method->is_old()) {
method = latest_version(klass, method);
}
SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass);
SET_METHOD_FLAG_USED_THIS_EPOCH(method);
assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
@ -123,11 +150,12 @@ inline traceid JfrTraceIdLoadBarrier::load(const ClassLoaderData* cld) {
if (cld->has_class_mirror_holder()) {
return 0;
}
const traceid id = set_used_and_get(cld);
const Klass* const class_loader_klass = cld->class_loader_klass();
if (class_loader_klass != nullptr) {
load(class_loader_klass);
}
return set_used_and_get(cld);
return id;
}
inline traceid JfrTraceIdLoadBarrier::load(const ModuleEntry* module) {
@ -158,6 +186,7 @@ inline traceid JfrTraceIdLoadBarrier::load_leakp(const Klass* klass) {
inline traceid JfrTraceIdLoadBarrier::load_leakp(const Klass* klass, const Method* method) {
assert(klass != nullptr, "invariant");
assert(method != nullptr, "invariant");
assert(!method->is_old(), "invariant");
assert(klass == method->method_holder(), "invariant");
assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
if (should_tag(method)) {
@ -175,6 +204,7 @@ inline traceid JfrTraceIdLoadBarrier::load_leakp(const Klass* klass, const Metho
inline traceid JfrTraceIdLoadBarrier::load_leakp_previous_epoch(const Klass* klass, const Method* method) {
assert(klass != nullptr, "invariant");
assert(method != nullptr, "invariant");
assert(!method->is_old(), "invariant");
assert(klass == method->method_holder(), "invariant");
assert(METHOD_AND_CLASS_USED_PREVIOUS_EPOCH(klass), "invariant");
if (METHOD_FLAG_NOT_USED_PREVIOUS_EPOCH(method)) {

View File

@ -645,7 +645,7 @@ static void write_thread_local_buffer(JfrChunkWriter& chunkwriter, Thread* t) {
size_t JfrRecorderService::flush() {
size_t total_elements = flush_metadata(_chunkwriter);
total_elements = flush_storage(_storage, _chunkwriter);
total_elements += flush_storage(_storage, _chunkwriter);
if (_string_pool.is_modified()) {
total_elements += flush_stringpool(_string_pool, _chunkwriter);
}

View File

@ -29,6 +29,7 @@
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
/*
* There are two separate repository instances.
@ -186,6 +187,7 @@ void JfrStackTraceRepository::record_for_leak_profiler(JavaThread* current_threa
}
traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) {
assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
assert(stacktrace.number_of_frames() > 0, "invariant");
const size_t index = stacktrace._hash % TABLE_SIZE;

View File

@ -390,15 +390,16 @@ static inline void write_stacktraces(JfrChunkWriter& cw) {
_resolved_list.iterate(scw);
}
// 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.
// First, all events are serialized, and unique type set blobs are written into the
// JfrCheckpoint system to be serialized to the chunk upon return.
// Then, we consolidate all stack trace blobs into a single TYPE_STACKTRACE checkpoint
// and serialize it directly to the chunk.
void JfrDeprecationManager::write_edges(JfrChunkWriter& cw, Thread* thread, bool on_error /* false */) {
if (_resolved_list.is_nonempty() && JfrEventSetting::is_enabled(JfrDeprecatedInvocationEvent)) {
write_events(cw, thread, on_error);
if (has_stacktrace()) {
write_stacktraces(cw);
}
write_events(cw, thread, on_error);
}
}

View File

@ -40,8 +40,6 @@
#define EVENT_STICKY_BIT 8192
#define IS_EVENT_KLASS(ptr) (((ptr)->trace_id() & (JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS)) != 0)
#define IS_EVENT_OR_HOST_KLASS(ptr) (((ptr)->trace_id() & (JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS | EVENT_HOST_KLASS)) != 0)
#define KLASS_HAS_STICKY_BIT(ptr) (((ptr)->trace_id() & STICKY_BIT) != 0)
#define ON_KLASS_REDEFINITION(k, t) if (KLASS_HAS_STICKY_BIT(k)) Jfr::on_klass_redefinition(k, t)
#define ON_KLASS_CREATION(k, p, t) Jfr::on_klass_creation(k, p, t)
#endif // SHARE_JFR_SUPPORT_JFRKLASSEXTENSION_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
@ -95,11 +95,13 @@ class JfrTraceFlag {
uint8_t* trace_flags_addr() const { \
return _trace_flags.flags_addr(); \
} \
uint8_t* trace_meta_addr() const { \
uint8_t* trace_flags_meta_addr() const { \
return _trace_flags.meta_addr(); \
} \
void copy_trace_flags(uint16_t rhs_flags) const { \
_trace_flags.set_flags(_trace_flags.flags() | rhs_flags); \
void copy_trace_flags(const Method* rhm) const { \
assert(rhm != nullptr, "invariant"); \
set_trace_flags(rhm->trace_flags()); \
assert(trace_flags()==rhm->trace_flags(), ""); \
}
#endif // SHARE_JFR_SUPPORT_JFRTRACEIDEXTENSION_HPP

View File

@ -36,16 +36,16 @@ class InstanceKlass;
class JfrInstrumentedClass {
private:
traceid _trace_id;
const InstanceKlass* _instance_klass;
const InstanceKlass* _ik;
bool _unloaded;
public:
JfrInstrumentedClass(traceid trace_id = 0, const InstanceKlass* instance_klass = nullptr, bool unloaded = false) :
_trace_id(trace_id), _instance_klass(instance_klass), _unloaded(unloaded) {
JfrInstrumentedClass(traceid trace_id = 0, const InstanceKlass* ik = nullptr, bool unloaded = false) :
_trace_id(trace_id), _ik(ik), _unloaded(unloaded) {
}
const InstanceKlass* instance_klass() const {
return _instance_klass;
return _ik;
}
traceid trace_id() const {

View File

@ -67,6 +67,8 @@ void JfrMethodProcessor::update_methods(const InstanceKlass* ik) {
const uint32_t idx = _methods->at(i).methods_array_index();
Method* const method = ik_methods->at(idx);
assert(method != nullptr, "invariant");
assert(method->name() == _methods->at(i).name(), "invariant");
assert(method->signature() == _methods->at(i).signature(), "invariant");
_methods->at(i).set_method(method);
// This is to keep the method from being unloaded during redefine / retransform.
// Equivalent functionality to that provided by the methodHandle. Unfortunately,

View File

@ -128,11 +128,11 @@ void JfrMethodTracer::retransform(JNIEnv* env, const JfrFilterClassClosure& clas
}
}
static void handle_no_bytecode_result(const Klass* klass) {
assert(klass != nullptr, "invariant");
if (JfrTraceId::has_sticky_bit(klass)) {
static void handle_no_bytecode_result(const InstanceKlass* ik) {
assert(ik != nullptr, "invariant");
if (JfrTraceId::has_sticky_bit(ik)) {
MutexLocker lock(ClassLoaderDataGraph_lock);
JfrTraceTagging::clear_sticky_bit(InstanceKlass::cast(klass));
JfrTraceTagging::clear_sticky(ik);
}
}
@ -143,11 +143,11 @@ void JfrMethodTracer::on_klass_creation(InstanceKlass*& ik, ClassFileParser& par
ResourceMark rm(THREAD);
// 1. Is the ik the initial load, i.e.the first InstanceKlass, or a scratch klass, denoting a redefine / retransform?
const Klass* const existing_klass = JfrClassTransformer::find_existing_klass(ik, THREAD);
const bool is_retransform = existing_klass != nullptr;
const InstanceKlass* const existing_ik = JfrClassTransformer::find_existing_klass(ik, THREAD);
const bool is_retransform = existing_ik != nullptr;
// 2. Test the ik and its methods against the currently installed filter object.
JfrMethodProcessor mp(is_retransform ? InstanceKlass::cast(existing_klass) : ik, THREAD);
JfrMethodProcessor mp(is_retransform ? existing_ik : ik, THREAD);
if (!mp.has_methods()) {
return;
}
@ -159,7 +159,7 @@ void JfrMethodTracer::on_klass_creation(InstanceKlass*& ik, ClassFileParser& par
// If no bytecode is returned, either an error occurred during transformation, but more
// likely the matched instructions were negative, i.e. instructions to remove existing instrumentation
// and so Java added no new instrumentation. By not returning a bytecode result, the klass is restored to its original, non-instrumented, version.
handle_no_bytecode_result(is_retransform ? InstanceKlass::cast(existing_klass) : ik);
handle_no_bytecode_result(is_retransform ? existing_ik : ik);
return;
}
// 4. Now create a new InstanceKlass representation from the modified bytecode.
@ -173,13 +173,12 @@ void JfrMethodTracer::on_klass_creation(InstanceKlass*& ik, ClassFileParser& par
// Keep the original cached class file data from the existing class.
JfrClassTransformer::transfer_cached_class_file_data(ik, new_ik, parser, THREAD);
JfrClassTransformer::rewrite_klass_pointer(ik, new_ik, parser, THREAD); // The ik is modified to point to new_ik here.
const InstanceKlass* const existing_ik = InstanceKlass::cast(existing_klass);
mp.update_methods(existing_ik);
existing_ik->module()->add_read(jdk_jfr_module());
// By setting the sticky bit on the existng klass, we receive a callback into on_klass_redefinition (see below)
// when our new methods are installed into the existing klass as part of retransformation / redefinition.
// Only when we know our new methods have been installed can we add the klass to the instrumented list (done as part of callback).
JfrTraceTagging::install_sticky_bit_for_retransform_klass(existing_ik, mp.methods(), mp.has_timing());
JfrTraceTagging::tag_sticky_for_retransform_klass(existing_ik, ik, mp.methods(), mp.has_timing());
return;
}
// Initial class load.
@ -203,28 +202,22 @@ static inline void log_add(const InstanceKlass* ik) {
}
}
void JfrMethodTracer::add_timing_entry(const InstanceKlass* ik, traceid klass_id) {
assert(ik != nullptr, "invariant");
void JfrMethodTracer::add_timing_entry(traceid klass_id) {
assert(_timing_entries != nullptr, "invariant");
if (JfrTraceId::has_timing_bit(ik)) {
JfrTraceId::clear_timing_bit(ik);
_timing_entries->append(klass_id);
}
_timing_entries->append(klass_id);
}
// At this point we have installed our new retransformed methods into the original klass, which is ik.
// jvmtiRedefineClassses::redefine_single_class() has finished so we are still at a safepoint.
// If the original klass is not already in the list, add it and also dynamically tag all
// artifacts that have the sticky bit set. If the klass has an associated TimedClass,
// also add the klass to the list of _timing_entries for publication.
void JfrMethodTracer::on_klass_redefinition(const InstanceKlass* ik, Thread* thread) {
// jvmtiRedefineClassses::redefine_single_class() is about to finish so we are still at a safepoint.
// If the original klass is not already in the list, add it. If the klass has an associated TimedClass,
// add also the klass_id to the list of _timing_entries for publication.
void JfrMethodTracer::on_klass_redefinition(const InstanceKlass* ik, bool has_timing) {
assert(ik != nullptr, "invariant");
assert(!ik->is_scratch_class(), "invarint");
assert(ik->has_been_redefined(), "invariant");
assert(JfrTraceId::has_sticky_bit(ik), "invariant");
assert(in_use(), "invariant");
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
const traceid klass_id = JfrTraceId::load_raw(ik);
const JfrInstrumentedClass jic(klass_id, ik, false);
@ -235,8 +228,9 @@ void JfrMethodTracer::on_klass_redefinition(const InstanceKlass* ik, Thread* thr
assert(!JfrTraceIdEpoch::has_method_tracer_changed_tag_state(), "invariant");
JfrTraceIdEpoch::set_method_tracer_tag_state();
}
add_timing_entry(ik, klass_id);
JfrTraceTagging::set_dynamic_tag_for_sticky_bit(ik);
if (has_timing) {
add_timing_entry(klass_id);
}
log_add(ik);
}
}
@ -258,8 +252,7 @@ void JfrMethodTracer::add_instrumented_class(InstanceKlass* ik, GrowableArray<Jf
ik->module()->add_read(jdk_jfr_module());
MutexLocker lock(ClassLoaderDataGraph_lock);
assert(!in_instrumented_list(ik, instrumented_classes()), "invariant");
JfrTraceTagging::set_dynamic_tag(ik, methods);
JfrTraceTagging::set_sticky_bit(ik, methods);
JfrTraceTagging::tag_sticky(ik, methods);
const JfrInstrumentedClass jik(JfrTraceId::load_raw(ik), ik, false);
const int idx = instrumented_classes()->append(jik);
if (idx == 0) {

View File

@ -51,7 +51,7 @@ class JfrMethodTracer: AllStatic {
static GrowableArray<jlong>* _timing_entries; // Guarded by ClassLoaderDataGraph_lock
static ModuleEntry* jdk_jfr_module();
static void add_timing_entry(const InstanceKlass* ik, traceid klass_id);
static void add_timing_entry(traceid klass_id);
static void retransform(JNIEnv* env, const JfrFilterClassClosure& classes, TRAPS);
static void add_instrumented_class(InstanceKlass* ik, GrowableArray<JfrTracedMethod>* methods);
@ -61,7 +61,7 @@ class JfrMethodTracer: AllStatic {
static void add_to_unloaded_set(const Klass* k);
static void trim_instrumented_classes(bool trim);
static GrowableArray<JfrInstrumentedClass>* instrumented_classes();
static void on_klass_redefinition(const InstanceKlass* ik, Thread* thread);
static void on_klass_redefinition(const InstanceKlass* ik, bool has_timing);
static void on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS);
static jlongArray set_filters(JNIEnv* env,
jobjectArray classes,

View File

@ -25,9 +25,11 @@
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
#include "jfr/support/methodtracer/jfrInstrumentedClass.hpp"
#include "jfr/support/methodtracer/jfrMethodTracer.hpp"
#include "jfr/support/methodtracer/jfrTraceTagging.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/method.hpp"
#include "runtime/safepoint.hpp"
#include "utilities/growableArray.hpp"
void JfrTraceTagging::tag_dynamic(const InstanceKlass* ik) {
@ -38,100 +40,39 @@ void JfrTraceTagging::tag_dynamic(const Method* method) {
JfrTraceId::load_no_enqueue(method);
}
void JfrTraceTagging::tag_dynamic(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods) {
assert(ik != nullptr, "invariant");
assert(methods != nullptr, "invariant");
for (int i = 0; i < methods->length(); ++i) {
const Method* const method = methods->at(i).method();
assert(method != nullptr, "invariant");
if (!method->is_old()) {
tag_dynamic(method);
continue;
}
// A redefinition / retransformation interleaved.
// Find and tag the latest version of the method.
tag_dynamic(ik->method_with_orig_idnum(method->orig_method_idnum()));
}
}
void JfrTraceTagging::set_dynamic_tag(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods) {
assert(ik != nullptr, "invariant");
assert(!ik->is_scratch_class(), "invariant");
tag_dynamic(ik, methods);
tag_dynamic(ik);
}
void JfrTraceTagging::set_dynamic_tag_for_sticky_bit(const InstanceKlass* ik) {
assert(ik != nullptr, "invariant");
assert(!ik->is_scratch_class(), "invariant");
assert(JfrTraceId::has_sticky_bit(ik), "invariant");
const int length = ik->methods()->length();
for (int i = 0; i < length; ++i) {
const Method* const m = ik->methods()->at(i);
if (JfrTraceId::has_sticky_bit(m)) {
tag_dynamic(m);
}
}
tag_dynamic(ik);
}
void JfrTraceTagging::tag_sticky(const InstanceKlass* ik) {
JfrTraceId::set_sticky_bit(ik);
}
void JfrTraceTagging::tag_sticky_enqueue(const InstanceKlass* ik) {
tag_sticky(ik);
JfrTraceIdLoadBarrier::enqueue(ik);
}
void JfrTraceTagging::tag_sticky(const Method* method) {
JfrTraceId::set_sticky_bit(method);
}
void JfrTraceTagging::tag_sticky(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods) {
assert(ik != nullptr, "invariant");
void JfrTraceTagging::tag_sticky(const GrowableArray<JfrTracedMethod>* methods) {
assert(methods != nullptr, "invariant");
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
for (int i = 0; i < methods->length(); ++i) {
const Method* const method = methods->at(i).method();
assert(method != nullptr, "invariant");
if (!method->is_old()) {
tag_sticky(method);
continue;
}
// A redefinition / retransformation interleaved.
// Find and tag the latest version of the method.
tag_sticky(ik->method_with_orig_idnum(method->orig_method_idnum()));
tag_sticky(method);
}
}
void JfrTraceTagging::tag_timing(const InstanceKlass* ik) {
JfrTraceId::set_timing_bit(ik);
}
void JfrTraceTagging::install_sticky_bit_for_retransform_klass(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods, bool timing) {
assert(ik != nullptr, "invariant");
assert(!ik->is_scratch_class(), "invariant");
MutexLocker lock(ClassLoaderDataGraph_lock);
if (JfrTraceId::has_sticky_bit(ik)) {
clear_sticky_bit(ik);
}
tag_sticky(ik, methods);
tag_sticky(ik);
if (timing) {
tag_timing(ik);
}
}
void JfrTraceTagging::set_sticky_bit(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods) {
void JfrTraceTagging::tag_sticky(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods) {
assert(ik != nullptr, "invariant");
assert(!ik->is_scratch_class(), "invariant");
assert(methods != nullptr, "invariant");
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
tag_sticky(ik, methods);
tag_sticky(ik);
tag_sticky(methods);
tag_sticky_enqueue(ik);
}
void JfrTraceTagging::clear_sticky_bit(const InstanceKlass* ik, bool dynamic_tag /* true */) {
void JfrTraceTagging::clear_sticky(const InstanceKlass* ik, bool dynamic_tag /* true */) {
assert(ik != nullptr, "invariant");
assert(!ik->is_scratch_class(), "invariant");
assert(JfrTraceId::has_sticky_bit(ik), "invariant");
@ -153,8 +94,78 @@ void JfrTraceTagging::clear_sticky_bit(const InstanceKlass* ik, bool dynamic_tag
tag_dynamic(ik);
}
JfrTraceId::clear_sticky_bit(ik);
if (JfrTraceId::has_timing_bit(ik)) {
JfrTraceId::clear_timing_bit(ik);
}
assert(!JfrTraceId::has_timing_bit(ik), "invariant");
}
void JfrTraceTagging::tag_sticky_for_retransform_klass(const InstanceKlass* existing_klass, const InstanceKlass* scratch_klass, const GrowableArray<JfrTracedMethod>* methods, bool timing) {
assert(existing_klass != nullptr, "invariant");
assert(scratch_klass != nullptr, "invariant");
// The scratch class has not yet received its official status.
// assert(scratch_klass->is_scratch_class(), "invariant");
if (timing) {
// Can be done outside lock because it is a scratch klass.
// Visibility guaranteed by upcoming safepoint.
JfrTraceId::set_timing_bit(scratch_klass);
}
MutexLocker lock(ClassLoaderDataGraph_lock);
if (JfrTraceId::has_sticky_bit(existing_klass)) {
clear_sticky(existing_klass);
}
tag_sticky(methods);
tag_sticky(existing_klass);
}
void JfrTraceTagging::on_klass_redefinition(const InstanceKlass* ik, const InstanceKlass* scratch_klass) {
assert(ik != nullptr, "invariant");
assert(ik->has_been_redefined(), "invariant");
assert(scratch_klass != nullptr, "invariant");
assert(scratch_klass->is_scratch_class(), "invariant");
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
const bool klass_has_sticky_bit = JfrTraceId::has_sticky_bit(ik);
if (klass_has_sticky_bit) {
JfrTraceIdLoadBarrier::enqueue(ik);
}
const Array<Method*>* new_methods = ik->methods();
assert(new_methods != nullptr, "invariant");
const int len = new_methods->length(); // Can be shorter, equal to, or longer than old methods length.
for (int i = 0; i < len; ++i) {
const Method* const nm = new_methods->at(i);
assert(nm != nullptr, "invariant");
const Method* const om = scratch_klass->method_with_orig_idnum(nm->orig_method_idnum());
if (om == nullptr) {
assert(AllowRedefinitionToAddDeleteMethods, "invariant");
// nm is a newly added Method.
continue;
}
assert(nm != om, "invariant");
assert(om->is_old(), "invariant");
assert(nm->orig_method_idnum() == om->orig_method_idnum(), "invariant");
assert(nm->name() == om->name() && nm->signature() == om->signature(), "invariant");
if (nm->trace_flags() == om->trace_flags()) {
continue;
}
const bool is_blessed = IS_METHOD_BLESSED(nm);
// Copy the old method trace flags under a safepoint.
nm->copy_trace_flags(om);
assert(nm->trace_flags() == om->trace_flags(), "invariant");
if (is_blessed) {
BLESS_METHOD(nm);
assert(IS_METHOD_BLESSED(nm), "invariant");
}
}
// A retransformed/redefined klass carrying the sticky bit
// needs additional processing by the JfrMethodTracer subsystem.
if (klass_has_sticky_bit) {
assert(JfrMethodTracer::in_use(), "invariant");
JfrMethodTracer::on_klass_redefinition(ik, JfrTraceId::has_timing_bit(scratch_klass));
}
}

View File

@ -39,18 +39,16 @@ template <typename E> class GrowableArray;
class JfrTraceTagging : AllStatic {
private:
static void tag_dynamic(const InstanceKlass* ik);
static void tag_dynamic(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods);
static void tag_dynamic(const Method* method);
static void tag_sticky(const InstanceKlass* ik);
static void tag_sticky(const Method* method);
static void tag_sticky(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods);
static void tag_timing(const InstanceKlass* ik);
static void tag_sticky(const GrowableArray<JfrTracedMethod>* methods);
static void tag_sticky_enqueue(const InstanceKlass* ik);
public:
static void set_dynamic_tag(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods);
static void set_dynamic_tag_for_sticky_bit(const InstanceKlass* ik);
static void install_sticky_bit_for_retransform_klass(const InstanceKlass* existing_klass, const GrowableArray<JfrTracedMethod>* methods, bool timing);
static void set_sticky_bit(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods);
static void clear_sticky_bit(const InstanceKlass* ik, bool dynamic_tag = true);
static void clear_sticky(const InstanceKlass* ik, bool dynamic_tag = true);
static void tag_sticky(const InstanceKlass* ik, const GrowableArray<JfrTracedMethod>* methods);
static void tag_sticky_for_retransform_klass(const InstanceKlass* existing_klass, const InstanceKlass* scratch_klass, const GrowableArray<JfrTracedMethod>* methods, bool timing);
static void on_klass_redefinition(const InstanceKlass* ik, const InstanceKlass* scratch_klass);
};
#endif /* SHARE_JFR_SUPPORT_METHODTRACER_JFRTRACETAGGING_HPP */

View File

@ -66,6 +66,9 @@
#include "utilities/checkedCast.hpp"
#include "utilities/events.hpp"
#include "utilities/macros.hpp"
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
Array<Method*>* VM_RedefineClasses::_old_methods = nullptr;
Array<Method*>* VM_RedefineClasses::_new_methods = nullptr;
@ -1173,7 +1176,6 @@ jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions(
}
}
}
JFR_ONLY(k_new_method->copy_trace_flags(k_old_method->trace_flags());)
log_trace(redefine, class, normalize)
("Method matched: new: %s [%d] == old: %s [%d]",
k_new_method->name_and_sig_as_C_string(), ni, k_old_method->name_and_sig_as_C_string(), oi);
@ -4393,7 +4395,7 @@ void VM_RedefineClasses::redefine_single_class(Thread* current, jclass the_jclas
// keep track of previous versions of this class
the_class->add_previous_version(scratch_class, emcp_method_count);
JFR_ONLY(ON_KLASS_REDEFINITION(the_class, current);)
JFR_ONLY(Jfr::on_klass_redefinition(the_class, scratch_class);)
_timer_rsc_phase1.stop();
if (log_is_enabled(Info, redefine, class, timer)) {

View File

@ -142,7 +142,6 @@ void SafepointMechanism::process(JavaThread *thread, bool allow_suspend, bool ch
do {
JavaThreadState state = thread->thread_state();
guarantee(state == _thread_in_vm, "Illegal threadstate encountered: %d", state);
JFR_ONLY(Jfr::check_and_process_sample_request(thread);)
if (global_poll()) {
// Any load in ::block() must not pass the global poll load.
// Otherwise we might load an old safepoint counter (for example).
@ -161,6 +160,7 @@ void SafepointMechanism::process(JavaThread *thread, bool allow_suspend, bool ch
need_rechecking = thread->handshake_state()->has_operation() && thread->handshake_state()->process_by_self(allow_suspend, check_async_exception);
} while (need_rechecking);
JFR_ONLY(Jfr::check_and_process_sample_request(thread);)
update_poll_values(thread);
assert(sp_before == thread->last_Java_sp(), "Anchor has changed");
}

View File

@ -77,7 +77,7 @@ final class ConstantMap {
if (id != 0) {
String msg = "Missing object ID " + id + " in pool " + getName() + ". All IDs should reference an object";
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, msg);
// assert false : msg;
assert false : msg;
}
return null;
}