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 sys 11import xml.etree.ElementTree as ET 12 13initial_vlen = 0 14failcount = 0 15 16def 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 26def 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 83def 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 129def 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 164def 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# 177try: 178 inferior = gdb.selected_inferior() 179 arch = inferior.architecture() 180 print("ATTACHED: %s" % arch.name()) 181except (gdb.error, AttributeError): 182 print("SKIPPING (not connected)", file=sys.stderr) 183 exit(0) 184 185if gdb.parse_and_eval('$pc') == 0: 186 print("SKIP: PC not set") 187 exit(0) 188 189try: 190 run_test() 191except (gdb.error): 192 print ("GDB Exception: %s" % (sys.exc_info()[0])) 193 failcount += 1 194 pass 195 196print("All tests complete: %d failures" % failcount) 197exit(failcount) 198