Mercurial > hg > truffle
comparison src/share/vm/services/heapDumper.cpp @ 615:c6c601a0f2d6
6797870: Add -XX:+{HeapDump,PrintClassHistogram}{Before,After}FullGC
Summary: Call newly created CollectedHeap::dump_{pre,post}_full_gc before and after every stop-world full collection cycle on GenCollectedHeap and ParallelScavengeHeap. (Support for G1CollectedHeap forthcoming under CR 6810861.) Small modifications to existing heap dumping and class histogram implementation, especially to allow multiple on-the-fly histos/dumps by the VM thread during a single safepoint.
Reviewed-by: jmasa, alanb, mchung
author | ysr |
---|---|
date | Mon, 02 Mar 2009 16:37:04 -0800 |
parents | e9be0e04635a |
children | 7bb995fbd3c0 |
comparison
equal
deleted
inserted
replaced
598:1fa16c3565be | 615:c6c601a0f2d6 |
---|---|
345 enum { | 345 enum { |
346 STACK_TRACE_ID = 1, | 346 STACK_TRACE_ID = 1, |
347 INITIAL_CLASS_COUNT = 200 | 347 INITIAL_CLASS_COUNT = 200 |
348 }; | 348 }; |
349 | 349 |
350 | |
351 // Supports I/O operations on a dump file | 350 // Supports I/O operations on a dump file |
352 | 351 |
353 class DumpWriter : public StackObj { | 352 class DumpWriter : public StackObj { |
354 private: | 353 private: |
355 enum { | 354 enum { |
1301 } | 1300 } |
1302 | 1301 |
1303 // The VM operation that performs the heap dump | 1302 // The VM operation that performs the heap dump |
1304 class VM_HeapDumper : public VM_GC_Operation { | 1303 class VM_HeapDumper : public VM_GC_Operation { |
1305 private: | 1304 private: |
1306 DumpWriter* _writer; | 1305 static VM_HeapDumper* _global_dumper; |
1306 static DumpWriter* _global_writer; | |
1307 DumpWriter* _local_writer; | |
1307 bool _gc_before_heap_dump; | 1308 bool _gc_before_heap_dump; |
1308 bool _is_segmented_dump; | 1309 bool _is_segmented_dump; |
1309 jlong _dump_start; | 1310 jlong _dump_start; |
1310 GrowableArray<Klass*>* _klass_map; | 1311 GrowableArray<Klass*>* _klass_map; |
1311 ThreadStackTrace** _stack_traces; | 1312 ThreadStackTrace** _stack_traces; |
1312 int _num_threads; | 1313 int _num_threads; |
1313 | 1314 |
1314 // accessors | 1315 // accessors and setters |
1315 DumpWriter* writer() const { return _writer; } | 1316 static VM_HeapDumper* dumper() { assert(_global_dumper != NULL, "Error"); return _global_dumper; } |
1317 static DumpWriter* writer() { assert(_global_writer != NULL, "Error"); return _global_writer; } | |
1318 void set_global_dumper() { | |
1319 assert(_global_dumper == NULL, "Error"); | |
1320 _global_dumper = this; | |
1321 } | |
1322 void set_global_writer() { | |
1323 assert(_global_writer == NULL, "Error"); | |
1324 _global_writer = _local_writer; | |
1325 } | |
1326 void clear_global_dumper() { _global_dumper = NULL; } | |
1327 void clear_global_writer() { _global_writer = NULL; } | |
1328 | |
1316 bool is_segmented_dump() const { return _is_segmented_dump; } | 1329 bool is_segmented_dump() const { return _is_segmented_dump; } |
1317 void set_segmented_dump() { _is_segmented_dump = true; } | 1330 void set_segmented_dump() { _is_segmented_dump = true; } |
1318 jlong dump_start() const { return _dump_start; } | 1331 jlong dump_start() const { return _dump_start; } |
1319 void set_dump_start(jlong pos); | 1332 void set_dump_start(jlong pos); |
1320 | 1333 |
1355 public: | 1368 public: |
1356 VM_HeapDumper(DumpWriter* writer, bool gc_before_heap_dump) : | 1369 VM_HeapDumper(DumpWriter* writer, bool gc_before_heap_dump) : |
1357 VM_GC_Operation(0 /* total collections, dummy, ignored */, | 1370 VM_GC_Operation(0 /* total collections, dummy, ignored */, |
1358 0 /* total full collections, dummy, ignored */, | 1371 0 /* total full collections, dummy, ignored */, |
1359 gc_before_heap_dump) { | 1372 gc_before_heap_dump) { |
1360 _writer = writer; | 1373 _local_writer = writer; |
1361 _gc_before_heap_dump = gc_before_heap_dump; | 1374 _gc_before_heap_dump = gc_before_heap_dump; |
1362 _is_segmented_dump = false; | 1375 _is_segmented_dump = false; |
1363 _dump_start = (jlong)-1; | 1376 _dump_start = (jlong)-1; |
1364 _klass_map = new (ResourceObj::C_HEAP) GrowableArray<Klass*>(INITIAL_CLASS_COUNT, true); | 1377 _klass_map = new (ResourceObj::C_HEAP) GrowableArray<Klass*>(INITIAL_CLASS_COUNT, true); |
1365 _stack_traces = NULL; | 1378 _stack_traces = NULL; |
1378 VMOp_Type type() const { return VMOp_HeapDumper; } | 1391 VMOp_Type type() const { return VMOp_HeapDumper; } |
1379 // used to mark sub-record boundary | 1392 // used to mark sub-record boundary |
1380 void check_segment_length(); | 1393 void check_segment_length(); |
1381 void doit(); | 1394 void doit(); |
1382 }; | 1395 }; |
1396 | |
1397 VM_HeapDumper* VM_HeapDumper::_global_dumper = NULL; | |
1398 DumpWriter* VM_HeapDumper::_global_writer = NULL; | |
1383 | 1399 |
1384 bool VM_HeapDumper::skip_operation() const { | 1400 bool VM_HeapDumper::skip_operation() const { |
1385 return false; | 1401 return false; |
1386 } | 1402 } |
1387 | 1403 |
1477 // writes a HPROF_LOAD_CLASS record for the class (and each of its | 1493 // writes a HPROF_LOAD_CLASS record for the class (and each of its |
1478 // array classes) | 1494 // array classes) |
1479 void VM_HeapDumper::do_load_class(klassOop k) { | 1495 void VM_HeapDumper::do_load_class(klassOop k) { |
1480 static u4 class_serial_num = 0; | 1496 static u4 class_serial_num = 0; |
1481 | 1497 |
1482 VM_HeapDumper* dumper = ((VM_HeapDumper*)VMThread::vm_operation()); | |
1483 DumpWriter* writer = dumper->writer(); | |
1484 | |
1485 // len of HPROF_LOAD_CLASS record | 1498 // len of HPROF_LOAD_CLASS record |
1486 u4 remaining = 2*oopSize + 2*sizeof(u4); | 1499 u4 remaining = 2*oopSize + 2*sizeof(u4); |
1487 | 1500 |
1488 // write a HPROF_LOAD_CLASS for the class and each array class | 1501 // write a HPROF_LOAD_CLASS for the class and each array class |
1489 do { | 1502 do { |
1490 DumperSupport::write_header(writer, HPROF_LOAD_CLASS, remaining); | 1503 DumperSupport::write_header(writer(), HPROF_LOAD_CLASS, remaining); |
1491 | 1504 |
1492 // class serial number is just a number | 1505 // class serial number is just a number |
1493 writer->write_u4(++class_serial_num); | 1506 writer()->write_u4(++class_serial_num); |
1494 | 1507 |
1495 // class ID | 1508 // class ID |
1496 Klass* klass = Klass::cast(k); | 1509 Klass* klass = Klass::cast(k); |
1497 writer->write_classID(klass); | 1510 writer()->write_classID(klass); |
1498 | 1511 |
1499 // add the klassOop and class serial number pair | 1512 // add the klassOop and class serial number pair |
1500 dumper->add_class_serial_number(klass, class_serial_num); | 1513 dumper()->add_class_serial_number(klass, class_serial_num); |
1501 | 1514 |
1502 writer->write_u4(STACK_TRACE_ID); | 1515 writer()->write_u4(STACK_TRACE_ID); |
1503 | 1516 |
1504 // class name ID | 1517 // class name ID |
1505 symbolOop name = klass->name(); | 1518 symbolOop name = klass->name(); |
1506 writer->write_objectID(name); | 1519 writer()->write_objectID(name); |
1507 | 1520 |
1508 // write a LOAD_CLASS record for the array type (if it exists) | 1521 // write a LOAD_CLASS record for the array type (if it exists) |
1509 k = klass->array_klass_or_null(); | 1522 k = klass->array_klass_or_null(); |
1510 } while (k != NULL); | 1523 } while (k != NULL); |
1511 } | 1524 } |
1512 | 1525 |
1513 // writes a HPROF_GC_CLASS_DUMP record for the given class | 1526 // writes a HPROF_GC_CLASS_DUMP record for the given class |
1514 void VM_HeapDumper::do_class_dump(klassOop k) { | 1527 void VM_HeapDumper::do_class_dump(klassOop k) { |
1515 VM_HeapDumper* dumper = ((VM_HeapDumper*)VMThread::vm_operation()); | 1528 DumperSupport::dump_class_and_array_classes(writer(), k); |
1516 DumpWriter* writer = dumper->writer(); | |
1517 DumperSupport::dump_class_and_array_classes(writer, k); | |
1518 } | 1529 } |
1519 | 1530 |
1520 // writes a HPROF_GC_CLASS_DUMP records for a given basic type | 1531 // writes a HPROF_GC_CLASS_DUMP records for a given basic type |
1521 // array (and each multi-dimensional array too) | 1532 // array (and each multi-dimensional array too) |
1522 void VM_HeapDumper::do_basic_type_array_class_dump(klassOop k) { | 1533 void VM_HeapDumper::do_basic_type_array_class_dump(klassOop k) { |
1523 VM_HeapDumper* dumper = ((VM_HeapDumper*)VMThread::vm_operation()); | 1534 DumperSupport::dump_basic_type_array_class(writer(), k); |
1524 DumpWriter* writer = dumper->writer(); | |
1525 DumperSupport::dump_basic_type_array_class(writer, k); | |
1526 } | 1535 } |
1527 | 1536 |
1528 // Walk the stack of the given thread. | 1537 // Walk the stack of the given thread. |
1529 // Dumps a HPROF_GC_ROOT_JAVA_FRAME record for each local | 1538 // Dumps a HPROF_GC_ROOT_JAVA_FRAME record for each local |
1530 // Dumps a HPROF_GC_ROOT_JNI_LOCAL record for each JNI local | 1539 // Dumps a HPROF_GC_ROOT_JNI_LOCAL record for each JNI local |
1656 } else { | 1665 } else { |
1657 // make the heap parsable (no need to retire TLABs) | 1666 // make the heap parsable (no need to retire TLABs) |
1658 ch->ensure_parsability(false); | 1667 ch->ensure_parsability(false); |
1659 } | 1668 } |
1660 | 1669 |
1670 // At this point we should be the only dumper active, so | |
1671 // the following should be safe. | |
1672 set_global_dumper(); | |
1673 set_global_writer(); | |
1674 | |
1661 // Write the file header - use 1.0.2 for large heaps, otherwise 1.0.1 | 1675 // Write the file header - use 1.0.2 for large heaps, otherwise 1.0.1 |
1662 size_t used = ch->used(); | 1676 size_t used = ch->used(); |
1663 const char* header; | 1677 const char* header; |
1664 if (used > (size_t)SegmentedHeapDumpThreshold) { | 1678 if (used > (size_t)SegmentedHeapDumpThreshold) { |
1665 set_segmented_dump(); | 1679 set_segmented_dump(); |
1666 header = "JAVA PROFILE 1.0.2"; | 1680 header = "JAVA PROFILE 1.0.2"; |
1667 } else { | 1681 } else { |
1668 header = "JAVA PROFILE 1.0.1"; | 1682 header = "JAVA PROFILE 1.0.1"; |
1669 } | 1683 } |
1684 | |
1670 // header is few bytes long - no chance to overflow int | 1685 // header is few bytes long - no chance to overflow int |
1671 writer()->write_raw((void*)header, (int)strlen(header)); | 1686 writer()->write_raw((void*)header, (int)strlen(header)); |
1672 writer()->write_u1(0); // terminator | 1687 writer()->write_u1(0); // terminator |
1673 writer()->write_u4(oopSize); | 1688 writer()->write_u4(oopSize); |
1674 writer()->write_u8(os::javaTimeMillis()); | 1689 writer()->write_u8(os::javaTimeMillis()); |
1721 SystemDictionary::always_strong_oops_do(&class_dumper); | 1736 SystemDictionary::always_strong_oops_do(&class_dumper); |
1722 | 1737 |
1723 // fixes up the length of the dump record. In the case of a segmented | 1738 // fixes up the length of the dump record. In the case of a segmented |
1724 // heap then the HPROF_HEAP_DUMP_END record is also written. | 1739 // heap then the HPROF_HEAP_DUMP_END record is also written. |
1725 end_of_dump(); | 1740 end_of_dump(); |
1741 | |
1742 // Now we clear the global variables, so that a future dumper might run. | |
1743 clear_global_dumper(); | |
1744 clear_global_writer(); | |
1726 } | 1745 } |
1727 | 1746 |
1728 void VM_HeapDumper::dump_stack_traces() { | 1747 void VM_HeapDumper::dump_stack_traces() { |
1729 // write a HPROF_TRACE record without any frames to be referenced as object alloc sites | 1748 // write a HPROF_TRACE record without any frames to be referenced as object alloc sites |
1730 DumperSupport::write_header(writer(), HPROF_TRACE, 3*sizeof(u4)); | 1749 DumperSupport::write_header(writer(), HPROF_TRACE, 3*sizeof(u4)); |
1788 return -1; | 1807 return -1; |
1789 } | 1808 } |
1790 | 1809 |
1791 // generate the dump | 1810 // generate the dump |
1792 VM_HeapDumper dumper(&writer, _gc_before_heap_dump); | 1811 VM_HeapDumper dumper(&writer, _gc_before_heap_dump); |
1793 VMThread::execute(&dumper); | 1812 if (Thread::current()->is_VM_thread()) { |
1813 assert(SafepointSynchronize::is_at_safepoint(), "Expected to be called at a safepoint"); | |
1814 dumper.doit(); | |
1815 } else { | |
1816 VMThread::execute(&dumper); | |
1817 } | |
1794 | 1818 |
1795 // close dump file and record any error that the writer may have encountered | 1819 // close dump file and record any error that the writer may have encountered |
1796 writer.close(); | 1820 writer.close(); |
1797 set_error(writer.error()); | 1821 set_error(writer.error()); |
1798 | 1822 |
1843 _error = os::strdup(error); | 1867 _error = os::strdup(error); |
1844 assert(_error != NULL, "allocation failure"); | 1868 assert(_error != NULL, "allocation failure"); |
1845 } | 1869 } |
1846 } | 1870 } |
1847 | 1871 |
1848 | 1872 // Called by error reporting by a single Java thread outside of a JVM safepoint, |
1849 // Called by error reporting | 1873 // or by heap dumping by the VM thread during a (GC) safepoint. Thus, these various |
1874 // callers are strictly serialized and guaranteed not to interfere below. For more | |
1875 // general use, however, this method will need modification to prevent | |
1876 // inteference when updating the static variables base_path and dump_file_seq below. | |
1850 void HeapDumper::dump_heap() { | 1877 void HeapDumper::dump_heap() { |
1851 static char path[JVM_MAXPATHLEN]; | 1878 static char base_path[JVM_MAXPATHLEN] = {'\0'}; |
1879 static uint dump_file_seq = 0; | |
1880 char my_path[JVM_MAXPATHLEN] = {'\0'}; | |
1852 | 1881 |
1853 // The dump file defaults to java_pid<pid>.hprof in the current working | 1882 // The dump file defaults to java_pid<pid>.hprof in the current working |
1854 // directory. HeapDumpPath=<file> can be used to specify an alternative | 1883 // directory. HeapDumpPath=<file> can be used to specify an alternative |
1855 // dump file name or a directory where dump file is created. | 1884 // dump file name or a directory where dump file is created. |
1856 bool use_default_filename = true; | 1885 if (dump_file_seq == 0) { // first time in, we initialize base_path |
1857 if (HeapDumpPath == NULL || HeapDumpPath[0] == '\0') { | 1886 bool use_default_filename = true; |
1858 path[0] = '\0'; // HeapDumpPath=<file> not specified | 1887 if (HeapDumpPath == NULL || HeapDumpPath[0] == '\0') { |
1859 } else { | 1888 // HeapDumpPath=<file> not specified |
1860 assert(strlen(HeapDumpPath) < sizeof(path), "HeapDumpPath too long"); | |
1861 strcpy(path, HeapDumpPath); | |
1862 // check if the path is a directory (must exist) | |
1863 DIR* dir = os::opendir(path); | |
1864 if (dir == NULL) { | |
1865 use_default_filename = false; | |
1866 } else { | 1889 } else { |
1867 // HeapDumpPath specified a directory. We append a file separator | 1890 assert(strlen(HeapDumpPath) < sizeof(base_path), "HeapDumpPath too long"); |
1868 // (if needed). | 1891 strcpy(base_path, HeapDumpPath); |
1869 os::closedir(dir); | 1892 // check if the path is a directory (must exist) |
1870 size_t fs_len = strlen(os::file_separator()); | 1893 DIR* dir = os::opendir(base_path); |
1871 if (strlen(path) >= fs_len) { | 1894 if (dir == NULL) { |
1872 char* end = path; | 1895 use_default_filename = false; |
1873 end += (strlen(path) - fs_len); | 1896 } else { |
1874 if (strcmp(end, os::file_separator()) != 0) { | 1897 // HeapDumpPath specified a directory. We append a file separator |
1875 assert(strlen(path) + strlen(os::file_separator()) < sizeof(path), | 1898 // (if needed). |
1876 "HeapDumpPath too long"); | 1899 os::closedir(dir); |
1877 strcat(path, os::file_separator()); | 1900 size_t fs_len = strlen(os::file_separator()); |
1901 if (strlen(base_path) >= fs_len) { | |
1902 char* end = base_path; | |
1903 end += (strlen(base_path) - fs_len); | |
1904 if (strcmp(end, os::file_separator()) != 0) { | |
1905 assert(strlen(base_path) + strlen(os::file_separator()) < sizeof(base_path), | |
1906 "HeapDumpPath too long"); | |
1907 strcat(base_path, os::file_separator()); | |
1908 } | |
1878 } | 1909 } |
1879 } | 1910 } |
1880 } | 1911 } |
1881 } | 1912 // If HeapDumpPath wasn't a file name then we append the default name |
1882 // If HeapDumpPath wasn't a file name then we append the default name | 1913 if (use_default_filename) { |
1883 if (use_default_filename) { | 1914 char fn[32]; |
1884 char fn[32]; | 1915 sprintf(fn, "java_pid%d", os::current_process_id()); |
1885 sprintf(fn, "java_pid%d.hprof", os::current_process_id()); | 1916 assert(strlen(base_path) + strlen(fn) < sizeof(base_path), "HeapDumpPath too long"); |
1886 assert(strlen(path) + strlen(fn) < sizeof(path), "HeapDumpPath too long"); | 1917 strcat(base_path, fn); |
1887 strcat(path, fn); | 1918 } |
1888 } | 1919 assert(strlen(base_path) < sizeof(my_path), "Buffer too small"); |
1920 strcpy(my_path, base_path); | |
1921 } else { | |
1922 // Append a sequence number id for dumps following the first | |
1923 char fn[33]; | |
1924 sprintf(fn, ".%d", dump_file_seq); | |
1925 assert(strlen(base_path) + strlen(fn) < sizeof(my_path), "HeapDumpPath too long"); | |
1926 strcpy(my_path, base_path); | |
1927 strcat(my_path, fn); | |
1928 } | |
1929 dump_file_seq++; // increment seq number for next time we dump | |
1930 assert(strlen(".hprof") + strlen(my_path) < sizeof(my_path), "HeapDumpPath too long"); | |
1931 strcat(my_path, ".hprof"); | |
1889 | 1932 |
1890 HeapDumper dumper(false /* no GC before heap dump */, | 1933 HeapDumper dumper(false /* no GC before heap dump */, |
1891 true /* send to tty */); | 1934 true /* send to tty */); |
1892 dumper.dump(path); | 1935 dumper.dump(my_path); |
1893 } | 1936 } |