[7/7] gdb: Add new commands to list module variables and functions

Message ID ade133229607f823783e9c65d68e2e08abd63d33.1564243858.git.andrew.burgess@embecosm.com
State New, archived
Headers

Commit Message

Andrew Burgess July 27, 2019, 4:22 p.m. UTC
  This patch adds two new commands "info module functions" and "info
module variables".  These commands list all of the functions and
variables grouped by module and then by file.

For example:

  (gdb) info module functions
  All functions in all modules:

  Module "mod1":

  File /some/path/gdb/testsuite/gdb.fortran/info-types.f90:
  35:	void mod1::__copy_mod1_M1t1(Type m1t1, Type m1t1);
  25:	void mod1::sub_m1_a(integer(kind=4));
  31:	integer(kind=4) mod1::sub_m1_b(void);

  Module "mod2":

  File /some/path/gdb/testsuite/gdb.fortran/info-types.f90:
  41:	void mod2::sub_m2_a(integer(kind=4), logical(kind=4));
  49:	logical(kind=4) mod2::sub_m2_b(real(kind=4));

The new commands take set of flags that allow the output to be
filtered, the user can filter by variable/function name, type, or
containing module.

As GDB doesn't currently track the relationship between a module and
the variables or functions within it in the symbol table, so I filter
based on the module prefix in order to find the functions or variables
in each module.  What this makes clear is that a user could get this
same information using "info variables" and simply provide the prefix
themselves, for example:

  (gdb) info module functions -m mod1 _a
  All functions matching regular expression "_a",
  	in all modules matching regular expression "mod1":

  Module "mod1":

  File /some/path/gdb/testsuite/gdb.fortran/info-types.f90:
  25:	void mod1::sub_m1_a(integer(kind=4));

Is similar to:

  (gdb) info functions mod1::.*_a.*
  All functions matching regular expression "mod1::.*_a":

  File /some/path/gdb/testsuite/gdb.fortran/info-types.f90:
  25:	void mod1::sub_m1_a(integer(kind=4));

The benefits I see for a separate command are that the user doesn't
have to think (or know) about the module prefix format, nor worry
about building a proper regexp.  The user can also easily can across
modules without having to build complex regexps.

This patch is a new implementation of an idea originally worked on by
Mark O'Connor, Chris January, David Lecomber, and Xavier Oro from ARM.

gdb/ChangeLog:

	* symtab.c (info_module_cmdlist): New variable.
	(info_module_command): New function.
	(info_module_subcommand_helper): 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.
	* NEWS: Mention new commands.

gdb/doc/ChangeLog:

	* gdb.texinfo (Symbols): Document new 'info module variables' and
	'info module functions' commands.

gdb/testsuite/ChangeLog:

	* 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.
---
 gdb/ChangeLog                              |  15 ++
 gdb/NEWS                                   |  14 ++
 gdb/doc/ChangeLog                          |   5 +
 gdb/doc/gdb.texinfo                        |  34 ++++
 gdb/symtab.c                               | 311 +++++++++++++++++++++++++++++
 gdb/testsuite/ChangeLog                    |   9 +
 gdb/testsuite/gdb.fortran/info-modules.exp | 127 +++++++++++-
 gdb/testsuite/gdb.fortran/info-types.exp   |   6 +-
 gdb/testsuite/gdb.fortran/info-types.f90   |  31 +++
 9 files changed, 546 insertions(+), 6 deletions(-)
  

Comments

Eli Zaretskii July 27, 2019, 5:05 p.m. UTC | #1
> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: Richard Bunt <Richard.Bunt@arm.com>,	Andrew Burgess <andrew.burgess@embecosm.com>
> Date: Sat, 27 Jul 2019 17:22:35 +0100
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 14c931602f4..9b7e6cc1c71 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -110,6 +110,20 @@ info modules [-q] [REGEXP]
>    their DWARF (for example Fortran) will give any results for this
>    command.
>  
> +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.

