Implement a simple "frame type" cache for Renesas RX

Message ID 20150702191958.0f5ed4c7@pinnacle.lan
State Changes Requested, archived
Headers

Commit Message

Kevin Buettner July 3, 2015, 2:19 a.m. UTC
  Anyone have comments regarding the change below?

My concerns are:

1) Is there an existing generic mechanism that I should be using?

2) If not, should I improve the implementation?  It wouldn't be hard
   to do, but the simple implementation shown below seems to be
   adequate for speeding things up...

Kevin

---

Implement a simple "frame type" cache for Renesas RX.

Determining the frame type (normal, exception, or fast interrupt) is
expensive when sniffing for normal frames or exception frames.  The
frame type is determined by scanning to the end of the function until
some type of return instruction is found.  The type of instruction
(RTS, RTE, RTFI) determines the type of the corresponding frame.  This
operation is especially slow when attempting to read memory over a slow
interface.

This change implements a simple 256-entry cache.  The cache is
extremely simple in that, if a collision occurs, the colliding entry
is overwritten even if there are unused entries in the table.  The
implementation could definitely be improved but even, as is, it
significantly improves the user experience.

A NULL entry for this cache is created when a new inferior is created.
The cache is actually created upon the first attempted access of the
cache.  It is freed when the inferior terminates.

gdb/ChangeLog:

	* rx-tdep.c (inferior.h): Include.
	(rx_frame_type): Add RX_FRAME_TYPE_NONE.
	(rx_frame_type_cache_key): New static global.
	(rx_frame_type cache): New struct.
	(rx_frame_type_cache_cleanup, get_rx_frame_type_cache)
	(rx_fame_type_cache_hash_index, rx_frame_type_cache_lookup)
	(rx_frame_type_cache_set): New functions.
	(rx_frame_type): Use cache.
	(_initialize_rx_tdep): Register inferior data, initializing
	rx_frame_type_cache_key.
---
 gdb/rx-tdep.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 110 insertions(+), 7 deletions(-)
  

Comments

Yao Qi July 6, 2015, 4:33 p.m. UTC | #1
Kevin Buettner <kevinb@redhat.com> writes:

> 1) Is there an existing generic mechanism that I should be using?

Hi Kevin,
Did you use general "code cache" stuff in GDB?  see
target.c:target_read_code, which reads target's code and cache them.
Nowadays, we only use target_read_code in x86 and x86_64, and it
improves the performance of parsing prologue to some extent.
  
Kevin Buettner July 8, 2015, 12:26 a.m. UTC | #2
On Mon, 06 Jul 2015 17:33:16 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Kevin Buettner <kevinb@redhat.com> writes:
> 
> > 1) Is there an existing generic mechanism that I should be using?
> 
> Did you use general "code cache" stuff in GDB?  see
> target.c:target_read_code, which reads target's code and cache them.
> Nowadays, we only use target_read_code in x86 and x86_64, and it
> improves the performance of parsing prologue to some extent.

Thank you for this suggestion.

My testing shows that target_read_code() handles this problem
quite nicely.

I'm withdrawing this patch from consideration and will instead
commit a much simpler patch which uses target_read_code()
instead.

Thanks again,

Kevin
  

Patch

diff --git a/gdb/rx-tdep.c b/gdb/rx-tdep.c
index 8442c76..43c47d9 100644
--- a/gdb/rx-tdep.c
+++ b/gdb/rx-tdep.c
@@ -20,6 +20,7 @@ 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "inferior.h"
 #include "arch-utils.h"
 #include "prologue-value.h"
 #include "target.h"
