Skip to content

Commit 1d204c5

Browse files
D-D-HRealCLanger
authored andcommitted
8260589: Crash in JfrTraceIdLoadBarrier::load(_jclass*)
Reviewed-by: clanger, mgronlun Backport-of: a9d2267
1 parent c1deb0c commit 1d204c5

File tree

6 files changed

+215
-13
lines changed

6 files changed

+215
-13
lines changed

src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "jfr/writers/jfrTypeWriterHost.hpp"
4040
#include "memory/iterator.hpp"
4141
#include "memory/resourceArea.hpp"
42+
#include "memory/universe.hpp"
4243
#include "oops/instanceKlass.hpp"
4344
#include "oops/objArrayKlass.hpp"
4445
#include "oops/oop.inline.hpp"
@@ -60,6 +61,7 @@ static JfrArtifactSet* _artifacts = NULL;
6061
static JfrArtifactClosure* _subsystem_callback = NULL;
6162
static bool _class_unload = false;
6263
static bool _flushpoint = false;
64+
static bool _clear_artifacts = false;
6365

6466
// incremented on each rotation
6567
static u8 checkpoint_id = 1;
@@ -80,6 +82,10 @@ static bool previous_epoch() {
8082
return !current_epoch();
8183
}
8284

85+
static bool is_initial_typeset_for_chunk() {
86+
return _clear_artifacts && !_class_unload;
87+
}
88+
8389
static bool is_complete() {
8490
return !_artifacts->has_klass_entries() && current_epoch();
8591
}
@@ -96,6 +102,35 @@ static traceid get_bootstrap_name(bool leakp) {
96102
return create_symbol_id(_artifacts->bootstrap_name(leakp));
97103
}
98104

