regcache::cooked_read unit test
authorYao Qi <yao.qi@linaro.org>
Fri, 24 Nov 2017 13:04:30 +0000 (13:04 +0000)
committerYao Qi <yao.qi@linaro.org>
Fri, 24 Nov 2017 13:04:30 +0000 (13:04 +0000)
This patch adds a unit test to regcache::cooked_read.  This unit test is a
little different from normal unit test, it is more about conformance test
or interaction test.  This test pass both raw register number and pseudo
register number to regcache::cooked_read, in order to inspect 1) return
value of cooked_read, 2) how are target_ops to_xfer_partial,
to_{fetch,store}_registers called (because regcache is updated by means of
these three target_ops methods).  With this test here, we have a clear
picture about how each port of GDB get cooked registers.

This patch also shares some code on mock target.

gdb:

2017-11-24  Yao Qi  <yao.qi@linaro.org>

* gdbarch-selftests.c (test_target_has_registers): Move it to
target.c.
(test_target_has_stack): Likewise.
(test_target_has_memory): Likewise.
(test_target_prepare_to_store): Likewise.
(test_target_store_registers): Likewise.
(test_target_ops): Likewise.
* regcache.c: Include selftest-arch.h and gdbthread.h.
(target_ops_no_register): New class.
(test_target_fetch_registers): New.
(test_target_store_registers): New.
(test_target_xfer_partial): New.
(readwrite_regcache): New.
(cooked_read_test): New.
(_initialize_regcache): Register the test.
* target.c: (test_target_has_registers): Moved from
gdbarch-selftests.c.
(test_target_has_stack): Likewise.
(test_target_has_memory): Likewise.
(test_target_prepare_to_store): Likewise.
(test_target_store_registers): Likewise.
* target.h (test_target_ops): New class.

gdb/ChangeLog
gdb/gdbarch-selftests.c
gdb/regcache.c
gdb/target.c
gdb/target.h

index a982caab68b7eb556d47e3aae18efbcd30d401ca..9947aaa8356c96072c11c97e27bde1889f2b6689 100644 (file)
@@ -1,3 +1,28 @@
+2017-11-24  Yao Qi  <yao.qi@linaro.org>
+
+       * gdbarch-selftests.c (test_target_has_registers): Move it to
+       target.c.
+       (test_target_has_stack): Likewise.
+       (test_target_has_memory): Likewise.
+       (test_target_prepare_to_store): Likewise.
+       (test_target_store_registers): Likewise.
+       (test_target_ops): Likewise.
+       * regcache.c: Include selftest-arch.h and gdbthread.h.
+       (target_ops_no_register): New class.
+       (test_target_fetch_registers): New.
+       (test_target_store_registers): New.
+       (test_target_xfer_partial): New.
+       (readwrite_regcache): New.
+       (cooked_read_test): New.
+       (_initialize_regcache): Register the test.
+       * target.c: (test_target_has_registers): Moved from
+       gdbarch-selftests.c.
+       (test_target_has_stack): Likewise.
+       (test_target_has_memory): Likewise.
+       (test_target_prepare_to_store): Likewise.
+       (test_target_store_registers): Likewise.
+       * target.h (test_target_ops): New class.
+
 2017-11-24  Alan Hayward  <alan.hayward@arm.com>
 
        * aarch64-tdep.c (_initialize_aarch64_tdep): Add target desc
index c748fcc6606ac0b5dbf3372ade48a5ff8b6a3c18..49d006f2dc88dd669b6f9b1acbb9c621baa2dedc 100644 (file)
 #include "gdbthread.h"
 #include "target.h"
 #include "target-float.h"
