[PATCHv3,3/5] gdb/mi: add regexp filtering to -file-list-exec-source-files

Message ID 504075e85591ea6376bdc915eaa86538235862fa.1623090529.git.andrew.burgess@embecosm.com
State New
Headers show
Series
  • "info sources" - group by objfile
Related show

Commit Message

Andrew Burgess June 7, 2021, 6:32 p.m.
This commit extends the existing MI command
-file-list-exec-source-files to provide the same regular expression
based filtering that the equivalent CLI command "info sources"
provides.

The new command syntax is:

  -file-list-exec-source-files [--basename | --dirname] [--] [REGEXP]

All options are optional, which ensures the command is backward
compatible.

As part of this work I have unified the CLI and MI code.

As a result of the unified code I now provide additional information
in the MI command output, there is now a new field 'debug-fully-read'
included with each source file.  This field which has the values
'true' or 'false', indicates if the source file is from a compilation
unit that has had its debug information fully read.  However, as this
is additional information, a well written front-end should just ignore
this field if it doesn't understand it, so things should still be
backward compatible.

gdb/ChangeLog:

	* NEWS: Mention additions to -file-list-exec-source-files.
	* mi/mi-cmd-file.c (print_partial_file_name): Delete.
	(mi_cmd_file_list_exec_source_files): Rewrite to handle command
	options, and make use of info_sources_worker.
	* symtab.c (struct info_sources_filter): Moved to symtab.h.
	(info_sources_filter::print): Take uiout argument, produce output
	through uiout.
	(struct output_source_filename_data)
	<output_source_filename_data>: Take uiout argument, store into
	m_uiout.  <output>: Rewrite comment, add additional arguments to
	declaration.  <operator()>: Send more arguments to
	output. <m_uiout>: New member variable.
	(output_source_filename_data::output): Take extra arguments,
	produce output through m_uiout, and structure for MI.
	(output_source_filename_data::print_header): Produce output
	through m_uiout.
	(info_sources_worker): New function, the implementation is taken
	from info_sources_command, but modified so produce output through
	a ui_out.
	(info_sources_command): The second half of this function has gone
	to become info_sources_worker.
	* symtab.h (struct info_sources_filter): Moved from symtab.c, add
	extra parameter to print member function.
	(info_sources_worker): Declare.

gdb/doc/ChangeLog:

	* gdb.texinfo (GDB/MI File Commands): Document extensions to
	-file-list-exec-source-files.

gdb/testsuite/ChangeLog:

	* gdb.dwarf2/dw2-filename.exp: Update expected results.
	* gdb.mi/mi-file.exp: Likewise.
	* gdb.mi/mi-info-sources-base.c: New file.
	* gdb.mi/mi-info-sources.c: New file.
	* gdb.mi/mi-info-sources.exp: New file.