Modulo the explanation of what is a module, this part is OK.

> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -18633,6 +18633,40 @@
>  printing header information and messages explaining why no functions
>  have been printed.
>  
> +@kindex info module functions
> +@cindex modules
> +@cindex functions
> +@cindex module functions
> +@item info module functions [-q] [-m @var{module-regexp}] [-t @var{type-regexp}] [@var{regexp}]
> +List all functions within all modules.  The set of functions listed
> +can be limited by providing some or all of the optional regular
> +expressions.  If @var{module-regexp} is provided, then only modules
> +matching @var{module-regexp} will be searched.  Only functions whose
> +type matches the optional regular expression @var{type-regexp} will be
> +listed.  And only functions 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
> +have been printed.
> +
> +@kindex info module variables
> +@cindex modules
> +@cindex variables
> +@cindex module variables
> +@item info module variables [-q] [-m @var{module-regexp}] [-t @var{type-regexp}] [@var{regexp}]
> +List all variables within all modules.  The set of variables listed
> +can be limited by providing some or all of the optional regular
> +expressions.  If @var{module-regexp} is provided, then only modules
> +matching @var{module-regexp} will be searched.  Only variables whose
> +type matches the optional regular expression @var{type-regexp} will be
> +listed.  And only 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 variables
> +have been printed.

These two commands are very similar, so I would suggest to describe
both of them in the same text, instead of repeating almost the same
description twice.