+#include "common/def-vector.h"
 
 namespace selftests {
 
-/* A mock process_stratum target_ops that doesn't read/write registers
-   anywhere.  */
-
-static int
-test_target_has_registers (target_ops *self)
-{
-  return 1;
-}
-
-static int
-test_target_has_stack (target_ops *self)
-{
-  return 1;
-}
-
-static int
-test_target_has_memory (target_ops *self)
-{
-  return 1;
-}
-
-static void
-test_target_prepare_to_store (target_ops *self, regcache *regs)
-{
-}
-
-static void
-test_target_store_registers (target_ops *self, regcache *regs, int regno)
-{
-}
-
-class test_target_ops : public target_ops
-{
-public:
-  test_target_ops ()
-    : target_ops {}
-  {
-    to_magic = OPS_MAGIC;
-    to_stratum = process_stratum;
-    to_has_memory = test_target_has_memory;
-    to_has_stack = test_target_has_stack;
-    to_has_registers = test_target_has_registers;
-    to_prepare_to_store = test_target_prepare_to_store;
-    to_store_registers = test_target_store_registers;
-
-    complete_target_initialization (this);
-  }
-};
-
 /* Test gdbarch methods register_to_value and value_to_register.  */
 
 static void
index 9c68bddbba01864ae223506bdf6845c28045b325..c37a520b3f8edd9327417c1f9934ca2175062b49 100644 (file)
@@ -1611,6 +1611,8 @@ maintenance_print_remote_registers (const char *args, int from_tty)
 
 #if GDB_SELF_TEST
 #include "selftest.h"
+#include "selftest-arch.h"
+#include "gdbthread.h"
 
 namespace selftests {
 
@@ -1679,6 +1681,192 @@ current_regcache_test (void)
   SELF_CHECK (regcache_access::current_regcache_size () == 2);
 }
 
+static void test_target_fetch_registers (target_ops *self, regcache *regs,
+                                        int regno);
+static void test_target_store_registers (target_ops *self, regcache *regs,
+                                        int regno);
+static enum target_xfer_status
+  test_target_xfer_partial (struct target_ops *ops,
+                           enum target_object object,
+                           const char *annex, gdb_byte *readbuf,
+                           const gdb_byte *writebuf,
+                           ULONGEST offset, ULONGEST len,
+                           ULONGEST *xfered_len);
+
+class target_ops_no_register : public test_target_ops
+{
+public:
+  target_ops_no_register ()
+    : test_target_ops {}
+  {
+    to_fetch_registers = test_target_fetch_registers;
+    to_store_registers = test_target_store_registers;
+    to_xfer_partial = test_target_xfer_partial;
+
+    to_data = this;
+  }
+
+  void reset ()
+  {
+    fetch_registers_called = 0;
+    store_registers_called = 0;
+    xfer_partial_called = 0;
+  }
+
+  unsigned int fetch_registers_called = 0;
+  unsigned int store_registers_called = 0;
+  unsigned int xfer_partial_called = 0;
+};
+
+static void
+test_target_fetch_registers (target_ops *self, regcache *regs, int regno)
+{
+  auto ops = static_cast<target_ops_no_register *> (self->to_data);
+
+  /* Mark register available.  */
+  regs->raw_supply_zeroed (regno);
+  ops->fetch_registers_called++;
+}
+
+static void
+test_target_store_registers (target_ops *self, regcache *regs, int regno)
+{
+  auto ops = static_cast<target_ops_no_register *> (self->to_data);
+
+  ops->store_registers_called++;
+}
+
+static enum target_xfer_status
+test_target_xfer_partial (struct target_ops *self, enum target_object object,
+                         const char *annex, gdb_byte *readbuf,
+                         const gdb_byte *writebuf,
+                         ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
+{
+  auto ops = static_cast<target_ops_no_register *> (self->to_data);
+
+  ops->xfer_partial_called++;
+
+  *xfered_len = len;
+  return TARGET_XFER_OK;
+}
+
+class readwrite_regcache : public regcache
+{
+public:
+  readwrite_regcache (struct gdbarch *gdbarch)
+    : regcache (gdbarch, nullptr, false)
+  {}
+};
+
+/* Test regcache::cooked_read gets registers from raw registers and
+   memory instead of target to_{fetch,store}_registers.  */
+
+static void
+cooked_read_test (struct gdbarch *gdbarch)
+{
+  /* Error out if debugging something, because we're going to push the
+     test target, which would pop any existing target.  */
+  if (current_target.to_stratum >= process_stratum)
+    error (_("target already pushed"));
+
+  /* Create a mock environment.  An inferior with a thread, with a
+     process_stratum target pushed.  */
+
+  target_ops_no_register mock_target;
+  ptid_t mock_ptid (1, 1);
+  inferior mock_inferior (mock_ptid.pid ());
+  address_space mock_aspace {};
+  mock_inferior.gdbarch = gdbarch;
+  mock_inferior.aspace = &mock_aspace;
+  thread_info mock_thread (&mock_inferior, mock_ptid);
+
+  scoped_restore restore_thread_list
+    = make_scoped_restore (&thread_list, &mock_thread);
+
+  /* Add the mock inferior to the inferior list so that look ups by
+     target+ptid can find it.  */
+  scoped_restore restore_inferior_list
+    = make_scoped_restore (&inferior_list);
+  inferior_list = &mock_inferior;
+
+  /* Switch to the mock inferior.  */
+  scoped_restore_current_inferior restore_current_inferior;
+  set_current_inferior (&mock_inferior);
+
+  /* Push the process_stratum target so we can mock accessing
+     registers.  */
+  push_target (&mock_target);
+
+  /* Pop it again on exit (return/exception).  */
+  struct on_exit
+  {
+    ~on_exit ()
+    {
+      pop_all_targets_at_and_above (process_stratum);
+    }
+  } pop_targets;
+
+  /* Switch to the mock thread.  */
+  scoped_restore restore_inferior_ptid
+    = make_scoped_restore (&inferior_ptid, mock_ptid);
+
+  /* Test that read one raw register from regcache_no_target will go
+     to the target layer.  */
+  int regnum;
+
+  /* Find a raw register which size isn't zero.  */
+  for (regnum = 0; regnum < gdbarch_num_regs (gdbarch); regnum++)
+    {
+      if (register_size (gdbarch, regnum) != 0)
+       break;
+    }
+
+  readwrite_regcache readwrite (gdbarch);
+  gdb::def_vector<gdb_byte> buf (register_size (gdbarch, regnum));
+
+  readwrite.raw_read (regnum, buf.data ());
+
+  /* raw_read calls target_fetch_registers.  */
+  SELF_CHECK (mock_target.fetch_registers_called > 0);
+  mock_target.reset ();
+
+  /* Mark all raw registers valid, so the following raw registers
+     accesses won't go to target.  */
+  for (auto i = 0; i < gdbarch_num_regs (gdbarch); i++)
+    readwrite.raw_update (i);
+
+  mock_target.reset ();
+  /* Then, read all raw and pseudo registers, and don't expect calling
+     to_{fetch,store}_registers.  */
+  for (int regnum = 0;
+       regnum < gdbarch_num_regs (gdbarch) + gdbarch_num_pseudo_regs (gdbarch);
+       regnum++)
+    {
+      if (register_size (gdbarch, regnum) == 0)
+       continue;
+
+      gdb::def_vector<gdb_byte> buf (register_size (gdbarch, regnum));
+
+      SELF_CHECK (REG_VALID == readwrite.cooked_read (regnum, buf.data ()));
+
+      if (gdbarch_bfd_arch_info (gdbarch)->arch != bfd_arch_mt)
+       {
+         /* MT pseudo registers are banked, and different banks are
+            selected by a raw registers, so GDB needs to write to
+            that raw register to get different banked pseudo registers.
+            See mt_select_coprocessor.  */
+         SELF_CHECK (mock_target.fetch_registers_called == 0);
+         SELF_CHECK (mock_target.store_registers_called == 0);
+       }
+
+      /* Some SPU pseudo registers are got via TARGET_OBJECT_SPU.  */
+      if (gdbarch_bfd_arch_info (gdbarch)->arch != bfd_arch_spu)
+       SELF_CHECK (mock_target.xfer_partial_called == 0);
+
+      mock_target.reset ();
+    }
+}
+
 } // namespace selftests
 #endif /* GDB_SELF_TEST */
 
@@ -1722,5 +1910,8 @@ Takes an optional file parameter."),
 
 #if GDB_SELF_TEST
   selftests::register_test ("current_regcache", selftests::current_regcache_test);
+
+  selftests::register_test_foreach_arch ("regcache::cooked_read_test",
+                                        selftests::cooked_read_test);
 #endif
 }
