mirror of
https://github.com/darlinghq/darling-openjdk.git
synced 2024-11-30 15:50:29 +00:00
8231081: TestMetadataRetention fails due to missing symbol id
Reviewed-by: egahlin
This commit is contained in:
parent
a5fab79984
commit
b4d37ba6b9
@ -24,15 +24,18 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/javaClasses.inline.hpp"
|
||||
#include "jfr/recorder/jfrRecorder.hpp"
|
||||
#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
|
||||
#include "jfr/leakprofiler/leakProfiler.hpp"
|
||||
#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
|
||||
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/jfrTypeSet.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
|
||||
#include "jfr/recorder/jfrRecorder.hpp"
|
||||
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
||||
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
||||
#include "jfr/recorder/storage/jfrMemorySpace.inline.hpp"
|
||||
#include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
|
||||
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
||||
#include "jfr/utilities/jfrBigEndian.hpp"
|
||||
#include "jfr/utilities/jfrTypes.hpp"
|
||||
#include "logging/log.hpp"
|
||||
@ -81,7 +84,7 @@ JfrCheckpointManager::~JfrCheckpointManager() {
|
||||
if (_lock != NULL) {
|
||||
delete _lock;
|
||||
}
|
||||
JfrTypeManager::clear();
|
||||
JfrTypeManager::destroy();
|
||||
}
|
||||
|
||||
static const size_t unlimited_mspace_size = 0;
|
||||
@ -332,6 +335,7 @@ size_t JfrCheckpointManager::write_epoch_transition_mspace() {
|
||||
|
||||
typedef DiscardOp<DefaultDiscarder<JfrBuffer> > DiscardOperation;
|
||||
size_t JfrCheckpointManager::clear() {
|
||||
JfrTypeSet::clear();
|
||||
DiscardOperation discarder(mutexed); // mutexed discard mode
|
||||
process_free_list(discarder, _free_list_mspace);
|
||||
process_free_list(discarder, _epoch_transition_mspace);
|
||||
@ -353,12 +357,34 @@ size_t JfrCheckpointManager::write_safepoint_types() {
|
||||
}
|
||||
|
||||
void JfrCheckpointManager::write_type_set() {
|
||||
JfrTypeManager::write_type_set();
|
||||
assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||
// can safepoint here
|
||||
MutexLocker cld_lock(ClassLoaderDataGraph_lock);
|
||||
MutexLocker module_lock(Module_lock);
|
||||
if (!LeakProfiler::is_running()) {
|
||||
JfrCheckpointWriter writer(true, true, Thread::current());
|
||||
JfrTypeSet::serialize(&writer, NULL, false);
|
||||
return;
|
||||
}
|
||||
Thread* const t = Thread::current();
|
||||
JfrCheckpointWriter leakp_writer(false, true, t);
|
||||
JfrCheckpointWriter writer(false, true, t);
|
||||
JfrTypeSet::serialize(&writer, &leakp_writer, false);
|
||||
ObjectSampleCheckpoint::on_type_set(leakp_writer);
|
||||
}
|
||||
|
||||
void JfrCheckpointManager::write_type_set_for_unloaded_classes() {
|
||||
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
||||
JfrTypeManager::write_type_set_for_unloaded_classes();
|
||||
JfrCheckpointWriter writer(false, true, Thread::current());
|
||||
const JfrCheckpointContext ctx = writer.context();
|
||||
JfrTypeSet::serialize(&writer, NULL, true);
|
||||
if (LeakProfiler::is_running()) {
|
||||
ObjectSampleCheckpoint::on_type_set_unload(writer);
|
||||
}
|
||||
if (!JfrRecorder::is_recording()) {
|
||||
// discard by rewind
|
||||
writer.set_context(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void JfrCheckpointManager::create_thread_blob(JavaThread* jt) {
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "jfr/recorder/jfrRecorder.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/jfrThreadState.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/jfrTypeSet.hpp"
|
||||
#include "jfr/support/jfrThreadLocal.hpp"
|
||||
#include "jfr/writers/jfrJavaEventWriter.hpp"
|
||||
#include "memory/metaspaceGCThresholdUpdater.hpp"
|
||||
@ -271,30 +270,6 @@ void VMOperationTypeConstant::serialize(JfrCheckpointWriter& writer) {
|
||||
}
|
||||
}
|
||||
|
||||
class TypeSetSerialization {
|
||||
private:
|
||||
JfrCheckpointWriter* _leakp_writer;
|
||||
bool _class_unload;
|
||||
public:
|
||||
TypeSetSerialization(bool class_unload, JfrCheckpointWriter* leakp_writer = NULL) :
|
||||
_leakp_writer(leakp_writer), _class_unload(class_unload) {}
|
||||
void write(JfrCheckpointWriter& writer) {
|
||||
JfrTypeSet::serialize(&writer, _leakp_writer, _class_unload);
|
||||
}
|
||||
};
|
||||
|
||||
void ClassUnloadTypeSet::serialize(JfrCheckpointWriter& writer) {
|
||||
TypeSetSerialization type_set(true);
|
||||
type_set.write(writer);
|
||||
};
|
||||
|
||||
TypeSet::TypeSet(JfrCheckpointWriter* leakp_writer) : _leakp_writer(leakp_writer) {}
|
||||
|
||||
void TypeSet::serialize(JfrCheckpointWriter& writer) {
|
||||
TypeSetSerialization type_set(false, _leakp_writer);
|
||||
type_set.write(writer);
|
||||
};
|
||||
|
||||
void ThreadStateConstant::serialize(JfrCheckpointWriter& writer) {
|
||||
JfrThreadState::serialize(writer);
|
||||
}
|
||||
|
@ -37,11 +37,6 @@ class JfrThreadGroupConstant : public JfrSerializer {
|
||||
void serialize(JfrCheckpointWriter& writer);
|
||||
};
|
||||
|
||||
class ClassUnloadTypeSet : public JfrSerializer {
|
||||
public:
|
||||
void serialize(JfrCheckpointWriter& writer);
|
||||
};
|
||||
|
||||
class FlagValueOriginConstant : public JfrSerializer {
|
||||
public:
|
||||
void serialize(JfrCheckpointWriter& writer);
|
||||
@ -107,14 +102,6 @@ class VMOperationTypeConstant : public JfrSerializer {
|
||||
void serialize(JfrCheckpointWriter& writer);
|
||||
};
|
||||
|
||||
class TypeSet : public JfrSerializer {
|
||||
private:
|
||||
JfrCheckpointWriter* _leakp_writer;
|
||||
public:
|
||||
explicit TypeSet(JfrCheckpointWriter* leakp_writer = NULL);
|
||||
void serialize(JfrCheckpointWriter& writer);
|
||||
};
|
||||
|
||||
class ThreadStateConstant : public JfrSerializer {
|
||||
public:
|
||||
void serialize(JfrCheckpointWriter& writer);
|
||||
|
@ -23,9 +23,6 @@
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "jfr/jfr.hpp"
|
||||
#include "jfr/leakprofiler/leakProfiler.hpp"
|
||||
#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
|
||||
#include "jfr/metadata/jfrSerializer.hpp"
|
||||
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/jfrType.hpp"
|
||||
@ -35,9 +32,9 @@
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/safepoint.hpp"
|
||||
#include "runtime/semaphore.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "utilities/exceptions.hpp"
|
||||
#include "runtime/semaphore.hpp"
|
||||
|
||||
class JfrSerializerRegistration : public JfrCHeapObj {
|
||||
private:
|
||||
@ -120,7 +117,7 @@ typedef StopOnNullIterator<const List> Iterator;
|
||||
static List types;
|
||||
static List safepoint_types;
|
||||
|
||||
void JfrTypeManager::clear() {
|
||||
void JfrTypeManager::destroy() {
|
||||
SerializerRegistrationGuard guard;
|
||||
Iterator iter(types);
|
||||
JfrSerializerRegistration* registration;
|
||||
@ -152,39 +149,6 @@ void JfrTypeManager::write_safepoint_types(JfrCheckpointWriter& writer) {
|
||||
}
|
||||
}
|
||||
|
||||
void JfrTypeManager::write_type_set() {
|
||||
assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||
// can safepoint here
|
||||
MutexLocker cld_lock(ClassLoaderDataGraph_lock);
|
||||
MutexLocker module_lock(Module_lock);
|
||||
if (!LeakProfiler::is_running()) {
|
||||
JfrCheckpointWriter writer(true, true, Thread::current());
|
||||
TypeSet set;
|
||||
set.serialize(writer);
|
||||
return;
|
||||
}
|
||||
JfrCheckpointWriter leakp_writer(false, true, Thread::current());
|
||||
JfrCheckpointWriter writer(false, true, Thread::current());
|
||||
TypeSet set(&leakp_writer);
|
||||
set.serialize(writer);
|
||||
ObjectSampleCheckpoint::on_type_set(leakp_writer);
|
||||
}
|
||||
|
||||
void JfrTypeManager::write_type_set_for_unloaded_classes() {
|
||||
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
||||
JfrCheckpointWriter writer(false, true, Thread::current());
|
||||
const JfrCheckpointContext ctx = writer.context();
|
||||
ClassUnloadTypeSet class_unload_set;
|
||||
class_unload_set.serialize(writer);
|
||||
if (LeakProfiler::is_running()) {
|
||||
ObjectSampleCheckpoint::on_type_set_unload(writer);
|
||||
}
|
||||
if (!Jfr::is_recording()) {
|
||||
// discard anything written
|
||||
writer.set_context(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void JfrTypeManager::create_thread_blob(JavaThread* jt) {
|
||||
assert(jt != NULL, "invariant");
|
||||
ResourceMark rm(jt);
|
||||
|
@ -32,11 +32,9 @@ class JfrCheckpointWriter;
|
||||
class JfrTypeManager : public AllStatic {
|
||||
public:
|
||||
static bool initialize();
|
||||
static void clear();
|
||||
static void destroy();
|
||||
static void write_types(JfrCheckpointWriter& writer);
|
||||
static void write_safepoint_types(JfrCheckpointWriter& writer);
|
||||
static void write_type_set();
|
||||
static void write_type_set_for_unloaded_classes();
|
||||
static void create_thread_blob(JavaThread* jt);
|
||||
static void write_thread_checkpoint(JavaThread* jt);
|
||||
};
|
||||
|
@ -204,9 +204,14 @@ int write__klass__leakp(JfrCheckpointWriter* writer, const void* k) {
|
||||
return write_klass(writer, klass, true);
|
||||
}
|
||||
|
||||
static bool is_implied(const Klass* klass) {
|
||||
assert(klass != NULL, "invariant");
|
||||
return klass->is_subclass_of(SystemDictionary::ClassLoader_klass()) || klass == SystemDictionary::Object_klass();
|
||||
}
|
||||
|
||||
static void do_implied(Klass* klass) {
|
||||
assert(klass != NULL, "invariant");
|
||||
if (klass->is_subclass_of(SystemDictionary::ClassLoader_klass()) || klass == SystemDictionary::Object_klass()) {
|
||||
if (is_implied(klass)) {
|
||||
if (_leakp_writer != NULL) {
|
||||
SET_LEAKP(klass);
|
||||
}
|
||||
@ -259,6 +264,16 @@ typedef JfrTypeWriterHost<KlassWriterImpl, TYPE_CLASS> KlassWriter;
|
||||
typedef CompositeFunctor<KlassPtr, KlassWriter, KlassArtifactRegistrator> KlassWriterRegistration;
|
||||
typedef JfrArtifactCallbackHost<KlassPtr, KlassWriterRegistration> KlassCallback;
|
||||
|
||||
template <>
|
||||
class LeakPredicate<const Klass*> {
|
||||
public:
|
||||
LeakPredicate(bool class_unload) {}
|
||||
bool operator()(const Klass* klass) {
|
||||
assert(klass != NULL, "invariant");
|
||||
return IS_LEAKP(klass) || is_implied(klass);
|
||||
}
|
||||
};
|
||||
|
||||
typedef LeakPredicate<KlassPtr> LeakKlassPredicate;
|
||||
typedef JfrPredicatedTypeWriterImplHost<KlassPtr, LeakKlassPredicate, write__klass__leakp> LeakKlassWriterImpl;
|
||||
typedef JfrTypeWriterHost<LeakKlassWriterImpl, TYPE_CLASS> LeakKlassWriter;
|
||||
@ -809,6 +824,12 @@ static void write_symbols() {
|
||||
_artifacts->tally(sw);
|
||||
}
|
||||
|
||||
static bool clear_artifacts = false;
|
||||
|
||||
void JfrTypeSet::clear() {
|
||||
clear_artifacts = true;
|
||||
}
|
||||
|
||||
typedef Wrapper<KlassPtr, ClearArtifact> ClearKlassBits;
|
||||
typedef Wrapper<MethodPtr, ClearArtifact> ClearMethodFlag;
|
||||
typedef MethodIteratorHost<ClearMethodFlag, ClearKlassBits, false> ClearKlassAndMethods;
|
||||
@ -820,7 +841,7 @@ static size_t teardown() {
|
||||
assert(_writer != NULL, "invariant");
|
||||
ClearKlassAndMethods clear(_writer);
|
||||
_artifacts->iterate_klasses(clear);
|
||||
_artifacts->clear();
|
||||
JfrTypeSet::clear();
|
||||
++checkpoint_id;
|
||||
}
|
||||
return total_count;
|
||||
@ -833,8 +854,9 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer
|
||||
if (_artifacts == NULL) {
|
||||
_artifacts = new JfrArtifactSet(class_unload);
|
||||
} else {
|
||||
_artifacts->initialize(class_unload);
|
||||
_artifacts->initialize(class_unload, clear_artifacts);
|
||||
}
|
||||
clear_artifacts = false;
|
||||
assert(_artifacts != NULL, "invariant");
|
||||
assert(!_artifacts->has_klass_entries(), "invariant");
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ class JfrCheckpointWriter;
|
||||
|
||||
class JfrTypeSet : AllStatic {
|
||||
public:
|
||||
static void clear();
|
||||
static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload);
|
||||
};
|
||||
|
||||
|
@ -35,6 +35,8 @@ JfrSymbolId::JfrSymbolId() :
|
||||
_cstring_table(new CStringTable(this)),
|
||||
_sym_list(NULL),
|
||||
_cstring_list(NULL),
|
||||
_sym_query(NULL),
|
||||
_cstring_query(NULL),
|
||||
_symbol_id_counter(1),
|
||||
_class_unload(false) {
|
||||
assert(_sym_table != NULL, "invariant");
|
||||
@ -66,9 +68,11 @@ void JfrSymbolId::clear() {
|
||||
assert(!_cstring_table->has_entries(), "invariant");
|
||||
|
||||
_sym_list = NULL;
|
||||
_cstring_list = NULL;
|
||||
_symbol_id_counter = 1;
|
||||
|
||||
_sym_query = NULL;
|
||||
_cstring_query = NULL;
|
||||
|
||||
assert(bootstrap != NULL, "invariant");
|
||||
bootstrap->reset();
|
||||
_cstring_list = bootstrap;
|
||||
@ -88,10 +92,10 @@ void JfrSymbolId::on_link(const SymbolEntry* entry) {
|
||||
}
|
||||
|
||||
bool JfrSymbolId::on_equals(uintptr_t hash, const SymbolEntry* entry) {
|
||||
// query might be NULL
|
||||
assert(entry != NULL, "invariant");
|
||||
assert(entry->hash() == hash, "invariant");
|
||||
return true;
|
||||
assert(_sym_query != NULL, "invariant");
|
||||
return _sym_query == entry->literal();
|
||||
}
|
||||
|
||||
void JfrSymbolId::on_unlink(const SymbolEntry* entry) {
|
||||
@ -99,18 +103,36 @@ void JfrSymbolId::on_unlink(const SymbolEntry* entry) {
|
||||
const_cast<Symbol*>(entry->literal())->decrement_refcount();
|
||||
}
|
||||
|
||||
static const char* resource_to_cstring(const char* resource_str) {
|
||||
assert(resource_str != NULL, "invariant");
|
||||
const size_t length = strlen(resource_str);
|
||||
char* const c_string = JfrCHeapObj::new_array<char>(length + 1);
|
||||
assert(c_string != NULL, "invariant");
|
||||
strncpy(c_string, resource_str, length + 1);
|
||||
return c_string;
|
||||
}
|
||||
|
||||
void JfrSymbolId::on_link(const CStringEntry* entry) {
|
||||
assert(entry != NULL, "invariant");
|
||||
assert(entry->id() == 0, "invariant");
|
||||
entry->set_id(++_symbol_id_counter);
|
||||
const_cast<CStringEntry*>(entry)->set_literal(resource_to_cstring(entry->literal()));
|
||||
entry->set_list_next(_cstring_list);
|
||||
_cstring_list = entry;
|
||||
}
|
||||
|
||||
static bool string_compare(const char* query, const char* candidate) {
|
||||
assert(query != NULL, "invariant");
|
||||
assert(candidate != NULL, "invariant");
|
||||
const size_t length = strlen(query);
|
||||
return strncmp(query, candidate, length) == 0;
|
||||
}
|
||||
|
||||
bool JfrSymbolId::on_equals(uintptr_t hash, const CStringEntry* entry) {
|
||||
assert(entry != NULL, "invariant");
|
||||
assert(entry->hash() == hash, "invariant");
|
||||
return true;
|
||||
assert(_cstring_query != NULL, "invariant");
|
||||
return string_compare(_cstring_query, entry->literal());
|
||||
}
|
||||
|
||||
void JfrSymbolId::on_unlink(const CStringEntry* entry) {
|
||||
@ -131,16 +153,10 @@ traceid JfrSymbolId::mark(const Symbol* symbol, bool leakp) {
|
||||
return mark((uintptr_t)symbol->identity_hash(), symbol, leakp);
|
||||
}
|
||||
|
||||
static unsigned int last_symbol_hash = 0;
|
||||
static traceid last_symbol_id = 0;
|
||||
|
||||
traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) {
|
||||
assert(data != NULL, "invariant");
|
||||
assert(_sym_table != NULL, "invariant");
|
||||
if (hash == last_symbol_hash) {
|
||||
assert(last_symbol_id != 0, "invariant");
|
||||
return last_symbol_id;
|
||||
}
|
||||
_sym_query = data;
|
||||
const SymbolEntry& entry = _sym_table->lookup_put(hash, data);
|
||||
if (_class_unload) {
|
||||
entry.set_unloading();
|
||||
@ -148,21 +164,13 @@ traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) {
|
||||
if (leakp) {
|
||||
entry.set_leakp();
|
||||
}
|
||||
last_symbol_hash = hash;
|
||||
last_symbol_id = entry.id();
|
||||
return last_symbol_id;
|
||||
return entry.id();
|
||||
}
|
||||
|
||||
static unsigned int last_cstring_hash = 0;
|
||||
static traceid last_cstring_id = 0;
|
||||
|
||||
traceid JfrSymbolId::mark(uintptr_t hash, const char* str, bool leakp) {
|
||||
assert(str != NULL, "invariant");
|
||||
assert(_cstring_table != NULL, "invariant");
|
||||
if (hash == last_cstring_hash) {
|
||||
assert(last_cstring_id != 0, "invariant");
|
||||
return last_cstring_id;
|
||||
}
|
||||
_cstring_query = str;
|
||||
const CStringEntry& entry = _cstring_table->lookup_put(hash, str);
|
||||
if (_class_unload) {
|
||||
entry.set_unloading();
|
||||
@ -170,9 +178,7 @@ traceid JfrSymbolId::mark(uintptr_t hash, const char* str, bool leakp) {
|
||||
if (leakp) {
|
||||
entry.set_leakp();
|
||||
}
|
||||
last_cstring_hash = hash;
|
||||
last_cstring_id = entry.id();
|
||||
return last_cstring_id;
|
||||
return entry.id();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -202,7 +208,7 @@ static const char* create_unsafe_anonymous_klass_symbol(const InstanceKlass* ik,
|
||||
sprintf(hash_buf, "/" UINTX_FORMAT, hash);
|
||||
const size_t hash_len = strlen(hash_buf);
|
||||
const size_t result_len = ik->name()->utf8_length();
|
||||
anonymous_symbol = JfrCHeapObj::new_array<char>(result_len + hash_len + 1);
|
||||
anonymous_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1);
|
||||
ik->name()->as_klass_external_name(anonymous_symbol, (int)result_len + 1);
|
||||
assert(strlen(anonymous_symbol) == result_len, "invariant");
|
||||
strcpy(anonymous_symbol + result_len, hash_buf);
|
||||
@ -215,21 +221,12 @@ bool JfrSymbolId::is_unsafe_anonymous_klass(const Klass* k) {
|
||||
return k->is_instance_klass() && ((const InstanceKlass*)k)->is_unsafe_anonymous();
|
||||
}
|
||||
|
||||
static unsigned int last_anonymous_hash = 0;
|
||||
static traceid last_anonymous_id = 0;
|
||||
|
||||
traceid JfrSymbolId::mark_unsafe_anonymous_klass_name(const InstanceKlass* ik, bool leakp) {
|
||||
assert(ik != NULL, "invariant");
|
||||
assert(ik->is_unsafe_anonymous(), "invariant");
|
||||
const uintptr_t hash = unsafe_anonymous_klass_name_hash(ik);
|
||||
if (hash == last_anonymous_hash) {
|
||||
assert(last_anonymous_id != 0, "invariant");
|
||||
return last_anonymous_id;
|
||||
}
|
||||
last_anonymous_hash = hash;
|
||||
const CStringEntry* const entry = _cstring_table->lookup_only(hash);
|
||||
last_anonymous_id = entry != NULL ? entry->id() : mark(hash, create_unsafe_anonymous_klass_symbol(ik, hash), leakp);
|
||||
return last_anonymous_id;
|
||||
const char* const anonymous_klass_symbol = create_unsafe_anonymous_klass_symbol(ik, hash);
|
||||
return mark(hash, anonymous_klass_symbol, leakp);
|
||||
}
|
||||
|
||||
traceid JfrSymbolId::mark(const Klass* k, bool leakp) {
|
||||
@ -249,23 +246,20 @@ traceid JfrSymbolId::mark(const Klass* k, bool leakp) {
|
||||
return symbol_id;
|
||||
}
|
||||
|
||||
static void reset_symbol_caches() {
|
||||
last_anonymous_hash = 0;
|
||||
last_symbol_hash = 0;
|
||||
last_cstring_hash = 0;
|
||||
}
|
||||
|
||||
JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_id(new JfrSymbolId()),
|
||||
_klass_list(NULL),
|
||||
_total_count(0) {
|
||||
_klass_list(NULL),
|
||||
_total_count(0) {
|
||||
initialize(class_unload);
|
||||
assert(_klass_list != NULL, "invariant");
|
||||
}
|
||||
|
||||
static const size_t initial_class_list_size = 200;
|
||||
|
||||
void JfrArtifactSet::initialize(bool class_unload) {
|
||||
void JfrArtifactSet::initialize(bool class_unload, bool clear /* false */) {
|
||||
assert(_symbol_id != NULL, "invariant");
|
||||
if (clear) {
|
||||
_symbol_id->clear();
|
||||
}
|
||||
_symbol_id->set_class_unload(class_unload);
|
||||
_total_count = 0;
|
||||
// resource allocation
|
||||
@ -273,13 +267,8 @@ void JfrArtifactSet::initialize(bool class_unload) {
|
||||
}
|
||||
|
||||
JfrArtifactSet::~JfrArtifactSet() {
|
||||
clear();
|
||||
delete _symbol_id;
|
||||
}
|
||||
|
||||
void JfrArtifactSet::clear() {
|
||||
reset_symbol_caches();
|
||||
_symbol_id->clear();
|
||||
delete _symbol_id;
|
||||
// _klass_list will be cleared by a ResourceMark
|
||||
}
|
||||
|
||||
|
@ -222,6 +222,8 @@ class JfrSymbolId : public JfrCHeapObj {
|
||||
CStringTable* _cstring_table;
|
||||
const SymbolEntry* _sym_list;
|
||||
const CStringEntry* _cstring_list;
|
||||
const Symbol* _sym_query;
|
||||
const char* _cstring_query;
|
||||
traceid _symbol_id_counter;
|
||||
bool _class_unload;
|
||||
|
||||
@ -300,9 +302,7 @@ class JfrArtifactSet : public JfrCHeapObj {
|
||||
~JfrArtifactSet();
|
||||
|
||||
// caller needs ResourceMark
|
||||
void initialize(bool class_unload);
|
||||
void clear();
|
||||
|
||||
void initialize(bool class_unload, bool clear = false);
|
||||
|
||||
traceid mark(uintptr_t hash, const Symbol* sym, bool leakp);
|
||||
traceid mark(const Klass* klass, bool leakp);
|
||||
|
115
test/jdk/jdk/jfr/jvm/TestClearStaleConstants.java
Normal file
115
test/jdk/jdk/jfr/jvm/TestClearStaleConstants.java
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package jdk.jfr.jvm;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.consumer.RecordedClass;
|
||||
import jdk.jfr.consumer.RecordedClassLoader;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.internal.JVM;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.jfr.EventNames;
|
||||
import jdk.test.lib.jfr.Events;
|
||||
import jdk.test.lib.jfr.TestClassLoader;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8231081
|
||||
* @key jfr
|
||||
* @requires vm.hasJFR
|
||||
* @modules jdk.jfr/jdk.jfr.internal
|
||||
* @library /test/lib /test/jdk
|
||||
* @run main/othervm -Xlog:class+unload -Xlog:gc -Xmx16m jdk.jfr.jvm.TestClearStaleConstants
|
||||
*/
|
||||
|
||||
/**
|
||||
* System.gc() will trigger class unloading if -XX:+ExplicitGCInvokesConcurrent is NOT set.
|
||||
* If this flag is set G1 will never unload classes on System.gc() and
|
||||
* As far as the "jfr" key guarantees no VM flags are set from the outside
|
||||
* it should be enough with System.gc().
|
||||
*/
|
||||
public final class TestClearStaleConstants {
|
||||
static class MyClass {
|
||||
}
|
||||
private final static String TEST_CLASS_NAME = "jdk.jfr.jvm.TestClearStaleConstants$MyClass";
|
||||
private final static String EVENT_NAME = EventNames.ClassDefine;
|
||||
|
||||
// to prevent the compiler to optimize away all unread writes
|
||||
public static TestClassLoader firstClassLoader;
|
||||
public static TestClassLoader secondClassLoader;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
firstClassLoader = new TestClassLoader();
|
||||
// define a class using a class loader under a recording
|
||||
Class<?> clz = recordClassDefinition(firstClassLoader);
|
||||
JVM jvm = JVM.getJVM();
|
||||
// we will now tag the defined and loaded clz as being in use (no recordings are running here)
|
||||
jvm.getClassIdNonIntrinsic(clz);
|
||||
// null out for unload to occur
|
||||
firstClassLoader = null;
|
||||
clz = null;
|
||||
// provoke unload
|
||||
System.gc();
|
||||
// try to define another class _with the same name_ using a different class loader
|
||||
secondClassLoader = new TestClassLoader();
|
||||
// this will throw a NPE for 8231081 because it will reuse the same class name
|
||||
// that symbol was marked as already serialized by the unload, but since no recordings were running
|
||||
// it was not written to any chunk. This creates a reference to a non-existing symbol, leading to an NPE (no symbol at the expected location).
|
||||
recordClassDefinition(secondClassLoader);
|
||||
}
|
||||
|
||||
private static Class<?> recordClassDefinition(TestClassLoader classLoader) throws Exception {
|
||||
try (Recording recording = new Recording()) {
|
||||
recording.enable(EVENT_NAME);
|
||||
recording.start();
|
||||
Class<?> clz = classLoader.loadClass(TEST_CLASS_NAME);
|
||||
recording.stop();
|
||||
assertClassDefineEvent(recording);
|
||||
return clz;
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertClassDefineEvent(Recording recording) throws Exception {
|
||||
boolean isAnyFound = false;
|
||||
for (RecordedEvent event : Events.fromRecording(recording)) {
|
||||
System.out.println(event);
|
||||
RecordedClass definedClass = event.getValue("definedClass");
|
||||
if (TEST_CLASS_NAME.equals(definedClass.getName())) {
|
||||
RecordedClassLoader definingClassLoader = definedClass.getClassLoader();
|
||||
String definingName = definingClassLoader.getType().getName();
|
||||
String testName = TestClassLoader.class.getName();
|
||||
String errorMsg = "Expected " + testName + ", got " + definingName;
|
||||
Asserts.assertEquals(testName, definingName, errorMsg);
|
||||
Asserts.assertFalse(isAnyFound, "Found more than 1 event");
|
||||
isAnyFound = true;
|
||||
}
|
||||
}
|
||||
Asserts.assertTrue(isAnyFound, "No events found");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user