]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Add RAM caching, checksum, and non-sequential register support.
authorDaniel Jacobowitz <drow@false.org>
Tue, 14 Mar 2006 13:58:41 +0000 (13:58 +0000)
committerDaniel Jacobowitz <drow@false.org>
Tue, 14 Mar 2006 13:58:41 +0000 (13:58 +0000)
gdb/Makefile.in
gdb/available.c
gdb/available.h
gdb/features/feature_to_c.sh [new file with mode: 0644]
gdb/features/gdb-target.dtd
gdb/parse-avail.c

index 1b017c5445218f9fe69e9937b2d76f4b94a8d22d..ef56a58dad0a622f71f9bfb7b913c9db027bc0ae 100644 (file)
@@ -609,6 +609,7 @@ gdb_sim_sh_h =      $(INCLUDE_DIR)/gdb/sim-sh.h
 splay_tree_h =  $(INCLUDE_DIR)/splay-tree.h
 safe_ctype_h =  $(INCLUDE_DIR)/safe-ctype.h
 hashtab_h =    $(INCLUDE_DIR)/hashtab.h
+filenames_h =  $(INCLUDE_DIR)/filenames.h
 
 #
 # $BUILD/ headers
@@ -1794,8 +1795,8 @@ auxv.o: auxv.c $(defs_h) $(target_h) $(gdbtypes_h) $(command_h) \
        $(inferior_h) $(valprint_h) $(gdb_assert_h) $(auxv_h) \
        $(elf_common_h)
 available.o: available.c $(defs_h) $(symfile_h) $(target_h) $(available_h) \
-       $(arch_utils_h) $(gdbtypes_h) \
-       $(gdb_string) $(gdb_assert) $(gdb_obstack_h)
+       $(arch_utils_h) $(gdbtypes_h) $(sha1_h) \
+       $(gdb_string) $(gdb_assert) $(gdb_obstack_h) $(gdb_stdint_h)
 avr-tdep.o: avr-tdep.c $(defs_h) $(frame_h) $(frame_unwind_h) \
        $(frame_base_h) $(trad_frame_h) $(gdbcmd_h) $(gdbcore_h) \
        $(inferior_h) $(symfile_h) $(arch_utils_h) $(regcache_h) \
@@ -2410,8 +2411,8 @@ parse.o: parse.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \
        $(frame_h) $(expression_h) $(value_h) $(command_h) $(language_h) \
        $(parser_defs_h) $(gdbcmd_h) $(symfile_h) $(inferior_h) \
        $(doublest_h) $(gdb_assert_h) $(block_h)
-parse-avail.o: parse-avail.c $(defs_h) $(available_h) \
-       $(gdb_string) $(gdb_obstack_h)
+parse-avail.o: parse-avail.c $(defs_h) $(available_h) $(gdb_assert_h) \
+       $(filenames_h) $(gdb_string) $(gdb_obstack_h)
 p-exp.o: p-exp.c $(defs_h) $(gdb_string_h) $(expression_h) $(value_h) \
        $(parser_defs_h) $(language_h) $(p_lang_h) $(bfd_h) $(symfile_h) \
        $(objfiles_h) $(block_h)
index b887d0977dd1810bd1c419d71828513365441898..f7ae4077aa417f81ae2ee025a580c92e157e6efb 100644 (file)
 #include "gdbtypes.h"
 #include "symfile.h"
 #include "target.h"
+#include "sha1.h"
 
 #include "available.h"
 
 #include "gdb_assert.h"
 #include "gdb_string.h"
 #include "gdb_obstack.h"
+#include "gdb_stdint.h"
 
 /* TODO: Remote target "guess" features from g packet size */
 
    to initialize a gdbarch, which leads to later failures when we expect
    e.g. current_regcache to have been initialized.  */
 
