[3/4] gdb: add new -group-by-binary flag to info sources command

Message ID 760d52a0ea6ab8c2195d71feea794f0bec70a163.1619456691.git.andrew.burgess@embecosm.com
State New
Headers show
Series
  • New option for 'info sources', also better MI support
Related show

Commit Message

Andrew Burgess April 26, 2021, 5:07 p.m.
Currently the 'info sources' command lists all of the known source
files together, regardless of their source, e.g. here is a session
debugging a test application that makes use of a shared library:

  (gdb) info sources
  Source files for which symbols have been read in:

  /tmp/info-sources/test.c, /usr/include/stdc-predef.h,
  /tmp/info-sources/header.h, /tmp/info-sources/helper.c

  Source files for which symbols will be read in on demand:

  (gdb)

In this commit I add a new flag to the 'info sources' command,
'-group-by-binary'.  When this flag is provided the results are
grouped by the binary file that uses that source file.  Here's the
same session using the new flag:

  (gdb) info sources -group-by-binary
  /tmp/info-sources/test.x:

  /tmp/info-sources/test.c, /usr/include/stdc-predef.h,
  /tmp/info-sources/header.h

  /lib64/ld-linux-x86-64.so.2:
  (Full debug information has not yet been read for this file.)

  system-supplied DSO at 0x7ffff7fcf000:
  (Full debug information has not yet been read for this file.)

  /tmp/info-sources/libhelper.so:

  /tmp/info-sources/helper.c, /usr/include/stdc-predef.h,
  /tmp/info-sources/header.h

  /lib64/libc.so.6:
  (Full debug information has not yet been read for this file.)

  (gdb)

Notice that in the new output some source files are repeated,
e.g. /tmp/info-sources/header.h, as multiple binaries use this source
file.

All of the existing regular expression based filtering that exists for
'info sources' still works with the new option.

gdb/ChangeLog:

	* NEWS: Mention new 'info sources' option.
	* symtab.c (struct filename_grouping_opts): New struct.
	(struct output_source_filename_data) <printed_filename_p>: New
	member function.
	(isrc_flag_option_def): Rename to...
	(isrc_match_flag_option_def): ...this.
	(info_sources_option_defs): Rename to...
	(info_sources_match_option_defs): ...this.  Update to take account
	of isrc_flag_option_def being renamed.
	(isrc_grouping_flag_option_def): New typedef.
	(info_sources_grouping_option_defs): New static global.
	(make_info_sources_options_def_group): Update to take two
	parameters, and return an array.
	(info_sources_command_completer): Update for changes to
	make_info_sources_options_def_group.
	(info_sources_command): Update to display results grouped by
	objfile name if the user supplies the required flag.
	(_initialize_symtab): Update for changes to
	make_info_sources_options_def_group.

gdb/doc/ChangeLog:

	* gdb.textinfo (Symbols): Merge two descriptions of 'info
	sources', and add description of new '-group-by-binary' flag.

gdb/testsuite/ChangeLog:

	* gdb.base/info_sources.exp: Add additional test.
	* gdb.base/info_sources_2-header.h: New file.
	* gdb.base/info_sources_2-lib.c: New file.
	* gdb.base/info_sources_2-test.c: New file.
	* gdb.base/info_sources_2.exp: New file.
---
 gdb/ChangeLog                                 |  22 +++
 gdb/NEWS                                      |   7 +
 gdb/doc/ChangeLog                             |   5 +
 gdb/doc/gdb.texinfo                           |  37 ++--
 gdb/symtab.c                                  |  92 ++++++++--
 gdb/testsuite/ChangeLog                       |   8 +
 gdb/testsuite/gdb.base/info_sources.exp       |   5 +
 .../gdb.base/info_sources_2-header.h          |  28 +++
 gdb/testsuite/gdb.base/info_sources_2-lib.c   |  25 +++
 gdb/testsuite/gdb.base/info_sources_2-test.c  |  26 +++
 gdb/testsuite/gdb.base/info_sources_2.exp     | 169 ++++++++++++++++++
 11 files changed, 393 insertions(+), 31 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/info_sources_2-header.h
 create mode 100644 gdb/testsuite/gdb.base/info_sources_2-lib.c
 create mode 100644 gdb/testsuite/gdb.base/info_sources_2-test.c
 create mode 100644 gdb/testsuite/gdb.base/info_sources_2.exp

