public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
To: gdb-patches@sourceware.org
Subject: [PATCH 26/26] gdbserver: allow gradually populating and selectively storing a regcache
Date: Tue, 28 Feb 2023 12:28:24 +0100	[thread overview]
Message-ID: <65c2083374c880b4802a8453dea62b23e2c0dda6.1677582745.git.tankut.baris.aktemur@intel.com> (raw)
In-Reply-To: <cover.1677582744.git.tankut.baris.aktemur@intel.com>

Currently, the regcache can be used by fetching all the registers from
the target.  For some targets, this could be a costly operation
because there is a large number of threads with a large register file
each.  In this patch, we revise the regcache implementation to allow
populating the contents gradually and storing the registers only when
they have updated values.

To this aim, we introduce a new register status: REG_DIRTY.  This
status denotes that a register value has been cached and also updated.
When invalidating the cache, only the dirty registers are stored to
the target.  In a typical debug session, it is more likely that only a
small subset of the register file has changed.  Therefore, selectively
storing the registers on targets with many threads and registers can
save substantial costs, with respect to storing the whole set.

The collect_register function now performs a lazy fetch.  If the
requested register value is not cached yet, it is requested from the
target.

The supply_register function updates the status of the supplied
register as follows: if the register was not available in the cache,
its status becomes REG_VALID, denoting that the value is now cached.
If the register is supplied again, it becomes REG_DIRTY.

The function that supply the whole register set (supply_regblock and
registers_from_string) are also updated to compare the present and new
values of each register, so that we can track the register statuses
(i.e.  dirty or not) properly.

Regression-tested on an X86_64 Linux target using the native-gdbserver
and native-extended-gdbserver board files.
---
 gdbserver/regcache.cc        | 96 ++++++++++++++++++++++++++++++------
 gdbserver/regcache.h         |  6 +++
 gdbsupport/common-regcache.h |  3 ++
 3 files changed, 91 insertions(+), 14 deletions(-)

diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index cfb68774402..cf020985c31 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -63,6 +63,18 @@ regcache::fetch ()
       gdb_assert (this->thread != nullptr);
       switch_to_thread (this->thread);
 
+      /* If there are individually-fetched dirty registers, first
+	 store them, then fetch all.  We prefer this to doing
+	 individual fetch for each registers, if needed, because it is
+	 more likely that very few registers are individually-fetched
+	 at this moment and that fetching all in one go is more
+	 efficient than fetching each reg one by one.  */
+      for (int i = 0; i < tdesc->reg_defs.size (); ++i)
+	{
+	  if (register_status[i] == REG_DIRTY)
+	    store_inferior_registers (this, i);
+	}
+
       /* Invalidate all registers, to prevent stale left-overs.  */
       discard ();
       fetch_inferior_registers (this, -1);
@@ -100,12 +112,17 @@ regcache_invalidate_thread (struct thread_info *thread)
 void
 regcache::invalidate ()
 {
-  if (registers_fetched)
+  scoped_restore_current_thread restore_thread;
+  gdb_assert (this->thread != nullptr);
+  switch_to_thread (this->thread);
+
+  /* Store dirty registers individually.  We prefer this to a
+     store-all, because it is more likely that a small number of
+     registers have changed.  */
+  for (int i = 0; i < tdesc->reg_defs.size (); ++i)
     {
-      scoped_restore_current_thread restore_thread;
-      gdb_assert (this->thread != nullptr);
-      switch_to_thread (this->thread);
-      store_inferior_registers (this, -1);
+      if (register_status[i] == REG_DIRTY)
+	store_inferior_registers (this, i);
     }
 
   discard ();
@@ -231,7 +248,8 @@ regcache::registers_to_string (char *buf)
   unsigned char *regs = registers;
   for (int i = 0; i < tdesc->reg_defs.size (); ++i)
     {
-      if (register_status[i] == REG_VALID)
+      if (register_status[i] == REG_VALID
+	  || register_status[i] == REG_DIRTY)
 	{
 	  bin2hex (regs, buf, register_size (tdesc, i));
 	  buf += register_size (tdesc, i) * 2;
@@ -258,9 +276,12 @@ regcache::registers_from_string (const char *buf)
       if (len > tdesc->registers_size * 2)
 	len = tdesc->registers_size * 2;
     }
-  hex2bin (buf, registers, len / 2);
-  /* All register data have been re-written.  Update the statuses.  */
-  memset (register_status, REG_VALID, tdesc->reg_defs.size ());
+
+  unsigned char *new_regs =
+    (unsigned char *) alloca (tdesc->registers_size);
+
+  hex2bin (buf, new_regs, len / 2);
+  supply_regblock (new_regs);
 }
 
 /* See regcache.h */
@@ -350,7 +371,7 @@ regcache::raw_supply (int n, const void *buf)
     {
       memcpy (register_data (n), buf, register_size (tdesc, n));
 #ifndef IN_PROCESS_AGENT
-      set_register_status (n, REG_VALID);
+      bump_register_status (n);
 #endif
     }
   else
@@ -370,7 +391,7 @@ supply_register_zeroed (struct regcache *regcache, int n)
   memset (regcache->register_data (n), 0,
 	  register_size (regcache->tdesc, n));
 #ifndef IN_PROCESS_AGENT
-  regcache->set_register_status (n, REG_VALID);
+  regcache->bump_register_status (n);
 #endif
 }
 
