[v3,gdb/testsuite] Add selftest disassemble-s390x

Message ID 20250409094718.27893-1-tdevries@suse.de
State Committed
Headers
Series [v3,gdb/testsuite] Add selftest disassemble-s390x |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed

Commit Message

Tom de Vries April 9, 2025, 9:47 a.m. UTC
  In commit a98a6fa2d8e ("s390: Add arch15 instructions"), support for
new instructions was added to libopcodes, but the added tests only exercise
this for gas.

Add a unit test disassemble-s390x that checks gdb's ability to
disassemble one of these instructions:
...
$ gdb -q -batch -ex "maint selftest -v disassemble-s390x"
Running selftest disassemble-s390x.
0xb9 0x68 0x00 0x03 -> clzg	%r0,%r3
Ran 1 unit tests, 0 failed
...

Tested on x86_64-linux and s390x-linux.
---
 gdb/disasm-selftests.c | 87 ++++++++++++++++++++++++++++++++++++++++++
 gdb/disasm-selftests.h | 32 ++++++++++++++++
 gdb/s390-tdep.c        | 52 +++++++++++++++++++++++++
 3 files changed, 171 insertions(+)
 create mode 100644 gdb/disasm-selftests.h


base-commit: 981fe5fd80faf511aa265e841a380c9b46be30e6
  

Comments

Tom de Vries April 23, 2025, 9:04 a.m. UTC | #1
On 4/9/25 11:47, Tom de Vries wrote:
> In commit a98a6fa2d8e ("s390: Add arch15 instructions"), support for
> new instructions was added to libopcodes, but the added tests only exercise
> this for gas.
> 
> Add a unit test disassemble-s390x that checks gdb's ability to
> disassemble one of these instructions:
> ...
> $ gdb -q -batch -ex "maint selftest -v disassemble-s390x"
> Running selftest disassemble-s390x.
> 0xb9 0x68 0x00 0x03 -> clzg	%r0,%r3
> Ran 1 unit tests, 0 failed
> ...
> 

I've pushed this.

Thanks,
- Tom

