[PATCH 4/6] Disassembly unit test: disassemble one instruction

Luis Machado lgustavo@codesourcery.com
Tue Jan 17 14:33:00 GMT 2017


On 01/16/2017 04:02 AM, Yao Qi wrote:
> This patch adds one unit test, which disassemble one instruction for
> every gdbarch if available.  The test needs one valid instruction of
> each gdbarch, and most of them are got from breakpoint instruction.
> For the rest gdbarch whose breakpoint instruction isn't a valid
> instruction, I copy one instruction from the gas/testsuite/gas/
> directory.
>
> I get the valid instruction of most gdbarch except ia64, mep, mips,
> tic6x, and xtensa.  People familiar with these arch should be easy
> to extend the test.
>
> In order to achieve "do the unit test for every gdbarch", I add
> selftest-arch.[c,h], so that we can register a function pointer,
> which has one argument gdbarch.  selftest.c will iterate over all
> gdbarches to call the registered function pointer.
>
> v2:
>  - Add comments for getting breakpoint instructions for score and
>    nios2,
>  - Provide more contents in gdb_disassembler_test::read_memory if
>    caller requests more than one instruction,
>  - Stop using compound literal,
>  - Use null_stream,
>  - Split "selftest for each gdbarch" out of selftest.{c,h},
>
> gdb:
>
> 2017-01-13  Yao Qi  <yao.qi@linaro.org>
>
> 	* Makefile.in (SFILES): Add disasm-selftests.c and
> 	selftest-arch.c.
> 	(COMMON_OBS): Add disasm-selftests.o and selftest-arch.o.
> 	* disasm-selftests.c: New file.
> 	* selftest-arch.c: New file.
> 	* selftest-arch.h: New file.
> ---
>  gdb/Makefile.in        |   5 ++
>  gdb/disasm-selftests.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++
>  gdb/selftest-arch.c    | 103 ++++++++++++++++++++++++++++
>  gdb/selftest-arch.h    |  26 ++++++++
>  4 files changed, 312 insertions(+)
>  create mode 100644 gdb/disasm-selftests.c
>  create mode 100644 gdb/selftest-arch.c
>  create mode 100644 gdb/selftest-arch.h
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 3ce7d69..e0fe442 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -1039,6 +1039,7 @@ SFILES = \
>  	dfp.c \
>  	dictionary.c \
>  	disasm.c \
> +	disasm-selftests.c \
>  	doublest.c \
>  	dtrace-probe.c \
>  	dummy-frame.c \
> @@ -1140,6 +1141,7 @@ SFILES = \
>  	rust-exp.y \
>  	rust-lang.c \
>  	selftest.c \
> +	selftest-arch.c \
>  	sentinel-frame.c \
>  	ser-base.c \
>  	ser-event.c \
> @@ -1396,6 +1398,7 @@ HFILES_NO_SRCDIR = \
>  	rs6000-tdep.h \
>  	s390-linux-tdep.h \
>  	score-tdep.h \
> +	selftest-arch.h \
>  	sentinel-frame.h \
>  	ser-base.h \
>  	ser-event.h \
> @@ -1643,6 +1646,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
>  	dfp.o \
>  	dictionary.o \
>  	disasm.o \
> +	disasm-selftests.o \
>  	doublest.o \
>  	dummy-frame.o \
>  	dwarf2-frame.o \
> @@ -1744,6 +1748,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
>  	run-time-clock.o \
>  	rust-lang.o \
>  	selftest.o \
> +	selftest-arch.o \
>  	sentinel-frame.o \
>  	ser-event.o \
>  	serial.o \
> diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c
> new file mode 100644
> index 0000000..46a0a21
> --- /dev/null
> +++ b/gdb/disasm-selftests.c
> @@ -0,0 +1,178 @@
> +/* Self tests for disassembler for GDB, the GNU debugger.
> +
> +   Copyright (C) 2017 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/>.  */
> +
> +#include "defs.h"
> +#include "disasm.h"
> +
> +#if GDB_SELF_TEST
> +#include "selftest.h"
> +#include "selftest-arch.h"
> +
> +namespace selftests {
> +
> +/* Test disassembly of one instruction.  */
> +
> +static void
> +gdb_disassembler_print_one_insn_test (struct gdbarch *gdbarch)

One suggestion. Do you think it is worth repeating the context of the 
test in the function names? We already know this is a gdb disassembler 
selftest from the name of the source file.

If we don't think it is worth it, we could significantly reduce the 
length of the name of these functions.

> +{
> +  size_t len = 0;
> +  const gdb_byte *insn = NULL;
> +
> +  switch (gdbarch_bfd_arch_info (gdbarch)->arch)
> +    {
> +    case bfd_arch_bfin:
> +      /* M3.L = 0xe117 */
> +      static const gdb_byte bfin_insn[] = {0x17, 0xe1, 0xff, 0xff};
> +
> +      insn = bfin_insn;
> +      len = sizeof (bfin_insn);
> +      break;
> +    case bfd_arch_arm:
> +      /* mov     r0, #0 */
> +      static const gdb_byte arm_insn[] = {0x0, 0x0, 0xa0, 0xe3};
> +
> +      insn = arm_insn;
> +      len = sizeof (arm_insn);
> +      break;
> +    case bfd_arch_ia64:
> +    case bfd_arch_mep:
> +    case bfd_arch_mips:
> +    case bfd_arch_tic6x:
> +    case bfd_arch_xtensa:
> +      return;
> +    case bfd_arch_s390:
> +      /* nopr %r7 */
> +      static const gdb_byte s390_insn[] = {0x07, 0x07};
> +
> +      insn = s390_insn;
> +      len = sizeof (s390_insn);
> +      break;
> +    case bfd_arch_xstormy16:
> +      /* nop */
> +      static const gdb_byte xstormy16_insn[] = {0x0, 0x0};
> +
> +      insn = xstormy16_insn;
> +      len = sizeof (xstormy16_insn);
> +      break;
> +    case bfd_arch_arc:
> +      {
> +	/* PR 21003 */
> +	if (gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_arc_arc601)
> +	  return;
> +      }
> +    case bfd_arch_nios2:
> +    case bfd_arch_score:
> +      /* nios2 and score need to know the current instruction to select
> +	 breakpoint instruction.  Give the breakpoint instruction kind
> +	 explicitly.  */
> +      insn = gdbarch_sw_breakpoint_from_kind (gdbarch, 4, (int *) &len);
> +      break;
> +    default:
> +      {
> +	/* Test disassemble breakpoint instruction.  */
> +	CORE_ADDR pc = 0;
> +	int kind = gdbarch_breakpoint_kind_from_pc (gdbarch, &pc);
> +
> +	insn = gdbarch_sw_breakpoint_from_kind (gdbarch, kind,
> +						(int *) &len);
> +
> +	break;
> +      }
> +    }
> +  SELF_CHECK (len > 0);
> +
> +  /* Test gdb_disassembler for a given gdbarch by reading data from a
> +     pre-allocated buffer.  If you want to see the disassembled
> +     instruction printed to gdb_stdout, set DISASSEMBLER_TEST_VERBOSE
> +     to true.  */
> +
> +  class gdb_disassembler_test : public gdb_disassembler
> +  {
> +  public:
> +
> +    const bool DISASSEMBLER_TEST_VERBOSE = false;
> +
> +    explicit gdb_disassembler_test (struct gdbarch *gdbarch,
> +				    const gdb_byte *insn,
> +				    size_t len)
> +      : gdb_disassembler (gdbarch,
> +			  (DISASSEMBLER_TEST_VERBOSE
> +			   ? gdb_stdout : null_stream ()),
> +			  gdb_disassembler_test::read_memory),
> +	m_insn (insn), m_len (len)
> +    {
> +    }
> +
> +    int

Can this ever return negative? If not, then unsigned? This ties down 
with the comment on the other patch.

> +    print_insn (CORE_ADDR memaddr)
> +    {
> +      if (DISASSEMBLER_TEST_VERBOSE)
> +	{
> +	  fprintf_unfiltered (stream (), "%s ",
> +			      gdbarch_bfd_arch_info (arch ())->arch_name);
> +	}
> +
> +      int len = gdb_disassembler::print_insn (memaddr);
> +
> +      if (DISASSEMBLER_TEST_VERBOSE)
> +	fprintf_unfiltered (stream (), "\n");
> +
> +      return len;
> +    }
> +
> +  private:
> +    /* A buffer contain one instruction.  */

"A buffer containing..." or "A buffer contains ..."

> +    const gdb_byte *m_insn;
> +
> +    /* Length of the buffer.  */
> +    size_t m_len;
> +
> +    static int read_memory (bfd_vma memaddr, gdb_byte *myaddr,
> +			    unsigned int len, struct disassemble_info *info)
> +    {
> +      gdb_disassembler_test *self
> +	= static_cast<gdb_disassembler_test *>(info->application_data);
> +
> +      /* The disassembler in opcodes may read more data than one
> +	 instruction.  */
> +      for (unsigned int i = 0; i < len; i++)
> +	myaddr[i] = self->m_insn[(memaddr + i) % self->m_len];
> +
> +      return 0;
> +    }
> +  };
> +
> +  gdb_disassembler_test di (gdbarch, insn, len);
> +
> +  SELF_CHECK (di.print_insn (0) == len);
> +}
> +
> +} // namespace selftests
> +#endif /* GDB_SELF_TEST */
> +
> +/* Suppress warning from -Wmissing-prototypes.  */
> +extern initialize_file_ftype _initialize_disasm_test;
> +
> +void
> +_initialize_disasm_test (void)
> +{
> +#if GDB_SELF_TEST
> +  register_self_test (selftests::gdb_disassembler_print_one_insn_test);
> +#endif
> +}
> diff --git a/gdb/selftest-arch.c b/gdb/selftest-arch.c
> new file mode 100644
> index 0000000..aa716be
> --- /dev/null
> +++ b/gdb/selftest-arch.c
> @@ -0,0 +1,103 @@
> +/* GDB self-test for each gdbarch.
> +   Copyright (C) 2017 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/>.  */
> +
> +#include "defs.h"
> +
> +#if GDB_SELF_TEST
> +#include "selftest.h"
> +#include "selftest-arch.h"
> +#include "arch-utils.h"
> +
> +static std::vector<self_test_function_with_gdbarch *> gdbarch_tests;
> +
> +void
> +register_self_test (self_test_function_with_gdbarch *function)
> +{
> +  gdbarch_tests.push_back (function);
> +}
> +
> +namespace selftests {
> +
> +static void
> +tests_with_arch (void)
> +{
> +  int failed = 0;
> +
> +  for (const auto &f : gdbarch_tests)
> +    {
> +      const char **arches = gdbarch_printable_names ();
> +      int i;
> +
> +      for (i = 0; arches[i] != NULL; i++)
> +	{
> +	  if (strcmp ("fr300", arches[i]) == 0)
> +	    {
> +	      /* PR 20946 */
> +	      continue;
> +	    }
> +	  else if (strcmp ("powerpc:EC603e", arches[i]) == 0
> +		   || strcmp ("powerpc:e500mc", arches[i]) == 0
> +		   || strcmp ("powerpc:e500mc64", arches[i]) == 0
> +		   || strcmp ("powerpc:titan", arches[i]) == 0
> +		   || strcmp ("powerpc:vle", arches[i]) == 0
> +		   || strcmp ("powerpc:e5500", arches[i]) == 0
> +		   || strcmp ("powerpc:e6500", arches[i]) == 0)
> +	    {
> +	      /* PR 19797 */
> +	      continue;
> +	    }
> +
> +	  QUIT;
> +
> +	  TRY
> +	    {
> +	      struct gdbarch_info info;
> +
> +	      gdbarch_info_init (&info);
> +	      info.bfd_arch_info = bfd_scan_arch (arches[i]);
> +
> +	      struct gdbarch *gdbarch = gdbarch_find_by_info (info);
> +	      SELF_CHECK (gdbarch != NULL);
> +	      f (gdbarch);
> +	    }
> +	  CATCH (ex, RETURN_MASK_ERROR)
> +	    {
> +	      ++failed;
> +	      exception_fprintf (gdb_stderr, ex,
> +				 _("Self test failed: arch %s: "), arches[i]);
> +	    }
> +	  END_CATCH
> +	}
> +    }
> +
> +  SELF_CHECK (failed == 0);
> +}
> +
> +} // namespace selftests
> +#endif /* GDB_SELF_TEST */
> +
> +/* Suppress warning from -Wmissing-prototypes.  */
> +extern initialize_file_ftype _initialize_selftests_with_arch;
> +
> +void
> +_initialize_selftests_with_arch (void)
> +{
> +#if GDB_SELF_TEST
> +  register_self_test (selftests::tests_with_arch);
> +#endif
> +}
> diff --git a/gdb/selftest-arch.h b/gdb/selftest-arch.h
> new file mode 100644
> index 0000000..d63c2d2
> --- /dev/null
> +++ b/gdb/selftest-arch.h
> @@ -0,0 +1,26 @@
> +/* GDB self-test for each gdbarch.
> +   Copyright (C) 2017 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 SELFTEST_ARCH_H
> +#define SELFTEST_ARCH_H
> +
> +typedef void self_test_function_with_gdbarch (struct gdbarch *);
> +
> +extern void register_self_test (self_test_function_with_gdbarch *function);
> +
> +#endif /* SELFTEST_ARCH_H */
>

Otherwise looks OK



More information about the Gdb-patches mailing list