History

scripts/gdb: fix interrupts.py after maple tree conversion

In commit 721255b9826b ("genirq: Use a maple tree for interrupt descriptor management"), the irq_desc_tree was replaced with a sparse_irqs tree using a maple tree structure. Since the script looked for the irq_desc_tree symbol which is no longer available, no interrupts would be printed and the script output would not be useful anymore. In addition to looking up the correct symbol (sparse_irqs), a new module (mapletree.py) is added whose mtree_load() implementation is largely copied after the C version and uses the same variable and intermediate function names wherever possible to ensure that both the C and Python version be updated in the future. This restores the scripts' output to match that of /proc/interrupts.

Florian Fainelli committed 1 year ago

Commit a02b0cd1 parent ea9b77f4 files · +293 −6Browse files

Unified · Split

4 files
  1. scripts/gdb/linux/constants.py.in +7 −0
  2. scripts/gdb/linux/interrupts.py +6 −6
  3. scripts/gdb/linux/mapletree.py +252 −0
  4. scripts/gdb/linux/xarray.py +28 −0
scripts/gdb/linux/constants.py.in+7−0
@@ -20,6 +20,7 @@ · 19 unmodified lines
2020 #include <linux/of_fdt.h>
2121 #include <linux/page_ext.h>
2222 #include <linux/radix-tree.h>
23+#include <linux/maple_tree.h>
2324 #include <linux/slab.h>
2425 #include <linux/threads.h>
2526 #include <linux/vmalloc.h>
@@ -93,6 +94,12 @@ LX_GDBPARSED(RADIX_TREE_MAP_SIZE) · 67 unmodified lines
9394 LX_GDBPARSED(RADIX_TREE_MAP_SHIFT)
9495 LX_GDBPARSED(RADIX_TREE_MAP_MASK)
9596
97+/* linux/maple_tree.h */
98+LX_VALUE(MAPLE_NODE_SLOTS)
99+LX_VALUE(MAPLE_RANGE64_SLOTS)
100+LX_VALUE(MAPLE_ARANGE64_SLOTS)
101+LX_GDBPARSED(MAPLE_NODE_MASK)
102+
96103 /* linux/vmalloc.h */
97104 LX_VALUE(VM_IOREMAP)
98105 LX_VALUE(VM_ALLOC)
scripts/gdb/linux/interrupts.py+6−6
@@ -7,7 +7,7 @@ import gdb · 6 unmodified lines
77 from linux import constants
88 from linux import cpus
99 from linux import utils
10−from linux import radixtree
10+from linux import mapletree
1111
1212 irq_desc_type = utils.CachedType("struct irq_desc")
1313
@@ -23,12 +23,12 @@ def irqd_is_level(desc): · 9 unmodified lines
2323 def show_irq_desc(prec, irq):
2424 text = ""
2525
26− desc = radixtree.lookup(gdb.parse_and_eval("&irq_desc_tree"), irq)
26+ desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq)
2727 if desc is None:
2828 return text
2929
30− desc = desc.cast(irq_desc_type.get_type())
31− if desc is None:
30+ desc = desc.cast(irq_desc_type.get_type().pointer())
31+ if desc == 0:
3232 return text
3333
3434 if irq_settings_is_hidden(desc):
@@ -221,8 +221,8 @@ class LxInterruptList(gdb.Command): · 186 unmodified lines
221221 gdb.write("CPU%-8d" % cpu)
222222 gdb.write("\n")
223223
224− if utils.gdb_eval_or_none("&irq_desc_tree") is None:
225− return
224+ if utils.gdb_eval_or_none("&sparse_irqs") is None:
225+ raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?")
226226
227227 for irq in range(nr_irqs):
228228 gdb.write(show_irq_desc(prec, irq))
scripts/gdb/linux/mapletree.py+252−0
@@ -0,0 +1,252 @@
1+# SPDX-License-Identifier: GPL-2.0
2+#
3+# Maple tree helpers
4+#
5+# Copyright (c) 2025 Broadcom
6+#
7+# Authors:
8+# Florian Fainelli <florian.fainelli@broadcom.com>
9+
10+import gdb
11+
12+from linux import utils
13+from linux import constants
14+from linux import xarray
15+
16+maple_tree_root_type = utils.CachedType("struct maple_tree")
17+maple_node_type = utils.CachedType("struct maple_node")
18+maple_enode_type = utils.CachedType("void")
19+
20+maple_dense = 0
21+maple_leaf_64 = 1
22+maple_range_64 = 2
23+maple_arange_64 = 3
24+
25+class Mas(object):
26+ ma_active = 0
27+ ma_start = 1
28+ ma_root = 2
29+ ma_none = 3
30+ ma_pause = 4
31+ ma_overflow = 5
32+ ma_underflow = 6
33+ ma_error = 7
34+
35+ def __init__(self, mt, first, end):
36+ if mt.type == maple_tree_root_type.get_type().pointer():
37+ self.tree = mt.dereference()
38+ elif mt.type != maple_tree_root_type.get_type():
39+ raise gdb.GdbError("must be {} not {}"
40+ .format(maple_tree_root_type.get_type().pointer(), mt.type))
41+ self.tree = mt
42+ self.index = first
43+ self.last = end
44+ self.node = None
45+ self.status = self.ma_start
46+ self.min = 0
47+ self.max = -1
48+
49+ def is_start(self):
50+ # mas_is_start()
51+ return self.status == self.ma_start
52+
53+ def is_ptr(self):
54+ # mas_is_ptr()
55+ return self.status == self.ma_root
56+
57+ def is_none(self):
58+ # mas_is_none()
59+ return self.status == self.ma_none
60+
61+ def root(self):
62+ # mas_root()
63+ return self.tree['ma_root'].cast(maple_enode_type.get_type().pointer())
64+
65+ def start(self):
66+ # mas_start()
67+ if self.is_start() is False:
68+ return None
69+
70+ self.min = 0
71+ self.max = ~0
72+
73+ while True:
74+ self.depth = 0
75+ root = self.root()
76+ if xarray.xa_is_node(root):
77+ self.depth = 0
78+ self.status = self.ma_active
79+ self.node = mte_safe_root(root)
80+ self.offset = 0
81+ if mte_dead_node(self.node) is True:
82+ continue
83+
84+ return None
85+
86+ self.node = None
87+ # Empty tree
88+ if root is None:
89+ self.status = self.ma_none
90+ self.offset = constants.LX_MAPLE_NODE_SLOTS
91+ return None
92+
93+ # Single entry tree
94+ self.status = self.ma_root
95+ self.offset = constants.LX_MAPLE_NODE_SLOTS
96+
97+ if self.index != 0:
98+ return None
99+
100+ return root
101+
102+ return None
103+
104+ def reset(self):
105+ # mas_reset()
106+ self.status = self.ma_start
107+ self.node = None
108+
109+def mte_safe_root(node):
110+ if node.type != maple_enode_type.get_type().pointer():
111+ raise gdb.GdbError("{} must be {} not {}"
112+ .format(mte_safe_root.__name__, maple_enode_type.get_type().pointer(), node.type))
113+ ulong_type = utils.get_ulong_type()
114+ indirect_ptr = node.cast(ulong_type) & ~0x2
115+ val = indirect_ptr.cast(maple_enode_type.get_type().pointer())
116+ return val
117+
118+def mte_node_type(entry):
119+ ulong_type = utils.get_ulong_type()
120+ val = None
121+ if entry.type == maple_enode_type.get_type().pointer():
122+ val = entry.cast(ulong_type)
123+ elif entry.type == ulong_type:
124+ val = entry
125+ else:
126+ raise gdb.GdbError("{} must be {} not {}"
127+ .format(mte_node_type.__name__, maple_enode_type.get_type().pointer(), entry.type))
128+ return (val >> 0x3) & 0xf
129+
130+def ma_dead_node(node):
131+ if node.type != maple_node_type.get_type().pointer():
132+ raise gdb.GdbError("{} must be {} not {}"
133+ .format(ma_dead_node.__name__, maple_node_type.get_type().pointer(), node.type))
134+ ulong_type = utils.get_ulong_type()
135+ parent = node['parent']
136+ indirect_ptr = node['parent'].cast(ulong_type) & ~constants.LX_MAPLE_NODE_MASK
137+ return indirect_ptr == node
138+
139+def mte_to_node(enode):
140+ ulong_type = utils.get_ulong_type()
141+ if enode.type == maple_enode_type.get_type().pointer():
142+ indirect_ptr = enode.cast(ulong_type)
143+ elif enode.type == ulong_type:
144+ indirect_ptr = enode
145+ else:
146+ raise gdb.GdbError("{} must be {} not {}"
147+ .format(mte_to_node.__name__, maple_enode_type.get_type().pointer(), enode.type))
148+ indirect_ptr = indirect_ptr & ~constants.LX_MAPLE_NODE_MASK
149+ return indirect_ptr.cast(maple_node_type.get_type().pointer())
150+
151+def mte_dead_node(enode):
152+ if enode.type != maple_enode_type.get_type().pointer():
153+ raise gdb.GdbError("{} must be {} not {}"
154+ .format(mte_dead_node.__name__, maple_enode_type.get_type().pointer(), enode.type))
155+ node = mte_to_node(enode)
156+ return ma_dead_node(node)
157+
158+def ma_is_leaf(tp):
159+ result = tp < maple_range_64
160+ return tp < maple_range_64
161+
162+def mt_pivots(t):
163+ if t == maple_dense:
164+ return 0
165+ elif t == maple_leaf_64 or t == maple_range_64:
166+ return constants.LX_MAPLE_RANGE64_SLOTS - 1
167+ elif t == maple_arange_64:
168+ return constants.LX_MAPLE_ARANGE64_SLOTS - 1
169+
170+def ma_pivots(node, t):
171+ if node.type != maple_node_type.get_type().pointer():
172+ raise gdb.GdbError("{}: must be {} not {}"
173+ .format(ma_pivots.__name__, maple_node_type.get_type().pointer(), node.type))
174+ if t == maple_arange_64:
175+ return node['ma64']['pivot']
176+ elif t == maple_leaf_64 or t == maple_range_64:
177+ return node['mr64']['pivot']
178+ else:
179+ return None
180+
181+def ma_slots(node, tp):
182+ if node.type != maple_node_type.get_type().pointer():
183+ raise gdb.GdbError("{}: must be {} not {}"
184+ .format(ma_slots.__name__, maple_node_type.get_type().pointer(), node.type))
185+ if tp == maple_arange_64:
186+ return node['ma64']['slot']
187+ elif tp == maple_range_64 or tp == maple_leaf_64:
188+ return node['mr64']['slot']
189+ elif tp == maple_dense:
190+ return node['slot']
191+ else:
192+ return None
193+
194+def mt_slot(mt, slots, offset):
195+ ulong_type = utils.get_ulong_type()
196+ return slots[offset].cast(ulong_type)
197+
198+def mtree_lookup_walk(mas):
199+ ulong_type = utils.get_ulong_type()
200+ n = mas.node
201+
202+ while True:
203+ node = mte_to_node(n)
204+ tp = mte_node_type(n)
205+ pivots = ma_pivots(node, tp)
206+ end = mt_pivots(tp)
207+ offset = 0
208+ while True:
209+ if pivots[offset] >= mas.index:
210+ break
211+ if offset >= end:
212+ break
213+ offset += 1
214+
215+ slots = ma_slots(node, tp)
216+ n = mt_slot(mas.tree, slots, offset)
217+ if ma_dead_node(node) is True:
218+ mas.reset()
219+ return None
220+ break
221+
222+ if ma_is_leaf(tp) is True:
223+ break
224+
225+ return n
226+
227+def mtree_load(mt, index):
228+ ulong_type = utils.get_ulong_type()
229+ # MT_STATE(...)
230+ mas = Mas(mt, index, index)
231+ entry = None
232+
233+ while True:
234+ entry = mas.start()
235+ if mas.is_none():
236+ return None
237+
238+ if mas.is_ptr():
239+ if index != 0:
240+ entry = None
241+ return entry
242+
243+ entry = mtree_lookup_walk(mas)
244+ if entry is None and mas.is_start():
245+ continue
246+ else:
247+ break
248+
249+ if xarray.xa_is_zero(entry):
250+ return None
251+
252+ return entry
scripts/gdb/linux/xarray.py+28−0
@@ -0,0 +1,28 @@
1+# SPDX-License-Identifier: GPL-2.0
2+#
3+# Xarray helpers
4+#
5+# Copyright (c) 2025 Broadcom
6+#
7+# Authors:
8+# Florian Fainelli <florian.fainelli@broadcom.com>
9+
10+import gdb
11+
12+from linux import utils
13+from linux import constants
14+
15+def xa_is_internal(entry):
16+ ulong_type = utils.get_ulong_type()
17+ return ((entry.cast(ulong_type) & 3) == 2)
18+
19+def xa_mk_internal(v):
20+ return ((v << 2) | 2)
21+
22+def xa_is_zero(entry):
23+ ulong_type = utils.get_ulong_type()
24+ return entry.cast(ulong_type) == xa_mk_internal(257)
25+
26+def xa_is_node(entry):
27+ ulong_type = utils.get_ulong_type()
28+ return xa_is_internal(entry) and (entry.cast(ulong_type) > 4096)