> Tested on x86_64-linux and s390x-linux.
> ---
>   gdb/disasm-selftests.c | 87 ++++++++++++++++++++++++++++++++++++++++++
>   gdb/disasm-selftests.h | 32 ++++++++++++++++
>   gdb/s390-tdep.c        | 52 +++++++++++++++++++++++++
>   3 files changed, 171 insertions(+)
>   create mode 100644 gdb/disasm-selftests.h
> 
> diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c
> index ffd25bdff93..3ccc1747982 100644
> --- a/gdb/disasm-selftests.c
> +++ b/gdb/disasm-selftests.c
> @@ -21,6 +21,7 @@
>   #include "gdbsupport/selftest.h"
>   #include "selftest-arch.h"
>   #include "gdbarch.h"
> +#include "disasm-selftests.h"
>   
>   namespace selftests {
>   
> @@ -329,6 +330,92 @@ memory_error_test (struct gdbarch *gdbarch)
>     SELF_CHECK (saw_memory_error);
>   }
>   
> +/* Disassemble INSN (a GDBARCH insn), and return the result.  */
> +
> +static std::string
> +disassemble_one_insn_to_string (struct gdbarch *gdbarch,
> +				gdb::array_view<const gdb_byte> insn)
> +{
> +  string_file buffer;
> +
> +  class gdb_disassembler_test : public gdb_disassembler
> +  {
> +  public:
> +
> +    explicit gdb_disassembler_test (struct gdbarch *gdbarch,
> +				    gdb::array_view<const gdb_byte> insn,
> +				    string_file &buffer)
> +      : gdb_disassembler (gdbarch,
> +			  &buffer,
> +			  gdb_disassembler_test::read_memory),
> +	m_insn (insn)
> +    {
> +    }
> +
> +    int
> +    print_insn (CORE_ADDR memaddr)
> +    {
> +      try
> +	{
> +	  return gdb_disassembler::print_insn (memaddr);
> +	}
> +      catch (const gdb_exception_error &)
> +	{
> +	  return -1;
> +	}
> +    }
> +
> +  private:
> +    gdb::array_view<const gdb_byte> m_insn;
> +
> +    static int read_memory (bfd_vma memaddr, gdb_byte *myaddr,
> +			    unsigned int len,
> +			    struct disassemble_info *info) noexcept
> +    {
> +      gdb_disassembler_test *self
> +	= static_cast<gdb_disassembler_test *>(info->application_data);
> +
> +      if (len > self->m_insn.size ())
> +	return -1;
> +
> +      for (size_t i = 0; i < len; i++)
> +	myaddr[i] = self->m_insn[i];
> +
> +      return 0;
> +    }
> +  };
> +
> +  gdb_disassembler_test di (gdbarch, insn, buffer);
> +  if (di.print_insn (0) != insn.size ())
> +    return "";
> +
> +  return buffer.string ();
> +}
> +
> +/* See disasm-selftests.h.  */
> +
> +void
> +disassemble_insn (gdbarch *gdbarch, gdb::byte_vector &insn,
> +		  const std::string &expected)
> +{
> +  std::string buffer
> +    = disassemble_one_insn_to_string (gdbarch, insn);
> +
> +  bool check_ok = buffer == expected;
> +
> +  if (run_verbose () || !check_ok)
> +    {
> +      for (gdb_byte b : insn)
> +	debug_printf ("0x%02x ", b);
> +      debug_printf ("-> %s\n", buffer.c_str ());
> +    }
> +
> +  if (!check_ok)
> +    debug_printf ("expected: %s\n", expected.c_str ());
> +
> +  SELF_CHECK (check_ok);
> +}
> +
>   } /* namespace selftests */
>   
>   void _initialize_disasm_selftests ();
> diff --git a/gdb/disasm-selftests.h b/gdb/disasm-selftests.h
> new file mode 100644
> index 00000000000..29acf876cce
> --- /dev/null
> +++ b/gdb/disasm-selftests.h
> @@ -0,0 +1,32 @@
> +/* Copyright (C) 2025 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GDB_DISASM_SELFTESTS_H
> +#define GDB_DISASM_SELFTESTS_H
> +
> +namespace selftests
> +{
> +
> +/* Check that disassembly of INSN (a GDBARCH insn) matches EXPECTED.  */
> +
> +void
> +disassemble_insn (gdbarch *gdbarch, gdb::byte_vector &insn,
> +		  const std::string &expected);
> +
> +}
> +
> +#endif /* GDB_DISASM_SELFTESTS_H */
> diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
> index d030a4d0323..a3b7658ceed 100644
> --- a/gdb/s390-tdep.c
> +++ b/gdb/s390-tdep.c
> @@ -41,6 +41,8 @@
>   #include "value.h"
>   #include "inferior.h"
>   #include "dwarf2/loc.h"
> +#include "gdbsupport/selftest.h"
> +#include "gdb/disasm-selftests.h"
>   
>   #include "features/s390-linux32.c"
>   #include "features/s390x-linux64.c"
> @@ -7468,6 +7470,51 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>     return gdbarch;
>   }
>   
> +#if GDB_SELF_TEST
> +namespace selftests {
> +
> +/* Return bfd_arch_info representing s390x.  */
> +
> +static const bfd_arch_info *
> +bfd_arch_info_s390x ()
> +{
> +  return bfd_lookup_arch (bfd_arch_s390, bfd_mach_s390_64);
> +}
> +
> +/* Return gdbarch representing s390x.  */
> +
> +static gdbarch *
> +gdbarch_s390x ()
> +{
> +  struct gdbarch_info info;
> +  info.bfd_arch_info = bfd_arch_info_s390x ();
> +  if (info.bfd_arch_info == nullptr)
> +    return nullptr;
> +
> +  info.osabi = GDB_OSABI_NONE;
> +  return gdbarch_find_by_info (info);
> +}
> +
> +/* Check disassembly of s390x instructions.  */
> +
> +static void
> +disassemble_s390x ()
> +{
> +  gdbarch *gdbarch = gdbarch_s390x ();
> +  if (gdbarch == nullptr)
> +    return;
> +
> +  scoped_restore disassembler_options_restore
> +    = make_scoped_restore (&s390_disassembler_options, "zarch");
> +
> +  gdb::byte_vector insn = { 0xb9, 0x68, 0x00, 0x03 };
> +  disassemble_insn (gdbarch, insn, "clzg\t%r0,%r3");
> +}
> +
> +} /* namespace selftests */
> +
> +#endif /* GDB_SELF_TEST */
> +
>   void _initialize_s390_tdep ();
>   void
>   _initialize_s390_tdep ()
> @@ -7477,4 +7524,9 @@ _initialize_s390_tdep ()
>   
>     initialize_tdesc_s390_linux32 ();
>     initialize_tdesc_s390x_linux64 ();
> +
> +#if GDB_SELF_TEST
> +  selftests::register_test ("disassemble-s390x",
> +			    selftests::disassemble_s390x);
> +#endif /* GDB_SELF_TEST */
>   }
> 
> base-commit: 981fe5fd80faf511aa265e841a380c9b46be30e6
  

Patch

diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c
index ffd25bdff93..3ccc1747982 100644
--- a/gdb/disasm-selftests.c
+++ b/gdb/disasm-selftests.c
@@ -21,6 +21,7 @@ 
 #include "gdbsupport/selftest.h"
 #include "selftest-arch.h"
 #include "gdbarch.h"
+#include "disasm-selftests.h"
 
 namespace selftests {
 
@@ -329,6 +330,92 @@  memory_error_test (struct gdbarch *gdbarch)
   SELF_CHECK (saw_memory_error);
 }
 
