PR ld/12380
[deliverable/binutils-gdb.git] / gdb / dcache.c
index 08e0addebf763e2843bb428ab9144caec0b760d2..0f034705a034e3ff43cdf0420a15343915c7a55d 100644 (file)
@@ -1,7 +1,7 @@
 /* Caching code for GDB, the GNU debugger.
 
    Copyright (C) 1992, 1993, 1995, 1996, 1998, 1999, 2000, 2001, 2003, 2007,
-   2008, 2009 Free Software Foundation, Inc.
+   2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -24,6 +24,7 @@
 #include "gdb_string.h"
 #include "gdbcore.h"
 #include "target.h"
+#include "inferior.h"
 #include "splay-tree.h"
 
 /* The data cache could lead to incorrect results because it doesn't
@@ -40,9 +41,9 @@
    of data, such as when performing a backtrace.
 
    The cache is a splay tree along with a linked list for replacement.
-   Each block caches a LINE_SIZE area of memory.  Wtihin each line we remember
-   the address of the line (which must be a multiple of LINE_SIZE) and the
-   actual data block.
+   Each block caches a LINE_SIZE area of memory.  Within each line we
+   remember the address of the line (which must be a multiple of
+   LINE_SIZE) and the actual data block.
 
    Lines are only allocated as needed, so DCACHE_SIZE really specifies the
    *maximum* number of lines in the cache.
 
 struct dcache_block
 {
-  struct dcache_block *newer;  /* for LRU and free list */
+  /* For least-recently-allocated and free lists.  */
+  struct dcache_block *prev;
+  struct dcache_block *next;
+
   CORE_ADDR addr;              /* address of data */
   gdb_byte data[LINE_SIZE];    /* bytes at given address */
   int refs;                    /* # hits */
@@ -96,18 +100,22 @@ struct dcache_block
 struct dcache_struct
 {
   splay_tree tree;
-  struct dcache_block *oldest;
-  struct dcache_block *newest;
+  struct dcache_block *oldest; /* least-recently-allocated list.  */
 
+  /* The free list is maintained identically to OLDEST to simplify
+     the code: we only need one set of accessors.  */
   struct dcache_block *freelist;
 
   /* The number of in-use lines in the cache.  */
   int size;
+
+  /* The ptid of last inferior to use cache or null_ptid.  */
+  ptid_t ptid;
 };
 
-static struct dcache_block *dcache_hit (DCACHE *dcache, CORE_ADDR addr);
+typedef void (block_func) (struct dcache_block *block, void *param);
 
-static int dcache_write_line (DCACHE *dcache, struct dcache_block *db);
+static struct dcache_block *dcache_hit (DCACHE *dcache, CORE_ADDR addr);
 
 static int dcache_read_line (DCACHE *dcache, struct dcache_block *db);
 
@@ -117,45 +125,131 @@ static void dcache_info (char *exp, int tty);
 
 void _initialize_dcache (void);
 
-static int dcache_enabled_p = 0;
+static int dcache_enabled_p = 0; /* OBSOLETE */
 
 static void
 show_dcache_enabled_p (struct ui_file *file, int from_tty,
                       struct cmd_list_element *c, const char *value)
 {
-  fprintf_filtered (file, _("Cache use for remote targets is %s.\n"), value);
+  fprintf_filtered (file, _("Deprecated remotecache flag is %s.\n"), value);
 }
 
+static DCACHE *last_cache; /* Used by info dcache.  */
 
-static DCACHE *last_cache; /* Used by info dcache */
-
-/* Free all the data cache blocks, thus discarding all cached data.  */
+/* Add BLOCK to circular block list BLIST, behind the block at *BLIST.
+   *BLIST is not updated (unless it was previously NULL of course).
+   This is for the least-recently-allocated list's sake:
+   BLIST points to the oldest block.
+   ??? This makes for poor cache usage of the free list,
+   but is it measurable?  */
 
-void
-dcache_invalidate (DCACHE *dcache)
+static void
+append_block (struct dcache_block **blist, struct dcache_block *block)
 {
-  struct dcache_block *block, *next;
+  if (*blist)
+    {
+      block->next = *blist;
+      block->prev = (*blist)->prev;
+      block->prev->next = block;
+      (*blist)->prev = block;
+      /* We don't update *BLIST here to maintain the invariant that for the
+        least-recently-allocated list *BLIST points to the oldest block.  */
+    }
+  else
+    {
+      block->next = block;
+      block->prev = block;
+      *blist = block;
+    }
+}
 
