From 148ea30ef6e624235f31f27ee7a285c7529eb68a Mon Sep 17 00:00:00 2001 From: nobody <> Date: Fri, 20 Sep 2002 14:59:00 +0000 Subject: This commit was manufactured by cvs2svn to create branch 'carlton_dictionary-branch'. Cherrypick from master 2002-09-20 14:58:59 UTC Fernando Nasser ' * source.c: Make global variables current_source_symtab and': gdb/breakpoint.c gdb/mi/ChangeLog gdb/printcmd.c gdb/stack.c gdb/tracepoint.c gdb/valops.c Cherrypick from gdb_5_3-branch 2002-09-03 22:29:15 UTC nobody 'This commit was manufactured by cvs2svn to create branch 'gdb_5_3-branch'.': gdb/mi/mi-cmd-stack.c gdb/objfiles.c --- gdb/breakpoint.c | 7823 +++++++++++++++++++++++++++++++++++++++++++++++++ gdb/mi/ChangeLog | 1836 ++++++++++++ gdb/mi/mi-cmd-stack.c | 309 ++ gdb/objfiles.c | 1011 +++++++ gdb/printcmd.c | 2572 ++++++++++++++++ gdb/stack.c | 2023 +++++++++++++ gdb/tracepoint.c | 2812 ++++++++++++++++++ gdb/valops.c | 3536 ++++++++++++++++++++++ 8 files changed, 21922 insertions(+) create mode 100644 gdb/breakpoint.c create mode 100644 gdb/mi/ChangeLog create mode 100644 gdb/mi/mi-cmd-stack.c create mode 100644 gdb/objfiles.c create mode 100644 gdb/printcmd.c create mode 100644 gdb/stack.c create mode 100644 gdb/tracepoint.c create mode 100644 gdb/valops.c diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c new file mode 100644 index 0000000..caa8f1c --- /dev/null +++ b/gdb/breakpoint.c @@ -0,0 +1,7823 @@ +/* Everything about breakpoints, for GDB. + + Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, + 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 Free Software + Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include +#include "symtab.h" +#include "frame.h" +#include "breakpoint.h" +#include "gdbtypes.h" +#include "expression.h" +#include "gdbcore.h" +#include "gdbcmd.h" +#include "value.h" +#include "command.h" +#include "inferior.h" +#include "gdbthread.h" +#include "target.h" +#include "language.h" +#include "gdb_string.h" +#include "demangle.h" +#include "annotate.h" +#include "symfile.h" +#include "objfiles.h" +#include "source.h" +#include "linespec.h" +#include "completer.h" +#include "gdb.h" +#include "ui-out.h" +#include "cli/cli-script.h" + +#include "gdb-events.h" + +/* Prototypes for local functions. */ + +static void until_break_command_continuation (struct continuation_arg *arg); + +static void catch_command_1 (char *, int, int); + +static void enable_delete_command (char *, int); + +static void enable_delete_breakpoint (struct breakpoint *); + +static void enable_once_command (char *, int); + +static void enable_once_breakpoint (struct breakpoint *); + +static void disable_command (char *, int); + +static void enable_command (char *, int); + +static void map_breakpoint_numbers (char *, void (*)(struct breakpoint *)); + +static void ignore_command (char *, int); + +static int breakpoint_re_set_one (PTR); + +static void clear_command (char *, int); + +static void catch_command (char *, int); + +static void handle_gnu_4_16_catch_command (char *, int, int); + +static struct symtabs_and_lines get_catch_sals (int); + +static void watch_command (char *, int); + +static int can_use_hardware_watchpoint (struct value *); + +extern void break_at_finish_command (char *, int); +extern void break_at_finish_at_depth_command (char *, int); + +extern void tbreak_at_finish_command (char *, int); + +static void break_command_1 (char *, int, int); + +static void mention (struct breakpoint *); + +struct breakpoint *set_raw_breakpoint (struct symtab_and_line, enum bptype); + +static void check_duplicates (struct breakpoint *); + +static void describe_other_breakpoints (CORE_ADDR, asection *); + +static void breakpoints_info (char *, int); + +static void breakpoint_1 (int, int); + +static bpstat bpstat_alloc (struct breakpoint *, bpstat); + +static int breakpoint_cond_eval (PTR); + +static void cleanup_executing_breakpoints (PTR); + +static void commands_command (char *, int); + +static void condition_command (char *, int); + +static int get_number_trailer (char **, int); + +void set_breakpoint_count (int); + +typedef enum + { + mark_inserted, + mark_uninserted + } +insertion_state_t; + +static int remove_breakpoint (struct breakpoint *, insertion_state_t); + +static enum print_stop_action print_it_typical (bpstat); + +static enum print_stop_action print_bp_stop_message (bpstat bs); + +typedef struct + { + enum exception_event_kind kind; + int enable_p; + } +args_for_catchpoint_enable; + +static int watchpoint_check (PTR); + +static int cover_target_enable_exception_callback (PTR); + +static void maintenance_info_breakpoints (char *, int); + +static void create_longjmp_breakpoint (char *); + +static void create_overlay_event_breakpoint (char *); + +static int hw_breakpoint_used_count (void); + +static int hw_watchpoint_used_count (enum bptype, int *); + +static void hbreak_command (char *, int); + +static void thbreak_command (char *, int); + +static void watch_command_1 (char *, int, int); + +static void rwatch_command (char *, int); + +static void awatch_command (char *, int); + +static void do_enable_breakpoint (struct breakpoint *, enum bpdisp); + +static void solib_load_unload_1 (char *hookname, + int tempflag, + char *dll_pathname, + char *cond_string, enum bptype bp_kind); + +static void create_fork_vfork_event_catchpoint (int tempflag, + char *cond_string, + enum bptype bp_kind); + +static void break_at_finish_at_depth_command_1 (char *arg, + int flag, int from_tty); + +static void break_at_finish_command_1 (char *arg, int flag, int from_tty); + +static void stop_command (char *arg, int from_tty); + +static void stopin_command (char *arg, int from_tty); + +static void stopat_command (char *arg, int from_tty); + +static char *ep_find_event_name_end (char *arg); + +static char *ep_parse_optional_if_clause (char **arg); + +static char *ep_parse_optional_filename (char **arg); + +#if defined(CHILD_INSERT_EXEC_CATCHPOINT) +static void catch_exec_command_1 (char *arg, int tempflag, int from_tty); +#endif + +static void create_exception_catchpoint (int tempflag, char *cond_string, + enum exception_event_kind ex_event, + struct symtab_and_line *sal); + +static void catch_exception_command_1 (enum exception_event_kind ex_event, + char *arg, int tempflag, int from_tty); + +static void tcatch_command (char *arg, int from_tty); + +static void ep_skip_leading_whitespace (char **s); + +/* Prototypes for exported functions. */ + +/* If FALSE, gdb will not use hardware support for watchpoints, even + if such is available. */ +static int can_use_hw_watchpoints; + +void _initialize_breakpoint (void); + +extern int addressprint; /* Print machine addresses? */ + +/* Are we executing breakpoint commands? */ +static int executing_breakpoint_commands; + +/* Are overlay event breakpoints enabled? */ +static int overlay_events_enabled; + +/* Walk the following statement or block through all breakpoints. + ALL_BREAKPOINTS_SAFE does so even if the statment deletes the current + breakpoint. */ + +#define ALL_BREAKPOINTS(B) for (B = breakpoint_chain; B; B = B->next) + +#define ALL_BREAKPOINTS_SAFE(B,TMP) \ + for (B = breakpoint_chain; \ + B ? (TMP=B->next, 1): 0; \ + B = TMP) + +/* True if SHIFT_INST_REGS defined, false otherwise. */ + +int must_shift_inst_regs = +#if defined(SHIFT_INST_REGS) +1 +#else +0 +#endif + ; + +/* True if breakpoint hit counts should be displayed in breakpoint info. */ + +int show_breakpoint_hit_counts = 1; + +/* Chain of all breakpoints defined. */ + +struct breakpoint *breakpoint_chain; + +/* Number of last breakpoint made. */ + +int breakpoint_count; + +/* Pointer to current exception event record */ +static struct exception_event_record *current_exception_event; + +/* Indicator of whether exception catchpoints should be nuked + between runs of a program */ +int exception_catchpoints_are_fragile = 0; + +/* Indicator of when exception catchpoints set-up should be + reinitialized -- e.g. when program is re-run */ +int exception_support_initialized = 0; + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been + loaded. + + This function must be used only when SOLIB_HAVE_LOAD_EVENT is TRUE, + or undefined results are guaranteed. + + This string's contents are only valid immediately after the + inferior has stopped in the dynamic linker hook, and becomes + invalid as soon as the inferior is continued. Clients should make + a copy of this string if they wish to continue the inferior and + then access the string. */ + +#ifndef SOLIB_LOADED_LIBRARY_PATHNAME +#define SOLIB_LOADED_LIBRARY_PATHNAME(pid) "" +#endif + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been + unloaded. + + This function must be used only when SOLIB_HAVE_UNLOAD_EVENT is + TRUE, or undefined results are guaranteed. + + This string's contents are only valid immediately after the + inferior has stopped in the dynamic linker hook, and becomes + invalid as soon as the inferior is continued. Clients should make + a copy of this string if they wish to continue the inferior and + then access the string. */ + +#ifndef SOLIB_UNLOADED_LIBRARY_PATHNAME +#define SOLIB_UNLOADED_LIBRARY_PATHNAME(pid) "" +#endif + +/* This function is called by the "catch load" command. It allows the + debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is loaded. */ + +#ifndef SOLIB_CREATE_CATCH_LOAD_HOOK +#define SOLIB_CREATE_CATCH_LOAD_HOOK(pid,tempflag,filename,cond_string) \ + error ("catch of library loads not yet implemented on this platform") +#endif + +/* This function is called by the "catch unload" command. It allows + the debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is + unloaded. */ + +#ifndef SOLIB_CREATE_CATCH_UNLOAD_HOOK +#define SOLIB_CREATE_CATCH_UNLOAD_HOOK(pid,tempflag,filename,cond_string) \ + error ("catch of library unloads not yet implemented on this platform") +#endif + +/* Set breakpoint count to NUM. */ + +void +set_breakpoint_count (int num) +{ + breakpoint_count = num; + set_internalvar (lookup_internalvar ("bpnum"), + value_from_longest (builtin_type_int, (LONGEST) num)); +} + +/* Used in run_command to zero the hit count when a new run starts. */ + +void +clear_breakpoint_hit_counts (void) +{ + struct breakpoint *b; + + ALL_BREAKPOINTS (b) + b->hit_count = 0; +} + +/* Default address, symtab and line to put a breakpoint at + for "break" command with no arg. + if default_breakpoint_valid is zero, the other three are + not valid, and "break" with no arg is an error. + + This set by print_stack_frame, which calls set_default_breakpoint. */ + +int default_breakpoint_valid; +CORE_ADDR default_breakpoint_address; +struct symtab *default_breakpoint_symtab; +int default_breakpoint_line; + +/* *PP is a string denoting a breakpoint. Get the number of the breakpoint. + Advance *PP after the string and any trailing whitespace. + + Currently the string can either be a number or "$" followed by the name + of a convenience variable. Making it an expression wouldn't work well + for map_breakpoint_numbers (e.g. "4 + 5 + 6"). + + TRAILER is a character which can be found after the number; most + commonly this is `-'. If you don't want a trailer, use \0. */ +static int +get_number_trailer (char **pp, int trailer) +{ + int retval = 0; /* default */ + char *p = *pp; + + if (p == NULL) + /* Empty line means refer to the last breakpoint. */ + return breakpoint_count; + else if (*p == '$') + { + /* Make a copy of the name, so we can null-terminate it + to pass to lookup_internalvar(). */ + char *varname; + char *start = ++p; + struct value *val; + + while (isalnum (*p) || *p == '_') + p++; + varname = (char *) alloca (p - start + 1); + strncpy (varname, start, p - start); + varname[p - start] = '\0'; + val = value_of_internalvar (lookup_internalvar (varname)); + if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_INT) + retval = (int) value_as_long (val); + else + { + printf_filtered ("Convenience variable must have integer value.\n"); + retval = 0; + } + } + else + { + if (*p == '-') + ++p; + while (*p >= '0' && *p <= '9') + ++p; + if (p == *pp) + /* There is no number here. (e.g. "cond a == b"). */ + { + /* Skip non-numeric token */ + while (*p && !isspace((int) *p)) + ++p; + /* Return zero, which caller must interpret as error. */ + retval = 0; + } + else + retval = atoi (*pp); + } + if (!(isspace (*p) || *p == '\0' || *p == trailer)) + { + /* Trailing junk: return 0 and let caller print error msg. */ + while (!(isspace (*p) || *p == '\0' || *p == trailer)) + ++p; + retval = 0; + } + while (isspace (*p)) + p++; + *pp = p; + return retval; +} + + +/* Like get_number_trailer, but don't allow a trailer. */ +int +get_number (char **pp) +{ + return get_number_trailer (pp, '\0'); +} + +/* Parse a number or a range. + * A number will be of the form handled by get_number. + * A range will be of the form - , and + * will represent all the integers between number1 and number2, + * inclusive. + * + * While processing a range, this fuction is called iteratively; + * At each call it will return the next value in the range. + * + * At the beginning of parsing a range, the char pointer PP will + * be advanced past and left pointing at the '-' token. + * Subsequent calls will not advance the pointer until the range + * is completed. The call that completes the range will advance + * pointer PP past . + */ + +int +get_number_or_range (char **pp) +{ + static int last_retval, end_value; + static char *end_ptr; + static int in_range = 0; + + if (**pp != '-') + { + /* Default case: pp is pointing either to a solo number, + or to the first number of a range. */ + last_retval = get_number_trailer (pp, '-'); + if (**pp == '-') + { + char **temp; + + /* This is the start of a range ( - ). + Skip the '-', parse and remember the second number, + and also remember the end of the final token. */ + + temp = &end_ptr; + end_ptr = *pp + 1; + while (isspace ((int) *end_ptr)) + end_ptr++; /* skip white space */ + end_value = get_number (temp); + if (end_value < last_retval) + { + error ("inverted range"); + } + else if (end_value == last_retval) + { + /* degenerate range (number1 == number2). Advance the + token pointer so that the range will be treated as a + single number. */ + *pp = end_ptr; + } + else + in_range = 1; + } + } + else if (! in_range) + error ("negative value"); + else + { + /* pp points to the '-' that betokens a range. All + number-parsing has already been done. Return the next + integer value (one greater than the saved previous value). + Do not advance the token pointer 'pp' until the end of range + is reached. */ + + if (++last_retval == end_value) + { + /* End of range reached; advance token pointer. */ + *pp = end_ptr; + in_range = 0; + } + } + return last_retval; +} + + + +/* condition N EXP -- set break condition of breakpoint N to EXP. */ + +static void +condition_command (char *arg, int from_tty) +{ + register struct breakpoint *b; + char *p; + register int bnum; + + if (arg == 0) + error_no_arg ("breakpoint number"); + + p = arg; + bnum = get_number (&p); + if (bnum == 0) + error ("Bad breakpoint argument: '%s'", arg); + + ALL_BREAKPOINTS (b) + if (b->number == bnum) + { + if (b->cond) + { + xfree (b->cond); + b->cond = 0; + } + if (b->cond_string != NULL) + xfree (b->cond_string); + + if (*p == 0) + { + b->cond = 0; + b->cond_string = NULL; + if (from_tty) + printf_filtered ("Breakpoint %d now unconditional.\n", bnum); + } + else + { + arg = p; + /* I don't know if it matters whether this is the string the user + typed in or the decompiled expression. */ + b->cond_string = savestring (arg, strlen (arg)); + b->cond = parse_exp_1 (&arg, block_for_pc (b->address), 0); + if (*arg) + error ("Junk at end of expression"); + } + breakpoints_changed (); + breakpoint_modify_event (b->number); + return; + } + + error ("No breakpoint number %d.", bnum); +} + +/* ARGSUSED */ +static void +commands_command (char *arg, int from_tty) +{ + register struct breakpoint *b; + char *p; + register int bnum; + struct command_line *l; + + /* If we allowed this, we would have problems with when to + free the storage, if we change the commands currently + being read from. */ + + if (executing_breakpoint_commands) + error ("Can't use the \"commands\" command among a breakpoint's commands."); + + p = arg; + bnum = get_number (&p); + + if (p && *p) + error ("Unexpected extra arguments following breakpoint number."); + + ALL_BREAKPOINTS (b) + if (b->number == bnum) + { + char tmpbuf[128]; + sprintf (tmpbuf, + "Type commands for when breakpoint %d is hit, one per line.", + bnum); + l = read_command_lines (tmpbuf, from_tty); + free_command_lines (&b->commands); + b->commands = l; + breakpoints_changed (); + breakpoint_modify_event (b->number); + return; + } + error ("No breakpoint number %d.", bnum); +} + +/* Like target_read_memory() but if breakpoints are inserted, return + the shadow contents instead of the breakpoints themselves. + + Read "memory data" from whatever target or inferior we have. + Returns zero if successful, errno value if not. EIO is used + for address out of bounds. If breakpoints are inserted, returns + shadow contents, not the breakpoints themselves. From breakpoint.c. */ + +int +read_memory_nobpt (CORE_ADDR memaddr, char *myaddr, unsigned len) +{ + int status; + struct breakpoint *b; + CORE_ADDR bp_addr = 0; + int bp_size = 0; + + if (BREAKPOINT_FROM_PC (&bp_addr, &bp_size) == NULL) + /* No breakpoints on this machine. */ + return target_read_memory (memaddr, myaddr, len); + + ALL_BREAKPOINTS (b) + { + if (b->type == bp_none) + warning ("reading through apparently deleted breakpoint #%d?", + b->number); + + /* memory breakpoint? */ + if (b->type == bp_watchpoint + || b->type == bp_hardware_watchpoint + || b->type == bp_read_watchpoint + || b->type == bp_access_watchpoint) + continue; + /* bp in memory? */ + if (!b->inserted) + continue; + /* Addresses and length of the part of the breakpoint that + we need to copy. */ + /* XXXX The m68k, sh and h8300 have different local and remote + breakpoint values. BREAKPOINT_FROM_PC still manages to + correctly determine the breakpoints memory address and size + for these targets. */ + bp_addr = b->address; + bp_size = 0; + if (BREAKPOINT_FROM_PC (&bp_addr, &bp_size) == NULL) + continue; + if (bp_size == 0) + /* bp isn't valid */ + continue; + if (bp_addr + bp_size <= memaddr) + /* The breakpoint is entirely before the chunk of memory we + are reading. */ + continue; + if (bp_addr >= memaddr + len) + /* The breakpoint is entirely after the chunk of memory we are + reading. */ + continue; + /* Copy the breakpoint from the shadow contents, and recurse for + the things before and after. */ + { + /* Offset within shadow_contents. */ + int bptoffset = 0; + + if (bp_addr < memaddr) + { + /* Only copy the second part of the breakpoint. */ + bp_size -= memaddr - bp_addr; + bptoffset = memaddr - bp_addr; + bp_addr = memaddr; + } + + if (bp_addr + bp_size > memaddr + len) + { + /* Only copy the first part of the breakpoint. */ + bp_size -= (bp_addr + bp_size) - (memaddr + len); + } + + memcpy (myaddr + bp_addr - memaddr, + b->shadow_contents + bptoffset, bp_size); + + if (bp_addr > memaddr) + { + /* Copy the section of memory before the breakpoint. */ + status = read_memory_nobpt (memaddr, myaddr, bp_addr - memaddr); + if (status != 0) + return status; + } + + if (bp_addr + bp_size < memaddr + len) + { + /* Copy the section of memory after the breakpoint. */ + status = read_memory_nobpt (bp_addr + bp_size, + myaddr + bp_addr + bp_size - memaddr, + memaddr + len - (bp_addr + bp_size)); + if (status != 0) + return status; + } + return 0; + } + } + /* Nothing overlaps. Just call read_memory_noerr. */ + return target_read_memory (memaddr, myaddr, len); +} + + +/* insert_breakpoints is used when starting or continuing the program. + remove_breakpoints is used when the program stops. + Both return zero if successful, + or an `errno' value if could not write the inferior. */ + +int +insert_breakpoints (void) +{ + register struct breakpoint *b, *temp; + int return_val = 0; /* return success code. */ + int val = 0; + int disabled_breaks = 0; + int hw_breakpoint_error = 0; +#ifdef ONE_PROCESS_WRITETEXT + int process_warning = 0; +#endif + + static char message1[] = "Error inserting catchpoint %d:\n"; + static char message[sizeof (message1) + 30]; + + struct ui_file *tmp_error_stream = mem_fileopen (); + make_cleanup_ui_file_delete (tmp_error_stream); + + /* Explicitly mark the warning -- this will only be printed if + there was an error. */ + fprintf_unfiltered (tmp_error_stream, "Warning:\n"); + + ALL_BREAKPOINTS_SAFE (b, temp) + { + if (b->enable_state == bp_permanent) + /* Permanent breakpoints cannot be inserted or removed. */ + continue; + if ((b->type == bp_watchpoint + || b->type == bp_hardware_watchpoint + || b->type == bp_read_watchpoint + || b->type == bp_access_watchpoint) && (!b->val)) + { + struct value *val; + val = evaluate_expression (b->exp); + release_value (val); + if (VALUE_LAZY (val)) + value_fetch_lazy (val); + b->val = val; + } + if (b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint + && b->type != bp_read_watchpoint + && b->type != bp_access_watchpoint + && b->type != bp_catch_fork + && b->type != bp_catch_vfork + && b->type != bp_catch_exec + && b->type != bp_catch_throw + && b->type != bp_catch_catch + && b->enable_state != bp_disabled + && b->enable_state != bp_shlib_disabled + && b->enable_state != bp_call_disabled + && !b->inserted + && !b->duplicate) + { + /* "Normal" instruction breakpoint: either the standard + trap-instruction bp (bp_breakpoint), or a + bp_hardware_breakpoint. */ + + /* First check to see if we have to handle an overlay. */ + if (overlay_debugging == ovly_off + || b->section == NULL + || !(section_is_overlay (b->section))) + { + /* No overlay handling: just set the breakpoint. */ + + if (b->type == bp_hardware_breakpoint) + val = target_insert_hw_breakpoint (b->address, + b->shadow_contents); + else + val = target_insert_breakpoint (b->address, b->shadow_contents); + } + else + { + /* This breakpoint is in an overlay section. + Shall we set a breakpoint at the LMA? */ + if (!overlay_events_enabled) + { + /* Yes -- overlay event support is not active, + so we must try to set a breakpoint at the LMA. + This will not work for a hardware breakpoint. */ + if (b->type == bp_hardware_breakpoint) + warning ("hardware breakpoint %d not supported in overlay!\n", + b->number); + else + { + CORE_ADDR addr = overlay_unmapped_address (b->address, + b->section); + /* Set a software (trap) breakpoint at the LMA. */ + val = target_insert_breakpoint (addr, b->shadow_contents); + if (val != 0) + fprintf_unfiltered (tmp_error_stream, + "Overlay breakpoint %d failed: in ROM?", + b->number); + } + } + /* Shall we set a breakpoint at the VMA? */ + if (section_is_mapped (b->section)) + { + /* Yes. This overlay section is mapped into memory. */ + if (b->type == bp_hardware_breakpoint) + val = target_insert_hw_breakpoint (b->address, + b->shadow_contents); + else + val = target_insert_breakpoint (b->address, + b->shadow_contents); + } + else + { + /* No. This breakpoint will not be inserted. + No error, but do not mark the bp as 'inserted'. */ + continue; + } + } + + if (val) + { + /* Can't set the breakpoint. */ +#if defined (DISABLE_UNSETTABLE_BREAK) + if (DISABLE_UNSETTABLE_BREAK (b->address)) + { + /* See also: disable_breakpoints_in_shlibs. */ + val = 0; + b->enable_state = bp_shlib_disabled; + if (!disabled_breaks) + { + fprintf_unfiltered (tmp_error_stream, + "Cannot insert breakpoint %d.\n", + b->number); + fprintf_unfiltered (tmp_error_stream, + "Temporarily disabling shared library breakpoints:\n"); + } + disabled_breaks = 1; + fprintf_unfiltered (tmp_error_stream, + "breakpoint #%d\n", b->number); + } + else +#endif + { +#ifdef ONE_PROCESS_WRITETEXT + process_warning = 1; +#endif + if (b->type == bp_hardware_breakpoint) + { + hw_breakpoint_error = 1; + fprintf_unfiltered (tmp_error_stream, + "Cannot insert hardware breakpoint %d.\n", + b->number); + } + else + { + fprintf_unfiltered (tmp_error_stream, + "Cannot insert breakpoint %d.\n", + b->number); + fprintf_filtered (tmp_error_stream, + "Error accessing memory address "); + print_address_numeric (b->address, 1, tmp_error_stream); + fprintf_filtered (tmp_error_stream, ": %s.\n", + safe_strerror (val)); + } + + } + } + else + b->inserted = 1; + + if (val) + return_val = val; /* remember failure */ + } + else if (ep_is_exception_catchpoint (b) + && b->enable_state != bp_disabled + && b->enable_state != bp_shlib_disabled + && b->enable_state != bp_call_disabled + && !b->inserted + && !b->duplicate) + + { + /* If we get here, we must have a callback mechanism for exception + events -- with g++ style embedded label support, we insert + ordinary breakpoints and not catchpoints. */ + /* Format possible error message */ + sprintf (message, message1, b->number); + + val = target_insert_breakpoint (b->address, b->shadow_contents); + if (val) + { + /* Couldn't set breakpoint for some reason */ + fprintf_unfiltered (tmp_error_stream, + "Cannot insert catchpoint %d; disabling it.\n", + b->number); + fprintf_filtered (tmp_error_stream, + "Error accessing memory address "); + print_address_numeric (b->address, 1, tmp_error_stream); + fprintf_filtered (tmp_error_stream, ": %s.\n", + safe_strerror (val)); + b->enable_state = bp_disabled; + } + else + { + /* Bp set, now make sure callbacks are enabled */ + int val; + args_for_catchpoint_enable args; + args.kind = b->type == bp_catch_catch ? + EX_EVENT_CATCH : EX_EVENT_THROW; + args.enable_p = 1; + val = catch_errors (cover_target_enable_exception_callback, + &args, + message, RETURN_MASK_ALL); + if (val != 0 && val != -1) + { + b->inserted = 1; + } + /* Check if something went wrong; val == 0 can be ignored */ + if (val == -1) + { + /* something went wrong */ + fprintf_unfiltered (tmp_error_stream, + "Cannot insert catchpoint %d; disabling it.\n", + b->number); + b->enable_state = bp_disabled; + } + } + + if (val) + return_val = val; /* remember failure */ + } + + else if ((b->type == bp_hardware_watchpoint || + b->type == bp_read_watchpoint || + b->type == bp_access_watchpoint) + && b->enable_state == bp_enabled + && b->disposition != disp_del_at_next_stop + && !b->inserted + && !b->duplicate) + { + struct frame_info *saved_frame; + int saved_level, within_current_scope; + struct value *mark = value_mark (); + struct value *v; + + /* Save the current frame and level so we can restore it after + evaluating the watchpoint expression on its own frame. */ + saved_frame = selected_frame; + saved_level = frame_relative_level (selected_frame); + + /* Determine if the watchpoint is within scope. */ + if (b->exp_valid_block == NULL) + within_current_scope = 1; + else + { + struct frame_info *fi; + fi = frame_find_by_id (b->watchpoint_frame); + within_current_scope = (fi != NULL); + if (within_current_scope) + select_frame (fi); + } + + if (within_current_scope) + { + /* Evaluate the expression and cut the chain of values + produced off from the value chain. + + Make sure the value returned isn't lazy; we use + laziness to determine what memory GDB actually needed + in order to compute the value of the expression. */ + v = evaluate_expression (b->exp); + VALUE_CONTENTS(v); + value_release_to_mark (mark); + + b->val_chain = v; + b->inserted = 1; + + /* Look at each value on the value chain. */ + for (; v; v = v->next) + { + /* If it's a memory location, and GDB actually needed + its contents to evaluate the expression, then we + must watch it. */ + if (VALUE_LVAL (v) == lval_memory + && ! VALUE_LAZY (v)) + { + struct type *vtype = check_typedef (VALUE_TYPE (v)); + + /* We only watch structs and arrays if user asked + for it explicitly, never if they just happen to + appear in the middle of some value chain. */ + if (v == b->val_chain + || (TYPE_CODE (vtype) != TYPE_CODE_STRUCT + && TYPE_CODE (vtype) != TYPE_CODE_ARRAY)) + { + CORE_ADDR addr; + int len, type; + + addr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); + len = TYPE_LENGTH (VALUE_TYPE (v)); + type = hw_write; + if (b->type == bp_read_watchpoint) + type = hw_read; + else if (b->type == bp_access_watchpoint) + type = hw_access; + + val = target_insert_watchpoint (addr, len, type); + if (val == -1) + { + /* Don't exit the loop, try to insert + every value on the value chain. That's + because we will be removing all the + watches below, and removing a + watchpoint we didn't insert could have + adverse effects. */ + b->inserted = 0; + } + val = 0; + } + } + } + /* Failure to insert a watchpoint on any memory value in the + value chain brings us here. */ + if (!b->inserted) + { + remove_breakpoint (b, mark_uninserted); + hw_breakpoint_error = 1; + fprintf_unfiltered (tmp_error_stream, + "Could not insert hardware watchpoint %d.\n", + b->number); + val = -1; + } + } + else + { + printf_filtered ("Hardware watchpoint %d deleted ", b->number); + printf_filtered ("because the program has left the block \n"); + printf_filtered ("in which its expression is valid.\n"); + if (b->related_breakpoint) + b->related_breakpoint->disposition = disp_del_at_next_stop; + b->disposition = disp_del_at_next_stop; + } + + /* Restore the frame and level. */ + if ((saved_frame != selected_frame) || + (saved_level != frame_relative_level (selected_frame))) + select_frame (saved_frame); + + if (val) + return_val = val; /* remember failure */ + } + else if ((b->type == bp_catch_fork + || b->type == bp_catch_vfork + || b->type == bp_catch_exec) + && b->enable_state == bp_enabled + && !b->inserted + && !b->duplicate) + { + val = -1; + switch (b->type) + { + case bp_catch_fork: + val = target_insert_fork_catchpoint (PIDGET (inferior_ptid)); + break; + case bp_catch_vfork: + val = target_insert_vfork_catchpoint (PIDGET (inferior_ptid)); + break; + case bp_catch_exec: + val = target_insert_exec_catchpoint (PIDGET (inferior_ptid)); + break; + default: + warning ("Internal error, %s line %d.", __FILE__, __LINE__); + break; + } + if (val < 0) + { + fprintf_unfiltered (tmp_error_stream, + "Cannot insert catchpoint %d.", b->number); + } + else + b->inserted = 1; + + if (val) + return_val = val; /* remember failure */ + } + } + + if (return_val) + { + /* If a hardware breakpoint or watchpoint was inserted, add a + message about possibly exhausted resources. */ + if (hw_breakpoint_error) + { + fprintf_unfiltered (tmp_error_stream, + "Could not insert hardware breakpoints:\n\ +You may have requested too many hardware breakpoints/watchpoints.\n"); + } +#ifdef ONE_PROCESS_WRITETEXT + if (process_warning) + fprintf_unfiltered (tmp_error_stream, + "The same program may be running in another process."); +#endif + target_terminal_ours_for_output (); + error_stream (tmp_error_stream); + } + return return_val; +} + +int +remove_breakpoints (void) +{ + register struct breakpoint *b; + int val; + + ALL_BREAKPOINTS (b) + { + if (b->inserted) + { + val = remove_breakpoint (b, mark_uninserted); + if (val != 0) + return val; + } + } + return 0; +} + +int +remove_hw_watchpoints (void) +{ + register struct breakpoint *b; + int val; + + ALL_BREAKPOINTS (b) + { + if (b->inserted + && (b->type == bp_hardware_watchpoint + || b->type == bp_read_watchpoint + || b->type == bp_access_watchpoint)) + { + val = remove_breakpoint (b, mark_uninserted); + if (val != 0) + return val; + } + } + return 0; +} + +int +reattach_breakpoints (int pid) +{ + register struct breakpoint *b; + int val; + struct cleanup *old_chain = save_inferior_ptid (); + + /* Set inferior_ptid; remove_breakpoint uses this global. */ + inferior_ptid = pid_to_ptid (pid); + ALL_BREAKPOINTS (b) + { + if (b->inserted) + { + remove_breakpoint (b, mark_inserted); + if (b->type == bp_hardware_breakpoint) + val = target_insert_hw_breakpoint (b->address, b->shadow_contents); + else + val = target_insert_breakpoint (b->address, b->shadow_contents); + if (val != 0) + { + do_cleanups (old_chain); + return val; + } + } + } + do_cleanups (old_chain); + return 0; +} + +void +update_breakpoints_after_exec (void) +{ + struct breakpoint *b; + struct breakpoint *temp; + + /* Doing this first prevents the badness of having delete_breakpoint() + write a breakpoint's current "shadow contents" to lift the bp. That + shadow is NOT valid after an exec()! */ + mark_breakpoints_out (); + + ALL_BREAKPOINTS_SAFE (b, temp) + { + /* Solib breakpoints must be explicitly reset after an exec(). */ + if (b->type == bp_shlib_event) + { + delete_breakpoint (b); + continue; + } + + /* Thread event breakpoints must be set anew after an exec(), + as must overlay event breakpoints. */ + if (b->type == bp_thread_event || b->type == bp_overlay_event) + { + delete_breakpoint (b); + continue; + } + + /* Step-resume breakpoints are meaningless after an exec(). */ + if (b->type == bp_step_resume) + { + delete_breakpoint (b); + continue; + } + + /* Ditto the sigtramp handler breakpoints. */ + if (b->type == bp_through_sigtramp) + { + delete_breakpoint (b); + continue; + } + + /* Ditto the exception-handling catchpoints. */ + if ((b->type == bp_catch_catch) || (b->type == bp_catch_throw)) + { + delete_breakpoint (b); + continue; + } + + /* Don't delete an exec catchpoint, because else the inferior + won't stop when it ought! + + Similarly, we probably ought to keep vfork catchpoints, 'cause + on this target, we may not be able to stop when the vfork is + seen, but only when the subsequent exec is seen. (And because + deleting fork catchpoints here but not vfork catchpoints will + seem mysterious to users, keep those too.) + + ??rehrauer: Let's hope that merely clearing out this catchpoint's + target address field, if any, is sufficient to have it be reset + automagically. Certainly on HP-UX that's true. + + Jim Blandy : Actually, zero is a perfectly + valid code address on some platforms (like the mn10200 and + mn10300 simulators). We shouldn't assign any special + interpretation to a breakpoint with a zero address. And in + fact, GDB doesn't --- I can't see what that comment above is + talking about. As far as I can tell, setting the address of a + bp_catch_exec/bp_catch_vfork/bp_catch_fork breakpoint to zero + is meaningless, since those are implemented with HP-UX kernel + hackery, not by storing breakpoint instructions somewhere. */ + if ((b->type == bp_catch_exec) || + (b->type == bp_catch_vfork) || + (b->type == bp_catch_fork)) + { + b->address = (CORE_ADDR) NULL; + continue; + } + + /* bp_finish is a special case. The only way we ought to be able + to see one of these when an exec() has happened, is if the user + caught a vfork, and then said "finish". Ordinarily a finish just + carries them to the call-site of the current callee, by setting + a temporary bp there and resuming. But in this case, the finish + will carry them entirely through the vfork & exec. + + We don't want to allow a bp_finish to remain inserted now. But + we can't safely delete it, 'cause finish_command has a handle to + the bp on a bpstat, and will later want to delete it. There's a + chance (and I've seen it happen) that if we delete the bp_finish + here, that its storage will get reused by the time finish_command + gets 'round to deleting the "use to be a bp_finish" breakpoint. + We really must allow finish_command to delete a bp_finish. + + In the absense of a general solution for the "how do we know + it's safe to delete something others may have handles to?" + problem, what we'll do here is just uninsert the bp_finish, and + let finish_command delete it. + + (We know the bp_finish is "doomed" in the sense that it's + momentary, and will be deleted as soon as finish_command sees + the inferior stopped. So it doesn't matter that the bp's + address is probably bogus in the new a.out, unlike e.g., the + solib breakpoints.) */ + + if (b->type == bp_finish) + { + continue; + } + + /* Without a symbolic address, we have little hope of the + pre-exec() address meaning the same thing in the post-exec() + a.out. */ + if (b->addr_string == NULL) + { + delete_breakpoint (b); + continue; + } + + /* If this breakpoint has survived the above battery of checks, then + it must have a symbolic address. Be sure that it gets reevaluated + to a target address, rather than reusing the old evaluation. + + Jim Blandy : As explained above in the comment + for bp_catch_exec and friends, I'm pretty sure this is entirely + unnecessary. A call to breakpoint_re_set_one always recomputes + the breakpoint's address from scratch, or deletes it if it can't. + So I think this assignment could be deleted without effect. */ + b->address = (CORE_ADDR) NULL; + } + /* FIXME what about longjmp breakpoints? Re-create them here? */ + create_overlay_event_breakpoint ("_ovly_debug_event"); +} + +int +detach_breakpoints (int pid) +{ + register struct breakpoint *b; + int val; + struct cleanup *old_chain = save_inferior_ptid (); + + if (pid == PIDGET (inferior_ptid)) + error ("Cannot detach breakpoints of inferior_ptid"); + + /* Set inferior_ptid; remove_breakpoint uses this global. */ + inferior_ptid = pid_to_ptid (pid); + ALL_BREAKPOINTS (b) + { + if (b->inserted) + { + val = remove_breakpoint (b, mark_inserted); + if (val != 0) + { + do_cleanups (old_chain); + return val; + } + } + } + do_cleanups (old_chain); + return 0; +} + +static int +remove_breakpoint (struct breakpoint *b, insertion_state_t is) +{ + int val; + + if (b->enable_state == bp_permanent) + /* Permanent breakpoints cannot be inserted or removed. */ + return 0; + + if (b->type == bp_none) + warning ("attempted to remove apparently deleted breakpoint #%d?", + b->number); + + if (b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint + && b->type != bp_read_watchpoint + && b->type != bp_access_watchpoint + && b->type != bp_catch_fork + && b->type != bp_catch_vfork + && b->type != bp_catch_exec + && b->type != bp_catch_catch + && b->type != bp_catch_throw) + { + /* "Normal" instruction breakpoint: either the standard + trap-instruction bp (bp_breakpoint), or a + bp_hardware_breakpoint. */ + + /* First check to see if we have to handle an overlay. */ + if (overlay_debugging == ovly_off + || b->section == NULL + || !(section_is_overlay (b->section))) + { + /* No overlay handling: just remove the breakpoint. */ + + if (b->type == bp_hardware_breakpoint) + val = target_remove_hw_breakpoint (b->address, + b->shadow_contents); + else + val = target_remove_breakpoint (b->address, b->shadow_contents); + } + else + { + /* This breakpoint is in an overlay section. + Did we set a breakpoint at the LMA? */ + if (!overlay_events_enabled) + { + /* Yes -- overlay event support is not active, so we + should have set a breakpoint at the LMA. Remove it. + */ + CORE_ADDR addr = overlay_unmapped_address (b->address, + b->section); + /* Ignore any failures: if the LMA is in ROM, we will + have already warned when we failed to insert it. */ + if (b->type != bp_hardware_breakpoint) + target_remove_hw_breakpoint (addr, b->shadow_contents); + else + target_remove_breakpoint (addr, b->shadow_contents); + } + /* Did we set a breakpoint at the VMA? + If so, we will have marked the breakpoint 'inserted'. */ + if (b->inserted) + { + /* Yes -- remove it. Previously we did not bother to + remove the breakpoint if the section had been + unmapped, but let's not rely on that being safe. We + don't know what the overlay manager might do. */ + if (b->type == bp_hardware_breakpoint) + val = target_remove_hw_breakpoint (b->address, + b->shadow_contents); + else + val = target_remove_breakpoint (b->address, + b->shadow_contents); + } + else + { + /* No -- not inserted, so no need to remove. No error. */ + val = 0; + } + } + if (val) + return val; + b->inserted = (is == mark_inserted); + } + else if ((b->type == bp_hardware_watchpoint || + b->type == bp_read_watchpoint || + b->type == bp_access_watchpoint) + && b->enable_state == bp_enabled + && !b->duplicate) + { + struct value *v; + struct value *n; + + b->inserted = (is == mark_inserted); + /* Walk down the saved value chain. */ + for (v = b->val_chain; v; v = v->next) + { + /* For each memory reference remove the watchpoint + at that address. */ + if (VALUE_LVAL (v) == lval_memory + && ! VALUE_LAZY (v)) + { + struct type *vtype = check_typedef (VALUE_TYPE (v)); + + if (v == b->val_chain + || (TYPE_CODE (vtype) != TYPE_CODE_STRUCT + && TYPE_CODE (vtype) != TYPE_CODE_ARRAY)) + { + CORE_ADDR addr; + int len, type; + + addr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); + len = TYPE_LENGTH (VALUE_TYPE (v)); + type = hw_write; + if (b->type == bp_read_watchpoint) + type = hw_read; + else if (b->type == bp_access_watchpoint) + type = hw_access; + + val = target_remove_watchpoint (addr, len, type); + if (val == -1) + b->inserted = 1; + val = 0; + } + } + } + /* Failure to remove any of the hardware watchpoints comes here. */ + if ((is == mark_uninserted) && (b->inserted)) + warning ("Could not remove hardware watchpoint %d.", + b->number); + + /* Free the saved value chain. We will construct a new one + the next time the watchpoint is inserted. */ + for (v = b->val_chain; v; v = n) + { + n = v->next; + value_free (v); + } + b->val_chain = NULL; + } + else if ((b->type == bp_catch_fork || + b->type == bp_catch_vfork || + b->type == bp_catch_exec) + && b->enable_state == bp_enabled + && !b->duplicate) + { + val = -1; + switch (b->type) + { + case bp_catch_fork: + val = target_remove_fork_catchpoint (PIDGET (inferior_ptid)); + break; + case bp_catch_vfork: + val = target_remove_vfork_catchpoint (PIDGET (inferior_ptid)); + break; + case bp_catch_exec: + val = target_remove_exec_catchpoint (PIDGET (inferior_ptid)); + break; + default: + warning ("Internal error, %s line %d.", __FILE__, __LINE__); + break; + } + if (val) + return val; + b->inserted = (is == mark_inserted); + } + else if ((b->type == bp_catch_catch || + b->type == bp_catch_throw) + && b->enable_state == bp_enabled + && !b->duplicate) + { + + val = target_remove_breakpoint (b->address, b->shadow_contents); + if (val) + return val; + b->inserted = (is == mark_inserted); + } + else if (ep_is_exception_catchpoint (b) + && b->inserted /* sometimes previous insert doesn't happen */ + && b->enable_state == bp_enabled + && !b->duplicate) + { + + val = target_remove_breakpoint (b->address, b->shadow_contents); + if (val) + return val; + + b->inserted = (is == mark_inserted); + } + + return 0; +} + +/* Clear the "inserted" flag in all breakpoints. */ + +void +mark_breakpoints_out (void) +{ + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + b->inserted = 0; +} + +/* Clear the "inserted" flag in all breakpoints and delete any + breakpoints which should go away between runs of the program. + + Plus other such housekeeping that has to be done for breakpoints + between runs. + + Note: this function gets called at the end of a run (by + generic_mourn_inferior) and when a run begins (by + init_wait_for_inferior). */ + + + +void +breakpoint_init_inferior (enum inf_context context) +{ + register struct breakpoint *b, *temp; + static int warning_needed = 0; + + ALL_BREAKPOINTS_SAFE (b, temp) + { + b->inserted = 0; + + switch (b->type) + { + case bp_call_dummy: + case bp_watchpoint_scope: + + /* If the call dummy breakpoint is at the entry point it will + cause problems when the inferior is rerun, so we better + get rid of it. + + Also get rid of scope breakpoints. */ + delete_breakpoint (b); + break; + + case bp_watchpoint: + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_access_watchpoint: + + /* Likewise for watchpoints on local expressions. */ + if (b->exp_valid_block != NULL) + delete_breakpoint (b); + if (context == inf_starting) + { + /* Reset val field to force reread of starting value + in insert_breakpoints. */ + if (b->val) + value_free (b->val); + b->val = NULL; + } + break; + default: + /* Likewise for exception catchpoints in dynamic-linked + executables where required */ + if (ep_is_exception_catchpoint (b) && + exception_catchpoints_are_fragile) + { + warning_needed = 1; + delete_breakpoint (b); + } + break; + } + } + + if (exception_catchpoints_are_fragile) + exception_support_initialized = 0; + + /* Don't issue the warning unless it's really needed... */ + if (warning_needed && (context != inf_exited)) + { + warning ("Exception catchpoints from last run were deleted."); + warning ("You must reinsert them explicitly."); + warning_needed = 0; + } +} + +/* breakpoint_here_p (PC) returns non-zero if an enabled breakpoint + exists at PC. It returns ordinary_breakpoint_here if it's an + ordinary breakpoint, or permanent_breakpoint_here if it's a + permanent breakpoint. + - When continuing from a location with an ordinary breakpoint, we + actually single step once before calling insert_breakpoints. + - When continuing from a localion with a permanent breakpoint, we + need to use the `SKIP_PERMANENT_BREAKPOINT' macro, provided by + the target, to advance the PC past the breakpoint. */ + +enum breakpoint_here +breakpoint_here_p (CORE_ADDR pc) +{ + register struct breakpoint *b; + int any_breakpoint_here = 0; + + ALL_BREAKPOINTS (b) + if ((b->enable_state == bp_enabled + || b->enable_state == bp_permanent) + && b->address == pc) /* bp is enabled and matches pc */ + { + if (overlay_debugging + && section_is_overlay (b->section) + && !section_is_mapped (b->section)) + continue; /* unmapped overlay -- can't be a match */ + else if (b->enable_state == bp_permanent) + return permanent_breakpoint_here; + else + any_breakpoint_here = 1; + } + + return any_breakpoint_here ? ordinary_breakpoint_here : 0; +} + + +/* breakpoint_inserted_here_p (PC) is just like breakpoint_here_p(), + but it only returns true if there is actually a breakpoint inserted + at PC. */ + +int +breakpoint_inserted_here_p (CORE_ADDR pc) +{ + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->inserted + && b->address == pc) /* bp is inserted and matches pc */ + { + if (overlay_debugging + && section_is_overlay (b->section) + && !section_is_mapped (b->section)) + continue; /* unmapped overlay -- can't be a match */ + else + return 1; + } + + return 0; +} + +/* Return nonzero if FRAME is a dummy frame. We can't use + PC_IN_CALL_DUMMY because figuring out the saved SP would take too + much time, at least using get_saved_register on the 68k. This + means that for this function to work right a port must use the + bp_call_dummy breakpoint. */ + +int +frame_in_dummy (struct frame_info *frame) +{ + struct breakpoint *b; + + if (!CALL_DUMMY_P) + return 0; + + if (USE_GENERIC_DUMMY_FRAMES) + return generic_pc_in_call_dummy (frame->pc, frame->frame, frame->frame); + + ALL_BREAKPOINTS (b) + { + if (b->type == bp_call_dummy + && b->frame == frame->frame + /* We need to check the PC as well as the frame on the sparc, + for signals.exp in the testsuite. */ + && (frame->pc + >= (b->address + - SIZEOF_CALL_DUMMY_WORDS / sizeof (LONGEST) * REGISTER_SIZE)) + && frame->pc <= b->address) + return 1; + } + return 0; +} + +/* breakpoint_thread_match (PC, PID) returns true if the breakpoint at + PC is valid for process/thread PID. */ + +int +breakpoint_thread_match (CORE_ADDR pc, ptid_t ptid) +{ + struct breakpoint *b; + int thread; + + thread = pid_to_thread_id (ptid); + + ALL_BREAKPOINTS (b) + if (b->enable_state != bp_disabled + && b->enable_state != bp_shlib_disabled + && b->enable_state != bp_call_disabled + && b->address == pc + && (b->thread == -1 || b->thread == thread)) + { + if (overlay_debugging + && section_is_overlay (b->section) + && !section_is_mapped (b->section)) + continue; /* unmapped overlay -- can't be a match */ + else + return 1; + } + + return 0; +} + + +/* bpstat stuff. External routines' interfaces are documented + in breakpoint.h. */ + +int +ep_is_catchpoint (struct breakpoint *ep) +{ + return + (ep->type == bp_catch_load) + || (ep->type == bp_catch_unload) + || (ep->type == bp_catch_fork) + || (ep->type == bp_catch_vfork) + || (ep->type == bp_catch_exec) + || (ep->type == bp_catch_catch) + || (ep->type == bp_catch_throw); + + /* ??rehrauer: Add more kinds here, as are implemented... */ +} + +int +ep_is_shlib_catchpoint (struct breakpoint *ep) +{ + return + (ep->type == bp_catch_load) + || (ep->type == bp_catch_unload); +} + +int +ep_is_exception_catchpoint (struct breakpoint *ep) +{ + return + (ep->type == bp_catch_catch) + || (ep->type == bp_catch_throw); +} + +/* Clear a bpstat so that it says we are not at any breakpoint. + Also free any storage that is part of a bpstat. */ + +void +bpstat_clear (bpstat *bsp) +{ + bpstat p; + bpstat q; + + if (bsp == 0) + return; + p = *bsp; + while (p != NULL) + { + q = p->next; + if (p->old_val != NULL) + value_free (p->old_val); + free_command_lines (&p->commands); + xfree (p); + p = q; + } + *bsp = NULL; +} + +/* Return a copy of a bpstat. Like "bs1 = bs2" but all storage that + is part of the bpstat is copied as well. */ + +bpstat +bpstat_copy (bpstat bs) +{ + bpstat p = NULL; + bpstat tmp; + bpstat retval = NULL; + + if (bs == NULL) + return bs; + + for (; bs != NULL; bs = bs->next) + { + tmp = (bpstat) xmalloc (sizeof (*tmp)); + memcpy (tmp, bs, sizeof (*tmp)); + if (p == NULL) + /* This is the first thing in the chain. */ + retval = tmp; + else + p->next = tmp; + p = tmp; + } + p->next = NULL; + return retval; +} + +/* Find the bpstat associated with this breakpoint */ + +bpstat +bpstat_find_breakpoint (bpstat bsp, struct breakpoint *breakpoint) +{ + if (bsp == NULL) + return NULL; + + for (; bsp != NULL; bsp = bsp->next) + { + if (bsp->breakpoint_at == breakpoint) + return bsp; + } + return NULL; +} + +/* Find a step_resume breakpoint associated with this bpstat. + (If there are multiple step_resume bp's on the list, this function + will arbitrarily pick one.) + + It is an error to use this function if BPSTAT doesn't contain a + step_resume breakpoint. + + See wait_for_inferior's use of this function. */ +struct breakpoint * +bpstat_find_step_resume_breakpoint (bpstat bsp) +{ + int current_thread; + + if (bsp == NULL) + error ("Internal error (bpstat_find_step_resume_breakpoint)"); + + current_thread = pid_to_thread_id (inferior_ptid); + + for (; bsp != NULL; bsp = bsp->next) + { + if ((bsp->breakpoint_at != NULL) && + (bsp->breakpoint_at->type == bp_step_resume) && + (bsp->breakpoint_at->thread == current_thread || + bsp->breakpoint_at->thread == -1)) + return bsp->breakpoint_at; + } + + error ("Internal error (no step_resume breakpoint found)"); +} + + +/* Return the breakpoint number of the first breakpoint we are stopped + at. *BSP upon return is a bpstat which points to the remaining + breakpoints stopped at (but which is not guaranteed to be good for + anything but further calls to bpstat_num). + Return 0 if passed a bpstat which does not indicate any breakpoints. */ + +int +bpstat_num (bpstat *bsp) +{ + struct breakpoint *b; + + if ((*bsp) == NULL) + return 0; /* No more breakpoint values */ + else + { + b = (*bsp)->breakpoint_at; + *bsp = (*bsp)->next; + if (b == NULL) + return -1; /* breakpoint that's been deleted since */ + else + return b->number; /* We have its number */ + } +} + +/* Modify BS so that the actions will not be performed. */ + +void +bpstat_clear_actions (bpstat bs) +{ + for (; bs != NULL; bs = bs->next) + { + free_command_lines (&bs->commands); + if (bs->old_val != NULL) + { + value_free (bs->old_val); + bs->old_val = NULL; + } + } +} + +/* Stub for cleaning up our state if we error-out of a breakpoint command */ +/* ARGSUSED */ +static void +cleanup_executing_breakpoints (PTR ignore) +{ + executing_breakpoint_commands = 0; +} + +/* Execute all the commands associated with all the breakpoints at this + location. Any of these commands could cause the process to proceed + beyond this point, etc. We look out for such changes by checking + the global "breakpoint_proceeded" after each command. */ + +void +bpstat_do_actions (bpstat *bsp) +{ + bpstat bs; + struct cleanup *old_chain; + struct command_line *cmd; + + /* Avoid endless recursion if a `source' command is contained + in bs->commands. */ + if (executing_breakpoint_commands) + return; + + executing_breakpoint_commands = 1; + old_chain = make_cleanup (cleanup_executing_breakpoints, 0); + +top: + /* Note that (as of this writing), our callers all appear to + be passing us the address of global stop_bpstat. And, if + our calls to execute_control_command cause the inferior to + proceed, that global (and hence, *bsp) will change. + + We must be careful to not touch *bsp unless the inferior + has not proceeded. */ + + /* This pointer will iterate over the list of bpstat's. */ + bs = *bsp; + + breakpoint_proceeded = 0; + for (; bs != NULL; bs = bs->next) + { + cmd = bs->commands; + while (cmd != NULL) + { + execute_control_command (cmd); + + if (breakpoint_proceeded) + break; + else + cmd = cmd->next; + } + if (breakpoint_proceeded) + /* The inferior is proceeded by the command; bomb out now. + The bpstat chain has been blown away by wait_for_inferior. + But since execution has stopped again, there is a new bpstat + to look at, so start over. */ + goto top; + else + free_command_lines (&bs->commands); + } + do_cleanups (old_chain); +} + +/* This is the normal print function for a bpstat. In the future, + much of this logic could (should?) be moved to bpstat_stop_status, + by having it set different print_it values. + + Current scheme: When we stop, bpstat_print() is called. It loops + through the bpstat list of things causing this stop, calling the + print_bp_stop_message function on each one. The behavior of the + print_bp_stop_message function depends on the print_it field of + bpstat. If such field so indicates, call this function here. + + Return values from this routine (ultimately used by bpstat_print() + and normal_stop() to decide what to do): + PRINT_NOTHING: Means we already printed all we needed to print, + don't print anything else. + PRINT_SRC_ONLY: Means we printed something, and we do *not* desire + that something to be followed by a location. + PRINT_SCR_AND_LOC: Means we printed something, and we *do* desire + that something to be followed by a location. + PRINT_UNKNOWN: Means we printed nothing or we need to do some more + analysis. */ + +static enum print_stop_action +print_it_typical (bpstat bs) +{ + struct cleanup *old_chain; + struct ui_stream *stb; + stb = ui_out_stream_new (uiout); + old_chain = make_cleanup_ui_out_stream_delete (stb); + /* bs->breakpoint_at can be NULL if it was a momentary breakpoint + which has since been deleted. */ + if (bs->breakpoint_at == NULL) + return PRINT_UNKNOWN; + + switch (bs->breakpoint_at->type) + { + case bp_breakpoint: + case bp_hardware_breakpoint: + annotate_breakpoint (bs->breakpoint_at->number); + ui_out_text (uiout, "\nBreakpoint "); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string (uiout, "reason", "breakpoint-hit"); + ui_out_field_int (uiout, "bkptno", bs->breakpoint_at->number); + ui_out_text (uiout, ", "); + return PRINT_SRC_AND_LOC; + break; + + case bp_shlib_event: + /* Did we stop because the user set the stop_on_solib_events + variable? (If so, we report this as a generic, "Stopped due + to shlib event" message.) */ + printf_filtered ("Stopped due to shared library event\n"); + return PRINT_NOTHING; + break; + + case bp_thread_event: + /* Not sure how we will get here. + GDB should not stop for these breakpoints. */ + printf_filtered ("Thread Event Breakpoint: gdb should not stop!\n"); + return PRINT_NOTHING; + break; + + case bp_overlay_event: + /* By analogy with the thread event, GDB should not stop for these. */ + printf_filtered ("Overlay Event Breakpoint: gdb should not stop!\n"); + return PRINT_NOTHING; + break; + + case bp_catch_load: + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (", bs->breakpoint_at->number); + printf_filtered ("loaded"); + printf_filtered (" %s), ", bs->breakpoint_at->triggered_dll_pathname); + return PRINT_SRC_AND_LOC; + break; + + case bp_catch_unload: + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (", bs->breakpoint_at->number); + printf_filtered ("unloaded"); + printf_filtered (" %s), ", bs->breakpoint_at->triggered_dll_pathname); + return PRINT_SRC_AND_LOC; + break; + + case bp_catch_fork: + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (", bs->breakpoint_at->number); + printf_filtered ("forked"); + printf_filtered (" process %d), ", + bs->breakpoint_at->forked_inferior_pid); + return PRINT_SRC_AND_LOC; + break; + + case bp_catch_vfork: + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (", bs->breakpoint_at->number); + printf_filtered ("vforked"); + printf_filtered (" process %d), ", + bs->breakpoint_at->forked_inferior_pid); + return PRINT_SRC_AND_LOC; + break; + + case bp_catch_exec: + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (exec'd %s), ", + bs->breakpoint_at->number, + bs->breakpoint_at->exec_pathname); + return PRINT_SRC_AND_LOC; + break; + + case bp_catch_catch: + if (current_exception_event && + (CURRENT_EXCEPTION_KIND == EX_EVENT_CATCH)) + { + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (exception caught), ", + bs->breakpoint_at->number); + printf_filtered ("throw location "); + if (CURRENT_EXCEPTION_THROW_PC && CURRENT_EXCEPTION_THROW_LINE) + printf_filtered ("%s:%d", + CURRENT_EXCEPTION_THROW_FILE, + CURRENT_EXCEPTION_THROW_LINE); + else + printf_filtered ("unknown"); + + printf_filtered (", catch location "); + if (CURRENT_EXCEPTION_CATCH_PC && CURRENT_EXCEPTION_CATCH_LINE) + printf_filtered ("%s:%d", + CURRENT_EXCEPTION_CATCH_FILE, + CURRENT_EXCEPTION_CATCH_LINE); + else + printf_filtered ("unknown"); + + printf_filtered ("\n"); + /* don't bother to print location frame info */ + return PRINT_SRC_ONLY; + } + else + { + /* really throw, some other bpstat will handle it */ + return PRINT_UNKNOWN; + } + break; + + case bp_catch_throw: + if (current_exception_event && + (CURRENT_EXCEPTION_KIND == EX_EVENT_THROW)) + { + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (exception thrown), ", + bs->breakpoint_at->number); + printf_filtered ("throw location "); + if (CURRENT_EXCEPTION_THROW_PC && CURRENT_EXCEPTION_THROW_LINE) + printf_filtered ("%s:%d", + CURRENT_EXCEPTION_THROW_FILE, + CURRENT_EXCEPTION_THROW_LINE); + else + printf_filtered ("unknown"); + + printf_filtered (", catch location "); + if (CURRENT_EXCEPTION_CATCH_PC && CURRENT_EXCEPTION_CATCH_LINE) + printf_filtered ("%s:%d", + CURRENT_EXCEPTION_CATCH_FILE, + CURRENT_EXCEPTION_CATCH_LINE); + else + printf_filtered ("unknown"); + + printf_filtered ("\n"); + /* don't bother to print location frame info */ + return PRINT_SRC_ONLY; + } + else + { + /* really catch, some other bpstat will handle it */ + return PRINT_UNKNOWN; + } + break; + + case bp_watchpoint: + case bp_hardware_watchpoint: + if (bs->old_val != NULL) + { + annotate_watchpoint (bs->breakpoint_at->number); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string (uiout, "reason", "watchpoint-trigger"); + mention (bs->breakpoint_at); + ui_out_tuple_begin (uiout, "value"); + ui_out_text (uiout, "\nOld value = "); + value_print (bs->old_val, stb->stream, 0, Val_pretty_default); + ui_out_field_stream (uiout, "old", stb); + ui_out_text (uiout, "\nNew value = "); + value_print (bs->breakpoint_at->val, stb->stream, 0, Val_pretty_default); + ui_out_field_stream (uiout, "new", stb); + ui_out_tuple_end (uiout); + ui_out_text (uiout, "\n"); + value_free (bs->old_val); + bs->old_val = NULL; + } + /* More than one watchpoint may have been triggered. */ + return PRINT_UNKNOWN; + break; + + case bp_read_watchpoint: + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string (uiout, "reason", "read-watchpoint-trigger"); + mention (bs->breakpoint_at); + ui_out_tuple_begin (uiout, "value"); + ui_out_text (uiout, "\nValue = "); + value_print (bs->breakpoint_at->val, stb->stream, 0, Val_pretty_default); + ui_out_field_stream (uiout, "value", stb); + ui_out_tuple_end (uiout); + ui_out_text (uiout, "\n"); + return PRINT_UNKNOWN; + break; + + case bp_access_watchpoint: + if (bs->old_val != NULL) + { + annotate_watchpoint (bs->breakpoint_at->number); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string (uiout, "reason", "access-watchpoint-trigger"); + mention (bs->breakpoint_at); + ui_out_tuple_begin (uiout, "value"); + ui_out_text (uiout, "\nOld value = "); + value_print (bs->old_val, stb->stream, 0, Val_pretty_default); + ui_out_field_stream (uiout, "old", stb); + value_free (bs->old_val); + bs->old_val = NULL; + ui_out_text (uiout, "\nNew value = "); + } + else + { + mention (bs->breakpoint_at); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string (uiout, "reason", "access-watchpoint-trigger"); + ui_out_tuple_begin (uiout, "value"); + ui_out_text (uiout, "\nValue = "); + } + value_print (bs->breakpoint_at->val, stb->stream, 0,Val_pretty_default); + ui_out_field_stream (uiout, "new", stb); + ui_out_tuple_end (uiout); + ui_out_text (uiout, "\n"); + return PRINT_UNKNOWN; + break; + + /* Fall through, we don't deal with these types of breakpoints + here. */ + + case bp_finish: + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string (uiout, "reason", "function-finished"); + return PRINT_UNKNOWN; + break; + + case bp_until: + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string (uiout, "reason", "location-reached"); + return PRINT_UNKNOWN; + break; + + case bp_none: + case bp_longjmp: + case bp_longjmp_resume: + case bp_step_resume: + case bp_through_sigtramp: + case bp_watchpoint_scope: + case bp_call_dummy: + default: + return PRINT_UNKNOWN; + } +} + +/* Generic routine for printing messages indicating why we + stopped. The behavior of this function depends on the value + 'print_it' in the bpstat structure. Under some circumstances we + may decide not to print anything here and delegate the task to + normal_stop(). */ + +static enum print_stop_action +print_bp_stop_message (bpstat bs) +{ + switch (bs->print_it) + { + case print_it_noop: + /* Nothing should be printed for this bpstat entry. */ + return PRINT_UNKNOWN; + break; + + case print_it_done: + /* We still want to print the frame, but we already printed the + relevant messages. */ + return PRINT_SRC_AND_LOC; + break; + + case print_it_normal: + /* Normal case, we handle everything in print_it_typical. */ + return print_it_typical (bs); + break; + default: + internal_error (__FILE__, __LINE__, + "print_bp_stop_message: unrecognized enum value"); + break; + } +} + +/* Print a message indicating what happened. This is called from + normal_stop(). The input to this routine is the head of the bpstat + list - a list of the eventpoints that caused this stop. This + routine calls the generic print routine for printing a message + about reasons for stopping. This will print (for example) the + "Breakpoint n," part of the output. The return value of this + routine is one of: + + PRINT_UNKNOWN: Means we printed nothing + PRINT_SRC_AND_LOC: Means we printed something, and expect subsequent + code to print the location. An example is + "Breakpoint 1, " which should be followed by + the location. + PRINT_SRC_ONLY: Means we printed something, but there is no need + to also print the location part of the message. + An example is the catch/throw messages, which + don't require a location appended to the end. + PRINT_NOTHING: We have done some printing and we don't need any + further info to be printed.*/ + +enum print_stop_action +bpstat_print (bpstat bs) +{ + int val; + + /* Maybe another breakpoint in the chain caused us to stop. + (Currently all watchpoints go on the bpstat whether hit or not. + That probably could (should) be changed, provided care is taken + with respect to bpstat_explains_signal). */ + for (; bs; bs = bs->next) + { + val = print_bp_stop_message (bs); + if (val == PRINT_SRC_ONLY + || val == PRINT_SRC_AND_LOC + || val == PRINT_NOTHING) + return val; + } + + /* We reached the end of the chain, or we got a null BS to start + with and nothing was printed. */ + return PRINT_UNKNOWN; +} + +/* Evaluate the expression EXP and return 1 if value is zero. + This is used inside a catch_errors to evaluate the breakpoint condition. + The argument is a "struct expression *" that has been cast to char * to + make it pass through catch_errors. */ + +static int +breakpoint_cond_eval (PTR exp) +{ + struct value *mark = value_mark (); + int i = !value_true (evaluate_expression ((struct expression *) exp)); + value_free_to_mark (mark); + return i; +} + +/* Allocate a new bpstat and chain it to the current one. */ + +static bpstat +bpstat_alloc (struct breakpoint *b, bpstat cbs /* Current "bs" value */ ) +{ + bpstat bs; + + bs = (bpstat) xmalloc (sizeof (*bs)); + cbs->next = bs; + bs->breakpoint_at = b; + /* If the condition is false, etc., don't do the commands. */ + bs->commands = NULL; + bs->old_val = NULL; + bs->print_it = print_it_normal; + return bs; +} + +/* Possible return values for watchpoint_check (this can't be an enum + because of check_errors). */ +/* The watchpoint has been deleted. */ +#define WP_DELETED 1 +/* The value has changed. */ +#define WP_VALUE_CHANGED 2 +/* The value has not changed. */ +#define WP_VALUE_NOT_CHANGED 3 + +#define BP_TEMPFLAG 1 +#define BP_HARDWAREFLAG 2 + +/* Check watchpoint condition. */ + +static int +watchpoint_check (PTR p) +{ + bpstat bs = (bpstat) p; + struct breakpoint *b; + struct frame_info *fr; + int within_current_scope; + + b = bs->breakpoint_at; + + if (b->exp_valid_block == NULL) + within_current_scope = 1; + else + { + /* There is no current frame at this moment. If we're going to have + any chance of handling watchpoints on local variables, we'll need + the frame chain (so we can determine if we're in scope). */ + reinit_frame_cache (); + fr = frame_find_by_id (b->watchpoint_frame); + within_current_scope = (fr != NULL); + /* in_function_epilogue_p() returns a non-zero value if we're still + in the function but the stack frame has already been invalidated. + Since we can't rely on the values of local variables after the + stack has been destroyed, we are treating the watchpoint in that + state as `not changed' without further checking. */ + if (within_current_scope && fr == get_current_frame () + && gdbarch_in_function_epilogue_p (current_gdbarch, read_pc ())) + return WP_VALUE_NOT_CHANGED; + if (within_current_scope) + /* If we end up stopping, the current frame will get selected + in normal_stop. So this call to select_frame won't affect + the user. */ + select_frame (fr); + } + + if (within_current_scope) + { + /* We use value_{,free_to_}mark because it could be a + *long* time before we return to the command level and + call free_all_values. We can't call free_all_values because + we might be in the middle of evaluating a function call. */ + + struct value *mark = value_mark (); + struct value *new_val = evaluate_expression (bs->breakpoint_at->exp); + if (!value_equal (b->val, new_val)) + { + release_value (new_val); + value_free_to_mark (mark); + bs->old_val = b->val; + b->val = new_val; + /* We will stop here */ + return WP_VALUE_CHANGED; + } + else + { + /* Nothing changed, don't do anything. */ + value_free_to_mark (mark); + /* We won't stop here */ + return WP_VALUE_NOT_CHANGED; + } + } + else + { + /* This seems like the only logical thing to do because + if we temporarily ignored the watchpoint, then when + we reenter the block in which it is valid it contains + garbage (in the case of a function, it may have two + garbage values, one before and one after the prologue). + So we can't even detect the first assignment to it and + watch after that (since the garbage may or may not equal + the first value assigned). */ + /* We print all the stop information in print_it_typical(), but + in this case, by the time we call print_it_typical() this bp + will be deleted already. So we have no choice but print the + information here. */ + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string (uiout, "reason", "watchpoint-scope"); + ui_out_text (uiout, "\nWatchpoint "); + ui_out_field_int (uiout, "wpnum", bs->breakpoint_at->number); + ui_out_text (uiout, " deleted because the program has left the block in\n\ +which its expression is valid.\n"); + + if (b->related_breakpoint) + b->related_breakpoint->disposition = disp_del_at_next_stop; + b->disposition = disp_del_at_next_stop; + + return WP_DELETED; + } +} + +/* Get a bpstat associated with having just stopped at address *PC + and frame address CORE_ADDRESS. Update *PC to point at the + breakpoint (if we hit a breakpoint). NOT_A_SW_BREAKPOINT is nonzero + if this is known to not be a real breakpoint (it could still be a + watchpoint, though). */ + +/* Determine whether we stopped at a breakpoint, etc, or whether we + don't understand this stop. Result is a chain of bpstat's such that: + + if we don't understand the stop, the result is a null pointer. + + if we understand why we stopped, the result is not null. + + Each element of the chain refers to a particular breakpoint or + watchpoint at which we have stopped. (We may have stopped for + several reasons concurrently.) + + Each element of the chain has valid next, breakpoint_at, + commands, FIXME??? fields. */ + +bpstat +bpstat_stop_status (CORE_ADDR *pc, int not_a_sw_breakpoint) +{ + register struct breakpoint *b, *temp; + CORE_ADDR bp_addr; + /* True if we've hit a breakpoint (as opposed to a watchpoint). */ + int real_breakpoint = 0; + /* Root of the chain of bpstat's */ + struct bpstats root_bs[1]; + /* Pointer to the last thing in the chain currently. */ + bpstat bs = root_bs; + static char message1[] = + "Error evaluating expression for watchpoint %d\n"; + char message[sizeof (message1) + 30 /* slop */ ]; + + /* Get the address where the breakpoint would have been. The + "not_a_sw_breakpoint" argument is meant to distinguish between a + breakpoint trap event and a trace/singlestep trap event. For a + trace/singlestep trap event, we would not want to subtract + DECR_PC_AFTER_BREAK from the PC. */ + + bp_addr = *pc - (not_a_sw_breakpoint ? 0 : DECR_PC_AFTER_BREAK); + + ALL_BREAKPOINTS_SAFE (b, temp) + { + if (b->enable_state == bp_disabled + || b->enable_state == bp_shlib_disabled + || b->enable_state == bp_call_disabled) + continue; + + if (b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint + && b->type != bp_read_watchpoint + && b->type != bp_access_watchpoint + && b->type != bp_hardware_breakpoint + && b->type != bp_catch_fork + && b->type != bp_catch_vfork + && b->type != bp_catch_exec + && b->type != bp_catch_catch + && b->type != bp_catch_throw) /* a non-watchpoint bp */ + { + if (b->address != bp_addr) /* address doesn't match */ + continue; + if (overlay_debugging /* unmapped overlay section */ + && section_is_overlay (b->section) + && !section_is_mapped (b->section)) + continue; + } + + if (b->type == bp_hardware_breakpoint) + { + if (b->address != (*pc - DECR_PC_AFTER_HW_BREAK)) + continue; + if (overlay_debugging /* unmapped overlay section */ + && section_is_overlay (b->section) + && !section_is_mapped (b->section)) + continue; + } + + /* Is this a catchpoint of a load or unload? If so, did we + get a load or unload of the specified library? If not, + ignore it. */ + if ((b->type == bp_catch_load) +#if defined(SOLIB_HAVE_LOAD_EVENT) + && (!SOLIB_HAVE_LOAD_EVENT (PIDGET (inferior_ptid)) + || ((b->dll_pathname != NULL) + && (strcmp (b->dll_pathname, + SOLIB_LOADED_LIBRARY_PATHNAME ( + PIDGET (inferior_ptid))) + != 0))) +#endif + ) + continue; + + if ((b->type == bp_catch_unload) +#if defined(SOLIB_HAVE_UNLOAD_EVENT) + && (!SOLIB_HAVE_UNLOAD_EVENT (PIDGET (inferior_ptid)) + || ((b->dll_pathname != NULL) + && (strcmp (b->dll_pathname, + SOLIB_UNLOADED_LIBRARY_PATHNAME ( + PIDGET (inferior_ptid))) + != 0))) +#endif + ) + continue; + + if ((b->type == bp_catch_fork) + && !target_has_forked (PIDGET (inferior_ptid), + &b->forked_inferior_pid)) + continue; + + if ((b->type == bp_catch_vfork) + && !target_has_vforked (PIDGET (inferior_ptid), + &b->forked_inferior_pid)) + continue; + + if ((b->type == bp_catch_exec) + && !target_has_execd (PIDGET (inferior_ptid), &b->exec_pathname)) + continue; + + if (ep_is_exception_catchpoint (b) && + !(current_exception_event = target_get_current_exception_event ())) + continue; + + /* Come here if it's a watchpoint, or if the break address matches */ + + bs = bpstat_alloc (b, bs); /* Alloc a bpstat to explain stop */ + + /* Watchpoints may change this, if not found to have triggered. */ + bs->stop = 1; + bs->print = 1; + + sprintf (message, message1, b->number); + if (b->type == bp_watchpoint || + b->type == bp_hardware_watchpoint) + { + switch (catch_errors (watchpoint_check, bs, message, + RETURN_MASK_ALL)) + { + case WP_DELETED: + /* We've already printed what needs to be printed. */ + /* Actually this is superfluous, because by the time we + call print_it_typical() the wp will be already deleted, + and the function will return immediately. */ + bs->print_it = print_it_done; + /* Stop. */ + break; + case WP_VALUE_CHANGED: + /* Stop. */ + ++(b->hit_count); + break; + case WP_VALUE_NOT_CHANGED: + /* Don't stop. */ + bs->print_it = print_it_noop; + bs->stop = 0; + continue; + default: + /* Can't happen. */ + /* FALLTHROUGH */ + case 0: + /* Error from catch_errors. */ + printf_filtered ("Watchpoint %d deleted.\n", b->number); + if (b->related_breakpoint) + b->related_breakpoint->disposition = disp_del_at_next_stop; + b->disposition = disp_del_at_next_stop; + /* We've already printed what needs to be printed. */ + bs->print_it = print_it_done; + + /* Stop. */ + break; + } + } + else if (b->type == bp_read_watchpoint || + b->type == bp_access_watchpoint) + { + CORE_ADDR addr; + struct value *v; + int found = 0; + + addr = target_stopped_data_address (); + if (addr == 0) + continue; + for (v = b->val_chain; v; v = v->next) + { + if (VALUE_LVAL (v) == lval_memory + && ! VALUE_LAZY (v)) + { + struct type *vtype = check_typedef (VALUE_TYPE (v)); + + if (v == b->val_chain + || (TYPE_CODE (vtype) != TYPE_CODE_STRUCT + && TYPE_CODE (vtype) != TYPE_CODE_ARRAY)) + { + CORE_ADDR vaddr; + + vaddr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); + /* Exact match not required. Within range is + sufficient. */ + if (addr >= vaddr && + addr < vaddr + TYPE_LENGTH (VALUE_TYPE (v))) + found = 1; + } + } + } + if (found) + switch (catch_errors (watchpoint_check, bs, message, + RETURN_MASK_ALL)) + { + case WP_DELETED: + /* We've already printed what needs to be printed. */ + bs->print_it = print_it_done; + /* Stop. */ + break; + case WP_VALUE_CHANGED: + if (b->type == bp_read_watchpoint) + { + /* Don't stop: read watchpoints shouldn't fire if + the value has changed. This is for targets which + cannot set read-only watchpoints. */ + bs->print_it = print_it_noop; + bs->stop = 0; + continue; + } + ++(b->hit_count); + break; + case WP_VALUE_NOT_CHANGED: + /* Stop. */ + ++(b->hit_count); + break; + default: + /* Can't happen. */ + case 0: + /* Error from catch_errors. */ + printf_filtered ("Watchpoint %d deleted.\n", b->number); + if (b->related_breakpoint) + b->related_breakpoint->disposition = disp_del_at_next_stop; + b->disposition = disp_del_at_next_stop; + /* We've already printed what needs to be printed. */ + bs->print_it = print_it_done; + break; + } + else /* found == 0 */ + { + /* This is a case where some watchpoint(s) triggered, + but not at the address of this watchpoint (FOUND + was left zero). So don't print anything for this + watchpoint. */ + bs->print_it = print_it_noop; + bs->stop = 0; + continue; + } + } + else + { + /* By definition, an encountered breakpoint is a triggered + breakpoint. */ + ++(b->hit_count); + + real_breakpoint = 1; + } + + if (b->frame && + b->frame != (get_current_frame ())->frame) + bs->stop = 0; + else + { + int value_is_zero = 0; + + if (b->cond) + { + /* Need to select the frame, with all that implies + so that the conditions will have the right context. */ + select_frame (get_current_frame ()); + value_is_zero + = catch_errors (breakpoint_cond_eval, (b->cond), + "Error in testing breakpoint condition:\n", + RETURN_MASK_ALL); + /* FIXME-someday, should give breakpoint # */ + free_all_values (); + } + if (b->cond && value_is_zero) + { + bs->stop = 0; + /* Don't consider this a hit. */ + --(b->hit_count); + } + else if (b->ignore_count > 0) + { + b->ignore_count--; + annotate_ignore_count_change (); + bs->stop = 0; + } + else + { + /* We will stop here */ + if (b->disposition == disp_disable) + b->enable_state = bp_disabled; + bs->commands = copy_command_lines (b->commands); + if (b->silent) + bs->print = 0; + if (bs->commands && + (STREQ ("silent", bs->commands->line) || + (xdb_commands && STREQ ("Q", bs->commands->line)))) + { + bs->commands = bs->commands->next; + bs->print = 0; + } + } + } + /* Print nothing for this entry if we dont stop or if we dont print. */ + if (bs->stop == 0 || bs->print == 0) + bs->print_it = print_it_noop; + } + + bs->next = NULL; /* Terminate the chain */ + bs = root_bs->next; /* Re-grab the head of the chain */ + + if (real_breakpoint && bs) + { + if (bs->breakpoint_at->type == bp_hardware_breakpoint) + { + if (DECR_PC_AFTER_HW_BREAK != 0) + { + *pc = *pc - DECR_PC_AFTER_HW_BREAK; + write_pc (*pc); + } + } + else + { + if (DECR_PC_AFTER_BREAK != 0 || must_shift_inst_regs) + { + *pc = bp_addr; +#if defined (SHIFT_INST_REGS) + SHIFT_INST_REGS (); +#else /* No SHIFT_INST_REGS. */ + write_pc (bp_addr); +#endif /* No SHIFT_INST_REGS. */ + } + } + } + + /* The value of a hardware watchpoint hasn't changed, but the + intermediate memory locations we are watching may have. */ + if (bs && !bs->stop && + (bs->breakpoint_at->type == bp_hardware_watchpoint || + bs->breakpoint_at->type == bp_read_watchpoint || + bs->breakpoint_at->type == bp_access_watchpoint)) + { + remove_breakpoints (); + insert_breakpoints (); + } + return bs; +} + +/* Tell what to do about this bpstat. */ +struct bpstat_what +bpstat_what (bpstat bs) +{ + /* Classify each bpstat as one of the following. */ + enum class + { + /* This bpstat element has no effect on the main_action. */ + no_effect = 0, + + /* There was a watchpoint, stop but don't print. */ + wp_silent, + + /* There was a watchpoint, stop and print. */ + wp_noisy, + + /* There was a breakpoint but we're not stopping. */ + bp_nostop, + + /* There was a breakpoint, stop but don't print. */ + bp_silent, + + /* There was a breakpoint, stop and print. */ + bp_noisy, + + /* We hit the longjmp breakpoint. */ + long_jump, + + /* We hit the longjmp_resume breakpoint. */ + long_resume, + + /* We hit the step_resume breakpoint. */ + step_resume, + + /* We hit the through_sigtramp breakpoint. */ + through_sig, + + /* We hit the shared library event breakpoint. */ + shlib_event, + + /* We caught a shared library event. */ + catch_shlib_event, + + /* This is just used to count how many enums there are. */ + class_last + }; + + /* Here is the table which drives this routine. So that we can + format it pretty, we define some abbreviations for the + enum bpstat_what codes. */ +#define kc BPSTAT_WHAT_KEEP_CHECKING +#define ss BPSTAT_WHAT_STOP_SILENT +#define sn BPSTAT_WHAT_STOP_NOISY +#define sgl BPSTAT_WHAT_SINGLE +#define slr BPSTAT_WHAT_SET_LONGJMP_RESUME +#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME +#define clrs BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE +#define sr BPSTAT_WHAT_STEP_RESUME +#define ts BPSTAT_WHAT_THROUGH_SIGTRAMP +#define shl BPSTAT_WHAT_CHECK_SHLIBS +#define shlr BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK + +/* "Can't happen." Might want to print an error message. + abort() is not out of the question, but chances are GDB is just + a bit confused, not unusable. */ +#define err BPSTAT_WHAT_STOP_NOISY + + /* Given an old action and a class, come up with a new action. */ + /* One interesting property of this table is that wp_silent is the same + as bp_silent and wp_noisy is the same as bp_noisy. That is because + after stopping, the check for whether to step over a breakpoint + (BPSTAT_WHAT_SINGLE type stuff) is handled in proceed() without + reference to how we stopped. We retain separate wp_silent and + bp_silent codes in case we want to change that someday. + + Another possibly interesting property of this table is that + there's a partial ordering, priority-like, of the actions. Once + you've decided that some action is appropriate, you'll never go + back and decide something of a lower priority is better. The + ordering is: + + kc < clr sgl shl shlr slr sn sr ss ts + sgl < clrs shl shlr slr sn sr ss ts + slr < err shl shlr sn sr ss ts + clr < clrs err shl shlr sn sr ss ts + clrs < err shl shlr sn sr ss ts + ss < shl shlr sn sr ts + sn < shl shlr sr ts + sr < shl shlr ts + shl < shlr + ts < + shlr < + + What I think this means is that we don't need a damned table + here. If you just put the rows and columns in the right order, + it'd look awfully regular. We could simply walk the bpstat list + and choose the highest priority action we find, with a little + logic to handle the 'err' cases, and the CLEAR_LONGJMP_RESUME/ + CLEAR_LONGJMP_RESUME_SINGLE distinction (which breakpoint.h says + is messy anyway). */ + + /* step_resume entries: a step resume breakpoint overrides another + breakpoint of signal handling (see comment in wait_for_inferior + at first PC_IN_SIGTRAMP where we set the step_resume breakpoint). */ + /* We handle the through_sigtramp_breakpoint the same way; having both + one of those and a step_resume_breakpoint is probably very rare (?). */ + + static const enum bpstat_what_main_action + table[(int) class_last][(int) BPSTAT_WHAT_LAST] = + { + /* old action */ + /* kc ss sn sgl slr clr clrs sr ts shl shlr + */ +/*no_effect */ + {kc, ss, sn, sgl, slr, clr, clrs, sr, ts, shl, shlr}, +/*wp_silent */ + {ss, ss, sn, ss, ss, ss, ss, sr, ts, shl, shlr}, +/*wp_noisy */ + {sn, sn, sn, sn, sn, sn, sn, sr, ts, shl, shlr}, +/*bp_nostop */ + {sgl, ss, sn, sgl, slr, clrs, clrs, sr, ts, shl, shlr}, +/*bp_silent */ + {ss, ss, sn, ss, ss, ss, ss, sr, ts, shl, shlr}, +/*bp_noisy */ + {sn, sn, sn, sn, sn, sn, sn, sr, ts, shl, shlr}, +/*long_jump */ + {slr, ss, sn, slr, slr, err, err, sr, ts, shl, shlr}, +/*long_resume */ + {clr, ss, sn, clrs, err, err, err, sr, ts, shl, shlr}, +/*step_resume */ + {sr, sr, sr, sr, sr, sr, sr, sr, ts, shl, shlr}, +/*through_sig */ + {ts, ts, ts, ts, ts, ts, ts, ts, ts, shl, shlr}, +/*shlib */ + {shl, shl, shl, shl, shl, shl, shl, shl, ts, shl, shlr}, +/*catch_shlib */ + {shlr, shlr, shlr, shlr, shlr, shlr, shlr, shlr, ts, shlr, shlr} + }; + +#undef kc +#undef ss +#undef sn +#undef sgl +#undef slr +#undef clr +#undef clrs +#undef err +#undef sr +#undef ts +#undef shl +#undef shlr + enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING; + struct bpstat_what retval; + + retval.call_dummy = 0; + for (; bs != NULL; bs = bs->next) + { + enum class bs_class = no_effect; + if (bs->breakpoint_at == NULL) + /* I suspect this can happen if it was a momentary breakpoint + which has since been deleted. */ + continue; + switch (bs->breakpoint_at->type) + { + case bp_none: + continue; + + case bp_breakpoint: + case bp_hardware_breakpoint: + case bp_until: + case bp_finish: + if (bs->stop) + { + if (bs->print) + bs_class = bp_noisy; + else + bs_class = bp_silent; + } + else + bs_class = bp_nostop; + break; + case bp_watchpoint: + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_access_watchpoint: + if (bs->stop) + { + if (bs->print) + bs_class = wp_noisy; + else + bs_class = wp_silent; + } + else + /* There was a watchpoint, but we're not stopping. + This requires no further action. */ + bs_class = no_effect; + break; + case bp_longjmp: + bs_class = long_jump; + break; + case bp_longjmp_resume: + bs_class = long_resume; + break; + case bp_step_resume: + if (bs->stop) + { + bs_class = step_resume; + } + else + /* It is for the wrong frame. */ + bs_class = bp_nostop; + break; + case bp_through_sigtramp: + bs_class = through_sig; + break; + case bp_watchpoint_scope: + bs_class = bp_nostop; + break; + case bp_shlib_event: + bs_class = shlib_event; + break; + case bp_thread_event: + case bp_overlay_event: + bs_class = bp_nostop; + break; + case bp_catch_load: + case bp_catch_unload: + /* Only if this catchpoint triggered should we cause the + step-out-of-dld behaviour. Otherwise, we ignore this + catchpoint. */ + if (bs->stop) + bs_class = catch_shlib_event; + else + bs_class = no_effect; + break; + case bp_catch_fork: + case bp_catch_vfork: + case bp_catch_exec: + if (bs->stop) + { + if (bs->print) + bs_class = bp_noisy; + else + bs_class = bp_silent; + } + else + /* There was a catchpoint, but we're not stopping. + This requires no further action. */ + bs_class = no_effect; + break; + case bp_catch_catch: + if (!bs->stop || CURRENT_EXCEPTION_KIND != EX_EVENT_CATCH) + bs_class = bp_nostop; + else if (bs->stop) + bs_class = bs->print ? bp_noisy : bp_silent; + break; + case bp_catch_throw: + if (!bs->stop || CURRENT_EXCEPTION_KIND != EX_EVENT_THROW) + bs_class = bp_nostop; + else if (bs->stop) + bs_class = bs->print ? bp_noisy : bp_silent; + break; + case bp_call_dummy: + /* Make sure the action is stop (silent or noisy), + so infrun.c pops the dummy frame. */ + bs_class = bp_silent; + retval.call_dummy = 1; + break; + } + current_action = table[(int) bs_class][(int) current_action]; + } + retval.main_action = current_action; + return retval; +} + +/* Nonzero if we should step constantly (e.g. watchpoints on machines + without hardware support). This isn't related to a specific bpstat, + just to things like whether watchpoints are set. */ + +int +bpstat_should_step (void) +{ + struct breakpoint *b; + ALL_BREAKPOINTS (b) + if (b->enable_state == bp_enabled && b->type == bp_watchpoint) + return 1; + return 0; +} + +/* Nonzero if there are enabled hardware watchpoints. */ +int +bpstat_have_active_hw_watchpoints (void) +{ + struct breakpoint *b; + ALL_BREAKPOINTS (b) + if ((b->enable_state == bp_enabled) && + (b->inserted) && + ((b->type == bp_hardware_watchpoint) || + (b->type == bp_read_watchpoint) || + (b->type == bp_access_watchpoint))) + return 1; + return 0; +} + + +/* Given a bpstat that records zero or more triggered eventpoints, this + function returns another bpstat which contains only the catchpoints + on that first list, if any. */ +void +bpstat_get_triggered_catchpoints (bpstat ep_list, bpstat *cp_list) +{ + struct bpstats root_bs[1]; + bpstat bs = root_bs; + struct breakpoint *ep; + char *dll_pathname; + + bpstat_clear (cp_list); + root_bs->next = NULL; + + for (; ep_list != NULL; ep_list = ep_list->next) + { + /* Is this eventpoint a catchpoint? If not, ignore it. */ + ep = ep_list->breakpoint_at; + if (ep == NULL) + break; + if ((ep->type != bp_catch_load) && + (ep->type != bp_catch_unload) && + (ep->type != bp_catch_catch) && + (ep->type != bp_catch_throw)) + /* pai: (temp) ADD fork/vfork here!! */ + continue; + + /* Yes; add it to the list. */ + bs = bpstat_alloc (ep, bs); + *bs = *ep_list; + bs->next = NULL; + bs = root_bs->next; + +#if defined(SOLIB_ADD) + /* Also, for each triggered catchpoint, tag it with the name of + the library that caused this trigger. (We copy the name now, + because it's only guaranteed to be available NOW, when the + catchpoint triggers. Clients who may wish to know the name + later must get it from the catchpoint itself.) */ + if (ep->triggered_dll_pathname != NULL) + xfree (ep->triggered_dll_pathname); + if (ep->type == bp_catch_load) + dll_pathname = SOLIB_LOADED_LIBRARY_PATHNAME ( + PIDGET (inferior_ptid)); + else + dll_pathname = SOLIB_UNLOADED_LIBRARY_PATHNAME ( + PIDGET (inferior_ptid)); +#else + dll_pathname = NULL; +#endif + if (dll_pathname) + { + ep->triggered_dll_pathname = (char *) + xmalloc (strlen (dll_pathname) + 1); + strcpy (ep->triggered_dll_pathname, dll_pathname); + } + else + ep->triggered_dll_pathname = NULL; + } + + *cp_list = bs; +} + +/* Print B to gdb_stdout. */ +static void +print_one_breakpoint (struct breakpoint *b, + CORE_ADDR *last_addr) +{ + register struct command_line *l; + register struct symbol *sym; + struct ep_type_description + { + enum bptype type; + char *description; + }; + static struct ep_type_description bptypes[] = + { + {bp_none, "?deleted?"}, + {bp_breakpoint, "breakpoint"}, + {bp_hardware_breakpoint, "hw breakpoint"}, + {bp_until, "until"}, + {bp_finish, "finish"}, + {bp_watchpoint, "watchpoint"}, + {bp_hardware_watchpoint, "hw watchpoint"}, + {bp_read_watchpoint, "read watchpoint"}, + {bp_access_watchpoint, "acc watchpoint"}, + {bp_longjmp, "longjmp"}, + {bp_longjmp_resume, "longjmp resume"}, + {bp_step_resume, "step resume"}, + {bp_through_sigtramp, "sigtramp"}, + {bp_watchpoint_scope, "watchpoint scope"}, + {bp_call_dummy, "call dummy"}, + {bp_shlib_event, "shlib events"}, + {bp_thread_event, "thread events"}, + {bp_overlay_event, "overlay events"}, + {bp_catch_load, "catch load"}, + {bp_catch_unload, "catch unload"}, + {bp_catch_fork, "catch fork"}, + {bp_catch_vfork, "catch vfork"}, + {bp_catch_exec, "catch exec"}, + {bp_catch_catch, "catch catch"}, + {bp_catch_throw, "catch throw"} + }; + + static char *bpdisps[] = + {"del", "dstp", "dis", "keep"}; + static char bpenables[] = "nynny"; + char wrap_indent[80]; + struct ui_stream *stb = ui_out_stream_new (uiout); + struct cleanup *old_chain = make_cleanup_ui_out_stream_delete (stb); + + annotate_record (); + ui_out_tuple_begin (uiout, "bkpt"); + + /* 1 */ + annotate_field (0); + ui_out_field_int (uiout, "number", b->number); + + /* 2 */ + annotate_field (1); + if (((int) b->type > (sizeof (bptypes) / sizeof (bptypes[0]))) + || ((int) b->type != bptypes[(int) b->type].type)) + internal_error (__FILE__, __LINE__, + "bptypes table does not describe type #%d.", + (int) b->type); + ui_out_field_string (uiout, "type", bptypes[(int) b->type].description); + + /* 3 */ + annotate_field (2); + ui_out_field_string (uiout, "disp", bpdisps[(int) b->disposition]); + + /* 4 */ + annotate_field (3); + ui_out_field_fmt (uiout, "enabled", "%c", bpenables[(int) b->enable_state]); + ui_out_spaces (uiout, 2); + + /* 5 and 6 */ + strcpy (wrap_indent, " "); + if (addressprint) + { + if (TARGET_ADDR_BIT <= 32) + strcat (wrap_indent, " "); + else + strcat (wrap_indent, " "); + } + switch (b->type) + { + case bp_none: + internal_error (__FILE__, __LINE__, + "print_one_breakpoint: bp_none encountered\n"); + break; + + case bp_watchpoint: + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_access_watchpoint: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + if (addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + print_expression (b->exp, stb->stream); + ui_out_field_stream (uiout, "what", stb); + break; + + case bp_catch_load: + case bp_catch_unload: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + if (addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + if (b->dll_pathname == NULL) + { + ui_out_field_string (uiout, "what", ""); + ui_out_spaces (uiout, 1); + } + else + { + ui_out_text (uiout, "library \""); + ui_out_field_string (uiout, "what", b->dll_pathname); + ui_out_text (uiout, "\" "); + } + break; + + case bp_catch_fork: + case bp_catch_vfork: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + if (addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + if (b->forked_inferior_pid != 0) + { + ui_out_text (uiout, "process "); + ui_out_field_int (uiout, "what", b->forked_inferior_pid); + ui_out_spaces (uiout, 1); + } + + case bp_catch_exec: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + if (addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + if (b->exec_pathname != NULL) + { + ui_out_text (uiout, "program \""); + ui_out_field_string (uiout, "what", b->exec_pathname); + ui_out_text (uiout, "\" "); + } + break; + + case bp_catch_catch: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + if (addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + ui_out_field_string (uiout, "what", "exception catch"); + ui_out_spaces (uiout, 1); + break; + + case bp_catch_throw: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + if (addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + ui_out_field_string (uiout, "what", "exception throw"); + ui_out_spaces (uiout, 1); + break; + + case bp_breakpoint: + case bp_hardware_breakpoint: + case bp_until: + case bp_finish: + case bp_longjmp: + case bp_longjmp_resume: + case bp_step_resume: + case bp_through_sigtramp: + case bp_watchpoint_scope: + case bp_call_dummy: + case bp_shlib_event: + case bp_thread_event: + case bp_overlay_event: + if (addressprint) + { + annotate_field (4); + ui_out_field_core_addr (uiout, "addr", b->address); + } + annotate_field (5); + *last_addr = b->address; + if (b->source_file) + { + sym = find_pc_sect_function (b->address, b->section); + if (sym) + { + ui_out_text (uiout, "in "); + ui_out_field_string (uiout, "func", + SYMBOL_SOURCE_NAME (sym)); + ui_out_wrap_hint (uiout, wrap_indent); + ui_out_text (uiout, " at "); + } + ui_out_field_string (uiout, "file", b->source_file); + ui_out_text (uiout, ":"); + ui_out_field_int (uiout, "line", b->line_number); + } + else + { + print_address_symbolic (b->address, stb->stream, demangle, ""); + ui_out_field_stream (uiout, "at", stb); + } + break; + } + + if (b->thread != -1) + { + /* FIXME: This seems to be redundant and lost here; see the + "stop only in" line a little further down. */ + ui_out_text (uiout, " thread "); + ui_out_field_int (uiout, "thread", b->thread); + } + + ui_out_text (uiout, "\n"); + + if (b->frame) + { + annotate_field (6); + ui_out_text (uiout, "\tstop only in stack frame at "); + ui_out_field_core_addr (uiout, "frame", b->frame); + ui_out_text (uiout, "\n"); + } + + if (b->cond) + { + annotate_field (7); + ui_out_text (uiout, "\tstop only if "); + print_expression (b->cond, stb->stream); + ui_out_field_stream (uiout, "cond", stb); + ui_out_text (uiout, "\n"); + } + + if (b->thread != -1) + { + /* FIXME should make an annotation for this */ + ui_out_text (uiout, "\tstop only in thread "); + ui_out_field_int (uiout, "thread", b->thread); + ui_out_text (uiout, "\n"); + } + + if (show_breakpoint_hit_counts && b->hit_count) + { + /* FIXME should make an annotation for this */ + if (ep_is_catchpoint (b)) + ui_out_text (uiout, "\tcatchpoint"); + else + ui_out_text (uiout, "\tbreakpoint"); + ui_out_text (uiout, " already hit "); + ui_out_field_int (uiout, "times", b->hit_count); + if (b->hit_count == 1) + ui_out_text (uiout, " time\n"); + else + ui_out_text (uiout, " times\n"); + } + + /* Output the count also if it is zero, but only if this is + mi. FIXME: Should have a better test for this. */ + if (ui_out_is_mi_like_p (uiout)) + if (show_breakpoint_hit_counts && b->hit_count == 0) + ui_out_field_int (uiout, "times", b->hit_count); + + if (b->ignore_count) + { + annotate_field (8); + ui_out_text (uiout, "\tignore next "); + ui_out_field_int (uiout, "ignore", b->ignore_count); + ui_out_text (uiout, " hits\n"); + } + + if ((l = b->commands)) + { + annotate_field (9); + ui_out_tuple_begin (uiout, "script"); + print_command_lines (uiout, l, 4); + ui_out_tuple_end (uiout); + } + ui_out_tuple_end (uiout); + do_cleanups (old_chain); +} + +struct captured_breakpoint_query_args + { + int bnum; + }; + +static int +do_captured_breakpoint_query (struct ui_out *uiout, void *data) +{ + struct captured_breakpoint_query_args *args = data; + register struct breakpoint *b; + CORE_ADDR dummy_addr = 0; + ALL_BREAKPOINTS (b) + { + if (args->bnum == b->number) + { + print_one_breakpoint (b, &dummy_addr); + return GDB_RC_OK; + } + } + return GDB_RC_NONE; +} + +enum gdb_rc +gdb_breakpoint_query (struct ui_out *uiout, int bnum) +{ + struct captured_breakpoint_query_args args; + args.bnum = bnum; + /* For the moment we don't trust print_one_breakpoint() to not throw + an error. */ + return catch_exceptions (uiout, do_captured_breakpoint_query, &args, + NULL, RETURN_MASK_ALL); +} + +/* Return non-zero if B is user settable (breakpoints, watchpoints, + catchpoints, et.al.). */ + +static int +user_settable_breakpoint (const struct breakpoint *b) +{ + return (b->type == bp_breakpoint + || b->type == bp_catch_load + || b->type == bp_catch_unload + || b->type == bp_catch_fork + || b->type == bp_catch_vfork + || b->type == bp_catch_exec + || b->type == bp_catch_catch + || b->type == bp_catch_throw + || b->type == bp_hardware_breakpoint + || b->type == bp_watchpoint + || b->type == bp_read_watchpoint + || b->type == bp_access_watchpoint + || b->type == bp_hardware_watchpoint); +} + +/* Print information on user settable breakpoint (watchpoint, etc) + number BNUM. If BNUM is -1 print all user settable breakpoints. + If ALLFLAG is non-zero, include non- user settable breakpoints. */ + +static void +breakpoint_1 (int bnum, int allflag) +{ + register struct breakpoint *b; + CORE_ADDR last_addr = (CORE_ADDR) -1; + int nr_printable_breakpoints; + + /* Compute the number of rows in the table. */ + nr_printable_breakpoints = 0; + ALL_BREAKPOINTS (b) + if (bnum == -1 + || bnum == b->number) + { + if (allflag || user_settable_breakpoint (b)) + nr_printable_breakpoints++; + } + + if (addressprint) + ui_out_table_begin (uiout, 6, nr_printable_breakpoints, "BreakpointTable"); + else + ui_out_table_begin (uiout, 5, nr_printable_breakpoints, "BreakpointTable"); + + if (nr_printable_breakpoints > 0) + annotate_breakpoints_headers (); + if (nr_printable_breakpoints > 0) + annotate_field (0); + ui_out_table_header (uiout, 3, ui_left, "number", "Num"); /* 1 */ + if (nr_printable_breakpoints > 0) + annotate_field (1); + ui_out_table_header (uiout, 14, ui_left, "type", "Type"); /* 2 */ + if (nr_printable_breakpoints > 0) + annotate_field (2); + ui_out_table_header (uiout, 4, ui_left, "disp", "Disp"); /* 3 */ + if (nr_printable_breakpoints > 0) + annotate_field (3); + ui_out_table_header (uiout, 3, ui_left, "enabled", "Enb"); /* 4 */ + if (addressprint) + { + if (nr_printable_breakpoints > 0) + annotate_field (4); + if (TARGET_ADDR_BIT <= 32) + ui_out_table_header (uiout, 10, ui_left, "addr", "Address");/* 5 */ + else + ui_out_table_header (uiout, 18, ui_left, "addr", "Address");/* 5 */ + } + if (nr_printable_breakpoints > 0) + annotate_field (5); + ui_out_table_header (uiout, 40, ui_noalign, "what", "What"); /* 6 */ + ui_out_table_body (uiout); + if (nr_printable_breakpoints > 0) + annotate_breakpoints_table (); + + ALL_BREAKPOINTS (b) + if (bnum == -1 + || bnum == b->number) + { + /* We only print out user settable breakpoints unless the + allflag is set. */ + if (allflag || user_settable_breakpoint (b)) + print_one_breakpoint (b, &last_addr); + } + + ui_out_table_end (uiout); + + if (nr_printable_breakpoints == 0) + { + if (bnum == -1) + ui_out_message (uiout, 0, "No breakpoints or watchpoints.\n"); + else + ui_out_message (uiout, 0, "No breakpoint or watchpoint number %d.\n", + bnum); + } + else + { + /* Compare against (CORE_ADDR)-1 in case some compiler decides + that a comparison of an unsigned with -1 is always false. */ + if (last_addr != (CORE_ADDR) -1) + set_next_address (last_addr); + } + + /* FIXME? Should this be moved up so that it is only called when + there have been breakpoints? */ + annotate_breakpoints_table_end (); +} + +/* ARGSUSED */ +static void +breakpoints_info (char *bnum_exp, int from_tty) +{ + int bnum = -1; + + if (bnum_exp) + bnum = parse_and_eval_long (bnum_exp); + + breakpoint_1 (bnum, 0); +} + +/* ARGSUSED */ +static void +maintenance_info_breakpoints (char *bnum_exp, int from_tty) +{ + int bnum = -1; + + if (bnum_exp) + bnum = parse_and_eval_long (bnum_exp); + + breakpoint_1 (bnum, 1); +} + +/* Print a message describing any breakpoints set at PC. */ + +static void +describe_other_breakpoints (CORE_ADDR pc, asection *section) +{ + register int others = 0; + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->address == pc) /* address match / overlay match */ + if (!overlay_debugging || b->section == section) + others++; + if (others > 0) + { + printf_filtered ("Note: breakpoint%s ", (others > 1) ? "s" : ""); + ALL_BREAKPOINTS (b) + if (b->address == pc) /* address match / overlay match */ + if (!overlay_debugging || b->section == section) + { + others--; + printf_filtered ("%d%s%s ", + b->number, + ((b->enable_state == bp_disabled || + b->enable_state == bp_shlib_disabled || + b->enable_state == bp_call_disabled) + ? " (disabled)" + : b->enable_state == bp_permanent + ? " (permanent)" + : ""), + (others > 1) ? "," + : ((others == 1) ? " and" : "")); + } + printf_filtered ("also set at pc "); + print_address_numeric (pc, 1, gdb_stdout); + printf_filtered (".\n"); + } +} + +/* Set the default place to put a breakpoint + for the `break' command with no arguments. */ + +void +set_default_breakpoint (int valid, CORE_ADDR addr, struct symtab *symtab, + int line) +{ + default_breakpoint_valid = valid; + default_breakpoint_address = addr; + default_breakpoint_symtab = symtab; + default_breakpoint_line = line; +} + +/* Return true iff it is meaningful to use the address member of + BPT. For some breakpoint types, the address member is irrelevant + and it makes no sense to attempt to compare it to other addresses + (or use it for any other purpose either). + + More specifically, each of the following breakpoint types will always + have a zero valued address and we don't want check_duplicates() to mark + breakpoints of any of these types to be a duplicate of an actual + breakpoint at address zero: + + bp_watchpoint + bp_hardware_watchpoint + bp_read_watchpoint + bp_access_watchpoint + bp_catch_exec + bp_longjmp_resume + bp_catch_fork + bp_catch_vork */ + +static int +breakpoint_address_is_meaningful (struct breakpoint *bpt) +{ + enum bptype type = bpt->type; + + return (type != bp_watchpoint + && type != bp_hardware_watchpoint + && type != bp_read_watchpoint + && type != bp_access_watchpoint + && type != bp_catch_exec + && type != bp_longjmp_resume + && type != bp_catch_fork + && type != bp_catch_vfork); +} + +/* Rescan breakpoints at the same address and section as BPT, + marking the first one as "first" and any others as "duplicates". + This is so that the bpt instruction is only inserted once. + If we have a permanent breakpoint at the same place as BPT, make + that one the official one, and the rest as duplicates. */ + +static void +check_duplicates (struct breakpoint *bpt) +{ + register struct breakpoint *b; + register int count = 0; + struct breakpoint *perm_bp = 0; + CORE_ADDR address = bpt->address; + asection *section = bpt->section; + + if (! breakpoint_address_is_meaningful (bpt)) + return; + + ALL_BREAKPOINTS (b) + if (b->enable_state != bp_disabled + && b->enable_state != bp_shlib_disabled + && b->enable_state != bp_call_disabled + && b->address == address /* address / overlay match */ + && (!overlay_debugging || b->section == section) + && breakpoint_address_is_meaningful (b)) + { + /* Have we found a permanent breakpoint? */ + if (b->enable_state == bp_permanent) + { + perm_bp = b; + break; + } + + count++; + b->duplicate = count > 1; + } + + /* If we found a permanent breakpoint at this address, go over the + list again and declare all the other breakpoints there to be the + duplicates. */ + if (perm_bp) + { + perm_bp->duplicate = 0; + + /* Permanent breakpoint should always be inserted. */ + if (! perm_bp->inserted) + internal_error (__FILE__, __LINE__, + "allegedly permanent breakpoint is not " + "actually inserted"); + + ALL_BREAKPOINTS (b) + if (b != perm_bp) + { + if (b->inserted) + internal_error (__FILE__, __LINE__, + "another breakpoint was inserted on top of " + "a permanent breakpoint"); + + if (b->enable_state != bp_disabled + && b->enable_state != bp_shlib_disabled + && b->enable_state != bp_call_disabled + && b->address == address /* address / overlay match */ + && (!overlay_debugging || b->section == section) + && breakpoint_address_is_meaningful (b)) + b->duplicate = 1; + } + } +} + +/* set_raw_breakpoint() is a low level routine for allocating and + partially initializing a breakpoint of type BPTYPE. The newly + created breakpoint's address, section, source file name, and line + number are provided by SAL. The newly created and partially + initialized breakpoint is added to the breakpoint chain and + is also returned as the value of this function. + + It is expected that the caller will complete the initialization of + the newly created breakpoint struct as well as output any status + information regarding the creation of a new breakpoint. In + particular, set_raw_breakpoint() does NOT set the breakpoint + number! Care should be taken to not allow an error() to occur + prior to completing the initialization of the breakpoint. If this + should happen, a bogus breakpoint will be left on the chain. */ + +struct breakpoint * +set_raw_breakpoint (struct symtab_and_line sal, enum bptype bptype) +{ + register struct breakpoint *b, *b1; + + b = (struct breakpoint *) xmalloc (sizeof (struct breakpoint)); + memset (b, 0, sizeof (*b)); + b->address = sal.pc; + if (sal.symtab == NULL) + b->source_file = NULL; + else + b->source_file = savestring (sal.symtab->filename, + strlen (sal.symtab->filename)); + b->section = sal.section; + b->type = bptype; + b->language = current_language->la_language; + b->input_radix = input_radix; + b->thread = -1; + b->line_number = sal.line; + b->enable_state = bp_enabled; + b->next = 0; + b->silent = 0; + b->ignore_count = 0; + b->commands = NULL; + b->frame = 0; + b->dll_pathname = NULL; + b->triggered_dll_pathname = NULL; + b->forked_inferior_pid = 0; + b->exec_pathname = NULL; + + /* Add this breakpoint to the end of the chain + so that a list of breakpoints will come out in order + of increasing numbers. */ + + b1 = breakpoint_chain; + if (b1 == 0) + breakpoint_chain = b; + else + { + while (b1->next) + b1 = b1->next; + b1->next = b; + } + + check_duplicates (b); + breakpoints_changed (); + + return b; +} + + +/* Note that the breakpoint object B describes a permanent breakpoint + instruction, hard-wired into the inferior's code. */ +void +make_breakpoint_permanent (struct breakpoint *b) +{ + b->enable_state = bp_permanent; + + /* By definition, permanent breakpoints are already present in the code. */ + b->inserted = 1; +} + +static struct breakpoint * +create_internal_breakpoint (CORE_ADDR address, enum bptype type) +{ + static int internal_breakpoint_number = -1; + struct symtab_and_line sal; + struct breakpoint *b; + + INIT_SAL (&sal); /* initialize to zeroes */ + + sal.pc = address; + sal.section = find_pc_overlay (sal.pc); + + b = set_raw_breakpoint (sal, type); + b->number = internal_breakpoint_number--; + b->disposition = disp_donttouch; + + return b; +} + + +static void +create_longjmp_breakpoint (char *func_name) +{ + struct breakpoint *b; + struct minimal_symbol *m; + + if (func_name == NULL) + b = create_internal_breakpoint (0, bp_longjmp_resume); + else + { + if ((m = lookup_minimal_symbol_text (func_name, NULL, NULL)) == NULL) + return; + + b = create_internal_breakpoint (SYMBOL_VALUE_ADDRESS (m), bp_longjmp); + } + + b->enable_state = bp_disabled; + b->silent = 1; + if (func_name) + b->addr_string = xstrdup (func_name); +} + +/* Call this routine when stepping and nexting to enable a breakpoint + if we do a longjmp(). When we hit that breakpoint, call + set_longjmp_resume_breakpoint() to figure out where we are going. */ + +void +enable_longjmp_breakpoint (void) +{ + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->type == bp_longjmp) + { + b->enable_state = bp_enabled; + check_duplicates (b); + } +} + +void +disable_longjmp_breakpoint (void) +{ + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->type == bp_longjmp + || b->type == bp_longjmp_resume) + { + b->enable_state = bp_disabled; + check_duplicates (b); + } +} + +static void +create_overlay_event_breakpoint (char *func_name) +{ + struct breakpoint *b; + struct minimal_symbol *m; + + if ((m = lookup_minimal_symbol_text (func_name, NULL, NULL)) == NULL) + return; + + b = create_internal_breakpoint (SYMBOL_VALUE_ADDRESS (m), + bp_overlay_event); + b->addr_string = xstrdup (func_name); + + if (overlay_debugging == ovly_auto) + { + b->enable_state = bp_enabled; + overlay_events_enabled = 1; + } + else + { + b->enable_state = bp_disabled; + overlay_events_enabled = 0; + } +} + +void +enable_overlay_breakpoints (void) +{ + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->type == bp_overlay_event) + { + b->enable_state = bp_enabled; + check_duplicates (b); + overlay_events_enabled = 1; + } +} + +void +disable_overlay_breakpoints (void) +{ + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->type == bp_overlay_event) + { + b->enable_state = bp_disabled; + check_duplicates (b); + overlay_events_enabled = 0; + } +} + +struct breakpoint * +create_thread_event_breakpoint (CORE_ADDR address) +{ + struct breakpoint *b; + char addr_string[80]; /* Surely an addr can't be longer than that. */ + + b = create_internal_breakpoint (address, bp_thread_event); + + b->enable_state = bp_enabled; + /* addr_string has to be used or breakpoint_re_set will delete me. */ + sprintf (addr_string, "*0x%s", paddr (b->address)); + b->addr_string = xstrdup (addr_string); + + return b; +} + +void +remove_thread_event_breakpoints (void) +{ + struct breakpoint *b, *temp; + + ALL_BREAKPOINTS_SAFE (b, temp) + if (b->type == bp_thread_event) + delete_breakpoint (b); +} + +#ifdef SOLIB_ADD +void +remove_solib_event_breakpoints (void) +{ + register struct breakpoint *b, *temp; + + ALL_BREAKPOINTS_SAFE (b, temp) + if (b->type == bp_shlib_event) + delete_breakpoint (b); +} + +struct breakpoint * +create_solib_event_breakpoint (CORE_ADDR address) +{ + struct breakpoint *b; + + b = create_internal_breakpoint (address, bp_shlib_event); + return b; +} + +/* Disable any breakpoints that are on code in shared libraries. Only + apply to enabled breakpoints, disabled ones can just stay disabled. */ + +void +disable_breakpoints_in_shlibs (int silent) +{ + struct breakpoint *b; + int disabled_shlib_breaks = 0; + + /* See also: insert_breakpoints, under DISABLE_UNSETTABLE_BREAK. */ + ALL_BREAKPOINTS (b) + { +#if defined (PC_SOLIB) + if (((b->type == bp_breakpoint) || + (b->type == bp_hardware_breakpoint)) && + b->enable_state == bp_enabled && + !b->duplicate && + PC_SOLIB (b->address)) + { + b->enable_state = bp_shlib_disabled; + if (!silent) + { + if (!disabled_shlib_breaks) + { + target_terminal_ours_for_output (); + warning ("Temporarily disabling shared library breakpoints:"); + } + disabled_shlib_breaks = 1; + warning ("breakpoint #%d ", b->number); + } + } +#endif + } +} + +/* Try to reenable any breakpoints in shared libraries. */ +void +re_enable_breakpoints_in_shlibs (void) +{ + struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->enable_state == bp_shlib_disabled) + { + char buf[1]; + + /* Do not reenable the breakpoint if the shared library + is still not mapped in. */ + if (target_read_memory (b->address, buf, 1) == 0) + b->enable_state = bp_enabled; + } +} + +#endif + +static void +solib_load_unload_1 (char *hookname, int tempflag, char *dll_pathname, + char *cond_string, enum bptype bp_kind) +{ + struct breakpoint *b; + struct symtabs_and_lines sals; + struct cleanup *old_chain; + struct cleanup *canonical_strings_chain = NULL; + char *addr_start = hookname; + char *addr_end = NULL; + char **canonical = (char **) NULL; + int thread = -1; /* All threads. */ + + /* Set a breakpoint on the specified hook. */ + sals = decode_line_1 (&hookname, 1, (struct symtab *) NULL, 0, &canonical); + addr_end = hookname; + + if (sals.nelts == 0) + { + warning ("Unable to set a breakpoint on dynamic linker callback."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + return; + } + if (sals.nelts != 1) + { + warning ("Unable to set unique breakpoint on dynamic linker callback."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + return; + } + + /* Make sure that all storage allocated in decode_line_1 gets freed + in case the following errors out. */ + old_chain = make_cleanup (xfree, sals.sals); + if (canonical != (char **) NULL) + { + make_cleanup (xfree, canonical); + canonical_strings_chain = make_cleanup (null_cleanup, 0); + if (canonical[0] != NULL) + make_cleanup (xfree, canonical[0]); + } + + resolve_sal_pc (&sals.sals[0]); + + /* Remove the canonical strings from the cleanup, they are needed below. */ + if (canonical != (char **) NULL) + discard_cleanups (canonical_strings_chain); + + b = set_raw_breakpoint (sals.sals[0], bp_kind); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = NULL; + b->cond_string = (cond_string == NULL) ? + NULL : savestring (cond_string, strlen (cond_string)); + b->thread = thread; + + if (canonical != (char **) NULL && canonical[0] != NULL) + b->addr_string = canonical[0]; + else if (addr_start) + b->addr_string = savestring (addr_start, addr_end - addr_start); + + b->enable_state = bp_enabled; + b->disposition = tempflag ? disp_del : disp_donttouch; + + if (dll_pathname == NULL) + b->dll_pathname = NULL; + else + { + b->dll_pathname = (char *) xmalloc (strlen (dll_pathname) + 1); + strcpy (b->dll_pathname, dll_pathname); + } + + mention (b); + do_cleanups (old_chain); +} + +void +create_solib_load_event_breakpoint (char *hookname, int tempflag, + char *dll_pathname, char *cond_string) +{ + solib_load_unload_1 (hookname, tempflag, dll_pathname, + cond_string, bp_catch_load); +} + +void +create_solib_unload_event_breakpoint (char *hookname, int tempflag, + char *dll_pathname, char *cond_string) +{ + solib_load_unload_1 (hookname,tempflag, dll_pathname, + cond_string, bp_catch_unload); +} + +static void +create_fork_vfork_event_catchpoint (int tempflag, char *cond_string, + enum bptype bp_kind) +{ + struct symtab_and_line sal; + struct breakpoint *b; + int thread = -1; /* All threads. */ + + INIT_SAL (&sal); + sal.pc = 0; + sal.symtab = NULL; + sal.line = 0; + + b = set_raw_breakpoint (sal, bp_kind); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = NULL; + b->cond_string = (cond_string == NULL) ? + NULL : savestring (cond_string, strlen (cond_string)); + b->thread = thread; + b->addr_string = NULL; + b->enable_state = bp_enabled; + b->disposition = tempflag ? disp_del : disp_donttouch; + b->forked_inferior_pid = 0; + + mention (b); +} + +void +create_fork_event_catchpoint (int tempflag, char *cond_string) +{ + create_fork_vfork_event_catchpoint (tempflag, cond_string, bp_catch_fork); +} + +void +create_vfork_event_catchpoint (int tempflag, char *cond_string) +{ + create_fork_vfork_event_catchpoint (tempflag, cond_string, bp_catch_vfork); +} + +void +create_exec_event_catchpoint (int tempflag, char *cond_string) +{ + struct symtab_and_line sal; + struct breakpoint *b; + int thread = -1; /* All threads. */ + + INIT_SAL (&sal); + sal.pc = 0; + sal.symtab = NULL; + sal.line = 0; + + b = set_raw_breakpoint (sal, bp_catch_exec); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = NULL; + b->cond_string = (cond_string == NULL) ? + NULL : savestring (cond_string, strlen (cond_string)); + b->thread = thread; + b->addr_string = NULL; + b->enable_state = bp_enabled; + b->disposition = tempflag ? disp_del : disp_donttouch; + + mention (b); +} + +static int +hw_breakpoint_used_count (void) +{ + register struct breakpoint *b; + int i = 0; + + ALL_BREAKPOINTS (b) + { + if (b->type == bp_hardware_breakpoint && b->enable_state == bp_enabled) + i++; + } + + return i; +} + +static int +hw_watchpoint_used_count (enum bptype type, int *other_type_used) +{ + register struct breakpoint *b; + int i = 0; + + *other_type_used = 0; + ALL_BREAKPOINTS (b) + { + if (b->enable_state == bp_enabled) + { + if (b->type == type) + i++; + else if ((b->type == bp_hardware_watchpoint || + b->type == bp_read_watchpoint || + b->type == bp_access_watchpoint) + && b->enable_state == bp_enabled) + *other_type_used = 1; + } + } + return i; +} + +/* Call this after hitting the longjmp() breakpoint. Use this to set + a new breakpoint at the target of the jmp_buf. + + FIXME - This ought to be done by setting a temporary breakpoint + that gets deleted automatically... */ + +void +set_longjmp_resume_breakpoint (CORE_ADDR pc, struct frame_info *frame) +{ + register struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->type == bp_longjmp_resume) + { + b->address = pc; + b->enable_state = bp_enabled; + if (frame != NULL) + b->frame = frame->frame; + else + b->frame = 0; + check_duplicates (b); + return; + } +} + +void +disable_watchpoints_before_interactive_call_start (void) +{ + struct breakpoint *b; + + ALL_BREAKPOINTS (b) + { + if (((b->type == bp_watchpoint) + || (b->type == bp_hardware_watchpoint) + || (b->type == bp_read_watchpoint) + || (b->type == bp_access_watchpoint) + || ep_is_exception_catchpoint (b)) + && (b->enable_state == bp_enabled)) + { + b->enable_state = bp_call_disabled; + check_duplicates (b); + } + } +} + +void +enable_watchpoints_after_interactive_call_stop (void) +{ + struct breakpoint *b; + + ALL_BREAKPOINTS (b) + { + if (((b->type == bp_watchpoint) + || (b->type == bp_hardware_watchpoint) + || (b->type == bp_read_watchpoint) + || (b->type == bp_access_watchpoint) + || ep_is_exception_catchpoint (b)) + && (b->enable_state == bp_call_disabled)) + { + b->enable_state = bp_enabled; + check_duplicates (b); + } + } +} + + +/* Set a breakpoint that will evaporate an end of command + at address specified by SAL. + Restrict it to frame FRAME if FRAME is nonzero. */ + +struct breakpoint * +set_momentary_breakpoint (struct symtab_and_line sal, struct frame_info *frame, + enum bptype type) +{ + register struct breakpoint *b; + b = set_raw_breakpoint (sal, type); + b->enable_state = bp_enabled; + b->disposition = disp_donttouch; + b->frame = (frame ? frame->frame : 0); + + /* If we're debugging a multi-threaded program, then we + want momentary breakpoints to be active in only a + single thread of control. */ + if (in_thread_list (inferior_ptid)) + b->thread = pid_to_thread_id (inferior_ptid); + + return b; +} + + +/* Tell the user we have just set a breakpoint B. */ + +static void +mention (struct breakpoint *b) +{ + int say_where = 0; + struct cleanup *old_chain; + struct ui_stream *stb; + + stb = ui_out_stream_new (uiout); + old_chain = make_cleanup_ui_out_stream_delete (stb); + + /* FIXME: This is misplaced; mention() is called by things (like hitting a + watchpoint) other than breakpoint creation. It should be possible to + clean this up and at the same time replace the random calls to + breakpoint_changed with this hook, as has already been done for + delete_breakpoint_hook and so on. */ + if (create_breakpoint_hook) + create_breakpoint_hook (b); + breakpoint_create_event (b->number); + + switch (b->type) + { + case bp_none: + printf_filtered ("(apparently deleted?) Eventpoint %d: ", b->number); + break; + case bp_watchpoint: + ui_out_text (uiout, "Watchpoint "); + ui_out_tuple_begin (uiout, "wpt"); + ui_out_field_int (uiout, "number", b->number); + ui_out_text (uiout, ": "); + print_expression (b->exp, stb->stream); + ui_out_field_stream (uiout, "exp", stb); + ui_out_tuple_end (uiout); + break; + case bp_hardware_watchpoint: + ui_out_text (uiout, "Hardware watchpoint "); + ui_out_tuple_begin (uiout, "wpt"); + ui_out_field_int (uiout, "number", b->number); + ui_out_text (uiout, ": "); + print_expression (b->exp, stb->stream); + ui_out_field_stream (uiout, "exp", stb); + ui_out_tuple_end (uiout); + break; + case bp_read_watchpoint: + ui_out_text (uiout, "Hardware read watchpoint "); + ui_out_tuple_begin (uiout, "hw-rwpt"); + ui_out_field_int (uiout, "number", b->number); + ui_out_text (uiout, ": "); + print_expression (b->exp, stb->stream); + ui_out_field_stream (uiout, "exp", stb); + ui_out_tuple_end (uiout); + break; + case bp_access_watchpoint: + ui_out_text (uiout, "Hardware access (read/write) watchpoint "); + ui_out_tuple_begin (uiout, "hw-awpt"); + ui_out_field_int (uiout, "number", b->number); + ui_out_text (uiout, ": "); + print_expression (b->exp, stb->stream); + ui_out_field_stream (uiout, "exp", stb); + ui_out_tuple_end (uiout); + break; + case bp_breakpoint: + if (ui_out_is_mi_like_p (uiout)) + { + say_where = 0; + break; + } + printf_filtered ("Breakpoint %d", b->number); + say_where = 1; + break; + case bp_hardware_breakpoint: + if (ui_out_is_mi_like_p (uiout)) + { + say_where = 0; + break; + } + printf_filtered ("Hardware assisted breakpoint %d", b->number); + say_where = 1; + break; + case bp_catch_load: + case bp_catch_unload: + printf_filtered ("Catchpoint %d (%s %s)", + b->number, + (b->type == bp_catch_load) ? "load" : "unload", + (b->dll_pathname != NULL) ? + b->dll_pathname : ""); + break; + case bp_catch_fork: + case bp_catch_vfork: + printf_filtered ("Catchpoint %d (%s)", + b->number, + (b->type == bp_catch_fork) ? "fork" : "vfork"); + break; + case bp_catch_exec: + printf_filtered ("Catchpoint %d (exec)", + b->number); + break; + case bp_catch_catch: + case bp_catch_throw: + printf_filtered ("Catchpoint %d (%s)", + b->number, + (b->type == bp_catch_catch) ? "catch" : "throw"); + break; + + case bp_until: + case bp_finish: + case bp_longjmp: + case bp_longjmp_resume: + case bp_step_resume: + case bp_through_sigtramp: + case bp_call_dummy: + case bp_watchpoint_scope: + case bp_shlib_event: + case bp_thread_event: + case bp_overlay_event: + break; + } + if (say_where) + { + if (addressprint || b->source_file == NULL) + { + printf_filtered (" at "); + print_address_numeric (b->address, 1, gdb_stdout); + } + if (b->source_file) + printf_filtered (": file %s, line %d.", + b->source_file, b->line_number); + } + do_cleanups (old_chain); + if (ui_out_is_mi_like_p (uiout)) + return; + printf_filtered ("\n"); +} + + +/* Add SALS.nelts breakpoints to the breakpoint table. For each + SALS.sal[i] breakpoint, include the corresponding ADDR_STRING[i], + COND[i] and COND_STRING[i] values. + + NOTE: If the function succeeds, the caller is expected to cleanup + the arrays ADDR_STRING, COND_STRING, COND and SALS (but not the + array contents). If the function fails (error() is called), the + caller is expected to cleanups both the ADDR_STRING, COND_STRING, + COND and SALS arrays and each of those arrays contents. */ + +static void +create_breakpoints (struct symtabs_and_lines sals, char **addr_string, + struct expression **cond, char **cond_string, + enum bptype type, enum bpdisp disposition, + int thread, int ignore_count, int from_tty) +{ + if (type == bp_hardware_breakpoint) + { + int i = hw_breakpoint_used_count (); + int target_resources_ok = + TARGET_CAN_USE_HARDWARE_WATCHPOINT (bp_hardware_breakpoint, + i + sals.nelts, 0); + if (target_resources_ok == 0) + error ("No hardware breakpoint support in the target."); + else if (target_resources_ok < 0) + error ("Hardware breakpoints used exceeds limit."); + } + + /* Now set all the breakpoints. */ + { + int i; + for (i = 0; i < sals.nelts; i++) + { + struct breakpoint *b; + struct symtab_and_line sal = sals.sals[i]; + + if (from_tty) + describe_other_breakpoints (sal.pc, sal.section); + + b = set_raw_breakpoint (sal, type); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = cond[i]; + b->thread = thread; + b->addr_string = addr_string[i]; + b->cond_string = cond_string[i]; + b->ignore_count = ignore_count; + b->enable_state = bp_enabled; + b->disposition = disposition; + mention (b); + } + } +} + +/* Parse ARG which is assumed to be a SAL specification possibly + followed by conditionals. On return, SALS contains an array of SAL + addresses found. ADDR_STRING contains a vector of (canonical) + address strings. ARG points to the end of the SAL. */ + +void +parse_breakpoint_sals (char **address, + struct symtabs_and_lines *sals, + char ***addr_string) +{ + char *addr_start = *address; + *addr_string = NULL; + /* If no arg given, or if first arg is 'if ', use the default + breakpoint. */ + if ((*address) == NULL + || (strncmp ((*address), "if", 2) == 0 && isspace ((*address)[2]))) + { + if (default_breakpoint_valid) + { + struct symtab_and_line sal; + INIT_SAL (&sal); /* initialize to zeroes */ + sals->sals = (struct symtab_and_line *) + xmalloc (sizeof (struct symtab_and_line)); + sal.pc = default_breakpoint_address; + sal.line = default_breakpoint_line; + sal.symtab = default_breakpoint_symtab; + sal.section = find_pc_overlay (sal.pc); + sals->sals[0] = sal; + sals->nelts = 1; + } + else + error ("No default breakpoint address now."); + } + else + { + /* Force almost all breakpoints to be in terms of the + current_source_symtab (which is decode_line_1's default). This + should produce the results we want almost all of the time while + leaving default_breakpoint_* alone. */ + + struct symtab_and_line cursal = + get_current_or_default_source_symtab_and_line (); + + if (default_breakpoint_valid + && (!cursal.symtab + || (strchr ("+-", (*address)[0]) != NULL))) + *sals = decode_line_1 (address, 1, default_breakpoint_symtab, + default_breakpoint_line, addr_string); + else + *sals = decode_line_1 (address, 1, (struct symtab *) NULL, 0, addr_string); + } + /* For any SAL that didn't have a canonical string, fill one in. */ + if (sals->nelts > 0 && *addr_string == NULL) + *addr_string = xcalloc (sals->nelts, sizeof (char **)); + if (addr_start != (*address)) + { + int i; + for (i = 0; i < sals->nelts; i++) + { + /* Add the string if not present. */ + if ((*addr_string)[i] == NULL) + (*addr_string)[i] = savestring (addr_start, (*address) - addr_start); + } + } +} + + +/* Convert each SAL into a real PC. Verify that the PC can be + inserted as a breakpoint. If it can't throw an error. */ + +void +breakpoint_sals_to_pc (struct symtabs_and_lines *sals, + char *address) +{ + int i; + for (i = 0; i < sals->nelts; i++) + { + resolve_sal_pc (&sals->sals[i]); + + /* It's possible for the PC to be nonzero, but still an illegal + value on some targets. + + For example, on HP-UX if you start gdb, and before running the + inferior you try to set a breakpoint on a shared library function + "foo" where the inferior doesn't call "foo" directly but does + pass its address to another function call, then we do find a + minimal symbol for the "foo", but it's address is invalid. + (Appears to be an index into a table that the loader sets up + when the inferior is run.) + + Give the target a chance to bless sals.sals[i].pc before we + try to make a breakpoint for it. */ + if (PC_REQUIRES_RUN_BEFORE_USE (sals->sals[i].pc)) + { + if (address == NULL) + error ("Cannot break without a running program."); + else + error ("Cannot break on %s without a running program.", + address); + } + } +} + +/* Set a breakpoint according to ARG (function, linenum or *address) + flag: first bit : 0 non-temporary, 1 temporary. + second bit : 0 normal breakpoint, 1 hardware breakpoint. */ + +static void +break_command_1 (char *arg, int flag, int from_tty) +{ + int tempflag, hardwareflag; + struct symtabs_and_lines sals; + register struct expression **cond = 0; + /* Pointers in arg to the start, and one past the end, of the + condition. */ + char **cond_string = (char **) NULL; + char *addr_start = arg; + char **addr_string; + struct cleanup *old_chain; + struct cleanup *breakpoint_chain = NULL; + int i; + int thread = -1; + int ignore_count = 0; + + hardwareflag = flag & BP_HARDWAREFLAG; + tempflag = flag & BP_TEMPFLAG; + + sals.sals = NULL; + sals.nelts = 0; + addr_string = NULL; + parse_breakpoint_sals (&arg, &sals, &addr_string); + + if (!sals.nelts) + return; + + /* Create a chain of things that always need to be cleaned up. */ + old_chain = make_cleanup (null_cleanup, 0); + + /* Make sure that all storage allocated to SALS gets freed. */ + make_cleanup (xfree, sals.sals); + + /* Cleanup the addr_string array but not its contents. */ + make_cleanup (xfree, addr_string); + + /* Allocate space for all the cond expressions. */ + cond = xcalloc (sals.nelts, sizeof (struct expression *)); + make_cleanup (xfree, cond); + + /* Allocate space for all the cond strings. */ + cond_string = xcalloc (sals.nelts, sizeof (char **)); + make_cleanup (xfree, cond_string); + + /* ----------------------------- SNIP ----------------------------- + Anything added to the cleanup chain beyond this point is assumed + to be part of a breakpoint. If the breakpoint create succeeds + then the memory is not reclaimed. */ + breakpoint_chain = make_cleanup (null_cleanup, 0); + + /* Mark the contents of the addr_string for cleanup. These go on + the breakpoint_chain and only occure if the breakpoint create + fails. */ + for (i = 0; i < sals.nelts; i++) + { + if (addr_string[i] != NULL) + make_cleanup (xfree, addr_string[i]); + } + + /* Resolve all line numbers to PC's and verify that the addresses + are ok for the target. */ + breakpoint_sals_to_pc (&sals, addr_start); + + /* Verify that condition can be parsed, before setting any + breakpoints. Allocate a separate condition expression for each + breakpoint. */ + thread = -1; /* No specific thread yet */ + for (i = 0; i < sals.nelts; i++) + { + char *tok = arg; + while (tok && *tok) + { + char *end_tok; + int toklen; + char *cond_start = NULL; + char *cond_end = NULL; + while (*tok == ' ' || *tok == '\t') + tok++; + + end_tok = tok; + + while (*end_tok != ' ' && *end_tok != '\t' && *end_tok != '\000') + end_tok++; + + toklen = end_tok - tok; + + if (toklen >= 1 && strncmp (tok, "if", toklen) == 0) + { + tok = cond_start = end_tok + 1; + cond[i] = parse_exp_1 (&tok, block_for_pc (sals.sals[i].pc), 0); + make_cleanup (xfree, cond[i]); + cond_end = tok; + cond_string[i] = savestring (cond_start, cond_end - cond_start); + make_cleanup (xfree, cond_string[i]); + } + else if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0) + { + char *tmptok; + + tok = end_tok + 1; + tmptok = tok; + thread = strtol (tok, &tok, 0); + if (tok == tmptok) + error ("Junk after thread keyword."); + if (!valid_thread_id (thread)) + error ("Unknown thread %d\n", thread); + } + else + error ("Junk at end of arguments."); + } + } + + create_breakpoints (sals, addr_string, cond, cond_string, + hardwareflag ? bp_hardware_breakpoint : bp_breakpoint, + tempflag ? disp_del : disp_donttouch, + thread, ignore_count, from_tty); + + if (sals.nelts > 1) + { + warning ("Multiple breakpoints were set."); + warning ("Use the \"delete\" command to delete unwanted breakpoints."); + } + /* That's it. Discard the cleanups for data inserted into the + breakpoint. */ + discard_cleanups (breakpoint_chain); + /* But cleanup everything else. */ + do_cleanups (old_chain); +} + +/* Set a breakpoint of TYPE/DISPOSITION according to ARG (function, + linenum or *address) with COND and IGNORE_COUNT. */ + +struct captured_breakpoint_args + { + char *address; + char *condition; + int hardwareflag; + int tempflag; + int thread; + int ignore_count; + }; + +static int +do_captured_breakpoint (void *data) +{ + struct captured_breakpoint_args *args = data; + struct symtabs_and_lines sals; + register struct expression **cond; + struct cleanup *old_chain; + struct cleanup *breakpoint_chain = NULL; + int i; + char **addr_string; + char **cond_string; + + char *address_end; + + /* Parse the source and lines spec. Delay check that the expression + didn't contain trailing garbage until after cleanups are in + place. */ + sals.sals = NULL; + sals.nelts = 0; + address_end = args->address; + addr_string = NULL; + parse_breakpoint_sals (&address_end, &sals, &addr_string); + + if (!sals.nelts) + return GDB_RC_NONE; + + /* Create a chain of things at always need to be cleaned up. */ + old_chain = make_cleanup (null_cleanup, 0); + + /* Always have a addr_string array, even if it is empty. */ + make_cleanup (xfree, addr_string); + + /* Make sure that all storage allocated to SALS gets freed. */ + make_cleanup (xfree, sals.sals); + + /* Allocate space for all the cond expressions. */ + cond = xcalloc (sals.nelts, sizeof (struct expression *)); + make_cleanup (xfree, cond); + + /* Allocate space for all the cond strings. */ + cond_string = xcalloc (sals.nelts, sizeof (char **)); + make_cleanup (xfree, cond_string); + + /* ----------------------------- SNIP ----------------------------- + Anything added to the cleanup chain beyond this point is assumed + to be part of a breakpoint. If the breakpoint create goes + through then that memory is not cleaned up. */ + breakpoint_chain = make_cleanup (null_cleanup, 0); + + /* Mark the contents of the addr_string for cleanup. These go on + the breakpoint_chain and only occure if the breakpoint create + fails. */ + for (i = 0; i < sals.nelts; i++) + { + if (addr_string[i] != NULL) + make_cleanup (xfree, addr_string[i]); + } + + /* Wait until now before checking for garbage at the end of the + address. That way cleanups can take care of freeing any + memory. */ + if (*address_end != '\0') + error ("Garbage %s following breakpoint address", address_end); + + /* Resolve all line numbers to PC's. */ + breakpoint_sals_to_pc (&sals, args->address); + + /* Verify that conditions can be parsed, before setting any + breakpoints. */ + for (i = 0; i < sals.nelts; i++) + { + if (args->condition != NULL) + { + char *tok = args->condition; + cond[i] = parse_exp_1 (&tok, block_for_pc (sals.sals[i].pc), 0); + if (*tok != '\0') + error ("Garbage %s follows condition", tok); + make_cleanup (xfree, cond[i]); + cond_string[i] = xstrdup (args->condition); + } + } + + create_breakpoints (sals, addr_string, cond, cond_string, + args->hardwareflag ? bp_hardware_breakpoint : bp_breakpoint, + args->tempflag ? disp_del : disp_donttouch, + args->thread, args->ignore_count, 0/*from-tty*/); + + /* That's it. Discard the cleanups for data inserted into the + breakpoint. */ + discard_cleanups (breakpoint_chain); + /* But cleanup everything else. */ + do_cleanups (old_chain); + return GDB_RC_OK; +} + +enum gdb_rc +gdb_breakpoint (char *address, char *condition, + int hardwareflag, int tempflag, + int thread, int ignore_count) +{ + struct captured_breakpoint_args args; + args.address = address; + args.condition = condition; + args.hardwareflag = hardwareflag; + args.tempflag = tempflag; + args.thread = thread; + args.ignore_count = ignore_count; + return catch_errors (do_captured_breakpoint, &args, + NULL, RETURN_MASK_ALL); +} + + +static void +break_at_finish_at_depth_command_1 (char *arg, int flag, int from_tty) +{ + struct frame_info *frame; + CORE_ADDR low, high, selected_pc = 0; + char *extra_args = NULL; + char *level_arg; + char *addr_string; + int extra_args_len = 0, if_arg = 0; + + if (!arg || + (arg[0] == 'i' && arg[1] == 'f' && (arg[2] == ' ' || arg[2] == '\t'))) + { + + if (default_breakpoint_valid) + { + if (selected_frame) + { + selected_pc = selected_frame->pc; + if (arg) + if_arg = 1; + } + else + error ("No selected frame."); + } + else + error ("No default breakpoint address now."); + } + else + { + extra_args = strchr (arg, ' '); + if (extra_args) + { + extra_args++; + extra_args_len = strlen (extra_args); + level_arg = (char *) xmalloc (extra_args - arg); + strncpy (level_arg, arg, extra_args - arg - 1); + level_arg[extra_args - arg - 1] = '\0'; + } + else + { + level_arg = (char *) xmalloc (strlen (arg) + 1); + strcpy (level_arg, arg); + } + + frame = parse_frame_specification (level_arg); + if (frame) + selected_pc = frame->pc; + else + selected_pc = 0; + } + if (if_arg) + { + extra_args = arg; + extra_args_len = strlen (arg); + } + + if (selected_pc) + { + if (find_pc_partial_function (selected_pc, (char **) NULL, &low, &high)) + { + addr_string = (char *) xmalloc (26 + extra_args_len); + if (extra_args_len) + sprintf (addr_string, "*0x%s %s", paddr_nz (high), extra_args); + else + sprintf (addr_string, "*0x%s", paddr_nz (high)); + break_command_1 (addr_string, flag, from_tty); + xfree (addr_string); + } + else + error ("No function contains the specified address"); + } + else + error ("Unable to set breakpoint at procedure exit"); +} + + +static void +break_at_finish_command_1 (char *arg, int flag, int from_tty) +{ + char *addr_string, *break_string, *beg_addr_string; + CORE_ADDR low, high; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct cleanup *old_chain; + char *extra_args = NULL; + int extra_args_len = 0; + int i, if_arg = 0; + + if (!arg || + (arg[0] == 'i' && arg[1] == 'f' && (arg[2] == ' ' || arg[2] == '\t'))) + { + if (default_breakpoint_valid) + { + if (selected_frame) + { + addr_string = (char *) xmalloc (15); + sprintf (addr_string, "*0x%s", paddr_nz (selected_frame->pc)); + if (arg) + if_arg = 1; + } + else + error ("No selected frame."); + } + else + error ("No default breakpoint address now."); + } + else + { + addr_string = (char *) xmalloc (strlen (arg) + 1); + strcpy (addr_string, arg); + } + + if (if_arg) + { + extra_args = arg; + extra_args_len = strlen (arg); + } + else if (arg) + { + /* get the stuff after the function name or address */ + extra_args = strchr (arg, ' '); + if (extra_args) + { + extra_args++; + extra_args_len = strlen (extra_args); + } + } + + sals.sals = NULL; + sals.nelts = 0; + + beg_addr_string = addr_string; + sals = decode_line_1 (&addr_string, 1, (struct symtab *) NULL, 0, + (char ***) NULL); + + xfree (beg_addr_string); + old_chain = make_cleanup (xfree, sals.sals); + for (i = 0; (i < sals.nelts); i++) + { + sal = sals.sals[i]; + if (find_pc_partial_function (sal.pc, (char **) NULL, &low, &high)) + { + break_string = (char *) xmalloc (extra_args_len + 26); + if (extra_args_len) + sprintf (break_string, "*0x%s %s", paddr_nz (high), extra_args); + else + sprintf (break_string, "*0x%s", paddr_nz (high)); + break_command_1 (break_string, flag, from_tty); + xfree (break_string); + } + else + error ("No function contains the specified address"); + } + if (sals.nelts > 1) + { + warning ("Multiple breakpoints were set.\n"); + warning ("Use the \"delete\" command to delete unwanted breakpoints."); + } + do_cleanups (old_chain); +} + + +/* Helper function for break_command_1 and disassemble_command. */ + +void +resolve_sal_pc (struct symtab_and_line *sal) +{ + CORE_ADDR pc; + + if (sal->pc == 0 && sal->symtab != NULL) + { + if (!find_line_pc (sal->symtab, sal->line, &pc)) + error ("No line %d in file \"%s\".", + sal->line, sal->symtab->filename); + sal->pc = pc; + } + + if (sal->section == 0 && sal->symtab != NULL) + { + struct blockvector *bv; + struct block *b; + struct symbol *sym; + int index; + + bv = blockvector_for_pc_sect (sal->pc, 0, &index, sal->symtab); + if (bv != NULL) + { + b = BLOCKVECTOR_BLOCK (bv, index); + sym = block_function (b); + if (sym != NULL) + { + fixup_symbol_section (sym, sal->symtab->objfile); + sal->section = SYMBOL_BFD_SECTION (sym); + } + else + { + /* It really is worthwhile to have the section, so we'll just + have to look harder. This case can be executed if we have + line numbers but no functions (as can happen in assembly + source). */ + + struct minimal_symbol *msym; + + msym = lookup_minimal_symbol_by_pc (sal->pc); + if (msym) + sal->section = SYMBOL_BFD_SECTION (msym); + } + } + } +} + +void +break_command (char *arg, int from_tty) +{ + break_command_1 (arg, 0, from_tty); +} + +void +break_at_finish_command (char *arg, int from_tty) +{ + break_at_finish_command_1 (arg, 0, from_tty); +} + +void +break_at_finish_at_depth_command (char *arg, int from_tty) +{ + break_at_finish_at_depth_command_1 (arg, 0, from_tty); +} + +void +tbreak_command (char *arg, int from_tty) +{ + break_command_1 (arg, BP_TEMPFLAG, from_tty); +} + +void +tbreak_at_finish_command (char *arg, int from_tty) +{ + break_at_finish_command_1 (arg, BP_TEMPFLAG, from_tty); +} + +static void +hbreak_command (char *arg, int from_tty) +{ + break_command_1 (arg, BP_HARDWAREFLAG, from_tty); +} + +static void +thbreak_command (char *arg, int from_tty) +{ + break_command_1 (arg, (BP_TEMPFLAG | BP_HARDWAREFLAG), from_tty); +} + +static void +stop_command (char *arg, int from_tty) +{ + printf_filtered ("Specify the type of breakpoint to set.\n\ +Usage: stop in \n\ + stop at \n"); +} + +static void +stopin_command (char *arg, int from_tty) +{ + int badInput = 0; + + if (arg == (char *) NULL) + badInput = 1; + else if (*arg != '*') + { + char *argptr = arg; + int hasColon = 0; + + /* look for a ':'. If this is a line number specification, then + say it is bad, otherwise, it should be an address or + function/method name */ + while (*argptr && !hasColon) + { + hasColon = (*argptr == ':'); + argptr++; + } + + if (hasColon) + badInput = (*argptr != ':'); /* Not a class::method */ + else + badInput = isdigit (*arg); /* a simple line number */ + } + + if (badInput) + printf_filtered ("Usage: stop in \n"); + else + break_command_1 (arg, 0, from_tty); +} + +static void +stopat_command (char *arg, int from_tty) +{ + int badInput = 0; + + if (arg == (char *) NULL || *arg == '*') /* no line number */ + badInput = 1; + else + { + char *argptr = arg; + int hasColon = 0; + + /* look for a ':'. If there is a '::' then get out, otherwise + it is probably a line number. */ + while (*argptr && !hasColon) + { + hasColon = (*argptr == ':'); + argptr++; + } + + if (hasColon) + badInput = (*argptr == ':'); /* we have class::method */ + else + badInput = !isdigit (*arg); /* not a line number */ + } + + if (badInput) + printf_filtered ("Usage: stop at \n"); + else + break_command_1 (arg, 0, from_tty); +} + +/* ARGSUSED */ +/* accessflag: hw_write: watch write, + hw_read: watch read, + hw_access: watch access (read or write) */ +static void +watch_command_1 (char *arg, int accessflag, int from_tty) +{ + struct breakpoint *b; + struct symtab_and_line sal; + struct expression *exp; + struct block *exp_valid_block; + struct value *val, *mark; + struct frame_info *frame; + struct frame_info *prev_frame = NULL; + char *exp_start = NULL; + char *exp_end = NULL; + char *tok, *end_tok; + int toklen; + char *cond_start = NULL; + char *cond_end = NULL; + struct expression *cond = NULL; + int i, other_type_used, target_resources_ok = 0; + enum bptype bp_type; + int mem_cnt = 0; + + INIT_SAL (&sal); /* initialize to zeroes */ + + /* Parse arguments. */ + innermost_block = NULL; + exp_start = arg; + exp = parse_exp_1 (&arg, 0, 0); + exp_end = arg; + exp_valid_block = innermost_block; + mark = value_mark (); + val = evaluate_expression (exp); + release_value (val); + if (VALUE_LAZY (val)) + value_fetch_lazy (val); + + tok = arg; + while (*tok == ' ' || *tok == '\t') + tok++; + end_tok = tok; + + while (*end_tok != ' ' && *end_tok != '\t' && *end_tok != '\000') + end_tok++; + + toklen = end_tok - tok; + if (toklen >= 1 && strncmp (tok, "if", toklen) == 0) + { + tok = cond_start = end_tok + 1; + cond = parse_exp_1 (&tok, 0, 0); + cond_end = tok; + } + if (*tok) + error ("Junk at end of command."); + + if (accessflag == hw_read) + bp_type = bp_read_watchpoint; + else if (accessflag == hw_access) + bp_type = bp_access_watchpoint; + else + bp_type = bp_hardware_watchpoint; + + mem_cnt = can_use_hardware_watchpoint (val); + if (mem_cnt == 0 && bp_type != bp_hardware_watchpoint) + error ("Expression cannot be implemented with read/access watchpoint."); + if (mem_cnt != 0) + { + i = hw_watchpoint_used_count (bp_type, &other_type_used); + target_resources_ok = + TARGET_CAN_USE_HARDWARE_WATCHPOINT (bp_type, i + mem_cnt, + other_type_used); + if (target_resources_ok == 0 && bp_type != bp_hardware_watchpoint) + error ("Target does not support this type of hardware watchpoint."); + + if (target_resources_ok < 0 && bp_type != bp_hardware_watchpoint) + error ("Target can only support one kind of HW watchpoint at a time."); + } + +#if defined(HPUXHPPA) + /* On HP-UX if you set a h/w + watchpoint before the "run" command, the inferior dies with a e.g., + SIGILL once you start it. I initially believed this was due to a + bad interaction between page protection traps and the initial + startup sequence by the dynamic linker. + + However, I tried avoiding that by having HP-UX's implementation of + TARGET_CAN_USE_HW_WATCHPOINT return FALSE if there was no inferior_ptid + yet, which forced slow watches before a "run" or "attach", and it + still fails somewhere in the startup code. + + Until I figure out what's happening, I'm disallowing watches altogether + before the "run" or "attach" command. We'll tell the user they must + set watches after getting the program started. */ + if (!target_has_execution) + { + warning ("can't do that without a running program; try \"break main\", \"run\" first"); + return; + } +#endif /* HPUXHPPA */ + + /* Change the type of breakpoint to an ordinary watchpoint if a hardware + watchpoint could not be set. */ + if (!mem_cnt || target_resources_ok <= 0) + bp_type = bp_watchpoint; + + /* Now set up the breakpoint. */ + b = set_raw_breakpoint (sal, bp_type); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->disposition = disp_donttouch; + b->exp = exp; + b->exp_valid_block = exp_valid_block; + b->exp_string = savestring (exp_start, exp_end - exp_start); + b->val = val; + b->cond = cond; + if (cond_start) + b->cond_string = savestring (cond_start, cond_end - cond_start); + else + b->cond_string = 0; + + frame = block_innermost_frame (exp_valid_block); + if (frame) + { + prev_frame = get_prev_frame (frame); + get_frame_id (frame, &b->watchpoint_frame); + } + else + { + memset (&b->watchpoint_frame, 0, sizeof (b->watchpoint_frame)); + } + + /* If the expression is "local", then set up a "watchpoint scope" + breakpoint at the point where we've left the scope of the watchpoint + expression. */ + if (innermost_block) + { + if (prev_frame) + { + struct breakpoint *scope_breakpoint; + scope_breakpoint = create_internal_breakpoint (get_frame_pc (prev_frame), + bp_watchpoint_scope); + + scope_breakpoint->enable_state = bp_enabled; + + /* Automatically delete the breakpoint when it hits. */ + scope_breakpoint->disposition = disp_del; + + /* Only break in the proper frame (help with recursion). */ + scope_breakpoint->frame = prev_frame->frame; + + /* Set the address at which we will stop. */ + scope_breakpoint->address = get_frame_pc (prev_frame); + + /* The scope breakpoint is related to the watchpoint. We + will need to act on them together. */ + b->related_breakpoint = scope_breakpoint; + } + } + value_free_to_mark (mark); + mention (b); +} + +/* Return count of locations need to be watched and can be handled + in hardware. If the watchpoint can not be handled + in hardware return zero. */ + +#if !defined(TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT) +#define TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(BYTE_SIZE) \ + ((BYTE_SIZE) <= (REGISTER_SIZE)) +#endif + +#if !defined(TARGET_REGION_OK_FOR_HW_WATCHPOINT) +#define TARGET_REGION_OK_FOR_HW_WATCHPOINT(ADDR,LEN) \ + (TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(LEN)) +#endif + +static int +can_use_hardware_watchpoint (struct value *v) +{ + int found_memory_cnt = 0; + struct value *head = v; + + /* Did the user specifically forbid us to use hardware watchpoints? */ + if (!can_use_hw_watchpoints) + return 0; + + /* Make sure that the value of the expression depends only upon + memory contents, and values computed from them within GDB. If we + find any register references or function calls, we can't use a + hardware watchpoint. + + The idea here is that evaluating an expression generates a series + of values, one holding the value of every subexpression. (The + expression a*b+c has five subexpressions: a, b, a*b, c, and + a*b+c.) GDB's values hold almost enough information to establish + the criteria given above --- they identify memory lvalues, + register lvalues, computed values, etcetera. So we can evaluate + the expression, and then scan the chain of values that leaves + behind to decide whether we can detect any possible change to the + expression's final value using only hardware watchpoints. + + However, I don't think that the values returned by inferior + function calls are special in any way. So this function may not + notice that an expression involving an inferior function call + can't be watched with hardware watchpoints. FIXME. */ + for (; v; v = v->next) + { + if (VALUE_LVAL (v) == lval_memory) + { + if (VALUE_LAZY (v)) + /* A lazy memory lvalue is one that GDB never needed to fetch; + we either just used its address (e.g., `a' in `a.b') or + we never needed it at all (e.g., `a' in `a,b'). */ + ; + else + { + /* Ahh, memory we actually used! Check if we can cover + it with hardware watchpoints. */ + struct type *vtype = check_typedef (VALUE_TYPE (v)); + + /* We only watch structs and arrays if user asked for it + explicitly, never if they just happen to appear in a + middle of some value chain. */ + if (v == head + || (TYPE_CODE (vtype) != TYPE_CODE_STRUCT + && TYPE_CODE (vtype) != TYPE_CODE_ARRAY)) + { + CORE_ADDR vaddr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); + int len = TYPE_LENGTH (VALUE_TYPE (v)); + + if (!TARGET_REGION_OK_FOR_HW_WATCHPOINT (vaddr, len)) + return 0; + else + found_memory_cnt++; + } + } + } + else if (v->lval != not_lval && v->modifiable == 0) + return 0; /* ??? What does this represent? */ + else if (v->lval == lval_register) + return 0; /* cannot watch a register with a HW watchpoint */ + } + + /* The expression itself looks suitable for using a hardware + watchpoint, but give the target machine a chance to reject it. */ + return found_memory_cnt; +} + +void +watch_command_wrapper (char *arg, int from_tty) +{ + watch_command (arg, from_tty); +} + +static void +watch_command (char *arg, int from_tty) +{ + watch_command_1 (arg, hw_write, from_tty); +} + +void +rwatch_command_wrapper (char *arg, int from_tty) +{ + rwatch_command (arg, from_tty); +} + +static void +rwatch_command (char *arg, int from_tty) +{ + watch_command_1 (arg, hw_read, from_tty); +} + +void +awatch_command_wrapper (char *arg, int from_tty) +{ + awatch_command (arg, from_tty); +} + +static void +awatch_command (char *arg, int from_tty) +{ + watch_command_1 (arg, hw_access, from_tty); +} + + +/* Helper routines for the until_command routine in infcmd.c. Here + because it uses the mechanisms of breakpoints. */ + +/* This function is called by fetch_inferior_event via the + cmd_continuation pointer, to complete the until command. It takes + care of cleaning up the temporary breakpoints set up by the until + command. */ +static void +until_break_command_continuation (struct continuation_arg *arg) +{ + struct cleanup *cleanups; + + cleanups = (struct cleanup *) arg->data.pointer; + do_exec_cleanups (cleanups); +} + +/* ARGSUSED */ +void +until_break_command (char *arg, int from_tty) +{ + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct frame_info *prev_frame = get_prev_frame (selected_frame); + struct breakpoint *breakpoint; + struct cleanup *old_chain; + struct continuation_arg *arg1; + + + clear_proceed_status (); + + /* Set a breakpoint where the user wants it and at return from + this function */ + + if (default_breakpoint_valid) + sals = decode_line_1 (&arg, 1, default_breakpoint_symtab, + default_breakpoint_line, (char ***) NULL); + else + sals = decode_line_1 (&arg, 1, (struct symtab *) NULL, + 0, (char ***) NULL); + + if (sals.nelts != 1) + error ("Couldn't get information on specified line."); + + sal = sals.sals[0]; + xfree (sals.sals); /* malloc'd, so freed */ + + if (*arg) + error ("Junk at end of arguments."); + + resolve_sal_pc (&sal); + + breakpoint = set_momentary_breakpoint (sal, selected_frame, bp_until); + + if (!event_loop_p || !target_can_async_p ()) + old_chain = make_cleanup_delete_breakpoint (breakpoint); + else + old_chain = make_exec_cleanup_delete_breakpoint (breakpoint); + + /* If we are running asynchronously, and the target supports async + execution, we are not waiting for the target to stop, in the call + tp proceed, below. This means that we cannot delete the + brekpoints until the target has actually stopped. The only place + where we get a chance to do that is in fetch_inferior_event, so + we must set things up for that. */ + + if (event_loop_p && target_can_async_p ()) + { + /* In this case the arg for the continuation is just the point + in the exec_cleanups chain from where to start doing + cleanups, because all the continuation does is the cleanups in + the exec_cleanup_chain. */ + arg1 = + (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg)); + arg1->next = NULL; + arg1->data.pointer = old_chain; + + add_continuation (until_break_command_continuation, arg1); + } + + /* Keep within the current frame */ + + if (prev_frame) + { + sal = find_pc_line (prev_frame->pc, 0); + sal.pc = prev_frame->pc; + breakpoint = set_momentary_breakpoint (sal, prev_frame, bp_until); + if (!event_loop_p || !target_can_async_p ()) + make_cleanup_delete_breakpoint (breakpoint); + else + make_exec_cleanup_delete_breakpoint (breakpoint); + } + + proceed (-1, TARGET_SIGNAL_DEFAULT, 0); + /* Do the cleanups now, anly if we are not running asynchronously, + of if we are, but the target is still synchronous. */ + if (!event_loop_p || !target_can_async_p ()) + do_cleanups (old_chain); +} + +#if 0 +/* These aren't used; I don't konw what they were for. */ +/* Set a breakpoint at the catch clause for NAME. */ +static int +catch_breakpoint (char *name) +{ +} + +static int +disable_catch_breakpoint (void) +{ +} + +static int +delete_catch_breakpoint (void) +{ +} + +static int +enable_catch_breakpoint (void) +{ +} +#endif /* 0 */ + +struct sal_chain +{ + struct sal_chain *next; + struct symtab_and_line sal; +}; + +#if 0 +/* Not really used -- invocation in handle_gnu_4_16_catch_command + had been commented out in the v.4.16 sources, and stays + disabled there now because "catch NAME" syntax isn't allowed. + pai/1997-07-11 */ +/* This isn't used; I don't know what it was for. */ +/* For each catch clause identified in ARGS, run FUNCTION + with that clause as an argument. */ +static struct symtabs_and_lines +map_catch_names (char *args, int (*function) ()) +{ + register char *p = args; + register char *p1; + struct symtabs_and_lines sals; +#if 0 + struct sal_chain *sal_chain = 0; +#endif + + if (p == 0) + error_no_arg ("one or more catch names"); + + sals.nelts = 0; + sals.sals = NULL; + + while (*p) + { + p1 = p; + /* Don't swallow conditional part. */ + if (p1[0] == 'i' && p1[1] == 'f' + && (p1[2] == ' ' || p1[2] == '\t')) + break; + + if (isalpha (*p1)) + { + p1++; + while (isalnum (*p1) || *p1 == '_' || *p1 == '$') + p1++; + } + + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be catch names."); + + *p1 = 0; +#if 0 + if (function (p)) + { + struct sal_chain *next = (struct sal_chain *) + alloca (sizeof (struct sal_chain)); + next->next = sal_chain; + next->sal = get_catch_sal (p); + sal_chain = next; + goto win; + } +#endif + printf_unfiltered ("No catch clause for exception %s.\n", p); +#if 0 + win: +#endif + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} +#endif + +/* This shares a lot of code with `print_frame_label_vars' from stack.c. */ + +static struct symtabs_and_lines +get_catch_sals (int this_level_only) +{ + register struct blockvector *bl; + register struct block *block; + int index, have_default = 0; + CORE_ADDR pc; + struct symtabs_and_lines sals; + struct sal_chain *sal_chain = 0; + char *blocks_searched; + + /* Not sure whether an error message is always the correct response, + but it's better than a core dump. */ + if (selected_frame == NULL) + error ("No selected frame."); + block = get_frame_block (selected_frame, 0); + pc = selected_frame->pc; + + sals.nelts = 0; + sals.sals = NULL; + + if (block == 0) + error ("No symbol table info available.\n"); + + bl = blockvector_for_pc (BLOCK_END (block) - 4, &index); + blocks_searched = (char *) alloca (BLOCKVECTOR_NBLOCKS (bl) * sizeof (char)); + memset (blocks_searched, 0, BLOCKVECTOR_NBLOCKS (bl) * sizeof (char)); + + while (block != 0) + { + CORE_ADDR end = BLOCK_END (block) - 4; + int last_index; + + if (bl != blockvector_for_pc (end, &index)) + error ("blockvector blotch"); + if (BLOCKVECTOR_BLOCK (bl, index) != block) + error ("blockvector botch"); + last_index = BLOCKVECTOR_NBLOCKS (bl); + index += 1; + + /* Don't print out blocks that have gone by. */ + while (index < last_index + && BLOCK_END (BLOCKVECTOR_BLOCK (bl, index)) < pc) + index++; + + while (index < last_index + && BLOCK_END (BLOCKVECTOR_BLOCK (bl, index)) < end) + { + if (blocks_searched[index] == 0) + { + struct block *b = BLOCKVECTOR_BLOCK (bl, index); + register int i; + register struct symbol *sym; + + ALL_BLOCK_SYMBOLS (b, i, sym) + { + if (STREQ (SYMBOL_NAME (sym), "default")) + { + if (have_default) + continue; + have_default = 1; + } + if (SYMBOL_CLASS (sym) == LOC_LABEL) + { + struct sal_chain *next = (struct sal_chain *) + alloca (sizeof (struct sal_chain)); + next->next = sal_chain; + next->sal = find_pc_line (SYMBOL_VALUE_ADDRESS (sym), + 0); + sal_chain = next; + } + } + blocks_searched[index] = 1; + } + index++; + } + if (have_default) + break; + if (sal_chain && this_level_only) + break; + + /* After handling the function's top-level block, stop. + Don't continue to its superblock, the block of + per-file symbols. */ + if (BLOCK_FUNCTION (block)) + break; + block = BLOCK_SUPERBLOCK (block); + } + + if (sal_chain) + { + struct sal_chain *tmp_chain; + + /* Count the number of entries. */ + for (index = 0, tmp_chain = sal_chain; tmp_chain; + tmp_chain = tmp_chain->next) + index++; + + sals.nelts = index; + sals.sals = (struct symtab_and_line *) + xmalloc (index * sizeof (struct symtab_and_line)); + for (index = 0; sal_chain; sal_chain = sal_chain->next, index++) + sals.sals[index] = sal_chain->sal; + } + + return sals; +} + +static void +ep_skip_leading_whitespace (char **s) +{ + if ((s == NULL) || (*s == NULL)) + return; + while (isspace (**s)) + *s += 1; +} + +/* This function examines a string, and attempts to find a token + that might be an event name in the leading characters. If a + possible match is found, a pointer to the last character of + the token is returned. Else, NULL is returned. */ + +static char * +ep_find_event_name_end (char *arg) +{ + char *s = arg; + char *event_name_end = NULL; + + /* If we could depend upon the presense of strrpbrk, we'd use that... */ + if (arg == NULL) + return NULL; + + /* We break out of the loop when we find a token delimiter. + Basically, we're looking for alphanumerics and underscores; + anything else delimites the token. */ + while (*s != '\0') + { + if (!isalnum (*s) && (*s != '_')) + break; + event_name_end = s; + s++; + } + + return event_name_end; +} + + +/* This function attempts to parse an optional "if " clause + from the arg string. If one is not found, it returns NULL. + + Else, it returns a pointer to the condition string. (It does not + attempt to evaluate the string against a particular block.) And, + it updates arg to point to the first character following the parsed + if clause in the arg string. */ + +static char * +ep_parse_optional_if_clause (char **arg) +{ + char *cond_string; + + if (((*arg)[0] != 'i') || ((*arg)[1] != 'f') || !isspace ((*arg)[2])) + return NULL; + + /* Skip the "if" keyword. */ + (*arg) += 2; + + /* Skip any extra leading whitespace, and record the start of the + condition string. */ + ep_skip_leading_whitespace (arg); + cond_string = *arg; + + /* Assume that the condition occupies the remainder of the arg string. */ + (*arg) += strlen (cond_string); + + return cond_string; +} + +/* This function attempts to parse an optional filename from the arg + string. If one is not found, it returns NULL. + + Else, it returns a pointer to the parsed filename. (This function + makes no attempt to verify that a file of that name exists, or is + accessible.) And, it updates arg to point to the first character + following the parsed filename in the arg string. + + Note that clients needing to preserve the returned filename for + future access should copy it to their own buffers. */ +static char * +ep_parse_optional_filename (char **arg) +{ + static char filename[1024]; + char *arg_p = *arg; + int i; + char c; + + if ((*arg_p == '\0') || isspace (*arg_p)) + return NULL; + + for (i = 0;; i++) + { + c = *arg_p; + if (isspace (c)) + c = '\0'; + filename[i] = c; + if (c == '\0') + break; + arg_p++; + } + *arg = arg_p; + + return filename; +} + +/* Commands to deal with catching events, such as signals, exceptions, + process start/exit, etc. */ + +typedef enum +{ + catch_fork, catch_vfork +} +catch_fork_kind; + +#if defined(CHILD_INSERT_FORK_CATCHPOINT) || defined(CHILD_INSERT_VFORK_CATCHPOINT) +static void catch_fork_command_1 (catch_fork_kind fork_kind, + char *arg, int tempflag, int from_tty); + +static void +catch_fork_command_1 (catch_fork_kind fork_kind, char *arg, int tempflag, + int from_tty) +{ + char *cond_string = NULL; + + ep_skip_leading_whitespace (&arg); + + /* The allowed syntax is: + catch [v]fork + catch [v]fork if + + First, check if there's an if clause. */ + cond_string = ep_parse_optional_if_clause (&arg); + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + /* If this target supports it, create a fork or vfork catchpoint + and enable reporting of such events. */ + switch (fork_kind) + { + case catch_fork: + create_fork_event_catchpoint (tempflag, cond_string); + break; + case catch_vfork: + create_vfork_event_catchpoint (tempflag, cond_string); + break; + default: + error ("unsupported or unknown fork kind; cannot catch it"); + break; + } +} +#endif + +#if defined(CHILD_INSERT_EXEC_CATCHPOINT) +static void +catch_exec_command_1 (char *arg, int tempflag, int from_tty) +{ + char *cond_string = NULL; + + ep_skip_leading_whitespace (&arg); + + /* The allowed syntax is: + catch exec + catch exec if + + First, check if there's an if clause. */ + cond_string = ep_parse_optional_if_clause (&arg); + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + /* If this target supports it, create an exec catchpoint + and enable reporting of such events. */ + create_exec_event_catchpoint (tempflag, cond_string); +} +#endif + +#if defined(SOLIB_ADD) +static void +catch_load_command_1 (char *arg, int tempflag, int from_tty) +{ + char *dll_pathname = NULL; + char *cond_string = NULL; + + ep_skip_leading_whitespace (&arg); + + /* The allowed syntax is: + catch load + catch load if + catch load + catch load if + + The user is not allowed to specify the after an + if clause. + + We'll ignore the pathological case of a file named "if". + + First, check if there's an if clause. If so, then there + cannot be a filename. */ + cond_string = ep_parse_optional_if_clause (&arg); + + /* If there was an if clause, then there cannot be a filename. + Else, there might be a filename and an if clause. */ + if (cond_string == NULL) + { + dll_pathname = ep_parse_optional_filename (&arg); + ep_skip_leading_whitespace (&arg); + cond_string = ep_parse_optional_if_clause (&arg); + } + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + /* Create a load breakpoint that only triggers when a load of + the specified dll (or any dll, if no pathname was specified) + occurs. */ + SOLIB_CREATE_CATCH_LOAD_HOOK (PIDGET (inferior_ptid), tempflag, + dll_pathname, cond_string); +} + +static void +catch_unload_command_1 (char *arg, int tempflag, int from_tty) +{ + char *dll_pathname = NULL; + char *cond_string = NULL; + + ep_skip_leading_whitespace (&arg); + + /* The allowed syntax is: + catch unload + catch unload if + catch unload + catch unload if + + The user is not allowed to specify the after an + if clause. + + We'll ignore the pathological case of a file named "if". + + First, check if there's an if clause. If so, then there + cannot be a filename. */ + cond_string = ep_parse_optional_if_clause (&arg); + + /* If there was an if clause, then there cannot be a filename. + Else, there might be a filename and an if clause. */ + if (cond_string == NULL) + { + dll_pathname = ep_parse_optional_filename (&arg); + ep_skip_leading_whitespace (&arg); + cond_string = ep_parse_optional_if_clause (&arg); + } + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + /* Create an unload breakpoint that only triggers when an unload of + the specified dll (or any dll, if no pathname was specified) + occurs. */ + SOLIB_CREATE_CATCH_UNLOAD_HOOK (PIDGET (inferior_ptid), tempflag, + dll_pathname, cond_string); +} +#endif /* SOLIB_ADD */ + +/* Commands to deal with catching exceptions. */ + +/* Set a breakpoint at the specified callback routine for an + exception event callback */ + +static void +create_exception_catchpoint (int tempflag, char *cond_string, + enum exception_event_kind ex_event, + struct symtab_and_line *sal) +{ + struct breakpoint *b; + int thread = -1; /* All threads. */ + enum bptype bptype; + + if (!sal) /* no exception support? */ + return; + + switch (ex_event) + { + case EX_EVENT_THROW: + bptype = bp_catch_throw; + break; + case EX_EVENT_CATCH: + bptype = bp_catch_catch; + break; + default: /* error condition */ + error ("Internal error -- invalid catchpoint kind"); + } + + b = set_raw_breakpoint (*sal, bptype); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = NULL; + b->cond_string = (cond_string == NULL) ? + NULL : savestring (cond_string, strlen (cond_string)); + b->thread = thread; + b->addr_string = NULL; + b->enable_state = bp_enabled; + b->disposition = tempflag ? disp_del : disp_donttouch; + mention (b); +} + +/* Deal with "catch catch" and "catch throw" commands */ + +static void +catch_exception_command_1 (enum exception_event_kind ex_event, char *arg, + int tempflag, int from_tty) +{ + char *cond_string = NULL; + struct symtab_and_line *sal = NULL; + + ep_skip_leading_whitespace (&arg); + + cond_string = ep_parse_optional_if_clause (&arg); + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + if ((ex_event != EX_EVENT_THROW) && + (ex_event != EX_EVENT_CATCH)) + error ("Unsupported or unknown exception event; cannot catch it"); + + /* See if we can find a callback routine */ + sal = target_enable_exception_callback (ex_event, 1); + + if (sal) + { + /* We have callbacks from the runtime system for exceptions. + Set a breakpoint on the sal found, if no errors */ + if (sal != (struct symtab_and_line *) -1) + create_exception_catchpoint (tempflag, cond_string, ex_event, sal); + else + return; /* something went wrong with setting up callbacks */ + } + else + { + /* No callbacks from runtime system for exceptions. + Try GNU C++ exception breakpoints using labels in debug info. */ + if (ex_event == EX_EVENT_CATCH) + { + handle_gnu_4_16_catch_command (arg, tempflag, from_tty); + } + else if (ex_event == EX_EVENT_THROW) + { + /* Set a breakpoint on __raise_exception () */ + + warning ("Unsupported with this platform/compiler combination."); + warning ("Perhaps you can achieve the effect you want by setting"); + warning ("a breakpoint on __raise_exception()."); + } + } +} + +/* Cover routine to allow wrapping target_enable_exception_catchpoints + inside a catch_errors */ + +static int +cover_target_enable_exception_callback (PTR arg) +{ + args_for_catchpoint_enable *args = arg; + struct symtab_and_line *sal; + sal = target_enable_exception_callback (args->kind, args->enable_p); + if (sal == NULL) + return 0; + else if (sal == (struct symtab_and_line *) -1) + return -1; + else + return 1; /*is valid */ +} + + + +/* This is the original v.4.16 and earlier version of the + catch_command_1() function. Now that other flavours of "catch" + have been introduced, and since exception handling can be handled + in other ways (through target ops) also, this is used only for the + GNU C++ exception handling system. + Note: Only the "catch" flavour of GDB 4.16 is handled here. The + "catch NAME" is now no longer allowed in catch_command_1(). Also, + there was no code in GDB 4.16 for "catch throw". + + Called from catch_exception_command_1 () */ + + +static void +handle_gnu_4_16_catch_command (char *arg, int tempflag, int from_tty) +{ + /* First, translate ARG into something we can deal with in terms + of breakpoints. */ + + struct symtabs_and_lines sals; + struct symtab_and_line sal; + register struct expression *cond = 0; + register struct breakpoint *b; + char *save_arg; + int i; + + INIT_SAL (&sal); /* initialize to zeroes */ + + /* If no arg given, or if first arg is 'if ', all active catch clauses + are breakpointed. */ + + if (!arg || (arg[0] == 'i' && arg[1] == 'f' + && (arg[2] == ' ' || arg[2] == '\t'))) + { + /* Grab all active catch clauses. */ + sals = get_catch_sals (0); + } + else + { + /* Grab selected catch clauses. */ + error ("catch NAME not implemented"); + +#if 0 + /* Not sure why this code has been disabled. I'm leaving + it disabled. We can never come here now anyway + since we don't allow the "catch NAME" syntax. + pai/1997-07-11 */ + + /* This isn't used; I don't know what it was for. */ + sals = map_catch_names (arg, catch_breakpoint); +#endif + } + + if (!sals.nelts) + return; + + save_arg = arg; + for (i = 0; i < sals.nelts; i++) + { + resolve_sal_pc (&sals.sals[i]); + + while (arg && *arg) + { + if (arg[0] == 'i' && arg[1] == 'f' + && (arg[2] == ' ' || arg[2] == '\t')) + cond = parse_exp_1 ((arg += 2, &arg), + block_for_pc (sals.sals[i].pc), 0); + else + error ("Junk at end of arguments."); + } + arg = save_arg; + } + + for (i = 0; i < sals.nelts; i++) + { + sal = sals.sals[i]; + + if (from_tty) + describe_other_breakpoints (sal.pc, sal.section); + + /* Important -- this is an ordinary breakpoint. For platforms + with callback support for exceptions, + create_exception_catchpoint() will create special bp types + (bp_catch_catch and bp_catch_throw), and there is code in + insert_breakpoints() and elsewhere that depends on that. */ + b = set_raw_breakpoint (sal, bp_breakpoint); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + + b->cond = cond; + b->enable_state = bp_enabled; + b->disposition = tempflag ? disp_del : disp_donttouch; + + mention (b); + } + + if (sals.nelts > 1) + { + warning ("Multiple breakpoints were set."); + warning ("Use the \"delete\" command to delete unwanted breakpoints."); + } + xfree (sals.sals); +} + +static void +catch_command_1 (char *arg, int tempflag, int from_tty) +{ + + /* The first argument may be an event name, such as "start" or "load". + If so, then handle it as such. If it doesn't match an event name, + then attempt to interpret it as an exception name. (This latter is + the v4.16-and-earlier GDB meaning of the "catch" command.) + + First, try to find the bounds of what might be an event name. */ + char *arg1_start = arg; + char *arg1_end; + int arg1_length; + + if (arg1_start == NULL) + { + /* Old behaviour was to use pre-v-4.16 syntax */ + /* catch_throw_command_1 (arg1_start, tempflag, from_tty); */ + /* return; */ + /* Now, this is not allowed */ + error ("Catch requires an event name."); + + } + arg1_end = ep_find_event_name_end (arg1_start); + if (arg1_end == NULL) + error ("catch requires an event"); + arg1_length = arg1_end + 1 - arg1_start; + + /* Try to match what we found against known event names. */ + if (strncmp (arg1_start, "signal", arg1_length) == 0) + { + error ("Catch of signal not yet implemented"); + } + else if (strncmp (arg1_start, "catch", arg1_length) == 0) + { + catch_exception_command_1 (EX_EVENT_CATCH, arg1_end + 1, + tempflag, from_tty); + } + else if (strncmp (arg1_start, "throw", arg1_length) == 0) + { + catch_exception_command_1 (EX_EVENT_THROW, arg1_end + 1, + tempflag, from_tty); + } + else if (strncmp (arg1_start, "thread_start", arg1_length) == 0) + { + error ("Catch of thread_start not yet implemented"); + } + else if (strncmp (arg1_start, "thread_exit", arg1_length) == 0) + { + error ("Catch of thread_exit not yet implemented"); + } + else if (strncmp (arg1_start, "thread_join", arg1_length) == 0) + { + error ("Catch of thread_join not yet implemented"); + } + else if (strncmp (arg1_start, "start", arg1_length) == 0) + { + error ("Catch of start not yet implemented"); + } + else if (strncmp (arg1_start, "exit", arg1_length) == 0) + { + error ("Catch of exit not yet implemented"); + } + else if (strncmp (arg1_start, "fork", arg1_length) == 0) + { +#if defined(CHILD_INSERT_FORK_CATCHPOINT) + catch_fork_command_1 (catch_fork, arg1_end + 1, tempflag, from_tty); +#else + error ("Catch of fork not yet implemented"); +#endif + } + else if (strncmp (arg1_start, "vfork", arg1_length) == 0) + { +#if defined(CHILD_INSERT_VFORK_CATCHPOINT) + catch_fork_command_1 (catch_vfork, arg1_end + 1, tempflag, from_tty); +#else + error ("Catch of vfork not yet implemented"); +#endif + } + else if (strncmp (arg1_start, "exec", arg1_length) == 0) + { +#if defined(CHILD_INSERT_EXEC_CATCHPOINT) + catch_exec_command_1 (arg1_end + 1, tempflag, from_tty); +#else + error ("Catch of exec not yet implemented"); +#endif + } + else if (strncmp (arg1_start, "load", arg1_length) == 0) + { +#if defined(SOLIB_ADD) + catch_load_command_1 (arg1_end + 1, tempflag, from_tty); +#else + error ("Catch of load not implemented"); +#endif + } + else if (strncmp (arg1_start, "unload", arg1_length) == 0) + { +#if defined(SOLIB_ADD) + catch_unload_command_1 (arg1_end + 1, tempflag, from_tty); +#else + error ("Catch of load not implemented"); +#endif + } + else if (strncmp (arg1_start, "stop", arg1_length) == 0) + { + error ("Catch of stop not yet implemented"); + } + + /* This doesn't appear to be an event name */ + + else + { + /* Pre-v.4.16 behaviour was to treat the argument + as the name of an exception */ + /* catch_throw_command_1 (arg1_start, tempflag, from_tty); */ + /* Now this is not allowed */ + error ("Unknown event kind specified for catch"); + + } +} + +/* Used by the gui, could be made a worker for other things. */ + +struct breakpoint * +set_breakpoint_sal (struct symtab_and_line sal) +{ + struct breakpoint *b; + b = set_raw_breakpoint (sal, bp_breakpoint); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = 0; + b->thread = -1; + return b; +} + +#if 0 +/* These aren't used; I don't know what they were for. */ +/* Disable breakpoints on all catch clauses described in ARGS. */ +static void +disable_catch (char *args) +{ + /* Map the disable command to catch clauses described in ARGS. */ +} + +/* Enable breakpoints on all catch clauses described in ARGS. */ +static void +enable_catch (char *args) +{ + /* Map the disable command to catch clauses described in ARGS. */ +} + +/* Delete breakpoints on all catch clauses in the active scope. */ +static void +delete_catch (char *args) +{ + /* Map the delete command to catch clauses described in ARGS. */ +} +#endif /* 0 */ + +static void +catch_command (char *arg, int from_tty) +{ + catch_command_1 (arg, 0, from_tty); +} + + +static void +tcatch_command (char *arg, int from_tty) +{ + catch_command_1 (arg, 1, from_tty); +} + +/* Delete breakpoints by address or line. */ + +static void +clear_command (char *arg, int from_tty) +{ + struct breakpoint *b, *tmp, *prev, *found; + int default_match; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + int i; + + if (arg) + { + sals = decode_line_spec (arg, 1); + default_match = 0; + } + else + { + sals.sals = (struct symtab_and_line *) + xmalloc (sizeof (struct symtab_and_line)); + make_cleanup (xfree, sals.sals); + INIT_SAL (&sal); /* initialize to zeroes */ + sal.line = default_breakpoint_line; + sal.symtab = default_breakpoint_symtab; + sal.pc = default_breakpoint_address; + if (sal.symtab == 0) + error ("No source file specified."); + + sals.sals[0] = sal; + sals.nelts = 1; + + default_match = 1; + } + + /* For each line spec given, delete bps which correspond + to it. Do it in two passes, solely to preserve the current + behavior that from_tty is forced true if we delete more than + one breakpoint. */ + + found = NULL; + for (i = 0; i < sals.nelts; i++) + { + /* If exact pc given, clear bpts at that pc. + If line given (pc == 0), clear all bpts on specified line. + If defaulting, clear all bpts on default line + or at default pc. + + defaulting sal.pc != 0 tests to do + + 0 1 pc + 1 1 pc _and_ line + 0 0 line + 1 0 */ + + sal = sals.sals[i]; + prev = NULL; + + /* Find all matching breakpoints, remove them from the + breakpoint chain, and add them to the 'found' chain. */ + ALL_BREAKPOINTS_SAFE (b, tmp) + { + /* Are we going to delete b? */ + if (b->type != bp_none + && b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint + && b->type != bp_read_watchpoint + && b->type != bp_access_watchpoint + /* Not if b is a watchpoint of any sort... */ + && (((sal.pc && (b->address == sal.pc)) + && (!section_is_overlay (b->section) + || b->section == sal.section)) + /* Yes, if sal.pc matches b (modulo overlays). */ + || ((default_match || (0 == sal.pc)) + && b->source_file != NULL + && sal.symtab != NULL + && STREQ (b->source_file, sal.symtab->filename) + && b->line_number == sal.line))) + /* Yes, if sal source file and line matches b. */ + { + /* Remove it from breakpoint_chain... */ + if (b == breakpoint_chain) + { + /* b is at the head of the list */ + breakpoint_chain = b->next; + } + else + { + prev->next = b->next; + } + /* And add it to 'found' chain. */ + b->next = found; + found = b; + } + else + { + /* Keep b, and keep a pointer to it. */ + prev = b; + } + } + } + /* Now go thru the 'found' chain and delete them. */ + if (found == 0) + { + if (arg) + error ("No breakpoint at %s.", arg); + else + error ("No breakpoint at this line."); + } + + if (found->next) + from_tty = 1; /* Always report if deleted more than one */ + if (from_tty) + printf_unfiltered ("Deleted breakpoint%s ", found->next ? "s" : ""); + breakpoints_changed (); + while (found) + { + if (from_tty) + printf_unfiltered ("%d ", found->number); + tmp = found->next; + delete_breakpoint (found); + found = tmp; + } + if (from_tty) + putchar_unfiltered ('\n'); +} + +/* Delete breakpoint in BS if they are `delete' breakpoints and + all breakpoints that are marked for deletion, whether hit or not. + This is called after any breakpoint is hit, or after errors. */ + +void +breakpoint_auto_delete (bpstat bs) +{ + struct breakpoint *b, *temp; + + for (; bs; bs = bs->next) + if (bs->breakpoint_at && bs->breakpoint_at->disposition == disp_del + && bs->stop) + delete_breakpoint (bs->breakpoint_at); + + ALL_BREAKPOINTS_SAFE (b, temp) + { + if (b->disposition == disp_del_at_next_stop) + delete_breakpoint (b); + } +} + +/* Delete a breakpoint and clean up all traces of it in the data + structures. */ + +void +delete_breakpoint (struct breakpoint *bpt) +{ + register struct breakpoint *b; + register bpstat bs; + + if (bpt == NULL) + error ("Internal error (attempted to delete a NULL breakpoint)"); + + + /* Has this bp already been deleted? This can happen because multiple + lists can hold pointers to bp's. bpstat lists are especial culprits. + + One example of this happening is a watchpoint's scope bp. When the + scope bp triggers, we notice that the watchpoint is out of scope, and + delete it. We also delete its scope bp. But the scope bp is marked + "auto-deleting", and is already on a bpstat. That bpstat is then + checked for auto-deleting bp's, which are deleted. + + A real solution to this problem might involve reference counts in bp's, + and/or giving them pointers back to their referencing bpstat's, and + teaching delete_breakpoint to only free a bp's storage when no more + references were extent. A cheaper bandaid was chosen. */ + if (bpt->type == bp_none) + return; + + if (delete_breakpoint_hook) + delete_breakpoint_hook (bpt); + breakpoint_delete_event (bpt->number); + + if (bpt->inserted) + remove_breakpoint (bpt, mark_inserted); + + if (breakpoint_chain == bpt) + breakpoint_chain = bpt->next; + + /* If we have callback-style exception catchpoints, don't go through + the adjustments to the C++ runtime library etc. if the inferior + isn't actually running. target_enable_exception_callback for a + null target ops vector gives an undesirable error message, so we + check here and avoid it. Since currently (1997-09-17) only HP-UX aCC's + exceptions are supported in this way, it's OK for now. FIXME */ + if (ep_is_exception_catchpoint (bpt) && target_has_execution) + { + static char message1[] = "Error in deleting catchpoint %d:\n"; + static char message[sizeof (message1) + 30]; + args_for_catchpoint_enable args; + + /* Format possible error msg */ + sprintf (message, message1, bpt->number); + args.kind = bpt->type == bp_catch_catch ? + EX_EVENT_CATCH : EX_EVENT_THROW; + args.enable_p = 0; + catch_errors (cover_target_enable_exception_callback, &args, + message, RETURN_MASK_ALL); + } + + + ALL_BREAKPOINTS (b) + if (b->next == bpt) + { + b->next = bpt->next; + break; + } + + check_duplicates (bpt); + /* If this breakpoint was inserted, and there is another breakpoint + at the same address, we need to insert the other breakpoint. */ + if (bpt->inserted + && bpt->type != bp_hardware_watchpoint + && bpt->type != bp_read_watchpoint + && bpt->type != bp_access_watchpoint + && bpt->type != bp_catch_fork + && bpt->type != bp_catch_vfork + && bpt->type != bp_catch_exec) + { + ALL_BREAKPOINTS (b) + if (b->address == bpt->address + && b->section == bpt->section + && !b->duplicate + && b->enable_state != bp_disabled + && b->enable_state != bp_shlib_disabled + && b->enable_state != bp_call_disabled) + { + int val; + + /* We should never reach this point if there is a permanent + breakpoint at the same address as the one being deleted. + If there is a permanent breakpoint somewhere, it should + always be the only one inserted. */ + if (b->enable_state == bp_permanent) + internal_error (__FILE__, __LINE__, + "another breakpoint was inserted on top of " + "a permanent breakpoint"); + + if (b->type == bp_hardware_breakpoint) + val = target_insert_hw_breakpoint (b->address, b->shadow_contents); + else + val = target_insert_breakpoint (b->address, b->shadow_contents); + + /* If there was an error in the insert, print a message, then stop execution. */ + if (val != 0) + { + struct ui_file *tmp_error_stream = mem_fileopen (); + make_cleanup_ui_file_delete (tmp_error_stream); + + + if (b->type == bp_hardware_breakpoint) + { + fprintf_unfiltered (tmp_error_stream, + "Cannot insert hardware breakpoint %d.\n" + "You may have requested too many hardware breakpoints.\n", + b->number); + } + else + { + fprintf_unfiltered (tmp_error_stream, "Cannot insert breakpoint %d.\n", b->number); + fprintf_filtered (tmp_error_stream, "Error accessing memory address "); + print_address_numeric (b->address, 1, tmp_error_stream); + fprintf_filtered (tmp_error_stream, ": %s.\n", + safe_strerror (val)); + } + + fprintf_unfiltered (tmp_error_stream,"The same program may be running in another process."); + target_terminal_ours_for_output (); + error_stream(tmp_error_stream); + } + else + b->inserted = 1; + } + } + + free_command_lines (&bpt->commands); + if (bpt->cond) + xfree (bpt->cond); + if (bpt->cond_string != NULL) + xfree (bpt->cond_string); + if (bpt->addr_string != NULL) + xfree (bpt->addr_string); + if (bpt->exp != NULL) + xfree (bpt->exp); + if (bpt->exp_string != NULL) + xfree (bpt->exp_string); + if (bpt->val != NULL) + value_free (bpt->val); + if (bpt->source_file != NULL) + xfree (bpt->source_file); + if (bpt->dll_pathname != NULL) + xfree (bpt->dll_pathname); + if (bpt->triggered_dll_pathname != NULL) + xfree (bpt->triggered_dll_pathname); + if (bpt->exec_pathname != NULL) + xfree (bpt->exec_pathname); + + /* Be sure no bpstat's are pointing at it after it's been freed. */ + /* FIXME, how can we find all bpstat's? + We just check stop_bpstat for now. */ + for (bs = stop_bpstat; bs; bs = bs->next) + if (bs->breakpoint_at == bpt) + { + bs->breakpoint_at = NULL; + bs->old_val = NULL; + /* bs->commands will be freed later. */ + } + /* On the chance that someone will soon try again to delete this same + bp, we mark it as deleted before freeing its storage. */ + bpt->type = bp_none; + + xfree (bpt); +} + +static void +do_delete_breakpoint_cleanup (void *b) +{ + delete_breakpoint (b); +} + +struct cleanup * +make_cleanup_delete_breakpoint (struct breakpoint *b) +{ + return make_cleanup (do_delete_breakpoint_cleanup, b); +} + +struct cleanup * +make_exec_cleanup_delete_breakpoint (struct breakpoint *b) +{ + return make_exec_cleanup (do_delete_breakpoint_cleanup, b); +} + +void +delete_command (char *arg, int from_tty) +{ + struct breakpoint *b, *temp; + + dont_repeat (); + + if (arg == 0) + { + int breaks_to_delete = 0; + + /* Delete all breakpoints if no argument. + Do not delete internal or call-dummy breakpoints, these + have to be deleted with an explicit breakpoint number argument. */ + ALL_BREAKPOINTS (b) + { + if (b->type != bp_call_dummy && + b->type != bp_shlib_event && + b->type != bp_thread_event && + b->type != bp_overlay_event && + b->number >= 0) + breaks_to_delete = 1; + } + + /* Ask user only if there are some breakpoints to delete. */ + if (!from_tty + || (breaks_to_delete && query ("Delete all breakpoints? "))) + { + ALL_BREAKPOINTS_SAFE (b, temp) + { + if (b->type != bp_call_dummy && + b->type != bp_shlib_event && + b->type != bp_thread_event && + b->type != bp_overlay_event && + b->number >= 0) + delete_breakpoint (b); + } + } + } + else + map_breakpoint_numbers (arg, delete_breakpoint); +} + +/* Reset a breakpoint given it's struct breakpoint * BINT. + The value we return ends up being the return value from catch_errors. + Unused in this case. */ + +static int +breakpoint_re_set_one (PTR bint) +{ + /* get past catch_errs */ + struct breakpoint *b = (struct breakpoint *) bint; + struct value *mark; + int i; + struct symtabs_and_lines sals; + char *s; + enum enable_state save_enable; + + switch (b->type) + { + case bp_none: + warning ("attempted to reset apparently deleted breakpoint #%d?", + b->number); + return 0; + case bp_breakpoint: + case bp_hardware_breakpoint: + case bp_catch_load: + case bp_catch_unload: + if (b->addr_string == NULL) + { + /* Anything without a string can't be re-set. */ + delete_breakpoint (b); + return 0; + } + /* HACK: cagney/2001-11-11: kettenis/2001-11-11: MarkK wrote: + + ``And a hack it is, although Apple's Darwin version of GDB + contains an almost identical hack to implement a "future + break" command. It seems to work in many real world cases, + but it is easy to come up with a test case where the patch + doesn't help at all.'' + + ``It seems that the way GDB implements breakpoints - in - + shared - libraries was designed for a.out shared library + systems (SunOS 4) where shared libraries were loaded at a + fixed address in memory. Since ELF shared libraries can (and + will) be loaded at any address in memory, things break. + Fixing this is not trivial. Therefore, I'm not sure whether + we should add this hack to the branch only. I cannot + guarantee that things will be fixed on the trunk in the near + future.'' + + In case we have a problem, disable this breakpoint. We'll + restore its status if we succeed. Don't disable a + shlib_disabled breakpoint though. There's a fair chance we + can't re-set it if the shared library it's in hasn't been + loaded yet. */ + save_enable = b->enable_state; + if (b->enable_state != bp_shlib_disabled) + b->enable_state = bp_disabled; + + set_language (b->language); + input_radix = b->input_radix; + s = b->addr_string; + sals = decode_line_1 (&s, 1, (struct symtab *) NULL, 0, (char ***) NULL); + for (i = 0; i < sals.nelts; i++) + { + resolve_sal_pc (&sals.sals[i]); + + /* Reparse conditions, they might contain references to the + old symtab. */ + if (b->cond_string != NULL) + { + s = b->cond_string; + if (b->cond) + xfree (b->cond); + b->cond = parse_exp_1 (&s, block_for_pc (sals.sals[i].pc), 0); + } + + /* We need to re-set the breakpoint if the address changes... */ + if (b->address != sals.sals[i].pc + /* ...or new and old breakpoints both have source files, and + the source file name or the line number changes... */ + || (b->source_file != NULL + && sals.sals[i].symtab != NULL + && (!STREQ (b->source_file, sals.sals[i].symtab->filename) + || b->line_number != sals.sals[i].line) + ) + /* ...or we switch between having a source file and not having + one. */ + || ((b->source_file == NULL) != (sals.sals[i].symtab == NULL)) + ) + { + if (b->source_file != NULL) + xfree (b->source_file); + if (sals.sals[i].symtab == NULL) + b->source_file = NULL; + else + b->source_file = + savestring (sals.sals[i].symtab->filename, + strlen (sals.sals[i].symtab->filename)); + b->line_number = sals.sals[i].line; + b->address = sals.sals[i].pc; + + /* Used to check for duplicates here, but that can + cause trouble, as it doesn't check for disabled + breakpoints. */ + + mention (b); + + /* Might be better to do this just once per breakpoint_re_set, + rather than once for every breakpoint. */ + breakpoints_changed (); + } + b->section = sals.sals[i].section; + b->enable_state = save_enable; /* Restore it, this worked. */ + + + /* Now that this is re-enabled, check_duplicates + can be used. */ + check_duplicates (b); + + } + xfree (sals.sals); + break; + + case bp_watchpoint: + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_access_watchpoint: + innermost_block = NULL; + /* The issue arises of what context to evaluate this in. The + same one as when it was set, but what does that mean when + symbols have been re-read? We could save the filename and + functionname, but if the context is more local than that, the + best we could do would be something like how many levels deep + and which index at that particular level, but that's going to + be less stable than filenames or function names. */ + + /* So for now, just use a global context. */ + if (b->exp) + xfree (b->exp); + b->exp = parse_expression (b->exp_string); + b->exp_valid_block = innermost_block; + mark = value_mark (); + if (b->val) + value_free (b->val); + b->val = evaluate_expression (b->exp); + release_value (b->val); + if (VALUE_LAZY (b->val)) + value_fetch_lazy (b->val); + + if (b->cond_string != NULL) + { + s = b->cond_string; + if (b->cond) + xfree (b->cond); + b->cond = parse_exp_1 (&s, (struct block *) 0, 0); + } + if (b->enable_state == bp_enabled) + mention (b); + value_free_to_mark (mark); + break; + case bp_catch_catch: + case bp_catch_throw: + break; + /* We needn't really do anything to reset these, since the mask + that requests them is unaffected by e.g., new libraries being + loaded. */ + case bp_catch_fork: + case bp_catch_vfork: + case bp_catch_exec: + break; + + default: + printf_filtered ("Deleting unknown breakpoint type %d\n", b->type); + /* fall through */ + /* Delete longjmp and overlay event breakpoints; they will be + reset later by breakpoint_re_set. */ + case bp_longjmp: + case bp_longjmp_resume: + case bp_overlay_event: + delete_breakpoint (b); + break; + + /* This breakpoint is special, it's set up when the inferior + starts and we really don't want to touch it. */ + case bp_shlib_event: + + /* Like bp_shlib_event, this breakpoint type is special. + Once it is set up, we do not want to touch it. */ + case bp_thread_event: + + /* Keep temporary breakpoints, which can be encountered when we step + over a dlopen call and SOLIB_ADD is resetting the breakpoints. + Otherwise these should have been blown away via the cleanup chain + or by breakpoint_init_inferior when we rerun the executable. */ + case bp_until: + case bp_finish: + case bp_watchpoint_scope: + case bp_call_dummy: + case bp_step_resume: + break; + } + + return 0; +} + +/* Re-set all breakpoints after symbols have been re-loaded. */ +void +breakpoint_re_set (void) +{ + struct breakpoint *b, *temp; + enum language save_language; + int save_input_radix; + static char message1[] = "Error in re-setting breakpoint %d:\n"; + char message[sizeof (message1) + 30 /* slop */ ]; + + save_language = current_language->la_language; + save_input_radix = input_radix; + ALL_BREAKPOINTS_SAFE (b, temp) + { + /* Format possible error msg */ + sprintf (message, message1, b->number); + catch_errors (breakpoint_re_set_one, b, message, RETURN_MASK_ALL); + } + set_language (save_language); + input_radix = save_input_radix; + + if (GET_LONGJMP_TARGET_P ()) + { + create_longjmp_breakpoint ("longjmp"); + create_longjmp_breakpoint ("_longjmp"); + create_longjmp_breakpoint ("siglongjmp"); + create_longjmp_breakpoint ("_siglongjmp"); + create_longjmp_breakpoint (NULL); + } + + create_overlay_event_breakpoint ("_ovly_debug_event"); +} + +/* Reset the thread number of this breakpoint: + + - If the breakpoint is for all threads, leave it as-is. + - Else, reset it to the current thread for inferior_ptid. */ +void +breakpoint_re_set_thread (struct breakpoint *b) +{ + if (b->thread != -1) + { + if (in_thread_list (inferior_ptid)) + b->thread = pid_to_thread_id (inferior_ptid); + } +} + +/* Set ignore-count of breakpoint number BPTNUM to COUNT. + If from_tty is nonzero, it prints a message to that effect, + which ends with a period (no newline). */ + +void +set_ignore_count (int bptnum, int count, int from_tty) +{ + register struct breakpoint *b; + + if (count < 0) + count = 0; + + ALL_BREAKPOINTS (b) + if (b->number == bptnum) + { + b->ignore_count = count; + if (from_tty) + { + if (count == 0) + printf_filtered ("Will stop next time breakpoint %d is reached.", + bptnum); + else if (count == 1) + printf_filtered ("Will ignore next crossing of breakpoint %d.", + bptnum); + else + printf_filtered ("Will ignore next %d crossings of breakpoint %d.", + count, bptnum); + } + breakpoints_changed (); + breakpoint_modify_event (b->number); + return; + } + + error ("No breakpoint number %d.", bptnum); +} + +/* Clear the ignore counts of all breakpoints. */ +void +breakpoint_clear_ignore_counts (void) +{ + struct breakpoint *b; + + ALL_BREAKPOINTS (b) + b->ignore_count = 0; +} + +/* Command to set ignore-count of breakpoint N to COUNT. */ + +static void +ignore_command (char *args, int from_tty) +{ + char *p = args; + register int num; + + if (p == 0) + error_no_arg ("a breakpoint number"); + + num = get_number (&p); + if (num == 0) + error ("bad breakpoint number: '%s'", args); + if (*p == 0) + error ("Second argument (specified ignore-count) is missing."); + + set_ignore_count (num, + longest_to_int (value_as_long (parse_and_eval (p))), + from_tty); + if (from_tty) + printf_filtered ("\n"); +} + +/* Call FUNCTION on each of the breakpoints + whose numbers are given in ARGS. */ + +static void +map_breakpoint_numbers (char *args, void (*function) (struct breakpoint *)) +{ + register char *p = args; + char *p1; + register int num; + register struct breakpoint *b, *tmp; + int match; + + if (p == 0) + error_no_arg ("one or more breakpoint numbers"); + + while (*p) + { + match = 0; + p1 = p; + + num = get_number_or_range (&p1); + if (num == 0) + { + warning ("bad breakpoint number at or near '%s'", p); + } + else + { + ALL_BREAKPOINTS_SAFE (b, tmp) + if (b->number == num) + { + struct breakpoint *related_breakpoint = b->related_breakpoint; + match = 1; + function (b); + if (related_breakpoint) + function (related_breakpoint); + break; + } + if (match == 0) + printf_unfiltered ("No breakpoint number %d.\n", num); + } + p = p1; + } +} + +/* Set ignore-count of breakpoint number BPTNUM to COUNT. + If from_tty is nonzero, it prints a message to that effect, + which ends with a period (no newline). */ + +void +disable_breakpoint (struct breakpoint *bpt) +{ + /* Never disable a watchpoint scope breakpoint; we want to + hit them when we leave scope so we can delete both the + watchpoint and its scope breakpoint at that time. */ + if (bpt->type == bp_watchpoint_scope) + return; + + /* You can't disable permanent breakpoints. */ + if (bpt->enable_state == bp_permanent) + return; + + bpt->enable_state = bp_disabled; + + check_duplicates (bpt); + + if (modify_breakpoint_hook) + modify_breakpoint_hook (bpt); + breakpoint_modify_event (bpt->number); +} + +/* ARGSUSED */ +static void +disable_command (char *args, int from_tty) +{ + register struct breakpoint *bpt; + if (args == 0) + ALL_BREAKPOINTS (bpt) + switch (bpt->type) + { + case bp_none: + warning ("attempted to disable apparently deleted breakpoint #%d?", + bpt->number); + continue; + case bp_breakpoint: + case bp_catch_load: + case bp_catch_unload: + case bp_catch_fork: + case bp_catch_vfork: + case bp_catch_exec: + case bp_catch_catch: + case bp_catch_throw: + case bp_hardware_breakpoint: + case bp_watchpoint: + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_access_watchpoint: + disable_breakpoint (bpt); + default: + continue; + } + else + map_breakpoint_numbers (args, disable_breakpoint); +} + +static void +do_enable_breakpoint (struct breakpoint *bpt, enum bpdisp disposition) +{ + struct frame_info *save_selected_frame = NULL; + int save_selected_frame_level = -1; + int target_resources_ok, other_type_used; + struct value *mark; + + if (bpt->type == bp_hardware_breakpoint) + { + int i; + i = hw_breakpoint_used_count (); + target_resources_ok = + TARGET_CAN_USE_HARDWARE_WATCHPOINT (bp_hardware_breakpoint, + i + 1, 0); + if (target_resources_ok == 0) + error ("No hardware breakpoint support in the target."); + else if (target_resources_ok < 0) + error ("Hardware breakpoints used exceeds limit."); + } + + if (bpt->enable_state != bp_permanent) + bpt->enable_state = bp_enabled; + bpt->disposition = disposition; + check_duplicates (bpt); + breakpoints_changed (); + + if (bpt->type == bp_watchpoint || + bpt->type == bp_hardware_watchpoint || + bpt->type == bp_read_watchpoint || + bpt->type == bp_access_watchpoint) + { + if (bpt->exp_valid_block != NULL) + { + struct frame_info *fr = + fr = frame_find_by_id (bpt->watchpoint_frame); + if (fr == NULL) + { + printf_filtered ("\ +Cannot enable watchpoint %d because the block in which its expression\n\ +is valid is not currently in scope.\n", bpt->number); + bpt->enable_state = bp_disabled; + return; + } + + save_selected_frame = selected_frame; + save_selected_frame_level = frame_relative_level (selected_frame); + select_frame (fr); + } + + value_free (bpt->val); + mark = value_mark (); + bpt->val = evaluate_expression (bpt->exp); + release_value (bpt->val); + if (VALUE_LAZY (bpt->val)) + value_fetch_lazy (bpt->val); + + if (bpt->type == bp_hardware_watchpoint || + bpt->type == bp_read_watchpoint || + bpt->type == bp_access_watchpoint) + { + int i = hw_watchpoint_used_count (bpt->type, &other_type_used); + int mem_cnt = can_use_hardware_watchpoint (bpt->val); + + /* Hack around 'unused var' error for some targets here */ + (void) mem_cnt, i; + target_resources_ok = TARGET_CAN_USE_HARDWARE_WATCHPOINT ( + bpt->type, i + mem_cnt, other_type_used); + /* we can consider of type is bp_hardware_watchpoint, convert to + bp_watchpoint in the following condition */ + if (target_resources_ok < 0) + { + printf_filtered ("\ +Cannot enable watchpoint %d because target watch resources\n\ +have been allocated for other watchpoints.\n", bpt->number); + bpt->enable_state = bp_disabled; + value_free_to_mark (mark); + return; + } + } + + if (save_selected_frame_level >= 0) + select_frame (save_selected_frame); + value_free_to_mark (mark); + } + if (modify_breakpoint_hook) + modify_breakpoint_hook (bpt); + breakpoint_modify_event (bpt->number); +} + +void +enable_breakpoint (struct breakpoint *bpt) +{ + do_enable_breakpoint (bpt, bpt->disposition); +} + +/* The enable command enables the specified breakpoints (or all defined + breakpoints) so they once again become (or continue to be) effective + in stopping the inferior. */ + +/* ARGSUSED */ +static void +enable_command (char *args, int from_tty) +{ + register struct breakpoint *bpt; + if (args == 0) + ALL_BREAKPOINTS (bpt) + switch (bpt->type) + { + case bp_none: + warning ("attempted to enable apparently deleted breakpoint #%d?", + bpt->number); + continue; + case bp_breakpoint: + case bp_catch_load: + case bp_catch_unload: + case bp_catch_fork: + case bp_catch_vfork: + case bp_catch_exec: + case bp_catch_catch: + case bp_catch_throw: + case bp_hardware_breakpoint: + case bp_watchpoint: + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_access_watchpoint: + enable_breakpoint (bpt); + default: + continue; + } + else + map_breakpoint_numbers (args, enable_breakpoint); +} + +static void +enable_once_breakpoint (struct breakpoint *bpt) +{ + do_enable_breakpoint (bpt, disp_disable); +} + +/* ARGSUSED */ +static void +enable_once_command (char *args, int from_tty) +{ + map_breakpoint_numbers (args, enable_once_breakpoint); +} + +static void +enable_delete_breakpoint (struct breakpoint *bpt) +{ + do_enable_breakpoint (bpt, disp_del); +} + +/* ARGSUSED */ +static void +enable_delete_command (char *args, int from_tty) +{ + map_breakpoint_numbers (args, enable_delete_breakpoint); +} + +/* Use default_breakpoint_'s, or nothing if they aren't valid. */ + +struct symtabs_and_lines +decode_line_spec_1 (char *string, int funfirstline) +{ + struct symtabs_and_lines sals; + if (string == 0) + error ("Empty line specification."); + if (default_breakpoint_valid) + sals = decode_line_1 (&string, funfirstline, + default_breakpoint_symtab, + default_breakpoint_line, + (char ***) NULL); + else + sals = decode_line_1 (&string, funfirstline, + (struct symtab *) NULL, 0, (char ***) NULL); + if (*string) + error ("Junk at end of line specification: %s", string); + return sals; +} + +void +_initialize_breakpoint (void) +{ + struct cmd_list_element *c; + + breakpoint_chain = 0; + /* Don't bother to call set_breakpoint_count. $bpnum isn't useful + before a breakpoint is set. */ + breakpoint_count = 0; + + add_com ("ignore", class_breakpoint, ignore_command, + "Set ignore-count of breakpoint number N to COUNT.\n\ +Usage is `ignore N COUNT'."); + if (xdb_commands) + add_com_alias ("bc", "ignore", class_breakpoint, 1); + + add_com ("commands", class_breakpoint, commands_command, + "Set commands to be executed when a breakpoint is hit.\n\ +Give breakpoint number as argument after \"commands\".\n\ +With no argument, the targeted breakpoint is the last one set.\n\ +The commands themselves follow starting on the next line.\n\ +Type a line containing \"end\" to indicate the end of them.\n\ +Give \"silent\" as the first line to make the breakpoint silent;\n\ +then no output is printed when it is hit, except what the commands print."); + + add_com ("condition", class_breakpoint, condition_command, + "Specify breakpoint number N to break only if COND is true.\n\ +Usage is `condition N COND', where N is an integer and COND is an\n\ +expression to be evaluated whenever breakpoint N is reached."); + + c = add_com ("tbreak", class_breakpoint, tbreak_command, + "Set a temporary breakpoint. Args like \"break\" command.\n\ +Like \"break\" except the breakpoint is only temporary,\n\ +so it will be deleted when hit. Equivalent to \"break\" followed\n\ +by using \"enable delete\" on the breakpoint number."); + set_cmd_completer (c, location_completer); + + c = add_com ("hbreak", class_breakpoint, hbreak_command, + "Set a hardware assisted breakpoint. Args like \"break\" command.\n\ +Like \"break\" except the breakpoint requires hardware support,\n\ +some target hardware may not have this support."); + set_cmd_completer (c, location_completer); + + c = add_com ("thbreak", class_breakpoint, thbreak_command, + "Set a temporary hardware assisted breakpoint. Args like \"break\" command.\n\ +Like \"hbreak\" except the breakpoint is only temporary,\n\ +so it will be deleted when hit."); + set_cmd_completer (c, location_completer); + + add_prefix_cmd ("enable", class_breakpoint, enable_command, + "Enable some breakpoints.\n\ +Give breakpoint numbers (separated by spaces) as arguments.\n\ +With no subcommand, breakpoints are enabled until you command otherwise.\n\ +This is used to cancel the effect of the \"disable\" command.\n\ +With a subcommand you can enable temporarily.", + &enablelist, "enable ", 1, &cmdlist); + if (xdb_commands) + add_com ("ab", class_breakpoint, enable_command, + "Enable some breakpoints.\n\ +Give breakpoint numbers (separated by spaces) as arguments.\n\ +With no subcommand, breakpoints are enabled until you command otherwise.\n\ +This is used to cancel the effect of the \"disable\" command.\n\ +With a subcommand you can enable temporarily."); + + add_com_alias ("en", "enable", class_breakpoint, 1); + + add_abbrev_prefix_cmd ("breakpoints", class_breakpoint, enable_command, + "Enable some breakpoints.\n\ +Give breakpoint numbers (separated by spaces) as arguments.\n\ +This is used to cancel the effect of the \"disable\" command.\n\ +May be abbreviated to simply \"enable\".\n", + &enablebreaklist, "enable breakpoints ", 1, &enablelist); + + add_cmd ("once", no_class, enable_once_command, + "Enable breakpoints for one hit. Give breakpoint numbers.\n\ +If a breakpoint is hit while enabled in this fashion, it becomes disabled.", + &enablebreaklist); + + add_cmd ("delete", no_class, enable_delete_command, + "Enable breakpoints and delete when hit. Give breakpoint numbers.\n\ +If a breakpoint is hit while enabled in this fashion, it is deleted.", + &enablebreaklist); + + add_cmd ("delete", no_class, enable_delete_command, + "Enable breakpoints and delete when hit. Give breakpoint numbers.\n\ +If a breakpoint is hit while enabled in this fashion, it is deleted.", + &enablelist); + + add_cmd ("once", no_class, enable_once_command, + "Enable breakpoints for one hit. Give breakpoint numbers.\n\ +If a breakpoint is hit while enabled in this fashion, it becomes disabled.", + &enablelist); + + add_prefix_cmd ("disable", class_breakpoint, disable_command, + "Disable some breakpoints.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To disable all breakpoints, give no argument.\n\ +A disabled breakpoint is not forgotten, but has no effect until reenabled.", + &disablelist, "disable ", 1, &cmdlist); + add_com_alias ("dis", "disable", class_breakpoint, 1); + add_com_alias ("disa", "disable", class_breakpoint, 1); + if (xdb_commands) + add_com ("sb", class_breakpoint, disable_command, + "Disable some breakpoints.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To disable all breakpoints, give no argument.\n\ +A disabled breakpoint is not forgotten, but has no effect until reenabled."); + + add_cmd ("breakpoints", class_alias, disable_command, + "Disable some breakpoints.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To disable all breakpoints, give no argument.\n\ +A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\ +This command may be abbreviated \"disable\".", + &disablelist); + + add_prefix_cmd ("delete", class_breakpoint, delete_command, + "Delete some breakpoints or auto-display expressions.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To delete all breakpoints, give no argument.\n\ +\n\ +Also a prefix command for deletion of other GDB objects.\n\ +The \"unset\" command is also an alias for \"delete\".", + &deletelist, "delete ", 1, &cmdlist); + add_com_alias ("d", "delete", class_breakpoint, 1); + if (xdb_commands) + add_com ("db", class_breakpoint, delete_command, + "Delete some breakpoints.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To delete all breakpoints, give no argument.\n"); + + add_cmd ("breakpoints", class_alias, delete_command, + "Delete some breakpoints or auto-display expressions.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To delete all breakpoints, give no argument.\n\ +This command may be abbreviated \"delete\".", + &deletelist); + + add_com ("clear", class_breakpoint, clear_command, + concat ("Clear breakpoint at specified line or function.\n\ +Argument may be line number, function name, or \"*\" and an address.\n\ +If line number is specified, all breakpoints in that line are cleared.\n\ +If function is specified, breakpoints at beginning of function are cleared.\n\ +If an address is specified, breakpoints at that address are cleared.\n\n", + "With no argument, clears all breakpoints in the line that the selected frame\n\ +is executing in.\n\ +\n\ +See also the \"delete\" command which clears breakpoints by number.", NULL)); + + c = add_com ("break", class_breakpoint, break_command, + concat ("Set breakpoint at specified line or function.\n\ +Argument may be line number, function name, or \"*\" and an address.\n\ +If line number is specified, break at start of code for that line.\n\ +If function is specified, break at start of code for that function.\n\ +If an address is specified, break at that exact address.\n", + "With no arg, uses current execution address of selected stack frame.\n\ +This is useful for breaking on return to a stack frame.\n\ +\n\ +Multiple breakpoints at one place are permitted, and useful if conditional.\n\ +\n\ +Do \"help breakpoints\" for info on other commands dealing with breakpoints.", NULL)); + set_cmd_completer (c, location_completer); + + add_com_alias ("b", "break", class_run, 1); + add_com_alias ("br", "break", class_run, 1); + add_com_alias ("bre", "break", class_run, 1); + add_com_alias ("brea", "break", class_run, 1); + + if (xdb_commands) + { + add_com_alias ("ba", "break", class_breakpoint, 1); + add_com_alias ("bu", "ubreak", class_breakpoint, 1); + } + + if (dbx_commands) + { + add_abbrev_prefix_cmd ("stop", class_breakpoint, stop_command, + "Break in function/address or break at a line in the current file.", + &stoplist, "stop ", 1, &cmdlist); + add_cmd ("in", class_breakpoint, stopin_command, + "Break in function or address.\n", &stoplist); + add_cmd ("at", class_breakpoint, stopat_command, + "Break at a line in the current file.\n", &stoplist); + add_com ("status", class_info, breakpoints_info, + concat ("Status of user-settable breakpoints, or breakpoint number NUMBER.\n\ +The \"Type\" column indicates one of:\n\ +\tbreakpoint - normal breakpoint\n\ +\twatchpoint - watchpoint\n\ +The \"Disp\" column contains one of \"keep\", \"del\", or \"dis\" to indicate\n\ +the disposition of the breakpoint after it gets hit. \"dis\" means that the\n\ +breakpoint will be disabled. The \"Address\" and \"What\" columns indicate the\n\ +address and file/line number respectively.\n\n", + "Convenience variable \"$_\" and default examine address for \"x\"\n\ +are set to the address of the last breakpoint listed.\n\n\ +Convenience variable \"$bpnum\" contains the number of the last\n\ +breakpoint set.", NULL)); + } + + add_info ("breakpoints", breakpoints_info, + concat ("Status of user-settable breakpoints, or breakpoint number NUMBER.\n\ +The \"Type\" column indicates one of:\n\ +\tbreakpoint - normal breakpoint\n\ +\twatchpoint - watchpoint\n\ +The \"Disp\" column contains one of \"keep\", \"del\", or \"dis\" to indicate\n\ +the disposition of the breakpoint after it gets hit. \"dis\" means that the\n\ +breakpoint will be disabled. The \"Address\" and \"What\" columns indicate the\n\ +address and file/line number respectively.\n\n", + "Convenience variable \"$_\" and default examine address for \"x\"\n\ +are set to the address of the last breakpoint listed.\n\n\ +Convenience variable \"$bpnum\" contains the number of the last\n\ +breakpoint set.", NULL)); + + if (xdb_commands) + add_com ("lb", class_breakpoint, breakpoints_info, + concat ("Status of user-settable breakpoints, or breakpoint number NUMBER.\n\ +The \"Type\" column indicates one of:\n\ +\tbreakpoint - normal breakpoint\n\ +\twatchpoint - watchpoint\n\ +The \"Disp\" column contains one of \"keep\", \"del\", or \"dis\" to indicate\n\ +the disposition of the breakpoint after it gets hit. \"dis\" means that the\n\ +breakpoint will be disabled. The \"Address\" and \"What\" columns indicate the\n\ +address and file/line number respectively.\n\n", + "Convenience variable \"$_\" and default examine address for \"x\"\n\ +are set to the address of the last breakpoint listed.\n\n\ +Convenience variable \"$bpnum\" contains the number of the last\n\ +breakpoint set.", NULL)); + + add_cmd ("breakpoints", class_maintenance, maintenance_info_breakpoints, + concat ("Status of all breakpoints, or breakpoint number NUMBER.\n\ +The \"Type\" column indicates one of:\n\ +\tbreakpoint - normal breakpoint\n\ +\twatchpoint - watchpoint\n\ +\tlongjmp - internal breakpoint used to step through longjmp()\n\ +\tlongjmp resume - internal breakpoint at the target of longjmp()\n\ +\tuntil - internal breakpoint used by the \"until\" command\n\ +\tfinish - internal breakpoint used by the \"finish\" command\n", + "The \"Disp\" column contains one of \"keep\", \"del\", or \"dis\" to indicate\n\ +the disposition of the breakpoint after it gets hit. \"dis\" means that the\n\ +breakpoint will be disabled. The \"Address\" and \"What\" columns indicate the\n\ +address and file/line number respectively.\n\n", + "Convenience variable \"$_\" and default examine address for \"x\"\n\ +are set to the address of the last breakpoint listed.\n\n\ +Convenience variable \"$bpnum\" contains the number of the last\n\ +breakpoint set.", NULL), + &maintenanceinfolist); + + add_com ("catch", class_breakpoint, catch_command, + "Set catchpoints to catch events.\n\ +Raised signals may be caught:\n\ +\tcatch signal - all signals\n\ +\tcatch signal - a particular signal\n\ +Raised exceptions may be caught:\n\ +\tcatch throw - all exceptions, when thrown\n\ +\tcatch throw - a particular exception, when thrown\n\ +\tcatch catch - all exceptions, when caught\n\ +\tcatch catch - a particular exception, when caught\n\ +Thread or process events may be caught:\n\ +\tcatch thread_start - any threads, just after creation\n\ +\tcatch thread_exit - any threads, just before expiration\n\ +\tcatch thread_join - any threads, just after joins\n\ +Process events may be caught:\n\ +\tcatch start - any processes, just after creation\n\ +\tcatch exit - any processes, just before expiration\n\ +\tcatch fork - calls to fork()\n\ +\tcatch vfork - calls to vfork()\n\ +\tcatch exec - calls to exec()\n\ +Dynamically-linked library events may be caught:\n\ +\tcatch load - loads of any library\n\ +\tcatch load - loads of a particular library\n\ +\tcatch unload - unloads of any library\n\ +\tcatch unload - unloads of a particular library\n\ +The act of your program's execution stopping may also be caught:\n\ +\tcatch stop\n\n\ +C++ exceptions may be caught:\n\ +\tcatch throw - all exceptions, when thrown\n\ +\tcatch catch - all exceptions, when caught\n\ +\n\ +Do \"help set follow-fork-mode\" for info on debugging your program\n\ +after a fork or vfork is caught.\n\n\ +Do \"help breakpoints\" for info on other commands dealing with breakpoints."); + + add_com ("tcatch", class_breakpoint, tcatch_command, + "Set temporary catchpoints to catch events.\n\ +Args like \"catch\" command.\n\ +Like \"catch\" except the catchpoint is only temporary,\n\ +so it will be deleted when hit. Equivalent to \"catch\" followed\n\ +by using \"enable delete\" on the catchpoint number."); + + c = add_com ("watch", class_breakpoint, watch_command, + "Set a watchpoint for an expression.\n\ +A watchpoint stops execution of your program whenever the value of\n\ +an expression changes."); + set_cmd_completer (c, location_completer); + + c = add_com ("rwatch", class_breakpoint, rwatch_command, + "Set a read watchpoint for an expression.\n\ +A watchpoint stops execution of your program whenever the value of\n\ +an expression is read."); + set_cmd_completer (c, location_completer); + + c = add_com ("awatch", class_breakpoint, awatch_command, + "Set a watchpoint for an expression.\n\ +A watchpoint stops execution of your program whenever the value of\n\ +an expression is either read or written."); + set_cmd_completer (c, location_completer); + + add_info ("watchpoints", breakpoints_info, + "Synonym for ``info breakpoints''."); + + + c = add_set_cmd ("can-use-hw-watchpoints", class_support, var_zinteger, + (char *) &can_use_hw_watchpoints, + "Set debugger's willingness to use watchpoint hardware.\n\ +If zero, gdb will not use hardware for new watchpoints, even if\n\ +such is available. (However, any hardware watchpoints that were\n\ +created before setting this to nonzero, will continue to use watchpoint\n\ +hardware.)", + &setlist); + add_show_from_set (c, &showlist); + + can_use_hw_watchpoints = 1; +} diff --git a/gdb/mi/ChangeLog b/gdb/mi/ChangeLog new file mode 100644 index 0000000..51d9973 --- /dev/null +++ b/gdb/mi/ChangeLog @@ -0,0 +1,1836 @@ +2002-09-11 Keith Seitz + + * mi-main.c (mi_cmd_data_list_register_names): Use cleanups + for the uiout list. Do the cleanups when returning an error. + (mi_cmd_data_list_changed_registers): Ditto. + (mi_cmd_data_list_register_values): Use cleanups for the uiout list + and tuples. Do the cleanups when returning errors. + +2002-07-29 Andrew Cagney + + + * mi-cmd-var.c: Include "gdb_string.h". + * mi-cmd-disas.c: Ditto. + +2002-06-17 Keith Seitz + + * gdbmi.texinfo: Update command examples with real MI behavior. + +2002-05-20 Keith Seitz + + * mi-main.c (captured_mi_execute_command): Add uiout parameter. + "data" is now a structure which is used to pass data to/from this + function to mi_execute_command. + Modify function to comply with requirements from catch_exceptions. + Store real return result and command's return result in data. + (mi_execute_command): Use catch_exceptions. + Use enum to handle actions to be performed instead of overloading + catch_errors return result and the mi return result. + +2002-04-14 Andrew Cagney + + * mi-main.c (mi_cmd_exec_return): + +2002-04-09 Andrew Cagney + + * mi-main.c (register_changed_p): Use frame_register_read instead + of read_relative_register_raw_bytes. + (get_register): Delete out-of-date comment. + +2002-04-07 Elena Zannoni + + * mi-cmd-disas.c: Run through indent. + +2002-04-07 Elena Zannoni + + * mi-cmd-disas.c (dump_insns): New function. + (do_mixed_source_and_assembly): New function. + (do_assembly_only): New function. + (do_disassembly): New function. + (mi_cmd_disassemble): Rewrite using smaller, more modular + functions. + +2002-04-05 Jim Blandy + + * mi-cmd-stack.c (list_args_or_locals): Pass new arg to + get_frame_block. (See entry in gdb/ChangeLog.) + +2002-04-05 Elena Zannoni + + * mi-cmd-disas.c (mi_cmd_disassemble): Use TARGET_PRINT_INSN + instead of tm_print_insn. + Update copyright year. + +2002-04-04 Daniel Jacobowitz + + * mi-cmd-disas.c (mi_cmd_disassemble): Skip end-of-function + markers in the line table. + +2002-03-15 Andrew Cagney + + * mi-main.c (XMALLOC): Delete macro. + * mi-out.c (XMALLOC): Ditto. + * mi-parse.c (XMALLOC): Ditto. + * mi-console.c (XMALLOC): Ditto. + * mi-cmd-var.c (XMALLOC): Ditto. + * mi-cmd-break.c (XMALLOC): Ditto. + + * mi/mi-cmd-var.c, mi/mi-console.c, mi/mi-out.c: Update copyright + * mi/mi-parse.c: Ditto. + +2002-02-24 Andrew Cagney + + From wiz at danbala: + * gdbmi.texinfo: Fix grammar and typos. + Fix PR gdb/287. + +2002-02-03 Jim Blandy + + * mi-cmd-stack.c (list_args_or_locals): Move declaration of + print_me inside the loop body, so it gets re-initialized every + iteration. The cases for the different symbol kinds leave + print_me unchanged if they don't want the symbol printed. + +2002-01-22 Andrew Cagney + + * gdbmi.texinfo: Remove makeinfo 3.12 hacks. + +2002-01-21 Andrew Cagney + + * mi-cmd-stack.c: Remove #else clause of #ifdef UI_OUT. + * mi-cmd-break.c: Ditto. + * mi-main.c: Ditto. + +2001-12-30 Eli Zaretskii + + * gdbmi.texinfo: Fix the application of GFDL in the Copyright notice. + +2001-10-12 Daniel Jacobowitz + + * mi-cmd-stack.c (list_args_or_locals): Use ALL_BLOCK_SYMBOLS. + +2001-09-18 Andrew Cagney + + * mi-main.c (mi_cmd_thread_select): Pass uiout to + gdb_thread_select. + (mi_cmd_thread_list_ids): Pass uiout to gdb_list_thread_ids. + + * mi-cmd-break.c (breakpoint_notify): Pass uiout to + gdb_breakpoint_query. + +2001-08-17 Keith Seitz + + * mi-cmd-var.c (varobj_update_one): Update call to + varobj_update to reflect recent api change. + +2001-07-26 Andrew Cagney + + * mi-main.c: Include "gdb.h". + * mi-cmd-break.c: Include "gdb.h". + +2001-07-12 Andrew Cagney + + * mi-main.c (mi_execute_command): Flush output after ``(gdb)'' + prompt. Bug reported by David Whedon. + (mi_execute_async_cli_command): Ditto. + (mi_exec_async_cli_cmd_continuation): Ditto. + (mi_command_loop): Ditto. + +2001-07-10 Mark Kettenis + + * mi-out.c (mi_out_new): Initialize suppress_ouput field of newly + created `struct ui_out_data'. + +2001-07-09 Kevin Buettner + + * mi-main.c (register_changed_p, get_register): Use alloca() + to allocate space previously allocated via gcc's + variable-length array extension. + (mi_cmd_data_write_register_values, mi_cmd_data_write_memory): + Change type of ``buffer'' to ``void *''. Don't cast return value + from xmalloc(). Add a cleanup to free the xmalloc'd buffer. + +2001-07-07 Andrew Cagney + + * mi-main.c (mi_cmd_data_evaluate_expression): Replace value_ptr + with `struct value *'. + +2001-07-08 Kevin Buettner + + * mi-out.c (mi_table_header, mi_field_int, mi_field_skip) + (mi_field_string) Make function declarators match earlier + declarations. + +2001-07-04 Andrew Cagney + + * mi-out.c (mi_ui_out_impl): Initialize is_mi_like_p to one. + +2001-06-27 Andrew Cagney + + * mi-out.c (mi_table_begin): Include nr_cols and nr_rows in mi1 + table output. + * mi-out.c (mi_table_begin): Only suppress output when mi0. Change + the header to a list. + (mi_table_body): For mi1, close the header list and open a table + body list. + (mi_table_end): For mi1, close the body list. + (mi_table_header): For mi1, output a tuple containing all the + header information. + (mi_open, mi_close): Reverse logic of mi_version test. + * gdbmi.texinfo (GDB/MI Breakpoint Table Commands): Update. + +2001-06-26 Andrew Cagney + + * gdbmi.texinfo (GDB/MI Output Syntax): Delete reference to query + packet. + +2001-06-26 Andrew Cagney + + * mi-cmd-stack.c (list_args_or_locals): Output a list of "args" or + "locals" entries. + * gdbmi.texinfo (stack-list-locals, stack-list-arguments) + (exec-interrupt, target-select, thread-select): Update + documentation. + +2001-06-26 Andrew Cagney + + * mi-cmd-stack.c (mi_cmd_stack_list_frames): Output a list of + "stack" entries. + (mi_cmd_stack_list_args): Ditto for "stack-args". + * gdbmi.texinfo (stack-list-frames, stack-list-arguments): Update + documentation. + (GDB/MI Stack Manipulation Commands): Fix section title. Was + Stack Manipulation Commands in GDB/MI. + +2001-06-25 Andrew Cagney + + * gdbmi.texinfo: Update output examples that contain stop reason + output, change the args=.... to a list. + (exec-return): Ditto. + +2001-06-25 Andrew Cagney + + * mi-main.c (mi_cmd_data_read_memory): Output the memory contents + - memory and data - as a list. + * gdbmi.texinfo (data-read-memory): Update documentation. + +2001-06-25 Andrew Cagney + + * mi-main.c (mi_cmd_data_list_register_values): Output a list of + register values. + * gdbmi.texinfo (data-list-register-values): Update documentation. + +2001-06-25 Andrew Cagney + + * mi-main.c (mi_cmd_data_list_register_names): Output a list of + register names. + (mi_cmd_data_list_register_names): Include the pseudo registers. + (mi_cmd_data_list_register_names): Don't leave holes in the list, + output "" for NULL registers. + * gdbmi.texinfo (data-list-register-names): Update documentation. + +2001-06-23 Andrew Cagney + + * mi-main.c (mi_cmd_data_list_changed_registers): Output a list of + register numbers. + * gdbmi.texinfo (data-list-changed-registers): Update + documentation. + +2001-06-23 Andrew Cagney + + * gdbmi.texinfo (data-disassemble): Update documentation of + output. Produces a list of instructions and a list of source + lines. + +2001-06-22 Andrew Cagney + + * mi-cmd-disas.c (mi_cmd_disassemble): For "-data-disassemble", + output a list instead of a tupple. + +2001-06-21 Andrew Cagney + + * mi-out.c (struct ui_out_data): Replace field first_header with + suppress_output. + (mi_begin, mi_end): Check suppress_header. + (mi_field_int, mi_field_skip): Ditto. + (mi_field_string, mi_field_fmt): Ditto. + (mi_table_begin): When nr_rows is zero, set suppress_header else, + output the start of the header. + (mi_table_body): Clear suppress header. + +2001-06-21 Andrew Cagney + + * mi-out.c (mi_open): For lists, when mi_version > 0, use ``[''. + (mi_close): Ditto for ``]''. + +2001-06-20 Andrew Cagney + + * mi-out.c (mi_table_header): Add parameter ``col_name''. + +2001-06-18 Andrew Cagney + + * mi-out.c: Include "gdb_assert.h". + (mi_table_begin): Add parameter ``nr_rows''. + +2001-06-18 Andrew Cagney + + * mi-main.c: Use strncmp as the "mi" test. Allow "mi", "mi0" and + "mi1". + (mi_command_loop): Add parameter mi_version, pass to mi_out_new. + (mi1_command_loop, mi0_command_loop): New functions. + (_initialize_mi_main): Recognize "mi", "mi0" and "mi1". + * mi-out.c (mi_out_new): Add parameter mi_version. + (struct ui_out_data): Add field mi_version. + * mi-out.h (mi_out_new): Update. + +2001-06-07 Andrew Cagney + + * gdbmi.texinfo (GDB/MI Output Syntax): Add tuples and lists to + syntax. + (GDB/MI Draft Changes to Output Syntax): Delete section. + +Mon Jun 11 17:22:25 2001 Andrew Cagney + + * mi-out.c: Fix typo. s/supress/suppress/. + +2001-06-09 Andrew Cagney + + * mi-out.c (mi_table_end, mi_table_begin, mi_begin, mi_end): Move + supress_field_separator updates from here. + (mi_open, mi_close): To here. + (mi_open): Add parameter name. Output a field_separator. + (mi_table_begin): Update. + (mi_table_header): Update. + (mi_begin): Update. + +2001-06-09 Andrew Cagney + + * mi-out.c (mi_table_begin): Make char* parameters constant. + (mi_table_header): Ditto. + (mi_field_int): Ditto. + (mi_field_skip): Ditto. + (mi_field_string): Ditto. + (mi_field_fmt): Ditto. + (mi_text): Ditto. + (mi_message): Ditto. + +2001-05-12 Andrew Cagney + + * mi-out.c (mi_close, mi_open): Output ``[]'' when a list. + +Fri May 11 13:55:07 2001 Andrew Cagney + + * mi-cmd-var.c: Replace ui_out_list_begin, ui_out_list_end and + make_cleanup_ui_out_list_end with ui_out_tupple_begin, + ui_out_tupple_end and make_cleanup_ui_out_tupple_begin_end. + * mi-cmd-stack.c: Ditto. + * mi-cmd-disas.c: Ditto. + * mi-main.c: Ditto. + +2001-05-10 Andrew Cagney + + * mi-out.c (mi_open, mi_close): Replace list_open and list_close. + (mi_table_begin): Update. + (mi_table_header): Update. + (mi_begin): Update. + (mi_table_body): Update. + (mi_table_end): Update. + (mi_end): Update. + +Thu May 10 16:28:13 2001 Andrew Cagney + + * mi-main.c (mi_execute_async_cli_command): Always initialize + old_cleanups. + +2001-05-08 Andrew Cagney + + * mi-out.c (mi_begin, mi_end): Replace mi_list_begin and + mi_list_end. + (mi_ui_out_impl): Update. + +2001-03-28 Andrew Cagney + + * mi-main.c (mi_cmd_data_read_memory): Use xcalloc. + +2001-03-26 Eli Zaretskii + + * gdbmi.texinfo: Update copyright. Change Permissions to GFDL. + +2001-03-20 Andrew Cagney + + * mi-cmd-disas.c (mi_cmd_disassemble): Initialize ``file_string'' + and ``line_num''. Consolidate declaration of argument variables. + +2001-03-19 Andrew Cagney + + * mi-out.h: Remove #ifdef __STDC__. + +2001-03-08 Andrew Cagney + + * mi-main.c (mi_cmd_data_list_register_names): Use NUM_REGS, not + ARCH_NUM_REGS. + (mi_cmd_data_list_changed_registers): Ditto. + (mi_cmd_data_list_register_values): Ditto. + (mi_cmd_data_write_register_values): Ditto. + +2001-03-06 Kevin Buettner + + * gdbmi.texinfo, mi-cmd-disas.c, mi-cmd-stack.c, mi-cmd-var.c, + mi-cmds.c, mi-cmds.h, mi-console.c, mi-console.h, mi-getopt.c, + mi-getopt.h, mi-out.c, mi-out.h, mi-parse.c, mi-parse.h: + Update/correct copyright notices. + +Wed Feb 7 19:50:37 2001 Andrew Cagney + + * mi-getopt.c: Add __FILE__ and __LINE__ parameter to calls to + internal_error. + * mi-console.c: Ditto. + * mi-cmds.c: Ditto. + * mi-cmd-break.c: Ditto. + +2001-01-27 Fernando Nasser + + From Momchil Velikov + * mi-cmd-disas.c (gdb_dis_asm_read_memory): Add missing memory + attributes argument in the call to `xfer_memory'. + +2000-12-14 Kevin Buettner + + * mi-cmd-disas.c, mi-cmd-var.c, mi-console.c, mi-main.c, + mi-parse.c: Replace occurrences of free() with xfree(). + +Fri Nov 17 16:07:23 2000 Andrew Cagney + + * mi-main.c: Replace asprintf with xasprintf. + * mi-cmd-var.c (mi_cmd_var_create): Ditto. + +2000-10-16 Eli Zaretskii + + * gdbmi.texinfo (GDB/MI Variable Objects): Dimensions of + multitable changed to "@columnfractions .4 .6". Suggested by + Dmitry Sivachenko . + +2000-08-23 Eli Zaretskii + + * gdbmi.texinfo: Change flathead -> @sc{gdb/mi}. + Fix typos and markup mistakes (from Dmitry S. + Sivachenko ). + +2000-07-24 Eli Zaretskii + + * gdbmi.texinfo: Change GDB -> @value{GDBN}, and + (gdb) -> (@value{GDBP}). Fix a few typos and some markup. From + Dmitry S. Sivachenko . + +Tue May 16 14:13:41 2000 Andrew Cagney + + * mi-main.c (mi_cmd_execute): Use free_current_contents. + (free_and_reset): Delete. + +Mon May 15 16:17:56 2000 Andrew Cagney + + * mi-main.c (mi_cmd_data_assign, mi_cmd_data_evaluate_expression), + mi-cmd-var.c (mi_cmd_var_create, mi_cmd_var_delete): Delete + make_cleanup_func casts. Not needed. + +2000-05-07 Eli Zaretskii + + * gdbmi.texinfo: Lots of typos and grammar fixes from Brian + Youmans <3diff@flib.gnu.ai.mit.edu>. + +Wed Apr 26 18:35:19 2000 Andrew Cagney + + * gdbmi.texinfo (GDB/MI Output Syntax v2.0): Convert Draft 2.0 + Output Syntax into a new section. Cross reference. + (menu): Fix tipo. GDB/MI Compatibility with CLI. + +2000-04-23 Eli Zaretskii + + * gdbmi.texinfo: Lots of changes, to include this document as part + of the GDB manual. + +2000-03-13 James Ingham + + * mi-cmd-var.c (mi_cmd_var_create): Add special frame cookie "@" + to indicate an "USE_CURRENT_FRAME" variable. + (varobj_update_one): Add "in_scope" and "type_changed" to the + result. + +2000-03-06 Elena Zannoni + + * mi-cmds.h: Export mi_cmd_data_write_register_values. + + * mi-cmds.c (mi_cmds): Implement data-write-register-values with + mi_cmd_data_write_register_values. + + * mi-main.c (mi_cmd_data_write_register_values): New + function. Write a value into a register. + +2000-03-06 Elena Zannoni + + * gdbmi.texinfo: Update data-disassemble documentation. + +2000-03-01 Elena Zannoni + + * mi-cmd-disas.c (mi_cmd_disassemble): Use + ui_out_field_core_addr() instead of print_address_numeric(), to + maintain consistency throughout MI. + +Wed Feb 23 17:09:39 2000 Andrew Cagney + + * mi-cmd-break.c, mi-cmd-disas.c, mi-cmd-stack.c, mi-cmd-var.c, + mi-cmds.c, mi-cmds.h, mi-console.c, mi-console.h, mi-getopt.c, + mi-getopt.h, mi-main.c, mi-out.c, mi-out.h, mi-parse.c, + mi-parse.h: Update copyright information. + +Wed Feb 23 13:31:16 2000 Andrew Cagney + + * mi-cmd-disas.c (gdb_dis_asm_read_memory): Change LEN to unsigned + long. Match ../include/dis-asm.h change. + +Wed Feb 23 10:30:55 2000 Andrew Cagney + + * gdbmi.texinfo: Update copyright - FSF. Update version + information. + + mi-cmd-break.c, mi-cmd-disas.c, mi-cmd-stack.c, mi-cmd-var.c, + mi-cmds.h, mi-main.c, mi-parse.c, mi-parse.h: Re-format using GNU + indent. + +2000-02-21 Elena Zannoni + + * mi-main.c: Add include of gdbcore.h for write_memory() + prototype. + +2000-02-18 Elena Zannoni + + * mi-cmd-disas.c (mi_cmd_disassemble): Change syntax of + command. Now use options. + Instead of printing the symbolic address of instructions via + print_address_symbolic(), use build_address_symbolic() and format + properly for output. + (gdb_do_disassmble): Delete. + +2000-02-18 Elena Zannoni + + * mi-cmd-disas.c (mi_cmd_disassemble): + +2000-02-17 Elena Zannoni + + * mi-main.c (mi_cmd_data_write_memory): New function. Write a + value into target memory. + + * mi-cmds.h (mi_cmd_data_write_memory): Export. + + * mi-cmds.c (mi_cmds): Hook up data-write-memory to + mi_cmd_data_write_memory(). + +2000-02-17 Elena Zannoni + + * mi-main.c (mi_cmd_target_download): Correct error message to + report right function name. + (mi_cmd_target_select): Add doing exec cleanups at end. + (mi_cmd_data_read_memory): Correct typo. + (mi_cmd_execute): Do not simply free last_async_command, but reset + it to NULL as well. + (free_and_reset): New function, free the argument and set it to + NULL. + (mi_cmd_target_select_continuation): Delete prototype. + +Tue Feb 1 00:17:12 2000 Andrew Cagney + + * mi-cmd-disas.c, mi-cmds.h, mi-console.c, mi-console.h, + mi-main.c, mi-out.c, mi-out.h: Update to reflect rename of + gdb-file / GDB_FILE to ui-file / ``struct ui_file''. + +Mon Jan 31 18:33:28 2000 Andrew Cagney + + * mi-main.c (mi_command_loop): Delete reference to + fputs_unfiltered_hook. + +2000-01-27 Elena Zannoni + + * mi-cmds.c (mi_cmds): Update entries for + mi_cmd_data_list_register_names, + mi_cmd_data_list_changed_registers, + mi_cmd_data_list_register_values. + + * mi-cmds.h (mi_cmd_data_list_register_names, + mi_cmd_data_list_changed_registers, + mi_cmd_data_list_register_values): Update to mi_cmd_argv_ftype. + + * mi-main.c (mi_cmd_data_list_register_names, + mi_cmd_data_list_changed_registers, + mi_cmd_data_list_register_values): Update to use argc, argv + parameters. + +2000-01-27 Elena Zannoni + + * mi-main.c (mi_cmd_data_read_memory): Correct the computation of + next-row. + +2000-01-27 Fernando Nasser + + * mi-cmd-var.c (mi_cmd_var_create): Test for NULL type. + (mi_cmd_var_set_format, mi_cmd_var_show_format, + mi_cmd_var_info_num_children, mi_cmd_var_list_children, + mi_cmd_var_info_type, mi_cmd_var_info_expression, + mi_cmd_var_show_attributes, mi_cmd_var_evaluate_expression, + mi_cmd_var_assign, mi_cmd_var_update): Prevent possibility of memory + leak on error. + +2000-01-27 Fernando Nasser + + * mi-out.c (mi_field_string): Test for NULL string pointer. + +2000-01-17 Elena Zannoni + + * mi-cmd-stack.c (mi_cmd_stack_list_frames): Call + print_frmae_info() with the correct arguments. + + * mi-main.c (mi_cmd_exec_return): Call + show_and_print_stack_frame() with LOC_AND_ADDRESS, so it does the + right thing. Update Copyright. + +2000-01-13 Elena Zannoni + + * mi-main.c: Move disassemble commands from here. + + * mi-cmd-disas.c: To here. New file. + +2000-01-13 Elena Zannoni + + * mi-cmd-stack.c: Remove include of mi-out.h. + + * mi-main.c (mi_cmd_disassemble): Update function to use argc/argv + interface. + + * mi-cmds.h: Ditto. + + * mi-cmds.c: Ditto. + +2000-01-12 Elena Zannoni + + * gdbmi.texinfo: Update stack commands descriptions. + Add thread commands descriptions and examples. + + * mi-main.c (mi_cmd_thread_list_ids): Fix typo. + +2000-01-12 Elena Zannoni + + * mi-main.c (mi_cmd_thread_list_ids): New function, print a list + of currently known threads ids, and the total number of threads. + (mi_cmd_thread_select): New function. Switch current thread. + + * mi-cmds.c (mi_cmds): Implement thread-list-ids by + mi_cmd_thread_list_ids, and thread-select by mi_cmd_thread_select. + + * mi-cmds.h (mi_cmd_thread_select, mi_cmd_thread_list_ids): Export. + +2000-01-11 Elena Zannoni + + * mi-main.c: Move stack commands from here. + + * mi-cmd-stack.c: To here. New file. + +2000-01-07 Elena Zannoni + + * mi-main.c (list_args_or_locals): Add a new paramter, the frame + for which to display args or locals. Don't use selected_frame + anymore, use the new parameter instead. Return void instead of + mi_cmd_result, let callers do so. + (mi_cmd_stack_list_args): Change interface. Now accept low and + high frame numbers to display args for a range of frames. Without + these two, display args for the whole stack. + (mi_cmd_stack_list_locals): Adapt to new interface for + list_args_or_locals. + +2000-01-06 Elena Zannoni + + * mi-main.c (mi_cmd_stack_info_depth, mi_cmd_stack_list_args, + mi_cmd_stack_list_frames, mi_cmd_stack_list_locals, + mi_cmd_stack_select_frame): Change to use argv type of parameters. + + * mi-cmds.c (mi_cmds): Change stack-info-depth, + stack-list-arguments, stack-list-frames, stack-list-locals, + stack-select-frame to use argv parameters. + + * mi-cmds.h (mi_cmd_stack_info_depth, mi_cmd_stack_list_args, + mi_cmd_stack_list_frames, mi_cmd_stack_list_locals, + mi_cmd_stack_select_frame): Update definitions. + +Tue Jan 4 12:38:54 2000 Andrew Cagney + + * mi-main.c (mi_command_loop): Force the MI interface to use seven + bit strings. + * gdbmi.texinfo: Make it clear that a quoted C string is seven + bit. + +Thu Dec 30 14:15:22 1999 Andrew Cagney + + * mi-getopt.c (mi_getopt): Rewrite. Allow long options. + * mi-getopt.h (struct mi_opt): Declare. + (mi_getopt): Update. + + * mi-main.c (mi_cmd_data_read_memory), mi-cmd-break.c + (mi_cmd_break_insert, mi_cmd_break_watch): Update. + +Wed Dec 29 23:38:35 1999 Andrew Cagney + + * mi-cmd-break.c (mi_cmd_break_insert): Add support for -c + , -i and -p . + (breakpoint_notify): New function. + (mi_cmd_break_insert): Wrap GDB call with callback hooks so that + MI is notified when ever a breakpoint is created. + + * gdbmi.texinfo: Update. + +Fri Dec 24 11:23:22 1999 Andrew Cagney + + * mi-main.c (gdb_do_disassemble): Strip out more useless #ifdef + UI_OUTs. + +1999-12-23 Elena Zannoni + + * mi-main.c (gdb_do_disassemble): Fix output. Lines that have no + assembly instructions must still be outputted, to keep the source + line numbering correct. + Remove #ifdef UI_OUT's, they are useless. + +1999-12-17 Elena Zannoni + + * mi-main.c (gdb_do_disassemble): Don't print a new list in mixed + mode, every time. Just do it when we actually encounter a new + source line. + +1999-12-17 Fernando Nasser + + * mi-cmd-var.c (mi_cmd_var_list_children): Add test for C++ pseudo + variable objects (private, public, protected) as these do not have + a type and the -var-list-children operation was dumping core. + +Fri Dec 17 20:23:33 1999 Andrew Cagney + + * gdbmi.texinfo: Document recommended syntax for options. + + * mi-main.c (mi_cmd_data_read_memory): Add support for ``-o + ''. + * gdbmi.texinfo: Document. + +Wed Dec 15 17:43:08 1999 Andrew Cagney + + * mi-getopt.h (mi_getopt): Change optarg to a char pointer. Check + optind. + * mi-cmd-break.c (mi_cmd_break_insert): Update. + + * mi-main.c (mi_cmd_data_read_memory): Add fields "next-row-addr", + "prev-row-addr", "next-page-addr", "prev-page-addr" and a per row + "addr". + * gdbmi.texinfo: Update. + +Wed Dec 15 01:05:40 1999 Andrew Cagney + + * mi-cmds.h (mi_cmd_result): Add MI_CMD_CAUGHT_ERROR for when the + error is caught. + + * mi-main.c (captured_mi_execute_command): When + MI_CMD_CAUGHT_ERROR return 0 rethrowing the eror. + +1999-12-13 Elena Zannoni + + * mi-cmd-break.c (mi_cmd_break_insert): Remove unused var. + + * mi-cmd-var.c (mi_cmd_var_update): Remove unused variables. + +Mon Dec 13 18:43:36 1999 Andrew Cagney + + * mi-parse.c (mi_parse): Quote the command when printing it. + (mi_parse_argv): Fix handling of quoted strings. Was not + de-quoting them. + (mi_parse_argv): Make static. + +Mon Dec 13 18:30:03 1999 Andrew Cagney + + * mi-cmds.h (mi_cmd_break_insert, mi_cmd_break_watch): Change type + to mi_cmd_argv_ftype. + * mi-cmds.c (mi_cmds): Update. + * mi-cmd-break.c (mi_cmd_break_insert, mi_cmd_break_watch): Change + to new style of arguments with argc and argv. Parse arguments + using mi_getopt. + + * mi-cmd-break.c (mi_cmd_break_insert): Wrap body in #ifdef UI_OUT + to avoid non-ui compile problems. + +Mon Dec 13 15:08:36 1999 Andrew Cagney + + * mi-getopt.h, mi-getopt.c: New files. Similar to getopt but with + well defined semantics. + +Mon Dec 13 14:22:21 1999 Andrew Cagney + + * mi-main.c (mi_cmd_break_insert, mi_cmd_break_watch, enum + wp_type, enum bp_type): Move from here. + * mi-cmd-break.c: To here. New file. + (mi_cmd_break_insert, mi_cmd_break_insert, mi_cmd_break_watch): + Use error to report problems. + +1999-12-09 Elena Zannoni + + * gdbmi.texinfo: Update description of exec-interrupt. + + * mi-main.c (mi_cmd_exec_interrupt): If the program is not + executing, don't try to interrupt it, but error out instead. Make + sure previous_async_command is not null before duplicating it into + last_async_command. + + * gdbmi.texinfo: Add examples for data-evaluate-expression. + +1999-12-08 Elena Zannoni + + * mi-cmd-var.c (mi_cmd_var_assign, mi_cmd_var_create, + mi_cmd_var_delete, mi_cmd_var_evaluate_expression, + mi_cmd_var_info_expression, mi_cmd_var_info_num_children, + mi_cmd_var_info_type, mi_cmd_var_list_children, + mi_cmd_var_set_format, mi_cmd_var_show_attributes, + mi_cmd_var_show_format, mi_cmd_var_update): Change to use new + style of arguments with argc and argv. + (next_arg): Delete. + (which_var): Delete. + + * mi-cmds.c (mi_cmds): Update entries for mi_cmd_var_assign, + mi_cmd_var_create, mi_cmd_var_delete, + mi_cmd_var_evaluate_expression, mi_cmd_var_info_expression, + mi_cmd_var_info_num_children, mi_cmd_var_info_type, + mi_cmd_var_list_children, mi_cmd_var_set_format, + mi_cmd_var_show_attributes, mi_cmd_var_show_format, + mi_cmd_var_update. + + * mi-cmds.h (mi_cmd_var_assign, mi_cmd_var_create, + mi_cmd_var_delete, mi_cmd_var_evaluate_expression, + mi_cmd_var_info_expression, mi_cmd_var_info_num_children, + mi_cmd_var_info_type, mi_cmd_var_list_children, + mi_cmd_var_set_format, mi_cmd_var_show_attributes, + mi_cmd_var_show_format, mi_cmd_var_update): Update declarations. + +1999-12-08 Elena Zannoni + + * gdbmi.texinfo: Comment out -data-assign command. * mi-main.c + (mi_cmd_data_assign): Do not use, comment out. * mi-cmds.h + (mi_cmd_data_assign): Remove. * mi-cmds.c: Remove -data-assign + command from MI interface. + +1999-12-07 Elena Zannoni + + * mi-parse.c (mi_parse): Add '\n' at end of error messages, so + that prompt comes out on new line. + + * gdbmi.texinfo: Update disassembly command output. + +1999-12-06 Elena Zannoni + + * mi-main.c (gdb_do_disassemble): Update output for UI_OUT case. + +1999-12-02 Elena Zannoni + + * gdbmi.texinfo: Update exec-until output, including the reason + for stopping. + +Thu Dec 2 17:17:22 1999 Andrew Cagney + + * mi-cmds.c: Include for memset. + +1999-12-01 Elena Zannoni + + * mi-main.c (mi_cmd_exec_return): ifdef the references to + return_command_wrapper(). + +1999-12-01 Elena Zannoni + + * mi-main.c (mi_cmd_gdb_exit, mi_cmd_exec_interrupt, + mi_cmd_target_select, mi_execute_async_cli_command, + mi_exec_async_cli_cmd_continuation, mi_load_progress): Don't print + last_async_command if it is NULL. + (mi_cmd_exec_return): + +1999-12-01 Elena Zannoni + + * mi-main.c (mi_cmd_exec_return): Reimplement using + return_command() instead of mi_execute_async_cli_command(). + +1999-12-01 Elena Zannoni + + * mi-cmds.h: Export mi_cmd_data_assign and + mi_cmd_data_evaluate_expression. + + * mi-cmds.c (mi_cmds): Hook data-assign to mi_cmd_data_assign and + data-evaluate-expression to mi_cmd_data_evaluate_expression. + + * mi-main.c (mi_cmd_data_assign): New function. Implement + data-assign command. + (mi_cmd_data_evaluate_expression): New function. Implement + data-evaluate-expression command. + +1999-12-01 Elena Zannoni + + * gdbmi.texinfo: Fix some texinfo formatting errors. + +1999-12-01 Elena Zannoni + + * gdbmi.texinfo: Update data-list-register-values description. + + * mi-cmds.h: Export mi_cmd_data_list_register_values. + + * mi-cmds.c (mi_cmds): Hook data-list-register-values to + mi_cmd_data_list_register_values. + + * mi-main.c (mi_cmd_data_list_register_values): New + function. Implements the -data-list-register-values command. + (get_register): New function. Output the contents of a given + register. + +Wed Dec 1 20:27:22 1999 Andrew Cagney + + * mi-main.c (mi_execute_async_cli_command): Append missing "\n" + for synchronous stopped message. + +1999-11-30 James Ingham + + * gdbmi.texinfo: Fix obvious typo in @end statement. + +Wed Dec 1 12:36:27 1999 Andrew Cagney + + * mi-cmd-var.c: Include "value.h". + * mi-console.c: Include . + +Wed Dec 1 00:21:03 1999 Andrew Cagney + + * mi-main.c (captured_mi_execute_command): For a CLI command, pass + "%s" to mi_execute_cli_command to stop core dumps. + (captured_mi_execute_command): Echo CLI commands on gdb_stdlog. + +Wed Dec 1 00:10:07 1999 Andrew Cagney + + * gdbmi.texinfo: Explain NR-BYTES and ADDR. + +Tue Nov 30 23:31:57 1999 Andrew Cagney + + * mi-cmd-var.c (mi_cmd_var_create, mi_cmd_var_delete, + mi_cmd_var_set_format, mi_cmd_var_show_format, + mi_cmd_var_info_num_children, mi_cmd_var_list_children, + mi_cmd_var_info_type, mi_cmd_var_info_expression, + mi_cmd_var_show_attributes, mi_cmd_var_evaluate_expression, + mi_cmd_var_assign, mi_cmd_var_update, varobj_update_one, next_arg, + which_var): New file. Move varobj commands to here from + mi-main.c. + + * mi-console.h, mi-console.c (mi_console_file_new, + mi_console_file_delete, mi_console_file_fputs, + mi_console_raw_packet, mi_console_file_flush): New files. Move + mi_console_file to here from mi-main.c. + +Tue Nov 30 19:37:25 1999 Andrew Cagney + + * mi-main.c (captured_mi_execute_command): Use fputstr_unfiltered + when printing error messages. + (mi_cmd_execute): Ditto. + +1999-11-29 Elena Zannoni + + * gdbmi.texinfo: Describe -data-list-changed-registers, + -data-list-register-names. Add examples for + -exec-next-instruction, exec-step-instruction, -exec-run, + -exec-until. Format examples for -data-read-memory. + update example for -target-download. + +1999-11-29 Elena Zannoni + + * gdbmi.texinfo: Remove mentioning of inaccurate watchpoint hit + count. + +Mon Nov 29 19:28:55 1999 Andrew Cagney + + * mi-main.c (mi_execute_async_cli_command): Return ``enum + mi_cmd_cmd_result''. mi_cmd_exec_run, mi_cmd_exec_next, + mi_cmd_exec_step, mi_cmd_exec_step_instruction, + mi_cmd_exec_finish, mi_cmd_exec_until, mi_cmd_exec_return, + mi_cmd_exec_continue): Update call. + (mi_execute_async_cli_command): When target is synchronous, fake + asynchronous behavour (ulgh). Allows tests to be run on built-in + simulator and native targets. + +Mon Nov 29 15:15:16 1999 Andrew Cagney + + * mi-cmds.h (mi_cmd_gdb_exit), mi-cmds.c (mi_cmds), mi-main.c + (mi_cmd_gdb_exit): Change function signature to mi_cmd_argv_ftype. + +1999-11-28 Andew Cagney + + * mi-parse.c: Include and + +1999-11-26 Elena Zannoni + + * gdbmi.texinfo: Added watchpoint command descriptions and + examples. + + * mi-main.c (mi_load_progress): Add parameter for total sent so far. + Print it as well. + +Fri Nov 26 10:17:49 1999 Andrew Cagney + + * gdbmi.texinfo (section Output Syntax): For lists, the + part of a is optional. Clarify syntax. + (appendix Proposed v2.0 Output Syntax): New section. Provide + record of discussion of possible changes to syntax. + +Wed Nov 24 19:41:35 1999 Andrew Cagney + + * mi-main.c (mi_cmd_data_read_memory): Simplify. Fix coredump + when arguments were bad. + (mi_cmd_execute): Change parameter to ``struct mi_parse''. Handle + case of argv_func as well as args_func. + (captured_mi_execute_command): Update. + + * mi-cmds.c (struct mi_cmd): Add field for mi_cmd_argv_ftype. + (mi_cmds): Update mi_cmd_data_read_memory. + (mi_lookup): Return + + * mi-cmds.h (mi_cmd_args_ftype): Rename mi_cmd_ftype. Make all + functions of type this type. + (mi_cmd_argv_ftype): Declare. + (mi_cmd_data_read_memory): Change type to mi_cmd_argv_fytpe. + (struct mi_cmd): Move declaration to here from mi-cmds.c. + (mi_lookup): Return a pointer to ``struct mi_cmd''. + +Wed Nov 24 15:03:34 1999 Andrew Cagney + + * mi-parse.c (mi_parse): Initialize TOKEN when a CLI command. + + * gdbmi.texinfo: Allow a before a CLI command. + + * mi-parse.h (struct mi_parse): Declare. + (mi_parse): Change to return a ``struct mi_parse''. + (enum mi_command_type): Delete PARSE_ERROR. + + * mi-main.c (struct mi_execute_command_context): Delete. + (captured_mi_execute_command): Update + (mi_execute_command): Update. Check for mi_parse returning NULL. + +Wed Nov 24 12:57:14 1999 Andrew Cagney + + * mi-parse.h: Remove const, from cmd parameter. Causes cascading + warnings. + +Wed Nov 24 15:03:34 1999 Andrew Cagney + + * mi-parse.c (mi_parse): New function. Move parse code to here. + * mi-main.c (parse): From here. Delete. + +Wed Nov 24 12:57:14 1999 Andrew Cagney + + * mi-parse.c, mi-parse.h: New files. Implement mi_parse_env. + +Wed Nov 24 11:24:05 1999 Andrew Cagney + + * mi-out.c (mi_field_string): Make string parameter constant. + +1999-11-23 Elena Zannoni + + * mi-cmds.h (mi_cmd_target_download): Export. + + * mi-cmds.c (mi_cmds): Add mi_cmd_target_download. + + * mi-main.c: Include . + (mi_cmd_target_download): New function, implement the + target-download command. + (mi_load_progress): New function. Called via the + show_load_progress hook. Prints updates every 0.5 secs. + (mi_command_loop): Initialize the show_load_progress hook. + +1999-11-22 Elena Zannoni + + * mi-main.c (mi_cmd_exec_until): New function. Implement until + command. + (mi_cmd_exec_step_instruction): New function. Implement stepi + command. + (mi_cmd_exec_next_instruction): New function. Implement nexti + command. + + * mi-cmds.c (mi_cmds): Add mi_cmd_exec_step_instruction, + mi_cmd_exec_next_instruction, mi_cmd_exec_until. + + * mi-cmds.h (mi_cmd_exec_step_instruction, + mi_cmd_exec_next_instruction, mi_cmd_exec_until): Export. + +Tue Nov 23 00:30:37 1999 Andrew Cagney + + * mi/gdbmi.texinfo: Document -data-read-memory. + + * mi-main.c (mi_cmd_data_read_memory): Fix off-by-one check of + argc. + (mi_cmd_data_read_memory): Label the output table with "memory". + +Thu Nov 18 18:15:53 1999 Andrew Cagney + + * mi-main.c (mi_cmd_exec_interrupt, mi_cmd_break_insert, + mi_cmd_break_watch, mi_cmd_disassemble, mi_cmd_execute): Replace + strdup with xstrdup. + +Thu Nov 18 20:50:09 1999 Andrew Cagney + + * mi-main.c (mi_cmd_data_read_memory): New function. Implement + data-read-memory. + + * mi-cmds.h, mi-cmds.c: Add mi_cmd_data_read_memory. + * mi-cmds.c (mi_cmds): Ditto. + +1999-11-11 Elena Zannoni + + * mi-cmds.h (mi_cmd_break_watch): Export. + + * mi-cmds.c (mi_cmds): Hook up break-watch to function + mi_cmd_break_watch. + + * mi-main.c (wp_type): New enumeration for the possible types of + watchpoints. + (mi_cmd_break_watch): New function, implements the break-watch + command. + +1999-11-11 Elena Zannoni + + * mi-main.c (mi_cmd_break_insert): Handle case in which the command is + just a -break-insert w/o args. + +Fri Nov 12 00:01:52 1999 Andrew Cagney + + * mi-out.c (mi_field_string): Always quote the string. + +1999-11-10 Elena Zannoni + + * mi-cmds.h(mi_cmd_data_list_changed_registers, + mi_cmd_data_list_register_names): Export. + + * mi-cmds.c (mi_cmds): Hook up data-list-changed-registers to + mi_cmd_data_list_changed_registers and data-list-register-names to + mi_cmd_data_list_register_names. + + * mi-main.c (mi_cmd_data_list_changed_registers): New function, + implements the data-list-changed-registers command. + (mi_cmd_data_list_register_names): New function, implements the + data-list-register-names command. + (register_changed_p): New function. Decide whether the register + contents have changed. + (setup_architecture_data): New function. Initialize registers + memory. + (_initialize_mi_main): Call setup_architecture_data(), and + register_gdbarch_swap(). + +Wed Nov 10 18:35:08 1999 Andrew Cagney + + * mi-main.c (mi_execute_command): Correctly quote error messages. + +Wed Nov 10 11:05:14 1999 Andrew Cagney + + * mi/gdbmi.texinfo: Delete . Replaced by + . + + * mi-main.c (mi_console_raw_packet): Always quote console output. + +Tue Nov 9 17:53:05 1999 Andrew Cagney + + * mi-main.c (mi_console_file_new), mi-out.c (mi_out_new): Replace + the tui_file with a mem_file. Ya! + + * mi-out.c (do_write): New function, wrapper to gdb_file_write. + (mi_out_put): Pass do_write to gdb_file_put. + + * mi-main.c (mi_console_file_flush): Rewrite. Use + mi_console_raw_packet to send data to the console. + (mi_console_raw_packet): New function. Correctly + create quoted C string packets. + +1999-11-08 Elena Zannoni + + * mi-cmds.c (mi_cmds): Break-insert is now implemented by + mi_cmd_break_insert. + * mi-cmds.h (mi_cmd_break_insert): Export. + * mi-main.c (bp_type): New enumeration. + (mi_cmd_break_insert): New function. Implements all flavors of + breakpoint insertion. + +Mon Nov 8 17:49:17 1999 Andrew Cagney + + * mi-main.c (mi_console_file_flush): Replace gdb_file_get_strbuf + with tui_file_get_strbuf. + +Fri Nov 5 17:06:07 1999 Andrew Cagney + + * mi-main.c (mi_console_file_delete, mi_console_file_fputs, + mi_console_file_flush): Call internal_error instead of error. + +Thu Nov 4 19:53:32 1999 Andrew Cagney + + * mi-main.c (captured_mi_execute_command): New function. + (mi_execute_command): Rewrite. Replace SET_TOP_LEVEL() with call + to captured_mi_execute_command via catch_errors. + +Thu Nov 4 20:33:58 1999 Andrew Cagney + + * mi-main.c (clean): Delete. + (mi_command_loop): Delete extern declaration of + mi_execute_command. + +1999-10-28 Elena Zannoni + + * mi-main.c (mi_cmd_stack_select_frame): Conditionalize the body + on UI_OUT, because select_frame_command_wrapper is only defined if + UI_OUT is. + (mi_cmd_exec_interrupt): Conditionalize the body on UI_OUT, + because interrupt_target_command_wrapper is only defined if UI_OUT is. + + * mi-cmds.c (mi_cmds): Implement command exec-interrupt by + mi_cmd_exec_interrupt. + + * mi-main.c (mi_cmd_exec_interrupt): New function. Implements + exec-interrupt command. + (mi_cmd_execute): If the target is running save execution command + token in previous_async_command. If the command is not 'interrupt' + and the target is running, reject it. + (clean): New function. Free the arg and reset it to NULL. + + * mi-cmds.h (mi_cmd_exec_interrupt):Export. + +1999-10-28 Elena Zannoni + + * mi-cmds.c (mi_cmds): Implement command stack-select-frame by + mi_cmd_stack_select_frame. + + * mi-main.c (mi_cmd_stack_select_frame): New function. Implements + stack-select-frame command. + + * mi-cmds.h (mi_cmd_select_frame):Export. + +1999-10-26 Elena Zannoni + + * mi-cmds.c (mi_cmds): Implement commands stack-list-locals and + stack-list-arguments by mi_cmd_stack_list_locals and + mi_cmd_stack_list_args. + + * mi-main.c (mi_cmd_stack_list_locals): New function. Implements + stack-list-locals command. + (mi_cmd_stack_list_args): New function. Implements + stack-list-arguments command. + (list_args_or_locals): New function. Do all the work for the + listing of locals or arguments. + + * mi-cmds.h (mi_cmd_stack_list_args,mi_cmd_stack_list_locals) : + Export. + +1999-10-25 Elena Zannoni + + * mi-cmds.c (mi_cmds): Add new command stack-info-depth. + + * mi-main.c (mi_cmd_stack_info_depth): New function. Implements + the stack-info-depth command. + * mi-cmds.h (mi_cmd_stack_info_depth): Export. + + +1999-10-22 Elena Zannoni + + * mi-main.c (mi_execute_command): Handle MI_CMD_ERROR case + properly, for command that return error code and don't set + mi_error_message. + + * mi-cmds.c (mi_cmds): Hook stack-list-frames command to + mi_cmd_stack_list_frames function. + * mi-cmds.h (mi_cmd_stack_list_frames): Export. + + * mi-main.c (mi_execute_command): Deal with a return code of + MI_CMD_ERROR from the execution of mi commands. + (mi_error_message): Static string variable, to contain the error + message from mi commands. + (mi_cmd_stack_list_frames): New function. Prints a backtrace. + +1999-10-18 Elena Zannoni + + * mi-main.c (mi_cmd_disassemble): Handle the new command line + parameter that specifies the number of disassembly lines to be + displayed. + (gdb_do_disassemble): Add new parameter. Count the number of lines + that have been displayed, and stop when limit is reached. + +Wed Oct 13 18:04:13 1999 Andrew Cagney + + * mi-main.c (mi_command_loop): Don't initialize ``flush_hook''. + +1999-10-13 Elena Zannoni + + * mi/gdbmi.texinfo: More reformatting of the grammars. + +1999-10-12 Elena Zannoni + + * mi/gdbmi.texinfo: More TeX formatting. + +1999-10-11 Elena Zannoni + + * mi/gdbmi.texinfo: First pass completed. All commands should have + some comments/info. + Escape '@' output special char. + Reformat for TeX. + +1999-10-08 Elena Zannoni + + * mi/gdbmi.texinfo: Filled in part of file command section, and + stack section. + +1999-10-07 Elena Zannoni + + * mi/gdbmi.texinfo: Filled in some sections about execution + commands. + +Tue Oct 5 15:27:28 1999 Andrew Cagney + + * mi-cmds.h: Sort table + * mi-cmds.c: Ditto. + (MI_TABLE_SIZE): Increase to 251. + +1999-10-04 Fernando Nasser + + * mi-main.c (mi_cmd_var_create, mi_cmd_var_delete): Add missing + cleanups. + +1999-10-04 Fernando Nasser + + * mi-main.c (next_arg): Returns lenght as well. + (which_var, mi_cmd_var_create, mi_cmd_var_delete, + mi_cmd_var_set_format, mi_cmd_var_update): Do not modify the input + string, use allocated storage instead. + (mi_cmd_var_assign): Adjust call to next_arg() to include new + argument. + +1999-10-04 Fernando Nasser + + * mi-main.c (mi_execute_command): Fix handling of errors. + +1999-10-04 Fernando Nasser + + * mi-out.c (mi_out_new): Call tui_sfileopen() instead of + deprecated gdb_file_init_astream(). + * mi-main.c (mi_console_file_new): Ditto. + +Mon Oct 4 15:17:29 1999 Andrew Cagney + + * mi-cmds.h: Sort function declarations. + (mi_lookup): Add extern. + + * mi-cmds.c (mi_lookup): Delete dead code. + (build_table): Call internal_error instead of error. + (build_table): Send trace output to gdb_stdlog. + +1999-10-01 Elena Zannoni + + * mi-main.c (mi_execute_async_cli_command): Don't do the cleanups + if target_executing is null. + +1999-09-28 Elena Zannoni + + * mi-main.c (async_p): Change var name to event_loop_p. + +Mon Sep 27 15:11:00 1999 Andrew Cagney + + * mi-main.c (mi_execute_async_cli_command, mi_execute_command): + Replace target_has_async with function target_can_async_p. + +Sun Sep 26 00:12:52 1999 Andrew Cagney + + * mi-main.c (mi_cmd_target_select_continuation): Delete function. + (mi_cmd_target_select): Simplify. target-connect is guarenteed to + be synchronous. + +Sun Sep 26 00:12:52 1999 Andrew Cagney + + * mi-cmds.h (mi_cmd_ftype): Replace mi_impl_ftype. + (enum mi_cmd_result): Define. + * mi-cmds.c (struct mi_cmd): Update. + (mi_lookup): Update. + * mi-main.c (mi_cmd_execute): Update. + + * mi-main.c (mi_cmd_gdb_exit, mi_cmd_exec_run, mi_cmd_exec_next, + mi_cmd_exec_step, mi_cmd_target_select, mi_cmd_exec_continue, + mi_cmd_exec_return, mi_cmd_exec_finish, mi_cmd_disassemble, + mi_cmd_var_create, mi_cmd_var_delete, mi_cmd_var_set_format, + mi_cmd_var_show_format, mi_cmd_var_info_num_children, + mi_cmd_var_list_children, mi_cmd_var_info_type, + mi_cmd_var_info_expression, mi_cmd_var_show_attributes, + mi_cmd_var_evaluate_expression, mi_cmd_var_update): Update. + Return MI_CMD_DONE. + +1999-09-22 Fernando Nasser + + * mi-main.c (mi_cmd_var_create): Use paddr() to format address + on trace output. + +1999-09-21 Fernando Nasser + + * mi-main.c (mi_cmd_var_create): Test for varobjdebug before + printing trace and send it to gdb_stdlog. + +Mon Sep 20 13:41:04 1999 Andrew Cagney + + * Makefile.in (mi-out.o): Add dependency list. + * mi-out.c: Include "mi-out.h". + +1999-09-18 Elena Zannoni + + * mi-main.c (_initialize_mi_main): Events on stadin are now + handled by stdin_event_handler. + +1999-09-17 Fernando Nasser + + * mi-cmds.c (mi_cmds): Add var-* commands. + +1999-09-17 Fernando Nasser + + * mi-main.c (mi_cmd_var_create, mi_cmd_var_delete, + mi_cmd_var_set_format, mi_cmd_var_show_format, + mi_cmd_var_info_num_children, mi_cmd_var_list_children, + mi_cmd_var_info_type, mi_cmd_var_info_expression, + mi_cmd_var_show_attributes, mi_cmd_var_evaluate_expression, + mi_cmd_var_assign, mi_cmd_var_update, varobj_update_one, + which_var, next_arg): New functions. Implement the -var-* + commands. + * mi-cmds.h: Add prototypes for the above. + +1999-09-14 Fernando Nasser + + * mi-cmds.c (mi_cmds): Add detach command. + +1999-09-09 Fernando Nasser + + * mi-cmds.c (lookup_table): Fix typo. + +1999-09-09 Fernando Nasser + + * mi-cmds.c (mi_cmds): Fix typo and missing command. + +1999-09-09 Fernando Nasser + + * mi-main.c: Properly align function prototypes. + (mi_cmd_target_select): Proper check for NULL value. + +1999-09-09 Fernando Nasser + + * mi-main.c (mi_execute_async_cli_command): Fix for native targets + that do not have async yet. + +1999-09-01 Elena Zannoni + + * mi-main.c (mi_cmd_disassemble): Remove unused var. + (gdb_do_disassemble): Ditto. + +1999-08-30 Elena Zannoni + + * mi-main.c: Replace all the occurrences of 'asynch' in variable + or function names with 'async' to make it consistent with the rest + of gdb. + +Mon Aug 30 18:16:39 1999 Andrew Cagney + + * mi-main.c: #include for isspace(). + +1999-08-27 Elena Zannoni + + * mi-main.c (gdb_do_disassemble): This function returns void, not + int. + +1999-08-26 Elena Zannoni + + * mi-main.c (mi_cmd_disassemble): Don't use atoi() on the high + address string, just treat it same as address low. + (gdb_do_disassemble): Parse high_address string before seeing if + it is zero. + +1999-08-25 Elena Zannoni + + * mi-main.c (mi_cmd_disassemble): New function to produce + disassembly output for mi. + (gdb_dis_asm_read_memory): New function. Read the disassembly from + the executable file, instead of target memory. + (compare_lines): New function. Compare order of disassembly lines. + (gdb_do_disassemble): New function. Do the real job of getting the + assembly code out. + + * mi-cmds.c (mi_cmds): Do data-disassemble mi command via the + mi_cmd_disassemble function. + + * mi-cmds.h: Export new function mi_cmd_disassemble. + +Wed Aug 25 15:58:31 1999 Andrew Cagney + + * mi-main.c (mi_command_loop): Remove references to ui-hooks. + +1999-08-21 Elena Zannoni + + * mi-main.c (mi_execute_asynch_cli_command): Fix the incorrect + usage of strcat(): allocate enough space for the string. + +1999-08-13 Elena Zannoni + + From Christopher Faylor + * mi-main.c (mi_execute_command): Make sure we flush all the + output after each command. + +1999-08-10 Elena Zannoni + + * mi-main.c (_initialize_mi_main): Remove casting in call to + add_file_handler. + +Sun Aug 8 17:20:57 1999 Andrew Cagney + + * mi-main.c (mi_cmd_target_select, mi_execute_asynch_cli_command): + Replace call to fatal with call to internal_error. + +1999-07-26 Fernando Nasser + + * mi-main.c (mi_cmd_execute): Add return code. + (mi_execute_command): Make appropriate changes when calling the + function mentioned above. + (mi_cmd_gdb_exit, mi_cmd_target_select, + mi_cmd_target_select_continuation, mi_execute_command, + mi_exec_asynch_cli_cmd, mi_exec_asynch_cli_cmd_continuation): + Print token, prefix, class and output (if any) in one single group + of statements. + (mi_execute_command, mi_cmd_execute): Fix error prefix. + (mi_cmd_execute): Use exec cleanup for token. + * mi-out.c (mi_out_rewind): New function. + * mi-out.h: Prototype for the above. + +1999-07-16 Fernando Nasser + + * mi-main.c (mi_cmd_gdb_exit): Use buffer for exit message. + (mi_cmd_execute): Route error messages to correct file. + (mi_execute_asynch_cli_command): Insert line feed after running + message. + +1999-07-16 Fernando Nasser + + * mi-out.h (mi_out_buffered): Add extern declaration. + * mi-out.c (mi_out_buffered): New function. Insert a string at the + current buffer position. + * mi-main.c (mi_cmd_target_select, mi_execute_command, + mi_cmd_execute, mi_execute_asynch_cli_command): Use the above + function instead of printing to raw_stdout. + (mi_cmd_target_select, mi_cmd_target_select_continuation, + mi_execute_command, mi_cmd_execute, mi_execute_cli_command, + mi_exec_asynch_cli_cmd_continuation): Fix handling of token and + prefix. + (mi_execute_cli_command): Remove parameter no longer needed. + +1999-07-15 Elena Zannoni + + * mi-main.c (mi_cmd_target_select_continuation): Print the numeric + token when we are connected. + (mi_execute_command): Don't print the token now, do it later. + (mi_execute_cli_command): Add a new parameter for the numeric + token. Print the token, the prefix and the class after the + command has executed, not before. + (mi_execute_asynch_cli_command): Don't print an extra blank line. + +1999-07-15 Fernando Nasser + + * mi-main.c (mi_gdb_exit): Add \n at the end. + +1999-07-15 Fernando Nasser + + * mi-main.c (mi_cmd_execute): New function. Dispatch a mi operation. + (mi_execute_command): Use the above. + +1999-07-15 Fernando Nasser + + * mi-main.c: Fix identation. + +1999-07-15 Elena Zannoni + + * mi-main.c: Include target.h and inferior.h. + (mi_cmd_target_select): New function to execute the target-select + mi operation. + (mi_cmd_target_select_continuation): New function. Continuation + for the target-select operation. + (mi_execute_command): In case of an MI command which requires + asynchronous execution, do not try to display the result now. If + the execution has to look synchronous don't display the "(gdb)" + prompt. + (mi_execute_asynch_cli_command): Invoke real asynchronous + commands, set up exec_cleanups, and continuations. + (mi_exec_asynch_cli_cmd_continuation): New function. Continuation + for all the MI execution commands except 'target-select'. + (mi_execute_command): Handle null commands by exiting gdb, instead + of core dumping. + + * mi-cmds.c (mi_cmds): Hook up -target-select operation to new mi + function. + + * mi-cmds.h (mi_cmd_target_select): Add extern declaration. + +Thu Jul 15 10:31:39 1999 Andrew Cagney + + * mi-main.c (struct mi_console_file): Add field ``prefix''. + (mi_console_file_new): Add argument prefix. Initialize prefix + field. + (mi_console_file_flush): Use ``prefix'' instead of "~" as the + prefix string. + (mi_command_loop): Update stream output prefixes. gdb_stdout == + "~", gdb_stderr / gdb_stdlog == "&", gdb_stdtarg == "@". + +1999-07-13 Fernando Nasser + + * mi-main.c (ui_out_data): New field first_header. Fix output when + no breakpoints are found. + (mi_table_begin, mi_table_body, mi_table_header): Test for + first_header. + (mi_table_end): Test for supress_field_separator. + (mi_message): Remove messages from MI output. + +1999-06-30 Fernando Nasser + + * mi-cmds.c (mi_cmds[]): Delete gdb-cli operation. + * mi-main.c (parse): Remove ifdefs for cli commands parsing. + (mi-execute-command): Ditto. + +Mon Jun 28 13:06:52 1999 Andrew Cagney + + * mi-out.h: New file. + (mi_out_new, mi_out_put): Move mi specific delcarations to here. + * ui-out.h: From here. + + * mi-main.c: Include "mi-out.h". + +1999-06-25 Fernando Nasser + + * top.c (print_gdb_version): Add the word HEADLESS when output + follows headless format. + (print_command_lines): Fix typo. + +1999-06-25 Elena Zannoni + + * event-loop.h: Export input_fd. + * mi-main.c (mi_command_loop): Use the event loop if running + asynchronously. + (mi_execute_command_wrapper): New function. + (_initialize_mi-main): Set things up for running asynchronously. + +1999-06-18 Fernando Nasser + + * mi-cmds.c (mi_lookup): Deleted. + (lookup_table): New function. Replaces old mi_lookup() for local + use. + (mi_lookup): New function. External interface for command table + searchs. + (build_table): New definition. + (mi_cmds[]): Add several command implementations and the gdb-cli + special operation. + (mi_cmd_execute): Deleted. + * mi-cmds.h: Add type definition for command implementation + function pointers, add declaration for new implementation + functions and a declaration for mi_lookup(). + * mi-main.c (mi_execute_asynch_cli_command): New + function. Captures code that was repeated for all asynch + operations. + (mi_cmd_exec_*): Use the above new function. + (mi_gdb_cmd_exit): Fix the output, printing something appropriate. + (mi_cmd_exec_finish): New operation implementation function. + (mi_cmd_exec_return): Ditto. + (parse): Prepare to remove cli commands. + (mi_execute_command): Fix the output and change the way mi-cmds is + used. + +1999-06-18 Fernando Nasser + + * mi-out.c (mi_table_begin): Add missing field separator call. + +Thu Jun 17 21:05:40 1999 Fernando Nasser + + * breakpoint.c (breakpoint_1): Remove space in breakpoint table + id. + (mention): Use ui_out for last new line (forgotten). + +1999-06-16 Fernando Nasser + + * mi-main.c (mi_console_file_flush): Prevent prefix printing when + buffer empty; change prefix to '~'. + (mi_cmd_exec_*): Prefix normal output with '^' instead of + ','; remove unwanted new lines before "stopped". + +1999-06-16 Fernando Nasser + + * mi-cmds.c (struct mi_cmds): Updated entries for -exec-continue + and exec-next operations. + (mi_cmd_execute): New text for error messages. + * mi-cmds.h: Add declaration for mi_cmd_exec_next and + mi_cmd_exec_continue. + * mi-main.c (mi_cmd_exec_next): New function. Implements exec-next + operation. + (mi_cmd_exec_continue): New function. Implements exec-continue + operation. + (mi_execute_comand): Add missing space to prompt. + (mi_cmd_exec_run): Ditto. + (mi_cmd_exec_step): Ditto. + * mi-out.c (mi_out_new): Add flags argument to ui_out_new call. + (ui_list_end): Reset supress_field_separator flag. + +Sat Jun 12 11:49:10 1999 Andrew Cagney + + * mi-cmds.h. mi-cmds.c (exec step): Command implemented by + mi_cmd_exec_step instead of cli call. + * mi-main.c (mi_cmd_exec_step): New function. + + * mi-cmds.h. mi-cmds.c (exec run): Command implemented by + mi_cmd_exec_run instead of cli call. + * mi-main.c (mi_cmd_exec_run): New function. + + * mi-cmds.h. mi-cmds.c (gdb exit): Command implemented by + mi_cmd_gdb_exit instead of quit_force. + * mi-main.c (mi_cmd_gdb_exit): New function. + +Sat Jun 12 11:33:23 1999 Andrew Cagney + + * mi-main.c (mi_command_loop): Pass mi_input to + simplified_command_loop. + (mi_input): New function. Calls gdb_readline with no prompt. + +Sat Jun 12 11:19:02 1999 Andrew Cagney + + * mi-main.c (mi_console_file_fputs): Re-implement. Use a buffer + to accumulate output. + + * mi-main.c (struct mi_console_file): Add a buffer. + (mi_console_file_new): Create a buffer. + (mi_console_file_flush): New function. + +Sat Jun 12 10:59:39 1999 Andrew Cagney + + * mi-cmds.h (raw_stdout): Declare. Will be moved later. + * mi-cmds.c (mi_cmd_execute): Send error messages to RAW stdout. + (mi_cmds): Sort by class. + + * mi-main.c (raw_stdout): Make global. + * mi-main.c: Remove #ifdef UI_OUT. File assumes UI_OUT is + present. + * mi-main.c: Include "gdb_string.h". + (mi_out_put): Delete declaration. + +1999-06-11 Fernando Nasser + + * mi-main.c: Add pre-processor test for UI_OUT. + (mi_execute_command): Add pre-processor test for UI_OUT. + +Fri Jun 11 23:11:41 1999 Andrew Cagney + + * mi-main.c (raw_stdout): New variable. + (mi_execute_command): Write mi-out direct to raw_stdout. + (mi_command_loop): Create raw_stdout. Attach gdb_stdout to the + console. + (mi_console_file_fputs, mi_console_file_delete, + mi_console_file_new): New functions. + (struct mi_console_file): Declare. + +Fri Jun 11 18:34:33 1999 Andrew Cagney + + * mi-main.c (mi_execute_command): Call mi_out_put to display the + result. + * mi-out.c (mi_out_put): New function. + * ui-out.h (mi_out_put): Add declare. Will move later. + * Makefile.in (mi-cmds.o, mi-main.o): Add dependency on ui-out.h. + + * mi-out.c (mi_field_string, mi_field_fmt, mi_message, mi_flush, + out_field_fmt, list_open, list_close): Replace gdb_stdout with + data->buffer. + (field_separator, list_open, list_close): Add uiout parameter. + (mi_table_begin, mi_table_body, mi_table_end, mi_list_begin, + mi_list_end, mi_field_string, mi_field_fmt, out_field_fmt, + out_field_fmt): Update. + + * mi-out.c (mi_out_new): Initialize supress_field_separator. + (supress_field_separator): Move into mi-out local data object. + (mi_table_begin, mi_list_begin, field_separator): Update. + +Fri Jun 11 16:08:37 1999 Andrew Cagney + + * mi-out.c (mi_out_new): New function, replace init_mi_out. + * mi-main.c (mi_command_loop): Call mi_out_new(). + + * ui-out.h (mi_out_new): Add declaration. Will move later. + (mi_ui_out_impl): Delete. + +Wed Jun 9 16:42:16 1999 Andrew Cagney + + * mi-main.c: Include "ui-hooks.h". + (mi_init_ui, mi_command_loop): New functions. + (_initialize_mi_main): Install ``mi'' as the interpreter when + selected. + +Mon Jun 7 18:43:43 1999 Andrew Cagney + + From Fernando Nasser + * mi-cmds.c (build_table): Clean up error message. + * mi-cmds.c (mi_cmd_execute), mi-main.c (mi_execute_command): Only + print debug information when mi_debug_p. + * mi-cmds.h (mi_debug_p), mi-main.c: Global, control debug messages. + +Thu Jun 3 00:44:52 1999 Andrew Cagney + + From Fernando Nasser : + * mi-cmds.c: Add CLI definitions for "exec-arguments", + "exec-next", "gdb-exit", "break-list", "break-info", "exec-step" + and "stack-list-frames" to mi_cmds. + (struct mi_command): Add ``from_tty'' argument to func. + * mi-cmds.h (quit_force): Declare. + +1999-05-31 Fernando Nasser + + * mi-out.c (mi_table_end): Remove unwanted "\n". + +Thu May 27 14:59:06 1999 Andrew Cagney + + * top.c: Include "ui-hooks.h". + (call_interp_loop): Tempoary. Pass mi_execute_command to + simplified_command_loop. Initialize gdb_stdout & gdb_stderr to + stdio gdb_file streams. Force all hooks to null. + + * mi-cmds.h, mi-main.c, mi-cmds.c: New files. + * Makefile.in (SFILES): Add mi-main.c, mi-cmds.c + (COMMON_OBS): Add mi-main.o, mi-cmds.o. + (mi_cmds_h): Define. + +Wed May 26 12:39:49 1999 Andrew Cagney + + * top.c (call_interp_loop): Hack. Add extern declaration for + mi_ui_out_impl. + +1999-05-25 Fernando Nasser + + * mi-out.c: New table syntax. + +Mon May 24 16:16:29 1999 Andrew Cagney + + mi-out.c (_initialize_mi_out): Add external declaration. + +1999-05-21 Fernando Nasser + + * mi-out.c (mi_table_begin): Added missing parameter. + +1999-05-21 Fernando Nasser + + * mi-out.c: Changed table markers and added table id. + +1999-05-21 Fernando Nasser + + * mi-out.c: New file. Implements low-level ui-out primitives for + CLI-based interaction. + + +Local Variables: +mode: change-log +left-margin: 8 +fill-column: 74 +version-control: never +End: diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c new file mode 100644 index 0000000..b4bae47 --- /dev/null +++ b/gdb/mi/mi-cmd-stack.c @@ -0,0 +1,309 @@ +/* MI Command Set - stack commands. + Copyright 2000, 2002 Free Software Foundation, Inc. + Contributed by Cygnus Solutions (a Red Hat company). + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "target.h" +#include "frame.h" +#include "value.h" +#include "mi-cmds.h" +#include "ui-out.h" +#include "symtab.h" + +/* FIXME: these should go in some .h file but stack.c doesn't have a + corresponding .h file. These wrappers will be obsolete anyway, once + we pull the plug on the sanitization. */ +extern void select_frame_command_wrapper (char *, int); + +static void list_args_or_locals (int locals, int values, struct frame_info *fi); + +/* Print a list of the stack frames. Args can be none, in which case + we want to print the whole backtrace, or a pair of numbers + specifying the frame numbers at which to start and stop the + display. If the two numbers are equal, a single frame will be + displayed. */ +enum mi_cmd_result +mi_cmd_stack_list_frames (char *command, char **argv, int argc) +{ + int frame_low; + int frame_high; + int i; + struct frame_info *fi; + + if (!target_has_stack) + error ("mi_cmd_stack_list_frames: No stack."); + + if (argc > 2 || argc == 1) + error ("mi_cmd_stack_list_frames: Usage: [FRAME_LOW FRAME_HIGH]"); + + if (argc == 2) + { + frame_low = atoi (argv[0]); + frame_high = atoi (argv[1]); + } + else + { + /* Called with no arguments, it means we want the whole + backtrace. */ + frame_low = -1; + frame_high = -1; + } + + /* Let's position fi on the frame at which to start the + display. Could be the innermost frame if the whole stack needs + displaying, or if frame_low is 0. */ + for (i = 0, fi = get_current_frame (); + fi && i < frame_low; + i++, fi = get_prev_frame (fi)); + + if (fi == NULL) + error ("mi_cmd_stack_list_frames: Not enough frames in stack."); + + ui_out_list_begin (uiout, "stack"); + + /* Now let;s print the frames up to frame_high, or until there are + frames in the stack. */ + for (; + fi && (i <= frame_high || frame_high == -1); + i++, fi = get_prev_frame (fi)) + { + QUIT; + /* level == i: always print the level 'i' + source == LOC_AND_ADDRESS: print the location and the address + always, even for level 0. + args == 0: don't print the arguments. */ + print_frame_info (fi /* frame info */ , + i /* level */ , + LOC_AND_ADDRESS /* source */ , + 0 /* args */ ); + } + + ui_out_list_end (uiout); + if (i < frame_high) + error ("mi_cmd_stack_list_frames: Not enough frames in stack."); + + return MI_CMD_DONE; +} + +enum mi_cmd_result +mi_cmd_stack_info_depth (char *command, char **argv, int argc) +{ + int frame_high; + int i; + struct frame_info *fi; + + if (!target_has_stack) + error ("mi_cmd_stack_info_depth: No stack."); + + if (argc > 1) + error ("mi_cmd_stack_info_depth: Usage: [MAX_DEPTH]"); + + if (argc == 1) + frame_high = atoi (argv[0]); + else + /* Called with no arguments, it means we want the real depth of + the stack. */ + frame_high = -1; + + for (i = 0, fi = get_current_frame (); + fi && (i < frame_high || frame_high == -1); + i++, fi = get_prev_frame (fi)) + QUIT; + + ui_out_field_int (uiout, "depth", i); + + return MI_CMD_DONE; +} + +/* Print a list of the locals for the current frame. With argument of + 0, print only the names, with argument of 1 print also the + values. */ +enum mi_cmd_result +mi_cmd_stack_list_locals (char *command, char **argv, int argc) +{ + if (argc != 1) + error ("mi_cmd_stack_list_locals: Usage: PRINT_VALUES"); + + list_args_or_locals (1, atoi (argv[0]), selected_frame); + return MI_CMD_DONE; +} + +/* Print a list of the arguments for the current frame. With argument + of 0, print only the names, with argument of 1 print also the + values. */ +enum mi_cmd_result +mi_cmd_stack_list_args (char *command, char **argv, int argc) +{ + int frame_low; + int frame_high; + int i; + struct frame_info *fi; + + if (argc < 1 || argc > 3 || argc == 2) + error ("mi_cmd_stack_list_args: Usage: PRINT_VALUES [FRAME_LOW FRAME_HIGH]"); + + if (argc == 3) + { + frame_low = atoi (argv[1]); + frame_high = atoi (argv[2]); + } + else + { + /* Called with no arguments, it means we want args for the whole + backtrace. */ + frame_low = -1; + frame_high = -1; + } + + /* Let's position fi on the frame at which to start the + display. Could be the innermost frame if the whole stack needs + displaying, or if frame_low is 0. */ + for (i = 0, fi = get_current_frame (); + fi && i < frame_low; + i++, fi = get_prev_frame (fi)); + + if (fi == NULL) + error ("mi_cmd_stack_list_args: Not enough frames in stack."); + + ui_out_list_begin (uiout, "stack-args"); + + /* Now let's print the frames up to frame_high, or until there are + frames in the stack. */ + for (; + fi && (i <= frame_high || frame_high == -1); + i++, fi = get_prev_frame (fi)) + { + QUIT; + ui_out_tuple_begin (uiout, "frame"); + ui_out_field_int (uiout, "level", i); + list_args_or_locals (0, atoi (argv[0]), fi); + ui_out_tuple_end (uiout); + } + + ui_out_list_end (uiout); + if (i < frame_high) + error ("mi_cmd_stack_list_args: Not enough frames in stack."); + + return MI_CMD_DONE; +} + +/* Print a list of the locals or the arguments for the currently + selected frame. If the argument passed is 0, printonly the names + of the variables, if an argument of 1 is passed, print the values + as well. */ +static void +list_args_or_locals (int locals, int values, struct frame_info *fi) +{ + struct block *block; + struct symbol *sym; + int i, nsyms; + static struct ui_stream *stb = NULL; + + stb = ui_out_stream_new (uiout); + + block = get_frame_block (fi, 0); + + ui_out_list_begin (uiout, locals ? "locals" : "args"); + + while (block != 0) + { + ALL_BLOCK_SYMBOLS (block, i, sym) + { + int print_me = 0; + + switch (SYMBOL_CLASS (sym)) + { + default: + case LOC_UNDEF: /* catches errors */ + case LOC_CONST: /* constant */ + case LOC_TYPEDEF: /* local typedef */ + case LOC_LABEL: /* local label */ + case LOC_BLOCK: /* local function */ + case LOC_CONST_BYTES: /* loc. byte seq. */ + case LOC_UNRESOLVED: /* unresolved static */ + case LOC_OPTIMIZED_OUT: /* optimized out */ + print_me = 0; + break; + + case LOC_ARG: /* argument */ + case LOC_REF_ARG: /* reference arg */ + case LOC_REGPARM: /* register arg */ + case LOC_REGPARM_ADDR: /* indirect register arg */ + case LOC_LOCAL_ARG: /* stack arg */ + case LOC_BASEREG_ARG: /* basereg arg */ + if (!locals) + print_me = 1; + break; + + case LOC_LOCAL: /* stack local */ + case LOC_BASEREG: /* basereg local */ + case LOC_STATIC: /* static */ + case LOC_REGISTER: /* register */ + if (locals) + print_me = 1; + break; + } + if (print_me) + { + if (values) + ui_out_tuple_begin (uiout, NULL); + ui_out_field_string (uiout, "name", SYMBOL_NAME (sym)); + + if (values) + { + struct symbol *sym2; + if (!locals) + sym2 = lookup_symbol (SYMBOL_NAME (sym), + block, VAR_NAMESPACE, + (int *) NULL, + (struct symtab **) NULL); + else + sym2 = sym; + print_variable_value (sym2, fi, stb->stream); + ui_out_field_stream (uiout, "value", stb); + ui_out_tuple_end (uiout); + } + } + } + if (BLOCK_FUNCTION (block)) + break; + else + block = BLOCK_SUPERBLOCK (block); + } + ui_out_list_end (uiout); + ui_out_stream_delete (stb); +} + +enum mi_cmd_result +mi_cmd_stack_select_frame (char *command, char **argv, int argc) +{ + if (!target_has_stack) + error ("mi_cmd_stack_select_frame: No stack."); + + if (argc > 1) + error ("mi_cmd_stack_select_frame: Usage: [FRAME_SPEC]"); + + /* with no args, don't change frame */ + if (argc == 0) + select_frame_command_wrapper (0, 1 /* not used */ ); + else + select_frame_command_wrapper (argv[0], 1 /* not used */ ); + return MI_CMD_DONE; +} diff --git a/gdb/objfiles.c b/gdb/objfiles.c new file mode 100644 index 0000000..9c5e49f --- /dev/null +++ b/gdb/objfiles.c @@ -0,0 +1,1011 @@ +/* GDB routines for manipulating objfiles. + + Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002 Free Software Foundation, Inc. + + Contributed by Cygnus Support, using pieces from other GDB modules. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This file contains support routines for creating, manipulating, and + destroying objfile structures. */ + +#include "defs.h" +#include "bfd.h" /* Binary File Description */ +#include "symtab.h" +#include "symfile.h" +#include "objfiles.h" +#include "gdb-stabs.h" +#include "target.h" +#include "bcache.h" + +#include +#include "gdb_stat.h" +#include +#include "gdb_obstack.h" +#include "gdb_string.h" + +#include "breakpoint.h" + +/* Prototypes for local functions */ + +#if defined(USE_MMALLOC) && defined(HAVE_MMAP) + +#include "mmalloc.h" + +static int open_existing_mapped_file (char *, long, int); + +static int open_mapped_file (char *filename, long mtime, int flags); + +static PTR map_to_file (int); + +#endif /* defined(USE_MMALLOC) && defined(HAVE_MMAP) */ + +static void add_to_objfile_sections (bfd *, sec_ptr, PTR); + +/* Externally visible variables that are owned by this module. + See declarations in objfile.h for more info. */ + +struct objfile *object_files; /* Linked list of all objfiles */ +struct objfile *current_objfile; /* For symbol file being read in */ +struct objfile *symfile_objfile; /* Main symbol table loaded from */ +struct objfile *rt_common_objfile; /* For runtime common symbols */ + +int mapped_symbol_files; /* Try to use mapped symbol files */ + +/* Locate all mappable sections of a BFD file. + objfile_p_char is a char * to get it through + bfd_map_over_sections; we cast it back to its proper type. */ + +#ifndef TARGET_KEEP_SECTION +#define TARGET_KEEP_SECTION(ASECT) 0 +#endif + +/* Called via bfd_map_over_sections to build up the section table that + the objfile references. The objfile contains pointers to the start + of the table (objfile->sections) and to the first location after + the end of the table (objfile->sections_end). */ + +static void +add_to_objfile_sections (bfd *abfd, sec_ptr asect, PTR objfile_p_char) +{ + struct objfile *objfile = (struct objfile *) objfile_p_char; + struct obj_section section; + flagword aflag; + + aflag = bfd_get_section_flags (abfd, asect); + + if (!(aflag & SEC_ALLOC) && !(TARGET_KEEP_SECTION (asect))) + return; + + if (0 == bfd_section_size (abfd, asect)) + return; + section.offset = 0; + section.objfile = objfile; + section.the_bfd_section = asect; + section.ovly_mapped = 0; + section.addr = bfd_section_vma (abfd, asect); + section.endaddr = section.addr + bfd_section_size (abfd, asect); + obstack_grow (&objfile->psymbol_obstack, (char *) §ion, sizeof (section)); + objfile->sections_end = (struct obj_section *) (((unsigned long) objfile->sections_end) + 1); +} + +/* Builds a section table for OBJFILE. + Returns 0 if OK, 1 on error (in which case bfd_error contains the + error). + + Note that while we are building the table, which goes into the + psymbol obstack, we hijack the sections_end pointer to instead hold + a count of the number of sections. When bfd_map_over_sections + returns, this count is used to compute the pointer to the end of + the sections table, which then overwrites the count. + + Also note that the OFFSET and OVLY_MAPPED in each table entry + are initialized to zero. + + Also note that if anything else writes to the psymbol obstack while + we are building the table, we're pretty much hosed. */ + +int +build_objfile_section_table (struct objfile *objfile) +{ + /* objfile->sections can be already set when reading a mapped symbol + file. I believe that we do need to rebuild the section table in + this case (we rebuild other things derived from the bfd), but we + can't free the old one (it's in the psymbol_obstack). So we just + waste some memory. */ + + objfile->sections_end = 0; + bfd_map_over_sections (objfile->obfd, add_to_objfile_sections, (char *) objfile); + objfile->sections = (struct obj_section *) + obstack_finish (&objfile->psymbol_obstack); + objfile->sections_end = objfile->sections + (unsigned long) objfile->sections_end; + return (0); +} + +/* Given a pointer to an initialized bfd (ABFD) and some flag bits + allocate a new objfile struct, fill it in as best we can, link it + into the list of all known objfiles, and return a pointer to the + new objfile struct. + + The FLAGS word contains various bits (OBJF_*) that can be taken as + requests for specific operations, like trying to open a mapped + version of the objfile (OBJF_MAPPED). Other bits like + OBJF_SHARED are simply copied through to the new objfile flags + member. */ + +struct objfile * +allocate_objfile (bfd *abfd, int flags) +{ + struct objfile *objfile = NULL; + struct objfile *last_one = NULL; + + if (mapped_symbol_files) + flags |= OBJF_MAPPED; + +#if defined(USE_MMALLOC) && defined(HAVE_MMAP) + if (abfd != NULL) + { + + /* If we can support mapped symbol files, try to open/reopen the + mapped file that corresponds to the file from which we wish to + read symbols. If the objfile is to be mapped, we must malloc + the structure itself using the mmap version, and arrange that + all memory allocation for the objfile uses the mmap routines. + If we are reusing an existing mapped file, from which we get + our objfile pointer, we have to make sure that we update the + pointers to the alloc/free functions in the obstack, in case + these functions have moved within the current gdb. */ + + int fd; + + fd = open_mapped_file (bfd_get_filename (abfd), bfd_get_mtime (abfd), + flags); + if (fd >= 0) + { + PTR md; + + if ((md = map_to_file (fd)) == NULL) + { + close (fd); + } + else if ((objfile = (struct objfile *) mmalloc_getkey (md, 0)) != NULL) + { + /* Update memory corruption handler function addresses. */ + init_malloc (md); + objfile->md = md; + objfile->mmfd = fd; + /* Update pointers to functions to *our* copies */ + obstack_chunkfun (&objfile->psymbol_cache.cache, xmmalloc); + obstack_freefun (&objfile->psymbol_cache.cache, xmfree); + obstack_chunkfun (&objfile->macro_cache.cache, xmmalloc); + obstack_freefun (&objfile->macro_cache.cache, xmfree); + obstack_chunkfun (&objfile->psymbol_obstack, xmmalloc); + obstack_freefun (&objfile->psymbol_obstack, xmfree); + obstack_chunkfun (&objfile->symbol_obstack, xmmalloc); + obstack_freefun (&objfile->symbol_obstack, xmfree); + obstack_chunkfun (&objfile->type_obstack, xmmalloc); + obstack_freefun (&objfile->type_obstack, xmfree); + /* If already in objfile list, unlink it. */ + unlink_objfile (objfile); + /* Forget things specific to a particular gdb, may have changed. */ + objfile->sf = NULL; + } + else + { + + /* Set up to detect internal memory corruption. MUST be + done before the first malloc. See comments in + init_malloc() and mmcheck(). */ + + init_malloc (md); + + objfile = (struct objfile *) + xmmalloc (md, sizeof (struct objfile)); + memset (objfile, 0, sizeof (struct objfile)); + objfile->md = md; + objfile->mmfd = fd; + objfile->flags |= OBJF_MAPPED; + mmalloc_setkey (objfile->md, 0, objfile); + obstack_specify_allocation_with_arg (&objfile->psymbol_cache.cache, + 0, 0, xmmalloc, xmfree, + objfile->md); + obstack_specify_allocation_with_arg (&objfile->macro_cache.cache, + 0, 0, xmmalloc, xmfree, + objfile->md); + obstack_specify_allocation_with_arg (&objfile->psymbol_obstack, + 0, 0, xmmalloc, xmfree, + objfile->md); + obstack_specify_allocation_with_arg (&objfile->symbol_obstack, + 0, 0, xmmalloc, xmfree, + objfile->md); + obstack_specify_allocation_with_arg (&objfile->type_obstack, + 0, 0, xmmalloc, xmfree, + objfile->md); + } + } + + if ((flags & OBJF_MAPPED) && (objfile == NULL)) + { + warning ("symbol table for '%s' will not be mapped", + bfd_get_filename (abfd)); + flags &= ~OBJF_MAPPED; + } + } +#else /* !defined(USE_MMALLOC) || !defined(HAVE_MMAP) */ + + if (flags & OBJF_MAPPED) + { + warning ("mapped symbol tables are not supported on this machine; missing or broken mmap()."); + + /* Turn off the global flag so we don't try to do mapped symbol tables + any more, which shuts up gdb unless the user specifically gives the + "mapped" keyword again. */ + + mapped_symbol_files = 0; + flags &= ~OBJF_MAPPED; + } + +#endif /* defined(USE_MMALLOC) && defined(HAVE_MMAP) */ + + /* If we don't support mapped symbol files, didn't ask for the file to be + mapped, or failed to open the mapped file for some reason, then revert + back to an unmapped objfile. */ + + if (objfile == NULL) + { + objfile = (struct objfile *) xmalloc (sizeof (struct objfile)); + memset (objfile, 0, sizeof (struct objfile)); + objfile->md = NULL; + objfile->psymbol_cache = bcache_xmalloc (); + objfile->macro_cache = bcache_xmalloc (); + obstack_specify_allocation (&objfile->psymbol_obstack, 0, 0, xmalloc, + xfree); + obstack_specify_allocation (&objfile->symbol_obstack, 0, 0, xmalloc, + xfree); + obstack_specify_allocation (&objfile->type_obstack, 0, 0, xmalloc, + xfree); + flags &= ~OBJF_MAPPED; + } + + /* Update the per-objfile information that comes from the bfd, ensuring + that any data that is reference is saved in the per-objfile data + region. */ + + objfile->obfd = abfd; + if (objfile->name != NULL) + { + xmfree (objfile->md, objfile->name); + } + if (abfd != NULL) + { + objfile->name = mstrsave (objfile->md, bfd_get_filename (abfd)); + objfile->mtime = bfd_get_mtime (abfd); + + /* Build section table. */ + + if (build_objfile_section_table (objfile)) + { + error ("Can't find the file sections in `%s': %s", + objfile->name, bfd_errmsg (bfd_get_error ())); + } + } + + /* Initialize the section indexes for this objfile, so that we can + later detect if they are used w/o being properly assigned to. */ + + objfile->sect_index_text = -1; + objfile->sect_index_data = -1; + objfile->sect_index_bss = -1; + objfile->sect_index_rodata = -1; + + /* Add this file onto the tail of the linked list of other such files. */ + + objfile->next = NULL; + if (object_files == NULL) + object_files = objfile; + else + { + for (last_one = object_files; + last_one->next; + last_one = last_one->next); + last_one->next = objfile; + } + + /* Save passed in flag bits. */ + objfile->flags |= flags; + + return (objfile); +} + +/* Put OBJFILE at the front of the list. */ + +void +objfile_to_front (struct objfile *objfile) +{ + struct objfile **objp; + for (objp = &object_files; *objp != NULL; objp = &((*objp)->next)) + { + if (*objp == objfile) + { + /* Unhook it from where it is. */ + *objp = objfile->next; + /* Put it in the front. */ + objfile->next = object_files; + object_files = objfile; + break; + } + } +} + +/* Unlink OBJFILE from the list of known objfiles, if it is found in the + list. + + It is not a bug, or error, to call this function if OBJFILE is not known + to be in the current list. This is done in the case of mapped objfiles, + for example, just to ensure that the mapped objfile doesn't appear twice + in the list. Since the list is threaded, linking in a mapped objfile + twice would create a circular list. + + If OBJFILE turns out to be in the list, we zap it's NEXT pointer after + unlinking it, just to ensure that we have completely severed any linkages + between the OBJFILE and the list. */ + +void +unlink_objfile (struct objfile *objfile) +{ + struct objfile **objpp; + + for (objpp = &object_files; *objpp != NULL; objpp = &((*objpp)->next)) + { + if (*objpp == objfile) + { + *objpp = (*objpp)->next; + objfile->next = NULL; + return; + } + } + + internal_error (__FILE__, __LINE__, + "unlink_objfile: objfile already unlinked"); +} + + +/* Destroy an objfile and all the symtabs and psymtabs under it. Note + that as much as possible is allocated on the symbol_obstack and + psymbol_obstack, so that the memory can be efficiently freed. + + Things which we do NOT free because they are not in malloc'd memory + or not in memory specific to the objfile include: + + objfile -> sf + + FIXME: If the objfile is using reusable symbol information (via mmalloc), + then we need to take into account the fact that more than one process + may be using the symbol information at the same time (when mmalloc is + extended to support cooperative locking). When more than one process + is using the mapped symbol info, we need to be more careful about when + we free objects in the reusable area. */ + +void +free_objfile (struct objfile *objfile) +{ + /* First do any symbol file specific actions required when we are + finished with a particular symbol file. Note that if the objfile + is using reusable symbol information (via mmalloc) then each of + these routines is responsible for doing the correct thing, either + freeing things which are valid only during this particular gdb + execution, or leaving them to be reused during the next one. */ + + if (objfile->sf != NULL) + { + (*objfile->sf->sym_finish) (objfile); + } + + /* We always close the bfd. */ + + if (objfile->obfd != NULL) + { + char *name = bfd_get_filename (objfile->obfd); + if (!bfd_close (objfile->obfd)) + warning ("cannot close \"%s\": %s", + name, bfd_errmsg (bfd_get_error ())); + xfree (name); + } + + /* Remove it from the chain of all objfiles. */ + + unlink_objfile (objfile); + + /* If we are going to free the runtime common objfile, mark it + as unallocated. */ + + if (objfile == rt_common_objfile) + rt_common_objfile = NULL; + + /* Before the symbol table code was redone to make it easier to + selectively load and remove information particular to a specific + linkage unit, gdb used to do these things whenever the monolithic + symbol table was blown away. How much still needs to be done + is unknown, but we play it safe for now and keep each action until + it is shown to be no longer needed. */ + + /* I *think* all our callers call clear_symtab_users. If so, no need + to call this here. */ + clear_pc_function_cache (); + + /* The last thing we do is free the objfile struct itself for the + non-reusable case, or detach from the mapped file for the + reusable case. Note that the mmalloc_detach or the xmfree() is + the last thing we can do with this objfile. */ + +#if defined(USE_MMALLOC) && defined(HAVE_MMAP) + + if (objfile->flags & OBJF_MAPPED) + { + /* Remember the fd so we can close it. We can't close it before + doing the detach, and after the detach the objfile is gone. */ + int mmfd; + + mmfd = objfile->mmfd; + mmalloc_detach (objfile->md); + objfile = NULL; + close (mmfd); + } + +#endif /* defined(USE_MMALLOC) && defined(HAVE_MMAP) */ + + /* If we still have an objfile, then either we don't support reusable + objfiles or this one was not reusable. So free it normally. */ + + if (objfile != NULL) + { + if (objfile->name != NULL) + { + xmfree (objfile->md, objfile->name); + } + if (objfile->global_psymbols.list) + xmfree (objfile->md, objfile->global_psymbols.list); + if (objfile->static_psymbols.list) + xmfree (objfile->md, objfile->static_psymbols.list); + /* Free the obstacks for non-reusable objfiles */ + bcache_xfree (objfile->psymbol_cache); + bcache_xfree (objfile->macro_cache); + obstack_free (&objfile->psymbol_obstack, 0); + obstack_free (&objfile->symbol_obstack, 0); + obstack_free (&objfile->type_obstack, 0); + xmfree (objfile->md, objfile); + objfile = NULL; + } +} + +static void +do_free_objfile_cleanup (void *obj) +{ + free_objfile (obj); +} + +struct cleanup * +make_cleanup_free_objfile (struct objfile *obj) +{ + return make_cleanup (do_free_objfile_cleanup, obj); +} + +/* Free all the object files at once and clean up their users. */ + +void +free_all_objfiles (void) +{ + struct objfile *objfile, *temp; + + ALL_OBJFILES_SAFE (objfile, temp) + { + free_objfile (objfile); + } + clear_symtab_users (); +} + +/* Relocate OBJFILE to NEW_OFFSETS. There should be OBJFILE->NUM_SECTIONS + entries in new_offsets. */ +void +objfile_relocate (struct objfile *objfile, struct section_offsets *new_offsets) +{ + struct section_offsets *delta = + (struct section_offsets *) alloca (SIZEOF_SECTION_OFFSETS); + + { + int i; + int something_changed = 0; + for (i = 0; i < objfile->num_sections; ++i) + { + delta->offsets[i] = + ANOFFSET (new_offsets, i) - ANOFFSET (objfile->section_offsets, i); + if (ANOFFSET (delta, i) != 0) + something_changed = 1; + } + if (!something_changed) + return; + } + + /* OK, get all the symtabs. */ + { + struct symtab *s; + + ALL_OBJFILE_SYMTABS (objfile, s) + { + struct linetable *l; + struct blockvector *bv; + int i; + + /* First the line table. */ + l = LINETABLE (s); + if (l) + { + for (i = 0; i < l->nitems; ++i) + l->item[i].pc += ANOFFSET (delta, s->block_line_section); + } + + /* Don't relocate a shared blockvector more than once. */ + if (!s->primary) + continue; + + bv = BLOCKVECTOR (s); + for (i = 0; i < BLOCKVECTOR_NBLOCKS (bv); ++i) + { + struct block *b; + struct symbol *sym; + int j; + + b = BLOCKVECTOR_BLOCK (bv, i); + BLOCK_START (b) += ANOFFSET (delta, s->block_line_section); + BLOCK_END (b) += ANOFFSET (delta, s->block_line_section); + + ALL_BLOCK_SYMBOLS (b, j, sym) + { + fixup_symbol_section (sym, objfile); + + /* The RS6000 code from which this was taken skipped + any symbols in STRUCT_NAMESPACE or UNDEF_NAMESPACE. + But I'm leaving out that test, on the theory that + they can't possibly pass the tests below. */ + if ((SYMBOL_CLASS (sym) == LOC_LABEL + || SYMBOL_CLASS (sym) == LOC_STATIC + || SYMBOL_CLASS (sym) == LOC_INDIRECT) + && SYMBOL_SECTION (sym) >= 0) + { + SYMBOL_VALUE_ADDRESS (sym) += + ANOFFSET (delta, SYMBOL_SECTION (sym)); + } +#ifdef MIPS_EFI_SYMBOL_NAME + /* Relocate Extra Function Info for ecoff. */ + + else if (SYMBOL_CLASS (sym) == LOC_CONST + && SYMBOL_NAMESPACE (sym) == LABEL_NAMESPACE + && strcmp (SYMBOL_NAME (sym), MIPS_EFI_SYMBOL_NAME) == 0) + ecoff_relocate_efi (sym, ANOFFSET (delta, + s->block_line_section)); +#endif + } + } + } + } + + { + struct partial_symtab *p; + + ALL_OBJFILE_PSYMTABS (objfile, p) + { + p->textlow += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + p->texthigh += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + } + } + + { + struct partial_symbol **psym; + + for (psym = objfile->global_psymbols.list; + psym < objfile->global_psymbols.next; + psym++) + { + fixup_psymbol_section (*psym, objfile); + if (SYMBOL_SECTION (*psym) >= 0) + SYMBOL_VALUE_ADDRESS (*psym) += ANOFFSET (delta, + SYMBOL_SECTION (*psym)); + } + for (psym = objfile->static_psymbols.list; + psym < objfile->static_psymbols.next; + psym++) + { + fixup_psymbol_section (*psym, objfile); + if (SYMBOL_SECTION (*psym) >= 0) + SYMBOL_VALUE_ADDRESS (*psym) += ANOFFSET (delta, + SYMBOL_SECTION (*psym)); + } + } + + { + struct minimal_symbol *msym; + ALL_OBJFILE_MSYMBOLS (objfile, msym) + if (SYMBOL_SECTION (msym) >= 0) + SYMBOL_VALUE_ADDRESS (msym) += ANOFFSET (delta, SYMBOL_SECTION (msym)); + } + /* Relocating different sections by different amounts may cause the symbols + to be out of order. */ + msymbols_sort (objfile); + + { + int i; + for (i = 0; i < objfile->num_sections; ++i) + (objfile->section_offsets)->offsets[i] = ANOFFSET (new_offsets, i); + } + + if (objfile->ei.entry_point != ~(CORE_ADDR) 0) + { + /* Relocate ei.entry_point with its section offset, use SECT_OFF_TEXT + only as a fallback. */ + struct obj_section *s; + s = find_pc_section (objfile->ei.entry_point); + if (s) + objfile->ei.entry_point += ANOFFSET (delta, s->the_bfd_section->index); + else + objfile->ei.entry_point += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + } + + { + struct obj_section *s; + bfd *abfd; + + abfd = objfile->obfd; + + ALL_OBJFILE_OSECTIONS (objfile, s) + { + int idx = s->the_bfd_section->index; + + s->addr += ANOFFSET (delta, idx); + s->endaddr += ANOFFSET (delta, idx); + } + } + + if (objfile->ei.entry_func_lowpc != INVALID_ENTRY_LOWPC) + { + objfile->ei.entry_func_lowpc += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + objfile->ei.entry_func_highpc += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + } + + if (objfile->ei.entry_file_lowpc != INVALID_ENTRY_LOWPC) + { + objfile->ei.entry_file_lowpc += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + objfile->ei.entry_file_highpc += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + } + + if (objfile->ei.main_func_lowpc != INVALID_ENTRY_LOWPC) + { + objfile->ei.main_func_lowpc += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + objfile->ei.main_func_highpc += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + } + + /* Relocate breakpoints as necessary, after things are relocated. */ + breakpoint_re_set (); +} + +/* Many places in gdb want to test just to see if we have any partial + symbols available. This function returns zero if none are currently + available, nonzero otherwise. */ + +int +have_partial_symbols (void) +{ + struct objfile *ofp; + + ALL_OBJFILES (ofp) + { + if (ofp->psymtabs != NULL) + { + return 1; + } + } + return 0; +} + +/* Many places in gdb want to test just to see if we have any full + symbols available. This function returns zero if none are currently + available, nonzero otherwise. */ + +int +have_full_symbols (void) +{ + struct objfile *ofp; + + ALL_OBJFILES (ofp) + { + if (ofp->symtabs != NULL) + { + return 1; + } + } + return 0; +} + + +/* This operations deletes all objfile entries that represent solibs that + weren't explicitly loaded by the user, via e.g., the add-symbol-file + command. + */ +void +objfile_purge_solibs (void) +{ + struct objfile *objf; + struct objfile *temp; + + ALL_OBJFILES_SAFE (objf, temp) + { + /* We assume that the solib package has been purged already, or will + be soon. + */ + if (!(objf->flags & OBJF_USERLOADED) && (objf->flags & OBJF_SHARED)) + free_objfile (objf); + } +} + + +/* Many places in gdb want to test just to see if we have any minimal + symbols available. This function returns zero if none are currently + available, nonzero otherwise. */ + +int +have_minimal_symbols (void) +{ + struct objfile *ofp; + + ALL_OBJFILES (ofp) + { + if (ofp->msymbols != NULL) + { + return 1; + } + } + return 0; +} + +#if defined(USE_MMALLOC) && defined(HAVE_MMAP) + +/* Given the name of a mapped symbol file in SYMSFILENAME, and the timestamp + of the corresponding symbol file in MTIME, try to open an existing file + with the name SYMSFILENAME and verify it is more recent than the base + file by checking it's timestamp against MTIME. + + If SYMSFILENAME does not exist (or can't be stat'd), simply returns -1. + + If SYMSFILENAME does exist, but is out of date, we check to see if the + user has specified creation of a mapped file. If so, we don't issue + any warning message because we will be creating a new mapped file anyway, + overwriting the old one. If not, then we issue a warning message so that + the user will know why we aren't using this existing mapped symbol file. + In either case, we return -1. + + If SYMSFILENAME does exist and is not out of date, but can't be opened for + some reason, then prints an appropriate system error message and returns -1. + + Otherwise, returns the open file descriptor. */ + +static int +open_existing_mapped_file (char *symsfilename, long mtime, int flags) +{ + int fd = -1; + struct stat sbuf; + + if (stat (symsfilename, &sbuf) == 0) + { + if (sbuf.st_mtime < mtime) + { + if (!(flags & OBJF_MAPPED)) + { + warning ("mapped symbol file `%s' is out of date, ignored it", + symsfilename); + } + } + else if ((fd = open (symsfilename, O_RDWR)) < 0) + { + if (error_pre_print) + { + printf_unfiltered (error_pre_print); + } + print_sys_errmsg (symsfilename, errno); + } + } + return (fd); +} + +/* Look for a mapped symbol file that corresponds to FILENAME and is more + recent than MTIME. If MAPPED is nonzero, the user has asked that gdb + use a mapped symbol file for this file, so create a new one if one does + not currently exist. + + If found, then return an open file descriptor for the file, otherwise + return -1. + + This routine is responsible for implementing the policy that generates + the name of the mapped symbol file from the name of a file containing + symbols that gdb would like to read. Currently this policy is to append + ".syms" to the name of the file. + + This routine is also responsible for implementing the policy that + determines where the mapped symbol file is found (the search path). + This policy is that when reading an existing mapped file, a file of + the correct name in the current directory takes precedence over a + file of the correct name in the same directory as the symbol file. + When creating a new mapped file, it is always created in the current + directory. This helps to minimize the chances of a user unknowingly + creating big mapped files in places like /bin and /usr/local/bin, and + allows a local copy to override a manually installed global copy (in + /bin for example). */ + +static int +open_mapped_file (char *filename, long mtime, int flags) +{ + int fd; + char *symsfilename; + + /* First try to open an existing file in the current directory, and + then try the directory where the symbol file is located. */ + + symsfilename = concat ("./", lbasename (filename), ".syms", (char *) NULL); + if ((fd = open_existing_mapped_file (symsfilename, mtime, flags)) < 0) + { + xfree (symsfilename); + symsfilename = concat (filename, ".syms", (char *) NULL); + fd = open_existing_mapped_file (symsfilename, mtime, flags); + } + + /* If we don't have an open file by now, then either the file does not + already exist, or the base file has changed since it was created. In + either case, if the user has specified use of a mapped file, then + create a new mapped file, truncating any existing one. If we can't + create one, print a system error message saying why we can't. + + By default the file is rw for everyone, with the user's umask taking + care of turning off the permissions the user wants off. */ + + if ((fd < 0) && (flags & OBJF_MAPPED)) + { + xfree (symsfilename); + symsfilename = concat ("./", lbasename (filename), ".syms", + (char *) NULL); + if ((fd = open (symsfilename, O_RDWR | O_CREAT | O_TRUNC, 0666)) < 0) + { + if (error_pre_print) + { + printf_unfiltered (error_pre_print); + } + print_sys_errmsg (symsfilename, errno); + } + } + + xfree (symsfilename); + return (fd); +} + +static PTR +map_to_file (int fd) +{ + PTR md; + CORE_ADDR mapto; + + md = mmalloc_attach (fd, (PTR) 0); + if (md != NULL) + { + mapto = (CORE_ADDR) mmalloc_getkey (md, 1); + md = mmalloc_detach (md); + if (md != NULL) + { + /* FIXME: should figure out why detach failed */ + md = NULL; + } + else if (mapto != (CORE_ADDR) NULL) + { + /* This mapping file needs to be remapped at "mapto" */ + md = mmalloc_attach (fd, (PTR) mapto); + } + else + { + /* This is a freshly created mapping file. */ + mapto = (CORE_ADDR) mmalloc_findbase (20 * 1024 * 1024); + if (mapto != 0) + { + /* To avoid reusing the freshly created mapping file, at the + address selected by mmap, we must truncate it before trying + to do an attach at the address we want. */ + ftruncate (fd, 0); + md = mmalloc_attach (fd, (PTR) mapto); + if (md != NULL) + { + mmalloc_setkey (md, 1, (PTR) mapto); + } + } + } + } + return (md); +} + +#endif /* defined(USE_MMALLOC) && defined(HAVE_MMAP) */ + +/* Returns a section whose range includes PC and SECTION, + or NULL if none found. Note the distinction between the return type, + struct obj_section (which is defined in gdb), and the input type + struct sec (which is a bfd-defined data type). The obj_section + contains a pointer to the bfd struct sec section. */ + +struct obj_section * +find_pc_sect_section (CORE_ADDR pc, struct sec *section) +{ + struct obj_section *s; + struct objfile *objfile; + + ALL_OBJSECTIONS (objfile, s) + if ((section == 0 || section == s->the_bfd_section) && + s->addr <= pc && pc < s->endaddr) + return (s); + + return (NULL); +} + +/* Returns a section whose range includes PC or NULL if none found. + Backward compatibility, no section. */ + +struct obj_section * +find_pc_section (CORE_ADDR pc) +{ + return find_pc_sect_section (pc, find_pc_mapped_section (pc)); +} + + +/* In SVR4, we recognize a trampoline by it's section name. + That is, if the pc is in a section named ".plt" then we are in + a trampoline. */ + +int +in_plt_section (CORE_ADDR pc, char *name) +{ + struct obj_section *s; + int retval = 0; + + s = find_pc_section (pc); + + retval = (s != NULL + && s->the_bfd_section->name != NULL + && STREQ (s->the_bfd_section->name, ".plt")); + return (retval); +} + +/* Return nonzero if NAME is in the import list of OBJFILE. Else + return zero. */ + +int +is_in_import_list (char *name, struct objfile *objfile) +{ + register int i; + + if (!objfile || !name || !*name) + return 0; + + for (i = 0; i < objfile->import_list_size; i++) + if (objfile->import_list[i] && STREQ (name, objfile->import_list[i])) + return 1; + return 0; +} + diff --git a/gdb/printcmd.c b/gdb/printcmd.c new file mode 100644 index 0000000..8542b8a --- /dev/null +++ b/gdb/printcmd.c @@ -0,0 +1,2572 @@ +/* Print values for GNU debugger GDB. + + Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, + 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 Free Software + Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "gdb_string.h" +#include "frame.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "value.h" +#include "language.h" +#include "expression.h" +#include "gdbcore.h" +#include "gdbcmd.h" +#include "target.h" +#include "breakpoint.h" +#include "demangle.h" +#include "valprint.h" +#include "annotate.h" +#include "symfile.h" /* for overlay functions */ +#include "objfiles.h" /* ditto */ +#include "completer.h" /* for completion functions */ +#include "ui-out.h" +#include "gdb_assert.h" + +extern int asm_demangle; /* Whether to demangle syms in asm printouts */ +extern int addressprint; /* Whether to print hex addresses in HLL " */ + +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; + +/* Default section to examine next. */ + +static asection *next_section; + +/* 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 struct value *last_examine_value; + +/* Largest offset between a symbolic value and an address, that will be + printed as `0x1234 '. */ + +static unsigned int max_symbolic_offset = UINT_MAX; + +/* Append the source filename and linenumber of the symbol when + printing a symbolic value as `' if set. */ +static int print_symbol_filename = 0; + +/* Number of auto-display expression currently being displayed. + So that we can disable it if we get an error or a signal within it. + -1 when not doing one. */ + +int current_display_number; + +/* Flag to low-level print routines that this value is being printed + in an epoch window. We'd like to pass this as a parameter, but + every routine would need to take it. Perhaps we can encapsulate + this in the I/O stream once we have GNU stdio. */ + +int inspect_it = 0; + +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) */ + int enabled_p; + }; + +/* Chain of expressions whose values should be displayed + automatically each time the program stops. */ + +static struct display *display_chain; + +static int display_number; + +/* Prototypes for exported functions. */ + +void output_command (char *, int); + +void _initialize_printcmd (void); + +/* Prototypes for local functions. */ + +static void delete_display (int); + +static void enable_display (char *, int); + +static void disable_display_command (char *, int); + +static void disassemble_command (char *, int); + +static void printf_command (char *, int); + +static void print_frame_nameless_args (struct frame_info *, long, + int, int, struct ui_file *); + +static void display_info (char *, int); + +static void do_one_display (struct display *); + +static void undisplay_command (char *, int); + +static void free_display (struct display *); + +static void display_command (char *, int); + +void x_command (char *, int); + +static void address_info (char *, int); + +static void set_command (char *, int); + +static void call_command (char *, int); + +static void inspect_command (char *, int); + +static void print_command (char *, int); + +static void print_command_1 (char *, int, int); + +static void validate_format (struct format_data, char *); + +static void do_examine (struct format_data, CORE_ADDR addr, + asection * section); + +static void print_formatted (struct value *, int, int, struct ui_file *); + +static struct format_data decode_format (char **, int, int); + +static int print_insn (CORE_ADDR, struct ui_file *); + +static void sym_info (char *, int); + + +/* 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. + If OSIZE is zero, then the size field of the returned value + should be set only if a size is explicitly specified by the + user. + 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. */ + +static struct format_data +decode_format (char **string_ptr, int oformat, int osize) +{ + struct format_data val; + register char *p = *string_ptr; + + val.format = '?'; + val.size = '?'; + 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++; + else if (*p >= 'a' && *p <= 'z') + val.format = *p++; + else + break; + } + + while (*p == ' ' || *p == '\t') + p++; + *string_ptr = p; + + /* Set defaults for format and size if not specified. */ + if (val.format == '?') + { + if (val.size == '?') + { + /* Neither has been specified. */ + val.format = oformat; + val.size = osize; + } + else + /* If a size is specified, any format makes a reasonable + default except 'i'. */ + val.format = oformat == 'i' ? 'x' : oformat; + } + else if (val.size == '?') + switch (val.format) + { + case 'a': + case 's': + /* Pick the appropriate size for an address. */ + if (TARGET_PTR_BIT == 64) + val.size = osize ? 'g' : osize; + else if (TARGET_PTR_BIT == 32) + val.size = osize ? 'w' : osize; + else if (TARGET_PTR_BIT == 16) + val.size = osize ? 'h' : osize; + else + /* Bad value for TARGET_PTR_BIT */ + internal_error (__FILE__, __LINE__, "failed internal consistency check"); + break; + case 'f': + /* Floating point has to be word or giantword. */ + if (osize == 'w' || osize == 'g') + val.size = osize; + else + /* Default it to giantword if the last used size is not + appropriate. */ + val.size = osize ? 'g' : osize; + break; + case 'c': + /* Characters default to one byte. */ + val.size = osize ? 'b' : osize; + break; + default: + /* The default is the size most recently specified. */ + val.size = osize; + } + + return val; +} + +/* Print value VAL on stream 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 (struct value *val, register int format, int size, + struct ui_file *stream) +{ + struct type *type = check_typedef (VALUE_TYPE (val)); + int len = TYPE_LENGTH (type); + + if (VALUE_LVAL (val) == lval_memory) + { + next_address = VALUE_ADDRESS (val) + len; + next_section = VALUE_BFD_SECTION (val); + } + + switch (format) + { + case 's': + /* FIXME: Need to handle wchar_t's here... */ + next_address = VALUE_ADDRESS (val) + + val_print_string (VALUE_ADDRESS (val), -1, 1, stream); + next_section = VALUE_BFD_SECTION (val); + break; + + case 'i': + /* The old comment says + "Force output out, print_insn not using _filtered". + I'm not completely sure what that means, I suspect most print_insn + now do use _filtered, so I guess it's obsolete. + --Yes, it does filter now, and so this is obsolete. -JB */ + + /* We often wrap here if there are long symbolic names. */ + wrap_here (" "); + next_address = VALUE_ADDRESS (val) + + print_insn (VALUE_ADDRESS (val), stream); + next_section = VALUE_BFD_SECTION (val); + break; + + default: + if (format == 0 + || TYPE_CODE (type) == TYPE_CODE_ARRAY + || TYPE_CODE (type) == TYPE_CODE_STRING + || TYPE_CODE (type) == TYPE_CODE_STRUCT + || TYPE_CODE (type) == TYPE_CODE_UNION) + /* If format is 0, use the 'natural' format for + * that type of value. If the type is non-scalar, + * we have to use language rules to print it as + * a series of scalars. + */ + value_print (val, stream, format, Val_pretty_default); + else + /* User specified format, so don't look to the + * the type to tell us what to do. + */ + print_scalar_formatted (VALUE_CONTENTS (val), type, + format, size, stream); + } +} + +/* 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 (char *valaddr, struct type *type, int format, int size, + struct ui_file *stream) +{ + LONGEST val_long; + unsigned int len = TYPE_LENGTH (type); + + if (len > sizeof (LONGEST) + && (format == 't' + || format == 'c' + || format == 'o' + || format == 'u' + || format == 'd' + || format == 'x')) + { + if (!TYPE_UNSIGNED (type) + || !extract_long_unsigned_integer (valaddr, len, &val_long)) + { + /* We can't print it normally, but we can print it in hex. + Printing it in the wrong radix is more useful than saying + "use /x, you dummy". */ + /* FIXME: we could also do octal or binary if that was the + desired format. */ + /* FIXME: we should be using the size field to give us a + minimum field width to print. */ + + if (format == 'o') + print_octal_chars (stream, valaddr, len); + else if (format == 'd') + print_decimal_chars (stream, valaddr, len); + else if (format == 't') + print_binary_chars (stream, valaddr, len); + else + /* replace with call to print_hex_chars? Looks + like val_print_type_code_int is redoing + work. - edie */ + + val_print_type_code_int (type, valaddr, stream); + + return; + } + + /* If we get here, extract_long_unsigned_integer set val_long. */ + } + else if (format != 'f') + val_long = unpack_long (type, valaddr); + + /* If the value is a pointer, and pointers and addresses are not the + same, then at this point, the value's length (in target bytes) is + TARGET_ADDR_BIT/TARGET_CHAR_BIT, not TYPE_LENGTH (type). */ + if (TYPE_CODE (type) == TYPE_CODE_PTR) + len = TARGET_ADDR_BIT / TARGET_CHAR_BIT; + + /* If we are printing it as unsigned, truncate it in case it is actually + a negative signed value (e.g. "print/u (short)-1" should print 65535 + (if shorts are 16 bits) instead of 4294967295). */ + if (format != 'd') + { + if (len < sizeof (LONGEST)) + val_long &= ((LONGEST) 1 << HOST_CHAR_BIT * len) - 1; + } + + switch (format) + { + case 'x': + if (!size) + { + /* no size specified, like in print. Print varying # of digits. */ + print_longest (stream, 'x', 1, val_long); + } + else + switch (size) + { + case 'b': + case 'h': + case 'w': + case 'g': + print_longest (stream, size, 1, val_long); + break; + default: + error ("Undefined output size \"%c\".", size); + } + break; + + case 'd': + print_longest (stream, 'd', 1, val_long); + break; + + case 'u': + print_longest (stream, 'u', 0, val_long); + break; + + case 'o': + if (val_long) + print_longest (stream, 'o', 1, val_long); + else + fprintf_filtered (stream, "0"); + break; + + case 'a': + { + CORE_ADDR addr = unpack_pointer (type, valaddr); + print_address (addr, stream); + } + break; + + case 'c': + value_print (value_from_longest (builtin_type_true_char, val_long), + stream, 0, Val_pretty_default); + break; + + case 'f': + if (len == TYPE_LENGTH (builtin_type_float)) + type = builtin_type_float; + else if (len == TYPE_LENGTH (builtin_type_double)) + type = builtin_type_double; + else if (len == TYPE_LENGTH (builtin_type_long_double)) + type = builtin_type_long_double; + print_floating (valaddr, type, stream); + break; + + case 0: + internal_error (__FILE__, __LINE__, "failed internal consistency check"); + + case 't': + /* Binary; 't' stands for "two". */ + { + char bits[8 * (sizeof val_long) + 1]; + char buf[8 * (sizeof val_long) + 32]; + char *cp = bits; + int width; + + if (!size) + width = 8 * (sizeof val_long); + else + switch (size) + { + case 'b': + width = 8; + break; + case 'h': + width = 16; + break; + case 'w': + width = 32; + break; + case 'g': + width = 64; + break; + default: + error ("Undefined output size \"%c\".", size); + } + + bits[width] = '\0'; + while (width-- > 0) + { + bits[width] = (val_long & 1) ? '1' : '0'; + val_long >>= 1; + } + if (!size) + { + while (*cp && *cp == '0') + cp++; + if (*cp == '\0') + cp--; + } + strcpy (buf, local_binary_format_prefix ()); + strcat (buf, cp); + strcat (buf, local_binary_format_suffix ()); + fprintf_filtered (stream, buf); + } + break; + + default: + error ("Undefined output format \"%c\".", format); + } +} + +/* Specify default address for `x' command. + `info lines' uses this. */ + +void +set_next_address (CORE_ADDR addr) +{ + next_address = addr; + + /* Make address available to the user as $_. */ + set_internalvar (lookup_internalvar ("_"), + value_from_pointer (lookup_pointer_type (builtin_type_void), + addr)); +} + +/* Optionally print address ADDR symbolically as on STREAM, + after LEADIN. Print nothing if no symbolic name is found nearby. + Optionally also print source file and line number, if available. + DO_DEMANGLE controls whether to print a symbol in its native "raw" form, + or to interpret it as a possible C++ name and convert it back to source + form. However note that DO_DEMANGLE can be overridden by the specific + settings of the demangle and asm_demangle variables. */ + +void +print_address_symbolic (CORE_ADDR addr, struct ui_file *stream, int do_demangle, + char *leadin) +{ + char *name = NULL; + char *filename = NULL; + int unmapped = 0; + int offset = 0; + int line = 0; + + /* throw away both name and filename */ + struct cleanup *cleanup_chain = make_cleanup (free_current_contents, &name); + make_cleanup (free_current_contents, &filename); + + if (build_address_symbolic (addr, do_demangle, &name, &offset, &filename, &line, &unmapped)) + { + do_cleanups (cleanup_chain); + return; + } + + fputs_filtered (leadin, stream); + if (unmapped) + fputs_filtered ("<*", stream); + else + fputs_filtered ("<", stream); + fputs_filtered (name, stream); + if (offset != 0) + fprintf_filtered (stream, "+%u", (unsigned int) offset); + + /* Append source filename and line number if desired. Give specific + line # of this addr, if we have it; else line # of the nearest symbol. */ + if (print_symbol_filename && filename != NULL) + { + if (line != -1) + fprintf_filtered (stream, " at %s:%d", filename, line); + else + fprintf_filtered (stream, " in %s", filename); + } + if (unmapped) + fputs_filtered ("*>", stream); + else + fputs_filtered (">", stream); + + do_cleanups (cleanup_chain); +} + +/* Given an address ADDR return all the elements needed to print the + address in a symbolic form. NAME can be mangled or not depending + on DO_DEMANGLE (and also on the asm_demangle global variable, + manipulated via ''set print asm-demangle''). Return 0 in case of + success, when all the info in the OUT paramters is valid. Return 1 + otherwise. */ +int +build_address_symbolic (CORE_ADDR addr, /* IN */ + int do_demangle, /* IN */ + char **name, /* OUT */ + int *offset, /* OUT */ + char **filename, /* OUT */ + int *line, /* OUT */ + int *unmapped) /* OUT */ +{ + struct minimal_symbol *msymbol; + struct symbol *symbol; + struct symtab *symtab = 0; + CORE_ADDR name_location = 0; + asection *section = 0; + char *name_temp = ""; + + /* Let's say it is unmapped. */ + *unmapped = 0; + + /* Determine if the address is in an overlay, and whether it is + mapped. */ + if (overlay_debugging) + { + section = find_pc_overlay (addr); + if (pc_in_unmapped_range (addr, section)) + { + *unmapped = 1; + addr = overlay_mapped_address (addr, section); + } + } + + /* On some targets, add in extra "flag" bits to PC for + disassembly. This should ensure that "rounding errors" in + symbol addresses that are masked for disassembly favour the + the correct symbol. */ + +#ifdef GDB_TARGET_UNMASK_DISAS_PC + addr = GDB_TARGET_UNMASK_DISAS_PC (addr); +#endif + + /* First try to find the address in the symbol table, then + in the minsyms. Take the closest one. */ + + /* This is defective in the sense that it only finds text symbols. So + really this is kind of pointless--we should make sure that the + minimal symbols have everything we need (by changing that we could + save some memory, but for many debug format--ELF/DWARF or + anything/stabs--it would be inconvenient to eliminate those minimal + symbols anyway). */ + msymbol = lookup_minimal_symbol_by_pc_section (addr, section); + symbol = find_pc_sect_function (addr, section); + + if (symbol) + { + name_location = BLOCK_START (SYMBOL_BLOCK_VALUE (symbol)); + if (do_demangle) + name_temp = SYMBOL_SOURCE_NAME (symbol); + else + name_temp = SYMBOL_LINKAGE_NAME (symbol); + } + + if (msymbol != NULL) + { + if (SYMBOL_VALUE_ADDRESS (msymbol) > name_location || symbol == NULL) + { + /* The msymbol is closer to the address than the symbol; + use the msymbol instead. */ + symbol = 0; + symtab = 0; + name_location = SYMBOL_VALUE_ADDRESS (msymbol); + if (do_demangle) + name_temp = SYMBOL_SOURCE_NAME (msymbol); + else + name_temp = SYMBOL_LINKAGE_NAME (msymbol); + } + } + if (symbol == NULL && msymbol == NULL) + return 1; + + /* On some targets, mask out extra "flag" bits from PC for handsome + disassembly. */ + +#ifdef GDB_TARGET_MASK_DISAS_PC + name_location = GDB_TARGET_MASK_DISAS_PC (name_location); + addr = GDB_TARGET_MASK_DISAS_PC (addr); +#endif + + /* If the nearest symbol is too far away, don't print anything symbolic. */ + + /* For when CORE_ADDR is larger than unsigned int, we do math in + CORE_ADDR. But when we detect unsigned wraparound in the + CORE_ADDR math, we ignore this test and print the offset, + because addr+max_symbolic_offset has wrapped through the end + of the address space back to the beginning, giving bogus comparison. */ + if (addr > name_location + max_symbolic_offset + && name_location + max_symbolic_offset > name_location) + return 1; + + *offset = addr - name_location; + + *name = xstrdup (name_temp); + + if (print_symbol_filename) + { + struct symtab_and_line sal; + + sal = find_pc_sect_line (addr, section, 0); + + if (sal.symtab) + { + *filename = xstrdup (sal.symtab->filename); + *line = sal.line; + } + else if (symtab && symbol && symbol->line) + { + *filename = xstrdup (symtab->filename); + *line = symbol->line; + } + else if (symtab) + { + *filename = xstrdup (symtab->filename); + *line = -1; + } + } + return 0; +} + +/* Print address ADDR on STREAM. USE_LOCAL means the same thing as for + print_longest. */ +void +print_address_numeric (CORE_ADDR addr, int use_local, struct ui_file *stream) +{ + /* Truncate address to the size of a target address, avoiding shifts + larger or equal than the width of a CORE_ADDR. The local + variable ADDR_BIT stops the compiler reporting a shift overflow + when it won't occur. */ + /* NOTE: This assumes that the significant address information is + kept in the least significant bits of ADDR - the upper bits were + either zero or sign extended. Should ADDRESS_TO_POINTER() or + some ADDRESS_TO_PRINTABLE() be used to do the conversion? */ + + int addr_bit = TARGET_ADDR_BIT; + + if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT)) + addr &= ((CORE_ADDR) 1 << addr_bit) - 1; + print_longest (stream, 'x', use_local, (ULONGEST) addr); +} + +/* Print address ADDR symbolically on STREAM. + First print it as a number. Then perhaps print + after the number. */ + +void +print_address (CORE_ADDR addr, struct ui_file *stream) +{ + print_address_numeric (addr, 1, stream); + print_address_symbolic (addr, stream, asm_demangle, " "); +} + +/* Print address ADDR symbolically on STREAM. Parameter DEMANGLE + controls whether to print the symbolic name "raw" or demangled. + Global setting "addressprint" controls whether to print hex address + or not. */ + +void +print_address_demangle (CORE_ADDR addr, struct ui_file *stream, int do_demangle) +{ + if (addr == 0) + { + fprintf_filtered (stream, "0"); + } + else if (addressprint) + { + print_address_numeric (addr, 1, stream); + print_address_symbolic (addr, stream, do_demangle, " "); + } + else + { + print_address_symbolic (addr, stream, do_demangle, ""); + } +} + + +/* These are the types that $__ will get after an examine command of one + of these sizes. */ + +static struct type *examine_i_type; + +static struct type *examine_b_type; +static struct type *examine_h_type; +static struct type *examine_w_type; +static struct type *examine_g_type; + +/* Examine data at address ADDR in format FMT. + Fetch it from memory and print on gdb_stdout. */ + +static void +do_examine (struct format_data fmt, CORE_ADDR addr, asection *sect) +{ + register char format = 0; + register char size; + register int count = 1; + struct type *val_type = NULL; + register int i; + register int maxelts; + + format = fmt.format; + size = fmt.size; + count = fmt.count; + next_address = addr; + next_section = sect; + + /* String or instruction format implies fetch single bytes + regardless of the specified size. */ + if (format == 's' || format == 'i') + size = 'b'; + + if (format == 'i') + val_type = examine_i_type; + else if (size == 'b') + val_type = examine_b_type; + else if (size == 'h') + val_type = examine_h_type; + else if (size == 'w') + val_type = examine_w_type; + else if (size == 'g') + val_type = examine_g_type; + + 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) + { + QUIT; + print_address (next_address, gdb_stdout); + printf_filtered (":"); + for (i = maxelts; + i > 0 && count > 0; + i--, count--) + { + printf_filtered ("\t"); + /* Note that print_formatted sets next_address for the next + object. */ + last_examine_address = next_address; + + if (last_examine_value) + value_free (last_examine_value); + + /* The value to be displayed is not fetched greedily. + Instead, to avoid the posibility of a fetched value not + being used, its retreval is delayed until the print code + uses it. When examining an instruction stream, the + disassembler will perform its own memory fetch using just + the address stored in LAST_EXAMINE_VALUE. FIXME: Should + the disassembler be modified so that LAST_EXAMINE_VALUE + is left with the byte sequence from the last complete + instruction fetched from memory? */ + last_examine_value = value_at_lazy (val_type, next_address, sect); + + if (last_examine_value) + release_value (last_examine_value); + + print_formatted (last_examine_value, format, size, gdb_stdout); + } + printf_filtered ("\n"); + gdb_flush (gdb_stdout); + } +} + +static void +validate_format (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); +} + +/* Evaluate string EXP as an expression in the current language and + print the resulting value. EXP may contain a format specifier as the + first argument ("/x myvar" for example, to print myvar in hex). + */ + +static void +print_command_1 (char *exp, int inspect, int voidprint) +{ + struct expression *expr; + register struct cleanup *old_chain = 0; + register char format = 0; + struct value *val; + struct format_data fmt; + int cleanup = 0; + + /* Pass inspect flag to the rest of the print routines in a global (sigh). */ + inspect_it = inspect; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, 0); + validate_format (fmt, "print"); + last_format = format = fmt.format; + } + else + { + fmt.count = 1; + fmt.format = 0; + fmt.size = 0; + } + + if (exp && *exp) + { + struct type *type; + expr = parse_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + cleanup = 1; + val = evaluate_expression (expr); + } + else + val = access_value_history (0); + + if (voidprint || (val && VALUE_TYPE (val) && + TYPE_CODE (VALUE_TYPE (val)) != TYPE_CODE_VOID)) + { + int histindex = record_latest_value (val); + + if (histindex >= 0) + annotate_value_history_begin (histindex, VALUE_TYPE (val)); + else + annotate_value_begin (VALUE_TYPE (val)); + + if (inspect) + printf_unfiltered ("\031(gdb-makebuffer \"%s\" %d '(\"", exp, histindex); + else if (histindex >= 0) + printf_filtered ("$%d = ", histindex); + + if (histindex >= 0) + annotate_value_history_value (); + + print_formatted (val, format, fmt.size, gdb_stdout); + printf_filtered ("\n"); + + if (histindex >= 0) + annotate_value_history_end (); + else + annotate_value_end (); + + if (inspect) + printf_unfiltered ("\") )\030"); + } + + if (cleanup) + do_cleanups (old_chain); + inspect_it = 0; /* Reset print routines to normal */ +} + +/* ARGSUSED */ +static void +print_command (char *exp, int from_tty) +{ + print_command_1 (exp, 0, 1); +} + +/* Same as print, except in epoch, it gets its own window */ +/* ARGSUSED */ +static void +inspect_command (char *exp, int from_tty) +{ + extern int epoch_interface; + + print_command_1 (exp, epoch_interface, 1); +} + +/* Same as print, except it doesn't print void results. */ +/* ARGSUSED */ +static void +call_command (char *exp, int from_tty) +{ + print_command_1 (exp, 0, 0); +} + +/* ARGSUSED */ +void +output_command (char *exp, int from_tty) +{ + struct expression *expr; + register struct cleanup *old_chain; + register char format = 0; + struct value *val; + struct format_data fmt; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, 0, 0); + validate_format (fmt, "output"); + format = fmt.format; + } + + expr = parse_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + + val = evaluate_expression (expr); + + annotate_value_begin (VALUE_TYPE (val)); + + print_formatted (val, format, fmt.size, gdb_stdout); + + annotate_value_end (); + + wrap_here (""); + gdb_flush (gdb_stdout); + + do_cleanups (old_chain); +} + +/* ARGSUSED */ +static void +set_command (char *exp, int from_tty) +{ + struct expression *expr = parse_expression (exp); + register struct cleanup *old_chain = + make_cleanup (free_current_contents, &expr); + evaluate_expression (expr); + do_cleanups (old_chain); +} + +/* ARGSUSED */ +static void +sym_info (char *arg, int from_tty) +{ + struct minimal_symbol *msymbol; + struct objfile *objfile; + struct obj_section *osect; + asection *sect; + CORE_ADDR addr, sect_addr; + int matches = 0; + unsigned int offset; + + if (!arg) + error_no_arg ("address"); + + addr = parse_and_eval_address (arg); + ALL_OBJSECTIONS (objfile, osect) + { + sect = osect->the_bfd_section; + sect_addr = overlay_mapped_address (addr, sect); + + if (osect->addr <= sect_addr && sect_addr < osect->endaddr && + (msymbol = lookup_minimal_symbol_by_pc_section (sect_addr, sect))) + { + matches = 1; + offset = sect_addr - SYMBOL_VALUE_ADDRESS (msymbol); + if (offset) + printf_filtered ("%s + %u in ", + SYMBOL_SOURCE_NAME (msymbol), offset); + else + printf_filtered ("%s in ", + SYMBOL_SOURCE_NAME (msymbol)); + if (pc_in_unmapped_range (addr, sect)) + printf_filtered ("load address range of "); + if (section_is_overlay (sect)) + printf_filtered ("%s overlay ", + section_is_mapped (sect) ? "mapped" : "unmapped"); + printf_filtered ("section %s", sect->name); + printf_filtered ("\n"); + } + } + if (matches == 0) + printf_filtered ("No symbol matches %s.\n", arg); +} + +/* ARGSUSED */ +static void +address_info (char *exp, int from_tty) +{ + register struct symbol *sym; + register struct minimal_symbol *msymbol; + register long val; + register long basereg; + asection *section; + CORE_ADDR load_addr; + 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 (0), VAR_NAMESPACE, + &is_a_field_of_this, (struct symtab **) NULL); + if (sym == NULL) + { + if (is_a_field_of_this) + { + printf_filtered ("Symbol \""); + fprintf_symbol_filtered (gdb_stdout, exp, + current_language->la_language, DMGL_ANSI); + printf_filtered ("\" is a field of the local class variable `this'\n"); + return; + } + + msymbol = lookup_minimal_symbol (exp, NULL, NULL); + + if (msymbol != NULL) + { + load_addr = SYMBOL_VALUE_ADDRESS (msymbol); + + printf_filtered ("Symbol \""); + fprintf_symbol_filtered (gdb_stdout, exp, + current_language->la_language, DMGL_ANSI); + printf_filtered ("\" is at "); + print_address_numeric (load_addr, 1, gdb_stdout); + printf_filtered (" in a file compiled without debugging"); + section = SYMBOL_BFD_SECTION (msymbol); + if (section_is_overlay (section)) + { + load_addr = overlay_unmapped_address (load_addr, section); + printf_filtered (",\n -- loaded at "); + print_address_numeric (load_addr, 1, gdb_stdout); + printf_filtered (" in overlay section %s", section->name); + } + printf_filtered (".\n"); + } + else + error ("No symbol \"%s\" in current context.", exp); + return; + } + + printf_filtered ("Symbol \""); + fprintf_symbol_filtered (gdb_stdout, SYMBOL_NAME (sym), + current_language->la_language, DMGL_ANSI); + printf_filtered ("\" is "); + val = SYMBOL_VALUE (sym); + basereg = SYMBOL_BASEREG (sym); + section = SYMBOL_BFD_SECTION (sym); + + switch (SYMBOL_CLASS (sym)) + { + case LOC_CONST: + case LOC_CONST_BYTES: + printf_filtered ("constant"); + break; + + case LOC_LABEL: + printf_filtered ("a label at address "); + print_address_numeric (load_addr = SYMBOL_VALUE_ADDRESS (sym), + 1, gdb_stdout); + if (section_is_overlay (section)) + { + load_addr = overlay_unmapped_address (load_addr, section); + printf_filtered (",\n -- loaded at "); + print_address_numeric (load_addr, 1, gdb_stdout); + printf_filtered (" in overlay section %s", section->name); + } + break; + + case LOC_REGISTER: + printf_filtered ("a variable in register %s", REGISTER_NAME (val)); + break; + + case LOC_STATIC: + printf_filtered ("static storage at address "); + print_address_numeric (load_addr = SYMBOL_VALUE_ADDRESS (sym), + 1, gdb_stdout); + if (section_is_overlay (section)) + { + load_addr = overlay_unmapped_address (load_addr, section); + printf_filtered (",\n -- loaded at "); + print_address_numeric (load_addr, 1, gdb_stdout); + printf_filtered (" in overlay section %s", section->name); + } + break; + + case LOC_INDIRECT: + printf_filtered ("external global (indirect addressing), at address *("); + print_address_numeric (load_addr = SYMBOL_VALUE_ADDRESS (sym), + 1, gdb_stdout); + printf_filtered (")"); + if (section_is_overlay (section)) + { + load_addr = overlay_unmapped_address (load_addr, section); + printf_filtered (",\n -- loaded at "); + print_address_numeric (load_addr, 1, gdb_stdout); + printf_filtered (" in overlay section %s", section->name); + } + break; + + case LOC_REGPARM: + printf_filtered ("an argument in register %s", REGISTER_NAME (val)); + break; + + case LOC_REGPARM_ADDR: + printf_filtered ("address of an argument in register %s", REGISTER_NAME (val)); + break; + + case LOC_ARG: + printf_filtered ("an argument at offset %ld", val); + break; + + case LOC_LOCAL_ARG: + printf_filtered ("an argument at frame offset %ld", val); + break; + + case LOC_LOCAL: + printf_filtered ("a local variable at frame offset %ld", val); + break; + + case LOC_REF_ARG: + printf_filtered ("a reference argument at offset %ld", val); + break; + + case LOC_BASEREG: + printf_filtered ("a variable at offset %ld from register %s", + val, REGISTER_NAME (basereg)); + break; + + case LOC_BASEREG_ARG: + printf_filtered ("an argument at offset %ld from register %s", + val, REGISTER_NAME (basereg)); + break; + + case LOC_TYPEDEF: + printf_filtered ("a typedef"); + break; + + case LOC_BLOCK: + printf_filtered ("a function at address "); +#ifdef GDB_TARGET_MASK_DISAS_PC + print_address_numeric + (load_addr = GDB_TARGET_MASK_DISAS_PC (BLOCK_START (SYMBOL_BLOCK_VALUE (sym))), + 1, gdb_stdout); +#else + print_address_numeric (load_addr = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)), + 1, gdb_stdout); +#endif + if (section_is_overlay (section)) + { + load_addr = overlay_unmapped_address (load_addr, section); + printf_filtered (",\n -- loaded at "); + print_address_numeric (load_addr, 1, gdb_stdout); + printf_filtered (" in overlay section %s", section->name); + } + break; + + case LOC_UNRESOLVED: + { + struct minimal_symbol *msym; + + msym = lookup_minimal_symbol (SYMBOL_NAME (sym), NULL, NULL); + if (msym == NULL) + printf_filtered ("unresolved"); + else + { + section = SYMBOL_BFD_SECTION (msym); + printf_filtered ("static storage at address "); + print_address_numeric (load_addr = SYMBOL_VALUE_ADDRESS (msym), + 1, gdb_stdout); + if (section_is_overlay (section)) + { + load_addr = overlay_unmapped_address (load_addr, section); + printf_filtered (",\n -- loaded at "); + print_address_numeric (load_addr, 1, gdb_stdout); + printf_filtered (" in overlay section %s", section->name); + } + } + } + break; + + case LOC_THREAD_LOCAL_STATIC: + printf_filtered ( + "a thread-local variable at offset %ld from the thread base register %s", + val, REGISTER_NAME (basereg)); + break; + + case LOC_OPTIMIZED_OUT: + printf_filtered ("optimized out"); + break; + + default: + printf_filtered ("of unknown (botched) type"); + break; + } + printf_filtered (".\n"); +} + +void +x_command (char *exp, int from_tty) +{ + struct expression *expr; + struct format_data fmt; + struct cleanup *old_chain; + struct value *val; + + fmt.format = last_format; + fmt.size = last_size; + fmt.count = 1; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, last_size); + } + + /* If we have an expression, evaluate it and use it as the address. */ + + if (exp != 0 && *exp != 0) + { + expr = parse_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); + val = evaluate_expression (expr); + if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_REF) + val = value_ind (val); + /* In rvalue contexts, such as this, functions are coerced into + pointers to functions. This makes "x/i main" work. */ + if (/* last_format == 'i' && */ + TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FUNC + && VALUE_LVAL (val) == lval_memory) + next_address = VALUE_ADDRESS (val); + else + next_address = value_as_address (val); + if (VALUE_BFD_SECTION (val)) + next_section = VALUE_BFD_SECTION (val); + do_cleanups (old_chain); + } + + do_examine (fmt, next_address, next_section); + + /* If the examine succeeds, we remember its size and format for next time. */ + last_size = fmt.size; + last_format = fmt.format; + + /* Set a couple of internal variables if appropriate. */ + if (last_examine_value) + { + /* Make last address examined available to the user as $_. Use + the correct pointer type. */ + struct type *pointer_type + = lookup_pointer_type (VALUE_TYPE (last_examine_value)); + set_internalvar (lookup_internalvar ("_"), + value_from_pointer (pointer_type, + last_examine_address)); + + /* Make contents of last address examined available to the user as $__. */ + /* If the last value has not been fetched from memory then don't + fetch it now - instead mark it by voiding the $__ variable. */ + if (VALUE_LAZY (last_examine_value)) + set_internalvar (lookup_internalvar ("__"), + allocate_value (builtin_type_void)); + else + set_internalvar (lookup_internalvar ("__"), last_examine_value); + } +} + + +/* Add an expression to the auto-display chain. + Specify the expression. */ + +static void +display_command (char *exp, int from_tty) +{ + struct format_data fmt; + register struct expression *expr; + register struct display *new; + int display_it = 1; + +#if defined(TUI) + if (tui_version && *exp == '$') + display_it = (tui_set_layout (exp) == TUI_FAILURE); +#endif + + if (display_it) + { + 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_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->enabled_p = 1; + display_chain = new; + + if (from_tty && target_has_execution) + do_one_display (new); + + dont_repeat (); + } +} + +static void +free_display (struct display *d) +{ + xfree (d->exp); + xfree (d); +} + +/* Clear out the display_chain. + Done when new symtabs are loaded, since this invalidates + the types stored in many expressions. */ + +void +clear_displays (void) +{ + register struct display *d; + + while ((d = display_chain) != NULL) + { + xfree (d->exp); + display_chain = d->next; + xfree (d); + } +} + +/* Delete the auto-display number NUM. */ + +static void +delete_display (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 (char *args, int from_tty) +{ + register char *p = args; + register char *p1; + register int num; + + 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 (struct display *d) +{ + int within_current_scope; + + if (d->enabled_p == 0) + return; + + if (d->block) + within_current_scope = contained_in (get_selected_block (0), d->block); + else + within_current_scope = 1; + if (!within_current_scope) + return; + + current_display_number = d->number; + + annotate_display_begin (); + printf_filtered ("%d", d->number); + annotate_display_number_end (); + printf_filtered (": "); + if (d->format.size) + { + CORE_ADDR addr; + struct value *val; + + annotate_display_format (); + + printf_filtered ("x/"); + if (d->format.count != 1) + printf_filtered ("%d", d->format.count); + printf_filtered ("%c", d->format.format); + if (d->format.format != 'i' && d->format.format != 's') + printf_filtered ("%c", d->format.size); + printf_filtered (" "); + + annotate_display_expression (); + + print_expression (d->exp, gdb_stdout); + annotate_display_expression_end (); + + if (d->format.count != 1) + printf_filtered ("\n"); + else + printf_filtered (" "); + + val = evaluate_expression (d->exp); + addr = value_as_address (val); + if (d->format.format == 'i') + addr = ADDR_BITS_REMOVE (addr); + + annotate_display_value (); + + do_examine (d->format, addr, VALUE_BFD_SECTION (val)); + } + else + { + annotate_display_format (); + + if (d->format.format) + printf_filtered ("/%c ", d->format.format); + + annotate_display_expression (); + + print_expression (d->exp, gdb_stdout); + annotate_display_expression_end (); + + printf_filtered (" = "); + + annotate_display_expression (); + + print_formatted (evaluate_expression (d->exp), + d->format.format, d->format.size, gdb_stdout); + printf_filtered ("\n"); + } + + annotate_display_end (); + + gdb_flush (gdb_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 (void) +{ + 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 +disable_display (int num) +{ + register struct display *d; + + for (d = display_chain; d; d = d->next) + if (d->number == num) + { + d->enabled_p = 0; + return; + } + printf_unfiltered ("No display number %d.\n", num); +} + +void +disable_current_display (void) +{ + if (current_display_number >= 0) + { + disable_display (current_display_number); + fprintf_unfiltered (gdb_stderr, "Disabling display %d to avoid infinite recursion.\n", + current_display_number); + } + current_display_number = -1; +} + +static void +display_info (char *ignore, int from_tty) +{ + register struct display *d; + + if (!display_chain) + printf_unfiltered ("There are no auto-display expressions now.\n"); + else + printf_filtered ("Auto-display expressions now in effect:\n\ +Num Enb Expression\n"); + + for (d = display_chain; d; d = d->next) + { + printf_filtered ("%d: %c ", d->number, "ny"[(int) d->enabled_p]); + if (d->format.size) + printf_filtered ("/%d%c%c ", d->format.count, d->format.size, + d->format.format); + else if (d->format.format) + printf_filtered ("/%c ", d->format.format); + print_expression (d->exp, gdb_stdout); + if (d->block && !contained_in (get_selected_block (0), d->block)) + printf_filtered (" (cannot be evaluated in the current context)"); + printf_filtered ("\n"); + gdb_flush (gdb_stdout); + } +} + +static void +enable_display (char *args, int from_tty) +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d = d->next) + d->enabled_p = 1; + } + 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->enabled_p = 1; + goto win; + } + printf_unfiltered ("No display number %d.\n", num); + win: + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} + +/* ARGSUSED */ +static void +disable_display_command (char *args, int from_tty) +{ + register char *p = args; + register char *p1; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d = d->next) + d->enabled_p = 0; + } + else + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + disable_display (atoi (p)); + + 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 (struct symbol *var, struct frame_info *frame, + struct ui_file *stream) +{ + struct value *val = read_var_value (var, frame); + + value_print (val, stream, 0, Val_pretty_default); +} + +/* 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). */ + +/* References here and elsewhere to "number of args according to the + stack frame" appear in all cases to refer to "number of ints of args + according to the stack frame". At least for VAX, i386, isi. */ + +void +print_frame_args (struct symbol *func, struct frame_info *fi, int num, + struct ui_file *stream) +{ + struct block *b = NULL; + int first = 1; + register int i; + register struct symbol *sym; + struct value *val; + /* Offset of next stack argument beyond the one we have seen that is + at the highest offset. + -1 if we haven't come to a stack argument yet. */ + long highest_offset = -1; + int arg_size; + /* Number of ints of arguments that we have printed so far. */ + int args_printed = 0; + struct cleanup *old_chain, *list_chain; + struct ui_stream *stb; + + stb = ui_out_stream_new (uiout); + old_chain = make_cleanup_ui_out_stream_delete (stb); + + if (func) + { + b = SYMBOL_BLOCK_VALUE (func); + /* Function blocks are order sensitive, and thus should not be + hashed. */ + gdb_assert (BLOCK_HASHTABLE (b) == 0); + + ALL_BLOCK_SYMBOLS (b, i, sym) + { + QUIT; + + /* Keep track of the highest stack argument offset seen, and + skip over any kinds of symbols we don't care about. */ + + switch (SYMBOL_CLASS (sym)) + { + case LOC_ARG: + case LOC_REF_ARG: + { + long current_offset = SYMBOL_VALUE (sym); + arg_size = TYPE_LENGTH (SYMBOL_TYPE (sym)); + + /* Compute address of next argument by adding the size of + this argument and rounding to an int boundary. */ + current_offset = + ((current_offset + arg_size + sizeof (int) - 1) + & ~(sizeof (int) - 1)); + + /* If this is the highest offset seen yet, set highest_offset. */ + if (highest_offset == -1 + || (current_offset > highest_offset)) + highest_offset = current_offset; + + /* Add the number of ints we're about to print to args_printed. */ + args_printed += (arg_size + sizeof (int) - 1) / sizeof (int); + } + + /* We care about types of symbols, but don't need to keep track of + stack offsets in them. */ + case LOC_REGPARM: + case LOC_REGPARM_ADDR: + case LOC_LOCAL_ARG: + case LOC_BASEREG_ARG: + break; + + /* Other types of symbols we just skip over. */ + default: + continue; + } + + /* We have to look up the symbol because arguments can have + two entries (one a parameter, one a local) and the one we + want is the local, which lookup_symbol will find for us. + This includes gcc1 (not gcc2) on the sparc when passing a + small structure and gcc2 when the argument type is float + and it is passed as a double and converted to float by + the prologue (in the latter case the type of the LOC_ARG + symbol is double and the type of the LOC_LOCAL symbol is + float). */ + /* But if the parameter name is null, don't try it. + Null parameter names occur on the RS/6000, for traceback tables. + FIXME, should we even print them? */ + + if (*SYMBOL_NAME (sym)) + { + struct symbol *nsym; + nsym = lookup_symbol + (SYMBOL_NAME (sym), + b, VAR_NAMESPACE, (int *) NULL, (struct symtab **) NULL); + if (SYMBOL_CLASS (nsym) == LOC_REGISTER) + { + /* There is a LOC_ARG/LOC_REGISTER pair. This means that + it was passed on the stack and loaded into a register, + or passed in a register and stored in a stack slot. + GDB 3.x used the LOC_ARG; GDB 4.0-4.11 used the LOC_REGISTER. + + Reasons for using the LOC_ARG: + (1) because find_saved_registers may be slow for remote + debugging, + (2) because registers are often re-used and stack slots + rarely (never?) are. Therefore using the stack slot is + much less likely to print garbage. + + Reasons why we might want to use the LOC_REGISTER: + (1) So that the backtrace prints the same value as + "print foo". I see no compelling reason why this needs + to be the case; having the backtrace print the value which + was passed in, and "print foo" print the value as modified + within the called function, makes perfect sense to me. + + Additional note: It might be nice if "info args" displayed + both values. + One more note: There is a case with sparc structure passing + where we need to use the LOC_REGISTER, but this is dealt with + by creating a single LOC_REGPARM in symbol reading. */ + + /* Leave sym (the LOC_ARG) alone. */ + ; + } + else + sym = nsym; + } + + /* Print the current arg. */ + if (!first) + ui_out_text (uiout, ", "); + ui_out_wrap_hint (uiout, " "); + + annotate_arg_begin (); + + list_chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); + fprintf_symbol_filtered (stb->stream, SYMBOL_SOURCE_NAME (sym), + SYMBOL_LANGUAGE (sym), DMGL_PARAMS | DMGL_ANSI); + ui_out_field_stream (uiout, "name", stb); + annotate_arg_name_end (); + ui_out_text (uiout, "="); + + /* Avoid value_print because it will deref ref parameters. We just + want to print their addresses. Print ??? for args whose address + we do not know. We pass 2 as "recurse" to val_print because our + standard indentation here is 4 spaces, and val_print indents + 2 for each recurse. */ + val = read_var_value (sym, fi); + + annotate_arg_value (val == NULL ? NULL : VALUE_TYPE (val)); + + if (val) + { + val_print (VALUE_TYPE (val), VALUE_CONTENTS (val), 0, + VALUE_ADDRESS (val), + stb->stream, 0, 0, 2, Val_no_prettyprint); + ui_out_field_stream (uiout, "value", stb); + } + else + ui_out_text (uiout, "???"); + + /* Invoke ui_out_tuple_end. */ + do_cleanups (list_chain); + + annotate_arg_end (); + + first = 0; + } + } + + /* Don't print nameless args in situations where we don't know + enough about the stack to find them. */ + if (num != -1) + { + long start; + + if (highest_offset == -1) + start = FRAME_ARGS_SKIP; + else + start = highest_offset; + + print_frame_nameless_args (fi, start, num - args_printed, + first, stream); + } + do_cleanups (old_chain); +} + +/* Print nameless args on STREAM. + FI is the frameinfo for this frame, START is the offset + of the first nameless arg, and NUM is the number of nameless args to + print. FIRST is nonzero if this is the first argument (not just + the first nameless arg). */ + +static void +print_frame_nameless_args (struct frame_info *fi, long start, int num, + int first, struct ui_file *stream) +{ + int i; + CORE_ADDR argsaddr; + long arg_value; + + for (i = 0; i < num; i++) + { + QUIT; +#ifdef NAMELESS_ARG_VALUE + NAMELESS_ARG_VALUE (fi, start, &arg_value); +#else + argsaddr = FRAME_ARGS_ADDRESS (fi); + if (!argsaddr) + return; + + arg_value = read_memory_integer (argsaddr + start, sizeof (int)); +#endif + + if (!first) + fprintf_filtered (stream, ", "); + +#ifdef PRINT_NAMELESS_INTEGER + PRINT_NAMELESS_INTEGER (stream, arg_value); +#else +#ifdef PRINT_TYPELESS_INTEGER + PRINT_TYPELESS_INTEGER (stream, builtin_type_int, (LONGEST) arg_value); +#else + fprintf_filtered (stream, "%ld", arg_value); +#endif /* PRINT_TYPELESS_INTEGER */ +#endif /* PRINT_NAMELESS_INTEGER */ + first = 0; + start += sizeof (int); + } +} + +/* ARGSUSED */ +static void +printf_command (char *arg, int from_tty) +{ + register char *f = NULL; + register char *s = arg; + char *string = NULL; + struct value **val_args; + char *substrings; + char *current_substring; + int nargs = 0; + int allocated_args = 20; + struct cleanup *old_cleanups; + + val_args = (struct value **) xmalloc (allocated_args + * sizeof (struct value *)); + old_cleanups = make_cleanup (free_current_contents, &val_args); + + 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 '\"'."); + + case '\\': + switch (c = *s++) + { + case '\\': + *f++ = '\\'; + break; + case 'a': + *f++ = '\a'; + break; + case 'b': + *f++ = '\b'; + break; + case 'f': + *f++ = '\f'; + break; + case 'n': + *f++ = '\n'; + break; + case 'r': + *f++ = '\r'; + break; + case 't': + *f++ = '\t'; + break; + case 'v': + *f++ = '\v'; + break; + case '"': + *f++ = '"'; + break; + default: + /* ??? TODO: handle other escape sequences */ + error ("Unrecognized escape character \\%c in format string.", + c); + } + 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++; + + /* Need extra space for the '\0's. Doubling the size is sufficient. */ + substrings = alloca (strlen (string) * 2); + current_substring = substrings; + + { + /* Now scan the string for %-specs and see what kinds of args they want. + argclass[I] classifies the %-specs so we can give printf_filtered + something of the right size. */ + + enum argclass + { + no_arg, int_arg, string_arg, double_arg, long_long_arg + }; + enum argclass *argclass; + enum argclass this_argclass; + char *last_arg; + int nargs_wanted; + int lcount; + int i; + + argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass); + nargs_wanted = 0; + f = string; + last_arg = string; + while (*f) + if (*f++ == '%') + { + lcount = 0; + while (strchr ("0123456789.hlL-+ #", *f)) + { + if (*f == 'l' || *f == 'L') + lcount++; + f++; + } + switch (*f) + { + case 's': + this_argclass = string_arg; + break; + + case 'e': + case 'f': + case 'g': + this_argclass = double_arg; + break; + + case '*': + error ("`*' not supported for precision or width in printf"); + + case 'n': + error ("Format specifier `n' not supported in printf"); + + case '%': + this_argclass = no_arg; + break; + + default: + if (lcount > 1) + this_argclass = long_long_arg; + else + this_argclass = int_arg; + break; + } + f++; + if (this_argclass != no_arg) + { + strncpy (current_substring, last_arg, f - last_arg); + current_substring += f - last_arg; + *current_substring++ = '\0'; + last_arg = f; + argclass[nargs_wanted++] = this_argclass; + } + } + + /* Now, parse all arguments and evaluate them. + Store the VALUEs in VAL_ARGS. */ + + while (*s != '\0') + { + char *s1; + if (nargs == allocated_args) + val_args = (struct value **) xrealloc ((char *) val_args, + (allocated_args *= 2) + * sizeof (struct 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] == double_arg) + { + struct type *type = VALUE_TYPE (val_args[nargs]); + if (TYPE_LENGTH (type) == sizeof (float)) + VALUE_TYPE (val_args[nargs]) = builtin_type_float; + if (TYPE_LENGTH (type) == 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 actually print them. */ + current_substring = substrings; + for (i = 0; i < nargs; i++) + { + switch (argclass[i]) + { + case string_arg: + { + char *str; + CORE_ADDR tem; + int j; + tem = value_as_address (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); + if (j != 0) + read_memory (tem, str, j); + str[j] = 0; + + printf_filtered (current_substring, str); + } + break; + case double_arg: + { + double val = value_as_double (val_args[i]); + printf_filtered (current_substring, val); + break; + } + case long_long_arg: +#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG) + { + long long val = value_as_long (val_args[i]); + printf_filtered (current_substring, val); + break; + } +#else + error ("long long not supported in printf"); +#endif + case int_arg: + { + /* FIXME: there should be separate int_arg and long_arg. */ + long val = value_as_long (val_args[i]); + printf_filtered (current_substring, val); + break; + } + default: /* purecov: deadcode */ + error ("internal error in printf_command"); /* purecov: deadcode */ + } + /* Skip to the next substring. */ + current_substring += strlen (current_substring) + 1; + } + /* Print the portion of the format string after the last argument. */ + printf_filtered (last_arg); + } + do_cleanups (old_cleanups); +} + +/* Dump a specified section of assembly code. With no command line + arguments, this command will dump the assembly code for the + function surrounding the pc value in the selected frame. With one + argument, it will dump the assembly code surrounding that pc value. + Two arguments are interpeted as bounds within which to dump + assembly. */ + +/* ARGSUSED */ +static void +disassemble_command (char *arg, int from_tty) +{ + CORE_ADDR low, high; + char *name; + CORE_ADDR pc, pc_masked; + char *space_index; +#if 0 + asection *section; +#endif + + name = NULL; + if (!arg) + { + if (!selected_frame) + error ("No frame selected.\n"); + + pc = get_frame_pc (selected_frame); + if (find_pc_partial_function (pc, &name, &low, &high) == 0) + error ("No function contains program counter for selected frame.\n"); +#if defined(TUI) + else if (tui_version) + low = tuiGetLowDisassemblyAddress (low, pc); +#endif + low += FUNCTION_START_OFFSET; + } + else if (!(space_index = (char *) strchr (arg, ' '))) + { + /* One argument. */ + pc = parse_and_eval_address (arg); + if (find_pc_partial_function (pc, &name, &low, &high) == 0) + error ("No function contains specified address.\n"); +#if defined(TUI) + else if (tui_version) + low = tuiGetLowDisassemblyAddress (low, pc); +#endif + low += FUNCTION_START_OFFSET; + } + else + { + /* Two arguments. */ + *space_index = '\0'; + low = parse_and_eval_address (arg); + high = parse_and_eval_address (space_index + 1); + } + +#if defined(TUI) + if (!tui_is_window_visible (DISASSEM_WIN)) +#endif + { + printf_filtered ("Dump of assembler code "); + if (name != NULL) + { + printf_filtered ("for function %s:\n", name); + } + else + { + printf_filtered ("from "); + print_address_numeric (low, 1, gdb_stdout); + printf_filtered (" to "); + print_address_numeric (high, 1, gdb_stdout); + printf_filtered (":\n"); + } + + /* Dump the specified range. */ + pc = low; + +#ifdef GDB_TARGET_MASK_DISAS_PC + pc_masked = GDB_TARGET_MASK_DISAS_PC (pc); +#else + pc_masked = pc; +#endif + + while (pc_masked < high) + { + QUIT; + print_address (pc_masked, gdb_stdout); + printf_filtered (":\t"); + /* We often wrap here if there are long symbolic names. */ + wrap_here (" "); + pc += print_insn (pc, gdb_stdout); + printf_filtered ("\n"); + +#ifdef GDB_TARGET_MASK_DISAS_PC + pc_masked = GDB_TARGET_MASK_DISAS_PC (pc); +#else + pc_masked = pc; +#endif + } + printf_filtered ("End of assembler dump.\n"); + gdb_flush (gdb_stdout); + } +#if defined(TUI) + else + { + tui_show_assembly (low); + } +#endif +} + +/* Print the instruction at address MEMADDR in debugged memory, + on STREAM. Returns length of the instruction, in bytes. */ + +static int +print_insn (CORE_ADDR memaddr, struct ui_file *stream) +{ + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + TARGET_PRINT_INSN_INFO->endian = BFD_ENDIAN_BIG; + else + TARGET_PRINT_INSN_INFO->endian = BFD_ENDIAN_LITTLE; + + if (TARGET_ARCHITECTURE != NULL) + TARGET_PRINT_INSN_INFO->mach = TARGET_ARCHITECTURE->mach; + /* else: should set .mach=0 but some disassemblers don't grok this */ + + TARGET_PRINT_INSN_INFO->stream = stream; + + return TARGET_PRINT_INSN (memaddr, TARGET_PRINT_INSN_INFO); +} + + +void +_initialize_printcmd (void) +{ + struct cmd_list_element *c; + + current_display_number = -1; + + add_info ("address", address_info, + "Describe where symbol SYM is stored."); + + add_info ("symbol", sym_info, + "Describe what symbol is at location ADDR.\n\ +Only for symbols with fixed locations (global or static scope)."); + + add_com ("x", class_vars, x_command, + concat ("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\ + t(binary), 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\ +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\".", NULL)); + + c = add_com ("disassemble", class_vars, disassemble_command, + "Disassemble a specified section of memory.\n\ +Default is the function surrounding the pc of the selected frame.\n\ +With a single argument, the function surrounding that address is dumped.\n\ +Two arguments are taken as a range of memory to dump."); + set_cmd_completer (c, location_completer); + if (xdb_commands) + add_com_alias ("va", "disassemble", class_xdb, 0); + +#if 0 + add_com ("whereis", class_vars, whereis_command, + "Print line number and file of definition of variable."); +#endif + + add_info ("display", display_info, + "Expressions to display when program stops, with code numbers."); + + add_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_command, + "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, + concat ("Evaluate expression EXP and assign result to variable VAR, using assignment\n\ +syntax appropriate for the current language (VAR = EXP or VAR := EXP for\n\ +example). VAR may be a debugger \"convenience\" variable (names starting\n\ +with $), a register (a few standard names starting with $), or an actual\n\ +variable in the program being debugged. EXP is any valid 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.\n\ +You can see these environment settings with the \"show\" command.", NULL), + &setlist, "set ", 1, &cmdlist); + if (dbx_commands) + add_com ("assign", class_vars, set_command, concat ("Evaluate expression \ +EXP and assign result to variable VAR, using assignment\n\ +syntax appropriate for the current language (VAR = EXP or VAR := EXP for\n\ +example). VAR may be a debugger \"convenience\" variable (names starting\n\ +with $), a register (a few standard names starting with $), or an actual\n\ +variable in the program being debugged. EXP is any valid 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.\n\ +You can see these environment settings with the \"show\" command.", NULL)); + + /* "call" is the same as "set", but handy for dbx users to call fns. */ + c = add_com ("call", class_vars, call_command, + "Call a function in the program.\n\ +The argument is the function name and arguments, in the notation of the\n\ +current working language. The result is printed and saved in the value\n\ +history, if it is not void."); + set_cmd_completer (c, location_completer); + + add_cmd ("variable", class_vars, set_command, + "Evaluate expression EXP and assign result to variable VAR, using assignment\n\ +syntax appropriate for the current language (VAR = EXP or VAR := EXP for\n\ +example). VAR may be a debugger \"convenience\" variable (names starting\n\ +with $), a register (a few standard names starting with $), or an actual\n\ +variable in the program being debugged. EXP is any valid expression.\n\ +This may usually be abbreviated to simply \"set\".", + &setlist); + + c = 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).", NULL)); + set_cmd_completer (c, location_completer); + add_com_alias ("p", "print", class_vars, 1); + + c = add_com ("inspect", class_vars, inspect_command, + "Same as \"print\" command, except that if you are running in the epoch\n\ +environment, the value is printed in its own window."); + set_cmd_completer (c, location_completer); + + add_show_from_set ( + add_set_cmd ("max-symbolic-offset", no_class, var_uinteger, + (char *) &max_symbolic_offset, + "Set the largest offset that will be printed in form.", + &setprintlist), + &showprintlist); + add_show_from_set ( + add_set_cmd ("symbol-filename", no_class, var_boolean, + (char *) &print_symbol_filename, + "Set printing of source filename and line number with .", + &setprintlist), + &showprintlist); + + /* For examine/instruction a single byte quantity is specified as + the data. This avoids problems with value_at_lazy() requiring a + valid data type (and rejecting VOID). */ + examine_i_type = init_type (TYPE_CODE_INT, 1, 0, "examine_i_type", NULL); + + examine_b_type = init_type (TYPE_CODE_INT, 1, 0, "examine_b_type", NULL); + examine_h_type = init_type (TYPE_CODE_INT, 2, 0, "examine_h_type", NULL); + examine_w_type = init_type (TYPE_CODE_INT, 4, 0, "examine_w_type", NULL); + examine_g_type = init_type (TYPE_CODE_INT, 8, 0, "examine_g_type", NULL); + +} diff --git a/gdb/stack.c b/gdb/stack.c new file mode 100644 index 0000000..4f41531 --- /dev/null +++ b/gdb/stack.c @@ -0,0 +1,2023 @@ +/* Print and select stack frames for GDB, the GNU debugger. + + Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, + 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 Free Software + Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include "defs.h" +#include "gdb_string.h" +#include "value.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "expression.h" +#include "language.h" +#include "frame.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "target.h" +#include "source.h" +#include "breakpoint.h" +#include "demangle.h" +#include "inferior.h" +#include "annotate.h" +#include "ui-out.h" + +/* Prototypes for exported functions. */ + +void args_info (char *, int); + +void locals_info (char *, int); + +void (*selected_frame_level_changed_hook) (int); + +void _initialize_stack (void); + +/* Prototypes for local functions. */ + +static void return_command (char *, int); + +static void down_command (char *, int); + +static void down_silently_base (char *); + +static void down_silently_command (char *, int); + +static void up_command (char *, int); + +static void up_silently_base (char *); + +static void up_silently_command (char *, int); + +void frame_command (char *, int); + +static void current_frame_command (char *, int); + +static void select_frame_command (char *, int); + +static void print_frame_arg_vars (struct frame_info *, struct ui_file *); + +static void catch_info (char *, int); + +static void args_plus_locals_info (char *, int); + +static void print_frame_label_vars (struct frame_info *, int, + struct ui_file *); + +static void print_frame_local_vars (struct frame_info *, int, + struct ui_file *); + +static int print_block_frame_labels (struct block *, int *, + struct ui_file *); + +static int print_block_frame_locals (struct block *, + struct frame_info *, + int, + struct ui_file *); + +static void print_frame (struct frame_info *fi, + int level, + int source, + int args, + struct symtab_and_line sal); + +static void print_frame_info_base (struct frame_info *, int, int, int); + +static void print_stack_frame_base (struct frame_info *, int, int); + +static void backtrace_command (char *, int); + +struct frame_info *parse_frame_specification (char *); + +static void frame_info (char *, int); + +extern int addressprint; /* Print addresses, or stay symbolic only? */ + +/* The "selected" stack frame is used by default for local and arg access. + May be zero, for no selected frame. */ + +struct frame_info *selected_frame; + +/* Level of the selected frame: + 0 for innermost, 1 for its caller, ... + or -1 for frame specified by address with no defined level. */ + +/* Level of the selected frame: 0 for innermost, 1 for its caller, ... + or -1 for NULL frame. */ + +int +frame_relative_level (struct frame_info *fi) +{ + if (fi == NULL) + return -1; + else + return fi->level; +} + +/* Zero means do things normally; we are interacting directly with the + user. One means print the full filename and linenumber when a + frame is printed, and do so in a format emacs18/emacs19.22 can + parse. Two means print similar annotations, but in many more + cases and in a slightly different syntax. */ + +int annotation_level = 0; + + +struct print_stack_frame_args + { + struct frame_info *fi; + int level; + int source; + int args; + }; + +static int print_stack_frame_base_stub (char *); + +/* Show and print the frame arguments. + Pass the args the way catch_errors wants them. */ +static int show_and_print_stack_frame_stub (void *args); +static int +show_and_print_stack_frame_stub (void *args) +{ + struct print_stack_frame_args *p = (struct print_stack_frame_args *) args; + + print_frame_info (p->fi, p->level, p->source, p->args); + + return 0; +} + +/* Show or print the frame arguments. + Pass the args the way catch_errors wants them. */ +static int print_stack_frame_stub (void *args); +static int +print_stack_frame_stub (void *args) +{ + struct print_stack_frame_args *p = (struct print_stack_frame_args *) args; + + print_frame_info_base (p->fi, p->level, p->source, p->args); + return 0; +} + +/* Print a stack frame briefly. FRAME_INFI should be the frame info + and LEVEL should be its level in the stack (or -1 for level not + defined). */ + +/* Pass the args the way catch_errors wants them. */ +static int +print_stack_frame_base_stub (char *args) +{ + struct print_stack_frame_args *p = (struct print_stack_frame_args *) args; + + print_frame_info_base (p->fi, p->level, p->source, p->args); + return 0; +} + +/* print the frame arguments to the terminal. + Pass the args the way catch_errors wants them. */ +static int print_only_stack_frame_stub (void *); +static int +print_only_stack_frame_stub (void *args) +{ + struct print_stack_frame_args *p = (struct print_stack_frame_args *) args; + + print_frame_info_base (p->fi, p->level, p->source, p->args); + return 0; +} + +/* Print a stack frame briefly. FRAME_INFI should be the frame info + and LEVEL should be its level in the stack (or -1 for level not defined). + This prints the level, the function executing, the arguments, + and the file name and line number. + If the pc is not at the beginning of the source line, + the actual pc is printed at the beginning. + + If SOURCE is 1, print the source line as well. + If SOURCE is -1, print ONLY the source line. */ + +static void +print_stack_frame_base (struct frame_info *fi, int level, int source) +{ + struct print_stack_frame_args args; + + args.fi = fi; + args.level = level; + args.source = source; + args.args = 1; + + catch_errors (print_stack_frame_stub, &args, "", RETURN_MASK_ALL); +} + +/* Show and print a stack frame briefly. FRAME_INFI should be the frame info + and LEVEL should be its level in the stack (or -1 for level not defined). + This prints the level, the function executing, the arguments, + and the file name and line number. + If the pc is not at the beginning of the source line, + the actual pc is printed at the beginning. + + If SOURCE is 1, print the source line as well. + If SOURCE is -1, print ONLY the source line. */ + +void +show_and_print_stack_frame (struct frame_info *fi, int level, int source) +{ + struct print_stack_frame_args args; + + args.fi = fi; + args.level = level; + args.source = source; + args.args = 1; + + catch_errors (show_and_print_stack_frame_stub, &args, "", RETURN_MASK_ALL); +} + + +/* Show or print a stack frame briefly. FRAME_INFI should be the frame info + and LEVEL should be its level in the stack (or -1 for level not defined). + This prints the level, the function executing, the arguments, + and the file name and line number. + If the pc is not at the beginning of the source line, + the actual pc is printed at the beginning. + + If SOURCE is 1, print the source line as well. + If SOURCE is -1, print ONLY the source line. */ + +void +print_stack_frame (struct frame_info *fi, int level, int source) +{ + struct print_stack_frame_args args; + + args.fi = fi; + args.level = level; + args.source = source; + args.args = 1; + + catch_errors (print_stack_frame_stub, (char *) &args, "", RETURN_MASK_ALL); +} + +/* Print a stack frame briefly. FRAME_INFI should be the frame info + and LEVEL should be its level in the stack (or -1 for level not defined). + This prints the level, the function executing, the arguments, + and the file name and line number. + If the pc is not at the beginning of the source line, + the actual pc is printed at the beginning. + + If SOURCE is 1, print the source line as well. + If SOURCE is -1, print ONLY the source line. */ + +void +print_only_stack_frame (struct frame_info *fi, int level, int source) +{ + struct print_stack_frame_args args; + + args.fi = fi; + args.level = level; + args.source = source; + args.args = 1; + + catch_errors (print_only_stack_frame_stub, &args, "", RETURN_MASK_ALL); +} + +struct print_args_args +{ + struct symbol *func; + struct frame_info *fi; + struct ui_file *stream; +}; + +static int print_args_stub (PTR); + +/* Pass the args the way catch_errors wants them. */ + +static int +print_args_stub (PTR args) +{ + int numargs; + struct print_args_args *p = (struct print_args_args *) args; + + numargs = FRAME_NUM_ARGS (p->fi); + print_frame_args (p->func, p->fi, numargs, p->stream); + return 0; +} + +/* Print information about a frame for frame "fi" at level "level". + Used in "where" output, also used to emit breakpoint or step + messages. + LEVEL is the level of the frame, or -1 if it is the + innermost frame but we don't want to print the level. + The meaning of the SOURCE argument is: + SRC_LINE: Print only source line + LOCATION: Print only location + LOC_AND_SRC: Print location and source line. */ + +static void +print_frame_info_base (struct frame_info *fi, int level, int source, int args) +{ + struct symtab_and_line sal; + int source_print; + int location_print; + +#if 0 + char buf[MAX_REGISTER_RAW_SIZE]; + CORE_ADDR sp; + + /* On the 68k, this spends too much time in m68k_find_saved_regs. */ + + /* Get the value of SP_REGNUM relative to the frame. */ + get_saved_register (buf, (int *) NULL, (CORE_ADDR *) NULL, + FRAME_INFO_ID (fi), SP_REGNUM, (enum lval_type *) NULL); + sp = extract_address (buf, REGISTER_RAW_SIZE (SP_REGNUM)); + + /* This is not a perfect test, because if a function alloca's some + memory, puts some code there, and then jumps into it, then the test + will succeed even though there is no call dummy. Probably best is + to check for a bp_call_dummy breakpoint. */ + if (PC_IN_CALL_DUMMY (fi->pc, sp, fi->frame)) +#else + if (frame_in_dummy (fi)) +#endif + { + annotate_frame_begin (level == -1 ? 0 : level, fi->pc); + + /* Do this regardless of SOURCE because we don't have any source + to list for this frame. */ + if (level >= 0) + printf_filtered ("#%-2d ", level); + annotate_function_call (); + printf_filtered ("\n"); + annotate_frame_end (); + return; + } + if (fi->signal_handler_caller) + { + annotate_frame_begin (level == -1 ? 0 : level, fi->pc); + + /* Do this regardless of SOURCE because we don't have any source + to list for this frame. */ + if (level >= 0) + printf_filtered ("#%-2d ", level); + annotate_signal_handler_caller (); + printf_filtered ("\n"); + annotate_frame_end (); + return; + } + + /* If fi is not the innermost frame, that normally means that fi->pc + points to *after* the call instruction, and we want to get the line + containing the call, never the next line. But if the next frame is + a signal_handler_caller or a dummy frame, then the next frame was + not entered as the result of a call, and we want to get the line + containing fi->pc. */ + sal = + find_pc_line (fi->pc, + fi->next != NULL + && !fi->next->signal_handler_caller + && !frame_in_dummy (fi->next)); + + location_print = (source == LOCATION + || source == LOC_AND_ADDRESS + || source == SRC_AND_LOC); + + if (location_print || !sal.symtab) + print_frame (fi, level, source, args, sal); + + source_print = (source == SRC_LINE || source == SRC_AND_LOC); + + if (sal.symtab) + set_current_source_symtab_and_line (&sal); + + if (source_print && sal.symtab) + { + struct symtab_and_line cursal; + int done = 0; + int mid_statement = (source == SRC_LINE) && (fi->pc != sal.pc); + + if (annotation_level) + done = identify_source_line (sal.symtab, sal.line, mid_statement, + fi->pc); + if (!done) + { + if (print_frame_info_listing_hook) + print_frame_info_listing_hook (sal.symtab, sal.line, sal.line + 1, 0); + else + { + /* We used to do this earlier, but that is clearly + wrong. This function is used by many different + parts of gdb, including normal_stop in infrun.c, + which uses this to print out the current PC + when we stepi/nexti into the middle of a source + line. Only the command line really wants this + behavior. Other UIs probably would like the + ability to decide for themselves if it is desired. */ + if (addressprint && mid_statement) + { + ui_out_field_core_addr (uiout, "addr", fi->pc); + ui_out_text (uiout, "\t"); + } + + print_source_lines (sal.symtab, sal.line, sal.line + 1, 0); + } + } + cursal = get_current_or_default_source_symtab_and_line (); + cursal.line = max (sal.line - get_lines_to_list () / 2, 1); + set_current_source_symtab_and_line (&cursal); + } + + if (source != 0) + set_default_breakpoint (1, fi->pc, sal.symtab, sal.line); + + annotate_frame_end (); + + gdb_flush (gdb_stdout); +} + +static void +print_frame (struct frame_info *fi, + int level, + int source, + int args, + struct symtab_and_line sal) +{ + struct symbol *func; + register char *funname = 0; + enum language funlang = language_unknown; + struct ui_stream *stb; + struct cleanup *old_chain; + struct cleanup *list_chain; + + stb = ui_out_stream_new (uiout); + old_chain = make_cleanup_ui_out_stream_delete (stb); + + func = find_pc_function (frame_address_in_block (fi)); + if (func) + { + /* In certain pathological cases, the symtabs give the wrong + function (when we are in the first function in a file which + is compiled without debugging symbols, the previous function + is compiled with debugging symbols, and the "foo.o" symbol + that is supposed to tell us where the file with debugging symbols + ends has been truncated by ar because it is longer than 15 + characters). This also occurs if the user uses asm() to create + a function but not stabs for it (in a file compiled -g). + + So look in the minimal symbol tables as well, and if it comes + up with a larger address for the function use that instead. + I don't think this can ever cause any problems; there shouldn't + be any minimal symbols in the middle of a function; if this is + ever changed many parts of GDB will need to be changed (and we'll + create a find_pc_minimal_function or some such). */ + + struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (frame_address_in_block (fi)); + if (msymbol != NULL + && (SYMBOL_VALUE_ADDRESS (msymbol) + > BLOCK_START (SYMBOL_BLOCK_VALUE (func)))) + { +#if 0 + /* There is no particular reason to think the line number + information is wrong. Someone might have just put in + a label with asm() but left the line numbers alone. */ + /* In this case we have no way of knowing the source file + and line number, so don't print them. */ + sal.symtab = 0; +#endif + /* We also don't know anything about the function besides + its address and name. */ + func = 0; + funname = SYMBOL_NAME (msymbol); + funlang = SYMBOL_LANGUAGE (msymbol); + } + else + { + /* I'd like to use SYMBOL_SOURCE_NAME() here, to display the + demangled name that we already have stored in the symbol + table, but we stored a version with DMGL_PARAMS turned + on, and here we don't want to display parameters. So call + the demangler again, with DMGL_ANSI only. (Yes, I know + that printf_symbol_filtered() will again try to demangle + the name on the fly, but the issue is that if + cplus_demangle() fails here, it'll fail there too. So we + want to catch the failure ("demangled==NULL" case below) + here, while we still have our hands on the function + symbol.) */ + char *demangled; + funname = SYMBOL_NAME (func); + funlang = SYMBOL_LANGUAGE (func); + if (funlang == language_cplus) + { + demangled = cplus_demangle (funname, DMGL_ANSI); + if (demangled == NULL) + /* If the demangler fails, try the demangled name from + the symbol table. This'll have parameters, but + that's preferable to diplaying a mangled name. */ + funname = SYMBOL_SOURCE_NAME (func); + } + } + } + else + { + struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (frame_address_in_block (fi)); + if (msymbol != NULL) + { + funname = SYMBOL_NAME (msymbol); + funlang = SYMBOL_LANGUAGE (msymbol); + } + } + + annotate_frame_begin (level == -1 ? 0 : level, fi->pc); + + list_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "frame"); + + if (level >= 0) + { + ui_out_text (uiout, "#"); + ui_out_field_fmt (uiout, "level", "%-2d", level); + ui_out_spaces (uiout, 1); + } + if (addressprint) + if (fi->pc != sal.pc || !sal.symtab || source == LOC_AND_ADDRESS) + { + annotate_frame_address (); + ui_out_field_core_addr (uiout, "addr", fi->pc); + annotate_frame_address_end (); + ui_out_text (uiout, " in "); + } + annotate_frame_function_name (); + fprintf_symbol_filtered (stb->stream, funname ? funname : "??", funlang, + DMGL_ANSI); + ui_out_field_stream (uiout, "func", stb); + ui_out_wrap_hint (uiout, " "); + annotate_frame_args (); + + ui_out_text (uiout, " ("); + if (args) + { + struct print_args_args args; + struct cleanup *args_list_chain; + args.fi = fi; + args.func = func; + args.stream = gdb_stdout; + args_list_chain = make_cleanup_ui_out_list_begin_end (uiout, "args"); + catch_errors (print_args_stub, &args, "", RETURN_MASK_ALL); + /* FIXME: args must be a list. If one argument is a string it will + have " that will not be properly escaped. */ + /* Invoke ui_out_tuple_end. */ + do_cleanups (args_list_chain); + QUIT; + } + ui_out_text (uiout, ")"); + if (sal.symtab && sal.symtab->filename) + { + annotate_frame_source_begin (); + ui_out_wrap_hint (uiout, " "); + ui_out_text (uiout, " at "); + annotate_frame_source_file (); + ui_out_field_string (uiout, "file", sal.symtab->filename); + annotate_frame_source_file_end (); + ui_out_text (uiout, ":"); + annotate_frame_source_line (); + ui_out_field_int (uiout, "line", sal.line); + annotate_frame_source_end (); + } + +#ifdef PC_SOLIB + if (!funname || (!sal.symtab || !sal.symtab->filename)) + { + char *lib = PC_SOLIB (fi->pc); + if (lib) + { + annotate_frame_where (); + ui_out_wrap_hint (uiout, " "); + ui_out_text (uiout, " from "); + ui_out_field_string (uiout, "from", lib); + } + } +#endif /* PC_SOLIB */ + + /* do_cleanups will call ui_out_tuple_end() for us. */ + do_cleanups (list_chain); + ui_out_text (uiout, "\n"); + do_cleanups (old_chain); +} + + +/* Show or print the frame info. If this is the tui, it will be shown in + the source display */ +void +print_frame_info (struct frame_info *fi, register int level, int source, + int args) +{ + print_frame_info_base (fi, level, source, args); +} + +/* Show the frame info. If this is the tui, it will be shown in + the source display otherwise, nothing is done */ +void +show_stack_frame (struct frame_info *fi) +{ +} + + +/* Read a frame specification in whatever the appropriate format is. + Call error() if the specification is in any way invalid (i.e. + this function never returns NULL). */ + +struct frame_info * +parse_frame_specification (char *frame_exp) +{ + int numargs = 0; +#define MAXARGS 4 + CORE_ADDR args[MAXARGS]; + int level; + + if (frame_exp) + { + char *addr_string, *p; + struct cleanup *tmp_cleanup; + + while (*frame_exp == ' ') + frame_exp++; + + while (*frame_exp) + { + if (numargs > MAXARGS) + error ("Too many args in frame specification"); + /* Parse an argument. */ + for (p = frame_exp; *p && *p != ' '; p++) + ; + addr_string = savestring (frame_exp, p - frame_exp); + + { + struct value *vp; + + tmp_cleanup = make_cleanup (xfree, addr_string); + + /* NOTE: we call parse_and_eval and then both + value_as_long and value_as_address rather than calling + parse_and_eval_long and parse_and_eval_address because + of the issue of potential side effects from evaluating + the expression. */ + vp = parse_and_eval (addr_string); + if (numargs == 0) + level = value_as_long (vp); + + args[numargs++] = value_as_address (vp); + do_cleanups (tmp_cleanup); + } + + /* Skip spaces, move to possible next arg. */ + while (*p == ' ') + p++; + frame_exp = p; + } + } + + switch (numargs) + { + case 0: + if (selected_frame == NULL) + error ("No selected frame."); + return selected_frame; + /* NOTREACHED */ + case 1: + { + struct frame_info *fid = + find_relative_frame (get_current_frame (), &level); + struct frame_info *tfid; + + if (level == 0) + /* find_relative_frame was successful */ + return fid; + + /* If SETUP_ARBITRARY_FRAME is defined, then frame specifications + take at least 2 addresses. It is important to detect this case + here so that "frame 100" does not give a confusing error message + like "frame specification requires two addresses". This of course + does not solve the "frame 100" problem for machines on which + a frame specification can be made with one address. To solve + that, we need a new syntax for a specifying a frame by address. + I think the cleanest syntax is $frame(0x45) ($frame(0x23,0x45) for + two args, etc.), but people might think that is too much typing, + so I guess *0x23,0x45 would be a possible alternative (commas + really should be used instead of spaces to delimit; using spaces + normally works in an expression). */ +#ifdef SETUP_ARBITRARY_FRAME + error ("No frame %s", paddr_d (args[0])); +#endif + + /* If (s)he specifies the frame with an address, he deserves what + (s)he gets. Still, give the highest one that matches. */ + + for (fid = get_current_frame (); + fid && fid->frame != args[0]; + fid = get_prev_frame (fid)) + ; + + if (fid) + while ((tfid = get_prev_frame (fid)) && + (tfid->frame == args[0])) + fid = tfid; + + /* We couldn't identify the frame as an existing frame, but + perhaps we can create one with a single argument. */ + } + + default: +#ifdef SETUP_ARBITRARY_FRAME + return SETUP_ARBITRARY_FRAME (numargs, args); +#else + /* Usual case. Do it here rather than have everyone supply + a SETUP_ARBITRARY_FRAME that does this. */ + if (numargs == 1) + return create_new_frame (args[0], 0); + error ("Too many args in frame specification"); +#endif + /* NOTREACHED */ + } + /* NOTREACHED */ +} + +/* FRAME_ARGS_ADDRESS_CORRECT is just like FRAME_ARGS_ADDRESS except + that if it is unsure about the answer, it returns 0 + instead of guessing (this happens on the VAX and i960, for example). + + On most machines, we never have to guess about the args address, + so FRAME_ARGS_ADDRESS{,_CORRECT} are the same. */ +#if !defined (FRAME_ARGS_ADDRESS_CORRECT) +#define FRAME_ARGS_ADDRESS_CORRECT FRAME_ARGS_ADDRESS +#endif + +/* Print verbosely the selected frame or the frame at address ADDR. + This means absolutely all information in the frame is printed. */ + +static void +frame_info (char *addr_exp, int from_tty) +{ + struct frame_info *fi; + struct symtab_and_line sal; + struct symbol *func; + struct symtab *s; + struct frame_info *calling_frame_info; + int i, count, numregs; + char *funname = 0; + enum language funlang = language_unknown; + + if (!target_has_stack) + error ("No stack."); + + fi = parse_frame_specification (addr_exp); + if (fi == NULL) + error ("Invalid frame specified."); + + sal = find_pc_line (fi->pc, + fi->next != NULL + && !fi->next->signal_handler_caller + && !frame_in_dummy (fi->next)); + func = get_frame_function (fi); + s = find_pc_symtab (fi->pc); + if (func) + { + /* I'd like to use SYMBOL_SOURCE_NAME() here, to display + * the demangled name that we already have stored in + * the symbol table, but we stored a version with + * DMGL_PARAMS turned on, and here we don't want + * to display parameters. So call the demangler again, + * with DMGL_ANSI only. RT + * (Yes, I know that printf_symbol_filtered() will + * again try to demangle the name on the fly, but + * the issue is that if cplus_demangle() fails here, + * it'll fail there too. So we want to catch the failure + * ("demangled==NULL" case below) here, while we still + * have our hands on the function symbol.) + */ + char *demangled; + funname = SYMBOL_NAME (func); + funlang = SYMBOL_LANGUAGE (func); + if (funlang == language_cplus) + { + demangled = cplus_demangle (funname, DMGL_ANSI); + /* If the demangler fails, try the demangled name + * from the symbol table. This'll have parameters, + * but that's preferable to diplaying a mangled name. + */ + if (demangled == NULL) + funname = SYMBOL_SOURCE_NAME (func); + } + } + else + { + register struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (fi->pc); + if (msymbol != NULL) + { + funname = SYMBOL_NAME (msymbol); + funlang = SYMBOL_LANGUAGE (msymbol); + } + } + calling_frame_info = get_prev_frame (fi); + + if (!addr_exp && frame_relative_level (selected_frame) >= 0) + { + printf_filtered ("Stack level %d, frame at ", + frame_relative_level (selected_frame)); + print_address_numeric (fi->frame, 1, gdb_stdout); + printf_filtered (":\n"); + } + else + { + printf_filtered ("Stack frame at "); + print_address_numeric (fi->frame, 1, gdb_stdout); + printf_filtered (":\n"); + } + printf_filtered (" %s = ", REGISTER_NAME (PC_REGNUM)); + print_address_numeric (fi->pc, 1, gdb_stdout); + + wrap_here (" "); + if (funname) + { + printf_filtered (" in "); + fprintf_symbol_filtered (gdb_stdout, funname, funlang, + DMGL_ANSI | DMGL_PARAMS); + } + wrap_here (" "); + if (sal.symtab) + printf_filtered (" (%s:%d)", sal.symtab->filename, sal.line); + puts_filtered ("; "); + wrap_here (" "); + printf_filtered ("saved %s ", REGISTER_NAME (PC_REGNUM)); + print_address_numeric (FRAME_SAVED_PC (fi), 1, gdb_stdout); + printf_filtered ("\n"); + + { + int frameless; + frameless = FRAMELESS_FUNCTION_INVOCATION (fi); + if (frameless) + printf_filtered (" (FRAMELESS),"); + } + + if (calling_frame_info) + { + printf_filtered (" called by frame at "); + print_address_numeric (calling_frame_info->frame, 1, gdb_stdout); + } + if (fi->next && calling_frame_info) + puts_filtered (","); + wrap_here (" "); + if (fi->next) + { + printf_filtered (" caller of frame at "); + print_address_numeric (fi->next->frame, 1, gdb_stdout); + } + if (fi->next || calling_frame_info) + puts_filtered ("\n"); + if (s) + printf_filtered (" source language %s.\n", language_str (s->language)); + +#ifdef PRINT_EXTRA_FRAME_INFO + PRINT_EXTRA_FRAME_INFO (fi); +#endif + + { + /* Address of the argument list for this frame, or 0. */ + CORE_ADDR arg_list = FRAME_ARGS_ADDRESS_CORRECT (fi); + /* Number of args for this frame, or -1 if unknown. */ + int numargs; + + if (arg_list == 0) + printf_filtered (" Arglist at unknown address.\n"); + else + { + printf_filtered (" Arglist at "); + print_address_numeric (arg_list, 1, gdb_stdout); + printf_filtered (","); + + numargs = FRAME_NUM_ARGS (fi); + if (numargs < 0) + puts_filtered (" args: "); + else if (numargs == 0) + puts_filtered (" no args."); + else if (numargs == 1) + puts_filtered (" 1 arg: "); + else + printf_filtered (" %d args: ", numargs); + print_frame_args (func, fi, numargs, gdb_stdout); + puts_filtered ("\n"); + } + } + { + /* Address of the local variables for this frame, or 0. */ + CORE_ADDR arg_list = FRAME_LOCALS_ADDRESS (fi); + + if (arg_list == 0) + printf_filtered (" Locals at unknown address,"); + else + { + printf_filtered (" Locals at "); + print_address_numeric (arg_list, 1, gdb_stdout); + printf_filtered (","); + } + } + + if (fi->saved_regs == NULL) + FRAME_INIT_SAVED_REGS (fi); + /* Print as much information as possible on the location of all the + registers. */ + { + enum lval_type lval; + int optimized; + CORE_ADDR addr; + int realnum; + int count; + int i; + int need_nl = 1; + + /* The sp is special; what's displayed isn't the save address, but + the value of the previous frame's sp. This is a legacy thing, + at one stage the frame cached the previous frame's SP instead + of its address, hence it was easiest to just display the cached + value. */ + if (SP_REGNUM >= 0) + { + /* Find out the location of the saved stack pointer with out + actually evaluating it. */ + frame_register_unwind (fi, SP_REGNUM, &optimized, &lval, &addr, + &realnum, NULL); + if (!optimized && lval == not_lval) + { + void *value = alloca (MAX_REGISTER_RAW_SIZE); + CORE_ADDR sp; + frame_register_unwind (fi, SP_REGNUM, &optimized, &lval, &addr, + &realnum, value); + sp = extract_address (value, REGISTER_RAW_SIZE (SP_REGNUM)); + printf_filtered (" Previous frame's sp is "); + print_address_numeric (sp, 1, gdb_stdout); + printf_filtered ("\n"); + need_nl = 0; + } + else if (!optimized && lval == lval_memory) + { + printf_filtered (" Previous frame's sp at "); + print_address_numeric (addr, 1, gdb_stdout); + printf_filtered ("\n"); + need_nl = 0; + } + else if (!optimized && lval == lval_register) + { + printf_filtered (" Previous frame's sp in %s\n", + REGISTER_NAME (realnum)); + need_nl = 0; + } + /* else keep quiet. */ + } + + count = 0; + numregs = NUM_REGS + NUM_PSEUDO_REGS; + for (i = 0; i < numregs; i++) + if (i != SP_REGNUM) + { + /* Find out the location of the saved register without + fetching the corresponding value. */ + frame_register_unwind (fi, i, &optimized, &lval, &addr, &realnum, + NULL); + /* For moment, only display registers that were saved on the + stack. */ + if (!optimized && lval == lval_memory) + { + if (count == 0) + puts_filtered (" Saved registers:\n "); + else + puts_filtered (","); + wrap_here (" "); + printf_filtered (" %s at ", REGISTER_NAME (i)); + print_address_numeric (addr, 1, gdb_stdout); + count++; + } + } + if (count || need_nl) + puts_filtered ("\n"); + } +} + +#if 0 +/* Set a limit on the number of frames printed by default in a + backtrace. */ + +static int backtrace_limit; + +static void +set_backtrace_limit_command (char *count_exp, int from_tty) +{ + int count = parse_and_eval_long (count_exp); + + if (count < 0) + error ("Negative argument not meaningful as backtrace limit."); + + backtrace_limit = count; +} + +static void +backtrace_limit_info (char *arg, int from_tty) +{ + if (arg) + error ("\"Info backtrace-limit\" takes no arguments."); + + printf_unfiltered ("Backtrace limit: %d.\n", backtrace_limit); +} +#endif + +/* Print briefly all stack frames or just the innermost COUNT frames. */ + +static void backtrace_command_1 (char *count_exp, int show_locals, + int from_tty); +static void +backtrace_command_1 (char *count_exp, int show_locals, int from_tty) +{ + struct frame_info *fi; + register int count; + register int i; + register struct frame_info *trailing; + register int trailing_level; + + if (!target_has_stack) + error ("No stack."); + + /* The following code must do two things. First, it must + set the variable TRAILING to the frame from which we should start + printing. Second, it must set the variable count to the number + of frames which we should print, or -1 if all of them. */ + trailing = get_current_frame (); + + /* The target can be in a state where there is no valid frames + (e.g., just connected). */ + if (trailing == NULL) + error ("No stack."); + + trailing_level = 0; + if (count_exp) + { + count = parse_and_eval_long (count_exp); + if (count < 0) + { + struct frame_info *current; + + count = -count; + + current = trailing; + while (current && count--) + { + QUIT; + current = get_prev_frame (current); + } + + /* Will stop when CURRENT reaches the top of the stack. TRAILING + will be COUNT below it. */ + while (current) + { + QUIT; + trailing = get_prev_frame (trailing); + current = get_prev_frame (current); + trailing_level++; + } + + count = -1; + } + } + else + count = -1; + + if (info_verbose) + { + struct partial_symtab *ps; + + /* Read in symbols for all of the frames. Need to do this in + a separate pass so that "Reading in symbols for xxx" messages + don't screw up the appearance of the backtrace. Also + if people have strong opinions against reading symbols for + backtrace this may have to be an option. */ + i = count; + for (fi = trailing; + fi != NULL && i--; + fi = get_prev_frame (fi)) + { + QUIT; + ps = find_pc_psymtab (frame_address_in_block (fi)); + if (ps) + PSYMTAB_TO_SYMTAB (ps); /* Force syms to come in */ + } + } + + for (i = 0, fi = trailing; + fi && count--; + i++, fi = get_prev_frame (fi)) + { + QUIT; + + /* Don't use print_stack_frame; if an error() occurs it probably + means further attempts to backtrace would fail (on the other + hand, perhaps the code does or could be fixed to make sure + the frame->prev field gets set to NULL in that case). */ + print_frame_info_base (fi, trailing_level + i, 0, 1); + if (show_locals) + print_frame_local_vars (fi, 1, gdb_stdout); + } + + /* If we've stopped before the end, mention that. */ + if (fi && from_tty) + printf_filtered ("(More stack frames follow...)\n"); +} + +static void +backtrace_command (char *arg, int from_tty) +{ + struct cleanup *old_chain = (struct cleanup *) NULL; + char **argv = (char **) NULL; + int argIndicatingFullTrace = (-1), totArgLen = 0, argc = 0; + char *argPtr = arg; + + if (arg != (char *) NULL) + { + int i; + + argv = buildargv (arg); + old_chain = make_cleanup_freeargv (argv); + argc = 0; + for (i = 0; (argv[i] != (char *) NULL); i++) + { + unsigned int j; + + for (j = 0; (j < strlen (argv[i])); j++) + argv[i][j] = tolower (argv[i][j]); + + if (argIndicatingFullTrace < 0 && subset_compare (argv[i], "full")) + argIndicatingFullTrace = argc; + else + { + argc++; + totArgLen += strlen (argv[i]); + } + } + totArgLen += argc; + if (argIndicatingFullTrace >= 0) + { + if (totArgLen > 0) + { + argPtr = (char *) xmalloc (totArgLen + 1); + if (!argPtr) + nomem (0); + else + { + memset (argPtr, 0, totArgLen + 1); + for (i = 0; (i < (argc + 1)); i++) + { + if (i != argIndicatingFullTrace) + { + strcat (argPtr, argv[i]); + strcat (argPtr, " "); + } + } + } + } + else + argPtr = (char *) NULL; + } + } + + backtrace_command_1 (argPtr, (argIndicatingFullTrace >= 0), from_tty); + + if (argIndicatingFullTrace >= 0 && totArgLen > 0) + xfree (argPtr); + + if (old_chain) + do_cleanups (old_chain); +} + +static void backtrace_full_command (char *arg, int from_tty); +static void +backtrace_full_command (char *arg, int from_tty) +{ + backtrace_command_1 (arg, 1, from_tty); +} + + +/* Print the local variables of a block B active in FRAME. + Return 1 if any variables were printed; 0 otherwise. */ + +static int +print_block_frame_locals (struct block *b, register struct frame_info *fi, + int num_tabs, register struct ui_file *stream) +{ + register int i, j; + register struct symbol *sym; + register int values_printed = 0; + + ALL_BLOCK_SYMBOLS (b, i, sym) + { + switch (SYMBOL_CLASS (sym)) + { + case LOC_LOCAL: + case LOC_REGISTER: + case LOC_STATIC: + case LOC_BASEREG: + values_printed = 1; + for (j = 0; j < num_tabs; j++) + fputs_filtered ("\t", stream); + fputs_filtered (SYMBOL_SOURCE_NAME (sym), stream); + fputs_filtered (" = ", stream); + print_variable_value (sym, fi, stream); + fprintf_filtered (stream, "\n"); + break; + + default: + /* Ignore symbols which are not locals. */ + break; + } + } + return values_printed; +} + +/* Same, but print labels. */ + +static int +print_block_frame_labels (struct block *b, int *have_default, + register struct ui_file *stream) +{ + register int i; + register struct symbol *sym; + register int values_printed = 0; + + ALL_BLOCK_SYMBOLS (b, i, sym) + { + if (STREQ (SYMBOL_NAME (sym), "default")) + { + if (*have_default) + continue; + *have_default = 1; + } + if (SYMBOL_CLASS (sym) == LOC_LABEL) + { + struct symtab_and_line sal; + sal = find_pc_line (SYMBOL_VALUE_ADDRESS (sym), 0); + values_printed = 1; + fputs_filtered (SYMBOL_SOURCE_NAME (sym), stream); + if (addressprint) + { + fprintf_filtered (stream, " "); + print_address_numeric (SYMBOL_VALUE_ADDRESS (sym), 1, stream); + } + fprintf_filtered (stream, " in file %s, line %d\n", + sal.symtab->filename, sal.line); + } + } + return values_printed; +} + +/* Print on STREAM all the local variables in frame FRAME, + including all the blocks active in that frame + at its current pc. + + Returns 1 if the job was done, + or 0 if nothing was printed because we have no info + on the function running in FRAME. */ + +static void +print_frame_local_vars (register struct frame_info *fi, register int num_tabs, + register struct ui_file *stream) +{ + register struct block *block = get_frame_block (fi, 0); + register int values_printed = 0; + + if (block == 0) + { + fprintf_filtered (stream, "No symbol table info available.\n"); + return; + } + + while (block != 0) + { + if (print_block_frame_locals (block, fi, num_tabs, stream)) + values_printed = 1; + /* After handling the function's top-level block, stop. + Don't continue to its superblock, the block of + per-file symbols. */ + if (BLOCK_FUNCTION (block)) + break; + block = BLOCK_SUPERBLOCK (block); + } + + if (!values_printed) + { + fprintf_filtered (stream, "No locals.\n"); + } +} + +/* Same, but print labels. */ + +static void +print_frame_label_vars (register struct frame_info *fi, int this_level_only, + register struct ui_file *stream) +{ + register struct blockvector *bl; + register struct block *block = get_frame_block (fi, 0); + register int values_printed = 0; + int index, have_default = 0; + char *blocks_printed; + CORE_ADDR pc = fi->pc; + + if (block == 0) + { + fprintf_filtered (stream, "No symbol table info available.\n"); + return; + } + + bl = blockvector_for_pc (BLOCK_END (block) - 4, &index); + blocks_printed = (char *) alloca (BLOCKVECTOR_NBLOCKS (bl) * sizeof (char)); + memset (blocks_printed, 0, BLOCKVECTOR_NBLOCKS (bl) * sizeof (char)); + + while (block != 0) + { + CORE_ADDR end = BLOCK_END (block) - 4; + int last_index; + + if (bl != blockvector_for_pc (end, &index)) + error ("blockvector blotch"); + if (BLOCKVECTOR_BLOCK (bl, index) != block) + error ("blockvector botch"); + last_index = BLOCKVECTOR_NBLOCKS (bl); + index += 1; + + /* Don't print out blocks that have gone by. */ + while (index < last_index + && BLOCK_END (BLOCKVECTOR_BLOCK (bl, index)) < pc) + index++; + + while (index < last_index + && BLOCK_END (BLOCKVECTOR_BLOCK (bl, index)) < end) + { + if (blocks_printed[index] == 0) + { + if (print_block_frame_labels (BLOCKVECTOR_BLOCK (bl, index), &have_default, stream)) + values_printed = 1; + blocks_printed[index] = 1; + } + index++; + } + if (have_default) + return; + if (values_printed && this_level_only) + return; + + /* After handling the function's top-level block, stop. + Don't continue to its superblock, the block of + per-file symbols. */ + if (BLOCK_FUNCTION (block)) + break; + block = BLOCK_SUPERBLOCK (block); + } + + if (!values_printed && !this_level_only) + { + fprintf_filtered (stream, "No catches.\n"); + } +} + +/* ARGSUSED */ +void +locals_info (char *args, int from_tty) +{ + if (!selected_frame) + error ("No frame selected."); + print_frame_local_vars (selected_frame, 0, gdb_stdout); +} + +static void +catch_info (char *ignore, int from_tty) +{ + struct symtab_and_line *sal; + + /* Check for target support for exception handling */ + sal = target_enable_exception_callback (EX_EVENT_CATCH, 1); + if (sal) + { + /* Currently not handling this */ + /* Ideally, here we should interact with the C++ runtime + system to find the list of active handlers, etc. */ + fprintf_filtered (gdb_stdout, "Info catch not supported with this target/compiler combination.\n"); +#if 0 + if (!selected_frame) + error ("No frame selected."); +#endif + } + else + { + /* Assume g++ compiled code -- old v 4.16 behaviour */ + if (!selected_frame) + error ("No frame selected."); + + print_frame_label_vars (selected_frame, 0, gdb_stdout); + } +} + +static void +print_frame_arg_vars (register struct frame_info *fi, + register struct ui_file *stream) +{ + struct symbol *func = get_frame_function (fi); + register struct block *b; + register int i; + register struct symbol *sym, *sym2; + register int values_printed = 0; + + if (func == 0) + { + fprintf_filtered (stream, "No symbol table info available.\n"); + return; + } + + b = SYMBOL_BLOCK_VALUE (func); + ALL_BLOCK_SYMBOLS (b, i, sym) + { + switch (SYMBOL_CLASS (sym)) + { + case LOC_ARG: + case LOC_LOCAL_ARG: + case LOC_REF_ARG: + case LOC_REGPARM: + case LOC_REGPARM_ADDR: + case LOC_BASEREG_ARG: + values_printed = 1; + fputs_filtered (SYMBOL_SOURCE_NAME (sym), stream); + fputs_filtered (" = ", stream); + + /* We have to look up the symbol because arguments can have + two entries (one a parameter, one a local) and the one we + want is the local, which lookup_symbol will find for us. + This includes gcc1 (not gcc2) on the sparc when passing a + small structure and gcc2 when the argument type is float + and it is passed as a double and converted to float by + the prologue (in the latter case the type of the LOC_ARG + symbol is double and the type of the LOC_LOCAL symbol is + float). There are also LOC_ARG/LOC_REGISTER pairs which + are not combined in symbol-reading. */ + + sym2 = lookup_symbol (SYMBOL_NAME (sym), + b, VAR_NAMESPACE, (int *) NULL, (struct symtab **) NULL); + print_variable_value (sym2, fi, stream); + fprintf_filtered (stream, "\n"); + break; + + default: + /* Don't worry about things which aren't arguments. */ + break; + } + } + if (!values_printed) + { + fprintf_filtered (stream, "No arguments.\n"); + } +} + +void +args_info (char *ignore, int from_tty) +{ + if (!selected_frame) + error ("No frame selected."); + print_frame_arg_vars (selected_frame, gdb_stdout); +} + + +static void +args_plus_locals_info (char *ignore, int from_tty) +{ + args_info (ignore, from_tty); + locals_info (ignore, from_tty); +} + + +/* Select frame FI (or NULL - to invalidate the current frame). */ + +void +select_frame (struct frame_info *fi) +{ + register struct symtab *s; + + selected_frame = fi; + /* NOTE: cagney/2002-05-04: FI can be NULL. This occures when the + frame is being invalidated. */ + if (selected_frame_level_changed_hook) + selected_frame_level_changed_hook (frame_relative_level (fi)); + + /* FIXME: kseitz/2002-08-28: It would be nice to call + selected_frame_level_changed_event right here, but due to limitations + in the current interfaces, we would end up flooding UIs with events + because select_frame is used extensively internally. + + Once we have frame-parameterized frame (and frame-related) commands, + the event notification can be moved here, since this function will only + be called when the users selected frame is being changed. */ + + /* Ensure that symbols for this frame are read in. Also, determine the + source language of this frame, and switch to it if desired. */ + if (fi) + { + s = find_pc_symtab (fi->pc); + if (s + && s->language != current_language->la_language + && s->language != language_unknown + && language_mode == language_mode_auto) + { + set_language (s->language); + } + } +} + + +/* Select frame FI. Also print the stack frame and show the source if + this is the tui version. */ +static void +select_and_print_frame (struct frame_info *fi) +{ + select_frame (fi); + if (fi) + { + print_stack_frame (fi, frame_relative_level (fi), 1); + } +} + +/* Return the symbol-block in which the selected frame is executing. + Can return zero under various legitimate circumstances. + + If ADDR_IN_BLOCK is non-zero, set *ADDR_IN_BLOCK to the relevant + code address within the block returned. We use this to decide + which macros are in scope. */ + +struct block * +get_selected_block (CORE_ADDR *addr_in_block) +{ + if (!target_has_stack) + return 0; + + if (!selected_frame) + return get_current_block (addr_in_block); + return get_frame_block (selected_frame, addr_in_block); +} + +/* Find a frame a certain number of levels away from FRAME. + LEVEL_OFFSET_PTR points to an int containing the number of levels. + Positive means go to earlier frames (up); negative, the reverse. + The int that contains the number of levels is counted toward + zero as the frames for those levels are found. + If the top or bottom frame is reached, that frame is returned, + but the final value of *LEVEL_OFFSET_PTR is nonzero and indicates + how much farther the original request asked to go. */ + +struct frame_info * +find_relative_frame (register struct frame_info *frame, + register int *level_offset_ptr) +{ + register struct frame_info *prev; + register struct frame_info *frame1; + + /* Going up is simple: just do get_prev_frame enough times + or until initial frame is reached. */ + while (*level_offset_ptr > 0) + { + prev = get_prev_frame (frame); + if (prev == 0) + break; + (*level_offset_ptr)--; + frame = prev; + } + /* Going down is just as simple. */ + if (*level_offset_ptr < 0) + { + while (*level_offset_ptr < 0) + { + frame1 = get_next_frame (frame); + if (!frame1) + break; + frame = frame1; + (*level_offset_ptr)++; + } + } + return frame; +} + +/* The "select_frame" command. With no arg, NOP. + With arg LEVEL_EXP, select the frame at level LEVEL if it is a + valid level. Otherwise, treat level_exp as an address expression + and select it. See parse_frame_specification for more info on proper + frame expressions. */ + +/* ARGSUSED */ +void +select_frame_command_wrapper (char *level_exp, int from_tty) +{ + select_frame_command (level_exp, from_tty); +} + +static void +select_frame_command (char *level_exp, int from_tty) +{ + struct frame_info *frame; + int level = frame_relative_level (selected_frame); + + if (!target_has_stack) + error ("No stack."); + + frame = parse_frame_specification (level_exp); + + select_frame (frame); + if (level != frame_relative_level (selected_frame)) + selected_frame_level_changed_event (frame_relative_level (selected_frame)); +} + +/* The "frame" command. With no arg, print selected frame briefly. + With arg, behaves like select_frame and then prints the selected + frame. */ + +void +frame_command (char *level_exp, int from_tty) +{ + select_frame_command (level_exp, from_tty); + show_and_print_stack_frame (selected_frame, + frame_relative_level (selected_frame), 1); +} + +/* The XDB Compatibility command to print the current frame. */ + +static void +current_frame_command (char *level_exp, int from_tty) +{ + if (target_has_stack == 0 || selected_frame == 0) + error ("No stack."); + print_only_stack_frame (selected_frame, + frame_relative_level (selected_frame), 1); +} + +/* Select the frame up one or COUNT stack levels + from the previously selected frame, and print it briefly. */ + +/* ARGSUSED */ +static void +up_silently_base (char *count_exp) +{ + register struct frame_info *fi; + int count = 1, count1; + if (count_exp) + count = parse_and_eval_long (count_exp); + count1 = count; + + if (target_has_stack == 0 || selected_frame == 0) + error ("No stack."); + + fi = find_relative_frame (selected_frame, &count1); + if (count1 != 0 && count_exp == 0) + error ("Initial frame selected; you cannot go up."); + select_frame (fi); + selected_frame_level_changed_event (frame_relative_level (selected_frame)); +} + +static void +up_silently_command (char *count_exp, int from_tty) +{ + up_silently_base (count_exp); +} + +static void +up_command (char *count_exp, int from_tty) +{ + up_silently_base (count_exp); + show_and_print_stack_frame (selected_frame, + frame_relative_level (selected_frame), 1); +} + +/* Select the frame down one or COUNT stack levels + from the previously selected frame, and print it briefly. */ + +/* ARGSUSED */ +static void +down_silently_base (char *count_exp) +{ + register struct frame_info *frame; + int count = -1, count1; + if (count_exp) + count = -parse_and_eval_long (count_exp); + count1 = count; + + if (target_has_stack == 0 || selected_frame == 0) + error ("No stack."); + + frame = find_relative_frame (selected_frame, &count1); + if (count1 != 0 && count_exp == 0) + { + + /* We only do this if count_exp is not specified. That way "down" + means to really go down (and let me know if that is + impossible), but "down 9999" can be used to mean go all the way + down without getting an error. */ + + error ("Bottom (i.e., innermost) frame selected; you cannot go down."); + } + + select_frame (frame); + selected_frame_level_changed_event (frame_relative_level (selected_frame)); +} + +/* ARGSUSED */ +static void +down_silently_command (char *count_exp, int from_tty) +{ + down_silently_base (count_exp); +} + +static void +down_command (char *count_exp, int from_tty) +{ + down_silently_base (count_exp); + show_and_print_stack_frame (selected_frame, + frame_relative_level (selected_frame), 1); +} + +void +return_command_wrapper (char *retval_exp, int from_tty) +{ + return_command (retval_exp, from_tty); +} + +static void +return_command (char *retval_exp, int from_tty) +{ + struct symbol *thisfun; + CORE_ADDR selected_frame_addr; + CORE_ADDR selected_frame_pc; + struct frame_info *frame; + struct value *return_value = NULL; + + if (selected_frame == NULL) + error ("No selected frame."); + thisfun = get_frame_function (selected_frame); + selected_frame_addr = FRAME_FP (selected_frame); + selected_frame_pc = selected_frame->pc; + + /* Compute the return value (if any -- possibly getting errors here). */ + + if (retval_exp) + { + struct type *return_type = NULL; + + return_value = parse_and_eval (retval_exp); + + /* Cast return value to the return type of the function. */ + if (thisfun != NULL) + return_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (thisfun)); + if (return_type == NULL) + return_type = builtin_type_int; + return_value = value_cast (return_type, return_value); + + /* Make sure we have fully evaluated it, since + it might live in the stack frame we're about to pop. */ + if (VALUE_LAZY (return_value)) + value_fetch_lazy (return_value); + } + + /* If interactive, require confirmation. */ + + if (from_tty) + { + if (thisfun != 0) + { + if (!query ("Make %s return now? ", SYMBOL_SOURCE_NAME (thisfun))) + { + error ("Not confirmed."); + /* NOTREACHED */ + } + } + else if (!query ("Make selected stack frame return now? ")) + error ("Not confirmed."); + } + + /* Do the real work. Pop until the specified frame is current. We + use this method because the selected_frame is not valid after + a POP_FRAME. The pc comparison makes this work even if the + selected frame shares its fp with another frame. */ + + while (selected_frame_addr != (frame = get_current_frame ())->frame + || selected_frame_pc != frame->pc) + POP_FRAME; + + /* Then pop that frame. */ + + POP_FRAME; + + /* Compute the return value (if any) and store in the place + for return values. */ + + if (retval_exp) + set_return_value (return_value); + + /* If we are at the end of a call dummy now, pop the dummy frame too. */ + + if (CALL_DUMMY_HAS_COMPLETED (read_pc(), read_sp (), + FRAME_FP (get_current_frame ()))) + POP_FRAME; + + /* If interactive, print the frame that is now current. */ + + if (from_tty) + frame_command ("0", 1); + else + select_frame_command ("0", 0); +} + +/* Sets the scope to input function name, provided that the + function is within the current stack frame */ + +struct function_bounds +{ + CORE_ADDR low, high; +}; + +static void func_command (char *arg, int from_tty); +static void +func_command (char *arg, int from_tty) +{ + struct frame_info *fp; + int found = 0; + struct symtabs_and_lines sals; + int i; + int level = 1; + struct function_bounds *func_bounds = (struct function_bounds *) NULL; + + if (arg != (char *) NULL) + return; + + fp = parse_frame_specification ("0"); + sals = decode_line_spec (arg, 1); + func_bounds = (struct function_bounds *) xmalloc ( + sizeof (struct function_bounds) * sals.nelts); + for (i = 0; (i < sals.nelts && !found); i++) + { + if (sals.sals[i].pc == (CORE_ADDR) 0 || + find_pc_partial_function (sals.sals[i].pc, + (char **) NULL, + &func_bounds[i].low, + &func_bounds[i].high) == 0) + { + func_bounds[i].low = + func_bounds[i].high = (CORE_ADDR) NULL; + } + } + + do + { + for (i = 0; (i < sals.nelts && !found); i++) + found = (fp->pc >= func_bounds[i].low && + fp->pc < func_bounds[i].high); + if (!found) + { + level = 1; + fp = find_relative_frame (fp, &level); + } + } + while (!found && level == 0); + + if (func_bounds) + xfree (func_bounds); + + if (!found) + printf_filtered ("'%s' not within current stack frame.\n", arg); + else if (fp != selected_frame) + select_and_print_frame (fp); +} + +/* Gets the language of the current frame. */ + +enum language +get_frame_language (void) +{ + register struct symtab *s; + enum language flang; /* The language of the current frame */ + + if (selected_frame) + { + s = find_pc_symtab (selected_frame->pc); + if (s) + flang = s->language; + else + flang = language_unknown; + } + else + flang = language_unknown; + + return flang; +} + +void +_initialize_stack (void) +{ +#if 0 + backtrace_limit = 30; +#endif + + add_com ("return", class_stack, return_command, + "Make selected stack frame return to its caller.\n\ +Control remains in the debugger, but when you continue\n\ +execution will resume in the frame above the one now selected.\n\ +If an argument is given, it is an expression for the value to return."); + + add_com ("up", class_stack, up_command, + "Select and print stack frame that called this one.\n\ +An argument says how many frames up to go."); + add_com ("up-silently", class_support, up_silently_command, + "Same as the `up' command, but does not print anything.\n\ +This is useful in command scripts."); + + add_com ("down", class_stack, down_command, + "Select and print stack frame called by this one.\n\ +An argument says how many frames down to go."); + add_com_alias ("do", "down", class_stack, 1); + add_com_alias ("dow", "down", class_stack, 1); + add_com ("down-silently", class_support, down_silently_command, + "Same as the `down' command, but does not print anything.\n\ +This is useful in command scripts."); + + add_com ("frame", class_stack, frame_command, + "Select and print a stack frame.\n\ +With no argument, print the selected stack frame. (See also \"info frame\").\n\ +An argument specifies the frame to select.\n\ +It can be a stack frame number or the address of the frame.\n\ +With argument, nothing is printed if input is coming from\n\ +a command file or a user-defined command."); + + add_com_alias ("f", "frame", class_stack, 1); + + if (xdb_commands) + { + add_com ("L", class_stack, current_frame_command, + "Print the current stack frame.\n"); + add_com_alias ("V", "frame", class_stack, 1); + } + add_com ("select-frame", class_stack, select_frame_command, + "Select a stack frame without printing anything.\n\ +An argument specifies the frame to select.\n\ +It can be a stack frame number or the address of the frame.\n"); + + add_com ("backtrace", class_stack, backtrace_command, + "Print backtrace of all stack frames, or innermost COUNT frames.\n\ +With a negative argument, print outermost -COUNT frames.\n\ +Use of the 'full' qualifier also prints the values of the local variables.\n"); + add_com_alias ("bt", "backtrace", class_stack, 0); + if (xdb_commands) + { + add_com_alias ("t", "backtrace", class_stack, 0); + add_com ("T", class_stack, backtrace_full_command, + "Print backtrace of all stack frames, or innermost COUNT frames \n\ +and the values of the local variables.\n\ +With a negative argument, print outermost -COUNT frames.\n\ +Usage: T \n"); + } + + add_com_alias ("where", "backtrace", class_alias, 0); + add_info ("stack", backtrace_command, + "Backtrace of the stack, or innermost COUNT frames."); + add_info_alias ("s", "stack", 1); + add_info ("frame", frame_info, + "All about selected stack frame, or frame at ADDR."); + add_info_alias ("f", "frame", 1); + add_info ("locals", locals_info, + "Local variables of current stack frame."); + add_info ("args", args_info, + "Argument variables of current stack frame."); + if (xdb_commands) + add_com ("l", class_info, args_plus_locals_info, + "Argument and local variables of current stack frame."); + + if (dbx_commands) + add_com ("func", class_stack, func_command, + "Select the stack frame that contains .\nUsage: func \n"); + + add_info ("catch", catch_info, + "Exceptions that can be caught in the current stack frame."); + +#if 0 + add_cmd ("backtrace-limit", class_stack, set_backtrace_limit_command, + "Specify maximum number of frames for \"backtrace\" to print by default.", + &setlist); + add_info ("backtrace-limit", backtrace_limit_info, + "The maximum number of frames for \"backtrace\" to print by default."); +#endif +} diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c new file mode 100644 index 0000000..2d12f31 --- /dev/null +++ b/gdb/tracepoint.c @@ -0,0 +1,2812 @@ +/* Tracing functionality for remote targets in custom GDB protocol + + Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software + Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "symtab.h" +#include "frame.h" +#include "gdbtypes.h" +#include "expression.h" +#include "gdbcmd.h" +#include "value.h" +#include "target.h" +#include "language.h" +#include "gdb_string.h" +#include "inferior.h" +#include "tracepoint.h" +#include "remote.h" +#include "linespec.h" +#include "regcache.h" +#include "completer.h" +#include "gdb-events.h" + +#include "ax.h" +#include "ax-gdb.h" + +/* readline include files */ +#include +#include + +/* readline defines this. */ +#undef savestring + +#ifdef HAVE_UNISTD_H +#include +#endif + +/* maximum length of an agent aexpression. + this accounts for the fact that packets are limited to 400 bytes + (which includes everything -- including the checksum), and assumes + the worst case of maximum length for each of the pieces of a + continuation packet. + + NOTE: expressions get mem2hex'ed otherwise this would be twice as + large. (400 - 31)/2 == 184 */ +#define MAX_AGENT_EXPR_LEN 184 + + +extern void (*readline_begin_hook) (char *, ...); +extern char *(*readline_hook) (char *); +extern void (*readline_end_hook) (void); +extern void x_command (char *, int); +extern int addressprint; /* Print machine addresses? */ + +/* GDB commands implemented in other modules: + */ + +extern void output_command (char *, int); +extern void registers_info (char *, int); +extern void args_info (char *, int); +extern void locals_info (char *, int); + + +/* If this definition isn't overridden by the header files, assume + that isatty and fileno exist on this system. */ +#ifndef ISATTY +#define ISATTY(FP) (isatty (fileno (FP))) +#endif + +/* + Tracepoint.c: + + This module defines the following debugger commands: + trace : set a tracepoint on a function, line, or address. + info trace : list all debugger-defined tracepoints. + delete trace : delete one or more tracepoints. + enable trace : enable one or more tracepoints. + disable trace : disable one or more tracepoints. + actions : specify actions to be taken at a tracepoint. + passcount : specify a pass count for a tracepoint. + tstart : start a trace experiment. + tstop : stop a trace experiment. + tstatus : query the status of a trace experiment. + tfind : find a trace frame in the trace buffer. + tdump : print everything collected at the current tracepoint. + save-tracepoints : write tracepoint setup into a file. + + This module defines the following user-visible debugger variables: + $trace_frame : sequence number of trace frame currently being debugged. + $trace_line : source line of trace frame currently being debugged. + $trace_file : source file of trace frame currently being debugged. + $tracepoint : tracepoint number of trace frame currently being debugged. + */ + + +/* ======= Important global variables: ======= */ + +/* Chain of all tracepoints defined. */ +struct tracepoint *tracepoint_chain; + +/* Number of last tracepoint made. */ +static int tracepoint_count; + +/* Number of last traceframe collected. */ +static int traceframe_number; + +/* Tracepoint for last traceframe collected. */ +static int tracepoint_number; + +/* Symbol for function for last traceframe collected */ +static struct symbol *traceframe_fun; + +/* Symtab and line for last traceframe collected */ +static struct symtab_and_line traceframe_sal; + +/* Tracing command lists */ +static struct cmd_list_element *tfindlist; + +/* ======= Important command functions: ======= */ +static void trace_command (char *, int); +static void tracepoints_info (char *, int); +static void delete_trace_command (char *, int); +static void enable_trace_command (char *, int); +static void disable_trace_command (char *, int); +static void trace_pass_command (char *, int); +static void trace_actions_command (char *, int); +static void trace_start_command (char *, int); +static void trace_stop_command (char *, int); +static void trace_status_command (char *, int); +static void trace_find_command (char *, int); +static void trace_find_pc_command (char *, int); +static void trace_find_tracepoint_command (char *, int); +static void trace_find_line_command (char *, int); +static void trace_find_range_command (char *, int); +static void trace_find_outside_command (char *, int); +static void tracepoint_save_command (char *, int); +static void trace_dump_command (char *, int); + +/* support routines */ +static void trace_mention (struct tracepoint *); + +struct collection_list; +static void add_aexpr (struct collection_list *, struct agent_expr *); +static unsigned char *mem2hex (unsigned char *, unsigned char *, int); +static void add_register (struct collection_list *collection, + unsigned int regno); +static struct cleanup *make_cleanup_free_actions (struct tracepoint *t); +static void free_actions_list (char **actions_list); +static void free_actions_list_cleanup_wrapper (void *); + +extern void _initialize_tracepoint (void); + +/* Utility: returns true if "target remote" */ +static int +target_is_remote (void) +{ + if (current_target.to_shortname && + strcmp (current_target.to_shortname, "remote") == 0) + return 1; + else + return 0; +} + +/* Utility: generate error from an incoming stub packet. */ +static void +trace_error (char *buf) +{ + if (*buf++ != 'E') + return; /* not an error msg */ + switch (*buf) + { + case '1': /* malformed packet error */ + if (*++buf == '0') /* general case: */ + error ("tracepoint.c: error in outgoing packet."); + else + error ("tracepoint.c: error in outgoing packet at field #%ld.", + strtol (buf, NULL, 16)); + case '2': + error ("trace API error 0x%s.", ++buf); + default: + error ("Target returns error code '%s'.", buf); + } +} + +/* Utility: wait for reply from stub, while accepting "O" packets */ +static char * +remote_get_noisy_reply (char *buf, + long sizeof_buf) +{ + do /* loop on reply from remote stub */ + { + QUIT; /* allow user to bail out with ^C */ + getpkt (buf, sizeof_buf, 0); + if (buf[0] == 0) + error ("Target does not support this command."); + else if (buf[0] == 'E') + trace_error (buf); + else if (buf[0] == 'O' && + buf[1] != 'K') + remote_console_output (buf + 1); /* 'O' message from stub */ + else + return buf; /* here's the actual reply */ + } + while (1); +} + +/* Set tracepoint count to NUM. */ +static void +set_tracepoint_count (int num) +{ + tracepoint_count = num; + set_internalvar (lookup_internalvar ("tpnum"), + value_from_longest (builtin_type_int, (LONGEST) num)); +} + +/* Set traceframe number to NUM. */ +static void +set_traceframe_num (int num) +{ + traceframe_number = num; + set_internalvar (lookup_internalvar ("trace_frame"), + value_from_longest (builtin_type_int, (LONGEST) num)); +} + +/* Set tracepoint number to NUM. */ +static void +set_tracepoint_num (int num) +{ + tracepoint_number = num; + set_internalvar (lookup_internalvar ("tracepoint"), + value_from_longest (builtin_type_int, (LONGEST) num)); +} + +/* Set externally visible debug variables for querying/printing + the traceframe context (line, function, file) */ + +static void +set_traceframe_context (CORE_ADDR trace_pc) +{ + static struct type *func_string, *file_string; + static struct type *func_range, *file_range; + struct value *func_val; + struct value *file_val; + static struct type *charstar; + int len; + + if (charstar == (struct type *) NULL) + charstar = lookup_pointer_type (builtin_type_char); + + if (trace_pc == -1) /* cease debugging any trace buffers */ + { + traceframe_fun = 0; + traceframe_sal.pc = traceframe_sal.line = 0; + traceframe_sal.symtab = NULL; + set_internalvar (lookup_internalvar ("trace_func"), + value_from_pointer (charstar, (LONGEST) 0)); + set_internalvar (lookup_internalvar ("trace_file"), + value_from_pointer (charstar, (LONGEST) 0)); + set_internalvar (lookup_internalvar ("trace_line"), + value_from_pointer (builtin_type_int, (LONGEST) - 1)); + return; + } + + /* save as globals for internal use */ + traceframe_sal = find_pc_line (trace_pc, 0); + traceframe_fun = find_pc_function (trace_pc); + + /* save linenumber as "$trace_line", a debugger variable visible to users */ + set_internalvar (lookup_internalvar ("trace_line"), + value_from_longest (builtin_type_int, + (LONGEST) traceframe_sal.line)); + + /* save func name as "$trace_func", a debugger variable visible to users */ + if (traceframe_fun == NULL || + SYMBOL_NAME (traceframe_fun) == NULL) + set_internalvar (lookup_internalvar ("trace_func"), + value_from_pointer (charstar, (LONGEST) 0)); + else + { + len = strlen (SYMBOL_NAME (traceframe_fun)); + func_range = create_range_type (func_range, + builtin_type_int, 0, len - 1); + func_string = create_array_type (func_string, + builtin_type_char, func_range); + func_val = allocate_value (func_string); + VALUE_TYPE (func_val) = func_string; + memcpy (VALUE_CONTENTS_RAW (func_val), + SYMBOL_NAME (traceframe_fun), + len); + func_val->modifiable = 0; + set_internalvar (lookup_internalvar ("trace_func"), func_val); + } + + /* save file name as "$trace_file", a debugger variable visible to users */ + if (traceframe_sal.symtab == NULL || + traceframe_sal.symtab->filename == NULL) + set_internalvar (lookup_internalvar ("trace_file"), + value_from_pointer (charstar, (LONGEST) 0)); + else + { + len = strlen (traceframe_sal.symtab->filename); + file_range = create_range_type (file_range, + builtin_type_int, 0, len - 1); + file_string = create_array_type (file_string, + builtin_type_char, file_range); + file_val = allocate_value (file_string); + VALUE_TYPE (file_val) = file_string; + memcpy (VALUE_CONTENTS_RAW (file_val), + traceframe_sal.symtab->filename, + len); + file_val->modifiable = 0; + set_internalvar (lookup_internalvar ("trace_file"), file_val); + } +} + +/* Low level routine to set a tracepoint. + Returns the tracepoint object so caller can set other things. + Does not set the tracepoint number! + Does not print anything. + + ==> This routine should not be called if there is a chance of later + error(); otherwise it leaves a bogus tracepoint on the chain. Validate + your arguments BEFORE calling this routine! */ + +static struct tracepoint * +set_raw_tracepoint (struct symtab_and_line sal) +{ + register struct tracepoint *t, *tc; + struct cleanup *old_chain; + + t = (struct tracepoint *) xmalloc (sizeof (struct tracepoint)); + old_chain = make_cleanup (xfree, t); + memset (t, 0, sizeof (*t)); + t->address = sal.pc; + if (sal.symtab == NULL) + t->source_file = NULL; + else + t->source_file = savestring (sal.symtab->filename, + strlen (sal.symtab->filename)); + + t->section = sal.section; + t->language = current_language->la_language; + t->input_radix = input_radix; + t->line_number = sal.line; + t->enabled_p = 1; + t->next = 0; + t->step_count = 0; + t->pass_count = 0; + t->addr_string = NULL; + + /* Add this tracepoint to the end of the chain + so that a list of tracepoints will come out in order + of increasing numbers. */ + + tc = tracepoint_chain; + if (tc == 0) + tracepoint_chain = t; + else + { + while (tc->next) + tc = tc->next; + tc->next = t; + } + discard_cleanups (old_chain); + return t; +} + +/* Set a tracepoint according to ARG (function, linenum or *address) */ +static void +trace_command (char *arg, int from_tty) +{ + char **canonical = (char **) NULL; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct tracepoint *t; + char *addr_start = 0, *addr_end = 0; + int i; + + if (!arg || !*arg) + error ("trace command requires an argument"); + + if (from_tty && info_verbose) + printf_filtered ("TRACE %s\n", arg); + + addr_start = arg; + sals = decode_line_1 (&arg, 1, (struct symtab *) NULL, 0, &canonical); + addr_end = arg; + if (!sals.nelts) + return; /* ??? Presumably decode_line_1 has already warned? */ + + /* Resolve all line numbers to PC's */ + for (i = 0; i < sals.nelts; i++) + resolve_sal_pc (&sals.sals[i]); + + /* Now set all the tracepoints. */ + for (i = 0; i < sals.nelts; i++) + { + sal = sals.sals[i]; + + t = set_raw_tracepoint (sal); + set_tracepoint_count (tracepoint_count + 1); + t->number = tracepoint_count; + + /* If a canonical line spec is needed use that instead of the + command string. */ + if (canonical != (char **) NULL && canonical[i] != NULL) + t->addr_string = canonical[i]; + else if (addr_start) + t->addr_string = savestring (addr_start, addr_end - addr_start); + + trace_mention (t); + } + + if (sals.nelts > 1) + { + printf_filtered ("Multiple tracepoints were set.\n"); + printf_filtered ("Use 'delete trace' to delete unwanted tracepoints.\n"); + } +} + +/* Tell the user we have just set a tracepoint TP. */ + +static void +trace_mention (struct tracepoint *tp) +{ + printf_filtered ("Tracepoint %d", tp->number); + + if (addressprint || (tp->source_file == NULL)) + { + printf_filtered (" at "); + print_address_numeric (tp->address, 1, gdb_stdout); + } + if (tp->source_file) + printf_filtered (": file %s, line %d.", + tp->source_file, tp->line_number); + + printf_filtered ("\n"); +} + +/* Print information on tracepoint number TPNUM_EXP, or all if omitted. */ + +static void +tracepoints_info (char *tpnum_exp, int from_tty) +{ + struct tracepoint *t; + struct action_line *action; + int found_a_tracepoint = 0; + char wrap_indent[80]; + struct symbol *sym; + int tpnum = -1; + + if (tpnum_exp) + tpnum = parse_and_eval_long (tpnum_exp); + + ALL_TRACEPOINTS (t) + if (tpnum == -1 || tpnum == t->number) + { + extern int addressprint; /* print machine addresses? */ + + if (!found_a_tracepoint++) + { + printf_filtered ("Num Enb "); + if (addressprint) + { + if (TARGET_ADDR_BIT <= 32) + printf_filtered ("Address "); + else + printf_filtered ("Address "); + } + printf_filtered ("PassC StepC What\n"); + } + strcpy (wrap_indent, " "); + if (addressprint) + { + if (TARGET_ADDR_BIT <= 32) + strcat (wrap_indent, " "); + else + strcat (wrap_indent, " "); + } + + printf_filtered ("%-3d %-3s ", t->number, + t->enabled_p ? "y" : "n"); + if (addressprint) + { + char *tmp; + + if (TARGET_ADDR_BIT <= 32) + tmp = local_hex_string_custom (t->address + & (CORE_ADDR) 0xffffffff, + "08l"); + else + tmp = local_hex_string_custom (t->address, "016l"); + + printf_filtered ("%s ", tmp); + } + printf_filtered ("%-5d %-5ld ", t->pass_count, t->step_count); + + if (t->source_file) + { + sym = find_pc_sect_function (t->address, t->section); + if (sym) + { + fputs_filtered ("in ", gdb_stdout); + fputs_filtered (SYMBOL_SOURCE_NAME (sym), gdb_stdout); + wrap_here (wrap_indent); + fputs_filtered (" at ", gdb_stdout); + } + fputs_filtered (t->source_file, gdb_stdout); + printf_filtered (":%d", t->line_number); + } + else + print_address_symbolic (t->address, gdb_stdout, demangle, " "); + + printf_filtered ("\n"); + if (t->actions) + { + printf_filtered (" Actions for tracepoint %d: \n", t->number); + for (action = t->actions; action; action = action->next) + { + printf_filtered ("\t%s\n", action->action); + } + } + } + if (!found_a_tracepoint) + { + if (tpnum == -1) + printf_filtered ("No tracepoints.\n"); + else + printf_filtered ("No tracepoint number %d.\n", tpnum); + } +} + +/* Optimization: the code to parse an enable, disable, or delete TP command + is virtually identical except for whether it performs an enable, disable, + or delete. Therefore I've combined them into one function with an opcode. + */ +enum tracepoint_opcode +{ + enable_op, + disable_op, + delete_op +}; + +/* This function implements enable, disable and delete commands. */ +static void +tracepoint_operation (struct tracepoint *t, int from_tty, + enum tracepoint_opcode opcode) +{ + struct tracepoint *t2; + + if (t == NULL) /* no tracepoint operand */ + return; + + switch (opcode) + { + case enable_op: + t->enabled_p = 1; + tracepoint_modify_event (t->number); + break; + case disable_op: + t->enabled_p = 0; + tracepoint_modify_event (t->number); + break; + case delete_op: + if (tracepoint_chain == t) + tracepoint_chain = t->next; + + ALL_TRACEPOINTS (t2) + if (t2->next == t) + { + tracepoint_delete_event (t2->number); + t2->next = t->next; + break; + } + + if (t->addr_string) + xfree (t->addr_string); + if (t->source_file) + xfree (t->source_file); + if (t->actions) + free_actions (t); + + xfree (t); + break; + } +} + +/* Utility: parse a tracepoint number and look it up in the list. + If MULTI_P is true, there might be a range of tracepoints in ARG. + if OPTIONAL_P is true, then if the argument is missing, the most + recent tracepoint (tracepoint_count) is returned. */ +struct tracepoint * +get_tracepoint_by_number (char **arg, int multi_p, int optional_p) +{ + struct tracepoint *t; + int tpnum; + char *instring = arg == NULL ? NULL : *arg; + + if (arg == NULL || *arg == NULL || ! **arg) + { + if (optional_p) + tpnum = tracepoint_count; + else + error_no_arg ("tracepoint number"); + } + else + tpnum = multi_p ? get_number_or_range (arg) : get_number (arg); + + if (tpnum <= 0) + { + if (instring && *instring) + printf_filtered ("bad tracepoint number at or near '%s'\n", instring); + else + printf_filtered ("Tracepoint argument missing and no previous tracepoint\n"); + return NULL; + } + + ALL_TRACEPOINTS (t) + if (t->number == tpnum) + { + return t; + } + + /* FIXME: if we are in the middle of a range we don't want to give + a message. The current interface to get_number_or_range doesn't + allow us to discover this. */ + printf_unfiltered ("No tracepoint number %d.\n", tpnum); + return NULL; +} + +/* Utility: parse a list of tracepoint numbers, and call a func for each. */ +static void +map_args_over_tracepoints (char *args, int from_tty, + enum tracepoint_opcode opcode) +{ + struct tracepoint *t, *tmp; + + if (args == 0 || *args == 0) /* do them all */ + ALL_TRACEPOINTS_SAFE (t, tmp) + tracepoint_operation (t, from_tty, opcode); + else + while (*args) + { + QUIT; /* give user option to bail out with ^C */ + t = get_tracepoint_by_number (&args, 1, 0); + tracepoint_operation (t, from_tty, opcode); + while (*args == ' ' || *args == '\t') + args++; + } +} + +/* The 'enable trace' command enables tracepoints. Not supported by all targets. */ +static void +enable_trace_command (char *args, int from_tty) +{ + dont_repeat (); + map_args_over_tracepoints (args, from_tty, enable_op); +} + +/* The 'disable trace' command enables tracepoints. Not supported by all targets. */ +static void +disable_trace_command (char *args, int from_tty) +{ + dont_repeat (); + map_args_over_tracepoints (args, from_tty, disable_op); +} + +/* Remove a tracepoint (or all if no argument) */ +static void +delete_trace_command (char *args, int from_tty) +{ + dont_repeat (); + if (!args || !*args) /* No args implies all tracepoints; */ + if (from_tty) /* confirm only if from_tty... */ + if (tracepoint_chain) /* and if there are tracepoints to delete! */ + if (!query ("Delete all tracepoints? ")) + return; + + map_args_over_tracepoints (args, from_tty, delete_op); +} + +/* Set passcount for tracepoint. + + First command argument is passcount, second is tracepoint number. + If tracepoint number omitted, apply to most recently defined. + Also accepts special argument "all". */ + +static void +trace_pass_command (char *args, int from_tty) +{ + struct tracepoint *t1 = (struct tracepoint *) -1, *t2; + unsigned int count; + int all = 0; + + if (args == 0 || *args == 0) + error ("passcount command requires an argument (count + optional TP num)"); + + count = strtoul (args, &args, 10); /* count comes first, then TP num */ + + while (*args && isspace ((int) *args)) + args++; + + if (*args && strncasecmp (args, "all", 3) == 0) + { + args += 3; /* skip special argument "all" */ + all = 1; + if (*args) + error ("Junk at end of arguments."); + } + else + t1 = get_tracepoint_by_number (&args, 1, 1); + + do + { + if (t1) + { + ALL_TRACEPOINTS (t2) + if (t1 == (struct tracepoint *) -1 || t1 == t2) + { + t2->pass_count = count; + tracepoint_modify_event (t2->number); + if (from_tty) + printf_filtered ("Setting tracepoint %d's passcount to %d\n", + t2->number, count); + } + if (! all && *args) + t1 = get_tracepoint_by_number (&args, 1, 0); + } + } + while (*args); +} + +/* ACTIONS functions: */ + +/* Prototypes for action-parsing utility commands */ +static void read_actions (struct tracepoint *); + +/* The three functions: + collect_pseudocommand, + while_stepping_pseudocommand, and + end_actions_pseudocommand + are placeholders for "commands" that are actually ONLY to be used + within a tracepoint action list. If the actual function is ever called, + it means that somebody issued the "command" at the top level, + which is always an error. */ + +static void +end_actions_pseudocommand (char *args, int from_tty) +{ + error ("This command cannot be used at the top level."); +} + +static void +while_stepping_pseudocommand (char *args, int from_tty) +{ + error ("This command can only be used in a tracepoint actions list."); +} + +static void +collect_pseudocommand (char *args, int from_tty) +{ + error ("This command can only be used in a tracepoint actions list."); +} + +/* Enter a list of actions for a tracepoint. */ +static void +trace_actions_command (char *args, int from_tty) +{ + struct tracepoint *t; + char tmpbuf[128]; + char *end_msg = "End with a line saying just \"end\"."; + + t = get_tracepoint_by_number (&args, 0, 1); + if (t) + { + sprintf (tmpbuf, "Enter actions for tracepoint %d, one per line.", + t->number); + + if (from_tty) + { + if (readline_begin_hook) + (*readline_begin_hook) ("%s %s\n", tmpbuf, end_msg); + else if (input_from_terminal_p ()) + printf_filtered ("%s\n%s\n", tmpbuf, end_msg); + } + + free_actions (t); + t->step_count = 0; /* read_actions may set this */ + read_actions (t); + + if (readline_end_hook) + (*readline_end_hook) (); + /* tracepoints_changed () */ + } + /* else just return */ +} + +/* worker function */ +static void +read_actions (struct tracepoint *t) +{ + char *line; + char *prompt1 = "> ", *prompt2 = " > "; + char *prompt = prompt1; + enum actionline_type linetype; + extern FILE *instream; + struct action_line *next = NULL, *temp; + struct cleanup *old_chain; + + /* Control-C quits instantly if typed while in this loop + since it should not wait until the user types a newline. */ + immediate_quit++; + /* FIXME: kettenis/20010823: Something is wrong here. In this file + STOP_SIGNAL is never defined. So this code has been left out, at + least for quite a while now. Replacing STOP_SIGNAL with SIGTSTP + leads to compilation failures since the variable job_control + isn't declared. Leave this alone for now. */ +#ifdef STOP_SIGNAL + if (job_control) + { + if (event_loop_p) + signal (STOP_SIGNAL, handle_stop_sig); + else + signal (STOP_SIGNAL, stop_sig); + } +#endif + old_chain = make_cleanup_free_actions (t); + while (1) + { + /* Make sure that all output has been output. Some machines may let + you get away with leaving out some of the gdb_flush, but not all. */ + wrap_here (""); + gdb_flush (gdb_stdout); + gdb_flush (gdb_stderr); + + if (readline_hook && instream == NULL) + line = (*readline_hook) (prompt); + else if (instream == stdin && ISATTY (instream)) + { + line = gdb_readline_wrapper (prompt); + if (line && *line) /* add it to command history */ + add_history (line); + } + else + line = gdb_readline (0); + + linetype = validate_actionline (&line, t); + if (linetype == BADLINE) + continue; /* already warned -- collect another line */ + + temp = xmalloc (sizeof (struct action_line)); + temp->next = NULL; + temp->action = line; + + if (next == NULL) /* first action for this tracepoint? */ + t->actions = next = temp; + else + { + next->next = temp; + next = temp; + } + + if (linetype == STEPPING) /* begin "while-stepping" */ + { + if (prompt == prompt2) + { + warning ("Already processing 'while-stepping'"); + continue; + } + else + prompt = prompt2; /* change prompt for stepping actions */ + } + else if (linetype == END) + { + if (prompt == prompt2) + { + prompt = prompt1; /* end of single-stepping actions */ + } + else + { /* end of actions */ + if (t->actions->next == NULL) + { + /* an "end" all by itself with no other actions means + this tracepoint has no actions. Discard empty list. */ + free_actions (t); + } + break; + } + } + } +#ifdef STOP_SIGNAL + if (job_control) + signal (STOP_SIGNAL, SIG_DFL); +#endif + immediate_quit--; + discard_cleanups (old_chain); +} + +/* worker function */ +enum actionline_type +validate_actionline (char **line, struct tracepoint *t) +{ + struct cmd_list_element *c; + struct expression *exp = NULL; + struct cleanup *old_chain = NULL; + char *p; + + for (p = *line; isspace ((int) *p);) + p++; + + /* symbol lookup etc. */ + if (*p == '\0') /* empty line: just prompt for another line. */ + return BADLINE; + + if (*p == '#') /* comment line */ + return GENERIC; + + c = lookup_cmd (&p, cmdlist, "", -1, 1); + if (c == 0) + { + warning ("'%s' is not an action that I know, or is ambiguous.", p); + return BADLINE; + } + + if (cmd_cfunc_eq (c, collect_pseudocommand)) + { + struct agent_expr *aexpr; + struct agent_reqs areqs; + + do + { /* repeat over a comma-separated list */ + QUIT; /* allow user to bail out with ^C */ + while (isspace ((int) *p)) + p++; + + if (*p == '$') /* look for special pseudo-symbols */ + { + if ((0 == strncasecmp ("reg", p + 1, 3)) || + (0 == strncasecmp ("arg", p + 1, 3)) || + (0 == strncasecmp ("loc", p + 1, 3))) + { + p = strchr (p, ','); + continue; + } + /* else fall thru, treat p as an expression and parse it! */ + } + exp = parse_exp_1 (&p, block_for_pc (t->address), 1); + old_chain = make_cleanup (free_current_contents, &exp); + + if (exp->elts[0].opcode == OP_VAR_VALUE) + { + if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_CONST) + { + warning ("constant %s (value %ld) will not be collected.", + SYMBOL_NAME (exp->elts[2].symbol), + SYMBOL_VALUE (exp->elts[2].symbol)); + return BADLINE; + } + else if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_OPTIMIZED_OUT) + { + warning ("%s is optimized away and cannot be collected.", + SYMBOL_NAME (exp->elts[2].symbol)); + return BADLINE; + } + } + + /* we have something to collect, make sure that the expr to + bytecode translator can handle it and that it's not too long */ + aexpr = gen_trace_for_expr (t->address, exp); + make_cleanup_free_agent_expr (aexpr); + + if (aexpr->len > MAX_AGENT_EXPR_LEN) + error ("expression too complicated, try simplifying"); + + ax_reqs (aexpr, &areqs); + (void) make_cleanup (xfree, areqs.reg_mask); + + if (areqs.flaw != agent_flaw_none) + error ("malformed expression"); + + if (areqs.min_height < 0) + error ("gdb: Internal error: expression has min height < 0"); + + if (areqs.max_height > 20) + error ("expression too complicated, try simplifying"); + + do_cleanups (old_chain); + } + while (p && *p++ == ','); + return GENERIC; + } + else if (cmd_cfunc_eq (c, while_stepping_pseudocommand)) + { + char *steparg; /* in case warning is necessary */ + + while (isspace ((int) *p)) + p++; + steparg = p; + + if (*p == '\0' || + (t->step_count = strtol (p, &p, 0)) == 0) + { + warning ("'%s': bad step-count; command ignored.", *line); + return BADLINE; + } + return STEPPING; + } + else if (cmd_cfunc_eq (c, end_actions_pseudocommand)) + return END; + else + { + warning ("'%s' is not a supported tracepoint action.", *line); + return BADLINE; + } +} + +/* worker function */ +void +free_actions (struct tracepoint *t) +{ + struct action_line *line, *next; + + for (line = t->actions; line; line = next) + { + next = line->next; + if (line->action) + xfree (line->action); + xfree (line); + } + t->actions = NULL; +} + +static void +do_free_actions_cleanup (void *t) +{ + free_actions (t); +} + +static struct cleanup * +make_cleanup_free_actions (struct tracepoint *t) +{ + return make_cleanup (do_free_actions_cleanup, t); +} + +struct memrange +{ + int type; /* 0 for absolute memory range, else basereg number */ + bfd_signed_vma start; + bfd_signed_vma end; +}; + +struct collection_list + { + unsigned char regs_mask[8]; /* room for up to 256 regs */ + long listsize; + long next_memrange; + struct memrange *list; + long aexpr_listsize; /* size of array pointed to by expr_list elt */ + long next_aexpr_elt; + struct agent_expr **aexpr_list; + + } +tracepoint_list, stepping_list; + +/* MEMRANGE functions: */ + +static int memrange_cmp (const void *, const void *); + +/* compare memranges for qsort */ +static int +memrange_cmp (const void *va, const void *vb) +{ + const struct memrange *a = va, *b = vb; + + if (a->type < b->type) + return -1; + if (a->type > b->type) + return 1; + if (a->type == 0) + { + if ((bfd_vma) a->start < (bfd_vma) b->start) + return -1; + if ((bfd_vma) a->start > (bfd_vma) b->start) + return 1; + } + else + { + if (a->start < b->start) + return -1; + if (a->start > b->start) + return 1; + } + return 0; +} + +/* Sort the memrange list using qsort, and merge adjacent memranges */ +static void +memrange_sortmerge (struct collection_list *memranges) +{ + int a, b; + + qsort (memranges->list, memranges->next_memrange, + sizeof (struct memrange), memrange_cmp); + if (memranges->next_memrange > 0) + { + for (a = 0, b = 1; b < memranges->next_memrange; b++) + { + if (memranges->list[a].type == memranges->list[b].type && + memranges->list[b].start - memranges->list[a].end <= + MAX_REGISTER_VIRTUAL_SIZE) + { + /* memrange b starts before memrange a ends; merge them. */ + if (memranges->list[b].end > memranges->list[a].end) + memranges->list[a].end = memranges->list[b].end; + continue; /* next b, same a */ + } + a++; /* next a */ + if (a != b) + memcpy (&memranges->list[a], &memranges->list[b], + sizeof (struct memrange)); + } + memranges->next_memrange = a + 1; + } +} + +/* Add a register to a collection list */ +static void +add_register (struct collection_list *collection, unsigned int regno) +{ + if (info_verbose) + printf_filtered ("collect register %d\n", regno); + if (regno > (8 * sizeof (collection->regs_mask))) + error ("Internal: register number %d too large for tracepoint", + regno); + collection->regs_mask[regno / 8] |= 1 << (regno % 8); +} + +/* Add a memrange to a collection list */ +static void +add_memrange (struct collection_list *memranges, int type, bfd_signed_vma base, + unsigned long len) +{ + if (info_verbose) + { + printf_filtered ("(%d,", type); + printf_vma (base); + printf_filtered (",%ld)\n", len); + } + + /* type: 0 == memory, n == basereg */ + memranges->list[memranges->next_memrange].type = type; + /* base: addr if memory, offset if reg relative. */ + memranges->list[memranges->next_memrange].start = base; + /* len: we actually save end (base + len) for convenience */ + memranges->list[memranges->next_memrange].end = base + len; + memranges->next_memrange++; + if (memranges->next_memrange >= memranges->listsize) + { + memranges->listsize *= 2; + memranges->list = xrealloc (memranges->list, + memranges->listsize); + } + + if (type != -1) /* better collect the base register! */ + add_register (memranges, type); +} + +/* Add a symbol to a collection list */ +static void +collect_symbol (struct collection_list *collect, struct symbol *sym, + long frame_regno, long frame_offset) +{ + unsigned long len; + unsigned int reg; + bfd_signed_vma offset; + + len = TYPE_LENGTH (check_typedef (SYMBOL_TYPE (sym))); + switch (SYMBOL_CLASS (sym)) + { + default: + printf_filtered ("%s: don't know symbol class %d\n", + SYMBOL_NAME (sym), SYMBOL_CLASS (sym)); + break; + case LOC_CONST: + printf_filtered ("constant %s (value %ld) will not be collected.\n", + SYMBOL_NAME (sym), SYMBOL_VALUE (sym)); + break; + case LOC_STATIC: + offset = SYMBOL_VALUE_ADDRESS (sym); + if (info_verbose) + { + char tmp[40]; + + sprintf_vma (tmp, offset); + printf_filtered ("LOC_STATIC %s: collect %ld bytes at %s.\n", + SYMBOL_NAME (sym), len, tmp /* address */); + } + add_memrange (collect, -1, offset, len); /* 0 == memory */ + break; + case LOC_REGISTER: + case LOC_REGPARM: + reg = SYMBOL_VALUE (sym); + if (info_verbose) + printf_filtered ("LOC_REG[parm] %s: ", SYMBOL_NAME (sym)); + add_register (collect, reg); + /* check for doubles stored in two registers */ + /* FIXME: how about larger types stored in 3 or more regs? */ + if (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_FLT && + len > REGISTER_RAW_SIZE (reg)) + add_register (collect, reg + 1); + break; + case LOC_REF_ARG: + printf_filtered ("Sorry, don't know how to do LOC_REF_ARG yet.\n"); + printf_filtered (" (will not collect %s)\n", + SYMBOL_NAME (sym)); + break; + case LOC_ARG: + reg = frame_regno; + offset = frame_offset + SYMBOL_VALUE (sym); + if (info_verbose) + { + printf_filtered ("LOC_LOCAL %s: Collect %ld bytes at offset ", + SYMBOL_NAME (sym), len); + printf_vma (offset); + printf_filtered (" from frame ptr reg %d\n", reg); + } + add_memrange (collect, reg, offset, len); + break; + case LOC_REGPARM_ADDR: + reg = SYMBOL_VALUE (sym); + offset = 0; + if (info_verbose) + { + printf_filtered ("LOC_REGPARM_ADDR %s: Collect %ld bytes at offset ", + SYMBOL_NAME (sym), len); + printf_vma (offset); + printf_filtered (" from reg %d\n", reg); + } + add_memrange (collect, reg, offset, len); + break; + case LOC_LOCAL: + case LOC_LOCAL_ARG: + reg = frame_regno; + offset = frame_offset + SYMBOL_VALUE (sym); + if (info_verbose) + { + printf_filtered ("LOC_LOCAL %s: Collect %ld bytes at offset ", + SYMBOL_NAME (sym), len); + printf_vma (offset); + printf_filtered (" from frame ptr reg %d\n", reg); + } + add_memrange (collect, reg, offset, len); + break; + case LOC_BASEREG: + case LOC_BASEREG_ARG: + reg = SYMBOL_BASEREG (sym); + offset = SYMBOL_VALUE (sym); + if (info_verbose) + { + printf_filtered ("LOC_BASEREG %s: collect %ld bytes at offset ", + SYMBOL_NAME (sym), len); + printf_vma (offset); + printf_filtered (" from basereg %d\n", reg); + } + add_memrange (collect, reg, offset, len); + break; + case LOC_UNRESOLVED: + printf_filtered ("Don't know LOC_UNRESOLVED %s\n", SYMBOL_NAME (sym)); + break; + case LOC_OPTIMIZED_OUT: + printf_filtered ("%s has been optimized out of existence.\n", + SYMBOL_NAME (sym)); + break; + } +} + +/* Add all locals (or args) symbols to collection list */ +static void +add_local_symbols (struct collection_list *collect, CORE_ADDR pc, + long frame_regno, long frame_offset, int type) +{ + struct symbol *sym; + struct block *block; + int i, count = 0; + + block = block_for_pc (pc); + while (block != 0) + { + QUIT; /* allow user to bail out with ^C */ + ALL_BLOCK_SYMBOLS (block, i, sym) + { + switch (SYMBOL_CLASS (sym)) + { + default: + warning ("don't know how to trace local symbol %s", + SYMBOL_NAME (sym)); + case LOC_LOCAL: + case LOC_STATIC: + case LOC_REGISTER: + case LOC_BASEREG: + if (type == 'L') /* collecting Locals */ + { + count++; + collect_symbol (collect, sym, frame_regno, frame_offset); + } + break; + case LOC_ARG: + case LOC_LOCAL_ARG: + case LOC_REF_ARG: + case LOC_REGPARM: + case LOC_REGPARM_ADDR: + case LOC_BASEREG_ARG: + if (type == 'A') /* collecting Arguments */ + { + count++; + collect_symbol (collect, sym, frame_regno, frame_offset); + } + } + } + if (BLOCK_FUNCTION (block)) + break; + else + block = BLOCK_SUPERBLOCK (block); + } + if (count == 0) + warning ("No %s found in scope.", type == 'L' ? "locals" : "args"); +} + +/* worker function */ +static void +clear_collection_list (struct collection_list *list) +{ + int ndx; + + list->next_memrange = 0; + for (ndx = 0; ndx < list->next_aexpr_elt; ndx++) + { + free_agent_expr (list->aexpr_list[ndx]); + list->aexpr_list[ndx] = NULL; + } + list->next_aexpr_elt = 0; + memset (list->regs_mask, 0, sizeof (list->regs_mask)); +} + +/* reduce a collection list to string form (for gdb protocol) */ +static char ** +stringify_collection_list (struct collection_list *list, char *string) +{ + char temp_buf[2048]; + char tmp2[40]; + int count; + int ndx = 0; + char *(*str_list)[]; + char *end; + long i; + + count = 1 + list->next_memrange + list->next_aexpr_elt + 1; + str_list = (char *(*)[]) xmalloc (count * sizeof (char *)); + + for (i = sizeof (list->regs_mask) - 1; i > 0; i--) + if (list->regs_mask[i] != 0) /* skip leading zeroes in regs_mask */ + break; + if (list->regs_mask[i] != 0) /* prepare to send regs_mask to the stub */ + { + if (info_verbose) + printf_filtered ("\nCollecting registers (mask): 0x"); + end = temp_buf; + *end++ = 'R'; + for (; i >= 0; i--) + { + QUIT; /* allow user to bail out with ^C */ + if (info_verbose) + printf_filtered ("%02X", list->regs_mask[i]); + sprintf (end, "%02X", list->regs_mask[i]); + end += 2; + } + (*str_list)[ndx] = savestring (temp_buf, end - temp_buf); + ndx++; + } + if (info_verbose) + printf_filtered ("\n"); + if (list->next_memrange > 0 && info_verbose) + printf_filtered ("Collecting memranges: \n"); + for (i = 0, count = 0, end = temp_buf; i < list->next_memrange; i++) + { + QUIT; /* allow user to bail out with ^C */ + sprintf_vma (tmp2, list->list[i].start); + if (info_verbose) + { + printf_filtered ("(%d, %s, %ld)\n", + list->list[i].type, + tmp2, + (long) (list->list[i].end - list->list[i].start)); + } + if (count + 27 > MAX_AGENT_EXPR_LEN) + { + (*str_list)[ndx] = savestring (temp_buf, count); + ndx++; + count = 0; + end = temp_buf; + } + + sprintf (end, "M%X,%s,%lX", + list->list[i].type, + tmp2, + (long) (list->list[i].end - list->list[i].start)); + + count += strlen (end); + end += count; + } + + for (i = 0; i < list->next_aexpr_elt; i++) + { + QUIT; /* allow user to bail out with ^C */ + if ((count + 10 + 2 * list->aexpr_list[i]->len) > MAX_AGENT_EXPR_LEN) + { + (*str_list)[ndx] = savestring (temp_buf, count); + ndx++; + count = 0; + end = temp_buf; + } + sprintf (end, "X%08X,", list->aexpr_list[i]->len); + end += 10; /* 'X' + 8 hex digits + ',' */ + count += 10; + + end = mem2hex (list->aexpr_list[i]->buf, end, list->aexpr_list[i]->len); + count += 2 * list->aexpr_list[i]->len; + } + + if (count != 0) + { + (*str_list)[ndx] = savestring (temp_buf, count); + ndx++; + count = 0; + end = temp_buf; + } + (*str_list)[ndx] = NULL; + + if (ndx == 0) + return NULL; + else + return *str_list; +} + +static void +free_actions_list_cleanup_wrapper (void *al) +{ + free_actions_list (al); +} + +static void +free_actions_list (char **actions_list) +{ + int ndx; + + if (actions_list == 0) + return; + + for (ndx = 0; actions_list[ndx]; ndx++) + xfree (actions_list[ndx]); + + xfree (actions_list); +} + +/* render all actions into gdb protocol */ +static void +encode_actions (struct tracepoint *t, char ***tdp_actions, + char ***stepping_actions) +{ + static char tdp_buff[2048], step_buff[2048]; + char *action_exp; + struct expression *exp = NULL; + struct action_line *action; + int i; + struct value *tempval; + struct collection_list *collect; + struct cmd_list_element *cmd; + struct agent_expr *aexpr; + int frame_reg; + LONGEST frame_offset; + + + clear_collection_list (&tracepoint_list); + clear_collection_list (&stepping_list); + collect = &tracepoint_list; + + *tdp_actions = NULL; + *stepping_actions = NULL; + + TARGET_VIRTUAL_FRAME_POINTER (t->address, &frame_reg, &frame_offset); + + for (action = t->actions; action; action = action->next) + { + QUIT; /* allow user to bail out with ^C */ + action_exp = action->action; + while (isspace ((int) *action_exp)) + action_exp++; + + if (*action_exp == '#') /* comment line */ + return; + + cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1); + if (cmd == 0) + error ("Bad action list item: %s", action_exp); + + if (cmd_cfunc_eq (cmd, collect_pseudocommand)) + { + do + { /* repeat over a comma-separated list */ + QUIT; /* allow user to bail out with ^C */ + while (isspace ((int) *action_exp)) + action_exp++; + + if (0 == strncasecmp ("$reg", action_exp, 4)) + { + for (i = 0; i < NUM_REGS; i++) + add_register (collect, i); + action_exp = strchr (action_exp, ','); /* more? */ + } + else if (0 == strncasecmp ("$arg", action_exp, 4)) + { + add_local_symbols (collect, + t->address, + frame_reg, + frame_offset, + 'A'); + action_exp = strchr (action_exp, ','); /* more? */ + } + else if (0 == strncasecmp ("$loc", action_exp, 4)) + { + add_local_symbols (collect, + t->address, + frame_reg, + frame_offset, + 'L'); + action_exp = strchr (action_exp, ','); /* more? */ + } + else + { + unsigned long addr, len; + struct cleanup *old_chain = NULL; + struct cleanup *old_chain1 = NULL; + struct agent_reqs areqs; + + exp = parse_exp_1 (&action_exp, + block_for_pc (t->address), 1); + old_chain = make_cleanup (free_current_contents, &exp); + + switch (exp->elts[0].opcode) + { + case OP_REGISTER: + i = exp->elts[1].longconst; + if (info_verbose) + printf_filtered ("OP_REGISTER: "); + add_register (collect, i); + break; + + case UNOP_MEMVAL: + /* safe because we know it's a simple expression */ + tempval = evaluate_expression (exp); + addr = VALUE_ADDRESS (tempval) + VALUE_OFFSET (tempval); + len = TYPE_LENGTH (check_typedef (exp->elts[1].type)); + add_memrange (collect, -1, addr, len); + break; + + case OP_VAR_VALUE: + collect_symbol (collect, + exp->elts[2].symbol, + frame_reg, + frame_offset); + break; + + default: /* full-fledged expression */ + aexpr = gen_trace_for_expr (t->address, exp); + + old_chain1 = make_cleanup_free_agent_expr (aexpr); + + ax_reqs (aexpr, &areqs); + if (areqs.flaw != agent_flaw_none) + error ("malformed expression"); + + if (areqs.min_height < 0) + error ("gdb: Internal error: expression has min height < 0"); + if (areqs.max_height > 20) + error ("expression too complicated, try simplifying"); + + discard_cleanups (old_chain1); + add_aexpr (collect, aexpr); + + /* take care of the registers */ + if (areqs.reg_mask_len > 0) + { + int ndx1; + int ndx2; + + for (ndx1 = 0; ndx1 < areqs.reg_mask_len; ndx1++) + { + QUIT; /* allow user to bail out with ^C */ + if (areqs.reg_mask[ndx1] != 0) + { + /* assume chars have 8 bits */ + for (ndx2 = 0; ndx2 < 8; ndx2++) + if (areqs.reg_mask[ndx1] & (1 << ndx2)) + /* it's used -- record it */ + add_register (collect, ndx1 * 8 + ndx2); + } + } + } + break; + } /* switch */ + do_cleanups (old_chain); + } /* do */ + } + while (action_exp && *action_exp++ == ','); + } /* if */ + else if (cmd_cfunc_eq (cmd, while_stepping_pseudocommand)) + { + collect = &stepping_list; + } + else if (cmd_cfunc_eq (cmd, end_actions_pseudocommand)) + { + if (collect == &stepping_list) /* end stepping actions */ + collect = &tracepoint_list; + else + break; /* end tracepoint actions */ + } + } /* for */ + memrange_sortmerge (&tracepoint_list); + memrange_sortmerge (&stepping_list); + + *tdp_actions = stringify_collection_list (&tracepoint_list, tdp_buff); + *stepping_actions = stringify_collection_list (&stepping_list, step_buff); +} + +static void +add_aexpr (struct collection_list *collect, struct agent_expr *aexpr) +{ + if (collect->next_aexpr_elt >= collect->aexpr_listsize) + { + collect->aexpr_list = + xrealloc (collect->aexpr_list, + 2 * collect->aexpr_listsize * sizeof (struct agent_expr *)); + collect->aexpr_listsize *= 2; + } + collect->aexpr_list[collect->next_aexpr_elt] = aexpr; + collect->next_aexpr_elt++; +} + +static char target_buf[2048]; + +/* Set "transparent" memory ranges + + Allow trace mechanism to treat text-like sections + (and perhaps all read-only sections) transparently, + i.e. don't reject memory requests from these address ranges + just because they haven't been collected. */ + +static void +remote_set_transparent_ranges (void) +{ + extern bfd *exec_bfd; + asection *s; + bfd_size_type size; + bfd_vma lma; + int anysecs = 0; + + if (!exec_bfd) + return; /* no information to give. */ + + strcpy (target_buf, "QTro"); + for (s = exec_bfd->sections; s; s = s->next) + { + char tmp1[40], tmp2[40]; + + if ((s->flags & SEC_LOAD) == 0 || + /* (s->flags & SEC_CODE) == 0 || */ + (s->flags & SEC_READONLY) == 0) + continue; + + anysecs = 1; + lma = s->lma; + size = bfd_get_section_size_before_reloc (s); + sprintf_vma (tmp1, lma); + sprintf_vma (tmp2, lma + size); + sprintf (target_buf + strlen (target_buf), + ":%s,%s", tmp1, tmp2); + } + if (anysecs) + { + putpkt (target_buf); + getpkt (target_buf, sizeof (target_buf), 0); + } +} + +/* tstart command: + + Tell target to clear any previous trace experiment. + Walk the list of tracepoints, and send them (and their actions) + to the target. If no errors, + Tell target to start a new trace experiment. */ + +static void +trace_start_command (char *args, int from_tty) +{ /* STUB_COMM MOSTLY_IMPLEMENTED */ + struct tracepoint *t; + char buf[2048]; + char **tdp_actions; + char **stepping_actions; + int ndx; + struct cleanup *old_chain = NULL; + + dont_repeat (); /* like "run", dangerous to repeat accidentally */ + + if (target_is_remote ()) + { + putpkt ("QTinit"); + remote_get_noisy_reply (target_buf, sizeof (target_buf)); + if (strcmp (target_buf, "OK")) + error ("Target does not support this command."); + + ALL_TRACEPOINTS (t) + { + char tmp[40]; + + sprintf_vma (tmp, t->address); + sprintf (buf, "QTDP:%x:%s:%c:%lx:%x", t->number, tmp, /* address */ + t->enabled_p ? 'E' : 'D', + t->step_count, t->pass_count); + + if (t->actions) + strcat (buf, "-"); + putpkt (buf); + remote_get_noisy_reply (target_buf, sizeof (target_buf)); + if (strcmp (target_buf, "OK")) + error ("Target does not support tracepoints."); + + if (t->actions) + { + encode_actions (t, &tdp_actions, &stepping_actions); + old_chain = make_cleanup (free_actions_list_cleanup_wrapper, + tdp_actions); + (void) make_cleanup (free_actions_list_cleanup_wrapper, + stepping_actions); + + /* do_single_steps (t); */ + if (tdp_actions) + { + for (ndx = 0; tdp_actions[ndx]; ndx++) + { + QUIT; /* allow user to bail out with ^C */ + sprintf (buf, "QTDP:-%x:%s:%s%c", + t->number, tmp, /* address */ + tdp_actions[ndx], + ((tdp_actions[ndx + 1] || stepping_actions) + ? '-' : 0)); + putpkt (buf); + remote_get_noisy_reply (target_buf, sizeof (target_buf)); + if (strcmp (target_buf, "OK")) + error ("Error on target while setting tracepoints."); + } + } + if (stepping_actions) + { + for (ndx = 0; stepping_actions[ndx]; ndx++) + { + QUIT; /* allow user to bail out with ^C */ + sprintf (buf, "QTDP:-%x:%s:%s%s%s", + t->number, tmp, /* address */ + ((ndx == 0) ? "S" : ""), + stepping_actions[ndx], + (stepping_actions[ndx + 1] ? "-" : "")); + putpkt (buf); + remote_get_noisy_reply (target_buf, sizeof (target_buf)); + if (strcmp (target_buf, "OK")) + error ("Error on target while setting tracepoints."); + } + } + + do_cleanups (old_chain); + } + } + /* Tell target to treat text-like sections as transparent */ + remote_set_transparent_ranges (); + /* Now insert traps and begin collecting data */ + putpkt ("QTStart"); + remote_get_noisy_reply (target_buf, sizeof (target_buf)); + if (strcmp (target_buf, "OK")) + error ("Bogus reply from target: %s", target_buf); + set_traceframe_num (-1); /* all old traceframes invalidated */ + set_tracepoint_num (-1); + set_traceframe_context (-1); + trace_running_p = 1; + if (trace_start_stop_hook) + trace_start_stop_hook (1, from_tty); + + } + else + error ("Trace can only be run on remote targets."); +} + +/* tstop command */ +static void +trace_stop_command (char *args, int from_tty) +{ /* STUB_COMM IS_IMPLEMENTED */ + if (target_is_remote ()) + { + putpkt ("QTStop"); + remote_get_noisy_reply (target_buf, sizeof (target_buf)); + if (strcmp (target_buf, "OK")) + error ("Bogus reply from target: %s", target_buf); + trace_running_p = 0; + if (trace_start_stop_hook) + trace_start_stop_hook (0, from_tty); + } + else + error ("Trace can only be run on remote targets."); +} + +unsigned long trace_running_p; + +/* tstatus command */ +static void +trace_status_command (char *args, int from_tty) +{ /* STUB_COMM IS_IMPLEMENTED */ + if (target_is_remote ()) + { + putpkt ("qTStatus"); + remote_get_noisy_reply (target_buf, sizeof (target_buf)); + + if (target_buf[0] != 'T' || + (target_buf[1] != '0' && target_buf[1] != '1')) + error ("Bogus reply from target: %s", target_buf); + + /* exported for use by the GUI */ + trace_running_p = (target_buf[1] == '1'); + } + else + error ("Trace can only be run on remote targets."); +} + +/* Worker function for the various flavors of the tfind command */ +static void +finish_tfind_command (char *msg, + long sizeof_msg, + int from_tty) +{ + int target_frameno = -1, target_tracept = -1; + CORE_ADDR old_frame_addr; + struct symbol *old_func; + char *reply; + + old_frame_addr = FRAME_FP (get_current_frame ()); + old_func = find_pc_function (read_pc ()); + + putpkt (msg); + reply = remote_get_noisy_reply (msg, sizeof_msg); + + while (reply && *reply) + switch (*reply) + { + case 'F': + if ((target_frameno = (int) strtol (++reply, &reply, 16)) == -1) + { + /* A request for a non-existant trace frame has failed. + Our response will be different, depending on FROM_TTY: + + If FROM_TTY is true, meaning that this command was + typed interactively by the user, then give an error + and DO NOT change the state of traceframe_number etc. + + However if FROM_TTY is false, meaning that we're either + in a script, a loop, or a user-defined command, then + DON'T give an error, but DO change the state of + traceframe_number etc. to invalid. + + The rationalle is that if you typed the command, you + might just have committed a typo or something, and you'd + like to NOT lose your current debugging state. However + if you're in a user-defined command or especially in a + loop, then you need a way to detect that the command + failed WITHOUT aborting. This allows you to write + scripts that search thru the trace buffer until the end, + and then continue on to do something else. */ + + if (from_tty) + error ("Target failed to find requested trace frame."); + else + { + if (info_verbose) + printf_filtered ("End of trace buffer.\n"); + /* The following will not recurse, since it's special-cased */ + trace_find_command ("-1", from_tty); + reply = NULL; /* break out of loop, + (avoid recursive nonsense) */ + } + } + break; + case 'T': + if ((target_tracept = (int) strtol (++reply, &reply, 16)) == -1) + error ("Target failed to find requested trace frame."); + break; + case 'O': /* "OK"? */ + if (reply[1] == 'K' && reply[2] == '\0') + reply += 2; + else + error ("Bogus reply from target: %s", reply); + break; + default: + error ("Bogus reply from target: %s", reply); + } + + flush_cached_frames (); + registers_changed (); + select_frame (get_current_frame ()); + set_traceframe_num (target_frameno); + set_tracepoint_num (target_tracept); + if (target_frameno == -1) + set_traceframe_context (-1); + else + set_traceframe_context (read_pc ()); + + if (from_tty) + { + int source_only; + + /* NOTE: in immitation of the step command, try to determine + whether we have made a transition from one function to another. + If so, we'll print the "stack frame" (ie. the new function and + it's arguments) -- otherwise we'll just show the new source line. + + This determination is made by checking (1) whether the current + function has changed, and (2) whether the current FP has changed. + Hack: if the FP wasn't collected, either at the current or the + previous frame, assume that the FP has NOT changed. */ + + if (old_func == find_pc_function (read_pc ()) && + (old_frame_addr == 0 || + FRAME_FP (get_current_frame ()) == 0 || + old_frame_addr == FRAME_FP (get_current_frame ()))) + source_only = -1; + else + source_only = 1; + + print_stack_frame (selected_frame, frame_relative_level (selected_frame), + source_only); + do_displays (); + } +} + +/* trace_find_command takes a trace frame number n, + sends "QTFrame:" to the target, + and accepts a reply that may contain several optional pieces + of information: a frame number, a tracepoint number, and an + indication of whether this is a trap frame or a stepping frame. + + The minimal response is just "OK" (which indicates that the + target does not give us a frame number or a tracepoint number). + Instead of that, the target may send us a string containing + any combination of: + F (gives the selected frame number) + T (gives the selected tracepoint number) + */ + +/* tfind command */ +static void +trace_find_command (char *args, int from_tty) +{ /* STUB_COMM PART_IMPLEMENTED */ + /* this should only be called with a numeric argument */ + int frameno = -1; + + if (target_is_remote ()) + { + if (trace_find_hook) + trace_find_hook (args, from_tty); + + if (args == 0 || *args == 0) + { /* TFIND with no args means find NEXT trace frame. */ + if (traceframe_number == -1) + frameno = 0; /* "next" is first one */ + else + frameno = traceframe_number + 1; + } + else if (0 == strcmp (args, "-")) + { + if (traceframe_number == -1) + error ("not debugging trace buffer"); + else if (from_tty && traceframe_number == 0) + error ("already at start of trace buffer"); + + frameno = traceframe_number - 1; + } + else + frameno = parse_and_eval_long (args); + + if (frameno < -1) + error ("invalid input (%d is less than zero)", frameno); + + sprintf (target_buf, "QTFrame:%x", frameno); + finish_tfind_command (target_buf, sizeof (target_buf), from_tty); + } + else + error ("Trace can only be run on remote targets."); +} + +/* tfind end */ +static void +trace_find_end_command (char *args, int from_tty) +{ + trace_find_command ("-1", from_tty); +} + +/* tfind none */ +static void +trace_find_none_command (char *args, int from_tty) +{ + trace_find_command ("-1", from_tty); +} + +/* tfind start */ +static void +trace_find_start_command (char *args, int from_tty) +{ + trace_find_command ("0", from_tty); +} + +/* tfind pc command */ +static void +trace_find_pc_command (char *args, int from_tty) +{ /* STUB_COMM PART_IMPLEMENTED */ + CORE_ADDR pc; + char tmp[40]; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0) + pc = read_pc (); /* default is current pc */ + else + pc = parse_and_eval_address (args); + + sprintf_vma (tmp, pc); + sprintf (target_buf, "QTFrame:pc:%s", tmp); + finish_tfind_command (target_buf, sizeof (target_buf), from_tty); + } + else + error ("Trace can only be run on remote targets."); +} + +/* tfind tracepoint command */ +static void +trace_find_tracepoint_command (char *args, int from_tty) +{ /* STUB_COMM PART_IMPLEMENTED */ + int tdp; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0) + { + if (tracepoint_number == -1) + error ("No current tracepoint -- please supply an argument."); + else + tdp = tracepoint_number; /* default is current TDP */ + } + else + tdp = parse_and_eval_long (args); + + sprintf (target_buf, "QTFrame:tdp:%x", tdp); + finish_tfind_command (target_buf, sizeof (target_buf), from_tty); + } + else + error ("Trace can only be run on remote targets."); +} + +/* TFIND LINE command: + + This command will take a sourceline for argument, just like BREAK + or TRACE (ie. anything that "decode_line_1" can handle). + + With no argument, this command will find the next trace frame + corresponding to a source line OTHER THAN THE CURRENT ONE. */ + +static void +trace_find_line_command (char *args, int from_tty) +{ /* STUB_COMM PART_IMPLEMENTED */ + static CORE_ADDR start_pc, end_pc; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct cleanup *old_chain; + char startpc_str[40], endpc_str[40]; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0) + { + sal = find_pc_line ((get_current_frame ())->pc, 0); + sals.nelts = 1; + sals.sals = (struct symtab_and_line *) + xmalloc (sizeof (struct symtab_and_line)); + sals.sals[0] = sal; + } + else + { + sals = decode_line_spec (args, 1); + sal = sals.sals[0]; + } + + old_chain = make_cleanup (xfree, sals.sals); + if (sal.symtab == 0) + { + printf_filtered ("TFIND: No line number information available"); + if (sal.pc != 0) + { + /* This is useful for "info line *0x7f34". If we can't tell the + user about a source line, at least let them have the symbolic + address. */ + printf_filtered (" for address "); + wrap_here (" "); + print_address (sal.pc, gdb_stdout); + printf_filtered (";\n -- will attempt to find by PC. \n"); + } + else + { + printf_filtered (".\n"); + return; /* no line, no PC; what can we do? */ + } + } + else if (sal.line > 0 + && find_line_pc_range (sal, &start_pc, &end_pc)) + { + if (start_pc == end_pc) + { + printf_filtered ("Line %d of \"%s\"", + sal.line, sal.symtab->filename); + wrap_here (" "); + printf_filtered (" is at address "); + print_address (start_pc, gdb_stdout); + wrap_here (" "); + printf_filtered (" but contains no code.\n"); + sal = find_pc_line (start_pc, 0); + if (sal.line > 0 && + find_line_pc_range (sal, &start_pc, &end_pc) && + start_pc != end_pc) + printf_filtered ("Attempting to find line %d instead.\n", + sal.line); + else + error ("Cannot find a good line."); + } + } + else + /* Is there any case in which we get here, and have an address + which the user would want to see? If we have debugging symbols + and no line numbers? */ + error ("Line number %d is out of range for \"%s\".\n", + sal.line, sal.symtab->filename); + + sprintf_vma (startpc_str, start_pc); + sprintf_vma (endpc_str, end_pc - 1); + if (args && *args) /* find within range of stated line */ + sprintf (target_buf, "QTFrame:range:%s:%s", startpc_str, endpc_str); + else /* find OUTSIDE OF range of CURRENT line */ + sprintf (target_buf, "QTFrame:outside:%s:%s", startpc_str, endpc_str); + finish_tfind_command (target_buf, sizeof (target_buf), from_tty); + do_cleanups (old_chain); + } + else + error ("Trace can only be run on remote targets."); +} + +/* tfind range command */ +static void +trace_find_range_command (char *args, int from_tty) +{ + static CORE_ADDR start, stop; + char start_str[40], stop_str[40]; + char *tmp; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0) + { /* XXX FIXME: what should default behavior be? */ + printf_filtered ("Usage: tfind range ,\n"); + return; + } + + if (0 != (tmp = strchr (args, ','))) + { + *tmp++ = '\0'; /* terminate start address */ + while (isspace ((int) *tmp)) + tmp++; + start = parse_and_eval_address (args); + stop = parse_and_eval_address (tmp); + } + else + { /* no explicit end address? */ + start = parse_and_eval_address (args); + stop = start + 1; /* ??? */ + } + + sprintf_vma (start_str, start); + sprintf_vma (stop_str, stop); + sprintf (target_buf, "QTFrame:range:%s:%s", start_str, stop_str); + finish_tfind_command (target_buf, sizeof (target_buf), from_tty); + } + else + error ("Trace can only be run on remote targets."); +} + +/* tfind outside command */ +static void +trace_find_outside_command (char *args, int from_tty) +{ + CORE_ADDR start, stop; + char start_str[40], stop_str[40]; + char *tmp; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0) + { /* XXX FIXME: what should default behavior be? */ + printf_filtered ("Usage: tfind outside ,\n"); + return; + } + + if (0 != (tmp = strchr (args, ','))) + { + *tmp++ = '\0'; /* terminate start address */ + while (isspace ((int) *tmp)) + tmp++; + start = parse_and_eval_address (args); + stop = parse_and_eval_address (tmp); + } + else + { /* no explicit end address? */ + start = parse_and_eval_address (args); + stop = start + 1; /* ??? */ + } + + sprintf_vma (start_str, start); + sprintf_vma (stop_str, stop); + sprintf (target_buf, "QTFrame:outside:%s:%s", start_str, stop_str); + finish_tfind_command (target_buf, sizeof (target_buf), from_tty); + } + else + error ("Trace can only be run on remote targets."); +} + +/* save-tracepoints command */ +static void +tracepoint_save_command (char *args, int from_tty) +{ + struct tracepoint *tp; + struct action_line *line; + FILE *fp; + char *i1 = " ", *i2 = " "; + char *indent, *actionline, *pathname; + char tmp[40]; + + if (args == 0 || *args == 0) + error ("Argument required (file name in which to save tracepoints"); + + if (tracepoint_chain == 0) + { + warning ("save-tracepoints: no tracepoints to save.\n"); + return; + } + + pathname = tilde_expand (args); + if (!(fp = fopen (pathname, "w"))) + error ("Unable to open file '%s' for saving tracepoints (%s)", + args, safe_strerror (errno)); + xfree (pathname); + + ALL_TRACEPOINTS (tp) + { + if (tp->addr_string) + fprintf (fp, "trace %s\n", tp->addr_string); + else + { + sprintf_vma (tmp, tp->address); + fprintf (fp, "trace *0x%s\n", tmp); + } + + if (tp->pass_count) + fprintf (fp, " passcount %d\n", tp->pass_count); + + if (tp->actions) + { + fprintf (fp, " actions\n"); + indent = i1; + for (line = tp->actions; line; line = line->next) + { + struct cmd_list_element *cmd; + + QUIT; /* allow user to bail out with ^C */ + actionline = line->action; + while (isspace ((int) *actionline)) + actionline++; + + fprintf (fp, "%s%s\n", indent, actionline); + if (*actionline != '#') /* skip for comment lines */ + { + cmd = lookup_cmd (&actionline, cmdlist, "", -1, 1); + if (cmd == 0) + error ("Bad action list item: %s", actionline); + if (cmd_cfunc_eq (cmd, while_stepping_pseudocommand)) + indent = i2; + else if (cmd_cfunc_eq (cmd, end_actions_pseudocommand)) + indent = i1; + } + } + } + } + fclose (fp); + if (from_tty) + printf_filtered ("Tracepoints saved to file '%s'.\n", args); + return; +} + +/* info scope command: list the locals for a scope. */ +static void +scope_info (char *args, int from_tty) +{ + struct symtabs_and_lines sals; + struct symbol *sym; + struct minimal_symbol *msym; + struct block *block; + char **canonical, *symname, *save_args = args; + int i, j, count = 0; + + if (args == 0 || *args == 0) + error ("requires an argument (function, line or *addr) to define a scope"); + + sals = decode_line_1 (&args, 1, NULL, 0, &canonical); + if (sals.nelts == 0) + return; /* presumably decode_line_1 has already warned */ + + /* Resolve line numbers to PC */ + resolve_sal_pc (&sals.sals[0]); + block = block_for_pc (sals.sals[0].pc); + + while (block != 0) + { + QUIT; /* allow user to bail out with ^C */ + ALL_BLOCK_SYMBOLS (block, i, sym) + { + QUIT; /* allow user to bail out with ^C */ + if (count == 0) + printf_filtered ("Scope for %s:\n", save_args); + count++; + + symname = SYMBOL_NAME (sym); + if (symname == NULL || *symname == '\0') + continue; /* probably botched, certainly useless */ + + printf_filtered ("Symbol %s is ", symname); + switch (SYMBOL_CLASS (sym)) + { + default: + case LOC_UNDEF: /* messed up symbol? */ + printf_filtered ("a bogus symbol, class %d.\n", + SYMBOL_CLASS (sym)); + count--; /* don't count this one */ + continue; + case LOC_CONST: + printf_filtered ("a constant with value %ld (0x%lx)", + SYMBOL_VALUE (sym), SYMBOL_VALUE (sym)); + break; + case LOC_CONST_BYTES: + printf_filtered ("constant bytes: "); + if (SYMBOL_TYPE (sym)) + for (j = 0; j < TYPE_LENGTH (SYMBOL_TYPE (sym)); j++) + fprintf_filtered (gdb_stdout, " %02x", + (unsigned) SYMBOL_VALUE_BYTES (sym)[j]); + break; + case LOC_STATIC: + printf_filtered ("in static storage at address "); + print_address_numeric (SYMBOL_VALUE_ADDRESS (sym), 1, gdb_stdout); + break; + case LOC_REGISTER: + printf_filtered ("a local variable in register $%s", + REGISTER_NAME (SYMBOL_VALUE (sym))); + break; + case LOC_ARG: + case LOC_LOCAL_ARG: + printf_filtered ("an argument at stack/frame offset %ld", + SYMBOL_VALUE (sym)); + break; + case LOC_LOCAL: + printf_filtered ("a local variable at frame offset %ld", + SYMBOL_VALUE (sym)); + break; + case LOC_REF_ARG: + printf_filtered ("a reference argument at offset %ld", + SYMBOL_VALUE (sym)); + break; + case LOC_REGPARM: + printf_filtered ("an argument in register $%s", + REGISTER_NAME (SYMBOL_VALUE (sym))); + break; + case LOC_REGPARM_ADDR: + printf_filtered ("the address of an argument, in register $%s", + REGISTER_NAME (SYMBOL_VALUE (sym))); + break; + case LOC_TYPEDEF: + printf_filtered ("a typedef.\n"); + continue; + case LOC_LABEL: + printf_filtered ("a label at address "); + print_address_numeric (SYMBOL_VALUE_ADDRESS (sym), 1, gdb_stdout); + break; + case LOC_BLOCK: + printf_filtered ("a function at address "); + print_address_numeric (BLOCK_START (SYMBOL_BLOCK_VALUE (sym)), 1, + gdb_stdout); + break; + case LOC_BASEREG: + printf_filtered ("a variable at offset %ld from register $%s", + SYMBOL_VALUE (sym), + REGISTER_NAME (SYMBOL_BASEREG (sym))); + break; + case LOC_BASEREG_ARG: + printf_filtered ("an argument at offset %ld from register $%s", + SYMBOL_VALUE (sym), + REGISTER_NAME (SYMBOL_BASEREG (sym))); + break; + case LOC_UNRESOLVED: + msym = lookup_minimal_symbol (SYMBOL_NAME (sym), NULL, NULL); + if (msym == NULL) + printf_filtered ("Unresolved Static"); + else + { + printf_filtered ("static storage at address "); + print_address_numeric (SYMBOL_VALUE_ADDRESS (msym), 1, + gdb_stdout); + } + break; + case LOC_OPTIMIZED_OUT: + printf_filtered ("optimized out.\n"); + continue; + } + if (SYMBOL_TYPE (sym)) + printf_filtered (", length %d.\n", + TYPE_LENGTH (check_typedef (SYMBOL_TYPE (sym)))); + } + if (BLOCK_FUNCTION (block)) + break; + else + block = BLOCK_SUPERBLOCK (block); + } + if (count <= 0) + printf_filtered ("Scope for %s contains no locals or arguments.\n", + save_args); +} + +/* worker function (cleanup) */ +static void +replace_comma (void *data) +{ + char *comma = data; + *comma = ','; +} + +/* tdump command */ +static void +trace_dump_command (char *args, int from_tty) +{ + struct tracepoint *t; + struct action_line *action; + char *action_exp, *next_comma; + struct cleanup *old_cleanups; + int stepping_actions = 0; + int stepping_frame = 0; + + if (!target_is_remote ()) + { + error ("Trace can only be run on remote targets."); + return; + } + + if (tracepoint_number == -1) + { + warning ("No current trace frame."); + return; + } + + ALL_TRACEPOINTS (t) + if (t->number == tracepoint_number) + break; + + if (t == NULL) + error ("No known tracepoint matches 'current' tracepoint #%d.", + tracepoint_number); + + old_cleanups = make_cleanup (null_cleanup, NULL); + + printf_filtered ("Data collected at tracepoint %d, trace frame %d:\n", + tracepoint_number, traceframe_number); + + /* The current frame is a trap frame if the frame PC is equal + to the tracepoint PC. If not, then the current frame was + collected during single-stepping. */ + + stepping_frame = (t->address != read_pc ()); + + for (action = t->actions; action; action = action->next) + { + struct cmd_list_element *cmd; + + QUIT; /* allow user to bail out with ^C */ + action_exp = action->action; + while (isspace ((int) *action_exp)) + action_exp++; + + /* The collection actions to be done while stepping are + bracketed by the commands "while-stepping" and "end". */ + + if (*action_exp == '#') /* comment line */ + continue; + + cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1); + if (cmd == 0) + error ("Bad action list item: %s", action_exp); + + if (cmd_cfunc_eq (cmd, while_stepping_pseudocommand)) + stepping_actions = 1; + else if (cmd_cfunc_eq (cmd, end_actions_pseudocommand)) + stepping_actions = 0; + else if (cmd_cfunc_eq (cmd, collect_pseudocommand)) + { + /* Display the collected data. + For the trap frame, display only what was collected at the trap. + Likewise for stepping frames, display only what was collected + while stepping. This means that the two boolean variables, + STEPPING_FRAME and STEPPING_ACTIONS should be equal. */ + if (stepping_frame == stepping_actions) + { + do + { /* repeat over a comma-separated list */ + QUIT; /* allow user to bail out with ^C */ + if (*action_exp == ',') + action_exp++; + while (isspace ((int) *action_exp)) + action_exp++; + + next_comma = strchr (action_exp, ','); + + if (0 == strncasecmp (action_exp, "$reg", 4)) + registers_info (NULL, from_tty); + else if (0 == strncasecmp (action_exp, "$loc", 4)) + locals_info (NULL, from_tty); + else if (0 == strncasecmp (action_exp, "$arg", 4)) + args_info (NULL, from_tty); + else + { /* variable */ + if (next_comma) + { + make_cleanup (replace_comma, next_comma); + *next_comma = '\0'; + } + printf_filtered ("%s = ", action_exp); + output_command (action_exp, from_tty); + printf_filtered ("\n"); + } + if (next_comma) + *next_comma = ','; + action_exp = next_comma; + } + while (action_exp && *action_exp == ','); + } + } + } + discard_cleanups (old_cleanups); +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null) + * "stolen" from sparc-stub.c + */ + +static const char hexchars[] = "0123456789abcdef"; + +static unsigned char * +mem2hex (unsigned char *mem, unsigned char *buf, int count) +{ + unsigned char ch; + + while (count-- > 0) + { + ch = *mem++; + + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + + return buf; +} + +int +get_traceframe_number (void) +{ + return traceframe_number; +} + + +/* module initialization */ +void +_initialize_tracepoint (void) +{ + struct cmd_list_element *c; + + tracepoint_chain = 0; + tracepoint_count = 0; + traceframe_number = -1; + tracepoint_number = -1; + + set_internalvar (lookup_internalvar ("tpnum"), + value_from_longest (builtin_type_int, (LONGEST) 0)); + set_internalvar (lookup_internalvar ("trace_frame"), + value_from_longest (builtin_type_int, (LONGEST) - 1)); + + if (tracepoint_list.list == NULL) + { + tracepoint_list.listsize = 128; + tracepoint_list.list = xmalloc + (tracepoint_list.listsize * sizeof (struct memrange)); + } + if (tracepoint_list.aexpr_list == NULL) + { + tracepoint_list.aexpr_listsize = 128; + tracepoint_list.aexpr_list = xmalloc + (tracepoint_list.aexpr_listsize * sizeof (struct agent_expr *)); + } + + if (stepping_list.list == NULL) + { + stepping_list.listsize = 128; + stepping_list.list = xmalloc + (stepping_list.listsize * sizeof (struct memrange)); + } + + if (stepping_list.aexpr_list == NULL) + { + stepping_list.aexpr_listsize = 128; + stepping_list.aexpr_list = xmalloc + (stepping_list.aexpr_listsize * sizeof (struct agent_expr *)); + } + + add_info ("scope", scope_info, + "List the variables local to a scope"); + + add_cmd ("tracepoints", class_trace, NULL, + "Tracing of program execution without stopping the program.", + &cmdlist); + + add_info ("tracepoints", tracepoints_info, + "Status of tracepoints, or tracepoint number NUMBER.\n\ +Convenience variable \"$tpnum\" contains the number of the\n\ +last tracepoint set."); + + add_info_alias ("tp", "tracepoints", 1); + + c = add_com ("save-tracepoints", class_trace, tracepoint_save_command, + "Save current tracepoint definitions as a script.\n\ +Use the 'source' command in another debug session to restore them."); + set_cmd_completer (c, filename_completer); + + add_com ("tdump", class_trace, trace_dump_command, + "Print everything collected at the current tracepoint."); + + add_prefix_cmd ("tfind", class_trace, trace_find_command, + "Select a trace frame;\n\ +No argument means forward by one frame; '-' meand backward by one frame.", + &tfindlist, "tfind ", 1, &cmdlist); + + add_cmd ("outside", class_trace, trace_find_outside_command, + "Select a trace frame whose PC is outside the given \ +range.\nUsage: tfind outside addr1, addr2", + &tfindlist); + + add_cmd ("range", class_trace, trace_find_range_command, + "Select a trace frame whose PC is in the given range.\n\ +Usage: tfind range addr1,addr2", + &tfindlist); + + add_cmd ("line", class_trace, trace_find_line_command, + "Select a trace frame by source line.\n\ +Argument can be a line number (with optional source file), \n\ +a function name, or '*' followed by an address.\n\ +Default argument is 'the next source line that was traced'.", + &tfindlist); + + add_cmd ("tracepoint", class_trace, trace_find_tracepoint_command, + "Select a trace frame by tracepoint number.\n\ +Default is the tracepoint for the current trace frame.", + &tfindlist); + + add_cmd ("pc", class_trace, trace_find_pc_command, + "Select a trace frame by PC.\n\ +Default is the current PC, or the PC of the current trace frame.", + &tfindlist); + + add_cmd ("end", class_trace, trace_find_end_command, + "Synonym for 'none'.\n\ +De-select any trace frame and resume 'live' debugging.", + &tfindlist); + + add_cmd ("none", class_trace, trace_find_none_command, + "De-select any trace frame and resume 'live' debugging.", + &tfindlist); + + add_cmd ("start", class_trace, trace_find_start_command, + "Select the first trace frame in the trace buffer.", + &tfindlist); + + add_com ("tstatus", class_trace, trace_status_command, + "Display the status of the current trace data collection."); + + add_com ("tstop", class_trace, trace_stop_command, + "Stop trace data collection."); + + add_com ("tstart", class_trace, trace_start_command, + "Start trace data collection."); + + add_com ("passcount", class_trace, trace_pass_command, + "Set the passcount for a tracepoint.\n\ +The trace will end when the tracepoint has been passed 'count' times.\n\ +Usage: passcount COUNT TPNUM, where TPNUM may also be \"all\";\n\ +if TPNUM is omitted, passcount refers to the last tracepoint defined."); + + add_com ("end", class_trace, end_actions_pseudocommand, + "Ends a list of commands or actions.\n\ +Several GDB commands allow you to enter a list of commands or actions.\n\ +Entering \"end\" on a line by itself is the normal way to terminate\n\ +such a list.\n\n\ +Note: the \"end\" command cannot be used at the gdb prompt."); + + add_com ("while-stepping", class_trace, while_stepping_pseudocommand, + "Specify single-stepping behavior at a tracepoint.\n\ +Argument is number of instructions to trace in single-step mode\n\ +following the tracepoint. This command is normally followed by\n\ +one or more \"collect\" commands, to specify what to collect\n\ +while single-stepping.\n\n\ +Note: this command can only be used in a tracepoint \"actions\" list."); + + add_com_alias ("ws", "while-stepping", class_alias, 0); + add_com_alias ("stepping", "while-stepping", class_alias, 0); + + add_com ("collect", class_trace, collect_pseudocommand, + "Specify one or more data items to be collected at a tracepoint.\n\ +Accepts a comma-separated list of (one or more) expressions. GDB will\n\ +collect all data (variables, registers) referenced by that expression.\n\ +Also accepts the following special arguments:\n\ + $regs -- all registers.\n\ + $args -- all function arguments.\n\ + $locals -- all variables local to the block/function scope.\n\ +Note: this command can only be used in a tracepoint \"actions\" list."); + + add_com ("actions", class_trace, trace_actions_command, + "Specify the actions to be taken at a tracepoint.\n\ +Tracepoint actions may include collecting of specified data, \n\ +single-stepping, or enabling/disabling other tracepoints, \n\ +depending on target's capabilities."); + + add_cmd ("tracepoints", class_trace, delete_trace_command, + "Delete specified tracepoints.\n\ +Arguments are tracepoint numbers, separated by spaces.\n\ +No argument means delete all tracepoints.", + &deletelist); + + add_cmd ("tracepoints", class_trace, disable_trace_command, + "Disable specified tracepoints.\n\ +Arguments are tracepoint numbers, separated by spaces.\n\ +No argument means disable all tracepoints.", + &disablelist); + + add_cmd ("tracepoints", class_trace, enable_trace_command, + "Enable specified tracepoints.\n\ +Arguments are tracepoint numbers, separated by spaces.\n\ +No argument means enable all tracepoints.", + &enablelist); + + c = add_com ("trace", class_trace, trace_command, + "Set a tracepoint at a specified line or function or address.\n\ +Argument may be a line number, function name, or '*' plus an address.\n\ +For a line number or function, trace at the start of its code.\n\ +If an address is specified, trace at that exact address.\n\n\ +Do \"help tracepoints\" for info on other tracepoint commands."); + set_cmd_completer (c, location_completer); + + add_com_alias ("tp", "trace", class_alias, 0); + add_com_alias ("tr", "trace", class_alias, 1); + add_com_alias ("tra", "trace", class_alias, 1); + add_com_alias ("trac", "trace", class_alias, 1); +} diff --git a/gdb/valops.c b/gdb/valops.c new file mode 100644 index 0000000..eaf4295 --- /dev/null +++ b/gdb/valops.c @@ -0,0 +1,3536 @@ +/* Perform non-arithmetic operations on values, for GDB. + Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, + 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 + Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "value.h" +#include "frame.h" +#include "inferior.h" +#include "gdbcore.h" +#include "target.h" +#include "demangle.h" +#include "language.h" +#include "gdbcmd.h" +#include "regcache.h" +#include "cp-abi.h" + +#include +#include "gdb_string.h" +#include "gdb_assert.h" + +/* Flag indicating HP compilers were used; needed to correctly handle some + value operations with HP aCC code/runtime. */ +extern int hp_som_som_object_present; + +extern int overload_debug; +/* Local functions. */ + +static int typecmp (int staticp, int varargs, int nargs, + struct field t1[], struct value *t2[]); + +static CORE_ADDR find_function_addr (struct value *, struct type **); +static struct value *value_arg_coerce (struct value *, struct type *, int); + + +static CORE_ADDR value_push (CORE_ADDR, struct value *); + +static struct value *search_struct_field (char *, struct value *, int, + struct type *, int); + +static struct value *search_struct_method (char *, struct value **, + struct value **, + int, int *, struct type *); + +static int check_field_in (struct type *, const char *); + +static CORE_ADDR allocate_space_in_inferior (int); + +static struct value *cast_into_complex (struct type *, struct value *); + +static struct fn_field *find_method_list (struct value ** argp, char *method, + int offset, + struct type *type, int *num_fns, + struct type **basetype, + int *boffset); + +void _initialize_valops (void); + +/* Flag for whether we want to abandon failed expression evals by default. */ + +#if 0 +static int auto_abandon = 0; +#endif + +int overload_resolution = 0; + +/* This boolean tells what gdb should do if a signal is received while in + a function called from gdb (call dummy). If set, gdb unwinds the stack + and restore the context to what as it was before the call. + The default is to stop in the frame where the signal was received. */ + +int unwind_on_signal_p = 0; + + + +/* Find the address of function name NAME in the inferior. */ + +struct value * +find_function_in_inferior (const char *name) +{ + register struct symbol *sym; + sym = lookup_symbol (name, 0, VAR_NAMESPACE, 0, NULL); + if (sym != NULL) + { + if (SYMBOL_CLASS (sym) != LOC_BLOCK) + { + error ("\"%s\" exists in this program but is not a function.", + name); + } + return value_of_variable (sym, NULL); + } + else + { + struct minimal_symbol *msymbol = lookup_minimal_symbol (name, NULL, NULL); + if (msymbol != NULL) + { + struct type *type; + CORE_ADDR maddr; + type = lookup_pointer_type (builtin_type_char); + type = lookup_function_type (type); + type = lookup_pointer_type (type); + maddr = SYMBOL_VALUE_ADDRESS (msymbol); + return value_from_pointer (type, maddr); + } + else + { + if (!target_has_execution) + error ("evaluation of this expression requires the target program to be active"); + else + error ("evaluation of this expression requires the program to have a function \"%s\".", name); + } + } +} + +/* Allocate NBYTES of space in the inferior using the inferior's malloc + and return a value that is a pointer to the allocated space. */ + +struct value * +value_allocate_space_in_inferior (int len) +{ + struct value *blocklen; + struct value *val = find_function_in_inferior (NAME_OF_MALLOC); + + blocklen = value_from_longest (builtin_type_int, (LONGEST) len); + val = call_function_by_hand (val, 1, &blocklen); + if (value_logical_not (val)) + { + if (!target_has_execution) + error ("No memory available to program now: you need to start the target first"); + else + error ("No memory available to program: call to malloc failed"); + } + return val; +} + +static CORE_ADDR +allocate_space_in_inferior (int len) +{ + return value_as_long (value_allocate_space_in_inferior (len)); +} + +/* Cast value ARG2 to type TYPE and return as a value. + More general than a C cast: accepts any two types of the same length, + and if ARG2 is an lvalue it can be cast into anything at all. */ +/* In C++, casts may change pointer or object representations. */ + +struct value * +value_cast (struct type *type, struct value *arg2) +{ + register enum type_code code1; + register enum type_code code2; + register int scalar; + struct type *type2; + + int convert_to_boolean = 0; + + if (VALUE_TYPE (arg2) == type) + return arg2; + + CHECK_TYPEDEF (type); + code1 = TYPE_CODE (type); + COERCE_REF (arg2); + type2 = check_typedef (VALUE_TYPE (arg2)); + + /* A cast to an undetermined-length array_type, such as (TYPE [])OBJECT, + is treated like a cast to (TYPE [N])OBJECT, + where N is sizeof(OBJECT)/sizeof(TYPE). */ + if (code1 == TYPE_CODE_ARRAY) + { + struct type *element_type = TYPE_TARGET_TYPE (type); + unsigned element_length = TYPE_LENGTH (check_typedef (element_type)); + if (element_length > 0 + && TYPE_ARRAY_UPPER_BOUND_TYPE (type) == BOUND_CANNOT_BE_DETERMINED) + { + struct type *range_type = TYPE_INDEX_TYPE (type); + int val_length = TYPE_LENGTH (type2); + LONGEST low_bound, high_bound, new_length; + if (get_discrete_bounds (range_type, &low_bound, &high_bound) < 0) + low_bound = 0, high_bound = 0; + new_length = val_length / element_length; + if (val_length % element_length != 0) + warning ("array element type size does not divide object size in cast"); + /* FIXME-type-allocation: need a way to free this type when we are + done with it. */ + range_type = create_range_type ((struct type *) NULL, + TYPE_TARGET_TYPE (range_type), + low_bound, + new_length + low_bound - 1); + VALUE_TYPE (arg2) = create_array_type ((struct type *) NULL, + element_type, range_type); + return arg2; + } + } + + if (current_language->c_style_arrays + && TYPE_CODE (type2) == TYPE_CODE_ARRAY) + arg2 = value_coerce_array (arg2); + + if (TYPE_CODE (type2) == TYPE_CODE_FUNC) + arg2 = value_coerce_function (arg2); + + type2 = check_typedef (VALUE_TYPE (arg2)); + COERCE_VARYING_ARRAY (arg2, type2); + code2 = TYPE_CODE (type2); + + if (code1 == TYPE_CODE_COMPLEX) + return cast_into_complex (type, arg2); + if (code1 == TYPE_CODE_BOOL) + { + code1 = TYPE_CODE_INT; + convert_to_boolean = 1; + } + if (code1 == TYPE_CODE_CHAR) + code1 = TYPE_CODE_INT; + if (code2 == TYPE_CODE_BOOL || code2 == TYPE_CODE_CHAR) + code2 = TYPE_CODE_INT; + + scalar = (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_FLT + || code2 == TYPE_CODE_ENUM || code2 == TYPE_CODE_RANGE); + + if (code1 == TYPE_CODE_STRUCT + && code2 == TYPE_CODE_STRUCT + && TYPE_NAME (type) != 0) + { + /* Look in the type of the source to see if it contains the + type of the target as a superclass. If so, we'll need to + offset the object in addition to changing its type. */ + struct value *v = search_struct_field (type_name_no_tag (type), + arg2, 0, type2, 1); + if (v) + { + VALUE_TYPE (v) = type; + return v; + } + } + if (code1 == TYPE_CODE_FLT && scalar) + return value_from_double (type, value_as_double (arg2)); + else if ((code1 == TYPE_CODE_INT || code1 == TYPE_CODE_ENUM + || code1 == TYPE_CODE_RANGE) + && (scalar || code2 == TYPE_CODE_PTR)) + { + LONGEST longest; + + if (hp_som_som_object_present && /* if target compiled by HP aCC */ + (code2 == TYPE_CODE_PTR)) + { + unsigned int *ptr; + struct value *retvalp; + + switch (TYPE_CODE (TYPE_TARGET_TYPE (type2))) + { + /* With HP aCC, pointers to data members have a bias */ + case TYPE_CODE_MEMBER: + retvalp = value_from_longest (type, value_as_long (arg2)); + /* force evaluation */ + ptr = (unsigned int *) VALUE_CONTENTS (retvalp); + *ptr &= ~0x20000000; /* zap 29th bit to remove bias */ + return retvalp; + + /* While pointers to methods don't really point to a function */ + case TYPE_CODE_METHOD: + error ("Pointers to methods not supported with HP aCC"); + + default: + break; /* fall out and go to normal handling */ + } + } + + /* When we cast pointers to integers, we mustn't use + POINTER_TO_ADDRESS to find the address the pointer + represents, as value_as_long would. GDB should evaluate + expressions just as the compiler would --- and the compiler + sees a cast as a simple reinterpretation of the pointer's + bits. */ + if (code2 == TYPE_CODE_PTR) + longest = extract_unsigned_integer (VALUE_CONTENTS (arg2), + TYPE_LENGTH (type2)); + else + longest = value_as_long (arg2); + return value_from_longest (type, convert_to_boolean ? + (LONGEST) (longest ? 1 : 0) : longest); + } + else if (code1 == TYPE_CODE_PTR && (code2 == TYPE_CODE_INT || + code2 == TYPE_CODE_ENUM || + code2 == TYPE_CODE_RANGE)) + { + /* TYPE_LENGTH (type) is the length of a pointer, but we really + want the length of an address! -- we are really dealing with + addresses (i.e., gdb representations) not pointers (i.e., + target representations) here. + + This allows things like "print *(int *)0x01000234" to work + without printing a misleading message -- which would + otherwise occur when dealing with a target having two byte + pointers and four byte addresses. */ + + int addr_bit = TARGET_ADDR_BIT; + + LONGEST longest = value_as_long (arg2); + if (addr_bit < sizeof (LONGEST) * HOST_CHAR_BIT) + { + if (longest >= ((LONGEST) 1 << addr_bit) + || longest <= -((LONGEST) 1 << addr_bit)) + warning ("value truncated"); + } + return value_from_longest (type, longest); + } + else if (TYPE_LENGTH (type) == TYPE_LENGTH (type2)) + { + if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR) + { + struct type *t1 = check_typedef (TYPE_TARGET_TYPE (type)); + struct type *t2 = check_typedef (TYPE_TARGET_TYPE (type2)); + if (TYPE_CODE (t1) == TYPE_CODE_STRUCT + && TYPE_CODE (t2) == TYPE_CODE_STRUCT + && !value_logical_not (arg2)) + { + struct value *v; + + /* Look in the type of the source to see if it contains the + type of the target as a superclass. If so, we'll need to + offset the pointer rather than just change its type. */ + if (TYPE_NAME (t1) != NULL) + { + v = search_struct_field (type_name_no_tag (t1), + value_ind (arg2), 0, t2, 1); + if (v) + { + v = value_addr (v); + VALUE_TYPE (v) = type; + return v; + } + } + + /* Look in the type of the target to see if it contains the + type of the source as a superclass. If so, we'll need to + offset the pointer rather than just change its type. + FIXME: This fails silently with virtual inheritance. */ + if (TYPE_NAME (t2) != NULL) + { + v = search_struct_field (type_name_no_tag (t2), + value_zero (t1, not_lval), 0, t1, 1); + if (v) + { + CORE_ADDR addr2 = value_as_address (arg2); + addr2 -= (VALUE_ADDRESS (v) + + VALUE_OFFSET (v) + + VALUE_EMBEDDED_OFFSET (v)); + return value_from_pointer (type, addr2); + } + } + } + /* No superclass found, just fall through to change ptr type. */ + } + VALUE_TYPE (arg2) = type; + arg2 = value_change_enclosing_type (arg2, type); + VALUE_POINTED_TO_OFFSET (arg2) = 0; /* pai: chk_val */ + return arg2; + } + /* OBSOLETE else if (chill_varying_type (type)) */ + /* OBSOLETE { */ + /* OBSOLETE struct type *range1, *range2, *eltype1, *eltype2; */ + /* OBSOLETE struct value *val; */ + /* OBSOLETE int count1, count2; */ + /* OBSOLETE LONGEST low_bound, high_bound; */ + /* OBSOLETE char *valaddr, *valaddr_data; */ + /* OBSOLETE *//* For lint warning about eltype2 possibly uninitialized: */ + /* OBSOLETE eltype2 = NULL; */ + /* OBSOLETE if (code2 == TYPE_CODE_BITSTRING) */ + /* OBSOLETE error ("not implemented: converting bitstring to varying type"); */ + /* OBSOLETE if ((code2 != TYPE_CODE_ARRAY && code2 != TYPE_CODE_STRING) */ + /* OBSOLETE || (eltype1 = check_typedef (TYPE_TARGET_TYPE (TYPE_FIELD_TYPE (type, 1))), */ + /* OBSOLETE eltype2 = check_typedef (TYPE_TARGET_TYPE (type2)), */ + /* OBSOLETE (TYPE_LENGTH (eltype1) != TYPE_LENGTH (eltype2) */ + /* OBSOLETE *//*|| TYPE_CODE (eltype1) != TYPE_CODE (eltype2) *//* ))) */ + /* OBSOLETE error ("Invalid conversion to varying type"); */ + /* OBSOLETE range1 = TYPE_FIELD_TYPE (TYPE_FIELD_TYPE (type, 1), 0); */ + /* OBSOLETE range2 = TYPE_FIELD_TYPE (type2, 0); */ + /* OBSOLETE if (get_discrete_bounds (range1, &low_bound, &high_bound) < 0) */ + /* OBSOLETE count1 = -1; */ + /* OBSOLETE else */ + /* OBSOLETE count1 = high_bound - low_bound + 1; */ + /* OBSOLETE if (get_discrete_bounds (range2, &low_bound, &high_bound) < 0) */ + /* OBSOLETE count1 = -1, count2 = 0; *//* To force error before */ + /* OBSOLETE else */ + /* OBSOLETE count2 = high_bound - low_bound + 1; */ + /* OBSOLETE if (count2 > count1) */ + /* OBSOLETE error ("target varying type is too small"); */ + /* OBSOLETE val = allocate_value (type); */ + /* OBSOLETE valaddr = VALUE_CONTENTS_RAW (val); */ + /* OBSOLETE valaddr_data = valaddr + TYPE_FIELD_BITPOS (type, 1) / 8; */ + /* OBSOLETE *//* Set val's __var_length field to count2. */ + /* OBSOLETE store_signed_integer (valaddr, TYPE_LENGTH (TYPE_FIELD_TYPE (type, 0)), */ + /* OBSOLETE count2); */ + /* OBSOLETE *//* Set the __var_data field to count2 elements copied from arg2. */ + /* OBSOLETE memcpy (valaddr_data, VALUE_CONTENTS (arg2), */ + /* OBSOLETE count2 * TYPE_LENGTH (eltype2)); */ + /* OBSOLETE *//* Zero the rest of the __var_data field of val. */ + /* OBSOLETE memset (valaddr_data + count2 * TYPE_LENGTH (eltype2), '\0', */ + /* OBSOLETE (count1 - count2) * TYPE_LENGTH (eltype2)); */ + /* OBSOLETE return val; */ + /* OBSOLETE } */ + else if (VALUE_LVAL (arg2) == lval_memory) + { + return value_at_lazy (type, VALUE_ADDRESS (arg2) + VALUE_OFFSET (arg2), + VALUE_BFD_SECTION (arg2)); + } + else if (code1 == TYPE_CODE_VOID) + { + return value_zero (builtin_type_void, not_lval); + } + else + { + error ("Invalid cast."); + return 0; + } +} + +/* Create a value of type TYPE that is zero, and return it. */ + +struct value * +value_zero (struct type *type, enum lval_type lv) +{ + struct value *val = allocate_value (type); + + memset (VALUE_CONTENTS (val), 0, TYPE_LENGTH (check_typedef (type))); + VALUE_LVAL (val) = lv; + + return val; +} + +/* Return a value with type TYPE located at ADDR. + + Call value_at only if the data needs to be fetched immediately; + if we can be 'lazy' and defer the fetch, perhaps indefinately, call + value_at_lazy instead. value_at_lazy simply records the address of + the data and sets the lazy-evaluation-required flag. The lazy flag + is tested in the VALUE_CONTENTS macro, which is used if and when + the contents are actually required. + + Note: value_at does *NOT* handle embedded offsets; perform such + adjustments before or after calling it. */ + +struct value * +value_at (struct type *type, CORE_ADDR addr, asection *sect) +{ + struct value *val; + + if (TYPE_CODE (check_typedef (type)) == TYPE_CODE_VOID) + error ("Attempt to dereference a generic pointer."); + + val = allocate_value (type); + + read_memory (addr, VALUE_CONTENTS_ALL_RAW (val), TYPE_LENGTH (type)); + + VALUE_LVAL (val) = lval_memory; + VALUE_ADDRESS (val) = addr; + VALUE_BFD_SECTION (val) = sect; + + return val; +} + +/* Return a lazy value with type TYPE located at ADDR (cf. value_at). */ + +struct value * +value_at_lazy (struct type *type, CORE_ADDR addr, asection *sect) +{ + struct value *val; + + if (TYPE_CODE (check_typedef (type)) == TYPE_CODE_VOID) + error ("Attempt to dereference a generic pointer."); + + val = allocate_value (type); + + VALUE_LVAL (val) = lval_memory; + VALUE_ADDRESS (val) = addr; + VALUE_LAZY (val) = 1; + VALUE_BFD_SECTION (val) = sect; + + return val; +} + +/* Called only from the VALUE_CONTENTS and VALUE_CONTENTS_ALL macros, + if the current data for a variable needs to be loaded into + VALUE_CONTENTS(VAL). Fetches the data from the user's process, and + clears the lazy flag to indicate that the data in the buffer is valid. + + If the value is zero-length, we avoid calling read_memory, which would + abort. We mark the value as fetched anyway -- all 0 bytes of it. + + This function returns a value because it is used in the VALUE_CONTENTS + macro as part of an expression, where a void would not work. The + value is ignored. */ + +int +value_fetch_lazy (struct value *val) +{ + CORE_ADDR addr = VALUE_ADDRESS (val) + VALUE_OFFSET (val); + int length = TYPE_LENGTH (VALUE_ENCLOSING_TYPE (val)); + + struct type *type = VALUE_TYPE (val); + if (length) + read_memory (addr, VALUE_CONTENTS_ALL_RAW (val), length); + + VALUE_LAZY (val) = 0; + return 0; +} + + +/* Store the contents of FROMVAL into the location of TOVAL. + Return a new value with the location of TOVAL and contents of FROMVAL. */ + +struct value * +value_assign (struct value *toval, struct value *fromval) +{ + register struct type *type; + struct value *val; + char *raw_buffer = (char*) alloca (MAX_REGISTER_RAW_SIZE); + int use_buffer = 0; + + if (!toval->modifiable) + error ("Left operand of assignment is not a modifiable lvalue."); + + COERCE_REF (toval); + + type = VALUE_TYPE (toval); + if (VALUE_LVAL (toval) != lval_internalvar) + fromval = value_cast (type, fromval); + else + COERCE_ARRAY (fromval); + CHECK_TYPEDEF (type); + + /* If TOVAL is a special machine register requiring conversion + of program values to a special raw format, + convert FROMVAL's contents now, with result in `raw_buffer', + and set USE_BUFFER to the number of bytes to write. */ + + if (VALUE_REGNO (toval) >= 0) + { + int regno = VALUE_REGNO (toval); + if (CONVERT_REGISTER_P (regno)) + { + struct type *fromtype = check_typedef (VALUE_TYPE (fromval)); + VALUE_TO_REGISTER (fromtype, regno, VALUE_CONTENTS (fromval), raw_buffer); + use_buffer = REGISTER_RAW_SIZE (regno); + } + } + + switch (VALUE_LVAL (toval)) + { + case lval_internalvar: + set_internalvar (VALUE_INTERNALVAR (toval), fromval); + val = value_copy (VALUE_INTERNALVAR (toval)->value); + val = value_change_enclosing_type (val, VALUE_ENCLOSING_TYPE (fromval)); + VALUE_EMBEDDED_OFFSET (val) = VALUE_EMBEDDED_OFFSET (fromval); + VALUE_POINTED_TO_OFFSET (val) = VALUE_POINTED_TO_OFFSET (fromval); + return val; + + case lval_internalvar_component: + set_internalvar_component (VALUE_INTERNALVAR (toval), + VALUE_OFFSET (toval), + VALUE_BITPOS (toval), + VALUE_BITSIZE (toval), + fromval); + break; + + case lval_memory: + { + char *dest_buffer; + CORE_ADDR changed_addr; + int changed_len; + + if (VALUE_BITSIZE (toval)) + { + char buffer[sizeof (LONGEST)]; + /* We assume that the argument to read_memory is in units of + host chars. FIXME: Is that correct? */ + changed_len = (VALUE_BITPOS (toval) + + VALUE_BITSIZE (toval) + + HOST_CHAR_BIT - 1) + / HOST_CHAR_BIT; + + if (changed_len > (int) sizeof (LONGEST)) + error ("Can't handle bitfields which don't fit in a %d bit word.", + (int) sizeof (LONGEST) * HOST_CHAR_BIT); + + read_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + buffer, changed_len); + modify_field (buffer, value_as_long (fromval), + VALUE_BITPOS (toval), VALUE_BITSIZE (toval)); + changed_addr = VALUE_ADDRESS (toval) + VALUE_OFFSET (toval); + dest_buffer = buffer; + } + else if (use_buffer) + { + changed_addr = VALUE_ADDRESS (toval) + VALUE_OFFSET (toval); + changed_len = use_buffer; + dest_buffer = raw_buffer; + } + else + { + changed_addr = VALUE_ADDRESS (toval) + VALUE_OFFSET (toval); + changed_len = TYPE_LENGTH (type); + dest_buffer = VALUE_CONTENTS (fromval); + } + + write_memory (changed_addr, dest_buffer, changed_len); + if (memory_changed_hook) + memory_changed_hook (changed_addr, changed_len); + target_changed_event (); + } + break; + + case lval_register: + if (VALUE_BITSIZE (toval)) + { + char buffer[sizeof (LONGEST)]; + int len = + REGISTER_RAW_SIZE (VALUE_REGNO (toval)) - VALUE_OFFSET (toval); + + if (len > (int) sizeof (LONGEST)) + error ("Can't handle bitfields in registers larger than %d bits.", + (int) sizeof (LONGEST) * HOST_CHAR_BIT); + + if (VALUE_BITPOS (toval) + VALUE_BITSIZE (toval) + > len * HOST_CHAR_BIT) + /* Getting this right would involve being very careful about + byte order. */ + error ("Can't assign to bitfields that cross register " + "boundaries."); + + read_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + buffer, len); + modify_field (buffer, value_as_long (fromval), + VALUE_BITPOS (toval), VALUE_BITSIZE (toval)); + write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + buffer, len); + } + else if (use_buffer) + write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + raw_buffer, use_buffer); + else + { + /* Do any conversion necessary when storing this type to more + than one register. */ +#ifdef REGISTER_CONVERT_FROM_TYPE + memcpy (raw_buffer, VALUE_CONTENTS (fromval), TYPE_LENGTH (type)); + REGISTER_CONVERT_FROM_TYPE (VALUE_REGNO (toval), type, raw_buffer); + write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + raw_buffer, TYPE_LENGTH (type)); +#else + write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval), + VALUE_CONTENTS (fromval), TYPE_LENGTH (type)); +#endif + } + + target_changed_event (); + + /* Assigning to the stack pointer, frame pointer, and other + (architecture and calling convention specific) registers may + cause the frame cache to be out of date. We just do this + on all assignments to registers for simplicity; I doubt the slowdown + matters. */ + reinit_frame_cache (); + break; + + case lval_reg_frame_relative: + { + /* value is stored in a series of registers in the frame + specified by the structure. Copy that value out, modify + it, and copy it back in. */ + int amount_to_copy = (VALUE_BITSIZE (toval) ? 1 : TYPE_LENGTH (type)); + int reg_size = REGISTER_RAW_SIZE (VALUE_FRAME_REGNUM (toval)); + int byte_offset = VALUE_OFFSET (toval) % reg_size; + int reg_offset = VALUE_OFFSET (toval) / reg_size; + int amount_copied; + + /* Make the buffer large enough in all cases. */ + /* FIXME (alloca): Not safe for very large data types. */ + char *buffer = (char *) alloca (amount_to_copy + + sizeof (LONGEST) + + MAX_REGISTER_RAW_SIZE); + + int regno; + struct frame_info *frame; + + /* Figure out which frame this is in currently. */ + for (frame = get_current_frame (); + frame && FRAME_FP (frame) != VALUE_FRAME (toval); + frame = get_prev_frame (frame)) + ; + + if (!frame) + error ("Value being assigned to is no longer active."); + + amount_to_copy += (reg_size - amount_to_copy % reg_size); + + /* Copy it out. */ + for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset, + amount_copied = 0); + amount_copied < amount_to_copy; + amount_copied += reg_size, regno++) + { + get_saved_register (buffer + amount_copied, + (int *) NULL, (CORE_ADDR *) NULL, + frame, regno, (enum lval_type *) NULL); + } + + /* Modify what needs to be modified. */ + if (VALUE_BITSIZE (toval)) + modify_field (buffer + byte_offset, + value_as_long (fromval), + VALUE_BITPOS (toval), VALUE_BITSIZE (toval)); + else if (use_buffer) + memcpy (buffer + byte_offset, raw_buffer, use_buffer); + else + memcpy (buffer + byte_offset, VALUE_CONTENTS (fromval), + TYPE_LENGTH (type)); + + /* Copy it back. */ + for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset, + amount_copied = 0); + amount_copied < amount_to_copy; + amount_copied += reg_size, regno++) + { + enum lval_type lval; + CORE_ADDR addr; + int optim; + + /* Just find out where to put it. */ + get_saved_register ((char *) NULL, + &optim, &addr, frame, regno, &lval); + + if (optim) + error ("Attempt to assign to a value that was optimized out."); + if (lval == lval_memory) + write_memory (addr, buffer + amount_copied, reg_size); + else if (lval == lval_register) + write_register_bytes (addr, buffer + amount_copied, reg_size); + else + error ("Attempt to assign to an unmodifiable value."); + } + + if (register_changed_hook) + register_changed_hook (-1); + target_changed_event (); + } + break; + + + default: + error ("Left operand of assignment is not an lvalue."); + } + + /* If the field does not entirely fill a LONGEST, then zero the sign bits. + If the field is signed, and is negative, then sign extend. */ + if ((VALUE_BITSIZE (toval) > 0) + && (VALUE_BITSIZE (toval) < 8 * (int) sizeof (LONGEST))) + { + LONGEST fieldval = value_as_long (fromval); + LONGEST valmask = (((ULONGEST) 1) << VALUE_BITSIZE (toval)) - 1; + + fieldval &= valmask; + if (!TYPE_UNSIGNED (type) && (fieldval & (valmask ^ (valmask >> 1)))) + fieldval |= ~valmask; + + fromval = value_from_longest (type, fieldval); + } + + val = value_copy (toval); + memcpy (VALUE_CONTENTS_RAW (val), VALUE_CONTENTS (fromval), + TYPE_LENGTH (type)); + VALUE_TYPE (val) = type; + val = value_change_enclosing_type (val, VALUE_ENCLOSING_TYPE (fromval)); + VALUE_EMBEDDED_OFFSET (val) = VALUE_EMBEDDED_OFFSET (fromval); + VALUE_POINTED_TO_OFFSET (val) = VALUE_POINTED_TO_OFFSET (fromval); + + return val; +} + +/* Extend a value VAL to COUNT repetitions of its type. */ + +struct value * +value_repeat (struct value *arg1, int count) +{ + struct value *val; + + if (VALUE_LVAL (arg1) != lval_memory) + error ("Only values in memory can be extended with '@'."); + if (count < 1) + error ("Invalid number %d of repetitions.", count); + + val = allocate_repeat_value (VALUE_ENCLOSING_TYPE (arg1), count); + + read_memory (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1), + VALUE_CONTENTS_ALL_RAW (val), + TYPE_LENGTH (VALUE_ENCLOSING_TYPE (val))); + VALUE_LVAL (val) = lval_memory; + VALUE_ADDRESS (val) = VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1); + + return val; +} + +struct value * +value_of_variable (struct symbol *var, struct block *b) +{ + struct value *val; + struct frame_info *frame = NULL; + + if (!b) + frame = NULL; /* Use selected frame. */ + else if (symbol_read_needs_frame (var)) + { + frame = block_innermost_frame (b); + if (!frame) + { + if (BLOCK_FUNCTION (b) + && SYMBOL_SOURCE_NAME (BLOCK_FUNCTION (b))) + error ("No frame is currently executing in block %s.", + SYMBOL_SOURCE_NAME (BLOCK_FUNCTION (b))); + else + error ("No frame is currently executing in specified block"); + } + } + + val = read_var_value (var, frame); + if (!val) + error ("Address of symbol \"%s\" is unknown.", SYMBOL_SOURCE_NAME (var)); + + return val; +} + +/* Given a value which is an array, return a value which is a pointer to its + first element, regardless of whether or not the array has a nonzero lower + bound. + + FIXME: A previous comment here indicated that this routine should be + substracting the array's lower bound. It's not clear to me that this + is correct. Given an array subscripting operation, it would certainly + work to do the adjustment here, essentially computing: + + (&array[0] - (lowerbound * sizeof array[0])) + (index * sizeof array[0]) + + However I believe a more appropriate and logical place to account for + the lower bound is to do so in value_subscript, essentially computing: + + (&array[0] + ((index - lowerbound) * sizeof array[0])) + + As further evidence consider what would happen with operations other + than array subscripting, where the caller would get back a value that + had an address somewhere before the actual first element of the array, + and the information about the lower bound would be lost because of + the coercion to pointer type. + */ + +struct value * +value_coerce_array (struct value *arg1) +{ + register struct type *type = check_typedef (VALUE_TYPE (arg1)); + + if (VALUE_LVAL (arg1) != lval_memory) + error ("Attempt to take address of value not located in memory."); + + return value_from_pointer (lookup_pointer_type (TYPE_TARGET_TYPE (type)), + (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1))); +} + +/* Given a value which is a function, return a value which is a pointer + to it. */ + +struct value * +value_coerce_function (struct value *arg1) +{ + struct value *retval; + + if (VALUE_LVAL (arg1) != lval_memory) + error ("Attempt to take address of value not located in memory."); + + retval = value_from_pointer (lookup_pointer_type (VALUE_TYPE (arg1)), + (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1))); + VALUE_BFD_SECTION (retval) = VALUE_BFD_SECTION (arg1); + return retval; +} + +/* Return a pointer value for the object for which ARG1 is the contents. */ + +struct value * +value_addr (struct value *arg1) +{ + struct value *arg2; + + struct type *type = check_typedef (VALUE_TYPE (arg1)); + if (TYPE_CODE (type) == TYPE_CODE_REF) + { + /* Copy the value, but change the type from (T&) to (T*). + We keep the same location information, which is efficient, + and allows &(&X) to get the location containing the reference. */ + arg2 = value_copy (arg1); + VALUE_TYPE (arg2) = lookup_pointer_type (TYPE_TARGET_TYPE (type)); + return arg2; + } + if (TYPE_CODE (type) == TYPE_CODE_FUNC) + return value_coerce_function (arg1); + + if (VALUE_LVAL (arg1) != lval_memory) + error ("Attempt to take address of value not located in memory."); + + /* Get target memory address */ + arg2 = value_from_pointer (lookup_pointer_type (VALUE_TYPE (arg1)), + (VALUE_ADDRESS (arg1) + + VALUE_OFFSET (arg1) + + VALUE_EMBEDDED_OFFSET (arg1))); + + /* This may be a pointer to a base subobject; so remember the + full derived object's type ... */ + arg2 = value_change_enclosing_type (arg2, lookup_pointer_type (VALUE_ENCLOSING_TYPE (arg1))); + /* ... and also the relative position of the subobject in the full object */ + VALUE_POINTED_TO_OFFSET (arg2) = VALUE_EMBEDDED_OFFSET (arg1); + VALUE_BFD_SECTION (arg2) = VALUE_BFD_SECTION (arg1); + return arg2; +} + +/* Given a value of a pointer type, apply the C unary * operator to it. */ + +struct value * +value_ind (struct value *arg1) +{ + struct type *base_type; + struct value *arg2; + + COERCE_ARRAY (arg1); + + base_type = check_typedef (VALUE_TYPE (arg1)); + + if (TYPE_CODE (base_type) == TYPE_CODE_MEMBER) + error ("not implemented: member types in value_ind"); + + /* Allow * on an integer so we can cast it to whatever we want. + This returns an int, which seems like the most C-like thing + to do. "long long" variables are rare enough that + BUILTIN_TYPE_LONGEST would seem to be a mistake. */ + if (TYPE_CODE (base_type) == TYPE_CODE_INT) + return value_at_lazy (builtin_type_int, + (CORE_ADDR) value_as_long (arg1), + VALUE_BFD_SECTION (arg1)); + else if (TYPE_CODE (base_type) == TYPE_CODE_PTR) + { + struct type *enc_type; + /* We may be pointing to something embedded in a larger object */ + /* Get the real type of the enclosing object */ + enc_type = check_typedef (VALUE_ENCLOSING_TYPE (arg1)); + enc_type = TYPE_TARGET_TYPE (enc_type); + /* Retrieve the enclosing object pointed to */ + arg2 = value_at_lazy (enc_type, + value_as_address (arg1) - VALUE_POINTED_TO_OFFSET (arg1), + VALUE_BFD_SECTION (arg1)); + /* Re-adjust type */ + VALUE_TYPE (arg2) = TYPE_TARGET_TYPE (base_type); + /* Add embedding info */ + arg2 = value_change_enclosing_type (arg2, enc_type); + VALUE_EMBEDDED_OFFSET (arg2) = VALUE_POINTED_TO_OFFSET (arg1); + + /* We may be pointing to an object of some derived type */ + arg2 = value_full_object (arg2, NULL, 0, 0, 0); + return arg2; + } + + error ("Attempt to take contents of a non-pointer value."); + return 0; /* For lint -- never reached */ +} + +/* Pushing small parts of stack frames. */ + +/* Push one word (the size of object that a register holds). */ + +CORE_ADDR +push_word (CORE_ADDR sp, ULONGEST word) +{ + register int len = REGISTER_SIZE; + char *buffer = alloca (MAX_REGISTER_RAW_SIZE); + + store_unsigned_integer (buffer, len, word); + if (INNER_THAN (1, 2)) + { + /* stack grows downward */ + sp -= len; + write_memory (sp, buffer, len); + } + else + { + /* stack grows upward */ + write_memory (sp, buffer, len); + sp += len; + } + + return sp; +} + +/* Push LEN bytes with data at BUFFER. */ + +CORE_ADDR +push_bytes (CORE_ADDR sp, char *buffer, int len) +{ + if (INNER_THAN (1, 2)) + { + /* stack grows downward */ + sp -= len; + write_memory (sp, buffer, len); + } + else + { + /* stack grows upward */ + write_memory (sp, buffer, len); + sp += len; + } + + return sp; +} + +#ifndef PARM_BOUNDARY +#define PARM_BOUNDARY (0) +#endif + +/* Push onto the stack the specified value VALUE. Pad it correctly for + it to be an argument to a function. */ + +static CORE_ADDR +value_push (register CORE_ADDR sp, struct value *arg) +{ + register int len = TYPE_LENGTH (VALUE_ENCLOSING_TYPE (arg)); + register int container_len = len; + register int offset; + + /* How big is the container we're going to put this value in? */ + if (PARM_BOUNDARY) + container_len = ((len + PARM_BOUNDARY / TARGET_CHAR_BIT - 1) + & ~(PARM_BOUNDARY / TARGET_CHAR_BIT - 1)); + + /* Are we going to put it at the high or low end of the container? */ + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + offset = container_len - len; + else + offset = 0; + + if (INNER_THAN (1, 2)) + { + /* stack grows downward */ + sp -= container_len; + write_memory (sp + offset, VALUE_CONTENTS_ALL (arg), len); + } + else + { + /* stack grows upward */ + write_memory (sp + offset, VALUE_CONTENTS_ALL (arg), len); + sp += container_len; + } + + return sp; +} + +CORE_ADDR +default_push_arguments (int nargs, struct value **args, CORE_ADDR sp, + int struct_return, CORE_ADDR struct_addr) +{ + /* ASSERT ( !struct_return); */ + int i; + for (i = nargs - 1; i >= 0; i--) + sp = value_push (sp, args[i]); + return sp; +} + + +/* Functions to use for the COERCE_FLOAT_TO_DOUBLE gdbarch method. + + How you should pass arguments to a function depends on whether it + was defined in K&R style or prototype style. If you define a + function using the K&R syntax that takes a `float' argument, then + callers must pass that argument as a `double'. If you define the + function using the prototype syntax, then you must pass the + argument as a `float', with no promotion. + + Unfortunately, on certain older platforms, the debug info doesn't + indicate reliably how each function was defined. A function type's + TYPE_FLAG_PROTOTYPED flag may be clear, even if the function was + defined in prototype style. When calling a function whose + TYPE_FLAG_PROTOTYPED flag is clear, GDB consults the + COERCE_FLOAT_TO_DOUBLE gdbarch method to decide what to do. + + For modern targets, it is proper to assume that, if the prototype + flag is clear, that can be trusted: `float' arguments should be + promoted to `double'. You should register the function + `standard_coerce_float_to_double' to get this behavior. + + For some older targets, if the prototype flag is clear, that + doesn't tell us anything. So we guess that, if we don't have a + type for the formal parameter (i.e., the first argument to + COERCE_FLOAT_TO_DOUBLE is null), then we should promote it; + otherwise, we should leave it alone. The function + `default_coerce_float_to_double' provides this behavior; it is the + default value, for compatibility with older configurations. */ +int +default_coerce_float_to_double (struct type *formal, struct type *actual) +{ + return formal == NULL; +} + + +int +standard_coerce_float_to_double (struct type *formal, struct type *actual) +{ + return 1; +} + + +/* Perform the standard coercions that are specified + for arguments to be passed to C functions. + + If PARAM_TYPE is non-NULL, it is the expected parameter type. + IS_PROTOTYPED is non-zero if the function declaration is prototyped. */ + +static struct value * +value_arg_coerce (struct value *arg, struct type *param_type, + int is_prototyped) +{ + register struct type *arg_type = check_typedef (VALUE_TYPE (arg)); + register struct type *type + = param_type ? check_typedef (param_type) : arg_type; + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_REF: + if (TYPE_CODE (arg_type) != TYPE_CODE_REF + && TYPE_CODE (arg_type) != TYPE_CODE_PTR) + { + arg = value_addr (arg); + VALUE_TYPE (arg) = param_type; + return arg; + } + break; + case TYPE_CODE_INT: + case TYPE_CODE_CHAR: + case TYPE_CODE_BOOL: + case TYPE_CODE_ENUM: + /* If we don't have a prototype, coerce to integer type if necessary. */ + if (!is_prototyped) + { + if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_int)) + type = builtin_type_int; + } + /* Currently all target ABIs require at least the width of an integer + type for an argument. We may have to conditionalize the following + type coercion for future targets. */ + if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_int)) + type = builtin_type_int; + break; + case TYPE_CODE_FLT: + /* FIXME: We should always convert floats to doubles in the + non-prototyped case. As many debugging formats include + no information about prototyping, we have to live with + COERCE_FLOAT_TO_DOUBLE for now. */ + if (!is_prototyped && COERCE_FLOAT_TO_DOUBLE (param_type, arg_type)) + { + if (TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_double)) + type = builtin_type_double; + else if (TYPE_LENGTH (type) > TYPE_LENGTH (builtin_type_double)) + type = builtin_type_long_double; + } + break; + case TYPE_CODE_FUNC: + type = lookup_pointer_type (type); + break; + case TYPE_CODE_ARRAY: + /* Arrays are coerced to pointers to their first element, unless + they are vectors, in which case we want to leave them alone, + because they are passed by value. */ + if (current_language->c_style_arrays) + if (!TYPE_VECTOR (type)) + type = lookup_pointer_type (TYPE_TARGET_TYPE (type)); + break; + case TYPE_CODE_UNDEF: + case TYPE_CODE_PTR: + case TYPE_CODE_STRUCT: + case TYPE_CODE_UNION: + case TYPE_CODE_VOID: + case TYPE_CODE_SET: + case TYPE_CODE_RANGE: + case TYPE_CODE_STRING: + case TYPE_CODE_BITSTRING: + case TYPE_CODE_ERROR: + case TYPE_CODE_MEMBER: + case TYPE_CODE_METHOD: + case TYPE_CODE_COMPLEX: + default: + break; + } + + return value_cast (type, arg); +} + +/* Determine a function's address and its return type from its value. + Calls error() if the function is not valid for calling. */ + +static CORE_ADDR +find_function_addr (struct value *function, struct type **retval_type) +{ + register struct type *ftype = check_typedef (VALUE_TYPE (function)); + register enum type_code code = TYPE_CODE (ftype); + struct type *value_type; + CORE_ADDR funaddr; + + /* If it's a member function, just look at the function + part of it. */ + + /* Determine address to call. */ + if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD) + { + funaddr = VALUE_ADDRESS (function); + value_type = TYPE_TARGET_TYPE (ftype); + } + else if (code == TYPE_CODE_PTR) + { + funaddr = value_as_address (function); + ftype = check_typedef (TYPE_TARGET_TYPE (ftype)); + if (TYPE_CODE (ftype) == TYPE_CODE_FUNC + || TYPE_CODE (ftype) == TYPE_CODE_METHOD) + { + funaddr = CONVERT_FROM_FUNC_PTR_ADDR (funaddr); + value_type = TYPE_TARGET_TYPE (ftype); + } + else + value_type = builtin_type_int; + } + else if (code == TYPE_CODE_INT) + { + /* Handle the case of functions lacking debugging info. + Their values are characters since their addresses are char */ + if (TYPE_LENGTH (ftype) == 1) + funaddr = value_as_address (value_addr (function)); + else + /* Handle integer used as address of a function. */ + funaddr = (CORE_ADDR) value_as_long (function); + + value_type = builtin_type_int; + } + else + error ("Invalid data type for function to be called."); + + *retval_type = value_type; + return funaddr; +} + +/* All this stuff with a dummy frame may seem unnecessarily complicated + (why not just save registers in GDB?). The purpose of pushing a dummy + frame which looks just like a real frame is so that if you call a + function and then hit a breakpoint (get a signal, etc), "backtrace" + will look right. Whether the backtrace needs to actually show the + stack at the time the inferior function was called is debatable, but + it certainly needs to not display garbage. So if you are contemplating + making dummy frames be different from normal frames, consider that. */ + +/* Perform a function call in the inferior. + ARGS is a vector of values of arguments (NARGS of them). + FUNCTION is a value, the function to be called. + Returns a value representing what the function returned. + May fail to return, if a breakpoint or signal is hit + during the execution of the function. + + ARGS is modified to contain coerced values. */ + +static struct value * +hand_function_call (struct value *function, int nargs, struct value **args) +{ + register CORE_ADDR sp; + register int i; + int rc; + CORE_ADDR start_sp; + /* CALL_DUMMY is an array of words (REGISTER_SIZE), but each word + is in host byte order. Before calling FIX_CALL_DUMMY, we byteswap it + and remove any extra bytes which might exist because ULONGEST is + bigger than REGISTER_SIZE. + + NOTE: This is pretty wierd, as the call dummy is actually a + sequence of instructions. But CISC machines will have + to pack the instructions into REGISTER_SIZE units (and + so will RISC machines for which INSTRUCTION_SIZE is not + REGISTER_SIZE). + + NOTE: This is pretty stupid. CALL_DUMMY should be in strict + target byte order. */ + + static ULONGEST *dummy; + int sizeof_dummy1; + char *dummy1; + CORE_ADDR old_sp; + struct type *value_type; + unsigned char struct_return; + CORE_ADDR struct_addr = 0; + struct regcache *retbuf; + struct cleanup *retbuf_cleanup; + struct inferior_status *inf_status; + struct cleanup *inf_status_cleanup; + CORE_ADDR funaddr; + int using_gcc; /* Set to version of gcc in use, or zero if not gcc */ + CORE_ADDR real_pc; + struct type *param_type = NULL; + struct type *ftype = check_typedef (SYMBOL_TYPE (function)); + int n_method_args = 0; + + dummy = alloca (SIZEOF_CALL_DUMMY_WORDS); + sizeof_dummy1 = REGISTER_SIZE * SIZEOF_CALL_DUMMY_WORDS / sizeof (ULONGEST); + dummy1 = alloca (sizeof_dummy1); + memcpy (dummy, CALL_DUMMY_WORDS, SIZEOF_CALL_DUMMY_WORDS); + + if (!target_has_execution) + noprocess (); + + /* Create a cleanup chain that contains the retbuf (buffer + containing the register values). This chain is create BEFORE the + inf_status chain so that the inferior status can cleaned up + (restored or discarded) without having the retbuf freed. */ + retbuf = regcache_xmalloc (current_gdbarch); + retbuf_cleanup = make_cleanup_regcache_xfree (retbuf); + + /* A cleanup for the inferior status. Create this AFTER the retbuf + so that this can be discarded or applied without interfering with + the regbuf. */ + inf_status = save_inferior_status (1); + inf_status_cleanup = make_cleanup_restore_inferior_status (inf_status); + + /* PUSH_DUMMY_FRAME is responsible for saving the inferior registers + (and POP_FRAME for restoring them). (At least on most machines) + they are saved on the stack in the inferior. */ + PUSH_DUMMY_FRAME; + + old_sp = read_sp (); + + /* Ensure that the initial SP is correctly aligned. */ + if (gdbarch_frame_align_p (current_gdbarch)) + { + /* NOTE: cagney/2002-09-18: + + On a RISC architecture, a void parameterless generic dummy + frame (i.e., no parameters, no result) typically does not + need to push anything the stack and hence can leave SP and + FP. Similarly, a framelss (possibly leaf) function does not + push anything on the stack and, hence, that too can leave FP + and SP unchanged. As a consequence, a sequence of void + parameterless generic dummy frame calls to frameless + functions will create a sequence of effectively identical + frames (SP, FP and TOS and PC the same). This, not + suprisingly, results in what appears to be a stack in an + infinite loop --- when GDB tries to find a generic dummy + frame on the internal dummy frame stack, it will always find + the first one. + + To avoid this problem, the code below always grows the stack. + That way, two dummy frames can never be identical. It does + burn a few bytes of stack but that is a small price to pay + :-). */ + sp = gdbarch_frame_align (current_gdbarch, old_sp); + if (sp == old_sp) + { + if (INNER_THAN (1, 2)) + /* Stack grows down. */ + sp = gdbarch_frame_align (current_gdbarch, old_sp - 1); + else + /* Stack grows up. */ + sp = gdbarch_frame_align (current_gdbarch, old_sp + 1); + } + gdb_assert ((INNER_THAN (1, 2) && sp <= old_sp) + || (INNER_THAN (2, 1) && sp >= old_sp)); + } + else + /* FIXME: cagney/2002-09-18: Hey, you loose! Who knows how badly + aligned the SP is! Further, per comment above, if the generic + dummy frame ends up empty (because nothing is pushed) GDB won't + be able to correctly perform back traces. If a target is + having trouble with backtraces, first thing to do is add + FRAME_ALIGN() to its architecture vector. After that, try + adding SAVE_DUMMY_FRAME_TOS() and modifying FRAME_CHAIN so that + when the next outer frame is a generic dummy, it returns the + current frame's base. */ + sp = old_sp; + + if (INNER_THAN (1, 2)) + { + /* Stack grows down */ + sp -= sizeof_dummy1; + start_sp = sp; + } + else + { + /* Stack grows up */ + start_sp = sp; + sp += sizeof_dummy1; + } + + /* NOTE: cagney/2002-09-10: Don't bother re-adjusting the stack + after allocating space for the call dummy. A target can specify + a SIZEOF_DUMMY1 (via SIZEOF_CALL_DUMMY_WORDS) such that all local + alignment requirements are met. */ + + funaddr = find_function_addr (function, &value_type); + CHECK_TYPEDEF (value_type); + + { + struct block *b = block_for_pc (funaddr); + /* If compiled without -g, assume GCC 2. */ + using_gcc = (b == NULL ? 2 : BLOCK_GCC_COMPILED (b)); + } + + /* Are we returning a value using a structure return or a normal + value return? */ + + struct_return = using_struct_return (function, funaddr, value_type, + using_gcc); + + /* Create a call sequence customized for this function + and the number of arguments for it. */ + for (i = 0; i < (int) (SIZEOF_CALL_DUMMY_WORDS / sizeof (dummy[0])); i++) + store_unsigned_integer (&dummy1[i * REGISTER_SIZE], + REGISTER_SIZE, + (ULONGEST) dummy[i]); + +#ifdef GDB_TARGET_IS_HPPA + real_pc = FIX_CALL_DUMMY (dummy1, start_sp, funaddr, nargs, args, + value_type, using_gcc); +#else + FIX_CALL_DUMMY (dummy1, start_sp, funaddr, nargs, args, + value_type, using_gcc); + real_pc = start_sp; +#endif + + if (CALL_DUMMY_LOCATION == ON_STACK) + { + write_memory (start_sp, (char *) dummy1, sizeof_dummy1); + if (USE_GENERIC_DUMMY_FRAMES) + generic_save_call_dummy_addr (start_sp, start_sp + sizeof_dummy1); + } + + if (CALL_DUMMY_LOCATION == BEFORE_TEXT_END) + { + /* Convex Unix prohibits executing in the stack segment. */ + /* Hope there is empty room at the top of the text segment. */ + extern CORE_ADDR text_end; + static int checked = 0; + if (!checked) + for (start_sp = text_end - sizeof_dummy1; start_sp < text_end; ++start_sp) + if (read_memory_integer (start_sp, 1) != 0) + error ("text segment full -- no place to put call"); + checked = 1; + sp = old_sp; + real_pc = text_end - sizeof_dummy1; + write_memory (real_pc, (char *) dummy1, sizeof_dummy1); + if (USE_GENERIC_DUMMY_FRAMES) + generic_save_call_dummy_addr (real_pc, real_pc + sizeof_dummy1); + } + + if (CALL_DUMMY_LOCATION == AFTER_TEXT_END) + { + extern CORE_ADDR text_end; + int errcode; + sp = old_sp; + real_pc = text_end; + errcode = target_write_memory (real_pc, (char *) dummy1, sizeof_dummy1); + if (errcode != 0) + error ("Cannot write text segment -- call_function failed"); + if (USE_GENERIC_DUMMY_FRAMES) + generic_save_call_dummy_addr (real_pc, real_pc + sizeof_dummy1); + } + + if (CALL_DUMMY_LOCATION == AT_ENTRY_POINT) + { + real_pc = funaddr; + if (USE_GENERIC_DUMMY_FRAMES) + /* NOTE: cagney/2002-04-13: The entry point is going to be + modified with a single breakpoint. */ + generic_save_call_dummy_addr (CALL_DUMMY_ADDRESS (), + CALL_DUMMY_ADDRESS () + 1); + } + +#ifdef lint + sp = old_sp; /* It really is used, for some ifdef's... */ +#endif + + if (nargs < TYPE_NFIELDS (ftype)) + error ("too few arguments in function call"); + + for (i = nargs - 1; i >= 0; i--) + { + int prototyped; + + /* FIXME drow/2002-05-31: Should just always mark methods as + prototyped. Can we respect TYPE_VARARGS? Probably not. */ + if (TYPE_CODE (ftype) == TYPE_CODE_METHOD) + prototyped = 1; + else + prototyped = TYPE_PROTOTYPED (ftype); + + if (i < TYPE_NFIELDS (ftype)) + args[i] = value_arg_coerce (args[i], TYPE_FIELD_TYPE (ftype, i), + prototyped); + else + args[i] = value_arg_coerce (args[i], NULL, 0); + + /*elz: this code is to handle the case in which the function to be called + has a pointer to function as parameter and the corresponding actual argument + is the address of a function and not a pointer to function variable. + In aCC compiled code, the calls through pointers to functions (in the body + of the function called by hand) are made via $$dyncall_external which + requires some registers setting, this is taken care of if we call + via a function pointer variable, but not via a function address. + In cc this is not a problem. */ + + if (using_gcc == 0) + if (param_type && TYPE_CODE (ftype) != TYPE_CODE_METHOD) + /* if this parameter is a pointer to function */ + if (TYPE_CODE (param_type) == TYPE_CODE_PTR) + if (TYPE_CODE (TYPE_TARGET_TYPE (param_type)) == TYPE_CODE_FUNC) + /* elz: FIXME here should go the test about the compiler used + to compile the target. We want to issue the error + message only if the compiler used was HP's aCC. + If we used HP's cc, then there is no problem and no need + to return at this point */ + if (using_gcc == 0) /* && compiler == aCC */ + /* go see if the actual parameter is a variable of type + pointer to function or just a function */ + if (args[i]->lval == not_lval) + { + char *arg_name; + if (find_pc_partial_function ((CORE_ADDR) args[i]->aligner.contents[0], &arg_name, NULL, NULL)) + error ("\ +You cannot use function <%s> as argument. \n\ +You must use a pointer to function type variable. Command ignored.", arg_name); + } + } + + if (REG_STRUCT_HAS_ADDR_P ()) + { + /* This is a machine like the sparc, where we may need to pass a + pointer to the structure, not the structure itself. */ + for (i = nargs - 1; i >= 0; i--) + { + struct type *arg_type = check_typedef (VALUE_TYPE (args[i])); + if ((TYPE_CODE (arg_type) == TYPE_CODE_STRUCT + || TYPE_CODE (arg_type) == TYPE_CODE_UNION + || TYPE_CODE (arg_type) == TYPE_CODE_ARRAY + || TYPE_CODE (arg_type) == TYPE_CODE_STRING + || TYPE_CODE (arg_type) == TYPE_CODE_BITSTRING + || TYPE_CODE (arg_type) == TYPE_CODE_SET + || (TYPE_CODE (arg_type) == TYPE_CODE_FLT + && TYPE_LENGTH (arg_type) > 8) + ) + && REG_STRUCT_HAS_ADDR (using_gcc, arg_type)) + { + CORE_ADDR addr; + int len; /* = TYPE_LENGTH (arg_type); */ + int aligned_len; + arg_type = check_typedef (VALUE_ENCLOSING_TYPE (args[i])); + len = TYPE_LENGTH (arg_type); + + if (STACK_ALIGN_P ()) + /* MVS 11/22/96: I think at least some of this + stack_align code is really broken. Better to let + PUSH_ARGUMENTS adjust the stack in a target-defined + manner. */ + aligned_len = STACK_ALIGN (len); + else + aligned_len = len; + if (INNER_THAN (1, 2)) + { + /* stack grows downward */ + sp -= aligned_len; + /* ... so the address of the thing we push is the + stack pointer after we push it. */ + addr = sp; + } + else + { + /* The stack grows up, so the address of the thing + we push is the stack pointer before we push it. */ + addr = sp; + sp += aligned_len; + } + /* Push the structure. */ + write_memory (addr, VALUE_CONTENTS_ALL (args[i]), len); + /* The value we're going to pass is the address of the + thing we just pushed. */ + /*args[i] = value_from_longest (lookup_pointer_type (value_type), + (LONGEST) addr); */ + args[i] = value_from_pointer (lookup_pointer_type (arg_type), + addr); + } + } + } + + + /* Reserve space for the return structure to be written on the + stack, if necessary. Make certain that the value is correctly + aligned. */ + + if (struct_return) + { + int len = TYPE_LENGTH (value_type); + if (STACK_ALIGN_P ()) + /* MVS 11/22/96: I think at least some of this stack_align + code is really broken. Better to let PUSH_ARGUMENTS adjust + the stack in a target-defined manner. */ + len = STACK_ALIGN (len); + if (INNER_THAN (1, 2)) + { + /* Stack grows downward. Align STRUCT_ADDR and SP after + making space for the return value. */ + sp -= len; + if (gdbarch_frame_align_p (current_gdbarch)) + sp = gdbarch_frame_align (current_gdbarch, sp); + struct_addr = sp; + } + else + { + /* Stack grows upward. Align the frame, allocate space, and + then again, re-align the frame??? */ + if (gdbarch_frame_align_p (current_gdbarch)) + sp = gdbarch_frame_align (current_gdbarch, sp); + struct_addr = sp; + sp += len; + if (gdbarch_frame_align_p (current_gdbarch)) + sp = gdbarch_frame_align (current_gdbarch, sp); + } + } + + /* elz: on HPPA no need for this extra alignment, maybe it is needed + on other architectures. This is because all the alignment is + taken care of in the above code (ifdef REG_STRUCT_HAS_ADDR) and + in hppa_push_arguments */ + if (EXTRA_STACK_ALIGNMENT_NEEDED) + { + /* MVS 11/22/96: I think at least some of this stack_align code + is really broken. Better to let PUSH_ARGUMENTS adjust the + stack in a target-defined manner. */ + if (STACK_ALIGN_P () && INNER_THAN (1, 2)) + { + /* If stack grows down, we must leave a hole at the top. */ + int len = 0; + + for (i = nargs - 1; i >= 0; i--) + len += TYPE_LENGTH (VALUE_ENCLOSING_TYPE (args[i])); + if (CALL_DUMMY_STACK_ADJUST_P) + len += CALL_DUMMY_STACK_ADJUST; + sp -= STACK_ALIGN (len) - len; + } + } + + sp = PUSH_ARGUMENTS (nargs, args, sp, struct_return, struct_addr); + + if (PUSH_RETURN_ADDRESS_P ()) + /* for targets that use no CALL_DUMMY */ + /* There are a number of targets now which actually don't write + any CALL_DUMMY instructions into the target, but instead just + save the machine state, push the arguments, and jump directly + to the callee function. Since this doesn't actually involve + executing a JSR/BSR instruction, the return address must be set + up by hand, either by pushing onto the stack or copying into a + return-address register as appropriate. Formerly this has been + done in PUSH_ARGUMENTS, but that's overloading its + functionality a bit, so I'm making it explicit to do it here. */ + sp = PUSH_RETURN_ADDRESS (real_pc, sp); + + if (STACK_ALIGN_P () && !INNER_THAN (1, 2)) + { + /* If stack grows up, we must leave a hole at the bottom, note + that sp already has been advanced for the arguments! */ + if (CALL_DUMMY_STACK_ADJUST_P) + sp += CALL_DUMMY_STACK_ADJUST; + sp = STACK_ALIGN (sp); + } + +/* XXX This seems wrong. For stacks that grow down we shouldn't do + anything here! */ + /* MVS 11/22/96: I think at least some of this stack_align code is + really broken. Better to let PUSH_ARGUMENTS adjust the stack in + a target-defined manner. */ + if (CALL_DUMMY_STACK_ADJUST_P) + if (INNER_THAN (1, 2)) + { + /* stack grows downward */ + sp -= CALL_DUMMY_STACK_ADJUST; + } + + /* Store the address at which the structure is supposed to be + written. Note that this (and the code which reserved the space + above) assumes that gcc was used to compile this function. Since + it doesn't cost us anything but space and if the function is pcc + it will ignore this value, we will make that assumption. + + Also note that on some machines (like the sparc) pcc uses a + convention like gcc's. */ + + if (struct_return) + STORE_STRUCT_RETURN (struct_addr, sp); + + /* Write the stack pointer. This is here because the statements above + might fool with it. On SPARC, this write also stores the register + window into the right place in the new stack frame, which otherwise + wouldn't happen. (See store_inferior_registers in sparc-nat.c.) */ + write_sp (sp); + + if (SAVE_DUMMY_FRAME_TOS_P ()) + SAVE_DUMMY_FRAME_TOS (sp); + + { + char *name; + struct symbol *symbol; + + name = NULL; + symbol = find_pc_function (funaddr); + if (symbol) + { + name = SYMBOL_SOURCE_NAME (symbol); + } + else + { + /* Try the minimal symbols. */ + struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (funaddr); + + if (msymbol) + { + name = SYMBOL_SOURCE_NAME (msymbol); + } + } + if (name == NULL) + { + char format[80]; + sprintf (format, "at %s", local_hex_format ()); + name = alloca (80); + /* FIXME-32x64: assumes funaddr fits in a long. */ + sprintf (name, format, (unsigned long) funaddr); + } + + /* Execute the stack dummy routine, calling FUNCTION. + When it is done, discard the empty frame + after storing the contents of all regs into retbuf. */ + rc = run_stack_dummy (real_pc + CALL_DUMMY_START_OFFSET, retbuf); + + if (rc == 1) + { + /* We stopped inside the FUNCTION because of a random signal. + Further execution of the FUNCTION is not allowed. */ + + if (unwind_on_signal_p) + { + /* The user wants the context restored. */ + + /* We must get back to the frame we were before the dummy call. */ + POP_FRAME; + + /* FIXME: Insert a bunch of wrap_here; name can be very long if it's + a C++ name with arguments and stuff. */ + error ("\ +The program being debugged was signaled while in a function called from GDB.\n\ +GDB has restored the context to what it was before the call.\n\ +To change this behavior use \"set unwindonsignal off\"\n\ +Evaluation of the expression containing the function (%s) will be abandoned.", + name); + } + else + { + /* The user wants to stay in the frame where we stopped (default).*/ + + /* If we restored the inferior status (via the cleanup), + we would print a spurious error message (Unable to + restore previously selected frame), would write the + registers from the inf_status (which is wrong), and + would do other wrong things. */ + discard_cleanups (inf_status_cleanup); + discard_inferior_status (inf_status); + + /* FIXME: Insert a bunch of wrap_here; name can be very long if it's + a C++ name with arguments and stuff. */ + error ("\ +The program being debugged was signaled while in a function called from GDB.\n\ +GDB remains in the frame where the signal was received.\n\ +To change this behavior use \"set unwindonsignal on\"\n\ +Evaluation of the expression containing the function (%s) will be abandoned.", + name); + } + } + + if (rc == 2) + { + /* We hit a breakpoint inside the FUNCTION. */ + + /* If we restored the inferior status (via the cleanup), we + would print a spurious error message (Unable to restore + previously selected frame), would write the registers from + the inf_status (which is wrong), and would do other wrong + things. */ + discard_cleanups (inf_status_cleanup); + discard_inferior_status (inf_status); + + /* The following error message used to say "The expression + which contained the function call has been discarded." It + is a hard concept to explain in a few words. Ideally, GDB + would be able to resume evaluation of the expression when + the function finally is done executing. Perhaps someday + this will be implemented (it would not be easy). */ + + /* FIXME: Insert a bunch of wrap_here; name can be very long if it's + a C++ name with arguments and stuff. */ + error ("\ +The program being debugged stopped while in a function called from GDB.\n\ +When the function (%s) is done executing, GDB will silently\n\ +stop (instead of continuing to evaluate the expression containing\n\ +the function call).", name); + } + + /* If we get here the called FUNCTION run to completion. */ + + /* Restore the inferior status, via its cleanup. At this stage, + leave the RETBUF alone. */ + do_cleanups (inf_status_cleanup); + + /* Figure out the value returned by the function. */ + /* elz: I defined this new macro for the hppa architecture only. + this gives us a way to get the value returned by the function + from the stack, at the same address we told the function to put + it. We cannot assume on the pa that r28 still contains the + address of the returned structure. Usually this will be + overwritten by the callee. I don't know about other + architectures, so I defined this macro */ +#ifdef VALUE_RETURNED_FROM_STACK + if (struct_return) + { + do_cleanups (retbuf_cleanup); + return VALUE_RETURNED_FROM_STACK (value_type, struct_addr); + } +#endif + /* NOTE: cagney/2002-09-10: Only when the stack has been correctly + aligned (using frame_align()) do we can trust STRUCT_ADDR and + fetch the return value direct from the stack. This lack of + trust comes about because legacy targets have a nasty habit of + silently, and local to PUSH_ARGUMENTS(), moving STRUCT_ADDR. + For such targets, just hope that value_being_returned() can + find the adjusted value. */ + if (struct_return && gdbarch_frame_align_p (current_gdbarch)) + { + struct value *retval = value_at (value_type, struct_addr, NULL); + do_cleanups (retbuf_cleanup); + return retval; + } + else + { + struct value *retval = value_being_returned (value_type, retbuf, + struct_return); + do_cleanups (retbuf_cleanup); + return retval; + } + } +} + +struct value * +call_function_by_hand (struct value *function, int nargs, struct value **args) +{ + if (CALL_DUMMY_P) + { + return hand_function_call (function, nargs, args); + } + else + { + error ("Cannot invoke functions on this machine."); + } +} + + + +/* Create a value for an array by allocating space in the inferior, copying + the data into that space, and then setting up an array value. + + The array bounds are set from LOWBOUND and HIGHBOUND, and the array is + populated from the values passed in ELEMVEC. + + The element type of the array is inherited from the type of the + first element, and all elements must have the same size (though we + don't currently enforce any restriction on their types). */ + +struct value * +value_array (int lowbound, int highbound, struct value **elemvec) +{ + int nelem; + int idx; + unsigned int typelength; + struct value *val; + struct type *rangetype; + struct type *arraytype; + CORE_ADDR addr; + + /* Validate that the bounds are reasonable and that each of the elements + have the same size. */ + + nelem = highbound - lowbound + 1; + if (nelem <= 0) + { + error ("bad array bounds (%d, %d)", lowbound, highbound); + } + typelength = TYPE_LENGTH (VALUE_ENCLOSING_TYPE (elemvec[0])); + for (idx = 1; idx < nelem; idx++) + { + if (TYPE_LENGTH (VALUE_ENCLOSING_TYPE (elemvec[idx])) != typelength) + { + error ("array elements must all be the same size"); + } + } + + rangetype = create_range_type ((struct type *) NULL, builtin_type_int, + lowbound, highbound); + arraytype = create_array_type ((struct type *) NULL, + VALUE_ENCLOSING_TYPE (elemvec[0]), rangetype); + + if (!current_language->c_style_arrays) + { + val = allocate_value (arraytype); + for (idx = 0; idx < nelem; idx++) + { + memcpy (VALUE_CONTENTS_ALL_RAW (val) + (idx * typelength), + VALUE_CONTENTS_ALL (elemvec[idx]), + typelength); + } + VALUE_BFD_SECTION (val) = VALUE_BFD_SECTION (elemvec[0]); + return val; + } + + /* Allocate space to store the array in the inferior, and then initialize + it by copying in each element. FIXME: Is it worth it to create a + local buffer in which to collect each value and then write all the + bytes in one operation? */ + + addr = allocate_space_in_inferior (nelem * typelength); + for (idx = 0; idx < nelem; idx++) + { + write_memory (addr + (idx * typelength), VALUE_CONTENTS_ALL (elemvec[idx]), + typelength); + } + + /* Create the array type and set up an array value to be evaluated lazily. */ + + val = value_at_lazy (arraytype, addr, VALUE_BFD_SECTION (elemvec[0])); + return (val); +} + +/* Create a value for a string constant by allocating space in the inferior, + copying the data into that space, and returning the address with type + TYPE_CODE_STRING. PTR points to the string constant data; LEN is number + of characters. + Note that string types are like array of char types with a lower bound of + zero and an upper bound of LEN - 1. Also note that the string may contain + embedded null bytes. */ + +struct value * +value_string (char *ptr, int len) +{ + struct value *val; + int lowbound = current_language->string_lower_bound; + struct type *rangetype = create_range_type ((struct type *) NULL, + builtin_type_int, + lowbound, len + lowbound - 1); + struct type *stringtype + = create_string_type ((struct type *) NULL, rangetype); + CORE_ADDR addr; + + if (current_language->c_style_arrays == 0) + { + val = allocate_value (stringtype); + memcpy (VALUE_CONTENTS_RAW (val), ptr, len); + return val; + } + + + /* Allocate space to store the string in the inferior, and then + copy LEN bytes from PTR in gdb to that address in the inferior. */ + + addr = allocate_space_in_inferior (len); + write_memory (addr, ptr, len); + + val = value_at_lazy (stringtype, addr, NULL); + return (val); +} + +struct value * +value_bitstring (char *ptr, int len) +{ + struct value *val; + struct type *domain_type = create_range_type (NULL, builtin_type_int, + 0, len - 1); + struct type *type = create_set_type ((struct type *) NULL, domain_type); + TYPE_CODE (type) = TYPE_CODE_BITSTRING; + val = allocate_value (type); + memcpy (VALUE_CONTENTS_RAW (val), ptr, TYPE_LENGTH (type)); + return val; +} + +/* See if we can pass arguments in T2 to a function which takes arguments + of types T1. T1 is a list of NARGS arguments, and T2 is a NULL-terminated + vector. If some arguments need coercion of some sort, then the coerced + values are written into T2. Return value is 0 if the arguments could be + matched, or the position at which they differ if not. + + STATICP is nonzero if the T1 argument list came from a + static member function. T2 will still include the ``this'' pointer, + but it will be skipped. + + For non-static member functions, we ignore the first argument, + which is the type of the instance variable. This is because we want + to handle calls with objects from derived classes. This is not + entirely correct: we should actually check to make sure that a + requested operation is type secure, shouldn't we? FIXME. */ + +static int +typecmp (int staticp, int varargs, int nargs, + struct field t1[], struct value *t2[]) +{ + int i; + + if (t2 == 0) + internal_error (__FILE__, __LINE__, "typecmp: no argument list"); + + /* Skip ``this'' argument if applicable. T2 will always include THIS. */ + if (staticp) + t2 ++; + + for (i = 0; + (i < nargs) && TYPE_CODE (t1[i].type) != TYPE_CODE_VOID; + i++) + { + struct type *tt1, *tt2; + + if (!t2[i]) + return i + 1; + + tt1 = check_typedef (t1[i].type); + tt2 = check_typedef (VALUE_TYPE (t2[i])); + + if (TYPE_CODE (tt1) == TYPE_CODE_REF + /* We should be doing hairy argument matching, as below. */ + && (TYPE_CODE (check_typedef (TYPE_TARGET_TYPE (tt1))) == TYPE_CODE (tt2))) + { + if (TYPE_CODE (tt2) == TYPE_CODE_ARRAY) + t2[i] = value_coerce_array (t2[i]); + else + t2[i] = value_addr (t2[i]); + continue; + } + + /* djb - 20000715 - Until the new type structure is in the + place, and we can attempt things like implicit conversions, + we need to do this so you can take something like a map, and properly access map["hello"], because the + argument to [] will be a reference to a pointer to a char, + and the argument will be a pointer to a char. */ + while ( TYPE_CODE(tt1) == TYPE_CODE_REF || + TYPE_CODE (tt1) == TYPE_CODE_PTR) + { + tt1 = check_typedef( TYPE_TARGET_TYPE(tt1) ); + } + while ( TYPE_CODE(tt2) == TYPE_CODE_ARRAY || + TYPE_CODE(tt2) == TYPE_CODE_PTR || + TYPE_CODE(tt2) == TYPE_CODE_REF) + { + tt2 = check_typedef( TYPE_TARGET_TYPE(tt2) ); + } + if (TYPE_CODE (tt1) == TYPE_CODE (tt2)) + continue; + /* Array to pointer is a `trivial conversion' according to the ARM. */ + + /* We should be doing much hairier argument matching (see section 13.2 + of the ARM), but as a quick kludge, just check for the same type + code. */ + if (TYPE_CODE (t1[i].type) != TYPE_CODE (VALUE_TYPE (t2[i]))) + return i + 1; + } + if (varargs || t2[i] == NULL) + return 0; + return i + 1; +} + +/* Helper function used by value_struct_elt to recurse through baseclasses. + Look for a field NAME in ARG1. Adjust the address of ARG1 by OFFSET bytes, + and search in it assuming it has (class) type TYPE. + If found, return value, else return NULL. + + If LOOKING_FOR_BASECLASS, then instead of looking for struct fields, + look for a baseclass named NAME. */ + +static struct value * +search_struct_field (char *name, struct value *arg1, int offset, + register struct type *type, int looking_for_baseclass) +{ + int i; + int nbases = TYPE_N_BASECLASSES (type); + + CHECK_TYPEDEF (type); + + if (!looking_for_baseclass) + for (i = TYPE_NFIELDS (type) - 1; i >= nbases; i--) + { + char *t_field_name = TYPE_FIELD_NAME (type, i); + + if (t_field_name && (strcmp_iw (t_field_name, name) == 0)) + { + struct value *v; + if (TYPE_FIELD_STATIC (type, i)) + { + v = value_static_field (type, i); + if (v == 0) + error ("field %s is nonexistent or has been optimised out", + name); + } + else + { + v = value_primitive_field (arg1, offset, i, type); + if (v == 0) + error ("there is no field named %s", name); + } + return v; + } + + if (t_field_name + && (t_field_name[0] == '\0' + || (TYPE_CODE (type) == TYPE_CODE_UNION + && (strcmp_iw (t_field_name, "else") == 0)))) + { + struct type *field_type = TYPE_FIELD_TYPE (type, i); + if (TYPE_CODE (field_type) == TYPE_CODE_UNION + || TYPE_CODE (field_type) == TYPE_CODE_STRUCT) + { + /* Look for a match through the fields of an anonymous union, + or anonymous struct. C++ provides anonymous unions. + + In the GNU Chill (OBSOLETE) implementation of + variant record types, each has + an (anonymous) union type, each member of the union + represents a . Each is represented as a struct, with a + member for each . */ + + struct value *v; + int new_offset = offset; + + /* This is pretty gross. In G++, the offset in an + anonymous union is relative to the beginning of the + enclosing struct. In the GNU Chill (OBSOLETE) + implementation of variant records, the bitpos is + zero in an anonymous union field, so we have to add + the offset of the union here. */ + if (TYPE_CODE (field_type) == TYPE_CODE_STRUCT + || (TYPE_NFIELDS (field_type) > 0 + && TYPE_FIELD_BITPOS (field_type, 0) == 0)) + new_offset += TYPE_FIELD_BITPOS (type, i) / 8; + + v = search_struct_field (name, arg1, new_offset, field_type, + looking_for_baseclass); + if (v) + return v; + } + } + } + + for (i = 0; i < nbases; i++) + { + struct value *v; + struct type *basetype = check_typedef (TYPE_BASECLASS (type, i)); + /* If we are looking for baseclasses, this is what we get when we + hit them. But it could happen that the base part's member name + is not yet filled in. */ + int found_baseclass = (looking_for_baseclass + && TYPE_BASECLASS_NAME (type, i) != NULL + && (strcmp_iw (name, TYPE_BASECLASS_NAME (type, i)) == 0)); + + if (BASETYPE_VIA_VIRTUAL (type, i)) + { + int boffset; + struct value *v2 = allocate_value (basetype); + + boffset = baseclass_offset (type, i, + VALUE_CONTENTS (arg1) + offset, + VALUE_ADDRESS (arg1) + + VALUE_OFFSET (arg1) + offset); + if (boffset == -1) + error ("virtual baseclass botch"); + + /* The virtual base class pointer might have been clobbered by the + user program. Make sure that it still points to a valid memory + location. */ + + boffset += offset; + if (boffset < 0 || boffset >= TYPE_LENGTH (type)) + { + CORE_ADDR base_addr; + + base_addr = VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1) + boffset; + if (target_read_memory (base_addr, VALUE_CONTENTS_RAW (v2), + TYPE_LENGTH (basetype)) != 0) + error ("virtual baseclass botch"); + VALUE_LVAL (v2) = lval_memory; + VALUE_ADDRESS (v2) = base_addr; + } + else + { + VALUE_LVAL (v2) = VALUE_LVAL (arg1); + VALUE_ADDRESS (v2) = VALUE_ADDRESS (arg1); + VALUE_OFFSET (v2) = VALUE_OFFSET (arg1) + boffset; + if (VALUE_LAZY (arg1)) + VALUE_LAZY (v2) = 1; + else + memcpy (VALUE_CONTENTS_RAW (v2), + VALUE_CONTENTS_RAW (arg1) + boffset, + TYPE_LENGTH (basetype)); + } + + if (found_baseclass) + return v2; + v = search_struct_field (name, v2, 0, TYPE_BASECLASS (type, i), + looking_for_baseclass); + } + else if (found_baseclass) + v = value_primitive_field (arg1, offset, i, type); + else + v = search_struct_field (name, arg1, + offset + TYPE_BASECLASS_BITPOS (type, i) / 8, + basetype, looking_for_baseclass); + if (v) + return v; + } + return NULL; +} + + +/* Return the offset (in bytes) of the virtual base of type BASETYPE + * in an object pointed to by VALADDR (on the host), assumed to be of + * type TYPE. OFFSET is number of bytes beyond start of ARG to start + * looking (in case VALADDR is the contents of an enclosing object). + * + * This routine recurses on the primary base of the derived class because + * the virtual base entries of the primary base appear before the other + * virtual base entries. + * + * If the virtual base is not found, a negative integer is returned. + * The magnitude of the negative integer is the number of entries in + * the virtual table to skip over (entries corresponding to various + * ancestral classes in the chain of primary bases). + * + * Important: This assumes the HP / Taligent C++ runtime + * conventions. Use baseclass_offset() instead to deal with g++ + * conventions. */ + +void +find_rt_vbase_offset (struct type *type, struct type *basetype, char *valaddr, + int offset, int *boffset_p, int *skip_p) +{ + int boffset; /* offset of virtual base */ + int index; /* displacement to use in virtual table */ + int skip; + + struct value *vp; + CORE_ADDR vtbl; /* the virtual table pointer */ + struct type *pbc; /* the primary base class */ + + /* Look for the virtual base recursively in the primary base, first. + * This is because the derived class object and its primary base + * subobject share the primary virtual table. */ + + boffset = 0; + pbc = TYPE_PRIMARY_BASE (type); + if (pbc) + { + find_rt_vbase_offset (pbc, basetype, valaddr, offset, &boffset, &skip); + if (skip < 0) + { + *boffset_p = boffset; + *skip_p = -1; + return; + } + } + else + skip = 0; + + + /* Find the index of the virtual base according to HP/Taligent + runtime spec. (Depth-first, left-to-right.) */ + index = virtual_base_index_skip_primaries (basetype, type); + + if (index < 0) + { + *skip_p = skip + virtual_base_list_length_skip_primaries (type); + *boffset_p = 0; + return; + } + + /* pai: FIXME -- 32x64 possible problem */ + /* First word (4 bytes) in object layout is the vtable pointer */ + vtbl = *(CORE_ADDR *) (valaddr + offset); + + /* Before the constructor is invoked, things are usually zero'd out. */ + if (vtbl == 0) + error ("Couldn't find virtual table -- object may not be constructed yet."); + + + /* Find virtual base's offset -- jump over entries for primary base + * ancestors, then use the index computed above. But also adjust by + * HP_ACC_VBASE_START for the vtable slots before the start of the + * virtual base entries. Offset is negative -- virtual base entries + * appear _before_ the address point of the virtual table. */ + + /* pai: FIXME -- 32x64 problem, if word = 8 bytes, change multiplier + & use long type */ + + /* epstein : FIXME -- added param for overlay section. May not be correct */ + vp = value_at (builtin_type_int, vtbl + 4 * (-skip - index - HP_ACC_VBASE_START), NULL); + boffset = value_as_long (vp); + *skip_p = -1; + *boffset_p = boffset; + return; +} + + +/* Helper function used by value_struct_elt to recurse through baseclasses. + Look for a field NAME in ARG1. Adjust the address of ARG1 by OFFSET bytes, + and search in it assuming it has (class) type TYPE. + If found, return value, else if name matched and args not return (value)-1, + else return NULL. */ + +static struct value * +search_struct_method (char *name, struct value **arg1p, + struct value **args, int offset, + int *static_memfuncp, register struct type *type) +{ + int i; + struct value *v; + int name_matched = 0; + char dem_opname[64]; + + CHECK_TYPEDEF (type); + for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--) + { + char *t_field_name = TYPE_FN_FIELDLIST_NAME (type, i); + /* FIXME! May need to check for ARM demangling here */ + if (strncmp (t_field_name, "__", 2) == 0 || + strncmp (t_field_name, "op", 2) == 0 || + strncmp (t_field_name, "type", 4) == 0) + { + if (cplus_demangle_opname (t_field_name, dem_opname, DMGL_ANSI)) + t_field_name = dem_opname; + else if (cplus_demangle_opname (t_field_name, dem_opname, 0)) + t_field_name = dem_opname; + } + if (t_field_name && (strcmp_iw (t_field_name, name) == 0)) + { + int j = TYPE_FN_FIELDLIST_LENGTH (type, i) - 1; + struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); + name_matched = 1; + + check_stub_method_group (type, i); + if (j > 0 && args == 0) + error ("cannot resolve overloaded method `%s': no arguments supplied", name); + else if (j == 0 && args == 0) + { + v = value_fn_field (arg1p, f, j, type, offset); + if (v != NULL) + return v; + } + else + while (j >= 0) + { + if (!typecmp (TYPE_FN_FIELD_STATIC_P (f, j), + TYPE_VARARGS (TYPE_FN_FIELD_TYPE (f, j)), + TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (f, j)), + TYPE_FN_FIELD_ARGS (f, j), args)) + { + if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) + return value_virtual_fn_field (arg1p, f, j, type, offset); + if (TYPE_FN_FIELD_STATIC_P (f, j) && static_memfuncp) + *static_memfuncp = 1; + v = value_fn_field (arg1p, f, j, type, offset); + if (v != NULL) + return v; + } + j--; + } + } + } + + for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--) + { + int base_offset; + + if (BASETYPE_VIA_VIRTUAL (type, i)) + { + if (TYPE_HAS_VTABLE (type)) + { + /* HP aCC compiled type, search for virtual base offset + according to HP/Taligent runtime spec. */ + int skip; + find_rt_vbase_offset (type, TYPE_BASECLASS (type, i), + VALUE_CONTENTS_ALL (*arg1p), + offset + VALUE_EMBEDDED_OFFSET (*arg1p), + &base_offset, &skip); + if (skip >= 0) + error ("Virtual base class offset not found in vtable"); + } + else + { + struct type *baseclass = check_typedef (TYPE_BASECLASS (type, i)); + char *base_valaddr; + + /* The virtual base class pointer might have been clobbered by the + user program. Make sure that it still points to a valid memory + location. */ + + if (offset < 0 || offset >= TYPE_LENGTH (type)) + { + base_valaddr = (char *) alloca (TYPE_LENGTH (baseclass)); + if (target_read_memory (VALUE_ADDRESS (*arg1p) + + VALUE_OFFSET (*arg1p) + offset, + base_valaddr, + TYPE_LENGTH (baseclass)) != 0) + error ("virtual baseclass botch"); + } + else + base_valaddr = VALUE_CONTENTS (*arg1p) + offset; + + base_offset = + baseclass_offset (type, i, base_valaddr, + VALUE_ADDRESS (*arg1p) + + VALUE_OFFSET (*arg1p) + offset); + if (base_offset == -1) + error ("virtual baseclass botch"); + } + } + else + { + base_offset = TYPE_BASECLASS_BITPOS (type, i) / 8; + } + v = search_struct_method (name, arg1p, args, base_offset + offset, + static_memfuncp, TYPE_BASECLASS (type, i)); + if (v == (struct value *) - 1) + { + name_matched = 1; + } + else if (v) + { +/* FIXME-bothner: Why is this commented out? Why is it here? */ +/* *arg1p = arg1_tmp; */ + return v; + } + } + if (name_matched) + return (struct value *) - 1; + else + return NULL; +} + +/* Given *ARGP, a value of type (pointer to a)* structure/union, + extract the component named NAME from the ultimate target structure/union + and return it as a value with its appropriate type. + ERR is used in the error message if *ARGP's type is wrong. + + C++: ARGS is a list of argument types to aid in the selection of + an appropriate method. Also, handle derived types. + + STATIC_MEMFUNCP, if non-NULL, points to a caller-supplied location + where the truthvalue of whether the function that was resolved was + a static member function or not is stored. + + ERR is an error message to be printed in case the field is not found. */ + +struct value * +value_struct_elt (struct value **argp, struct value **args, + char *name, int *static_memfuncp, char *err) +{ + register struct type *t; + struct value *v; + + COERCE_ARRAY (*argp); + + t = check_typedef (VALUE_TYPE (*argp)); + + /* Follow pointers until we get to a non-pointer. */ + + while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF) + { + *argp = value_ind (*argp); + /* Don't coerce fn pointer to fn and then back again! */ + if (TYPE_CODE (VALUE_TYPE (*argp)) != TYPE_CODE_FUNC) + COERCE_ARRAY (*argp); + t = check_typedef (VALUE_TYPE (*argp)); + } + + if (TYPE_CODE (t) == TYPE_CODE_MEMBER) + error ("not implemented: member type in value_struct_elt"); + + if (TYPE_CODE (t) != TYPE_CODE_STRUCT + && TYPE_CODE (t) != TYPE_CODE_UNION) + error ("Attempt to extract a component of a value that is not a %s.", err); + + /* Assume it's not, unless we see that it is. */ + if (static_memfuncp) + *static_memfuncp = 0; + + if (!args) + { + /* if there are no arguments ...do this... */ + + /* Try as a field first, because if we succeed, there + is less work to be done. */ + v = search_struct_field (name, *argp, 0, t, 0); + if (v) + return v; + + /* C++: If it was not found as a data field, then try to + return it as a pointer to a method. */ + + if (destructor_name_p (name, t)) + error ("Cannot get value of destructor"); + + v = search_struct_method (name, argp, args, 0, static_memfuncp, t); + + if (v == (struct value *) - 1) + error ("Cannot take address of a method"); + else if (v == 0) + { + if (TYPE_NFN_FIELDS (t)) + error ("There is no member or method named %s.", name); + else + error ("There is no member named %s.", name); + } + return v; + } + + if (destructor_name_p (name, t)) + { + if (!args[1]) + { + /* Destructors are a special case. */ + int m_index, f_index; + + v = NULL; + if (get_destructor_fn_field (t, &m_index, &f_index)) + { + v = value_fn_field (NULL, TYPE_FN_FIELDLIST1 (t, m_index), + f_index, NULL, 0); + } + if (v == NULL) + error ("could not find destructor function named %s.", name); + else + return v; + } + else + { + error ("destructor should not have any argument"); + } + } + else + v = search_struct_method (name, argp, args, 0, static_memfuncp, t); + + if (v == (struct value *) - 1) + { + error ("One of the arguments you tried to pass to %s could not be converted to what the function wants.", name); + } + else if (v == 0) + { + /* See if user tried to invoke data as function. If so, + hand it back. If it's not callable (i.e., a pointer to function), + gdb should give an error. */ + v = search_struct_field (name, *argp, 0, t, 0); + } + + if (!v) + error ("Structure has no component named %s.", name); + return v; +} + +/* Search through the methods of an object (and its bases) + * to find a specified method. Return the pointer to the + * fn_field list of overloaded instances. + * Helper function for value_find_oload_list. + * ARGP is a pointer to a pointer to a value (the object) + * METHOD is a string containing the method name + * OFFSET is the offset within the value + * TYPE is the assumed type of the object + * NUM_FNS is the number of overloaded instances + * BASETYPE is set to the actual type of the subobject where the method is found + * BOFFSET is the offset of the base subobject where the method is found */ + +static struct fn_field * +find_method_list (struct value **argp, char *method, int offset, + struct type *type, int *num_fns, + struct type **basetype, int *boffset) +{ + int i; + struct fn_field *f; + CHECK_TYPEDEF (type); + + *num_fns = 0; + + /* First check in object itself */ + for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--) + { + /* pai: FIXME What about operators and type conversions? */ + char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i); + if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0)) + { + int len = TYPE_FN_FIELDLIST_LENGTH (type, i); + struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); + + *num_fns = len; + *basetype = type; + *boffset = offset; + + /* Resolve any stub methods. */ + check_stub_method_group (type, i); + + return f; + } + } + + /* Not found in object, check in base subobjects */ + for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--) + { + int base_offset; + if (BASETYPE_VIA_VIRTUAL (type, i)) + { + if (TYPE_HAS_VTABLE (type)) + { + /* HP aCC compiled type, search for virtual base offset + * according to HP/Taligent runtime spec. */ + int skip; + find_rt_vbase_offset (type, TYPE_BASECLASS (type, i), + VALUE_CONTENTS_ALL (*argp), + offset + VALUE_EMBEDDED_OFFSET (*argp), + &base_offset, &skip); + if (skip >= 0) + error ("Virtual base class offset not found in vtable"); + } + else + { + /* probably g++ runtime model */ + base_offset = VALUE_OFFSET (*argp) + offset; + base_offset = + baseclass_offset (type, i, + VALUE_CONTENTS (*argp) + base_offset, + VALUE_ADDRESS (*argp) + base_offset); + if (base_offset == -1) + error ("virtual baseclass botch"); + } + } + else + /* non-virtual base, simply use bit position from debug info */ + { + base_offset = TYPE_BASECLASS_BITPOS (type, i) / 8; + } + f = find_method_list (argp, method, base_offset + offset, + TYPE_BASECLASS (type, i), num_fns, basetype, + boffset); + if (f) + return f; + } + return NULL; +} + +/* Return the list of overloaded methods of a specified name. + * ARGP is a pointer to a pointer to a value (the object) + * METHOD is the method name + * OFFSET is the offset within the value contents + * NUM_FNS is the number of overloaded instances + * BASETYPE is set to the type of the base subobject that defines the method + * BOFFSET is the offset of the base subobject which defines the method */ + +struct fn_field * +value_find_oload_method_list (struct value **argp, char *method, int offset, + int *num_fns, struct type **basetype, + int *boffset) +{ + struct type *t; + + t = check_typedef (VALUE_TYPE (*argp)); + + /* code snarfed from value_struct_elt */ + while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF) + { + *argp = value_ind (*argp); + /* Don't coerce fn pointer to fn and then back again! */ + if (TYPE_CODE (VALUE_TYPE (*argp)) != TYPE_CODE_FUNC) + COERCE_ARRAY (*argp); + t = check_typedef (VALUE_TYPE (*argp)); + } + + if (TYPE_CODE (t) == TYPE_CODE_MEMBER) + error ("Not implemented: member type in value_find_oload_lis"); + + if (TYPE_CODE (t) != TYPE_CODE_STRUCT + && TYPE_CODE (t) != TYPE_CODE_UNION) + error ("Attempt to extract a component of a value that is not a struct or union"); + + return find_method_list (argp, method, 0, t, num_fns, basetype, boffset); +} + +/* Given an array of argument types (ARGTYPES) (which includes an + entry for "this" in the case of C++ methods), the number of + arguments NARGS, the NAME of a function whether it's a method or + not (METHOD), and the degree of laxness (LAX) in conforming to + overload resolution rules in ANSI C++, find the best function that + matches on the argument types according to the overload resolution + rules. + + In the case of class methods, the parameter OBJ is an object value + in which to search for overloaded methods. + + In the case of non-method functions, the parameter FSYM is a symbol + corresponding to one of the overloaded functions. + + Return value is an integer: 0 -> good match, 10 -> debugger applied + non-standard coercions, 100 -> incompatible. + + If a method is being searched for, VALP will hold the value. + If a non-method is being searched for, SYMP will hold the symbol for it. + + If a method is being searched for, and it is a static method, + then STATICP will point to a non-zero value. + + Note: This function does *not* check the value of + overload_resolution. Caller must check it to see whether overload + resolution is permitted. + */ + +int +find_overload_match (struct type **arg_types, int nargs, char *name, int method, + int lax, struct value **objp, struct symbol *fsym, + struct value **valp, struct symbol **symp, int *staticp) +{ + int nparms; + struct type **parm_types; + int champ_nparms = 0; + struct value *obj = (objp ? *objp : NULL); + + short oload_champ = -1; /* Index of best overloaded function */ + short oload_ambiguous = 0; /* Current ambiguity state for overload resolution */ + /* 0 => no ambiguity, 1 => two good funcs, 2 => incomparable funcs */ + short oload_ambig_champ = -1; /* 2nd contender for best match */ + short oload_non_standard = 0; /* did we have to use non-standard conversions? */ + short oload_incompatible = 0; /* are args supplied incompatible with any function? */ + + struct badness_vector *bv; /* A measure of how good an overloaded instance is */ + struct badness_vector *oload_champ_bv = NULL; /* The measure for the current best match */ + + struct value *temp = obj; + struct fn_field *fns_ptr = NULL; /* For methods, the list of overloaded methods */ + struct symbol **oload_syms = NULL; /* For non-methods, the list of overloaded function symbols */ + int num_fns = 0; /* Number of overloaded instances being considered */ + struct type *basetype = NULL; + int boffset; + register int jj; + register int ix; + int static_offset; + struct cleanup *cleanups = NULL; + + char *obj_type_name = NULL; + char *func_name = NULL; + + /* Get the list of overloaded methods or functions */ + if (method) + { + obj_type_name = TYPE_NAME (VALUE_TYPE (obj)); + /* Hack: evaluate_subexp_standard often passes in a pointer + value rather than the object itself, so try again */ + if ((!obj_type_name || !*obj_type_name) && + (TYPE_CODE (VALUE_TYPE (obj)) == TYPE_CODE_PTR)) + obj_type_name = TYPE_NAME (TYPE_TARGET_TYPE (VALUE_TYPE (obj))); + + fns_ptr = value_find_oload_method_list (&temp, name, 0, + &num_fns, + &basetype, &boffset); + if (!fns_ptr || !num_fns) + error ("Couldn't find method %s%s%s", + obj_type_name, + (obj_type_name && *obj_type_name) ? "::" : "", + name); + /* If we are dealing with stub method types, they should have + been resolved by find_method_list via value_find_oload_method_list + above. */ + gdb_assert (TYPE_DOMAIN_TYPE (fns_ptr[0].type) != NULL); + } + else + { + int i = -1; + func_name = cplus_demangle (SYMBOL_NAME (fsym), DMGL_NO_OPTS); + + /* If the name is NULL this must be a C-style function. + Just return the same symbol. */ + if (!func_name) + { + *symp = fsym; + return 0; + } + + oload_syms = make_symbol_overload_list (fsym); + cleanups = make_cleanup (xfree, oload_syms); + while (oload_syms[++i]) + num_fns++; + if (!num_fns) + error ("Couldn't find function %s", func_name); + } + + oload_champ_bv = NULL; + + /* Consider each candidate in turn */ + for (ix = 0; ix < num_fns; ix++) + { + static_offset = 0; + if (method) + { + if (TYPE_FN_FIELD_STATIC_P (fns_ptr, ix)) + static_offset = 1; + nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix)); + } + else + { + /* If it's not a method, this is the proper place */ + nparms=TYPE_NFIELDS(SYMBOL_TYPE(oload_syms[ix])); + } + + /* Prepare array of parameter types */ + parm_types = (struct type **) xmalloc (nparms * (sizeof (struct type *))); + for (jj = 0; jj < nparms; jj++) + parm_types[jj] = (method + ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type) + : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]), jj)); + + /* Compare parameter types to supplied argument types. Skip THIS for + static methods. */ + bv = rank_function (parm_types, nparms, arg_types + static_offset, + nargs - static_offset); + + if (!oload_champ_bv) + { + oload_champ_bv = bv; + oload_champ = 0; + champ_nparms = nparms; + } + else + /* See whether current candidate is better or worse than previous best */ + switch (compare_badness (bv, oload_champ_bv)) + { + case 0: + oload_ambiguous = 1; /* top two contenders are equally good */ + oload_ambig_champ = ix; + break; + case 1: + oload_ambiguous = 2; /* incomparable top contenders */ + oload_ambig_champ = ix; + break; + case 2: + oload_champ_bv = bv; /* new champion, record details */ + oload_ambiguous = 0; + oload_champ = ix; + oload_ambig_champ = -1; + champ_nparms = nparms; + break; + case 3: + default: + break; + } + xfree (parm_types); + if (overload_debug) + { + if (method) + fprintf_filtered (gdb_stderr,"Overloaded method instance %s, # of parms %d\n", fns_ptr[ix].physname, nparms); + else + fprintf_filtered (gdb_stderr,"Overloaded function instance %s # of parms %d\n", SYMBOL_DEMANGLED_NAME (oload_syms[ix]), nparms); + for (jj = 0; jj < nargs - static_offset; jj++) + fprintf_filtered (gdb_stderr,"...Badness @ %d : %d\n", jj, bv->rank[jj]); + fprintf_filtered (gdb_stderr,"Overload resolution champion is %d, ambiguous? %d\n", oload_champ, oload_ambiguous); + } + } /* end loop over all candidates */ + /* NOTE: dan/2000-03-10: Seems to be a better idea to just pick one + if they have the exact same goodness. This is because there is no + way to differentiate based on return type, which we need to in + cases like overloads of .begin() */ +#if 0 + if (oload_ambiguous) + { + if (method) + error ("Cannot resolve overloaded method %s%s%s to unique instance; disambiguate by specifying function signature", + obj_type_name, + (obj_type_name && *obj_type_name) ? "::" : "", + name); + else + error ("Cannot resolve overloaded function %s to unique instance; disambiguate by specifying function signature", + func_name); + } +#endif + + /* Check how bad the best match is. */ + static_offset = 0; + if (method && TYPE_FN_FIELD_STATIC_P (fns_ptr, oload_champ)) + static_offset = 1; + for (ix = 1; ix <= nargs - static_offset; ix++) + { + if (oload_champ_bv->rank[ix] >= 100) + oload_incompatible = 1; /* truly mismatched types */ + + else if (oload_champ_bv->rank[ix] >= 10) + oload_non_standard = 1; /* non-standard type conversions needed */ + } + if (oload_incompatible) + { + if (method) + error ("Cannot resolve method %s%s%s to any overloaded instance", + obj_type_name, + (obj_type_name && *obj_type_name) ? "::" : "", + name); + else + error ("Cannot resolve function %s to any overloaded instance", + func_name); + } + else if (oload_non_standard) + { + if (method) + warning ("Using non-standard conversion to match method %s%s%s to supplied arguments", + obj_type_name, + (obj_type_name && *obj_type_name) ? "::" : "", + name); + else + warning ("Using non-standard conversion to match function %s to supplied arguments", + func_name); + } + + if (method) + { + if (staticp && TYPE_FN_FIELD_STATIC_P (fns_ptr, oload_champ)) + *staticp = 1; + else if (staticp) + *staticp = 0; + if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, oload_champ)) + *valp = value_virtual_fn_field (&temp, fns_ptr, oload_champ, basetype, boffset); + else + *valp = value_fn_field (&temp, fns_ptr, oload_champ, basetype, boffset); + } + else + { + *symp = oload_syms[oload_champ]; + xfree (func_name); + } + + if (objp) + { + if (TYPE_CODE (VALUE_TYPE (temp)) != TYPE_CODE_PTR + && TYPE_CODE (VALUE_TYPE (*objp)) == TYPE_CODE_PTR) + { + temp = value_addr (temp); + } + *objp = temp; + } + if (cleanups != NULL) + do_cleanups (cleanups); + + return oload_incompatible ? 100 : (oload_non_standard ? 10 : 0); +} + +/* C++: return 1 is NAME is a legitimate name for the destructor + of type TYPE. If TYPE does not have a destructor, or + if NAME is inappropriate for TYPE, an error is signaled. */ +int +destructor_name_p (const char *name, const struct type *type) +{ + /* destructors are a special case. */ + + if (name[0] == '~') + { + char *dname = type_name_no_tag (type); + char *cp = strchr (dname, '<'); + unsigned int len; + + /* Do not compare the template part for template classes. */ + if (cp == NULL) + len = strlen (dname); + else + len = cp - dname; + if (strlen (name + 1) != len || !STREQN (dname, name + 1, len)) + error ("name of destructor must equal name of class"); + else + return 1; + } + return 0; +} + +/* Helper function for check_field: Given TYPE, a structure/union, + return 1 if the component named NAME from the ultimate + target structure/union is defined, otherwise, return 0. */ + +static int +check_field_in (register struct type *type, const char *name) +{ + register int i; + + for (i = TYPE_NFIELDS (type) - 1; i >= TYPE_N_BASECLASSES (type); i--) + { + char *t_field_name = TYPE_FIELD_NAME (type, i); + if (t_field_name && (strcmp_iw (t_field_name, name) == 0)) + return 1; + } + + /* C++: If it was not found as a data field, then try to + return it as a pointer to a method. */ + + /* Destructors are a special case. */ + if (destructor_name_p (name, type)) + { + int m_index, f_index; + + return get_destructor_fn_field (type, &m_index, &f_index); + } + + for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; --i) + { + if (strcmp_iw (TYPE_FN_FIELDLIST_NAME (type, i), name) == 0) + return 1; + } + + for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--) + if (check_field_in (TYPE_BASECLASS (type, i), name)) + return 1; + + return 0; +} + + +/* C++: Given ARG1, a value of type (pointer to a)* structure/union, + return 1 if the component named NAME from the ultimate + target structure/union is defined, otherwise, return 0. */ + +int +check_field (struct value *arg1, const char *name) +{ + register struct type *t; + + COERCE_ARRAY (arg1); + + t = VALUE_TYPE (arg1); + + /* Follow pointers until we get to a non-pointer. */ + + for (;;) + { + CHECK_TYPEDEF (t); + if (TYPE_CODE (t) != TYPE_CODE_PTR && TYPE_CODE (t) != TYPE_CODE_REF) + break; + t = TYPE_TARGET_TYPE (t); + } + + if (TYPE_CODE (t) == TYPE_CODE_MEMBER) + error ("not implemented: member type in check_field"); + + if (TYPE_CODE (t) != TYPE_CODE_STRUCT + && TYPE_CODE (t) != TYPE_CODE_UNION) + error ("Internal error: `this' is not an aggregate"); + + return check_field_in (t, name); +} + +/* C++: Given an aggregate type CURTYPE, and a member name NAME, + return the address of this member as a "pointer to member" + type. If INTYPE is non-null, then it will be the type + of the member we are looking for. This will help us resolve + "pointers to member functions". This function is used + to resolve user expressions of the form "DOMAIN::NAME". */ + +struct value * +value_struct_elt_for_reference (struct type *domain, int offset, + struct type *curtype, char *name, + struct type *intype) +{ + register struct type *t = curtype; + register int i; + struct value *v; + + if (TYPE_CODE (t) != TYPE_CODE_STRUCT + && TYPE_CODE (t) != TYPE_CODE_UNION) + error ("Internal error: non-aggregate type to value_struct_elt_for_reference"); + + for (i = TYPE_NFIELDS (t) - 1; i >= TYPE_N_BASECLASSES (t); i--) + { + char *t_field_name = TYPE_FIELD_NAME (t, i); + + if (t_field_name && STREQ (t_field_name, name)) + { + if (TYPE_FIELD_STATIC (t, i)) + { + v = value_static_field (t, i); + if (v == NULL) + error ("static field %s has been optimized out", + name); + return v; + } + if (TYPE_FIELD_PACKED (t, i)) + error ("pointers to bitfield members not allowed"); + + return value_from_longest + (lookup_reference_type (lookup_member_type (TYPE_FIELD_TYPE (t, i), + domain)), + offset + (LONGEST) (TYPE_FIELD_BITPOS (t, i) >> 3)); + } + } + + /* C++: If it was not found as a data field, then try to + return it as a pointer to a method. */ + + /* Destructors are a special case. */ + if (destructor_name_p (name, t)) + { + error ("member pointers to destructors not implemented yet"); + } + + /* Perform all necessary dereferencing. */ + while (intype && TYPE_CODE (intype) == TYPE_CODE_PTR) + intype = TYPE_TARGET_TYPE (intype); + + for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i) + { + char *t_field_name = TYPE_FN_FIELDLIST_NAME (t, i); + char dem_opname[64]; + + if (strncmp (t_field_name, "__", 2) == 0 || + strncmp (t_field_name, "op", 2) == 0 || + strncmp (t_field_name, "type", 4) == 0) + { + if (cplus_demangle_opname (t_field_name, dem_opname, DMGL_ANSI)) + t_field_name = dem_opname; + else if (cplus_demangle_opname (t_field_name, dem_opname, 0)) + t_field_name = dem_opname; + } + if (t_field_name && STREQ (t_field_name, name)) + { + int j = TYPE_FN_FIELDLIST_LENGTH (t, i); + struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i); + + check_stub_method_group (t, i); + + if (intype == 0 && j > 1) + error ("non-unique member `%s' requires type instantiation", name); + if (intype) + { + while (j--) + if (TYPE_FN_FIELD_TYPE (f, j) == intype) + break; + if (j < 0) + error ("no member function matches that type instantiation"); + } + else + j = 0; + + if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) + { + return value_from_longest + (lookup_reference_type + (lookup_member_type (TYPE_FN_FIELD_TYPE (f, j), + domain)), + (LONGEST) METHOD_PTR_FROM_VOFFSET (TYPE_FN_FIELD_VOFFSET (f, j))); + } + else + { + struct symbol *s = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, j), + 0, VAR_NAMESPACE, 0, NULL); + if (s == NULL) + { + v = 0; + } + else + { + v = read_var_value (s, 0); +#if 0 + VALUE_TYPE (v) = lookup_reference_type + (lookup_member_type (TYPE_FN_FIELD_TYPE (f, j), + domain)); +#endif + } + return v; + } + } + } + for (i = TYPE_N_BASECLASSES (t) - 1; i >= 0; i--) + { + struct value *v; + int base_offset; + + if (BASETYPE_VIA_VIRTUAL (t, i)) + base_offset = 0; + else + base_offset = TYPE_BASECLASS_BITPOS (t, i) / 8; + v = value_struct_elt_for_reference (domain, + offset + base_offset, + TYPE_BASECLASS (t, i), + name, + intype); + if (v) + return v; + } + return 0; +} + + +/* Given a pointer value V, find the real (RTTI) type + of the object it points to. + Other parameters FULL, TOP, USING_ENC as with value_rtti_type() + and refer to the values computed for the object pointed to. */ + +struct type * +value_rtti_target_type (struct value *v, int *full, int *top, int *using_enc) +{ + struct value *target; + + target = value_ind (v); + + return value_rtti_type (target, full, top, using_enc); +} + +/* Given a value pointed to by ARGP, check its real run-time type, and + if that is different from the enclosing type, create a new value + using the real run-time type as the enclosing type (and of the same + type as ARGP) and return it, with the embedded offset adjusted to + be the correct offset to the enclosed object + RTYPE is the type, and XFULL, XTOP, and XUSING_ENC are the other + parameters, computed by value_rtti_type(). If these are available, + they can be supplied and a second call to value_rtti_type() is avoided. + (Pass RTYPE == NULL if they're not available */ + +struct value * +value_full_object (struct value *argp, struct type *rtype, int xfull, int xtop, + int xusing_enc) +{ + struct type *real_type; + int full = 0; + int top = -1; + int using_enc = 0; + struct value *new_val; + + if (rtype) + { + real_type = rtype; + full = xfull; + top = xtop; + using_enc = xusing_enc; + } + else + real_type = value_rtti_type (argp, &full, &top, &using_enc); + + /* If no RTTI data, or if object is already complete, do nothing */ + if (!real_type || real_type == VALUE_ENCLOSING_TYPE (argp)) + return argp; + + /* If we have the full object, but for some reason the enclosing + type is wrong, set it *//* pai: FIXME -- sounds iffy */ + if (full) + { + argp = value_change_enclosing_type (argp, real_type); + return argp; + } + + /* Check if object is in memory */ + if (VALUE_LVAL (argp) != lval_memory) + { + warning ("Couldn't retrieve complete object of RTTI type %s; object may be in register(s).", TYPE_NAME (real_type)); + + return argp; + } + + /* All other cases -- retrieve the complete object */ + /* Go back by the computed top_offset from the beginning of the object, + adjusting for the embedded offset of argp if that's what value_rtti_type + used for its computation. */ + new_val = value_at_lazy (real_type, VALUE_ADDRESS (argp) - top + + (using_enc ? 0 : VALUE_EMBEDDED_OFFSET (argp)), + VALUE_BFD_SECTION (argp)); + VALUE_TYPE (new_val) = VALUE_TYPE (argp); + VALUE_EMBEDDED_OFFSET (new_val) = using_enc ? top + VALUE_EMBEDDED_OFFSET (argp) : top; + return new_val; +} + + + + +/* C++: return the value of the class instance variable, if one exists. + Flag COMPLAIN signals an error if the request is made in an + inappropriate context. */ + +struct value * +value_of_this (int complain) +{ + struct symbol *func, *sym; + struct block *b; + int i; + static const char funny_this[] = "this"; + struct value *this; + + if (selected_frame == 0) + { + if (complain) + error ("no frame selected"); + else + return 0; + } + + func = get_frame_function (selected_frame); + if (!func) + { + if (complain) + error ("no `this' in nameless context"); + else + return 0; + } + + b = SYMBOL_BLOCK_VALUE (func); + i = BLOCK_NSYMS (b); + if (i <= 0) + { + if (complain) + error ("no args, no `this'"); + else + return 0; + } + + /* Calling lookup_block_symbol is necessary to get the LOC_REGISTER + symbol instead of the LOC_ARG one (if both exist). */ + sym = lookup_block_symbol (b, funny_this, NULL, VAR_NAMESPACE); + if (sym == NULL) + { + if (complain) + error ("current stack frame not in method"); + else + return NULL; + } + + this = read_var_value (sym, selected_frame); + if (this == 0 && complain) + error ("`this' argument at unknown address"); + return this; +} + +/* Create a slice (sub-string, sub-array) of ARRAY, that is LENGTH elements + long, starting at LOWBOUND. The result has the same lower bound as + the original ARRAY. */ + +struct value * +value_slice (struct value *array, int lowbound, int length) +{ + struct type *slice_range_type, *slice_type, *range_type; + LONGEST lowerbound, upperbound, offset; + struct value *slice; + struct type *array_type; + array_type = check_typedef (VALUE_TYPE (array)); + COERCE_VARYING_ARRAY (array, array_type); + if (TYPE_CODE (array_type) != TYPE_CODE_ARRAY + && TYPE_CODE (array_type) != TYPE_CODE_STRING + && TYPE_CODE (array_type) != TYPE_CODE_BITSTRING) + error ("cannot take slice of non-array"); + range_type = TYPE_INDEX_TYPE (array_type); + if (get_discrete_bounds (range_type, &lowerbound, &upperbound) < 0) + error ("slice from bad array or bitstring"); + if (lowbound < lowerbound || length < 0 + || lowbound + length - 1 > upperbound) + /* OBSOLETE Chill allows zero-length strings but not arrays. */ + /* OBSOLETE || (current_language->la_language == language_chill */ + /* OBSOLETE && length == 0 && TYPE_CODE (array_type) == TYPE_CODE_ARRAY)) */ + error ("slice out of range"); + /* FIXME-type-allocation: need a way to free this type when we are + done with it. */ + slice_range_type = create_range_type ((struct type *) NULL, + TYPE_TARGET_TYPE (range_type), + lowbound, lowbound + length - 1); + if (TYPE_CODE (array_type) == TYPE_CODE_BITSTRING) + { + int i; + slice_type = create_set_type ((struct type *) NULL, slice_range_type); + TYPE_CODE (slice_type) = TYPE_CODE_BITSTRING; + slice = value_zero (slice_type, not_lval); + for (i = 0; i < length; i++) + { + int element = value_bit_index (array_type, + VALUE_CONTENTS (array), + lowbound + i); + if (element < 0) + error ("internal error accessing bitstring"); + else if (element > 0) + { + int j = i % TARGET_CHAR_BIT; + if (BITS_BIG_ENDIAN) + j = TARGET_CHAR_BIT - 1 - j; + VALUE_CONTENTS_RAW (slice)[i / TARGET_CHAR_BIT] |= (1 << j); + } + } + /* We should set the address, bitssize, and bitspos, so the clice + can be used on the LHS, but that may require extensions to + value_assign. For now, just leave as a non_lval. FIXME. */ + } + else + { + struct type *element_type = TYPE_TARGET_TYPE (array_type); + offset + = (lowbound - lowerbound) * TYPE_LENGTH (check_typedef (element_type)); + slice_type = create_array_type ((struct type *) NULL, element_type, + slice_range_type); + TYPE_CODE (slice_type) = TYPE_CODE (array_type); + slice = allocate_value (slice_type); + if (VALUE_LAZY (array)) + VALUE_LAZY (slice) = 1; + else + memcpy (VALUE_CONTENTS (slice), VALUE_CONTENTS (array) + offset, + TYPE_LENGTH (slice_type)); + if (VALUE_LVAL (array) == lval_internalvar) + VALUE_LVAL (slice) = lval_internalvar_component; + else + VALUE_LVAL (slice) = VALUE_LVAL (array); + VALUE_ADDRESS (slice) = VALUE_ADDRESS (array); + VALUE_OFFSET (slice) = VALUE_OFFSET (array) + offset; + } + return slice; +} + +/* Assuming OBSOLETE chill_varying_type (VARRAY) is true, return an + equivalent value as a fixed-length array. */ + +struct value * +varying_to_slice (struct value *varray) +{ + struct type *vtype = check_typedef (VALUE_TYPE (varray)); + LONGEST length = unpack_long (TYPE_FIELD_TYPE (vtype, 0), + VALUE_CONTENTS (varray) + + TYPE_FIELD_BITPOS (vtype, 0) / 8); + return value_slice (value_primitive_field (varray, 0, 1, vtype), 0, length); +} + +/* Create a value for a FORTRAN complex number. Currently most of + the time values are coerced to COMPLEX*16 (i.e. a complex number + composed of 2 doubles. This really should be a smarter routine + that figures out precision inteligently as opposed to assuming + doubles. FIXME: fmb */ + +struct value * +value_literal_complex (struct value *arg1, struct value *arg2, struct type *type) +{ + struct value *val; + struct type *real_type = TYPE_TARGET_TYPE (type); + + val = allocate_value (type); + arg1 = value_cast (real_type, arg1); + arg2 = value_cast (real_type, arg2); + + memcpy (VALUE_CONTENTS_RAW (val), + VALUE_CONTENTS (arg1), TYPE_LENGTH (real_type)); + memcpy (VALUE_CONTENTS_RAW (val) + TYPE_LENGTH (real_type), + VALUE_CONTENTS (arg2), TYPE_LENGTH (real_type)); + return val; +} + +/* Cast a value into the appropriate complex data type. */ + +static struct value * +cast_into_complex (struct type *type, struct value *val) +{ + struct type *real_type = TYPE_TARGET_TYPE (type); + if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_COMPLEX) + { + struct type *val_real_type = TYPE_TARGET_TYPE (VALUE_TYPE (val)); + struct value *re_val = allocate_value (val_real_type); + struct value *im_val = allocate_value (val_real_type); + + memcpy (VALUE_CONTENTS_RAW (re_val), + VALUE_CONTENTS (val), TYPE_LENGTH (val_real_type)); + memcpy (VALUE_CONTENTS_RAW (im_val), + VALUE_CONTENTS (val) + TYPE_LENGTH (val_real_type), + TYPE_LENGTH (val_real_type)); + + return value_literal_complex (re_val, im_val, type); + } + else if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FLT + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_INT) + return value_literal_complex (val, value_zero (real_type, not_lval), type); + else + error ("cannot cast non-number to complex"); +} + +void +_initialize_valops (void) +{ +#if 0 + add_show_from_set + (add_set_cmd ("abandon", class_support, var_boolean, (char *) &auto_abandon, + "Set automatic abandonment of expressions upon failure.", + &setlist), + &showlist); +#endif + + add_show_from_set + (add_set_cmd ("overload-resolution", class_support, var_boolean, (char *) &overload_resolution, + "Set overload resolution in evaluating C++ functions.", + &setlist), + &showlist); + overload_resolution = 1; + + add_show_from_set ( + add_set_cmd ("unwindonsignal", no_class, var_boolean, + (char *) &unwind_on_signal_p, +"Set unwinding of stack if a signal is received while in a call dummy.\n\ +The unwindonsignal lets the user determine what gdb should do if a signal\n\ +is received while in a function called from gdb (call dummy). If set, gdb\n\ +unwinds the stack and restore the context to what as it was before the call.\n\ +The default is to stop in the frame where the signal was received.", &setlist), + &showlist); +} -- cgit v1.1