mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-24 18:44:20 +08:00
regcache::cooked_read unit test
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.
This commit is contained in:
parent
6654d750c7
commit
1b30aaa566
@ -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
|
||||
|
@ -25,58 +25,10 @@
|
||||
#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
|
||||
|
191
gdb/regcache.c
191
gdb/regcache.c
@ -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
|
||||
}
|
||||
|
47
gdb/target.c
47
gdb/target.c
@ -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)
|
||||
|
14
gdb/target.h
14
gdb/target.h
@ -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) */
|
||||
|
Loading…
Reference in New Issue
Block a user