[PATCH] Implement a simple "frame type" cache for Renesas RX

Kevin Buettner kevinb@redhat.com
Fri Jul 3 02:20:00 GMT 2015


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(-)

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);
 }



More information about the Gdb-patches mailing list