Index: stable/12/contrib/gdb/gdb/cli/cli-cmds.c =================================================================== --- stable/12/contrib/gdb/gdb/cli/cli-cmds.c (revision 358111) +++ stable/12/contrib/gdb/gdb/cli/cli-cmds.c (revision 358112) @@ -1,1288 +1,1287 @@ /* GDB CLI commands. Copyright 2000, 2001, 2002, 2003, 2004 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 "readline/readline.h" -#include "readline/tilde.h" #include "completer.h" #include "target.h" /* For baud_rate, remote_debug and remote_timeout */ #include "gdb_wait.h" /* For shell escape implementation */ #include "gdb_regex.h" /* Used by apropos_command */ #include "gdb_string.h" #include "gdb_vfork.h" #include "linespec.h" #include "expression.h" #include "frame.h" #include "value.h" #include "language.h" #include "filenames.h" /* for DOSish file names */ #include "objfiles.h" #include "source.h" #include "disasm.h" #include "ui-out.h" #include "top.h" #include "cli/cli-decode.h" #include "cli/cli-script.h" #include "cli/cli-setshow.h" #include "cli/cli-cmds.h" #ifdef TUI #include "tui/tui.h" /* For tui_active et.al. */ #endif #ifndef GDBINIT_FILENAME #define GDBINIT_FILENAME ".gdbinit" #endif /* Prototypes for local command functions */ static void complete_command (char *, int); static void echo_command (char *, int); static void pwd_command (char *, int); static void show_version (char *, int); static void help_command (char *, int); static void show_command (char *, int); static void info_command (char *, int); static void show_debug (char *, int); static void set_debug (char *, int); static void show_user (char *, int); static void make_command (char *, int); static void shell_escape (char *, int); static void edit_command (char *, int); static void list_command (char *, int); void apropos_command (char *, int); /* Prototypes for local utility functions */ static void ambiguous_line_spec (struct symtabs_and_lines *); /* Limit the call depth of user-defined commands */ int max_user_call_depth; /* Define all cmd_list_elements. */ /* Chain containing all defined commands. */ struct cmd_list_element *cmdlist; /* Chain containing all defined info subcommands. */ struct cmd_list_element *infolist; /* Chain containing all defined enable subcommands. */ struct cmd_list_element *enablelist; /* Chain containing all defined disable subcommands. */ struct cmd_list_element *disablelist; /* Chain containing all defined toggle subcommands. */ struct cmd_list_element *togglelist; /* Chain containing all defined stop subcommands. */ struct cmd_list_element *stoplist; /* Chain containing all defined delete subcommands. */ struct cmd_list_element *deletelist; /* Chain containing all defined "enable breakpoint" subcommands. */ struct cmd_list_element *enablebreaklist; /* Chain containing all defined set subcommands */ struct cmd_list_element *setlist; /* Chain containing all defined unset subcommands */ struct cmd_list_element *unsetlist; /* Chain containing all defined show subcommands. */ struct cmd_list_element *showlist; /* Chain containing all defined \"set history\". */ struct cmd_list_element *sethistlist; /* Chain containing all defined \"show history\". */ struct cmd_list_element *showhistlist; /* Chain containing all defined \"unset history\". */ struct cmd_list_element *unsethistlist; /* Chain containing all defined maintenance subcommands. */ struct cmd_list_element *maintenancelist; /* Chain containing all defined "maintenance info" subcommands. */ struct cmd_list_element *maintenanceinfolist; /* Chain containing all defined "maintenance print" subcommands. */ struct cmd_list_element *maintenanceprintlist; struct cmd_list_element *setprintlist; struct cmd_list_element *showprintlist; struct cmd_list_element *setdebuglist; struct cmd_list_element *showdebuglist; struct cmd_list_element *setchecklist; struct cmd_list_element *showchecklist; /* Utility used everywhere when at least one argument is needed and none is supplied. */ void error_no_arg (char *why) { error ("Argument required (%s).", why); } /* The "info" command is defined as a prefix, with allow_unknown = 0. Therefore, its own definition is called only for "info" with no args. */ static void info_command (char *arg, int from_tty) { printf_unfiltered ("\"info\" must be followed by the name of an info command.\n"); help_list (infolist, "info ", -1, gdb_stdout); } /* The "show" command with no arguments shows all the settings. */ static void show_command (char *arg, int from_tty) { cmd_show_list (showlist, from_tty, ""); } /* Provide documentation on command or list given by COMMAND. FROM_TTY is ignored. */ static void help_command (char *command, int from_tty) { help_cmd (command, gdb_stdout); } /* String compare function for qsort. */ static int compare_strings (const void *arg1, const void *arg2) { const char **s1 = (const char **) arg1; const char **s2 = (const char **) arg2; return strcmp (*s1, *s2); } /* The "complete" command is used by Emacs to implement completion. */ static void complete_command (char *arg, int from_tty) { int i; int argpoint; char **completions, *point, *arg_prefix; dont_repeat (); if (arg == NULL) arg = ""; argpoint = strlen (arg); /* complete_line assumes that its first argument is somewhere within, and except for filenames at the beginning of, the word to be completed. The following crude imitation of readline's word-breaking tries to accomodate this. */ point = arg + argpoint; while (point > arg) { if (strchr (rl_completer_word_break_characters, point[-1]) != 0) break; point--; } arg_prefix = alloca (point - arg + 1); memcpy (arg_prefix, arg, point - arg); arg_prefix[point - arg] = 0; completions = complete_line (point, arg, argpoint); if (completions) { int item, size; for (size = 0; completions[size]; ++size) ; qsort (completions, size, sizeof (char *), compare_strings); /* We do extra processing here since we only want to print each unique item once. */ item = 0; while (item < size) { int next_item; printf_unfiltered ("%s%s\n", arg_prefix, completions[item]); next_item = item + 1; while (next_item < size && ! strcmp (completions[item], completions[next_item])) { xfree (completions[next_item]); ++next_item; } xfree (completions[item]); item = next_item; } xfree (completions); } } int is_complete_command (struct cmd_list_element *c) { return cmd_cfunc_eq (c, complete_command); } static void show_version (char *args, int from_tty) { immediate_quit++; print_gdb_version (gdb_stdout); printf_filtered ("\n"); immediate_quit--; } /* Handle the quit command. */ void quit_command (char *args, int from_tty) { if (!quit_confirm ()) error ("Not confirmed."); quit_force (args, from_tty); } static void pwd_command (char *args, int from_tty) { if (args) error ("The \"pwd\" command does not take an argument: %s", args); getcwd (gdb_dirbuf, sizeof (gdb_dirbuf)); if (strcmp (gdb_dirbuf, current_directory) != 0) printf_unfiltered ("Working directory %s\n (canonically %s).\n", current_directory, gdb_dirbuf); else printf_unfiltered ("Working directory %s.\n", current_directory); } void cd_command (char *dir, int from_tty) { int len; /* Found something other than leading repetitions of "/..". */ int found_real_path; char *p; /* If the new directory is absolute, repeat is a no-op; if relative, repeat might be useful but is more likely to be a mistake. */ dont_repeat (); if (dir == 0) error_no_arg ("new working directory"); dir = tilde_expand (dir); make_cleanup (xfree, dir); if (chdir (dir) < 0) perror_with_name (dir); #ifdef HAVE_DOS_BASED_FILE_SYSTEM /* There's too much mess with DOSish names like "d:", "d:.", "d:./foo" etc. Instead of having lots of special #ifdef'ed code, simply get the canonicalized name of the current directory. */ dir = getcwd (gdb_dirbuf, sizeof (gdb_dirbuf)); #endif len = strlen (dir); if (IS_DIR_SEPARATOR (dir[len - 1])) { /* Remove the trailing slash unless this is a root directory (including a drive letter on non-Unix systems). */ if (!(len == 1) /* "/" */ #ifdef HAVE_DOS_BASED_FILE_SYSTEM && !(len == 3 && dir[1] == ':') /* "d:/" */ #endif ) len--; } dir = savestring (dir, len); if (IS_ABSOLUTE_PATH (dir)) current_directory = dir; else { if (IS_DIR_SEPARATOR (current_directory[strlen (current_directory) - 1])) current_directory = concat (current_directory, dir, NULL); else current_directory = concat (current_directory, SLASH_STRING, dir, NULL); xfree (dir); } /* Now simplify any occurrences of `.' and `..' in the pathname. */ found_real_path = 0; for (p = current_directory; *p;) { if (IS_DIR_SEPARATOR (p[0]) && p[1] == '.' && (p[2] == 0 || IS_DIR_SEPARATOR (p[2]))) strcpy (p, p + 2); else if (IS_DIR_SEPARATOR (p[0]) && p[1] == '.' && p[2] == '.' && (p[3] == 0 || IS_DIR_SEPARATOR (p[3]))) { if (found_real_path) { /* Search backwards for the directory just before the "/.." and obliterate it and the "/..". */ char *q = p; while (q != current_directory && !IS_DIR_SEPARATOR (q[-1])) --q; if (q == current_directory) /* current_directory is a relative pathname ("can't happen"--leave it alone). */ ++p; else { strcpy (q - 1, p + 3); p = q - 1; } } else /* We are dealing with leading repetitions of "/..", for example "/../..", which is the Mach super-root. */ p += 3; } else { found_real_path = 1; ++p; } } forget_cached_source_info (); if (from_tty) pwd_command ((char *) 0, 1); } void source_command (char *args, int from_tty) { FILE *stream; struct cleanup *old_cleanups; char *file = args; if (file == NULL) { error ("source command requires pathname of file to source."); } file = tilde_expand (file); old_cleanups = make_cleanup (xfree, file); stream = fopen (file, FOPEN_RT); if (!stream) { if (from_tty) perror_with_name (file); else return; } script_from_file (stream, file); do_cleanups (old_cleanups); } static void echo_command (char *text, int from_tty) { char *p = text; int c; if (text) while ((c = *p++) != '\0') { if (c == '\\') { /* \ at end of argument is used after spaces so they won't be lost. */ if (*p == 0) return; c = parse_escape (&p); if (c >= 0) printf_filtered ("%c", c); } else printf_filtered ("%c", c); } /* Force this output to appear now. */ wrap_here (""); gdb_flush (gdb_stdout); } static void shell_escape (char *arg, int from_tty) { #ifdef CANT_FORK /* If ARG is NULL, they want an inferior shell, but `system' just reports if the shell is available when passed a NULL arg. */ int rc = system (arg ? arg : ""); if (!arg) arg = "inferior shell"; if (rc == -1) { fprintf_unfiltered (gdb_stderr, "Cannot execute %s: %s\n", arg, safe_strerror (errno)); gdb_flush (gdb_stderr); } else if (rc) { fprintf_unfiltered (gdb_stderr, "%s exited with status %d\n", arg, rc); gdb_flush (gdb_stderr); } #ifdef GLOBAL_CURDIR /* Make sure to return to the directory GDB thinks it is, in case the shell command we just ran changed it. */ chdir (current_directory); #endif #else /* Can fork. */ int rc, status, pid; if ((pid = vfork ()) == 0) { char *p, *user_shell; if ((user_shell = (char *) getenv ("SHELL")) == NULL) user_shell = "/bin/sh"; /* Get the name of the shell for arg0 */ if ((p = strrchr (user_shell, '/')) == NULL) p = user_shell; else p++; /* Get past '/' */ if (!arg) execl (user_shell, p, (char *) 0); else execl (user_shell, p, "-c", arg, (char *) 0); fprintf_unfiltered (gdb_stderr, "Cannot execute %s: %s\n", user_shell, safe_strerror (errno)); gdb_flush (gdb_stderr); _exit (0177); } if (pid != -1) while ((rc = wait (&status)) != pid && rc != -1) ; else error ("Fork failed"); #endif /* Can fork. */ } static void edit_command (char *arg, int from_tty) { struct symtabs_and_lines sals; struct symtab_and_line sal; struct symbol *sym; char *arg1; int cmdlen, log10; unsigned m; char *editor; char *p; /* Pull in the current default source line if necessary */ if (arg == 0) { set_default_source_symtab_and_line (); sal = get_current_source_symtab_and_line (); } /* bare "edit" edits file with present line. */ if (arg == 0) { if (sal.symtab == 0) error ("No default source file yet."); sal.line += get_lines_to_list () / 2; } else { /* Now should only be one argument -- decode it in SAL */ arg1 = arg; sals = decode_line_1 (&arg1, 0, 0, 0, 0, 0); if (! sals.nelts) return; /* C++ */ if (sals.nelts > 1) { ambiguous_line_spec (&sals); xfree (sals.sals); return; } sal = sals.sals[0]; xfree (sals.sals); if (*arg1) error ("Junk at end of line specification."); /* if line was specified by address, first print exactly which line, and which file. In this case, sal.symtab == 0 means address is outside of all known source files, not that user failed to give a filename. */ if (*arg == '*') { if (sal.symtab == 0) /* FIXME-32x64--assumes sal.pc fits in long. */ error ("No source file for address %s.", local_hex_string((unsigned long) sal.pc)); sym = find_pc_function (sal.pc); if (sym) { print_address_numeric (sal.pc, 1, gdb_stdout); printf_filtered (" is in "); fputs_filtered (SYMBOL_PRINT_NAME (sym), gdb_stdout); printf_filtered (" (%s:%d).\n", sal.symtab->filename, sal.line); } else { print_address_numeric (sal.pc, 1, gdb_stdout); printf_filtered (" is at %s:%d.\n", sal.symtab->filename, sal.line); } } /* If what was given does not imply a symtab, it must be an undebuggable symbol which means no source code. */ if (sal.symtab == 0) error ("No line number known for %s.", arg); } if ((editor = (char *) getenv ("EDITOR")) == NULL) editor = "/bin/ex"; /* Approximate base-10 log of line to 1 unit for digit count */ for(log10=32, m=0x80000000; !(sal.line & m) && log10>0; log10--, m=m>>1); log10 = 1 + (int)((log10 + (0 == ((m-1) & sal.line)))/3.32192809); cmdlen = strlen(editor) + 1 + (NULL == sal.symtab->dirname ? 0 : strlen(sal.symtab->dirname) + 1) + (NULL == sal.symtab->filename? 0 : strlen(sal.symtab->filename)+ 1) + log10 + 2; p = xmalloc(cmdlen); sprintf(p,"%s +%d %s%s",editor,sal.line, (NULL == sal.symtab->dirname ? "./" : (NULL != sal.symtab->filename && *(sal.symtab->filename) != '/') ? sal.symtab->dirname : ""), (NULL == sal.symtab->filename ? "unknown" : sal.symtab->filename) ); shell_escape(p, from_tty); xfree(p); } static void list_command (char *arg, int from_tty) { struct symtabs_and_lines sals, sals_end; struct symtab_and_line sal, sal_end, cursal; struct symbol *sym; char *arg1; int no_end = 1; int dummy_end = 0; int dummy_beg = 0; int linenum_beg = 0; char *p; /* Pull in the current default source line if necessary */ if (arg == 0 || arg[0] == '+' || arg[0] == '-') { set_default_source_symtab_and_line (); cursal = get_current_source_symtab_and_line (); } /* "l" or "l +" lists next ten lines. */ if (arg == 0 || strcmp (arg, "+") == 0) { print_source_lines (cursal.symtab, cursal.line, cursal.line + get_lines_to_list (), 0); return; } /* "l -" lists previous ten lines, the ones before the ten just listed. */ if (strcmp (arg, "-") == 0) { print_source_lines (cursal.symtab, max (get_first_line_listed () - get_lines_to_list (), 1), get_first_line_listed (), 0); return; } /* Now if there is only one argument, decode it in SAL and set NO_END. If there are two arguments, decode them in SAL and SAL_END and clear NO_END; however, if one of the arguments is blank, set DUMMY_BEG or DUMMY_END to record that fact. */ if (!have_full_symbols () && !have_partial_symbols ()) error ("No symbol table is loaded. Use the \"file\" command."); arg1 = arg; if (*arg1 == ',') dummy_beg = 1; else { sals = decode_line_1 (&arg1, 0, 0, 0, 0, 0); if (!sals.nelts) return; /* C++ */ if (sals.nelts > 1) { ambiguous_line_spec (&sals); xfree (sals.sals); return; } sal = sals.sals[0]; xfree (sals.sals); } /* Record whether the BEG arg is all digits. */ for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++); linenum_beg = (p == arg1); while (*arg1 == ' ' || *arg1 == '\t') arg1++; if (*arg1 == ',') { no_end = 0; arg1++; while (*arg1 == ' ' || *arg1 == '\t') arg1++; if (*arg1 == 0) dummy_end = 1; else { if (dummy_beg) sals_end = decode_line_1 (&arg1, 0, 0, 0, 0, 0); else sals_end = decode_line_1 (&arg1, 0, sal.symtab, sal.line, 0, 0); if (sals_end.nelts == 0) return; if (sals_end.nelts > 1) { ambiguous_line_spec (&sals_end); xfree (sals_end.sals); return; } sal_end = sals_end.sals[0]; xfree (sals_end.sals); } } if (*arg1) error ("Junk at end of line specification."); if (!no_end && !dummy_beg && !dummy_end && sal.symtab != sal_end.symtab) error ("Specified start and end are in different files."); if (dummy_beg && dummy_end) error ("Two empty args do not say what lines to list."); /* if line was specified by address, first print exactly which line, and which file. In this case, sal.symtab == 0 means address is outside of all known source files, not that user failed to give a filename. */ if (*arg == '*') { if (sal.symtab == 0) /* FIXME-32x64--assumes sal.pc fits in long. */ error ("No source file for address %s.", local_hex_string ((unsigned long) sal.pc)); sym = find_pc_function (sal.pc); if (sym) { print_address_numeric (sal.pc, 1, gdb_stdout); printf_filtered (" is in "); fputs_filtered (SYMBOL_PRINT_NAME (sym), gdb_stdout); printf_filtered (" (%s:%d).\n", sal.symtab->filename, sal.line); } else { print_address_numeric (sal.pc, 1, gdb_stdout); printf_filtered (" is at %s:%d.\n", sal.symtab->filename, sal.line); } } /* If line was not specified by just a line number, and it does not imply a symtab, it must be an undebuggable symbol which means no source code. */ if (!linenum_beg && sal.symtab == 0) error ("No line number known for %s.", arg); /* If this command is repeated with RET, turn it into the no-arg variant. */ if (from_tty) *arg = 0; if (dummy_beg && sal_end.symtab == 0) error ("No default source file yet. Do \"help list\"."); if (dummy_beg) print_source_lines (sal_end.symtab, max (sal_end.line - (get_lines_to_list () - 1), 1), sal_end.line + 1, 0); else if (sal.symtab == 0) error ("No default source file yet. Do \"help list\"."); else if (no_end) { int first_line = sal.line - get_lines_to_list () / 2; if (first_line < 1) first_line = 1; print_source_lines (sal.symtab, first_line, first_line + get_lines_to_list (), 0); } else print_source_lines (sal.symtab, sal.line, (dummy_end ? sal.line + get_lines_to_list () : sal_end.line + 1), 0); } /* 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. */ 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 (!deprecated_selected_frame) error ("No frame selected.\n"); pc = get_frame_pc (deprecated_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) /* NOTE: cagney/2003-02-13 The `tui_active' was previously `tui_version'. */ if (tui_active) /* FIXME: cagney/2004-02-07: This should be an observer. */ low = tui_get_low_disassembly_address (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) /* NOTE: cagney/2003-02-13 The `tui_active' was previously `tui_version'. */ if (tui_active) /* FIXME: cagney/2004-02-07: This should be an observer. */ low = tui_get_low_disassembly_address (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. */ gdb_disassembly (uiout, 0, 0, 0, -1, low, high); printf_filtered ("End of assembler dump.\n"); gdb_flush (gdb_stdout); } #if defined(TUI) else { tui_show_assembly (low); } #endif } static void make_command (char *arg, int from_tty) { char *p; if (arg == 0) p = "make"; else { p = xmalloc (sizeof ("make ") + strlen (arg)); strcpy (p, "make "); strcpy (p + sizeof ("make ") - 1, arg); } shell_escape (p, from_tty); } static void show_user (char *args, int from_tty) { struct cmd_list_element *c; extern struct cmd_list_element *cmdlist; if (args) { c = lookup_cmd (&args, cmdlist, "", 0, 1); if (c->class != class_user) error ("Not a user command."); show_user_1 (c, gdb_stdout); } else { for (c = cmdlist; c; c = c->next) { if (c->class == class_user) show_user_1 (c, gdb_stdout); } } } /* Search through names of commands and documentations for a certain regular expression. */ void apropos_command (char *searchstr, int from_tty) { extern struct cmd_list_element *cmdlist; /*This is the main command list*/ regex_t pattern; char *pattern_fastmap; char errorbuffer[512]; pattern_fastmap = xcalloc (256, sizeof (char)); if (searchstr == NULL) error("REGEXP string is empty"); if (regcomp(&pattern,searchstr,REG_ICASE) == 0) { pattern.fastmap=pattern_fastmap; re_compile_fastmap(&pattern); apropos_cmd (gdb_stdout,cmdlist,&pattern,""); } else { regerror(regcomp(&pattern,searchstr,REG_ICASE),NULL,errorbuffer,512); error("Error in regular expression:%s",errorbuffer); } xfree (pattern_fastmap); } /* Print a list of files and line numbers which a user may choose from in order to list a function which was specified ambiguously (as with `list classname::overloadedfuncname', for example). The vector in SALS provides the filenames and line numbers. */ static void ambiguous_line_spec (struct symtabs_and_lines *sals) { int i; for (i = 0; i < sals->nelts; ++i) printf_filtered ("file: \"%s\", line number: %d\n", sals->sals[i].symtab->filename, sals->sals[i].line); } static void set_debug (char *arg, int from_tty) { printf_unfiltered ("\"set debug\" must be followed by the name of a print subcommand.\n"); help_list (setdebuglist, "set debug ", -1, gdb_stdout); } static void show_debug (char *args, int from_tty) { cmd_show_list (showdebuglist, from_tty, ""); } void init_cmd_lists (void) { max_user_call_depth = 1024; cmdlist = NULL; infolist = NULL; enablelist = NULL; disablelist = NULL; togglelist = NULL; stoplist = NULL; deletelist = NULL; enablebreaklist = NULL; setlist = NULL; unsetlist = NULL; showlist = NULL; sethistlist = NULL; showhistlist = NULL; unsethistlist = NULL; maintenancelist = NULL; maintenanceinfolist = NULL; maintenanceprintlist = NULL; setprintlist = NULL; showprintlist = NULL; setchecklist = NULL; showchecklist = NULL; } void init_cli_cmds (void) { struct cmd_list_element *c; /* Define the classes of commands. They will appear in the help list in the reverse of this order. */ add_cmd ("internals", class_maintenance, NULL, "Maintenance commands.\n\ Some gdb commands are provided just for use by gdb maintainers.\n\ These commands are subject to frequent change, and may not be as\n\ well documented as user commands.", &cmdlist); add_cmd ("obscure", class_obscure, NULL, "Obscure features.", &cmdlist); add_cmd ("aliases", class_alias, NULL, "Aliases of other commands.", &cmdlist); add_cmd ("user-defined", class_user, NULL, "User-defined commands.\n\ The commands in this class are those defined by the user.\n\ Use the \"define\" command to define a command.", &cmdlist); add_cmd ("support", class_support, NULL, "Support facilities.", &cmdlist); if (!dbx_commands) add_cmd ("status", class_info, NULL, "Status inquiries.", &cmdlist); add_cmd ("files", class_files, NULL, "Specifying and examining files.", &cmdlist); add_cmd ("breakpoints", class_breakpoint, NULL, "Making program stop at certain points.", &cmdlist); add_cmd ("data", class_vars, NULL, "Examining data.", &cmdlist); add_cmd ("stack", class_stack, NULL, "Examining the stack.\n\ The stack is made up of stack frames. Gdb assigns numbers to stack frames\n\ counting from zero for the innermost (currently executing) frame.\n\n\ At any time gdb identifies one frame as the \"selected\" frame.\n\ Variable lookups are done with respect to the selected frame.\n\ When the program being debugged stops, gdb selects the innermost frame.\n\ The commands below can be used to select other frames by number or address.", &cmdlist); add_cmd ("running", class_run, NULL, "Running the program.", &cmdlist); /* Define general commands. */ add_com ("pwd", class_files, pwd_command, "Print working directory. This is used for your program as well."); c = add_cmd ("cd", class_files, cd_command, "Set working directory to DIR for debugger and program being debugged.\n\ The change does not take effect for the program being debugged\n\ until the next time it is started.", &cmdlist); set_cmd_completer (c, filename_completer); add_com ("echo", class_support, echo_command, "Print a constant string. Give string as argument.\n\ C escape sequences may be used in the argument.\n\ No newline is added at the end of the argument;\n\ use \"\\n\" if you want a newline to be printed.\n\ Since leading and trailing whitespace are ignored in command arguments,\n\ if you want to print some you must use \"\\\" before leading whitespace\n\ to be printed or after trailing whitespace."); add_com ("document", class_support, document_command, "Document a user-defined command.\n\ Give command name as argument. Give documentation on following lines.\n\ End with a line of just \"end\"."); add_com ("define", class_support, define_command, "Define a new command name. Command name is argument.\n\ Definition appears on following lines, one command per line.\n\ End with a line of just \"end\".\n\ Use the \"document\" command to give documentation for the new command.\n\ Commands defined in this way may have up to ten arguments."); c = add_cmd ("source", class_support, source_command, "Read commands from a file named FILE.\n\ Note that the file \"" GDBINIT_FILENAME "\" is read automatically in this way\n\ when gdb is started.", &cmdlist); set_cmd_completer (c, filename_completer); add_com ("quit", class_support, quit_command, "Exit gdb."); c = add_com ("help", class_support, help_command, "Print list of commands."); set_cmd_completer (c, command_completer); add_com_alias ("q", "quit", class_support, 1); add_com_alias ("h", "help", class_support, 1); c = add_set_cmd ("verbose", class_support, var_boolean, (char *) &info_verbose, "Set ", &setlist), add_show_from_set (c, &showlist); set_cmd_sfunc (c, set_verbose); set_verbose (NULL, 0, c); add_prefix_cmd ("history", class_support, set_history, "Generic command for setting command history parameters.", &sethistlist, "set history ", 0, &setlist); add_prefix_cmd ("history", class_support, show_history, "Generic command for showing command history parameters.", &showhistlist, "show history ", 0, &showlist); add_show_from_set (add_set_cmd ("expansion", no_class, var_boolean, (char *) &history_expansion_p, "Set history expansion on command input.\n\ Without an argument, history expansion is enabled.", &sethistlist), &showhistlist); add_prefix_cmd ("info", class_info, info_command, "Generic command for showing things about the program being debugged.", &infolist, "info ", 0, &cmdlist); add_com_alias ("i", "info", class_info, 1); add_com ("complete", class_obscure, complete_command, "List the completions for the rest of the line as a command."); add_prefix_cmd ("show", class_info, show_command, "Generic command for showing things about the debugger.", &showlist, "show ", 0, &cmdlist); /* Another way to get at the same thing. */ add_info ("set", show_command, "Show all GDB settings."); add_cmd ("commands", no_class, show_commands, "Show the history of commands you typed.\n\ You can supply a command number to start with, or a `+' to start after\n\ the previous command number shown.", &showlist); add_cmd ("version", no_class, show_version, "Show what version of GDB this is.", &showlist); add_com ("while", class_support, while_command, "Execute nested commands WHILE the conditional expression is non zero.\n\ The conditional expression must follow the word `while' and must in turn be\n\ followed by a new line. The nested commands must be entered one per line,\n\ and should be terminated by the word `end'."); add_com ("if", class_support, if_command, "Execute nested commands once IF the conditional expression is non zero.\n\ The conditional expression must follow the word `if' and must in turn be\n\ followed by a new line. The nested commands must be entered one per line,\n\ and should be terminated by the word 'else' or `end'. If an else clause\n\ is used, the same rules apply to its nested commands as to the first ones."); /* If target is open when baud changes, it doesn't take effect until the next open (I think, not sure). */ add_show_from_set (add_set_cmd ("remotebaud", no_class, var_zinteger, (char *) &baud_rate, "Set baud rate for remote serial I/O.\n\ This value is used to set the speed of the serial port when debugging\n\ using remote targets.", &setlist), &showlist); c = add_set_cmd ("remotedebug", no_class, var_zinteger, (char *) &remote_debug, "Set debugging of remote protocol.\n\ When enabled, each packet sent or received with the remote target\n\ is displayed.", &setlist); deprecate_cmd (c, "set debug remote"); deprecate_cmd (add_show_from_set (c, &showlist), "show debug remote"); add_show_from_set (add_set_cmd ("remote", no_class, var_zinteger, (char *) &remote_debug, "Set debugging of remote protocol.\n\ When enabled, each packet sent or received with the remote target\n\ is displayed.", &setdebuglist), &showdebuglist); add_show_from_set ( add_set_cmd ("remotetimeout", no_class, var_integer, (char *) &remote_timeout, "Set timeout limit to wait for target to respond.\n\ This value is used to set the time limit for gdb to wait for a response\n\ from the target.", &setlist), &showlist); add_prefix_cmd ("debug", no_class, set_debug, "Generic command for setting gdb debugging flags", &setdebuglist, "set debug ", 0, &setlist); add_prefix_cmd ("debug", no_class, show_debug, "Generic command for showing gdb debugging flags", &showdebuglist, "show debug ", 0, &showlist); c = add_com ("shell", class_support, shell_escape, "Execute the rest of the line as a shell command.\n\ With no arguments, run an inferior shell."); set_cmd_completer (c, filename_completer); c = add_com ("edit", class_files, edit_command, concat ("Edit specified file or function.\n\ With no argument, edits file containing most recent line listed.\n\ ", "\ Editing targets can be specified in these ways:\n\ FILE:LINENUM, to edit at that line in that file,\n\ FUNCTION, to edit at the beginning of that function,\n\ FILE:FUNCTION, to distinguish among like-named static functions.\n\ *ADDRESS, to edit at the line containing that address.\n\ Uses EDITOR environment variable contents as editor (or ex as default).",NULL)); c->completer = location_completer; add_com ("list", class_files, list_command, concat ("List specified function or line.\n\ With no argument, lists ten more lines after or around previous listing.\n\ \"list -\" lists the ten lines before a previous ten-line listing.\n\ One argument specifies a line, and ten lines are listed around that line.\n\ Two arguments with comma between specify starting and ending lines to list.\n\ ", "\ Lines can be specified in these ways:\n\ LINENUM, to list around that line in current file,\n\ FILE:LINENUM, to list around that line in that file,\n\ FUNCTION, to list around beginning of that function,\n\ FILE:FUNCTION, to distinguish among like-named static functions.\n\ *ADDRESS, to list around the line containing that address.\n\ With two args if one is empty it stands for ten lines away from the other arg.", NULL)); if (!xdb_commands) add_com_alias ("l", "list", class_files, 1); else add_com_alias ("v", "list", class_files, 1); if (dbx_commands) add_com_alias ("file", "list", class_files, 1); 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); /* NOTE: cagney/2000-03-20: Being able to enter ``(gdb) !ls'' would be a really useful feature. Unfortunately, the below wont do this. Instead it adds support for the form ``(gdb) ! ls'' (i.e. the space is required). If the ``!'' command below is added the complains about no ``!'' command would be replaced by complains about how the ``!'' command is broken :-) */ if (xdb_commands) add_com_alias ("!", "shell", class_support, 0); c = add_com ("make", class_support, make_command, "Run the ``make'' program using the rest of the line as arguments."); set_cmd_completer (c, filename_completer); add_cmd ("user", no_class, show_user, "Show definitions of user defined commands.\n\ Argument is the name of the user defined command.\n\ With no argument, show definitions of all user defined commands.", &showlist); add_com ("apropos", class_support, apropos_command, "Search for commands matching a REGEXP"); add_show_from_set ( add_set_cmd ("max-user-call-depth", no_class, var_integer, (char *) &max_user_call_depth, "Set the max call depth for user-defined commands.\n", &setlist), &showlist); } Index: stable/12/contrib/gdb/gdb/cli/cli-setshow.c =================================================================== --- stable/12/contrib/gdb/gdb/cli/cli-setshow.c (revision 358111) +++ stable/12/contrib/gdb/gdb/cli/cli-setshow.c (revision 358112) @@ -1,387 +1,386 @@ /* Handle set and show GDB commands. Copyright 2000, 2001, 2002, 2003 Free Software Foundation, Inc. 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 "readline/tilde.h" #include "value.h" #include #include "gdb_string.h" #include "ui-out.h" #include "cli/cli-decode.h" #include "cli/cli-cmds.h" #include "cli/cli-setshow.h" /* Prototypes for local functions */ static int parse_binary_operation (char *); static enum auto_boolean parse_auto_binary_operation (const char *arg) { if (arg != NULL && *arg != '\0') { int length = strlen (arg); while (isspace (arg[length - 1]) && length > 0) length--; if (strncmp (arg, "on", length) == 0 || strncmp (arg, "1", length) == 0 || strncmp (arg, "yes", length) == 0 || strncmp (arg, "enable", length) == 0) return AUTO_BOOLEAN_TRUE; else if (strncmp (arg, "off", length) == 0 || strncmp (arg, "0", length) == 0 || strncmp (arg, "no", length) == 0 || strncmp (arg, "disable", length) == 0) return AUTO_BOOLEAN_FALSE; else if (strncmp (arg, "auto", length) == 0 || (strncmp (arg, "-1", length) == 0 && length > 1)) return AUTO_BOOLEAN_AUTO; } error ("\"on\", \"off\" or \"auto\" expected."); return AUTO_BOOLEAN_AUTO; /* pacify GCC */ } static int parse_binary_operation (char *arg) { int length; if (!arg || !*arg) return 1; length = strlen (arg); while (arg[length - 1] == ' ' || arg[length - 1] == '\t') length--; if (strncmp (arg, "on", length) == 0 || strncmp (arg, "1", length) == 0 || strncmp (arg, "yes", length) == 0 || strncmp (arg, "enable", length) == 0) return 1; else if (strncmp (arg, "off", length) == 0 || strncmp (arg, "0", length) == 0 || strncmp (arg, "no", length) == 0 || strncmp (arg, "disable", length) == 0) return 0; else { error ("\"on\" or \"off\" expected."); return 0; } } /* Do a "set" or "show" command. ARG is NULL if no argument, or the text of the argument, and FROM_TTY is nonzero if this command is being entered directly by the user (i.e. these are just like any other command). C is the command list element for the command. */ void do_setshow_command (char *arg, int from_tty, struct cmd_list_element *c) { if (c->type == set_cmd) { switch (c->var_type) { case var_string: { char *new; char *p; char *q; int ch; if (arg == NULL) arg = ""; new = (char *) xmalloc (strlen (arg) + 2); p = arg; q = new; while ((ch = *p++) != '\000') { if (ch == '\\') { /* \ at end of argument is used after spaces so they won't be lost. */ /* This is obsolete now that we no longer strip trailing whitespace and actually, the backslash didn't get here in my test, readline or something did something funky with a backslash right before a newline. */ if (*p == 0) break; ch = parse_escape (&p); if (ch == 0) break; /* C loses */ else if (ch > 0) *q++ = ch; } else *q++ = ch; } #if 0 if (*(p - 1) != '\\') *q++ = ' '; #endif *q++ = '\0'; new = (char *) xrealloc (new, q - new); if (*(char **) c->var != NULL) xfree (*(char **) c->var); *(char **) c->var = new; } break; case var_string_noescape: if (arg == NULL) arg = ""; if (*(char **) c->var != NULL) xfree (*(char **) c->var); *(char **) c->var = savestring (arg, strlen (arg)); break; case var_filename: if (arg == NULL) error_no_arg ("filename to set it to."); if (*(char **) c->var != NULL) xfree (*(char **) c->var); *(char **) c->var = tilde_expand (arg); break; case var_boolean: *(int *) c->var = parse_binary_operation (arg); break; case var_auto_boolean: *(enum auto_boolean *) c->var = parse_auto_binary_operation (arg); break; case var_uinteger: if (arg == NULL) error_no_arg ("integer to set it to."); *(unsigned int *) c->var = parse_and_eval_long (arg); if (*(unsigned int *) c->var == 0) *(unsigned int *) c->var = UINT_MAX; break; case var_integer: { unsigned int val; if (arg == NULL) error_no_arg ("integer to set it to."); val = parse_and_eval_long (arg); if (val == 0) *(int *) c->var = INT_MAX; else if (val >= INT_MAX) error ("integer %u out of range", val); else *(int *) c->var = val; break; } case var_zinteger: if (arg == NULL) error_no_arg ("integer to set it to."); *(int *) c->var = parse_and_eval_long (arg); break; case var_enum: { int i; int len; int nmatches; const char *match = NULL; char *p; /* if no argument was supplied, print an informative error message */ if (arg == NULL) { char msg[1024]; strcpy (msg, "Requires an argument. Valid arguments are "); for (i = 0; c->enums[i]; i++) { if (i != 0) strcat (msg, ", "); strcat (msg, c->enums[i]); } strcat (msg, "."); error ("%s", msg); } p = strchr (arg, ' '); if (p) len = p - arg; else len = strlen (arg); nmatches = 0; for (i = 0; c->enums[i]; i++) if (strncmp (arg, c->enums[i], len) == 0) { if (c->enums[i][len] == '\0') { match = c->enums[i]; nmatches = 1; break; /* exact match. */ } else { match = c->enums[i]; nmatches++; } } if (nmatches <= 0) error ("Undefined item: \"%s\".", arg); if (nmatches > 1) error ("Ambiguous item \"%s\".", arg); *(const char **) c->var = match; } break; default: error ("gdb internal error: bad var_type in do_setshow_command"); } } else if (c->type == show_cmd) { struct cleanup *old_chain; struct ui_stream *stb; int quote; stb = ui_out_stream_new (uiout); old_chain = make_cleanup_ui_out_stream_delete (stb); /* Possibly call the pre hook. */ if (c->pre_show_hook) (c->pre_show_hook) (c); /* Print doc minus "show" at start. */ print_doc_line (gdb_stdout, c->doc + 5); ui_out_text (uiout, " is "); ui_out_wrap_hint (uiout, " "); quote = 0; switch (c->var_type) { case var_string: { unsigned char *p; if (*(unsigned char **) c->var) fputstr_filtered (*(unsigned char **) c->var, '"', stb->stream); quote = 1; } break; case var_string_noescape: case var_filename: case var_enum: if (*(char **) c->var) fputs_filtered (*(char **) c->var, stb->stream); quote = 1; break; case var_boolean: fputs_filtered (*(int *) c->var ? "on" : "off", stb->stream); break; case var_auto_boolean: switch (*(enum auto_boolean*) c->var) { case AUTO_BOOLEAN_TRUE: fputs_filtered ("on", stb->stream); break; case AUTO_BOOLEAN_FALSE: fputs_filtered ("off", stb->stream); break; case AUTO_BOOLEAN_AUTO: fputs_filtered ("auto", stb->stream); break; default: internal_error (__FILE__, __LINE__, "do_setshow_command: invalid var_auto_boolean"); break; } break; case var_uinteger: if (*(unsigned int *) c->var == UINT_MAX) { fputs_filtered ("unlimited", stb->stream); break; } /* else fall through */ case var_zinteger: fprintf_filtered (stb->stream, "%u", *(unsigned int *) c->var); break; case var_integer: if (*(int *) c->var == INT_MAX) { fputs_filtered ("unlimited", stb->stream); } else fprintf_filtered (stb->stream, "%d", *(int *) c->var); break; default: error ("gdb internal error: bad var_type in do_setshow_command"); } if (quote) ui_out_text (uiout, "\""); ui_out_field_stream (uiout, "value", stb); if (quote) ui_out_text (uiout, "\""); ui_out_text (uiout, ".\n"); do_cleanups (old_chain); } else error ("gdb internal error: bad cmd_type in do_setshow_command"); c->func (c, NULL, from_tty); if (c->type == set_cmd && set_hook) set_hook (c); } /* Show all the settings in a list of show commands. */ void cmd_show_list (struct cmd_list_element *list, int from_tty, char *prefix) { struct cleanup *showlist_chain; showlist_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "showlist"); for (; list != NULL; list = list->next) { /* If we find a prefix, run its list, prefixing our output by its prefix (with "show " skipped). */ if (list->prefixlist && !list->abbrev_flag) { struct cleanup *optionlist_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "optionlist"); ui_out_field_string (uiout, "prefix", list->prefixname + 5); cmd_show_list (*list->prefixlist, from_tty, list->prefixname + 5); /* Close the tuple. */ do_cleanups (optionlist_chain); } if (list->type == show_cmd) { struct cleanup *option_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "option"); ui_out_text (uiout, prefix); ui_out_field_string (uiout, "name", list->name); ui_out_text (uiout, ": "); do_setshow_command ((char *) NULL, from_tty, list); /* Close the tuple. */ do_cleanups (option_chain); } } /* Close the tuple. */ do_cleanups (showlist_chain); } Index: stable/12/contrib/gdb/gdb/event-top.c =================================================================== --- stable/12/contrib/gdb/gdb/event-top.c (revision 358111) +++ stable/12/contrib/gdb/gdb/event-top.c (revision 358112) @@ -1,1187 +1,1186 @@ /* Top level stuff for GDB, the GNU debugger. Copyright 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. Written by Elena Zannoni of Cygnus Solutions. 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 "top.h" #include "inferior.h" #include "target.h" #include "terminal.h" /* for job_control */ #include "event-loop.h" #include "event-top.h" #include "interps.h" #include /* For dont_repeat() */ #include "gdbcmd.h" /* readline include files */ #include "readline/readline.h" -#include "readline/history.h" /* readline defines this. */ #undef savestring static void rl_callback_read_char_wrapper (gdb_client_data client_data); static void command_line_handler (char *rl); static void command_line_handler_continuation (struct continuation_arg *arg); static void change_line_handler (void); static void change_annotation_level (void); static void command_handler (char *command); static void async_do_nothing (gdb_client_data arg); static void async_disconnect (gdb_client_data arg); static void async_stop_sig (gdb_client_data arg); static void async_float_handler (gdb_client_data arg); /* Signal handlers. */ static void handle_sigquit (int sig); static void handle_sighup (int sig); static void handle_sigfpe (int sig); #if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) static void handle_sigwinch (int sig); #endif /* Functions to be invoked by the event loop in response to signals. */ static void async_do_nothing (gdb_client_data); static void async_disconnect (gdb_client_data); static void async_float_handler (gdb_client_data); static void async_stop_sig (gdb_client_data); /* Readline offers an alternate interface, via callback functions. These are all included in the file callback.c in the readline distribution. This file provides (mainly) a function, which the event loop uses as callback (i.e. event handler) whenever an event is detected on the standard input file descriptor. readline_callback_read_char is called (by the GDB event loop) whenever there is a new character ready on the input stream. This function incrementally builds a buffer internal to readline where it accumulates the line read up to the point of invocation. In the special case in which the character read is newline, the function invokes a GDB supplied callback routine, which does the processing of a full command line. This latter routine is the asynchronous analog of the old command_line_input in gdb. Instead of invoking (and waiting for) readline to read the command line and pass it back to command_loop for processing, the new command_line_handler function has the command line already available as its parameter. INPUT_HANDLER is to be set to the function that readline will invoke when a complete line of input is ready. CALL_READLINE is to be set to the function that readline offers as callback to the event_loop. */ void (*input_handler) (char *); void (*call_readline) (gdb_client_data); /* Important variables for the event loop. */ /* This is used to determine if GDB is using the readline library or its own simplified form of readline. It is used by the asynchronous form of the set editing command. ezannoni: as of 1999-04-29 I expect that this variable will not be used after gdb is changed to use the event loop as default engine, and event-top.c is merged into top.c. */ int async_command_editing_p; /* This variable contains the new prompt that the user sets with the set prompt command. */ char *new_async_prompt; /* This is the annotation suffix that will be used when the annotation_level is 2. */ char *async_annotation_suffix; /* This is used to display the notification of the completion of an asynchronous execution command. */ int exec_done_display_p = 0; /* This is the file descriptor for the input stream that GDB uses to read commands from. */ int input_fd; /* This is the prompt stack. Prompts will be pushed on the stack as needed by the different 'kinds' of user inputs GDB is asking for. See event-loop.h. */ struct prompts the_prompts; /* signal handling variables */ /* Each of these is a pointer to a function that the event loop will invoke if the corresponding signal has received. The real signal handlers mark these functions as ready to be executed and the event loop, in a later iteration, calls them. See the function invoke_async_signal_handler. */ void *sigint_token; #ifdef SIGHUP void *sighup_token; #endif void *sigquit_token; void *sigfpe_token; #if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) void *sigwinch_token; #endif #ifdef STOP_SIGNAL void *sigtstp_token; #endif /* Structure to save a partially entered command. This is used when the user types '\' at the end of a command line. This is necessary because each line of input is handled by a different call to command_line_handler, and normally there is no state retained between different calls. */ int more_to_come = 0; struct readline_input_state { char *linebuffer; char *linebuffer_ptr; } readline_input_state; /* This hook is called by rl_callback_read_char_wrapper after each character is processed. */ void (*after_char_processing_hook) (); /* Wrapper function for calling into the readline library. The event loop expects the callback function to have a paramter, while readline expects none. */ static void rl_callback_read_char_wrapper (gdb_client_data client_data) { rl_callback_read_char (); if (after_char_processing_hook) (*after_char_processing_hook) (); } /* Initialize all the necessary variables, start the event loop, register readline, and stdin, start the loop. */ void cli_command_loop (void) { int length; char *a_prompt; char *gdb_prompt = get_prompt (); /* If we are using readline, set things up and display the first prompt, otherwise just print the prompt. */ if (async_command_editing_p) { /* Tell readline what the prompt to display is and what function it will need to call after a whole line is read. This also displays the first prompt. */ length = strlen (PREFIX (0)) + strlen (gdb_prompt) + strlen (SUFFIX (0)) + 1; a_prompt = (char *) xmalloc (length); strcpy (a_prompt, PREFIX (0)); strcat (a_prompt, gdb_prompt); strcat (a_prompt, SUFFIX (0)); rl_callback_handler_install (a_prompt, input_handler); } else display_gdb_prompt (0); /* Now it's time to start the event loop. */ start_event_loop (); } /* Change the function to be invoked every time there is a character ready on stdin. This is used when the user sets the editing off, therefore bypassing readline, and letting gdb handle the input itself, via gdb_readline2. Also it is used in the opposite case in which the user sets editing on again, by restoring readline handling of the input. */ static void change_line_handler (void) { /* NOTE: this operates on input_fd, not instream. If we are reading commands from a file, instream will point to the file. However in async mode, we always read commands from a file with editing off. This means that the 'set editing on/off' will have effect only on the interactive session. */ if (async_command_editing_p) { /* Turn on editing by using readline. */ call_readline = rl_callback_read_char_wrapper; input_handler = command_line_handler; } else { /* Turn off editing by using gdb_readline2. */ rl_callback_handler_remove (); call_readline = gdb_readline2; /* Set up the command handler as well, in case we are called as first thing from .gdbinit. */ input_handler = command_line_handler; } } /* Displays the prompt. The prompt that is displayed is the current top of the prompt stack, if the argument NEW_PROMPT is 0. Otherwise, it displays whatever NEW_PROMPT is. This is used after each gdb command has completed, and in the following cases: 1. when the user enters a command line which is ended by '\' indicating that the command will continue on the next line. In that case the prompt that is displayed is the empty string. 2. When the user is entering 'commands' for a breakpoint, or actions for a tracepoint. In this case the prompt will be '>' 3. Other???? FIXME: 2. & 3. not implemented yet for async. */ void display_gdb_prompt (char *new_prompt) { int prompt_length = 0; char *gdb_prompt = get_prompt (); /* Each interpreter has its own rules on displaying the command prompt. */ if (!current_interp_display_prompt_p ()) return; if (target_executing && sync_execution) { /* This is to trick readline into not trying to display the prompt. Even though we display the prompt using this function, readline still tries to do its own display if we don't call rl_callback_handler_install and rl_callback_handler_remove (which readline detects because a global variable is not set). If readline did that, it could mess up gdb signal handlers for SIGINT. Readline assumes that between calls to rl_set_signals and rl_clear_signals gdb doesn't do anything with the signal handlers. Well, that's not the case, because when the target executes we change the SIGINT signal handler. If we allowed readline to display the prompt, the signal handler change would happen exactly between the calls to the above two functions. Calling rl_callback_handler_remove(), does the job. */ rl_callback_handler_remove (); return; } if (!new_prompt) { /* Just use the top of the prompt stack. */ prompt_length = strlen (PREFIX (0)) + strlen (SUFFIX (0)) + strlen (gdb_prompt) + 1; new_prompt = (char *) alloca (prompt_length); /* Prefix needs to have new line at end. */ strcpy (new_prompt, PREFIX (0)); strcat (new_prompt, gdb_prompt); /* Suffix needs to have a new line at end and \032 \032 at beginning. */ strcat (new_prompt, SUFFIX (0)); } if (async_command_editing_p) { rl_callback_handler_remove (); rl_callback_handler_install (new_prompt, input_handler); } /* new_prompt at this point can be the top of the stack or the one passed in */ else if (new_prompt) { /* Don't use a _filtered function here. It causes the assumed character position to be off, since the newline we read from the user is not accounted for. */ fputs_unfiltered (new_prompt, gdb_stdout); gdb_flush (gdb_stdout); } } /* Used when the user requests a different annotation level, with 'set annotate'. It pushes a new prompt (with prefix and suffix) on top of the prompt stack, if the annotation level desired is 2, otherwise it pops the top of the prompt stack when we want the annotation level to be the normal ones (1 or 0). */ static void change_annotation_level (void) { char *prefix, *suffix; if (!PREFIX (0) || !PROMPT (0) || !SUFFIX (0)) { /* The prompt stack has not been initialized to "", we are using gdb w/o the --async switch */ warning ("Command has same effect as set annotate"); return; } if (annotation_level > 1) { if (!strcmp (PREFIX (0), "") && !strcmp (SUFFIX (0), "")) { /* Push a new prompt if the previous annotation_level was not >1. */ prefix = (char *) alloca (strlen (async_annotation_suffix) + 10); strcpy (prefix, "\n\032\032pre-"); strcat (prefix, async_annotation_suffix); strcat (prefix, "\n"); suffix = (char *) alloca (strlen (async_annotation_suffix) + 6); strcpy (suffix, "\n\032\032"); strcat (suffix, async_annotation_suffix); strcat (suffix, "\n"); push_prompt (prefix, (char *) 0, suffix); } } else { if (strcmp (PREFIX (0), "") && strcmp (SUFFIX (0), "")) { /* Pop the top of the stack, we are going back to annotation < 1. */ pop_prompt (); } } } /* Pushes a new prompt on the prompt stack. Each prompt has three parts: prefix, prompt, suffix. Usually prefix and suffix are empty strings, except when the annotation level is 2. Memory is allocated within savestring for the new prompt. */ void push_prompt (char *prefix, char *prompt, char *suffix) { the_prompts.top++; PREFIX (0) = savestring (prefix, strlen (prefix)); /* Note that this function is used by the set annotate 2 command. This is why we take care of saving the old prompt in case a new one is not specified. */ if (prompt) PROMPT (0) = savestring (prompt, strlen (prompt)); else PROMPT (0) = savestring (PROMPT (-1), strlen (PROMPT (-1))); SUFFIX (0) = savestring (suffix, strlen (suffix)); } /* Pops the top of the prompt stack, and frees the memory allocated for it. */ void pop_prompt (void) { /* If we are not during a 'synchronous' execution command, in which case, the top prompt would be empty. */ if (strcmp (PROMPT (0), "")) /* This is for the case in which the prompt is set while the annotation level is 2. The top prompt will be changed, but when we return to annotation level < 2, we want that new prompt to be in effect, until the user does another 'set prompt'. */ if (strcmp (PROMPT (0), PROMPT (-1))) { xfree (PROMPT (-1)); PROMPT (-1) = savestring (PROMPT (0), strlen (PROMPT (0))); } xfree (PREFIX (0)); xfree (PROMPT (0)); xfree (SUFFIX (0)); the_prompts.top--; } /* When there is an event ready on the stdin file desriptor, instead of calling readline directly throught the callback function, or instead of calling gdb_readline2, give gdb a chance to detect errors and do something. */ void stdin_event_handler (int error, gdb_client_data client_data) { if (error) { printf_unfiltered ("error detected on stdin\n"); delete_file_handler (input_fd); discard_all_continuations (); /* If stdin died, we may as well kill gdb. */ quit_command ((char *) 0, stdin == instream); } else (*call_readline) (client_data); } /* Re-enable stdin after the end of an execution command in synchronous mode, or after an error from the target, and we aborted the exec operation. */ void async_enable_stdin (void *dummy) { /* See NOTE in async_disable_stdin() */ /* FIXME: cagney/1999-09-27: Call this before clearing sync_execution. Current target_terminal_ours() implementations check for sync_execution before switching the terminal. */ target_terminal_ours (); pop_prompt (); sync_execution = 0; } /* Disable reads from stdin (the console) marking the command as synchronous. */ void async_disable_stdin (void) { sync_execution = 1; push_prompt ("", "", ""); /* FIXME: cagney/1999-09-27: At present this call is technically redundant since infcmd.c and infrun.c both already call target_terminal_inferior(). As the terminal handling (in sync/async mode) is refined, the duplicate calls can be eliminated (Here or in infcmd.c/infrun.c). */ target_terminal_inferior (); /* Add the reinstate of stdin to the list of cleanups to be done in case the target errors out and dies. These cleanups are also done in case of normal successful termination of the execution command, by complete_execution(). */ make_exec_error_cleanup (async_enable_stdin, NULL); } /* Handles a gdb command. This function is called by command_line_handler, which has processed one or more input lines into COMMAND. */ /* NOTE: 1999-04-30 This is the asynchronous version of the command_loop function. The command_loop function will be obsolete when we switch to use the event loop at every execution of gdb. */ static void command_handler (char *command) { struct cleanup *old_chain; int stdin_is_tty = ISATTY (stdin); struct continuation_arg *arg1; struct continuation_arg *arg2; long time_at_cmd_start; #ifdef HAVE_SBRK long space_at_cmd_start = 0; #endif extern int display_time; extern int display_space; quit_flag = 0; if (instream == stdin && stdin_is_tty) reinitialize_more_filter (); old_chain = make_cleanup (null_cleanup, 0); /* If readline returned a NULL command, it means that the connection with the terminal is gone. This happens at the end of a testsuite run, after Expect has hung up but GDB is still alive. In such a case, we just quit gdb killing the inferior program too. */ if (command == 0) quit_command ((char *) 0, stdin == instream); time_at_cmd_start = get_run_time (); if (display_space) { #ifdef HAVE_SBRK char *lim = (char *) sbrk (0); space_at_cmd_start = lim - lim_at_start; #endif } execute_command (command, instream == stdin); /* Set things up for this function to be compete later, once the execution has completed, if we are doing an execution command, otherwise, just go ahead and finish. */ if (target_can_async_p () && target_executing) { arg1 = (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg)); arg2 = (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg)); arg1->next = arg2; arg2->next = NULL; arg1->data.longint = time_at_cmd_start; #ifdef HAVE_SBRK arg2->data.longint = space_at_cmd_start; #endif add_continuation (command_line_handler_continuation, arg1); } /* Do any commands attached to breakpoint we stopped at. Only if we are always running synchronously. Or if we have just executed a command that doesn't start the target. */ if (!target_can_async_p () || !target_executing) { bpstat_do_actions (&stop_bpstat); do_cleanups (old_chain); if (display_time) { long cmd_time = get_run_time () - time_at_cmd_start; printf_unfiltered ("Command execution time: %ld.%06ld\n", cmd_time / 1000000, cmd_time % 1000000); } if (display_space) { #ifdef HAVE_SBRK char *lim = (char *) sbrk (0); long space_now = lim - lim_at_start; long space_diff = space_now - space_at_cmd_start; printf_unfiltered ("Space used: %ld (%c%ld for this command)\n", space_now, (space_diff >= 0 ? '+' : '-'), space_diff); #endif } } } /* Do any commands attached to breakpoint we stopped at. Only if we are always running synchronously. Or if we have just executed a command that doesn't start the target. */ void command_line_handler_continuation (struct continuation_arg *arg) { extern int display_time; extern int display_space; long time_at_cmd_start = arg->data.longint; long space_at_cmd_start = arg->next->data.longint; bpstat_do_actions (&stop_bpstat); /*do_cleanups (old_chain); *//*?????FIXME????? */ if (display_time) { long cmd_time = get_run_time () - time_at_cmd_start; printf_unfiltered ("Command execution time: %ld.%06ld\n", cmd_time / 1000000, cmd_time % 1000000); } if (display_space) { #ifdef HAVE_SBRK char *lim = (char *) sbrk (0); long space_now = lim - lim_at_start; long space_diff = space_now - space_at_cmd_start; printf_unfiltered ("Space used: %ld (%c%ld for this command)\n", space_now, (space_diff >= 0 ? '+' : '-'), space_diff); #endif } } /* Handle a complete line of input. This is called by the callback mechanism within the readline library. Deal with incomplete commands as well, by saving the partial input in a global buffer. */ /* NOTE: 1999-04-30 This is the asynchronous version of the command_line_input function. command_line_input will become obsolete once we use the event loop as the default mechanism in GDB. */ static void command_line_handler (char *rl) { static char *linebuffer = 0; static unsigned linelength = 0; char *p; char *p1; extern char *line; extern int linesize; char *nline; char got_eof = 0; int repeat = (instream == stdin); if (annotation_level > 1 && instream == stdin) { printf_unfiltered ("\n\032\032post-"); puts_unfiltered (async_annotation_suffix); printf_unfiltered ("\n"); } if (linebuffer == 0) { linelength = 80; linebuffer = (char *) xmalloc (linelength); } p = linebuffer; if (more_to_come) { strcpy (linebuffer, readline_input_state.linebuffer); p = readline_input_state.linebuffer_ptr; xfree (readline_input_state.linebuffer); more_to_come = 0; pop_prompt (); } #ifdef STOP_SIGNAL if (job_control) signal (STOP_SIGNAL, handle_stop_sig); #endif /* 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 (source_file_name != NULL) { ++source_line_number; sprintf (source_error, "%s%s:%d: Error in sourced command file:\n", source_pre_error, source_file_name, source_line_number); error_pre_print = source_error; } /* If we are in this case, then command_handler will call quit and exit from gdb. */ if (!rl || rl == (char *) EOF) { got_eof = 1; command_handler (0); } if (strlen (rl) + 1 + (p - linebuffer) > linelength) { linelength = strlen (rl) + 1 + (p - linebuffer); nline = (char *) xrealloc (linebuffer, linelength); p += nline - linebuffer; linebuffer = nline; } p1 = rl; /* Copy line. Don't copy null at end. (Leaves line alone if this was just a newline) */ while (*p1) *p++ = *p1++; xfree (rl); /* Allocated in readline. */ if (p > linebuffer && *(p - 1) == '\\') { p--; /* Put on top of '\'. */ readline_input_state.linebuffer = savestring (linebuffer, strlen (linebuffer)); readline_input_state.linebuffer_ptr = p; /* We will not invoke a execute_command if there is more input expected to complete the command. So, we need to print an empty prompt here. */ more_to_come = 1; push_prompt ("", "", ""); display_gdb_prompt (0); return; } #ifdef STOP_SIGNAL if (job_control) signal (STOP_SIGNAL, SIG_DFL); #endif #define SERVER_COMMAND_LENGTH 7 server_command = (p - linebuffer > SERVER_COMMAND_LENGTH) && strncmp (linebuffer, "server ", SERVER_COMMAND_LENGTH) == 0; if (server_command) { /* Note that we don't set `line'. Between this and the check in dont_repeat, this insures that repeating will still do the right thing. */ *p = '\0'; command_handler (linebuffer + SERVER_COMMAND_LENGTH); display_gdb_prompt (0); return; } /* Do history expansion if that is wished. */ if (history_expansion_p && instream == stdin && ISATTY (instream)) { char *history_value; int expanded; *p = '\0'; /* Insert null now. */ expanded = history_expand (linebuffer, &history_value); if (expanded) { /* Print the changes. */ printf_unfiltered ("%s\n", history_value); /* If there was an error, call this function again. */ if (expanded < 0) { xfree (history_value); return; } if (strlen (history_value) > linelength) { linelength = strlen (history_value) + 1; linebuffer = (char *) xrealloc (linebuffer, linelength); } strcpy (linebuffer, history_value); p = linebuffer + strlen (linebuffer); xfree (history_value); } } /* If we just got an empty line, and that is supposed to repeat the previous command, return the value in the global buffer. */ if (repeat && p == linebuffer && *p != '\\') { command_handler (line); display_gdb_prompt (0); return; } for (p1 = linebuffer; *p1 == ' ' || *p1 == '\t'; p1++); if (repeat && !*p1) { command_handler (line); display_gdb_prompt (0); return; } *p = 0; /* Add line to history if appropriate. */ if (instream == stdin && ISATTY (stdin) && *linebuffer) add_history (linebuffer); /* Note: lines consisting solely of comments are added to the command history. This is useful when you type a command, and then realize you don't want to execute it quite yet. You can comment out the command and then later fetch it from the value history and remove the '#'. The kill ring is probably better, but some people are in the habit of commenting things out. */ if (*p1 == '#') *p1 = '\0'; /* Found a comment. */ /* Save into global buffer if appropriate. */ if (repeat) { if (linelength > linesize) { line = xrealloc (line, linelength); linesize = linelength; } strcpy (line, linebuffer); if (!more_to_come) { command_handler (line); display_gdb_prompt (0); } return; } command_handler (linebuffer); display_gdb_prompt (0); return; } /* Does reading of input from terminal w/o the editing features provided by the readline library. */ /* NOTE: 1999-04-30 Asynchronous version of gdb_readline. gdb_readline will become obsolete when the event loop is made the default execution for gdb. */ void gdb_readline2 (gdb_client_data client_data) { int c; char *result; int input_index = 0; int result_size = 80; static int done_once = 0; /* Unbuffer the input stream, so that, later on, the calls to fgetc fetch only one char at the time from the stream. The fgetc's will get up to the first newline, but there may be more chars in the stream after '\n'. If we buffer the input and fgetc drains the stream, getting stuff beyond the newline as well, a select, done afterwards will not trigger. */ if (!done_once && !ISATTY (instream)) { setbuf (instream, NULL); done_once = 1; } result = (char *) xmalloc (result_size); /* We still need the while loop here, even though it would seem obvious to invoke gdb_readline2 at every character entered. If not using the readline library, the terminal is in cooked mode, which sends the characters all at once. Poll will notice that the input fd has changed state only after enter is pressed. At this point we still need to fetch all the chars entered. */ while (1) { /* Read from stdin if we are executing a user defined command. This is the right thing for prompt_for_continue, at least. */ c = fgetc (instream ? instream : stdin); if (c == EOF) { if (input_index > 0) /* The last line does not end with a newline. Return it, and if we are called again fgetc will still return EOF and we'll return NULL then. */ break; xfree (result); (*input_handler) (0); } if (c == '\n') #ifndef CRLF_SOURCE_FILES break; #else { if (input_index > 0 && result[input_index - 1] == '\r') input_index--; break; } #endif result[input_index++] = c; while (input_index >= result_size) { result_size *= 2; result = (char *) xrealloc (result, result_size); } } result[input_index++] = '\0'; (*input_handler) (result); } /* Initialization of signal handlers and tokens. There is a function handle_sig* for each of the signals GDB cares about. Specifically: SIGINT, SIGFPE, SIGQUIT, SIGTSTP, SIGHUP, SIGWINCH. These functions are the actual signal handlers associated to the signals via calls to signal(). The only job for these functions is to enqueue the appropriate event/procedure with the event loop. Such procedures are the old signal handlers. The event loop will take care of invoking the queued procedures to perform the usual tasks associated with the reception of the signal. */ /* NOTE: 1999-04-30 This is the asynchronous version of init_signals. init_signals will become obsolete as we move to have to event loop as the default for gdb. */ void async_init_signals (void) { signal (SIGINT, handle_sigint); sigint_token = create_async_signal_handler (async_request_quit, NULL); /* If SIGTRAP was set to SIG_IGN, then the SIG_IGN will get passed to the inferior and breakpoints will be ignored. */ #ifdef SIGTRAP signal (SIGTRAP, SIG_DFL); #endif /* If we initialize SIGQUIT to SIG_IGN, then the SIG_IGN will get passed to the inferior, which we don't want. It would be possible to do a "signal (SIGQUIT, SIG_DFL)" after we fork, but on BSD4.3 systems using vfork, that can affect the GDB process as well as the inferior (the signal handling tables might be in memory, shared between the two). Since we establish a handler for SIGQUIT, when we call exec it will set the signal to SIG_DFL for us. */ signal (SIGQUIT, handle_sigquit); sigquit_token = create_async_signal_handler (async_do_nothing, NULL); #ifdef SIGHUP if (signal (SIGHUP, handle_sighup) != SIG_IGN) sighup_token = create_async_signal_handler (async_disconnect, NULL); else sighup_token = create_async_signal_handler (async_do_nothing, NULL); #endif signal (SIGFPE, handle_sigfpe); sigfpe_token = create_async_signal_handler (async_float_handler, NULL); #if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) signal (SIGWINCH, handle_sigwinch); sigwinch_token = create_async_signal_handler (SIGWINCH_HANDLER, NULL); #endif #ifdef STOP_SIGNAL sigtstp_token = create_async_signal_handler (async_stop_sig, NULL); #endif } void mark_async_signal_handler_wrapper (void *token) { mark_async_signal_handler ((struct async_signal_handler *) token); } /* Tell the event loop what to do if SIGINT is received. See event-signal.c. */ void handle_sigint (int sig) { signal (sig, handle_sigint); /* If immediate_quit is set, we go ahead and process the SIGINT right away, even if we usually would defer this to the event loop. The assumption here is that it is safe to process ^C immediately if immediate_quit is set. If we didn't, SIGINT would be really processed only the next time through the event loop. To get to that point, though, the command that we want to interrupt needs to finish first, which is unacceptable. */ if (immediate_quit) async_request_quit (0); else /* If immediate quit is not set, we process SIGINT the next time through the loop, which is fine. */ mark_async_signal_handler_wrapper (sigint_token); } /* Do the quit. All the checks have been done by the caller. */ void async_request_quit (gdb_client_data arg) { quit_flag = 1; quit (); } /* Tell the event loop what to do if SIGQUIT is received. See event-signal.c. */ static void handle_sigquit (int sig) { mark_async_signal_handler_wrapper (sigquit_token); signal (sig, handle_sigquit); } /* Called by the event loop in response to a SIGQUIT. */ static void async_do_nothing (gdb_client_data arg) { /* Empty function body. */ } #ifdef SIGHUP /* Tell the event loop what to do if SIGHUP is received. See event-signal.c. */ static void handle_sighup (int sig) { mark_async_signal_handler_wrapper (sighup_token); signal (sig, handle_sighup); } /* Called by the event loop to process a SIGHUP */ static void async_disconnect (gdb_client_data arg) { catch_errors (quit_cover, NULL, "Could not kill the program being debugged", RETURN_MASK_ALL); signal (SIGHUP, SIG_DFL); /*FIXME: ??????????? */ kill (getpid (), SIGHUP); } #endif #ifdef STOP_SIGNAL void handle_stop_sig (int sig) { mark_async_signal_handler_wrapper (sigtstp_token); signal (sig, handle_stop_sig); } static void async_stop_sig (gdb_client_data arg) { char *prompt = get_prompt (); #if STOP_SIGNAL == SIGTSTP signal (SIGTSTP, SIG_DFL); #if HAVE_SIGPROCMASK { sigset_t zero; sigemptyset (&zero); sigprocmask (SIG_SETMASK, &zero, 0); } #elif HAVE_SIGSETMASK sigsetmask (0); #endif kill (getpid (), SIGTSTP); signal (SIGTSTP, handle_stop_sig); #else signal (STOP_SIGNAL, handle_stop_sig); #endif printf_unfiltered ("%s", prompt); gdb_flush (gdb_stdout); /* Forget about any previous command -- null line now will do nothing. */ dont_repeat (); } #endif /* STOP_SIGNAL */ /* Tell the event loop what to do if SIGFPE is received. See event-signal.c. */ static void handle_sigfpe (int sig) { mark_async_signal_handler_wrapper (sigfpe_token); signal (sig, handle_sigfpe); } /* Event loop will call this functin to process a SIGFPE. */ static void async_float_handler (gdb_client_data arg) { /* This message is based on ANSI C, section 4.7. Note that integer divide by zero causes this, so "float" is a misnomer. */ error ("Erroneous arithmetic operation."); } /* Tell the event loop what to do if SIGWINCH is received. See event-signal.c. */ #if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) static void handle_sigwinch (int sig) { mark_async_signal_handler_wrapper (sigwinch_token); signal (sig, handle_sigwinch); } #endif /* Called by do_setshow_command. */ void set_async_editing_command (char *args, int from_tty, struct cmd_list_element *c) { change_line_handler (); } /* Called by do_setshow_command. */ void set_async_annotation_level (char *args, int from_tty, struct cmd_list_element *c) { change_annotation_level (); } /* Called by do_setshow_command. */ void set_async_prompt (char *args, int from_tty, struct cmd_list_element *c) { PROMPT (0) = savestring (new_async_prompt, strlen (new_async_prompt)); } /* Set things up for readline to be invoked via the alternate interface, i.e. via a callback function (rl_callback_read_char), and hook up instream to the event loop. */ void gdb_setup_readline (void) { /* This function is a noop for the sync case. The assumption is that the sync setup is ALL done in gdb_init, and we would only mess it up here. The sync stuff should really go away over time. */ if (event_loop_p) { gdb_stdout = stdio_fileopen (stdout); gdb_stderr = stdio_fileopen (stderr); gdb_stdlog = gdb_stderr; /* for moment */ gdb_stdtarg = gdb_stderr; /* for moment */ /* If the input stream is connected to a terminal, turn on editing. */ if (ISATTY (instream)) { /* Tell gdb that we will be using the readline library. This could be overwritten by a command in .gdbinit like 'set editing on' or 'off'. */ async_command_editing_p = 1; /* When a character is detected on instream by select or poll, readline will be invoked via this callback function. */ call_readline = rl_callback_read_char_wrapper; } else { async_command_editing_p = 0; call_readline = gdb_readline2; } /* When readline has read an end-of-line character, it passes the complete line to gdb for processing. command_line_handler is the function that does this. */ input_handler = command_line_handler; /* Tell readline to use the same input stream that gdb uses. */ rl_instream = instream; /* Get a file descriptor for the input stream, so that we can register it with the event loop. */ input_fd = fileno (instream); /* Now we need to create the event sources for the input file descriptor. */ /* At this point in time, this is the only event source that we register with the even loop. Another source is going to be the target program (inferior), but that must be registered only when it actually exists (I.e. after we say 'run' or after we connect to a remote target. */ add_file_handler (input_fd, stdin_event_handler, 0); } } /* Disable command input through the standard CLI channels. Used in the suspend proc for interpreters that use the standard gdb readline interface, like the cli & the mi. */ void gdb_disable_readline (void) { if (event_loop_p) { /* FIXME - It is too heavyweight to delete and remake these every time you run an interpreter that needs readline. It is probably better to have the interpreters cache these, which in turn means that this needs to be moved into interpreter specific code. */ #if 0 ui_file_delete (gdb_stdout); ui_file_delete (gdb_stderr); gdb_stdlog = NULL; gdb_stdtarg = NULL; #endif rl_callback_handler_remove (); delete_file_handler (input_fd); } } Index: stable/12/contrib/gdb/gdb/top.c =================================================================== --- stable/12/contrib/gdb/gdb/top.c (revision 358111) +++ stable/12/contrib/gdb/gdb/top.c (revision 358112) @@ -1,1912 +1,1911 @@ /* Top level stuff for GDB, the GNU debugger. Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 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 "gdbcmd.h" #include "call-cmds.h" #include "cli/cli-cmds.h" #include "cli/cli-script.h" #include "cli/cli-setshow.h" #include "cli/cli-decode.h" #include "symtab.h" #include "inferior.h" #include #include "target.h" #include "breakpoint.h" #include "gdbtypes.h" #include "expression.h" #include "value.h" #include "language.h" #include "terminal.h" /* For job_control. */ #include "annotate.h" #include "completer.h" #include "top.h" #include "version.h" #include "serial.h" #include "doublest.h" #include "gdb_assert.h" /* readline include files */ #include "readline/readline.h" -#include "readline/history.h" /* readline defines this. */ #undef savestring #include #include #include "event-top.h" #include "gdb_string.h" #include "gdb_stat.h" #include #include "ui-out.h" #include "cli-out.h" /* Default command line prompt. This is overriden in some configs. */ #ifndef DEFAULT_PROMPT #define DEFAULT_PROMPT "(gdb) " #endif /* Initialization file name for gdb. This is overridden in some configs. */ #ifndef GDBINIT_FILENAME #define GDBINIT_FILENAME ".gdbinit" #endif char gdbinit[] = GDBINIT_FILENAME; int inhibit_gdbinit = 0; /* If nonzero, and GDB has been configured to be able to use windows, attempt to open them upon startup. */ int use_windows = 0; extern char lang_frame_mismatch_warn[]; /* language.c */ /* Flag for whether we want all the "from_tty" gubbish printed. */ int caution = 1; /* Default is yes, sigh. */ /* stdio stream that command input is being read from. Set to stdin normally. Set by source_command to the file we are sourcing. Set to NULL if we are executing a user-defined command or interacting via a GUI. */ FILE *instream; /* Current working directory. */ char *current_directory; /* The directory name is actually stored here (usually). */ char gdb_dirbuf[1024]; /* Function to call before reading a command, if nonzero. The function receives two args: an input stream, and a prompt string. */ void (*window_hook) (FILE *, char *); int epoch_interface; int xgdb_verbose; /* gdb prints this when reading a command interactively */ static char *gdb_prompt_string; /* the global prompt string */ /* Buffer used for reading command lines, and the size allocated for it so far. */ char *line; int linesize = 100; /* Nonzero if the current command is modified by "server ". This affects things like recording into the command history, commands repeating on RETURN, etc. This is so a user interface (emacs, GUI, whatever) can issue its own commands and also send along commands from the user, and have the user not notice that the user interface is issuing commands too. */ int server_command; /* Baud rate specified for talking to serial target systems. Default is left as -1, so targets can choose their own defaults. */ /* FIXME: This means that "show remotebaud" and gr_files_info can print -1 or (unsigned int)-1. This is a Bad User Interface. */ int baud_rate = -1; /* Timeout limit for response from target. */ /* The default value has been changed many times over the years. It was originally 5 seconds. But that was thought to be a long time to sit and wait, so it was changed to 2 seconds. That was thought to be plenty unless the connection was going through some terminal server or multiplexer or other form of hairy serial connection. In mid-1996, remote_timeout was moved from remote.c to top.c and it began being used in other remote-* targets. It appears that the default was changed to 20 seconds at that time, perhaps because the Renesas E7000 ICE didn't always respond in a timely manner. But if 5 seconds is a long time to sit and wait for retransmissions, 20 seconds is far worse. This demonstrates the difficulty of using a single variable for all protocol timeouts. As remote.c is used much more than remote-e7000.c, it was changed back to 2 seconds in 1999. */ int remote_timeout = 2; /* Non-zero tells remote* modules to output debugging info. */ int remote_debug = 0; /* Non-zero means the target is running. Note: this is different from saying that there is an active target and we are stopped at a breakpoint, for instance. This is a real indicator whether the target is off and running, which gdb is doing something else. */ int target_executing = 0; /* Level of control structure. */ static int control_level; /* Sbrk location on entry to main. Used for statistics only. */ #ifdef HAVE_SBRK char *lim_at_start; #endif /* Signal to catch ^Z typed while reading a command: SIGTSTP or SIGCONT. */ #ifndef STOP_SIGNAL #ifdef SIGTSTP #define STOP_SIGNAL SIGTSTP static void stop_sig (int); #endif #endif /* Hooks for alternate command interfaces. */ /* Called after most modules have been initialized, but before taking users command file. If the UI fails to initialize and it wants GDB to continue using the default UI, then it should clear this hook before returning. */ void (*init_ui_hook) (char *argv0); /* This hook is called from within gdb's many mini-event loops which could steal control from a real user interface's event loop. It returns non-zero if the user is requesting a detach, zero otherwise. */ int (*ui_loop_hook) (int); /* Called instead of command_loop at top level. Can be invoked via throw_exception(). */ void (*command_loop_hook) (void); /* Called from print_frame_info to list the line we stopped in. */ void (*print_frame_info_listing_hook) (struct symtab * s, int line, int stopline, int noerror); /* Replaces most of query. */ int (*query_hook) (const char *, va_list); /* Replaces most of warning. */ void (*warning_hook) (const char *, va_list); /* These three functions support getting lines of text from the user. They are used in sequence. First readline_begin_hook is called with a text string that might be (for example) a message for the user to type in a sequence of commands to be executed at a breakpoint. If this function calls back to a GUI, it might take this opportunity to pop up a text interaction window with this message. Next, readline_hook is called with a prompt that is emitted prior to collecting the user input. It can be called multiple times. Finally, readline_end_hook is called to notify the GUI that we are done with the interaction window and it can close it. */ void (*readline_begin_hook) (char *, ...); char *(*readline_hook) (char *); void (*readline_end_hook) (void); /* Called as appropriate to notify the interface of the specified breakpoint conditions. */ void (*create_breakpoint_hook) (struct breakpoint * bpt); void (*delete_breakpoint_hook) (struct breakpoint * bpt); void (*modify_breakpoint_hook) (struct breakpoint * bpt); /* Called as appropriate to notify the interface that we have attached to or detached from an already running process. */ void (*attach_hook) (void); void (*detach_hook) (void); /* Called during long calculations to allow GUI to repair window damage, and to check for stop buttons, etc... */ void (*interactive_hook) (void); /* Called when the registers have changed, as a hint to a GUI to minimize window update. */ void (*registers_changed_hook) (void); /* Tell the GUI someone changed the register REGNO. -1 means that the caller does not know which register changed or that several registers have changed (see value_assign). */ void (*register_changed_hook) (int regno); /* Tell the GUI someone changed LEN bytes of memory at ADDR */ void (*memory_changed_hook) (CORE_ADDR addr, int len); /* Called when going to wait for the target. Usually allows the GUI to run while waiting for target events. */ ptid_t (*target_wait_hook) (ptid_t ptid, struct target_waitstatus * status); /* Used by UI as a wrapper around command execution. May do various things like enabling/disabling buttons, etc... */ void (*call_command_hook) (struct cmd_list_element * c, char *cmd, int from_tty); /* Called after a `set' command has finished. Is only run if the `set' command succeeded. */ void (*set_hook) (struct cmd_list_element * c); /* Called when the current thread changes. Argument is thread id. */ void (*context_hook) (int id); /* Takes control from error (). Typically used to prevent longjmps out of the middle of the GUI. Usually used in conjunction with a catch routine. */ NORETURN void (*error_hook) (void) ATTR_NORETURN; /* One should use catch_errors rather than manipulating these directly. */ #if defined(HAVE_SIGSETJMP) #define SIGJMP_BUF sigjmp_buf #define SIGSETJMP(buf) sigsetjmp((buf), 1) #define SIGLONGJMP(buf,val) siglongjmp((buf), (val)) #else #define SIGJMP_BUF jmp_buf #define SIGSETJMP(buf) setjmp(buf) #define SIGLONGJMP(buf,val) longjmp((buf), (val)) #endif /* Where to go for throw_exception(). */ static SIGJMP_BUF *catch_return; /* Return for reason REASON to the nearest containing catch_errors(). */ NORETURN void throw_exception (enum return_reason reason) { quit_flag = 0; immediate_quit = 0; /* Perhaps it would be cleaner to do this via the cleanup chain (not sure I can think of a reason why that is vital, though). */ bpstat_clear_actions (stop_bpstat); /* Clear queued breakpoint commands */ disable_current_display (); do_cleanups (ALL_CLEANUPS); if (event_loop_p && target_can_async_p () && !target_executing) do_exec_cleanups (ALL_CLEANUPS); if (event_loop_p && sync_execution) do_exec_error_cleanups (ALL_CLEANUPS); if (annotation_level > 1) switch (reason) { case RETURN_QUIT: annotate_quit (); break; case RETURN_ERROR: annotate_error (); break; } /* Jump to the containing catch_errors() call, communicating REASON to that call via setjmp's return value. Note that REASON can't be zero, by definition in defs.h. */ (NORETURN void) SIGLONGJMP (*catch_return, (int) reason); } /* Call FUNC() with args FUNC_UIOUT and FUNC_ARGS, catching any errors. Set FUNC_CAUGHT to an ``enum return_reason'' if the function is aborted (using throw_exception() or zero if the function returns normally. Set FUNC_VAL to the value returned by the function or 0 if the function was aborted. Must not be called with immediate_quit in effect (bad things might happen, say we got a signal in the middle of a memcpy to quit_return). This is an OK restriction; with very few exceptions immediate_quit can be replaced by judicious use of QUIT. MASK specifies what to catch; it is normally set to RETURN_MASK_ALL, if for no other reason than that the code which calls catch_errors might not be set up to deal with a quit which isn't caught. But if the code can deal with it, it generally should be RETURN_MASK_ERROR, unless for some reason it is more useful to abort only the portion of the operation inside the catch_errors. Note that quit should return to the command line fairly quickly, even if some further processing is being done. */ /* MAYBE: cagney/1999-11-05: catch_errors() in conjunction with error() et.al. could maintain a set of flags that indicate the the current state of each of the longjmp buffers. This would give the longjmp code the chance to detect a longjmp botch (before it gets to longjmperror()). Prior to 1999-11-05 this wasn't possible as code also randomly used a SET_TOP_LEVEL macro that directly initialize the longjmp buffers. */ /* MAYBE: cagney/1999-11-05: Should the catch_errors and cleanups code be consolidated into a single file instead of being distributed between utils.c and top.c? */ static void catcher (catch_exceptions_ftype *func, struct ui_out *func_uiout, void *func_args, int *func_val, enum return_reason *func_caught, char *errstring, char **gdberrmsg, return_mask mask) { SIGJMP_BUF *saved_catch; SIGJMP_BUF catch; struct cleanup *saved_cleanup_chain; char *saved_error_pre_print; char *saved_quit_pre_print; struct ui_out *saved_uiout; /* Return value from SIGSETJMP(): enum return_reason if error or quit caught, 0 otherwise. */ int caught; /* Return value from FUNC(): Hopefully non-zero. Explicitly set to zero if an error quit was caught. */ int val; /* Override error/quit messages during FUNC. */ saved_error_pre_print = error_pre_print; saved_quit_pre_print = quit_pre_print; if (mask & RETURN_MASK_ERROR) error_pre_print = errstring; if (mask & RETURN_MASK_QUIT) quit_pre_print = errstring; /* Override the global ``struct ui_out'' builder. */ saved_uiout = uiout; uiout = func_uiout; /* Prevent error/quit during FUNC from calling cleanups established prior to here. */ saved_cleanup_chain = save_cleanups (); /* Call FUNC, catching error/quit events. */ saved_catch = catch_return; catch_return = &catch; caught = SIGSETJMP (catch); if (!caught) val = (*func) (func_uiout, func_args); else { val = 0; /* If caller wants a copy of the low-level error message, make one. This is used in the case of a silent error whereby the caller may optionally want to issue the message. */ if (gdberrmsg) *gdberrmsg = error_last_message (); } catch_return = saved_catch; /* FIXME: cagney/1999-11-05: A correct FUNC implementation will clean things up (restoring the cleanup chain) to the state they were just prior to the call. Unfortunately, many FUNC's are not that well behaved. This could be fixed by adding either a do_cleanups call (to cover the problem) or an assertion check to detect bad FUNCs code. */ /* Restore the cleanup chain, the error/quit messages, and the uiout builder, to their original states. */ restore_cleanups (saved_cleanup_chain); uiout = saved_uiout; if (mask & RETURN_MASK_QUIT) quit_pre_print = saved_quit_pre_print; if (mask & RETURN_MASK_ERROR) error_pre_print = saved_error_pre_print; /* Return normally if no error/quit event occurred or this catcher can handle this exception. The caller analyses the func return values. */ if (!caught || (mask & RETURN_MASK (caught))) { *func_val = val; *func_caught = caught; return; } /* The caller didn't request that the event be caught, relay the event to the next containing catch_errors(). */ throw_exception (caught); } int catch_exceptions (struct ui_out *uiout, catch_exceptions_ftype *func, void *func_args, char *errstring, return_mask mask) { int val; enum return_reason caught; catcher (func, uiout, func_args, &val, &caught, errstring, NULL, mask); gdb_assert (val >= 0); gdb_assert (caught <= 0); if (caught < 0) return caught; return val; } int catch_exceptions_with_msg (struct ui_out *uiout, catch_exceptions_ftype *func, void *func_args, char *errstring, char **gdberrmsg, return_mask mask) { int val; enum return_reason caught; catcher (func, uiout, func_args, &val, &caught, errstring, gdberrmsg, mask); gdb_assert (val >= 0); gdb_assert (caught <= 0); if (caught < 0) return caught; return val; } struct catch_errors_args { catch_errors_ftype *func; void *func_args; }; static int do_catch_errors (struct ui_out *uiout, void *data) { struct catch_errors_args *args = data; return args->func (args->func_args); } int catch_errors (catch_errors_ftype *func, void *func_args, char *errstring, return_mask mask) { int val; enum return_reason caught; struct catch_errors_args args; args.func = func; args.func_args = func_args; catcher (do_catch_errors, uiout, &args, &val, &caught, errstring, NULL, mask); if (caught != 0) return 0; return val; } struct captured_command_args { catch_command_errors_ftype *command; char *arg; int from_tty; }; static int do_captured_command (void *data) { struct captured_command_args *context = data; context->command (context->arg, context->from_tty); /* FIXME: cagney/1999-11-07: Technically this do_cleanups() call isn't needed. Instead an assertion check could be made that simply confirmed that the called function correctly cleaned up after itself. Unfortunately, old code (prior to 1999-11-04) in main.c was calling SET_TOP_LEVEL(), calling the command function, and then *always* calling do_cleanups(). For the moment we remain ``bug compatible'' with that old code.. */ do_cleanups (ALL_CLEANUPS); return 1; } int catch_command_errors (catch_command_errors_ftype * command, char *arg, int from_tty, return_mask mask) { struct captured_command_args args; args.command = command; args.arg = arg; args.from_tty = from_tty; return catch_errors (do_captured_command, &args, "", mask); } /* Handler for SIGHUP. */ #ifdef SIGHUP /* Just a little helper function for disconnect(). */ /* NOTE 1999-04-29: This function will be static again, once we modify gdb to use the event loop as the default command loop and we merge event-top.c into this file, top.c */ /* static */ int quit_cover (void *s) { caution = 0; /* Throw caution to the wind -- we're exiting. This prevents asking the user dumb questions. */ quit_command ((char *) 0, 0); return 0; } static void disconnect (int signo) { catch_errors (quit_cover, NULL, "Could not kill the program being debugged", RETURN_MASK_ALL); signal (SIGHUP, SIG_DFL); kill (getpid (), SIGHUP); } #endif /* defined SIGHUP */ /* Line number we are currently in in a file which is being sourced. */ /* NOTE 1999-04-29: This variable will be static again, once we modify gdb to use the event loop as the default command loop and we merge event-top.c into this file, top.c */ /* static */ int source_line_number; /* Name of the file we are sourcing. */ /* NOTE 1999-04-29: This variable will be static again, once we modify gdb to use the event loop as the default command loop and we merge event-top.c into this file, top.c */ /* static */ char *source_file_name; /* Buffer containing the error_pre_print used by the source stuff. Malloc'd. */ /* NOTE 1999-04-29: This variable will be static again, once we modify gdb to use the event loop as the default command loop and we merge event-top.c into this file, top.c */ /* static */ char *source_error; static int source_error_allocated; /* Something to glom on to the start of error_pre_print if source_file_name is set. */ /* NOTE 1999-04-29: This variable will be static again, once we modify gdb to use the event loop as the default command loop and we merge event-top.c into this file, top.c */ /* static */ char *source_pre_error; /* Clean up on error during a "source" command (or execution of a user-defined command). */ void do_restore_instream_cleanup (void *stream) { /* Restore the previous input stream. */ instream = stream; } /* Read commands from STREAM. */ void read_command_file (FILE *stream) { struct cleanup *cleanups; cleanups = make_cleanup (do_restore_instream_cleanup, instream); instream = stream; command_loop (); do_cleanups (cleanups); } void (*pre_init_ui_hook) (void); #ifdef __MSDOS__ void do_chdir_cleanup (void *old_dir) { chdir (old_dir); xfree (old_dir); } #endif /* Execute the line P as a command. Pass FROM_TTY as second argument to the defining function. */ void execute_command (char *p, int from_tty) { struct cmd_list_element *c; enum language flang; static int warned = 0; char *line; free_all_values (); /* Force cleanup of any alloca areas if using C alloca instead of a builtin alloca. */ alloca (0); /* This can happen when command_line_input hits end of file. */ if (p == NULL) return; serial_log_command (p); while (*p == ' ' || *p == '\t') p++; if (*p) { char *arg; line = p; c = lookup_cmd (&p, cmdlist, "", 0, 1); /* If the target is running, we allow only a limited set of commands. */ if (event_loop_p && target_can_async_p () && target_executing) if (strcmp (c->name, "help") != 0 && strcmp (c->name, "pwd") != 0 && strcmp (c->name, "show") != 0 && strcmp (c->name, "stop") != 0) error ("Cannot execute this command while the target is running."); /* Pass null arg rather than an empty one. */ arg = *p ? p : 0; /* FIXME: cagney/2002-02-02: The c->type test is pretty dodgy while the is_complete_command(cfunc) test is just plain bogus. They should both be replaced by a test of the form c->strip_trailing_white_space_p. */ /* NOTE: cagney/2002-02-02: The function.cfunc in the below can't be replaced with func. This is because it is the cfunc, and not the func, that has the value that the is_complete_command hack is testing for. */ /* Clear off trailing whitespace, except for set and complete command. */ if (arg && c->type != set_cmd && !is_complete_command (c)) { p = arg + strlen (arg) - 1; while (p >= arg && (*p == ' ' || *p == '\t')) p--; *(p + 1) = '\0'; } /* If this command has been pre-hooked, run the hook first. */ execute_cmd_pre_hook (c); if (c->flags & DEPRECATED_WARN_USER) deprecated_cmd_warning (&line); if (c->class == class_user) execute_user_command (c, arg); else if (c->type == set_cmd || c->type == show_cmd) do_setshow_command (arg, from_tty & caution, c); else if (!cmd_func_p (c)) error ("That is not a command, just a help topic."); else if (call_command_hook) call_command_hook (c, arg, from_tty & caution); else cmd_func (c, arg, from_tty & caution); /* If this command has been post-hooked, run the hook last. */ execute_cmd_post_hook (c); } /* Tell the user if the language has changed (except first time). */ if (current_language != expected_language) { if (language_mode == language_mode_auto) { language_info (1); /* Print what changed. */ } warned = 0; } /* Warn the user if the working language does not match the language of the current frame. Only warn the user if we are actually running the program, i.e. there is a stack. */ /* FIXME: This should be cacheing the frame and only running when the frame changes. */ if (target_has_stack) { flang = get_frame_language (); if (!warned && flang != language_unknown && flang != current_language->la_language) { printf_filtered ("%s\n", lang_frame_mismatch_warn); warned = 1; } } } /* Read commands from `instream' and execute them until end of file or error reading instream. */ void command_loop (void) { struct cleanup *old_chain; char *command; int stdin_is_tty = ISATTY (stdin); long time_at_cmd_start; #ifdef HAVE_SBRK long space_at_cmd_start = 0; #endif extern int display_time; extern int display_space; while (instream && !feof (instream)) { if (window_hook && instream == stdin) (*window_hook) (instream, get_prompt ()); quit_flag = 0; if (instream == stdin && stdin_is_tty) reinitialize_more_filter (); old_chain = make_cleanup (null_cleanup, 0); /* Get a command-line. This calls the readline package. */ command = command_line_input (instream == stdin ? get_prompt () : (char *) NULL, instream == stdin, "prompt"); if (command == 0) return; time_at_cmd_start = get_run_time (); if (display_space) { #ifdef HAVE_SBRK char *lim = (char *) sbrk (0); space_at_cmd_start = lim - lim_at_start; #endif } execute_command (command, instream == stdin); /* Do any commands attached to breakpoint we stopped at. */ bpstat_do_actions (&stop_bpstat); do_cleanups (old_chain); if (display_time) { long cmd_time = get_run_time () - time_at_cmd_start; printf_unfiltered ("Command execution time: %ld.%06ld\n", cmd_time / 1000000, cmd_time % 1000000); } if (display_space) { #ifdef HAVE_SBRK char *lim = (char *) sbrk (0); long space_now = lim - lim_at_start; long space_diff = space_now - space_at_cmd_start; printf_unfiltered ("Space used: %ld (%c%ld for this command)\n", space_now, (space_diff >= 0 ? '+' : '-'), space_diff); #endif } } } /* Read commands from `instream' and execute them until end of file or error reading instream. This command loop doesnt care about any such things as displaying time and space usage. If the user asks for those, they won't work. */ void simplified_command_loop (char *(*read_input_func) (char *), void (*execute_command_func) (char *, int)) { struct cleanup *old_chain; char *command; int stdin_is_tty = ISATTY (stdin); while (instream && !feof (instream)) { quit_flag = 0; if (instream == stdin && stdin_is_tty) reinitialize_more_filter (); old_chain = make_cleanup (null_cleanup, 0); /* Get a command-line. */ command = (*read_input_func) (instream == stdin ? get_prompt () : (char *) NULL); if (command == 0) return; (*execute_command_func) (command, instream == stdin); /* Do any commands attached to breakpoint we stopped at. */ bpstat_do_actions (&stop_bpstat); do_cleanups (old_chain); } } /* Commands call this if they do not want to be repeated by null lines. */ void dont_repeat (void) { if (server_command) return; /* If we aren't reading from standard input, we are saving the last thing read from stdin in line and don't want to delete it. Null lines won't repeat here in any case. */ if (instream == stdin) *line = 0; } /* Read a line from the stream "instream" without command line editing. It prints PROMPT_ARG once at the start. Action is compatible with "readline", e.g. space for the result is malloc'd and should be freed by the caller. A NULL return means end of file. */ char * gdb_readline (char *prompt_arg) { int c; char *result; int input_index = 0; int result_size = 80; if (prompt_arg) { /* Don't use a _filtered function here. It causes the assumed character position to be off, since the newline we read from the user is not accounted for. */ fputs_unfiltered (prompt_arg, gdb_stdout); gdb_flush (gdb_stdout); } result = (char *) xmalloc (result_size); while (1) { /* Read from stdin if we are executing a user defined command. This is the right thing for prompt_for_continue, at least. */ c = fgetc (instream ? instream : stdin); if (c == EOF) { if (input_index > 0) /* The last line does not end with a newline. Return it, and if we are called again fgetc will still return EOF and we'll return NULL then. */ break; xfree (result); return NULL; } if (c == '\n') #ifndef CRLF_SOURCE_FILES break; #else { if (input_index > 0 && result[input_index - 1] == '\r') input_index--; break; } #endif result[input_index++] = c; while (input_index >= result_size) { result_size *= 2; result = (char *) xrealloc (result, result_size); } } result[input_index++] = '\0'; return result; } /* Variables which control command line editing and history substitution. These variables are given default values at the end of this file. */ static int command_editing_p; /* NOTE 1999-04-29: This variable will be static again, once we modify gdb to use the event loop as the default command loop and we merge event-top.c into this file, top.c */ /* static */ int history_expansion_p; static int write_history_p; static int history_size; static char *history_filename; /* This is like readline(), but it has some gdb-specific behavior. gdb can use readline in both the synchronous and async modes during a single gdb invocation. At the ordinary top-level prompt we might be using the async readline. That means we can't use rl_pre_input_hook, since it doesn't work properly in async mode. However, for a secondary prompt (" >", such as occurs during a `define'), gdb just calls readline() directly, running it in synchronous mode. So for operate-and-get-next to work in this situation, we have to switch the hooks around. That is what gdb_readline_wrapper is for. */ char * gdb_readline_wrapper (char *prompt) { /* Set the hook that works in this case. */ if (event_loop_p && after_char_processing_hook) { rl_pre_input_hook = (Function *) after_char_processing_hook; after_char_processing_hook = NULL; } return readline (prompt); } #ifdef STOP_SIGNAL static void stop_sig (int signo) { #if STOP_SIGNAL == SIGTSTP signal (SIGTSTP, SIG_DFL); #if HAVE_SIGPROCMASK { sigset_t zero; sigemptyset (&zero); sigprocmask (SIG_SETMASK, &zero, 0); } #elif HAVE_SIGSETMASK sigsetmask (0); #endif kill (getpid (), SIGTSTP); signal (SIGTSTP, stop_sig); #else signal (STOP_SIGNAL, stop_sig); #endif printf_unfiltered ("%s", get_prompt ()); gdb_flush (gdb_stdout); /* Forget about any previous command -- null line now will do nothing. */ dont_repeat (); } #endif /* STOP_SIGNAL */ /* Initialize signal handlers. */ static void float_handler (int signo) { /* This message is based on ANSI C, section 4.7. Note that integer divide by zero causes this, so "float" is a misnomer. */ signal (SIGFPE, float_handler); error ("Erroneous arithmetic operation."); } static void do_nothing (int signo) { /* Under System V the default disposition of a signal is reinstated after the signal is caught and delivered to an application process. On such systems one must restore the replacement signal handler if one wishes to continue handling the signal in one's program. On BSD systems this is not needed but it is harmless, and it simplifies the code to just do it unconditionally. */ signal (signo, do_nothing); } static void init_signals (void) { signal (SIGINT, request_quit); /* If SIGTRAP was set to SIG_IGN, then the SIG_IGN will get passed to the inferior and breakpoints will be ignored. */ #ifdef SIGTRAP signal (SIGTRAP, SIG_DFL); #endif /* If we initialize SIGQUIT to SIG_IGN, then the SIG_IGN will get passed to the inferior, which we don't want. It would be possible to do a "signal (SIGQUIT, SIG_DFL)" after we fork, but on BSD4.3 systems using vfork, that can affect the GDB process as well as the inferior (the signal handling tables might be in memory, shared between the two). Since we establish a handler for SIGQUIT, when we call exec it will set the signal to SIG_DFL for us. */ signal (SIGQUIT, do_nothing); #ifdef SIGHUP if (signal (SIGHUP, do_nothing) != SIG_IGN) signal (SIGHUP, disconnect); #endif signal (SIGFPE, float_handler); #if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) signal (SIGWINCH, SIGWINCH_HANDLER); #endif } /* The current saved history number from operate-and-get-next. This is -1 if not valid. */ static int operate_saved_history = -1; /* This is put on the appropriate hook and helps operate-and-get-next do its work. */ static void gdb_rl_operate_and_get_next_completion (void) { int delta = where_history () - operate_saved_history; /* The `key' argument to rl_get_previous_history is ignored. */ rl_get_previous_history (delta, 0); operate_saved_history = -1; /* readline doesn't automatically update the display for us. */ rl_redisplay (); after_char_processing_hook = NULL; rl_pre_input_hook = NULL; } /* This is a gdb-local readline command handler. It accepts the current command line (like RET does) and, if this command was taken from the history, arranges for the next command in the history to appear on the command line when the prompt returns. We ignore the arguments. */ static int gdb_rl_operate_and_get_next (int count, int key) { int where; if (event_loop_p) { /* Use the async hook. */ after_char_processing_hook = gdb_rl_operate_and_get_next_completion; } else { /* This hook only works correctly when we are using the synchronous readline. */ rl_pre_input_hook = (Function *) gdb_rl_operate_and_get_next_completion; } /* Find the current line, and find the next line to use. */ where = where_history(); /* FIXME: kettenis/20020817: max_input_history is renamed into history_max_entries in readline-4.2. When we do a new readline import, we should probably change it here too, even though readline maintains backwards compatibility for now by still defining max_input_history. */ if ((history_is_stifled () && (history_length >= max_input_history)) || (where >= history_length - 1)) operate_saved_history = where; else operate_saved_history = where + 1; return rl_newline (1, key); } /* Read one line from the command input stream `instream' into the local static buffer `linebuffer' (whose current length is `linelength'). The buffer is made bigger as necessary. Returns the address of the start of the line. NULL is returned for end of file. *If* the instream == stdin & stdin is a terminal, the line read is copied into the file line saver (global var char *line, length linesize) so that it can be duplicated. This routine either uses fancy command line editing or simple input as the user has requested. */ char * command_line_input (char *prompt_arg, int repeat, char *annotation_suffix) { static char *linebuffer = 0; static unsigned linelength = 0; char *p; char *p1; char *rl; char *local_prompt = prompt_arg; char *nline; char got_eof = 0; /* The annotation suffix must be non-NULL. */ if (annotation_suffix == NULL) annotation_suffix = ""; if (annotation_level > 1 && instream == stdin) { local_prompt = alloca ((prompt_arg == NULL ? 0 : strlen (prompt_arg)) + strlen (annotation_suffix) + 40); if (prompt_arg == NULL) local_prompt[0] = '\0'; else strcpy (local_prompt, prompt_arg); strcat (local_prompt, "\n\032\032"); strcat (local_prompt, annotation_suffix); strcat (local_prompt, "\n"); } if (linebuffer == 0) { linelength = 80; linebuffer = (char *) xmalloc (linelength); } p = linebuffer; /* Control-C quits instantly if typed while in this loop since it should not wait until the user types a newline. */ immediate_quit++; #ifdef STOP_SIGNAL if (job_control) { if (event_loop_p) signal (STOP_SIGNAL, handle_stop_sig); else signal (STOP_SIGNAL, stop_sig); } #endif 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 (source_file_name != NULL) { ++source_line_number; sprintf (source_error, "%s%s:%d: Error in sourced command file:\n", source_pre_error, source_file_name, source_line_number); error_pre_print = source_error; } if (annotation_level > 1 && instream == stdin) { puts_unfiltered ("\n\032\032pre-"); puts_unfiltered (annotation_suffix); puts_unfiltered ("\n"); } /* Don't use fancy stuff if not talking to stdin. */ if (readline_hook && instream == NULL) { rl = (*readline_hook) (local_prompt); } else if (command_editing_p && instream == stdin && ISATTY (instream)) { rl = gdb_readline_wrapper (local_prompt); } else { rl = gdb_readline (local_prompt); } if (annotation_level > 1 && instream == stdin) { puts_unfiltered ("\n\032\032post-"); puts_unfiltered (annotation_suffix); puts_unfiltered ("\n"); } if (!rl || rl == (char *) EOF) { got_eof = 1; break; } if (strlen (rl) + 1 + (p - linebuffer) > linelength) { linelength = strlen (rl) + 1 + (p - linebuffer); nline = (char *) xrealloc (linebuffer, linelength); p += nline - linebuffer; linebuffer = nline; } p1 = rl; /* Copy line. Don't copy null at end. (Leaves line alone if this was just a newline) */ while (*p1) *p++ = *p1++; xfree (rl); /* Allocated in readline. */ if (p == linebuffer || *(p - 1) != '\\') break; p--; /* Put on top of '\'. */ local_prompt = (char *) 0; } #ifdef STOP_SIGNAL if (job_control) signal (STOP_SIGNAL, SIG_DFL); #endif immediate_quit--; if (got_eof) return NULL; #define SERVER_COMMAND_LENGTH 7 server_command = (p - linebuffer > SERVER_COMMAND_LENGTH) && strncmp (linebuffer, "server ", SERVER_COMMAND_LENGTH) == 0; if (server_command) { /* Note that we don't set `line'. Between this and the check in dont_repeat, this insures that repeating will still do the right thing. */ *p = '\0'; return linebuffer + SERVER_COMMAND_LENGTH; } /* Do history expansion if that is wished. */ if (history_expansion_p && instream == stdin && ISATTY (instream)) { char *history_value; int expanded; *p = '\0'; /* Insert null now. */ expanded = history_expand (linebuffer, &history_value); if (expanded) { /* Print the changes. */ printf_unfiltered ("%s\n", history_value); /* If there was an error, call this function again. */ if (expanded < 0) { xfree (history_value); return command_line_input (prompt_arg, repeat, annotation_suffix); } if (strlen (history_value) > linelength) { linelength = strlen (history_value) + 1; linebuffer = (char *) xrealloc (linebuffer, linelength); } strcpy (linebuffer, history_value); p = linebuffer + strlen (linebuffer); xfree (history_value); } } /* If we just got an empty line, and that is supposed to repeat the previous command, return the value in the global buffer. */ if (repeat && p == linebuffer) return line; for (p1 = linebuffer; *p1 == ' ' || *p1 == '\t'; p1++); if (repeat && !*p1) return line; *p = 0; /* Add line to history if appropriate. */ if (instream == stdin && ISATTY (stdin) && *linebuffer) add_history (linebuffer); /* Note: lines consisting solely of comments are added to the command history. This is useful when you type a command, and then realize you don't want to execute it quite yet. You can comment out the command and then later fetch it from the value history and remove the '#'. The kill ring is probably better, but some people are in the habit of commenting things out. */ if (*p1 == '#') *p1 = '\0'; /* Found a comment. */ /* Save into global buffer if appropriate. */ if (repeat) { if (linelength > linesize) { line = xrealloc (line, linelength); linesize = linelength; } strcpy (line, linebuffer); return line; } return linebuffer; } /* Print the GDB banner. */ void print_gdb_version (struct ui_file *stream) { /* From GNU coding standards, first line is meant to be easy for a program to parse, and is just canonical program name and version number, which starts after last space. */ fprintf_filtered (stream, "GNU gdb %s\n", version); /* Second line is a copyright notice. */ fprintf_filtered (stream, "Copyright 2004 Free Software Foundation, Inc.\n"); /* Following the copyright is a brief statement that the program is free software, that users are free to copy and change it on certain conditions, that it is covered by the GNU GPL, and that there is no warranty. */ fprintf_filtered (stream, "\ GDB is free software, covered by the GNU General Public License, and you are\n\ welcome to change it and/or distribute copies of it under certain conditions.\n\ Type \"show copying\" to see the conditions.\n\ There is absolutely no warranty for GDB. Type \"show warranty\" for details.\n"); /* After the required info we print the configuration information. */ fprintf_filtered (stream, "This GDB was configured as \""); if (strcmp (host_name, target_name) != 0) { fprintf_filtered (stream, "--host=%s --target=%s", host_name, target_name); } else { fprintf_filtered (stream, "%s", host_name); } fprintf_filtered (stream, "\"."); } /* get_prompt: access method for the GDB prompt string. */ char * get_prompt (void) { if (event_loop_p) return PROMPT (0); else return gdb_prompt_string; } void set_prompt (char *s) { /* ??rehrauer: I don't know why this fails, since it looks as though assignments to prompt are wrapped in calls to savestring... if (prompt != NULL) xfree (prompt); */ if (event_loop_p) PROMPT (0) = savestring (s, strlen (s)); else gdb_prompt_string = savestring (s, strlen (s)); } /* If necessary, make the user confirm that we should quit. Return non-zero if we should quit, zero if we shouldn't. */ int quit_confirm (void) { if (! ptid_equal (inferior_ptid, null_ptid) && target_has_execution) { char *s; /* This is something of a hack. But there's no reliable way to see if a GUI is running. The `use_windows' variable doesn't cut it. */ if (init_ui_hook) s = "A debugging session is active.\nDo you still want to close the debugger?"; else if (attach_flag) s = "The program is running. Quit anyway (and detach it)? "; else s = "The program is running. Exit anyway? "; if (!query ("%s", s)) return 0; } return 1; } /* Helper routine for quit_force that requires error handling. */ struct qt_args { char *args; int from_tty; }; static int quit_target (void *arg) { struct qt_args *qt = (struct qt_args *)arg; if (! ptid_equal (inferior_ptid, null_ptid) && target_has_execution) { if (attach_flag) target_detach (qt->args, qt->from_tty); else target_kill (); } /* UDI wants this, to kill the TIP. */ target_close (¤t_target, 1); /* Save the history information if it is appropriate to do so. */ if (write_history_p && history_filename) write_history (history_filename); do_final_cleanups (ALL_CLEANUPS); /* Do any final cleanups before exiting */ return 0; } /* Quit without asking for confirmation. */ void quit_force (char *args, int from_tty) { int exit_code = 0; struct qt_args qt; /* An optional expression may be used to cause gdb to terminate with the value of that expression. */ if (args) { struct value *val = parse_and_eval (args); exit_code = (int) value_as_long (val); } qt.args = args; qt.from_tty = from_tty; /* We want to handle any quit errors and exit regardless. */ catch_errors (quit_target, &qt, "Quitting: ", RETURN_MASK_ALL); exit (exit_code); } /* Returns whether GDB is running on a terminal and whether the user desires that questions be asked of them on that terminal. */ int input_from_terminal_p (void) { return gdb_has_a_terminal () && (instream == stdin) & caution; } static void dont_repeat_command (char *ignored, int from_tty) { *line = 0; /* Can't call dont_repeat here because we're not necessarily reading from stdin. */ } /* Functions to manipulate command line editing control variables. */ /* Number of commands to print in each call to show_commands. */ #define Hist_print 10 void show_commands (char *args, int from_tty) { /* Index for history commands. Relative to history_base. */ int offset; /* Number of the history entry which we are planning to display next. Relative to history_base. */ static int num = 0; /* The first command in the history which doesn't exist (i.e. one more than the number of the last command). Relative to history_base. */ int hist_len; /* Print out some of the commands from the command history. */ /* First determine the length of the history list. */ hist_len = history_size; for (offset = 0; offset < history_size; offset++) { if (!history_get (history_base + offset)) { hist_len = offset; break; } } if (args) { if (args[0] == '+' && args[1] == '\0') /* "info editing +" should print from the stored position. */ ; else /* "info editing " should print around command number . */ num = (parse_and_eval_long (args) - history_base) - Hist_print / 2; } /* "show commands" means print the last Hist_print commands. */ else { num = hist_len - Hist_print; } if (num < 0) num = 0; /* If there are at least Hist_print commands, we want to display the last Hist_print rather than, say, the last 6. */ if (hist_len - num < Hist_print) { num = hist_len - Hist_print; if (num < 0) num = 0; } for (offset = num; offset < num + Hist_print && offset < hist_len; offset++) { printf_filtered ("%5d %s\n", history_base + offset, (history_get (history_base + offset))->line); } /* The next command we want to display is the next one that we haven't displayed yet. */ num += Hist_print; /* If the user repeats this command with return, it should do what "show commands +" does. This is unnecessary if arg is null, because "show commands +" is not useful after "show commands". */ if (from_tty && args) { args[0] = '+'; args[1] = '\0'; } } /* Called by do_setshow_command. */ static void set_history_size_command (char *args, int from_tty, struct cmd_list_element *c) { if (history_size == INT_MAX) unstifle_history (); else if (history_size >= 0) stifle_history (history_size); else { history_size = INT_MAX; error ("History size must be non-negative"); } } void set_history (char *args, int from_tty) { printf_unfiltered ("\"set history\" must be followed by the name of a history subcommand.\n"); help_list (sethistlist, "set history ", -1, gdb_stdout); } void show_history (char *args, int from_tty) { cmd_show_list (showhistlist, from_tty, ""); } int info_verbose = 0; /* Default verbose msgs off */ /* Called by do_setshow_command. An elaborate joke. */ void set_verbose (char *args, int from_tty, struct cmd_list_element *c) { char *cmdname = "verbose"; struct cmd_list_element *showcmd; showcmd = lookup_cmd_1 (&cmdname, showlist, NULL, 1); if (info_verbose) { c->doc = "Set verbose printing of informational messages."; showcmd->doc = "Show verbose printing of informational messages."; } else { c->doc = "Set verbosity."; showcmd->doc = "Show verbosity."; } } /* Init the history buffer. Note that we are called after the init file(s) * have been read so that the user can change the history file via his * .gdbinit file (for instance). The GDBHISTFILE environment variable * overrides all of this. */ void init_history (void) { char *tmpenv; tmpenv = getenv ("HISTSIZE"); if (tmpenv) history_size = atoi (tmpenv); else if (!history_size) history_size = 256; stifle_history (history_size); tmpenv = getenv ("GDBHISTFILE"); if (tmpenv) history_filename = savestring (tmpenv, strlen (tmpenv)); else if (!history_filename) { /* We include the current directory so that if the user changes directories the file written will be the same as the one that was read. */ #ifdef __MSDOS__ /* No leading dots in file names are allowed on MSDOS. */ history_filename = concat (current_directory, "/_gdb_history", NULL); #else history_filename = concat (current_directory, "/.gdb_history", NULL); #endif } read_history (history_filename); } static void init_main (void) { struct cmd_list_element *c; /* If we are running the asynchronous version, we initialize the prompts differently. */ if (!event_loop_p) { gdb_prompt_string = savestring (DEFAULT_PROMPT, strlen (DEFAULT_PROMPT)); } else { /* initialize the prompt stack to a simple "(gdb) " prompt or to whatever the DEFAULT_PROMPT is. */ the_prompts.top = 0; PREFIX (0) = ""; PROMPT (0) = savestring (DEFAULT_PROMPT, strlen (DEFAULT_PROMPT)); SUFFIX (0) = ""; /* Set things up for annotation_level > 1, if the user ever decides to use it. */ async_annotation_suffix = "prompt"; /* Set the variable associated with the setshow prompt command. */ new_async_prompt = savestring (PROMPT (0), strlen (PROMPT (0))); /* If gdb was started with --annotate=2, this is equivalent to the user entering the command 'set annotate 2' at the gdb prompt, so we need to do extra processing. */ if (annotation_level > 1) set_async_annotation_level (NULL, 0, NULL); } /* Set the important stuff up for command editing. */ command_editing_p = 1; history_expansion_p = 0; write_history_p = 0; /* Setup important stuff for command line editing. */ rl_completion_entry_function = readline_line_completion_function; rl_completer_word_break_characters = default_word_break_characters (); rl_completer_quote_characters = get_gdb_completer_quote_characters (); rl_readline_name = "gdb"; rl_terminal_name = getenv ("TERM"); /* The name for this defun comes from Bash, where it originated. 15 is Control-o, the same binding this function has in Bash. */ rl_add_defun ("operate-and-get-next", gdb_rl_operate_and_get_next, 15); /* The set prompt command is different depending whether or not the async version is run. NOTE: this difference is going to disappear as we make the event loop be the default engine of gdb. */ if (!event_loop_p) { add_show_from_set (add_set_cmd ("prompt", class_support, var_string, (char *) &gdb_prompt_string, "Set gdb's prompt", &setlist), &showlist); } else { c = add_set_cmd ("prompt", class_support, var_string, (char *) &new_async_prompt, "Set gdb's prompt", &setlist); add_show_from_set (c, &showlist); set_cmd_sfunc (c, set_async_prompt); } add_com ("dont-repeat", class_support, dont_repeat_command, "Don't repeat this command.\n\ Primarily used inside of user-defined commands that should not be repeated when\n\ hitting return."); /* The set editing command is different depending whether or not the async version is run. NOTE: this difference is going to disappear as we make the event loop be the default engine of gdb. */ if (!event_loop_p) { add_show_from_set (add_set_cmd ("editing", class_support, var_boolean, (char *) &command_editing_p, "Set editing of command lines as they are typed.\n\ Use \"on\" to enable the editing, and \"off\" to disable it.\n\ Without an argument, command line editing is enabled. To edit, use\n\ EMACS-like or VI-like commands like control-P or ESC.", &setlist), &showlist); } else { c = add_set_cmd ("editing", class_support, var_boolean, (char *) &async_command_editing_p, "Set editing of command lines as they are typed.\n\ Use \"on\" to enable the editing, and \"off\" to disable it.\n\ Without an argument, command line editing is enabled. To edit, use\n\ EMACS-like or VI-like commands like control-P or ESC.", &setlist); add_show_from_set (c, &showlist); set_cmd_sfunc (c, set_async_editing_command); } add_show_from_set (add_set_cmd ("save", no_class, var_boolean, (char *) &write_history_p, "Set saving of the history record on exit.\n\ Use \"on\" to enable the saving, and \"off\" to disable it.\n\ Without an argument, saving is enabled.", &sethistlist), &showhistlist); c = add_set_cmd ("size", no_class, var_integer, (char *) &history_size, "Set the size of the command history,\n\ ie. the number of previous commands to keep a record of.", &sethistlist); add_show_from_set (c, &showhistlist); set_cmd_sfunc (c, set_history_size_command); c = add_set_cmd ("filename", no_class, var_filename, (char *) &history_filename, "Set the filename in which to record the command history\n\ (the list of previous commands of which a record is kept).", &sethistlist); set_cmd_completer (c, filename_completer); add_show_from_set (c, &showhistlist); add_show_from_set (add_set_cmd ("confirm", class_support, var_boolean, (char *) &caution, "Set whether to confirm potentially dangerous operations.", &setlist), &showlist); /* The set annotate command is different depending whether or not the async version is run. NOTE: this difference is going to disappear as we make the event loop be the default engine of gdb. */ if (!event_loop_p) { c = add_set_cmd ("annotate", class_obscure, var_zinteger, (char *) &annotation_level, "Set annotation_level.\n\ 0 == normal; 1 == fullname (for use when running under emacs)\n\ 2 == output annotated suitably for use by programs that control GDB.", &setlist); c = add_show_from_set (c, &showlist); } else { c = add_set_cmd ("annotate", class_obscure, var_zinteger, (char *) &annotation_level, "Set annotation_level.\n\ 0 == normal; 1 == fullname (for use when running under emacs)\n\ 2 == output annotated suitably for use by programs that control GDB.", &setlist); add_show_from_set (c, &showlist); set_cmd_sfunc (c, set_async_annotation_level); } if (event_loop_p) { add_show_from_set (add_set_cmd ("exec-done-display", class_support, var_boolean, (char *) &exec_done_display_p, "Set notification of completion for asynchronous execution commands.\n\ Use \"on\" to enable the notification, and \"off\" to disable it.", &setlist), &showlist); } } void gdb_init (char *argv0) { if (pre_init_ui_hook) pre_init_ui_hook (); /* Run the init function of each source file */ getcwd (gdb_dirbuf, sizeof (gdb_dirbuf)); current_directory = gdb_dirbuf; #ifdef __MSDOS__ /* Make sure we return to the original directory upon exit, come what may, since the OS doesn't do that for us. */ make_final_cleanup (do_chdir_cleanup, xstrdup (current_directory)); #endif init_cmd_lists (); /* This needs to be done first */ initialize_targets (); /* Setup target_terminal macros for utils.c */ initialize_utils (); /* Make errors and warnings possible */ initialize_all_files (); initialize_current_architecture (); init_cli_cmds(); init_main (); /* But that omits this file! Do it now */ /* The signal handling mechanism is different depending whether or not the async version is run. NOTE: in the future we plan to make the event loop be the default engine of gdb, and this difference will disappear. */ if (event_loop_p) async_init_signals (); else init_signals (); /* We need a default language for parsing expressions, so simple things like "set width 0" won't fail if no language is explicitly set in a config file or implicitly set by reading an executable during startup. */ set_language (language_c); expected_language = current_language; /* don't warn about the change. */ /* Allow another UI to initialize. If the UI fails to initialize, and it wants GDB to revert to the CLI, it should clear init_ui_hook. */ if (init_ui_hook) init_ui_hook (argv0); } Index: stable/12/contrib/gdb/gdb/tracepoint.c =================================================================== --- stable/12/contrib/gdb/gdb/tracepoint.c (revision 358111) +++ stable/12/contrib/gdb/gdb/tracepoint.c (revision 358112) @@ -1,2811 +1,2810 @@ /* Tracing functionality for remote targets in custom GDB protocol Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003 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 "block.h" #include "dictionary.h" #include "ax.h" #include "ax-gdb.h" /* readline include files */ #include "readline/readline.h" -#include "readline/history.h" /* 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); /* 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_longest (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 || DEPRECATED_SYMBOL_NAME (traceframe_fun) == NULL) set_internalvar (lookup_internalvar ("trace_func"), value_from_pointer (charstar, (LONGEST) 0)); else { len = strlen (DEPRECATED_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), DEPRECATED_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) { 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, NULL); 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_PRINT_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; /* if EOF is typed, *line is NULL */ if (*line == NULL) return END; 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.", DEPRECATED_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.", DEPRECATED_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_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", DEPRECATED_SYMBOL_NAME (sym), SYMBOL_CLASS (sym)); break; case LOC_CONST: printf_filtered ("constant %s (value %ld) will not be collected.\n", DEPRECATED_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", DEPRECATED_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: ", DEPRECATED_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 > DEPRECATED_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", DEPRECATED_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 ", DEPRECATED_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 ", DEPRECATED_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 ", DEPRECATED_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 ", DEPRECATED_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", DEPRECATED_SYMBOL_NAME (sym)); break; case LOC_OPTIMIZED_OUT: printf_filtered ("%s has been optimized out of existence.\n", DEPRECATED_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; struct dict_iterator iter; int count = 0; block = block_for_pc (pc); while (block != 0) { QUIT; /* allow user to bail out with ^C */ ALL_BLOCK_SYMBOLS (block, iter, sym) { switch (SYMBOL_CLASS (sym)) { default: warning ("don't know how to trace local symbol %s", DEPRECATED_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 (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 = get_frame_base (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 || get_frame_base (get_current_frame ()) == 0 || old_frame_addr == get_frame_base (get_current_frame ()))) source_only = -1; else source_only = 1; print_stack_frame (deprecated_selected_frame, frame_relative_level (deprecated_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_frame_pc (get_current_frame ()), 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; struct dict_iterator iter; int 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, NULL); 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, iter, sym) { QUIT; /* allow user to bail out with ^C */ if (count == 0) printf_filtered ("Scope for %s:\n", save_args); count++; symname = DEPRECATED_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 (DEPRECATED_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 () - DECR_PC_AFTER_BREAK)); 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); } Index: stable/12/gnu/usr.bin/gdb/Makefile.inc =================================================================== --- stable/12/gnu/usr.bin/gdb/Makefile.inc (revision 358111) +++ stable/12/gnu/usr.bin/gdb/Makefile.inc (revision 358112) @@ -1,66 +1,66 @@ # $FreeBSD$ .include VERSION= "6.1.1 [FreeBSD]" VENDOR= marcel PACKAGE= gdb BMAKE_GDB= ${.CURDIR:H} BMAKE_ROOT= ${BMAKE_GDB:H} BMAKE_BU= ${BMAKE_ROOT}/binutils CNTRB_BU= ${SRCTOP}/contrib/binutils CNTRB_GDB= ${SRCTOP}/contrib/gdb OBJ_BU= ${OBJTOP}/gnu/usr.bin/binutils OBJ_GDB= ${OBJTOP}/gnu/usr.bin/gdb # These assignments duplicate much of the functionality of # MACHINE_CPUARCH, but there's no easy way to export make functions... .if defined(TARGET_ARCH) TARGET_CPUARCH=${TARGET_ARCH:${__TO_CPUARCH}} .else TARGET_CPUARCH=${MACHINE_CPUARCH} .endif TARGET_ARCH?= ${MACHINE_ARCH} TARGET_SUBDIR= ${BMAKE_GDB}/arch/${TARGET_CPUARCH} .if ${TARGET_ARCH} != ${MACHINE_ARCH} GDB_CROSS_DEBUGGER= .endif .PATH: ${CNTRB_GDB}/gdb ${CNTRB_GDB}/gdb/cli ${CNTRB_GDB}/gdb/mi \ ${CNTRB_GDB}/gdb/signals ${CNTRB_GDB}/gdb/tui ${TARGET_SUBDIR} CFLAGS+= -DHAVE_CONFIG_H -DRL_NO_COMPAT -DMI_OUT=1 CFLAGS+= -DDEBUGDIR=\"${DEBUGDIR}\" CFLAGS+= -I. CFLAGS+= -I${TARGET_SUBDIR} CFLAGS+= -I${BMAKE_BU}/libbfd -I${BMAKE_BU}/libbfd/${TARGET_CPUARCH} CFLAGS+= -I${CNTRB_GDB}/gdb CFLAGS+= -I${CNTRB_GDB}/gdb/config CFLAGS+= -I${CNTRB_BU}/include CFLAGS+= -I${CNTRB_GDB}/include CFLAGS+= -I${CNTRB_BU}/bfd -CFLAGS+= -I${SRCTOP}/lib/libedit/edit +CFLAGS+= -I${SYSROOT:U${DESTDIR}}/${INCLUDEDIR}/edit GENSRCS+= nm.h tm.h .if defined(GDB_CROSS_DEBUGGER) CFLAGS+= -DCROSS_DEBUGGER -I${BMAKE_ROOT:H:H} GDB_SUFFIX= -${TARGET_ARCH} MAN= .elif ${MK_GDB_LIBEXEC} != "no" BINDIR?= /usr/libexec MAN= .endif .include "${TARGET_SUBDIR}/Makefile" SRCS+= ${GENSRCS} CLEANFILES+= ${GENSRCS} .include "../Makefile.inc" Index: stable/12/gnu/usr.bin/gdb/kgdb/trgt.c =================================================================== --- stable/12/gnu/usr.bin/gdb/kgdb/trgt.c (revision 358111) +++ stable/12/gnu/usr.bin/gdb/kgdb/trgt.c (revision 358112) @@ -1,396 +1,395 @@ /* * Copyright (c) 2004 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include "kgdb.h" #ifdef CROSS_DEBUGGER /* * We suppress the call to add_target() of core_ops in corelow.c because if * there are multiple core_stratum targets, the find_core_target() function * won't know which one to return and returns none. We need it to return * our target. We only have to do that when we're building a cross-debugger * because fbsd-threads.c is part of a native debugger and it too defines * coreops_suppress_target with 1 as the initializer. */ int coreops_suppress_target = 1; #endif static CORE_ADDR stoppcbs; static void kgdb_core_cleanup(void *); static char *vmcore; static struct target_ops kgdb_trgt_ops; kvm_t *kvm; static char kvm_err[_POSIX2_LINE_MAX]; #define KERNOFF (kgdb_kernbase ()) #define PINKERNEL(x) ((x) >= KERNOFF) static int kgdb_resolve_symbol(const char *name, kvaddr_t *kva) { struct minimal_symbol *ms; ms = lookup_minimal_symbol (name, NULL, NULL); if (ms == NULL) return (1); *kva = SYMBOL_VALUE_ADDRESS (ms); return (0); } static CORE_ADDR kgdb_kernbase (void) { static CORE_ADDR kernbase; struct minimal_symbol *sym; if (kernbase == 0) { sym = lookup_minimal_symbol ("kernbase", NULL, NULL); if (sym == NULL) { kernbase = KERNBASE; } else { kernbase = SYMBOL_VALUE_ADDRESS (sym); } } return kernbase; } static void kgdb_trgt_open(char *filename, int from_tty) { struct cleanup *old_chain; struct thread_info *ti; struct kthr *kt; kvm_t *nkvm; char *temp; int ontop; target_preopen (from_tty); if (!filename) error ("No vmcore file specified."); if (!exec_bfd) error ("Can't open a vmcore without a kernel"); filename = tilde_expand (filename); if (filename[0] != '/') { temp = concat (current_directory, "/", filename, NULL); xfree(filename); filename = temp; } old_chain = make_cleanup (xfree, filename); nkvm = kvm_open2(bfd_get_filename(exec_bfd), filename, write_files ? O_RDWR : O_RDONLY, kvm_err, kgdb_resolve_symbol); if (nkvm == NULL) error ("Failed to open vmcore: %s", kvm_err); /* Don't free the filename now and close any previous vmcore. */ discard_cleanups(old_chain); unpush_target(&kgdb_trgt_ops); kvm = nkvm; vmcore = filename; old_chain = make_cleanup(kgdb_core_cleanup, NULL); ontop = !push_target (&kgdb_trgt_ops); discard_cleanups (old_chain); kgdb_dmesg(); init_thread_list(); kt = kgdb_thr_init(); while (kt != NULL) { ti = add_thread(pid_to_ptid(kt->tid)); kt = kgdb_thr_next(kt); } if (curkthr != 0) inferior_ptid = pid_to_ptid(curkthr->tid); if (ontop) { /* XXX: fetch registers? */ kld_init(); flush_cached_frames(); select_frame (get_current_frame()); print_stack_frame(get_selected_frame(), frame_relative_level(get_selected_frame()), 1); } else warning( "you won't be able to access this vmcore until you terminate\n\ your %s; do ``info files''", target_longname); } static void kgdb_trgt_close(int quitting) { if (kvm != NULL) { inferior_ptid = null_ptid; CLEAR_SOLIB(); if (kvm_close(kvm) != 0) warning("cannot close \"%s\": %s", vmcore, kvm_geterr(kvm)); kvm = NULL; xfree(vmcore); vmcore = NULL; if (kgdb_trgt_ops.to_sections) { xfree(kgdb_trgt_ops.to_sections); kgdb_trgt_ops.to_sections = NULL; kgdb_trgt_ops.to_sections_end = NULL; } } } static void kgdb_core_cleanup(void *arg) { kgdb_trgt_close(0); } static void kgdb_trgt_detach(char *args, int from_tty) { if (args) error ("Too many arguments"); unpush_target(&kgdb_trgt_ops); reinit_frame_cache(); if (from_tty) printf_filtered("No vmcore file now.\n"); } static char * kgdb_trgt_extra_thread_info(struct thread_info *ti) { return (kgdb_thr_extra_thread_info(ptid_get_pid(ti->ptid))); } static void kgdb_trgt_files_info(struct target_ops *target) { printf_filtered ("\t`%s', ", vmcore); wrap_here (" "); printf_filtered ("file type %s.\n", "FreeBSD kernel vmcore"); } static void kgdb_trgt_find_new_threads(void) { struct target_ops *tb; if (kvm != NULL) return; tb = find_target_beneath(&kgdb_trgt_ops); if (tb->to_find_new_threads != NULL) tb->to_find_new_threads(); } static char * kgdb_trgt_pid_to_str(ptid_t ptid) { static char buf[33]; snprintf(buf, sizeof(buf), "Thread %d", ptid_get_pid(ptid)); return (buf); } static int kgdb_trgt_thread_alive(ptid_t ptid) { return (kgdb_thr_lookup_tid(ptid_get_pid(ptid)) != NULL); } static int kgdb_trgt_xfer_memory(CORE_ADDR memaddr, char *myaddr, int len, int write, struct mem_attrib *attrib, struct target_ops *target) { struct target_ops *tb; if (kvm != NULL) { if (len == 0) return (0); if (!write) return (kvm_read2(kvm, memaddr, myaddr, len)); else return (kvm_write(kvm, memaddr, myaddr, len)); } tb = find_target_beneath(target); return (tb->to_xfer_memory(memaddr, myaddr, len, write, attrib, tb)); } static int kgdb_trgt_ignore_breakpoints(CORE_ADDR addr, char *contents) { return 0; } static void kgdb_switch_to_thread(int tid) { char buf[16]; int thread_id; thread_id = pid_to_thread_id(pid_to_ptid(tid)); if (thread_id == 0) error ("invalid tid"); snprintf(buf, sizeof(buf), "%d", thread_id); gdb_thread_select(uiout, buf); } static void kgdb_set_proc_cmd (char *arg, int from_tty) { CORE_ADDR addr; struct kthr *thr; if (!arg) error_no_arg ("proc address for the new context"); if (kvm == NULL) error ("only supported for core file target"); addr = (CORE_ADDR) parse_and_eval_address (arg); if (!PINKERNEL (addr)) { thr = kgdb_thr_lookup_pid((int)addr); if (thr == NULL) error ("invalid pid"); } else { thr = kgdb_thr_lookup_paddr(addr); if (thr == NULL) error("invalid proc address"); } kgdb_switch_to_thread(thr->tid); } static void kgdb_set_tid_cmd (char *arg, int from_tty) { CORE_ADDR addr; struct kthr *thr; if (!arg) error_no_arg ("TID or thread address for the new context"); addr = (CORE_ADDR) parse_and_eval_address (arg); if (kvm != NULL && PINKERNEL (addr)) { thr = kgdb_thr_lookup_taddr(addr); if (thr == NULL) error("invalid thread address"); addr = thr->tid; } kgdb_switch_to_thread(addr); } int fbsdcoreops_suppress_target = 1; void initialize_kgdb_target(void) { kgdb_trgt_ops.to_magic = OPS_MAGIC; kgdb_trgt_ops.to_shortname = "kernel"; kgdb_trgt_ops.to_longname = "kernel core dump file"; kgdb_trgt_ops.to_doc = "Use a vmcore file as a target. Specify the filename of the vmcore file."; kgdb_trgt_ops.to_stratum = core_stratum; kgdb_trgt_ops.to_has_memory = 1; kgdb_trgt_ops.to_has_registers = 1; kgdb_trgt_ops.to_has_stack = 1; kgdb_trgt_ops.to_open = kgdb_trgt_open; kgdb_trgt_ops.to_close = kgdb_trgt_close; kgdb_trgt_ops.to_attach = find_default_attach; kgdb_trgt_ops.to_detach = kgdb_trgt_detach; kgdb_trgt_ops.to_extra_thread_info = kgdb_trgt_extra_thread_info; kgdb_trgt_ops.to_fetch_registers = kgdb_trgt_fetch_registers; kgdb_trgt_ops.to_files_info = kgdb_trgt_files_info; kgdb_trgt_ops.to_find_new_threads = kgdb_trgt_find_new_threads; kgdb_trgt_ops.to_pid_to_str = kgdb_trgt_pid_to_str; kgdb_trgt_ops.to_store_registers = kgdb_trgt_store_registers; kgdb_trgt_ops.to_thread_alive = kgdb_trgt_thread_alive; kgdb_trgt_ops.to_xfer_memory = kgdb_trgt_xfer_memory; kgdb_trgt_ops.to_insert_breakpoint = kgdb_trgt_ignore_breakpoints; kgdb_trgt_ops.to_remove_breakpoint = kgdb_trgt_ignore_breakpoints; add_target(&kgdb_trgt_ops); add_com ("proc", class_obscure, kgdb_set_proc_cmd, "Set current process context"); add_com ("tid", class_obscure, kgdb_set_tid_cmd, "Set current thread context"); } CORE_ADDR kgdb_trgt_stop_pcb(u_int cpuid, u_int pcbsz) { static int once = 0; if (stoppcbs == 0 && !once) { once = 1; stoppcbs = kgdb_lookup("stoppcbs"); } if (stoppcbs == 0) return 0; return (stoppcbs + pcbsz * cpuid); } Index: stable/12 =================================================================== --- stable/12 (revision 358111) +++ stable/12 (revision 358112) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r352242,352249