]> git.proxmox.com Git - mirror_qemu.git/blob - tests/tcg/multiarch/gdbstub/registers.py
tests/tcg: add an explicit gdbstub register tester
[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 sys
11 import xml.etree.ElementTree as ET
12
13 initial_vlen = 0
14 failcount = 0
15
16 def report(cond, msg):
17 "Report success/fail of test."
18 if cond:
19 print("PASS: %s" % (msg))
20 else:
21 print("FAIL: %s" % (msg))
22 global failcount
23 failcount += 1
24
25
26 def fetch_xml_regmap():
27 """
28 Iterate through the XML descriptions and validate.
29
30 We check for any duplicate registers and report them. Return a
31 reg_map hash containing the names, regnums and initial values of
32 all registers.
33 """
34
35 # First check the XML descriptions we have sent. Most arches
36 # support XML but a few of the ancient ones don't in which case we
37 # need to gracefully fail.
38
39 try:
40 xml = gdb.execute("maint print xml-tdesc", False, True)
41 except (gdb.error):
42 print("SKIP: target does not support XML")
43 return None
44
45 total_regs = 0
46 reg_map = {}
47 frame = gdb.selected_frame()
48
49 tree = ET.fromstring(xml)
50 for f in tree.findall("feature"):
51 name = f.attrib["name"]
52 regs = f.findall("reg")
53
54 total = len(regs)
55 total_regs += total
56 base = int(regs[0].attrib["regnum"])
57 top = int(regs[-1].attrib["regnum"])
58
59 print(f"feature: {name} has {total} registers from {base} to {top}")
60
61 for r in regs:
62 name = r.attrib["name"]
63 regnum = int(r.attrib["regnum"])
64 try:
65 value = frame.read_register(name)
66 except ValueError:
67 report(False, f"failed to read reg: {name}")
68
69 entry = { "name": name, "initial": value, "regnum": regnum }
70
71 if name in reg_map:
72 report(False, f"duplicate register {entry} vs {reg_map[name]}")
73 continue
74
75 reg_map[name] = entry
76
77 # Validate we match
78 report(total_regs == len(reg_map.keys()),
79 f"counted all {total_regs} registers in XML")
80
81 return reg_map
82
83 def crosscheck_remote_xml(reg_map):
84 """
85 Cross-check the list of remote-registers with the XML info.
86 """
87
88 remote = gdb.execute("maint print remote-registers", False, True)
89 r_regs = remote.split("\n")
90
91 total_regs = len(reg_map.keys())
92 total_r_regs = 0
93
94 for r in r_regs:
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 # check in the XML
104 try:
105 x_reg = reg_map[r_name]
106 except KeyError:
107 report(False, f"{r_name} not in XML description")
108 continue
109
110 x_reg["seen"] = True
111 x_regnum = x_reg["regnum"]
112 if r_regnum != x_regnum:
113 report(False, f"{r_name} {r_regnum} == {x_regnum} (xml)")
114 else:
115 total_r_regs += 1
116
117 # Just print a mismatch in totals as gdb will filter out 64 bit
118 # registers on a 32 bit machine. Also print what is missing to
119 # help with debug.
120 if total_regs != total_r_regs:
121 print(f"xml-tdesc has ({total_regs}) registers")
122 print(f"remote-registers has ({total_r_regs}) registers")
123
124 for x_key in reg_map.keys():
125 x_reg = reg_map[x_key]
126 if "seen" not in x_reg:
127 print(f"{x_reg} wasn't seen in remote-registers")
128
129 def complete_and_diff(reg_map):
130 """
131 Let the program run to (almost) completion and then iterate
132 through all the registers we know about and report which ones have
133 changed.
134 """
135 # Let the program get to the end and we can check what changed
136 b = gdb.Breakpoint("_exit")
137 if b.pending: # workaround Microblaze weirdness
138 b.delete()
139 gdb.Breakpoint("_Exit")
140
141 gdb.execute("continue")
142
143 frame = gdb.selected_frame()
144 changed = 0
145
146 for e in reg_map.values():
147 name = e["name"]
148 old_val = e["initial"]
149
150 try:
151 new_val = frame.read_register(name)
152 except:
153 report(False, f"failed to read {name} at end of run")
154 continue
155
156 if new_val != old_val:
157 print(f"{name} changes from {old_val} to {new_val}")
158 changed += 1
159
160 # as long as something changed we can be confident its working
161 report(changed > 0, f"{changed} registers were changed")
162
163
164 def run_test():
165 "Run through the tests"
166
167 reg_map = fetch_xml_regmap()
168
169 if reg_map is not None:
170 crosscheck_remote_xml(reg_map)
171 complete_and_diff(reg_map)
172
173
174 #
175 # This runs as the script it sourced (via -x, via run-test.py)
176 #
177 try:
178 inferior = gdb.selected_inferior()
179 arch = inferior.architecture()
180 print("ATTACHED: %s" % arch.name())
181 except (gdb.error, AttributeError):
182 print("SKIPPING (not connected)", file=sys.stderr)
183 exit(0)
184
185 if gdb.parse_and_eval('$pc') == 0:
186 print("SKIP: PC not set")
187 exit(0)
188
189 try:
190 run_test()
191 except (gdb.error):
192 print ("GDB Exception: %s" % (sys.exc_info()[0]))
193 failcount += 1
194 pass
195
196 print("All tests complete: %d failures" % failcount)
197 exit(failcount)