> +  c = add_cmd ("functions", class_info, info_module_functions_command, _("\
> +Display functions within each module.\n\

I'd say instead "Display functions arranged by modules."

Thanks.
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 14c931602f4..9b7e6cc1c71 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -110,6 +110,20 @@  info modules [-q] [REGEXP]
   their DWARF (for example Fortran) will give any results for this
   command.
 
+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/gdb.texinfo b/gdb/doc/gdb.texinfo
index 71ee20f29d5..5ba07253d6c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18633,6 +18633,40 @@ 
 printing header information and messages explaining why no functions
 have been printed.
 
+@kindex info module functions
+@cindex modules
+@cindex functions
+@cindex module functions
+@item info module functions [-q] [-m @var{module-regexp}] [-t @var{type-regexp}] [@var{regexp}]
+List all functions within all modules.  The set of functions listed
+can be limited by providing some or all of the optional regular
+expressions.  If @var{module-regexp} is provided, then only modules
+matching @var{module-regexp} will be searched.  Only functions whose
+type matches the optional regular expression @var{type-regexp} will be
+listed.  And only functions 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
+have been printed.
+
+@kindex info module variables
+@cindex modules
+@cindex variables
+@cindex module variables
+@item info module variables [-q] [-m @var{module-regexp}] [-t @var{type-regexp}] [@var{regexp}]
+List all variables within all modules.  The set of variables listed
+can be limited by providing some or all of the optional regular
+expressions.  If @var{module-regexp} is provided, then only modules
+matching @var{module-regexp} will be searched.  Only variables whose
+type matches the optional regular expression @var{type-regexp} will be
+listed.  And only 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 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 793be13ab9d..d8c6518bb39 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -6179,6 +6179,278 @@  symbol_set_symtab (struct symbol *symbol, struct symtab *symtab)
 
 
 
+/* 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);
+}
+
+/* Implement the core of both 'info module functions' and 'info module
+   variables'.  */
+
+static void
+info_module_subcommand_helper (bool quiet,
+			       const char *module_regexp,
+			       const char *regexp,
+			       const char *type_regexp,
+			       search_domain kind)
+{
+  /* Must make sure that if we're interrupted, MODULES gets freed.  */
+  std::vector<symbol_search> modules = search_symbols (module_regexp,
+						       MODULES_DOMAIN,
+						       NULL, 0, NULL,
+						       true);
+
+  /* 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");
+    }
+
+  /* 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<symbol_search> 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 += "::";
+
+      bool module_header_printed = false;
+      const char *last_filename = "";
+      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;
+
+	  if (!module_header_printed)
+	    {
+	      printf_filtered ("\n");
+	      printf_filtered (_("Module \"%s\":\n"),
+			       SYMBOL_PRINT_NAME (p.symbol));
+	      module_header_printed = true;
+	    }
+
+	  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
+{
+  int quiet = 0;
+  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<info_modules_var_func_options> {
+    "q",
+    [] (info_modules_var_func_options *opt) { return &opt->quiet; },
+    nullptr, /* show_cmd_cb */
+    nullptr /* set_doc */
+  },
+
+  gdb::option::string_option_def<info_modules_var_func_options> {
+    "t",
+    [] (info_modules_var_func_options *opt) { return &opt->type_regexp; },
+    nullptr, /* show_cmd_cb */
+    nullptr /* set_doc */
+  },
+
+  gdb::option::string_option_def<info_modules_var_func_options> {
+    "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_helper (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_helper (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)
 {
@@ -6226,6 +6498,45 @@  REGEXP is given.  The optional flag -q disables printing of headers."));
 		_("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 within each module.\n\
+Usage: info module functions [-q] [-m MODREGEXP] [-t TYPEREGEXP] [REGEXP]\n\
+Print a summary of all functions within each module, grouped by module\n\
+and file.  For each function the line on which the function is defined\n\
+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 within each module.\n\
+Usage: info module variables [-q] [-m MODREGEXP] [-t TYPEREGEXP] [REGEXP]\n\
+Print a summary of all variables within each module, grouped by module\n\
+and file.  For each variable the line on which the variable is defined\n\
+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_info ("sources", info_sources_command,
 	    _("Source files in the program."));
 
diff --git a/gdb/testsuite/gdb.fortran/info-modules.exp b/gdb/testsuite/gdb.fortran/info-modules.exp
index 33110236271..3a39461ffbc 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 <http://www.gnu.org/licenses/>.
 
-# This file tests 'info modules'.
+# This file tests 'info modules', 'info module functions', and 'info
+# module variables'.
 
 load_lib "fortran.exp"
 
@@ -30,13 +31,18 @@  if { ![runto MAIN__] } {
     continue
 }
 
+set integer4 [fortran_int4]
+set real4 [fortran_real4]
+
+# Test 'info modules' command.
+
 gdb_test "info modules" \
     [multi_line \
 	 "All defined modules:" \
 	 "" \
 	 "File .*:" \
 	 "16:\[\t \]+mod1" \
-	 "22:\[\t \]+mod2" ]
+	 "37:\[\t \]+mod2" ]
 
 gdb_test "info modules 1" \
     [multi_line \
@@ -50,7 +56,7 @@  gdb_test "info modules 2" \
 	 "All modules matching regular expression \"2\":" \
 	 "" \
 	 "File .*:" \
-	 "22:\[\t \]+mod2" ]
+	 "37:\[\t \]+mod2" ]
 
 gdb_test "info modules mod" \
     [multi_line \
@@ -58,4 +64,117 @@  gdb_test "info modules mod" \
 	 "" \
 	 "File .*:" \
 	 "16:\[\t \]+mod1" \
-	 "22:\[\t \]+mod2" ]
+	 "37:\[\t \]+mod2" ]
+
+# Test 'info module functions'.
+
+gdb_test "info module functions" \
+    [multi_line \
+	 "All functions in all modules:" \
+	 "" \
+	 "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\\);" \
+	 "" \
+	 "Module \"mod2\":" \
+	 "" \
+	 "File .*:" \
+	 "41:\[\t \]+void mod2::sub_m2_a\\(${integer4}, ${logical4}\\);" \
+	 "49:\[\t \]+${logical4} mod2::sub_m2_b\\(${real4}\\);" ]
+
+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 \"mod1\":" \
+	 "" \
+	 "File .*:" \
+	 "25:\[\t \]+void mod1::sub_m1_a\\(${integer4}\\);" \
+	 "31:\[\t \]+${integer4} mod1::sub_m1_b\\(void\\);" \
+	 "" \
+	 "Module \"mod2\":" \
+	 "" \
+	 "File .*:" \
+	 "41:\[\t \]+void mod2::sub_m2_a\\(${integer4}, ${logical4}\\);" ]
+
+# Test 'info module variables'.
+
+gdb_test "info module variables" \
+    [multi_line \
+	 "All variables in all modules:" \
+	 "" \
+	 "Module \"mod1\":" \
+	 "" \
+	 "File .*:" \
+	 "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;" \
+	 "" \
+	 "Module \"mod2\":" \
+	 "" \
+	 "File .*:" \
+	 "38:\[\t \]+${integer4} mod2::mod2_var_1;" \
+	 "39:\[\t \]+${real4} mod2::mod2_var_2;" ]
+
+gdb_test "info module variables -t real" \
+    [multi_line \
+	 "All variables with type matching regular expression \"real\" in all modules:" \
+	 "" \
+	 "Module \"mod1\":" \
+	 "" \
+	 "File .*:" \
+	 "21:\[\t \]+${real4} mod1::mod1_var_1;" \
+	 "" \
+	 "Module \"mod2\":" \
+	 "" \
+	 "File .*:" \
+	 "39:\[\t \]+${real4} mod2::mod2_var_2;" ]
+
+gdb_test "info module variables -m mod2" \
+    [multi_line \
+	 "All variables in all modules matching regular expression \"mod2\":" \
+	 "" \
+	 "Module \"mod2\":" \
+	 "" \
+	 "File .*:" \
+	 "38:\[\t \]+${integer4} mod2::mod2_var_1;" \
+	 "39:\[\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 .*:" \
+	 "39:\[\t \]+${real4} mod2::mod2_var_2;" ]
+
+gdb_test "info module variables _1" \
+    [multi_line \
+	 "All variables matching regular expression \"_1\" in all modules:" \
+	 "" \
+	 "Module \"mod1\":" \
+	 "" \
+	 "File .*:" \
+	 "21:\[\t \]+${real4} mod1::mod1_var_1;" \
+	 "" \
+	 "Module \"mod2\":" \
+	 "" \
+	 "File .*:" \
+	 "38:\[\t \]+${integer4} mod2::mod2_var_1;" ]
diff --git a/gdb/testsuite/gdb.fortran/info-types.exp b/gdb/testsuite/gdb.fortran/info-types.exp
index b0656bce2b7..71220736cd2 100644
--- a/gdb/testsuite/gdb.fortran/info-types.exp
+++ b/gdb/testsuite/gdb.fortran/info-types.exp
@@ -33,6 +33,7 @@  if { ![runto MAIN__] } {
 set integer4 [fortran_int4]
 set logical4 [fortran_logical4]
 set character1 [fortran_character1]
+set real4 [fortran_real4]
 
 gdb_test "info types" \
     [multi_line \
@@ -42,6 +43,7 @@  gdb_test "info types" \
 	 "\[\t \]+${character1}" \
 	 "\[\t \]+${integer4}" \
 	 "\[\t \]+${logical4}" \
-	 "20:\[\t \]+Type __vtype_mod1_M1t1;" \
+	 "35:\[\t \]+Type __vtype_mod1_M1t1;" \
 	 "17:\[\t \]+Type m1t1;" \
-	 "26:\[\t \]+Type s1;" ]
+	 "\[\t \]+${real4}" \
+	 "57:\[\t \]+Type s1;" ]
diff --git a/gdb/testsuite/gdb.fortran/info-types.f90 b/gdb/testsuite/gdb.fortran/info-types.f90
index 859a9485c8f..d2b61372226 100644
--- a/gdb/testsuite/gdb.fortran/info-types.f90
+++ b/gdb/testsuite/gdb.fortran/info-types.f90
@@ -17,10 +17,41 @@  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
 
 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
 
 program info_types_test