| /* C preprocessor macro expansion commands for GDB. | |
| Copyright (C) 2002-2022 Free Software Foundation, Inc. | |
| Contributed by Red Hat, 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 3 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, see <http://www.gnu.org/licenses/>. */ | |
| #include "defs.h" | |
| #include "macrotab.h" | |
| #include "macroexp.h" | |
| #include "macroscope.h" | |
| #include "cli/cli-style.h" | |
| #include "cli/cli-utils.h" | |
| #include "command.h" | |
| #include "gdbcmd.h" | |
| #include "linespec.h" | |
| /* The `macro' prefix command. */ | |
| static struct cmd_list_element *macrolist; | |
| /* Macro expansion commands. */ | |
| /* Prints an informational message regarding the lack of macro information. */ | |
| static void | |
| macro_inform_no_debuginfo (void) | |
| { | |
| gdb_puts ("GDB has no preprocessor macro information for that code.\n"); | |
| } | |
| static void | |
| macro_expand_command (const char *exp, int from_tty) | |
| { | |
| /* You know, when the user doesn't specify any expression, it would be | |
| really cool if this defaulted to the last expression evaluated. | |
| Then it would be easy to ask, "Hey, what did I just evaluate?" But | |
| at the moment, the `print' commands don't save the last expression | |
| evaluated, just its value. */ | |
| if (! exp || ! *exp) | |
| error (_("You must follow the `macro expand' command with the" | |
| " expression you\n" | |
| "want to expand.")); | |
| gdb::unique_xmalloc_ptr<macro_scope> ms = default_macro_scope (); | |
| if (ms != nullptr) | |
| { | |
| gdb::unique_xmalloc_ptr<char> expanded = macro_expand (exp, *ms); | |
| gdb_puts ("expands to: "); | |
| gdb_puts (expanded.get ()); | |
| gdb_puts ("\n"); | |
| } | |
| else | |
| macro_inform_no_debuginfo (); | |
| } | |
| static void | |
| macro_expand_once_command (const char *exp, int from_tty) | |
| { | |
| /* You know, when the user doesn't specify any expression, it would be | |
| really cool if this defaulted to the last expression evaluated. | |
| And it should set the once-expanded text as the new `last | |
| expression'. That way, you could just hit return over and over and | |
| see the expression expanded one level at a time. */ | |
| if (! exp || ! *exp) | |
| error (_("You must follow the `macro expand-once' command with" | |
| " the expression\n" | |
| "you want to expand.")); | |
| gdb::unique_xmalloc_ptr<macro_scope> ms = default_macro_scope (); | |
| if (ms != nullptr) | |
| { | |
| gdb::unique_xmalloc_ptr<char> expanded = macro_expand_once (exp, *ms); | |
| gdb_puts ("expands to: "); | |
| gdb_puts (expanded.get ()); | |
| gdb_puts ("\n"); | |
| } | |
| else | |
| macro_inform_no_debuginfo (); | |
| } | |
| /* Outputs the include path of a macro starting at FILE and LINE to STREAM. | |
| Care should be taken that this function does not cause any lookups into | |
| the splay tree so that it can be safely used while iterating. */ | |
| static void | |
| show_pp_source_pos (struct ui_file *stream, | |
| struct macro_source_file *file, | |
| int line) | |
| { | |
| std::string fullname = macro_source_fullname (file); | |
| gdb_printf (stream, "%ps:%d\n", | |
| styled_string (file_name_style.style (), | |
| fullname.c_str ()), | |
| line); | |
| while (file->included_by) | |
| { | |
| fullname = macro_source_fullname (file->included_by); | |
| gdb_puts (_(" included at "), stream); | |
| fputs_styled (fullname.c_str (), file_name_style.style (), stream); | |
| gdb_printf (stream, ":%d\n", file->included_at_line); | |
| file = file->included_by; | |
| } | |
| } | |
| /* Outputs a macro for human consumption, detailing the include path | |
| and macro definition. NAME is the name of the macro. | |
| D the definition. FILE the start of the include path, and LINE the | |
| line number in FILE. | |
| Care should be taken that this function does not cause any lookups into | |
| the splay tree so that it can be safely used while iterating. */ | |
| static void | |
| print_macro_definition (const char *name, | |
| const struct macro_definition *d, | |
| struct macro_source_file *file, | |
| int line) | |
| { | |
| gdb_printf ("Defined at "); | |
| show_pp_source_pos (gdb_stdout, file, line); | |
| if (line != 0) | |
| gdb_printf ("#define %s", name); | |
| else | |
| gdb_printf ("-D%s", name); | |
| if (d->kind == macro_function_like) | |
| { | |
| int i; | |
| gdb_puts ("("); | |
| for (i = 0; i < d->argc; i++) | |
| { | |
| gdb_puts (d->argv[i]); | |
| if (i + 1 < d->argc) | |
| gdb_puts (", "); | |
| } | |
| gdb_puts (")"); | |
| } | |
| if (line != 0) | |
| gdb_printf (" %s\n", d->replacement); | |
| else | |
| gdb_printf ("=%s\n", d->replacement); | |
| } | |
| /* The implementation of the `info macro' command. */ | |
| static void | |
| info_macro_command (const char *args, int from_tty) | |
| { | |
| gdb::unique_xmalloc_ptr<struct macro_scope> ms; | |
| const char *name; | |
| int show_all_macros_named = 0; | |
| const char *arg_start = args; | |
| int processing_args = 1; | |
| while (processing_args | |
| && arg_start && *arg_start == '-' && *arg_start != '\0') | |
| { | |
| const char *p = skip_to_space (arg_start); | |
| if (strncmp (arg_start, "-a", p - arg_start) == 0 | |
| || strncmp (arg_start, "-all", p - arg_start) == 0) | |
| show_all_macros_named = 1; | |
| else if (strncmp (arg_start, "--", p - arg_start) == 0) | |
| /* Our macro support seems rather C specific but this would | |
| seem necessary for languages allowing - in macro names. | |
| e.g. Scheme's (defmacro ->foo () "bar\n") */ | |
| processing_args = 0; | |
| else | |
| report_unrecognized_option_error ("info macro", arg_start); | |
| arg_start = skip_spaces (p); | |
| } | |
| name = arg_start; | |
| if (! name || ! *name) | |
| error (_("You must follow the `info macro' command with the name" | |
| " of the macro\n" | |
| "whose definition you want to see.")); | |
| ms = default_macro_scope (); | |
| if (! ms) | |
| macro_inform_no_debuginfo (); | |
| else if (show_all_macros_named) | |
| macro_for_each (ms->file->table, [&] (const char *macro_name, | |
| const macro_definition *macro, | |
| macro_source_file *source, | |
| int line) | |
| { | |
| if (strcmp (name, macro_name) == 0) | |
| print_macro_definition (name, macro, source, line); | |
| }); | |
| else | |
| { | |
| struct macro_definition *d; | |
| d = macro_lookup_definition (ms->file, ms->line, name); | |
| if (d) | |
| { | |
| int line; | |
| struct macro_source_file *file | |
| = macro_definition_location (ms->file, ms->line, name, &line); | |
| print_macro_definition (name, d, file, line); | |
| } | |
| else | |
| { | |
| gdb_printf ("The symbol `%s' has no definition as a C/C++" | |
| " preprocessor macro\n" | |
| "at ", name); | |
| show_pp_source_pos (gdb_stdout, ms->file, ms->line); | |
| } | |
| } | |
| } | |
| /* Implementation of the "info macros" command. */ | |
| static void | |
| info_macros_command (const char *args, int from_tty) | |
| { | |
| gdb::unique_xmalloc_ptr<struct macro_scope> ms; | |
| if (args == NULL) | |
| ms = default_macro_scope (); | |
| else | |
| { | |
| std::vector<symtab_and_line> sals | |
| = decode_line_with_current_source (args, 0); | |
| if (!sals.empty ()) | |
| ms = sal_macro_scope (sals[0]); | |
| } | |
| if (! ms || ! ms->file || ! ms->file->table) | |
| macro_inform_no_debuginfo (); | |
| else | |
| macro_for_each_in_scope (ms->file, ms->line, print_macro_definition); | |
| } | |
| /* User-defined macros. */ | |
| static void | |
| skip_ws (const char **expp) | |
| { | |
| while (macro_is_whitespace (**expp)) | |
| ++*expp; | |
| } | |
| /* Try to find the bounds of an identifier. If an identifier is | |
| found, returns a newly allocated string; otherwise returns NULL. | |
| EXPP is a pointer to an input string; it is updated to point to the | |
| text following the identifier. If IS_PARAMETER is true, this | |
| function will also allow "..." forms as used in varargs macro | |
| parameters. */ | |
| static gdb::unique_xmalloc_ptr<char> | |
| extract_identifier (const char **expp, int is_parameter) | |
| { | |
| char *result; | |
| const char *p = *expp; | |
| unsigned int len; | |
| if (is_parameter && startswith (p, "...")) | |
| { | |
| /* Ok. */ | |
| } | |
| else | |
| { | |
| if (! *p || ! macro_is_identifier_nondigit (*p)) | |
| return NULL; | |
| for (++p; | |
| *p && (macro_is_identifier_nondigit (*p) || macro_is_digit (*p)); | |
| ++p) | |
| ; | |
| } | |
| if (is_parameter && startswith (p, "...")) | |
| p += 3; | |
| len = p - *expp; | |
| result = (char *) xmalloc (len + 1); | |
| memcpy (result, *expp, len); | |
| result[len] = '\0'; | |
| *expp += len; | |
| return gdb::unique_xmalloc_ptr<char> (result); | |
| } | |
| struct temporary_macro_definition : public macro_definition | |
| { | |
| temporary_macro_definition () | |
| { | |
| table = nullptr; | |
| kind = macro_object_like; | |
| argc = 0; | |
| argv = nullptr; | |
| replacement = nullptr; | |
| } | |
| ~temporary_macro_definition () | |
| { | |
| int i; | |
| for (i = 0; i < argc; ++i) | |
| xfree ((char *) argv[i]); | |
| xfree ((char *) argv); | |
| /* Note that the 'replacement' field is not allocated. */ | |
| } | |
| }; | |
| static void | |
| macro_define_command (const char *exp, int from_tty) | |
| { | |
| temporary_macro_definition new_macro; | |
| if (!exp) | |
| error (_("usage: macro define NAME[(ARGUMENT-LIST)] [REPLACEMENT-LIST]")); | |
| skip_ws (&exp); | |
| gdb::unique_xmalloc_ptr<char> name = extract_identifier (&exp, 0); | |
| if (name == NULL) | |
| error (_("Invalid macro name.")); | |
| if (*exp == '(') | |
| { | |
| /* Function-like macro. */ | |
| int alloced = 5; | |
| char **argv = XNEWVEC (char *, alloced); | |
| new_macro.kind = macro_function_like; | |
| new_macro.argc = 0; | |
| new_macro.argv = (const char * const *) argv; | |
| /* Skip the '(' and whitespace. */ | |
| ++exp; | |
| skip_ws (&exp); | |
| while (*exp != ')') | |
| { | |
| int i; | |
| if (new_macro.argc == alloced) | |
| { | |
| alloced *= 2; | |
| argv = (char **) xrealloc (argv, alloced * sizeof (char *)); | |
| /* Must update new_macro as well... */ | |
| new_macro.argv = (const char * const *) argv; | |
| } | |
| argv[new_macro.argc] = extract_identifier (&exp, 1).release (); | |
| if (! argv[new_macro.argc]) | |
| error (_("Macro is missing an argument.")); | |
| ++new_macro.argc; | |
| for (i = new_macro.argc - 2; i >= 0; --i) | |
| { | |
| if (! strcmp (argv[i], argv[new_macro.argc - 1])) | |
| error (_("Two macro arguments with identical names.")); | |
| } | |
| skip_ws (&exp); | |
| if (*exp == ',') | |
| { | |
| ++exp; | |
| skip_ws (&exp); | |
| } | |
| else if (*exp != ')') | |
| error (_("',' or ')' expected at end of macro arguments.")); | |
| } | |
| /* Skip the closing paren. */ | |
| ++exp; | |
| skip_ws (&exp); | |
| macro_define_function (macro_main (macro_user_macros), -1, name.get (), | |
| new_macro.argc, (const char **) new_macro.argv, | |
| exp); | |
| } | |
| else | |
| { | |
| skip_ws (&exp); | |
| macro_define_object (macro_main (macro_user_macros), -1, name.get (), | |
| exp); | |
| } | |
| } | |
| static void | |
| macro_undef_command (const char *exp, int from_tty) | |
| { | |
| if (!exp) | |
| error (_("usage: macro undef NAME")); | |
| skip_ws (&exp); | |
| gdb::unique_xmalloc_ptr<char> name = extract_identifier (&exp, 0); | |
| if (name == nullptr) | |
| error (_("Invalid macro name.")); | |
| macro_undef (macro_main (macro_user_macros), -1, name.get ()); | |
| } | |
| static void | |
| print_one_macro (const char *name, const struct macro_definition *macro, | |
| struct macro_source_file *source, int line) | |
| { | |
| gdb_printf ("macro define %s", name); | |
| if (macro->kind == macro_function_like) | |
| { | |
| int i; | |
| gdb_printf ("("); | |
| for (i = 0; i < macro->argc; ++i) | |
| gdb_printf ("%s%s", (i > 0) ? ", " : "", | |
| macro->argv[i]); | |
| gdb_printf (")"); | |
| } | |
| gdb_printf (" %s\n", macro->replacement); | |
| } | |
| static void | |
| macro_list_command (const char *exp, int from_tty) | |
| { | |
| macro_for_each (macro_user_macros, print_one_macro); | |
| } | |
| /* Initializing the `macrocmd' module. */ | |
| void _initialize_macrocmd (); | |
| void | |
| _initialize_macrocmd () | |
| { | |
| /* We introduce a new command prefix, `macro', under which we'll put | |
| the various commands for working with preprocessor macros. */ | |
| add_basic_prefix_cmd ("macro", class_info, | |
| _("Prefix for commands dealing with C preprocessor macros."), | |
| ¯olist, 0, &cmdlist); | |
| cmd_list_element *macro_expand_cmd | |
| = add_cmd ("expand", no_class, macro_expand_command, _("\ | |
| Fully expand any C/C++ preprocessor macro invocations in EXPRESSION.\n\ | |
| Show the expanded expression."), | |
| ¯olist); | |
| add_alias_cmd ("exp", macro_expand_cmd, no_class, 1, ¯olist); | |
| cmd_list_element *macro_expand_once_cmd | |
| = add_cmd ("expand-once", no_class, macro_expand_once_command, _("\ | |
| Expand C/C++ preprocessor macro invocations appearing directly in EXPRESSION.\n\ | |
| Show the expanded expression.\n\ | |
| \n\ | |
| This command differs from `macro expand' in that it only expands macro\n\ | |
| invocations that appear directly in EXPRESSION; if expanding a macro\n\ | |
| introduces further macro invocations, those are left unexpanded.\n\ | |
| \n\ | |
| `macro expand-once' helps you see how a particular macro expands,\n\ | |
| whereas `macro expand' shows you how all the macros involved in an\n\ | |
| expression work together to yield a pre-processed expression."), | |
| ¯olist); | |
| add_alias_cmd ("exp1", macro_expand_once_cmd, no_class, 1, ¯olist); | |
| add_info ("macro", info_macro_command, | |
| _("Show the definition of MACRO, and it's source location.\n\ | |
| Usage: info macro [-a|-all] [--] MACRO\n\ | |
| Options: \n\ | |
| -a, --all Output all definitions of MACRO in the current compilation\ | |
| unit.\n\ | |
| -- Specify the end of arguments and the beginning of the MACRO.")); | |
| add_info ("macros", info_macros_command, | |
| _("Show the definitions of all macros at LINESPEC, or the current \ | |
| source location.\n\ | |
| Usage: info macros [LINESPEC]")); | |
| add_cmd ("define", no_class, macro_define_command, _("\ | |
| Define a new C/C++ preprocessor macro.\n\ | |
| The GDB command `macro define DEFINITION' is equivalent to placing a\n\ | |
| preprocessor directive of the form `#define DEFINITION' such that the\n\ | |
| definition is visible in all the inferior's source files.\n\ | |
| For example:\n\ | |
| (gdb) macro define PI (3.1415926)\n\ | |
| (gdb) macro define MIN(x,y) ((x) < (y) ? (x) : (y))"), | |
| ¯olist); | |
| add_cmd ("undef", no_class, macro_undef_command, _("\ | |
| Remove the definition of the C/C++ preprocessor macro with the given name."), | |
| ¯olist); | |
| add_cmd ("list", no_class, macro_list_command, | |
| _("List all the macros defined using the `macro define' command."), | |
| ¯olist); | |
| } |