-- 
2.25.4

Comments

Carl Love via Gdb-patches April 26, 2021, 5:34 p.m. | #1
> From: Andrew Burgess <andrew.burgess@embecosm.com>

> Date: Mon, 26 Apr 2021 18:07:02 +0100

> 

> +info sources [-group-by-binary] [-dirname] [-basename] [REGEXP]

> +  The 'info sources' command now supports a new flag

> +  '-group-by-binary'.  When this flag is supplied the results are

> +  formatted as a list of loaded binaries followed by the source files

> +  for each binary.  A single source file can appear multiple times in

> +  the output if it is part of multiple binaries.


I'm worried about the confusion we will cause by the "binary"
terminology.  Elsewhere we call these "object files"; see, for
example, "Auto-loading sequences".  Can we keep our terminology
consistent?

> +When the optional @code{-group-by-binary} flag is given then the

> +output is organized as a list of the binaries currently loaded into

> +@value{GDBN}, for each binary, all of the source files associated with

> +that binary are given.  A single source file can be repeated in this

> +output format, if it is part of multiple binaries.


And here you don't even explain what is a "binary" for this purpose.

> +If the optional @var{regexp} is provided, then only source files that

> +match the regular expression will be printed.  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{-}).


I wonder how the new output will look when REGEXP is given.  Suppose
some binary has no sources that match the regexp: will that binary
appear with an empty list of source files, or will it not appear at
all?  And if the latter, isn't it confusing?

Thanks.
Carl Love via Gdb-patches May 13, 2021, 3:05 p.m. | #2
On 2021-04-26 1:07 p.m., Andrew Burgess wrote:
> Currently the 'info sources' command lists all of the known source

> files together, regardless of their source, e.g. here is a session

> debugging a test application that makes use of a shared library:

> 

>   (gdb) info sources

>   Source files for which symbols have been read in:

> 

>   /tmp/info-sources/test.c, /usr/include/stdc-predef.h,

>   /tmp/info-sources/header.h, /tmp/info-sources/helper.c

> 

>   Source files for which symbols will be read in on demand:

> 

>   (gdb)

> 

> In this commit I add a new flag to the 'info sources' command,

> '-group-by-binary'.  When this flag is provided the results are

> grouped by the binary file that uses that source file.  Here's the

> same session using the new flag:

> 

>   (gdb) info sources -group-by-binary

>   /tmp/info-sources/test.x:

> 

>   /tmp/info-sources/test.c, /usr/include/stdc-predef.h,

>   /tmp/info-sources/header.h

> 

>   /lib64/ld-linux-x86-64.so.2:

>   (Full debug information has not yet been read for this file.)

> 

>   system-supplied DSO at 0x7ffff7fcf000:

>   (Full debug information has not yet been read for this file.)

> 

>   /tmp/info-sources/libhelper.so:

> 

>   /tmp/info-sources/helper.c, /usr/include/stdc-predef.h,

>   /tmp/info-sources/header.h

> 

>   /lib64/libc.so.6:

>   (Full debug information has not yet been read for this file.)

> 

>   (gdb)

> 

> Notice that in the new output some source files are repeated,

> e.g. /tmp/info-sources/header.h, as multiple binaries use this source

> file.

> 

> All of the existing regular expression based filtering that exists for

> 'info sources' still works with the new option.


In my opinion the new output is just better, more structured.  Do we
even need to hide it behind an option?  Can we just... change it and
that's it?  I'd lke if we didn't have to keep the extra complexity
unnecessarily.

