From patchwork Wed Nov 15 09:12:50 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yao Qi X-Patchwork-Id: 24250 Received: (qmail 37986 invoked by alias); 15 Nov 2017 09:13:46 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 8048 invoked by uid 89); 15 Nov 2017 09:13:02 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.7 required=5.0 tests=BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KB_WAM_FROM_NAME_SINGLEWORD, RCVD_IN_DNSWL_NONE, SPF_PASS, UNSUBSCRIBE_BODY autolearn=ham version=3.3.2 spammy=HX-Google-Smtp-Source:AGs4zMY X-HELO: mail-wm0-f46.google.com Received: from mail-wm0-f46.google.com (HELO mail-wm0-f46.google.com) (74.125.82.46) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 15 Nov 2017 09:12:59 +0000 Received: by mail-wm0-f46.google.com with SMTP id z3so1477644wme.5 for ; Wed, 15 Nov 2017 01:12:59 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=bjwkjfDPV2XPnwu/dxoKozJkPR4F5W0YiLQfc0jvEOk=; b=efyyMs07AhCoxT2ZtHq3DoMtfGxzWZEaN3OkO9yyH5ztv5+IpDHC9xGjrwrhHdZlwO 8QdX6grTy5pNqILpW72cGS0DppRp83TgnbRKjlrccHc0/VAunBRW4jhCn3dRQ5Y3XTG0 klC0F2t6avtKr6Pc4k/NhvTI+m2wJyxtp/HBbjn5CrSK2QVs/tEyvDyzq72WZ63y/mvv mC5fE3SghdKZjpRoD+bPGHtMv+0eaVWFABXmyLCLNLTpKZujjLWBXUV9/eB6o8E+DFnt GUDFkhYycLvUX9fSrjLEqhv2uRN2FU6auSWM3o9hxMjouEzbZvZoB1/HlHI9l02TpDgu LSAQ== X-Gm-Message-State: AJaThX5GFz+ifKrftmWGi6vC/n4Uct06j8s1P53vbsNfNWIorme/qZcN VzaZWX8PWX2HqMtJg36Ud89D0Q== X-Google-Smtp-Source: AGs4zMY/Z4wXsL58hWsPZIHqPshuY7/vefT29lkpPvahKVNHyhzkZtwA98nXQggtqF4vXGokZ1OkcQ== X-Received: by 10.28.183.132 with SMTP id h126mr7346135wmf.76.1510737177139; Wed, 15 Nov 2017 01:12:57 -0800 (PST) Received: from E107787-LIN.cambridge.arm.com (static.42.136.251.148.clients.your-server.de. [148.251.136.42]) by smtp.gmail.com with ESMTPSA id k69sm16954826wmg.45.2017.11.15.01.12.56 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 15 Nov 2017 01:12:56 -0800 (PST) From: Yao Qi X-Google-Original-From: Yao Qi To: gdb-patches@sourceware.org Subject: [PATCH 2/3] regcache::cooked_read unit test Date: Wed, 15 Nov 2017 09:12:50 +0000 Message-Id: <1510737171-15435-3-git-send-email-yao.qi@linaro.org> In-Reply-To: <1510737171-15435-1-git-send-email-yao.qi@linaro.org> References: <1510737171-15435-1-git-send-email-yao.qi@linaro.org> X-IsSubscribed: yes 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-09 Yao Qi * 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/gdbarch-selftests.c | 50 +------------ gdb/regcache.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++ gdb/target.c | 47 ++++++++++++ gdb/target.h | 14 ++++ 4 files changed, 253 insertions(+), 49 deletions(-) diff --git a/gdb/gdbarch-selftests.c b/gdb/gdbarch-selftests.c index c748fcc..49d006f 100644 --- a/gdb/gdbarch-selftests.c +++ b/gdb/gdbarch-selftests.c @@ -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 diff --git a/gdb/regcache.c b/gdb/regcache.c index 9c68bdd..3bc8608 100644 --- a/gdb/regcache.c +++ b/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 (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 (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 (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 regcache (gdbarch); + gdb::def_vector buf (register_size (gdbarch, regnum)); + + regcache.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++) + regcache.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 buf (register_size (gdbarch, regnum)); + + SELF_CHECK (REG_VALID == regcache.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 } diff --git a/gdb/target.c b/gdb/target.c index 2e02a77..3bfc8b5 100644 --- a/gdb/target.c +++ b/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) diff --git a/gdb/target.h b/gdb/target.h index 1683af6..0d1e7bd 100644 --- a/gdb/target.h +++ b/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) */