index 2e02a774e6223dbdeb4892d0a81f9c0eea47a4e2..3bfc8b5aefa80ceb3614d15536301ac329220293 100644 (file)
@@ -4018,6 +4018,53 @@ set_write_memory_permission (const char *args, int from_tty,
   update_observer_mode ();
 }
 
+#if GDB_SELF_TEST
+namespace selftests {
+
+static int
+test_target_has_registers (target_ops *self)
+{
+  return 1;
+}
+
+static int
+test_target_has_stack (target_ops *self)
+{
+  return 1;
+}
+
+static int
+test_target_has_memory (target_ops *self)
+{
+  return 1;
+}
+
+static void
+test_target_prepare_to_store (target_ops *self, regcache *regs)
+{
+}
+
+static void
+test_target_store_registers (target_ops *self, regcache *regs, int regno)
+{
+}
+
+test_target_ops::test_target_ops ()
+  : target_ops {}
+{
+  to_magic = OPS_MAGIC;
+  to_stratum = process_stratum;
+  to_has_memory = test_target_has_memory;
+  to_has_stack = test_target_has_stack;
+  to_has_registers = test_target_has_registers;
+  to_prepare_to_store = test_target_prepare_to_store;
+  to_store_registers = test_target_store_registers;
+
+  complete_target_initialization (this);
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
 
 void
 initialize_targets (void)
index 1683af64d5641f6f7831bdea1ad6db73bdddf40b..0d1e7bd6e8d3d9a60fed70745dd9745b3fd10e92 100644 (file)
@@ -2523,4 +2523,18 @@ extern void target_prepare_to_generate_core (void);
 /* See to_done_generating_core.  */
 extern void target_done_generating_core (void);
 
+#if GDB_SELF_TEST
+namespace selftests {
+
+/* A mock process_stratum target_ops that doesn't read/write registers
+   anywhere.  */
+
+class test_target_ops : public target_ops
+{
+public:
+  test_target_ops ();
+};
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
 #endif /* !defined (TARGET_H) */