Skip to content
Permalink
Browse files
8260589: Crash in JfrTraceIdLoadBarrier::load(_jclass*)
Co-authored-by: Denghui Dong <ddong@openjdk.org>
Reviewed-by: jbachorik, egahlin
  • Loading branch information
Markus Grönlund and Denghui Dong committed Mar 22, 2021
1 parent 42104e5 commit a9d2267f8d306522522c999ff584ccaa34c46456
@@ -41,6 +41,7 @@
#include "jfr/writers/jfrTypeWriterHost.hpp"
#include "memory/iterator.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/instanceKlass.inline.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/oop.inline.hpp"
@@ -63,6 +64,7 @@ static JfrArtifactSet* _artifacts = NULL;
static JfrArtifactClosure* _subsystem_callback = NULL;
static bool _class_unload = false;
static bool _flushpoint = false;
static bool _clear_artifacts = false;

// incremented on each rotation
static u8 checkpoint_id = 1;
@@ -83,6 +85,10 @@ static bool previous_epoch() {
return !current_epoch();
}

static bool is_initial_typeset_for_chunk() {
return _clear_artifacts && !_class_unload;
}

static bool is_complete() {
return !_artifacts->has_klass_entries() && current_epoch();
}
@@ -99,6 +105,35 @@ static traceid get_bootstrap_name(bool leakp) {
return create_symbol_id(_artifacts->bootstrap_name(leakp));
}

static const char* primitive_name(KlassPtr type_array_klass) {
switch (type_array_klass->name()->base()[1]) {
case JVM_SIGNATURE_BOOLEAN: return "boolean";
case JVM_SIGNATURE_BYTE: return "byte";
case JVM_SIGNATURE_CHAR: return "char";
case JVM_SIGNATURE_SHORT: return "short";
case JVM_SIGNATURE_INT: return "int";
case JVM_SIGNATURE_LONG: return "long";
case JVM_SIGNATURE_FLOAT: return "float";
case JVM_SIGNATURE_DOUBLE: return "double";
}
assert(false, "invalid type array klass");
return NULL;
}

static Symbol* primitive_symbol(KlassPtr type_array_klass) {
if (type_array_klass == NULL) {
// void.class
static Symbol* const void_class_name = SymbolTable::probe("void", 4);
assert(void_class_name != NULL, "invariant");
return void_class_name;
}
const char* const primitive_type_str = primitive_name(type_array_klass);
assert(primitive_type_str != NULL, "invariant");
Symbol* const primitive_type_sym = SymbolTable::probe(primitive_type_str, (int)strlen(primitive_type_str));
assert(primitive_type_sym != NULL, "invariant");
return primitive_type_sym;
}

