diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 6dca96555a..2bd67260dc 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2019-10-31 Andrew Burgess + + * symtab.c (info_module_cmdlist): New variable. + (info_module_command): New function. + (search_module_symbols): New function. + (info_module_subcommand): New function. + (struct info_modules_var_func_options): New struct. + (info_modules_var_func_options_defs): New variable. + (make_info_modules_var_func_options_def_group): New function. + (info_module_functions_command): New function. + (info_module_variables_command): New function. + (info_module_var_func_command_completer): New function. + (_initialize_symtab): Register new 'info module functions' and + 'info module variables' commands. + * symtab.h (typedef symbol_search_in_module): New typedef. + (search_module_symbols): Declare new function. + * NEWS: Mention new commands. + 2019-10-31 Andrew Burgess * dwarf2read.c (dw2_symtab_iter_next): Handle MODULE_DOMAIN. diff --git a/gdb/NEWS b/gdb/NEWS index d46f7094d4..59895bd68b 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -163,6 +163,20 @@ info modules [-q] [REGEXP] Return a list of Fortran modules matching REGEXP, or all modules if no REGEXP is given. +info module functions [-q] [-m MODULE_REGEXP] [-t TYPE_REGEXP] [REGEXP] + Return a list of functions within all modules, grouped by module. + The list of functions can be restricted with the optional regular + expressions. MODULE_REGEXP matches against the module name, + TYPE_REGEXP matches against the function type signature, and REGEXP + matches against the function name. + +info module variables [-q] [-m MODULE_REGEXP] [-t TYPE_REGEXP] [REGEXP] + Return a list of variables within all modules, grouped by module. + The list of variables can be restricted with the optional regular + expressions. MODULE_REGEXP matches against the module name, + TYPE_REGEXP matches against the variable type, and REGEXP matches + against the variable name. + * Changed commands help diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 52497a1772..ce89ee444e 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2019-10-31 Andrew Burgess + + * gdb.texinfo (Symbols): Document new 'info module variables' and + 'info module functions' commands. + 2019-10-31 Andrew Burgess * gdb.texinfo (Symbols): Document new 'info modules' command. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index ee06df2bb2..70e4be1524 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -18896,6 +18896,25 @@ The optional flag @samp{-q}, which stands for @samp{quiet}, disables printing header information and messages explaining why no modules have been printed. +@kindex info module +@cindex Fortran modules, information about +@cindex functions and variables by Fortran module +@cindex module functions and variables +@item info module functions @r{[}-q@r{]} @r{[}-m @var{module-regexp}@r{]} @r{[}-t @var{type-regexp}@r{]} @r{[}@var{regexp}@r{]} +@itemx info module variables @r{[}-q@r{]} @r{[}-m @var{module-regexp}@r{]} @r{[}-t @var{type-regexp}@r{]} @r{[}@var{regexp}@r{]} +List all functions or variables within all Fortran modules. The set +of functions or variables listed can be limited by providing some or +all of the optional regular expressions. If @var{module-regexp} is +provided, then only Fortran modules matching @var{module-regexp} will +be searched. Only functions or variables whose type matches the +optional regular expression @var{type-regexp} will be listed. And +only functions or variables whose name matches the optional regular +expression @var{regexp} will be listed. + +The optional flag @samp{-q}, which stands for @samp{quiet}, disables +printing header information and messages explaining why no functions +or variables have been printed. + @kindex info classes @cindex Objective-C, classes and selectors @item info classes diff --git a/gdb/symtab.c b/gdb/symtab.c index 4c14edae17..72a54ec1ca 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -6340,6 +6340,315 @@ get_msymbol_address (struct objfile *objf, const struct minimal_symbol *minsym) +/* Hold the sub-commands of 'info module'. */ + +static struct cmd_list_element *info_module_cmdlist = NULL; + +/* Implement the 'info module' command, just displays some help text for + the available sub-commands. */ + +static void +info_module_command (const char *args, int from_tty) +{ + help_list (info_module_cmdlist, "info module ", class_info, gdb_stdout); +} + +/* See symtab.h. */ + +std::vector +search_module_symbols (const char *module_regexp, const char *regexp, + const char *type_regexp, search_domain kind) +{ + std::vector results; + + /* Search for all modules matching MODULE_REGEXP. */ + std::vector modules = search_symbols (module_regexp, + MODULES_DOMAIN, + NULL, 0, NULL, + true); + + /* Now search for all symbols of the required KIND matching the required + regular expressions. We figure out which ones are in which modules + below. */ + std::vector symbols = search_symbols (regexp, kind, + type_regexp, 0, + NULL, true); + + /* Now iterate over all MODULES, checking to see which items from + SYMBOLS are in each module. */ + for (const symbol_search &p : modules) + { + QUIT; + + /* This is a module. */ + gdb_assert (p.symbol != nullptr); + + std::string prefix = SYMBOL_PRINT_NAME (p.symbol); + prefix += "::"; + + for (const symbol_search &q : symbols) + { + if (q.symbol == nullptr) + continue; + + if (strncmp (SYMBOL_PRINT_NAME (q.symbol), prefix.c_str (), + prefix.size ()) != 0) + continue; + + results.push_back ({p, q}); + } + } + + return results; +} + +/* Implement the core of both 'info module functions' and 'info module + variables'. */ + +static void +info_module_subcommand (bool quiet, const char *module_regexp, + const char *regexp, const char *type_regexp, + search_domain kind) +{ + /* Print a header line. Don't build the header line bit by bit as this + prevents internationalisation. */ + if (!quiet) + { + if (module_regexp == nullptr) + { + if (type_regexp == nullptr) + { + if (regexp == nullptr) + printf_filtered ((kind == VARIABLES_DOMAIN + ? _("All variables in all modules:") + : _("All functions in all modules:"))); + else + printf_filtered + ((kind == VARIABLES_DOMAIN + ? _("All variables matching regular expression" + " \"%s\" in all modules:") + : _("All functions matching regular expression" + " \"%s\" in all modules:")), + regexp); + } + else + { + if (regexp == nullptr) + printf_filtered + ((kind == VARIABLES_DOMAIN + ? _("All variables with type matching regular " + "expression \"%s\" in all modules:") + : _("All functions with type matching regular " + "expression \"%s\" in all modules:")), + type_regexp); + else + printf_filtered + ((kind == VARIABLES_DOMAIN + ? _("All variables matching regular expression " + "\"%s\",\n\twith type matching regular " + "expression \"%s\" in all modules:") + : _("All functions matching regular expression " + "\"%s\",\n\twith type matching regular " + "expression \"%s\" in all modules:")), + regexp, type_regexp); + } + } + else + { + if (type_regexp == nullptr) + { + if (regexp == nullptr) + printf_filtered + ((kind == VARIABLES_DOMAIN + ? _("All variables in all modules matching regular " + "expression \"%s\":") + : _("All functions in all modules matching regular " + "expression \"%s\":")), + module_regexp); + else + printf_filtered + ((kind == VARIABLES_DOMAIN + ? _("All variables matching regular expression " + "\"%s\",\n\tin all modules matching regular " + "expression \"%s\":") + : _("All functions matching regular expression " + "\"%s\",\n\tin all modules matching regular " + "expression \"%s\":")), + regexp, module_regexp); + } + else + { + if (regexp == nullptr) + printf_filtered + ((kind == VARIABLES_DOMAIN + ? _("All variables with type matching regular " + "expression \"%s\"\n\tin all modules matching " + "regular expression \"%s\":") + : _("All functions with type matching regular " + "expression \"%s\"\n\tin all modules matching " + "regular expression \"%s\":")), + type_regexp, module_regexp); + else + printf_filtered + ((kind == VARIABLES_DOMAIN + ? _("All variables matching regular expression " + "\"%s\",\n\twith type matching regular expression " + "\"%s\",\n\tin all modules matching regular " + "expression \"%s\":") + : _("All functions matching regular expression " + "\"%s\",\n\twith type matching regular expression " + "\"%s\",\n\tin all modules matching regular " + "expression \"%s\":")), + regexp, type_regexp, module_regexp); + } + } + printf_filtered ("\n"); + } + + /* Find all symbols of type KIND matching the given regular expressions + along with the symbols for the modules in which those symbols + reside. */ + std::vector module_symbols + = search_module_symbols (module_regexp, regexp, type_regexp, kind); + + std::sort (module_symbols.begin (), module_symbols.end (), + [] (const module_symbol_search &a, const module_symbol_search &b) + { + if (a.first < b.first) + return true; + else if (a.first == b.first) + return a.second < b.second; + else + return false; + }); + + const char *last_filename = ""; + const symbol *last_module_symbol = nullptr; + for (const module_symbol_search &ms : module_symbols) + { + const symbol_search &p = ms.first; + const symbol_search &q = ms.second; + + gdb_assert (q.symbol != nullptr); + + if (last_module_symbol != p.symbol) + { + printf_filtered ("\n"); + printf_filtered (_("Module \"%s\":\n"), + SYMBOL_PRINT_NAME (p.symbol)); + last_module_symbol = p.symbol; + last_filename = ""; + } + + print_symbol_info (FUNCTIONS_DOMAIN, q.symbol, q.block, + last_filename); + last_filename + = symtab_to_filename_for_display (symbol_symtab (q.symbol)); + } +} + +/* Hold the option values for the 'info module .....' sub-commands. */ + +struct info_modules_var_func_options +{ + bool quiet = false; + char *type_regexp = nullptr; + char *module_regexp = nullptr; + + ~info_modules_var_func_options () + { + xfree (type_regexp); + xfree (module_regexp); + } +}; + +/* The options used by 'info module variables' and 'info module functions' + commands. */ + +static const gdb::option::option_def info_modules_var_func_options_defs [] = { + gdb::option::boolean_option_def { + "q", + [] (info_modules_var_func_options *opt) { return &opt->quiet; }, + nullptr, /* show_cmd_cb */ + nullptr /* set_doc */ + }, + + gdb::option::string_option_def { + "t", + [] (info_modules_var_func_options *opt) { return &opt->type_regexp; }, + nullptr, /* show_cmd_cb */ + nullptr /* set_doc */ + }, + + gdb::option::string_option_def { + "m", + [] (info_modules_var_func_options *opt) { return &opt->module_regexp; }, + nullptr, /* show_cmd_cb */ + nullptr /* set_doc */ + } +}; + +/* Return the option group used by the 'info module ...' sub-commands. */ + +static inline gdb::option::option_def_group +make_info_modules_var_func_options_def_group + (info_modules_var_func_options *opts) +{ + return {{info_modules_var_func_options_defs}, opts}; +} + +/* Implements the 'info module functions' command. */ + +static void +info_module_functions_command (const char *args, int from_tty) +{ + info_modules_var_func_options opts; + auto grp = make_info_modules_var_func_options_def_group (&opts); + gdb::option::process_options + (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp); + if (args != nullptr && *args == '\0') + args = nullptr; + + info_module_subcommand (opts.quiet, opts.module_regexp, args, + opts.type_regexp, FUNCTIONS_DOMAIN); +} + +/* Implements the 'info module variables' command. */ + +static void +info_module_variables_command (const char *args, int from_tty) +{ + info_modules_var_func_options opts; + auto grp = make_info_modules_var_func_options_def_group (&opts); + gdb::option::process_options + (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp); + if (args != nullptr && *args == '\0') + args = nullptr; + + info_module_subcommand (opts.quiet, opts.module_regexp, args, + opts.type_regexp, VARIABLES_DOMAIN); +} + +/* Command completer for 'info module ...' sub-commands. */ + +static void +info_module_var_func_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, + const char * /* word */) +{ + + const auto group = make_info_modules_var_func_options_def_group (nullptr); + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group)) + return; + + const char *word = advance_to_expression_complete_word_point (tracker, text); + symbol_completer (ignore, tracker, text, word); +} + + + void _initialize_symtab (void) { @@ -6402,6 +6711,45 @@ Options:\n\ _("All module names, or those matching REGEXP.")); set_cmd_completer_handle_brkchars (c, info_types_command_completer); + add_prefix_cmd ("module", class_info, info_module_command, _("\ +Print information about modules."), + &info_module_cmdlist, "info module ", + 0, &infolist); + + c = add_cmd ("functions", class_info, info_module_functions_command, _("\ +Display functions arranged by modules.\n\ +Usage: info module functions [-q] [-m MODREGEXP] [-t TYPEREGEXP] [REGEXP]\n\ +Print a summary of all functions within each Fortran module, grouped by\n\ +module and file. For each function the line on which the function is\n\ +defined is given along with the type signature and name of the function.\n\ +\n\ +If REGEXP is provided then only functions whose name matches REGEXP are\n\ +listed. If MODREGEXP is provided then only functions in modules matching\n\ +MODREGEXP are listed. If TYPEREGEXP is given then only functions whose\n\ +type signature matches TYPEREGEXP are listed.\n\ +\n\ +The -q flag suppresses printing some header information."), + &info_module_cmdlist); + set_cmd_completer_handle_brkchars + (c, info_module_var_func_command_completer); + + c = add_cmd ("variables", class_info, info_module_variables_command, _("\ +Display variables arranged by modules.\n\ +Usage: info module variables [-q] [-m MODREGEXP] [-t TYPEREGEXP] [REGEXP]\n\ +Print a summary of all variables within each Fortran module, grouped by\n\ +module and file. For each variable the line on which the variable is\n\ +defined is given along with the type and name of the variable.\n\ +\n\ +If REGEXP is provided then only variables whose name matches REGEXP are\n\ +listed. If MODREGEXP is provided then only variables in modules matching\n\ +MODREGEXP are listed. If TYPEREGEXP is given then only variables whose\n\ +type matches TYPEREGEXP are listed.\n\ +\n\ +The -q flag suppresses printing some header information."), + &info_module_cmdlist); + set_cmd_completer_handle_brkchars + (c, info_module_var_func_command_completer); + add_com ("rbreak", class_breakpoint, rbreak_command, _("Set a breakpoint for all functions matching REGEXP.")); diff --git a/gdb/symtab.h b/gdb/symtab.h index 72d1c7ff84..eac44aee15 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -2072,6 +2072,22 @@ extern std::vector search_symbols (const char *, int, const char **, bool); + +/* When searching for Fortran symbols within modules (functions/variables) + we return a vector of this type. The first item in the pair is the + module symbol, and the second item is the symbol for the function or + variable we found. */ +typedef std::pair module_symbol_search; + +/* Searches the symbols to find function and variables symbols (depending + on KIND) within Fortran modules. The MODULE_REGEXP matches against the + name of the module, REGEXP matches against the name of the symbol within + the module, and TYPE_REGEXP matches against the type of the symbol + within the module. */ +extern std::vector search_module_symbols + (const char *module_regexp, const char *regexp, + const char *type_regexp, search_domain kind); + extern bool treg_matches_sym_type_name (const compiled_regex &treg, const struct symbol *sym); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 6514c0764d..4d1eecdb5c 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2019-10-31 Andrew Burgess + + * gdb.fortran/info-modules.exp: Update expected results, and add + additional tests for 'info module functinos', and 'info module + variables'. + * gdb.fortran/info-types.exp: Update expected results. + * gdb.fortran/info-types.f90: Extend testcase with additional + module variables and functions. + 2019-10-31 Andrew Burgess * gdb.fortran/info-modules.exp: New file. diff --git a/gdb/testsuite/gdb.fortran/info-modules.exp b/gdb/testsuite/gdb.fortran/info-modules.exp index f961d28b00..43570066d2 100644 --- a/gdb/testsuite/gdb.fortran/info-modules.exp +++ b/gdb/testsuite/gdb.fortran/info-modules.exp @@ -13,7 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# This file tests 'info modules'. +# This file tests 'info modules', 'info module functions', and 'info +# module variables'. load_lib "fortran.exp" @@ -31,6 +32,12 @@ if { ![runto MAIN__] } { continue } +set logical4 [fortran_logical4] +set integer4 [fortran_int4] +set real4 [fortran_real4] + +# Test 'info modules' command. + gdb_test "info modules" \ [multi_line \ "All defined modules:" \ @@ -64,3 +71,117 @@ gdb_test "info modules mod" \ "" \ "File .*${srcfile}:" \ "16:\[\t \]+mod1" ] + +# Test 'info module functions'. + +gdb_test "info module functions" \ + [multi_line \ + "All functions in all modules:" \ + "" \ + "Module \"mod2\":" \ + "" \ + "File .*${srcfile2}:" \ + "22:\[\t \]+void mod2::sub_m2_a\\(${integer4}, ${logical4}\\);" \ + "30:\[\t \]+${logical4} mod2::sub_m2_b\\(${real4}\\);" \ + "" \ + "Module \"mod1\":" \ + "" \ + "File .*${srcfile}:" \ + "35:\[\t \]+void mod1::__copy_mod1_M1t1\\(Type m1t1, Type m1t1\\);" \ + "25:\[\t \]+void mod1::sub_m1_a\\(${integer4}\\);" \ + "31:\[\t \]+${integer4} mod1::sub_m1_b\\(void\\);" ] + +gdb_test "info module functions -m mod1" \ + [multi_line \ + "All functions in all modules matching regular expression \"mod1\":" \ + "" \ + "Module \"mod1\":" \ + "" \ + "File .*:" \ + "35:\[\t \]+void mod1::__copy_mod1_M1t1\\(Type m1t1, Type m1t1\\);" \ + "25:\[\t \]+void mod1::sub_m1_a\\(${integer4}\\);" \ + "31:\[\t \]+${integer4} mod1::sub_m1_b\\(void\\);" ] + +gdb_test "info module functions -t integer" \ + [multi_line \ + "All functions with type matching regular expression \"integer\" in all modules:" \ + "" \ + "Module \"mod2\":" \ + "" \ + "File .*${srcfile2}:" \ + "22:\[\t \]+void mod2::sub_m2_a\\(${integer4}, ${logical4}\\);" \ + "" \ + "Module \"mod1\":" \ + "" \ + "File .*${srcfile}:" \ + "25:\[\t \]+void mod1::sub_m1_a\\(${integer4}\\);" \ + "31:\[\t \]+${integer4} mod1::sub_m1_b\\(void\\);" ] + +# Test 'info module variables'. + +gdb_test "info module variables" \ + [multi_line \ + "All variables in all modules:" \ + "" \ + "Module \"mod2\":" \ + "" \ + "File .*${srcfile2}:" \ + "19:\[\t \]+${integer4} mod2::mod2_var_1;" \ + "20:\[\t \]+${real4} mod2::mod2_var_2;" \ + "" \ + "Module \"mod1\":" \ + "" \ + "File .*${srcfile}:" \ + "35:\[\t \]+Type m1t1 mod1::__def_init_mod1_M1t1;" \ + "35:\[\t \]+Type __vtype_mod1_M1t1 mod1::__vtab_mod1_M1t1;" \ + "21:\[\t \]+${real4} mod1::mod1_var_1;" \ + "22:\[\t \]+${integer4} mod1::mod1_var_2;" ] + +gdb_test "info module variables -t real" \ + [multi_line \ + "All variables with type matching regular expression \"real\" in all modules:" \ + "" \ + "Module \"mod2\":" \ + "" \ + "File .*:" \ + "20:\[\t \]+${real4} mod2::mod2_var_2;" \ + "" \ + "Module \"mod1\":" \ + "" \ + "File .*:" \ + "21:\[\t \]+${real4} mod1::mod1_var_1;" ] + +gdb_test "info module variables -m mod2" \ + [multi_line \ + "All variables in all modules matching regular expression \"mod2\":" \ + "" \ + "Module \"mod2\":" \ + "" \ + "File .*${srcfile2}:" \ + "19:\[\t \]+${integer4} mod2::mod2_var_1;" \ + "20:\[\t \]+${real4} mod2::mod2_var_2;" ] + +gdb_test "info module variables -m mod2 -t real" \ + [multi_line \ + "All variables with type matching regular expression \"real\"" \ + " in all modules matching regular expression \"mod2\":" \ + "" \ + "Module \"mod2\":" \ + "" \ + "File .*${srcfile2}:" \ + "20:\[\t \]+${real4} mod2::mod2_var_2;" ] + +gdb_test "info module variables _1" \ + [multi_line \ + "All variables matching regular expression \"_1\" in all modules:" \ + "" \ + "Module \"mod2\":" \ + "" \ + "File .*:" \ + "19:\[\t \]+${integer4} mod2::mod2_var_1;" \ + "" \ + "Module \"mod1\":" \ + "" \ + "File .*:" \ + "21:\[\t \]+${real4} mod1::mod1_var_1;" ] + diff --git a/gdb/testsuite/gdb.fortran/info-types-2.f90 b/gdb/testsuite/gdb.fortran/info-types-2.f90 index a404418423..3fe2259da4 100644 --- a/gdb/testsuite/gdb.fortran/info-types-2.f90 +++ b/gdb/testsuite/gdb.fortran/info-types-2.f90 @@ -17,4 +17,20 @@ ! mod2 is defined. module mod2 integer :: mod2_var_1 = 123 + real, parameter :: mod2_var_2 = 0.5 +contains + subroutine sub_m2_a(a, b) + integer :: a + logical :: b + print*, "sub_m2_a = ", abc + print*, "a = ", a + print*, "b = ", b + end subroutine sub_m2_a + + logical function sub_m2_b(x) + real :: x + print*, "sub_m2_b = ", cde + print*, "x = ", x + sub_m2_b = .true. + end function sub_m2_b end module mod2 diff --git a/gdb/testsuite/gdb.fortran/info-types.exp b/gdb/testsuite/gdb.fortran/info-types.exp index 954e083e40..324b4e0129 100644 --- a/gdb/testsuite/gdb.fortran/info-types.exp +++ b/gdb/testsuite/gdb.fortran/info-types.exp @@ -35,6 +35,7 @@ set integer4 [fortran_int4] set integer8 [fortran_int8] set logical4 [fortran_logical4] set character1 [fortran_character1] +set real4 [fortran_real4] gdb_test "info types" \ [multi_line \ @@ -45,7 +46,8 @@ gdb_test "info types" \ "\[\t \]+${integer4}" \ "(\[\t \]+${integer8}" \ ")?\[\t \]+${logical4}" \ - "(20:\[\t \]+Type __vtype_mod1_M1t1;" \ + "(35:\[\t \]+Type __vtype_mod1_M1t1;" \ ")?$decimal:\[\t \]+Type m1t1;" \ - "22:\[\t \]+Type s1;(" \ + "\[\t \]+${real4}" \ + "37:\[\t \]+Type s1;(" \ ".*)?"] diff --git a/gdb/testsuite/gdb.fortran/info-types.f90 b/gdb/testsuite/gdb.fortran/info-types.f90 index ec52ef98ef..d3513aca30 100644 --- a/gdb/testsuite/gdb.fortran/info-types.f90 +++ b/gdb/testsuite/gdb.fortran/info-types.f90 @@ -17,6 +17,21 @@ module mod1 type :: m1t1 integer :: b end type m1t1 + + real :: mod1_var_1 = 1.0 + integer, parameter :: mod1_var_2 = 456 + +contains + subroutine sub_m1_a(arg) + integer :: arg + print*, "sub_m1_a" + print*, "arg = ", arg + end subroutine sub_m1_a + + integer function sub_m1_b() + print*, "sub_m1_b" + sub_m1_b = 3 + end function sub_m1_b end module mod1 program info_types_test