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 22*6d22d999STony Jonesfrom __future__ import print_function 23*6d22d999STony 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(): 125*6d22d999STony Jones list = sorted(lines) 1266745d8eaSPaolo Bonzini for stack in list: 127*6d22d999STony Jones print("%s %d" % (stack, lines[stack])) 128