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