+
+
+/* Support for caching XML objects read from the target.
+
+   TODO ITEMS:
+   - Support caching to disk.
+   - Support compiled-in feature cache.
+   - Figure out memory management for feature contents strings.
+*/
+
+
+/* Saved information about cached XML objects.  Each cache entry
+   corresponds to a file in the cache, or an object fetched from
+   the target with one particular annex.  */
+
+struct xml_cache_entry
+{
+  const char *annex;
+  const char *contents;
+
+  union
+  {
+    /* We use a union to represent the checksum in order to guarantee
+       sufficient alignment.  */
+    uint32_t words[5];
+    unsigned char bytes[20];
+  } sha1sum;
+
+  struct xml_cache_entry *next;
+};
+
+/* A list of all the cached objects.  */
+
+static struct xml_cache_entry *xml_global_cache;
+
+/* Look for a feature in the cache with ANNEX and CHECKSUM.  If no
+   entry is found, return NULL.  */
+
+static const char *
+find_xml_feature_in_cache (const char *annex, const unsigned char *checksum)
+{
+  struct xml_cache_entry *ent;
+
+  for (ent = xml_global_cache; ent != NULL; ent = ent->next)
+    {
+      if (strcmp (ent->annex, annex) != 0)
+       continue;
+      if (memcmp (ent->sha1sum.bytes, checksum, 20) != 0)
+       continue;
+
+      return ent->contents;
+    }
+
+  return NULL;
+}
+
+/* Add CONTENTS, which represents the object named ANNEX, to the
+   cache if it is not already cached.  */
+
+static void
+add_xml_feature_to_cache (const char *annex, const char *contents)
+{
+  struct xml_cache_entry new_ent;
+
+  /* FIXME: Again, memory allocation?  */
+  new_ent.annex = xstrdup (annex);
+  new_ent.contents = xstrdup (contents);
+
+  sha1_buffer (new_ent.contents, strlen (new_ent.contents),
+              new_ent.sha1sum.bytes);
+
+  /* If this entry is already in the cache, do not add it again.  */
+  if (find_xml_feature_in_cache (annex, new_ent.sha1sum.bytes))
+    return;
+
+  new_ent.next = xml_global_cache;
+
+  xml_global_cache = xmalloc (sizeof (struct xml_cache_entry));
+  memcpy (xml_global_cache, &new_ent, sizeof (struct xml_cache_entry));
+}
+
+/* Convert an ASCII checksum string, CHECKSUM, to a binary blob,
+   BYTES.  Returns 0 for success, or -1 if a bad character is
+   encountered.  CHECKSUM does not need to be NUL terminated.  */
+
+static int
+checksum_to_bytes (char *checksum, unsigned char *bytes)
+{
+  int i;
+
+  for (i = 0; i < 20; i++)
+    {
+      int n;
+      char c1, c2;
+
+      c1 = checksum[2 * i];
+      if (c1 >= '0' && c1 <= '9')
+       n = c1 - '0';
+      else if (c1 >= 'a' && c1 <= 'f')
+       n = c1 - 'a' + 10;
+      else if (c1 >= 'A' && c1 <= 'F')
+       n = c1 - 'A' + 10;
+      else
+       return -1;
+
+      n *= 16;
+
+      c2 = checksum[2 * i + 1];
+      if (c2 >= '0' && c2 <= '9')
+       n += c2 - '0';
+      else if (c2 >= 'a' && c2 <= 'f')
+       n += c2 - 'a' + 10;
+      else if (c2 >= 'A' && c2 <= 'F')
+       n += c2 - 'A' + 10;
+      else
+       return -1;
+
+      bytes[i] = n;
+    }
+
+  return 0;
+}
+
+/* Baton passed to fetch_available_features_from_target.  */
+
+struct fetch_features_baton
+{
+  struct target_ops *ops;
+
+  struct fetch_features_checksum
+  {
+    const char *annex;
+    unsigned char checksum[20];
+  } *checksums;
+};
+
 /* Read a string representation of available features from the target,
    using TARGET_OBJECT_AVAILABLE_FEATURES.  The returned string is
-   malloc allocated and NUL-terminated.  If NAME is NULL, the overall
-   feature set is read; otherwise the specified name is read (e.g.
-   resolving xi:include).  */
+   malloc allocated and NUL-terminated.  NAME should be a non-NULL
+   string identifying the XML document we want; the top level document
+   is "target.xml".  Other calls may be performed for the DTD or
+   for xi:include.  */
 
 static char *
