Constify add_setshow_*
[deliverable/binutils-gdb.git] / gdb / dcache.c
index b71b707e4b799dbdf6ce88f54a3114b9ecdf7eab..740438fe1132e822b095e32c0654930c3d2460ba 100644 (file)
@@ -1,7 +1,6 @@
 /* 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.
+   Copyright (C) 1992-2017 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #include "defs.h"
 #include "dcache.h"
 #include "gdbcmd.h"
-#include "gdb_string.h"
 #include "gdbcore.h"
-#include "target.h"
+#include "target-dcache.h"
 #include "inferior.h"
 #include "splay-tree.h"
 
+/* Commands with a prefix of `{set,show} dcache'.  */
+static struct cmd_list_element *dcache_set_list = NULL;
+static struct cmd_list_element *dcache_show_list = NULL;
+
 /* The data cache could lead to incorrect results because it doesn't
    know about volatile variables, thus making it impossible to debug
    functions which use memory mapped I/O devices.  Set the nocache
 
 /* The maximum number of lines stored.  The total size of the cache is
    equal to DCACHE_SIZE times LINE_SIZE.  */
-#define DCACHE_SIZE 4096
+#define DCACHE_DEFAULT_SIZE 4096
+static unsigned dcache_size = DCACHE_DEFAULT_SIZE;
 
-/* The size of a cache line.  Smaller values reduce the time taken to
+/* The default size of a cache line.  Smaller values reduce the time taken to
    read a single byte and make the cache more granular, but increase
    overhead and reduce the effectiveness of the cache as a prefetcher.  */
-#define LINE_SIZE_POWER 6
-#define LINE_SIZE (1 << LINE_SIZE_POWER)
+#define DCACHE_DEFAULT_LINE_SIZE 64
+static unsigned dcache_line_size = DCACHE_DEFAULT_LINE_SIZE;
 
 /* Each cache block holds LINE_SIZE bytes of data
    starting at a multiple-of-LINE_SIZE address.  */
 
-#define LINE_SIZE_MASK  ((LINE_SIZE - 1))
-#define XFORM(x)       ((x) & LINE_SIZE_MASK)
-#define MASK(x)         ((x) & ~LINE_SIZE_MASK)
+#define LINE_SIZE_MASK(dcache)  ((dcache->line_size - 1))
+#define XFORM(dcache, x)       ((x) & LINE_SIZE_MASK (dcache))
+#define MASK(dcache, x)         ((x) & ~LINE_SIZE_MASK (dcache))
 
 struct dcache_block
 {
-  /* for least-recently-allocated and free lists */
+  /* 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 */
+  gdb_byte data[1];            /* line_size bytes at given address */
 };
 
 struct dcache_struct
 {
   splay_tree tree;
-  struct dcache_block *oldest; /* least-recently-allocated list */
+  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.  */
@@ -108,6 +111,7 @@ struct dcache_struct
 
   /* The number of in-use lines in the cache.  */
   int size;
+  CORE_ADDR line_size;  /* current line_size.  */
 
   /* The ptid of last inferior to use cache or null_ptid.  */
   ptid_t ptid;
@@ -117,16 +121,10 @@ typedef void (block_func) (struct dcache_block *block, void *param);
 
 static struct dcache_block *dcache_hit (DCACHE *dcache, CORE_ADDR addr);
 
-static int dcache_write_line (DCACHE *dcache, struct dcache_block *db);
-
 static int dcache_read_line (DCACHE *dcache, struct dcache_block *db);
 
 static struct dcache_block *dcache_alloc (DCACHE *dcache, CORE_ADDR addr);
 
-static void dcache_info (char *exp, int tty);
-
-void _initialize_dcache (void);
-
 static int dcache_enabled_p = 0; /* OBSOLETE */
 
 static void
@@ -136,8 +134,6 @@ show_dcache_enabled_p (struct ui_file *file, int from_tty,
   fprintf_filtered (file, _("Deprecated remotecache flag is %s.\n"), value);
 }
 
-static DCACHE *last_cache; /* Used by info dcache */
-
 /* 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:
@@ -209,6 +205,26 @@ for_each_block (struct dcache_block **blist, block_func *func, void *param)
   while (*blist && db != *blist);
 }
 
+/* BLOCK_FUNC routine for dcache_free.  */
+
+static void
+free_block (struct dcache_block *block, void *param)
+{
+  xfree (block);
+}
+
+/* Free a data cache.  */
+
+void
+dcache_free (DCACHE *dcache)
+{
+  splay_tree_delete (dcache->tree);
+  for_each_block (&dcache->oldest, free_block, NULL);
+  for_each_block (&dcache->freelist, free_block, NULL);
+  xfree (dcache);
+}
+
+
 /* BLOCK_FUNC function for dcache_invalidate.
    This doesn't remove the block from the oldest list on purpose.
    dcache_invalidate will do it later.  */
@@ -232,6 +248,16 @@ dcache_invalidate (DCACHE *dcache)
   dcache->oldest = NULL;
   dcache->size = 0;
   dcache->ptid = null_ptid;
+
+  if (dcache->line_size != dcache_line_size)
+    {
+      /* We've been asked to use a different line size.
+        All of our freelist blocks are now the wrong size, so free them.  */
+
+      for_each_block (&dcache->freelist, free_block, dcache);
+      dcache->freelist = NULL;
+      dcache->line_size = dcache_line_size;
+    }
 }
 
 /* Invalidate the line associated with ADDR.  */
