GNU Linux-libre 6.8.9-gnu
[releases.git] / scripts / tracing / draw_functrace.py
1 #!/usr/bin/env python
2 # SPDX-License-Identifier: GPL-2.0-only
3
4 """
5 Copyright 2008 (c) Frederic Weisbecker <fweisbec@gmail.com>
6
7 This script parses a trace provided by the function tracer in
8 kernel/trace/trace_functions.c
9 The resulted trace is processed into a tree to produce a more human
10 view of the call stack by drawing textual but hierarchical tree of
11 calls. Only the functions's names and the call time are provided.
12
13 Usage:
14         Be sure that you have CONFIG_FUNCTION_TRACER
15         # mount -t tracefs nodev /sys/kernel/tracing
16         # echo function > /sys/kernel/tracing/current_tracer
17         $ cat /sys/kernel/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
25 import sys, re
26
27 class 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
89 class 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
95 class 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
102 def 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
112 def 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
128 if __name__ == "__main__":
129         main()