-fetch_available_features_from_target (const char *name, void *ops_)
+fetch_available_features_from_target (const char *name, void *baton_)
 {
-  struct target_ops *ops = ops_;
+  struct fetch_features_baton *baton = baton_;
   char *features_str;
   gdb_byte *features_buf;
   LONGEST len;
 
-  struct gdb_feature_set *features;
-  struct gdb_available_feature **slot;
-  int ret;
+  if (baton->checksums)
+    {
+      struct fetch_features_checksum *checksum_ent;
 
-  len = target_read_whole (ops, TARGET_OBJECT_AVAILABLE_FEATURES,
-                          NULL, &features_buf);
+      for (checksum_ent = baton->checksums;
+          checksum_ent->annex != NULL;
+          checksum_ent++)
+       if (strcmp (checksum_ent->annex, name) == 0)
+         break;
+
+      if (checksum_ent)
+       {
+         const char *cached_str;
+
+         cached_str = find_xml_feature_in_cache (name,
+                                                 checksum_ent->checksum);
+
+         /* This function always returns something which the caller is
+            responsible for freeing.  So, if we got a match, return a
+            copy of it.  */
+         if (cached_str)
+           return xstrdup (cached_str);
+       }
+    }
+
+  len = target_read_whole (baton->ops, TARGET_OBJECT_AVAILABLE_FEATURES,
+                          name, &features_buf);
   if (len <= 0)
     return NULL;
 
@@ -85,6 +245,9 @@ fetch_available_features_from_target (const char *name, void *ops_)
       features_str[len] = '\0';
     }
 
+  if (baton->checksums)
+    add_xml_feature_to_cache (name, features_str);
+
   return features_str;
 }
 
@@ -92,25 +255,120 @@ fetch_available_features_from_target (const char *name, void *ops_)
    to a binary representation.  The string representation is fetched using
    TARGET_OBJECT_AVAILABLE_FEATURES.  */
 