-  block = dcache->oldest;
+/* Remove BLOCK from circular block list BLIST.  */
 
-  while (block)
+static void
+remove_block (struct dcache_block **blist, struct dcache_block *block)
+{
+  if (block->next == block)
     {
-      splay_tree_remove (dcache->tree, (splay_tree_key) block->addr);
-      next = block->newer;
+      *blist = NULL;
+    }
+  else
+    {
+      block->next->prev = block->prev;
+      block->prev->next = block->next;
+      /* If we removed the block *BLIST points to, shift it to the next block
+        to maintain the invariant that for the least-recently-allocated list
+        *BLIST points to the oldest block.  */
+      if (*blist == block)
+       *blist = block->next;
+    }
+}
+
+/* Iterate over all elements in BLIST, calling FUNC.
+   PARAM is passed to FUNC.
+   FUNC may remove the block it's passed, but only that block.  */
+
+static void
+for_each_block (struct dcache_block **blist, block_func *func, void *param)
+{
+  struct dcache_block *db;
 
-      block->newer = dcache->freelist;
-      dcache->freelist = block;
+  if (*blist == NULL)
+    return;
 
-      block = next;
+  db = *blist;
+  do
+    {
+      struct dcache_block *next = db->next;
+
+      func (db, param);
+      db = next;
     }
+  while (*blist && db != *blist);
+}
+
+/* BLOCK_FUNC function for dcache_invalidate.
+   This doesn't remove the block from the oldest list on purpose.
+   dcache_invalidate will do it later.  */
+
+static void
+invalidate_block (struct dcache_block *block, void *param)
+{
+  DCACHE *dcache = (DCACHE *) param;
+
+  splay_tree_remove (dcache->tree, (splay_tree_key) block->addr);
+  append_block (&dcache->freelist, block);
+}
+
+/* Free all the data cache blocks, thus discarding all cached data.  */
+
+void
+dcache_invalidate (DCACHE *dcache)
+{
+  for_each_block (&dcache->oldest, invalidate_block, dcache);
 
   dcache->oldest = NULL;
-  dcache->newest = NULL;
   dcache->size = 0;
+  dcache->ptid = null_ptid;
+}
+
+/* Invalidate the line associated with ADDR.  */
+
+static void
+dcache_invalidate_line (DCACHE *dcache, CORE_ADDR addr)
+{
+  struct dcache_block *db = dcache_hit (dcache, addr);
+
+  if (db)
+    {
+      splay_tree_remove (dcache->tree, (splay_tree_key) db->addr);
+      remove_block (&dcache->oldest, db);
+      append_block (&dcache->freelist, db);
+      --dcache->size;
+    }
 }
 
 /* If addr is present in the dcache, return the address of the block
-   containing it.  */
+   containing it.  Otherwise return NULL.  */
 
 static struct dcache_block *
 dcache_hit (DCACHE *dcache, CORE_ADDR addr)
@@ -173,7 +267,9 @@ dcache_hit (DCACHE *dcache, CORE_ADDR addr)
   return db;
 }
 
-/* Fill a cache line from target memory.  */
+/* Fill a cache line from target memory.
+   The result is 1 for success, 0 if the (entire) cache line
+   wasn't readable.  */
 
 static int
 dcache_read_line (DCACHE *dcache, struct dcache_block *db)
@@ -198,8 +294,9 @@ dcache_read_line (DCACHE *dcache, struct dcache_block *db)
       else
        reg_len = region->hi - memaddr;
 