+/* Disassemble INSN (a GDBARCH insn), and return the result.  */
+
+static std::string
+disassemble_one_insn_to_string (struct gdbarch *gdbarch,
+				gdb::array_view<const gdb_byte> insn)
+{
+  string_file buffer;
+
+  class gdb_disassembler_test : public gdb_disassembler
+  {
+  public:
+
+    explicit gdb_disassembler_test (struct gdbarch *gdbarch,
+				    gdb::array_view<const gdb_byte> insn,
+				    string_file &buffer)
+      : gdb_disassembler (gdbarch,
+			  &buffer,
+			  gdb_disassembler_test::read_memory),
+	m_insn (insn)
+    {
+    }
+
+    int
+    print_insn (CORE_ADDR memaddr)
+    {
+      try
+	{
+	  return gdb_disassembler::print_insn (memaddr);
+	}
+      catch (const gdb_exception_error &)
+	{
+	  return -1;
+	}
+    }
+
+  private:
+    gdb::array_view<const gdb_byte> m_insn;
+
+    static int read_memory (bfd_vma memaddr, gdb_byte *myaddr,
+			    unsigned int len,
+			    struct disassemble_info *info) noexcept
+    {
+      gdb_disassembler_test *self
+	= static_cast<gdb_disassembler_test *>(info->application_data);
+
+      if (len > self->m_insn.size ())
+	return -1;
+
+      for (size_t i = 0; i < len; i++)
+	myaddr[i] = self->m_insn[i];
+
+      return 0;
+    }
+  };
+
+  gdb_disassembler_test di (gdbarch, insn, buffer);
+  if (di.print_insn (0) != insn.size ())
+    return "";
+
+  return buffer.string ();
+}
+
+/* See disasm-selftests.h.  */
+
+void
+disassemble_insn (gdbarch *gdbarch, gdb::byte_vector &insn,
+		  const std::string &expected)
+{
+  std::string buffer
+    = disassemble_one_insn_to_string (gdbarch, insn);
+
+  bool check_ok = buffer == expected;
+
+  if (run_verbose () || !check_ok)
+    {
+      for (gdb_byte b : insn)
+	debug_printf ("0x%02x ", b);
+      debug_printf ("-> %s\n", buffer.c_str ());
+    }
+
+  if (!check_ok)
+    debug_printf ("expected: %s\n", expected.c_str ());
+
+  SELF_CHECK (check_ok);
+}
+
 } /* namespace selftests */
 
 void _initialize_disasm_selftests ();
diff --git a/gdb/disasm-selftests.h b/gdb/disasm-selftests.h
new file mode 100644
index 00000000000..29acf876cce
--- /dev/null
+++ b/gdb/disasm-selftests.h
@@ -0,0 +1,32 @@ 
+/* Copyright (C) 2025 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GDB_DISASM_SELFTESTS_H
+#define GDB_DISASM_SELFTESTS_H
+
+namespace selftests
+{
+
+/* Check that disassembly of INSN (a GDBARCH insn) matches EXPECTED.  */
+
+void
+disassemble_insn (gdbarch *gdbarch, gdb::byte_vector &insn,
+		  const std::string &expected);
+
+}
+
+#endif /* GDB_DISASM_SELFTESTS_H */
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index d030a4d0323..a3b7658ceed 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -41,6 +41,8 @@ 
 #include "value.h"
 #include "inferior.h"
 #include "dwarf2/loc.h"
+#include "gdbsupport/selftest.h"
+#include "gdb/disasm-selftests.h"
 
 #include "features/s390-linux32.c"
 #include "features/s390x-linux64.c"
@@ -7468,6 +7470,51 @@  s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   return gdbarch;
 }
 
+#if GDB_SELF_TEST
+namespace selftests {
+
+/* Return bfd_arch_info representing s390x.  */
+
+static const bfd_arch_info *
+bfd_arch_info_s390x ()
+{
+  return bfd_lookup_arch (bfd_arch_s390, bfd_mach_s390_64);
+}
+
+/* Return gdbarch representing s390x.  */
+
+static gdbarch *
+gdbarch_s390x ()
+{
+  struct gdbarch_info info;
+  info.bfd_arch_info = bfd_arch_info_s390x ();
+  if (info.bfd_arch_info == nullptr)
+    return nullptr;
+
+  info.osabi = GDB_OSABI_NONE;
+  return gdbarch_find_by_info (info);
+}
+
+/* Check disassembly of s390x instructions.  */
+
+static void
+disassemble_s390x ()
+{
+  gdbarch *gdbarch = gdbarch_s390x ();
+  if (gdbarch == nullptr)
+    return;
+
+  scoped_restore disassembler_options_restore
+    = make_scoped_restore (&s390_disassembler_options, "zarch");
+
+  gdb::byte_vector insn = { 0xb9, 0x68, 0x00, 0x03 };
+  disassemble_insn (gdbarch, insn, "clzg\t%r0,%r3");
+}
+
+} /* namespace selftests */
+
+#endif /* GDB_SELF_TEST */
+
 void _initialize_s390_tdep ();
 void
 _initialize_s390_tdep ()
@@ -7477,4 +7524,9 @@  _initialize_s390_tdep ()
 
   initialize_tdesc_s390_linux32 ();
   initialize_tdesc_s390x_linux64 ();
+
+#if GDB_SELF_TEST
+  selftests::register_test ("disassemble-s390x",
+			    selftests::disassemble_s390x);
+#endif /* GDB_SELF_TEST */
 }