+/* TODO: Document \n conventions */
+
 struct gdb_feature_set *
 available_features_from_target_object (struct target_ops *ops,
                                       struct obstack *obstack)
 {
+  struct fetch_features_baton baton;
   struct gdb_feature_set *features;
-  char *features_str, *cur;
-  gdb_byte *features_buf;
-  LONGEST len;
-  struct gdb_available_feature **slot;
+  char *features_str, *checksums_str;
   int ret;
+  struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
+
+  /* Initialize the baton.  */
+  baton.ops = ops;
+  baton.checksums = NULL;
+
+  /* Attempt to read checksums from the target.  */
+  checksums_str = fetch_available_features_from_target ("CHECKSUMS", &baton);
+  if (checksums_str)
+    {
+      char *p;
+      int n_checksums;
+
+      make_cleanup (xfree, checksums_str);
+
+      /* Allow for one checksum in case there is no trailing newline,
+        and one to serve as a NULL terminator.  */
+      n_checksums = 2;
+
+      /* Allocate one additional checksum per newline.  */
+      for (p = checksums_str; *p; p++)
+       if (*p == '\n')
+         n_checksums++;
 
-  features_str = fetch_available_features_from_target (NULL, ops);
+      baton.checksums = xmalloc (n_checksums
+                                * sizeof (struct fetch_features_checksum));
+      make_cleanup (xfree, baton.checksums);
+
+      n_checksums = 0;
+      p = checksums_str;
+      while (*p)
+       {
+         char *field_end;
+
+         /* Find the first space on the line, marking the end of the
+            checksum.  */
+         field_end = p;
+         while (*field_end && *field_end != '\n'
+                && *field_end != ' ')
+           field_end++;
+
+         /* Check for a malformed checksum.  */
+         if (*field_end != ' '
+             || field_end - p != 40
+             || checksum_to_bytes (p, baton.checksums[n_checksums].checksum))
+           {
+             /* Skip this line.  */
+             p = field_end;
+             while (*p && *p != '\n')
+               p++;
+             if (*p == '\n')
+               p++;
+             continue;
+           }
+
+         *field_end = '\0';
+
+         /* Skip whitespace after the checksum.  */
+         p = field_end + 1;
+         while (*p == ' ')
+           p++;
+
+         field_end = p;
+         while (*field_end && *field_end != '\n')
+           field_end++;
+
+         if (field_end == p)
+           {
+             /* Malformed line; skip it.  */
+             if (*p == '\n')
+               p++;
+             continue;
+           }
+
+         baton.checksums[n_checksums++].annex = p;
+
+         /* Advance to the next line, inserting a NUL for the end of
+            the annex name if necessary.  */
+         if (*field_end)
+           {
+             *field_end = '\0';
+             p = field_end + 1;
+           }
+         else
+           break;
+       }
+
+      baton.checksums[n_checksums].annex = NULL;
+    }
+
+  /* FIXME: Memory management: what happens to features_str?  */
+
+  features_str = fetch_available_features_from_target ("target.xml", &baton);
+  if (features_str == NULL)
+    return NULL;
 
   features = OBSTACK_ZALLOC (obstack, struct gdb_feature_set);
   features->obstack = obstack;
   ret = available_features_from_xml_string (&features->features, obstack,
                                            features_str,
                                            fetch_available_features_from_target,
-                                           ops, 0);
+                                           &baton, 0);
+
+  do_cleanups (back_to);
 
   if (ret < 0)
     {
index 600722d57cbf10efb81fdb5edc48bf7c5825ce4d..e8d6624540970a657411779a3bf7f375a6a2dd58 100644 (file)
@@ -83,7 +83,7 @@ struct gdb_available_register
   /* The protocol number used by this target to provide this
      feature.  For instance, the register number used for remote
      p/P packets to access this register.  */
-  int protocol_number;
+  long protocol_number;
 
   /* Data private to the architecture associated with this feature.
      This is a NUL-terminated string.  */
@@ -158,7 +158,6 @@ const char *available_register_name (struct gdbarch *, int);
 
 int available_register_target_regnum (struct gdbarch *, int);
 
-
 /* Internal routines shared by available.c and parse-avail.c.  */
 
 typedef char *(*xml_fetch_another) (const char *, void *);
diff --git a/gdb/features/feature_to_c.sh b/gdb/features/feature_to_c.sh
new file mode 100644 (file)
index 0000000..8d04cee
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+input=$1
+output=$2
+
+if test -z "$input" || test -z "$output"; then
+  echo "Usage: $0 INPUTFILE OUTPUTFILE"
+  exit 1
+fi
+
+arrayname=xml_feature_`basename $input | sed 's/\..*//g; s/-/_/g'`
+
+gawk 'BEGIN { n = 0
+  print "const char '$arrayname'[] = {"
+  for (i = 0; i < 255; i++)
+    _ord_[sprintf("%c", i)] = i
+} {
+  split($0, line, "");
+  printf "  "
+  for (i = 1; i <= length(line); i++) {
+    c = line[i]
+    if (c == "'\''") {
+      printf "'\''\\'\'''\'', "
+    } else if (c == "\\") {
+      printf "'\''\\\\'\'', "
+    } else if (match (c, "[[:print:]]") != 0) {
+      printf "'\''" c "'\'', "
+    } else {
+      printf "'\''\\%03o'\'', ", _ord_[c]
+    }
+    if (i % 10 == 0)
+      printf "\n   "
+  }
+  printf "'\''\\n'\'', \n"
+} END {
+  print "  0 };"
+}' < $input > $output
index 54b865aa44aa1363c1c3749d5e2b87a8eae37c51..54db394c4bf58feed14182f6cc4d23f68a203c1e 100644 (file)
 
 <!-- TODO: GDB does not yet support descriptions.  -->
 
