Skip to content
Permalink
Browse files
8260589: Crash in JfrTraceIdLoadBarrier::load(_jclass*)
Reviewed-by: clanger, mgronlun
Backport-of: a9d2267
  • Loading branch information
Denghui Dong authored and RealCLanger committed Jul 7, 2021
1 parent c1deb0c commit 1d204c554ffe969567161cc05992486ff47d346d
@@ -39,6 +39,7 @@
#include "jfr/writers/jfrTypeWriterHost.hpp"
#include "memory/iterator.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/oop.inline.hpp"
@@ -60,6 +61,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;
@@ -80,6 +82,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();
}
@@ -96,6 +102,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");
@@ -152,6 +187,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");
return klass->is_instance_klass() && ((const InstanceKlass*)klass)->is_anonymous();
@@ -260,12 +300,54 @@ static void do_klass(Klass* klass) {
do_implied(klass);
}

static traceid primitive_id(KlassPtr array_klass) {
if (array_klass == NULL) {
// The first klass id is reserved for the void.class.
return MaxJfrEventId + 101;
}
// Derive the traceid for a primitive mirror from its associated array klass (+1).
return JfrTraceId::get(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());
}

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::singleArrayKlassObj());
write_primitive(_writer, Universe::doubleArrayKlassObj());
write_primitive(_writer, NULL); // void.class
}
}

static void do_klasses() {
if (_class_unload) {
ClassLoaderDataGraph::classes_unloading_do(&do_unloaded_klass);
return;
}
ClassLoaderDataGraph::classes_do(&do_klass);
do_primitives();
}

typedef SerializePredicate<KlassPtr> KlassPredicate;
@@ -310,6 +392,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;
}
@@ -887,10 +974,8 @@ static void write_symbols() {
_artifacts->tally(sw);
}

static bool clear_artifacts = false;

void JfrTypeSet::clear() {
clear_artifacts = true;
_clear_artifacts = true;
}

typedef Wrapper<KlassPtr, ClearArtifact> ClearKlassBits;
@@ -904,8 +989,10 @@ static size_t teardown() {
assert(_writer != NULL, "invariant");
ClearKlassAndMethods clear(_writer);
_artifacts->iterate_klasses(clear);
JfrTypeSet::clear();
_clear_artifacts = true;
++checkpoint_id;
} else {
_clear_artifacts = false;
}
return total_count;
}
@@ -917,9 +1004,8 @@ 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);
}
clear_artifacts = false;
assert(_artifacts != NULL, "invariant");
assert(!_artifacts->has_klass_entries(), "invariant");
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2018, 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 = MaxJfrEventId + 100;
static volatile traceid class_id_counter = MaxJfrEventId + 101; // + 101 is for the void.class primitive
return atomic_inc(&class_id_counter) << TRACE_ID_SHIFT;
}

@@ -148,6 +148,10 @@ 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();
}
@@ -171,6 +175,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();
}
}

traceid JfrTraceId::get(jclass jc) {
@@ -181,12 +189,31 @@ traceid JfrTraceId::get(jclass jc) {
return get(java_lang_Class::as_Klass(my_oop));
}

traceid JfrTraceId::use(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 = MaxJfrEventId + 101;
} else {
id = JfrTraceId::get(tak) + 1;
}
return id;
}

traceid JfrTraceId::use(jclass jc, bool raw /* false */) {
assert(jc != NULL, "invariant");
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_vm, "invariant");
const oop my_oop = JNIHandles::resolve(jc);
assert(my_oop != NULL, "invariant");
return use(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 ? get(k) : use(k)) : load_primitive(mirror);
}

bool JfrTraceId::in_visible_set(const jclass jc) {
@@ -83,6 +83,7 @@ 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();

static traceid get(const Klass* klass);
@@ -91,7 +92,7 @@ class JfrTraceId : public AllStatic {

// tag construct as used, returns pre-tagged traceid
static traceid use(const Klass* klass);
static traceid use(jclass jc);
static traceid use(jclass jc, bool raw = false);
static traceid use(const Method* method);
static traceid use(const Klass* klass, const Method* method);
static traceid use(const ModuleEntry* module);
@@ -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 RESTORE_ID(k) JfrTraceId::restore(k);

@@ -70,6 +70,7 @@ TypeArrayKlass* TypeArrayKlass::create_klass(BasicType type,
// including classes in the bootstrap (NULL) class loader.
// GC walks these as strong roots.
null_loader_data->add_class(ak);
JFR_ONLY(ASSIGN_PRIMITIVE_CLASS_ID(ak);)

// Call complete_create_array_klass after all instance variables have been initialized.
complete_create_array_klass(ak, ak->super(), ModuleEntryTable::javabase_moduleEntry(), CHECK_NULL);
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2021, Alibaba Group Holding Limited. 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.
*/

package jdk.jfr.jvm;

import java.util.List;

import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedEvent;
import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.Events;

/**
* @test TestPrimitiveClasses
* @key jfr
* @requires vm.hasJFR
* @library /test/lib
* @run main/othervm jdk.jfr.jvm.TestPrimitiveClasses
*/
public class TestPrimitiveClasses {

private static class MyEvent extends Event {
Class<?> booleanClass = boolean.class;
Class<?> charClass = char.class;
Class<?> floatClass = float.class;
Class<?> doubleClass = double.class;
Class<?> byteClass = byte.class;
Class<?> shortClass = short.class;
Class<?> intClass = int.class;
Class<?> longClass = long.class;
Class<?> voidClass = void.class;
}

public static void main(String[] args) throws Exception {
try (Recording r = new Recording()) {
r.enable(MyEvent.class);
r.start();
MyEvent myEvent = new MyEvent();
myEvent.commit();
r.stop();
List<RecordedEvent> events = Events.fromRecording(r);
Events.hasEvents(events);
RecordedEvent event = events.get(0);
System.out.println(event);
testField(event, "booleanClass", boolean.class);
testField(event, "charClass", char.class);
testField(event, "floatClass", float.class);
testField(event, "doubleClass", double.class);
testField(event, "byteClass", byte.class);
testField(event, "shortClass", short.class);
testField(event, "intClass", int.class);
testField(event, "longClass", long.class);
testField(event, "voidClass", void.class);
}
}

private static void testField(RecordedEvent event, String fieldName, Class<?> expected) {
Asserts.assertTrue(event.hasField(fieldName));
RecordedClass classField = event.getValue(fieldName);
Asserts.assertEquals(classField.getName(), expected.getName());
Asserts.assertEquals(classField.getClassLoader().getName(), "bootstrap");
Asserts.assertEquals(classField.getModifiers(), expected.getModifiers());
}
}

1 comment on commit 1d204c5

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on 1d204c5 Jul 7, 2021

Please sign in to comment.