105+
static const char* primitive_name(KlassPtr type_array_klass) {
106+
switch (type_array_klass->name()->base()[1]) {
107+
case JVM_SIGNATURE_BOOLEAN: return "boolean";
108+
case JVM_SIGNATURE_BYTE: return "byte";
109+
case JVM_SIGNATURE_CHAR: return "char";
110+
case JVM_SIGNATURE_SHORT: return "short";
111+
case JVM_SIGNATURE_INT: return "int";
112+
case JVM_SIGNATURE_LONG: return "long";
113+
case JVM_SIGNATURE_FLOAT: return "float";
114+
case JVM_SIGNATURE_DOUBLE: return "double";
115+
}
116+
assert(false, "invalid type array klass");
117+
return NULL;
118+
}
119+
120+
static Symbol* primitive_symbol(KlassPtr type_array_klass) {
121+
if (type_array_klass == NULL) {
122+
// void.class
123+
static Symbol* const void_class_name = SymbolTable::probe("void", 4);
124+
assert(void_class_name != NULL, "invariant");
125+
return void_class_name;
126+
}
127+
const char* const primitive_type_str = primitive_name(type_array_klass);
128+
assert(primitive_type_str != NULL, "invariant");
129+
Symbol* const primitive_type_sym = SymbolTable::probe(primitive_type_str, (int)strlen(primitive_type_str));
130+
assert(primitive_type_sym != NULL, "invariant");
131+
return primitive_type_sym;
132+
}
133+
99134
template <typename T>
100135
static traceid artifact_id(const T* ptr) {
101136
assert(ptr != NULL, "invariant");
@@ -152,6 +187,11 @@ static s4 get_flags(const T* ptr) {
152187
return ptr->access_flags().get_flags();
153188
}
154189

190+
// Same as JVM_GetClassModifiers
191+
static u4 get_primitive_flags() {
192+
return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC;
193+
}
194+
155195
static bool is_unsafe_anonymous(const Klass* klass) {
156196
assert(klass != NULL, "invariant");
157197
return klass->is_instance_klass() && ((const InstanceKlass*)klass)->is_anonymous();
@@ -260,12 +300,54 @@ static void do_klass(Klass* klass) {
260300
do_implied(klass);
261301
}
262302

303+
static traceid primitive_id(KlassPtr array_klass) {
304+
if (array_klass == NULL) {
305+
// The first klass id is reserved for the void.class.
306+
return MaxJfrEventId + 101;
307+
}
308+
// Derive the traceid for a primitive mirror from its associated array klass (+1).
309+
return JfrTraceId::get(array_klass) + 1;
310+
}
311+
312+
static void write_primitive(JfrCheckpointWriter* writer, KlassPtr type_array_klass) {
313+
assert(writer != NULL, "invariant");
314+
assert(_artifacts != NULL, "invariant");
315+
writer->write(primitive_id(type_array_klass));
316+
writer->write(cld_id(get_cld(Universe::boolArrayKlassObj()), false));
317+
writer->write(mark_symbol(primitive_symbol(type_array_klass), false));
318+
writer->write(package_id(Universe::boolArrayKlassObj(), false));
319+
writer->write(get_primitive_flags());
320+
}
321+
322+
static int primitives_count = 9;
323+
324+
// A mirror representing a primitive class (e.g. int.class) has no reified Klass*,
325+
// instead it has an associated TypeArrayKlass* (e.g. int[].class).
326+
// We can use the TypeArrayKlass* as a proxy for deriving the id of the primitive class.
327+
// The exception is the void.class, which has neither a Klass* nor a TypeArrayKlass*.
328+
// It will use a reserved constant.
329+
static void do_primitives() {
330+
// Only write the primitive classes once per chunk.
331+
if (is_initial_typeset_for_chunk()) {
332+
write_primitive(_writer, Universe::boolArrayKlassObj());
333+
write_primitive(_writer, Universe::byteArrayKlassObj());
334+
write_primitive(_writer, Universe::charArrayKlassObj());
335+
write_primitive(_writer, Universe::shortArrayKlassObj());
336+
write_primitive(_writer, Universe::intArrayKlassObj());
337+
write_primitive(_writer, Universe::longArrayKlassObj());
338+
write_primitive(_writer, Universe::singleArrayKlassObj());
339+
write_primitive(_writer, Universe::doubleArrayKlassObj());
340+
write_primitive(_writer, NULL); // void.class
341+
}
342+
}
343+
263344
static void do_klasses() {
264345
if (_class_unload) {
265346
ClassLoaderDataGraph::classes_unloading_do(&do_unloaded_klass);
266347
return;
267348
}
268349
ClassLoaderDataGraph::classes_do(&do_klass);
350+
do_primitives();
269351
}
270352

271353
typedef SerializePredicate<KlassPtr> KlassPredicate;
@@ -310,6 +392,11 @@ static bool write_klasses() {
310392
_subsystem_callback = &callback;
311393
do_klasses();
312394
}
395+
if (is_initial_typeset_for_chunk()) {
396+
// Because the set of primitives is written outside the callback,
397+
// their count is not automatically incremented.
398+
kw.add(primitives_count);
399+
}
313400
if (is_complete()) {
314401
return false;
315402
}
@@ -887,10 +974,8 @@ static void write_symbols() {
887974
_artifacts->tally(sw);
888975
}
889976

890-
static bool clear_artifacts = false;
891-
892977
void JfrTypeSet::clear() {
893-
clear_artifacts = true;
978+
_clear_artifacts = true;
894979
}
895980

896981
typedef Wrapper<KlassPtr, ClearArtifact> ClearKlassBits;
@@ -904,8 +989,10 @@ static size_t teardown() {
904989
assert(_writer != NULL, "invariant");
905990
ClearKlassAndMethods clear(_writer);
906991
_artifacts->iterate_klasses(clear);
907-
JfrTypeSet::clear();
992+
_clear_artifacts = true;
908993
++checkpoint_id;
994+
} else {
995+
_clear_artifacts = false;
909996
}
910997
return total_count;
911998
}
@@ -917,9 +1004,8 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer
9171004
if (_artifacts == NULL) {
9181005
_artifacts = new JfrArtifactSet(class_unload);
9191006
} else {
920-
_artifacts->initialize(class_unload, clear_artifacts);
1007+
_artifacts->initialize(class_unload, _clear_artifacts);
9211008
}
922-
clear_artifacts = false;
9231009
assert(_artifacts != NULL, "invariant");
9241010
assert(!_artifacts->has_klass_entries(), "invariant");
9251011
}

