1*4882a593Smuzhiyun#!/usr/bin/env python 2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 3*4882a593Smuzhiyun 4*4882a593Smuzhiyun""" 5*4882a593SmuzhiyunCopyright 2008 (c) Frederic Weisbecker <fweisbec@gmail.com> 6*4882a593Smuzhiyun 7*4882a593SmuzhiyunThis script parses a trace provided by the function tracer in 8*4882a593Smuzhiyunkernel/trace/trace_functions.c 9*4882a593SmuzhiyunThe resulted trace is processed into a tree to produce a more human 10*4882a593Smuzhiyunview of the call stack by drawing textual but hierarchical tree of 11*4882a593Smuzhiyuncalls. Only the functions's names and the the call time are provided. 12*4882a593Smuzhiyun 13*4882a593SmuzhiyunUsage: 14*4882a593Smuzhiyun Be sure that you have CONFIG_FUNCTION_TRACER 15*4882a593Smuzhiyun # mount -t debugfs nodev /sys/kernel/debug 16*4882a593Smuzhiyun # echo function > /sys/kernel/debug/tracing/current_tracer 17*4882a593Smuzhiyun $ cat /sys/kernel/debug/tracing/trace_pipe > ~/raw_trace_func 18*4882a593Smuzhiyun Wait some times but not too much, the script is a bit slow. 19*4882a593Smuzhiyun Break the pipe (Ctrl + Z) 20*4882a593Smuzhiyun $ scripts/tracing/draw_functrace.py < ~/raw_trace_func > draw_functrace 21*4882a593Smuzhiyun Then you have your drawn trace in draw_functrace 22*4882a593Smuzhiyun""" 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun 25*4882a593Smuzhiyunimport sys, re 26*4882a593Smuzhiyun 27*4882a593Smuzhiyunclass CallTree: 28*4882a593Smuzhiyun """ This class provides a tree representation of the functions 29*4882a593Smuzhiyun call stack. If a function has no parent in the kernel (interrupt, 30*4882a593Smuzhiyun syscall, kernel thread...) then it is attached to a virtual parent 31*4882a593Smuzhiyun called ROOT. 32*4882a593Smuzhiyun """ 33*4882a593Smuzhiyun ROOT = None 34*4882a593Smuzhiyun 35*4882a593Smuzhiyun def __init__(self, func, time = None, parent = None): 36*4882a593Smuzhiyun self._func = func 37*4882a593Smuzhiyun self._time = time 38*4882a593Smuzhiyun if parent is None: 39*4882a593Smuzhiyun self._parent = CallTree.ROOT 40*4882a593Smuzhiyun else: 41*4882a593Smuzhiyun self._parent = parent 42*4882a593Smuzhiyun self._children = [] 43*4882a593Smuzhiyun 44*4882a593Smuzhiyun def calls(self, func, calltime): 45*4882a593Smuzhiyun """ If a function calls another one, call this method to insert it 46*4882a593Smuzhiyun into the tree at the appropriate place. 47*4882a593Smuzhiyun @return: A reference to the newly created child node. 48*4882a593Smuzhiyun """ 49*4882a593Smuzhiyun child = CallTree(func, calltime, self) 50*4882a593Smuzhiyun self._children.append(child) 51*4882a593Smuzhiyun return child 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun def getParent(self, func): 54*4882a593Smuzhiyun """ Retrieve the last parent of the current node that 55*4882a593Smuzhiyun has the name given by func. If this function is not 56*4882a593Smuzhiyun on a parent, then create it as new child of root 57*4882a593Smuzhiyun @return: A reference to the parent. 58*4882a593Smuzhiyun """ 59*4882a593Smuzhiyun tree = self 60*4882a593Smuzhiyun while tree != CallTree.ROOT and tree._func != func: 61*4882a593Smuzhiyun tree = tree._parent 62*4882a593Smuzhiyun if tree == CallTree.ROOT: 63*4882a593Smuzhiyun child = CallTree.ROOT.calls(func, None) 64*4882a593Smuzhiyun return child 65*4882a593Smuzhiyun return tree 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun def __repr__(self): 68*4882a593Smuzhiyun return self.__toString("", True) 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun def __toString(self, branch, lastChild): 71*4882a593Smuzhiyun if self._time is not None: 72*4882a593Smuzhiyun s = "%s----%s (%s)\n" % (branch, self._func, self._time) 73*4882a593Smuzhiyun else: 74*4882a593Smuzhiyun s = "%s----%s\n" % (branch, self._func) 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun i = 0 77*4882a593Smuzhiyun if lastChild: 78*4882a593Smuzhiyun branch = branch[:-1] + " " 79*4882a593Smuzhiyun while i < len(self._children): 80*4882a593Smuzhiyun if i != len(self._children) - 1: 81*4882a593Smuzhiyun s += "%s" % self._children[i].__toString(branch +\ 82*4882a593Smuzhiyun " |", False) 83*4882a593Smuzhiyun else: 84*4882a593Smuzhiyun s += "%s" % self._children[i].__toString(branch +\ 85*4882a593Smuzhiyun " |", True) 86*4882a593Smuzhiyun i += 1 87*4882a593Smuzhiyun return s 88*4882a593Smuzhiyun 89*4882a593Smuzhiyunclass BrokenLineException(Exception): 90*4882a593Smuzhiyun """If the last line is not complete because of the pipe breakage, 91*4882a593Smuzhiyun we want to stop the processing and ignore this line. 92*4882a593Smuzhiyun """ 93*4882a593Smuzhiyun pass 94*4882a593Smuzhiyun 95*4882a593Smuzhiyunclass CommentLineException(Exception): 96*4882a593Smuzhiyun """ If the line is a comment (as in the beginning of the trace file), 97*4882a593Smuzhiyun just ignore it. 98*4882a593Smuzhiyun """ 99*4882a593Smuzhiyun pass 100*4882a593Smuzhiyun 101*4882a593Smuzhiyun 102*4882a593Smuzhiyundef parseLine(line): 103*4882a593Smuzhiyun line = line.strip() 104*4882a593Smuzhiyun if line.startswith("#"): 105*4882a593Smuzhiyun raise CommentLineException 106*4882a593Smuzhiyun m = re.match("[^]]+?\\] +([a-z.]+) +([0-9.]+): (\\w+) <-(\\w+)", line) 107*4882a593Smuzhiyun if m is None: 108*4882a593Smuzhiyun raise BrokenLineException 109*4882a593Smuzhiyun return (m.group(2), m.group(3), m.group(4)) 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun 112*4882a593Smuzhiyundef main(): 113*4882a593Smuzhiyun CallTree.ROOT = CallTree("Root (Nowhere)", None, None) 114*4882a593Smuzhiyun tree = CallTree.ROOT 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun for line in sys.stdin: 117*4882a593Smuzhiyun try: 118*4882a593Smuzhiyun calltime, callee, caller = parseLine(line) 119*4882a593Smuzhiyun except BrokenLineException: 120*4882a593Smuzhiyun break 121*4882a593Smuzhiyun except CommentLineException: 122*4882a593Smuzhiyun continue 123*4882a593Smuzhiyun tree = tree.getParent(caller) 124*4882a593Smuzhiyun tree = tree.calls(callee, calltime) 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun print(CallTree.ROOT) 127*4882a593Smuzhiyun 128*4882a593Smuzhiyunif __name__ == "__main__": 129*4882a593Smuzhiyun main() 130