@@ -259,7 +285,7 @@ dcache_hit (DCACHE *dcache, CORE_ADDR addr)
   struct dcache_block *db;
 
   splay_tree_node node = splay_tree_lookup (dcache->tree,
-                                           (splay_tree_key) MASK (addr));
+                                           (splay_tree_key) MASK (dcache, addr));
 
   if (!node)
     return NULL;
@@ -283,7 +309,7 @@ dcache_read_line (DCACHE *dcache, struct dcache_block *db)
   int reg_len;
   struct mem_region *region;
 
-  len = LINE_SIZE;
+  len = dcache->line_size;
   memaddr = db->addr;
   myaddr  = db->data;
 
@@ -305,15 +331,14 @@ dcache_read_line (DCACHE *dcache, struct dcache_block *db)
          len     -= reg_len;
          continue;
        }
-      
-      res = target_read (&current_target, TARGET_OBJECT_RAW_MEMORY,
-                        NULL, myaddr, memaddr, reg_len);
-      if (res < reg_len)
+
+      res = target_read_raw_memory (memaddr, myaddr, reg_len);
+      if (res != 0)
        return 0;
 
-      memaddr += res;
-      myaddr += res;
-      len -= res;
+      memaddr += reg_len;
+      myaddr += reg_len;
+      len -= reg_len;
     }
 
   return 1;
@@ -327,7 +352,7 @@ dcache_alloc (DCACHE *dcache, CORE_ADDR addr)
 {
   struct dcache_block *db;
 
-  if (dcache->size >= DCACHE_SIZE)
+  if (dcache->size >= dcache_size)
     {
       /* Evict the least recently allocated line.  */
       db = dcache->oldest;
@@ -341,12 +366,14 @@ dcache_alloc (DCACHE *dcache, CORE_ADDR addr)
       if (db)
        remove_block (&dcache->freelist, db);
       else
-       db = xmalloc (sizeof (struct dcache_block));
+       db = ((struct dcache_block *)
+             xmalloc (offsetof (struct dcache_block, data)
+                      + dcache->line_size));
 
       dcache->size++;
     }
 
-  db->addr = MASK (addr);
+  db->addr = MASK (dcache, addr);
   db->refs = 0;
 
   /* Put DB at the end of the list, it's the newest.  */
@@ -376,30 +403,26 @@ dcache_peek_byte (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr)
          return 0;
     }
 