---
 gdb/ChangeLog                               |  27 +++
 gdb/NEWS                                    |  18 ++
 gdb/doc/ChangeLog                           |   5 +
 gdb/doc/gdb.texinfo                         |  74 ++++++--
 gdb/mi/mi-cmd-file.c                        |  86 +++++----
 gdb/symtab.c                                | 190 ++++++++++----------
 gdb/symtab.h                                |  63 +++++++
 gdb/testsuite/ChangeLog                     |   8 +
 gdb/testsuite/gdb.dwarf2/dw2-filename.exp   |   2 +-
 gdb/testsuite/gdb.mi/mi-file.exp            |   2 +-
 gdb/testsuite/gdb.mi/mi-info-sources-base.c |  23 +++
 gdb/testsuite/gdb.mi/mi-info-sources.c      |  25 +++
 gdb/testsuite/gdb.mi/mi-info-sources.exp    | 147 +++++++++++++++
 13 files changed, 521 insertions(+), 149 deletions(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-info-sources-base.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-info-sources.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-info-sources.exp

-- 
2.25.4

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index ab678acec8b..8885c4773a8 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -46,6 +46,24 @@ 
     all locations of the selected breakpoint.  This is equivalent to
     the '-force' flag of the CLI's "cond" command.
 
+ ** '-file-list-exec-source-files [--basename | --dirname] [--] [REGEXP]'
+
+    The existing -file-list-exec-source-files command now takes an
+    optional REGEXP which is used to filter the source files that are
+    included in the results.
+
+    By default REGEXP is matched against the full filename of the
+    source file. When one of --basename or --dirname is given then
+    REGEXP is only matched against the specified part of the full
+    source filename.
+
+    The results from -file-list-exec-source-files now include a
+    'debug-fully-read' field which takes the value 'true' or 'false'.
+    A 'true' value indicates the source file is from a compilation
+    unit that has had its debug information fully read in by GDB, a
+    value of 'false' indicates GDB has only performed a partial scan
+    of the debug information so far.
+
 * GDB now supports core file debugging for x86_64 Cygwin programs.
 
 * GDB will now look for the .gdbinit file in a config directory before
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 90d827a50e7..af17eb6260f 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -35624,18 +35624,49 @@ 
 
 
 @subheading The @code{-file-list-exec-source-files} Command
+@kindex info sources
 @findex -file-list-exec-source-files
 
 @subsubheading Synopsis
 
 @smallexample
- -file-list-exec-source-files
-@end smallexample
+ -file-list-exec-source-files @r{[} @var{--dirname} @r{|} @var{--basename} @r{]}
+                              @r{[} -- @r{]}
+                              @r{[} @var{regexp} @r{]}
+@end smallexample
+
+This command returns information about the source files @value{GDBN}
+knows about, it will output both the filename and fullname (absolute
+file name) of a source file, though the fullname can be elided if this
+information is not known to @value{GDBN}.
+
+With no arguments this command returns a list of source files.  Each
+source file is represented by a tuple with the fields; @var{file},
+@var{fullname}, and @var{debug-fully-read}.  The @var{file} is the
+display name for the file, while @var{fullname} is the absolute name
+of the file.  The @var{fullname} field can be elided if the absolute
+name of the source file can't be computed.  The field
+@var{debug-fully-read} will be a string, either @code{true} or
+@code{false}.  When @code{true}, this indicates the full debug
+information for the compilation unit describing this file has been
+read in.  When @code{false}, the full debug information has not yet
+been read in.  While reading in the full debug information it is
+possible that @value{GDBN} could become aware of additional source
+files.
 
-List the source files for the current executable.
+The optional @var{regexp} can be used to filter the list of source
+files returned.  The @var{regexp} will be matched against the full
+source file name.  The matching is case-sensitive, except on operating
+systems that have case-insensitive filesystem (e.g.,
+MS-Windows).  @samp{--} can be used before @var{regexp} to prevent
+@value{GDBN} interpreting @var{regexp} as a command option (e.g.@: if
+@var{regexp} starts with @samp{-}).
 
-It will always output both the filename and fullname (absolute file
-name) of a source file.
+If @code{--dirname} is provided, then @var{regexp} is matched only
+against the directory name of each source file.  If @code{--basename}
+is provided, then @var{regexp} is matched against the basename of each
+source file.  Only one of @code{--dirname} or @code{--basename} may be
+given, and if either is given then @var{regexp} is required.
 
 @subsubheading @value{GDBN} Command
 
@@ -35644,13 +35675,34 @@ 
 
 @subsubheading Example
 @smallexample
-(gdb)
+(@value{GDBP})
 -file-list-exec-source-files
-^done,files=[
-@{file=foo.c,fullname=/home/foo.c@},
-@{file=/home/bar.c,fullname=/home/bar.c@},
-@{file=gdb_could_not_find_fullpath.c@}]
-(gdb)
+^done,files=[@{file="foo.c",fullname="/home/foo.c",debug-fully-read="true"@},
+             @{file="/home/bar.c",fullname="/home/bar.c",debug-fully-read="true"@},
+             @{file="gdb_could_not_find_fullpath.c",debug-fully-read="true"@}]
+(@value{GDBP})
+-file-list-exec-source-files
+^done,files=[@{file="test.c",
+              fullname="/tmp/info-sources/test.c",
+              debug-fully-read="true"@},
+             @{file="/usr/include/stdc-predef.h",
+              fullname="/usr/include/stdc-predef.h",
+              debug-fully-read="true"@},
+             @{file="header.h",
+              fullname="/tmp/info-sources/header.h",
+              debug-fully-read="true"@},
+             @{file="helper.c",
+              fullname="/tmp/info-sources/helper.c",
+              debug-fully-read="true"@}]
+(@value{GDBP})
+-file-list-exec-source-files -- \\.c
+^done,files=[@{file="test.c",
+              fullname="/tmp/info-sources/test.c",
+              debug-fully-read="true"@},
+             @{file="helper.c",
+              fullname="/tmp/info-sources/helper.c",
+              debug-fully-read="true"@}]
+(@value{GDBP})
 @end smallexample
 
 @subheading The @code{-file-list-shared-libraries} Command
diff --git a/gdb/mi/mi-cmd-file.c b/gdb/mi/mi-cmd-file.c
index 430449c919e..684f7eb3f0c 100644
--- a/gdb/mi/mi-cmd-file.c
+++ b/gdb/mi/mi-cmd-file.c
@@ -62,54 +62,64 @@  mi_cmd_file_list_exec_source_file (const char *command, char **argv, int argc)
 		       COMPUNIT_MACRO_TABLE (SYMTAB_COMPUNIT (st.symtab)) != NULL);
 }
 