@@ -392,11 +413,26 @@ regcache::supply_regblock (const void *buf)
 {
   gdb_assert (buf != nullptr);
 
-  memcpy (registers, buf, tdesc->registers_size);
 #ifndef IN_PROCESS_AGENT
-  for (int i = 0; i < tdesc->reg_defs.size (); i++)
-    set_register_status (i, REG_VALID);
+  /* First, update the statuses.  Mark dirty only those that have
+     changed.  */
+  unsigned char *regs = registers;
+  unsigned char *new_regs = (unsigned char *) buf;
+  for (int i = 0; i < tdesc->reg_defs.size (); ++i)
+    {
+      int size = register_size (tdesc, i);
+      bool first_time = (get_register_status (i) == REG_UNKNOWN);
+      bool valid = (get_register_status (i) == REG_VALID);
+
+      if (first_time
+	  || (valid && (memcmp (new_regs, regs, size) != 0)))
+	bump_register_status (i);
+
+      regs += size;
+      new_regs += size;
+    }
 #endif
+  memcpy (registers, buf, tdesc->registers_size);
 }
 
 #ifndef IN_PROCESS_AGENT
@@ -413,6 +449,15 @@ supply_register_by_name (struct regcache *regcache,
 void
 collect_register (struct regcache *regcache, int n, void *buf)
 {
+#ifndef IN_PROCESS_AGENT
+  if (regcache->get_register_status (n) == REG_UNKNOWN)
+    {
+      /* This register has not been fetched from the target, yet.
+	 Do it now.  */
+      fetch_inferior_registers (regcache, n);
+    }
+#endif
+
   regcache->raw_collect (n, buf);
 }
 
@@ -513,6 +558,29 @@ regcache::set_register_status (int regnum, enum register_status status)
 #endif
 }
 
+void
+regcache::bump_register_status (int regnum)
+{
+#ifndef IN_PROCESS_AGENT
+  if (register_status == nullptr)
+    return;
+#endif
+
+  switch (get_register_status (regnum))
+    {
+    case REG_UNKNOWN:
+      set_register_status (regnum, REG_VALID);
+      break;
+
+    case REG_VALID:
+      set_register_status (regnum, REG_DIRTY);
+      break;
+
+    default:
+      break;
+    }
+}
+
 /* See gdbsupport/common-regcache.h.  */
 
 bool
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 216044889ec..132709fa71f 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -74,6 +74,12 @@ struct regcache : public reg_buffer_common
   /* Set the status of register REGNUM to STATUS.  */
   void set_register_status (int regnum, enum register_status status);
 
+  /* Shift the register status "one level" towards REG_DIRTY.
+     REG_UNKNOWN becomes REG_VALID;
+     REG_VALID becomes REG_DIRTY;
+     REG_DIRTY and REG_UNAVAILABLE stay the same.  */
+  void bump_register_status (int regnum);
+
   /* See gdbsupport/common-regcache.h.  */
   void raw_supply (int regnum, const void *buf) override;
 
diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h
index bf14610f98f..e81238fe7e0 100644
--- a/gdbsupport/common-regcache.h
+++ b/gdbsupport/common-regcache.h
@@ -31,6 +31,9 @@ enum register_status : signed char
     /* The register value is valid and cached.  */
     REG_VALID = 1,
 