-  *ptr = db->data[XFORM (addr)];
+  *ptr = db->data[XFORM (dcache, addr)];
   return 1;
 }
 
 /* Write the byte at PTR into ADDR in the data cache.
 
-   The caller is responsible for also promptly writing the data
-   through to target memory.
-
-   If addr is not in cache, this function does nothing; writing to
-   an area of memory which wasn't present in the cache doesn't cause
-   it to be loaded in.
+   The caller should have written the data through to target memory
+   already.
 
-   Always return 1 (meaning success) to simplify dcache_xfer_memory.  */
+   If ADDR is not in cache, this function does nothing; writing to an
+   area of memory which wasn't present in the cache doesn't cause it
+   to be loaded in.  */
 
-static int
-dcache_poke_byte (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr)
+static void
+dcache_poke_byte (DCACHE *dcache, CORE_ADDR addr, const gdb_byte *ptr)
 {
   struct dcache_block *db = dcache_hit (dcache, addr);
 
   if (db)
-    db->data[XFORM (addr)] = *ptr;
-
-  return 1;
+    db->data[XFORM (dcache, addr)] = *ptr;
 }
 
 static int
@@ -418,10 +441,7 @@ dcache_splay_tree_compare (splay_tree_key a, splay_tree_key b)
 DCACHE *
 dcache_init (void)
 {
-  DCACHE *dcache;
-  int i;
-
-  dcache = (DCACHE *) xmalloc (sizeof (*dcache));
+  DCACHE *dcache = XNEW (DCACHE);
 
   dcache->tree = splay_tree_new (dcache_splay_tree_compare,
                                 NULL,
@@ -430,53 +450,24 @@ dcache_init (void)
   dcache->oldest = NULL;
   dcache->freelist = NULL;
   dcache->size = 0;
+  dcache->line_size = dcache_line_size;
   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)
-{
-  if (last_cache == dcache)
-    last_cache = NULL;
-
-  splay_tree_delete (dcache->tree);
-  for_each_block (&dcache->oldest, free_block, NULL);
-  for_each_block (&dcache->freelist, free_block, NULL);
-  xfree (dcache);
-}
-
-/* Read or write LEN bytes from inferior memory at MEMADDR, transferring
-   to or from debugger address MYADDR.  Write to inferior if SHOULD_WRITE is
-   nonzero. 
 
-   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.  */
+/* Read LEN bytes from dcache memory at MEMADDR, transferring to
+   debugger address MYADDR.  If the data is presently cached, this
+   fills the cache.  Arguments/return are like the target_xfer_partial
+   interface.  */
 
-int
-dcache_xfer_memory (struct target_ops *ops, DCACHE *dcache,
-                   CORE_ADDR memaddr, gdb_byte *myaddr,
-                   int len, int should_write)
+enum target_xfer_status
+dcache_read_memory_partial (struct target_ops *ops, DCACHE *dcache,
+                           CORE_ADDR memaddr, gdb_byte *myaddr,
+                           ULONGEST len, ULONGEST *xfered_len)
 {
-  int i;
-  int res;
-  int (*xfunc) (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr);
-  xfunc = should_write ? dcache_poke_byte : dcache_peek_byte;
+  ULONGEST i;
 
   /* If this is a different inferior from what we've recorded,
      flush the cache.  */
@@ -487,35 +478,29 @@ dcache_xfer_memory (struct target_ops *ops, DCACHE *dcache,
       dcache->ptid = inferior_ptid;
     }
 
-  /* Do write-through first, so that if it fails, we don't write to
-     the cache at all.  */
-
-  if (should_write)
-    {
-      res = target_write (ops, TARGET_OBJECT_RAW_MEMORY,
-                         NULL, myaddr, memaddr, len);
-      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))
+      if (!dcache_peek_byte (dcache, memaddr + i, myaddr + i))
        {
          /* 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;
+         break;
        }
     }
-    
-  return len;
+
+  if (i == 0)
+    {
+      /* Even though reading the whole line failed, we may be able to
+        read a piece starting where the caller wanted.  */
+      return raw_memory_xfer_partial (ops, myaddr, NULL, memaddr, len,
+                                     xfered_len);
+    }
+  else
+    {
+      *xfered_len = i;
+      return TARGET_XFER_OK;
+    }
 }
 
 /* FIXME: There would be some benefit to making the cache write-back and
@@ -527,38 +512,50 @@ 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.  */
+/* Just update any cache lines which are already present.  This is
+   called by the target_xfer_partial machinery when writing raw
+   memory.  */
 
 void
