Skip to content
Permalink
Browse files
8267697: [lworld] [lw3] VM crashes during heap dump if Java heap cont…
…ains flat arrays
  • Loading branch information
Alex Menkov committed Sep 13, 2021
1 parent 3111124 commit 0c0c9e176e3be05ae46a0f8f1722aff879c8d197
@@ -40,8 +40,11 @@
#include "oops/klass.inline.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/flatArrayKlass.hpp"
#include "oops/flatArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp"
@@ -380,6 +383,22 @@ enum {
INITIAL_CLASS_COUNT = 200
};


// Inlined fields of primitive classes are dumped as identity objects and require unique object ids.
// We cannot use address of the object in container oop (as we do for identity objects)
// because the address can be the same for inlined object which contains inlined field with offset 0.
class InlinedObjectSupport : public StackObj {
friend class DumpWriter;
InlinedObjectSupport(int initial_value = 1) : _counter(initial_value) {}

int _counter;
int getId() { return _counter++; }
public:
InlinedObjectSupport save() const {
return InlinedObjectSupport(_counter);
}
};

// Supports I/O operations for a dump

class DumpWriter : public StackObj {
@@ -414,6 +433,8 @@ class DumpWriter : public StackObj {
// Returns true if we have enough room in the buffer for 'len' bytes.
bool can_write_fast(size_t len);

InlinedObjectSupport _inlined_object_support;

public:
// Takes ownership of the writer and compressor.
DumpWriter(AbstractWriter* writer, AbstractCompressor* compressor);
@@ -425,13 +446,16 @@ class DumpWriter : public StackObj {

char const* error() const { return _backend.error(); }

InlinedObjectSupport& inlined_object_support() { return _inlined_object_support; }

// writer functions
void write_raw(void* s, size_t len);
void write_u1(u1 x);
void write_u2(u2 x);
void write_u4(u4 x);
void write_u8(u8 x);
void write_objectID(oop o);
void write_inlinedObjectID(InlinedObjectSupport &inlinedObjectSupport);
void write_symbolID(Symbol* o);
void write_classID(Klass* k);
void write_id(u4 x);
@@ -538,6 +562,14 @@ void DumpWriter::write_objectID(oop o) {
#endif
}

void DumpWriter::write_inlinedObjectID(InlinedObjectSupport& inlinedObjectSupport) {
#ifdef _LP64
write_u8(inlinedObjectSupport.getId());
#else
write_u4(inlinedObjectSupport.getId());
#endif
}

void DumpWriter::write_symbolID(Symbol* s) {
address a = (address)((uintptr_t)s);
#ifdef _LP64
@@ -637,18 +669,28 @@ class DumperSupport : AllStatic {
static void dump_float(DumpWriter* writer, jfloat f);
// dump a jdouble
static void dump_double(DumpWriter* writer, jdouble d);
// dumps the raw value of the given field
static void dump_field_value(DumpWriter* writer, char type, oop obj, int offset);
// dumps the raw value of the given field; obj and offset specify the object (offset is 0 for identity objects)
// for inlined fields writed object id generated by writer->inlined_object_support()
static void dump_field_value(DumpWriter* writer, const FieldStream& fld, oop obj, int offset);
// returns the size of the static fields; also counts the static fields
static u4 get_static_fields_size(InstanceKlass* ik, u2& field_count);
// dumps static fields of the given class
static void dump_static_fields(DumpWriter* writer, Klass* k);
// dump the raw values of the instance fields of the given object
static void dump_instance_fields(DumpWriter* writer, oop o);
// dump the raw values of the instance fields of the given identity or inlined object;
// for identity objects offset is 0 and 'klass' is o->klass(),
// for inlined objects offset is the offset in the holder object, 'klass' is inlined object class
static void dump_instance_fields(DumpWriter* writer, oop o, int offset, Klass* klass);
// dump inlined instance fields of the given object (identity or inlined);
// o is the holder object, offset and klass specify flattened field (or field of flattened field, etc.);
// for identity object offset is 0 and klass is o->klass()
static void dump_inlined_instance_fields(DumpWriter* writer, oop o, int offset, Klass* klass, InlinedObjectSupport &ios);

// get the count of the instance fields for a given class
static u2 get_instance_fields_count(InstanceKlass* ik);
// dumps the definition of the instance fields for a given class
static void dump_instance_field_descriptors(DumpWriter* writer, Klass* k);
// creates HPROF_GC_INSTANCE_DUMP record for the given inlined object
static void dump_inlined_object(DumpWriter* writer, oop holder, int offset, InlineKlass* klass, InlinedObjectSupport& ios);
// creates HPROF_GC_INSTANCE_DUMP record for the given object
static void dump_instance(DumpWriter* writer, oop o);
// creates HPROF_GC_CLASS_DUMP record for the given class and each of its
@@ -659,7 +701,8 @@ class DumperSupport : AllStatic {
static void dump_basic_type_array_class(DumpWriter* writer, Klass* k);

// creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
static void dump_object_array(DumpWriter* writer, objArrayOop array);
//static void dump_object_array(DumpWriter* writer, objArrayOop array);
static void dump_object_array(DumpWriter* writer, arrayOop array);
// creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array
static void dump_prim_array(DumpWriter* writer, typeArrayOop array);
// create HPROF_FRAME record for the given method and bci
@@ -693,6 +736,7 @@ void DumperSupport:: write_header(DumpWriter* writer, hprofTag tag, u4 len) {
hprofTag DumperSupport::sig2tag(Symbol* sig) {
switch (sig->char_at(0)) {
case JVM_SIGNATURE_CLASS : return HPROF_NORMAL_OBJECT;
case JVM_SIGNATURE_INLINE_TYPE: return HPROF_NORMAL_OBJECT;
case JVM_SIGNATURE_ARRAY : return HPROF_NORMAL_OBJECT;
case JVM_SIGNATURE_BYTE : return HPROF_BYTE;
case JVM_SIGNATURE_CHAR : return HPROF_CHAR;
@@ -723,6 +767,7 @@ hprofTag DumperSupport::type2tag(BasicType type) {
u4 DumperSupport::sig2size(Symbol* sig) {
switch (sig->char_at(0)) {
case JVM_SIGNATURE_CLASS:
case JVM_SIGNATURE_INLINE_TYPE:
case JVM_SIGNATURE_ARRAY: return sizeof(address);
case JVM_SIGNATURE_BOOLEAN:
case JVM_SIGNATURE_BYTE: return 1;
@@ -765,9 +810,19 @@ void DumperSupport::dump_double(DumpWriter* writer, jdouble d) {
writer->write_u8((u8)u.l);
}

// dumps the raw value of the given field
void DumperSupport::dump_field_value(DumpWriter* writer, char type, oop obj, int offset) {
// dumps the raw value of the given field; obj and offset specify the object (offset is 0 for identity objects)
// for inlined fields writed object id generated by writer->inlined_object_support()
void DumperSupport::dump_field_value(DumpWriter* writer, const FieldStream& fld, oop obj, int offset) {
char type = fld.signature()->char_at(0);
offset += fld.offset();
switch (type) {
case JVM_SIGNATURE_INLINE_TYPE: {
if (fld.field_descriptor().is_inlined()) {
writer->write_inlinedObjectID(writer->inlined_object_support());
break;
}
}
// pass through
case JVM_SIGNATURE_CLASS :
case JVM_SIGNATURE_ARRAY : {
oop o = obj->obj_field_access<ON_UNKNOWN_OOP_REF | AS_NO_KEEPALIVE>(offset);
@@ -895,8 +950,11 @@ void DumperSupport::dump_static_fields(DumpWriter* writer, Klass* k) {
writer->write_symbolID(fld.name()); // name
writer->write_u1(sig2tag(sig)); // type

// if this changes, need to handle this properly (dump inlined objects after dump_static_fields)
assert(!fld.field_descriptor().is_inlined(), "static fields cannot be inlined");

// value
dump_field_value(writer, sig->char_at(0), ik->java_mirror(), fld.offset());
dump_field_value(writer, fld, ik->java_mirror(), 0);
}
}

@@ -926,19 +984,35 @@ void DumperSupport::dump_static_fields(DumpWriter* writer, Klass* k) {
}
}


// dump the raw values of the instance fields of the given object
void DumperSupport::dump_instance_fields(DumpWriter* writer, oop o) {
InstanceKlass* ik = InstanceKlass::cast(o->klass());
void DumperSupport::dump_instance_fields(DumpWriter* writer, oop o, int offset, Klass *klass) {
InstanceKlass* ik = InstanceKlass::cast(klass);

for (FieldStream fld(ik, false, false); !fld.eos(); fld.next()) {
if (!fld.access_flags().is_static()) {
Symbol* sig = fld.signature();
dump_field_value(writer, sig->char_at(0), o, fld.offset());
dump_field_value(writer, fld, o, offset);
}
}
}

// dumps the definition of the instance fields for a given class
void DumperSupport::dump_inlined_instance_fields(DumpWriter *writer, oop o, int offset, Klass *klass, InlinedObjectSupport &ios) {
InstanceKlass* ik = InstanceKlass::cast(klass);

assert(&ios != &writer->inlined_object_support(), "must be saved copy");

for (FieldStream fld(ik, false, false); !fld.eos(); fld.next()) {
if (!fld.access_flags().is_static()) {
if (fld.field_descriptor().is_inlined()) {
InstanceKlass* holder_klass = fld.field_descriptor().field_holder();
InlineKlass* field_klass = InlineKlass::cast(holder_klass->get_inline_type_field_klass(fld.index()));
dump_inlined_object(writer, o, offset + fld.offset(), field_klass, ios);
}
}
}
}

// gets the count of the instance fields for a given class
u2 DumperSupport::get_instance_fields_count(InstanceKlass* ik) {
u2 field_count = 0;

@@ -964,6 +1038,37 @@ void DumperSupport::dump_instance_field_descriptors(DumpWriter* writer, Klass* k
}
}

// creates HPROF_GC_INSTANCE_DUMP record for the given inlined object
void DumperSupport::dump_inlined_object(DumpWriter* writer, oop holder, int offset, InlineKlass* klass, InlinedObjectSupport& ios) {
u4 is = instance_size(klass);
u4 size = 1 + sizeof(address) + 4 + sizeof(address) + 4 + is;

writer->start_sub_record(HPROF_GC_INSTANCE_DUMP, size);
writer->write_inlinedObjectID(ios);

writer->write_u4(STACK_TRACE_ID);

// class ID
writer->write_classID(klass);

// number of bytes that follow
writer->write_u4(is);

// the object if flattened, so all fields are stored without headers
// update offset here instead of handling it in both dump_instance_fields and dump_inlined_instance_fields
offset -= klass->first_field_offset();

InlinedObjectSupport saved_ios = writer->inlined_object_support().save();

// field values
dump_instance_fields(writer, holder, offset, klass);

writer->end_sub_record();

// dump flattened fields
dump_inlined_instance_fields(writer, holder, offset, klass, saved_ios);
}

// creates HPROF_GC_INSTANCE_DUMP record for the given object
void DumperSupport::dump_instance(DumpWriter* writer, oop o) {
InstanceKlass* ik = InstanceKlass::cast(o->klass());
@@ -980,10 +1085,15 @@ void DumperSupport::dump_instance(DumpWriter* writer, oop o) {
// number of bytes that follow
writer->write_u4(is);

InlinedObjectSupport saved_ios = writer->inlined_object_support().save();

// field values
dump_instance_fields(writer, o);
dump_instance_fields(writer, o, 0, o->klass());

writer->end_sub_record();

// dump inlined fields
dump_inlined_instance_fields(writer, o, 0, o->klass(), saved_ios);
}

// creates HPROF_GC_CLASS_DUMP record for the given class and each of
@@ -1078,8 +1188,8 @@ void DumperSupport::dump_class_and_array_classes(DumpWriter* writer, Klass* k) {
// creates HPROF_GC_CLASS_DUMP record for a given primitive array
// class (and each multi-dimensional array class too)
void DumperSupport::dump_basic_type_array_class(DumpWriter* writer, Klass* k) {
// array classes
while (k != NULL) {
// array classes
while (k != NULL) {
Klass* klass = k;

u4 size = 1 + sizeof(address) + 4 + 6 * sizeof(address) + 4 + 2 + 2 + 2;
@@ -1114,12 +1224,12 @@ void DumperSupport::dump_basic_type_array_class(DumpWriter* writer, Klass* k) {
// which means we need to truncate arrays that are too long.
int DumperSupport::calculate_array_max_length(DumpWriter* writer, arrayOop array, short header_size) {
BasicType type = ArrayKlass::cast(array->klass())->element_type();
assert(type >= T_BOOLEAN && type <= T_OBJECT, "invalid array element type");
assert((type >= T_BOOLEAN && type <= T_OBJECT) || type == T_INLINE_TYPE, "invalid array element type");

int length = array->length();

int type_size;
if (type == T_OBJECT) {
if (type == T_OBJECT || type == T_INLINE_TYPE) {
type_size = sizeof(address);
} else {
type_size = type2aelembytes(type);
@@ -1139,7 +1249,7 @@ int DumperSupport::calculate_array_max_length(DumpWriter* writer, arrayOop array
}

// creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop array) {
void DumperSupport::dump_object_array(DumpWriter* writer, arrayOop array) {
// sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID) + sizeof(classID)
short header_size = 1 + 2 * 4 + 2 * sizeof(address);
int length = calculate_array_max_length(writer, array, header_size);
@@ -1153,20 +1263,43 @@ void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop array) {
// array class ID
writer->write_classID(array->klass());

// [id]* elements
for (int index = 0; index < length; index++) {
oop o = array->obj_at(index);
if (o != NULL && log_is_enabled(Debug, cds, heap) && mask_dormant_archived_object(o) == NULL) {
ResourceMark rm;
log_debug(cds, heap)("skipped dormant archived object " INTPTR_FORMAT " (%s) referenced by " INTPTR_FORMAT " (%s)",
p2i(o), o->klass()->external_name(),
p2i(array), array->klass()->external_name());
InlinedObjectSupport ios = writer->inlined_object_support().save();
if (array->is_objArray()) {
// [id]* elements
objArrayOop objArray = objArrayOop(array);
for (int index = 0; index < length; index++) {
oop o = objArray->obj_at(index);
if (o != NULL && log_is_enabled(Debug, cds, heap) && mask_dormant_archived_object(o) == NULL) {
ResourceMark rm;
log_debug(cds, heap)("skipped dormant archived object " INTPTR_FORMAT " (%s) referenced by " INTPTR_FORMAT " (%s)",
p2i(o), o->klass()->external_name(),
p2i(array), array->klass()->external_name());
}
o = mask_dormant_archived_object(o);
writer->write_objectID(o);
}
} else { // flatArray
// [id]* elements
flatArrayOop flatArray = flatArrayOop(array);
for (int index = 0; index < length; index++) {
writer->write_inlinedObjectID(writer->inlined_object_support());
}
o = mask_dormant_archived_object(o);
writer->write_objectID(o);
}

writer->end_sub_record();

if (array->is_flatArray()) {
flatArrayOop flatArray = flatArrayOop(array);
FlatArrayKlass* vaklass = FlatArrayKlass::cast(flatArray->klass());
InlineKlass* vklass = vaklass->element_klass();
for (int index = 0; index < length; index++) {
// need offset in the holder to read inlined object. calculate it from flatArrayOop::value_at_addr()
int offset = (int)((address)flatArray->value_at_addr(index, vaklass->layout_helper())
- cast_from_oop<address>(flatArray));

dump_inlined_object(writer, flatArray, offset, vklass, ios);
}
}
}

#define WRITE_ARRAY(Array, Type, Size, Length) \
@@ -1437,9 +1570,9 @@ void HeapObjectDumper::do_object(oop o) {
if (o->is_instance()) {
// create a HPROF_GC_INSTANCE record for each object
DumperSupport::dump_instance(writer(), o);
} else if (o->is_objArray()) {
} else if (o->is_objArray() || o->is_flatArray()) {
// create a HPROF_GC_OBJ_ARRAY_DUMP record for each object array
DumperSupport::dump_object_array(writer(), objArrayOop(o));
DumperSupport::dump_object_array(writer(), arrayOop(o));
} else if (o->is_typeArray()) {
// create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array
DumperSupport::dump_prim_array(writer(), typeArrayOop(o));

0 comments on commit 0c0c9e1

Please sign in to comment.