diff options
author | Daniel Jacobowitz <dan@debian.org> | 2006-03-14 13:58:41 +0000 |
---|---|---|
committer | Daniel Jacobowitz <dan@debian.org> | 2006-03-14 13:58:41 +0000 |
commit | 74748088534c8a59ceeb931e5de9c53270d8d7ed (patch) | |
tree | 686502d797387f6fcfa7ded973bbc2e2a9a9ca52 | |
parent | a836d93922f76af3ce3b3a1f0a141bf523d99132 (diff) | |
download | gdb-74748088534c8a59ceeb931e5de9c53270d8d7ed.tar.gz |
Add RAM caching, checksum, and non-sequential register support.
-rw-r--r-- | gdb/Makefile.in | 9 | ||||
-rw-r--r-- | gdb/available.c | 290 | ||||
-rw-r--r-- | gdb/available.h | 3 | ||||
-rw-r--r-- | gdb/features/feature_to_c.sh | 37 | ||||
-rw-r--r-- | gdb/features/gdb-target.dtd | 9 | ||||
-rw-r--r-- | gdb/parse-avail.c | 39 |
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, ®->protocol_number) < 0) + data->unhandled++; + } + else if (strcmp (name, "bitsize") == 0) { if (xml_parse_one_integer (val, ®->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) { |