Introduce a script that provides a simple ascii representation of the timer migration tree on top of boot trace events. First boot with: trace_event==tmigr_connect_cpu_parent,tmigr_connect_child_parent Then parse the result with: scripts/timer_migration_tree.py < /sys/kernel/tracing/trace On a system with 8 CPUs, this produces the following output: Tree for capacity 1024 /-0, node 0, lvl:-1 | |--1, node 0, lvl:-1 | |--2, node 0, lvl:-1 | |--3, node 0, lvl:-1 -- /00000000dcebac8b, node 0, lvl:0 |--4, node 0, lvl:-1 | |--5, node 0, lvl:-1 | |--6, node 0, lvl:-1 | \-7, node 0, lvl:-1
Showing the changes to scripts/timer_migration_tree.py (1 of 1 file). Show all
Unified · Split
| @@ -0,0 +1,122 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | # SPDX-License-Identifier: GPL-2.0 | ||
| 3 | |||
| 4 | """ | ||
| 5 | Draw the timer migration tree. | ||
| 6 | |||
| 7 | 1) Boot with trace_event==tmigr_connect_cpu_parent,tmigr_connect_child_parent | ||
| 8 | 2) ./timer_migration_tree.py < /sys/kernel/tracing/trace | ||
| 9 | """ | ||
| 10 | |||
| 11 | import re, sys | ||
| 12 | from ete3 import Tree | ||
| 13 | |||
| 14 | class Node: | ||
| 15 | def __init__(self, group): | ||
| 16 | self.group = group | ||
| 17 | self.children = [] | ||
| 18 | self.parent = None | ||
| 19 | self.num_children = 0 | ||
| 20 | self.groupmask = 0 | ||
| 21 | self.lvl = -1 | ||
| 22 | |||
| 23 | def set_groupmask(self, groupmask): | ||
| 24 | self.groupmask = groupmask | ||
| 25 | |||
| 26 | def set_parent(self, parent): | ||
| 27 | self.parent = parent | ||
| 28 | |||
| 29 | def add_child(self, child): | ||
| 30 | self.children.append(child) | ||
| 31 | |||
| 32 | def set_lvl(self, lvl): | ||
| 33 | self.lvl = lvl | ||
| 34 | |||
| 35 | def set_numa(self, numa): | ||
| 36 | self.numa = numa | ||
| 37 | |||
| 38 | def set_num_children(self, num_children): | ||
| 39 | self.num_children = num_children | ||
| 40 | |||
| 41 | def __repr__(self): | ||
| 42 | if self.parent: | ||
| 43 | parent_grp = self.parent.group | ||
| 44 | else: | ||
| 45 | parent_grp = "-" | ||
| 46 | return "Group: %s mask: %s parent: %s lvl: %d numa: %d num_children: %d" % (self.group, self.groupmask, parent_grp, self.lvl, self.numa, self.num_children) | ||
| 47 | |||
| 48 | hierarchies = { } | ||
| 49 | |||
| 50 | def get_hierarchy(capacity): | ||
| 51 | if capacity not in hierarchies: | ||
| 52 | hierarchies[capacity] = {} | ||
| 53 | return hierarchies[capacity] | ||
| 54 | |||
| 55 | def get_node(capacity, group): | ||
| 56 | hier = get_hierarchy(capacity) | ||
| 57 | if group in hier: | ||
| 58 | return hier[group] | ||
| 59 | else: | ||
| 60 | n = Node(group) | ||
| 61 | hier[group] = n | ||
| 62 | return n | ||
| 63 | |||
| 64 | def tmigr_connect_cpu_parent(ts, line): | ||
| 65 | s = re.search("tmigr_connect_cpu_parent: cpu=([0-9]+) groupmask=([0-9a-zA-Z]+) parent=([0-9a-zA-Z]+) lvl=([0-9]+) numa=([-]?[0-9]+) capacity=([-]?[0-9]+) num_children=([0-9]+)", line) | ||
| 66 | if s is None: | ||
| 67 | return False | ||
| 68 | (cpu, groupmask, parent, lvl, numa, capacity, num_children) = (int(s.group(1)), s.group(2), s.group(3), int(s.group(4)), int(s.group(5)), int(s.group(6)), int(s.group(7))) | ||
| 69 | n = get_node(capacity, cpu) | ||
| 70 | p = get_node(capacity, parent) | ||
| 71 | n.set_parent(p) | ||
| 72 | n.set_groupmask(groupmask) | ||
| 73 | n.set_lvl(-1) | ||
| 74 | p.set_lvl(lvl) | ||
| 75 | p.set_numa(numa) | ||
| 76 | n.set_numa(numa) | ||
| 77 | p.set_num_children(num_children) | ||
| 78 | p.add_child(n) | ||
| 79 | |||
| 80 | def tmigr_connect_child_parent(ts, line): | ||
| 81 | s = re.search("tmigr_connect_child_parent: group=([0-9a-zA-Z]+) groupmask=([0-9a-zA-Z]+) parent=([0-9a-zA-Z]+) lvl=([0-9]+) numa=([-]?[0-9]+) capacity=([-]?[0-9]+) num_children=([0-9]+)", line) | ||
| 82 | if s is None: | ||
| 83 | return False | ||
| 84 | (group, groupmask, parent, lvl, numa, capacity, num_children) = (s.group(1), s.group(2), s.group(3), int(s.group(4)), int(s.group(5)), int(s.group(6)), int(s.group(7))) | ||
| 85 | n = get_node(capacity, group) | ||
| 86 | p = get_node(capacity, parent) | ||
| 87 | n.set_parent(p) | ||
| 88 | n.set_groupmask(groupmask) | ||
| 89 | p.set_lvl(lvl) | ||
| 90 | p.set_numa(numa) | ||
| 91 | p.set_num_children(num_children) | ||
| 92 | p.add_child(n) | ||
| 93 | |||
| 94 | def populate(enode, node): | ||
| 95 | enode = enode.add_child(name = node.group) | ||
| 96 | enode.add_feature("groupmask", "m:%s" % node.groupmask) | ||
| 97 | enode.add_feature("lvl", "lvl:%d" % node.lvl) | ||
| 98 | enode.add_feature("numa", "node %d" % node.numa) | ||
| 99 | enode.add_feature("num_children", "c=%d" % node.num_children) | ||
| 100 | for child in node.children: | ||
| 101 | populate(enode, child) | ||
| 102 | |||
| 103 | if __name__ == "__main__": | ||
| 104 | for line in sys.stdin: | ||
| 105 | s = re.search("([0-9]+[.][0-9]{6}): (.+?)$", line, re.S) | ||
| 106 | if s is not None: | ||
| 107 | if tmigr_connect_cpu_parent(float(s.group(1)), s.group(2)): | ||
| 108 | continue | ||
| 109 | if tmigr_connect_child_parent(float(s.group(1)), s.group(2)): | ||
| 110 | continue | ||
| 111 | |||
| 112 | for cap in hierarchies: | ||
| 113 | h = hierarchies[cap] | ||
| 114 | print("Tree for capacity %d" % cap) | ||
| 115 | for k in h: | ||
| 116 | n = h[k] | ||
| 117 | while n.parent != None: | ||
| 118 | n = n.parent | ||
| 119 | root = Tree() | ||
| 120 | populate(root, n) | ||
| 121 | print(root.get_ascii(show_internal=True, attributes=["name", "numa", "lvl"])) | ||
| 122 | break | ||