Simon
Andrew Burgess May 15, 2021, 8:45 a.m. | #3
* Simon Marchi <simon.marchi@polymtl.ca> [2021-05-13 11:05:44 -0400]:

> On 2021-04-26 1:07 p.m., Andrew Burgess wrote:

> > Currently the 'info sources' command lists all of the known source

> > files together, regardless of their source, e.g. here is a session

> > debugging a test application that makes use of a shared library:

> > 

> >   (gdb) info sources

> >   Source files for which symbols have been read in:

> > 

> >   /tmp/info-sources/test.c, /usr/include/stdc-predef.h,

> >   /tmp/info-sources/header.h, /tmp/info-sources/helper.c

> > 

> >   Source files for which symbols will be read in on demand:

> > 

> >   (gdb)

> > 

> > In this commit I add a new flag to the 'info sources' command,

> > '-group-by-binary'.  When this flag is provided the results are

> > grouped by the binary file that uses that source file.  Here's the

> > same session using the new flag:

> > 

> >   (gdb) info sources -group-by-binary

> >   /tmp/info-sources/test.x:

> > 

> >   /tmp/info-sources/test.c, /usr/include/stdc-predef.h,

> >   /tmp/info-sources/header.h

> > 

> >   /lib64/ld-linux-x86-64.so.2:

> >   (Full debug information has not yet been read for this file.)

> > 

> >   system-supplied DSO at 0x7ffff7fcf000:

> >   (Full debug information has not yet been read for this file.)

> > 

> >   /tmp/info-sources/libhelper.so:

> > 

> >   /tmp/info-sources/helper.c, /usr/include/stdc-predef.h,

> >   /tmp/info-sources/header.h

> > 

> >   /lib64/libc.so.6:

> >   (Full debug information has not yet been read for this file.)

> > 

> >   (gdb)

> > 

> > Notice that in the new output some source files are repeated,

> > e.g. /tmp/info-sources/header.h, as multiple binaries use this source

> > file.

> > 

> > All of the existing regular expression based filtering that exists for

> > 'info sources' still works with the new option.

> 

> In my opinion the new output is just better, more structured.  Do we

> even need to hide it behind an option?  Can we just... change it and

> that's it?  I'd lke if we didn't have to keep the extra complexity

> unnecessarily.


I'm not against changing the output, but I thought as a rule we didn't
significantly change existing command behaviour unless we could show
that the existing behaviour was wrong.

I guess how do folk feel about changing this behaviour?

Thanks,
Andrew
Carl Love via Gdb-patches May 15, 2021, 1:19 p.m. | #4
On 2021-05-15 4:45 a.m., Andrew Burgess wrote:
> I'm not against changing the output, but I thought as a rule we didn't

> significantly change existing command behaviour unless we could show

> that the existing behaviour was wrong.

>

> I guess how do folk feel about changing this behaviour?


For the CLI, I think we can have a bit of "artistic freedom" to improve
the output, as long as we don't regress use cases.  If we all agree the
new format is better, I would find it disappointing if the fruit of your
work was hidden behind an option, where most people won't know about it
and won't use it.

Simon

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 1cdf19c09a0..f84ea0427a7 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -157,6 +157,13 @@  ptype[/FLAGS] TYPE | EXPRESSION
   offsets of struct members.  Default behavior is given by 'show print
   type hex'.
 
+info sources [-group-by-binary] [-dirname] [-basename] [REGEXP]
+  The 'info sources' command now supports a new flag
+  '-group-by-binary'.  When this flag is supplied the results are
+  formatted as a list of loaded binaries followed by the source files
+  for each binary.  A single source file can appear multiple times in
+  the output if it is part of multiple binaries.
+
 * Removed targets and native configurations
 
 ARM Symbian			arm*-*-symbianelf*
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a068de6c95b..03bf603ad42 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -19335,20 +19335,29 @@ 
 
 
 @kindex info sources