template <typename T>
static traceid artifact_id(const T* ptr) {
assert(ptr != NULL, "invariant");
@@ -154,6 +189,11 @@ static s4 get_flags(const T* ptr) {
return ptr->access_flags().get_flags();
}

// Same as JVM_GetClassModifiers
static u4 get_primitive_flags() {
return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC;
}

static bool is_unsafe_anonymous(const Klass* klass) {
assert(klass != NULL, "invariant");
assert(!klass->is_objArray_klass(), "invariant");
@@ -225,6 +265,27 @@ static void do_klass(Klass* klass) {
_subsystem_callback->do_artifact(klass);
}


static traceid primitive_id(KlassPtr array_klass) {
if (array_klass == NULL) {
// The first klass id is reserved for the void.class.
return LAST_TYPE_ID + 1;
}
// Derive the traceid for a primitive mirror from its associated array klass (+1).
return JfrTraceId::load_raw(array_klass) + 1;
}

static void write_primitive(JfrCheckpointWriter* writer, KlassPtr type_array_klass) {
assert(writer != NULL, "invariant");
assert(_artifacts != NULL, "invariant");
writer->write(primitive_id(type_array_klass));
writer->write(cld_id(get_cld(Universe::boolArrayKlassObj()), false));
writer->write(mark_symbol(primitive_symbol(type_array_klass), false));
writer->write(package_id(Universe::boolArrayKlassObj(), false));
writer->write(get_primitive_flags());
writer->write<bool>(false);
}

static void do_loader_klass(const Klass* klass) {
if (klass != NULL && _artifacts->should_do_loader_klass(klass)) {
if (_leakp_writer != NULL) {
@@ -295,6 +356,28 @@ static void do_classloaders() {
assert(mark_stack.is_empty(), "invariant");
}

static int primitives_count = 9;

// A mirror representing a primitive class (e.g. int.class) has no reified Klass*,
// instead it has an associated TypeArrayKlass* (e.g. int[].class).
// We can use the TypeArrayKlass* as a proxy for deriving the id of the primitive class.
// The exception is the void.class, which has neither a Klass* nor a TypeArrayKlass*.
// It will use a reserved constant.
static void do_primitives() {
// Only write the primitive classes once per chunk.
if (is_initial_typeset_for_chunk()) {
write_primitive(_writer, Universe::boolArrayKlassObj());
write_primitive(_writer, Universe::byteArrayKlassObj());
write_primitive(_writer, Universe::charArrayKlassObj());
write_primitive(_writer, Universe::shortArrayKlassObj());
write_primitive(_writer, Universe::intArrayKlassObj());
write_primitive(_writer, Universe::longArrayKlassObj());
write_primitive(_writer, Universe::floatArrayKlassObj());
write_primitive(_writer, Universe::doubleArrayKlassObj());
write_primitive(_writer, NULL); // void.class
}
}

static void do_object() {
SET_TRANSIENT(vmClasses::Object_klass());
do_klass(vmClasses::Object_klass());
@@ -307,6 +390,7 @@ static void do_klasses() {
}
JfrTraceIdLoadBarrier::do_klasses(&do_klass, previous_epoch());
do_classloaders();
do_primitives();
do_object();
}

@@ -352,6 +436,11 @@ static bool write_klasses() {
_subsystem_callback = &callback;
do_klasses();
}
if (is_initial_typeset_for_chunk()) {
// Because the set of primitives is written outside the callback,
// their count is not automatically incremented.
kw.add(primitives_count);
}
if (is_complete()) {
return false;
}
@@ -979,8 +1068,6 @@ typedef Wrapper<KlassPtr, ClearArtifact> ClearKlassBits;
typedef Wrapper<MethodPtr, ClearArtifact> ClearMethodFlag;
typedef MethodIteratorHost<ClearMethodFlag, ClearKlassBits, AlwaysTrue, false> ClearKlassAndMethods;

static bool clear_artifacts = false;

static void clear_klasses_and_methods() {
ClearKlassAndMethods clear(_writer);
_artifacts->iterate_klasses(clear);
@@ -992,8 +1079,10 @@ static size_t teardown() {
if (previous_epoch()) {
clear_klasses_and_methods();
JfrKlassUnloading::clear();
clear_artifacts = true;
_clear_artifacts = true;
++checkpoint_id;
} else {
_clear_artifacts = false;
}
return total_count;
}
@@ -1006,12 +1095,11 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer
if (_artifacts == NULL) {
_artifacts = new JfrArtifactSet(class_unload);
} else {
_artifacts->initialize(class_unload, clear_artifacts);
_artifacts->initialize(class_unload, _clear_artifacts);
}
if (!_class_unload) {
JfrKlassUnloading::sort(previous_epoch());
}
clear_artifacts = false;
assert(_artifacts != NULL, "invariant");
assert(!_artifacts->has_klass_entries(), "invariant");
}
@@ -1042,7 +1130,7 @@ size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* l
void JfrTypeSet::clear() {
ResourceMark rm;
JfrKlassUnloading::clear();
clear_artifacts = true;
_clear_artifacts = true;
setup(NULL, NULL, false, false);
register_klasses();
clear_packages();
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2021, 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
@@ -52,7 +52,7 @@ static traceid atomic_inc(traceid volatile* const dest) {
}

static traceid next_class_id() {
static volatile traceid class_id_counter = LAST_TYPE_ID;
static volatile traceid class_id_counter = LAST_TYPE_ID + 1; // + 1 is for the void.class primitive
return atomic_inc(&class_id_counter) << TRACE_ID_SHIFT;
}

@@ -148,16 +148,44 @@ void JfrTraceId::assign(const ClassLoaderData* cld) {
cld->set_trace_id(next_class_loader_data_id());
}

traceid JfrTraceId::assign_primitive_klass_id() {
return next_class_id();
}

traceid JfrTraceId::assign_thread_id() {
return next_thread_id();
}

traceid JfrTraceId::load_raw(jclass jc) {
// A mirror representing a primitive class (e.g. int.class) has no reified Klass*,
// instead it has an associated TypeArrayKlass* (e.g. int[].class).
// We can use the TypeArrayKlass* as a proxy for deriving the id of the primitive class.
// The exception is the void.class, which has neither a Klass* nor a TypeArrayKlass*.
// It will use a reserved constant.
static traceid load_primitive(const oop mirror) {
assert(java_lang_Class::is_primitive(mirror), "invariant");
const Klass* const tak = java_lang_Class::array_klass_acquire(mirror);
traceid id;
if (tak == NULL) {
// The first klass id is reserved for the void.class
id = LAST_TYPE_ID + 1;
} else {
id = JfrTraceId::load_raw(tak) + 1;
}
JfrTraceIdEpoch::set_changed_tag_state();
return id;
}

traceid JfrTraceId::load(jclass jc, bool raw /* false */) {
assert(jc != NULL, "invariant");
assert(JavaThread::current()->thread_state() == _thread_in_vm, "invariant");
const oop my_oop = JNIHandles::resolve(jc);
assert(my_oop != NULL, "invariant");
return load_raw(java_lang_Class::as_Klass(my_oop));
const oop mirror = JNIHandles::resolve(jc);
assert(mirror != NULL, "invariant");
const Klass* const k = java_lang_Class::as_Klass(mirror);
return k != NULL ? (raw ? load_raw(k) : load(k)) : load_primitive(mirror);
}

traceid JfrTraceId::load_raw(jclass jc) {
return load(jc, true);
}

// used by CDS / APPCDS as part of "remove_unshareable_info"
@@ -186,6 +214,10 @@ void JfrTraceId::restore(const Klass* k) {
const traceid event_flags = k->trace_id();
// get a fresh traceid and restore the original event flags
k->set_trace_id(next_class_id() | event_flags);
if (k->is_typeArray_klass()) {
// the next id is reserved for the corresponding primitive class
next_class_id();
}
}

bool JfrTraceId::in_visible_set(const jclass jc) {
@@ -83,11 +83,12 @@ class JfrTraceId : public AllStatic {
static void assign(const ModuleEntry* module);
static void assign(const PackageEntry* package);
static void assign(const ClassLoaderData* cld);
static traceid assign_primitive_klass_id();
static traceid assign_thread_id();

// through load barrier
static traceid load(const Klass* klass);
static traceid load(jclass jc);
static traceid load(jclass jc, bool raw = false);
static traceid load(const Method* method);
static traceid load(const Klass* klass, const Method* method);
static traceid load(const ModuleEntry* module);
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2021, 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
@@ -35,10 +35,6 @@
#include "runtime/thread.inline.hpp"
#include "utilities/debug.hpp"

inline traceid JfrTraceId::load(jclass jc) {
return JfrTraceIdLoadBarrier::load(jc);
}

inline traceid JfrTraceId::load(const Klass* klass) {
return JfrTraceIdLoadBarrier::load(klass);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, 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
@@ -23,12 +23,10 @@
*/

#include "precompiled.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
#include "jfr/utilities/jfrEpochQueue.inline.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/mutexLocker.hpp"

@@ -70,11 +68,3 @@ void JfrTraceIdLoadBarrier::do_klasses(klass_callback callback, bool previous_ep
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
klass_queue().iterate(callback, previous_epoch);
}

traceid JfrTraceIdLoadBarrier::load(jclass jc) {
assert(jc != NULL, "invariant");
assert(JavaThread::current()->thread_state() == _thread_in_vm, "invariant");
const oop my_oop = JNIHandles::resolve(jc);
assert(my_oop != NULL, "invariant");
return load(java_lang_Class::as_Klass(my_oop));
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, 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
@@ -25,7 +25,6 @@
#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDLOADBARRIER_HPP
#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDLOADBARRIER_HPP

#include "jni.h"
#include "jfr/utilities/jfrTypes.hpp"
#include "memory/allocation.hpp"

@@ -78,7 +77,6 @@ class JfrTraceIdLoadBarrier : AllStatic {
static traceid load(const ClassLoaderData* cld);
static traceid load(const Klass* klass);
static traceid load(const Klass* klass, const Method* method);
static traceid load(jclass jc);
static traceid load(const Method* method);
static traceid load(const ModuleEntry* module);
static traceid load(const PackageEntry* package);
@@ -39,6 +39,7 @@
static size_t trace_id_size() { return sizeof(traceid); }

#define INIT_ID(data) JfrTraceId::assign(data)
#define ASSIGN_PRIMITIVE_CLASS_ID(data) JfrTraceId::assign_primitive_klass_id()
#define REMOVE_ID(k) JfrTraceId::remove(k);
#define REMOVE_METHOD_ID(method) JfrTraceId::remove(method);
#define RESTORE_ID(k) JfrTraceId::restore(k);
@@ -62,7 +62,7 @@ TypeArrayKlass* TypeArrayKlass::create_klass(BasicType type,
// mirror creation fails, loaded_classes_do() doesn't find
// an array class without a mirror.
null_loader_data->add_class(ak);

JFR_ONLY(ASSIGN_PRIMITIVE_CLASS_ID(ak);)
return ak;
}

Loading

1 comment on commit a9d2267

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on a9d2267 Mar 22, 2021

Please sign in to comment.