[PATCHv3,5/5] gdb: change info sources to group results by objfile

Message ID c36f7225bb49e5a734c1dd224939aaa7737196b6.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.
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 change the format of the 'info sources' results so
that the results are grouped by the object file that uses that source
file.  Here's the same session with the new output format:

  (gdb) info sources
  /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:
  (Objfile has no debug information.)

  system-supplied DSO at 0x7ffff7fcf000:
  (Objfile has no debug information.)

  /tmp/info-sources/libhelper.so:

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

  /lib64/libc.so.6:
  (Objfile has no debug information.)

  (gdb)

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

Further, some object files are tagged with the message '(Objfile has
no debug information.)', it is also possible to see the message '(Full
debug information has not yet been read for this file.)', which is
printed when some symtabs within an objfile have not yet been
expanded.

All of the existing regular expression based filtering still works.

An original version of this patch added the new format as an option to
'info sources', however, it was felt that the new layout was so much
better than the old style that GDB should just switch to the new
result format completely.

gdb/ChangeLog:

	* NEWS: Mention changes to 'info sources'.
	* symtab.c (info_sources_filter::print): Delete.
	(struct output_source_filename_data) <print_header>: Delete
	declaration.  <printed_filename_p>: New member function.
	(output_source_filename_data::print_header): Delete.
	(info_sources_worker): Update group-by-objfile style output to
	make it CLI suitable, simplify non-group-by-objfile now this is
	only used from the MI.
	(info_sources_command): Make group-by-objfile be the default for
	CLI info sources command.
	* symtab.h (struct info_sources_filter) <print>: Delete.

gdb/doc/ChangeLog:

	* gdb.texinfo (Symbols): Document new output format for 'info
	sources'.

gdb/testsuite/ChangeLog:

	* 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                                 |  14 ++
 gdb/NEWS                                      |   6 +
 gdb/doc/ChangeLog                             |   5 +
 gdb/doc/gdb.texinfo                           |  44 +++--
 gdb/symtab.c                                  |  94 ++++------
 gdb/symtab.h                                  |   5 -
 gdb/testsuite/ChangeLog                       |   7 +
 .../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, 342 insertions(+), 81 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

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 468d276a939..fdf0c4de987 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -213,6 +213,12 @@  ptype[/FLAGS] TYPE | EXPRESSION
   offsets of struct members.  Default behavior is given by 'show print
   type hex'.
 
+info sources
+  The info sources command output has been restructured.  The results
+  are now based around a list of objfiles (executable and libraries),
+  and for each objfile the source files that are part of that objfile
+  are listed.
+
 * Removed targets and native configurations
 
 ARM Symbian			arm*-*-symbianelf*
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 6312a520b7b..e574d96dabe 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -19335,20 +19335,36 @@ 
 
 
 @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{[}-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.  The
+source files are presented based on a list of object files
+(executables and libraries) currently loaded into @value{GDBN}.  For
+each object file all of the associated source files are listed.
+
+Each source file will only be printed once for each object file, but a
+single source file can be repeated in the output if it is part of
+multiple object files.
+
+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.
+
+It is possible that an object file may be printed in the list with no
+associated source files.  This can happen when either no source files
+match @var{regexp}, or, the object file was compiled without debug
+information and so @value{GDBN} is unable to find any source file
+names.
 
 @kindex info functions
 @item info functions [-q] [-n]
diff --git a/gdb/symtab.c b/gdb/symtab.c
index cd6da063141..7fd037f9701 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4252,33 +4252,6 @@  info_sources_filter::matches (const char *fullname) const
   return true;
 }
 
-/* See class declaration.  */
-
-void
-info_sources_filter::print (struct ui_out *uiout) const
-{
-  if (m_c_regexp.has_value ())
-    {
-      gdb_assert (m_regexp != nullptr);
-
-      switch (m_match_type)
-	{
-	case match_on::DIRNAME:
-	  uiout->message (_("(dirname matching regular expression \"%s\")"),
-			  m_regexp);
-	  break;
-	case match_on::BASENAME:
-	  uiout->message (_("(basename matching regular expression \"%s\")"),
-			  m_regexp);
-	  break;
-	case match_on::FULLNAME:
-	  printf_filtered (_("(filename matching regular expression \"%s\")"),
-			   m_regexp);
-	  break;
-	}
-    }
-}
-
 /* Data structure to maintain the state used for printing the results of
    the 'info sources' command.  */
 
