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