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