#!/usr/bin/perl -w # # stackcollapse-go.pl collapse golang samples into single lines. # # Parses golang smaples generated by "go tool pprof" and outputs stacks as # single lines, with methods separated by semicolons, and then a space and an # occurrence count. For use with flamegraph.pl. # # USAGE: ./stackcollapse-go.pl infile > outfile # # Example Input: # ... # Samples: # samples/count cpu/nanoseconds # 1 10000000: 1 2 # 2 10000000: 3 2 # 1 10000000: 4 2 # ... # Locations # 1: 0x58b265 scanblock :0 s=0 # 2: 0x599530 GC :0 s=0 # 3: 0x58a999 flushptrbuf :0 s=0 # 4: 0x58d6a8 runtime.MSpan_Sweep :0 s=0 # ... # Mappings # ... # # Example Output: # # GC;flushptrbuf 2 # GC;runtime.MSpan_Sweep 1 # GC;scanblock 1 # # Input may contain many stacks as generated from go tool pprof: # # go tool pprof -seconds=60 -raw -output=a.pprof http://$ADDR/debug/pprof/profile # # For format of text profile, See golang/src/internal/pprof/profile/profile.go # # Copyright 2017 Sijie Yang (yangsijie@baidu.com). 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) # # 16-Jan-2017 Sijie Yang Created this. use strict; use Getopt::Long; # tunables my $help = 0; sub usage { die < outfile\n USAGE_END } GetOptions( 'help' => \$help, ) or usage(); $help && usage(); # internals my $state = "ignore"; my %stacks; my %frames; my %collapsed; sub remember_stack { my ($stack, $count) = @_; $stacks{$stack} += $count; } # # Output stack string in required format. For example, for the following samples, # format_statck() would return GC;runtime.MSpan_Sweep for stack "4 2" # # Locations # 1: 0x58b265 scanblock :0 s=0 # 2: 0x599530 GC :0 s=0 # 3: 0x58a999 flushptrbuf :0 s=0 # 4: 0x58d6a8 runtime.MSpan_Sweep :0 s=0 # sub format_statck { my ($stack) = @_; my @loc_list = split(/ /, $stack); for (my $i=0; $i<=$#loc_list; $i++) { my $loc_name = $frames{$loc_list[$i]}; $loc_list[$i] = $loc_name if ($loc_name); } return join(";", reverse(@loc_list)); } foreach (<>) { next if m/^#/; chomp; if ($state eq "ignore") { if (/Samples:/) { $state = "sample"; next; } } elsif ($state eq "sample") { if (/^\s*([0-9]+)\s*[0-9]+: ([0-9 ]+)/) { my $samples = $1; my $stack = $2; remember_stack($stack, $samples); } elsif (/Locations/) { $state = "location"; next; } } elsif ($state eq "location") { if (/^\s*([0-9]*): 0x[0-9a-f]+ (M=[0-9]+ )?([^ ]+) .*/) { my $loc_id = $1; my $loc_name = $3; $frames{$loc_id} = $loc_name; } elsif (/Mappings/) { $state = "mapping"; last; } } } foreach my $k (keys %stacks) { my $stack = format_statck($k); my $count = $stacks{$k}; $collapsed{$stack} += $count; } foreach my $k (sort { $a cmp $b } keys %collapsed) { print "$k $collapsed{$k}\n"; }