[PATCH 3/8] Disassembly unit test: disassemble one instruction

Yao Qi
2017-01-10T12:26:44Z

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 extend
selftest.[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.

gdb:

2017-01-06 Yao Qi <yao.qi@linaro.org>

* disasm.c [GDB_SELF_TEST]: Include selftest.h.
(selftests::gdb_disassembler_print_one_insn_test): New function.
(_initialize_disasm): New function.
* selftest.c: Include "arch-utils.h".
(gdbarch_tests): New vector.
(register_self_test): New function.
(run_self_tests): Iterate each gdbarch and call functions in
gdbarch_tests.
* selftest.h (self_test_function_with_gdbarch): New typedef.
(register_self_test): Declare.
---
gdb/disasm.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/selftest.c | 55 +++++++++++++++++++++
gdb/selftest.h | 3 ++
3 files changed, 206 insertions(+)

diff --git a/gdb/disasm.c b/gdb/disasm.c
index 6a3f3aa..6e403da 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -27,6 +27,10 @@
#include "source.h"
#include <algorithm>

+#if GDB_SELF_TEST
+#include "selftest.h"
+#endif /* GDB_SELF_TEST */
+
/* Disassemble functions.
FIXME: We should get rid of all the duplicate code in gdb that does
the same thing: disassemble_command() and the gdbtk variation. */
@@ -290,6 +294,139 @@ gdb_disassembler::pretty_print_insn (struct ui_out *uiout,
return size;
}

+#if GDB_SELF_TEST
+
+namespace selftests {
+
+/* Test disassembly one instruction. */
+
+static void
+gdb_disassembler_print_one_insn_test (struct gdbarch *gdbarch)
+{
+ int len = -1;
+ const gdb_byte *insn = NULL;
+
+ switch (gdbarch_bfd_arch_info (gdbarch)->arch)
+ {
+ case bfd_arch_bfin:
+ /* M3.L = 0xe117 */
+ insn = (const gdb_byte[]) {0x17, 0xe1, 0xff, 0xff};
+ len = 4;
+ break;
+ case bfd_arch_arm:
+ /* mov r0, #0 */
+ insn = (const gdb_byte[]) {0x0, 0x0, 0xa0, 0xe3};
+ len = 4;
+ 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 */
+ insn = (const gdb_byte[]) {0x07, 0x07};
+ len = 2;
+ break;
+ case bfd_arch_xstormy16:
+ /* nop */
+ insn = (const gdb_byte[]) {0x0, 0x0};
+ len = 2;
+ 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:
+ insn = gdbarch_sw_breakpoint_from_kind (gdbarch, 4, &len);
+ break;
+ case bfd_arch_sh:
+ insn = gdbarch_sw_breakpoint_from_kind (gdbarch, 2, &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,
+ &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, define macro
+ DISASSEMBLER_TEST_VERBOSE. */
+
+ class gdb_disassembler_test : public gdb_disassembler
+ {
+ public:
+
+#ifndef DISASSEMBLER_TEST_VERBOSE
+ explicit gdb_disassembler_test (struct gdbarch *gdbarch,
+ const gdb_byte *insn)
+ : gdb_disassembler (gdbarch, ui_file_new (),
+ gdb_disassembler_test::read_memory),
+ m_insn (insn)
+ {
+ }
+
+ ~gdb_disassembler_test ()
+ {
+ ui_file_delete ((struct ui_file *) m_di.stream);
+ }
+#else
+ explicit gdb_disassembler_test (struct gdbarch *gdbarch,
+ const gdb_byte *insn)
+ : gdb_disassembler (gdbarch, gdb_stdout,
+ gdb_disassembler_test::read_memory),
+ m_insn (insn)
+ {
+ }
+
+ int
+ print_insn (CORE_ADDR memaddr)
+ {
+ fprintf_unfiltered (stream (), "%s ",
+ gdbarch_bfd_arch_info (arch ())->arch_name);
+
+ int len = gdb_disassembler::print_insn (memaddr);
+
+ fprintf_unfiltered (stream (), "\n");
+ return len;
+ }
+#endif /* DISASSEMBLER_TEST_VERBOSE */
+
+ private:
+ const gdb_byte *m_insn;
+
+ 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);
+
+ memcpy (myaddr, self->m_insn, len);
+ return 0;
+ }
+ };
+
+ gdb_disassembler_test di (gdbarch, insn);
+
+ SELF_CHECK (di.print_insn (0) == len);
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
static int
dump_insns (struct ui_out *uiout, gdb_disassembler *di,
CORE_ADDR low, CORE_ADDR high,
@@ -937,3 +1074,14 @@ gdb_buffered_insn_length (struct gdbarch *gdbarch,

return gdbarch_print_insn (gdbarch, addr, &di);
}
+
+/* Suppress warning from -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_disasm;
+
+void
+_initialize_disasm (void)
+{
+#if GDB_SELF_TEST
+ register_self_test (selftests::gdb_disassembler_print_one_insn_test);
+#endif
+}
diff --git a/gdb/selftest.c b/gdb/selftest.c
index adc7dda..8835473 100644
--- a/gdb/selftest.c
+++ b/gdb/selftest.c
@@ -18,11 +18,13 @@

#include "defs.h"
#include "selftest.h"
+#include "arch-utils.h"
#include <vector>

/* All the tests that have been registered. */

static std::vector<self_test_function *> tests;
+static std::vector<self_test_function_with_gdbarch *> gdbarch_tests;

/* See selftest.h. */

@@ -32,6 +34,12 @@ register_self_test (self_test_function *function)
tests.push_back (function);
}

+void
+register_self_test (self_test_function_with_gdbarch *function)
+{
+ gdbarch_tests.push_back (function);
+}
+
/* See selftest.h. */

void
@@ -55,6 +63,53 @@ run_self_tests (void)
END_CATCH
}

+ 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
+ }
+ }
+
printf_filtered (_("Ran %lu unit tests, %d failed\n"),
(long) tests.size (), failed);
}
diff --git a/gdb/selftest.h b/gdb/selftest.h
index 94684ff..4446780 100644
--- a/gdb/selftest.h
+++ b/gdb/selftest.h
@@ -24,9 +24,12 @@

typedef void self_test_function (void);

+typedef void self_test_function_with_gdbarch (struct gdbarch *);
+
/* Register a new self-test. */

extern void register_self_test (self_test_function *function);
+extern void register_self_test (self_test_function_with_gdbarch *function);

/* Run all the self tests. This print a message describing the number
of test and the number of failures. */
--
1.9.1