/java-thread-tools

Primary LanguageShellApache License 2.0Apache-2.0

Tools to manipulate Java thread dumps

jtgrep

It's a shell script that prints threads from a thread dump matching a pattern. It also prints the rest of the context from the thread dump, e.g. the header with the date.

It should work with thread dumps from the Oracle JVM and OpenJDK.

Examples

Print all threads where the string "GC" appears:

$ jtgrep GC thread-dump-19486-20140827-123856.log
2014-08-27 12:38:57
Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.2-b09 mixed mode):

"GC Daemon" daemon prio=10 tid=0x0000000019229800 nid=0x4e06 in Object.wait() [0x00002b9ea5dce000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000007280274b8> (a sun.misc.GC$LatencyLock)
        at sun.misc.GC$Daemon.run(GC.java:117)
        - locked <0x00000007280274b8> (a sun.misc.GC$LatencyLock)

   Locked ownable synchronizers:
        - None

"Surrogate Locker Thread (Concurrent GC)" daemon prio=10 tid=0x00000000176d0800 nid=0x4c2c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Gang worker#0 (Parallel GC Threads)" prio=10 tid=0x00000000174f7000 nid=0x4c20 runnable 

"Gang worker#1 (Parallel GC Threads)" prio=10 tid=0x00000000174f9000 nid=0x4c21 runnable 

"Gang worker#2 (Parallel GC Threads)" prio=10 tid=0x00000000174fb000 nid=0x4c22 runnable 

"Gang worker#3 (Parallel GC Threads)" prio=10 tid=0x00000000174fc800 nid=0x4c23 runnable 

"Gang worker#4 (Parallel GC Threads)" prio=10 tid=0x00000000174fe800 nid=0x4c24 runnable 

"Gang worker#5 (Parallel GC Threads)" prio=10 tid=0x0000000017500800 nid=0x4c25 runnable 

"Concurrent Mark-Sweep GC Thread" prio=10 tid=0x00000000175c3800 nid=0x4c28 runnable 
JNI global references: 878

$

It also works from standard input, if no files are named or if a single hyphen-minus (-) is given as file name:

$ cat thread-dump-19486-20140827-123856.log | jtgrep nid=0x4e06
2014-08-27 12:38:57
Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.2-b09 mixed mode):

"GC Daemon" daemon prio=10 tid=0x0000000019229800 nid=0x4e06 in Object.wait() [0x00002b9ea5dce000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007280274b8> (a sun.misc.GC$LatencyLock)
    at sun.misc.GC$Daemon.run(GC.java:117)
    - locked <0x00000007280274b8> (a sun.misc.GC$LatencyLock)

   Locked ownable synchronizers:
    - None

JNI global references: 878

Just like grep, it can invert the matching:

$ jtgrep GC thread-dump-19486-20140827-123856.log | jtgrep -v Surrogate
2014-08-27 12:38:57
Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.2-b09 mixed mode):

"GC Daemon" daemon prio=10 tid=0x0000000019229800 nid=0x4e06 in Object.wait() [0x00002b9ea5dce000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007280274b8> (a sun.misc.GC$LatencyLock)
    at sun.misc.GC$Daemon.run(GC.java:117)
    - locked <0x00000007280274b8> (a sun.misc.GC$LatencyLock)

   Locked ownable synchronizers:
    - None

"Gang worker#0 (Parallel GC Threads)" prio=10 tid=0x00000000174f7000 nid=0x4c20 runnable 

"Gang worker#1 (Parallel GC Threads)" prio=10 tid=0x00000000174f9000 nid=0x4c21 runnable 

"Gang worker#2 (Parallel GC Threads)" prio=10 tid=0x00000000174fb000 nid=0x4c22 runnable 

"Gang worker#3 (Parallel GC Threads)" prio=10 tid=0x00000000174fc800 nid=0x4c23 runnable 

"Gang worker#4 (Parallel GC Threads)" prio=10 tid=0x00000000174fe800 nid=0x4c24 runnable 

"Gang worker#5 (Parallel GC Threads)" prio=10 tid=0x0000000017500800 nid=0x4c25 runnable 

"Concurrent Mark-Sweep GC Thread" prio=10 tid=0x00000000175c3800 nid=0x4c28 runnable 
JNI global references: 878

It can also ignore the case:

$ jtgrep GC thread-dump-19486-20140827-123856.log | jtgrep -vi surrogate
2014-08-27 12:38:57
Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.2-b09 mixed mode):

