+ /* Get regcache from (target2,ptid2), a new regcache is added to
+ current_regcache, since this time we're using a differen
+ target. */
+ test_get_thread_arch_aspace_regcache (&test_target2, ptid2,
+ target_gdbarch (),
+ NULL);
+ SELF_CHECK (regcache_access::current_regcache_size () == 4);
+
+ /* Mark that (target1,ptid2) changed. The regcache of (target1,
+ ptid2) should be removed from current_regcache. */
+ registers_changed_ptid (&test_target1, ptid2);
+ SELF_CHECK (regcache_access::current_regcache_size () == 3);
+
+ /* Get the regcache from (target2,ptid2) again, confirming the
+ registers_changed_ptid call above did not delete it. */
+ test_get_thread_arch_aspace_regcache (&test_target2, ptid2,
+ target_gdbarch (),
+ NULL);
+ SELF_CHECK (regcache_access::current_regcache_size () == 3);
+
+ /* Confirm that marking all regcaches of all targets as changed
+ clears current_regcache. */
+ registers_changed_ptid (nullptr, minus_one_ptid);
+ SELF_CHECK (regcache_access::current_regcache_size () == 0);
+}
+
+class target_ops_no_register : public test_target_ops
+{
+public:
+ target_ops_no_register ()
+ : test_target_ops {}
+ {}
+
+ void reset ()
+ {
+ fetch_registers_called = 0;
+ store_registers_called = 0;
+ xfer_partial_called = 0;
+ }
+
+ void fetch_registers (regcache *regs, int regno) override;
+ void store_registers (regcache *regs, int regno) override;
+
+ enum target_xfer_status xfer_partial (enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len) override;
+
+ unsigned int fetch_registers_called = 0;
+ unsigned int store_registers_called = 0;
+ unsigned int xfer_partial_called = 0;
+};
+
+void
+target_ops_no_register::fetch_registers (regcache *regs, int regno)
+{
+ /* Mark register available. */
+ regs->raw_supply_zeroed (regno);
+ this->fetch_registers_called++;
+}
+
+void
+target_ops_no_register::store_registers (regcache *regs, int regno)
+{
+ this->store_registers_called++;
+}
+
+enum target_xfer_status
+target_ops_no_register::xfer_partial (enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len)
+{
+ this->xfer_partial_called++;
+
+ *xfered_len = len;
+ return TARGET_XFER_OK;
+}
+
+class readwrite_regcache : public regcache
+{
+public:
+ readwrite_regcache (process_stratum_target *target,
+ struct gdbarch *gdbarch)
+ : regcache (target, gdbarch, nullptr)
+ {}
+};
+
+/* 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_top_target ()->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);
+ mock_inferior.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. */
+
+ /* Find a raw register which size isn't zero. */
+ int nonzero_regnum;
+ for (nonzero_regnum = 0;
+ nonzero_regnum < gdbarch_num_regs (gdbarch);
+ nonzero_regnum++)
+ {
+ if (register_size (gdbarch, nonzero_regnum) != 0)
+ break;
+ }
+
+ readwrite_regcache readwrite (&mock_target, gdbarch);
+ gdb::def_vector<gdb_byte> buf (register_size (gdbarch, nonzero_regnum));
+
+ readwrite.raw_read (nonzero_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_cooked_regs (gdbarch); regnum++)
+ {
+ if (register_size (gdbarch, regnum) == 0)
+ continue;
+
+ gdb::def_vector<gdb_byte> inner_buf (register_size (gdbarch, regnum));
+
+ SELF_CHECK (REG_VALID == readwrite.cooked_read (regnum,
+ inner_buf.data ()));
+
+ SELF_CHECK (mock_target.fetch_registers_called == 0);
+ SELF_CHECK (mock_target.store_registers_called == 0);
+ SELF_CHECK (mock_target.xfer_partial_called == 0);
+
+ mock_target.reset ();
+ }
+
+ readonly_detached_regcache readonly (readwrite);
+
+ /* GDB may go to target layer to fetch all registers and memory for
+ readonly regcache. */
+ mock_target.reset ();
+
+ for (int regnum = 0; regnum < gdbarch_num_cooked_regs (gdbarch); regnum++)
+ {
+ if (register_size (gdbarch, regnum) == 0)
+ continue;
+
+ gdb::def_vector<gdb_byte> inner_buf (register_size (gdbarch, regnum));
+ enum register_status status = readonly.cooked_read (regnum,
+ inner_buf.data ());
+
+ if (regnum < gdbarch_num_regs (gdbarch))
+ {
+ auto bfd_arch = gdbarch_bfd_arch_info (gdbarch)->arch;
+
+ if (bfd_arch == bfd_arch_frv || bfd_arch == bfd_arch_h8300
+ || bfd_arch == bfd_arch_m32c || bfd_arch == bfd_arch_sh
+ || bfd_arch == bfd_arch_alpha || bfd_arch == bfd_arch_v850
+ || bfd_arch == bfd_arch_msp430 || bfd_arch == bfd_arch_mep
+ || bfd_arch == bfd_arch_mips || bfd_arch == bfd_arch_v850_rh850
+ || bfd_arch == bfd_arch_tic6x || bfd_arch == bfd_arch_mn10300
+ || bfd_arch == bfd_arch_rl78 || bfd_arch == bfd_arch_score
+ || bfd_arch == bfd_arch_riscv || bfd_arch == bfd_arch_csky)
+ {
+ /* Raw registers. If raw registers are not in save_reggroup,
+ their status are unknown. */
+ if (gdbarch_register_reggroup_p (gdbarch, regnum, save_reggroup))
+ SELF_CHECK (status == REG_VALID);
+ else
+ SELF_CHECK (status == REG_UNKNOWN);
+ }
+ else
+ SELF_CHECK (status == REG_VALID);
+ }
+ else
+ {
+ if (gdbarch_register_reggroup_p (gdbarch, regnum, save_reggroup))
+ SELF_CHECK (status == REG_VALID);
+ else
+ {
+ /* If pseudo registers are not in save_reggroup, some of
+ them can be computed from saved raw registers, but some
+ of them are unknown. */
+ auto bfd_arch = gdbarch_bfd_arch_info (gdbarch)->arch;
+
+ if (bfd_arch == bfd_arch_frv
+ || bfd_arch == bfd_arch_m32c
+ || bfd_arch == bfd_arch_mep
+ || bfd_arch == bfd_arch_sh)
+ SELF_CHECK (status == REG_VALID || status == REG_UNKNOWN);
+ else if (bfd_arch == bfd_arch_mips
+ || bfd_arch == bfd_arch_h8300)
+ SELF_CHECK (status == REG_UNKNOWN);
+ else
+ SELF_CHECK (status == REG_VALID);
+ }
+ }
+
+ SELF_CHECK (mock_target.fetch_registers_called == 0);
+ SELF_CHECK (mock_target.store_registers_called == 0);
+ SELF_CHECK (mock_target.xfer_partial_called == 0);
+
+ mock_target.reset ();
+ }
+}
+
+/* Test regcache::cooked_write by writing some expected contents to
+ registers, and checking that contents read from registers and the
+ expected contents are the same. */
+
+static void
+cooked_write_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_top_target ()->stratum () >= process_stratum)
+ error (_("target already pushed"));
+
+ /* Create a mock environment. A process_stratum target pushed. */
+
+ target_ops_no_register mock_target;
+
+ /* 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;
+
+ readwrite_regcache readwrite (&mock_target, gdbarch);
+
+ const int num_regs = gdbarch_num_cooked_regs (gdbarch);
+
+ for (auto regnum = 0; regnum < num_regs; regnum++)
+ {
+ if (register_size (gdbarch, regnum) == 0
+ || gdbarch_cannot_store_register (gdbarch, regnum))
+ continue;
+
+ auto bfd_arch = gdbarch_bfd_arch_info (gdbarch)->arch;
+
+ if (bfd_arch == bfd_arch_sparc
+ /* SPARC64_CWP_REGNUM, SPARC64_PSTATE_REGNUM,
+ SPARC64_ASI_REGNUM and SPARC64_CCR_REGNUM are hard to test. */
+ && gdbarch_ptr_bit (gdbarch) == 64
+ && (regnum >= gdbarch_num_regs (gdbarch)
+ && regnum <= gdbarch_num_regs (gdbarch) + 4))
+ continue;
+
+ std::vector<gdb_byte> expected (register_size (gdbarch, regnum), 0);
+ std::vector<gdb_byte> buf (register_size (gdbarch, regnum), 0);
+ const auto type = register_type (gdbarch, regnum);
+
+ if (TYPE_CODE (type) == TYPE_CODE_FLT
+ || TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
+ {
+ /* Generate valid float format. */
+ target_float_from_string (expected.data (), type, "1.25");
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_INT
+ || TYPE_CODE (type) == TYPE_CODE_ARRAY
+ || TYPE_CODE (type) == TYPE_CODE_PTR
+ || TYPE_CODE (type) == TYPE_CODE_UNION
+ || TYPE_CODE (type) == TYPE_CODE_STRUCT)
+ {
+ if (bfd_arch == bfd_arch_ia64
+ || (regnum >= gdbarch_num_regs (gdbarch)
+ && (bfd_arch == bfd_arch_xtensa
+ || bfd_arch == bfd_arch_bfin
+ || bfd_arch == bfd_arch_m32c
+ /* m68hc11 pseudo registers are in memory. */
+ || bfd_arch == bfd_arch_m68hc11
+ || bfd_arch == bfd_arch_m68hc12
+ || bfd_arch == bfd_arch_s390))
+ || (bfd_arch == bfd_arch_frv
+ /* FRV pseudo registers except iacc0. */
+ && regnum > gdbarch_num_regs (gdbarch)))
+ {
+ /* Skip setting the expected values for some architecture
+ registers. */
+ }
+ else if (bfd_arch == bfd_arch_rl78 && regnum == 40)
+ {
+ /* RL78_PC_REGNUM */
+ for (auto j = 0; j < register_size (gdbarch, regnum) - 1; j++)
+ expected[j] = j;
+ }
+ else
+ {
+ for (auto j = 0; j < register_size (gdbarch, regnum); j++)
+ expected[j] = j;
+ }
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_FLAGS)
+ {
+ /* No idea how to test flags. */
+ continue;
+ }
+ else
+ {
+ /* If we don't know how to create the expected value for the
+ this type, make it fail. */
+ SELF_CHECK (0);
+ }
+
+ readwrite.cooked_write (regnum, expected.data ());
+
+ SELF_CHECK (readwrite.cooked_read (regnum, buf.data ()) == REG_VALID);
+ SELF_CHECK (expected == buf);
+ }