-      /* Skip non-cacheable/non-readable regions.  */
-      if (!region->attrib.cache || region->attrib.mode == MEM_WO)
+      /* Skip non-readable regions.  The cache attribute can be ignored,
+         since we may be loading this for a stack access.  */
+      if (region->attrib.mode == MEM_WO)
        {
          memaddr += reg_len;
          myaddr  += reg_len;
@@ -230,9 +327,9 @@ dcache_alloc (DCACHE *dcache, CORE_ADDR addr)
 
   if (dcache->size >= DCACHE_SIZE)
     {
-      /* Evict the least recently used line.  */
+      /* Evict the least recently allocated line.  */
       db = dcache->oldest;
-      dcache->oldest = db->newer;
+      remove_block (&dcache->oldest, db);
 
       splay_tree_remove (dcache->tree, (splay_tree_key) db->addr);
     }
@@ -240,7 +337,7 @@ dcache_alloc (DCACHE *dcache, CORE_ADDR addr)
     {
       db = dcache->freelist;
       if (db)
-        dcache->freelist = db->newer;
+       remove_block (&dcache->freelist, db);
       else
        db = xmalloc (sizeof (struct dcache_block));
 
@@ -248,16 +345,10 @@ dcache_alloc (DCACHE *dcache, CORE_ADDR addr)
     }
 
   db->addr = MASK (addr);
-  db->newer = NULL;
   db->refs = 0;
 
-  if (dcache->newest)
-    dcache->newest->newer = db;
-
-  dcache->newest = db;
-
-  if (!dcache->oldest)
-    dcache->oldest = db;
+  /* Put DB at the end of the list, it's the newest.  */
+  append_block (&dcache->oldest, db);
 
   splay_tree_insert (dcache->tree, (splay_tree_key) db->addr,
                     (splay_tree_value) db);
@@ -265,7 +356,7 @@ dcache_alloc (DCACHE *dcache, CORE_ADDR addr)
   return db;
 }
 
-/* Using the data cache DCACHE return the contents of the byte at
+/* Using the data cache DCACHE, store in *PTR the contents of the byte at
    address ADDR in the remote machine.  
 
    Returns 1 for success, 0 for error.  */
@@ -296,7 +387,7 @@ dcache_peek_byte (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr)
    an area of memory which wasn't present in the cache doesn't cause
    it to be loaded in.
 
-   Always return 1 to simplify dcache_xfer_memory.  */
+   Always return 1 (meaning success) to simplify dcache_xfer_memory.  */
 
 static int
 dcache_poke_byte (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr)
@@ -320,13 +411,12 @@ dcache_splay_tree_compare (splay_tree_key a, splay_tree_key b)
     return -1;
 }
 
-/* Initialize the data cache.  */
+/* Allocate and initialize a data cache.  */
 
 DCACHE *
 dcache_init (void)
 {
   DCACHE *dcache;
-  int i;
 
   dcache = (DCACHE *) xmalloc (sizeof (*dcache));
 
@@ -335,30 +425,33 @@ dcache_init (void)
                                 NULL);
 
   dcache->oldest = NULL;
-  dcache->newest = NULL;
   dcache->freelist = NULL;
   dcache->size = 0;
+  dcache->ptid = null_ptid;
   last_cache = dcache;
 
   return dcache;
 }
 
+/* BLOCK_FUNC routine for dcache_free.  */
+
+static void
+free_block (struct dcache_block *block, void *param)
+{
+  free (block);
+}
+
 /* Free a data cache.  */
 
 void
 dcache_free (DCACHE *dcache)
 {
-  struct dcache_block *db, *next;
-
   if (last_cache == dcache)
     last_cache = NULL;
 
   splay_tree_delete (dcache->tree);
-  for (db = dcache->freelist; db != NULL; db = next)
-    {
-      next = db->newer;
-      xfree (db);
-    }
+  for_each_block (&dcache->oldest, free_block, NULL);
+  for_each_block (&dcache->freelist, free_block, NULL);
   xfree (dcache);
 }
 
@@ -366,7 +459,11 @@ dcache_free (DCACHE *dcache)
    to or from debugger address MYADDR.  Write to inferior if SHOULD_WRITE is
    nonzero. 
 
