Back to home page

OSCL-LXR

 
 

    


0001 # stackcollapse.py - format perf samples with one line per distinct call stack
0002 # SPDX-License-Identifier: GPL-2.0
0003 #
0004 # This script's output has two space-separated fields.  The first is a semicolon
0005 # separated stack including the program name (from the "comm" field) and the
0006 # function names from the call stack.  The second is a count:
0007 #
0008 #  swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 2
0009 #
0010 # The file is sorted according to the first field.
0011 #
0012 # Input may be created and processed using:
0013 #
0014 #  perf record -a -g -F 99 sleep 60
0015 #  perf script report stackcollapse > out.stacks-folded
0016 #
0017 # (perf script record stackcollapse works too).
0018 #
0019 # Written by Paolo Bonzini <pbonzini@redhat.com>
0020 # Based on Brendan Gregg's stackcollapse-perf.pl script.
0021 
0022 from __future__ import print_function
0023 
0024 import os
0025 import sys
0026 from collections import defaultdict
0027 from optparse import OptionParser, make_option
0028 
0029 sys.path.append(os.environ['PERF_EXEC_PATH'] + \
0030     '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
0031 
0032 from perf_trace_context import *
0033 from Core import *
0034 from EventClass import *
0035 
0036 # command line parsing
0037 
0038 option_list = [
0039     # formatting options for the bottom entry of the stack
0040     make_option("--include-tid", dest="include_tid",
0041                  action="store_true", default=False,
0042                  help="include thread id in stack"),
0043     make_option("--include-pid", dest="include_pid",
0044                  action="store_true", default=False,
0045                  help="include process id in stack"),
0046     make_option("--no-comm", dest="include_comm",
0047                  action="store_false", default=True,
0048                  help="do not separate stacks according to comm"),
0049     make_option("--tidy-java", dest="tidy_java",
0050                  action="store_true", default=False,
0051                  help="beautify Java signatures"),
0052     make_option("--kernel", dest="annotate_kernel",
0053                  action="store_true", default=False,
0054                  help="annotate kernel functions with _[k]")
0055 ]
0056 
0057 parser = OptionParser(option_list=option_list)
0058 (opts, args) = parser.parse_args()
0059 
0060 if len(args) != 0:
0061     parser.error("unexpected command line argument")
0062 if opts.include_tid and not opts.include_comm:
0063     parser.error("requesting tid but not comm is invalid")
0064 if opts.include_pid and not opts.include_comm:
0065     parser.error("requesting pid but not comm is invalid")
0066 
0067 # event handlers
0068 
0069 lines = defaultdict(lambda: 0)
0070 
0071 def process_event(param_dict):
0072     def tidy_function_name(sym, dso):
0073         if sym is None:
0074             sym = '[unknown]'
0075 
0076         sym = sym.replace(';', ':')
0077         if opts.tidy_java:
0078             # the original stackcollapse-perf.pl script gives the
0079             # example of converting this:
0080             #    Lorg/mozilla/javascript/MemberBox;.<init>(Ljava/lang/reflect/Method;)V
0081             # to this:
0082             #    org/mozilla/javascript/MemberBox:.init
0083             sym = sym.replace('<', '')
0084             sym = sym.replace('>', '')
0085             if sym[0] == 'L' and sym.find('/'):
0086                 sym = sym[1:]
0087             try:
0088                 sym = sym[:sym.index('(')]
0089             except ValueError:
0090                 pass
0091 
0092         if opts.annotate_kernel and dso == '[kernel.kallsyms]':
0093             return sym + '_[k]'
0094         else:
0095             return sym
0096 
0097     stack = list()
0098     if 'callchain' in param_dict:
0099         for entry in param_dict['callchain']:
0100             entry.setdefault('sym', dict())
0101             entry['sym'].setdefault('name', None)
0102             entry.setdefault('dso', None)
0103             stack.append(tidy_function_name(entry['sym']['name'],
0104                                             entry['dso']))
0105     else:
0106         param_dict.setdefault('symbol', None)
0107         param_dict.setdefault('dso', None)
0108         stack.append(tidy_function_name(param_dict['symbol'],
0109                                         param_dict['dso']))
0110 
0111     if opts.include_comm:
0112         comm = param_dict["comm"].replace(' ', '_')
0113         sep = "-"
0114         if opts.include_pid:
0115             comm = comm + sep + str(param_dict['sample']['pid'])
0116             sep = "/"
0117         if opts.include_tid:
0118             comm = comm + sep + str(param_dict['sample']['tid'])
0119         stack.append(comm)
0120 
0121     stack_string = ';'.join(reversed(stack))
0122     lines[stack_string] = lines[stack_string] + 1
0123 
0124 def trace_end():
0125     list = sorted(lines)
0126     for stack in list:
0127         print("%s %d" % (stack, lines[stack]))