< prev index next >
src/share/vm/services/heapDumper.cpp
Print this page
rev 9054 : 8144732: VM_HeapDumper hits assert with bad dump_len
Reviewed-by: dsamersoff
*** 48,59 ****
/*
* HPROF binary format - description copied from:
* src/share/demo/jvmti/hprof/hprof_io.c
*
*
! * header "JAVA PROFILE 1.0.1" or "JAVA PROFILE 1.0.2"
! * (0-terminated)
*
* u4 size of identifiers. Identifiers are used to represent
* UTF8 strings, objects, stack traces, etc. They usually
* have the same size as host pointers. For example, on
* Solaris and Win32, the size is 4.
--- 48,58 ----
/*
* HPROF binary format - description copied from:
* src/share/demo/jvmti/hprof/hprof_io.c
*
*
! * header "JAVA PROFILE 1.0.2" (0-terminated)
*
* u4 size of identifiers. Identifiers are used to represent
* UTF8 strings, objects, stack traces, etc. They usually
* have the same size as host pointers. For example, on
* Solaris and Win32, the size is 4.
*** 380,389 ****
--- 379,390 ----
char* _buffer; // internal buffer
size_t _size;
size_t _pos;
+ jlong _dump_start;
+
char* _error; // error message when I/O fails
void set_file_descriptor(int fd) { _fd = fd; }
int file_descriptor() const { return _fd; }
*** 403,412 ****
--- 404,417 ----
void close();
bool is_open() const { return file_descriptor() >= 0; }
void flush();
+ jlong dump_start() const { return _dump_start; }
+ void set_dump_start(jlong pos);
+ julong current_record_length();
+
// total number of bytes written to the disk
julong bytes_written() const { return _bytes_written; }
// adjust the number of bytes written to disk (used to keep the count
// of the number of bytes written in case of rewrites)
*** 444,453 ****
--- 449,459 ----
} while (_buffer == NULL && _size > 0);
assert((_size > 0 && _buffer != NULL) || (_size == 0 && _buffer == NULL), "sanity check");
_pos = 0;
_error = NULL;
_bytes_written = 0L;
+ _dump_start = (jlong)-1;
_fd = os::create_binary_file(path, false); // don't replace existing file
// if the open failed we record the error
if (_fd < 0) {
_error = (char*)os::strdup(strerror(errno));
*** 471,480 ****
--- 477,502 ----
::close(file_descriptor());
set_file_descriptor(-1);
}
}
+ // sets the dump starting position
+ void DumpWriter::set_dump_start(jlong pos) {
+ _dump_start = pos;
+ }
+
+ julong DumpWriter::current_record_length() {
+ if (is_open()) {
+ // calculate the size of the dump record
+ julong dump_end = bytes_written() + bytes_unwritten();
+ assert(dump_end == (size_t)current_offset(), "checking");
+ julong dump_len = dump_end - dump_start() - 4;
+ return dump_len;
+ }
+ return 0;
+ }
+
// write directly to the file
void DumpWriter::write_internal(void* s, size_t len) {
if (is_open()) {
const char* pos = (char*)s;
ssize_t n = 0;
*** 639,648 ****
--- 661,682 ----
static void dump_object_array(DumpWriter* writer, objArrayOop 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
static void dump_stack_frame(DumpWriter* writer, int frame_serial_num, int class_serial_num, Method* m, int bci);
+
+ // check if we need to truncate an array
+ static int calculate_array_max_length(DumpWriter* writer, arrayOop array, short header_size);
+
+ // writes a HPROF_HEAP_DUMP_SEGMENT record
+ static void write_dump_header(DumpWriter* writer);
+
+ // fixes up the length of the current dump record
+ static void write_current_dump_record_length(DumpWriter* writer);
+
+ // fixes up the current dump record and writes HPROF_HEAP_DUMP_END record
+ static void end_of_dump(DumpWriter* writer);
};
// write a header of the given type
void DumperSupport:: write_header(DumpWriter* writer, hprofTag tag, u4 len) {
writer->write_u1((u1)tag);
*** 1045,1098 ****
// get the array class for the next rank
k = klass->array_klass_or_null();
}
}
// creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop array) {
writer->write_u1(HPROF_GC_OBJ_ARRAY_DUMP);
writer->write_objectID(array);
writer->write_u4(STACK_TRACE_ID);
! writer->write_u4((u4)array->length());
// array class ID
writer->write_classID(array->klass());
// [id]* elements
! for (int index=0; index<array->length(); index++) {
oop o = array->obj_at(index);
writer->write_objectID(o);
}
}
! #define WRITE_ARRAY(Array, Type, Size) \
! for (int i=0; i<Array->length(); i++) { writer->write_##Size((Size)array->Type##_at(i)); }
!
// creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array
void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array) {
BasicType type = TypeArrayKlass::cast(array->klass())->element_type();
writer->write_u1(HPROF_GC_PRIM_ARRAY_DUMP);
writer->write_objectID(array);
writer->write_u4(STACK_TRACE_ID);
! writer->write_u4((u4)array->length());
writer->write_u1(type2tag(type));
// nothing to copy
! if (array->length() == 0) {
return;
}
// If the byte ordering is big endian then we can copy most types directly
- u4 length_in_bytes = (u4)array->length() * type2aelembytes(type);
switch (type) {
case T_INT : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, int, u4);
} else {
writer->write_raw((void*)(array->int_at_addr(0)), length_in_bytes);
}
break;
}
--- 1079,1185 ----
// get the array class for the next rank
k = klass->array_klass_or_null();
}
}
+ // 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(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");
+
+ int length = array->length();
+
+ int type_size;
+ if (type == T_OBJECT) {
+ type_size = sizeof(address);
+ } else {
+ type_size = type2aelembytes(type);
+ }
+
+ size_t length_in_bytes = (size_t)length * type_size;
+
+ // Create a new record if the current record is non-empty and the array can't fit.
+ julong current_record_length = writer->current_record_length();
+ if (current_record_length > 0 &&
+ (current_record_length + header_size + length_in_bytes) > max_juint) {
+ write_current_dump_record_length(writer);
+ write_dump_header(writer);
+
+ // We now have an empty record.
+ current_record_length = 0;
+ }
+
+ // Calculate max bytes we can use.
+ uint max_bytes = max_juint - (header_size + current_record_length);
+
+ // Array too long for the record?
+ // Calculate max length and return it.
+ if (length_in_bytes > max_bytes) {
+ length = max_bytes / type_size;
+ length_in_bytes = (size_t)length * type_size;
+
+ warning("cannot dump array of type %s[] with length %d; truncating to length %d",
+ type2name_tab[type], array->length(), length);
+ }
+ return length;
+ }
+
// creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop 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);
writer->write_u1(HPROF_GC_OBJ_ARRAY_DUMP);
writer->write_objectID(array);
writer->write_u4(STACK_TRACE_ID);
! 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);
writer->write_objectID(o);
}
}
! #define WRITE_ARRAY(Array, Type, Size, Length) \
! for (int i = 0; i < Length; i++) { writer->write_##Size((Size)array->Type##_at(i)); }
// creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array
void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array) {
BasicType type = TypeArrayKlass::cast(array->klass())->element_type();
+ // 2 * sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID)
+ short header_size = 2 * 1 + 2 * 4 + sizeof(address);
+
+ int length = calculate_array_max_length(writer, array, header_size);
+ int type_size = type2aelembytes(type);
+ u4 length_in_bytes = (u4)length * type_size;
+
writer->write_u1(HPROF_GC_PRIM_ARRAY_DUMP);
writer->write_objectID(array);
writer->write_u4(STACK_TRACE_ID);
! writer->write_u4(length);
writer->write_u1(type2tag(type));
// nothing to copy
! if (length == 0) {
return;
}
// If the byte ordering is big endian then we can copy most types directly
switch (type) {
case T_INT : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, int, u4, length);
} else {
writer->write_raw((void*)(array->int_at_addr(0)), length_in_bytes);
}
break;
}
*** 1100,1134 ****
writer->write_raw((void*)(array->byte_at_addr(0)), length_in_bytes);
break;
}
case T_CHAR : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, char, u2);
} else {
writer->write_raw((void*)(array->char_at_addr(0)), length_in_bytes);
}
break;
}
case T_SHORT : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, short, u2);
} else {
writer->write_raw((void*)(array->short_at_addr(0)), length_in_bytes);
}
break;
}
case T_BOOLEAN : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, bool, u1);
} else {
writer->write_raw((void*)(array->bool_at_addr(0)), length_in_bytes);
}
break;
}
case T_LONG : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, long, u8);
} else {
writer->write_raw((void*)(array->long_at_addr(0)), length_in_bytes);
}
break;
}
--- 1187,1221 ----
writer->write_raw((void*)(array->byte_at_addr(0)), length_in_bytes);
break;
}
case T_CHAR : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, char, u2, length);
} else {
writer->write_raw((void*)(array->char_at_addr(0)), length_in_bytes);
}
break;
}
case T_SHORT : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, short, u2, length);
} else {
writer->write_raw((void*)(array->short_at_addr(0)), length_in_bytes);
}
break;
}
case T_BOOLEAN : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, bool, u1, length);
} else {
writer->write_raw((void*)(array->bool_at_addr(0)), length_in_bytes);
}
break;
}
case T_LONG : {
if (Bytes::is_Java_byte_ordering_different()) {
! WRITE_ARRAY(array, long, u8, length);
} else {
writer->write_raw((void*)(array->long_at_addr(0)), length_in_bytes);
}
break;
}
*** 1136,1153 ****
// handle float/doubles in a special value to ensure than NaNs are
// written correctly. TO DO: Check if we can avoid this on processors that
// use IEEE 754.
case T_FLOAT : {
! for (int i=0; i<array->length(); i++) {
! dump_float( writer, array->float_at(i) );
}
break;
}
case T_DOUBLE : {
! for (int i=0; i<array->length(); i++) {
! dump_double( writer, array->double_at(i) );
}
break;
}
default : ShouldNotReachHere();
}
--- 1223,1240 ----
// handle float/doubles in a special value to ensure than NaNs are
// written correctly. TO DO: Check if we can avoid this on processors that
// use IEEE 754.
case T_FLOAT : {
! for (int i = 0; i < length; i++) {
! dump_float(writer, array->float_at(i));
}
break;
}
case T_DOUBLE : {
! for (int i = 0; i < length; i++) {
! dump_double(writer, array->double_at(i));
}
break;
}
default : ShouldNotReachHere();
}
*** 1360,1371 ****
static DumpWriter* _global_writer;
DumpWriter* _local_writer;
JavaThread* _oome_thread;
Method* _oome_constructor;
bool _gc_before_heap_dump;
- bool _is_segmented_dump;
- jlong _dump_start;
GrowableArray<Klass*>* _klass_map;
ThreadStackTrace** _stack_traces;
int _num_threads;
// accessors and setters
--- 1447,1456 ----
*** 1380,1394 ****
_global_writer = _local_writer;
}
void clear_global_dumper() { _global_dumper = NULL; }
void clear_global_writer() { _global_writer = NULL; }
- bool is_segmented_dump() const { return _is_segmented_dump; }
- void set_segmented_dump() { _is_segmented_dump = true; }
- jlong dump_start() const { return _dump_start; }
- void set_dump_start(jlong pos);
-
bool skip_operation() const;
// writes a HPROF_LOAD_CLASS record
static void do_load_class(Klass* k);
--- 1465,1474 ----
*** 1409,1438 ****
}
// HPROF_TRACE and HPROF_FRAME records
void dump_stack_traces();
- // writes a HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT record
- void write_dump_header();
-
- // fixes up the length of the current dump record
- void write_current_dump_record_length();
-
- // fixes up the current dump record )and writes HPROF_HEAP_DUMP_END
- // record in the case of a segmented heap dump)
- void end_of_dump();
-
public:
VM_HeapDumper(DumpWriter* writer, bool gc_before_heap_dump, bool oome) :
VM_GC_Operation(0 /* total collections, dummy, ignored */,
GCCause::_heap_dump /* GC Cause */,
0 /* total full collections, dummy, ignored */,
gc_before_heap_dump) {
_local_writer = writer;
_gc_before_heap_dump = gc_before_heap_dump;
- _is_segmented_dump = false;
- _dump_start = (jlong)-1;
_klass_map = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(INITIAL_CLASS_COUNT, true);
_stack_traces = NULL;
_num_threads = 0;
if (oome) {
assert(!Thread::current()->is_VM_thread(), "Dump from OutOfMemoryError cannot be called by the VMThread");
--- 1489,1506 ----
*** 1468,1558 ****
bool VM_HeapDumper::skip_operation() const {
return false;
}
! // sets the dump starting position
! void VM_HeapDumper::set_dump_start(jlong pos) {
! _dump_start = pos;
! }
!
! // writes a HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT record
! void VM_HeapDumper::write_dump_header() {
! if (writer()->is_open()) {
! if (is_segmented_dump()) {
! writer()->write_u1(HPROF_HEAP_DUMP_SEGMENT);
! } else {
! writer()->write_u1(HPROF_HEAP_DUMP);
! }
! writer()->write_u4(0); // current ticks
// record the starting position for the dump (its length will be fixed up later)
! set_dump_start(writer()->current_offset());
! writer()->write_u4(0);
}
}
// fixes up the length of the current dump record
! void VM_HeapDumper::write_current_dump_record_length() {
! if (writer()->is_open()) {
! assert(dump_start() >= 0, "no dump start recorded");
!
! // calculate the size of the dump record
! julong dump_end = writer()->current_offset();
! julong dump_len = (dump_end - dump_start() - 4);
// record length must fit in a u4
if (dump_len > max_juint) {
warning("record is too large");
}
// seek to the dump start and fix-up the length
! writer()->seek_to_offset(dump_start());
! writer()->write_u4((u4)dump_len);
// adjust the total size written to keep the bytes written correct.
! writer()->adjust_bytes_written(-((jlong) sizeof(u4)));
// seek to dump end so we can continue
! writer()->seek_to_offset(dump_end);
// no current dump record
! set_dump_start((jlong)-1);
}
}
// used on a sub-record boundary to check if we need to start a
// new segment.
void VM_HeapDumper::check_segment_length() {
if (writer()->is_open()) {
! if (is_segmented_dump()) {
! // don't use current_offset that would be too expensive on a per record basis
! julong dump_end = writer()->bytes_written() + writer()->bytes_unwritten();
! assert(dump_end == (julong)writer()->current_offset(), "checking");
! julong dump_len = (dump_end - dump_start() - 4);
! assert(dump_len <= max_juint, "bad dump length");
! if (dump_len > HeapDumpSegmentSize) {
! write_current_dump_record_length();
! write_dump_header();
! }
}
}
}
! // fixes up the current dump record )and writes HPROF_HEAP_DUMP_END
! // record in the case of a segmented heap dump)
! void VM_HeapDumper::end_of_dump() {
! if (writer()->is_open()) {
! write_current_dump_record_length();
! // for segmented dump we write the end record
! if (is_segmented_dump()) {
! writer()->write_u1(HPROF_HEAP_DUMP_END);
! writer()->write_u4(0);
! writer()->write_u4(0);
! }
}
}
// marks sub-record boundary
void HeapObjectDumper::mark_end_of_record() {
--- 1536,1605 ----
bool VM_HeapDumper::skip_operation() const {
return false;
}
! // writes a HPROF_HEAP_DUMP_SEGMENT record
! void DumperSupport::write_dump_header(DumpWriter* writer) {
! if (writer->is_open()) {
! writer->write_u1(HPROF_HEAP_DUMP_SEGMENT);
! writer->write_u4(0); // current ticks
// record the starting position for the dump (its length will be fixed up later)
! writer->set_dump_start(writer->current_offset());
! writer->write_u4(0);
}
}
// fixes up the length of the current dump record
! void DumperSupport::write_current_dump_record_length(DumpWriter* writer) {
! if (writer->is_open()) {
! julong dump_end = writer->bytes_written() + writer->bytes_unwritten();
! julong dump_len = writer->current_record_length();
// record length must fit in a u4
if (dump_len > max_juint) {
warning("record is too large");
}
// seek to the dump start and fix-up the length
! assert(writer->dump_start() >= 0, "no dump start recorded");
! writer->seek_to_offset(writer->dump_start());
! writer->write_u4((u4)dump_len);
// adjust the total size written to keep the bytes written correct.
! writer->adjust_bytes_written(-((jlong) sizeof(u4)));
// seek to dump end so we can continue
! writer->seek_to_offset(dump_end);
// no current dump record
! writer->set_dump_start((jlong)-1);
}
}
// used on a sub-record boundary to check if we need to start a
// new segment.
void VM_HeapDumper::check_segment_length() {
if (writer()->is_open()) {
! julong dump_len = writer()->current_record_length();
! if (dump_len > 2UL*G) {
! DumperSupport::write_current_dump_record_length(writer());
! DumperSupport::write_dump_header(writer());
}
}
}
! // fixes up the current dump record and writes HPROF_HEAP_DUMP_END record
! void DumperSupport::end_of_dump(DumpWriter* writer) {
! if (writer->is_open()) {
! write_current_dump_record_length(writer);
! writer->write_u1(HPROF_HEAP_DUMP_END);
! writer->write_u4(0);
! writer->write_u4(0);
}
}
// marks sub-record boundary
void HeapObjectDumper::mark_end_of_record() {
*** 1714,1733 ****
// HPROF_HEADER
// [HPROF_UTF8]*
// [HPROF_LOAD_CLASS]*
// [[HPROF_FRAME]*|HPROF_TRACE]*
// [HPROF_GC_CLASS_DUMP]*
! // HPROF_HEAP_DUMP
//
// The HPROF_TRACE records represent the stack traces where the heap dump
// is generated and a "dummy trace" record which does not include
// any frames. The dummy trace record is used to be referenced as the
// unknown object alloc site.
//
! // The HPROF_HEAP_DUMP record has a length following by sub-records. To allow
! // the heap dump be generated in a single pass we remember the position of
! // the dump length and fix it up after all sub-records have been written.
// To generate the sub-records we iterate over the heap, writing
// HPROF_GC_INSTANCE_DUMP, HPROF_GC_OBJ_ARRAY_DUMP, and HPROF_GC_PRIM_ARRAY_DUMP
// records as we go. Once that is done we write records for some of the GC
// roots.
--- 1761,1781 ----
// HPROF_HEADER
// [HPROF_UTF8]*
// [HPROF_LOAD_CLASS]*
// [[HPROF_FRAME]*|HPROF_TRACE]*
// [HPROF_GC_CLASS_DUMP]*
! // [HPROF_HEAP_DUMP_SEGMENT]*
! // HPROF_HEAP_DUMP_END
//
// The HPROF_TRACE records represent the stack traces where the heap dump
// is generated and a "dummy trace" record which does not include
// any frames. The dummy trace record is used to be referenced as the
// unknown object alloc site.
//
! // Each HPROF_HEAP_DUMP_SEGMENT record has a length followed by sub-records.
! // To allow the heap dump be generated in a single pass we remember the position
! // of the dump length and fix it up after all sub-records have been written.
// To generate the sub-records we iterate over the heap, writing
// HPROF_GC_INSTANCE_DUMP, HPROF_GC_OBJ_ARRAY_DUMP, and HPROF_GC_PRIM_ARRAY_DUMP
// records as we go. Once that is done we write records for some of the GC
// roots.
*** 1750,1768 ****
// At this point we should be the only dumper active, so
// the following should be safe.
set_global_dumper();
set_global_writer();
! // Write the file header - use 1.0.2 for large heaps, otherwise 1.0.1
size_t used = ch->used();
! const char* header;
! if (used > (size_t)SegmentedHeapDumpThreshold) {
! set_segmented_dump();
! header = "JAVA PROFILE 1.0.2";
! } else {
! header = "JAVA PROFILE 1.0.1";
! }
// header is few bytes long - no chance to overflow int
writer()->write_raw((void*)header, (int)strlen(header));
writer()->write_u1(0); // terminator
writer()->write_u4(oopSize);
--- 1798,1810 ----
// At this point we should be the only dumper active, so
// the following should be safe.
set_global_dumper();
set_global_writer();
! // Write the file header - we always use 1.0.2
size_t used = ch->used();
! const char* header = "JAVA PROFILE 1.0.2";
// header is few bytes long - no chance to overflow int
writer()->write_raw((void*)header, (int)strlen(header));
writer()->write_u1(0); // terminator
writer()->write_u4(oopSize);
*** 1778,1799 ****
// write HPROF_FRAME and HPROF_TRACE records
// this must be called after _klass_map is built when iterating the classes above.
dump_stack_traces();
! // write HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT
! write_dump_header();
// Writes HPROF_GC_CLASS_DUMP records
ClassLoaderDataGraph::classes_do(&do_class_dump);
Universe::basic_type_classes_do(&do_basic_type_array_class_dump);
check_segment_length();
// writes HPROF_GC_INSTANCE_DUMP records.
! // After each sub-record is written check_segment_length will be invoked. When
! // generated a segmented heap dump this allows us to check if the current
! // segment exceeds a threshold and if so, then a new segment is started.
// The HPROF_GC_CLASS_DUMP and HPROF_GC_INSTANCE_DUMP are the vast bulk
// of the heap dump.
HeapObjectDumper obj_dumper(this, writer());
Universe::heap()->safe_object_iterate(&obj_dumper);
--- 1820,1841 ----
// write HPROF_FRAME and HPROF_TRACE records
// this must be called after _klass_map is built when iterating the classes above.
dump_stack_traces();
! // write HPROF_HEAP_DUMP_SEGMENT
! DumperSupport::write_dump_header(writer());
// Writes HPROF_GC_CLASS_DUMP records
ClassLoaderDataGraph::classes_do(&do_class_dump);
Universe::basic_type_classes_do(&do_basic_type_array_class_dump);
check_segment_length();
// writes HPROF_GC_INSTANCE_DUMP records.
! // After each sub-record is written check_segment_length will be invoked
! // to check if the current segment exceeds a threshold. If so, a new
! // segment is started.
// The HPROF_GC_CLASS_DUMP and HPROF_GC_INSTANCE_DUMP are the vast bulk
// of the heap dump.
HeapObjectDumper obj_dumper(this, writer());
Universe::heap()->safe_object_iterate(&obj_dumper);
*** 1815,1827 ****
// HPROF_GC_ROOT_STICKY_CLASS
StickyClassDumper class_dumper(writer());
SystemDictionary::always_strong_classes_do(&class_dumper);
! // fixes up the length of the dump record. In the case of a segmented
! // heap then the HPROF_HEAP_DUMP_END record is also written.
! end_of_dump();
// Now we clear the global variables, so that a future dumper might run.
clear_global_dumper();
clear_global_writer();
}
--- 1857,1868 ----
// HPROF_GC_ROOT_STICKY_CLASS
StickyClassDumper class_dumper(writer());
SystemDictionary::always_strong_classes_do(&class_dumper);
! // fixes up the length of the dump record and writes the HPROF_HEAP_DUMP_END record.
! DumperSupport::end_of_dump(writer());
// Now we clear the global variables, so that a future dumper might run.
clear_global_dumper();
clear_global_writer();
}
< prev index next >