@@ -4312,12 +4285,6 @@  struct output_source_filename_data
      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.
-     SYMBOL_MSG is a message that describes what will or has been done with
-     the symbols of the matching source files.  */
-  void print_header (const char *symbol_msg);
-
   /* An overload suitable for use as a callback to
      quick_symbol_functions::map_symbol_filenames.  */
   void operator() (const char *filename, const char *fullname)
@@ -4327,6 +4294,14 @@  struct output_source_filename_data
     output (filename, fullname, false);
   }
 
+  /* 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:
 
   /* Flag of whether we're printing the first one.  */
@@ -4392,16 +4367,6 @@  output_source_filename_data::output (const char *disp_name,
     }
 }
 
-/* See comment is class declaration above.  */
-
-void
-output_source_filename_data::print_header (const char *symbol_msg)
-{
-  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
    matching the user supplied regular expression against?  */
 
@@ -4468,13 +4433,7 @@  info_sources_worker (struct ui_out *uiout,
   gdb::optional<ui_out_emit_tuple> output_tuple;
   gdb::optional<ui_out_emit_list> sources_list;
 
-  gdb_assert (!group_by_objfile || uiout->is_mi_like_p ());
-
-  if (!group_by_objfile)
-    {
-      if (!uiout->is_mi_like_p ())
-	data.print_header (_("Source files for which symbols have been read in:\n"));
-    }
+  gdb_assert (group_by_objfile || uiout->is_mi_like_p ());
 
   for (objfile *objfile : current_program_space->objfiles ())
     {
@@ -4482,18 +4441,31 @@  info_sources_worker (struct ui_out *uiout,
 	{
 	  output_tuple.emplace (uiout, nullptr);
 	  uiout->field_string ("filename", objfile_name (objfile));
+	  uiout->text (":\n");
 	  bool debug_fully_readin = !objfile->has_unexpanded_symtabs ();
-	  const char *debug_info_state;
-	  if (objfile_has_symbols (objfile))
+	  if (uiout->is_mi_like_p ())
 	    {
-	      if (debug_fully_readin)
-		debug_info_state = "fully-read";
+	      const char *debug_info_state;
+	      if (objfile_has_symbols (objfile))
+		{
+		  if (debug_fully_readin)
+		    debug_info_state = "fully-read";
+		  else
+		    debug_info_state = "partially-read";
+		}
 	      else
-		debug_info_state = "partially-read";
+		debug_info_state = "none";
+	      current_uiout->field_string ("debug-info", debug_info_state);
 	    }
 	  else
-	    debug_info_state = "none";
-	  current_uiout->field_string ("debug-info", debug_info_state);
+	    {
+	      if (!debug_fully_readin)
+		uiout->text ("(Full debug information has not yet been read "
+			     "for this file.)\n");
+	      if (!objfile_has_symbols (objfile))
+		uiout->text ("(Objfile has no debug information.)\n");
+	      uiout->text ("\n");
+	    }
 	  sources_list.emplace (uiout, "sources");
 	}
 
@@ -4510,6 +4482,8 @@  info_sources_worker (struct ui_out *uiout,
       if (group_by_objfile)
 	{
 	  objfile->map_symbol_filenames (data, true /* need_fullname */);
+	  if (data.printed_filename_p ())
+	    uiout->text ("\n\n");
 	  data.reset_output ();
 	  sources_list.reset ();
 	  output_tuple.reset ();
@@ -4518,12 +4492,8 @@  info_sources_worker (struct ui_out *uiout,
 
   if (!group_by_objfile)
     {
-      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");
     }
 }
 
@@ -4559,7 +4529,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);
-  info_sources_worker (current_uiout, false, filter);
+  info_sources_worker (current_uiout, true, filter);
 }
 
 /* Compare FILE against all the entries of FILENAMES.  If BASENAMES is
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 803b1bc0ce7..160049ab628 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -2419,11 +2419,6 @@  struct info_sources_filter
      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.  */
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..3aed25b07ac
--- /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' 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 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 ${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