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 9import gdb 10import xml.etree.ElementTree as ET 11from test_gdbstub import main, report 12 13 14initial_vlen = 0 15 16 17def 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 70def 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 80def 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 141def 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 164def 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 200def 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 211main(run_test) 212