-dcache_update (DCACHE *dcache, CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+dcache_update (DCACHE *dcache, enum target_xfer_status status,
+              CORE_ADDR memaddr, const gdb_byte *myaddr,
+              ULONGEST len)
 {
-  int i;
+  ULONGEST i;
+
   for (i = 0; i < len; i++)
-    dcache_poke_byte (dcache, memaddr + i, myaddr + i);
+    if (status == TARGET_XFER_OK)
+      dcache_poke_byte (dcache, memaddr + i, myaddr + i);
+    else
+      {
+       /* Discard the whole cache line so we don't have a partially
+          valid line.  */
+       dcache_invalidate_line (dcache, memaddr + i);
+      }
 }
 
+/* Print DCACHE line INDEX.  */
+
 static void
-dcache_print_line (int index)
+dcache_print_line (DCACHE *dcache, int index)
 {
   splay_tree_node n;
   struct dcache_block *db;
   int i, j;
 
-  if (!last_cache)
+  if (dcache == NULL)
     {
       printf_filtered (_("No data cache available.\n"));
       return;
     }
 
-  n = splay_tree_min (last_cache->tree);
+  n = splay_tree_min (dcache->tree);
 
   for (i = index; i > 0; --i)
     {
       if (!n)
        break;
-      n = splay_tree_successor (last_cache->tree, n->key);
+      n = splay_tree_successor (dcache->tree, n->key);
     }
 
   if (!n)
@@ -570,28 +567,31 @@ dcache_print_line (int index)
   db = (struct dcache_block *) n->value;
 
   printf_filtered (_("Line %d: address %s [%d hits]\n"),
-                  index, paddress (target_gdbarch, db->addr), db->refs);
+                  index, paddress (target_gdbarch (), db->addr), db->refs);
 
-  for (j = 0; j < LINE_SIZE; j++)
+  for (j = 0; j < dcache->line_size; j++)
     {
       printf_filtered ("%02x ", db->data[j]);
 
-      /* Print a newline every 16 bytes (48 characters) */
-      if ((j % 16 == 15) && (j != LINE_SIZE - 1))
+      /* Print a newline every 16 bytes (48 characters) */
+      if ((j % 16 == 15) && (j != dcache->line_size - 1))
        printf_filtered ("\n");
     }
   printf_filtered ("\n");
 }
 
+/* Parse EXP and show the info about DCACHE.  */
+
 static void
-dcache_info (char *exp, int tty)
+dcache_info_1 (DCACHE *dcache, const char *exp)
 {
   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)
        {
@@ -599,25 +599,27 @@ dcache_info (char *exp, int tty)
           return;
        }
 
-      dcache_print_line (i);
+      dcache_print_line (dcache, i);
       return;
     }
 
-  printf_filtered (_("Dcache line width %d, maximum size %d\n"),
-                  LINE_SIZE, DCACHE_SIZE);
+  printf_filtered (_("Dcache %u lines of %u bytes each.\n"),
+                  dcache_size,
+                  dcache ? (unsigned) dcache->line_size
+                  : dcache_line_size);
 
-  if (!last_cache || ptid_equal (last_cache->ptid, null_ptid))
+  if (dcache == NULL || ptid_equal (dcache->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));
+                  target_pid_to_str (dcache->ptid));
 
   refcount = 0;
 
