The OpenJDK Mac OS X Port now has dtrace probes. Many of these probes are enabled automatically, though some resource intensive probes must be
enabled with a JVM argument. Java code JSDT probes are not supported. (For dtrace implementation information, see Mac OS X Port Dtrace Probe Implementation.)
What Is It Good For?
DTrace and its probes allow the Java developer and plain old Java user to learn more about the execution of a given Java process without
modifying any of the code. For example, if you want to know when garbage collection starts in a running java program with pid 12345, you'd
type:
% sudo /usr/sbin/dtrace -n 'hotspot$1:::gc-end{printf("GC: %d\n", timestamp);}' 1213 dtrace: description 'hotspot$1:::gc-end' matched 2 probes CPU ID FUNCTION:NAME 0 23653 _ZN15VM_GC_Operation13notify_gc_endEv:gc-end GC: 334679181 1 23653 _ZN15VM_GC_Operation13notify_gc_endEv:gc-end GC: 1487377551 0 23653 _ZN15VM_GC_Operation13notify_gc_endEv:gc-end GC: 3231453137
Of course, you'll also want to do more sophisticated things that don't fit on a command line, so you can put the
d program into a file, say gcEnd.d:
hotspot$1:::gc-end { printf("GC: %d\n", timestamp); }
and type:
% sudo /usr/sbin/dtrace -s gcEnd.d 1213
You can get a complete list of probes available in HotSpot by typing:
% sudo /usr/sbin/dtrace -l 1213 | grep hotspot 23638 hotspot1213 libjvm.dylib _ZN13instanceKlass15initialize_implE19instanceKlassHandleP6Thread [instanceKlass::initialize_impl] class-initialization-clinit 23639 hotspot1213 libjvm.dylib _ZN13instanceKlass15initialize_implE19instanceKlassHandleP6Thread [instanceKlass::initialize_impl] class-initialization-concurrent 23640 hotspot1213 libjvm.dylib _ZN13instanceKlass15initialize_implE19instanceKlassHandleP6Thread [instanceKlass::initialize_impl] class-initialization-end 23641 hotspot1213 libjvm.dylib _ZN13instanceKlass15initialize_implE19instanceKlassHandleP6Thread [instanceKlass::initialize_impl] class-initialization-erroneous 23642 hotspot1213 libjvm.dylib _ZN13instanceKlass15initialize_implE19instanceKlassHandleP6Thread [instanceKlass::initialize_impl] class-initialization-error ...
One can do much more with dtrace than simple probe entry/exit notification. There are examples and tutorials available
on the web, for example: http://dsc.sun.com/solaris/articles/dtrace_example.html. Let me give you some ideas with a
couple more examples.
First, lets extend the gcEnd.d program above to measure the time intervals occupied by garbage collecton. To compute
this, we need to record the time when gc begins, and then compute the difference in time when gc ends:
long startTime; hotspot$1:::gc-begin { startTime = timestamp; } hotspot$1:::gc-end { printf("GC time interval: %d\n", timestamp-startTime); }
My final example uses probes that are not enabled by default. These optional probes can be turned on with the
following JVM flags:
-XX:+DTraceMethodProbes -XX:+DTraceAllocProbes -XX:+DTraceMonitorProbes
Or turn them all on with:
-XX:+ExtendedDTraceProbes
Start a Java program with extended dtrace probes:
% java -XX:+ExtendedDTraceProbes -jar /Developer/Extras/Java/JFC/Java2D/Java2D.jar
Run dtrace with the following command (substituting the correct process id), wait a bit, and type control-c:
% sudo /usr/sbin/dtrace -s object_allocation_stat.d -p 1444 ^C Top 100 allocations by size: 40 java/lang/ref/SoftReference 192 [C 192 java/lang/String 2016 sun/java2d/loops/GraphicsPrimitiveMgr$PrimitiveSpec
The object_allocation_stat.d example code:
#!/usr/sbin/dtrace -Zs #pragma D option quiet #pragma D option destructive #pragma D option defaultargs #pragma D option bufsize=16m #pragma D option aggrate=100ms /* * Usage: * object_allocation_stat.d -p JAVA_PID [PAUSE_AT_STARTUP_FILE] * * This script collects statistics about object allocations. * * Notes: * - this probes are available only with '-XX:+ExtendedDtraceProbes' option, * so pass this option too. * - This script SLOW DOWN your java application. So, not be surprised :) * * * Note that Hotspot probes definitions are located in libjvm.so and * straightforward attempt to run d-script may fail, e.g.: * ./dscript.d -c "java HelloWorld" * "probe description hotspotPID:::probename does not match any probes" * This is because dtrace tries to enable probes before libjvm.so is loaded. * * To avoid this use one of following options to start java: * 1) -XX:+PauseAtStartup * 2) -XX:+PauseAtStartup -XX:PauseAtStartupFile=FILENAME * * In first case the file named 'vm.paused.JAVA_PID' will be created and * then java process will be paused till 'vm.paused.JAVA_PID' will be deleted. * In second case jvm will wait till 'FILENAME' will be deleted. * * Then you can launch d-script you want to run by passing JAVA_PID as -p <JAVA_PID> * and delete PAUSE_AT_STARTUP_FILE manually or by passing it as first argument * (PAUSE_AT_STARTUP_FILE) to d-script. * * NB: -XX:+PauseAtStartup requires -XX:+UnlockDiagnosticVMOptions * */ self char *str_ptr; self string class_name; long long ALLOCATED_OBJECTS_CNT; string PAUSE_AT_STARTUP_FILE; :::BEGIN / $1 != "" / { PAUSE_AT_STARTUP_FILE = $1; printf("rm %s\n\n", PAUSE_AT_STARTUP_FILE); system("rm %s", PAUSE_AT_STARTUP_FILE); } BEGIN { TOP_N = 100; ALLOCATED_OBJECTS_CNT = 0; } /* * hotspot:::object-alloc probe arguments: * arg0: uintptr_t, Java thread id * arg1: char*, a pointer to mUTF-8 string containing the name of * the class of the object being allocated * arg2: uintptr_t, the length of the class name (in bytes) * arg3: uintptr_t, the size of the object being allocated */ hotspot$target:::object-alloc { ALLOCATED_OBJECTS_CNT ++; self->str_ptr = (char*) copyin(arg1, args[2]+1); self->str_ptr[args[2]] = '\0'; self->class_name = (string) self->str_ptr; @allocs_count[self->class_name] = count(); @allocs_size[self->class_name] = sum(args[3]); /* printf("%s:\t[%u] class_name=%s (len=%u), size=%u\n", probename, args[0], self->class_name, args[2], args[3]); */ } :::END { printf("Top %d allocations by size:\n", TOP_N); trunc(@allocs_size, TOP_N); printa("%10@d %s\n", @allocs_size); printf("\n"); printf("Top %d allocations by count:\n", TOP_N); trunc(@allocs_count, TOP_N); printa("%10@d %s\n", @allocs_count); printf("\nTotal number of allocated objects: %d\n", ALLOCATED_OBJECTS_CNT); }