]> git.proxmox.com Git - mirror_qemu.git/blob - tests/tcg/multiarch/gdbstub/registers.py
688c0611072b26114f4bfc72a563ca5f0e639ef2
[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
48 tree = ET.fromstring(xml)
49 for f in tree.findall("feature"):
50 name = f.attrib["name"]
51 regs = f.findall("reg")
52
53 total = len(regs)
54 total_regs += total
55 base = int(regs[0].attrib["regnum"])
56 top = int(regs[-1].attrib["regnum"])
57
58 print(f"feature: {name} has {total} registers from {base} to {top}")
59
60 for r in regs:
61 name = r.attrib["name"]
62 regnum = int(r.attrib["regnum"])
63
64 entry = { "name": name, "regnum": regnum }
65
66 if name in reg_map:
67 report(False, f"duplicate register {entry} vs {reg_map[name]}")
68 continue
69
70 reg_map[name] = entry
71
72 # Validate we match
73 report(total_regs == len(reg_map.keys()),
74 f"counted all {total_regs} registers in XML")
75
76 return reg_map
77
78 def get_register_by_regnum(reg_map, regnum):
79 """
80 Helper to find a register from the map via its XML regnum
81 """
82 for regname, entry in reg_map.items():
83 if entry['regnum'] == regnum:
84 return entry
85 return None
86
87 def crosscheck_remote_xml(reg_map):
88 """
89 Cross-check the list of remote-registers with the XML info.
90 """
91
92 remote = gdb.execute("maint print remote-registers", False, True)
93 r_regs = remote.split("\n")
94
95 total_regs = len(reg_map.keys())
96 total_r_regs = 0
97 total_r_elided_regs = 0
98
99 for r in r_regs:
100 r = r.replace("long long", "long_long")
101 r = r.replace("long double", "long_double")
102 fields = r.split()
103 # Some of the registers reported here are "pseudo" registers that
104 # gdb invents based on actual registers so we need to filter them
105 # out.
106 if len(fields) == 8:
107 r_name = fields[0]
108 r_regnum = int(fields[6])
109
110 # Some registers are "hidden" so don't have a name
111 # although they still should have a register number
112 if r_name == "''":
113 total_r_elided_regs += 1
114 x_reg = get_register_by_regnum(reg_map, r_regnum)
115 if x_reg is not None:
116 x_reg["hidden"] = True
117 continue
118
119 # check in the XML
120 try:
121 x_reg = reg_map[r_name]
122 except KeyError:
123 report(False, f"{r_name} not in XML description")
124 continue
125
126 x_reg["seen"] = True
127 x_regnum = x_reg["regnum"]
128 if r_regnum != x_regnum:
129 report(False, f"{r_name} {r_regnum} == {x_regnum} (xml)")
130 else:
131 total_r_regs += 1
132
133 report(total_regs == total_r_regs + total_r_elided_regs,
134 "All XML Registers accounted for")
135
136 print(f"xml-tdesc has {total_regs} registers")
137 print(f"remote-registers has {total_r_regs} registers")
138 print(f"of which {total_r_elided_regs} are hidden")
139
140 for x_key in reg_map.keys():
141 x_reg = reg_map[x_key]
142 if "hidden" in x_reg:
143 print(f"{x_reg} elided by gdb")
144 elif "seen" not in x_reg:
145 print(f"{x_reg} wasn't seen in remote-registers")
146
147 def initial_register_read(reg_map):
148 """
149 Do an initial read of all registers that we know gdb cares about
150 (so ignore the elided ones).
151 """
152 frame = gdb.selected_frame()
153
154 for e in reg_map.values():
155 name = e["name"]
156 regnum = e["regnum"]
157
158 try:
159 if "hidden" in e:
160 value = frame.read_register(regnum)
161 e["initial"] = value
162 elif "seen" in e:
163 value = frame.read_register(name)
164 e["initial"] = value
165
166 except ValueError:
167 report(False, f"failed to read reg: {name}")
168
169
170 def complete_and_diff(reg_map):
171 """
172 Let the program run to (almost) completion and then iterate
173 through all the registers we know about and report which ones have
174 changed.
175 """
176 # Let the program get to the end and we can check what changed
177 b = gdb.Breakpoint("_exit")
178 if b.pending: # workaround Microblaze weirdness
179 b.delete()
180 gdb.Breakpoint("_Exit")
181
182 gdb.execute("continue")
183
184 frame = gdb.selected_frame()
185 changed = 0
186
187 for e in reg_map.values():
188 if "initial" in e and "hidden" not in e:
189 name = e["name"]
190 old_val = e["initial"]
191
192 try:
193 new_val = frame.read_register(name)
194 except ValueError:
195 report(False, f"failed to read {name} at end of run")
196 continue
197
198 if new_val != old_val:
199 print(f"{name} changes from {old_val} to {new_val}")
200 changed += 1
201
202 # as long as something changed we can be confident its working
203 report(changed > 0, f"{changed} registers were changed")
204
205
206 def run_test():
207 "Run through the tests"
208
209 reg_map = fetch_xml_regmap()
210
211 if reg_map is not None:
212 crosscheck_remote_xml(reg_map)
213 initial_register_read(reg_map)
214 complete_and_diff(reg_map)
215
216
217 #
218 # This runs as the script it sourced (via -x, via run-test.py)
219 #
220 try:
221 inferior = gdb.selected_inferior()
222 arch = inferior.architecture()
223 print("ATTACHED: %s" % arch.name())
224 except (gdb.error, AttributeError):
225 print("SKIPPING (not connected)", file=sys.stderr)
226 exit(0)
227
228 if gdb.parse_and_eval('$pc') == 0:
229 print("SKIP: PC not set")
230 exit(0)
231
232 try:
233 run_test()
234 except (gdb.error):
235 print ("GDB Exception: %s" % (sys.exc_info()[0]))
236 failcount += 1
237 pass
238
239 print("All tests complete: %d failures" % failcount)
240 exit(failcount)