-@item info sources
-Print the names of all source files in your program for which there is
-debugging information, organized into two lists: files whose symbols
-have already been read, and files whose symbols will be read when needed.
-
-@item info sources [-dirname | -basename] [--] [@var{regexp}]
-Like @samp{info sources}, but only print the names of the files
-matching the provided @var{regexp}.
-By default, the @var{regexp} is used to match anywhere in the filename.
-If @code{-dirname}, only files having a dirname matching @var{regexp} are shown.
-If  @code{-basename}, only files having a basename matching @var{regexp}
-are shown.
-The matching is case-sensitive, except on operating systems that
-have case-insensitive filesystem (e.g., MS-Windows).
+@item info sources @r{[}-group-by-binary@r{]} @r{[}-dirname | -basename@r{]} @r{[}--@r{]} @r{[}@var{regexp}@r{]}
+With no options @samp{info sources} prints the names of all source
+files in your program for which there is debugging information,
+organized into two lists: files whose symbols have already been read,
+and files whose symbols will be read when needed.
+
+When the optional @code{-group-by-binary} flag is given then the
+output is organized as a list of the binaries currently loaded into
+@value{GDBN}, for each binary, all of the source files associated with
+that binary are given.  A single source file can be repeated in this
+output format, if it is part of multiple binaries.
+
+If the optional @var{regexp} is provided, then only source files that
+match the regular expression will be printed.  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{-}).
+
+By default, the @var{regexp} is used to match anywhere in the
+filename.  If @code{-dirname}, only files having a dirname matching
+@var{regexp} are shown.  If @code{-basename}, only files having a
+basename matching @var{regexp} are shown.
 
 @kindex info functions
 @item info functions [-q] [-n]
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 5650d225752..d8d0667dfdb 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4212,6 +4212,14 @@  struct filename_partial_match_opts
   bool basename = false;
 };
 
+/* For the 'info sources' command, should the results be grouped by objfile
+   name?  */
+struct filename_grouping_opts
+{
+  /* Group source files by the containing binary.  */
+  bool group_by_binary = false;
+};
+
 /* Data structure to maintain the state used for printing the results of
    the 'info sources' command.  */
 
@@ -4255,6 +4263,14 @@  struct output_source_filename_data
     output (fullname != nullptr ? fullname : filename);
   }
 
+  /* Return true if at least one filename has been printed (after a call to
+     output) since either this object was created, or the last call to
+     reset_output.  */
+  bool printed_filename_p () const
+  {
+    return !m_first;
+  }
+
 private:
 
   /* Set the current regular expression, used for matching against files,
@@ -4358,18 +4374,18 @@  output_source_filename_data::print_header (const char *symbol_msg)
   puts_filtered ("\n");
 }
 
-using isrc_flag_option_def
+using isrc_match_flag_option_def
   = gdb::option::flag_option_def<filename_partial_match_opts>;
 
-static const gdb::option::option_def info_sources_option_defs[] = {
+static const gdb::option::option_def info_sources_match_option_defs[] = {
 
-  isrc_flag_option_def {
+  isrc_match_flag_option_def {
     "dirname",
     [] (filename_partial_match_opts *opts) { return &opts->dirname; },
     N_("Show only the files having a dirname matching REGEXP."),
   },
 
-  isrc_flag_option_def {
+  isrc_match_flag_option_def {
     "basename",
     [] (filename_partial_match_opts *opts) { return &opts->basename; },
     N_("Show only the files having a basename matching REGEXP."),
@@ -4377,13 +4393,31 @@  static const gdb::option::option_def info_sources_option_defs[] = {
 
 };
 
+using isrc_grouping_flag_option_def
+  = gdb::option::flag_option_def<filename_grouping_opts>;
+
+static const gdb::option::option_def info_sources_grouping_option_defs[] = {
+
+  isrc_grouping_flag_option_def {
+    "group-by-binary",
+    [] (filename_grouping_opts *opts) { return &opts->group_by_binary; },
+    N_("Group source files by the binary they are from."),
+  },
+
+};
+
 /* Create an option_def_group for the "info sources" options, with
    ISRC_OPTS as context.  */
 
