# perf script event handlers, generated by perf script -g python # Licensed under the terms of the GNU GPL License version 2 # The common_* event handler fields are the most useful fields common to # all events. They don't necessarily correspond to the 'common_*' fields # in the format files. Those fields not available as handler params can # be retrieved using Python functions of the form common_*(context). # See the perf-trace-python Documentation for the list of available functions. import os import sys import json from collections import defaultdict sys.path.append(os.environ["PERF_EXEC_PATH"] + "/scripts/python/Perf-Trace-Util/lib/Perf/Trace") from perf_trace_context import * class Function: def __init__(self, dso, name, sym): self.cost = 0 self.dso = dso self.name = name self.sym = sym self.callees = defaultdict(lambda: 0) class DSO: def __init__(self): self.functions = dict() # a map of all encountered dso's and the functions therein # this is done to prevent name clashes dsos = defaultdict(lambda: DSO()) def addFunction(dsoName, name, sym): global dsos dso = dsos[dsoName] function = dso.functions.get(name, None) # create function if it's not yet known if not function: function = Function(dsoName, name, sym) dso.functions[name] = function return function # write the callgrind file format to stdout def trace_end(): global dsos print("version: 1") print("creator: perf-callgrind 0.1") print("part: 1") # TODO: get access to command line, it's in the perf data header # but not accessible to the scripting backend, is it? print("events: Samples") for name, dso in dsos.iteritems(): print("ob=%s" % name) for sym, function in dso.functions.iteritems(): print("fn=%s" % sym) print("0 %d" % function.cost) for callee, cost in function.callees.iteritems(): print("cob=%s" % callee.dso) print("cfn=%s" % callee.name) print("calls=1 0") print("0 %d" % cost) print("") def process_event(event): caller = None if not event["callchain"]: # only add the single symbol where we got the sample, without a backtrace dsoName = event.get("dso", "???") name = event.get("symbol", "???") caller = addFunction(dsoName, name, None) else: # add a function for every frame in the callchain for item in reversed(event["callchain"]): dsoName = item.get("dso", "???") name = "???" if "sym" in item: name = item["sym"]["name"] function = addFunction(dsoName, name, item.get("sym", None)) # add current frame to parent's callee list if caller is not None: caller.callees[function] += 1 caller = function # increase the self cost of the last frame # all other frames include it now and kcachegrind will automatically # take care of adapting their inclusive cost if caller is not None: caller.cost += 1