Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8267697: [lworld] [lw3] VM crashes during heap dump if Java heap contains flat arrays #548

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -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));