-/* A callback for map_partial_symbol_filenames.  */
-
-static void
-print_partial_file_name (const char *filename, const char *fullname)
-{
-  struct ui_out *uiout = current_uiout;
-
-  uiout->begin (ui_out_type_tuple, NULL);
-
-  uiout->field_string ("file", filename);
-
-  if (fullname)
-    uiout->field_string ("fullname", fullname);
-
-  uiout->end (ui_out_type_tuple);
-}
+/* Implement -file-list-exec-source-files command.  */
 
 void
 mi_cmd_file_list_exec_source_files (const char *command, char **argv, int argc)
 {
-  struct ui_out *uiout = current_uiout;
-
-  if (!mi_valid_noargs ("-file-list-exec-source-files", argc, argv))
-    error (_("-file-list-exec-source-files: Usage: No args"));
-
-  /* Print the table header.  */
-  uiout->begin (ui_out_type_list, "files");
-
-  /* Look at all of the file symtabs.  */
-  for (objfile *objfile : current_program_space->objfiles ())
+  enum opt
+    {
+      MATCH_BASENAME_OPT,
+      MATCH_DIRNAME_OPT
+    };
+  static const struct mi_opt opts[] =
+  {
+    {"-basename", MATCH_BASENAME_OPT, 0},
+    {"-dirname", MATCH_DIRNAME_OPT, 0},
+    { 0, 0, 0 }
+  };
+
+  /* Parse arguments.  */
+  int oind = 0;
+  char *oarg;
+
+  bool match_on_basename = false;
+  bool match_on_dirname = false;
+
+  while (1)
     {
-      for (compunit_symtab *cu : objfile->compunits ())
+      int opt = mi_getopt ("-file-list-exec-source-files", argc, argv,
+			   opts, &oind, &oarg);
+      if (opt < 0)
+	break;
+      switch ((enum opt) opt)
 	{
-	  for (symtab *s : compunit_filetabs (cu))
-	    {
-	      uiout->begin (ui_out_type_tuple, NULL);
-
-	      uiout->field_string ("file", symtab_to_filename_for_display (s));
-	      uiout->field_string ("fullname", symtab_to_fullname (s));
-
-	      uiout->end (ui_out_type_tuple);
-	    }
+	case MATCH_BASENAME_OPT:
+	  match_on_basename = true;
+	  break;
+	case MATCH_DIRNAME_OPT:
+	  match_on_dirname = true;
+	  break;
 	}
     }
 
-  map_symbol_filenames (print_partial_file_name, true /*need_fullname*/);
+  if ((argc - oind > 1) || (match_on_basename && match_on_dirname))
+    error (_("-file-list-exec-source-files: Usage: [--basename | --dirname] [--] REGEXP"));
+
+  const char *regexp = nullptr;
+  if (argc - oind == 1)
+    regexp = argv[oind];
+
+  info_sources_filter::match_on match_type;
+  if (match_on_dirname)
+    match_type = info_sources_filter::match_on::DIRNAME;
+  else if (match_on_basename)
+    match_type = info_sources_filter::match_on::BASENAME;
+  else
+    match_type = info_sources_filter::match_on::FULLNAME;
 
-  uiout->end (ui_out_type_list);
+  info_sources_filter filter (match_type, regexp);
+  info_sources_worker (current_uiout, filter);
 }
 
 /* See mi-cmds.h.  */
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 2ff79e0cddf..e300596be6e 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4200,58 +4200,6 @@  operator_chars (const char *p, const char **end)
 }
 
 
