xref: /openbmc/linux/scripts/tracing/draw_functrace.py (revision b8bb76713ec50df2f11efee386e16f93d51e1076)
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