16745d8eaSPaolo Bonzini# stackcollapse.py - format perf samples with one line per distinct call stack 2*b2441318SGreg 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 <[email protected]> 206745d8eaSPaolo Bonzini# Based on Brendan Gregg's stackcollapse-perf.pl script. 216745d8eaSPaolo Bonzini 226745d8eaSPaolo Bonziniimport os 236745d8eaSPaolo Bonziniimport sys 246745d8eaSPaolo Bonzinifrom collections import defaultdict 256745d8eaSPaolo Bonzinifrom optparse import OptionParser, make_option 266745d8eaSPaolo Bonzini 276745d8eaSPaolo Bonzinisys.path.append(os.environ['PERF_EXEC_PATH'] + \ 286745d8eaSPaolo Bonzini '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') 296745d8eaSPaolo Bonzini 306745d8eaSPaolo Bonzinifrom perf_trace_context import * 316745d8eaSPaolo Bonzinifrom Core import * 326745d8eaSPaolo Bonzinifrom EventClass import * 336745d8eaSPaolo Bonzini 346745d8eaSPaolo Bonzini# command line parsing 356745d8eaSPaolo Bonzini 366745d8eaSPaolo Bonzinioption_list = [ 376745d8eaSPaolo Bonzini # formatting options for the bottom entry of the stack 386745d8eaSPaolo Bonzini make_option("--include-tid", dest="include_tid", 396745d8eaSPaolo Bonzini action="store_true", default=False, 406745d8eaSPaolo Bonzini help="include thread id in stack"), 416745d8eaSPaolo Bonzini make_option("--include-pid", dest="include_pid", 426745d8eaSPaolo Bonzini action="store_true", default=False, 436745d8eaSPaolo Bonzini help="include process id in stack"), 446745d8eaSPaolo Bonzini make_option("--no-comm", dest="include_comm", 456745d8eaSPaolo Bonzini action="store_false", default=True, 466745d8eaSPaolo Bonzini help="do not separate stacks according to comm"), 476745d8eaSPaolo Bonzini make_option("--tidy-java", dest="tidy_java", 486745d8eaSPaolo Bonzini action="store_true", default=False, 496745d8eaSPaolo Bonzini help="beautify Java signatures"), 506745d8eaSPaolo Bonzini make_option("--kernel", dest="annotate_kernel", 516745d8eaSPaolo Bonzini action="store_true", default=False, 526745d8eaSPaolo Bonzini help="annotate kernel functions with _[k]") 536745d8eaSPaolo Bonzini] 546745d8eaSPaolo Bonzini 556745d8eaSPaolo Bonziniparser = OptionParser(option_list=option_list) 566745d8eaSPaolo Bonzini(opts, args) = parser.parse_args() 576745d8eaSPaolo Bonzini 586745d8eaSPaolo Bonziniif len(args) != 0: 596745d8eaSPaolo Bonzini parser.error("unexpected command line argument") 606745d8eaSPaolo Bonziniif opts.include_tid and not opts.include_comm: 616745d8eaSPaolo Bonzini parser.error("requesting tid but not comm is invalid") 626745d8eaSPaolo Bonziniif opts.include_pid and not opts.include_comm: 636745d8eaSPaolo Bonzini parser.error("requesting pid but not comm is invalid") 646745d8eaSPaolo Bonzini 656745d8eaSPaolo Bonzini# event handlers 666745d8eaSPaolo Bonzini 676745d8eaSPaolo Bonzinilines = defaultdict(lambda: 0) 686745d8eaSPaolo Bonzini 696745d8eaSPaolo Bonzinidef process_event(param_dict): 706745d8eaSPaolo Bonzini def tidy_function_name(sym, dso): 716745d8eaSPaolo Bonzini if sym is None: 726745d8eaSPaolo Bonzini sym = '[unknown]' 736745d8eaSPaolo Bonzini 746745d8eaSPaolo Bonzini sym = sym.replace(';', ':') 756745d8eaSPaolo Bonzini if opts.tidy_java: 766745d8eaSPaolo Bonzini # the original stackcollapse-perf.pl script gives the 776745d8eaSPaolo Bonzini # example of converting this: 786745d8eaSPaolo Bonzini # Lorg/mozilla/javascript/MemberBox;.<init>(Ljava/lang/reflect/Method;)V 796745d8eaSPaolo Bonzini # to this: 806745d8eaSPaolo Bonzini # org/mozilla/javascript/MemberBox:.init 816745d8eaSPaolo Bonzini sym = sym.replace('<', '') 826745d8eaSPaolo Bonzini sym = sym.replace('>', '') 836745d8eaSPaolo Bonzini if sym[0] == 'L' and sym.find('/'): 846745d8eaSPaolo Bonzini sym = sym[1:] 856745d8eaSPaolo Bonzini try: 866745d8eaSPaolo Bonzini sym = sym[:sym.index('(')] 876745d8eaSPaolo Bonzini except ValueError: 886745d8eaSPaolo Bonzini pass 896745d8eaSPaolo Bonzini 906745d8eaSPaolo Bonzini if opts.annotate_kernel and dso == '[kernel.kallsyms]': 916745d8eaSPaolo Bonzini return sym + '_[k]' 926745d8eaSPaolo Bonzini else: 936745d8eaSPaolo Bonzini return sym 946745d8eaSPaolo Bonzini 956745d8eaSPaolo Bonzini stack = list() 966745d8eaSPaolo Bonzini if 'callchain' in param_dict: 976745d8eaSPaolo Bonzini for entry in param_dict['callchain']: 986745d8eaSPaolo Bonzini entry.setdefault('sym', dict()) 996745d8eaSPaolo Bonzini entry['sym'].setdefault('name', None) 1006745d8eaSPaolo Bonzini entry.setdefault('dso', None) 1016745d8eaSPaolo Bonzini stack.append(tidy_function_name(entry['sym']['name'], 1026745d8eaSPaolo Bonzini entry['dso'])) 1036745d8eaSPaolo Bonzini else: 1046745d8eaSPaolo Bonzini param_dict.setdefault('symbol', None) 1056745d8eaSPaolo Bonzini param_dict.setdefault('dso', None) 1066745d8eaSPaolo Bonzini stack.append(tidy_function_name(param_dict['symbol'], 1076745d8eaSPaolo Bonzini param_dict['dso'])) 1086745d8eaSPaolo Bonzini 1096745d8eaSPaolo Bonzini if opts.include_comm: 1106745d8eaSPaolo Bonzini comm = param_dict["comm"].replace(' ', '_') 1116745d8eaSPaolo Bonzini sep = "-" 1126745d8eaSPaolo Bonzini if opts.include_pid: 1136745d8eaSPaolo Bonzini comm = comm + sep + str(param_dict['sample']['pid']) 1146745d8eaSPaolo Bonzini sep = "/" 1156745d8eaSPaolo Bonzini if opts.include_tid: 1166745d8eaSPaolo Bonzini comm = comm + sep + str(param_dict['sample']['tid']) 1176745d8eaSPaolo Bonzini stack.append(comm) 1186745d8eaSPaolo Bonzini 1196745d8eaSPaolo Bonzini stack_string = ';'.join(reversed(stack)) 1206745d8eaSPaolo Bonzini lines[stack_string] = lines[stack_string] + 1 1216745d8eaSPaolo Bonzini 1226745d8eaSPaolo Bonzinidef trace_end(): 1236745d8eaSPaolo Bonzini list = lines.keys() 1246745d8eaSPaolo Bonzini list.sort() 1256745d8eaSPaolo Bonzini for stack in list: 1266745d8eaSPaolo Bonzini print "%s %d" % (stack, lines[stack]) 127