123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- #!/usr/bin/perl -w
- #
- # stackcollapse-jstack.pl collapse jstack samples into single lines.
- #
- # Parses Java stacks generated by jstack(1) and outputs RUNNABLE stacks as
- # single lines, with methods separated by semicolons, and then a space and an
- # occurrence count. This also filters some other "RUNNABLE" states that we
- # know are probably not running, such as epollWait. For use with flamegraph.pl.
- #
- # You want this to process the output of at least 100 jstack(1)s. ie, run it
- # 100 times with a sleep interval, and append to a file. This is really a poor
- # man's Java profiler, due to the overheads of jstack(1), and how it isn't
- # capturing stacks asynchronously. For a better profiler, see:
- # http://www.brendangregg.com/blog/2014-06-12/java-flame-graphs.html
- #
- # USAGE: ./stackcollapse-jstack.pl infile > outfile
- #
- # Example input:
- #
- # "MyProg" #273 daemon prio=9 os_prio=0 tid=0x00007f273c038800 nid=0xe3c runnable [0x00007f28a30f2000]
- # java.lang.Thread.State: RUNNABLE
- # at java.net.SocketInputStream.socketRead0(Native Method)
- # at java.net.SocketInputStream.read(SocketInputStream.java:121)
- # ...
- # at java.lang.Thread.run(Thread.java:744)
- #
- # Example output:
- #
- # MyProg;java.lang.Thread.run;java.net.SocketInputStream.read;java.net.SocketInputStream.socketRead0 1
- #
- # Input may be created and processed using:
- #
- # i=0; while (( i++ < 200 )); do jstack PID >> out.jstacks; sleep 10; done
- # cat out.jstacks | ./stackcollapse-jstack.pl > out.stacks-folded
- #
- # WARNING: jstack(1) incurs overheads. Test before use, or use a real profiler.
- #
- # Copyright 2014 Brendan Gregg. All rights reserved.
- #
- # This program is free software; you can redistribute it and/or
- # modify it under the terms of the GNU General Public License
- # as published by the Free Software Foundation; either version 2
- # of the License, or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software Foundation,
- # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- #
- # (http://www.gnu.org/copyleft/gpl.html)
- #
- # 14-Sep-2014 Brendan Gregg Created this.
- use strict;
- use Getopt::Long;
- # tunables
- my $include_tname = 1; # include thread names in stacks
- my $include_tid = 0; # include thread IDs in stacks
- my $shorten_pkgs = 0; # shorten package names
- my $help = 0;
- sub usage {
- die <<USAGE_END;
- USAGE: $0 [options] infile > outfile\n
- --include-tname
- --no-include-tname # include/omit thread names in stacks (default: include)
- --include-tid
- --no-include-tid # include/omit thread IDs in stacks (default: omit)
- --shorten-pkgs
- --no-shorten-pkgs # (don't) shorten package names (default: don't shorten)
- eg,
- $0 --no-include-tname stacks.txt > collapsed.txt
- USAGE_END
- }
- GetOptions(
- 'include-tname!' => \$include_tname,
- 'include-tid!' => \$include_tid,
- 'shorten-pkgs!' => \$shorten_pkgs,
- 'help' => \$help,
- ) or usage();
- $help && usage();
- # internals
- my %collapsed;
- sub remember_stack {
- my ($stack, $count) = @_;
- $collapsed{$stack} += $count;
- }
- my @stack;
- my $tname;
- my $state = "?";
- foreach (<>) {
- next if m/^#/;
- chomp;
- if (m/^$/) {
- # only include RUNNABLE states
- goto clear if $state ne "RUNNABLE";
- # save stack
- if (defined $tname) { unshift @stack, $tname; }
- remember_stack(join(";", @stack), 1) if @stack;
- clear:
- undef @stack;
- undef $tname;
- $state = "?";
- next;
- }
- #
- # While parsing jstack output, the $state variable may be altered from
- # RUNNABLE to other states. This causes the stacks to be filtered later,
- # since only RUNNABLE stacks are included.
- #
- if (/^"([^"]*)/) {
- my $name = $1;
- if ($include_tname) {
- $tname = $name;
- unless ($include_tid) {
- $tname =~ s/-\d+$//;
- }
- }
- # set state for various background threads
- $state = "BACKGROUND" if $name =~ /C. CompilerThread/;
- $state = "BACKGROUND" if $name =~ /Signal Dispatcher/;
- $state = "BACKGROUND" if $name =~ /Service Thread/;
- $state = "BACKGROUND" if $name =~ /Attach Listener/;
- } elsif (/java.lang.Thread.State: (\S+)/) {
- $state = $1 if $state eq "?";
- } elsif (/^\s*at ([^\(]*)/) {
- my $func = $1;
- if ($shorten_pkgs) {
- my ($pkgs, $clsFunc) = ( $func =~ m/(.*\.)([^.]+\.[^.]+)$/ );
- $pkgs =~ s/(\w)\w*/$1/g;
- $func = $pkgs . $clsFunc;
- }
- unshift @stack, $func;
- # fix state for epollWait
- $state = "WAITING" if $func =~ /epollWait/;
- $state = "WAITING" if $func =~ /EPoll\.wait/;
- # fix state for various networking functions
- $state = "NETWORK" if $func =~ /socketAccept$/;
- $state = "NETWORK" if $func =~ /Socket.*accept0$/;
- $state = "NETWORK" if $func =~ /socketRead0$/;
- } elsif (/^\s*-/ or /^2\d\d\d-/ or /^Full thread dump/ or
- /^JNI global references:/) {
- # skip these info lines
- next;
- } else {
- warn "Unrecognized line: $_";
- }
- }
- foreach my $k (sort { $a cmp $b } keys %collapsed) {
- print "$k $collapsed{$k}\n";
- }
|