+    /* The register value is valid, cached, and has been changed.  */
+    REG_DIRTY = 2,
+
     /* The register value is unavailable.  E.g., we're inspecting a
        traceframe, and this register wasn't collected.  Note that this
        "unavailable" is different from saying the register does not
-- 
2.25.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


  parent reply	other threads:[~2023-02-28 11:31 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-28 11:27 [PATCH 00/26] gdbserver: refactor regcache and allow gradually populating Tankut Baris Aktemur
2023-02-28 11:27 ` [PATCH 01/26] gdbserver: convert init_register_cache into regcache::initialize Tankut Baris Aktemur
2023-12-21 20:12   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 02/26] gdbserver: convert new_register_cache into a regcache constructor Tankut Baris Aktemur
2023-12-21 20:19   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 03/26] gdbserver: by-pass regcache to access tdesc only Tankut Baris Aktemur
2023-12-21 20:22   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 04/26] gdbserver: boolify and defaultize the 'fetch' parameter of get_thread_regcache Tankut Baris Aktemur
2023-12-21 20:24   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 05/26] gdbserver: add a pointer to the owner thread in regcache Tankut Baris Aktemur
2023-12-21 20:28   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 06/26] gdbserver: turn part of get_thread_regcache into regcache::fetch Tankut Baris Aktemur
2023-12-21 20:48   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 07/26] gdbserver: convert regcache_cpy into regcache::copy_from Tankut Baris Aktemur
2023-12-21 20:50   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 08/26] gdbserver: convert free_register_cache into a destructor of regcache Tankut Baris Aktemur
2023-12-21 20:57   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 09/26] gdbserver: extract out regcache::invalidate and regcache::discard Tankut Baris Aktemur
2023-12-21 21:08   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 10/26] gdbserver: convert registers_to_string into regcache::registers_to_string Tankut Baris Aktemur
2023-12-21 21:13   ` Simon Marchi
2023-12-21 21:19     ` Simon Marchi
2023-02-28 11:28 ` [PATCH 11/26] gdbserver: convert registers_from_string into regcache::registers_from_string Tankut Baris Aktemur
2023-02-28 11:28 ` [PATCH 12/26] gdbserver: convert supply_regblock to regcache::supply_regblock Tankut Baris Aktemur
2023-12-21 21:23   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 13/26] gdbserver: convert register_data into regcache::register_data Tankut Baris Aktemur
2023-12-21 21:26   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 14/26] gdbserver: introduce and use regcache::set_register_status Tankut Baris Aktemur
2023-12-21 21:30   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 15/26] gdbserver: check for nullptr condition in regcache::get_register_status Tankut Baris Aktemur
2023-12-21 21:32   ` Simon Marchi
2023-12-21 21:34   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 16/26] gdbserver: boolify regcache fields Tankut Baris Aktemur
2023-12-22  3:20   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 17/26] gdbserver: rename regcache's registers_valid to registers_fetched Tankut Baris Aktemur
2023-12-22  3:23   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 18/26] gdbsupport: fix a typo in a comment in common-regcache.h Tankut Baris Aktemur
2023-12-22  3:24   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 19/26] gdbserver: fix the declared type of register_status in regcache Tankut Baris Aktemur
2023-12-22  3:35   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 20/26] gdbserver: make some regcache fields private Tankut Baris Aktemur
2023-12-22  3:39   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 21/26] gdbserver: use REG_UNKNOWN for a regcache's register statuses Tankut Baris Aktemur
2023-12-22  4:32   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 22/26] gdbserver: zero-out register values in regcache-discard Tankut Baris Aktemur
2023-12-22  4:36   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 23/26] gdbserver: set register statuses in registers_from_string Tankut Baris Aktemur
2023-12-22  4:40   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 24/26] gdbserver: return tracked register status in regcache_raw_read_unsigned Tankut Baris Aktemur
2023-12-22  4:42   ` Simon Marchi
2023-02-28 11:28 ` [PATCH 25/26] gdbserver: refuse null argument in regcache::supply_regblock Tankut Baris Aktemur
2023-12-22  4:54   ` Simon Marchi
2023-02-28 11:28 ` Tankut Baris Aktemur [this message]
2023-12-22 16:25   ` [PATCH 26/26] gdbserver: allow gradually populating and selectively storing a regcache Simon Marchi
2023-03-07 20:39 ` [PATCH 00/26] gdbserver: refactor regcache and allow gradually populating Tom Tromey
2023-03-13 14:33   ` Aktemur, Tankut Baris
2023-03-28 13:42 ` Aktemur, Tankut Baris
2023-06-20 12:58 ` Aktemur, Tankut Baris

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=65c2083374c880b4802a8453dea62b23e2c0dda6.1677582745.git.tankut.baris.aktemur@intel.com \
    --to=tankut.baris.aktemur@intel.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).