@@ -57,6 +58,7 @@  enum
 
 /* RX frame types.  */
 enum rx_frame_type {
+  RX_FRAME_TYPE_NONE,			/* For frame type cache. */
   RX_FRAME_TYPE_NORMAL,
   RX_FRAME_TYPE_EXCEPTION,
   RX_FRAME_TYPE_FAST_INTERRUPT
@@ -451,6 +453,80 @@  rx_analyze_frame_prologue (struct frame_info *this_frame,
   return *this_prologue_cache;
 }
 
+/* Frame Type Cache.  Scanning memory for return instructions can be
+   slow.  This is a simple cache implemention which speeds things up
+   a bit.  */
+
+static const struct inferior_data *rx_frame_type_cache_key;
+
+struct rx_frame_type_cache
+{
+  CORE_ADDR addr;
+  enum rx_frame_type frame_type;
+};
+
+/* Free data structure associated with a frame type cache.  */
+
+static void
+rx_frame_type_cache_cleanup (struct inferior *inf, void *data)
+{
+  xfree (data);
+}
+
+/* Fetch frame type cache associated with current inferior.  */
+
+static struct rx_frame_type_cache *
+get_rx_frame_type_cache (void)
+{
+  struct inferior *inf = current_inferior ();
+  struct rx_frame_type_cache *cache
+    = inferior_data (inf, rx_frame_type_cache_key);
+
+  if (cache == NULL)
+    {
+      cache = XCNEWVEC (struct rx_frame_type_cache, 256);
+      set_inferior_data (inf, rx_frame_type_cache_key, cache);
+    }
+
+  gdb_assert (cache != NULL);
+
+  return cache;
+}
+
+/* Compute hash index for address ADDR.  */
+
+static int
+rx_frame_type_cache_hash_index (CORE_ADDR addr)
+{
+  return (addr + (addr >> 8) + (addr >> 16)) & 0xff;
+}
+
+/* Look up address ADDR in the frame type cache.  Return the frame
+   type if found, otherwise return RX_FRAME_TYPE_NONE.  */
+
+static enum rx_frame_type
+rx_frame_type_cache_lookup (CORE_ADDR addr)
+{
+  struct rx_frame_type_cache *c = get_rx_frame_type_cache ()
+                                + rx_frame_type_cache_hash_index (addr);
+
+  if (c->addr == addr)
+    return c->frame_type;
+  else
+    return RX_FRAME_TYPE_NONE;
+}
+
+/* Set cache entry for address ADDR using frame type FRAME_TYPE.  */
+
+static void
+rx_frame_type_cache_set (CORE_ADDR addr, enum rx_frame_type frame_type)
+{
+  struct rx_frame_type_cache *c = get_rx_frame_type_cache ()
+                                + rx_frame_type_cache_hash_index (addr);
+  c->addr = addr;
+  c->frame_type = frame_type;
+}
+
 /* Determine type of frame by scanning the function for a return
    instruction.  */
 
@@ -462,10 +538,11 @@  rx_frame_type (struct frame_info *this_frame, void **this_cache)
   int bytes_read;
   struct rx_get_opcode_byte_handle opcode_handle;
   RX_Opcode_Decoded opc;
+  enum rx_frame_type frame_type;
 
   gdb_assert (this_cache != NULL);
 
-  /* If we have a cached value, return it.  */
+  /* If we have a cached value in the prologue cache, return it.  */
 
   if (*this_cache != NULL)
     {
@@ -474,8 +551,9 @@  rx_frame_type (struct frame_info *this_frame, void **this_cache)
       return p->frame_type;
     }
 
-  /* No cached value; scan the function.  The frame type is cached in
-     rx_analyze_prologue / rx_analyze_frame_prologue.  */
+  /* No cached value; prepare to scan the function.  The frame type is
+     cached (in the prologue cache data structure) in rx_analyze_prologue 
+     and rx_analyze_frame_prologue.  */
   
   pc = get_frame_pc (this_frame);
   
@@ -485,6 +563,16 @@  rx_frame_type (struct frame_info *this_frame, void **this_cache)
   if (!find_pc_partial_function (pc, &name, &start_pc, &lim_pc))
     lim_pc = pc + 20;
 
+  /* See if frame type has been cached in per-inferior cache for
+     lim_pc.  Return that value if found.  */
+  frame_type = rx_frame_type_cache_lookup (lim_pc);
+  if (frame_type != RX_FRAME_TYPE_NONE)
+    return frame_type;
+
+  /* Frame type has not been cached in either the prologue cache data
+     structure or the per-inferior cache.  Scan instruction stream up
+     to lim_pc for some type of return instruction.  */
+  frame_type = RX_FRAME_TYPE_NORMAL;
   while (pc < lim_pc)
     {
       opcode_handle.pc = pc;
@@ -492,16 +580,28 @@  rx_frame_type (struct frame_info *this_frame, void **this_cache)
 				     &opcode_handle);
 
       if (bytes_read <= 0 || opc.id == RXO_rts)
-	return RX_FRAME_TYPE_NORMAL;
+	{
+	  frame_type = RX_FRAME_TYPE_NORMAL;
+	  break;
+	}
       else if (opc.id == RXO_rtfi)
-	return RX_FRAME_TYPE_FAST_INTERRUPT;
+	{
+	  frame_type = RX_FRAME_TYPE_FAST_INTERRUPT;
+	  break;
+	}
       else if (opc.id == RXO_rte)
-        return RX_FRAME_TYPE_EXCEPTION;
+	{
+	  frame_type = RX_FRAME_TYPE_EXCEPTION;
+	  break;
+	}
 
       pc += bytes_read;
     }
 
-  return RX_FRAME_TYPE_NORMAL;
+  /* Cache result in the per-inferior frame type cache.  */
+  rx_frame_type_cache_set (lim_pc, frame_type);
+
+  return frame_type;
 }
 
 
@@ -1158,4 +1258,7 @@  void
 _initialize_rx_tdep (void)
 {
   register_gdbarch_init (bfd_arch_rx, rx_gdbarch_init);
+
+  rx_frame_type_cache_key
+    = register_inferior_data_with_cleanup (NULL, rx_frame_type_cache_cleanup);
 }