summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Jacobowitz <dan@debian.org>2006-03-14 13:58:41 +0000
committerDaniel Jacobowitz <dan@debian.org>2006-03-14 13:58:41 +0000
commit74748088534c8a59ceeb931e5de9c53270d8d7ed (patch)
tree686502d797387f6fcfa7ded973bbc2e2a9a9ca52
parenta836d93922f76af3ce3b3a1f0a141bf523d99132 (diff)
downloadgdb-74748088534c8a59ceeb931e5de9c53270d8d7ed.tar.gz
Add RAM caching, checksum, and non-sequential register support.
-rw-r--r--gdb/Makefile.in9
-rw-r--r--gdb/available.c290
-rw-r--r--gdb/available.h3
-rw-r--r--gdb/features/feature_to_c.sh37
-rw-r--r--gdb/features/gdb-target.dtd9
-rw-r--r--gdb/parse-avail.c39
6 files changed, 350 insertions, 37 deletions
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 1b017c54452..ef56a58dad0 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -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)
diff --git a/gdb/available.c b/gdb/available.c
index b887d0977dd..f7ae4077aa4 100644
--- a/gdb/available.c
+++ b/gdb/available.c
@@ -27,12 +27,14 @@
#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 */
@@ -53,26 +55,184 @@
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)
{
diff --git a/gdb/available.h b/gdb/available.h
index 600722d57cb..e8d66245409 100644
--- a/gdb/available.h
+++ b/gdb/available.h
@@ -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
index 00000000000..8d04cee4a4c
--- /dev/null
+++ b/gdb/features/feature_to_c.sh
@@ -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
diff --git a/gdb/features/gdb-target.dtd b/gdb/features/gdb-target.dtd
index 54b865aa44a..54db394c4bf 100644
--- a/gdb/features/gdb-target.dtd
+++ b/gdb/features/gdb-target.dtd
@@ -30,15 +30,18 @@
<!-- 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'
diff --git a/gdb/parse-avail.c b/gdb/parse-avail.c
index b331d8a53be..b4a1f871c71 100644
--- a/gdb/parse-avail.c
+++ b/gdb/parse-avail.c
@@ -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)
{