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