# Exercise the register functionality by exhaustively iterating # through all supported registers on the system. # # This is launched via tests/guest-debug/run-test.py but you can also # call it directly if using it for debugging/introspection: # # SPDX-License-Identifier: GPL-2.0-or-later import gdb import xml.etree.ElementTree as ET from test_gdbstub import main, report initial_vlen = 0 def fetch_xml_regmap(): """ Iterate through the XML descriptions and validate. We check for any duplicate registers and report them. Return a reg_map hash containing the names, regnums and initial values of all registers. """ # First check the XML descriptions we have sent. Most arches # support XML but a few of the ancient ones don't in which case we # need to gracefully fail. try: xml = gdb.execute("maint print xml-tdesc", False, True) except (gdb.error): print("SKIP: target does not support XML") return None total_regs = 0 reg_map = {} tree = ET.fromstring(xml) for f in tree.findall("feature"): name = f.attrib["name"] regs = f.findall("reg") total = len(regs) total_regs += total base = int(regs[0].attrib["regnum"]) top = int(regs[-1].attrib["regnum"]) print(f"feature: {name} has {total} registers from {base} to {top}") for r in regs: name = r.attrib["name"] regnum = int(r.attrib["regnum"]) entry = { "name": name, "regnum": regnum } if name in reg_map: report(False, f"duplicate register {entry} vs {reg_map[name]}") continue reg_map[name] = entry # Validate we match report(total_regs == len(reg_map.keys()), f"counted all {total_regs} registers in XML") return reg_map def get_register_by_regnum(reg_map, regnum): """ Helper to find a register from the map via its XML regnum """ for regname, entry in reg_map.items(): if entry['regnum'] == regnum: return entry return None def crosscheck_remote_xml(reg_map): """ Cross-check the list of remote-registers with the XML info. """ remote = gdb.execute("maint print remote-registers", False, True) r_regs = remote.split("\n") total_regs = len(reg_map.keys()) total_r_regs = 0 total_r_elided_regs = 0 for r in r_regs: r = r.replace("long long", "long_long") r = r.replace("long double", "long_double") fields = r.split() # Some of the registers reported here are "pseudo" registers that # gdb invents based on actual registers so we need to filter them # out. if len(fields) == 8: r_name = fields[0] r_regnum = int(fields[6]) # Some registers are "hidden" so don't have a name # although they still should have a register number if r_name == "''": total_r_elided_regs += 1 x_reg = get_register_by_regnum(reg_map, r_regnum) if x_reg is not None: x_reg["hidden"] = True continue # check in the XML try: x_reg = reg_map[r_name] except KeyError: report(False, f"{r_name} not in XML description") continue x_reg["seen"] = True x_regnum = x_reg["regnum"] if r_regnum != x_regnum: report(False, f"{r_name} {r_regnum} == {x_regnum} (xml)") else: total_r_regs += 1 report(total_regs == total_r_regs + total_r_elided_regs, "All XML Registers accounted for") print(f"xml-tdesc has {total_regs} registers") print(f"remote-registers has {total_r_regs} registers") print(f"of which {total_r_elided_regs} are hidden") for x_key in reg_map.keys(): x_reg = reg_map[x_key] if "hidden" in x_reg: print(f"{x_reg} elided by gdb") elif "seen" not in x_reg: print(f"{x_reg} wasn't seen in remote-registers") def initial_register_read(reg_map): """ Do an initial read of all registers that we know gdb cares about (so ignore the elided ones). """ frame = gdb.selected_frame() for e in reg_map.values(): name = e["name"] regnum = e["regnum"] try: if "hidden" in e: value = frame.read_register(regnum) e["initial"] = value elif "seen" in e: value = frame.read_register(name) e["initial"] = value except ValueError: report(False, f"failed to read reg: {name}") def complete_and_diff(reg_map): """ Let the program run to (almost) completion and then iterate through all the registers we know about and report which ones have changed. """ # Let the program get to the end and we can check what changed b = gdb.Breakpoint("_exit") if b.pending: # workaround Microblaze weirdness b.delete() gdb.Breakpoint("_Exit") gdb.execute("continue") frame = gdb.selected_frame() changed = 0 for e in reg_map.values(): if "initial" in e and "hidden" not in e: name = e["name"] old_val = e["initial"] try: new_val = frame.read_register(name) except ValueError: report(False, f"failed to read {name} at end of run") continue if new_val != old_val: print(f"{name} changes from {old_val} to {new_val}") changed += 1 # as long as something changed we can be confident its working report(changed > 0, f"{changed} registers were changed") def run_test(): "Run through the tests" reg_map = fetch_xml_regmap() if reg_map is not None: crosscheck_remote_xml(reg_map) initial_register_read(reg_map) complete_and_diff(reg_map) main(run_test)