History

perf script: Add stackcollapse.py script

Add stackcollapse.py script as an example of parsing call chains, and also of using optparse to access command line options. The flame graph tools include a set of scripts that parse output from various tools (including "perf script"), remove the offsets in the function and collapse each stack to a single line. The website also says "perf report could have a report style [...] that output folded stacks directly, obviating the need for stackcollapse-perf.pl", so here it is. This script is a Python rewrite of stackcollapse-perf.pl, using the perf scripting interface to access the perf data directly from Python.

Paolo Bonzini committed 10 years ago

Commit 6745d8e1 parent 7da36e93 files · +138 −0Browse files

Showing the changes to tools/perf/scripts/python (3 of 3 files). Show all

Unified · Split

3 files
  1. tools/perf/scripts/python/bin/stackcollapse-record +8 −0
  2. tools/perf/scripts/python/bin/stackcollapse-report +3 −0
  3. tools/perf/scripts/python/stackcollapse.py +127 −0
tools/perf/scripts/python/bin/stackcollapse-record+8−0
@@ -0,0 +1,8 @@
1+#!/bin/sh
2+
3+#
4+# stackcollapse.py can cover all type of perf samples including
5+# the tracepoints, so no special record requirements, just record what
6+# you want to analyze.
7+#
8+perf record "$@"
tools/perf/scripts/python/bin/stackcollapse-report+3−0
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+# description: produce callgraphs in short form for scripting use
3+perf script -s "$PERF_EXEC_PATH"/scripts/python/stackcollapse.py -- "$@"
tools/perf/scripts/python/stackcollapse.py+127−0
@@ -0,0 +1,127 @@
1+#!/usr/bin/perl -w
2+#
3+# stackcollapse.py - format perf samples with one line per distinct call stack
4+#
5+# This script's output has two space-separated fields. The first is a semicolon
6+# separated stack including the program name (from the "comm" field) and the
7+# function names from the call stack. The second is a count:
8+#
9+# swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 2
10+#
11+# The file is sorted according to the first field.
12+#
13+# Input may be created and processed using:
14+#
15+# perf record -a -g -F 99 sleep 60
16+# perf script report stackcollapse > out.stacks-folded
17+#
18+# (perf script record stackcollapse works too).
19+#
20+# Written by Paolo Bonzini <pbonzini@redhat.com>
21+# Based on Brendan Gregg's stackcollapse-perf.pl script.
22+
23+import os
24+import sys
25+from collections import defaultdict
26+from optparse import OptionParser, make_option
27+
28+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
29+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
30+
31+from perf_trace_context import *
32+from Core import *
33+from EventClass import *
34+
35+# command line parsing
36+
37+option_list = [
38+ # formatting options for the bottom entry of the stack
39+ make_option("--include-tid", dest="include_tid",
40+ action="store_true", default=False,
41+ help="include thread id in stack"),
42+ make_option("--include-pid", dest="include_pid",
43+ action="store_true", default=False,
44+ help="include process id in stack"),
45+ make_option("--no-comm", dest="include_comm",
46+ action="store_false", default=True,
47+ help="do not separate stacks according to comm"),
48+ make_option("--tidy-java", dest="tidy_java",
49+ action="store_true", default=False,
50+ help="beautify Java signatures"),
51+ make_option("--kernel", dest="annotate_kernel",
52+ action="store_true", default=False,
53+ help="annotate kernel functions with _[k]")
54+]
55+
56+parser = OptionParser(option_list=option_list)
57+(opts, args) = parser.parse_args()
58+
59+if len(args) != 0:
60+ parser.error("unexpected command line argument")
61+if opts.include_tid and not opts.include_comm:
62+ parser.error("requesting tid but not comm is invalid")
63+if opts.include_pid and not opts.include_comm:
64+ parser.error("requesting pid but not comm is invalid")
65+
66+# event handlers
67+
68+lines = defaultdict(lambda: 0)
69+
70+def process_event(param_dict):
71+ def tidy_function_name(sym, dso):
72+ if sym is None:
73+ sym = '[unknown]'
74+
75+ sym = sym.replace(';', ':')
76+ if opts.tidy_java:
77+ # the original stackcollapse-perf.pl script gives the
78+ # example of converting this:
79+ # Lorg/mozilla/javascript/MemberBox;.<init>(Ljava/lang/reflect/Method;)V
80+ # to this:
81+ # org/mozilla/javascript/MemberBox:.init
82+ sym = sym.replace('<', '')
83+ sym = sym.replace('>', '')
84+ if sym[0] == 'L' and sym.find('/'):
85+ sym = sym[1:]
86+ try:
87+ sym = sym[:sym.index('(')]
88+ except ValueError:
89+ pass
90+
91+ if opts.annotate_kernel and dso == '[kernel.kallsyms]':
92+ return sym + '_[k]'
93+ else:
94+ return sym
95+
96+ stack = list()
97+ if 'callchain' in param_dict:
98+ for entry in param_dict['callchain']:
99+ entry.setdefault('sym', dict())
100+ entry['sym'].setdefault('name', None)
101+ entry.setdefault('dso', None)
102+ stack.append(tidy_function_name(entry['sym']['name'],
103+ entry['dso']))
104+ else:
105+ param_dict.setdefault('symbol', None)
106+ param_dict.setdefault('dso', None)
107+ stack.append(tidy_function_name(param_dict['symbol'],
108+ param_dict['dso']))
109+
110+ if opts.include_comm:
111+ comm = param_dict["comm"].replace(' ', '_')
112+ sep = "-"
113+ if opts.include_pid:
114+ comm = comm + sep + str(param_dict['sample']['pid'])
115+ sep = "/"
116+ if opts.include_tid:
117+ comm = comm + sep + str(param_dict['sample']['tid'])
118+ stack.append(comm)
119+
120+ stack_string = ';'.join(reversed(stack))
121+ lines[stack_string] = lines[stack_string] + 1
122+
123+def trace_end():
124+ list = lines.keys()
125+ list.sort()
126+ for stack in list:
127+ print "%s %d" % (stack, lines[stack])