-<!-- Registers do not have an explicit register number field; they
-     are numbered sequentially from the containing feature's base-regnum
-     when the feature is referenced.  -->
+<!-- Registers are not required to have an explicit register number field;
+     they are numbered sequentially starting at zero.  If a register does
+     have an explicit number, the next register will be assigned the next
+     sequential number by default.  When the feature is referenced, register
+     numbers are adjusted by the reference's base-regnum.  -->
 <!-- arch_data; see above -->
 <!-- Kill save-restore in favor of a more complete scheme -->
 <!ELEMENT reg          (description*)>
 <!ATTLIST reg
        name            CDATA   #REQUIRED
        bitsize         CDATA   #REQUIRED
+       regnum          CDATA   #IMPLIED
        readonly        (yes | no) 'no'
        save-restore    (yes | no) 'yes'
        type            CDATA   'int'
index b331d8a53beab5e79fbf5463383b77cc6765adce..b4a1f871c71b9b9e0f80ea33c10a1a52810ffe8f 100644 (file)
@@ -328,6 +328,7 @@ xml_start_reg (struct xml_feature_parse_data *data,
                       sizeof (struct gdb_available_register));
   memset (reg, 0, sizeof (struct gdb_available_register));
 
+  reg->protocol_number = -1;
   reg->bitsize = -1;
   reg->readonly = -1;
   reg->save_restore = -1;
@@ -343,6 +344,12 @@ xml_start_reg (struct xml_feature_parse_data *data,
       else if (strcmp (name, "name") == 0)
        reg->name = obstrdup (data->obstack, val);
 
+      else if (strcmp (name, "regnum") == 0)
+       {
+         if (xml_parse_one_integer (val, &reg->protocol_number) < 0)
+           data->unhandled++;
+       }
+
       else if (strcmp (name, "bitsize") == 0)
        {
          if (xml_parse_one_integer (val, &reg->bitsize) < 0)
@@ -457,10 +464,8 @@ xml_start_feature_ref (struct xml_feature_parse_data *data,
     }
 
   /* Set register numbers in the new feature.  */
-  for (i = new_feature->protocol_number, reg = new_feature->registers;
-       reg != NULL;
-       i++, reg = reg->next)
-    reg->protocol_number = i;
+  for (reg = new_feature->registers; reg != NULL; reg = reg->next)
+    reg->protocol_number += new_feature->protocol_number;
 
   new_feature->next = data->target_features;
   data->target_features = new_feature;
@@ -682,10 +687,11 @@ xml_feature_end_element (void *data_, const XML_Char *name)
       feature->next = data->seen_features;
       data->seen_features = feature;
 
-      /* Reverse the list of registers.  */
       if (feature->registers)
        {
+         /* Reverse the list of registers.  */
          struct gdb_available_register *reg1, *reg2, *reg3;
+         int next;
 
          reg1 = NULL;
          reg2 = feature->registers;
@@ -699,6 +705,13 @@ xml_feature_end_element (void *data_, const XML_Char *name)
            }
 
          feature->registers = reg1;
+
+         next = 0;
+         for (reg1 = feature->registers; reg1; reg1 = reg1->next)
+           if (reg1->protocol_number == -1)
+             reg1->protocol_number = next++;
+           else
+             next = reg1->protocol_number + 1;
        }
 
       break;
@@ -765,14 +778,15 @@ xml_parser_cleanup (void *parser)
   struct xml_feature_parse_data *data;
 
   data = XML_GetUserData (parser);
-  while (data->state)
-    {
-      struct xml_state_stack *prev;
+  if (data)
+    while (data->state)
+      {
+       struct xml_state_stack *prev;
 
-      prev = data->state->prev;
-      xfree (data->state);
-      data->state = prev;
-    }
+       prev = data->state->prev;
+       xfree (data->state);
+       data->state = prev;
+      }
 
   XML_ParserFree (parser);
 }
@@ -801,6 +815,7 @@ xml_feature_external_entity (XML_Parser parser,
   back_to = make_cleanup (xml_parser_cleanup, entity_parser);
 
   XML_SetElementHandler (entity_parser, NULL, NULL);
+  XML_SetUserData (entity_parser, NULL);
 
   if (XML_Parse (entity_parser, text, strlen (text), 1) != XML_STATUS_OK)
     {