-static inline gdb::option::option_def_group
-make_info_sources_options_def_group (filename_partial_match_opts *isrc_opts)
+static inline std::array<gdb::option::option_def_group, 2>
+make_info_sources_options_def_group
+	(filename_partial_match_opts *isrc_match_opts,
+	 filename_grouping_opts *isrc_grp_opts)
 {
-  return {{info_sources_option_defs}, isrc_opts};
+  return {{
+      {{info_sources_match_option_defs}, isrc_match_opts},
+      {{info_sources_grouping_option_defs}, isrc_grp_opts},
+    }};
 }
 
 /* Completer for "info sources".  */
@@ -4393,7 +4427,7 @@  info_sources_command_completer (cmd_list_element *ignore,
 				completion_tracker &tracker,
 				const char *text, const char *word)
 {
-  const auto group = make_info_sources_options_def_group (nullptr);
+  const auto group = make_info_sources_options_def_group (nullptr, nullptr);
   if (gdb::option::complete_options
       (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
     return;
@@ -4407,14 +4441,17 @@  info_sources_command (const char *args, int from_tty)
   if (!have_full_symbols () && !have_partial_symbols ())
     error (_("No symbol table is loaded.  Use the \"file\" command."));
 
+  struct filename_grouping_opts grp_opts;
   struct filename_partial_match_opts match_opts;
-  auto group = make_info_sources_options_def_group (&match_opts);
+  auto group = make_info_sources_options_def_group (&match_opts, &grp_opts);
   gdb::option::process_options
     (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, group);
 
   if (match_opts.dirname && match_opts.basename)
     error (_("You cannot give both -basename and -dirname to 'info sources'."));
 
+  bool group_by_binary = grp_opts.group_by_binary;
+
   const char *regex = nullptr;
   if (args != nullptr && *args != '\000')
     regex = args;
@@ -4425,27 +4462,47 @@  info_sources_command (const char *args, int from_tty)
   struct output_source_filename_data data (regex, match_opts.dirname,
 					   match_opts.basename);
 
-  data.print_header (_("Source files for which symbols have been read in:\n"));
+  if (!group_by_binary)
+    data.print_header (_("Source files for which symbols have been read in:\n"));
 
   for (objfile *objfile : current_program_space->objfiles ())
     {
+      if (group_by_binary)
+	{
+	  printf_filtered ("%s:\n", objfile_name (objfile));
+	  bool debug_fully_readin = !objfile->has_unexpanded_symbols ();
+	  if (!debug_fully_readin)
+	    printf_filtered ("(Full debug information has not yet been read for this file.)\n");
+	  printf_filtered ("\n");
+	}
+
       for (compunit_symtab *cu : objfile->compunits ())
 	{
 	  for (symtab *s : compunit_filetabs (cu))
 	    {
 	      const char *fullname = symtab_to_fullname (s);
-
 	      data.output (fullname);
 	    }
 	}
+
+      if (group_by_binary)
+	{
+	  objfile->map_symbol_filenames (data, true /* need_fullname */);
+	  if (data.printed_filename_p ())
+	    printf_filtered ("\n\n");
+	  data.reset_output ();
+	}
     }
-  printf_filtered ("\n\n");
 
-  data.print_header (_("Source files for which symbols will be read in on demand:\n"));
+  if (!group_by_binary)
+    {
+      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");
+      data.reset_output ();
+      map_symbol_filenames (data, true /*need_fullname*/);
+      printf_filtered ("\n");
+    }
 }
 
 /* Compare FILE against all the entries of FILENAMES.  If BASENAMES is
@@ -6805,7 +6862,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, nullptr);
 
   static std::string info_sources_help
     = gdb::option::build_help (_("\
diff --git a/gdb/testsuite/gdb.base/info_sources.exp b/gdb/testsuite/gdb.base/info_sources.exp
index a4f7d1966e6..659a1a4368c 100644
--- a/gdb/testsuite/gdb.base/info_sources.exp
+++ b/gdb/testsuite/gdb.base/info_sources.exp
@@ -103,3 +103,8 @@  if { ! [is_remote host] } {
 # Test non matching regexp, with option terminator:
 test_info_sources "-b -- -d" 0 0
 test_info_sources "-d -- -d" 0 0
+
+# Test with the -group-by-binary option.
+test_info_sources "-group-by-binary" 1 1
+test_info_sources "-group-by-binary -basename info_sources" 1 1
+test_info_sources "-group-by-binary -basename base" 0 1
diff --git a/gdb/testsuite/gdb.base/info_sources_2-header.h b/gdb/testsuite/gdb.base/info_sources_2-header.h
new file mode 100644
index 00000000000..b3379babc0a
--- /dev/null
+++ b/gdb/testsuite/gdb.base/info_sources_2-header.h
@@ -0,0 +1,28 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+#ifndef INFO_SOURCES_2_HEADER
+#define INFO_SOURCES_2_HEADER
+
+extern int foo (void);
+
+inline static int compare_values (int a, int b)
+{
+  return a == b;
+}
+
+#endif /* INFO_SOURCES_2_HEADER */
diff --git a/gdb/testsuite/gdb.base/info_sources_2-lib.c b/gdb/testsuite/gdb.base/info_sources_2-lib.c
new file mode 100644
index 00000000000..7df1a8114ad
--- /dev/null
+++ b/gdb/testsuite/gdb.base/info_sources_2-lib.c
@@ -0,0 +1,25 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+
+#include "info_sources_2-header.h"
+
+int
+foo ()
+{
+  return compare_values (0, 1);
+}
diff --git a/gdb/testsuite/gdb.base/info_sources_2-test.c b/gdb/testsuite/gdb.base/info_sources_2-test.c
new file mode 100644
index 00000000000..87a030ae87d
--- /dev/null
+++ b/gdb/testsuite/gdb.base/info_sources_2-test.c
@@ -0,0 +1,26 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+
+#include "info_sources_2-header.h"
+
+int
+main ()
+{
+  int res = foo ();
+  return compare_values (res, 1);
+}
diff --git a/gdb/testsuite/gdb.base/info_sources_2.exp b/gdb/testsuite/gdb.base/info_sources_2.exp
new file mode 100644
index 00000000000..dd4f4f08263
--- /dev/null
+++ b/gdb/testsuite/gdb.base/info_sources_2.exp
@@ -0,0 +1,169 @@ 
+# 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 'info sources -group-by-binary' when the test file makes use of
+# a shared library.
+
+if { [skip_shlib_tests] } {
+    return 0
+}
+
+standard_testfile -test.c -lib.c
+set solib_name [standard_output_file ${testfile}-lib.so]
+
+if { [gdb_compile_shlib ${srcdir}/${subdir}/${srcfile2} ${solib_name} \
+      {debug}] != "" } {
+    untested "failed to compile shared library"
+    return -1
+}
+
+if {[gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile} executable \
+     [list debug shlib=${solib_name} ]] != ""} {
+    untested "failed to compile executable"
+    return -1
+}
+
+clean_restart ${binfile}
+
+if ![runto foo] {
+    untested "failed to run to function foo"
+    return -1
+}
+
+# Invoke 'info sources -group-by-binary EXTRA_ARGS' and extract the results.
+# The results are then compared to the list ARGS.
+#
+# The list ARGS should consist of pairs of values, the first item being the
+# path to an object file, and the second item being the name of a source file.
+# This proc checks that source file was listed as being a source file for the
+# given object file.
+#
+# If the name of the source file starts with the character "!" (exclamation
+# character, without the quotes) then the check is inverted, that the source
+# file is NOT listed for the given object file.
+proc run_info_sources { extra_args args } {
+    global gdb_prompt srcdir subdir
+
+    with_test_prefix "args: ${extra_args}" {
+
+	# The results of running info sources will be placed into this local.
+	array set info_sources {}
+
+	# The command we are going to run.
+	set cmd "info sources -group-by-binary ${extra_args}"
+	set command_regex [string_to_regexp $cmd]
+
+	# Run the command and extract the results into INFO_SOURCES.
+	set objfile_name ""
+	set source_files {}
+	gdb_test_multiple $cmd "" {
+	    -re "${command_regex}\r\n" {
+		exp_continue
+	    }
+
+	    -re "^(\[^\r\n\]+):\r\n" {
+		set objfile_name $expect_out(1,string)
+		exp_continue
+	    }
+
+	    -re "^\\(Full debug information has not yet been read for this file\\.\\)\r\n" {
+		exp_continue
+	    }
+
+	    -re "^\r\n" {
+		exp_continue
+	    }
+
+	    -re "^$gdb_prompt $" {
+		pass $gdb_test_name
+	    }
+
+	    -re "^(\[^\r\n\]+)\r\n" {
+		if { $objfile_name == "" } {
+		    fail "${gdb_test_name} (no objfile name)"
+		    return
+		}
+
+		set files {}
+		foreach f [split $expect_out(1,string) ,] {
+		    lappend files [string trim $f]
+		}
+		set info_sources($objfile_name) $files
+		set $objfile_name ""
+		exp_continue
+	    }
+	}
+
+	# Now check ARGS agaisnt the values held in INFO_SOURCES map.
+	foreach {objfile sourcefile} $args {
+	    # First, figure out if we're expecting SOURCEFILE to be present,
+	    # or not.
+	    set present True
+	    set match_type "is"
+	    if {[string index $sourcefile 0] == "!"} {
+		set present False
+		set match_type "is not"
+		set sourcefile [string range $sourcefile 1 end]
+	    }
+
+	    # Figure out the path for SOURCEFILE that we're looking for.
+	    set sourcepath [file normalize ${srcdir}/${subdir}/${sourcefile}]
+
+	    # Make sure we handle the case where there are no source files
+	    # associated with a particular objfile.
+	    set source_list {}
+	    if [info exists info_sources($objfile)] {
+		set source_list $info_sources($objfile)
+	    }
+
+	    # Now perform the search, and check the results.
+	    set idx [lsearch -exact $source_list $sourcepath]
+	    gdb_assert {($present && $idx >= 0) || (!$present && $idx == -1)} \
+		"source file '$sourcefile' ${match_type} present for '[file tail $objfile]'"
+	}
+    }
+}
+
+# The actual tests.
+
+run_info_sources "" \
+    ${binfile} ${srcfile} \
+    ${binfile} ${testfile}-header.h \
+    ${solib_name} ${srcfile2} \
+    ${solib_name} ${testfile}-header.h
+
+run_info_sources "-basename info_sources_2" \
+    ${binfile} ${srcfile} \
+    ${binfile} ${testfile}-header.h \
+    ${solib_name} ${srcfile2} \
+    ${solib_name} ${testfile}-header.h
+
+run_info_sources "-basename \\.c" \
+    ${binfile} ${srcfile} \
+    ${binfile} !${testfile}-header.h \
+    ${solib_name} ${srcfile2} \
+    ${solib_name} !${testfile}-header.h
+
+run_info_sources "-basename -- -test\\.c" \
+    ${binfile} ${srcfile} \
+    ${binfile} !${testfile}-header.h \
+    ${solib_name} !${srcfile2} \
+    ${solib_name} !${testfile}-header.h
+
+run_info_sources "-basename -- -lib\\.c" \
+    ${binfile} !${srcfile} \
+    ${binfile} !${testfile}-header.h \
+    ${solib_name} ${srcfile2} \
+    ${solib_name} !${testfile}-header.h