-   Returns length of data written or read; 0 for error.  */
+   Return the number of bytes actually transfered, or -1 if the
+   transfer is not supported or otherwise fails.  Return of a non-negative
+   value less than LEN indicates that no further transfer is possible.
+   NOTE: This is different than the to_xfer_partial interface, in which
+   positive values less than LEN mean further transfers may be possible.  */
 
 int
 dcache_xfer_memory (struct target_ops *ops, DCACHE *dcache,
@@ -376,8 +473,18 @@ dcache_xfer_memory (struct target_ops *ops, DCACHE *dcache,
   int i;
   int res;
   int (*xfunc) (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr);
+
   xfunc = should_write ? dcache_poke_byte : dcache_peek_byte;
 
+  /* If this is a different inferior from what we've recorded,
+     flush the cache.  */
+
+  if (! ptid_equal (inferior_ptid, dcache->ptid))
+    {
+      dcache_invalidate (dcache);
+      dcache->ptid = inferior_ptid;
+    }
+
   /* Do write-through first, so that if it fails, we don't write to
      the cache at all.  */
 
@@ -385,14 +492,25 @@ dcache_xfer_memory (struct target_ops *ops, DCACHE *dcache,
     {
       res = target_write (ops, TARGET_OBJECT_RAW_MEMORY,
                          NULL, myaddr, memaddr, len);
-      if (res < len)
-       return 0;
+      if (res <= 0)
+       return res;
+      /* Update LEN to what was actually written.  */
+      len = res;
     }
       
   for (i = 0; i < len; i++)
     {
       if (!xfunc (dcache, memaddr + i, myaddr + i))
-       return 0;
+       {
+         /* That failed.  Discard its cache line so we don't have a
+            partially read line.  */
+         dcache_invalidate_line (dcache, memaddr + i);
+         /* If we're writing, we still wrote LEN bytes.  */
+         if (should_write)
+           return len;
+         else
+           return i;
+       }
     }
     
   return len;
@@ -407,6 +525,19 @@ dcache_xfer_memory (struct target_ops *ops, DCACHE *dcache,
    "logically" connected but not actually a single call to one of the
    memory transfer functions.  */
 
+/* Just update any cache lines which are already present.  This is called
+   by memory_xfer_partial in cases where the access would otherwise not go
+   through the cache.  */
+
+void
+dcache_update (DCACHE *dcache, CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+{
+  int i;
+
+  for (i = 0; i < len; i++)
+    dcache_poke_byte (dcache, memaddr + i, myaddr + i);
+}
+
 static void
 dcache_print_line (int index)
 {
@@ -437,14 +568,14 @@ dcache_print_line (int index)
     
   db = (struct dcache_block *) n->value;
 
-  printf_filtered (_("Line %d: address %lx [%d hits]\n"),
-                 index, db->addr, db->refs);
+  printf_filtered (_("Line %d: address %s [%d hits]\n"),
+                  index, paddress (target_gdbarch, db->addr), db->refs);
 
   for (j = 0; j < LINE_SIZE; j++)
     {
       printf_filtered ("%02x ", db->data[j]);
 
-      /* Print a newline every 16 bytes (48 characters) */
+      /* Print a newline every 16 bytes (48 characters) */
       if ((j % 16 == 15) && (j != LINE_SIZE - 1))
        printf_filtered ("\n");
     }
@@ -455,11 +586,12 @@ static void
 dcache_info (char *exp, int tty)
 {
   splay_tree_node n;
-  int i, refcount, lineno;
+  int i, refcount;
 
   if (exp)
     {
       char *linestart;
+
       i = strtol (exp, &linestart, 10);
       if (linestart == exp || i < 0)
        {
@@ -474,12 +606,15 @@ dcache_info (char *exp, int tty)
   printf_filtered (_("Dcache line width %d, maximum size %d\n"),
                   LINE_SIZE, DCACHE_SIZE);
 
-  if (!last_cache)
+  if (!last_cache || ptid_equal (last_cache->ptid, null_ptid))
     {
       printf_filtered (_("No data cache available.\n"));
       return;
     }
 
+  printf_filtered (_("Contains data for %s\n"),
+                  target_pid_to_str (last_cache->ptid));
+
   refcount = 0;
 
   n = splay_tree_min (last_cache->tree);
@@ -489,8 +624,8 @@ dcache_info (char *exp, int tty)
     {
       struct dcache_block *db = (struct dcache_block *) n->value;
 
-      printf_filtered (_("Line %d: address %lx [%d hits]\n"),
-                      i, db->addr, db->refs);
+      printf_filtered (_("Line %d: address %s [%d hits]\n"),
+                      i, paddress (target_gdbarch, db->addr), db->refs);
       i++;
       refcount += db->refs;
 
@@ -507,11 +642,10 @@ _initialize_dcache (void)
                           &dcache_enabled_p, _("\
 Set cache use for remote targets."), _("\
 Show cache use for remote targets."), _("\
-When on, use data caching for remote targets.  For many remote targets\n\
-this option can offer better throughput for reading target memory.\n\
-Unfortunately, gdb does not currently know anything about volatile\n\
-registers and thus data caching will produce incorrect results with\n\
-volatile registers are in use.  By default, this option is off."),
+This used to enable the data cache for remote targets.  The cache\n\
+functionality is now controlled by the memory region system and the\n\
+\"stack-cache\" flag; \"remotecache\" now does nothing and\n\
+exists only for compatibility reasons."),
                           NULL,
                           show_dcache_enabled_p,
                           &setlist, &showlist);
This page took 0.030109 seconds and 4 git commands to generate.