-/* Class used to encapsulate the filename filtering for the "info sources"
-   command.  */
-struct info_sources_filter
-{
-  /* If filename filtering is being used (see M_C_REGEXP) then which part
-     of the filename is being filtered against?  */
-  enum class match_on
-  {
-    /* Match against the full filename.  */
-    FULLNAME,
-
-    /* Match only against the directory part of the full filename.  */
-    DIRNAME,
-
-    /* Match only against the basename part of the full filename.  */
-    BASENAME
-  };
-
-  /* Create a filter of MATCH_TYPE using regular expression REGEXP.  If
-     REGEXP is nullptr then all files will match the filter and MATCH_TYPE
-     is ignored.
-
-     The string pointed too by REGEXP must remain live and unchanged for
-     this lifetime of this object as the object only retains a copy of the
-     pointer.  */
-  info_sources_filter (match_on match_type, const char *regexp);
-
-  DISABLE_COPY_AND_ASSIGN (info_sources_filter);
-
-  /* Does FULLNAME match the filter defined by this object, return true if
-     it does, otherwise, return false.  If there is no filtering defined
-     then this function will always return true.  */
-  bool matches (const char *fullname) const;
-
-  /* Print a single line describing this filter, used as part of the "info
-     sources" command output.  If there is no filter in place then nothing
-     is printed.  */
-  void print () const;
-
-private:
-
-  /* The type of filtering in place.  */
-  match_on m_match_type;
-
-  /* Points to the original regexp used to create this filter.  */
-  const char *m_regexp;
-
-  /* A compiled version of M_REGEXP.  This object is only given a value if
-     M_REGEXP is not nullptr and is not the empty string.  */
-  gdb::optional<compiled_regex> m_c_regexp;
-};
-
 /* See class declaration.  */
 
 info_sources_filter::info_sources_filter (match_on match_type,
@@ -4307,7 +4255,7 @@  info_sources_filter::matches (const char *fullname) const
 /* See class declaration.  */
 
 void
-info_sources_filter::print () const
+info_sources_filter::print (struct ui_out *uiout) const
 {
   if (m_c_regexp.has_value ())
     {
@@ -4316,12 +4264,12 @@  info_sources_filter::print () const
       switch (m_match_type)
 	{
 	case match_on::DIRNAME:
-	  printf_filtered (_("(dirname matching regular expression \"%s\")"),
-			   m_regexp);
+	  uiout->message (_("(dirname matching regular expression \"%s\")"),
+			  m_regexp);
 	  break;
 	case match_on::BASENAME:
-	  printf_filtered (_("(basename matching regular expression \"%s\")"),
-			   m_regexp);
+	  uiout->message (_("(basename matching regular expression \"%s\")"),
+			  m_regexp);
 	  break;
 	case match_on::FULLNAME:
 	  printf_filtered (_("(filename matching regular expression \"%s\")"),
@@ -4337,10 +4285,12 @@  info_sources_filter::print () const
 struct output_source_filename_data
 {
   /* Create an object for displaying the results of the 'info sources'
-     command.  FILTER must remain valid and unchanged for the lifetime of
-     this object as this object retains a reference to FILTER.  */
-  output_source_filename_data (const info_sources_filter &filter)
-    : m_filter (filter)
+     command to UIOUT.  FILTER must remain valid and unchanged for the
+     lifetime of this object as this object retains a reference to FILTER.  */
+  output_source_filename_data (struct ui_out *uiout,
+			       const info_sources_filter &filter)
+    : m_filter (filter),
+      m_uiout (uiout)
   { /* Nothing.  */ }
 
   DISABLE_COPY_AND_ASSIGN (output_source_filename_data);
@@ -4353,9 +4303,14 @@  struct output_source_filename_data
     m_filename_seen_cache.clear ();
   }
 
-  /* Worker for sources_info.  Force line breaks at ,'s.  NAME is the name
-     to print.  */
-  void output (const char *name);
+  /* Worker for sources_info, outputs the file name formatted for either
+     cli or mi (based on the current_uiout).  In cli mode displays
+     FULLNAME with a comma separating this name from any previously
+     printed name (line breaks are added at the comma).  In MI mode
+     outputs a tuple containing DISP_NAME (the files display name),
+     FULLNAME, and EXPANDED_P (true when this file is from a fully
+     expanded symtab, otherwise false).  */
+  void output (const char *disp_name, const char *fullname, bool expanded_p);
 
   /* Prints the header messages for the source files that will be printed
      with the matching info present in the current object state.
@@ -4367,7 +4322,9 @@  struct output_source_filename_data
      quick_symbol_functions::map_symbol_filenames.  */
   void operator() (const char *filename, const char *fullname)
   {
-    output (fullname != nullptr ? fullname : filename);
+    /* The false here indicates that this file is from an unexpanded
+       symtab.  */
+    output (filename, fullname, false);
   }
 
 private:
@@ -4380,12 +4337,17 @@  struct output_source_filename_data
 
   /* How source filename should be filtered.  */
   const info_sources_filter &m_filter;
+
+  /* The object to which output is sent.  */
+  struct ui_out *m_uiout;
 };
 
 /* See comment in class declaration above.  */
 
 void
-output_source_filename_data::output (const char *name)
+output_source_filename_data::output (const char *disp_name,
+				     const char *fullname,
+				     bool expanded_p)
 {
   /* Since a single source file can result in several partial symbol
      tables, we need to avoid printing it more than once.  Note: if
@@ -4397,20 +4359,37 @@  output_source_filename_data::output (const char *name)
      symtabs; it doesn't hurt to check.  */
 
   /* Was NAME already seen?  If so, then don't print it again.  */
-  if (m_filename_seen_cache.seen (name))
+  if (m_filename_seen_cache.seen (fullname))
     return;
 
   /* If the filter rejects this file then don't print it.  */
-  if (!m_filter.matches (name))
+  if (!m_filter.matches (fullname))
     return;
 
+  ui_out_emit_tuple ui_emitter (m_uiout, nullptr);
+
   /* Print it and reset *FIRST.  */
   if (!m_first)
-    printf_filtered (", ");
+    m_uiout->text (", ");
   m_first = false;
 
   wrap_here ("");
-  fputs_styled (name, file_name_style.style (), gdb_stdout);
+  if (m_uiout->is_mi_like_p ())
+    {
+      m_uiout->field_string ("file", disp_name, file_name_style.style ());
+      if (fullname != nullptr)
+	m_uiout->field_string ("fullname", fullname,
+			       file_name_style.style ());
+      m_uiout->field_string ("debug-fully-read",
+			     (expanded_p ? "true" : "false"));
+    }
+  else
+    {
+      if (fullname == nullptr)
+	fullname = disp_name;
+      m_uiout->field_string ("fullname", fullname,
+			     file_name_style.style ());
+    }
 }
 
 /* See comment is class declaration above.  */
@@ -4418,9 +4397,9 @@  output_source_filename_data::output (const char *name)
 void
 output_source_filename_data::print_header (const char *symbol_msg)
 {
-  puts_filtered (symbol_msg);
-  m_filter.print ();
-  puts_filtered ("\n");
+  m_uiout->text (symbol_msg);
+  m_filter.print (m_uiout);
+  m_uiout->text ("\n");
 }
 
 /* For the 'info sources' command, what part of the file names should we be
@@ -4476,6 +4455,42 @@  info_sources_command_completer (cmd_list_element *ignore,
     return;
 }
 
+/* See symtab.h.  */
+
+void
+info_sources_worker (struct ui_out *uiout,
+		     const info_sources_filter &filter)
+{
+  output_source_filename_data data (uiout, filter);
+
+  ui_out_emit_list results_emitter (uiout, "files");
+  gdb::optional<ui_out_emit_tuple> output_tuple;
+  gdb::optional<ui_out_emit_list> sources_list;
+
+  if (!uiout->is_mi_like_p ())
+    data.print_header (_("Source files for which symbols have been read in:\n"));
+
+  for (objfile *objfile : current_program_space->objfiles ())
+    {
+      for (compunit_symtab *cu : objfile->compunits ())
+	{
+	  for (symtab *s : compunit_filetabs (cu))
+	    {
+	      const char *file = symtab_to_filename_for_display (s);
+	      const char *fullname = symtab_to_fullname (s);
+	      data.output (file, fullname, true);
+	    }
+	}
+    }
+
+  uiout->text ("\n\n");
+  if (!uiout->is_mi_like_p ())
+    data.print_header (_("Source files for which symbols will be read in on demand:\n"));
+  data.reset_output ();
+  map_symbol_filenames (data, true /*need_fullname*/);
+  uiout->text ("\n");
+}
+
 /* Implement the 'info sources' command.  */
 
 static void
@@ -4493,7 +4508,7 @@  info_sources_command (const char *args, int from_tty)
     error (_("You cannot give both -basename and -dirname to 'info sources'."));
 
   const char *regex = nullptr;
-  if (args != nullptr && *args != '\000')
+  if (args != NULL && *args != '\000')
     regex = args;
 
   if ((match_opts.dirname || match_opts.basename) && regex == nullptr)
@@ -4508,29 +4523,7 @@  info_sources_command (const char *args, int from_tty)
     match_type = info_sources_filter::match_on::FULLNAME;
 
   info_sources_filter filter (match_type, regex);
-  output_source_filename_data data (filter);
-
-  data.print_header (_("Source files for which symbols have been read in:\n"));
-
-  for (objfile *objfile : current_program_space->objfiles ())
-    {
-      for (compunit_symtab *cu : objfile->compunits ())
-	{
-	  for (symtab *s : compunit_filetabs (cu))
-	    {
-	      const char *fullname = symtab_to_fullname (s);
-
-	      data.output (fullname);
-	    }
-	}
-    }
-  printf_filtered ("\n\n");
-
-  data.print_header (_("Source files for which symbols will be read in on demand:\n"));
-
-  data.reset_output ();
-  map_symbol_filenames (data, true /*need_fullname*/);
-  printf_filtered ("\n");
+  info_sources_worker (current_uiout, filter);
 }
 
 /* Compare FILE against all the entries of FILENAMES.  If BASENAMES is
@@ -6890,7 +6883,8 @@  Print information about all types matching REGEXP, or all types if no\n\
 REGEXP is given.  The optional flag -q disables printing of headers."));
   set_cmd_completer_handle_brkchars (c, info_types_command_completer);
 
-  const auto info_sources_opts = make_info_sources_options_def_group (nullptr);
+  const auto info_sources_opts
+    = make_info_sources_options_def_group (nullptr);
 
   static std::string info_sources_help
     = gdb::option::build_help (_("\
diff --git a/gdb/symtab.h b/gdb/symtab.h
index efdbada9761..7dd659284ed 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -2384,4 +2384,67 @@  class symbol_searcher
   std::vector<bound_minimal_symbol> m_minimal_symbols;
 };
 
+/* Class used to encapsulate the filename filtering for the "info sources"
+   command.  */
+
+struct info_sources_filter
+{
+  /* If filename filtering is being used (see M_C_REGEXP) then which part
+     of the filename is being filtered against?  */
+  enum class match_on
+  {
+    /* Match against the full filename.  */
+    FULLNAME,
+
+    /* Match only against the directory part of the full filename.  */
+    DIRNAME,
+
+    /* Match only against the basename part of the full filename.  */
+    BASENAME
+  };
+
+  /* Create a filter of MATCH_TYPE using regular expression REGEXP.  If
+     REGEXP is nullptr then all files will match the filter and MATCH_TYPE
+     is ignored.
+
+     The string pointed too by REGEXP must remain live and unchanged for
+     this lifetime of this object as the object only retains a copy of the
+     pointer.  */
+  info_sources_filter (match_on match_type, const char *regexp);
+
+  DISABLE_COPY_AND_ASSIGN (info_sources_filter);
+
+  /* Does FULLNAME match the filter defined by this object, return true if
+     it does, otherwise, return false.  If there is no filtering defined
+     then this function will always return true.  */
+  bool matches (const char *fullname) const;
+
+  /* Print a single line describing this filter to UIOUT, used as part of
+     the "info sources" command output.  If there is no filter in place
+     then nothing is printed.  */
+  void print (struct ui_out *uiout) const;
+
+private:
+
+  /* The type of filtering in place.  */
+  match_on m_match_type;
+
+  /* Points to the original regexp used to create this filter.  */
+  const char *m_regexp;
+
+  /* A compiled version of M_REGEXP.  This object is only given a value if
+     M_REGEXP is not nullptr and is not the empty string.  */
+  gdb::optional<compiled_regex> m_c_regexp;
+};
+
+/* Perform the core of the 'info sources' command.
+
+   FILTER is used to perform regular expression based filtering on the
+   source files that will be displayed.
+
+   Output is written to UIOUT in CLI or MI style as appropriate.  */
+
+extern void info_sources_worker (struct ui_out *uiout,
+				 const info_sources_filter &filter);
+
 #endif /* !defined(SYMTAB_H) */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-filename.exp b/gdb/testsuite/gdb.dwarf2/dw2-filename.exp
index 3e60f7d2bc9..bd303c8547b 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-filename.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-filename.exp
@@ -37,7 +37,7 @@  clean_restart ${testfile}
 # the full path to that file.  What we want to verify, most of all,
 # is that the file and fullname fields are now inverted.
 gdb_test "interpreter-exec mi -file-list-exec-source-files" \
-         ".*{file=\"file1\\.txt\",fullname=\".+file1\\.txt\"}.*"
+    ".*{file=\"file1\\.txt\",fullname=\".+file1\\.txt\",debug-fully-read=\"\[^\"\]+\"}.*"
 
 # And `info sources' should return the fullname incl. the directories.
 gdb_test "info sources" {[/]file1\.txt.*}
diff --git a/gdb/testsuite/gdb.mi/mi-file.exp b/gdb/testsuite/gdb.mi/mi-file.exp
index 15d7d9f0944..4cae33c017e 100644
--- a/gdb/testsuite/gdb.mi/mi-file.exp
+++ b/gdb/testsuite/gdb.mi/mi-file.exp
@@ -68,7 +68,7 @@  proc test_file_list_exec_source_files {} {
 
     # get the path and absolute path to the current executable
     mi_gdb_test "222-file-list-exec-source-files" \
-	    "222\\\^done,files=\\\[\{file=\".*${srcfile}\",fullname=\"$fullname_syntax${srcfile}\"\}.*]" \
+	    "222\\\^done,files=\\\[\{file=\".*${srcfile}\",fullname=\"$fullname_syntax${srcfile}\",debug-fully-read=\"\[^\"\]+\"\}.*]" \
               "Getting a list of source files."
 }
 
diff --git a/gdb/testsuite/gdb.mi/mi-info-sources-base.c b/gdb/testsuite/gdb.mi/mi-info-sources-base.c
new file mode 100644
index 00000000000..cc736123600
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-info-sources-base.c
@@ -0,0 +1,23 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2021 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 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/>.  */
+
+/* This file was originally copied from gdb.base/info_sources_base.c.  */
+
+void some_other_func (void)
+{
+  return;
+}
diff --git a/gdb/testsuite/gdb.mi/mi-info-sources.c b/gdb/testsuite/gdb.mi/mi-info-sources.c
new file mode 100644
index 00000000000..b91b8280c2b
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-info-sources.c
@@ -0,0 +1,25 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2021 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 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/>.  */
+
+/* This file was originally copied from gdb.base/info_sources.c.  */
+
+extern void some_other_func (void);
+int main ()
+{
+  some_other_func ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.mi/mi-info-sources.exp b/gdb/testsuite/gdb.mi/mi-info-sources.exp
new file mode 100644
index 00000000000..c218af4ba80
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-info-sources.exp
@@ -0,0 +1,147 @@ 
+# Copyright 2021 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 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/>.
+
+# Test the -file-list-exec-source-files command.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+standard_testfile .c -base.c
+
+if {[prepare_for_testing $testfile.exp $testfile \
+	 [list $srcfile $srcfile2] debug]} {
+    untested $testfile.exp
+    return -1
+}
+
+mi_clean_restart $binfile
+
+mi_runto_main
+
+# Helper to build expected MI output pattern for a list.  NAME is the
+# name of the list (which can be the empty string) and args is one
+# or more strings representing the fields of the list, which will be
+# joined with a comma.
+#
+# If any of the fields in args matches ".*" then the comma before and
+# after are dropped from the final pattern.
+proc mi_list { name args } {
+    set str ""
+
+    if { $name != "" } {
+	set str "${name}="
+    }
+
+    set pattern ""
+    foreach a $args {
+	if { [string length $pattern] > 0 } {
+	    if { [string range $pattern end-1 end] != ".*" \
+		     && [string range $a 0 1] != ".*" } {
+		set pattern "${pattern},"
+	    }
+	}
+	set pattern "${pattern}${a}"
+    }
+    set str "$str\\\[${pattern}\\\]"
+    return ${str}
+}
+
+# Helper to build expected MI output pattern for a tuple.  NAME is the
+# name of the tuple (which can be the empty string) and args is one
+# or more strings representing the fields of the tuple, which will be
+# joined with a comma.
+#
+# If any of the fields in args matches ".*" then the comma before and
+# after are dropped from the final pattern.
+proc mi_tuple { name args } {
+    set str ""
+
+    if { $name != "" } {
+	set str "${name}="
+    }
+
+    set pattern ""
+    foreach a $args {
+	if { [string length $pattern] > 0 } {
+	    if { [string range $pattern end-1 end] != ".*" \
+		     && [string range $a 0 1] != ".*" } {
+		set pattern "${pattern},"
+	    }
+	}
+	set pattern "${pattern}${a}"
+    }
+    set str "$str\\{${pattern}\\}"
+    return ${str}
+}
+
+# Helper to build expected MI output pattern for a single field.  NAME
+# is the name of the field, and PATTERN matches the fields contents.
+# This proc will add quotes around PATTERN.
+proc mi_field { name pattern } {
+    set str ""
+
+    if { $name != "" } {
+	set str "${name}="
+    }
+
+    set str "$str\"${pattern}\""
+    return ${str}
+}
+
+# Run tests on '-file-list-exec-source-files'.  DEBUG_FULLY_READ is either the string
+# "true" or "false" and indicates if the GDB will have read all the
+# debug for the test program or not yet.
+proc check_info_sources { debug_fully_read } {
+
+    with_test_prefix "debug_read=${debug_fully_read}" {
+
+	if { $debug_fully_read } {
+	    set p [mi_list "files" \
+		       [mi_tuple "" \
+			    [mi_field "file" "\[^\"\]+/mi-info-sources-base\\.c"] \
+			    [mi_field "fullname" "\[^\"\]+/mi-info-sources-base\\.c"] \
+			    [mi_field "debug-fully-read" "${debug_fully_read}"]] \
+		       [mi_tuple "" \
+			    [mi_field "file" "\[^\"\]+/mi-info-sources\\.c"] \
+			    [mi_field "fullname" "\[^\"\]+/mi-info-sources\\.c"] \
+			    [mi_field "debug-fully-read" "true"]]]
+	} else {
+	    set p [mi_list "files" \
+		       [mi_tuple "" \
+			    [mi_field "file" "\[^\"\]+/mi-info-sources\\.c"] \
+			    [mi_field "fullname" "\[^\"\]+/mi-info-sources\\.c"] \
+			    [mi_field "debug-fully-read" "true"]] \
+		       [mi_tuple "" \
+			    [mi_field "file" "\[^\"\]+/mi-info-sources-base\\.c"] \
+			    [mi_field "fullname" "\[^\"\]+/mi-info-sources-base\\.c"] \
+			    [mi_field "debug-fully-read" "${debug_fully_read}"]]]
+	}
+	mi_gdb_test "-file-list-exec-source-files" ".*\\^done,${p}" "-file-list-exec-source-files"
+
+	set p [mi_list "files" \
+		   [mi_tuple "" \
+			[mi_field "file" "\[^\"\]+/mi-info-sources-base\\.c"] \
+			[mi_field "fullname" "\[^\"\]+/mi-info-sources-base\\.c"] \
+			[mi_field "debug-fully-read" "${debug_fully_read}"]]]
+	mi_gdb_test "-file-list-exec-source-files --basename -- base" ".*\\^done,${p}" \
+	    "-file-list-exec-source-files --basename -- base"
+    }
+}
+
+check_info_sources "false"
+
+mi_continue_to "some_other_func"
+
+check_info_sources "true"