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