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