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