2005-11-16 Andrew Stubbs * value.c: Include exceptions.h. (fixup_internalvar_endian): Don't fix the endian if the type needs fixing due to keep-variable. (keep_variable_command): New function. (fixup_internalvar_type_info): New function. (lookup_internalvar): Call fixup_internalvar_type_info() if needed. Initialise new 'temp' and 'type_needs_fixing' fields. (set_internalvar): Reset type_needs_fixing. (get_name_from_type): New function. (clear_internalvars): Rewrite to support keep-variable command. (show_convenience): Call fixup_internalvar_type_info() if needed. (_initialize_values): Add keep-variable command. * exec.c (exec_file_attach). Call clear_internalvars(). * symfile.c (symbol_file_command). Call clear_internalvars(). * value.h (struct internalvar): Add 'temp', 'type_needs_fixing', 'type_name', and 'enclosing_type_name' fields. * Makefile.in (value.o): Add dependency on exceptions.h. doc/ * gdb.texinfo (Convenience variables): Add keep-variable command. Index: src/gdb/value.c =================================================================== --- src.orig/gdb/value.c 2005-11-07 18:27:03.000000000 +0000 +++ src/gdb/value.c 2005-11-07 18:27:04.000000000 +0000 @@ -37,6 +37,7 @@ #include "gdb_assert.h" #include "regcache.h" #include "block.h" +#include "exceptions.h" /* Prototypes for exported functions. */ @@ -779,10 +780,12 @@ fixup_internalvar_endian (struct interna gdb_byte temp; gdb_byte *array = value_contents_raw (var->value); - /* Don't do anything if the endian has not changed. + /* Don't do anything if the endian has not changed or if the type needs + fixing - the endian will be fixed with the type later. Also disregard FP types because they don't seem to vary with endian - at least, not on i686 Linux or sparc Solaris. */ if (var->endian != TARGET_BYTE_ORDER + && var->type_needs_fixing == 0 && TYPE_CODE (value_type (var->value)) != TYPE_CODE_FLT) { /* Reverse the bytes. @@ -811,6 +814,92 @@ fixup_all_internalvars_endian (void) } +/* Ensure that the specified internal variable will not be destroyed + when new symtabs are loaded. The argument 'name', must + be an internal variable name, prefixed with a dollar character. */ +static void +keep_variable_command (char* name, int from_tty) +{ + struct internalvar* intvar; + char* str; + + if (name == NULL || name[0] != '$') + { + printf_unfiltered( "usage: keep-variable $\n" ); + return; + } + + str = ++name; /* Remove $ character, i.e. "$var" ==> "var" */ + intvar = lookup_internalvar(str); + intvar->temp = 0; /* Preserve variable by clearing 'temp' attribute */ +} + +/* If the symbol table is reloaded then permanent variables will lose + their type info. clear_internalvars() makes a note of the name of + any such types in the variable. + This function is called whenever the variable is next required to + get the new type info. It also fixes the endianess if required. */ + +static void +fixup_internalvar_type_info (struct internalvar *var) +{ + int i, error=0; + + /* For each in type and enclosing type ... */ + for (i=0; i<2; i++) + { + char *type_name = i==0 ? var->type_name + : var->enclosing_type_name; + struct type **var_type = i==0 ? &var->value->type + : &var->value->enclosing_type; + + if (type_name != NULL) + { + /* Get the typename using parse expression - it can cope + with builtin types even when there is no symbol table. */ + struct expression *expr = (struct expression *) + catch_errors ((catch_errors_ftype *)parse_expression, + type_name, NULL, RETURN_MASK_ALL); + register struct cleanup *old_chain = + make_cleanup (free_current_contents, &expr); + + if (expr != NULL && expr->nelts >= 2 + && expr->elts[0].opcode == OP_TYPE) + /* Overwite the old (now broken) type with the shiny new one. */ + *var_type = expr->elts[1].type; + else + error=1; + + do_cleanups (old_chain); + } + } + + + if (error) + { + /* Types no longer exist. + The only thing to do is wipe the value. */ + xfree (var->value); + var->value = allocate_value (builtin_type_void); + release_value (var->value); + + /* Tell the user we have had to do this. + The message will occur when they are accessing the variable. */ + printf_unfiltered ( + "The type of '$%s' was lost when the symbol table was updated.\n", + var->name); + } + + /* Return the internal variable to its normal state. */ + xfree (var->type_name); + xfree (var->enclosing_type_name); + var->type_needs_fixing = 0; + + /* Fix up the endianess. */ + fixup_internalvar_endian (var); +} + + /* Look up an internal variable with name NAME. NAME should not normally include a dollar sign. @@ -824,12 +913,18 @@ lookup_internalvar (char *name) for (var = internalvars; var; var = var->next) if (strcmp (var->name, name) == 0) - return var; + { + if (var->type_needs_fixing) + fixup_internalvar_type_info (var); + return var; + } var = (struct internalvar *) xmalloc (sizeof (struct internalvar)); var->name = concat (name, (char *)NULL); var->value = allocate_value (builtin_type_void); var->endian = TARGET_BYTE_ORDER; + var->temp = 1; + var->type_needs_fixing = 0; release_value (var->value); var->next = internalvars; internalvars = var; @@ -885,6 +980,7 @@ set_internalvar (struct internalvar *var xfree (var->value); var->value = newval; var->endian = TARGET_BYTE_ORDER; + var->type_needs_fixing = 0; release_value (newval); /* End code which must not call error(). */ } @@ -895,21 +991,136 @@ internalvar_name (struct internalvar *va return var->name; } -/* Free all internalvars. Done when new symtabs are loaded, - because that makes the values invalid. */ + +/* Get the typename from a struct type. + Returns the name of type as parse_expresion will understand. + Returns NULL if the type is unsupported. */ + +static char * +get_name_from_type (const struct type *type) +{ + char *result; + char *prefix = NULL, pointers=0; + + /* Follow the pointer types until the base type is discovered. */ + while (TYPE_CODE (type) == TYPE_CODE_PTR) + { + pointers++; + type = TYPE_TARGET_TYPE (type); + } + + /* Find the appropriate string for the type. + If we can't find one then return NULL and fixup_internalvar_type_info() + will replace the variable with void. If this is a problem then this + table will have to be extended to support the relavent type. */ + switch (TYPE_CODE (type)) + { + case TYPE_CODE_STRUCT: + prefix = "struct"; + /* Fall through. */ + case TYPE_CODE_UNION: + if (prefix == NULL) + prefix = "union"; + /* Fall through. */ + case TYPE_CODE_ENUM: + if (prefix == NULL) + prefix = "enum"; + + /* Note that these types do not have names, but do have tag names. */ + + if (TYPE_TAG_NAME(type) == NULL) + return NULL; + + result = xmalloc (strlen (prefix) + + 1 /* space */ + + strlen (TYPE_TAG_NAME(type)) + + 1 /* space */ + + pointers + + 1 /* terminator */); + sprintf( result, "%s %s ", prefix, TYPE_TAG_NAME(type)); + + break; + default: + /* Other types all (?) have names. */ + + if ( TYPE_NAME(type) == NULL ) + return NULL; + + result = xmalloc (strlen (TYPE_NAME(type)) + + 1 /* space */ + + pointers + + 1 /* terminator */); + sprintf (result, "%s ", TYPE_NAME(type)); + } + + /* Add stars for pointer types. */ + while ( pointers-- > 0 ) + strcat (result, "*"); + + return result; +} + + +/* Traverse list of intervalvars. If an internalvar's 'temp' + attribute is set, then free it - otherwise ignore it and check + the next member of the list. Ensure that 'internalvars' points + to the head of the list. + + Although the variables are kept, the type information is not. + Therefore take a note of the textual names. The types will then + be patched up later when they are referenced. The function that + does this is fixup_internalvars_type_info(). + + In order to take the notes BEFORE the types vanish a few extra + calls to this function have been inserted elsewhere. + + The majority of internalvar's will be cleared when new symtabs are + loaded, because at this point they become invalid. */ void clear_internalvars (void) { struct internalvar *var; + struct internalvar *next, **prev; - while (internalvars) + var = internalvars; + prev = &internalvars; + + while (var) { - var = internalvars; - internalvars = var->next; - xfree (var->name); - xfree (var->value); - xfree (var); + next = var->next; + + /* Is this a temporary variable? If so - free it. */ + if (var->temp) + { + /* Move the next variable up the list - we may want to keep it. */ + *prev = next; + + xfree (var->name); + xfree (var->value); + xfree (var); + } + else + { + /* This is a permanent variable so it will not be removed from + the list. Mark it as the current tail of the list. */ + prev = &var->next; + + /* Note down the values of the types. + This ensures that they can be fixed up after the symbol + tables are rebuilt. + Don't do it twice - the lazy fixup means it may not have been + fixed since the last time we were here. */ + if ( var->type_needs_fixing == 0 ) + { + var->type_needs_fixing = 1; + var->type_name = get_name_from_type (var->value->type); + var->enclosing_type_name = + get_name_from_type (var->value->enclosing_type); + } + } + + var = next; } } @@ -921,6 +1132,9 @@ show_convenience (char *ignore, int from for (var = internalvars; var; var = var->next) { + if (var->type_needs_fixing) + fixup_internalvar_type_info (var); + if (!varseen) { varseen = 1; @@ -1730,4 +1944,7 @@ A few convenience variables are given va init-if-undefined = \n\ Ensure that an internal variable exists and set it to\n\ a given value if it does not.")); + + add_com("keep-variable", class_vars, keep_variable_command, + _("Ensures that specified internal variable will not be removed")); } Index: src/gdb/exec.c =================================================================== --- src.orig/gdb/exec.c 2005-11-07 17:22:56.000000000 +0000 +++ src/gdb/exec.c 2005-11-07 18:27:04.000000000 +0000 @@ -266,6 +266,11 @@ exec_file_attach (char *filename, int fr validate_files (); + /* Delete convenience variables BEFORE their types become invalid. + This way a note can be made of what the types of the permanent + variables were and they can recover later. */ + clear_internalvars (); + set_gdbarch_from_file (exec_bfd); push_target (&exec_ops); Index: src/gdb/symfile.c =================================================================== --- src.orig/gdb/symfile.c 2005-11-07 17:22:56.000000000 +0000 +++ src/gdb/symfile.c 2005-11-07 18:27:04.000000000 +0000 @@ -1268,6 +1268,11 @@ symbol_file_command (char *args, int fro { dont_repeat (); + /* Delete convenience variables BEFORE their types become invalid. + This way a note can be made of what the types of the permanent + variables were and they can recover later. */ + clear_internalvars (); + if (args == NULL) { symbol_file_clear (from_tty); Index: src/gdb/value.h =================================================================== --- src.orig/gdb/value.h 2005-11-07 18:27:03.000000000 +0000 +++ src/gdb/value.h 2005-11-07 18:27:04.000000000 +0000 @@ -246,6 +246,15 @@ struct internalvar char *name; struct value *value; int endian; + + /* 'temp' determines whether the variable will be destroyed when + new symtabs are loaded. */ + int temp; + /* These are used to fixup the type pointers after the symbol tables + are reloaded. */ + char type_needs_fixing; + char *type_name; + char *enclosing_type_name; }; Index: src/gdb/Makefile.in =================================================================== --- src.orig/gdb/Makefile.in 2005-11-07 18:27:03.000000000 +0000 +++ src/gdb/Makefile.in 2005-11-07 18:27:04.000000000 +0000 @@ -2735,7 +2735,7 @@ valprint.o: valprint.c $(defs_h) $(gdb_s value.o: value.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \ $(value_h) $(gdbcore_h) $(command_h) $(gdbcmd_h) $(target_h) \ $(language_h) $(scm_lang_h) $(demangle_h) $(doublest_h) \ - $(gdb_assert_h) $(regcache_h) $(block_h) + $(gdb_assert_h) $(regcache_h) $(block_h) $(exceptions_h) varobj.o: varobj.c $(defs_h) $(value_h) $(expression_h) $(frame_h) \ $(language_h) $(wrapper_h) $(gdbcmd_h) $(gdb_string_h) $(varobj_h) vaxbsd-nat.o: vaxbsd-nat.c $(defs_h) $(inferior_h) $(regcache_h) $(target_h) \ Index: src/gdb/doc/gdb.texinfo =================================================================== --- src.orig/gdb/doc/gdb.texinfo 2005-11-07 18:27:03.000000000 +0000 +++ src/gdb/doc/gdb.texinfo 2005-11-07 19:01:11.000000000 +0000 @@ -6120,6 +6120,11 @@ Using a convenience variable for the fir value is @code{void} until you assign a new value. You can alter the value with another assignment at any time. +@value{GDBN} commands that wipe the symbol table, such as @samp{file} and +@samp{symbol-file}, cause all convenience variables to be deleted - their +types are lost so their value would become meaningless. This can be avoided +using the @samp{keep-variable} command below. + Convenience variables have no fixed types. You can assign a convenience variable any type of value, including structures and arrays, even if that variable already has a value of a different type. The convenience @@ -6143,6 +6148,24 @@ override default values used in a comman If the variable is already defined then the expression is not evaluated so any side-effects do not occur. + +@kindex keep-variable +@cindex convenience variables, keeping +@item keep-variable $@var{variable} +Mark the variable to be preserved across commands which destroy the symbol +table, such as @samp{symbol-file}. + +The next time the variable is accessed (by whatever means) @value{GDBN} will +attempt to find a type, in the current symbol table, with the same name as +the type used previously. It does not check whether the new type is in any +way compatible with the old type. + +If a suitable type does not exist (at the time the variable is accessed) then +@value{GDBN} will print a message and reset the value to @code{void}. + +If the symbol table is destroyed and recreated multiple times, the content +of the intermediate tables has no effect, @emph{provided that the variable +is not accessed while they are loaded}. @end table One of the ways to use a convenience variable is as a counter to be