]> git.proxmox.com Git - mirror_qemu.git/blob - tests/tcg/multiarch/gdbstub/registers.py
tests/tcg: Factor out gdbstub test functions
[mirror_qemu.git] / tests / tcg / multiarch / gdbstub / registers.py
1 # Exercise the register functionality by exhaustively iterating
2 # through all supported registers on the system.
3 #
4 # This is launched via tests/guest-debug/run-test.py but you can also
5 # call it directly if using it for debugging/introspection:
6 #
7 # SPDX-License-Identifier: GPL-2.0-or-later
8
9 import gdb
10 import xml.etree.ElementTree as ET
11 from test_gdbstub import main, report
12
13
14 initial_vlen = 0
15
16
17 def fetch_xml_regmap():
18 """
19 Iterate through the XML descriptions and validate.
20
21 We check for any duplicate registers and report them. Return a
22 reg_map hash containing the names, regnums and initial values of
23 all registers.
24 """
25
26 # First check the XML descriptions we have sent. Most arches
27 # support XML but a few of the ancient ones don't in which case we
28 # need to gracefully fail.
29
30 try:
31 xml = gdb.execute("maint print xml-tdesc", False, True)
32 except (gdb.error):
33 print("SKIP: target does not support XML")
34 return None
35
36 total_regs = 0
37 reg_map = {}
38
39 tree = ET.fromstring(xml)
40 for f in tree.findall("feature"):
41 name = f.attrib["name"]
42 regs = f.findall("reg")
43
44 total = len(regs)
45 total_regs += total
46 base = int(regs[0].attrib["regnum"])
47 top = int(regs[-1].attrib["regnum"])
48
49 print(f"feature: {name} has {total} registers from {base} to {top}")
50
51 for r in regs:
52 name = r.attrib["name"]
53 regnum = int(r.attrib["regnum"])
54
55 entry = { "name": name, "regnum": regnum }
56
57 if name in reg_map:
58 report(False, f"duplicate register {entry} vs {reg_map[name]}")
59 continue
60
61 reg_map[name] = entry
62
63 # Validate we match
64 report(total_regs == len(reg_map.keys()),
65 f"counted all {total_regs} registers in XML")
66
67 return reg_map
68
69
70 def get_register_by_regnum(reg_map, regnum):
71 """
72 Helper to find a register from the map via its XML regnum
73 """
74 for regname, entry in reg_map.items():
75 if entry['regnum'] == regnum:
76 return entry
77 return None
78
79
80 def crosscheck_remote_xml(reg_map):
81 """
82 Cross-check the list of remote-registers with the XML info.
83 """
84
85 remote = gdb.execute("maint print remote-registers", False, True)
86 r_regs = remote.split("\n")
87
88 total_regs = len(reg_map.keys())
89 total_r_regs = 0
90 total_r_elided_regs = 0
91
92 for r in r_regs:
93 r = r.replace("long long", "long_long")
94 r = r.replace("long double", "long_double")
95 fields = r.split()
96 # Some of the registers reported here are "pseudo" registers that
97 # gdb invents based on actual registers so we need to filter them
98 # out.
99 if len(fields) == 8:
100 r_name = fields[0]
101 r_regnum = int(fields[6])
102
103 # Some registers are "hidden" so don't have a name
104 # although they still should have a register number
105 if r_name == "''":
106 total_r_elided_regs += 1
107 x_reg = get_register_by_regnum(reg_map, r_regnum)
108 if x_reg is not None:
109 x_reg["hidden"] = True
110 continue
111
112 # check in the XML
113 try:
114 x_reg = reg_map[r_name]
115 except KeyError:
116 report(False, f"{r_name} not in XML description")
117 continue
118
119 x_reg["seen"] = True
120 x_regnum = x_reg["regnum"]
121 if r_regnum != x_regnum:
122 report(False, f"{r_name} {r_regnum} == {x_regnum} (xml)")
123 else:
124 total_r_regs += 1
125
126 report(total_regs == total_r_regs + total_r_elided_regs,
127 "All XML Registers accounted for")
128
129 print(f"xml-tdesc has {total_regs} registers")
130 print(f"remote-registers has {total_r_regs} registers")
131 print(f"of which {total_r_elided_regs} are hidden")
132
133 for x_key in reg_map.keys():
134 x_reg = reg_map[x_key]
135 if "hidden" in x_reg:
136 print(f"{x_reg} elided by gdb")
137 elif "seen" not in x_reg:
138 print(f"{x_reg} wasn't seen in remote-registers")
139
140
141 def initial_register_read(reg_map):
142 """
143 Do an initial read of all registers that we know gdb cares about
144 (so ignore the elided ones).
145 """
146 frame = gdb.selected_frame()
147
148 for e in reg_map.values():
149 name = e["name"]
150 regnum = e["regnum"]
151
152 try:
153 if "hidden" in e:
154 value = frame.read_register(regnum)
155 e["initial"] = value
156 elif "seen" in e:
157 value = frame.read_register(name)
158 e["initial"] = value
159
160 except ValueError:
161 report(False, f"failed to read reg: {name}")
162
163
164 def complete_and_diff(reg_map):
165 """
166 Let the program run to (almost) completion and then iterate
167 through all the registers we know about and report which ones have
168 changed.
169 """
170 # Let the program get to the end and we can check what changed
171 b = gdb.Breakpoint("_exit")
172 if b.pending: # workaround Microblaze weirdness
173 b.delete()
174 gdb.Breakpoint("_Exit")
175
176 gdb.execute("continue")
177
178 frame = gdb.selected_frame()
179 changed = 0
180
181 for e in reg_map.values():
182 if "initial" in e and "hidden" not in e:
183 name = e["name"]
184 old_val = e["initial"]
185
186 try:
187 new_val = frame.read_register(name)
188 except ValueError:
189 report(False, f"failed to read {name} at end of run")
190 continue
191
192 if new_val != old_val:
193 print(f"{name} changes from {old_val} to {new_val}")
194 changed += 1
195
196 # as long as something changed we can be confident its working
197 report(changed > 0, f"{changed} registers were changed")
198
199
200 def run_test():
201 "Run through the tests"
202
203 reg_map = fetch_xml_regmap()
204
205 if reg_map is not None:
206 crosscheck_remote_xml(reg_map)
207 initial_register_read(reg_map)
208 complete_and_diff(reg_map)
209
210
211 main(run_test)