diff options
Diffstat (limited to 'gdb/RCS/printcmd.c,v')
-rw-r--r-- | gdb/RCS/printcmd.c,v | 1707 |
1 files changed, 1707 insertions, 0 deletions
diff --git a/gdb/RCS/printcmd.c,v b/gdb/RCS/printcmd.c,v new file mode 100644 index 0000000..2ab3acc --- /dev/null +++ b/gdb/RCS/printcmd.c,v @@ -0,0 +1,1707 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.04.26.00.54.22; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.04.25.15.38.49; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@(1) Depend on XXX_BIG_ENDIAN rather than testing gdb's object code. +(2) Print to the STREAM parameter, not to stdout. +(3) Use is_nan with new arguments. +(4) Use unpack_double with new args, and deal with invalid results. +(5) Don't print history numbers in the "print" command for things +that weren't recorded in history (because they were invalid values). +@ +text +@/* Print values for GNU debugger GDB. + Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include <stdio.h> +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "symtab.h" +#include "value.h" +#include "expression.h" + +struct format_data +{ + int count; + char format; + char size; +}; + +/* Last specified output format. */ + +static char last_format = 'x'; + +/* Last specified examination size. 'b', 'h', 'w' or `q'. */ + +static char last_size = 'w'; + +/* Default address to examine next. */ + +static CORE_ADDR next_address; + +/* Last address examined. */ + +static CORE_ADDR last_examine_address; + +/* Contents of last address examined. + This is not valid past the end of the `x' command! */ + +static value last_examine_value; + +/* Number of auto-display expression currently being displayed. + So that we can deleted it if we get an error or a signal within it. + -1 when not doing one. */ + +int current_display_number; + +static void do_one_display (); + +void do_displays (); +void print_address (); +void print_scalar_formatted (); + + +/* Decode a format specification. *STRING_PTR should point to it. + OFORMAT and OSIZE are used as defaults for the format and size + if none are given in the format specification. + The structure returned describes all the data + found in the specification. In addition, *STRING_PTR is advanced + past the specification and past all whitespace following it. */ + +struct format_data +decode_format (string_ptr, oformat, osize) + char **string_ptr; + char oformat; + char osize; +{ + struct format_data val; + register char *p = *string_ptr; + + val.format = oformat; + val.size = osize; + val.count = 1; + + if (*p >= '0' && *p <= '9') + val.count = atoi (p); + while (*p >= '0' && *p <= '9') p++; + + /* Now process size or format letters that follow. */ + + while (1) + { + if (*p == 'b' || *p == 'h' || *p == 'w' || *p == 'g') + val.size = *p++; +#ifdef LONG_LONG + else if (*p == 'l') + { + val.size = 'g'; + p++; + } +#endif + else if (*p >= 'a' && *p <= 'z') + val.format = *p++; + else + break; + } + +#ifndef LONG_LONG + /* Make sure 'g' size is not used on integer types. + Well, actually, we can handle hex. */ + if (val.size == 'g' && val.format != 'f' && val.format != 'x') + val.size = 'w'; +#endif + + while (*p == ' ' || *p == '\t') p++; + *string_ptr = p; + + return val; +} + +/* Print value VAL on stdout according to FORMAT, a letter or 0. + Do not end with a newline. + 0 means print VAL according to its own type. + SIZE is the letter for the size of datum being printed. + This is used to pad hex numbers so they line up. */ + +static void +print_formatted (val, format, size) + register value val; + register char format; + char size; +{ + int len = TYPE_LENGTH (VALUE_TYPE (val)); + + if (VALUE_LVAL (val) == lval_memory) + next_address = VALUE_ADDRESS (val) + len; + + switch (format) + { + case 's': + next_address = VALUE_ADDRESS (val) + + value_print (value_addr (val), stdout, 0); + break; + + case 'i': + next_address = VALUE_ADDRESS (val) + + print_insn (VALUE_ADDRESS (val), stdout); + break; + + default: + if (format == 0 + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_ARRAY + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_STRUCT + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_UNION + || VALUE_REPEATED (val)) + value_print (val, stdout, format); + else + print_scalar_formatted (VALUE_CONTENTS (val), VALUE_TYPE (val), + format, size, stdout); + } +} + +/* Print a scalar of data of type TYPE, pointed to in GDB by VALADDR, + according to letters FORMAT and SIZE on STREAM. + FORMAT may not be zero. Formats s and i are not supported at this level. + + This is how the elements of an array or structure are printed + with a format. */ + +void +print_scalar_formatted (valaddr, type, format, size, stream) + char *valaddr; + struct type *type; + char format; + int size; + FILE *stream; +{ + LONGEST val_long; + int len = TYPE_LENGTH (type); + + if (size == 'g' && sizeof (LONGEST) < 8 + && format == 'x') + { + /* ok, we're going to have to get fancy here. Assumption: a + long is four bytes. */ + unsigned long v1, v2, tmp; + + v1 = unpack_long (builtin_type_long, valaddr); + v2 = unpack_long (builtin_type_long, valaddr + 4); + +#ifdef BYTES_BIG_ENDIAN +#else + /* Little endian -- swap the two for printing */ + tmp = v1; + v1 = v2; + v2 = tmp; +#endif + + switch (format) + { + case 'x': + fprintf (stream, "0x%08x%08x", v1, v2); + break; + default: + error ("Output size \"g\" unimplemented for format \"%c\".", + format); + } + return; + } + + val_long = unpack_long (type, valaddr); + + /* If value is unsigned, truncate it in case negative. */ + if (format != 'd') + { + if (len == sizeof (char)) + val_long &= (1 << 8 * sizeof(char)) - 1; + else if (len == sizeof (short)) + val_long &= (1 << 8 * sizeof(short)) - 1; + else if (len == sizeof (long)) + val_long &= (unsigned long) - 1; + } + + switch (format) + { + case 'x': +#ifdef LONG_LONG + if (!size) + size = (len < sizeof (long long) ? 'w' : 'g'); + switch (size) + { + case 'b': + fprintf (stream, "0x%02llx", val_long); + break; + case 'h': + fprintf (stream, "0x%04llx", val_long); + break; + case 0: /* no size specified, like in print */ + case 'w': + fprintf (stream, "0x%08llx", val_long); + break; + case 'g': + fprintf (stream, "0x%16llx", val_long); + break; + default: + error ("Undefined output size \"%c\".", size); + } +#else + switch (size) + { + case 'b': + fprintf (stream, "0x%02x", val_long); + break; + case 'h': + fprintf (stream, "0x%04x", val_long); + break; + case 0: /* no size specified, like in print */ + case 'w': + fprintf (stream, "0x%08x", val_long); + break; + case 'g': + fprintf (stream, "0x%16x", val_long); + break; + default: + error ("Undefined output size \"%c\".", size); + } +#endif /* not LONG_LONG */ + break; + + case 'd': +#ifdef LONG_LONG + fprintf (stream, "%lld", val_long); +#else + fprintf (stream, "%d", val_long); +#endif + break; + + case 'u': +#ifdef LONG_LONG + fprintf (stream, "%llu", val_long); +#else + fprintf (stream, "%u", val_long); +#endif + break; + + case 'o': + if (val_long) +#ifdef LONG_LONG + fprintf (stream, "0%llo", val_long); +#else + fprintf (stream, "0%o", val_long); +#endif + else + fprintf (stream, "0"); + break; + + case 'a': + print_address ((CORE_ADDR) val_long, stream); + break; + + case 'c': + value_print (value_from_long (builtin_type_char, val_long), stream, 0); + break; + + case 'f': + if (len == sizeof (float)) + type = builtin_type_float; + else if (len == sizeof (double)) + type = builtin_type_double; + else abort(); + +#ifdef IEEE_FLOAT + if (is_nan (valaddr, len)) + { + fprintf (stream, "NaN"); + break; + } +#endif + { + double doub; + int inv; + + doub = unpack_double (type, valaddr, &inv); + if (inv) + fprintf (stream, "Invalid float value"); + else + fprintf (stream, len > 4? "%.16g": "%.6g", doub); + } + break; + + case 0: + abort (); + + default: + error ("Undefined output format \"%c\".", format); + } +} + +/* Specify default address for `x' command. + `info lines' uses this. */ + +void +set_next_address (addr) + CORE_ADDR addr; +{ + next_address = addr; + + /* Make address available to the user as $_. */ + set_internalvar (lookup_internalvar ("_"), + value_from_long (builtin_type_int, (LONGEST) addr)); +} + +/* Print address ADDR symbolically on STREAM. + First print it as a number. Then perhaps print + <SYMBOL + OFFSET> after the number. */ + +void +print_address (addr, stream) + CORE_ADDR addr; + FILE *stream; +{ + register int i; + struct symbol *fs; + char *name; + int name_location; + + fprintf (stream, "0x%x", addr); + + fs = find_pc_function (addr); + + if (!fs) + { + i = find_pc_misc_function (addr); + + if (i < 0) return; /* If nothing comes through, don't + print anything symbolic */ + + name = misc_function_vector[i].name; + name_location = misc_function_vector[i].address; + } + else + { + name = fs->name; + name_location = BLOCK_START (SYMBOL_BLOCK_VALUE (fs)); + } + + if (addr - name_location) + fprintf (stream, " <%s+%d>", + name, + addr - name_location); + else + fprintf (stream, " <%s>", name); +} + +/* Examine data at address ADDR in format FMT. + Fetch it from memory and print on stdout. */ + +static void +do_examine (fmt, addr) + struct format_data fmt; + CORE_ADDR addr; +{ + register char format = 0; + register char size; + register int count = 1; + struct type *val_type; + register int i; + register int maxelts; + + format = fmt.format; + size = fmt.size; + count = fmt.count; + next_address = addr; + + /* String or instruction format implies fetch single bytes + regardless of the specified size. */ + if (format == 's' || format == 'i') + size = 'b'; + + if (size == 'b') + val_type = builtin_type_char; + else if (size == 'h') + val_type = builtin_type_short; + else if (size == 'w') + val_type = builtin_type_long; + else if (size == 'g') +#ifndef LONG_LONG + val_type = builtin_type_double; +#else + val_type = builtin_type_long_long; +#endif + + maxelts = 8; + if (size == 'w') + maxelts = 4; + if (size == 'g') + maxelts = 2; + if (format == 's' || format == 'i') + maxelts = 1; + + /* Print as many objects as specified in COUNT, at most maxelts per line, + with the address of the next one at the start of each line. */ + + while (count > 0) + { + print_address (next_address, stdout); + fputc (':', stdout); + for (i = maxelts; + i > 0 && count > 0; + i--, count--) + { + fputc ('\t', stdout); + /* Note that print_formatted sets next_address for the next + object. */ + last_examine_address = next_address; + last_examine_value = value_at (val_type, next_address); + print_formatted (last_examine_value, format, size); + } + fputc ('\n', stdout); + fflush (stdout); + } +} + +static void +validate_format (fmt, cmdname) + struct format_data fmt; + char *cmdname; +{ + if (fmt.size != 0) + error ("Size letters are meaningless in \"%s\" command.", cmdname); + if (fmt.count != 1) + error ("Item count other than 1 is meaningless in \"%s\" command.", + cmdname); + if (fmt.format == 'i' || fmt.format == 's') + error ("Format letter \"%c\" is meaningless in \"%s\" command.", + fmt.format, cmdname); +} + +static void +print_command (exp) + char *exp; +{ + struct expression *expr; + register struct cleanup *old_chain = 0; + register char format = 0; + register value val; + struct format_data fmt; + int histindex; + int cleanup = 0; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, 0); + validate_format (fmt, "print"); + last_format = format = fmt.format; + } + + if (exp && *exp) + { + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + cleanup = 1; + val = evaluate_expression (expr); + } + else + val = access_value_history (0); + + histindex = record_latest_value (val); + if (histindex >= 0) printf ("$%d = ", histindex); + + print_formatted (val, format, fmt.size); + printf ("\n"); + + if (cleanup) + do_cleanups (old_chain); +} + +static void +output_command (exp) + char *exp; +{ + struct expression *expr; + register struct cleanup *old_chain; + register char format = 0; + register value val; + struct format_data fmt; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, 0, 0); + validate_format (fmt, "print"); + format = fmt.format; + } + + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + + val = evaluate_expression (expr); + + print_formatted (val, format, fmt.size); + + do_cleanups (old_chain); +} + +static void +set_command (exp) + char *exp; +{ + struct expression *expr = parse_c_expression (exp); + register struct cleanup *old_chain + = make_cleanup (free_current_contents, &expr); + evaluate_expression (expr); + do_cleanups (old_chain); +} + +static void +address_info (exp) + char *exp; +{ + register struct symbol *sym; + register CORE_ADDR val; + int is_a_field_of_this; /* C++: lookup_symbol sets this to nonzero + if exp is a field of `this'. */ + + if (exp == 0) + error ("Argument required."); + + sym = lookup_symbol (exp, get_selected_block (), VAR_NAMESPACE, + &is_a_field_of_this); + if (sym == 0) + { + register int i; + + if (is_a_field_of_this) + { + printf("Symbol \"%s\" is a field of the local class variable `this'\n", exp); + return; + } + + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, exp)) + break; + + if (i < misc_function_count) + printf ("Symbol \"%s\" is at 0x%x in a file compiled without -g.\n", + exp, misc_function_vector[i].address); + else + error ("No symbol \"%s\" in current context.", exp); + return; + } + + printf ("Symbol \"%s\" is ", SYMBOL_NAME (sym)); + val = SYMBOL_VALUE (sym); + + switch (SYMBOL_CLASS (sym)) + { + case LOC_CONST: + case LOC_CONST_BYTES: + printf ("constant"); + break; + + case LOC_LABEL: + printf ("a label at address 0x%x", val); + break; + + case LOC_REGISTER: + printf ("a variable in register %s", reg_names[val]); + break; + + case LOC_STATIC: + printf ("static at address 0x%x", val); + break; + + case LOC_REGPARM: + printf ("an argument in register %s", reg_names[val]); + break; + + case LOC_ARG: + printf ("an argument at offset %d", val); + break; + + case LOC_LOCAL: + printf ("a local variable at frame offset %d", val); + break; + + case LOC_TYPEDEF: + printf ("a typedef"); + break; + + case LOC_BLOCK: + printf ("a function at address 0x%x", + BLOCK_START (SYMBOL_BLOCK_VALUE (sym))); + break; + } + printf (".\n"); +} + +static void +x_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct expression *expr; + struct format_data fmt; + struct cleanup *old_chain; + + fmt.format = last_format; + fmt.size = last_size; + fmt.count = 1; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, last_size); + last_size = fmt.size; + last_format = fmt.format; + } + + /* If we have an expression, evaluate it and use it as the address. */ + + if (exp != 0 && *exp != 0) + { + expr = parse_c_expression (exp); + /* Cause expression not to be there any more + if this command is repeated with Newline. + But don't clobber a user-defined command's definition. */ + if (from_tty) + *exp = 0; + old_chain = make_cleanup (free_current_contents, &expr); + next_address = (CORE_ADDR) value_as_long (evaluate_expression (expr)); + do_cleanups (old_chain); + } + + do_examine (fmt, next_address); + + /* Set a couple of internal variables if appropriate. */ + if (last_examine_value) + { + /* Make last address examined available to the user as $_. */ + set_internalvar (lookup_internalvar ("_"), + value_from_long (builtin_type_int, + (LONGEST) last_examine_address)); + + /* Make contents of last address examined available to the user as $__.*/ + set_internalvar (lookup_internalvar ("__"), last_examine_value); + } +} + +/* Commands for printing types of things. */ + +static void +whatis_command (exp) + char *exp; +{ + struct expression *expr; + register value val; + register struct cleanup *old_chain; + + if (exp) + { + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + val = evaluate_type (expr); + } + else + val = access_value_history (0); + + printf ("type = "); + type_print (VALUE_TYPE (val), "", stdout, 1); + printf ("\n"); + + if (exp) + do_cleanups (old_chain); +} + +static void +ptype_command (typename) + char *typename; +{ + register char *p = typename; + register int len; + extern struct block *get_current_block (); + register struct block *b + = (have_inferior_p () || have_core_file_p ()) ? get_current_block () : 0; + register struct type *type; + + if (typename == 0) + error_no_arg ("type name"); + + while (*p && *p != ' ' && *p != '\t') p++; + len = p - typename; + while (*p == ' ' || *p == '\t') p++; + + if (len == 6 && !strncmp (typename, "struct", 6)) + type = lookup_struct (p, b); + else if (len == 5 && !strncmp (typename, "union", 5)) + type = lookup_union (p, b); + else if (len == 4 && !strncmp (typename, "enum", 4)) + type = lookup_enum (p, b); + else + { + type = lookup_typename (typename, b, 1); + if (type == 0) + { + register struct symbol *sym + = lookup_symbol (typename, b, STRUCT_NAMESPACE, 0); + if (sym == 0) + error ("No type named %s.", typename); + printf ("No type named %s, but there is a ", + typename); + switch (TYPE_CODE (SYMBOL_TYPE (sym))) + { + case TYPE_CODE_STRUCT: + printf ("struct"); + break; + + case TYPE_CODE_UNION: + printf ("union"); + break; + + case TYPE_CODE_ENUM: + printf ("enum"); + } + printf (" %s. Type \"help ptype\".\n", typename); + type = SYMBOL_TYPE (sym); + } + } + + type_print (type, "", stdout, 1); + printf ("\n"); +} + +enum display_status {disabled, enabled}; + +struct display +{ + /* Chain link to next auto-display item. */ + struct display *next; + /* Expression to be evaluated and displayed. */ + struct expression *exp; + /* Item number of this auto-display item. */ + int number; + /* Display format specified. */ + struct format_data format; + /* Innermost block required by this expression when evaluated */ + struct block *block; + /* Status of this display (enabled or disabled) */ + enum display_status status; +}; + +/* Chain of expressions whose values should be displayed + automatically each time the program stops. */ + +static struct display *display_chain; + +static int display_number; + +/* Add an expression to the auto-display chain. + Specify the expression. */ + +static void +display_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct format_data fmt; + register struct expression *expr; + register struct display *new; + extern struct block *innermost_block; + + if (exp == 0) + { + do_displays (); + return; + } + + if (*exp == '/') + { + exp++; + fmt = decode_format (&exp, 0, 0); + if (fmt.size && fmt.format == 0) + fmt.format = 'x'; + if (fmt.format == 'i' || fmt.format == 's') + fmt.size = 'b'; + } + else + { + fmt.format = 0; + fmt.size = 0; + fmt.count = 0; + } + + innermost_block = 0; + expr = parse_c_expression (exp); + + new = (struct display *) xmalloc (sizeof (struct display)); + + new->exp = expr; + new->block = innermost_block; + new->next = display_chain; + new->number = ++display_number; + new->format = fmt; + new->status = enabled; + display_chain = new; + + if (from_tty && have_inferior_p ()) + do_one_display (new); + + dont_repeat (); +} + +static void +free_display (d) + struct display *d; +{ + free (d->exp); + free (d); +} + +/* Clear out the display_chain. + Done when new symtabs are loaded, since this invalidates + the types stored in many expressions. */ + +void +clear_displays () +{ + register struct display *d; + + while (d = display_chain) + { + free (d->exp); + display_chain = d->next; + free (d); + } +} + +/* Delete the auto-display number NUM. */ + +void +delete_display (num) + int num; +{ + register struct display *d1, *d; + + if (!display_chain) + error ("No display number %d.", num); + + if (display_chain->number == num) + { + d1 = display_chain; + display_chain = d1->next; + free_display (d1); + } + else + for (d = display_chain; ; d = d->next) + { + if (d->next == 0) + error ("No display number %d.", num); + if (d->next->number == num) + { + d1 = d->next; + d->next = d1->next; + free_display (d1); + break; + } + } +} + +/* Delete some values from the auto-display chain. + Specify the element numbers. */ + +static void +undisplay_command (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d, *d1; + + if (args == 0) + { + if (query ("Delete all auto-display expressions? ")) + clear_displays (); + dont_repeat (); + return; + } + + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + delete_display (num); + + p = p1; + while (*p == ' ' || *p == '\t') p++; + } + dont_repeat (); +} + +/* Display a single auto-display. + Do nothing if the display cannot be printed in the current context, + or if the display is disabled. */ + +static void +do_one_display (d) + struct display *d; +{ + int within_current_scope; + + if (d->status == disabled) + return; + + if (d->block) + within_current_scope = contained_in (get_selected_block (), d->block); + else + within_current_scope = 1; + if (!within_current_scope) + return; + + current_display_number = d->number; + + printf ("%d: ", d->number); + if (d->format.size) + { + printf ("x/"); + if (d->format.count != 1) + printf ("%d", d->format.count); + printf ("%c", d->format.format); + if (d->format.format != 'i' && d->format.format != 's') + printf ("%c", d->format.size); + printf (" "); + print_expression (d->exp, stdout); + if (d->format.count != 1) + printf ("\n"); + else + printf (" "); + do_examine (d->format, + (CORE_ADDR) value_as_long (evaluate_expression (d->exp))); + + } + else + { + if (d->format.format) + printf ("/%c ", d->format.format); + print_expression (d->exp, stdout); + printf (" = "); + print_formatted (evaluate_expression (d->exp), + d->format.format, d->format.size); + printf ("\n"); + } + + fflush (stdout); + current_display_number = -1; +} + +/* Display all of the values on the auto-display chain which can be + evaluated in the current scope. */ + +void +do_displays () +{ + register struct display *d; + + for (d = display_chain; d; d = d->next) + do_one_display (d); +} + +/* Delete the auto-display which we were in the process of displaying. + This is done when there is an error or a signal. */ + +void +delete_current_display () +{ + if (current_display_number >= 0) + { + delete_display (current_display_number); + fprintf (stderr, "Deleting display %d to avoid infinite recursion.\n", + current_display_number); + } + current_display_number = -1; +} + +static void +display_info () +{ + register struct display *d; + + if (!display_chain) + printf ("There are no auto-display expressions now.\n"); + else + printf ("Auto-display expressions now in effect:\n\ +Num Enb Expression\n"); + + for (d = display_chain; d; d = d->next) + { + printf ("%d: %c ", d->number, "ny"[(int)d->status]); + if (d->format.size) + printf ("/%d%c%c ", d->format.count, d->format.size, + d->format.format); + else if (d->format.format) + printf ("/%c ", d->format.format); + print_expression (d->exp, stdout); + if (d->block && !contained_in (get_selected_block (), d->block)) + printf (" (cannot be evaluated in the current context)"); + printf ("\n"); + fflush (stdout); + } +} + +void +enable_display (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d->next) + d->status = enabled; + } + else + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + for (d = display_chain; d; d = d->next) + if (d->number == num) + { + d->status = enabled; + goto win; + } + printf ("No display number %d.\n", num); + win: + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} + +void +disable_display (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d->next) + d->status = disabled; + } + else + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + for (d = display_chain; d; d = d->next) + if (d->number == num) + { + d->status = disabled; + goto win; + } + printf ("No display number %d.\n", num); + win: + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} + + +/* Print the value in stack frame FRAME of a variable + specified by a struct symbol. */ + +void +print_variable_value (var, frame, stream) + struct symbol *var; + CORE_ADDR frame; + FILE *stream; +{ + value val = read_var_value (var, frame); + value_print (val, stream, 0); +} + +/* Print the arguments of a stack frame, given the function FUNC + running in that frame (as a symbol), the info on the frame, + and the number of args according to the stack frame (or -1 if unknown). */ + +static void print_frame_nameless_args (); + +void +print_frame_args (func, fi, num, stream) + struct symbol *func; + struct frame_info *fi; + int num; + FILE *stream; +{ + struct block *b; + int nsyms = 0; + int first = 1; + register int i; + register int last_offset = FRAME_ARGS_SKIP; + register int last_regparm = 0; + register struct symbol *lastsym, *sym, *nextsym; + register value val; + register CORE_ADDR addr = FRAME_ARGS_ADDRESS (fi); + + if (func) + { + b = SYMBOL_BLOCK_VALUE (func); + nsyms = BLOCK_NSYMS (b); + } + + lastsym = 0; + while (1) + { + /* Find first arg that is not before LAST_OFFSET. */ + nextsym = 0; + for (i = 0; i < nsyms; i++) + { + QUIT; + sym = BLOCK_SYM (b, i); + if (SYMBOL_CLASS (sym) == LOC_ARG) + { + if (SYMBOL_VALUE (sym) >= last_offset + && (nextsym == 0 + || SYMBOL_VALUE (sym) < SYMBOL_VALUE (nextsym))) + nextsym = sym; + } + else if (SYMBOL_CLASS (sym) == LOC_REGPARM) + { + /* This shouldn't be sorted by number. Since we can't + find nameless args with register parameters, print + this out in order by .stabs. */ + if (sym > lastsym && nextsym == 0) + nextsym = sym; + } + } + if (nextsym == 0) + break; + sym = nextsym; + /* Print any nameless args between the last arg printed + and the next arg. */ + if (SYMBOL_CLASS (sym) == LOC_ARG + && last_offset != (SYMBOL_VALUE (sym) / sizeof (int)) * sizeof (int)) + { + print_frame_nameless_args (addr, last_offset, SYMBOL_VALUE (sym), + stream); + first = 0; + } + /* Print the next arg. */ + if (SYMBOL_CLASS (sym) == LOC_REGPARM) + val = value_from_register (SYMBOL_TYPE (sym), + SYMBOL_VALUE (sym), + FRAME_INFO_ID (fi)); + else + val = value_at (SYMBOL_TYPE (sym), addr + SYMBOL_VALUE (sym)); + + if (! first) + fprintf (stream, ", "); + fprintf (stream, "%s=", SYMBOL_NAME (sym)); + value_print (val, stream, 0); + first = 0; + if (SYMBOL_CLASS (sym) == LOC_ARG) + last_offset = SYMBOL_VALUE (sym) + TYPE_LENGTH (SYMBOL_TYPE (sym)); + else + { + last_regparm = SYMBOL_VALUE (sym) + 1; + last_offset += TYPE_LENGTH (SYMBOL_TYPE (sym)); + } + + /* Round up address of next arg to multiple of size of int. */ + last_offset + = ((last_offset + sizeof (int) - 1) / sizeof (int)) * sizeof (int); + lastsym = sym; + } + if (num >= 0 && num * sizeof (int) + FRAME_ARGS_SKIP > last_offset) + print_frame_nameless_args (addr, last_offset, + num * sizeof (int) + FRAME_ARGS_SKIP, stream); +} + +static void +print_frame_nameless_args (argsaddr, start, end, stream) + CORE_ADDR argsaddr; + int start; + int end; + FILE *stream; +{ + while (start < end) + { + QUIT; + if (start != FRAME_ARGS_SKIP) + fprintf (stream, ", "); + fprintf (stream, "%d", + read_memory_integer (argsaddr + start, sizeof (int))); + start += sizeof (int); + } +} + +static void +printf_command (arg) + char *arg; +{ + register char *f; + register char *s = arg; + char *string; + value *val_args; + int nargs = 0; + int allocated_args = 20; + char *arg_bytes; + char *argclass; + int i; + int argindex; + int nargs_wanted; + + val_args = (value *) xmalloc (allocated_args * sizeof (value)); + + if (s == 0) + error_no_arg ("format-control string and values to print"); + + /* Skip white space before format string */ + while (*s == ' ' || *s == '\t') s++; + + /* A format string should follow, enveloped in double quotes */ + if (*s++ != '"') + error ("Bad format string, missing '\"'."); + + /* Parse the format-control string and copy it into the string STRING, + processing some kinds of escape sequence. */ + + f = string = (char *) alloca (strlen (s) + 1); + while (*s != '"') + { + int c = *s++; + switch (c) + { + case '\0': + error ("Bad format string, non-terminated '\"'."); + /* doesn't return */ + + case '\\': + switch (c = *s++) + { + case '\\': + *f++ = '\\'; + break; + case 'n': + *f++ = '\n'; + break; + case 't': + *f++ = '\t'; + break; + case 'r': + *f++ = '\r'; + break; + case '"': + *f++ = '"'; + break; + default: + /* ??? TODO: handle other escape sequences */ + error ("Unrecognized \\ escape character in format string."); + } + break; + + default: + *f++ = c; + } + } + + /* Skip over " and following space and comma. */ + s++; + *f++ = '\0'; + while (*s == ' ' || *s == '\t') s++; + + if (*s != ',' && *s != 0) + error ("Invalid argument syntax"); + + if (*s == ',') s++; + while (*s == ' ' || *s == '\t') s++; + + /* Now scan the string for %-specs and see what kinds of args they want. + argclass[I] is set to 1 if the Ith arg should be a string. + It's set to 2 if the Ith arg should be floating point. */ + + argclass = (char *) alloca (strlen (s)); + nargs_wanted = 0; + f = string; + while (*f) + if (*f++ == '%') + { + while (index ("0123456789.hlL-+ #", *f)) f++; + if (*f == 's') + argclass[nargs_wanted++] = 1; + else if (*f == 'e' || *f == 'f' || *f == 'g') + argclass[nargs_wanted++] = 2; + else if (*f != '%') + argclass[nargs_wanted++] = 0; + f++; + } + + /* Now, parse all arguments and evaluate them. + Store the VALUEs in VAL_ARGS. */ + + while (*s != '\0') + { + char *s1; + if (nargs == allocated_args) + val_args = (value *) xrealloc (val_args, + (allocated_args *= 2) + * sizeof (value)); + s1 = s; + val_args[nargs] = parse_to_comma_and_eval (&s1); + + /* If format string wants a float, unchecked-convert the value to + floating point of the same size */ + + if (argclass[nargs] == 2) + { + argclass[nargs] = 0; + if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (float)) + VALUE_TYPE (val_args[nargs]) = builtin_type_float; + if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (double)) + VALUE_TYPE (val_args[nargs]) = builtin_type_double; + } + nargs++; + s = s1; + if (*s == ',') + s++; + } + + if (nargs != nargs_wanted) + error ("Wrong number of arguments for specified format-string"); + + /* Now lay out an argument-list containing the arguments + as doubles, integers and C pointers. */ + + arg_bytes = (char *) alloca (sizeof (double) * nargs); + argindex = 0; + for (i = 0; i < nargs; i++) + { + if (argclass[i]) + { + char *str; + int tem, j; + tem = value_as_long (val_args[i]); + + /* This is a %s argument. Find the length of the string. */ + for (j = 0; ; j++) + { + char c; + QUIT; + read_memory (tem + j, &c, 1); + if (c == 0) + break; + } + + /* Copy the string contents into a string inside GDB. */ + str = (char *) alloca (j + 1); + read_memory (tem, str, j); + str[j] = 0; + + /* Pass address of internal copy as the arg to vprintf. */ + *((int *) &arg_bytes[argindex]) = (int) str; + argindex += sizeof (int); + } + else if (VALUE_TYPE (val_args[i])->code == TYPE_CODE_FLT) + { + *((double *) &arg_bytes[argindex]) = value_as_double (val_args[i]); + argindex += sizeof (double); + } + else +#ifdef LONG_LONG + if (TYPE_LENGTH (VALUE_TYPE (val_args[i])) == sizeof (long long)) + { + *(long long *) &arg_bytes[argindex] = value_as_long (val_args[i]); + argindex += sizeof (long long); + } + else +#endif + { + *((int *) &arg_bytes[argindex]) = value_as_long (val_args[i]); + argindex += sizeof (int); + } + } + + vprintf (string, arg_bytes); +} + +extern struct cmd_list_element *enablelist, *disablelist, *deletelist; +extern struct cmd_list_element *cmdlist, *setlist; + +void +_initialize_printcmd () +{ + current_display_number = -1; + + add_info ("address", address_info, + "Describe where variable VAR is stored."); + + add_com ("x", class_vars, x_command, + "Examine memory: x/FMT ADDRESS.\n\ +ADDRESS is an expression for the memory address to examine.\n\ +FMT is a repeat count followed by a format letter and a size letter.\n\ +Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\ + f(float), a(address), i(instruction), c(char) and s(string).\n\ +Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\ + g is meaningful only with f, for type double.\n\ +The specified number of objects of the specified size are printed\n\ +according to the format.\n\n\ +Defaults for format and size letters are those previously used.\n\ +Default count is 1. Default address is following last thing printed\n\ +with this command or \"print\"."); + + add_com ("ptype", class_vars, ptype_command, + "Print definition of type TYPE.\n\ +Argument may be a type name defined by typedef, or \"struct STRUCTNAME\"\n\ +or \"union UNIONNAME\" or \"enum ENUMNAME\".\n\ +The selected stack frame's lexical context is used to look up the name."); + + add_com ("whatis", class_vars, whatis_command, + "Print data type of expression EXP."); + + add_info ("display", display_info, + "Expressions to display when program stops, with code numbers."); + + add_abbrev_cmd ("undisplay", class_vars, undisplay_command, + "Cancel some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means cancel all automatic-display expressions.\n\ +\"delete display\" has the same effect as this command.\n\ +Do \"info display\" to see current list of code numbers.", + &cmdlist); + + add_com ("display", class_vars, display_command, + "Print value of expression EXP each time the program stops.\n\ +/FMT may be used before EXP as in the \"print\" command.\n\ +/FMT \"i\" or \"s\" or including a size-letter is allowed,\n\ +as in the \"x\" command, and then EXP is used to get the address to examine\n\ +and examining is done as in the \"x\" command.\n\n\ +With no argument, display all currently requested auto-display expressions.\n\ +Use \"undisplay\" to cancel display requests previously made."); + + add_cmd ("display", class_vars, enable_display, + "Enable some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to resume displaying.\n\ +No argument means enable all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &enablelist); + + add_cmd ("display", class_vars, disable_display, + "Disable some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means disable all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &disablelist); + + add_cmd ("display", class_vars, undisplay_command, + "Cancel some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means cancel all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &deletelist); + + add_com ("printf", class_vars, printf_command, + "printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\ +This is useful for formatted output in user-defined commands."); + add_com ("output", class_vars, output_command, + "Like \"print\" but don't put in value history and don't print newline.\n\ +This is useful in user-defined commands."); + + add_prefix_cmd ("set", class_vars, set_command, +"Perform an assignment VAR = EXP.\n\ +You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\ +(names starting with $), a register (a few standard names starting with $),\n\ +or an actual variable in the program being debugged. EXP is any expression.\n\ +Use \"set variable\" for variables with names identical to set subcommands.\n\ +\nWith a subcommand, this command modifies parts of the gdb environment", + &setlist, "set ", 1, &cmdlist); + + add_cmd ("variable", class_vars, set_command, + "Perform an assignment VAR = EXP.\n\ +You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\ +(names starting with $), a register (a few standard names starting with $),\n\ +or an actual variable in the program being debugged. EXP is any expression.\n\ +This may usually be abbreviated to simply \"set\".", + &setlist); + + add_com ("print", class_vars, print_command, + concat ("Print value of expression EXP.\n\ +Variables accessible are those of the lexical environment of the selected\n\ +stack frame, plus all those whose scope is global or an entire file.\n\ +\n\ +$NUM gets previous value number NUM. $ and $$ are the last two values.\n\ +$$NUM refers to NUM'th value back from the last one.\n\ +Names starting with $ refer to registers (with the values they would have\n\ +if the program were to return to the stack frame now selected, restoring\n\ +all registers saved by frames farther in) or else to debugger\n\ +\"convenience\" variables (any such name not a known register).\n\ +Use assignment expressions to give values to convenience variables.\n", + "\n\ +\{TYPE}ADREXP refers to a datum of data type TYPE, located at address ADREXP.\n\ +@@ is a binary operator for treating consecutive data objects\n\ +anywhere in memory as an array. FOO@@NUM gives an array whose first\n\ +element is FOO, whose second element is stored in the space following\n\ +where FOO is stored, etc. FOO must be an expression whose value\n\ +resides in memory.\n", + "\n\ +EXP may be preceded with /FMT, where FMT is a format letter\n\ +but no count or size letter (see \"x\" command).")); + add_com_alias ("p", "print", class_vars, 1); +} + +@ + + +1.1 +log +@Initial revision +@ +text +@a191 1 + int bigendian = 0; +d196 7 +a202 5 + { + union { + char a, b, c, d; + long i; + } x; +a203 10 + x.i = 1; + if (x.a) + { + /* Little endian */ + tmp = v1; + v1 = v2; + v2 = tmp; + } + } + +d207 1 +a207 1 + printf ("0x%08x%08x", v1, v2); +d238 1 +a238 1 + printf ("0x%02llx", val_long); +d241 1 +a241 1 + printf ("0x%04llx", val_long); +d245 1 +a245 1 + printf ("0x%08llx", val_long); +d248 1 +a248 1 + printf ("0x%16llx", val_long); +d257 1 +a257 1 + printf ("0x%02x", val_long); +d260 1 +a260 1 + printf ("0x%04x", val_long); +d264 1 +a264 1 + printf ("0x%08x", val_long); +d267 1 +a267 1 + printf ("0x%16x", val_long); +d277 1 +a277 1 + printf ("%lld", val_long); +d279 1 +a279 1 + printf ("%d", val_long); +d285 1 +a285 1 + printf ("%llu", val_long); +d287 1 +a287 1 + printf ("%u", val_long); +d294 1 +a294 1 + printf ("0%llo", val_long); +d296 1 +a296 1 + printf ("0%o", val_long); +d299 1 +a299 1 + printf ("0"); +d313 1 +a313 1 + if (len == sizeof (double)) +d315 2 +d318 1 +a318 1 + if (is_nan (unpack_double (type, valaddr))) +d320 1 +a320 1 + printf ("Nan"); +d324 10 +a333 4 + if (len > 4) + printf ("%.16g", unpack_double (type, valaddr)); + else + printf ("%.6g", unpack_double (type, valaddr)); +d515 1 +a515 1 + printf ("$%d = ", histindex); +@ |