Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf script: Add stackcollapse.py script

Add stackcollapse.py script as an example of parsing call chains, and
also of using optparse to access command line options.

The flame graph tools include a set of scripts that parse output from
various tools (including "perf script"), remove the offsets in the
function and collapse each stack to a single line. The website also
says "perf report could have a report style [...] that output folded
stacks directly, obviating the need for stackcollapse-perf.pl", so here
it is.

This script is a Python rewrite of stackcollapse-perf.pl, using the perf
scripting interface to access the perf data directly from Python.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Brendan Gregg <bgregg@netflix.com>
Link: http://lkml.kernel.org/r/1460467573-22989-1-git-send-email-pbonzini@redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Paolo Bonzini and committed by
Arnaldo Carvalho de Melo
6745d8ea 7da36e94

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