[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PULL 06/23] tests/tcg: add an explicit gdbstub register tester
From: |
Alex Bennée |
Subject: |
Re: [PULL 06/23] tests/tcg: add an explicit gdbstub register tester |
Date: |
Thu, 16 Nov 2023 14:59:07 +0000 |
User-agent: |
mu4e 1.11.24; emacs 29.1 |
Luis Machado <luis.machado@arm.com> writes:
> On 11/15/23 20:56, Alex Bennée via Gdb wrote:
>> "Nicholas Piggin" <npiggin@gmail.com> writes:
>>
>>> On Wed Nov 8, 2023 at 12:23 AM AEST, Alex Bennée wrote:
>>>> We already do a couple of "info registers" for specific tests but this
>>>> is a more comprehensive multiarch test. It also has some output
>>>> helpful for debugging the gdbstub by showing which XML features are
>>>> advertised and what the underlying register numbers are.
>>>>
>>>> My initial motivation was to see if there are any duplicate register
>>>> names exposed via the gdbstub while I was reviewing the proposed
>>>> register interface for TCG plugins.
>>>>
>>>> Mismatches between the xml and remote-desc are reported for debugging
>>>> but do not fail the test.
>>>>
>>>> We also skip the tests for the following arches for now until we can
>>>> investigate and fix any issues:
>>>>
>>>> - s390x (fails to read v0l->v15l, not seen in remote-registers)
>>>> - ppc64 (fails to read vs0h->vs31h, not seen in remote-registers)
>>>
>>> binutils-gdb.git/gdb/rs6000-tdep.c has:
>>>
>>> static const char *
>>> rs6000_register_name (struct gdbarch *gdbarch, int regno)
>>> {
>>> ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
>>>
>>> /* The upper half "registers" have names in the XML description,
>>> but we present only the low GPRs and the full 64-bit registers
>>> to the user. */
>>> if (tdep->ppc_ev0_upper_regnum >= 0
>>> && tdep->ppc_ev0_upper_regnum <= regno
>>> && regno < tdep->ppc_ev0_upper_regnum + ppc_num_gprs)
>>> return "";
>>>
>>> /* Hide the upper halves of the vs0~vs31 registers. */
>>> if (tdep->ppc_vsr0_regnum >= 0
>>> && tdep->ppc_vsr0_upper_regnum <= regno
>>> && regno < tdep->ppc_vsr0_upper_regnum + ppc_num_gprs)
>>> return "";
>>>
>>> (s390 looks similar for V0-V15 lower).
>>>
>>> I guess it is because the upper half is not a real register but an
>>> extension of an existing FP register to make a vector register. I
>>> just don't know how that should be resolved with QEMU.
>>>
>>> Should we put an exception in the test case for these? Or is there
>>> something we should be doing differently with the XML regs?
>>
>> Yeah I suspect this is just inconsistency between targets on gdb. My
>> naive assumption was XML should match the displayed registers but it
>> seems there is additional filtering going on.
>>
>> It seems in this case the registers are still there and have regnums (so
>> I assume the stub could be asked for them) but the names have been
>> squashed. I guess we could detect that and accept it?
>>
>>>
>>> i386 gdb does similar:
>>>
>>> static const char *
>>> i386_register_name (struct gdbarch *gdbarch, int regnum)
>>> {
>>> /* Hide the upper YMM registers. */
>>> if (i386_ymmh_regnum_p (gdbarch, regnum))
>>> return "";
>>>
>>> /* Hide the upper YMM16-31 registers. */
>>> if (i386_ymmh_avx512_regnum_p (gdbarch, regnum))
>>> return "";
>>>
>>> /* Hide the upper ZMM registers. */
>>> if (i386_zmmh_regnum_p (gdbarch, regnum))
>>> return "";
>>>
>>> return tdesc_register_name (gdbarch, regnum);
>>> }
>>>
>>> So, I'm not sure how they don't fail this test. Does QEMU just
>>> not have YMM/ZMM in XML regmap?
>>
>> No I think we only send the core one with XMM regs and there are no
>> additional registers sent via gdb_register_coprocessor.
>>
>>>
>>> Thanks,
>>> Nick
>
> FTR, luis.machado@linaro.org doesn't exist anymore.
>
> As for the XML, it serves as an architecture hint/description of what
> features and registers
> are available.
>
> GDB will process that and will potentially include additional
> pseudo-registers (so QEMU doesn't
> need to do so, unless it is some pseudo-register not accounted by gdb).
>
> The rest of the features/registers gdb doesn't care about, it will just add
> them to the end of the
> list, and will assign whatever number is next. GDB will be able to read/write
> them, but nothing more
> than that.
So with a bit of fiddling I can do:
modified tests/tcg/multiarch/gdbstub/registers.py
@@ -44,7 +44,6 @@ def fetch_xml_regmap():
total_regs = 0
reg_map = {}
- frame = gdb.selected_frame()
tree = ET.fromstring(xml)
for f in tree.findall("feature"):
@@ -61,12 +60,8 @@ def fetch_xml_regmap():
for r in regs:
name = r.attrib["name"]
regnum = int(r.attrib["regnum"])
- try:
- value = frame.read_register(name)
- except ValueError:
- report(False, f"failed to read reg: {name}")
- entry = { "name": name, "initial": value, "regnum": regnum }
+ entry = { "name": name, "regnum": regnum }
if name in reg_map:
report(False, f"duplicate register {entry} vs {reg_map[name]}")
@@ -80,6 +75,15 @@ def fetch_xml_regmap():
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.
@@ -90,6 +94,7 @@ def crosscheck_remote_xml(reg_map):
total_regs = len(reg_map.keys())
total_r_regs = 0
+ total_r_elided_regs = 0
for r in r_regs:
fields = r.split()
@@ -100,6 +105,15 @@ def crosscheck_remote_xml(reg_map):
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]
@@ -118,13 +132,38 @@ def crosscheck_remote_xml(reg_map):
# registers on a 32 bit machine. Also print what is missing to
# help with debug.
if total_regs != total_r_regs:
- print(f"xml-tdesc has ({total_regs}) registers")
- print(f"remote-registers has ({total_r_regs}) registers")
+ 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 "seen" not in x_reg:
- print(f"{x_reg} wasn't seen in remote-registers")
+ if "hidden" in x_reg:
+ print(f"{x_reg} elided by gdb")
+ elif "seen" not in x_reg:
+ report(False, 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)
+ else:
+ value = frame.read_register(name)
+
+ e["initial"] = value
+ except ValueError:
+ report(False, f"failed to read reg: {name}")
+
def complete_and_diff(reg_map):
"""
@@ -144,18 +183,19 @@ def complete_and_diff(reg_map):
changed = 0
for e in reg_map.values():
- name = e["name"]
- old_val = e["initial"]
+ if "hidden" not in e:
+ name = e["name"]
+ old_val = e["initial"]
- try:
- new_val = frame.read_register(name)
- except:
- report(False, f"failed to read {name} at end of run")
- continue
+ 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
+ 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")
@@ -168,6 +208,7 @@ def run_test():
if reg_map is not None:
crosscheck_remote_xml(reg_map)
+ initial_register_read(reg_map)
complete_and_diff(reg_map)
I'll wrap that into my next set of patches.
--
Alex Bennée
Virtualisation Tech Lead @ Linaro