-  n = splay_tree_min (last_cache->tree);
+  n = splay_tree_min (dcache->tree);
   i = 0;
 
   while (n)
@@ -625,16 +627,62 @@ dcache_info (char *exp, int tty)
       struct dcache_block *db = (struct dcache_block *) n->value;
 
       printf_filtered (_("Line %d: address %s [%d hits]\n"),
-                      i, paddress (target_gdbarch, db->addr), db->refs);
+                      i, paddress (target_gdbarch (), db->addr), db->refs);
       i++;
       refcount += db->refs;
 
-      n = splay_tree_successor (last_cache->tree, n->key);
+      n = splay_tree_successor (dcache->tree, n->key);
     }
 
   printf_filtered (_("Cache state: %d active lines, %d hits\n"), i, refcount);
 }
 
+static void
+info_dcache_command (const char *exp, int tty)
+{
+  dcache_info_1 (target_dcache_get (), exp);
+}
+
+static void
+set_dcache_size (const char *args, int from_tty,
+                struct cmd_list_element *c)
+{
+  if (dcache_size == 0)
+    {
+      dcache_size = DCACHE_DEFAULT_SIZE;
+      error (_("Dcache size must be greater than 0."));
+    }
+  target_dcache_invalidate ();
+}
+
+static void
+set_dcache_line_size (const char *args, int from_tty,
+                     struct cmd_list_element *c)
+{
+  if (dcache_line_size < 2
+      || (dcache_line_size & (dcache_line_size - 1)) != 0)
+    {
+      unsigned d = dcache_line_size;
+      dcache_line_size = DCACHE_DEFAULT_LINE_SIZE;
+      error (_("Invalid dcache line size: %u (must be power of 2)."), d);
+    }
+  target_dcache_invalidate ();
+}
+
+static void
+set_dcache_command (const char *arg, int from_tty)
+{
+  printf_unfiltered (
+     "\"set dcache\" must be followed by the name of a subcommand.\n");
+  help_list (dcache_set_list, "set dcache ", all_commands, gdb_stdout);
+}
+
+static void
+show_dcache_command (const char *args, int from_tty)
+{
+  cmd_show_list (dcache_show_list, from_tty, "");
+}
+
 void
 _initialize_dcache (void)
 {
@@ -650,10 +698,34 @@ exists only for compatibility reasons."),
                           show_dcache_enabled_p,
                           &setlist, &showlist);
 
-  add_info ("dcache", dcache_info,
+  add_info ("dcache", info_dcache_command,
            _("\
 Print information on the dcache performance.\n\
 With no arguments, this command prints the cache configuration and a\n\
 summary of each line in the cache.  Use \"info dcache <lineno> to dump\"\n\
 the contents of a given line."));
+
+  add_prefix_cmd ("dcache", class_obscure, set_dcache_command, _("\
+Use this command to set number of lines in dcache and line-size."),
+                 &dcache_set_list, "set dcache ", /*allow_unknown*/0, &setlist);
+  add_prefix_cmd ("dcache", class_obscure, show_dcache_command, _("\
+Show dcachesettings."),
+                 &dcache_show_list, "show dcache ", /*allow_unknown*/0, &showlist);
+
+  add_setshow_zuinteger_cmd ("line-size", class_obscure,
+                            &dcache_line_size, _("\
+Set dcache line size in bytes (must be power of 2)."), _("\
+Show dcache line size."),
+                            NULL,
+                            set_dcache_line_size,
+                            NULL,
+                            &dcache_set_list, &dcache_show_list);
+  add_setshow_zuinteger_cmd ("size", class_obscure,
+                            &dcache_size, _("\
+Set number of dcache lines."), _("\
+Show number of dcache lines."),
+                            NULL,
+                            set_dcache_size,
+                            NULL,
+                            &dcache_set_list, &dcache_show_list);
 }
This page took 0.052369 seconds and 4 git commands to generate.