stackcollapse-perf.pl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. #!/usr/bin/perl -w
  2. #
  3. # stackcollapse-perf.pl collapse perf samples into single lines.
  4. #
  5. # Parses a list of multiline stacks generated by "perf script", and
  6. # outputs a semicolon separated stack followed by a space and a count.
  7. # If memory addresses (+0xd) are present, they are stripped, and resulting
  8. # identical stacks are colased with their counts summed.
  9. #
  10. # USAGE: ./stackcollapse-perf.pl [options] infile > outfile
  11. #
  12. # Run "./stackcollapse-perf.pl -h" to list options.
  13. #
  14. # Example input:
  15. #
  16. # swapper 0 [000] 158665.570607: cpu-clock:
  17. # ffffffff8103ce3b native_safe_halt ([kernel.kallsyms])
  18. # ffffffff8101c6a3 default_idle ([kernel.kallsyms])
  19. # ffffffff81013236 cpu_idle ([kernel.kallsyms])
  20. # ffffffff815bf03e rest_init ([kernel.kallsyms])
  21. # ffffffff81aebbfe start_kernel ([kernel.kallsyms].init.text)
  22. # [...]
  23. #
  24. # Example output:
  25. #
  26. # swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 1
  27. #
  28. # Input may be created and processed using:
  29. #
  30. # perf record -a -g -F 997 sleep 60
  31. # perf script | ./stackcollapse-perf.pl > out.stacks-folded
  32. #
  33. # The output of "perf script" should include stack traces. If these are missing
  34. # for you, try manually selecting the perf script output; eg:
  35. #
  36. # perf script -f comm,pid,tid,cpu,time,event,ip,sym,dso,trace | ...
  37. #
  38. # This is also required for the --pid or --tid options, so that the output has
  39. # both the PID and TID.
  40. #
  41. # Copyright 2012 Joyent, Inc. All rights reserved.
  42. # Copyright 2012 Brendan Gregg. All rights reserved.
  43. #
  44. # CDDL HEADER START
  45. #
  46. # The contents of this file are subject to the terms of the
  47. # Common Development and Distribution License (the "License").
  48. # You may not use this file except in compliance with the License.
  49. #
  50. # You can obtain a copy of the license at docs/cddl1.txt or
  51. # http://opensource.org/licenses/CDDL-1.0.
  52. # See the License for the specific language governing permissions
  53. # and limitations under the License.
  54. #
  55. # When distributing Covered Code, include this CDDL HEADER in each
  56. # file and include the License file at docs/cddl1.txt.
  57. # If applicable, add the following below this CDDL HEADER, with the
  58. # fields enclosed by brackets "[]" replaced with your own identifying
  59. # information: Portions Copyright [yyyy] [name of copyright owner]
  60. #
  61. # CDDL HEADER END
  62. #
  63. # 02-Mar-2012 Brendan Gregg Created this.
  64. # 02-Jul-2014 " " Added process name to stacks.
  65. use strict;
  66. use Getopt::Long;
  67. my %collapsed;
  68. sub remember_stack {
  69. my ($stack, $count) = @_;
  70. $collapsed{$stack} += $count;
  71. }
  72. my $annotate_kernel = 0; # put an annotation on kernel function
  73. my $annotate_jit = 0; # put an annotation on jit symbols
  74. my $annotate_all = 0; # enale all annotations
  75. my $include_pname = 1; # include process names in stacks
  76. my $include_pid = 0; # include process ID with process name
  77. my $include_tid = 0; # include process & thread ID with process name
  78. my $include_addrs = 0; # include raw address where a symbol can't be found
  79. my $tidy_java = 1; # condense Java signatures
  80. my $tidy_generic = 1; # clean up function names a little
  81. my $target_pname; # target process name from perf invocation
  82. my $event_filter = ""; # event type filter, defaults to first encountered event
  83. my $event_defaulted = 0; # whether we defaulted to an event (none provided)
  84. my $event_warning = 0; # if we printed a warning for the event
  85. my $show_inline = 0;
  86. my $show_context = 0;
  87. GetOptions('inline' => \$show_inline,
  88. 'context' => \$show_context,
  89. 'pid' => \$include_pid,
  90. 'kernel' => \$annotate_kernel,
  91. 'jit' => \$annotate_jit,
  92. 'all' => \$annotate_all,
  93. 'tid' => \$include_tid,
  94. 'addrs' => \$include_addrs,
  95. 'event-filter=s' => \$event_filter)
  96. or die <<USAGE_END;
  97. USAGE: $0 [options] infile > outfile\n
  98. --pid # include PID with process names [1]
  99. --tid # include TID and PID with process names [1]
  100. --inline # un-inline using addr2line
  101. --all # all annotations (--kernel --jit)
  102. --kernel # annotate kernel functions with a _[k]
  103. --jit # annotate jit functions with a _[j]
  104. --context # adds source context to --inline
  105. --addrs # include raw addresses where symbols can't be found
  106. --event-filter=EVENT # event name filter\n
  107. [1] perf script must emit both PID and TIDs for these to work; eg, Linux < 4.1:
  108. perf script -f comm,pid,tid,cpu,time,event,ip,sym,dso,trace
  109. for Linux >= 4.1:
  110. perf script -F comm,pid,tid,cpu,time,event,ip,sym,dso,trace
  111. If you save this output add --header on Linux >= 3.14 to include perf info.
  112. USAGE_END
  113. if ($annotate_all) {
  114. $annotate_kernel = $annotate_jit = 1;
  115. }
  116. # for the --inline option
  117. sub inline {
  118. my ($pc, $mod) = @_;
  119. # capture addr2line output
  120. my $a2l_output = `addr2line -a $pc -e $mod -i -f -s -C`;
  121. # remove first line
  122. $a2l_output =~ s/^(.*\n){1}//;
  123. my @fullfunc;
  124. my $one_item = "";
  125. for (split /^/, $a2l_output) {
  126. chomp $_;
  127. # remove discriminator info if exists
  128. $_ =~ s/ \(discriminator \S+\)//;
  129. if ($one_item eq "") {
  130. $one_item = $_;
  131. } else {
  132. if ($show_context == 1) {
  133. unshift @fullfunc, $one_item . ":$_";
  134. } else {
  135. unshift @fullfunc, $one_item;
  136. }
  137. $one_item = "";
  138. }
  139. }
  140. return join(";", @fullfunc);
  141. }
  142. my @stack;
  143. my $pname;
  144. my $m_pid;
  145. my $m_tid;
  146. #
  147. # Main loop
  148. #
  149. while (defined($_ = <>)) {
  150. # find the name of the process launched by perf, by stepping backwards
  151. # over the args to find the first non-option (no dash):
  152. if (/^# cmdline/) {
  153. my @args = split ' ', $_;
  154. foreach my $arg (reverse @args) {
  155. if ($arg !~ /^-/) {
  156. $target_pname = $arg;
  157. $target_pname =~ s:.*/::; # strip pathname
  158. last;
  159. }
  160. }
  161. }
  162. # skip remaining comments
  163. next if m/^#/;
  164. chomp;
  165. # end of stack. save cached data.
  166. if (m/^$/) {
  167. # ignore filtered samples
  168. next if not $pname;
  169. if ($include_pname) {
  170. if (defined $pname) {
  171. unshift @stack, $pname;
  172. } else {
  173. unshift @stack, "";
  174. }
  175. }
  176. remember_stack(join(";", @stack), 1) if @stack;
  177. undef @stack;
  178. undef $pname;
  179. next;
  180. }
  181. #
  182. # event record start
  183. #
  184. if (/^(\S.+?)\s+(\d+)\/*(\d+)*\s+/) {
  185. # default "perf script" output has TID but not PID
  186. # eg, "java 25607 4794564.109216: cycles:"
  187. # eg, "java 12688 [002] 6544038.708352: cpu-clock:"
  188. # eg, "V8 WorkerThread 25607 4794564.109216: cycles:"
  189. # eg, "java 24636/25607 [000] 4794564.109216: cycles:"
  190. # eg, "java 12688/12764 6544038.708352: cpu-clock:"
  191. # eg, "V8 WorkerThread 24636/25607 [000] 94564.109216: cycles:"
  192. # other combinations possible
  193. my ($comm, $pid, $tid) = ($1, $2, $3);
  194. if (not $tid) {
  195. $tid = $pid;
  196. $pid = "?";
  197. }
  198. if (/(\S+):\s*$/) {
  199. my $event = $1;
  200. if ($event_filter eq "") {
  201. # By default only show events of the first encountered
  202. # event type. Merging together different types, such as
  203. # instructions and cycles, produces misleading results.
  204. $event_filter = $event;
  205. $event_defaulted = 1;
  206. } elsif ($event ne $event_filter) {
  207. if ($event_defaulted and $event_warning == 0) {
  208. # only print this warning if necessary:
  209. # when we defaulted and there was
  210. # multiple event types.
  211. print STDERR "Filtering for events of type: $event\n";
  212. $event_warning = 1;
  213. }
  214. next;
  215. }
  216. }
  217. ($m_pid, $m_tid) = ($pid, $tid);
  218. if ($include_tid) {
  219. $pname = "$comm-$m_pid/$m_tid";
  220. } elsif ($include_pid) {
  221. $pname = "$comm-$m_pid";
  222. } else {
  223. $pname = "$comm";
  224. }
  225. $pname =~ tr/ /_/;
  226. #
  227. # stack line
  228. #
  229. } elsif (/^\s*(\w+)\s*(.+) \((\S*)\)/) {
  230. # ignore filtered samples
  231. next if not $pname;
  232. my ($pc, $rawfunc, $mod) = ($1, $2, $3);
  233. # Linux 4.8 included symbol offsets in perf script output by default, eg:
  234. # 7fffb84c9afc cpu_startup_entry+0x800047c022ec ([kernel.kallsyms])
  235. # strip these off:
  236. $rawfunc =~ s/\+0x[\da-f]+$//;
  237. if ($show_inline == 1 && $mod !~ m/(perf-\d+.map|kernel\.|\[[^\]]+\])/) {
  238. unshift @stack, inline($pc, $mod);
  239. next;
  240. }
  241. next if $rawfunc =~ /^\(/; # skip process names
  242. my @inline;
  243. for (split /\->/, $rawfunc) {
  244. my $func = $_;
  245. if ($func eq "[unknown]") {
  246. if ($mod ne "[unknown]") { # use module name instead, if known
  247. $func = $mod;
  248. $func =~ s/.*\///;
  249. } else {
  250. $func = "unknown";
  251. }
  252. if ($include_addrs) {
  253. $func = "\[$func \<$pc\>\]";
  254. } else {
  255. $func = "\[$func\]";
  256. }
  257. }
  258. if ($tidy_generic) {
  259. $func =~ s/;/:/g;
  260. if ($func !~ m/\.\(.*\)\./) {
  261. # This doesn't look like a Go method name (such as
  262. # "net/http.(*Client).Do"), so everything after the first open
  263. # paren (that is not part of an "(anonymous namespace)") is
  264. # just noise.
  265. $func =~ s/\((?!anonymous namespace\)).*//;
  266. }
  267. # now tidy this horrible thing:
  268. # 13a80b608e0a RegExp:[&<>\"\'] (/tmp/perf-7539.map)
  269. $func =~ tr/"\'//d;
  270. # fall through to $tidy_java
  271. }
  272. if ($tidy_java and $pname eq "java") {
  273. # along with $tidy_generic, converts the following:
  274. # Lorg/mozilla/javascript/ContextFactory;.call(Lorg/mozilla/javascript/ContextAction;)Ljava/lang/Object;
  275. # Lorg/mozilla/javascript/ContextFactory;.call(Lorg/mozilla/javascript/C
  276. # Lorg/mozilla/javascript/MemberBox;.<init>(Ljava/lang/reflect/Method;)V
  277. # into:
  278. # org/mozilla/javascript/ContextFactory:.call
  279. # org/mozilla/javascript/ContextFactory:.call
  280. # org/mozilla/javascript/MemberBox:.init
  281. $func =~ s/^L// if $func =~ m:/:;
  282. }
  283. #
  284. # Annotations
  285. #
  286. # detect inlined from the @inline array
  287. # detect kernel from the module name; eg, frames to parse include:
  288. # ffffffff8103ce3b native_safe_halt ([kernel.kallsyms])
  289. # 8c3453 tcp_sendmsg (/lib/modules/4.3.0-rc1-virtual/build/vmlinux)
  290. # 7d8 ipv4_conntrack_local+0x7f8f80b8 ([nf_conntrack_ipv4])
  291. # detect jit from the module name; eg:
  292. # 7f722d142778 Ljava/io/PrintStream;::print (/tmp/perf-19982.map)
  293. if (scalar(@inline) > 0) {
  294. $func .= "_[i]"; # inlined
  295. } elsif ($annotate_kernel == 1 && $mod =~ m/(^\[|vmlinux$)/ && $mod !~ /unknown/) {
  296. $func .= "_[k]"; # kernel
  297. } elsif ($annotate_jit == 1 && $mod =~ m:/tmp/perf-\d+\.map:) {
  298. $func .= "_[j]"; # jitted
  299. }
  300. push @inline, $func;
  301. }
  302. unshift @stack, @inline;
  303. } else {
  304. warn "Unrecognized line: $_";
  305. }
  306. }
  307. foreach my $k (sort { $a cmp $b } keys %collapsed) {
  308. print "$k $collapsed{$k}\n";
  309. }