Mercurial > hg > graal-jvmci-8
comparison agent/src/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java @ 12820:8ef918538e22
6313383: SA: Update jmap to support HPROF binary format "JAVA PROFILE 1.0.2"
Summary: Adds support for large(>4G) heap dumps in hprof format. Adds tests and updates testlibrary.
Reviewed-by: sla, allwin
Contributed-by: fredrik.arvidsson@oracle.com
author | sla |
---|---|
date | Fri, 04 Oct 2013 13:44:49 +0200 |
parents | 6bd680e9ea35 |
children | de6a9e811145 |
comparison
equal
deleted
inserted
replaced
12818:763705f0fec3 | 12820:8ef918538e22 |
---|---|
42 * the network). | 42 * the network). |
43 * | 43 * |
44 * WARNING: This format is still under development, and is subject to | 44 * WARNING: This format is still under development, and is subject to |
45 * change without notice. | 45 * change without notice. |
46 * | 46 * |
47 * header "JAVA PROFILE 1.0.1" (0-terminated) | 47 * header "JAVA PROFILE 1.0.1" or "JAVA PROFILE 1.0.2" (0-terminated) |
48 * u4 size of identifiers. Identifiers are used to represent | 48 * u4 size of identifiers. Identifiers are used to represent |
49 * UTF8 strings, objects, stack traces, etc. They usually | 49 * UTF8 strings, objects, stack traces, etc. They usually |
50 * have the same size as host pointers. For example, on | 50 * have the same size as host pointers. For example, on |
51 * Solaris and Win32, the size is 4. | 51 * Solaris and Win32, the size is 4. |
52 * u4 high word | 52 * u4 high word |
290 * | 290 * |
291 * u4 0x00000001: alloc traces on/off | 291 * u4 0x00000001: alloc traces on/off |
292 * 0x00000002: cpu sampling on/off | 292 * 0x00000002: cpu sampling on/off |
293 * u2 stack trace depth | 293 * u2 stack trace depth |
294 * | 294 * |
295 * | |
296 * When the header is "JAVA PROFILE 1.0.2" a heap dump can optionally | |
297 * be generated as a sequence of heap dump segments. This sequence is | |
298 * terminated by an end record. The additional tags allowed by format | |
299 * "JAVA PROFILE 1.0.2" are: | |
300 * | |
301 * HPROF_HEAP_DUMP_SEGMENT denote a heap dump segment | |
302 * | |
303 * [heap dump sub-records]* | |
304 * The same sub-record types allowed by HPROF_HEAP_DUMP | |
305 * | |
306 * HPROF_HEAP_DUMP_END denotes the end of a heap dump | |
307 * | |
295 */ | 308 */ |
296 | 309 |
297 public class HeapHprofBinWriter extends AbstractHeapGraphWriter { | 310 public class HeapHprofBinWriter extends AbstractHeapGraphWriter { |
311 | |
312 // The heap size threshold used to determine if segmented format | |
313 // ("JAVA PROFILE 1.0.2") should be used. | |
314 private static final long HPROF_SEGMENTED_HEAP_DUMP_THRESHOLD = 2L * 0x40000000; | |
315 | |
316 // The approximate size of a heap segment. Used to calculate when to create | |
317 // a new segment. | |
318 private static final long HPROF_SEGMENTED_HEAP_DUMP_SEGMENT_SIZE = 1L * 0x40000000; | |
319 | |
298 // hprof binary file header | 320 // hprof binary file header |
299 private static final String HPROF_HEADER = "JAVA PROFILE 1.0.1"; | 321 private static final String HPROF_HEADER_1_0_1 = "JAVA PROFILE 1.0.1"; |
322 private static final String HPROF_HEADER_1_0_2 = "JAVA PROFILE 1.0.2"; | |
300 | 323 |
301 // constants in enum HprofTag | 324 // constants in enum HprofTag |
302 private static final int HPROF_UTF8 = 0x01; | 325 private static final int HPROF_UTF8 = 0x01; |
303 private static final int HPROF_LOAD_CLASS = 0x02; | 326 private static final int HPROF_LOAD_CLASS = 0x02; |
304 private static final int HPROF_UNLOAD_CLASS = 0x03; | 327 private static final int HPROF_UNLOAD_CLASS = 0x03; |
309 private static final int HPROF_START_THREAD = 0x0A; | 332 private static final int HPROF_START_THREAD = 0x0A; |
310 private static final int HPROF_END_THREAD = 0x0B; | 333 private static final int HPROF_END_THREAD = 0x0B; |
311 private static final int HPROF_HEAP_DUMP = 0x0C; | 334 private static final int HPROF_HEAP_DUMP = 0x0C; |
312 private static final int HPROF_CPU_SAMPLES = 0x0D; | 335 private static final int HPROF_CPU_SAMPLES = 0x0D; |
313 private static final int HPROF_CONTROL_SETTINGS = 0x0E; | 336 private static final int HPROF_CONTROL_SETTINGS = 0x0E; |
337 | |
338 // 1.0.2 record types | |
339 private static final int HPROF_HEAP_DUMP_SEGMENT = 0x1C; | |
340 private static final int HPROF_HEAP_DUMP_END = 0x2C; | |
314 | 341 |
315 // Heap dump constants | 342 // Heap dump constants |
316 // constants in enum HprofGcTag | 343 // constants in enum HprofGcTag |
317 private static final int HPROF_GC_ROOT_UNKNOWN = 0xFF; | 344 private static final int HPROF_GC_ROOT_UNKNOWN = 0xFF; |
318 private static final int HPROF_GC_ROOT_JNI_GLOBAL = 0x01; | 345 private static final int HPROF_GC_ROOT_JNI_GLOBAL = 0x01; |
350 private static final int JVM_SIGNATURE_FLOAT = 'F'; | 377 private static final int JVM_SIGNATURE_FLOAT = 'F'; |
351 private static final int JVM_SIGNATURE_DOUBLE = 'D'; | 378 private static final int JVM_SIGNATURE_DOUBLE = 'D'; |
352 private static final int JVM_SIGNATURE_ARRAY = '['; | 379 private static final int JVM_SIGNATURE_ARRAY = '['; |
353 private static final int JVM_SIGNATURE_CLASS = 'L'; | 380 private static final int JVM_SIGNATURE_CLASS = 'L'; |
354 | 381 |
355 | |
356 public synchronized void write(String fileName) throws IOException { | 382 public synchronized void write(String fileName) throws IOException { |
357 // open file stream and create buffered data output stream | 383 // open file stream and create buffered data output stream |
358 FileOutputStream fos = new FileOutputStream(fileName); | 384 fos = new FileOutputStream(fileName); |
359 FileChannel chn = fos.getChannel(); | |
360 out = new DataOutputStream(new BufferedOutputStream(fos)); | 385 out = new DataOutputStream(new BufferedOutputStream(fos)); |
361 | 386 |
362 VM vm = VM.getVM(); | 387 VM vm = VM.getVM(); |
363 dbg = vm.getDebugger(); | 388 dbg = vm.getDebugger(); |
364 objectHeap = vm.getObjectHeap(); | 389 objectHeap = vm.getObjectHeap(); |
383 INT_SIZE = objectHeap.getIntSize(); | 408 INT_SIZE = objectHeap.getIntSize(); |
384 LONG_SIZE = objectHeap.getLongSize(); | 409 LONG_SIZE = objectHeap.getLongSize(); |
385 FLOAT_SIZE = objectHeap.getFloatSize(); | 410 FLOAT_SIZE = objectHeap.getFloatSize(); |
386 DOUBLE_SIZE = objectHeap.getDoubleSize(); | 411 DOUBLE_SIZE = objectHeap.getDoubleSize(); |
387 | 412 |
413 // Check weather we should dump the heap as segments | |
414 useSegmentedHeapDump = vm.getUniverse().heap().used() > HPROF_SEGMENTED_HEAP_DUMP_THRESHOLD; | |
415 | |
388 // hprof bin format header | 416 // hprof bin format header |
389 writeFileHeader(); | 417 writeFileHeader(); |
390 | 418 |
391 // dummy stack trace without any frames so that | 419 // dummy stack trace without any frames so that |
392 // HAT can be run without -stack false option | 420 // HAT can be run without -stack false option |
393 writeDummyTrace(); | 421 writeDummyTrace(); |
394 | 422 |
395 // hprof UTF-8 symbols section | 423 // hprof UTF-8 symbols section |
396 writeSymbols(); | 424 writeSymbols(); |
425 | |
397 // HPROF_LOAD_CLASS records for all classes | 426 // HPROF_LOAD_CLASS records for all classes |
398 writeClasses(); | 427 writeClasses(); |
399 | 428 |
400 // write heap data now | |
401 out.writeByte((byte)HPROF_HEAP_DUMP); | |
402 out.writeInt(0); // relative timestamp | |
403 | |
404 // remember position of dump length, we will fixup | |
405 // length later - hprof format requires length. | |
406 out.flush(); | |
407 long dumpStart = chn.position(); | |
408 | |
409 // write dummy length of 0 and we'll fix it later. | |
410 out.writeInt(0); | |
411 | |
412 // write CLASS_DUMP records | 429 // write CLASS_DUMP records |
413 writeClassDumpRecords(); | 430 writeClassDumpRecords(); |
414 | 431 |
415 // this will write heap data into the buffer stream | 432 // this will write heap data into the buffer stream |
416 super.write(); | 433 super.write(); |
434 | |
435 // flush buffer stream. | |
436 out.flush(); | |
437 | |
438 // Fill in final length | |
439 fillInHeapRecordLength(); | |
440 | |
441 if (useSegmentedHeapDump) { | |
442 // Write heap segment-end record | |
443 out.writeByte((byte) HPROF_HEAP_DUMP_END); | |
444 out.writeInt(0); | |
445 out.writeInt(0); | |
446 } | |
417 | 447 |
418 // flush buffer stream and throw it. | 448 // flush buffer stream and throw it. |
419 out.flush(); | 449 out.flush(); |
420 out = null; | 450 out = null; |
421 | 451 |
452 // close the file stream | |
453 fos.close(); | |
454 } | |
455 | |
456 @Override | |
457 protected void writeHeapRecordPrologue() throws IOException { | |
458 if (currentSegmentStart == 0) { | |
459 // write heap data header, depending on heap size use segmented heap | |
460 // format | |
461 out.writeByte((byte) (useSegmentedHeapDump ? HPROF_HEAP_DUMP_SEGMENT | |
462 : HPROF_HEAP_DUMP)); | |
463 out.writeInt(0); | |
464 | |
465 // remember position of dump length, we will fixup | |
466 // length later - hprof format requires length. | |
467 out.flush(); | |
468 currentSegmentStart = fos.getChannel().position(); | |
469 | |
470 // write dummy length of 0 and we'll fix it later. | |
471 out.writeInt(0); | |
472 } | |
473 } | |
474 | |
475 @Override | |
476 protected void writeHeapRecordEpilogue() throws IOException { | |
477 if (useSegmentedHeapDump) { | |
478 out.flush(); | |
479 if ((fos.getChannel().position() - currentSegmentStart - 4) >= HPROF_SEGMENTED_HEAP_DUMP_SEGMENT_SIZE) { | |
480 fillInHeapRecordLength(); | |
481 currentSegmentStart = 0; | |
482 } | |
483 } | |
484 } | |
485 | |
486 private void fillInHeapRecordLength() throws IOException { | |
487 | |
422 // now get current position to calculate length | 488 // now get current position to calculate length |
423 long dumpEnd = chn.position(); | 489 long dumpEnd = fos.getChannel().position(); |
490 | |
424 // calculate length of heap data | 491 // calculate length of heap data |
425 int dumpLen = (int) (dumpEnd - dumpStart - 4); | 492 long dumpLenLong = (dumpEnd - currentSegmentStart - 4L); |
493 | |
494 // Check length boundary, overflow could happen but is _very_ unlikely | |
495 if(dumpLenLong >= (4L * 0x40000000)){ | |
496 throw new RuntimeException("Heap segment size overflow."); | |
497 } | |
498 | |
499 // Save the current position | |
500 long currentPosition = fos.getChannel().position(); | |
426 | 501 |
427 // seek the position to write length | 502 // seek the position to write length |
428 chn.position(dumpStart); | 503 fos.getChannel().position(currentSegmentStart); |
504 | |
505 int dumpLen = (int) dumpLenLong; | |
429 | 506 |
430 // write length as integer | 507 // write length as integer |
431 fos.write((dumpLen >>> 24) & 0xFF); | 508 fos.write((dumpLen >>> 24) & 0xFF); |
432 fos.write((dumpLen >>> 16) & 0xFF); | 509 fos.write((dumpLen >>> 16) & 0xFF); |
433 fos.write((dumpLen >>> 8) & 0xFF); | 510 fos.write((dumpLen >>> 8) & 0xFF); |
434 fos.write((dumpLen >>> 0) & 0xFF); | 511 fos.write((dumpLen >>> 0) & 0xFF); |
435 | 512 |
436 // close the file stream | 513 //Reset to previous current position |
437 fos.close(); | 514 fos.getChannel().position(currentPosition); |
438 } | 515 } |
439 | 516 |
440 private void writeClassDumpRecords() throws IOException { | 517 private void writeClassDumpRecords() throws IOException { |
441 SystemDictionary sysDict = VM.getVM().getSystemDictionary(); | 518 SystemDictionary sysDict = VM.getVM().getSystemDictionary(); |
442 try { | 519 try { |
443 sysDict.allClassesDo(new SystemDictionary.ClassVisitor() { | 520 sysDict.allClassesDo(new SystemDictionary.ClassVisitor() { |
444 public void visit(Klass k) { | 521 public void visit(Klass k) { |
445 try { | 522 try { |
523 writeHeapRecordPrologue(); | |
446 writeClassDumpRecord(k); | 524 writeClassDumpRecord(k); |
525 writeHeapRecordEpilogue(); | |
447 } catch (IOException e) { | 526 } catch (IOException e) { |
448 throw new RuntimeException(e); | 527 throw new RuntimeException(e); |
449 } | 528 } |
450 } | 529 } |
451 }); | 530 }); |
882 } | 961 } |
883 | 962 |
884 // writes hprof binary file header | 963 // writes hprof binary file header |
885 private void writeFileHeader() throws IOException { | 964 private void writeFileHeader() throws IOException { |
886 // version string | 965 // version string |
887 out.writeBytes(HPROF_HEADER); | 966 if(useSegmentedHeapDump) { |
967 out.writeBytes(HPROF_HEADER_1_0_2); | |
968 } | |
969 else { | |
970 out.writeBytes(HPROF_HEADER_1_0_1); | |
971 } | |
888 out.writeByte((byte)'\0'); | 972 out.writeByte((byte)'\0'); |
889 | 973 |
890 // write identifier size. we use pointers as identifiers. | 974 // write identifier size. we use pointers as identifiers. |
891 out.writeInt(OBJ_ID_SIZE); | 975 out.writeInt(OBJ_ID_SIZE); |
892 | 976 |
974 // stack trace with this id. | 1058 // stack trace with this id. |
975 private static final int DUMMY_STACK_TRACE_ID = 1; | 1059 private static final int DUMMY_STACK_TRACE_ID = 1; |
976 private static final int EMPTY_FRAME_DEPTH = -1; | 1060 private static final int EMPTY_FRAME_DEPTH = -1; |
977 | 1061 |
978 private DataOutputStream out; | 1062 private DataOutputStream out; |
1063 private FileOutputStream fos; | |
979 private Debugger dbg; | 1064 private Debugger dbg; |
980 private ObjectHeap objectHeap; | 1065 private ObjectHeap objectHeap; |
981 private SymbolTable symTbl; | 1066 private SymbolTable symTbl; |
982 | 1067 |
983 // oopSize of the debuggee | 1068 // oopSize of the debuggee |
984 private int OBJ_ID_SIZE; | 1069 private int OBJ_ID_SIZE; |
1070 | |
1071 // Added for hprof file format 1.0.2 support | |
1072 private boolean useSegmentedHeapDump; | |
1073 private long currentSegmentStart; | |
985 | 1074 |
986 private long BOOLEAN_BASE_OFFSET; | 1075 private long BOOLEAN_BASE_OFFSET; |
987 private long BYTE_BASE_OFFSET; | 1076 private long BYTE_BASE_OFFSET; |
988 private long CHAR_BASE_OFFSET; | 1077 private long CHAR_BASE_OFFSET; |
989 private long SHORT_BASE_OFFSET; | 1078 private long SHORT_BASE_OFFSET; |
1003 private long DOUBLE_SIZE; | 1092 private long DOUBLE_SIZE; |
1004 | 1093 |
1005 private static class ClassData { | 1094 private static class ClassData { |
1006 int instSize; | 1095 int instSize; |
1007 List fields; | 1096 List fields; |
1097 | |
1008 ClassData(int instSize, List fields) { | 1098 ClassData(int instSize, List fields) { |
1009 this.instSize = instSize; | 1099 this.instSize = instSize; |
1010 this.fields = fields; | 1100 this.fields = fields; |
1011 } | 1101 } |
1012 } | 1102 } |