src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* 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) {
5252
}
5353

5454
static traceid next_class_id() {
55-
static volatile traceid class_id_counter = MaxJfrEventId + 100;
55+
static volatile traceid class_id_counter = MaxJfrEventId + 101; // + 101 is for the void.class primitive
5656
return atomic_inc(&class_id_counter) << TRACE_ID_SHIFT;
5757
}
5858

@@ -148,6 +148,10 @@ void JfrTraceId::assign(const ClassLoaderData* cld) {
148148
cld->set_trace_id(next_class_loader_data_id());
149149
}
150150

151+
traceid JfrTraceId::assign_primitive_klass_id() {
152+
return next_class_id();
153+
}
154+
151155
traceid JfrTraceId::assign_thread_id() {
152156
return next_thread_id();
153157
}
@@ -171,6 +175,10 @@ void JfrTraceId::restore(const Klass* k) {
171175
const traceid event_flags = k->trace_id();
172176
// get a fresh traceid and restore the original event flags
173177
k->set_trace_id(next_class_id() | event_flags);
178+
if (k->is_typeArray_klass()) {
179+
// the next id is reserved for the corresponding primitive class
180+
next_class_id();
181+
}
174182
}
175183

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

184-
traceid JfrTraceId::use(jclass jc) {
192+
// A mirror representing a primitive class (e.g. int.class) has no reified Klass*,
193+
// instead it has an associated TypeArrayKlass* (e.g. int[].class).
194+
// We can use the TypeArrayKlass* as a proxy for deriving the id of the primitive class.
195+
// The exception is the void.class, which has neither a Klass* nor a TypeArrayKlass*.
196+
// It will use a reserved constant.
197+
static traceid load_primitive(const oop mirror) {
198+
assert(java_lang_Class::is_primitive(mirror), "invariant");
199+
const Klass* const tak = java_lang_Class::array_klass_acquire(mirror);
200+
traceid id;
201+
if (tak == NULL) {
202+
// The first klass id is reserved for the void.class
203+
id = MaxJfrEventId + 101;
204+
} else {
205+
id = JfrTraceId::get(tak) + 1;
206+
}
207+
return id;
208+
}
209+
210+
traceid JfrTraceId::use(jclass jc, bool raw /* false */) {
185211
assert(jc != NULL, "invariant");
186212
assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_vm, "invariant");
187-
const oop my_oop = JNIHandles::resolve(jc);
188-
assert(my_oop != NULL, "invariant");
189-
return use(java_lang_Class::as_Klass(my_oop));
213+
const oop mirror = JNIHandles::resolve(jc);
214+
assert(mirror != NULL, "invariant");
215+
const Klass* const k = java_lang_Class::as_Klass(mirror);
216+
return k != NULL ? (raw ? get(k) : use(k)) : load_primitive(mirror);
190217
}
191218

