Skip to content

Commit

Permalink
8260589
Browse files Browse the repository at this point in the history
  • Loading branch information
mgronlun committed Feb 26, 2021
1 parent c54724d commit 2d08073
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 11 deletions.
101 changes: 95 additions & 6 deletions src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp
Expand Up @@ -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"
Expand All @@ -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;
Expand All @@ -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();
}
Expand All @@ -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");
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -225,6 +265,28 @@ 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 int 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);
return 1;
}

static void do_loader_klass(const Klass* klass) {
if (klass != NULL && _artifacts->should_do_loader_klass(klass)) {
if (_leakp_writer != NULL) {
Expand Down Expand Up @@ -295,6 +357,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());
Expand All @@ -307,6 +391,7 @@ static void do_klasses() {
}
JfrTraceIdLoadBarrier::do_klasses(&do_klass, previous_epoch());
do_classloaders();
do_primitives();
do_object();
}

Expand Down Expand Up @@ -352,6 +437,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;
}
Expand Down Expand Up @@ -979,8 +1069,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);
Expand All @@ -992,8 +1080,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;
}
Expand All @@ -1006,12 +1096,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");
}
Expand Down Expand Up @@ -1042,7 +1131,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();
Expand Down
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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();
}
Expand Down
Expand Up @@ -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();

// through load barrier
Expand Down
Expand Up @@ -71,10 +71,30 @@ void JfrTraceIdLoadBarrier::do_klasses(klass_callback callback, bool previous_ep
klass_queue().iterate(callback, previous_epoch);
}

// 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 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));
const oop mirror = JNIHandles::resolve(jc);
assert(mirror != NULL, "invariant");
const Klass* const k = java_lang_Class::as_Klass(mirror);
return k != NULL ? load(k) : load_primitive(mirror);
}
1 change: 1 addition & 0 deletions src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/oops/typeArrayKlass.cpp
Expand Up @@ -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;
}

Expand Down
86 changes: 86 additions & 0 deletions test/jdk/jdk/jfr/jvm/TestPrimitiveClasses.java
@@ -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());
}
}

0 comments on commit 2d08073

Please sign in to comment.