16745d8eaSPaolo Bonzini# stackcollapse.py - format perf samples with one line per distinct call stack
2b2441318SGreg Kroah-Hartman# SPDX-License-Identifier: GPL-2.0
36745d8eaSPaolo Bonzini#
46745d8eaSPaolo Bonzini# This script's output has two space-separated fields.  The first is a semicolon
56745d8eaSPaolo Bonzini# separated stack including the program name (from the "comm" field) and the
66745d8eaSPaolo Bonzini# function names from the call stack.  The second is a count:
76745d8eaSPaolo Bonzini#
86745d8eaSPaolo Bonzini#  swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 2
96745d8eaSPaolo Bonzini#
106745d8eaSPaolo Bonzini# The file is sorted according to the first field.
116745d8eaSPaolo Bonzini#
126745d8eaSPaolo Bonzini# Input may be created and processed using:
136745d8eaSPaolo Bonzini#
146745d8eaSPaolo Bonzini#  perf record -a -g -F 99 sleep 60
156745d8eaSPaolo Bonzini#  perf script report stackcollapse > out.stacks-folded
166745d8eaSPaolo Bonzini#
176745d8eaSPaolo Bonzini# (perf script record stackcollapse works too).
186745d8eaSPaolo Bonzini#
196745d8eaSPaolo Bonzini# Written by Paolo Bonzini <pbonzini@redhat.com>
206745d8eaSPaolo Bonzini# Based on Brendan Gregg's stackcollapse-perf.pl script.
216745d8eaSPaolo Bonzini
226d22d999STony Jonesfrom __future__ import print_function
236d22d999STony Jones
246745d8eaSPaolo Bonziniimport os
256745d8eaSPaolo Bonziniimport sys
266745d8eaSPaolo Bonzinifrom collections import defaultdict
276745d8eaSPaolo Bonzinifrom optparse import OptionParser, make_option
286745d8eaSPaolo Bonzini
296745d8eaSPaolo Bonzinisys.path.append(os.environ['PERF_EXEC_PATH'] + \
306745d8eaSPaolo Bonzini    '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
316745d8eaSPaolo Bonzini
326745d8eaSPaolo Bonzinifrom perf_trace_context import *
336745d8eaSPaolo Bonzinifrom Core import *
346745d8eaSPaolo Bonzinifrom EventClass import *
356745d8eaSPaolo Bonzini
366745d8eaSPaolo Bonzini# command line parsing
376745d8eaSPaolo Bonzini
386745d8eaSPaolo Bonzinioption_list = [
396745d8eaSPaolo Bonzini    # formatting options for the bottom entry of the stack
406745d8eaSPaolo Bonzini    make_option("--include-tid", dest="include_tid",
416745d8eaSPaolo Bonzini                 action="store_true", default=False,
426745d8eaSPaolo Bonzini                 help="include thread id in stack"),
436745d8eaSPaolo Bonzini    make_option("--include-pid", dest="include_pid",
446745d8eaSPaolo Bonzini                 action="store_true", default=False,
456745d8eaSPaolo Bonzini                 help="include process id in stack"),
466745d8eaSPaolo Bonzini    make_option("--no-comm", dest="include_comm",
476745d8eaSPaolo Bonzini                 action="store_false", default=True,
486745d8eaSPaolo Bonzini                 help="do not separate stacks according to comm"),
496745d8eaSPaolo Bonzini    make_option("--tidy-java", dest="tidy_java",
506745d8eaSPaolo Bonzini                 action="store_true", default=False,
516745d8eaSPaolo Bonzini                 help="beautify Java signatures"),
526745d8eaSPaolo Bonzini    make_option("--kernel", dest="annotate_kernel",
536745d8eaSPaolo Bonzini                 action="store_true", default=False,
546745d8eaSPaolo Bonzini                 help="annotate kernel functions with _[k]")
556745d8eaSPaolo Bonzini]
566745d8eaSPaolo Bonzini
576745d8eaSPaolo Bonziniparser = OptionParser(option_list=option_list)
586745d8eaSPaolo Bonzini(opts, args) = parser.parse_args()
596745d8eaSPaolo Bonzini
606745d8eaSPaolo Bonziniif len(args) != 0:
616745d8eaSPaolo Bonzini    parser.error("unexpected command line argument")
626745d8eaSPaolo Bonziniif opts.include_tid and not opts.include_comm:
636745d8eaSPaolo Bonzini    parser.error("requesting tid but not comm is invalid")
646745d8eaSPaolo Bonziniif opts.include_pid and not opts.include_comm:
656745d8eaSPaolo Bonzini    parser.error("requesting pid but not comm is invalid")
666745d8eaSPaolo Bonzini
676745d8eaSPaolo Bonzini# event handlers
686745d8eaSPaolo Bonzini
696745d8eaSPaolo Bonzinilines = defaultdict(lambda: 0)
706745d8eaSPaolo Bonzini
716745d8eaSPaolo Bonzinidef process_event(param_dict):
726745d8eaSPaolo Bonzini    def tidy_function_name(sym, dso):
736745d8eaSPaolo Bonzini        if sym is None:
746745d8eaSPaolo Bonzini            sym = '[unknown]'
756745d8eaSPaolo Bonzini
766745d8eaSPaolo Bonzini        sym = sym.replace(';', ':')
776745d8eaSPaolo Bonzini        if opts.tidy_java:
786745d8eaSPaolo Bonzini            # the original stackcollapse-perf.pl script gives the
796745d8eaSPaolo Bonzini            # example of converting this:
806745d8eaSPaolo Bonzini            #    Lorg/mozilla/javascript/MemberBox;.<init>(Ljava/lang/reflect/Method;)V
816745d8eaSPaolo Bonzini            # to this:
826745d8eaSPaolo Bonzini            #    org/mozilla/javascript/MemberBox:.init
836745d8eaSPaolo Bonzini            sym = sym.replace('<', '')
846745d8eaSPaolo Bonzini            sym = sym.replace('>', '')
856745d8eaSPaolo Bonzini            if sym[0] == 'L' and sym.find('/'):
866745d8eaSPaolo Bonzini                sym = sym[1:]
876745d8eaSPaolo Bonzini            try:
886745d8eaSPaolo Bonzini                sym = sym[:sym.index('(')]
896745d8eaSPaolo Bonzini            except ValueError:
906745d8eaSPaolo Bonzini                pass
916745d8eaSPaolo Bonzini
926745d8eaSPaolo Bonzini        if opts.annotate_kernel and dso == '[kernel.kallsyms]':
936745d8eaSPaolo Bonzini            return sym + '_[k]'
946745d8eaSPaolo Bonzini        else:
956745d8eaSPaolo Bonzini            return sym
966745d8eaSPaolo Bonzini
976745d8eaSPaolo Bonzini    stack = list()
986745d8eaSPaolo Bonzini    if 'callchain' in param_dict:
996745d8eaSPaolo Bonzini        for entry in param_dict['callchain']:
1006745d8eaSPaolo Bonzini            entry.setdefault('sym', dict())
1016745d8eaSPaolo Bonzini            entry['sym'].setdefault('name', None)
1026745d8eaSPaolo Bonzini            entry.setdefault('dso', None)
1036745d8eaSPaolo Bonzini            stack.append(tidy_function_name(entry['sym']['name'],
1046745d8eaSPaolo Bonzini                                            entry['dso']))
1056745d8eaSPaolo Bonzini    else:
1066745d8eaSPaolo Bonzini        param_dict.setdefault('symbol', None)
1076745d8eaSPaolo Bonzini        param_dict.setdefault('dso', None)
1086745d8eaSPaolo Bonzini        stack.append(tidy_function_name(param_dict['symbol'],
1096745d8eaSPaolo Bonzini                                        param_dict['dso']))
1106745d8eaSPaolo Bonzini
1116745d8eaSPaolo Bonzini    if opts.include_comm:
1126745d8eaSPaolo Bonzini        comm = param_dict["comm"].replace(' ', '_')
1136745d8eaSPaolo Bonzini        sep = "-"
1146745d8eaSPaolo Bonzini        if opts.include_pid:
1156745d8eaSPaolo Bonzini            comm = comm + sep + str(param_dict['sample']['pid'])
1166745d8eaSPaolo Bonzini            sep = "/"
1176745d8eaSPaolo Bonzini        if opts.include_tid:
1186745d8eaSPaolo Bonzini            comm = comm + sep + str(param_dict['sample']['tid'])
1196745d8eaSPaolo Bonzini        stack.append(comm)
1206745d8eaSPaolo Bonzini
1216745d8eaSPaolo Bonzini    stack_string = ';'.join(reversed(stack))
1226745d8eaSPaolo Bonzini    lines[stack_string] = lines[stack_string] + 1
1236745d8eaSPaolo Bonzini
1246745d8eaSPaolo Bonzinidef trace_end():
1256d22d999STony Jones    list = sorted(lines)
1266745d8eaSPaolo Bonzini    for stack in list:
1276d22d999STony Jones        print("%s %d" % (stack, lines[stack]))
128