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