192219
bool JfrTraceId::in_visible_set(const jclass jc) {

src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class JfrTraceId : public AllStatic {
8383
static void assign(const ModuleEntry* module);
8484
static void assign(const PackageEntry* package);
8585
static void assign(const ClassLoaderData* cld);
86+
static traceid assign_primitive_klass_id();
8687
static traceid assign_thread_id();
8788

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

9293
// tag construct as used, returns pre-tagged traceid
9394
static traceid use(const Klass* klass);
94-
static traceid use(jclass jc);
95+
static traceid use(jclass jc, bool raw = false);
9596
static traceid use(const Method* method);
9697
static traceid use(const Klass* klass, const Method* method);
9798
static traceid use(const ModuleEntry* module);

src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
static size_t trace_id_size() { return sizeof(traceid); }
4040

4141
#define INIT_ID(data) JfrTraceId::assign(data)
42+
#define ASSIGN_PRIMITIVE_CLASS_ID(data) JfrTraceId::assign_primitive_klass_id()
4243
#define REMOVE_ID(k) JfrTraceId::remove(k);
4344
#define RESTORE_ID(k) JfrTraceId::restore(k);
4445

src/hotspot/share/oops/typeArrayKlass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ TypeArrayKlass* TypeArrayKlass::create_klass(BasicType type,
7070
// including classes in the bootstrap (NULL) class loader.
7171
// GC walks these as strong roots.
7272
null_loader_data->add_class(ak);
73+
JFR_ONLY(ASSIGN_PRIMITIVE_CLASS_ID(ak);)
7374

7475
// Call complete_create_array_klass after all instance variables have been initialized.
7576
complete_create_array_klass(ak, ak->super(), ModuleEntryTable::javabase_moduleEntry(), CHECK_NULL);
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2021, Alibaba Group Holding Limited. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package jdk.jfr.jvm;
25+
26+
import java.util.List;
27+
28+
import jdk.jfr.Event;
29+
import jdk.jfr.Recording;
30+
import jdk.jfr.consumer.RecordedClass;
31+
import jdk.jfr.consumer.RecordedEvent;
32+
import jdk.test.lib.Asserts;
33+
import jdk.test.lib.jfr.Events;
34+
35+
/**
36+
* @test TestPrimitiveClasses
37+
* @key jfr
38+
* @requires vm.hasJFR
39+
* @library /test/lib
40+
* @run main/othervm jdk.jfr.jvm.TestPrimitiveClasses
41+
*/
42+
public class TestPrimitiveClasses {
43+
44+
private static class MyEvent extends Event {
45+
Class<?> booleanClass = boolean.class;
46+
Class<?> charClass = char.class;
47+
Class<?> floatClass = float.class;
48+
Class<?> doubleClass = double.class;
49+
Class<?> byteClass = byte.class;
50+
Class<?> shortClass = short.class;
51+
Class<?> intClass = int.class;
52+
Class<?> longClass = long.class;
53+
Class<?> voidClass = void.class;
54+
}
55+
56+
public static void main(String[] args) throws Exception {
57+
try (Recording r = new Recording()) {
58+
r.enable(MyEvent.class);
59+
r.start();
60+
MyEvent myEvent = new MyEvent();
61+
myEvent.commit();
62+
r.stop();
63+
List<RecordedEvent> events = Events.fromRecording(r);
64+
Events.hasEvents(events);
65+
RecordedEvent event = events.get(0);
66+
System.out.println(event);
67+
testField(event, "booleanClass", boolean.class);
68+
testField(event, "charClass", char.class);
69+
testField(event, "floatClass", float.class);
70+
testField(event, "doubleClass", double.class);
71+
testField(event, "byteClass", byte.class);
72+
testField(event, "shortClass", short.class);
73+
testField(event, "intClass", int.class);
74+
testField(event, "longClass", long.class);
75+
testField(event, "voidClass", void.class);
76+
}
77+
}
78+
79+
private static void testField(RecordedEvent event, String fieldName, Class<?> expected) {
80+
Asserts.assertTrue(event.hasField(fieldName));
81+
RecordedClass classField = event.getValue(fieldName);
82+
Asserts.assertEquals(classField.getName(), expected.getName());
83+
Asserts.assertEquals(classField.getClassLoader().getName(), "bootstrap");
84+
Asserts.assertEquals(classField.getModifiers(), expected.getModifiers());
85+
}
86+
}

0 commit comments

Comments
 (0)