< prev index next >

src/hotspot/share/services/heapDumper.cpp

Print this page
@@ -38,12 +38,15 @@
  #include "memory/resourceArea.hpp"
  #include "memory/universe.hpp"
  #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"
  #include "runtime/jniHandles.hpp"
  #include "runtime/os.hpp"

@@ -378,10 +381,26 @@
  enum {
    STACK_TRACE_ID = 1,
    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 AbstractDumpWriter;
+     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
  // Base class for dump and parallel dump
  class AbstractDumpWriter : public StackObj {
   protected:
    enum {

@@ -408,10 +427,13 @@
    // Can be called if we have enough room in the buffer.
    void write_fast(void* s, size_t len);
  
    // 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:
    AbstractDumpWriter() :
      _buffer(NULL),
      _size(io_buffer_max_size),
      _pos(0),

@@ -419,18 +441,21 @@
  
    // total number of bytes written to the disk
    virtual julong bytes_written() const = 0;
    virtual char const* error() const = 0;
  
+   InlinedObjectSupport& inlined_object_support() { return _inlined_object_support; }
+ 
    size_t position() const                       { return _pos; }
    // writer functions
    virtual 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);
  
    // Start a new sub-record. Starts a new heap dump segment if needed.

@@ -518,10 +543,18 @@
  #else
    write_u4((u4)a);
  #endif
  }
  
+ void AbstractDumpWriter::write_inlinedObjectID(InlinedObjectSupport& inlinedObjectSupport) {
+ #ifdef _LP64
+   write_u8(inlinedObjectSupport.getId());
+ #else
+   write_u4(inlinedObjectSupport.getId());
+ #endif
+ }
+ 
  void AbstractDumpWriter::write_symbolID(Symbol* s) {
    address a = (address)((uintptr_t)s);
  #ifdef _LP64
    write_u8((u8)a);
  #else

@@ -897,33 +930,43 @@
  
    // dump a jfloat
    static void dump_float(AbstractDumpWriter* writer, jfloat f);
    // dump a jdouble
    static void dump_double(AbstractDumpWriter* writer, jdouble d);
-   // dumps the raw value of the given field
-   static void dump_field_value(AbstractDumpWriter* 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(AbstractDumpWriter* 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(AbstractDumpWriter* writer, Klass* k);
-   // dump the raw values of the instance fields of the given object
-   static void dump_instance_fields(AbstractDumpWriter* 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(AbstractDumpWriter* 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(AbstractDumpWriter* 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(AbstractDumpWriter* writer, Klass* k);
+   // creates HPROF_GC_INSTANCE_DUMP record for the given inlined object
+   static void dump_inlined_object(AbstractDumpWriter* writer, oop holder, int offset, InlineKlass* klass, InlinedObjectSupport& ios);
    // creates HPROF_GC_INSTANCE_DUMP record for the given object
    static void dump_instance(AbstractDumpWriter* writer, oop o);
    // creates HPROF_GC_CLASS_DUMP record for the given class and each of its
    // array classes
    static void dump_class_and_array_classes(AbstractDumpWriter* writer, Klass* k);
    // creates HPROF_GC_CLASS_DUMP record for a given primitive array
    // class (and each multi-dimensional array class too)
    static void dump_basic_type_array_class(AbstractDumpWriter* writer, Klass* k);
  
    // creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
-   static void dump_object_array(AbstractDumpWriter* writer, objArrayOop array);
+   static void dump_object_array(AbstractDumpWriter* writer, arrayOop array);
    // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array
    static void dump_prim_array(AbstractDumpWriter* writer, typeArrayOop array);
    // create HPROF_FRAME record for the given method and bci
    static void dump_stack_frame(AbstractDumpWriter* writer, int frame_serial_num, int class_serial_num, Method* m, int bci);
  

@@ -953,10 +996,11 @@
  
  // returns hprof tag for the given type signature
  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;
      case JVM_SIGNATURE_FLOAT    : return HPROF_FLOAT;
      case JVM_SIGNATURE_DOUBLE   : return HPROF_DOUBLE;

@@ -983,10 +1027,11 @@
  }
  
  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;
      case JVM_SIGNATURE_SHORT:
      case JVM_SIGNATURE_CHAR: return 2;

@@ -1025,13 +1070,23 @@
      u.d = (double)d;
    }
    writer->write_u8((u8)u.l);
  }
  
- // dumps the raw value of the given field
- void DumperSupport::dump_field_value(AbstractDumpWriter* 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(AbstractDumpWriter* 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);
        if (o != NULL && log_is_enabled(Debug, cds, heap) && mask_dormant_archived_object(o) == NULL) {
          ResourceMark rm;

@@ -1155,12 +1210,15 @@
        Symbol* sig = fld.signature();
  
        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);
      }
    }
  
    // Add resolved_references for each class that has them
    oop resolved_references = ik->constants()->resolved_references_or_null();

@@ -1186,23 +1244,39 @@
      writer->write_u1(sig2tag(vmSymbols::int_array_signature())); // type
      writer->write_objectID(init_lock);
    }
  }
  
+ 
  // dump the raw values of the instance fields of the given object
- void DumperSupport::dump_instance_fields(AbstractDumpWriter* writer, oop o) {
-   InstanceKlass* ik = InstanceKlass::cast(o->klass());
+ void DumperSupport::dump_instance_fields(AbstractDumpWriter* 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(AbstractDumpWriter *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;
  
    for (FieldStream fldc(ik, true, true); !fldc.eos(); fldc.next()) {
      if (!fldc.access_flags().is_static()) field_count++;

@@ -1224,10 +1298,41 @@
        writer->write_u1(sig2tag(sig));       // type
      }
    }
  }
  
+ // creates HPROF_GC_INSTANCE_DUMP record for the given inlined object
+ void DumperSupport::dump_inlined_object(AbstractDumpWriter* 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(AbstractDumpWriter* writer, oop o) {
    InstanceKlass* ik = InstanceKlass::cast(o->klass());
    u4 is = instance_size(ik);
    u4 size = 1 + sizeof(address) + 4 + sizeof(address) + 4 + is;

@@ -1240,14 +1345,19 @@
    writer->write_classID(ik);
  
    // 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
  // its array classes
  void DumperSupport::dump_class_and_array_classes(AbstractDumpWriter* writer, Klass* k) {

@@ -1303,11 +1413,11 @@
    dump_instance_field_descriptors(writer, ik);
  
    writer->end_sub_record();
  
    // array classes
-   k = ik->array_klass_or_null();
+   k = k->array_klass_or_null();
    while (k != NULL) {
      assert(k->is_objArray_klass(), "not an ObjArrayKlass");
  
      u4 size = 1 + sizeof(address) + 4 + 6 * sizeof(address) + 4 + 2 + 2 + 2;
      writer->start_sub_record(HPROF_GC_CLASS_DUMP, size);

@@ -1338,12 +1448,12 @@
  }
  
  // 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(AbstractDumpWriter* 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;
      writer->start_sub_record(HPROF_GC_CLASS_DUMP, size);
      writer->write_classID(klass);

@@ -1374,16 +1484,16 @@
  
  // Hprof uses an u4 as record length field,
  // which means we need to truncate arrays that are too long.
  int DumperSupport::calculate_array_max_length(AbstractDumpWriter* 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);
    }
  

@@ -1399,11 +1509,11 @@
    }
    return length;
  }
  
  // creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
- void DumperSupport::dump_object_array(AbstractDumpWriter* writer, objArrayOop array) {
+ void DumperSupport::dump_object_array(AbstractDumpWriter* 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);
    u4 size = header_size + length * sizeof(address);
  

@@ -1413,24 +1523,47 @@
    writer->write_u4(length);
  
    // 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) \
    for (int i = 0; i < Length; i++) { writer->write_##Size((Size)Array->Type##_at(i)); }
  

@@ -1769,13 +1902,13 @@
    }
  
    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));
    }
  }
< prev index next >