"GC Daemon" daemon prio=10 tid=0x0000000019229800 nid=0x4e06 in Object.wait() [0x00002b9ea5dce000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007280274b8> (a sun.misc.GC$LatencyLock)
    at sun.misc.GC$Daemon.run(GC.java:117)
    - locked <0x00000007280274b8> (a sun.misc.GC$LatencyLock)

   Locked ownable synchronizers:
    - None

"Gang worker#0 (Parallel GC Threads)" prio=10 tid=0x00000000174f7000 nid=0x4c20 runnable 

"Gang worker#1 (Parallel GC Threads)" prio=10 tid=0x00000000174f9000 nid=0x4c21 runnable 

"Gang worker#2 (Parallel GC Threads)" prio=10 tid=0x00000000174fb000 nid=0x4c22 runnable 

"Gang worker#3 (Parallel GC Threads)" prio=10 tid=0x00000000174fc800 nid=0x4c23 runnable 

"Gang worker#4 (Parallel GC Threads)" prio=10 tid=0x00000000174fe800 nid=0x4c24 runnable 

"Gang worker#5 (Parallel GC Threads)" prio=10 tid=0x0000000017500800 nid=0x4c25 runnable 

"Concurrent Mark-Sweep GC Thread" prio=10 tid=0x00000000175c3800 nid=0x4c28 runnable 
JNI global references: 878

Finally, it can also count threads instead of printing them, in which case the non-thread output is also suppressed:

$ jtgrep -c RUN thread-dump-7660-20140829-104003.log
11
$ jtgrep -c RUN thread-dump-7660-20140829-1040*.log
thread-dump-7660-20140829-104003.log:11
thread-dump-7660-20140829-104013.log:13
thread-dump-7660-20140829-104024.log:18
thread-dump-7660-20140829-104034.log:18
thread-dump-7660-20140829-104045.log:18
thread-dump-7660-20140829-104055.log:14
$ cat thread-dump-7660-20140829-1040*.log | jtgrep -c RUN
92

Usage

Running the command without any parameter will show its options:

$ jtgrep 
Usage: jtgrep [-i] [-v] PATTERN [FILE...]
    -c Replace normal output by a count of matching threads for each input file
    -h Suppress the prefixing of file names on output when counting
    -H Print the file name for each match when counting
    -i Ignore case distinctions in the pattern
    -v Invert the sense of matching, to select non-matching threads

Dependencies

It works best with GNU awk, as mawk doesn't have an option to ignore the case.

jtid

It's a shell script that prints thread names with their decimal ID, usable in the OS context. This information can for example be used to read from /proc/[PID]/task/[TID]/stat.

Examples

$ jtgrep GC thread-dump-7660-20140829-104003.log | jtid
"Surrogate Locker Thread (Concurrent GC)" 7674
"Gang worker#0 (Parallel GC Threads)" 7662
"Gang worker#1 (Parallel GC Threads)" 7663
"Gang worker#2 (Parallel GC Threads)" 7664
"Gang worker#3 (Parallel GC Threads)" 7665
"Gang worker#4 (Parallel GC Threads)" 7666
"Gang worker#5 (Parallel GC Threads)" 7667
"Concurrent Mark-Sweep GC Thread" 7670

The PID of the JVM being 7660, we could read /proc/7660/task/7670/stat to get the ID of the last processor used by the CMS thread (as the 39th field).

Feedback welcome!

Add a comment, create an issue, ping me on Twitter...


Licensed under the Apache License, Version 2.0