Fri, 24 Jan 2020 09:41:30 +0800
8144732: VM_HeapDumper hits assert with bad dump_len
Reviewed-by: dsamersoff
src/share/vm/runtime/arguments.cpp | file | annotate | diff | comparison | revisions | |
src/share/vm/services/heapDumper.cpp | file | annotate | diff | comparison | revisions |
1.1 --- a/src/share/vm/runtime/arguments.cpp Fri Sep 27 16:23:12 2019 +0200 1.2 +++ b/src/share/vm/runtime/arguments.cpp Fri Jan 24 09:41:30 2020 +0800 1.3 @@ -305,6 +305,7 @@ 1.4 { "UseOldInlining", JDK_Version::jdk_update(8, 20), JDK_Version::jdk(10) }, 1.5 { "AutoShutdownNMT", JDK_Version::jdk_update(8, 40), JDK_Version::jdk(10) }, 1.6 { "CompilationRepeat", JDK_Version::jdk(8), JDK_Version::jdk(9) }, 1.7 + { "SegmentedHeapDumpThreshold", JDK_Version::jdk_update(8, 252), JDK_Version::jdk(10) }, 1.8 #ifdef PRODUCT 1.9 { "DesiredMethodLimit", 1.10 JDK_Version::jdk_update(7, 2), JDK_Version::jdk(8) },
2.1 --- a/src/share/vm/services/heapDumper.cpp Fri Sep 27 16:23:12 2019 +0200 2.2 +++ b/src/share/vm/services/heapDumper.cpp Fri Jan 24 09:41:30 2020 +0800 2.3 @@ -50,8 +50,7 @@ 2.4 * src/share/demo/jvmti/hprof/hprof_io.c 2.5 * 2.6 * 2.7 - * header "JAVA PROFILE 1.0.1" or "JAVA PROFILE 1.0.2" 2.8 - * (0-terminated) 2.9 + * header "JAVA PROFILE 1.0.2" (0-terminated) 2.10 * 2.11 * u4 size of identifiers. Identifiers are used to represent 2.12 * UTF8 strings, objects, stack traces, etc. They usually 2.13 @@ -382,6 +381,8 @@ 2.14 size_t _size; 2.15 size_t _pos; 2.16 2.17 + jlong _dump_start; 2.18 + 2.19 char* _error; // error message when I/O fails 2.20 2.21 void set_file_descriptor(int fd) { _fd = fd; } 2.22 @@ -405,6 +406,10 @@ 2.23 bool is_open() const { return file_descriptor() >= 0; } 2.24 void flush(); 2.25 2.26 + jlong dump_start() const { return _dump_start; } 2.27 + void set_dump_start(jlong pos); 2.28 + julong current_record_length(); 2.29 + 2.30 // total number of bytes written to the disk 2.31 julong bytes_written() const { return _bytes_written; } 2.32 2.33 @@ -446,6 +451,7 @@ 2.34 _pos = 0; 2.35 _error = NULL; 2.36 _bytes_written = 0L; 2.37 + _dump_start = (jlong)-1; 2.38 _fd = os::create_binary_file(path, false); // don't replace existing file 2.39 2.40 // if the open failed we record the error 2.41 @@ -473,6 +479,22 @@ 2.42 } 2.43 } 2.44 2.45 +// sets the dump starting position 2.46 +void DumpWriter::set_dump_start(jlong pos) { 2.47 + _dump_start = pos; 2.48 +} 2.49 + 2.50 +julong DumpWriter::current_record_length() { 2.51 + if (is_open()) { 2.52 + // calculate the size of the dump record 2.53 + julong dump_end = bytes_written() + bytes_unwritten(); 2.54 + assert(dump_end == (size_t)current_offset(), "checking"); 2.55 + julong dump_len = dump_end - dump_start() - 4; 2.56 + return dump_len; 2.57 + } 2.58 + return 0; 2.59 +} 2.60 + 2.61 // write directly to the file 2.62 void DumpWriter::write_internal(void* s, size_t len) { 2.63 if (is_open()) { 2.64 @@ -641,6 +663,18 @@ 2.65 static void dump_prim_array(DumpWriter* writer, typeArrayOop array); 2.66 // create HPROF_FRAME record for the given method and bci 2.67 static void dump_stack_frame(DumpWriter* writer, int frame_serial_num, int class_serial_num, Method* m, int bci); 2.68 + 2.69 + // check if we need to truncate an array 2.70 + static int calculate_array_max_length(DumpWriter* writer, arrayOop array, short header_size); 2.71 + 2.72 + // writes a HPROF_HEAP_DUMP_SEGMENT record 2.73 + static void write_dump_header(DumpWriter* writer); 2.74 + 2.75 + // fixes up the length of the current dump record 2.76 + static void write_current_dump_record_length(DumpWriter* writer); 2.77 + 2.78 + // fixes up the current dump record and writes HPROF_HEAP_DUMP_END record 2.79 + static void end_of_dump(DumpWriter* writer); 2.80 }; 2.81 2.82 // write a header of the given type 2.83 @@ -1047,50 +1081,103 @@ 2.84 } 2.85 } 2.86 2.87 +// Hprof uses an u4 as record length field, 2.88 +// which means we need to truncate arrays that are too long. 2.89 +int DumperSupport::calculate_array_max_length(DumpWriter* writer, arrayOop array, short header_size) { 2.90 + BasicType type = ArrayKlass::cast(array->klass())->element_type(); 2.91 + assert(type >= T_BOOLEAN && type <= T_OBJECT, "invalid array element type"); 2.92 + 2.93 + int length = array->length(); 2.94 + 2.95 + int type_size; 2.96 + if (type == T_OBJECT) { 2.97 + type_size = sizeof(address); 2.98 + } else { 2.99 + type_size = type2aelembytes(type); 2.100 + } 2.101 + 2.102 + size_t length_in_bytes = (size_t)length * type_size; 2.103 + 2.104 + // Create a new record if the current record is non-empty and the array can't fit. 2.105 + julong current_record_length = writer->current_record_length(); 2.106 + if (current_record_length > 0 && 2.107 + (current_record_length + header_size + length_in_bytes) > max_juint) { 2.108 + write_current_dump_record_length(writer); 2.109 + write_dump_header(writer); 2.110 + 2.111 + // We now have an empty record. 2.112 + current_record_length = 0; 2.113 + } 2.114 + 2.115 + // Calculate max bytes we can use. 2.116 + uint max_bytes = max_juint - (header_size + current_record_length); 2.117 + 2.118 + // Array too long for the record? 2.119 + // Calculate max length and return it. 2.120 + if (length_in_bytes > max_bytes) { 2.121 + length = max_bytes / type_size; 2.122 + length_in_bytes = (size_t)length * type_size; 2.123 + 2.124 + warning("cannot dump array of type %s[] with length %d; truncating to length %d", 2.125 + type2name_tab[type], array->length(), length); 2.126 + } 2.127 + return length; 2.128 +} 2.129 + 2.130 // creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array 2.131 void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop array) { 2.132 + // sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID) + sizeof(classID) 2.133 + short header_size = 1 + 2 * 4 + 2 * sizeof(address); 2.134 + 2.135 + int length = calculate_array_max_length(writer, array, header_size); 2.136 2.137 writer->write_u1(HPROF_GC_OBJ_ARRAY_DUMP); 2.138 writer->write_objectID(array); 2.139 writer->write_u4(STACK_TRACE_ID); 2.140 - writer->write_u4((u4)array->length()); 2.141 + writer->write_u4(length); 2.142 + 2.143 2.144 // array class ID 2.145 writer->write_classID(array->klass()); 2.146 2.147 // [id]* elements 2.148 - for (int index=0; index<array->length(); index++) { 2.149 + for (int index = 0; index < length; index++) { 2.150 oop o = array->obj_at(index); 2.151 writer->write_objectID(o); 2.152 } 2.153 } 2.154 2.155 -#define WRITE_ARRAY(Array, Type, Size) \ 2.156 - for (int i=0; i<Array->length(); i++) { writer->write_##Size((Size)array->Type##_at(i)); } 2.157 - 2.158 +#define WRITE_ARRAY(Array, Type, Size, Length) \ 2.159 + for (int i = 0; i < Length; i++) { writer->write_##Size((Size)array->Type##_at(i)); } 2.160 2.161 // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array 2.162 void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array) { 2.163 BasicType type = TypeArrayKlass::cast(array->klass())->element_type(); 2.164 2.165 + // 2 * sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID) 2.166 + short header_size = 2 * 1 + 2 * 4 + sizeof(address); 2.167 + 2.168 + int length = calculate_array_max_length(writer, array, header_size); 2.169 + int type_size = type2aelembytes(type); 2.170 + u4 length_in_bytes = (u4)length * type_size; 2.171 + 2.172 writer->write_u1(HPROF_GC_PRIM_ARRAY_DUMP); 2.173 writer->write_objectID(array); 2.174 writer->write_u4(STACK_TRACE_ID); 2.175 - writer->write_u4((u4)array->length()); 2.176 + writer->write_u4(length); 2.177 writer->write_u1(type2tag(type)); 2.178 2.179 // nothing to copy 2.180 - if (array->length() == 0) { 2.181 + if (length == 0) { 2.182 return; 2.183 } 2.184 2.185 // If the byte ordering is big endian then we can copy most types directly 2.186 - u4 length_in_bytes = (u4)array->length() * type2aelembytes(type); 2.187 2.188 switch (type) { 2.189 case T_INT : { 2.190 if (Bytes::is_Java_byte_ordering_different()) { 2.191 - WRITE_ARRAY(array, int, u4); 2.192 + WRITE_ARRAY(array, int, u4, length); 2.193 } else { 2.194 writer->write_raw((void*)(array->int_at_addr(0)), length_in_bytes); 2.195 } 2.196 @@ -1102,7 +1189,7 @@ 2.197 } 2.198 case T_CHAR : { 2.199 if (Bytes::is_Java_byte_ordering_different()) { 2.200 - WRITE_ARRAY(array, char, u2); 2.201 + WRITE_ARRAY(array, char, u2, length); 2.202 } else { 2.203 writer->write_raw((void*)(array->char_at_addr(0)), length_in_bytes); 2.204 } 2.205 @@ -1110,7 +1197,7 @@ 2.206 } 2.207 case T_SHORT : { 2.208 if (Bytes::is_Java_byte_ordering_different()) { 2.209 - WRITE_ARRAY(array, short, u2); 2.210 + WRITE_ARRAY(array, short, u2, length); 2.211 } else { 2.212 writer->write_raw((void*)(array->short_at_addr(0)), length_in_bytes); 2.213 } 2.214 @@ -1118,7 +1205,7 @@ 2.215 } 2.216 case T_BOOLEAN : { 2.217 if (Bytes::is_Java_byte_ordering_different()) { 2.218 - WRITE_ARRAY(array, bool, u1); 2.219 + WRITE_ARRAY(array, bool, u1, length); 2.220 } else { 2.221 writer->write_raw((void*)(array->bool_at_addr(0)), length_in_bytes); 2.222 } 2.223 @@ -1126,7 +1213,7 @@ 2.224 } 2.225 case T_LONG : { 2.226 if (Bytes::is_Java_byte_ordering_different()) { 2.227 - WRITE_ARRAY(array, long, u8); 2.228 + WRITE_ARRAY(array, long, u8, length); 2.229 } else { 2.230 writer->write_raw((void*)(array->long_at_addr(0)), length_in_bytes); 2.231 } 2.232 @@ -1138,14 +1225,14 @@ 2.233 // use IEEE 754. 2.234 2.235 case T_FLOAT : { 2.236 - for (int i=0; i<array->length(); i++) { 2.237 - dump_float( writer, array->float_at(i) ); 2.238 + for (int i = 0; i < length; i++) { 2.239 + dump_float(writer, array->float_at(i)); 2.240 } 2.241 break; 2.242 } 2.243 case T_DOUBLE : { 2.244 - for (int i=0; i<array->length(); i++) { 2.245 - dump_double( writer, array->double_at(i) ); 2.246 + for (int i = 0; i < length; i++) { 2.247 + dump_double(writer, array->double_at(i)); 2.248 } 2.249 break; 2.250 } 2.251 @@ -1362,8 +1449,6 @@ 2.252 JavaThread* _oome_thread; 2.253 Method* _oome_constructor; 2.254 bool _gc_before_heap_dump; 2.255 - bool _is_segmented_dump; 2.256 - jlong _dump_start; 2.257 GrowableArray<Klass*>* _klass_map; 2.258 ThreadStackTrace** _stack_traces; 2.259 int _num_threads; 2.260 @@ -1382,11 +1467,6 @@ 2.261 void clear_global_dumper() { _global_dumper = NULL; } 2.262 void clear_global_writer() { _global_writer = NULL; } 2.263 2.264 - bool is_segmented_dump() const { return _is_segmented_dump; } 2.265 - void set_segmented_dump() { _is_segmented_dump = true; } 2.266 - jlong dump_start() const { return _dump_start; } 2.267 - void set_dump_start(jlong pos); 2.268 - 2.269 bool skip_operation() const; 2.270 2.271 // writes a HPROF_LOAD_CLASS record 2.272 @@ -1411,16 +1491,6 @@ 2.273 // HPROF_TRACE and HPROF_FRAME records 2.274 void dump_stack_traces(); 2.275 2.276 - // writes a HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT record 2.277 - void write_dump_header(); 2.278 - 2.279 - // fixes up the length of the current dump record 2.280 - void write_current_dump_record_length(); 2.281 - 2.282 - // fixes up the current dump record )and writes HPROF_HEAP_DUMP_END 2.283 - // record in the case of a segmented heap dump) 2.284 - void end_of_dump(); 2.285 - 2.286 public: 2.287 VM_HeapDumper(DumpWriter* writer, bool gc_before_heap_dump, bool oome) : 2.288 VM_GC_Operation(0 /* total collections, dummy, ignored */, 2.289 @@ -1429,8 +1499,6 @@ 2.290 gc_before_heap_dump) { 2.291 _local_writer = writer; 2.292 _gc_before_heap_dump = gc_before_heap_dump; 2.293 - _is_segmented_dump = false; 2.294 - _dump_start = (jlong)-1; 2.295 _klass_map = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(INITIAL_CLASS_COUNT, true); 2.296 _stack_traces = NULL; 2.297 _num_threads = 0; 2.298 @@ -1470,35 +1538,23 @@ 2.299 return false; 2.300 } 2.301 2.302 -// sets the dump starting position 2.303 -void VM_HeapDumper::set_dump_start(jlong pos) { 2.304 - _dump_start = pos; 2.305 -} 2.306 - 2.307 - // writes a HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT record 2.308 -void VM_HeapDumper::write_dump_header() { 2.309 - if (writer()->is_open()) { 2.310 - if (is_segmented_dump()) { 2.311 - writer()->write_u1(HPROF_HEAP_DUMP_SEGMENT); 2.312 - } else { 2.313 - writer()->write_u1(HPROF_HEAP_DUMP); 2.314 - } 2.315 - writer()->write_u4(0); // current ticks 2.316 + // writes a HPROF_HEAP_DUMP_SEGMENT record 2.317 +void DumperSupport::write_dump_header(DumpWriter* writer) { 2.318 + if (writer->is_open()) { 2.319 + writer->write_u1(HPROF_HEAP_DUMP_SEGMENT); 2.320 + writer->write_u4(0); // current ticks 2.321 2.322 // record the starting position for the dump (its length will be fixed up later) 2.323 - set_dump_start(writer()->current_offset()); 2.324 - writer()->write_u4(0); 2.325 + writer->set_dump_start(writer->current_offset()); 2.326 + writer->write_u4(0); 2.327 } 2.328 } 2.329 2.330 // fixes up the length of the current dump record 2.331 -void VM_HeapDumper::write_current_dump_record_length() { 2.332 - if (writer()->is_open()) { 2.333 - assert(dump_start() >= 0, "no dump start recorded"); 2.334 - 2.335 - // calculate the size of the dump record 2.336 - julong dump_end = writer()->current_offset(); 2.337 - julong dump_len = (dump_end - dump_start() - 4); 2.338 +void DumperSupport::write_current_dump_record_length(DumpWriter* writer) { 2.339 + if (writer->is_open()) { 2.340 + julong dump_end = writer->bytes_written() + writer->bytes_unwritten(); 2.341 + julong dump_len = writer->current_record_length(); 2.342 2.343 // record length must fit in a u4 2.344 if (dump_len > max_juint) { 2.345 @@ -1506,17 +1562,18 @@ 2.346 } 2.347 2.348 // seek to the dump start and fix-up the length 2.349 - writer()->seek_to_offset(dump_start()); 2.350 - writer()->write_u4((u4)dump_len); 2.351 + assert(writer->dump_start() >= 0, "no dump start recorded"); 2.352 + writer->seek_to_offset(writer->dump_start()); 2.353 + writer->write_u4((u4)dump_len); 2.354 2.355 // adjust the total size written to keep the bytes written correct. 2.356 - writer()->adjust_bytes_written(-((jlong) sizeof(u4))); 2.357 + writer->adjust_bytes_written(-((jlong) sizeof(u4))); 2.358 2.359 // seek to dump end so we can continue 2.360 - writer()->seek_to_offset(dump_end); 2.361 + writer->seek_to_offset(dump_end); 2.362 2.363 // no current dump record 2.364 - set_dump_start((jlong)-1); 2.365 + writer->set_dump_start((jlong)-1); 2.366 } 2.367 } 2.368 2.369 @@ -1524,33 +1581,23 @@ 2.370 // new segment. 2.371 void VM_HeapDumper::check_segment_length() { 2.372 if (writer()->is_open()) { 2.373 - if (is_segmented_dump()) { 2.374 - // don't use current_offset that would be too expensive on a per record basis 2.375 - julong dump_end = writer()->bytes_written() + writer()->bytes_unwritten(); 2.376 - assert(dump_end == (julong)writer()->current_offset(), "checking"); 2.377 - julong dump_len = (dump_end - dump_start() - 4); 2.378 - assert(dump_len <= max_juint, "bad dump length"); 2.379 + julong dump_len = writer()->current_record_length(); 2.380 2.381 - if (dump_len > HeapDumpSegmentSize) { 2.382 - write_current_dump_record_length(); 2.383 - write_dump_header(); 2.384 - } 2.385 + if (dump_len > 2UL*G) { 2.386 + DumperSupport::write_current_dump_record_length(writer()); 2.387 + DumperSupport::write_dump_header(writer()); 2.388 } 2.389 } 2.390 } 2.391 2.392 -// fixes up the current dump record )and writes HPROF_HEAP_DUMP_END 2.393 -// record in the case of a segmented heap dump) 2.394 -void VM_HeapDumper::end_of_dump() { 2.395 - if (writer()->is_open()) { 2.396 - write_current_dump_record_length(); 2.397 +// fixes up the current dump record and writes HPROF_HEAP_DUMP_END record 2.398 +void DumperSupport::end_of_dump(DumpWriter* writer) { 2.399 + if (writer->is_open()) { 2.400 + write_current_dump_record_length(writer); 2.401 2.402 - // for segmented dump we write the end record 2.403 - if (is_segmented_dump()) { 2.404 - writer()->write_u1(HPROF_HEAP_DUMP_END); 2.405 - writer()->write_u4(0); 2.406 - writer()->write_u4(0); 2.407 - } 2.408 + writer->write_u1(HPROF_HEAP_DUMP_END); 2.409 + writer->write_u4(0); 2.410 + writer->write_u4(0); 2.411 } 2.412 } 2.413 2.414 @@ -1716,16 +1763,17 @@ 2.415 // [HPROF_LOAD_CLASS]* 2.416 // [[HPROF_FRAME]*|HPROF_TRACE]* 2.417 // [HPROF_GC_CLASS_DUMP]* 2.418 -// HPROF_HEAP_DUMP 2.419 +// [HPROF_HEAP_DUMP_SEGMENT]* 2.420 +// HPROF_HEAP_DUMP_END 2.421 // 2.422 // The HPROF_TRACE records represent the stack traces where the heap dump 2.423 // is generated and a "dummy trace" record which does not include 2.424 // any frames. The dummy trace record is used to be referenced as the 2.425 // unknown object alloc site. 2.426 // 2.427 -// The HPROF_HEAP_DUMP record has a length following by sub-records. To allow 2.428 -// the heap dump be generated in a single pass we remember the position of 2.429 -// the dump length and fix it up after all sub-records have been written. 2.430 +// Each HPROF_HEAP_DUMP_SEGMENT record has a length followed by sub-records. 2.431 +// To allow the heap dump be generated in a single pass we remember the position 2.432 +// of the dump length and fix it up after all sub-records have been written. 2.433 // To generate the sub-records we iterate over the heap, writing 2.434 // HPROF_GC_INSTANCE_DUMP, HPROF_GC_OBJ_ARRAY_DUMP, and HPROF_GC_PRIM_ARRAY_DUMP 2.435 // records as we go. Once that is done we write records for some of the GC 2.436 @@ -1752,15 +1800,9 @@ 2.437 set_global_dumper(); 2.438 set_global_writer(); 2.439 2.440 - // Write the file header - use 1.0.2 for large heaps, otherwise 1.0.1 2.441 + // Write the file header - we always use 1.0.2 2.442 size_t used = ch->used(); 2.443 - const char* header; 2.444 - if (used > (size_t)SegmentedHeapDumpThreshold) { 2.445 - set_segmented_dump(); 2.446 - header = "JAVA PROFILE 1.0.2"; 2.447 - } else { 2.448 - header = "JAVA PROFILE 1.0.1"; 2.449 - } 2.450 + const char* header = "JAVA PROFILE 1.0.2"; 2.451 2.452 // header is few bytes long - no chance to overflow int 2.453 writer()->write_raw((void*)header, (int)strlen(header)); 2.454 @@ -1780,8 +1822,8 @@ 2.455 // this must be called after _klass_map is built when iterating the classes above. 2.456 dump_stack_traces(); 2.457 2.458 - // write HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENT 2.459 - write_dump_header(); 2.460 + // write HPROF_HEAP_DUMP_SEGMENT 2.461 + DumperSupport::write_dump_header(writer()); 2.462 2.463 // Writes HPROF_GC_CLASS_DUMP records 2.464 ClassLoaderDataGraph::classes_do(&do_class_dump); 2.465 @@ -1789,9 +1831,9 @@ 2.466 check_segment_length(); 2.467 2.468 // writes HPROF_GC_INSTANCE_DUMP records. 2.469 - // After each sub-record is written check_segment_length will be invoked. When 2.470 - // generated a segmented heap dump this allows us to check if the current 2.471 - // segment exceeds a threshold and if so, then a new segment is started. 2.472 + // After each sub-record is written check_segment_length will be invoked 2.473 + // to check if the current segment exceeds a threshold. If so, a new 2.474 + // segment is started. 2.475 // The HPROF_GC_CLASS_DUMP and HPROF_GC_INSTANCE_DUMP are the vast bulk 2.476 // of the heap dump. 2.477 HeapObjectDumper obj_dumper(this, writer()); 2.478 @@ -1817,9 +1859,8 @@ 2.479 StickyClassDumper class_dumper(writer()); 2.480 SystemDictionary::always_strong_classes_do(&class_dumper); 2.481 2.482 - // fixes up the length of the dump record. In the case of a segmented 2.483 - // heap then the HPROF_HEAP_DUMP_END record is also written. 2.484 - end_of_dump(); 2.485 + // fixes up the length of the dump record and writes the HPROF_HEAP_DUMP_END record. 2.486 + DumperSupport::end_of_dump(writer()); 2.487 2.488 // Now we clear the global variables, so that a future dumper might run. 2.489 clear_global_dumper();