public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/6 v3] gdb/debuginfod: Add on-demand debuginfo downloading
@ 2023-06-01  1:43 Aaron Merey
  2023-06-01  1:43 ` [PATCH 1/6 v2] gdb/debuginfod: Add debuginfod_section_query Aaron Merey
                   ` (5 more replies)
  0 siblings, 6 replies; 33+ messages in thread
From: Aaron Merey @ 2023-06-01  1:43 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, Aaron Merey

Aaron Merey (6):
  gdb/debuginfod: Add debuginfod_section_query
  gdb: Add command 'maint set/show debuginfod download-sections'
  gdb: Buffer gdb_stdout during events that might download deferred
    debuginfo
  gdb/progspace: Add reverse safe iterator and template for unwrapping
    iterator
  gdb/debuginfod: Support on-demand debuginfo downloading
  gdb/debuginfod: Add .debug_line downloading

 config/debuginfod.m4                       |  33 +++
 gdb/cli-out.c                              |  28 +-
 gdb/cli-out.h                              |   1 +
 gdb/config.in                              |   3 +
 gdb/configure                              | 116 +++++++-
 gdb/configure.ac                           |   2 +-
 gdb/debuginfod-support.c                   | 139 ++++++++-
 gdb/debuginfod-support.h                   |  24 ++
 gdb/doc/gdb.texinfo                        |  16 +-
 gdb/dwarf2/frame.c                         |  13 +
 gdb/dwarf2/frame.h                         |   4 +
 gdb/dwarf2/index-cache.c                   |  33 +++
 gdb/dwarf2/index-cache.h                   |  13 +
 gdb/dwarf2/line-header.c                   | 215 +++++++++-----
 gdb/dwarf2/line-header.h                   |  10 +
 gdb/dwarf2/public.h                        |   7 +
 gdb/dwarf2/read-gdb-index.c                | 183 ++++++++++--
 gdb/dwarf2/read.c                          | 311 ++++++++++++++++++++-
 gdb/dwarf2/read.h                          |  41 +++
 gdb/dwarf2/section.c                       |   3 +-
 gdb/elfread.c                              |   2 +-
 gdb/infrun.c                               |  17 +-
 gdb/mi/mi-out.c                            |  29 ++
 gdb/mi/mi-out.h                            |   1 +
 gdb/objfile-flags.h                        |   4 +
 gdb/objfiles.h                             |  33 ++-
 gdb/progspace.c                            |   8 +-
 gdb/progspace.h                            | 159 ++++++++---
 gdb/python/py-mi.c                         |   2 +
 gdb/quick-symbol.h                         |   4 +
 gdb/stack.c                                |  38 ++-
 gdb/symfile-debug.c                        | 136 ++++-----
 gdb/symfile.c                              |  13 +-
 gdb/symtab.c                               |  18 +-
 gdb/testsuite/gdb.debuginfod/libsection1.c |  40 +++
 gdb/testsuite/gdb.debuginfod/libsection2.c |  37 +++
 gdb/testsuite/gdb.debuginfod/section.c     |  29 ++
 gdb/testsuite/gdb.debuginfod/section.exp   | 202 +++++++++++++
 gdb/testsuite/gdb.python/py-objfile.exp    |   2 +-
 gdb/testsuite/lib/debuginfod-support.exp   |  35 ++-
 gdb/thread.c                               | 174 +++++++-----
 gdb/ui-file.c                              |  62 ++++
 gdb/ui-file.h                              |  45 ++-
 gdb/ui-out.c                               |  21 ++
 gdb/ui-out.h                               |  66 +++++
 45 files changed, 2058 insertions(+), 314 deletions(-)
 create mode 100644 gdb/testsuite/gdb.debuginfod/libsection1.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/libsection2.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/section.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/section.exp

-- 
2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 1/6 v2] gdb/debuginfod: Add debuginfod_section_query
  2023-06-01  1:43 [PATCH 0/6 v3] gdb/debuginfod: Add on-demand debuginfo downloading Aaron Merey
@ 2023-06-01  1:43 ` Aaron Merey
  2023-06-07 13:35   ` Andrew Burgess
  2023-06-01  1:43 ` [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections' Aaron Merey
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-06-01  1:43 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, Aaron Merey

v1: https://sourceware.org/pipermail/gdb-patches/2023-February/197455.html

v2 improves the description of AC_DEBUGINFOD_SECTION and associated
macro names.  An if-statement in debuginfod_section_query is also
replaced with a gdb_assert.

Commit message:

Add new function debuginfod_section_query.  This function queries
debuginfod servers for an individual ELF/DWARF section associated with
a given build-id.

Also check for libdebuginfod version >= 0.188 at configure time.
debuginfod_section_query simply returns -ENOSYS if this condition
is not met.
---
 config/debuginfod.m4     |  33 +++++++++++
 gdb/config.in            |   3 +
 gdb/configure            | 116 +++++++++++++++++++++++++++++++++++++--
 gdb/configure.ac         |   2 +-
 gdb/debuginfod-support.c |  60 ++++++++++++++++++++
 gdb/debuginfod-support.h |  24 ++++++++
 6 files changed, 232 insertions(+), 6 deletions(-)

diff --git a/config/debuginfod.m4 b/config/debuginfod.m4
index 2c1bfbdb544..4c443b77d3a 100644
--- a/config/debuginfod.m4
+++ b/config/debuginfod.m4
@@ -26,3 +26,36 @@ else
   AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])
 fi
 ])
+
+AC_DEFUN([AC_DEBUGINFOD_SECTION],
+[
+# Handle optional debuginfod support as well as optional section
+# downloading support.
+#
+# Define HAVE_LIBDEBUGINFOD if libdebuginfod is found with version >= 0.179.
+#
+# Define HAVE_LIBDEBUGINFOD_FIND_SECTION if libdebuginfod is found with
+# version >= 0.188.
+AC_ARG_WITH([debuginfod],
+  AC_HELP_STRING([--with-debuginfod], [Enable debuginfo lookups with debuginfod (auto/yes/no)]),
+  [], [with_debuginfod=auto])
+AC_MSG_CHECKING([whether to use debuginfod])
+AC_MSG_RESULT([$with_debuginfod])
+
+if test "x$with_debuginfod" != xno; then
+  PKG_CHECK_MODULES([DEBUGINFOD], [libdebuginfod >= 0.188],
+    [AC_DEFINE([HAVE_LIBDEBUGINFOD_FIND_SECTION], [1],
+	       [Define to 1 if debuginfod section downloading is supported.])],
+    [AC_MSG_WARN([libdebuginfod is missing or some features may be unavailable.])])
+
+  PKG_CHECK_MODULES([DEBUGINFOD], [libdebuginfod >= 0.179],
+    [AC_DEFINE([HAVE_LIBDEBUGINFOD], [1], [Define to 1 if debuginfod is enabled.])],
+    [if test "x$with_debuginfod" = xyes; then
+      AC_MSG_ERROR(["--with-debuginfod was given, but libdebuginfod is missing or unusable."])
+     else
+      AC_MSG_WARN([libdebuginfod is missing or unusable; some features may be unavailable.])
+     fi])
+else
+  AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])
+fi
+])
diff --git a/gdb/config.in b/gdb/config.in
index a7da88b92d7..95dd2fa22cb 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -244,6 +244,9 @@
 /* Define to 1 if debuginfod is enabled. */
 #undef HAVE_LIBDEBUGINFOD
 
+/* Define to 1 if debuginfod section downloading is supported. */
+#undef HAVE_LIBDEBUGINFOD_FIND_SECTION
+
 /* Define if you have the expat library. */
 #undef HAVE_LIBEXPAT
 
diff --git a/gdb/configure b/gdb/configure
index 5bb2a0795e5..f58a2b6b92a 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -18349,7 +18349,13 @@ esac
 
 # Handle optional debuginfod support
 
-# Handle optional debuginfod support
+# Handle optional debuginfod support as well as optional section
+# downloading support.
+#
+# Define HAVE_LIBDEBUGINFOD if libdebuginfod is found with version >= 0.179.
+#
+# Define HAVE_LIBDEBUGINFOD_FIND_SECTION if libdebuginfod is found with
+# version >= 0.188.
 
 # Check whether --with-debuginfod was given.
 if test "${with_debuginfod+set}" = set; then :
@@ -18365,6 +18371,106 @@ $as_echo "$with_debuginfod" >&6; }
 
 if test "x$with_debuginfod" != xno; then
 
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.188" >&5
+$as_echo_n "checking for libdebuginfod >= 0.188... " >&6; }
+
+if test -n "$DEBUGINFOD_CFLAGS"; then
+    pkg_cv_DEBUGINFOD_CFLAGS="$DEBUGINFOD_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_DEBUGINFOD_CFLAGS=`$PKG_CONFIG --cflags "libdebuginfod >= 0.188" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$DEBUGINFOD_LIBS"; then
+    pkg_cv_DEBUGINFOD_LIBS="$DEBUGINFOD_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_DEBUGINFOD_LIBS=`$PKG_CONFIG --libs "libdebuginfod >= 0.188" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+if test $pkg_failed = no; then
+  pkg_save_LDFLAGS="$LDFLAGS"
+  LDFLAGS="$LDFLAGS $pkg_cv_DEBUGINFOD_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+else
+  pkg_failed=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LDFLAGS=$pkg_save_LDFLAGS
+fi
+
+
+
+if test $pkg_failed = yes; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
+        else
+	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$DEBUGINFOD_PKG_ERRORS" >&5
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: libdebuginfod is missing or some features may be unavailable." >&2;}
+elif test $pkg_failed = untried; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: libdebuginfod is missing or some features may be unavailable." >&2;}
+else
+	DEBUGINFOD_CFLAGS=$pkg_cv_DEBUGINFOD_CFLAGS
+	DEBUGINFOD_LIBS=$pkg_cv_DEBUGINFOD_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_LIBDEBUGINFOD_FIND_SECTION 1" >>confdefs.h
+
+fi
+
+
 pkg_failed=no
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.179" >&5
 $as_echo_n "checking for libdebuginfod >= 0.179... " >&6; }
@@ -18448,18 +18554,18 @@ fi
 	echo "$DEBUGINFOD_PKG_ERRORS" >&5
 
 	if test "x$with_debuginfod" = xyes; then
-       as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
+      as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
      else
-       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
+      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
 $as_echo "$as_me: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&2;}
      fi
 elif test $pkg_failed = untried; then
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 	if test "x$with_debuginfod" = xyes; then
-       as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
+      as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
      else
-       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
+      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
 $as_echo "$as_me: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&2;}
      fi
 else
diff --git a/gdb/configure.ac b/gdb/configure.ac
index fb43cd10d6c..499802bb4c9 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -342,7 +342,7 @@ case $host_os in
 esac
 
 # Handle optional debuginfod support
-AC_DEBUGINFOD
+AC_DEBUGINFOD_SECTION
 
 # Libunwind support for ia64.
 AC_ARG_WITH(libunwind-ia64,
diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
index 5853f420a18..8be43a91dcc 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -80,6 +80,15 @@ debuginfod_exec_query (const unsigned char *build_id,
   return scoped_fd (-ENOSYS);
 }
 
+scoped_fd
+debuginfod_section_query (const unsigned char *build_id,
+			  int build_id_len,
+			  const char *filename,
+			  const char *section_name,
+			  gdb::unique_xmalloc_ptr<char> *destname)
+{
+  return scoped_fd (-ENOSYS);
+}
 #define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
 
 #else
@@ -401,6 +410,57 @@ debuginfod_exec_query (const unsigned char *build_id,
 
   return fd;
 }
+
+/* See debuginfod-support.h  */
+
+scoped_fd
+debuginfod_section_query (const unsigned char *build_id,
+			  int build_id_len,
+			  const char *filename,
+			  const char *section_name,
+			  gdb::unique_xmalloc_ptr<char> *destname)
+{
+#if !defined (HAVE_LIBDEBUGINFOD_FIND_SECTION)
+  return scoped_fd (-ENOSYS);
+#else
+
+ if (!debuginfod_is_enabled ())
+    return scoped_fd (-ENOSYS);
+
+  debuginfod_client *c = get_debuginfod_client ();
+
+  if (c == nullptr)
+    return scoped_fd (-ENOMEM);
+
+  char *dname = nullptr;
+  std::string desc = std::string ("section ") + section_name + " for";
+  scoped_fd fd;
+  gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
+
+  {
+    user_data data (desc.c_str (), filename);
+    debuginfod_set_user_data (c, &data);
+    if (target_supports_terminal_ours ())
+      {
+	term_state.emplace ();
+	target_terminal::ours ();
+      }
+
+    fd = scoped_fd (debuginfod_find_section (c, build_id, build_id_len,
+					     section_name, &dname));
+    debuginfod_set_user_data (c, nullptr);
+  }
+
+  print_outcome (fd.get (), desc.c_str (), filename);
+  gdb_assert (destname != nullptr);
+
+  if (fd.get () >= 0)
+    destname->reset (dname);
+
+  return fd;
+#endif /* HAVE_LIBDEBUGINFOD_FIND_SECTION */
+}
+
 #endif
 
 /* Set callback for "set debuginfod enabled".  */
diff --git a/gdb/debuginfod-support.h b/gdb/debuginfod-support.h
index 633600a79da..9701e3b4685 100644
--- a/gdb/debuginfod-support.h
+++ b/gdb/debuginfod-support.h
@@ -81,4 +81,28 @@ extern scoped_fd debuginfod_exec_query (const unsigned char *build_id,
 					const char *filename,
 					gdb::unique_xmalloc_ptr<char>
 					  *destname);
+
+/* Query debuginfod servers for the binary contents of a ELF/DWARF section
+   from a file matching BUILD_ID.  BUILD_ID can be given as a binary blob
+   or a null-terminated string.  If given as a binary blob, BUILD_ID_LEN
+   should be the number of bytes.  If given as a null-terminated string,
+   BUILD_ID_LEN should be 0.
+
+   FILENAME should be the name or path associated with the file matching
+   BUILD_ID.  It is used for printing messages to the user.
+
+   SECTION_NAME should be the name of an ELF/DWARF section.
+
+   If the file is successfully retrieved, return a file descriptor and store
+   the file's local path in DESTNAME.  If unsuccessful, print an error message
+   and return a negative errno.  If GDB is not built with debuginfod or
+   libdebuginfod does not support section queries, this function returns
+   -ENOSYS.  */
+
+extern scoped_fd debuginfod_section_query (const unsigned char *build_id,
+					   int build_id_len,
+					   const char *filename,
+					   const char *section_name,
+					   gdb::unique_xmalloc_ptr<char>
+					     *destname);
 #endif /* DEBUGINFOD_SUPPORT_H */
-- 
2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections'
  2023-06-01  1:43 [PATCH 0/6 v3] gdb/debuginfod: Add on-demand debuginfo downloading Aaron Merey
  2023-06-01  1:43 ` [PATCH 1/6 v2] gdb/debuginfod: Add debuginfod_section_query Aaron Merey
@ 2023-06-01  1:43 ` Aaron Merey
  2023-06-01  6:13   ` Eli Zaretskii
                     ` (3 more replies)
  2023-06-01  1:43 ` [PATCH 3/6 v3] gdb: Buffer gdb_stdout during events that might download deferred debuginfo Aaron Merey
                   ` (3 subsequent siblings)
  5 siblings, 4 replies; 33+ messages in thread
From: Aaron Merey @ 2023-06-01  1:43 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, Aaron Merey

v1: https://sourceware.org/pipermail/gdb-patches/2023-February/197461.html

v2 removes 'set debuginfod enabled lazy' and replaces it with
'maint set debuginfod download-sections'.

Commit message:

This setting controls whether GDB may attempt to download ELF/DWARF
sections from debuginfod servers.

This setting is enabled by default if gdb is built with debuginfod
section download support (requires libdebuginfod 0.188).

Also update debuginfod command help text and gdb.texinfo with
information on section downloading and the new command.
---
 gdb/debuginfod-support.c | 66 ++++++++++++++++++++++++++++++++++++++--
 gdb/doc/gdb.texinfo      | 16 ++++++++--
 2 files changed, 77 insertions(+), 5 deletions(-)

diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
index 8be43a91dcc..6d0521b64e2 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -31,6 +31,10 @@
 static cmd_list_element *set_debuginfod_prefix_list;
 static cmd_list_element *show_debuginfod_prefix_list;
 
+/* maint set/show debuginfod commands.  */
+static cmd_list_element *maint_set_debuginfod_cmdlist;
+static cmd_list_element *maint_show_debuginfod_cmdlist;
+
 static const char debuginfod_on[] = "on";
 static const char debuginfod_off[] = "off";
 static const char debuginfod_ask[] = "ask";
@@ -50,6 +54,13 @@ static const char *debuginfod_enabled =
   debuginfod_off;
 #endif
 
+static bool debuginfod_download_sections =
+#if defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
+  true;
+#else
+  false;
+#endif
+
 static unsigned int debuginfod_verbose = 1;
 
 #ifndef HAVE_LIBDEBUGINFOD
@@ -424,7 +435,7 @@ debuginfod_section_query (const unsigned char *build_id,
   return scoped_fd (-ENOSYS);
 #else
 
- if (!debuginfod_is_enabled ())
+ if (!debuginfod_download_sections || !debuginfod_is_enabled ())
     return scoped_fd (-ENOSYS);
 
   debuginfod_client *c = get_debuginfod_client ();
@@ -550,6 +561,31 @@ show_debuginfod_verbose_command (ui_file *file, int from_tty,
 	      value);
 }
 
+/* Set callback for "maint set debuginfod download-sections".  */
+
+static void
+maint_set_debuginfod_download_sections (bool value)
+{
+#if !defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
+  if (value)
+    {
+      error (_("Support for section downloading is not compiled into GDB. " \
+"Defaulting to \"off\"."));
+      return;
+    }
+#endif
+
+  debuginfod_download_sections = value;
+}
+
+/* Get callback for "maint set debuginfod download-sections".  */
+
+static bool
+maint_get_debuginfod_download_sections ()
+{
+  return debuginfod_download_sections;
+}
+
 /* Register debuginfod commands.  */
 
 void _initialize_debuginfod ();
@@ -568,8 +604,11 @@ _initialize_debuginfod ()
 			_("Set whether to use debuginfod."),
 			_("Show whether to use debuginfod."),
 			_("\
-When on, enable the use of debuginfod to download missing debug info and\n\
-source files."),
+When set to \"on\", enable the use of debuginfod to download missing\n\
+debug info and source files. GDB may also download components of debug\n\
+info instead of entire files. \"off\" disables the use of debuginfod.\n\
+When set to \"ask\", a prompt may ask whether to enable or disable\n\
+debuginfod." ),
 			set_debuginfod_enabled,
 			get_debuginfod_enabled,
 			show_debuginfod_enabled,
@@ -600,4 +639,25 @@ query.\nTo disable, set to zero.  Verbose output is displayed by default."),
 			     show_debuginfod_verbose_command,
 			     &set_debuginfod_prefix_list,
 			     &show_debuginfod_prefix_list);
+
+  /* maint set/show debuginfod */
+  add_setshow_prefix_cmd ("debuginfod", class_maintenance,
+			  _("Set debuginfod specific variables."),
+			  _("Show debuginfod specific variables."),
+			  &maint_set_debuginfod_cmdlist,
+			  &maint_show_debuginfod_cmdlist,
+			  &maintenance_set_cmdlist, &maintenance_show_cmdlist);
+
+  /* maint set/show debuginfod download-sections */
+  add_setshow_boolean_cmd ("download-sections", class_maintenance, _("\
+Set whether debuginfod may download individual ELF/DWARF sections."), _("\
+Show whether debuginfod may download individual ELF/DWARF sections."), _("\
+When enabled, debuginfod may attempt to download individual ELF/DWARF \
+sections from debug info files.\nIf disabled, only whole debug info files \
+may be downloaded."),
+			  maint_set_debuginfod_download_sections,
+			  maint_get_debuginfod_download_sections,
+			  nullptr,
+			  &maint_set_debuginfod_cmdlist,
+			  &maint_show_debuginfod_cmdlist);
 }
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index fc55c4e7b43..91da9777fd6 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -40872,6 +40872,16 @@ Create a core file? (y or n) @kbd{n}
 (@value{GDBP})
 @end smallexample
 
+@kindex maint set debuginfod
+@kindex maint show debuginfod
+@item maint set debuginfod download-sections
+@itemx maint set debuginfod download-sections @r{[}on|off@r{]}
+@itemx maint show debuginfod download-sections
+Controls whether @value{GDBN} will attempt to download individual
+ELF/DWARF sections from @code{debuginfod}.  If disabled, only
+whole debug info files will be downloaded.  This could result
+in @value{GDBN} downloading larger amounts of data.
+
 @cindex @value{GDBN} internal error
 @cindex internal errors, control of @value{GDBN} behavior
 @cindex demangler crashes
@@ -49382,8 +49392,10 @@ regarding @code{debuginfod}.
 @item set debuginfod enabled
 @itemx set debuginfod enabled on
 @cindex enable debuginfod
-@value{GDBN} will attempt to query @code{debuginfod} servers when missing debug
-info or source files.
+@value{GDBN} will attempt to query @code{debuginfod} servers for missing debug
+info or source files.  @value{GDBN} may attempt to download individual ELF/DWARF
+sections such as @code{.gdb_index} to help reduce the total amount of data
+downloaded from debuginfod servers.
 
 @item set debuginfod enabled off
 @value{GDBN} will not attempt to query @code{debuginfod} servers when missing
-- 
2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 3/6 v3] gdb: Buffer gdb_stdout during events that might download deferred debuginfo
  2023-06-01  1:43 [PATCH 0/6 v3] gdb/debuginfod: Add on-demand debuginfo downloading Aaron Merey
  2023-06-01  1:43 ` [PATCH 1/6 v2] gdb/debuginfod: Add debuginfod_section_query Aaron Merey
  2023-06-01  1:43 ` [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections' Aaron Merey
@ 2023-06-01  1:43 ` Aaron Merey
  2023-06-01  6:16   ` Eli Zaretskii
  2023-06-07 13:25   ` Andrew Burgess
  2023-06-01  1:43 ` [PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator Aaron Merey
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 33+ messages in thread
From: Aaron Merey @ 2023-06-01  1:43 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, Aaron Merey

v2: https://sourceware.org/pipermail/gdb-patches/2023-April/198944.html

v3 simplifies do_redirect_to_buffer and adds output buffering during
print_stop_event.

This patch currently introduces regressions to three gdb.base/solib-display.exp
'after rerun' tests.  Because print_stop_event output to gdb_stdout is
now buffered until the function exits, any writes to gdb_stderr during
print_stop_event will always appear before any gdb_stdout output.

In these testcases instead of getting the following expected output:

```

Breakpoint 1, main () at /home/amerey/binutils-gdb/gdb/testsuite/gdb.base/solib-display-main.c:30
30        bar ();
1: (int) a_global = 41
warning: Unable to display "(int) b_global": No symbol "b_global" in current context.
3: (int) c_global = 43
```

gdb instead prints:

```
warning: Unable to display "(int) b_global": No symbol "b_global" in current context.

Breakpoint 1, main () at /home/amerey/binutils-gdb/gdb/testsuite/gdb.base/solib-display-main.c:30
30        bar ();
1: (int) a_global = 41
3: (int) c_global = 43
```

I'm working on a fix that buffers both gdb_stdout and gdb_stderr and
organizes the flush so that output to both streams appears in the
same order that it was written.  However I didn't think this issue is
severe enough to hold off on posting this patch as it is.  I can
file a PR and mark these tests as KFAIL for now if desired.

Commit message:

Introduce new ui_file buffer_file to temporarily collect output
during print_thread, print_frame_info and print_stop_event.

This ensures that output from these functions is not interrupted
by debuginfod progress messages.

With the addition of deferred debuginfo downloading it is possible
for download progress messages to print during these events.
Without any intervention we can end up with poorly formatted output:

    (gdb) backtrace
    [...]
    #8  0x00007fbe8af7d7cf in pygi_invoke_c_callable (Downloading separate debug info for /lib64/libpython3.11.so.1.0
    function_cache=0x561221b224d0, state=<optimized out>...

To fix this we accumulate writes to gdb_stdout in a buffer_file
while progress messages skip the buffer and print to gdb_stdout
immediately.  This ensures progress messages print first, followed by
uninterrupted frame/thread/stop info:

    (gdb) backtrace
    [...]
    Downloading separate debug info for /lib64/libpython3.11.so.1.0
    #8  0x00007fbe8af7d7cf in pygi_invoke_c_callable (function_cache=0x561221b224d0, state=<optimized out>...
---
 gdb/cli-out.c            |  28 ++++++-
 gdb/cli-out.h            |   1 +
 gdb/debuginfod-support.c |  15 ++--
 gdb/infrun.c             |  17 +++-
 gdb/mi/mi-out.c          |  29 +++++++
 gdb/mi/mi-out.h          |   1 +
 gdb/python/py-mi.c       |   2 +
 gdb/stack.c              |  38 ++++++---
 gdb/thread.c             | 174 +++++++++++++++++++++++----------------
 gdb/ui-file.c            |  62 ++++++++++++++
 gdb/ui-file.h            |  45 +++++++++-
 gdb/ui-out.c             |  21 +++++
 gdb/ui-out.h             |  66 +++++++++++++++
 13 files changed, 405 insertions(+), 94 deletions(-)

diff --git a/gdb/cli-out.c b/gdb/cli-out.c
index 20d3d93f1ad..a188457711c 100644
--- a/gdb/cli-out.c
+++ b/gdb/cli-out.c
@@ -263,6 +263,25 @@ cli_ui_out::do_redirect (ui_file *outstream)
     m_streams.pop_back ();
 }
 
+void
+cli_ui_out::do_redirect_to_buffer (buffer_file *buf_file)
+{
+  auto it = std::find (m_streams.begin (), m_streams.end (), gdb_stdout);
+
+  if (it == m_streams.end ())
+    return;
+
+  if (buf_file != nullptr)
+    *it = buf_file;
+  else
+    {
+      buffer_file *buf = dynamic_cast<buffer_file *>(*it);
+
+      if (buf != nullptr)
+	*it = buf->get_stream ();
+    }
+}
+
 /* Initialize a progress update to be displayed with
    cli_ui_out::do_progress_notify.  */
 
@@ -299,7 +318,8 @@ cli_ui_out::do_progress_notify (const std::string &msg,
 				double howmuch, double total)
 {
   int chars_per_line = get_chars_per_line ();
-  struct ui_file *stream = m_streams.back ();
+  struct ui_file *stream
+    = buffer_file::get_unbuffered_stream (m_streams.back ());
   cli_progress_info &info (m_progress_info.back ());
 
   if (chars_per_line > MAX_CHARS_PER_LINE)
@@ -384,7 +404,8 @@ cli_ui_out::do_progress_notify (const std::string &msg,
 void
 cli_ui_out::clear_progress_notify ()
 {
-  struct ui_file *stream = m_streams.back ();
+  struct ui_file *stream
+    = buffer_file::get_unbuffered_stream (m_streams.back ());
   int chars_per_line = get_chars_per_line ();
 
   scoped_restore save_pagination
@@ -413,10 +434,11 @@ void
 cli_ui_out::do_progress_end ()
 {
   struct ui_file *stream = m_streams.back ();
-  m_progress_info.pop_back ();
 
   if (stream->isatty ())
     clear_progress_notify ();
+
+  m_progress_info.pop_back ();
 }
 
 /* local functions */
diff --git a/gdb/cli-out.h b/gdb/cli-out.h
index 34016182269..fa8ca1114e1 100644
--- a/gdb/cli-out.h
+++ b/gdb/cli-out.h
@@ -71,6 +71,7 @@ class cli_ui_out : public ui_out
   virtual void do_wrap_hint (int indent) override;
   virtual void do_flush () override;
   virtual void do_redirect (struct ui_file *outstream) override;
+  virtual void do_redirect_to_buffer (buffer_file *buf_file) override;
 
   virtual void do_progress_start () override;
   virtual void do_progress_notify (const std::string &, const char *,
diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
index 6d0521b64e2..548e8c5f76b 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -168,7 +168,8 @@ progressfn (debuginfod_client *c, long cur, long total)
 
   if (check_quit_flag ())
     {
-      gdb_printf ("Cancelling download of %s %s...\n",
+      ui_file *outstream = buffer_file::get_unbuffered_stream (gdb_stdout);
+      gdb_printf (outstream, "Cancelling download of %s %s...\n",
 		  data->desc, styled_fname.c_str ());
       return 1;
     }
@@ -284,10 +285,14 @@ static void
 print_outcome (int fd, const char *desc, const char *fname)
 {
   if (fd < 0 && fd != -ENOENT)
-    gdb_printf (_("Download failed: %s.  Continuing without %s %ps.\n"),
-		safe_strerror (-fd),
-		desc,
-		styled_string (file_name_style.style (), fname));
+    {
+      ui_file *outstream = buffer_file::get_unbuffered_stream (gdb_stdout);
+      gdb_printf (outstream,
+		  _("Download failed: %s.  Continuing without %s %ps.\n"),
+		  safe_strerror (-fd),
+		  desc,
+		  styled_string (file_name_style.style (), fname));
+    }
 }
 
 /* See debuginfod-support.h  */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 4d6df757d23..f719a4aad49 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -8706,10 +8706,10 @@ print_stop_location (const target_waitstatus &ws)
     print_stack_frame (get_selected_frame (nullptr), 0, source_flag, 1);
 }
 
-/* See infrun.h.  */
+/* See `print_stop_event` in infrun.h.  */
 
-void
-print_stop_event (struct ui_out *uiout, bool displays)
+static void
+do_print_stop_event (struct ui_out *uiout, bool displays)
 {
   struct target_waitstatus last;
   struct thread_info *tp;
@@ -8738,6 +8738,17 @@ print_stop_event (struct ui_out *uiout, bool displays)
     }
 }
 
+/* See infrun.h.  This function itself sets up buffered output for the
+   duration of do_print_stop_event, which performs the actual event
+   printing.  */
+
+void
+print_stop_event (struct ui_out *uiout, bool displays)
+{
+  do_with_buffered_output<void (ui_out *, bool)>
+    (do_print_stop_event, uiout, displays);
+}
+
 /* See infrun.h.  */
 
 void
diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c
index 29a416a426d..b763d2a785c 100644
--- a/gdb/mi/mi-out.c
+++ b/gdb/mi/mi-out.c
@@ -194,6 +194,35 @@ mi_ui_out::do_redirect (ui_file *outstream)
     m_streams.pop_back ();
 }
 
+void
+mi_ui_out::do_redirect_to_buffer (buffer_file *buf_file)
+{
+  auto it = std::find (m_streams.begin (), m_streams.end (), gdb_stdout);
+
+  if (it == m_streams.end ())
+    return;
+
+  if (buf_file != nullptr)
+    {
+      string_file *sstream = dynamic_cast<string_file *>(*it);
+
+      if (sstream != nullptr)
+	{
+	  buf_file->write (sstream->data (), sstream->size ());
+	  sstream->clear ();
+	}
+
+      *it = buf_file;
+    }
+  else
+    {
+      buffer_file *buf = dynamic_cast<buffer_file *>(*it);
+
+      if (buf != nullptr)
+	*it = buf->get_stream ();
+    }
+}
+
 void
 mi_ui_out::field_separator ()
 {
diff --git a/gdb/mi/mi-out.h b/gdb/mi/mi-out.h
index 10c9f8a4585..d2f2345daf5 100644
--- a/gdb/mi/mi-out.h
+++ b/gdb/mi/mi-out.h
@@ -79,6 +79,7 @@ class mi_ui_out : public ui_out
   virtual void do_wrap_hint (int indent) override;
   virtual void do_flush () override;
   virtual void do_redirect (struct ui_file *outstream) override;
+  virtual void do_redirect_to_buffer (buffer_file *buf_file) override;
 
   virtual bool do_is_mi_like_p () const override
   { return true; }
diff --git a/gdb/python/py-mi.c b/gdb/python/py-mi.c
index 0fcd57844e7..34540901888 100644
--- a/gdb/python/py-mi.c
+++ b/gdb/python/py-mi.c
@@ -62,6 +62,8 @@ class py_ui_out : public ui_out
   void do_progress_notify (const std::string &, const char *, double, double)
     override
   { }
+  void do_redirect_to_buffer (buffer_file *) override
+  { }
 
   void do_table_begin (int nbrofcols, int nr_rows, const char *tblid) override
   {
diff --git a/gdb/stack.c b/gdb/stack.c
index 002bf580634..608bb5fc1f0 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -220,7 +220,8 @@ static void print_frame_local_vars (frame_info_ptr frame,
 				    const char *regexp, const char *t_regexp,
 				    int num_tabs, struct ui_file *stream);
 
-static void print_frame (const frame_print_options &opts,
+static void print_frame (struct ui_out *uiout,
+			 const frame_print_options &opts,
 			 frame_info_ptr frame, int print_level,
 			 enum print_what print_what,  int print_args,
 			 struct symtab_and_line sal);
@@ -1020,16 +1021,15 @@ get_user_print_what_frame_info (gdb::optional<enum print_what> *what)
    Used in "where" output, and to emit breakpoint or step
    messages.  */
 
-void
-print_frame_info (const frame_print_options &fp_opts,
-		  frame_info_ptr frame, int print_level,
-		  enum print_what print_what, int print_args,
-		  int set_current_sal)
+static void
+do_print_frame_info (struct ui_out *uiout, const frame_print_options &fp_opts,
+		     frame_info_ptr frame, int print_level,
+		     enum print_what print_what, int print_args,
+		     int set_current_sal)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
   int source_print;
   int location_print;
-  struct ui_out *uiout = current_uiout;
 
   if (!current_uiout->is_mi_like_p ()
       && fp_opts.print_frame_info != print_frame_info_auto)
@@ -1105,7 +1105,8 @@ print_frame_info (const frame_print_options &fp_opts,
 		    || print_what == LOC_AND_ADDRESS
 		    || print_what == SHORT_LOCATION);
   if (location_print || !sal.symtab)
-    print_frame (fp_opts, frame, print_level, print_what, print_args, sal);
+    print_frame (uiout, fp_opts, frame, print_level,
+		 print_what, print_args, sal);
 
   source_print = (print_what == SRC_LINE || print_what == SRC_AND_LOC);
 
@@ -1185,6 +1186,23 @@ print_frame_info (const frame_print_options &fp_opts,
   gdb_flush (gdb_stdout);
 }
 
+/* Redirect output to a temporary buffer for the duration
+   of do_print_frame_info.  */
+
+void
+print_frame_info (const frame_print_options &fp_opts,
+		  frame_info_ptr frame, int print_level,
+		  enum print_what print_what, int print_args,
+		  int set_current_sal)
+{
+  using ftype = void (ui_out *, const frame_print_options &, frame_info_ptr,
+		      int, enum print_what, int, int);
+
+  do_with_buffered_output<ftype> (do_print_frame_info, current_uiout,
+				  fp_opts, frame, print_level, print_what,
+				  print_args, set_current_sal);
+}
+
 /* See stack.h.  */
 
 void
@@ -1309,13 +1327,13 @@ find_frame_funname (frame_info_ptr frame, enum language *funlang,
 }
 
 static void
-print_frame (const frame_print_options &fp_opts,
+print_frame (struct ui_out *uiout,
+	     const frame_print_options &fp_opts,
 	     frame_info_ptr frame, int print_level,
 	     enum print_what print_what, int print_args,
 	     struct symtab_and_line sal)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
-  struct ui_out *uiout = current_uiout;
   enum language funlang = language_unknown;
   struct value_print_options opts;
   struct symbol *func;
diff --git a/gdb/thread.c b/gdb/thread.c
index 7f7f035b5ab..03105fbd4ce 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1040,6 +1040,106 @@ thread_target_id_str (thread_info *tp)
     return target_id;
 }
 
+/* Print thread TP. GLOBAL_IDS indicates whether REQUESTED_THREADS
+   is a list of global or per-inferior thread ids.  */
+
+static void
+do_print_thread (ui_out *uiout, const char *requested_threads,
+		 int global_ids, int pid, int show_global_ids,
+		 int default_inf_num, thread_info *tp,
+		 thread_info *current_thread)
+{
+  int core;
+
+  if (!should_print_thread (requested_threads, default_inf_num,
+			    global_ids, pid, tp))
+    return;
+
+  ui_out_emit_tuple tuple_emitter (uiout, NULL);
+
+  if (!uiout->is_mi_like_p ())
+    {
+      if (tp == current_thread)
+	uiout->field_string ("current", "*");
+      else
+	uiout->field_skip ("current");
+
+      uiout->field_string ("id-in-tg", print_thread_id (tp));
+    }
+
+  if (show_global_ids || uiout->is_mi_like_p ())
+    uiout->field_signed ("id", tp->global_num);
+
+  /* Switch to the thread (and inferior / target).  */
+  switch_to_thread (tp);
+
+  /* For the CLI, we stuff everything into the target-id field.
+     This is a gross hack to make the output come out looking
+     correct.  The underlying problem here is that ui-out has no
+     way to specify that a field's space allocation should be
+     shared by several fields.  For MI, we do the right thing
+     instead.  */
+
+  if (uiout->is_mi_like_p ())
+    {
+      uiout->field_string ("target-id", target_pid_to_str (tp->ptid));
+
+      const char *extra_info = target_extra_thread_info (tp);
+      if (extra_info != nullptr)
+	uiout->field_string ("details", extra_info);
+
+      const char *name = thread_name (tp);
+      if (name != NULL)
+	uiout->field_string ("name", name);
+    }
+  else
+    {
+      uiout->field_string ("target-id", thread_target_id_str (tp));
+    }
+
+  if (tp->state == THREAD_RUNNING)
+    uiout->text ("(running)\n");
+  else
+    {
+      /* The switch above put us at the top of the stack (leaf
+	 frame).  */
+      print_stack_frame (get_selected_frame (NULL),
+			 /* For MI output, print frame level.  */
+			 uiout->is_mi_like_p (),
+			 LOCATION, 0);
+    }
+
+  if (uiout->is_mi_like_p ())
+    {
+      const char *state = "stopped";
+
+      if (tp->state == THREAD_RUNNING)
+	state = "running";
+      uiout->field_string ("state", state);
+    }
+
+  core = target_core_of_thread (tp->ptid);
+  if (uiout->is_mi_like_p () && core != -1)
+    uiout->field_signed ("core", core);
+}
+
+/* Redirect output to a temporary buffer for the duration
+   of do_print_thread.  */
+
+static void
+print_thread (ui_out *uiout, const char *requested_threads,
+	      int global_ids, int pid, int show_global_ids,
+	      int default_inf_num, thread_info *tp, thread_info *current_thread)
+
+{
+  using ftype = void (ui_out *, const char *, int, int, int,
+		      int, thread_info *, thread_info *);
+
+  do_with_buffered_output<ftype>
+    (do_print_thread, uiout, requested_threads, global_ids, pid,
+     show_global_ids, default_inf_num, tp, current_thread);
+}
+
 /* Like print_thread_info, but in addition, GLOBAL_IDS indicates
    whether REQUESTED_THREADS is a list of global or per-inferior
    thread ids.  */
@@ -1123,82 +1223,12 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
     for (inferior *inf : all_inferiors ())
       for (thread_info *tp : inf->threads ())
 	{
-	  int core;
-
 	  any_thread = true;
 	  if (tp == current_thread && tp->state == THREAD_EXITED)
 	    current_exited = true;
 
-	  if (!should_print_thread (requested_threads, default_inf_num,
-				    global_ids, pid, tp))
-	    continue;
-
-	  ui_out_emit_tuple tuple_emitter (uiout, NULL);
-
-	  if (!uiout->is_mi_like_p ())
-	    {
-	      if (tp == current_thread)
-		uiout->field_string ("current", "*");
-	      else
-		uiout->field_skip ("current");
-
-	      uiout->field_string ("id-in-tg", print_thread_id (tp));
-	    }
-
-	  if (show_global_ids || uiout->is_mi_like_p ())
-	    uiout->field_signed ("id", tp->global_num);
-
-	  /* Switch to the thread (and inferior / target).  */
-	  switch_to_thread (tp);
-
-	  /* For the CLI, we stuff everything into the target-id field.
-	     This is a gross hack to make the output come out looking
-	     correct.  The underlying problem here is that ui-out has no
-	     way to specify that a field's space allocation should be
-	     shared by several fields.  For MI, we do the right thing
-	     instead.  */
-
-	  if (uiout->is_mi_like_p ())
-	    {
-	      uiout->field_string ("target-id", target_pid_to_str (tp->ptid));
-
-	      const char *extra_info = target_extra_thread_info (tp);
-	      if (extra_info != nullptr)
-		uiout->field_string ("details", extra_info);
-
-	      const char *name = thread_name (tp);
-	      if (name != NULL)
-		uiout->field_string ("name", name);
-	    }
-	  else
-	    {
-	      uiout->field_string ("target-id", thread_target_id_str (tp));
-	    }
-
-	  if (tp->state == THREAD_RUNNING)
-	    uiout->text ("(running)\n");
-	  else
-	    {
-	      /* The switch above put us at the top of the stack (leaf
-		 frame).  */
-	      print_stack_frame (get_selected_frame (NULL),
-				 /* For MI output, print frame level.  */
-				 uiout->is_mi_like_p (),
-				 LOCATION, 0);
-	    }
-
-	  if (uiout->is_mi_like_p ())
-	    {
-	      const char *state = "stopped";
-
-	      if (tp->state == THREAD_RUNNING)
-		state = "running";
-	      uiout->field_string ("state", state);
-	    }
-
-	  core = target_core_of_thread (tp->ptid);
-	  if (uiout->is_mi_like_p () && core != -1)
-	    uiout->field_signed ("core", core);
+	  print_thread (uiout, requested_threads, global_ids, pid,
+			show_global_ids, default_inf_num, tp, current_thread);
 	}
 
     /* This end scope restores the current thread and the frame
diff --git a/gdb/ui-file.c b/gdb/ui-file.c
index e0814fe2b2d..73277cd853e 100644
--- a/gdb/ui-file.c
+++ b/gdb/ui-file.c
@@ -234,6 +234,68 @@ string_file::can_emit_style_escape ()
 
 \f
 
+/* See ui-file.h.  */
+
+void
+buffer_file::wrap_here (int indent)
+{
+  m_string_wraps.emplace (m_string_wraps.end (),
+			  string_wrap_pair (m_string, indent));
+  m_string.clear ();
+}
+
+/* See ui-file.h.  */
+
+void
+buffer_file::flush_to_stream ()
+{
+  /* Add m_string to m_string_wraps with no corresponding wrap_here.  */
+  wrap_here (-1);
+
+  for (string_wrap_pair pair : m_string_wraps)
+    {
+      std::string buf = std::move (pair.first);
+      size_t size = buf.size ();
+
+      /* Write each line separately.  */
+      for (size_t prev = 0, cur = 0; cur < size; ++cur)
+	if (buf.at (cur) == '\n' || cur == size - 1)
+	  {
+	    std::string sub = buf.substr (prev, cur - prev + 1);
+	    m_stream->puts (sub.c_str ());
+	    prev = cur + 1;
+	  }
+
+      if (pair.second >= 0)
+	m_stream->wrap_here (pair.second);
+    }
+
+  m_string_wraps.clear ();
+}
+
+/* See ui-file.h.  */
+
+ui_file *
+buffer_file::get_unbuffered_stream (ui_file *stream)
+{
+  buffer_file *buf = dynamic_cast<buffer_file *> (stream);
+
+  if (buf == nullptr)
+    return stream;
+
+  return get_unbuffered_stream (buf->m_stream);
+}
+
+/* See ui-file.h.  */
+
+bool
+buffer_file::isatty ()
+{
+  return m_stream->isatty ();
+}
+
+\f
+
 stdio_file::stdio_file (FILE *file, bool close_p)
 {
   set_stream (file);
diff --git a/gdb/ui-file.h b/gdb/ui-file.h
index de24620e247..a19eb7a9bfe 100644
--- a/gdb/ui-file.h
+++ b/gdb/ui-file.h
@@ -220,13 +220,56 @@ class string_file : public ui_file
   bool empty () const { return m_string.empty (); }
   void clear () { return m_string.clear (); }
 
-private:
+protected:
   /* The internal buffer.  */
   std::string m_string;
 
   bool m_term_out;
 };
 
+/* A string_file implementation that collects output on behalf of a
+   given ui_file.  Provides access to the underlying ui_file so
+   that buffering can be selectively bypassed.  */
+
+class buffer_file : public string_file
+{
+public:
+  explicit buffer_file (bool term_out, ui_file *stream)
+    : string_file (term_out), m_stream (stream)
+  {}
+
+  /* Return the stream associated with this buffer_file.  */
+  ui_file *get_stream ()
+  {
+    return m_stream;
+  }
+
+  /* Record the wrap hint.  When flushing the buffer, the underlying
+     ui_file's wrap_here will be called at the current point in the output.  */
+  void wrap_here (int indent) override;
+
+  /* Flush collected output to the underlying ui_file.  */
+  void flush_to_stream ();
+
+  /* Return true if the underlying stream is a tty.  */
+  bool isatty () override;
+
+  /* Return a pointer to STREAM's underlying ui_file.  Recursively called until
+     a non-buffer_file is found.  */
+  static ui_file *get_unbuffered_stream (ui_file *stream);
+
+private:
+
+  /* The underlying output stream.  */
+  ui_file *m_stream;
+
+  typedef std::pair<std::string, int> string_wrap_pair;
+
+  /* A collection of strings paired with an int representing the argument
+     to a wrap_here call.  */
+  std::vector<string_wrap_pair> m_string_wraps;
+};
+
 /* A ui_file implementation that maps directly onto <stdio.h>'s FILE.
    A stdio_file can either own its underlying file, or not.  If it
    owns the file, then destroying the stdio_file closes the underlying
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index 9380630c320..3749317a25b 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -799,6 +799,12 @@ ui_out::redirect (ui_file *outstream)
   do_redirect (outstream);
 }
 
+void
+ui_out::redirect_to_buffer (buffer_file *buf_file)
+{
+  do_redirect_to_buffer (buf_file);
+}
+
 /* Test the flags against the mask given.  */
 ui_out_flags
 ui_out::test_flags (ui_out_flags mask)
@@ -871,3 +877,18 @@ ui_out::ui_out (ui_out_flags flags)
 ui_out::~ui_out ()
 {
 }
+
+ui_out_buffer_pop::ui_out_buffer_pop (ui_out *uiout)
+: m_uiout (uiout),
+  m_buf_file (uiout->can_emit_style_escape (), gdb_stdout),
+  m_prev_gdb_stdout (gdb_stdout)
+{
+  m_uiout->redirect_to_buffer (&m_buf_file);
+  gdb_stdout = &m_buf_file;
+}
+
+ui_out_buffer_pop::~ui_out_buffer_pop ()
+{
+  m_uiout->redirect_to_buffer (nullptr);
+  gdb_stdout = m_prev_gdb_stdout;
+}
diff --git a/gdb/ui-out.h b/gdb/ui-out.h
index ba5b1de68ab..ceee7be6938 100644
--- a/gdb/ui-out.h
+++ b/gdb/ui-out.h
@@ -262,6 +262,9 @@ class ui_out
   /* Redirect the output of a ui_out object temporarily.  */
   void redirect (ui_file *outstream);
 
+  /* Redirect the output of a ui_out object to a buffer_file temporarily.  */
+  void redirect_to_buffer (buffer_file *buf_file);
+
   ui_out_flags test_flags (ui_out_flags mask);
 
   /* HACK: Code in GDB is currently checking to see the type of ui_out
@@ -360,6 +363,7 @@ class ui_out
   virtual void do_wrap_hint (int indent) = 0;
   virtual void do_flush () = 0;
   virtual void do_redirect (struct ui_file *outstream) = 0;
+  virtual void do_redirect_to_buffer (buffer_file *buf_file) = 0;
 
   virtual void do_progress_start () = 0;
   virtual void do_progress_notify (const std::string &, const char *,
@@ -470,4 +474,66 @@ class ui_out_redirect_pop
   struct ui_out *m_uiout;
 };
 
+/* On construction, redirect a uiout and gdb_stdout to a buffer_file.
+   On destruction restore uiout and gdb_stdout.  */
+
+class ui_out_buffer_pop
+{
+public:
+  ui_out_buffer_pop (ui_out *uiout);
+
+  ~ui_out_buffer_pop ();
+
+  /* Flush buffered output to the underlying output stream.  */
+  void flush ()
+  {
+    m_buf_file.flush_to_stream ();
+  }
+
+  ui_out_buffer_pop (const ui_out_buffer_pop &) = delete;
+  ui_out_buffer_pop &operator= (const ui_out_buffer_pop &) = delete;
+
+private:
+  /* ui_out being temporarily redirected.  */
+  struct ui_out *m_uiout;
+
+  /* Buffer to which output is temporarily redirected to for the lifetime
+     of this object.  */
+  buffer_file m_buf_file;
+
+  /* Original gdb_stdout at the time of this object's construction.  */
+  ui_file *m_prev_gdb_stdout;
+};
+
+/* Redirect output to a buffer_file for the duration of FUNC.  */
+
+template<typename F, typename... Arg>
+void
+do_with_buffered_output (F func, ui_out *uiout, Arg... args)
+{
+  ui_out_buffer_pop buf (uiout);
+
+  try
+    {
+      func (uiout, std::forward<Arg> (args)...);
+    }
+  catch (gdb_exception &ex)
+    {
+      /* Ideally flush would be called in the destructor of buf,
+	 however flushing might cause an exception to be thrown.
+	 Catch it and ensure the first exception propagates.  */
+      try
+	{
+	  buf.flush ();
+	}
+      catch (const gdb_exception &ignore)
+	{
+	}
+
+      throw_exception (std::move (ex));
+    }
+
+  /* Try was successful.  Let any further exceptions propagate.  */
+  buf.flush ();
+}
 #endif /* UI_OUT_H */
-- 
2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator
  2023-06-01  1:43 [PATCH 0/6 v3] gdb/debuginfod: Add on-demand debuginfo downloading Aaron Merey
                   ` (2 preceding siblings ...)
  2023-06-01  1:43 ` [PATCH 3/6 v3] gdb: Buffer gdb_stdout during events that might download deferred debuginfo Aaron Merey
@ 2023-06-01  1:43 ` Aaron Merey
  2023-06-15 13:44   ` Aaron Merey
  2023-07-31 10:11   ` [PATCH " Andrew Burgess
  2023-06-01  1:43 ` [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading Aaron Merey
  2023-06-01  1:43 ` [PATCH 6/6 v3] gdb/debuginfod: Add .debug_line downloading Aaron Merey
  5 siblings, 2 replies; 33+ messages in thread
From: Aaron Merey @ 2023-06-01  1:43 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, Aaron Merey

To facilitate the deletion of objfiles, progspace objects use a safe
iterator that holds a reference to the next objfile in the progspace's
objfile_list.  This allows objfiles to be deleted in a loop without
invalidating the loop's iterator.  progspace also uses an unwrapping
iterator over std::unique_ptr<objfile> that automatically deferences
the unique_ptr.

This patch changes the objfile safe iterator to be a reverse safe
iterator.  It changes the unwrapping iterator into a template.  It
also modifies objfile_list insertion so that separate debug objfiles
are placed into the list after the parent objfile, instead of before.

These changes are intended to prepare gdb for on-demand debuginfo
downloading and the downloading of .gdb_index sections.

With on-demand downloading enabled, gdb might download a debuginfo
file during symtab expansion.  In this case an objfile could be added
to an objfiles_list during iteration over the list (for example, in
iterate_over_symtabs).  We want these loops to also iterate over newly
downloaded objfiles. So objfiles need to be inserted into objfiles_list
after their parent since it is during the search of the parent
objfile for some symbol or filename that the separate debug objfile
might be downloaded.

The unwrapping iterator is changed into a template in order to
use it with objfile qf_require_partial_symbols, which is now also
uses with a safe iterator.  This is because after a separate debug
objfile is downloaded on-demand, we want to remove any .gdb_index
quick_symbol_functions from the parent objfile during iteration over
the parent's quick_symbol_functions.  The newly downloaded separate
debug objfile contains the index and all of the related symbols
so the .gdb_index should not be associated with the parent objfile
any longer.

Finally a safe reverse iterator is now used during progspace objfile
deletion in order to prevent iterator invalidation during the loop
in which objfiles are deleted.  This could happen during forward
iteration over objfiles_list when a separate debug objfile immediately
follows it's parent objfile in the list (which is now possible since
objfiles are inserted into the list after their parent).  Deletion
of the parent would cause deletion of the separate debug objfile,
which would invalidate the safe forward iterator's reference to the
next objfile in the list.  A safe reverse iterator deletes separate
debug objfiles before their parent, so the iterator's reference to
the next objfile always stays valid.

A small change was also made to a testcase in py-objfile.exp to
account for the new placement of separate debug objfiles in
objfiles_list.
---
 gdb/objfiles.h                          |  22 +++-
 gdb/progspace.c                         |   8 +-
 gdb/progspace.h                         | 159 +++++++++++++++++++-----
 gdb/symfile-debug.c                     | 136 ++++++++++----------
 gdb/testsuite/gdb.python/py-objfile.exp |   2 +-
 5 files changed, 218 insertions(+), 109 deletions(-)

diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 189856f0a51..bb7b0a4579d 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -613,6 +613,17 @@ struct objfile
   /* See quick_symbol_functions.  */
   void require_partial_symbols (bool verbose);
 
+  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
+  void remove_partial_symbol (quick_symbol_functions *target)
+  {
+    for (quick_symbol_functions_up &qf_up : qf)
+      if (qf_up.get () == target)
+	{
+	  qf.remove (qf_up);
+	  return;
+	}
+  }
+
   /* Return the relocation offset applied to SECTION.  */
   CORE_ADDR section_offset (bfd_section *section) const
   {
@@ -699,13 +710,20 @@ struct objfile
 
 private:
 
+  using qf_list = std::forward_list<quick_symbol_functions_up>;
+  using unwrapping_qf_range = iterator_range<unwrapping_iterator<qf_list::iterator>>;
+  using qf_safe_range = basic_safe_range<unwrapping_qf_range>;
+
   /* Ensure that partial symbols have been read and return the "quick" (aka
      partial) symbol functions for this symbol reader.  */
-  const std::forward_list<quick_symbol_functions_up> &
+  qf_safe_range
   qf_require_partial_symbols ()
   {
     this->require_partial_symbols (true);
-    return qf;
+    return qf_safe_range
+      (unwrapping_qf_range
+	(unwrapping_iterator<qf_list::iterator> (qf.begin ()),
+	 unwrapping_iterator<qf_list::iterator> (qf.end ())));
   }
 
 public:
diff --git a/gdb/progspace.c b/gdb/progspace.c
index 32bdfebcf7c..1ed75eef2f9 100644
--- a/gdb/progspace.c
+++ b/gdb/progspace.c
@@ -139,19 +139,19 @@ program_space::free_all_objfiles ()
 
 void
 program_space::add_objfile (std::unique_ptr<objfile> &&objfile,
-			    struct objfile *before)
+			    struct objfile *after)
 {
-  if (before == nullptr)
+  if (after == nullptr)
     objfiles_list.push_back (std::move (objfile));
   else
     {
       auto iter = std::find_if (objfiles_list.begin (), objfiles_list.end (),
 				[=] (const std::unique_ptr<::objfile> &objf)
 				{
-				  return objf.get () == before;
+				  return objf.get () == after;
 				});
       gdb_assert (iter != objfiles_list.end ());
-      objfiles_list.insert (iter, std::move (objfile));
+      objfiles_list.insert (++iter, std::move (objfile));
     }
 }
 
diff --git a/gdb/progspace.h b/gdb/progspace.h
index 85215f0e2f1..6e33e48c88e 100644
--- a/gdb/progspace.h
+++ b/gdb/progspace.h
@@ -40,56 +40,141 @@ struct address_space;
 struct program_space;
 struct so_list;
 
+/* An iterator that wraps an iterator over std::unique_ptr, and dereferences
+   the returned object.  This is useful for iterating over a list of shared
+   pointers and returning raw pointers -- which helped avoid touching a lot
+   of code when changing how objfiles are managed.  */
+
+template<typename UniquePtrIter>
+class unwrapping_iterator
+{
+public:
+  typedef unwrapping_iterator self_type;
+  typedef typename UniquePtrIter::value_type::pointer value_type;
+  typedef typename UniquePtrIter::reference  reference;
+  typedef typename UniquePtrIter::pointer pointer;
+  typedef typename UniquePtrIter::iterator_category iterator_category;
+  typedef typename UniquePtrIter::difference_type difference_type;
+
+  unwrapping_iterator (UniquePtrIter iter)
+    : m_iter (std::move (iter))
+  {
+  }
+
+  value_type operator* () const
+  {
+    return m_iter->get ();
+  }
+
+  unwrapping_iterator operator++ ()
+  {
+    ++m_iter;
+    return *this;
+  }
+
+  bool operator!= (const unwrapping_iterator &other) const
+  {
+    return m_iter != other.m_iter;
+  }
+
+private:
+  /* The underlying iterator.  */
+  UniquePtrIter m_iter;
+};
+
 typedef std::list<std::unique_ptr<objfile>> objfile_list;
 
-/* An iterator that wraps an iterator over std::unique_ptr<objfile>,
-   and dereferences the returned object.  This is useful for iterating
-   over a list of shared pointers and returning raw pointers -- which
-   helped avoid touching a lot of code when changing how objfiles are
-   managed.  */
+/* An reverse iterator that wraps an iterator over objfile_list, and
+   dereferences the returned object.  This is useful for reverse iterating
+   over a list of shared pointers and returning raw pointers -- which helped
+   avoid touching a lot of code when changing how objfiles are managed.  */
 
-class unwrapping_objfile_iterator
+class unwrapping_reverse_objfile_iterator
 {
 public:
-
-  typedef unwrapping_objfile_iterator self_type;
+  typedef unwrapping_reverse_objfile_iterator self_type;
   typedef typename ::objfile *value_type;
   typedef typename ::objfile &reference;
   typedef typename ::objfile **pointer;
   typedef typename objfile_list::iterator::iterator_category iterator_category;
   typedef typename objfile_list::iterator::difference_type difference_type;
 
-  unwrapping_objfile_iterator (objfile_list::iterator iter)
-    : m_iter (std::move (iter))
-  {
-  }
-
-  objfile *operator* () const
+  value_type operator* () const
   {
     return m_iter->get ();
   }
 
-  unwrapping_objfile_iterator operator++ ()
+  unwrapping_reverse_objfile_iterator operator++ ()
   {
-    ++m_iter;
+    if (m_iter != m_begin)
+      --m_iter;
+    else
+      {
+	/* We can't decrement M_ITER since it is the begin iterator of the
+	   objfile list.  Set M_ITER to the list's end iterator to indicate
+	   this is now one-past-the-end.  */
+	m_iter = m_end;
+
+	/* Overwrite M_BEGIN to avoid possibly copying an invalid iterator.  */
+	m_begin = m_end;
+      }
+
     return *this;
   }
 
-  bool operator!= (const unwrapping_objfile_iterator &other) const
+  bool operator!= (const unwrapping_reverse_objfile_iterator &other) const
   {
     return m_iter != other.m_iter;
   }
 
+  /* Return an unwrapping reverse iterator starting at the last element of
+     OBJF_LIST.  */
+  static unwrapping_reverse_objfile_iterator begin (objfile_list &objf_list)
+  {
+    auto begin = objf_list.begin ();
+    auto end = objf_list.end ();
+    auto rev_begin = objf_list.end ();
+
+    /* Start REV_BEGIN on the last objfile in OBJF_LIST.  */
+    if (begin != end)
+      --rev_begin;
+
+    return unwrapping_reverse_objfile_iterator (rev_begin, begin, end);
+  }
+
+  /* Return a one-past-the-end unwrapping reverse iterator.  */
+  static unwrapping_reverse_objfile_iterator end (objfile_list &objf_list)
+  {
+    return unwrapping_reverse_objfile_iterator (objf_list.end (),
+						objf_list.end (),
+						objf_list.end ());
+  }
+
 private:
+  /* begin and end methods should be used to create these objects.  */
+  unwrapping_reverse_objfile_iterator (objfile_list::iterator iter,
+				       objfile_list::iterator begin,
+				       objfile_list::iterator end)
+    : m_iter (std::move (iter)), m_begin (std::move (begin)),
+      m_end (std::move (end))
+  {
+  }
 
-  /* The underlying iterator.  */
-  objfile_list::iterator m_iter;
-};
+ /* The underlying iterator.  */
+ objfile_list::iterator m_iter;
 
+ /* The underlying iterator pointing to the first objfile in the sequence.  Used
+    to track when to stop decrementing M_ITER.  */
+ objfile_list::iterator m_begin;
 
-/* A range that returns unwrapping_objfile_iterators.  */
+  /* The underlying iterator's one-past-the-end.  */
+ objfile_list::iterator m_end;
+};
 
-using unwrapping_objfile_range = iterator_range<unwrapping_objfile_iterator>;
+/* A range that returns unwrapping_iterators.  */
+
+using unwrapping_objfile_range
+  = iterator_range<unwrapping_iterator<objfile_list::iterator>>;
 
 /* A program space represents a symbolic view of an address space.
    Roughly speaking, it holds all the data associated with a
@@ -209,11 +294,12 @@ struct program_space
   objfiles_range objfiles ()
   {
     return objfiles_range
-      (unwrapping_objfile_iterator (objfiles_list.begin ()),
-       unwrapping_objfile_iterator (objfiles_list.end ()));
+      (unwrapping_iterator<objfile_list::iterator> (objfiles_list.begin ()),
+       unwrapping_iterator<objfile_list::iterator> (objfiles_list.end ()));
   }
 
-  using objfiles_safe_range = basic_safe_range<objfiles_range>;
+  using objfiles_reverse_range = iterator_range<unwrapping_reverse_objfile_iterator>;
+  using objfiles_safe_reverse_range = basic_safe_range<objfiles_reverse_range>;
 
   /* An iterable object that can be used to iterate over all objfiles.
      The basic use is in a foreach, like:
@@ -221,20 +307,25 @@ struct program_space
      for (objfile *objf : pspace->objfiles_safe ()) { ... }
 
      This variant uses a basic_safe_iterator so that objfiles can be
-     deleted during iteration.  */
-  objfiles_safe_range objfiles_safe ()
+     deleted during iteration.
+
+     The use of a reverse iterator helps ensure that separate debug
+     objfiles are deleted before their parent objfile.  This prevents
+     the invalidation of an iterator due to the deletion of a parent
+     objfile.  */
+  objfiles_safe_reverse_range objfiles_safe ()
   {
-    return objfiles_safe_range
-      (objfiles_range
-	 (unwrapping_objfile_iterator (objfiles_list.begin ()),
-	  unwrapping_objfile_iterator (objfiles_list.end ())));
+    return objfiles_safe_reverse_range
+      (objfiles_reverse_range
+	(unwrapping_reverse_objfile_iterator::begin (objfiles_list),
+	 unwrapping_reverse_objfile_iterator::end (objfiles_list)));
   }
 
-  /* Add OBJFILE to the list of objfiles, putting it just before
-     BEFORE.  If BEFORE is nullptr, it will go at the end of the
+  /* Add OBJFILE to the list of objfiles, putting it just after
+     AFTER.  If AFTER is nullptr, it will go at the end of the
      list.  */
   void add_objfile (std::unique_ptr<objfile> &&objfile,
-		    struct objfile *before);
+		    struct objfile *after);
 
   /* Remove OBJFILE from the list of objfiles.  */
   void remove_objfile (struct objfile *objfile);
diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
index 9db5c47a8ce..784b81b5ca6 100644
--- a/gdb/symfile-debug.c
+++ b/gdb/symfile-debug.c
@@ -109,9 +109,9 @@ objfile::has_unexpanded_symtabs ()
 		objfile_debug_name (this));
 
   bool result = false;
-  for (const auto &iter : qf_require_partial_symbols ())
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
     {
-      if (iter->has_unexpanded_symtabs (this))
+      if (qf->has_unexpanded_symtabs (this))
 	{
 	  result = true;
 	  break;
@@ -134,9 +134,9 @@ objfile::find_last_source_symtab ()
     gdb_printf (gdb_stdlog, "qf->find_last_source_symtab (%s)\n",
 		objfile_debug_name (this));
 
-  for (const auto &iter : qf_require_partial_symbols ())
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
     {
-      retval = iter->find_last_source_symtab (this);
+      retval = qf->find_last_source_symtab (this);
       if (retval != nullptr)
 	break;
     }
@@ -167,8 +167,8 @@ objfile::forget_cached_source_info ()
 	}
     }
 
-  for (const auto &iter : qf_require_partial_symbols ())
-    iter->forget_cached_source_info (this);
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
+    qf->forget_cached_source_info (this);
 }
 
 bool
@@ -214,17 +214,17 @@ objfile::map_symtabs_matching_filename
     return result;
   };
 
-  for (const auto &iter : qf_require_partial_symbols ())
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
     {
-      if (!iter->expand_symtabs_matching (this,
-					  match_one_filename,
-					  nullptr,
-					  nullptr,
-					  on_expansion,
-					  (SEARCH_GLOBAL_BLOCK
-					   | SEARCH_STATIC_BLOCK),
-					  UNDEF_DOMAIN,
-					  ALL_DOMAIN))
+      if (!qf->expand_symtabs_matching (this,
+					match_one_filename,
+					nullptr,
+					nullptr,
+					on_expansion,
+					(SEARCH_GLOBAL_BLOCK
+					 | SEARCH_STATIC_BLOCK),
+					UNDEF_DOMAIN,
+					ALL_DOMAIN))
 	{
 	  retval = false;
 	  break;
@@ -283,18 +283,18 @@ objfile::lookup_symbol (block_enum kind, const char *name, domain_enum domain)
     return true;
   };
 
-  for (const auto &iter : qf_require_partial_symbols ())
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
     {
-      if (!iter->expand_symtabs_matching (this,
-					  nullptr,
-					  &lookup_name,
-					  nullptr,
-					  search_one_symtab,
-					  kind == GLOBAL_BLOCK
-					  ? SEARCH_GLOBAL_BLOCK
-					  : SEARCH_STATIC_BLOCK,
-					  domain,
-					  ALL_DOMAIN))
+      if (!qf->expand_symtabs_matching (this,
+					nullptr,
+					&lookup_name,
+					nullptr,
+					search_one_symtab,
+					kind == GLOBAL_BLOCK
+					? SEARCH_GLOBAL_BLOCK
+					: SEARCH_STATIC_BLOCK,
+					domain,
+					ALL_DOMAIN))
 	break;
     }
 
@@ -314,8 +314,8 @@ objfile::print_stats (bool print_bcache)
     gdb_printf (gdb_stdlog, "qf->print_stats (%s, %d)\n",
 		objfile_debug_name (this), print_bcache);
 
-  for (const auto &iter : qf_require_partial_symbols ())
-    iter->print_stats (this, print_bcache);
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
+    qf->print_stats (this, print_bcache);
 }
 
 void
@@ -340,16 +340,16 @@ objfile::expand_symtabs_for_function (const char *func_name)
   lookup_name_info base_lookup (func_name, symbol_name_match_type::FULL);
   lookup_name_info lookup_name = base_lookup.make_ignore_params ();
 
-  for (const auto &iter : qf_require_partial_symbols ())
-    iter->expand_symtabs_matching (this,
-				   nullptr,
-				   &lookup_name,
-				   nullptr,
-				   nullptr,
-				   (SEARCH_GLOBAL_BLOCK
-				    | SEARCH_STATIC_BLOCK),
-				   VAR_DOMAIN,
-				   ALL_DOMAIN);
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
+    qf->expand_symtabs_matching (this,
+				 nullptr,
+				 &lookup_name,
+				 nullptr,
+				 nullptr,
+				 (SEARCH_GLOBAL_BLOCK
+				  | SEARCH_STATIC_BLOCK),
+				 VAR_DOMAIN,
+				 ALL_DOMAIN);
 }
 
 void
@@ -359,8 +359,8 @@ objfile::expand_all_symtabs ()
     gdb_printf (gdb_stdlog, "qf->expand_all_symtabs (%s)\n",
 		objfile_debug_name (this));
 
-  for (const auto &iter : qf_require_partial_symbols ())
-    iter->expand_all_symtabs (this);
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
+    qf->expand_all_symtabs (this);
 }
 
 void
@@ -377,16 +377,16 @@ objfile::expand_symtabs_with_fullname (const char *fullname)
     return filename_cmp (basenames ? basename : fullname, filename) == 0;
   };
 
-  for (const auto &iter : qf_require_partial_symbols ())
-    iter->expand_symtabs_matching (this,
-				   file_matcher,
-				   nullptr,
-				   nullptr,
-				   nullptr,
-				   (SEARCH_GLOBAL_BLOCK
-				    | SEARCH_STATIC_BLOCK),
-				   UNDEF_DOMAIN,
-				   ALL_DOMAIN);
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
+    qf->expand_symtabs_matching (this,
+				 file_matcher,
+				 nullptr,
+				 nullptr,
+				 nullptr,
+				 (SEARCH_GLOBAL_BLOCK
+				  | SEARCH_STATIC_BLOCK),
+				 UNDEF_DOMAIN,
+				 ALL_DOMAIN);
 }
 
 void
@@ -402,9 +402,9 @@ objfile::expand_matching_symbols
 		domain_name (domain), global,
 		host_address_to_string (ordered_compare));
 
-  for (const auto &iter : qf_require_partial_symbols ())
-    iter->expand_matching_symbols (this, name, domain, global,
-				   ordered_compare);
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
+    qf->expand_matching_symbols (this, name, domain, global,
+				 ordered_compare);
 }
 
 bool
@@ -429,10 +429,10 @@ objfile::expand_symtabs_matching
 		host_address_to_string (&expansion_notify),
 		search_domain_name (kind));
 
-  for (const auto &iter : qf_require_partial_symbols ())
-    if (!iter->expand_symtabs_matching (this, file_matcher, lookup_name,
-					symbol_matcher, expansion_notify,
-					search_flags, domain, kind))
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
+    if (!qf->expand_symtabs_matching (this, file_matcher, lookup_name,
+				      symbol_matcher, expansion_notify,
+				      search_flags, domain, kind))
       return false;
   return true;
 }
@@ -454,10 +454,10 @@ objfile::find_pc_sect_compunit_symtab (struct bound_minimal_symbol msymbol,
 		host_address_to_string (section),
 		warn_if_readin);
 
-  for (const auto &iter : qf_require_partial_symbols ())
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
     {
-      retval = iter->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
-						   warn_if_readin);
+      retval = qf->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
+						 warn_if_readin);
       if (retval != nullptr)
 	break;
     }
@@ -482,8 +482,8 @@ objfile::map_symbol_filenames (gdb::function_view<symbol_filename_ftype> fun,
 		objfile_debug_name (this),
 		need_fullname);
 
-  for (const auto &iter : qf_require_partial_symbols ())
-    iter->map_symbol_filenames (this, fun, need_fullname);
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
+    qf->map_symbol_filenames (this, fun, need_fullname);
 }
 
 struct compunit_symtab *
@@ -496,9 +496,9 @@ objfile::find_compunit_symtab_by_address (CORE_ADDR address)
 		hex_string (address));
 
   struct compunit_symtab *result = NULL;
-  for (const auto &iter : qf_require_partial_symbols ())
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
     {
-      result = iter->find_compunit_symtab_by_address (this, address);
+      result = qf->find_compunit_symtab_by_address (this, address);
       if (result != nullptr)
 	break;
     }
@@ -521,10 +521,10 @@ objfile::lookup_global_symbol_language (const char *name,
   enum language result = language_unknown;
   *symbol_found_p = false;
 
-  for (const auto &iter : qf_require_partial_symbols ())
+  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
     {
-      result = iter->lookup_global_symbol_language (this, name, domain,
-						    symbol_found_p);
+      result = qf->lookup_global_symbol_language (this, name, domain,
+						  symbol_found_p);
       if (*symbol_found_p)
 	break;
     }
diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp
index 61b9942de79..0bf49976b73 100644
--- a/gdb/testsuite/gdb.python/py-objfile.exp
+++ b/gdb/testsuite/gdb.python/py-objfile.exp
@@ -135,7 +135,7 @@ gdb_test "p main" "= {<text variable, no debug info>} $hex <main>" \
 gdb_py_test_silent_cmd "python objfile.add_separate_debug_file(\"${binfile}\")" \
     "Add separate debug file file" 1
 
-gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[0\]" \
+gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[1\]" \
     "Get separate debug info objfile" 1
 
 gdb_test "python print (sep_objfile.owner.filename)" "${testfile}2" \
-- 
2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading
  2023-06-01  1:43 [PATCH 0/6 v3] gdb/debuginfod: Add on-demand debuginfo downloading Aaron Merey
                   ` (3 preceding siblings ...)
  2023-06-01  1:43 ` [PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator Aaron Merey
@ 2023-06-01  1:43 ` Aaron Merey
  2023-06-15 13:44   ` Aaron Merey
                     ` (2 more replies)
  2023-06-01  1:43 ` [PATCH 6/6 v3] gdb/debuginfod: Add .debug_line downloading Aaron Merey
  5 siblings, 3 replies; 33+ messages in thread
From: Aaron Merey @ 2023-06-01  1:43 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, Aaron Merey

v2.2: https://sourceware.org/pipermail/gdb-patches/2023-May/199326.html

v3 includes testcases instead of adding them in a separate patch.
Tests related to section downloading are now included in
testsuite/gdb.debuginfod/section.exp.

Commit message:

At the beginning of a session, gdb may attempt to download debuginfo
for all shared libraries associated with the process or core file
being debugged.  This can be a waste of time and storage space when much
of the debuginfo ends up not being used during the session.

To reduce the gdb's startup latency and to download only the debuginfo
that is really needed, this patch adds on-demand downloading of debuginfo.

'set debuginfo enabled on' now causes gdb to attempt to download a .gdb_index
for each shared library instead of its full debuginfo.  Each corresponding
separate debuginfo will be deferred until gdb needs to expand symtabs
associated with the debuginfo's index.

Because these indices are significantly smaller than their corresponding
debuginfo, this generally reduces the total amount of data gdb downloads.
Reductions of 80%-95% have been observed when debugging large GUI programs.

    (gdb) set debuginfod enabled on
    (gdb) start
    Downloading section .gdb_index for /lib64/libcurl.so.4
    [...]
    1826        client->server_mhandle = curl_multi_init ();
    (gdb) step
    Downloading separate debug info for /lib64/libcurl.so.4
    Downloading separate debug info for [libcurl dwz]
    Downloading source file /usr/src/debug/curl-7.85.0-6.fc37.x86_64/build-full/lib/../../lib/multi.c
    curl_multi_init () at ../../lib/multi.c:457
    457     {
    (gdb)

Some of the key functions below include dwarf2_has_separate_index which
downloads the separate .gdb_index.  If successful, the shared library
objfile owns the index until the separate debug objfile is downloaded
or confirmed to not be available.

read_full_dwarf_from_debuginfod downloads the full debuginfo and
initializes the separate debug objfile.  It is called by functions
such as dwarf2_gdb_index::expand_symtabs_matching and
dwarf2_base_index_functions::find_pc_sect_compunit_symtab when symtab
expansion is required.
---
 gdb/dwarf2/frame.c                         |  13 ++
 gdb/dwarf2/frame.h                         |   4 +
 gdb/dwarf2/index-cache.c                   |  33 ++++
 gdb/dwarf2/index-cache.h                   |  13 ++
 gdb/dwarf2/public.h                        |   7 +
 gdb/dwarf2/read-gdb-index.c                | 156 ++++++++++++++++--
 gdb/dwarf2/read.c                          | 146 ++++++++++++++++-
 gdb/dwarf2/read.h                          |  10 ++
 gdb/dwarf2/section.c                       |   3 +-
 gdb/elfread.c                              |   2 +-
 gdb/objfile-flags.h                        |   4 +
 gdb/objfiles.h                             |  17 +-
 gdb/quick-symbol.h                         |   4 +
 gdb/symfile.c                              |  13 +-
 gdb/symtab.c                               |  18 +-
 gdb/testsuite/gdb.debuginfod/libsection1.c |  40 +++++
 gdb/testsuite/gdb.debuginfod/libsection2.c |  37 +++++
 gdb/testsuite/gdb.debuginfod/section.c     |  29 ++++
 gdb/testsuite/gdb.debuginfod/section.exp   | 181 +++++++++++++++++++++
 gdb/testsuite/lib/debuginfod-support.exp   |  30 ++++
 20 files changed, 734 insertions(+), 26 deletions(-)
 create mode 100644 gdb/testsuite/gdb.debuginfod/libsection1.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/libsection2.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/section.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/section.exp

diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index a561aaf3100..3613f8252a7 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -1609,6 +1609,19 @@ set_comp_unit (struct objfile *objfile, struct comp_unit *unit)
   return dwarf2_frame_bfd_data.set (abfd, unit);
 }
 
+/* See frame.h.  */
+
+void
+dwarf2_clear_frame_data (struct objfile *objfile)
+{
+  bfd *abfd = objfile->obfd.get ();
+
+  if (gdb_bfd_requires_relocations (abfd))
+    dwarf2_frame_objfile_data.clear (objfile);
+  else
+    dwarf2_frame_bfd_data.clear (abfd);
+}
+
 /* Find the FDE for *PC.  Return a pointer to the FDE, and store the
    initial location associated with it into *PC.  */
 
diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
index 5643e557513..2391e313e7c 100644
--- a/gdb/dwarf2/frame.h
+++ b/gdb/dwarf2/frame.h
@@ -238,6 +238,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
 extern const struct frame_base *
   dwarf2_frame_base_sniffer (frame_info_ptr this_frame);
 
+/* Delete OBJFILEs comp_unit.  */
+
+extern void dwarf2_clear_frame_data (struct objfile * objfile);
+
 /* Compute the DWARF CFA for a frame.  */
 
 CORE_ADDR dwarf2_frame_cfa (frame_info_ptr this_frame);
diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
index 79ab706ee9d..bbafcd321b2 100644
--- a/gdb/dwarf2/index-cache.c
+++ b/gdb/dwarf2/index-cache.c
@@ -216,6 +216,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
   return {};
 }
 
+/* See index-cache.h.  */
+
+gdb::array_view<const gdb_byte>
+index_cache::lookup_gdb_index_debuginfod (const char *index_path,
+					  std::unique_ptr<index_cache_resource> *resource)
+{
+  try
+    {
+      /* Try to map that file.  */
+      index_cache_resource_mmap *mmap_resource
+	= new index_cache_resource_mmap (index_path);
+
+      /* Hand the resource to the caller.  */
+      resource->reset (mmap_resource);
+
+      return gdb::array_view<const gdb_byte>
+	  ((const gdb_byte *) mmap_resource->mapping.get (),
+	   mmap_resource->mapping.size ());
+    }
+  catch (const gdb_exception_error &except)
+    {
+      warning (_("Unable to read %s: %s"), index_path, except.what ());
+    }
+
+  return {};
+}
+
 #else /* !HAVE_SYS_MMAN_H */
 
 /* See dwarf-index-cache.h.  This is a no-op on unsupported systems.  */
@@ -227,6 +254,12 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
   return {};
 }
 
+gdb::array_view<const gdb_byte>
+index_cache::lookup_gdb_index_debuginfod (const char *index_path,
+					  std::unique_ptr<index_cache_resource> *resource)
+{
+  return {};
+}
 #endif
 
 /* See dwarf-index-cache.h.  */
diff --git a/gdb/dwarf2/index-cache.h b/gdb/dwarf2/index-cache.h
index 1efff17049f..e400afd5123 100644
--- a/gdb/dwarf2/index-cache.h
+++ b/gdb/dwarf2/index-cache.h
@@ -67,6 +67,19 @@ class index_cache
   lookup_gdb_index (const bfd_build_id *build_id,
 		    std::unique_ptr<index_cache_resource> *resource);
 
+  /* Look for an index file located at INDEX_PATH in the debuginfod cache.
+     Unlike lookup_gdb_index, this function does not exit early if the
+     index cache has not been enabled.
+
+     If found, return the contents as an array_view and store the underlying
+     resources (allocated memory, mapped file, etc) in RESOURCE.  The returned
+     array_view is valid as long as RESOURCE is not destroyed.
+
+     If no matching index file is found, return an empty array view.  */
+  gdb::array_view<const gdb_byte>
+  lookup_gdb_index_debuginfod (const char *index_path,
+			       std::unique_ptr<index_cache_resource> *resource);
+
   /* Return the number of cache hits.  */
   unsigned int n_hits () const
   { return m_n_hits; }
diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h
index 0e74857eb1a..4a44cdbc223 100644
--- a/gdb/dwarf2/public.h
+++ b/gdb/dwarf2/public.h
@@ -40,4 +40,11 @@ extern void dwarf2_initialize_objfile (struct objfile *objfile);
 
 extern void dwarf2_build_frame_info (struct objfile *);
 
+/* Query debuginfod for the .gdb_index associated with OBJFILE.  If
+   successful, create an objfile to hold the .gdb_index information
+   and act as a placeholder until the full debuginfo needs to be
+   downloaded.  */
+
+extern bool dwarf2_has_separate_index (struct objfile *);
+
 #endif /* DWARF2_PUBLIC_H */
diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
index 1006386cb2d..d3516e92361 100644
--- a/gdb/dwarf2/read-gdb-index.c
+++ b/gdb/dwarf2/read-gdb-index.c
@@ -136,6 +136,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
      gdb.dwarf2/gdb-index.exp testcase.  */
   void dump (struct objfile *objfile) override;
 
+  /* Calls do_expand_matching_symbols and downloads debuginfo if necessary.  */
   void expand_matching_symbols
     (struct objfile *,
      const lookup_name_info &lookup_name,
@@ -143,6 +144,14 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
      int global,
      symbol_compare_ftype *ordered_compare) override;
 
+  void do_expand_matching_symbols
+    (struct objfile *,
+     const lookup_name_info &lookup_name,
+     domain_enum domain,
+     int global,
+     symbol_compare_ftype *ordered_compare);
+
+  /* Calls do_expand_symtabs_matching and downloads debuginfo if necessary.  */
   bool expand_symtabs_matching
     (struct objfile *objfile,
      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
@@ -152,8 +161,59 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
      block_search_flags search_flags,
      domain_enum domain,
      enum search_domain kind) override;
+
+  bool do_expand_symtabs_matching
+    (struct objfile *objfile,
+     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+     const lookup_name_info *lookup_name,
+     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+     block_search_flags search_flags,
+     domain_enum domain,
+     enum search_domain kind);
+
+  /* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
+     debuginfo if necessary.  */
+  void expand_all_symtabs (struct objfile *objfile) override;
+
+  /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
+     debuginfo if necessary.  */
+  struct symtab *find_last_source_symtab (struct objfile *objfile) override;
 };
 
+void
+dwarf2_gdb_index::expand_all_symtabs (struct objfile *objfile)
+{
+  try
+    {
+      dwarf2_base_index_functions::expand_all_symtabs (objfile);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	exception_print (gdb_stderr, e);
+      else
+	read_full_dwarf_from_debuginfod (objfile, this);
+    }
+}
+
+struct symtab *
+dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
+{
+  try
+    {
+      return dwarf2_base_index_functions::find_last_source_symtab (objfile);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	exception_print (gdb_stderr, e);
+      else
+	read_full_dwarf_from_debuginfod (objfile, this);
+      return nullptr;
+    }
+}
+
 /* This dumps minimal information about the index.
    It is called via "mt print objfiles".
    One use is to verify .gdb_index has been loaded by the
@@ -315,7 +375,7 @@ dw2_symtab_iter_next (struct dw2_symtab_iterator *iter,
 }
 
 void
-dwarf2_gdb_index::expand_matching_symbols
+dwarf2_gdb_index::do_expand_matching_symbols
   (struct objfile *objfile,
    const lookup_name_info &name, domain_enum domain,
    int global,
@@ -353,6 +413,29 @@ dwarf2_gdb_index::expand_matching_symbols
     }, per_objfile);
 }
 
+void
+dwarf2_gdb_index::expand_matching_symbols
+  (struct objfile *objfile,
+   const lookup_name_info &lookup_name,
+   domain_enum domain,
+   int global,
+   symbol_compare_ftype *ordered_compare)
+{
+  try
+    {
+      do_expand_matching_symbols (objfile, lookup_name, domain,
+				  global, ordered_compare);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	exception_print (gdb_stderr, e);
+      else
+	read_full_dwarf_from_debuginfod (objfile, this);
+      return;
+    }
+}
+
 /* Helper for dw2_expand_matching symtabs.  Called on each symbol
    matched, to expand corresponding CUs that were marked.  IDX is the
    index of the symbol name that matched.  */
@@ -455,7 +538,7 @@ dw2_expand_marked_cus
 }
 
 bool
-dwarf2_gdb_index::expand_symtabs_matching
+dwarf2_gdb_index::do_expand_symtabs_matching
     (struct objfile *objfile,
      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
      const lookup_name_info *lookup_name,
@@ -504,6 +587,39 @@ dwarf2_gdb_index::expand_symtabs_matching
   return result;
 }
 
+bool
+dwarf2_gdb_index::expand_symtabs_matching
+    (struct objfile *objfile,
+     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+     const lookup_name_info *lookup_name,
+     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+     block_search_flags search_flags,
+     domain_enum domain,
+     enum search_domain kind)
+{
+  if (objfile->flags & OBJF_READNEVER)
+    return false;
+
+  try
+    {
+      return do_expand_symtabs_matching (objfile, file_matcher, lookup_name,
+					 symbol_matcher, expansion_notify,
+					 search_flags, domain, kind);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	{
+	  exception_print (gdb_stderr, e);
+	  return false;
+	}
+
+      read_full_dwarf_from_debuginfod (objfile, this);
+      return true;
+    }
+}
+
 quick_symbol_functions_up
 mapped_gdb_index::make_quick_functions () const
 {
@@ -797,28 +913,32 @@ dwarf2_read_gdb_index
 
   /* If there is a .dwz file, read it so we can get its CU list as
      well.  */
-  dwz = dwarf2_get_dwz_file (per_bfd);
-  if (dwz != NULL)
+  if (get_gdb_index_contents_dwz != nullptr)
     {
       mapped_gdb_index dwz_map;
       const gdb_byte *dwz_types_ignore;
       offset_type dwz_types_elements_ignore;
+      dwz = dwarf2_get_dwz_file (per_bfd);
 
-      gdb::array_view<const gdb_byte> dwz_index_content
-	= get_gdb_index_contents_dwz (objfile, dwz);
-
-      if (dwz_index_content.empty ())
-	return 0;
-
-      if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()),
-				       1, dwz_index_content, &dwz_map,
-				       &dwz_list, &dwz_list_elements,
-				       &dwz_types_ignore,
-				       &dwz_types_elements_ignore))
+      if (dwz != nullptr)
 	{
-	  warning (_("could not read '.gdb_index' section from %s; skipping"),
-		   bfd_get_filename (dwz->dwz_bfd.get ()));
-	  return 0;
+	  gdb::array_view<const gdb_byte> dwz_index_content
+	    = get_gdb_index_contents_dwz (objfile, dwz);
+
+	  if (dwz_index_content.empty ())
+	    return 0;
+
+	  if (!read_gdb_index_from_buffer (bfd_get_filename
+					     (dwz->dwz_bfd.get ()),
+					   1, dwz_index_content, &dwz_map,
+					   &dwz_list, &dwz_list_elements,
+					   &dwz_types_ignore,
+					   &dwz_types_elements_ignore))
+	    {
+	      warning (_("could not read '.gdb_index' section from %s; skipping"),
+		       bfd_get_filename (dwz->dwz_bfd.get ()));
+		return 0;
+	    }
 	}
     }
 
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 4828409222c..96d1ff53d91 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -34,6 +34,7 @@
 #include "dwarf2/attribute.h"
 #include "dwarf2/comp-unit-head.h"
 #include "dwarf2/cu.h"
+#include "dwarf2/frame.h"
 #include "dwarf2/index-cache.h"
 #include "dwarf2/index-common.h"
 #include "dwarf2/leb.h"
@@ -95,6 +96,8 @@
 #include "split-name.h"
 #include "gdbsupport/parallel-for.h"
 #include "gdbsupport/thread-pool.h"
+#include "inferior.h"
+#include "debuginfod-support.h"
 
 /* When == 1, print basic high level tracing messages.
    When > 1, be more verbose.
@@ -3168,7 +3171,7 @@ dwarf2_base_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd,
 }
 
 struct compunit_symtab *
-dwarf2_base_index_functions::find_pc_sect_compunit_symtab
+dwarf2_base_index_functions::do_find_pc_sect_compunit_symtab
      (struct objfile *objfile,
       struct bound_minimal_symbol msymbol,
       CORE_ADDR pc,
@@ -3199,6 +3202,32 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
   return result;
 }
 
+struct compunit_symtab *
+dwarf2_base_index_functions::find_pc_sect_compunit_symtab
+     (struct objfile *objfile,
+      struct bound_minimal_symbol msymbol,
+      CORE_ADDR pc,
+      struct obj_section *section,
+      int warn_if_readin)
+{
+  if (objfile->flags & OBJF_READNEVER)
+    return nullptr;
+
+  try
+    {
+      return do_find_pc_sect_compunit_symtab (objfile, msymbol, pc,
+					      section, warn_if_readin);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	exception_print (gdb_stderr, e);
+      else
+	read_full_dwarf_from_debuginfod (objfile, this);
+      return nullptr;
+    }
+}
+
 void
 dwarf2_base_index_functions::map_symbol_filenames
      (struct objfile *objfile,
@@ -3355,6 +3384,29 @@ get_gdb_index_contents_from_cache_dwz (objfile *obj, dwz_file *dwz)
   return global_index_cache.lookup_gdb_index (build_id, &dwz->index_cache_res);
 }
 
+/* Query debuginfod for the .gdb_index matching OBJFILE's build-id.  Return the
+   contents if successful.  */
+
+static gdb::array_view<const gdb_byte>
+get_gdb_index_contents_from_debuginfod (objfile *objfile, dwarf2_per_bfd *per_bfd)
+{
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  if (build_id == nullptr)
+    return {};
+
+  gdb::unique_xmalloc_ptr<char> index_path;
+  scoped_fd fd = debuginfod_section_query (build_id->data, build_id->size,
+					   bfd_get_filename
+					     (objfile->obfd.get ()),
+					   ".gdb_index",
+					   &index_path);
+  if (fd.get () < 0)
+    return {};
+
+  return global_index_cache.lookup_gdb_index_debuginfod
+    (index_path.get (), &per_bfd->index_cache_res);
+}
+
 static quick_symbol_functions_up make_cooked_index_funcs ();
 
 /* See dwarf2/public.h.  */
@@ -3420,10 +3472,102 @@ dwarf2_initialize_objfile (struct objfile *objfile)
       return;
     }
 
+  if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED)
+      && dwarf2_read_gdb_index (per_objfile,
+				get_gdb_index_contents_from_debuginfod,
+				nullptr))
+    {
+      dwarf_read_debug_printf ("found .gdb_index from debuginfod");
+      objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
+      objfile->qf.begin ()->get ()->from_separate_index = true;
+      return;
+    }
+
   global_index_cache.miss ();
   objfile->qf.push_front (make_cooked_index_funcs ());
 }
 
+/* See read.h.  */
+
+void
+read_full_dwarf_from_debuginfod (struct objfile *objfile,
+				 dwarf2_base_index_functions *fncs)
+{
+  gdb_assert (objfile->flags & OBJF_DOWNLOAD_DEFERRED);
+
+  const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  const char *filename;
+  gdb_bfd_ref_ptr debug_bfd;
+  gdb::unique_xmalloc_ptr<char> symfile_path;
+  scoped_fd fd;
+
+  if (build_id == nullptr)
+    goto unset;
+
+  filename = bfd_get_filename (objfile->obfd.get ());
+  fd = debuginfod_debuginfo_query (build_id->data, build_id->size,
+				   filename, &symfile_path);
+  if (fd.get () < 0)
+    goto unset;
+
+  /* Separate debuginfo successfully retrieved from server.  */
+  debug_bfd = symfile_bfd_open (symfile_path.get ());
+  if (debug_bfd == nullptr
+      || !build_id_verify (debug_bfd.get (), build_id->size, build_id->data))
+    {
+      warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
+	       filename);
+      goto unset;
+    }
+
+  /* Clear frame data so it can be recalculated using DWARF.  */
+  dwarf2_clear_frame_data (objfile);
+
+  /* This may also trigger a dwz download.  */
+  symbol_file_add_separate (debug_bfd, symfile_path.get (),
+			    current_inferior ()->symfile_flags, objfile);
+
+unset:
+  objfile->remove_deferred_status ();
+}
+
+/* See public.h.  */
+
+bool
+dwarf2_has_separate_index (struct objfile *objfile)
+{
+  if (objfile->flags & OBJF_DOWNLOAD_DEFERRED)
+    return true;
+  if (objfile->flags & OBJF_MAINLINE)
+    return false;
+  if (!IS_DIR_SEPARATOR (*objfile_filename (objfile)))
+    return false;
+
+  gdb::unique_xmalloc_ptr<char> index_path;
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+
+  if (build_id == nullptr)
+    return false;
+
+  scoped_fd fd = debuginfod_section_query (build_id->data,
+					   build_id->size,
+					   bfd_get_filename
+					     (objfile->obfd.get ()),
+					   ".gdb_index",
+					   &index_path);
+
+  if (fd.get () < 0)
+    return false;
+
+  /* We found a separate .gdb_index file so a separate debuginfo file
+     should exist, but we don't want to download it until necessary.
+     Attach the index to this objfile and defer the debuginfo download
+     until gdb needs to expand symtabs referenced by the index.  */
+  objfile->flags |= OBJF_DOWNLOAD_DEFERRED;
+  dwarf2_initialize_objfile (objfile);
+  return true;
+}
+
 \f
 
 /* Build a partial symbol table.  */
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 37023a20709..e3131693b81 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -866,6 +866,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
      CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
        override final;
 
+  struct compunit_symtab *do_find_pc_sect_compunit_symtab
+    (struct objfile *objfile, struct bound_minimal_symbol msymbol,
+     CORE_ADDR pc, struct obj_section *section, int warn_if_readin);
+
   struct compunit_symtab *find_compunit_symtab_by_address
     (struct objfile *objfile, CORE_ADDR address) override
   {
@@ -942,4 +946,10 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
 				       dwarf2_section_info *section,
 				       addrmap *mutable_map);
 
+/* If OBJFILE contains information from a separately downloaded .gdb_index,
+   attempt to download the full debuginfo.  */
+
+extern void read_full_dwarf_from_debuginfod (struct objfile *,
+					     dwarf2_base_index_functions *);
+
 #endif /* DWARF2READ_H */
diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c
index c9ef41893ee..8cb09e3381a 100644
--- a/gdb/dwarf2/section.c
+++ b/gdb/dwarf2/section.c
@@ -54,7 +54,8 @@ dwarf2_section_info::get_bfd_owner () const
       section = get_containing_section ();
       gdb_assert (!section->is_virtual);
     }
-  gdb_assert (section->s.section != nullptr);
+  if (section->s.section == nullptr)
+    error (_("Can't find owner of DWARF section."));
   return section->s.section->owner;
 }
 
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 799e3b914f8..133341ea615 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -1242,7 +1242,7 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
 	    symbol_file_add_separate (debug_bfd, debugfile.c_str (),
 				      symfile_flags, objfile);
 	}
-      else
+      else if (!dwarf2_has_separate_index (objfile))
 	{
 	  has_dwarf2 = false;
 	  const struct bfd_build_id *build_id
diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h
index 9dee2ee51a0..fb3f741c899 100644
--- a/gdb/objfile-flags.h
+++ b/gdb/objfile-flags.h
@@ -60,6 +60,10 @@ enum objfile_flag : unsigned
     /* User requested that we do not read this objfile's symbolic
        information.  */
     OBJF_READNEVER = 1 << 6,
+
+    /* A separate .gdb_index has been downloaded for this objfile.
+       Debuginfo for this objfile can be downloaded when required.  */
+    OBJF_DOWNLOAD_DEFERRED = 1 << 7,
   };
 
 DEF_ENUM_FLAGS_TYPE (enum objfile_flag, objfile_flags);
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index bb7b0a4579d..57bc1d45965 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -613,11 +613,22 @@ struct objfile
   /* See quick_symbol_functions.  */
   void require_partial_symbols (bool verbose);
 
-  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
-  void remove_partial_symbol (quick_symbol_functions *target)
+  /*  Indicate that the aquisition of this objfile's separate debug objfile
+      is no longer deferred.  Used when the debug objfile has been aquired
+      or could not be found.  */
+  void remove_deferred_status ()
   {
+    flags &= ~OBJF_DOWNLOAD_DEFERRED;
+
+   /* Remove quick_symbol_functions derived from a separately downloaded
+      index.  If available the separate debug objfile's index will be used
+      instead, since that objfile actually contains the symbols and CUs
+      referenced in the index.
+
+      No more than one element of qf should have from_separate_index set
+      to true.  */
     for (quick_symbol_functions_up &qf_up : qf)
-      if (qf_up.get () == target)
+      if (qf_up->from_separate_index)
 	{
 	  qf.remove (qf_up);
 	  return;
diff --git a/gdb/quick-symbol.h b/gdb/quick-symbol.h
index a7fea2ccb49..e7163503e39 100644
--- a/gdb/quick-symbol.h
+++ b/gdb/quick-symbol.h
@@ -225,6 +225,10 @@ struct quick_symbol_functions
   virtual void read_partial_symbols (struct objfile *objfile)
   {
   }
+
+  /* True if this quick_symbol_functions is derived from a separately
+     downloaded index.  */
+  bool from_separate_index = false;
 };
 
 typedef std::unique_ptr<quick_symbol_functions> quick_symbol_functions_up;
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 96239679c77..c476196184a 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -991,6 +991,10 @@ syms_from_objfile (struct objfile *objfile,
 static void
 finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
 {
+  struct objfile *parent = objfile->separate_debug_objfile_backlink;
+  bool was_deferred
+    = (parent != nullptr) && (parent->flags & OBJF_DOWNLOAD_DEFERRED);
+
   /* If this is the main symbol file we have to clean up all users of the
      old main symbol file.  Otherwise it is sufficient to fixup all the
      breakpoints that may have been redefined by this symbol file.  */
@@ -1001,7 +1005,8 @@ finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
 
       clear_symtab_users (add_flags);
     }
-  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0)
+  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0
+	   && !was_deferred)
     {
       breakpoint_re_set ();
     }
@@ -1127,6 +1132,12 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name,
 
   finish_new_objfile (objfile, add_flags);
 
+  /* Remove deferred status now in case any observers trigger symtab
+     expansion.  Otherwise gdb might try to read parent for psymbols
+     when it should read the separate debug objfile instead.  */
+  if (parent != nullptr && (parent->flags & OBJF_DOWNLOAD_DEFERRED))
+    parent->remove_deferred_status ();
+
   gdb::observers::new_objfile.notify (objfile);
 
   bfd_cache_close_all ();
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 5e1b9d91879..2408725fa73 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -2897,14 +2897,30 @@ find_pc_sect_compunit_symtab (CORE_ADDR pc, struct obj_section *section)
   if (best_cust != NULL)
     return best_cust;
 
+  int warn_if_readin = 1;
+
   /* Not found in symtabs, search the "quick" symtabs (e.g. psymtabs).  */
 
   for (objfile *objf : current_program_space->objfiles ())
     {
+      bool was_deferred = objf->flags & OBJF_DOWNLOAD_DEFERRED;
+
       struct compunit_symtab *result
-	= objf->find_pc_sect_compunit_symtab (msymbol, pc, section, 1);
+	= objf->find_pc_sect_compunit_symtab (msymbol, pc, section,
+					      warn_if_readin);
+
       if (result != NULL)
 	return result;
+
+      /* If objf's separate debug info was just acquired, disable
+	 warn_if_readin for the next iteration of this loop.  This prevents
+	 a spurious warning in case an observer already triggered expansion
+	 of the separate debug objfile's symtabs.  */
+      if (was_deferred && objf->separate_debug_objfile != nullptr
+	  && (objf->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	warn_if_readin = 0;
+      else if (warn_if_readin == 0)
+	warn_if_readin = 1;
     }
 
   return NULL;
diff --git a/gdb/testsuite/gdb.debuginfod/libsection1.c b/gdb/testsuite/gdb.debuginfod/libsection1.c
new file mode 100644
index 00000000000..60824b415c6
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/libsection1.c
@@ -0,0 +1,40 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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 <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+
+extern void libsection2_test ();
+extern void *libsection2_thread_test (void *);
+
+void
+libsection1_test ()
+{
+  pthread_t thr;
+
+  printf ("In libsection1\n");
+  libsection2_test ();
+
+  pthread_create (&thr, NULL, libsection2_thread_test, NULL);
+
+  /* Give the new thread a chance to actually enter libsection2_thread_test.  */
+  sleep (3);
+  printf ("Cancelling thread\n");
+
+  pthread_cancel (thr);
+}
diff --git a/gdb/testsuite/gdb.debuginfod/libsection2.c b/gdb/testsuite/gdb.debuginfod/libsection2.c
new file mode 100644
index 00000000000..629a67f94a5
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/libsection2.c
@@ -0,0 +1,37 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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 <stdio.h>
+
+void
+libsection2_test ()
+{
+  printf ("In libsection2\n");
+}
+
+void *
+libsection2_thread_test (void *arg)
+{
+  (void) arg;
+
+  printf ("In thread test\n");
+
+  while (1)
+    ;
+
+  return NULL;
+}
diff --git a/gdb/testsuite/gdb.debuginfod/section.c b/gdb/testsuite/gdb.debuginfod/section.c
new file mode 100644
index 00000000000..d391a8f898e
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/section.c
@@ -0,0 +1,29 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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 <stdio.h>
+
+extern void libsection1_test ();
+
+int
+main()
+{
+  libsection1_test ();
+  printf ("in section exec\n");
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
new file mode 100644
index 00000000000..96e9750cd38
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/section.exp
@@ -0,0 +1,181 @@
+# Copyright 2023 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 debuginfod functionality
+
+standard_testfile
+
+load_lib debuginfod-support.exp
+
+require allow_debuginfod_tests
+
+set sourcetmp [standard_output_file tmp-${srcfile}]
+set outputdir [standard_output_file {}]
+
+# SECTEXEC is an executable which calls a function from LIB_SL1.
+set sectfile "section"
+set sectsrc $srcdir/$subdir/section.c
+set sectexec [standard_output_file $sectfile]
+
+# Solib LIB_SL1 calls functions from LIB_SL2.
+set libfile1 "libsection1"
+set libsrc1 $srcdir/$subdir/$libfile1.c
+set lib_sl1 [standard_output_file $libfile1.sl]
+
+set libfile2 "libsection2"
+set libsrc2 $srcdir/$subdir/$libfile2.c
+set lib_sl2 [standard_output_file $libfile2.sl]
+
+set lib_opts1 [list debug build-id shlib=$lib_sl2]
+set lib_opts2 [list debug build-id]
+set exec_opts [list debug build-id shlib=$lib_sl1 shlib=$lib_sl2]
+
+clean_restart
+
+if {[enable_section_downloads] == 0} {
+    untested "GDB does not support debuginfod section downloads"
+    return -1
+}
+
+# Compile SECTEXEC, LIB_SL1 and LIB_SL2.
+if { [gdb_compile_shlib $libsrc2 $lib_sl2 $lib_opts2] != "" } {
+    untested "failed to compile $libfile2"
+    return -1
+}
+
+if { [gdb_compile_shlib_pthreads $libsrc1 $lib_sl1 $lib_opts1] != "" } {
+    untested "failed to compile $libfile1"
+    return -1
+}
+
+if { [gdb_compile $sectsrc $sectexec executable $exec_opts] != "" } {
+    untested "failed to compile $sectfile"
+    return -1
+}
+
+# Add .gdb_index to solibs.
+if { [have_index $lib_sl1] != "gdb_index"
+     && [add_gdb_index $lib_sl1] == 0 } {
+    untested "failed to add .gdb_index to $libfile1"
+    return -1
+}
+
+if { [have_index $lib_sl2] != "gdb_index"
+     && [add_gdb_index $lib_sl2] == 0 } {
+    untested "failed to add .gdb_index to $libfile2"
+    return -1
+}
+
+# Strip solib debuginfo into separate files.
+if { [gdb_gnu_strip_debug $lib_sl1 ""] != 0} {
+   fail "strip $lib_sl1 debuginfo"
+   return -1
+}
+
+if { [gdb_gnu_strip_debug $lib_sl2 ""] != 0} {
+   fail "strip $lib_sl2 debuginfo"
+   return -1
+}
+
+# Move debuginfo files into directory that debuginfod will serve from.
+set debugdir [standard_output_file "debug"]
+set debuginfo_sl1 [standard_output_file $libfile1.sl.debug]
+set debuginfo_sl2 [standard_output_file $libfile2.sl.debug]
+
+file mkdir $debugdir
+file rename -force $debuginfo_sl1 $debugdir
+file rename -force $debuginfo_sl2 $debugdir
+
+# Restart GDB and clear the debuginfod client cache. Then load BINFILE into
+# GDB and start running it.  Match output with pattern RES and use TESTNAME
+# as the test name.
+proc_with_prefix clean_restart_with_prompt { binfile res testname } {
+    global cache
+
+    clean_restart
+
+    # Delete client cache so debuginfo downloads again.
+    file delete -force $cache
+
+    gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
+    gdb_test "start" $res "file [file tail $binfile] start $testname" \
+	     ".*Enable debuginfod.*" "y"
+}
+
+# Tests with no debuginfod server running.
+proc_with_prefix no_url { } {
+    global sectexec libfile1 libfile2
+
+    # Check that no section is downloaded and no debuginfo is found.
+    gdb_test "file $sectexec" "" "file [file tail $sectexec] file no url"
+    gdb_test "start" "" "file [file tail $sectexec] start no url"
+    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile1.*" \
+	     "file [file tail $libfile1] found no url"
+    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile2.*" \
+	     "file [file tail $libfile2] found no url"
+}
+
+# Tests with a debuginfod server running.
+proc_with_prefix local_url { } {
+    global sectexec
+    global libsrc1 lib_sl1 libfile1
+    global libsrc2 lib_sl2 libfile2
+    global debugdir db
+
+    set url [start_debuginfod $db $debugdir]
+    if { $url == "" } {
+	unresolved "failed to start debuginfod server"
+	return
+    }
+
+    # Point GDB to the server.
+    setenv DEBUGINFOD_URLS $url
+
+    # Download .gdb_index for solibs.
+    set res ".*section \.gdb_index for $lib_sl1.*\
+	section \.gdb_index for $lib_sl2.*"
+    clean_restart_with_prompt $sectexec $res "index"
+
+    # Download debuginfo when stepping into a function.
+    set res ".*separate debug info for $lib_sl1.*\"In ${libfile1}\\\\n\".*"
+    gdb_test "step" $res "file [file tail $lib_sl1] step"
+
+    clean_restart_with_prompt $sectexec "" "break"
+
+    # Download debuginfo when setting a breakpoint.
+    set res "Download.*separate debug info for $lib_sl2.*"
+    gdb_test "br libsection2_test" $res "file [file tail $sectexec] break set"
+
+    # Hit the breakpoint.
+    set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
+    gdb_test "c" $res "file [file tail $sectexec] break continue"
+
+    # Check that download progress message is correctly formatted
+    # during backtrace.
+    set res ".* separate debug info for $lib_sl1.*#0  libsection2_test\
+	\\(\\) at.*"
+    set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
+    gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
+}
+
+# Create CACHE and DB directories ready for debuginfod to use.
+prepare_for_debuginfod cache db
+
+with_debuginfod_env $cache {
+    no_url
+    local_url
+}
+
+stop_debuginfod
diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
index 50a8b512a4a..6368c27e9d0 100644
--- a/gdb/testsuite/lib/debuginfod-support.exp
+++ b/gdb/testsuite/lib/debuginfod-support.exp
@@ -194,3 +194,33 @@ proc stop_debuginfod { } {
 	unset debuginfod_spawn_id
     }
 }
+
+# Return 1 if gdb is configured to download ELF/DWARF sections from
+# debuginfod servers.  Otherwise return 0.
+proc enable_section_downloads { } {
+    global gdb_prompt
+
+    set cmd "maint set debuginfod download-sections on"
+    set msg "enable section downloads"
+
+    gdb_test_multiple $cmd $msg {
+	-re ".*Undefined maintenance.*" {
+	    perror "Undefined command: \"$cmd\""
+	    return 0
+	}
+	-re ".*not compiled into GDB.*" {
+	    perror "Unsupported command: \"$cmd\""
+	    return 0
+	}
+	-re "\r\n${gdb_prompt} $" {
+	    return 1
+	}
+	timeout {
+	    perror "timeout: \"$cmd\""
+	    return 0
+	}
+    }
+
+    perror "Unexpected output for \"$cmd\""
+    return 0
+}
-- 
2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 6/6 v3] gdb/debuginfod: Add .debug_line downloading
  2023-06-01  1:43 [PATCH 0/6 v3] gdb/debuginfod: Add on-demand debuginfo downloading Aaron Merey
                   ` (4 preceding siblings ...)
  2023-06-01  1:43 ` [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading Aaron Merey
@ 2023-06-01  1:43 ` Aaron Merey
  2023-06-15 13:45   ` Aaron Merey
  5 siblings, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-06-01  1:43 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess, Aaron Merey

v2: https://sourceware.org/pipermail/gdb-patches/2023-April/198946.html

v3 adds tests for .debug_line downloading and modifies logging for
the testsuite debuginfod server (see commit message).

Commit message:

ELF/DWARF section downloading allows gdb to download .gdb_index files in
order to defer full debuginfo downloads.  However .gdb_index does not
contain any information regarding source filenames.  When a gdb command
includes a filename argument (ex. 'break main.c:50'), this results in
the mass downloading of all deferred debuginfo so that gdb can search the
debuginfo for matching source filenames.  This can result in unnecessary
downloads.

To improve this, have gdb instead download each debuginfo's .debug_line
(and .debug_line_str if using DWARF5) when executing these commands.
Download full debuginfo only when its .debug_line contains a matching
filename.

Since the combined size of .debug_line and .debug_line_str is only about
1% the size of the corresponding debuginfo, significant time can be saved
by checking these sections before choosing to download an entire debuginfo.

This patch also redirects stdout and stderr of the debuginfod server
used by testsuite/gdb.debuginfod tests to a server_log standard output
file.  While adding tests for this patch I ran into an issue where the
test server would block when logging to stderr, presumably because the
stderr buffer filled up and wasn't being read from.  Redirecting the
log to a file fixes this and also makes the server log more accessible
when debugging test failures.
---
 gdb/dwarf2/line-header.c                 | 215 +++++++++++++++--------
 gdb/dwarf2/line-header.h                 |  10 ++
 gdb/dwarf2/read-gdb-index.c              |  27 +++
 gdb/dwarf2/read.c                        | 165 +++++++++++++++++
 gdb/dwarf2/read.h                        |  31 ++++
 gdb/testsuite/gdb.debuginfod/section.exp |  23 ++-
 gdb/testsuite/lib/debuginfod-support.exp |   5 +-
 7 files changed, 397 insertions(+), 79 deletions(-)

diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
index d072a91bac9..b9210d84f6b 100644
--- a/gdb/dwarf2/line-header.c
+++ b/gdb/dwarf2/line-header.c
@@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
 {
   LONGEST length = read_initial_length (abfd, buf, bytes_read);
 
-  gdb_assert (cu_header->initial_length_size == 4
-	      || cu_header->initial_length_size == 8
-	      || cu_header->initial_length_size == 12);
+  if (cu_header != nullptr)
+    {
+      gdb_assert (cu_header->initial_length_size == 4
+		  || cu_header->initial_length_size == 8
+		  || cu_header->initial_length_size == 12);
 
-  if (cu_header->initial_length_size != *bytes_read)
-    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
+      if (cu_header->initial_length_size != *bytes_read)
+	complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
+    }
 
   *offset_size = (*bytes_read == 4) ? 4 : 8;
   return length;
 }
 
-/* Read directory or file name entry format, starting with byte of
-   format count entries, ULEB128 pairs of entry formats, ULEB128 of
-   entries count and the entries themselves in the described entry
-   format.  */
+
+/* Like read_formatted_entries but the .debug_line and .debug_line_str
+   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
+   where these sections are read from separate files without necessarily
+   having access to the entire debuginfo file they originate from.  */
 
 static void
-read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
-			const gdb_byte **bufp, struct line_header *lh,
-			unsigned int offset_size,
-			void (*callback) (struct line_header *lh,
-					  const char *name,
-					  dir_index d_index,
-					  unsigned int mod_time,
-					  unsigned int length))
+read_formatted_entries
+  (bfd *parent_bfd, const gdb_byte **line_bufp,
+   const gdb::array_view<const gdb_byte> line_str_data,
+   struct line_header *lh,
+   unsigned int offset_size,
+   void (*callback) (struct line_header *lh,
+		     const char *name,
+		     dir_index d_index,
+		     unsigned int mod_time,
+		     unsigned int length))
 {
   gdb_byte format_count, formati;
   ULONGEST data_count, datai;
-  const gdb_byte *buf = *bufp;
+  const gdb_byte *buf = *line_bufp;
+  const gdb_byte *str_buf = line_str_data.data ();
   const gdb_byte *format_header_data;
   unsigned int bytes_read;
 
-  format_count = read_1_byte (abfd, buf);
+  format_count = read_1_byte (parent_bfd, buf);
   buf += 1;
   format_header_data = buf;
   for (formati = 0; formati < format_count; formati++)
     {
-      read_unsigned_leb128 (abfd, buf, &bytes_read);
+      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
       buf += bytes_read;
-      read_unsigned_leb128 (abfd, buf, &bytes_read);
+      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
       buf += bytes_read;
     }
 
-  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
+  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
   buf += bytes_read;
   for (datai = 0; datai < data_count; datai++)
     {
@@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
 
       for (formati = 0; formati < format_count; formati++)
 	{
-	  ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
+	  ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
 	  format += bytes_read;
 
-	  ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
+	  ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
 	  format += bytes_read;
 
 	  gdb::optional<const char *> string;
@@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
 	  switch (form)
 	    {
 	    case DW_FORM_string:
-	      string.emplace (read_direct_string (abfd, buf, &bytes_read));
+	      string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
 	      buf += bytes_read;
 	      break;
 
 	    case DW_FORM_line_strp:
 	      {
-		const char *str
-		  = per_objfile->read_line_string (buf, offset_size);
+		if (line_str_data.empty ())
+		  error (_("Dwarf Error: DW_FORM_line_strp used without " \
+			   "required section"));
+		if (line_str_data.size () <= offset_size)
+		  error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
+			   "of section .debug_line"));
+
+		ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
+
+		const char *str;
+		if (str_buf[str_offset] == '\0')
+		  str = nullptr;
+		else
+		  str = (const char *) (str_buf + str_offset);
 		string.emplace (str);
 		buf += offset_size;
+		break;
 	      }
-	      break;
 
 	    case DW_FORM_data1:
-	      uint.emplace (read_1_byte (abfd, buf));
+	      uint.emplace (read_1_byte (parent_bfd, buf));
 	      buf += 1;
 	      break;
 
 	    case DW_FORM_data2:
-	      uint.emplace (read_2_bytes (abfd, buf));
+	      uint.emplace (read_2_bytes (parent_bfd, buf));
 	      buf += 2;
 	      break;
 
 	    case DW_FORM_data4:
-	      uint.emplace (read_4_bytes (abfd, buf));
+	      uint.emplace (read_4_bytes (parent_bfd, buf));
 	      buf += 4;
 	      break;
 
 	    case DW_FORM_data8:
-	      uint.emplace (read_8_bytes (abfd, buf));
+	      uint.emplace (read_8_bytes (parent_bfd, buf));
 	      buf += 8;
 	      break;
 
@@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
 	      break;
 
 	    case DW_FORM_udata:
-	      uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
+	      uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
 	      buf += bytes_read;
 	      break;
 
@@ -248,28 +267,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
       callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
     }
 
-  *bufp = buf;
+  *line_bufp = buf;
 }
 
 /* See line-header.h.  */
 
 line_header_up
-dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
-			   dwarf2_per_objfile *per_objfile,
-			   struct dwarf2_section_info *section,
-			   const struct comp_unit_head *cu_header,
-			   const char *comp_dir)
+dwarf_decode_line_header (bfd *parent_bfd,
+			  gdb::array_view<const gdb_byte> line_data,
+			  gdb::array_view<const gdb_byte> line_str_data,
+			  const gdb_byte **debug_line_ptr,
+			  bool is_dwz,
+			  const struct comp_unit_head *cu_header,
+			  const char *comp_dir)
 {
-  const gdb_byte *line_ptr;
+  const gdb_byte *line_ptr, *buf;
   unsigned int bytes_read, offset_size;
   int i;
   const char *cur_dir, *cur_file;
 
-  bfd *abfd = section->get_bfd_owner ();
+  buf = *debug_line_ptr;
 
   /* Make sure that at least there's room for the total_length field.
      That could be 12 bytes long, but we're just going to fudge that.  */
-  if (to_underlying (sect_off) + 4 >= section->size)
+  if (buf + 4 >= line_data.data () + line_data.size ())
     {
       dwarf2_statement_list_fits_in_line_number_section_complaint ();
       return 0;
@@ -277,62 +298,65 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
 
   line_header_up lh (new line_header (comp_dir));
 
-  lh->sect_off = sect_off;
+  lh->sect_off = (sect_offset) (buf - line_data.data ());
   lh->offset_in_dwz = is_dwz;
 
-  line_ptr = section->buffer + to_underlying (sect_off);
+  line_ptr = buf;
 
   /* Read in the header.  */
   LONGEST unit_length
-    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
+    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
 					      &bytes_read, &offset_size);
-  line_ptr += bytes_read;
 
-  const gdb_byte *start_here = line_ptr;
+  line_ptr += bytes_read;
 
-  if (line_ptr + unit_length > (section->buffer + section->size))
+  if (line_ptr + unit_length > buf + line_data.size ())
     {
       dwarf2_statement_list_fits_in_line_number_section_complaint ();
       return 0;
     }
+
+  const gdb_byte *start_here = line_ptr;
+
   lh->statement_program_end = start_here + unit_length;
-  lh->version = read_2_bytes (abfd, line_ptr);
+  lh->version = read_2_bytes (parent_bfd, line_ptr);
   line_ptr += 2;
   if (lh->version > 5)
     {
       /* This is a version we don't understand.  The format could have
 	 changed in ways we don't handle properly so just punt.  */
       complaint (_("unsupported version in .debug_line section"));
-      return NULL;
+      return nullptr;
     }
   if (lh->version >= 5)
     {
       gdb_byte segment_selector_size;
 
       /* Skip address size.  */
-      read_1_byte (abfd, line_ptr);
+      read_1_byte (parent_bfd, line_ptr);
       line_ptr += 1;
 
-      segment_selector_size = read_1_byte (abfd, line_ptr);
+      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
       line_ptr += 1;
       if (segment_selector_size != 0)
 	{
 	  complaint (_("unsupported segment selector size %u "
 		       "in .debug_line section"),
 		     segment_selector_size);
-	  return NULL;
+	  return nullptr;
 	}
     }
 
-  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
+  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
   line_ptr += offset_size;
   lh->statement_program_start = line_ptr + header_length;
-  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
+
+  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
   line_ptr += 1;
 
   if (lh->version >= 4)
     {
-      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
+      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
       line_ptr += 1;
     }
   else
@@ -345,41 +369,47 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
 		   "in `.debug_line' section"));
     }
 
-  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
+  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
   line_ptr += 1;
-  lh->line_base = read_1_signed_byte (abfd, line_ptr);
+
+  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
   line_ptr += 1;
-  lh->line_range = read_1_byte (abfd, line_ptr);
+
+  lh->line_range = read_1_byte (parent_bfd, line_ptr);
   line_ptr += 1;
-  lh->opcode_base = read_1_byte (abfd, line_ptr);
+
+  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
   line_ptr += 1;
+
   lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
 
   lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
   for (i = 1; i < lh->opcode_base; ++i)
     {
-      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
+      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
       line_ptr += 1;
     }
 
   if (lh->version >= 5)
     {
       /* Read directory table.  */
-      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
-			      offset_size,
-			      [] (struct line_header *header, const char *name,
-				  dir_index d_index, unsigned int mod_time,
-				  unsigned int length)
+      read_formatted_entries
+	(parent_bfd, &line_ptr, line_str_data,
+	 lh.get (), offset_size,
+	 [] (struct line_header *header, const char *name,
+	     dir_index d_index, unsigned int mod_time,
+	     unsigned int length)
 	{
 	  header->add_include_dir (name);
 	});
 
       /* Read file name table.  */
-      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
-			      offset_size,
-			      [] (struct line_header *header, const char *name,
-				  dir_index d_index, unsigned int mod_time,
-				  unsigned int length)
+      read_formatted_entries
+	(parent_bfd, &line_ptr, line_str_data,
+	 lh.get (), offset_size,
+	 [] (struct line_header *header, const char *name,
+	     dir_index d_index, unsigned int mod_time,
+	     unsigned int length)
 	{
 	  header->add_file_name (name, d_index, mod_time, length);
 	});
@@ -387,7 +417,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
   else
     {
       /* Read directory table.  */
-      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
+      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
 	{
 	  line_ptr += bytes_read;
 	  lh->add_include_dir (cur_dir);
@@ -395,17 +425,17 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
       line_ptr += bytes_read;
 
       /* Read file name table.  */
-      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
+      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
 	{
 	  unsigned int mod_time, length;
 	  dir_index d_index;
 
 	  line_ptr += bytes_read;
-	  d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+	  d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
 	  line_ptr += bytes_read;
-	  mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+	  mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
 	  line_ptr += bytes_read;
-	  length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+	  length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
 	  line_ptr += bytes_read;
 
 	  lh->add_file_name (cur_file, d_index, mod_time, length);
@@ -413,9 +443,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
       line_ptr += bytes_read;
     }
 
-  if (line_ptr > (section->buffer + section->size))
+  if (line_ptr > (buf + line_data.size ()))
     complaint (_("line number info header doesn't "
 		 "fit in `.debug_line' section"));
 
+  *debug_line_ptr += unit_length + offset_size;
   return lh;
 }
+
+line_header_up
+dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
+			   dwarf2_per_objfile *per_objfile,
+			   struct dwarf2_section_info *section,
+			   const struct comp_unit_head *cu_header,
+			   const char *comp_dir)
+{
+  struct objfile *objfile = per_objfile->objfile;
+  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
+
+  /* Read .debug_line.  */
+  dwarf2_section_info *line_sec = &per_bfd->line;
+  bfd_size_type line_size = line_sec->get_size (objfile);
+
+  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
+
+  /* Read .debug_line_str.  */
+  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
+  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
+
+  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
+					    line_str_size);
+
+  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
+
+  return dwarf_decode_line_header
+    (per_bfd->obfd, line, line_str, &line_ptr,
+     is_dwz, cu_header, comp_dir);
+}
diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
index 59a42e336f5..44e32828ddb 100644
--- a/gdb/dwarf2/line-header.h
+++ b/gdb/dwarf2/line-header.h
@@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
    struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
    const char *comp_dir);
 
+/* Like above but the .debug_line and .debug_line_str are stored in
+   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
+   statement program header within LINE_DATA.  */
+
+extern line_header_up dwarf_decode_line_header
+  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
+   gdb::array_view<const gdb_byte> line_str_data,
+   const gdb_byte **debug_line_ptr, bool is_dwz,
+  const comp_unit_head *cu_header, const char *comp_dir);
+
 #endif /* DWARF2_LINE_HEADER_H */
diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
index d3516e92361..64f202fddd3 100644
--- a/gdb/dwarf2/read-gdb-index.c
+++ b/gdb/dwarf2/read-gdb-index.c
@@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
   }
 };
 
+struct mapped_debug_line;
+typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
+
 struct dwarf2_gdb_index : public dwarf2_base_index_functions
 {
   /* This dumps minimal information about the index.
@@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
   /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
      debuginfo if necessary.  */
   struct symtab *find_last_source_symtab (struct objfile *objfile) override;
+
+  /* Filename information related to this .gdb_index.  */
+  mapped_debug_line_up mdl;
+
+  /* Return true if any of the filenames in this .gdb_index's .debug_line
+     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
+  bool filename_in_debug_line
+  (objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
 };
 
 void
@@ -587,6 +599,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
   return result;
 }
 
+bool
+dwarf2_gdb_index::filename_in_debug_line
+  (objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  if (mdl == nullptr)
+    mdl.reset (new mapped_debug_line (objfile));
+
+  return mdl->contains_matching_filename (file_matcher);
+}
+
 bool
 dwarf2_gdb_index::expand_symtabs_matching
     (struct objfile *objfile,
@@ -615,6 +638,10 @@ dwarf2_gdb_index::expand_symtabs_matching
 	  return false;
 	}
 
+      if (file_matcher != nullptr
+	  && !filename_in_debug_line (objfile, file_matcher))
+	return true;
+
       read_full_dwarf_from_debuginfod (objfile, this);
       return true;
     }
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 96d1ff53d91..715b3c06a56 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -81,6 +81,7 @@
 #include "gdbsupport/gdb_optional.h"
 #include "gdbsupport/underlying.h"
 #include "gdbsupport/hash_enum.h"
+#include "gdbsupport/scoped_mmap.h"
 #include "filename-seen-cache.h"
 #include "producer.h"
 #include <fcntl.h>
@@ -2115,6 +2116,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
   return this_cu->file_names;
 }
 
+#if !HAVE_SYS_MMAN_H
+
+bool
+mapped_debug_line::contains_matching_filename
+  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  return false;
+}
+
+gdb::array_view<const gdb_byte>
+mapped_debug_line::read_debug_line_separate
+  (char *filename, std::unique_ptr<index_cache_resource> *resource)
+{
+  return {};
+}
+
+bool
+mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
+{
+  return false;
+}
+
+#else /* !HAVE_SYS_MMAN_H */
+
+struct line_resource_mmap final : public index_cache_resource
+{
+  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
+     file doesn't exist. */
+  line_resource_mmap (const char *filename)
+    : mapping (mmap_file (filename))
+  {}
+
+  scoped_mmap mapping;
+};
+
+/* See read.h.  */
+
+bool
+mapped_debug_line::contains_matching_filename
+  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  for (line_header_up &lh : line_headers)
+    for (file_entry &fe : lh->file_names ())
+      {
+	const char *filename = fe.name;
+
+	if (file_matcher (fe.name, false))
+	  return true;
+
+	bool basename_match = file_matcher (lbasename (fe.name), true);
+
+	if (!basenames_may_differ && !basename_match)
+	  continue;
+
+	/* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
+	   until DWARF5.  Since we don't have access to the CU at this
+	   point we just check for a partial match on the filename.
+	   If there is a match, the full debuginfo will be downloaded
+	   ane the match will be re-evalute with DW_AT_comp_dir.  */
+	if (lh->version < 5 && fe.d_index == 0)
+	  return basename_match;
+
+	const char *dirname = fe.include_dir (&*lh);
+	std::string fullname;
+
+	if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
+	  fullname = filename;
+	else
+	  fullname = std::string (dirname) + SLASH_STRING + filename;
+
+	gdb::unique_xmalloc_ptr<char> rewritten
+	  = rewrite_source_path (fullname.c_str ());
+	if (rewritten != nullptr)
+	  fullname = rewritten.release ();
+
+	if (file_matcher (fullname.c_str (), false))
+	  return true;
+      }
+
+  return false;
+}
+
+/* See read.h.  */
+
+gdb::array_view<const gdb_byte>
+mapped_debug_line::read_debug_line_separate
+  (char *filename, std::unique_ptr<index_cache_resource> *resource)
+{
+  if (filename == nullptr)
+    return {};
+
+  try
+  {
+    line_resource_mmap *mmap_resource
+      = new line_resource_mmap (filename);
+
+    resource->reset (mmap_resource);
+
+    return gdb::array_view<const gdb_byte>
+      ((const gdb_byte *) mmap_resource->mapping.get (),
+       mmap_resource->mapping.size ());
+  }
+  catch (const gdb_exception &except)
+  {
+    exception_print (gdb_stderr, except);
+  }
+
+  return {};
+}
+
+/* See read.h.  */
+
+bool
+mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
+{
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  if (build_id == nullptr)
+    return false;
+
+  gdb::unique_xmalloc_ptr<char> line_path;
+  scoped_fd line_fd = debuginfod_section_query (build_id->data,
+						build_id->size,
+						bfd_get_filename
+						  (objfile->obfd.get ()),
+						".debug_line",
+						&line_path);
+
+  if (line_fd.get () < 0)
+    return false;
+
+  gdb::unique_xmalloc_ptr<char> line_str_path;
+  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
+						    build_id->size,
+						    bfd_get_filename
+						      (objfile->obfd.get ()),
+						    ".debug_line_str",
+						    &line_str_path);
+
+  line_data = read_debug_line_separate (line_path.get (), &line_resource);
+  line_str_data = read_debug_line_separate (line_str_path.get (),
+					    &line_str_resource);
+
+  const gdb_byte *line_ptr = line_data.data ();
+
+  while (line_ptr < line_data.data () + line_data.size ())
+    {
+      line_header_up lh
+	= dwarf_decode_line_header (objfile->obfd.get (),
+				    line_data, line_str_data,
+				    &line_ptr, false,
+				    nullptr, nullptr);
+      line_headers.emplace_back (lh.release ());
+    }
+
+  return true;
+}
+#endif /* !HAVE_SYS_MMAN_H */
+
+mapped_debug_line::mapped_debug_line (objfile *objfile)
+{
+  if (!read_debug_line_from_debuginfod (objfile))
+    line_headers.clear ();
+}
+
 /* A helper for the "quick" functions which computes and caches the
    real path for a given file name from the line table.  */
 
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index e3131693b81..b8a8b76bde0 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -34,6 +34,7 @@
 #include "gdbsupport/hash_enum.h"
 #include "gdbsupport/function-view.h"
 #include "gdbsupport/packed.h"
+#include "dwarf2/line-header.h"
 
 /* Hold 'maintenance (set|show) dwarf' commands.  */
 extern struct cmd_list_element *set_dwarf_cmdlist;
@@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
 extern void read_full_dwarf_from_debuginfod (struct objfile *,
 					     dwarf2_base_index_functions *);
 
+struct mapped_debug_line
+{
+  mapped_debug_line (objfile *objfile);
+
+  /* Return true if any of the mapped .debug_line's filenames match
+     FILE_MATCHER.  */
+
+  bool contains_matching_filename
+    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
+
+private:
+  std::vector<line_header_up> line_headers;
+
+  gdb::array_view<const gdb_byte> line_data;
+  gdb::array_view<const gdb_byte> line_str_data;
+
+  std::unique_ptr<index_cache_resource> line_resource;
+  std::unique_ptr<index_cache_resource> line_str_resource;
+
+  /* Download the .debug_line and .debug_line_str associated with OBJFILE
+     and populate line_headers.  */
+
+  bool read_debug_line_from_debuginfod (objfile *objfile);
+
+  /* Initialize line_data and line_str_data with the .debug_line and
+    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
+
+  gdb::array_view<const gdb_byte> read_debug_line_separate
+    (char *filename, std::unique_ptr<index_cache_resource> *resource);
+};
 #endif /* DWARF2READ_H */
diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
index 96e9750cd38..1380eb7ef78 100644
--- a/gdb/testsuite/gdb.debuginfod/section.exp
+++ b/gdb/testsuite/gdb.debuginfod/section.exp
@@ -106,7 +106,7 @@ proc_with_prefix clean_restart_with_prompt { binfile res testname } {
 
     clean_restart
 
-    # Delete client cache so debuginfo downloads again.
+    # Delete client cache so progress messages always appear.
     file delete -force $cache
 
     gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
@@ -168,6 +168,27 @@ proc_with_prefix local_url { } {
 	\\(\\) at.*"
     set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
     gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
+
+    clean_restart_with_prompt $sectexec "" "line 1"
+
+    # List source file using .debug_line download.
+    set res ".*\.debug_line.*$lib_sl1.*21.*extern void libsection2_test.*"
+    gdb_test "list $libsrc1:21" $res "file [file tail $sectexec] line 1 list"
+
+    clean_restart_with_prompt $sectexec "" "line 2"
+
+    # Set breakpoint using .debug_line download.
+    set res ".*section \.debug_line for $lib_sl1.*Breakpoint 2 at.*$libsrc1.*"
+    gdb_test "br $libsrc1:37" $res "file [file tail $sectexec] line 2 br"
+
+    # Continue to breakpoint.
+    set res "Breakpoint 2, libsection1_test.*\"Cancelling thread\\\\n\".*"
+    gdb_test "c" $res "file [file tail $sectexec] line 2 continue"
+
+    # Check that download progress message is correctly formatted
+    # when printing threads.
+    set res ".*separate debug info for $lib_sl2\.\.\.\r\n.* 2    Thread.*"
+    gdb_test "info thr" $res "file [file tail $libfile2] line thread"
 }
 
 # Create CACHE and DB directories ready for debuginfod to use.
diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
index 6368c27e9d0..07ea23999bd 100644
--- a/gdb/testsuite/lib/debuginfod-support.exp
+++ b/gdb/testsuite/lib/debuginfod-support.exp
@@ -113,6 +113,8 @@ proc with_debuginfod_env { cache body } {
 proc start_debuginfod { db debugdir } {
     global debuginfod_spawn_id spawn_id
 
+    set logfile [standard_output_file "server_log"]
+
     # Find an unused port.
     set port 7999
     set found false
@@ -127,7 +129,8 @@ proc start_debuginfod { db debugdir } {
 	    set old_spawn_id $spawn_id
 	}
 
-	spawn debuginfod -vvvv -d $db -p $port -F $debugdir
+	spawn sh -c "debuginfod -vvvv -d $db -p $port -F $debugdir 2>&1 \
+		| tee $logfile"
 	set debuginfod_spawn_id $spawn_id
 
 	if { [info exists old_spawn_id] } {
-- 
2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections'
  2023-06-01  1:43 ` [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections' Aaron Merey
@ 2023-06-01  6:13   ` Eli Zaretskii
  2023-06-01 22:35     ` Aaron Merey
  2023-06-07 13:57   ` Andrew Burgess
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 33+ messages in thread
From: Eli Zaretskii @ 2023-06-01  6:13 UTC (permalink / raw)
  To: Aaron Merey; +Cc: gdb-patches, aburgess

> Cc: aburgess@redhat.com,
> 	Aaron Merey <amerey@redhat.com>
> Date: Wed, 31 May 2023 21:43:43 -0400
> From: Aaron Merey via Gdb-patches <gdb-patches@sourceware.org>
> 
>  gdb/debuginfod-support.c | 66 ++++++++++++++++++++++++++++++++++++++--
>  gdb/doc/gdb.texinfo      | 16 ++++++++--
>  2 files changed, 77 insertions(+), 5 deletions(-)

Thanks.

> @@ -568,8 +604,11 @@ _initialize_debuginfod ()
>  			_("Set whether to use debuginfod."),
>  			_("Show whether to use debuginfod."),
>  			_("\
> -When on, enable the use of debuginfod to download missing debug info and\n\
> -source files."),
> +When set to \"on\", enable the use of debuginfod to download missing\n\
> +debug info and source files. GDB may also download components of debug\n\
> +info instead of entire files. \"off\" disables the use of debuginfod.\n\

Our conventions are to leave two spaces between sentences, not one.

> +When set to \"ask\", a prompt may ask whether to enable or disable\n\
> +debuginfod." ),

"a prompt may ask" sounds awkward and somewhat mysterious.  How about

  When set to "ask", prompt whether to enable or disable [...]

instead?

> +@kindex maint set debuginfod
> +@kindex maint show debuginfod
> +@item maint set debuginfod download-sections
> +@itemx maint set debuginfod download-sections @r{[}on|off@r{]}
> +@itemx maint show debuginfod download-sections
> +Controls whether @value{GDBN} will attempt to download individual
> +ELF/DWARF sections from @code{debuginfod}.  If disabled, only
> +whole debug info files will be downloaded.  This could result
> +in @value{GDBN} downloading larger amounts of data.

I'd make the last sentence a part of the one before it, for clarity of
what "this" means.  Like this:

   If disabled, only whole debug info files will be downloaded; this
   could result in @value{GDBN} downloading larger amounts of data.

>  @cindex @value{GDBN} internal error
>  @cindex internal errors, control of @value{GDBN} behavior
>  @cindex demangler crashes
> @@ -49382,8 +49392,10 @@ regarding @code{debuginfod}.
>  @item set debuginfod enabled
>  @itemx set debuginfod enabled on
>  @cindex enable debuginfod
> -@value{GDBN} will attempt to query @code{debuginfod} servers when missing debug
> -info or source files.
> +@value{GDBN} will attempt to query @code{debuginfod} servers for missing debug
> +info or source files.

Why "attempt to query"? why not just "query"?  Or maybe "attempt to
download"?

> @value{GDBN} may attempt to download individual ELF/DWARF
> +sections such as @code{.gdb_index} to help reduce the total amount of data
> +downloaded from debuginfod servers.

Once again, I'd drop the "attempt" part.  More importantly, a
cross-reference to where "maint set debuginfod" is described is
missing here.  I would suggest

  @value{GDBN} may download individual ELF/DWARF sections such as
  @code{.gdb_index} to help reduce the total amount of data downloaded
  from debuginfod servers; this can be controlled by @w{@code{maint
  set debuginfod download-sections}} (@pxref{Maintenance Commands,
  maint set debuginfod download-sections}).

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 3/6 v3] gdb: Buffer gdb_stdout during events that might download deferred debuginfo
  2023-06-01  1:43 ` [PATCH 3/6 v3] gdb: Buffer gdb_stdout during events that might download deferred debuginfo Aaron Merey
@ 2023-06-01  6:16   ` Eli Zaretskii
  2023-06-01 22:36     ` Aaron Merey
  2023-06-07 13:25   ` Andrew Burgess
  1 sibling, 1 reply; 33+ messages in thread
From: Eli Zaretskii @ 2023-06-01  6:16 UTC (permalink / raw)
  To: Aaron Merey; +Cc: gdb-patches, aburgess

> Cc: aburgess@redhat.com,
> 	Aaron Merey <amerey@redhat.com>
> Date: Wed, 31 May 2023 21:43:44 -0400
> From: Aaron Merey via Gdb-patches <gdb-patches@sourceware.org>
> 
> diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
> index 6d0521b64e2..548e8c5f76b 100644
> --- a/gdb/debuginfod-support.c
> +++ b/gdb/debuginfod-support.c
> @@ -168,7 +168,8 @@ progressfn (debuginfod_client *c, long cur, long total)
>  
>    if (check_quit_flag ())
>      {
> -      gdb_printf ("Cancelling download of %s %s...\n",
> +      ui_file *outstream = buffer_file::get_unbuffered_stream (gdb_stdout);
> +      gdb_printf (outstream, "Cancelling download of %s %s...\n",
>  		  data->desc, styled_fname.c_str ());

A nit: this is a user-level message, so I think it should be in _().

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections'
  2023-06-01  6:13   ` Eli Zaretskii
@ 2023-06-01 22:35     ` Aaron Merey
  2023-06-02  6:49       ` Eli Zaretskii
  0 siblings, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-06-01 22:35 UTC (permalink / raw)
  To: eliz; +Cc: aburgess, gdb-patches, Aaron Merey

Thanks Eli, I incorporated your suggestions.

Aaron

---

v1: https://sourceware.org/pipermail/gdb-patches/2023-February/197461.html

v2 removes 'set debuginfod enabled lazy' and replaces it with
'maint set debuginfod download-sections'.

Commit message:

This setting controls whether GDB may attempt to download ELF/DWARF
sections from debuginfod servers.

This setting is enabled by default if gdb is built with debuginfod
section download support (requires libdebuginfod 0.188).

Also update debuginfod command help text and gdb.texinfo with
information on section downloading and the new command.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/debuginfod-support.c | 65 ++++++++++++++++++++++++++++++++++++++--
 gdb/doc/gdb.texinfo      | 18 +++++++++--
 2 files changed, 78 insertions(+), 5 deletions(-)

diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
index 8be43a91dcc..66b58e8b673 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -31,6 +31,10 @@
 static cmd_list_element *set_debuginfod_prefix_list;
 static cmd_list_element *show_debuginfod_prefix_list;
 
+/* maint set/show debuginfod commands.  */
+static cmd_list_element *maint_set_debuginfod_cmdlist;
+static cmd_list_element *maint_show_debuginfod_cmdlist;
+
 static const char debuginfod_on[] = "on";
 static const char debuginfod_off[] = "off";
 static const char debuginfod_ask[] = "ask";
@@ -50,6 +54,13 @@ static const char *debuginfod_enabled =
   debuginfod_off;
 #endif
 
+static bool debuginfod_download_sections =
+#if defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
+  true;
+#else
+  false;
+#endif
+
 static unsigned int debuginfod_verbose = 1;
 
 #ifndef HAVE_LIBDEBUGINFOD
@@ -424,7 +435,7 @@ debuginfod_section_query (const unsigned char *build_id,
   return scoped_fd (-ENOSYS);
 #else
 
- if (!debuginfod_is_enabled ())
+ if (!debuginfod_download_sections || !debuginfod_is_enabled ())
     return scoped_fd (-ENOSYS);
 
   debuginfod_client *c = get_debuginfod_client ();
@@ -550,6 +561,31 @@ show_debuginfod_verbose_command (ui_file *file, int from_tty,
 	      value);
 }
 
+/* Set callback for "maint set debuginfod download-sections".  */
+
+static void
+maint_set_debuginfod_download_sections (bool value)
+{
+#if !defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
+  if (value)
+    {
+      error (_("Support for section downloading is not compiled into GDB. " \
+"Defaulting to \"off\"."));
+      return;
+    }
+#endif
+
+  debuginfod_download_sections = value;
+}
+
+/* Get callback for "maint set debuginfod download-sections".  */
+
+static bool
+maint_get_debuginfod_download_sections ()
+{
+  return debuginfod_download_sections;
+}
+
 /* Register debuginfod commands.  */
 
 void _initialize_debuginfod ();
@@ -568,8 +604,10 @@ _initialize_debuginfod ()
 			_("Set whether to use debuginfod."),
 			_("Show whether to use debuginfod."),
 			_("\
-When on, enable the use of debuginfod to download missing debug info and\n\
-source files."),
+When set to \"on\", enable the use of debuginfod to download missing\n\
+debug info and source files.  GDB may also download components of debug\n\
+info instead of entire files.  \"off\" disables the use of debuginfod.\n\
+When set to \"ask\", prompt whether to enable or disable debuginfod." ),
 			set_debuginfod_enabled,
 			get_debuginfod_enabled,
 			show_debuginfod_enabled,
@@ -600,4 +638,25 @@ query.\nTo disable, set to zero.  Verbose output is displayed by default."),
 			     show_debuginfod_verbose_command,
 			     &set_debuginfod_prefix_list,
 			     &show_debuginfod_prefix_list);
+
+  /* maint set/show debuginfod */
+  add_setshow_prefix_cmd ("debuginfod", class_maintenance,
+			  _("Set debuginfod specific variables."),
+			  _("Show debuginfod specific variables."),
+			  &maint_set_debuginfod_cmdlist,
+			  &maint_show_debuginfod_cmdlist,
+			  &maintenance_set_cmdlist, &maintenance_show_cmdlist);
+
+  /* maint set/show debuginfod download-sections */
+  add_setshow_boolean_cmd ("download-sections", class_maintenance, _("\
+Set whether debuginfod may download individual ELF/DWARF sections."), _("\
+Show whether debuginfod may download individual ELF/DWARF sections."), _("\
+When enabled, debuginfod may attempt to download individual ELF/DWARF \
+sections from debug info files.\nIf disabled, only whole debug info files \
+may be downloaded."),
+			  maint_set_debuginfod_download_sections,
+			  maint_get_debuginfod_download_sections,
+			  nullptr,
+			  &maint_set_debuginfod_cmdlist,
+			  &maint_show_debuginfod_cmdlist);
 }
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index fc55c4e7b43..c717e5a5b23 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -40872,6 +40872,16 @@ Create a core file? (y or n) @kbd{n}
 (@value{GDBP})
 @end smallexample
 
+@kindex maint set debuginfod
+@kindex maint show debuginfod
+@item maint set debuginfod download-sections
+@itemx maint set debuginfod download-sections @r{[}on|off@r{]}
+@itemx maint show debuginfod download-sections
+Controls whether @value{GDBN} will attempt to download individual
+ELF/DWARF sections from @code{debuginfod}.  If disabled, only
+whole debug info files will be downloaded; this could result
+in @value{GDBN} downloading larger amounts of data.
+
 @cindex @value{GDBN} internal error
 @cindex internal errors, control of @value{GDBN} behavior
 @cindex demangler crashes
@@ -49382,8 +49392,12 @@ regarding @code{debuginfod}.
 @item set debuginfod enabled
 @itemx set debuginfod enabled on
 @cindex enable debuginfod
-@value{GDBN} will attempt to query @code{debuginfod} servers when missing debug
-info or source files.
+@value{GDBN} may query @code{debuginfod} servers for missing debug info and
+source files.  @value{GDBN} may also download individual ELF/DWARF sections
+such as @code{.gdb_index} to help reduce the total amount of data downloaded
+from @code{debuginfod} servers; this can be controlled by @w{@code{maint
+set debuginfod download-sections}} (@pxref{Maintenance Commands, maint set
+debuginfod download-sections}).
 
 @item set debuginfod enabled off
 @value{GDBN} will not attempt to query @code{debuginfod} servers when missing
-- 
2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 3/6 v3] gdb: Buffer gdb_stdout during events that might download deferred debuginfo
  2023-06-01  6:16   ` Eli Zaretskii
@ 2023-06-01 22:36     ` Aaron Merey
  0 siblings, 0 replies; 33+ messages in thread
From: Aaron Merey @ 2023-06-01 22:36 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, aburgess

On Thu, Jun 1, 2023 at 2:15 AM Eli Zaretskii <eliz@gnu.org> wrote:
> > diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
> > index 6d0521b64e2..548e8c5f76b 100644
> > --- a/gdb/debuginfod-support.c
> > +++ b/gdb/debuginfod-support.c
> > @@ -168,7 +168,8 @@ progressfn (debuginfod_client *c, long cur, long total)
> >
> >    if (check_quit_flag ())
> >      {
> > -      gdb_printf ("Cancelling download of %s %s...\n",
> > +      ui_file *outstream = buffer_file::get_unbuffered_stream (gdb_stdout);
> > +      gdb_printf (outstream, "Cancelling download of %s %s...\n",
> >                 data->desc, styled_fname.c_str ());
>
> A nit: this is a user-level message, so I think it should be in _().

Thanks Eli, fixed.

Aaron


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections'
  2023-06-01 22:35     ` Aaron Merey
@ 2023-06-02  6:49       ` Eli Zaretskii
  0 siblings, 0 replies; 33+ messages in thread
From: Eli Zaretskii @ 2023-06-02  6:49 UTC (permalink / raw)
  To: Aaron Merey; +Cc: aburgess, gdb-patches

> From: Aaron Merey <amerey@redhat.com>
> Cc: aburgess@redhat.com,
> 	gdb-patches@sourceware.org,
> 	Aaron Merey <amerey@redhat.com>
> Date: Thu,  1 Jun 2023 18:35:04 -0400
> 
> Thanks Eli, I incorporated your suggestions.

Thank you.  The documentation parts of this patch are OK.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 3/6 v3] gdb: Buffer gdb_stdout during events that might download deferred debuginfo
  2023-06-01  1:43 ` [PATCH 3/6 v3] gdb: Buffer gdb_stdout during events that might download deferred debuginfo Aaron Merey
  2023-06-01  6:16   ` Eli Zaretskii
@ 2023-06-07 13:25   ` Andrew Burgess
  1 sibling, 0 replies; 33+ messages in thread
From: Andrew Burgess @ 2023-06-07 13:25 UTC (permalink / raw)
  To: Aaron Merey, gdb-patches; +Cc: Aaron Merey

Aaron Merey <amerey@redhat.com> writes:

> v2: https://sourceware.org/pipermail/gdb-patches/2023-April/198944.html
>
> v3 simplifies do_redirect_to_buffer and adds output buffering during
> print_stop_event.
>
> This patch currently introduces regressions to three gdb.base/solib-display.exp
> 'after rerun' tests.  Because print_stop_event output to gdb_stdout is
> now buffered until the function exits, any writes to gdb_stderr during
> print_stop_event will always appear before any gdb_stdout output.
>
> In these testcases instead of getting the following expected output:
>
> ```
>
> Breakpoint 1, main () at /home/amerey/binutils-gdb/gdb/testsuite/gdb.base/solib-display-main.c:30
> 30        bar ();
> 1: (int) a_global = 41
> warning: Unable to display "(int) b_global": No symbol "b_global" in current context.
> 3: (int) c_global = 43
> ```
>
> gdb instead prints:
>
> ```
> warning: Unable to display "(int) b_global": No symbol "b_global" in current context.
>
> Breakpoint 1, main () at /home/amerey/binutils-gdb/gdb/testsuite/gdb.base/solib-display-main.c:30
> 30        bar ();
> 1: (int) a_global = 41
> 3: (int) c_global = 43
> ```
>
> I'm working on a fix that buffers both gdb_stdout and gdb_stderr and
> organizes the flush so that output to both streams appears in the
> same order that it was written.  However I didn't think this issue is
> severe enough to hold off on posting this patch as it is.  I can
> file a PR and mark these tests as KFAIL for now if desired.

If you're working on an updated patch then I think we have the time to
wait for that.  I don't see a need to get a partial fix in first, unless
I'm missing something.

Thanks,
Andrew

>
> Commit message:
>
> Introduce new ui_file buffer_file to temporarily collect output
> during print_thread, print_frame_info and print_stop_event.
>
> This ensures that output from these functions is not interrupted
> by debuginfod progress messages.
>
> With the addition of deferred debuginfo downloading it is possible
> for download progress messages to print during these events.
> Without any intervention we can end up with poorly formatted output:
>
>     (gdb) backtrace
>     [...]
>     #8  0x00007fbe8af7d7cf in pygi_invoke_c_callable (Downloading separate debug info for /lib64/libpython3.11.so.1.0
>     function_cache=0x561221b224d0, state=<optimized out>...
>
> To fix this we accumulate writes to gdb_stdout in a buffer_file
> while progress messages skip the buffer and print to gdb_stdout
> immediately.  This ensures progress messages print first, followed by
> uninterrupted frame/thread/stop info:
>
>     (gdb) backtrace
>     [...]
>     Downloading separate debug info for /lib64/libpython3.11.so.1.0
>     #8  0x00007fbe8af7d7cf in pygi_invoke_c_callable (function_cache=0x561221b224d0, state=<optimized out>...
> ---
>  gdb/cli-out.c            |  28 ++++++-
>  gdb/cli-out.h            |   1 +
>  gdb/debuginfod-support.c |  15 ++--
>  gdb/infrun.c             |  17 +++-
>  gdb/mi/mi-out.c          |  29 +++++++
>  gdb/mi/mi-out.h          |   1 +
>  gdb/python/py-mi.c       |   2 +
>  gdb/stack.c              |  38 ++++++---
>  gdb/thread.c             | 174 +++++++++++++++++++++++----------------
>  gdb/ui-file.c            |  62 ++++++++++++++
>  gdb/ui-file.h            |  45 +++++++++-
>  gdb/ui-out.c             |  21 +++++
>  gdb/ui-out.h             |  66 +++++++++++++++
>  13 files changed, 405 insertions(+), 94 deletions(-)
>
> diff --git a/gdb/cli-out.c b/gdb/cli-out.c
> index 20d3d93f1ad..a188457711c 100644
> --- a/gdb/cli-out.c
> +++ b/gdb/cli-out.c
> @@ -263,6 +263,25 @@ cli_ui_out::do_redirect (ui_file *outstream)
>      m_streams.pop_back ();
>  }
>  
> +void
> +cli_ui_out::do_redirect_to_buffer (buffer_file *buf_file)
> +{
> +  auto it = std::find (m_streams.begin (), m_streams.end (), gdb_stdout);
> +
> +  if (it == m_streams.end ())
> +    return;
> +
> +  if (buf_file != nullptr)
> +    *it = buf_file;
> +  else
> +    {
> +      buffer_file *buf = dynamic_cast<buffer_file *>(*it);
> +
> +      if (buf != nullptr)
> +	*it = buf->get_stream ();
> +    }
> +}
> +
>  /* Initialize a progress update to be displayed with
>     cli_ui_out::do_progress_notify.  */
>  
> @@ -299,7 +318,8 @@ cli_ui_out::do_progress_notify (const std::string &msg,
>  				double howmuch, double total)
>  {
>    int chars_per_line = get_chars_per_line ();
> -  struct ui_file *stream = m_streams.back ();
> +  struct ui_file *stream
> +    = buffer_file::get_unbuffered_stream (m_streams.back ());
>    cli_progress_info &info (m_progress_info.back ());
>  
>    if (chars_per_line > MAX_CHARS_PER_LINE)
> @@ -384,7 +404,8 @@ cli_ui_out::do_progress_notify (const std::string &msg,
>  void
>  cli_ui_out::clear_progress_notify ()
>  {
> -  struct ui_file *stream = m_streams.back ();
> +  struct ui_file *stream
> +    = buffer_file::get_unbuffered_stream (m_streams.back ());
>    int chars_per_line = get_chars_per_line ();
>  
>    scoped_restore save_pagination
> @@ -413,10 +434,11 @@ void
>  cli_ui_out::do_progress_end ()
>  {
>    struct ui_file *stream = m_streams.back ();
> -  m_progress_info.pop_back ();
>  
>    if (stream->isatty ())
>      clear_progress_notify ();
> +
> +  m_progress_info.pop_back ();
>  }
>  
>  /* local functions */
> diff --git a/gdb/cli-out.h b/gdb/cli-out.h
> index 34016182269..fa8ca1114e1 100644
> --- a/gdb/cli-out.h
> +++ b/gdb/cli-out.h
> @@ -71,6 +71,7 @@ class cli_ui_out : public ui_out
>    virtual void do_wrap_hint (int indent) override;
>    virtual void do_flush () override;
>    virtual void do_redirect (struct ui_file *outstream) override;
> +  virtual void do_redirect_to_buffer (buffer_file *buf_file) override;
>  
>    virtual void do_progress_start () override;
>    virtual void do_progress_notify (const std::string &, const char *,
> diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
> index 6d0521b64e2..548e8c5f76b 100644
> --- a/gdb/debuginfod-support.c
> +++ b/gdb/debuginfod-support.c
> @@ -168,7 +168,8 @@ progressfn (debuginfod_client *c, long cur, long total)
>  
>    if (check_quit_flag ())
>      {
> -      gdb_printf ("Cancelling download of %s %s...\n",
> +      ui_file *outstream = buffer_file::get_unbuffered_stream (gdb_stdout);
> +      gdb_printf (outstream, "Cancelling download of %s %s...\n",
>  		  data->desc, styled_fname.c_str ());
>        return 1;
>      }
> @@ -284,10 +285,14 @@ static void
>  print_outcome (int fd, const char *desc, const char *fname)
>  {
>    if (fd < 0 && fd != -ENOENT)
> -    gdb_printf (_("Download failed: %s.  Continuing without %s %ps.\n"),
> -		safe_strerror (-fd),
> -		desc,
> -		styled_string (file_name_style.style (), fname));
> +    {
> +      ui_file *outstream = buffer_file::get_unbuffered_stream (gdb_stdout);
> +      gdb_printf (outstream,
> +		  _("Download failed: %s.  Continuing without %s %ps.\n"),
> +		  safe_strerror (-fd),
> +		  desc,
> +		  styled_string (file_name_style.style (), fname));
> +    }
>  }
>  
>  /* See debuginfod-support.h  */
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 4d6df757d23..f719a4aad49 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -8706,10 +8706,10 @@ print_stop_location (const target_waitstatus &ws)
>      print_stack_frame (get_selected_frame (nullptr), 0, source_flag, 1);
>  }
>  
> -/* See infrun.h.  */
> +/* See `print_stop_event` in infrun.h.  */
>  
> -void
> -print_stop_event (struct ui_out *uiout, bool displays)
> +static void
> +do_print_stop_event (struct ui_out *uiout, bool displays)
>  {
>    struct target_waitstatus last;
>    struct thread_info *tp;
> @@ -8738,6 +8738,17 @@ print_stop_event (struct ui_out *uiout, bool displays)
>      }
>  }
>  
> +/* See infrun.h.  This function itself sets up buffered output for the
> +   duration of do_print_stop_event, which performs the actual event
> +   printing.  */
> +
> +void
> +print_stop_event (struct ui_out *uiout, bool displays)
> +{
> +  do_with_buffered_output<void (ui_out *, bool)>
> +    (do_print_stop_event, uiout, displays);
> +}
> +
>  /* See infrun.h.  */
>  
>  void
> diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c
> index 29a416a426d..b763d2a785c 100644
> --- a/gdb/mi/mi-out.c
> +++ b/gdb/mi/mi-out.c
> @@ -194,6 +194,35 @@ mi_ui_out::do_redirect (ui_file *outstream)
>      m_streams.pop_back ();
>  }
>  
> +void
> +mi_ui_out::do_redirect_to_buffer (buffer_file *buf_file)
> +{
> +  auto it = std::find (m_streams.begin (), m_streams.end (), gdb_stdout);
> +
> +  if (it == m_streams.end ())
> +    return;
> +
> +  if (buf_file != nullptr)
> +    {
> +      string_file *sstream = dynamic_cast<string_file *>(*it);
> +
> +      if (sstream != nullptr)
> +	{
> +	  buf_file->write (sstream->data (), sstream->size ());
> +	  sstream->clear ();
> +	}
> +
> +      *it = buf_file;
> +    }
> +  else
> +    {
> +      buffer_file *buf = dynamic_cast<buffer_file *>(*it);
> +
> +      if (buf != nullptr)
> +	*it = buf->get_stream ();
> +    }
> +}
> +
>  void
>  mi_ui_out::field_separator ()
>  {
> diff --git a/gdb/mi/mi-out.h b/gdb/mi/mi-out.h
> index 10c9f8a4585..d2f2345daf5 100644
> --- a/gdb/mi/mi-out.h
> +++ b/gdb/mi/mi-out.h
> @@ -79,6 +79,7 @@ class mi_ui_out : public ui_out
>    virtual void do_wrap_hint (int indent) override;
>    virtual void do_flush () override;
>    virtual void do_redirect (struct ui_file *outstream) override;
> +  virtual void do_redirect_to_buffer (buffer_file *buf_file) override;
>  
>    virtual bool do_is_mi_like_p () const override
>    { return true; }
> diff --git a/gdb/python/py-mi.c b/gdb/python/py-mi.c
> index 0fcd57844e7..34540901888 100644
> --- a/gdb/python/py-mi.c
> +++ b/gdb/python/py-mi.c
> @@ -62,6 +62,8 @@ class py_ui_out : public ui_out
>    void do_progress_notify (const std::string &, const char *, double, double)
>      override
>    { }
> +  void do_redirect_to_buffer (buffer_file *) override
> +  { }
>  
>    void do_table_begin (int nbrofcols, int nr_rows, const char *tblid) override
>    {
> diff --git a/gdb/stack.c b/gdb/stack.c
> index 002bf580634..608bb5fc1f0 100644
> --- a/gdb/stack.c
> +++ b/gdb/stack.c
> @@ -220,7 +220,8 @@ static void print_frame_local_vars (frame_info_ptr frame,
>  				    const char *regexp, const char *t_regexp,
>  				    int num_tabs, struct ui_file *stream);
>  
> -static void print_frame (const frame_print_options &opts,
> +static void print_frame (struct ui_out *uiout,
> +			 const frame_print_options &opts,
>  			 frame_info_ptr frame, int print_level,
>  			 enum print_what print_what,  int print_args,
>  			 struct symtab_and_line sal);
> @@ -1020,16 +1021,15 @@ get_user_print_what_frame_info (gdb::optional<enum print_what> *what)
>     Used in "where" output, and to emit breakpoint or step
>     messages.  */
>  
> -void
> -print_frame_info (const frame_print_options &fp_opts,
> -		  frame_info_ptr frame, int print_level,
> -		  enum print_what print_what, int print_args,
> -		  int set_current_sal)
> +static void
> +do_print_frame_info (struct ui_out *uiout, const frame_print_options &fp_opts,
> +		     frame_info_ptr frame, int print_level,
> +		     enum print_what print_what, int print_args,
> +		     int set_current_sal)
>  {
>    struct gdbarch *gdbarch = get_frame_arch (frame);
>    int source_print;
>    int location_print;
> -  struct ui_out *uiout = current_uiout;
>  
>    if (!current_uiout->is_mi_like_p ()
>        && fp_opts.print_frame_info != print_frame_info_auto)
> @@ -1105,7 +1105,8 @@ print_frame_info (const frame_print_options &fp_opts,
>  		    || print_what == LOC_AND_ADDRESS
>  		    || print_what == SHORT_LOCATION);
>    if (location_print || !sal.symtab)
> -    print_frame (fp_opts, frame, print_level, print_what, print_args, sal);
> +    print_frame (uiout, fp_opts, frame, print_level,
> +		 print_what, print_args, sal);
>  
>    source_print = (print_what == SRC_LINE || print_what == SRC_AND_LOC);
>  
> @@ -1185,6 +1186,23 @@ print_frame_info (const frame_print_options &fp_opts,
>    gdb_flush (gdb_stdout);
>  }
>  
> +/* Redirect output to a temporary buffer for the duration
> +   of do_print_frame_info.  */
> +
> +void
> +print_frame_info (const frame_print_options &fp_opts,
> +		  frame_info_ptr frame, int print_level,
> +		  enum print_what print_what, int print_args,
> +		  int set_current_sal)
> +{
> +  using ftype = void (ui_out *, const frame_print_options &, frame_info_ptr,
> +		      int, enum print_what, int, int);
> +
> +  do_with_buffered_output<ftype> (do_print_frame_info, current_uiout,
> +				  fp_opts, frame, print_level, print_what,
> +				  print_args, set_current_sal);
> +}
> +
>  /* See stack.h.  */
>  
>  void
> @@ -1309,13 +1327,13 @@ find_frame_funname (frame_info_ptr frame, enum language *funlang,
>  }
>  
>  static void
> -print_frame (const frame_print_options &fp_opts,
> +print_frame (struct ui_out *uiout,
> +	     const frame_print_options &fp_opts,
>  	     frame_info_ptr frame, int print_level,
>  	     enum print_what print_what, int print_args,
>  	     struct symtab_and_line sal)
>  {
>    struct gdbarch *gdbarch = get_frame_arch (frame);
> -  struct ui_out *uiout = current_uiout;
>    enum language funlang = language_unknown;
>    struct value_print_options opts;
>    struct symbol *func;
> diff --git a/gdb/thread.c b/gdb/thread.c
> index 7f7f035b5ab..03105fbd4ce 100644
> --- a/gdb/thread.c
> +++ b/gdb/thread.c
> @@ -1040,6 +1040,106 @@ thread_target_id_str (thread_info *tp)
>      return target_id;
>  }
>  
> +/* Print thread TP. GLOBAL_IDS indicates whether REQUESTED_THREADS
> +   is a list of global or per-inferior thread ids.  */
> +
> +static void
> +do_print_thread (ui_out *uiout, const char *requested_threads,
> +		 int global_ids, int pid, int show_global_ids,
> +		 int default_inf_num, thread_info *tp,
> +		 thread_info *current_thread)
> +{
> +  int core;
> +
> +  if (!should_print_thread (requested_threads, default_inf_num,
> +			    global_ids, pid, tp))
> +    return;
> +
> +  ui_out_emit_tuple tuple_emitter (uiout, NULL);
> +
> +  if (!uiout->is_mi_like_p ())
> +    {
> +      if (tp == current_thread)
> +	uiout->field_string ("current", "*");
> +      else
> +	uiout->field_skip ("current");
> +
> +      uiout->field_string ("id-in-tg", print_thread_id (tp));
> +    }
> +
> +  if (show_global_ids || uiout->is_mi_like_p ())
> +    uiout->field_signed ("id", tp->global_num);
> +
> +  /* Switch to the thread (and inferior / target).  */
> +  switch_to_thread (tp);
> +
> +  /* For the CLI, we stuff everything into the target-id field.
> +     This is a gross hack to make the output come out looking
> +     correct.  The underlying problem here is that ui-out has no
> +     way to specify that a field's space allocation should be
> +     shared by several fields.  For MI, we do the right thing
> +     instead.  */
> +
> +  if (uiout->is_mi_like_p ())
> +    {
> +      uiout->field_string ("target-id", target_pid_to_str (tp->ptid));
> +
> +      const char *extra_info = target_extra_thread_info (tp);
> +      if (extra_info != nullptr)
> +	uiout->field_string ("details", extra_info);
> +
> +      const char *name = thread_name (tp);
> +      if (name != NULL)
> +	uiout->field_string ("name", name);
> +    }
> +  else
> +    {
> +      uiout->field_string ("target-id", thread_target_id_str (tp));
> +    }
> +
> +  if (tp->state == THREAD_RUNNING)
> +    uiout->text ("(running)\n");
> +  else
> +    {
> +      /* The switch above put us at the top of the stack (leaf
> +	 frame).  */
> +      print_stack_frame (get_selected_frame (NULL),
> +			 /* For MI output, print frame level.  */
> +			 uiout->is_mi_like_p (),
> +			 LOCATION, 0);
> +    }
> +
> +  if (uiout->is_mi_like_p ())
> +    {
> +      const char *state = "stopped";
> +
> +      if (tp->state == THREAD_RUNNING)
> +	state = "running";
> +      uiout->field_string ("state", state);
> +    }
> +
> +  core = target_core_of_thread (tp->ptid);
> +  if (uiout->is_mi_like_p () && core != -1)
> +    uiout->field_signed ("core", core);
> +}
> +
> +/* Redirect output to a temporary buffer for the duration
> +   of do_print_thread.  */
> +
> +static void
> +print_thread (ui_out *uiout, const char *requested_threads,
> +	      int global_ids, int pid, int show_global_ids,
> +	      int default_inf_num, thread_info *tp, thread_info *current_thread)
> +
> +{
> +  using ftype = void (ui_out *, const char *, int, int, int,
> +		      int, thread_info *, thread_info *);
> +
> +  do_with_buffered_output<ftype>
> +    (do_print_thread, uiout, requested_threads, global_ids, pid,
> +     show_global_ids, default_inf_num, tp, current_thread);
> +}
> +
>  /* Like print_thread_info, but in addition, GLOBAL_IDS indicates
>     whether REQUESTED_THREADS is a list of global or per-inferior
>     thread ids.  */
> @@ -1123,82 +1223,12 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
>      for (inferior *inf : all_inferiors ())
>        for (thread_info *tp : inf->threads ())
>  	{
> -	  int core;
> -
>  	  any_thread = true;
>  	  if (tp == current_thread && tp->state == THREAD_EXITED)
>  	    current_exited = true;
>  
> -	  if (!should_print_thread (requested_threads, default_inf_num,
> -				    global_ids, pid, tp))
> -	    continue;
> -
> -	  ui_out_emit_tuple tuple_emitter (uiout, NULL);
> -
> -	  if (!uiout->is_mi_like_p ())
> -	    {
> -	      if (tp == current_thread)
> -		uiout->field_string ("current", "*");
> -	      else
> -		uiout->field_skip ("current");
> -
> -	      uiout->field_string ("id-in-tg", print_thread_id (tp));
> -	    }
> -
> -	  if (show_global_ids || uiout->is_mi_like_p ())
> -	    uiout->field_signed ("id", tp->global_num);
> -
> -	  /* Switch to the thread (and inferior / target).  */
> -	  switch_to_thread (tp);
> -
> -	  /* For the CLI, we stuff everything into the target-id field.
> -	     This is a gross hack to make the output come out looking
> -	     correct.  The underlying problem here is that ui-out has no
> -	     way to specify that a field's space allocation should be
> -	     shared by several fields.  For MI, we do the right thing
> -	     instead.  */
> -
> -	  if (uiout->is_mi_like_p ())
> -	    {
> -	      uiout->field_string ("target-id", target_pid_to_str (tp->ptid));
> -
> -	      const char *extra_info = target_extra_thread_info (tp);
> -	      if (extra_info != nullptr)
> -		uiout->field_string ("details", extra_info);
> -
> -	      const char *name = thread_name (tp);
> -	      if (name != NULL)
> -		uiout->field_string ("name", name);
> -	    }
> -	  else
> -	    {
> -	      uiout->field_string ("target-id", thread_target_id_str (tp));
> -	    }
> -
> -	  if (tp->state == THREAD_RUNNING)
> -	    uiout->text ("(running)\n");
> -	  else
> -	    {
> -	      /* The switch above put us at the top of the stack (leaf
> -		 frame).  */
> -	      print_stack_frame (get_selected_frame (NULL),
> -				 /* For MI output, print frame level.  */
> -				 uiout->is_mi_like_p (),
> -				 LOCATION, 0);
> -	    }
> -
> -	  if (uiout->is_mi_like_p ())
> -	    {
> -	      const char *state = "stopped";
> -
> -	      if (tp->state == THREAD_RUNNING)
> -		state = "running";
> -	      uiout->field_string ("state", state);
> -	    }
> -
> -	  core = target_core_of_thread (tp->ptid);
> -	  if (uiout->is_mi_like_p () && core != -1)
> -	    uiout->field_signed ("core", core);
> +	  print_thread (uiout, requested_threads, global_ids, pid,
> +			show_global_ids, default_inf_num, tp, current_thread);
>  	}
>  
>      /* This end scope restores the current thread and the frame
> diff --git a/gdb/ui-file.c b/gdb/ui-file.c
> index e0814fe2b2d..73277cd853e 100644
> --- a/gdb/ui-file.c
> +++ b/gdb/ui-file.c
> @@ -234,6 +234,68 @@ string_file::can_emit_style_escape ()
>  
>  \f
>  
> +/* See ui-file.h.  */
> +
> +void
> +buffer_file::wrap_here (int indent)
> +{
> +  m_string_wraps.emplace (m_string_wraps.end (),
> +			  string_wrap_pair (m_string, indent));
> +  m_string.clear ();
> +}
> +
> +/* See ui-file.h.  */
> +
> +void
> +buffer_file::flush_to_stream ()
> +{
> +  /* Add m_string to m_string_wraps with no corresponding wrap_here.  */
> +  wrap_here (-1);
> +
> +  for (string_wrap_pair pair : m_string_wraps)
> +    {
> +      std::string buf = std::move (pair.first);
> +      size_t size = buf.size ();
> +
> +      /* Write each line separately.  */
> +      for (size_t prev = 0, cur = 0; cur < size; ++cur)
> +	if (buf.at (cur) == '\n' || cur == size - 1)
> +	  {
> +	    std::string sub = buf.substr (prev, cur - prev + 1);
> +	    m_stream->puts (sub.c_str ());
> +	    prev = cur + 1;
> +	  }
> +
> +      if (pair.second >= 0)
> +	m_stream->wrap_here (pair.second);
> +    }
> +
> +  m_string_wraps.clear ();
> +}
> +
> +/* See ui-file.h.  */
> +
> +ui_file *
> +buffer_file::get_unbuffered_stream (ui_file *stream)
> +{
> +  buffer_file *buf = dynamic_cast<buffer_file *> (stream);
> +
> +  if (buf == nullptr)
> +    return stream;
> +
> +  return get_unbuffered_stream (buf->m_stream);
> +}
> +
> +/* See ui-file.h.  */
> +
> +bool
> +buffer_file::isatty ()
> +{
> +  return m_stream->isatty ();
> +}
> +
> +\f
> +
>  stdio_file::stdio_file (FILE *file, bool close_p)
>  {
>    set_stream (file);
> diff --git a/gdb/ui-file.h b/gdb/ui-file.h
> index de24620e247..a19eb7a9bfe 100644
> --- a/gdb/ui-file.h
> +++ b/gdb/ui-file.h
> @@ -220,13 +220,56 @@ class string_file : public ui_file
>    bool empty () const { return m_string.empty (); }
>    void clear () { return m_string.clear (); }
>  
> -private:
> +protected:
>    /* The internal buffer.  */
>    std::string m_string;
>  
>    bool m_term_out;
>  };
>  
> +/* A string_file implementation that collects output on behalf of a
> +   given ui_file.  Provides access to the underlying ui_file so
> +   that buffering can be selectively bypassed.  */
> +
> +class buffer_file : public string_file
> +{
> +public:
> +  explicit buffer_file (bool term_out, ui_file *stream)
> +    : string_file (term_out), m_stream (stream)
> +  {}
> +
> +  /* Return the stream associated with this buffer_file.  */
> +  ui_file *get_stream ()
> +  {
> +    return m_stream;
> +  }
> +
> +  /* Record the wrap hint.  When flushing the buffer, the underlying
> +     ui_file's wrap_here will be called at the current point in the output.  */
> +  void wrap_here (int indent) override;
> +
> +  /* Flush collected output to the underlying ui_file.  */
> +  void flush_to_stream ();
> +
> +  /* Return true if the underlying stream is a tty.  */
> +  bool isatty () override;
> +
> +  /* Return a pointer to STREAM's underlying ui_file.  Recursively called until
> +     a non-buffer_file is found.  */
> +  static ui_file *get_unbuffered_stream (ui_file *stream);
> +
> +private:
> +
> +  /* The underlying output stream.  */
> +  ui_file *m_stream;
> +
> +  typedef std::pair<std::string, int> string_wrap_pair;
> +
> +  /* A collection of strings paired with an int representing the argument
> +     to a wrap_here call.  */
> +  std::vector<string_wrap_pair> m_string_wraps;
> +};
> +
>  /* A ui_file implementation that maps directly onto <stdio.h>'s FILE.
>     A stdio_file can either own its underlying file, or not.  If it
>     owns the file, then destroying the stdio_file closes the underlying
> diff --git a/gdb/ui-out.c b/gdb/ui-out.c
> index 9380630c320..3749317a25b 100644
> --- a/gdb/ui-out.c
> +++ b/gdb/ui-out.c
> @@ -799,6 +799,12 @@ ui_out::redirect (ui_file *outstream)
>    do_redirect (outstream);
>  }
>  
> +void
> +ui_out::redirect_to_buffer (buffer_file *buf_file)
> +{
> +  do_redirect_to_buffer (buf_file);
> +}
> +
>  /* Test the flags against the mask given.  */
>  ui_out_flags
>  ui_out::test_flags (ui_out_flags mask)
> @@ -871,3 +877,18 @@ ui_out::ui_out (ui_out_flags flags)
>  ui_out::~ui_out ()
>  {
>  }
> +
> +ui_out_buffer_pop::ui_out_buffer_pop (ui_out *uiout)
> +: m_uiout (uiout),
> +  m_buf_file (uiout->can_emit_style_escape (), gdb_stdout),
> +  m_prev_gdb_stdout (gdb_stdout)
> +{
> +  m_uiout->redirect_to_buffer (&m_buf_file);
> +  gdb_stdout = &m_buf_file;
> +}
> +
> +ui_out_buffer_pop::~ui_out_buffer_pop ()
> +{
> +  m_uiout->redirect_to_buffer (nullptr);
> +  gdb_stdout = m_prev_gdb_stdout;
> +}
> diff --git a/gdb/ui-out.h b/gdb/ui-out.h
> index ba5b1de68ab..ceee7be6938 100644
> --- a/gdb/ui-out.h
> +++ b/gdb/ui-out.h
> @@ -262,6 +262,9 @@ class ui_out
>    /* Redirect the output of a ui_out object temporarily.  */
>    void redirect (ui_file *outstream);
>  
> +  /* Redirect the output of a ui_out object to a buffer_file temporarily.  */
> +  void redirect_to_buffer (buffer_file *buf_file);
> +
>    ui_out_flags test_flags (ui_out_flags mask);
>  
>    /* HACK: Code in GDB is currently checking to see the type of ui_out
> @@ -360,6 +363,7 @@ class ui_out
>    virtual void do_wrap_hint (int indent) = 0;
>    virtual void do_flush () = 0;
>    virtual void do_redirect (struct ui_file *outstream) = 0;
> +  virtual void do_redirect_to_buffer (buffer_file *buf_file) = 0;
>  
>    virtual void do_progress_start () = 0;
>    virtual void do_progress_notify (const std::string &, const char *,
> @@ -470,4 +474,66 @@ class ui_out_redirect_pop
>    struct ui_out *m_uiout;
>  };
>  
> +/* On construction, redirect a uiout and gdb_stdout to a buffer_file.
> +   On destruction restore uiout and gdb_stdout.  */
> +
> +class ui_out_buffer_pop
> +{
> +public:
> +  ui_out_buffer_pop (ui_out *uiout);
> +
> +  ~ui_out_buffer_pop ();
> +
> +  /* Flush buffered output to the underlying output stream.  */
> +  void flush ()
> +  {
> +    m_buf_file.flush_to_stream ();
> +  }
> +
> +  ui_out_buffer_pop (const ui_out_buffer_pop &) = delete;
> +  ui_out_buffer_pop &operator= (const ui_out_buffer_pop &) = delete;
> +
> +private:
> +  /* ui_out being temporarily redirected.  */
> +  struct ui_out *m_uiout;
> +
> +  /* Buffer to which output is temporarily redirected to for the lifetime
> +     of this object.  */
> +  buffer_file m_buf_file;
> +
> +  /* Original gdb_stdout at the time of this object's construction.  */
> +  ui_file *m_prev_gdb_stdout;
> +};
> +
> +/* Redirect output to a buffer_file for the duration of FUNC.  */
> +
> +template<typename F, typename... Arg>
> +void
> +do_with_buffered_output (F func, ui_out *uiout, Arg... args)
> +{
> +  ui_out_buffer_pop buf (uiout);
> +
> +  try
> +    {
> +      func (uiout, std::forward<Arg> (args)...);
> +    }
> +  catch (gdb_exception &ex)
> +    {
> +      /* Ideally flush would be called in the destructor of buf,
> +	 however flushing might cause an exception to be thrown.
> +	 Catch it and ensure the first exception propagates.  */
> +      try
> +	{
> +	  buf.flush ();
> +	}
> +      catch (const gdb_exception &ignore)
> +	{
> +	}
> +
> +      throw_exception (std::move (ex));
> +    }
> +
> +  /* Try was successful.  Let any further exceptions propagate.  */
> +  buf.flush ();
> +}
>  #endif /* UI_OUT_H */
> -- 
> 2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 1/6 v2] gdb/debuginfod: Add debuginfod_section_query
  2023-06-01  1:43 ` [PATCH 1/6 v2] gdb/debuginfod: Add debuginfod_section_query Aaron Merey
@ 2023-06-07 13:35   ` Andrew Burgess
  2023-07-27 11:04     ` Andrew Burgess
  0 siblings, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2023-06-07 13:35 UTC (permalink / raw)
  To: Aaron Merey, gdb-patches; +Cc: Aaron Merey

Aaron Merey <amerey@redhat.com> writes:

> v1: https://sourceware.org/pipermail/gdb-patches/2023-February/197455.html
>
> v2 improves the description of AC_DEBUGINFOD_SECTION and associated
> macro names.  An if-statement in debuginfod_section_query is also
> replaced with a gdb_assert.
>
> Commit message:
>
> Add new function debuginfod_section_query.  This function queries
> debuginfod servers for an individual ELF/DWARF section associated with
> a given build-id.
>
> Also check for libdebuginfod version >= 0.188 at configure time.
> debuginfod_section_query simply returns -ENOSYS if this condition
> is not met.

This LGTM.

I don't think this should be merged until there's a use for the new GDB
functions (when later patches are also approved).

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew


> ---
>  config/debuginfod.m4     |  33 +++++++++++
>  gdb/config.in            |   3 +
>  gdb/configure            | 116 +++++++++++++++++++++++++++++++++++++--
>  gdb/configure.ac         |   2 +-
>  gdb/debuginfod-support.c |  60 ++++++++++++++++++++
>  gdb/debuginfod-support.h |  24 ++++++++
>  6 files changed, 232 insertions(+), 6 deletions(-)
>
> diff --git a/config/debuginfod.m4 b/config/debuginfod.m4
> index 2c1bfbdb544..4c443b77d3a 100644
> --- a/config/debuginfod.m4
> +++ b/config/debuginfod.m4
> @@ -26,3 +26,36 @@ else
>    AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])
>  fi
>  ])
> +
> +AC_DEFUN([AC_DEBUGINFOD_SECTION],
> +[
> +# Handle optional debuginfod support as well as optional section
> +# downloading support.
> +#
> +# Define HAVE_LIBDEBUGINFOD if libdebuginfod is found with version >= 0.179.
> +#
> +# Define HAVE_LIBDEBUGINFOD_FIND_SECTION if libdebuginfod is found with
> +# version >= 0.188.
> +AC_ARG_WITH([debuginfod],
> +  AC_HELP_STRING([--with-debuginfod], [Enable debuginfo lookups with debuginfod (auto/yes/no)]),
> +  [], [with_debuginfod=auto])
> +AC_MSG_CHECKING([whether to use debuginfod])
> +AC_MSG_RESULT([$with_debuginfod])
> +
> +if test "x$with_debuginfod" != xno; then
> +  PKG_CHECK_MODULES([DEBUGINFOD], [libdebuginfod >= 0.188],
> +    [AC_DEFINE([HAVE_LIBDEBUGINFOD_FIND_SECTION], [1],
> +	       [Define to 1 if debuginfod section downloading is supported.])],
> +    [AC_MSG_WARN([libdebuginfod is missing or some features may be unavailable.])])
> +
> +  PKG_CHECK_MODULES([DEBUGINFOD], [libdebuginfod >= 0.179],
> +    [AC_DEFINE([HAVE_LIBDEBUGINFOD], [1], [Define to 1 if debuginfod is enabled.])],
> +    [if test "x$with_debuginfod" = xyes; then
> +      AC_MSG_ERROR(["--with-debuginfod was given, but libdebuginfod is missing or unusable."])
> +     else
> +      AC_MSG_WARN([libdebuginfod is missing or unusable; some features may be unavailable.])
> +     fi])
> +else
> +  AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])
> +fi
> +])
> diff --git a/gdb/config.in b/gdb/config.in
> index a7da88b92d7..95dd2fa22cb 100644
> --- a/gdb/config.in
> +++ b/gdb/config.in
> @@ -244,6 +244,9 @@
>  /* Define to 1 if debuginfod is enabled. */
>  #undef HAVE_LIBDEBUGINFOD
>  
> +/* Define to 1 if debuginfod section downloading is supported. */
> +#undef HAVE_LIBDEBUGINFOD_FIND_SECTION
> +
>  /* Define if you have the expat library. */
>  #undef HAVE_LIBEXPAT
>  
> diff --git a/gdb/configure b/gdb/configure
> index 5bb2a0795e5..f58a2b6b92a 100755
> --- a/gdb/configure
> +++ b/gdb/configure
> @@ -18349,7 +18349,13 @@ esac
>  
>  # Handle optional debuginfod support
>  
> -# Handle optional debuginfod support
> +# Handle optional debuginfod support as well as optional section
> +# downloading support.
> +#
> +# Define HAVE_LIBDEBUGINFOD if libdebuginfod is found with version >= 0.179.
> +#
> +# Define HAVE_LIBDEBUGINFOD_FIND_SECTION if libdebuginfod is found with
> +# version >= 0.188.
>  
>  # Check whether --with-debuginfod was given.
>  if test "${with_debuginfod+set}" = set; then :
> @@ -18365,6 +18371,106 @@ $as_echo "$with_debuginfod" >&6; }
>  
>  if test "x$with_debuginfod" != xno; then
>  
> +pkg_failed=no
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.188" >&5
> +$as_echo_n "checking for libdebuginfod >= 0.188... " >&6; }
> +
> +if test -n "$DEBUGINFOD_CFLAGS"; then
> +    pkg_cv_DEBUGINFOD_CFLAGS="$DEBUGINFOD_CFLAGS"
> + elif test -n "$PKG_CONFIG"; then
> +    if test -n "$PKG_CONFIG" && \
> +    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
> +  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
> +  ac_status=$?
> +  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
> +  test $ac_status = 0; }; then
> +  pkg_cv_DEBUGINFOD_CFLAGS=`$PKG_CONFIG --cflags "libdebuginfod >= 0.188" 2>/dev/null`
> +		      test "x$?" != "x0" && pkg_failed=yes
> +else
> +  pkg_failed=yes
> +fi
> + else
> +    pkg_failed=untried
> +fi
> +if test -n "$DEBUGINFOD_LIBS"; then
> +    pkg_cv_DEBUGINFOD_LIBS="$DEBUGINFOD_LIBS"
> + elif test -n "$PKG_CONFIG"; then
> +    if test -n "$PKG_CONFIG" && \
> +    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.188\""; } >&5
> +  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.188") 2>&5
> +  ac_status=$?
> +  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
> +  test $ac_status = 0; }; then
> +  pkg_cv_DEBUGINFOD_LIBS=`$PKG_CONFIG --libs "libdebuginfod >= 0.188" 2>/dev/null`
> +		      test "x$?" != "x0" && pkg_failed=yes
> +else
> +  pkg_failed=yes
> +fi
> + else
> +    pkg_failed=untried
> +fi
> +
> +if test $pkg_failed = no; then
> +  pkg_save_LDFLAGS="$LDFLAGS"
> +  LDFLAGS="$LDFLAGS $pkg_cv_DEBUGINFOD_LIBS"
> +  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h.  */
> +
> +int
> +main ()
> +{
> +
> +  ;
> +  return 0;
> +}
> +_ACEOF
> +if ac_fn_c_try_link "$LINENO"; then :
> +
> +else
> +  pkg_failed=yes
> +fi
> +rm -f core conftest.err conftest.$ac_objext \
> +    conftest$ac_exeext conftest.$ac_ext
> +  LDFLAGS=$pkg_save_LDFLAGS
> +fi
> +
> +
> +
> +if test $pkg_failed = yes; then
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +$as_echo "no" >&6; }
> +
> +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
> +        _pkg_short_errors_supported=yes
> +else
> +        _pkg_short_errors_supported=no
> +fi
> +        if test $_pkg_short_errors_supported = yes; then
> +	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
> +        else
> +	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdebuginfod >= 0.188" 2>&1`
> +        fi
> +	# Put the nasty error message in config.log where it belongs
> +	echo "$DEBUGINFOD_PKG_ERRORS" >&5
> +
> +	{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or some features may be unavailable." >&5
> +$as_echo "$as_me: WARNING: libdebuginfod is missing or some features may be unavailable." >&2;}
> +elif test $pkg_failed = untried; then
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +$as_echo "no" >&6; }
> +	{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or some features may be unavailable." >&5
> +$as_echo "$as_me: WARNING: libdebuginfod is missing or some features may be unavailable." >&2;}
> +else
> +	DEBUGINFOD_CFLAGS=$pkg_cv_DEBUGINFOD_CFLAGS
> +	DEBUGINFOD_LIBS=$pkg_cv_DEBUGINFOD_LIBS
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
> +$as_echo "yes" >&6; }
> +
> +$as_echo "#define HAVE_LIBDEBUGINFOD_FIND_SECTION 1" >>confdefs.h
> +
> +fi
> +
> +
>  pkg_failed=no
>  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdebuginfod >= 0.179" >&5
>  $as_echo_n "checking for libdebuginfod >= 0.179... " >&6; }
> @@ -18448,18 +18554,18 @@ fi
>  	echo "$DEBUGINFOD_PKG_ERRORS" >&5
>  
>  	if test "x$with_debuginfod" = xyes; then
> -       as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
> +      as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
>       else
> -       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
> +      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
>  $as_echo "$as_me: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&2;}
>       fi
>  elif test $pkg_failed = untried; then
>          { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
>  $as_echo "no" >&6; }
>  	if test "x$with_debuginfod" = xyes; then
> -       as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
> +      as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
>       else
> -       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
> +      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
>  $as_echo "$as_me: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&2;}
>       fi
>  else
> diff --git a/gdb/configure.ac b/gdb/configure.ac
> index fb43cd10d6c..499802bb4c9 100644
> --- a/gdb/configure.ac
> +++ b/gdb/configure.ac
> @@ -342,7 +342,7 @@ case $host_os in
>  esac
>  
>  # Handle optional debuginfod support
> -AC_DEBUGINFOD
> +AC_DEBUGINFOD_SECTION
>  
>  # Libunwind support for ia64.
>  AC_ARG_WITH(libunwind-ia64,
> diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
> index 5853f420a18..8be43a91dcc 100644
> --- a/gdb/debuginfod-support.c
> +++ b/gdb/debuginfod-support.c
> @@ -80,6 +80,15 @@ debuginfod_exec_query (const unsigned char *build_id,
>    return scoped_fd (-ENOSYS);
>  }
>  
> +scoped_fd
> +debuginfod_section_query (const unsigned char *build_id,
> +			  int build_id_len,
> +			  const char *filename,
> +			  const char *section_name,
> +			  gdb::unique_xmalloc_ptr<char> *destname)
> +{
> +  return scoped_fd (-ENOSYS);
> +}
>  #define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
>  
>  #else
> @@ -401,6 +410,57 @@ debuginfod_exec_query (const unsigned char *build_id,
>  
>    return fd;
>  }
> +
> +/* See debuginfod-support.h  */
> +
> +scoped_fd
> +debuginfod_section_query (const unsigned char *build_id,
> +			  int build_id_len,
> +			  const char *filename,
> +			  const char *section_name,
> +			  gdb::unique_xmalloc_ptr<char> *destname)
> +{
> +#if !defined (HAVE_LIBDEBUGINFOD_FIND_SECTION)
> +  return scoped_fd (-ENOSYS);
> +#else
> +
> + if (!debuginfod_is_enabled ())
> +    return scoped_fd (-ENOSYS);
> +
> +  debuginfod_client *c = get_debuginfod_client ();
> +
> +  if (c == nullptr)
> +    return scoped_fd (-ENOMEM);
> +
> +  char *dname = nullptr;
> +  std::string desc = std::string ("section ") + section_name + " for";
> +  scoped_fd fd;
> +  gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
> +
> +  {
> +    user_data data (desc.c_str (), filename);
> +    debuginfod_set_user_data (c, &data);
> +    if (target_supports_terminal_ours ())
> +      {
> +	term_state.emplace ();
> +	target_terminal::ours ();
> +      }
> +
> +    fd = scoped_fd (debuginfod_find_section (c, build_id, build_id_len,
> +					     section_name, &dname));
> +    debuginfod_set_user_data (c, nullptr);
> +  }
> +
> +  print_outcome (fd.get (), desc.c_str (), filename);
> +  gdb_assert (destname != nullptr);
> +
> +  if (fd.get () >= 0)
> +    destname->reset (dname);
> +
> +  return fd;
> +#endif /* HAVE_LIBDEBUGINFOD_FIND_SECTION */
> +}
> +
>  #endif
>  
>  /* Set callback for "set debuginfod enabled".  */
> diff --git a/gdb/debuginfod-support.h b/gdb/debuginfod-support.h
> index 633600a79da..9701e3b4685 100644
> --- a/gdb/debuginfod-support.h
> +++ b/gdb/debuginfod-support.h
> @@ -81,4 +81,28 @@ extern scoped_fd debuginfod_exec_query (const unsigned char *build_id,
>  					const char *filename,
>  					gdb::unique_xmalloc_ptr<char>
>  					  *destname);
> +
> +/* Query debuginfod servers for the binary contents of a ELF/DWARF section
> +   from a file matching BUILD_ID.  BUILD_ID can be given as a binary blob
> +   or a null-terminated string.  If given as a binary blob, BUILD_ID_LEN
> +   should be the number of bytes.  If given as a null-terminated string,
> +   BUILD_ID_LEN should be 0.
> +
> +   FILENAME should be the name or path associated with the file matching
> +   BUILD_ID.  It is used for printing messages to the user.
> +
> +   SECTION_NAME should be the name of an ELF/DWARF section.
> +
> +   If the file is successfully retrieved, return a file descriptor and store
> +   the file's local path in DESTNAME.  If unsuccessful, print an error message
> +   and return a negative errno.  If GDB is not built with debuginfod or
> +   libdebuginfod does not support section queries, this function returns
> +   -ENOSYS.  */
> +
> +extern scoped_fd debuginfod_section_query (const unsigned char *build_id,
> +					   int build_id_len,
> +					   const char *filename,
> +					   const char *section_name,
> +					   gdb::unique_xmalloc_ptr<char>
> +					     *destname);
>  #endif /* DEBUGINFOD_SUPPORT_H */
> -- 
> 2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections'
  2023-06-01  1:43 ` [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections' Aaron Merey
  2023-06-01  6:13   ` Eli Zaretskii
@ 2023-06-07 13:57   ` Andrew Burgess
  2023-07-27 12:04   ` Andrew Burgess
  2023-07-27 12:19   ` Andrew Burgess
  3 siblings, 0 replies; 33+ messages in thread
From: Andrew Burgess @ 2023-06-07 13:57 UTC (permalink / raw)
  To: Aaron Merey, gdb-patches; +Cc: Aaron Merey

Aaron Merey <amerey@redhat.com> writes:

> v1: https://sourceware.org/pipermail/gdb-patches/2023-February/197461.html
>
> v2 removes 'set debuginfod enabled lazy' and replaces it with
> 'maint set debuginfod download-sections'.
>
> Commit message:
>
> This setting controls whether GDB may attempt to download ELF/DWARF
> sections from debuginfod servers.
>
> This setting is enabled by default if gdb is built with debuginfod
> section download support (requires libdebuginfod 0.188).
>
> Also update debuginfod command help text and gdb.texinfo with
> information on section downloading and the new command.
> ---
>  gdb/debuginfod-support.c | 66 ++++++++++++++++++++++++++++++++++++++--
>  gdb/doc/gdb.texinfo      | 16 ++++++++--
>  2 files changed, 77 insertions(+), 5 deletions(-)
>
> diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
> index 8be43a91dcc..6d0521b64e2 100644
> --- a/gdb/debuginfod-support.c
> +++ b/gdb/debuginfod-support.c
> @@ -31,6 +31,10 @@
>  static cmd_list_element *set_debuginfod_prefix_list;
>  static cmd_list_element *show_debuginfod_prefix_list;
>  
> +/* maint set/show debuginfod commands.  */
> +static cmd_list_element *maint_set_debuginfod_cmdlist;
> +static cmd_list_element *maint_show_debuginfod_cmdlist;
> +
>  static const char debuginfod_on[] = "on";
>  static const char debuginfod_off[] = "off";
>  static const char debuginfod_ask[] = "ask";
> @@ -50,6 +54,13 @@ static const char *debuginfod_enabled =
>    debuginfod_off;
>  #endif
>  
> +static bool debuginfod_download_sections =

Should have a comment.

> +#if defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
> +  true;
> +#else
> +  false;
> +#endif
> +
>  static unsigned int debuginfod_verbose = 1;
>  
>  #ifndef HAVE_LIBDEBUGINFOD
> @@ -424,7 +435,7 @@ debuginfod_section_query (const unsigned char *build_id,
>    return scoped_fd (-ENOSYS);
>  #else
>  
> - if (!debuginfod_is_enabled ())
> + if (!debuginfod_download_sections || !debuginfod_is_enabled ())
>      return scoped_fd (-ENOSYS);
>  
>    debuginfod_client *c = get_debuginfod_client ();
> @@ -550,6 +561,31 @@ show_debuginfod_verbose_command (ui_file *file, int from_tty,
>  	      value);
>  }
>  
> +/* Set callback for "maint set debuginfod download-sections".  */
> +
> +static void
> +maint_set_debuginfod_download_sections (bool value)
> +{
> +#if !defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
> +  if (value)
> +    {
> +      error (_("Support for section downloading is not compiled into GDB. " \
> +"Defaulting to \"off\"."));
> +      return;
> +    }

You can drop the return here, error is marked noreturn.  With that gone,
you can remove the '{' and '}' too.

> +#endif
> +
> +  debuginfod_download_sections = value;
> +}
> +
> +/* Get callback for "maint set debuginfod download-sections".  */
> +
> +static bool
> +maint_get_debuginfod_download_sections ()
> +{
> +  return debuginfod_download_sections;
> +}
> +
>  /* Register debuginfod commands.  */
>  
>  void _initialize_debuginfod ();
> @@ -568,8 +604,11 @@ _initialize_debuginfod ()
>  			_("Set whether to use debuginfod."),
>  			_("Show whether to use debuginfod."),
>  			_("\
> -When on, enable the use of debuginfod to download missing debug info and\n\
> -source files."),
> +When set to \"on\", enable the use of debuginfod to download missing\n\
> +debug info and source files. GDB may also download components of debug\n\
> +info instead of entire files. \"off\" disables the use of debuginfod.\n\
> +When set to \"ask\", a prompt may ask whether to enable or disable\n\
> +debuginfod." ),
>  			set_debuginfod_enabled,
>  			get_debuginfod_enabled,
>  			show_debuginfod_enabled,
> @@ -600,4 +639,25 @@ query.\nTo disable, set to zero.  Verbose output is displayed by default."),
>  			     show_debuginfod_verbose_command,
>  			     &set_debuginfod_prefix_list,
>  			     &show_debuginfod_prefix_list);
> +
> +  /* maint set/show debuginfod */

This comment, and the one below, should end with a full stop and double
space.

> +  add_setshow_prefix_cmd ("debuginfod", class_maintenance,
> +			  _("Set debuginfod specific variables."),
> +			  _("Show debuginfod specific variables."),
> +			  &maint_set_debuginfod_cmdlist,
> +			  &maint_show_debuginfod_cmdlist,
> +			  &maintenance_set_cmdlist, &maintenance_show_cmdlist);
> +
> +  /* maint set/show debuginfod download-sections */
> +  add_setshow_boolean_cmd ("download-sections", class_maintenance, _("\
> +Set whether debuginfod may download individual ELF/DWARF sections."), _("\
> +Show whether debuginfod may download individual ELF/DWARF sections."), _("\
> +When enabled, debuginfod may attempt to download individual ELF/DWARF \
> +sections from debug info files.\nIf disabled, only whole debug info files \
> +may be downloaded."),
> +			  maint_set_debuginfod_download_sections,
> +			  maint_get_debuginfod_download_sections,
> +			  nullptr,
> +			  &maint_set_debuginfod_cmdlist,
> +			  &maint_show_debuginfod_cmdlist);

The indentation is off by one here.

I think all the above are pretty minor.  With those fixed this GDB code
changes are good.

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew

>  }
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index fc55c4e7b43..91da9777fd6 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -40872,6 +40872,16 @@ Create a core file? (y or n) @kbd{n}
>  (@value{GDBP})
>  @end smallexample
>  
> +@kindex maint set debuginfod
> +@kindex maint show debuginfod
> +@item maint set debuginfod download-sections
> +@itemx maint set debuginfod download-sections @r{[}on|off@r{]}
> +@itemx maint show debuginfod download-sections
> +Controls whether @value{GDBN} will attempt to download individual
> +ELF/DWARF sections from @code{debuginfod}.  If disabled, only
> +whole debug info files will be downloaded.  This could result
> +in @value{GDBN} downloading larger amounts of data.
> +
>  @cindex @value{GDBN} internal error
>  @cindex internal errors, control of @value{GDBN} behavior
>  @cindex demangler crashes
> @@ -49382,8 +49392,10 @@ regarding @code{debuginfod}.
>  @item set debuginfod enabled
>  @itemx set debuginfod enabled on
>  @cindex enable debuginfod
> -@value{GDBN} will attempt to query @code{debuginfod} servers when missing debug
> -info or source files.
> +@value{GDBN} will attempt to query @code{debuginfod} servers for missing debug
> +info or source files.  @value{GDBN} may attempt to download individual ELF/DWARF
> +sections such as @code{.gdb_index} to help reduce the total amount of data
> +downloaded from debuginfod servers.
>  
>  @item set debuginfod enabled off
>  @value{GDBN} will not attempt to query @code{debuginfod} servers when missing
> -- 
> 2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator
  2023-06-01  1:43 ` [PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator Aaron Merey
@ 2023-06-15 13:44   ` Aaron Merey
  2023-07-03 17:39     ` [PING*2][PATCH " Aaron Merey
  2023-07-31 10:11   ` [PATCH " Andrew Burgess
  1 sibling, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-06-15 13:44 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess

Ping

Thanks,
Aaron

On Wed, May 31, 2023 at 9:44 PM Aaron Merey <amerey@redhat.com> wrote:
>
> To facilitate the deletion of objfiles, progspace objects use a safe
> iterator that holds a reference to the next objfile in the progspace's
> objfile_list.  This allows objfiles to be deleted in a loop without
> invalidating the loop's iterator.  progspace also uses an unwrapping
> iterator over std::unique_ptr<objfile> that automatically deferences
> the unique_ptr.
>
> This patch changes the objfile safe iterator to be a reverse safe
> iterator.  It changes the unwrapping iterator into a template.  It
> also modifies objfile_list insertion so that separate debug objfiles
> are placed into the list after the parent objfile, instead of before.
>
> These changes are intended to prepare gdb for on-demand debuginfo
> downloading and the downloading of .gdb_index sections.
>
> With on-demand downloading enabled, gdb might download a debuginfo
> file during symtab expansion.  In this case an objfile could be added
> to an objfiles_list during iteration over the list (for example, in
> iterate_over_symtabs).  We want these loops to also iterate over newly
> downloaded objfiles. So objfiles need to be inserted into objfiles_list
> after their parent since it is during the search of the parent
> objfile for some symbol or filename that the separate debug objfile
> might be downloaded.
>
> The unwrapping iterator is changed into a template in order to
> use it with objfile qf_require_partial_symbols, which is now also
> uses with a safe iterator.  This is because after a separate debug
> objfile is downloaded on-demand, we want to remove any .gdb_index
> quick_symbol_functions from the parent objfile during iteration over
> the parent's quick_symbol_functions.  The newly downloaded separate
> debug objfile contains the index and all of the related symbols
> so the .gdb_index should not be associated with the parent objfile
> any longer.
>
> Finally a safe reverse iterator is now used during progspace objfile
> deletion in order to prevent iterator invalidation during the loop
> in which objfiles are deleted.  This could happen during forward
> iteration over objfiles_list when a separate debug objfile immediately
> follows it's parent objfile in the list (which is now possible since
> objfiles are inserted into the list after their parent).  Deletion
> of the parent would cause deletion of the separate debug objfile,
> which would invalidate the safe forward iterator's reference to the
> next objfile in the list.  A safe reverse iterator deletes separate
> debug objfiles before their parent, so the iterator's reference to
> the next objfile always stays valid.
>
> A small change was also made to a testcase in py-objfile.exp to
> account for the new placement of separate debug objfiles in
> objfiles_list.
> ---
>  gdb/objfiles.h                          |  22 +++-
>  gdb/progspace.c                         |   8 +-
>  gdb/progspace.h                         | 159 +++++++++++++++++++-----
>  gdb/symfile-debug.c                     | 136 ++++++++++----------
>  gdb/testsuite/gdb.python/py-objfile.exp |   2 +-
>  5 files changed, 218 insertions(+), 109 deletions(-)
>
> diff --git a/gdb/objfiles.h b/gdb/objfiles.h
> index 189856f0a51..bb7b0a4579d 100644
> --- a/gdb/objfiles.h
> +++ b/gdb/objfiles.h
> @@ -613,6 +613,17 @@ struct objfile
>    /* See quick_symbol_functions.  */
>    void require_partial_symbols (bool verbose);
>
> +  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
> +  void remove_partial_symbol (quick_symbol_functions *target)
> +  {
> +    for (quick_symbol_functions_up &qf_up : qf)
> +      if (qf_up.get () == target)
> +       {
> +         qf.remove (qf_up);
> +         return;
> +       }
> +  }
> +
>    /* Return the relocation offset applied to SECTION.  */
>    CORE_ADDR section_offset (bfd_section *section) const
>    {
> @@ -699,13 +710,20 @@ struct objfile
>
>  private:
>
> +  using qf_list = std::forward_list<quick_symbol_functions_up>;
> +  using unwrapping_qf_range = iterator_range<unwrapping_iterator<qf_list::iterator>>;
> +  using qf_safe_range = basic_safe_range<unwrapping_qf_range>;
> +
>    /* Ensure that partial symbols have been read and return the "quick" (aka
>       partial) symbol functions for this symbol reader.  */
> -  const std::forward_list<quick_symbol_functions_up> &
> +  qf_safe_range
>    qf_require_partial_symbols ()
>    {
>      this->require_partial_symbols (true);
> -    return qf;
> +    return qf_safe_range
> +      (unwrapping_qf_range
> +       (unwrapping_iterator<qf_list::iterator> (qf.begin ()),
> +        unwrapping_iterator<qf_list::iterator> (qf.end ())));
>    }
>
>  public:
> diff --git a/gdb/progspace.c b/gdb/progspace.c
> index 32bdfebcf7c..1ed75eef2f9 100644
> --- a/gdb/progspace.c
> +++ b/gdb/progspace.c
> @@ -139,19 +139,19 @@ program_space::free_all_objfiles ()
>
>  void
>  program_space::add_objfile (std::unique_ptr<objfile> &&objfile,
> -                           struct objfile *before)
> +                           struct objfile *after)
>  {
> -  if (before == nullptr)
> +  if (after == nullptr)
>      objfiles_list.push_back (std::move (objfile));
>    else
>      {
>        auto iter = std::find_if (objfiles_list.begin (), objfiles_list.end (),
>                                 [=] (const std::unique_ptr<::objfile> &objf)
>                                 {
> -                                 return objf.get () == before;
> +                                 return objf.get () == after;
>                                 });
>        gdb_assert (iter != objfiles_list.end ());
> -      objfiles_list.insert (iter, std::move (objfile));
> +      objfiles_list.insert (++iter, std::move (objfile));
>      }
>  }
>
> diff --git a/gdb/progspace.h b/gdb/progspace.h
> index 85215f0e2f1..6e33e48c88e 100644
> --- a/gdb/progspace.h
> +++ b/gdb/progspace.h
> @@ -40,56 +40,141 @@ struct address_space;
>  struct program_space;
>  struct so_list;
>
> +/* An iterator that wraps an iterator over std::unique_ptr, and dereferences
> +   the returned object.  This is useful for iterating over a list of shared
> +   pointers and returning raw pointers -- which helped avoid touching a lot
> +   of code when changing how objfiles are managed.  */
> +
> +template<typename UniquePtrIter>
> +class unwrapping_iterator
> +{
> +public:
> +  typedef unwrapping_iterator self_type;
> +  typedef typename UniquePtrIter::value_type::pointer value_type;
> +  typedef typename UniquePtrIter::reference  reference;
> +  typedef typename UniquePtrIter::pointer pointer;
> +  typedef typename UniquePtrIter::iterator_category iterator_category;
> +  typedef typename UniquePtrIter::difference_type difference_type;
> +
> +  unwrapping_iterator (UniquePtrIter iter)
> +    : m_iter (std::move (iter))
> +  {
> +  }
> +
> +  value_type operator* () const
> +  {
> +    return m_iter->get ();
> +  }
> +
> +  unwrapping_iterator operator++ ()
> +  {
> +    ++m_iter;
> +    return *this;
> +  }
> +
> +  bool operator!= (const unwrapping_iterator &other) const
> +  {
> +    return m_iter != other.m_iter;
> +  }
> +
> +private:
> +  /* The underlying iterator.  */
> +  UniquePtrIter m_iter;
> +};
> +
>  typedef std::list<std::unique_ptr<objfile>> objfile_list;
>
> -/* An iterator that wraps an iterator over std::unique_ptr<objfile>,
> -   and dereferences the returned object.  This is useful for iterating
> -   over a list of shared pointers and returning raw pointers -- which
> -   helped avoid touching a lot of code when changing how objfiles are
> -   managed.  */
> +/* An reverse iterator that wraps an iterator over objfile_list, and
> +   dereferences the returned object.  This is useful for reverse iterating
> +   over a list of shared pointers and returning raw pointers -- which helped
> +   avoid touching a lot of code when changing how objfiles are managed.  */
>
> -class unwrapping_objfile_iterator
> +class unwrapping_reverse_objfile_iterator
>  {
>  public:
> -
> -  typedef unwrapping_objfile_iterator self_type;
> +  typedef unwrapping_reverse_objfile_iterator self_type;
>    typedef typename ::objfile *value_type;
>    typedef typename ::objfile &reference;
>    typedef typename ::objfile **pointer;
>    typedef typename objfile_list::iterator::iterator_category iterator_category;
>    typedef typename objfile_list::iterator::difference_type difference_type;
>
> -  unwrapping_objfile_iterator (objfile_list::iterator iter)
> -    : m_iter (std::move (iter))
> -  {
> -  }
> -
> -  objfile *operator* () const
> +  value_type operator* () const
>    {
>      return m_iter->get ();
>    }
>
> -  unwrapping_objfile_iterator operator++ ()
> +  unwrapping_reverse_objfile_iterator operator++ ()
>    {
> -    ++m_iter;
> +    if (m_iter != m_begin)
> +      --m_iter;
> +    else
> +      {
> +       /* We can't decrement M_ITER since it is the begin iterator of the
> +          objfile list.  Set M_ITER to the list's end iterator to indicate
> +          this is now one-past-the-end.  */
> +       m_iter = m_end;
> +
> +       /* Overwrite M_BEGIN to avoid possibly copying an invalid iterator.  */
> +       m_begin = m_end;
> +      }
> +
>      return *this;
>    }
>
> -  bool operator!= (const unwrapping_objfile_iterator &other) const
> +  bool operator!= (const unwrapping_reverse_objfile_iterator &other) const
>    {
>      return m_iter != other.m_iter;
>    }
>
> +  /* Return an unwrapping reverse iterator starting at the last element of
> +     OBJF_LIST.  */
> +  static unwrapping_reverse_objfile_iterator begin (objfile_list &objf_list)
> +  {
> +    auto begin = objf_list.begin ();
> +    auto end = objf_list.end ();
> +    auto rev_begin = objf_list.end ();
> +
> +    /* Start REV_BEGIN on the last objfile in OBJF_LIST.  */
> +    if (begin != end)
> +      --rev_begin;
> +
> +    return unwrapping_reverse_objfile_iterator (rev_begin, begin, end);
> +  }
> +
> +  /* Return a one-past-the-end unwrapping reverse iterator.  */
> +  static unwrapping_reverse_objfile_iterator end (objfile_list &objf_list)
> +  {
> +    return unwrapping_reverse_objfile_iterator (objf_list.end (),
> +                                               objf_list.end (),
> +                                               objf_list.end ());
> +  }
> +
>  private:
> +  /* begin and end methods should be used to create these objects.  */
> +  unwrapping_reverse_objfile_iterator (objfile_list::iterator iter,
> +                                      objfile_list::iterator begin,
> +                                      objfile_list::iterator end)
> +    : m_iter (std::move (iter)), m_begin (std::move (begin)),
> +      m_end (std::move (end))
> +  {
> +  }
>
> -  /* The underlying iterator.  */
> -  objfile_list::iterator m_iter;
> -};
> + /* The underlying iterator.  */
> + objfile_list::iterator m_iter;
>
> + /* The underlying iterator pointing to the first objfile in the sequence.  Used
> +    to track when to stop decrementing M_ITER.  */
> + objfile_list::iterator m_begin;
>
> -/* A range that returns unwrapping_objfile_iterators.  */
> +  /* The underlying iterator's one-past-the-end.  */
> + objfile_list::iterator m_end;
> +};
>
> -using unwrapping_objfile_range = iterator_range<unwrapping_objfile_iterator>;
> +/* A range that returns unwrapping_iterators.  */
> +
> +using unwrapping_objfile_range
> +  = iterator_range<unwrapping_iterator<objfile_list::iterator>>;
>
>  /* A program space represents a symbolic view of an address space.
>     Roughly speaking, it holds all the data associated with a
> @@ -209,11 +294,12 @@ struct program_space
>    objfiles_range objfiles ()
>    {
>      return objfiles_range
> -      (unwrapping_objfile_iterator (objfiles_list.begin ()),
> -       unwrapping_objfile_iterator (objfiles_list.end ()));
> +      (unwrapping_iterator<objfile_list::iterator> (objfiles_list.begin ()),
> +       unwrapping_iterator<objfile_list::iterator> (objfiles_list.end ()));
>    }
>
> -  using objfiles_safe_range = basic_safe_range<objfiles_range>;
> +  using objfiles_reverse_range = iterator_range<unwrapping_reverse_objfile_iterator>;
> +  using objfiles_safe_reverse_range = basic_safe_range<objfiles_reverse_range>;
>
>    /* An iterable object that can be used to iterate over all objfiles.
>       The basic use is in a foreach, like:
> @@ -221,20 +307,25 @@ struct program_space
>       for (objfile *objf : pspace->objfiles_safe ()) { ... }
>
>       This variant uses a basic_safe_iterator so that objfiles can be
> -     deleted during iteration.  */
> -  objfiles_safe_range objfiles_safe ()
> +     deleted during iteration.
> +
> +     The use of a reverse iterator helps ensure that separate debug
> +     objfiles are deleted before their parent objfile.  This prevents
> +     the invalidation of an iterator due to the deletion of a parent
> +     objfile.  */
> +  objfiles_safe_reverse_range objfiles_safe ()
>    {
> -    return objfiles_safe_range
> -      (objfiles_range
> -        (unwrapping_objfile_iterator (objfiles_list.begin ()),
> -         unwrapping_objfile_iterator (objfiles_list.end ())));
> +    return objfiles_safe_reverse_range
> +      (objfiles_reverse_range
> +       (unwrapping_reverse_objfile_iterator::begin (objfiles_list),
> +        unwrapping_reverse_objfile_iterator::end (objfiles_list)));
>    }
>
> -  /* Add OBJFILE to the list of objfiles, putting it just before
> -     BEFORE.  If BEFORE is nullptr, it will go at the end of the
> +  /* Add OBJFILE to the list of objfiles, putting it just after
> +     AFTER.  If AFTER is nullptr, it will go at the end of the
>       list.  */
>    void add_objfile (std::unique_ptr<objfile> &&objfile,
> -                   struct objfile *before);
> +                   struct objfile *after);
>
>    /* Remove OBJFILE from the list of objfiles.  */
>    void remove_objfile (struct objfile *objfile);
> diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
> index 9db5c47a8ce..784b81b5ca6 100644
> --- a/gdb/symfile-debug.c
> +++ b/gdb/symfile-debug.c
> @@ -109,9 +109,9 @@ objfile::has_unexpanded_symtabs ()
>                 objfile_debug_name (this));
>
>    bool result = false;
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      if (iter->has_unexpanded_symtabs (this))
> +      if (qf->has_unexpanded_symtabs (this))
>         {
>           result = true;
>           break;
> @@ -134,9 +134,9 @@ objfile::find_last_source_symtab ()
>      gdb_printf (gdb_stdlog, "qf->find_last_source_symtab (%s)\n",
>                 objfile_debug_name (this));
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      retval = iter->find_last_source_symtab (this);
> +      retval = qf->find_last_source_symtab (this);
>        if (retval != nullptr)
>         break;
>      }
> @@ -167,8 +167,8 @@ objfile::forget_cached_source_info ()
>         }
>      }
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->forget_cached_source_info (this);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->forget_cached_source_info (this);
>  }
>
>  bool
> @@ -214,17 +214,17 @@ objfile::map_symtabs_matching_filename
>      return result;
>    };
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      if (!iter->expand_symtabs_matching (this,
> -                                         match_one_filename,
> -                                         nullptr,
> -                                         nullptr,
> -                                         on_expansion,
> -                                         (SEARCH_GLOBAL_BLOCK
> -                                          | SEARCH_STATIC_BLOCK),
> -                                         UNDEF_DOMAIN,
> -                                         ALL_DOMAIN))
> +      if (!qf->expand_symtabs_matching (this,
> +                                       match_one_filename,
> +                                       nullptr,
> +                                       nullptr,
> +                                       on_expansion,
> +                                       (SEARCH_GLOBAL_BLOCK
> +                                        | SEARCH_STATIC_BLOCK),
> +                                       UNDEF_DOMAIN,
> +                                       ALL_DOMAIN))
>         {
>           retval = false;
>           break;
> @@ -283,18 +283,18 @@ objfile::lookup_symbol (block_enum kind, const char *name, domain_enum domain)
>      return true;
>    };
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      if (!iter->expand_symtabs_matching (this,
> -                                         nullptr,
> -                                         &lookup_name,
> -                                         nullptr,
> -                                         search_one_symtab,
> -                                         kind == GLOBAL_BLOCK
> -                                         ? SEARCH_GLOBAL_BLOCK
> -                                         : SEARCH_STATIC_BLOCK,
> -                                         domain,
> -                                         ALL_DOMAIN))
> +      if (!qf->expand_symtabs_matching (this,
> +                                       nullptr,
> +                                       &lookup_name,
> +                                       nullptr,
> +                                       search_one_symtab,
> +                                       kind == GLOBAL_BLOCK
> +                                       ? SEARCH_GLOBAL_BLOCK
> +                                       : SEARCH_STATIC_BLOCK,
> +                                       domain,
> +                                       ALL_DOMAIN))
>         break;
>      }
>
> @@ -314,8 +314,8 @@ objfile::print_stats (bool print_bcache)
>      gdb_printf (gdb_stdlog, "qf->print_stats (%s, %d)\n",
>                 objfile_debug_name (this), print_bcache);
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->print_stats (this, print_bcache);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->print_stats (this, print_bcache);
>  }
>
>  void
> @@ -340,16 +340,16 @@ objfile::expand_symtabs_for_function (const char *func_name)
>    lookup_name_info base_lookup (func_name, symbol_name_match_type::FULL);
>    lookup_name_info lookup_name = base_lookup.make_ignore_params ();
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->expand_symtabs_matching (this,
> -                                  nullptr,
> -                                  &lookup_name,
> -                                  nullptr,
> -                                  nullptr,
> -                                  (SEARCH_GLOBAL_BLOCK
> -                                   | SEARCH_STATIC_BLOCK),
> -                                  VAR_DOMAIN,
> -                                  ALL_DOMAIN);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->expand_symtabs_matching (this,
> +                                nullptr,
> +                                &lookup_name,
> +                                nullptr,
> +                                nullptr,
> +                                (SEARCH_GLOBAL_BLOCK
> +                                 | SEARCH_STATIC_BLOCK),
> +                                VAR_DOMAIN,
> +                                ALL_DOMAIN);
>  }
>
>  void
> @@ -359,8 +359,8 @@ objfile::expand_all_symtabs ()
>      gdb_printf (gdb_stdlog, "qf->expand_all_symtabs (%s)\n",
>                 objfile_debug_name (this));
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->expand_all_symtabs (this);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->expand_all_symtabs (this);
>  }
>
>  void
> @@ -377,16 +377,16 @@ objfile::expand_symtabs_with_fullname (const char *fullname)
>      return filename_cmp (basenames ? basename : fullname, filename) == 0;
>    };
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->expand_symtabs_matching (this,
> -                                  file_matcher,
> -                                  nullptr,
> -                                  nullptr,
> -                                  nullptr,
> -                                  (SEARCH_GLOBAL_BLOCK
> -                                   | SEARCH_STATIC_BLOCK),
> -                                  UNDEF_DOMAIN,
> -                                  ALL_DOMAIN);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->expand_symtabs_matching (this,
> +                                file_matcher,
> +                                nullptr,
> +                                nullptr,
> +                                nullptr,
> +                                (SEARCH_GLOBAL_BLOCK
> +                                 | SEARCH_STATIC_BLOCK),
> +                                UNDEF_DOMAIN,
> +                                ALL_DOMAIN);
>  }
>
>  void
> @@ -402,9 +402,9 @@ objfile::expand_matching_symbols
>                 domain_name (domain), global,
>                 host_address_to_string (ordered_compare));
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->expand_matching_symbols (this, name, domain, global,
> -                                  ordered_compare);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->expand_matching_symbols (this, name, domain, global,
> +                                ordered_compare);
>  }
>
>  bool
> @@ -429,10 +429,10 @@ objfile::expand_symtabs_matching
>                 host_address_to_string (&expansion_notify),
>                 search_domain_name (kind));
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    if (!iter->expand_symtabs_matching (this, file_matcher, lookup_name,
> -                                       symbol_matcher, expansion_notify,
> -                                       search_flags, domain, kind))
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    if (!qf->expand_symtabs_matching (this, file_matcher, lookup_name,
> +                                     symbol_matcher, expansion_notify,
> +                                     search_flags, domain, kind))
>        return false;
>    return true;
>  }
> @@ -454,10 +454,10 @@ objfile::find_pc_sect_compunit_symtab (struct bound_minimal_symbol msymbol,
>                 host_address_to_string (section),
>                 warn_if_readin);
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      retval = iter->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
> -                                                  warn_if_readin);
> +      retval = qf->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
> +                                                warn_if_readin);
>        if (retval != nullptr)
>         break;
>      }
> @@ -482,8 +482,8 @@ objfile::map_symbol_filenames (gdb::function_view<symbol_filename_ftype> fun,
>                 objfile_debug_name (this),
>                 need_fullname);
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->map_symbol_filenames (this, fun, need_fullname);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->map_symbol_filenames (this, fun, need_fullname);
>  }
>
>  struct compunit_symtab *
> @@ -496,9 +496,9 @@ objfile::find_compunit_symtab_by_address (CORE_ADDR address)
>                 hex_string (address));
>
>    struct compunit_symtab *result = NULL;
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      result = iter->find_compunit_symtab_by_address (this, address);
> +      result = qf->find_compunit_symtab_by_address (this, address);
>        if (result != nullptr)
>         break;
>      }
> @@ -521,10 +521,10 @@ objfile::lookup_global_symbol_language (const char *name,
>    enum language result = language_unknown;
>    *symbol_found_p = false;
>
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      result = iter->lookup_global_symbol_language (this, name, domain,
> -                                                   symbol_found_p);
> +      result = qf->lookup_global_symbol_language (this, name, domain,
> +                                                 symbol_found_p);
>        if (*symbol_found_p)
>         break;
>      }
> diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp
> index 61b9942de79..0bf49976b73 100644
> --- a/gdb/testsuite/gdb.python/py-objfile.exp
> +++ b/gdb/testsuite/gdb.python/py-objfile.exp
> @@ -135,7 +135,7 @@ gdb_test "p main" "= {<text variable, no debug info>} $hex <main>" \
>  gdb_py_test_silent_cmd "python objfile.add_separate_debug_file(\"${binfile}\")" \
>      "Add separate debug file file" 1
>
> -gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[0\]" \
> +gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[1\]" \
>      "Get separate debug info objfile" 1
>
>  gdb_test "python print (sep_objfile.owner.filename)" "${testfile}2" \
> --
> 2.40.1
>


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading
  2023-06-01  1:43 ` [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading Aaron Merey
@ 2023-06-15 13:44   ` Aaron Merey
  2023-07-03 17:39     ` [PING*2][PATCH " Aaron Merey
  2023-07-07 14:18   ` [PATCH " Andrew Burgess
  2023-07-27 10:24   ` [PATCH " Andrew Burgess
  2 siblings, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-06-15 13:44 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess

Ping

Thanks,
Aaron

On Wed, May 31, 2023 at 9:44 PM Aaron Merey <amerey@redhat.com> wrote:
>
> v2.2: https://sourceware.org/pipermail/gdb-patches/2023-May/199326.html
>
> v3 includes testcases instead of adding them in a separate patch.
> Tests related to section downloading are now included in
> testsuite/gdb.debuginfod/section.exp.
>
> Commit message:
>
> At the beginning of a session, gdb may attempt to download debuginfo
> for all shared libraries associated with the process or core file
> being debugged.  This can be a waste of time and storage space when much
> of the debuginfo ends up not being used during the session.
>
> To reduce the gdb's startup latency and to download only the debuginfo
> that is really needed, this patch adds on-demand downloading of debuginfo.
>
> 'set debuginfo enabled on' now causes gdb to attempt to download a .gdb_index
> for each shared library instead of its full debuginfo.  Each corresponding
> separate debuginfo will be deferred until gdb needs to expand symtabs
> associated with the debuginfo's index.
>
> Because these indices are significantly smaller than their corresponding
> debuginfo, this generally reduces the total amount of data gdb downloads.
> Reductions of 80%-95% have been observed when debugging large GUI programs.
>
>     (gdb) set debuginfod enabled on
>     (gdb) start
>     Downloading section .gdb_index for /lib64/libcurl.so.4
>     [...]
>     1826        client->server_mhandle = curl_multi_init ();
>     (gdb) step
>     Downloading separate debug info for /lib64/libcurl.so.4
>     Downloading separate debug info for [libcurl dwz]
>     Downloading source file /usr/src/debug/curl-7.85.0-6.fc37.x86_64/build-full/lib/../../lib/multi.c
>     curl_multi_init () at ../../lib/multi.c:457
>     457     {
>     (gdb)
>
> Some of the key functions below include dwarf2_has_separate_index which
> downloads the separate .gdb_index.  If successful, the shared library
> objfile owns the index until the separate debug objfile is downloaded
> or confirmed to not be available.
>
> read_full_dwarf_from_debuginfod downloads the full debuginfo and
> initializes the separate debug objfile.  It is called by functions
> such as dwarf2_gdb_index::expand_symtabs_matching and
> dwarf2_base_index_functions::find_pc_sect_compunit_symtab when symtab
> expansion is required.
> ---
>  gdb/dwarf2/frame.c                         |  13 ++
>  gdb/dwarf2/frame.h                         |   4 +
>  gdb/dwarf2/index-cache.c                   |  33 ++++
>  gdb/dwarf2/index-cache.h                   |  13 ++
>  gdb/dwarf2/public.h                        |   7 +
>  gdb/dwarf2/read-gdb-index.c                | 156 ++++++++++++++++--
>  gdb/dwarf2/read.c                          | 146 ++++++++++++++++-
>  gdb/dwarf2/read.h                          |  10 ++
>  gdb/dwarf2/section.c                       |   3 +-
>  gdb/elfread.c                              |   2 +-
>  gdb/objfile-flags.h                        |   4 +
>  gdb/objfiles.h                             |  17 +-
>  gdb/quick-symbol.h                         |   4 +
>  gdb/symfile.c                              |  13 +-
>  gdb/symtab.c                               |  18 +-
>  gdb/testsuite/gdb.debuginfod/libsection1.c |  40 +++++
>  gdb/testsuite/gdb.debuginfod/libsection2.c |  37 +++++
>  gdb/testsuite/gdb.debuginfod/section.c     |  29 ++++
>  gdb/testsuite/gdb.debuginfod/section.exp   | 181 +++++++++++++++++++++
>  gdb/testsuite/lib/debuginfod-support.exp   |  30 ++++
>  20 files changed, 734 insertions(+), 26 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.debuginfod/libsection1.c
>  create mode 100644 gdb/testsuite/gdb.debuginfod/libsection2.c
>  create mode 100644 gdb/testsuite/gdb.debuginfod/section.c
>  create mode 100644 gdb/testsuite/gdb.debuginfod/section.exp
>
> diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
> index a561aaf3100..3613f8252a7 100644
> --- a/gdb/dwarf2/frame.c
> +++ b/gdb/dwarf2/frame.c
> @@ -1609,6 +1609,19 @@ set_comp_unit (struct objfile *objfile, struct comp_unit *unit)
>    return dwarf2_frame_bfd_data.set (abfd, unit);
>  }
>
> +/* See frame.h.  */
> +
> +void
> +dwarf2_clear_frame_data (struct objfile *objfile)
> +{
> +  bfd *abfd = objfile->obfd.get ();
> +
> +  if (gdb_bfd_requires_relocations (abfd))
> +    dwarf2_frame_objfile_data.clear (objfile);
> +  else
> +    dwarf2_frame_bfd_data.clear (abfd);
> +}
> +
>  /* Find the FDE for *PC.  Return a pointer to the FDE, and store the
>     initial location associated with it into *PC.  */
>
> diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
> index 5643e557513..2391e313e7c 100644
> --- a/gdb/dwarf2/frame.h
> +++ b/gdb/dwarf2/frame.h
> @@ -238,6 +238,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
>  extern const struct frame_base *
>    dwarf2_frame_base_sniffer (frame_info_ptr this_frame);
>
> +/* Delete OBJFILEs comp_unit.  */
> +
> +extern void dwarf2_clear_frame_data (struct objfile * objfile);
> +
>  /* Compute the DWARF CFA for a frame.  */
>
>  CORE_ADDR dwarf2_frame_cfa (frame_info_ptr this_frame);
> diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
> index 79ab706ee9d..bbafcd321b2 100644
> --- a/gdb/dwarf2/index-cache.c
> +++ b/gdb/dwarf2/index-cache.c
> @@ -216,6 +216,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
>    return {};
>  }
>
> +/* See index-cache.h.  */
> +
> +gdb::array_view<const gdb_byte>
> +index_cache::lookup_gdb_index_debuginfod (const char *index_path,
> +                                         std::unique_ptr<index_cache_resource> *resource)
> +{
> +  try
> +    {
> +      /* Try to map that file.  */
> +      index_cache_resource_mmap *mmap_resource
> +       = new index_cache_resource_mmap (index_path);
> +
> +      /* Hand the resource to the caller.  */
> +      resource->reset (mmap_resource);
> +
> +      return gdb::array_view<const gdb_byte>
> +         ((const gdb_byte *) mmap_resource->mapping.get (),
> +          mmap_resource->mapping.size ());
> +    }
> +  catch (const gdb_exception_error &except)
> +    {
> +      warning (_("Unable to read %s: %s"), index_path, except.what ());
> +    }
> +
> +  return {};
> +}
> +
>  #else /* !HAVE_SYS_MMAN_H */
>
>  /* See dwarf-index-cache.h.  This is a no-op on unsupported systems.  */
> @@ -227,6 +254,12 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
>    return {};
>  }
>
> +gdb::array_view<const gdb_byte>
> +index_cache::lookup_gdb_index_debuginfod (const char *index_path,
> +                                         std::unique_ptr<index_cache_resource> *resource)
> +{
> +  return {};
> +}
>  #endif
>
>  /* See dwarf-index-cache.h.  */
> diff --git a/gdb/dwarf2/index-cache.h b/gdb/dwarf2/index-cache.h
> index 1efff17049f..e400afd5123 100644
> --- a/gdb/dwarf2/index-cache.h
> +++ b/gdb/dwarf2/index-cache.h
> @@ -67,6 +67,19 @@ class index_cache
>    lookup_gdb_index (const bfd_build_id *build_id,
>                     std::unique_ptr<index_cache_resource> *resource);
>
> +  /* Look for an index file located at INDEX_PATH in the debuginfod cache.
> +     Unlike lookup_gdb_index, this function does not exit early if the
> +     index cache has not been enabled.
> +
> +     If found, return the contents as an array_view and store the underlying
> +     resources (allocated memory, mapped file, etc) in RESOURCE.  The returned
> +     array_view is valid as long as RESOURCE is not destroyed.
> +
> +     If no matching index file is found, return an empty array view.  */
> +  gdb::array_view<const gdb_byte>
> +  lookup_gdb_index_debuginfod (const char *index_path,
> +                              std::unique_ptr<index_cache_resource> *resource);
> +
>    /* Return the number of cache hits.  */
>    unsigned int n_hits () const
>    { return m_n_hits; }
> diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h
> index 0e74857eb1a..4a44cdbc223 100644
> --- a/gdb/dwarf2/public.h
> +++ b/gdb/dwarf2/public.h
> @@ -40,4 +40,11 @@ extern void dwarf2_initialize_objfile (struct objfile *objfile);
>
>  extern void dwarf2_build_frame_info (struct objfile *);
>
> +/* Query debuginfod for the .gdb_index associated with OBJFILE.  If
> +   successful, create an objfile to hold the .gdb_index information
> +   and act as a placeholder until the full debuginfo needs to be
> +   downloaded.  */
> +
> +extern bool dwarf2_has_separate_index (struct objfile *);
> +
>  #endif /* DWARF2_PUBLIC_H */
> diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> index 1006386cb2d..d3516e92361 100644
> --- a/gdb/dwarf2/read-gdb-index.c
> +++ b/gdb/dwarf2/read-gdb-index.c
> @@ -136,6 +136,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       gdb.dwarf2/gdb-index.exp testcase.  */
>    void dump (struct objfile *objfile) override;
>
> +  /* Calls do_expand_matching_symbols and downloads debuginfo if necessary.  */
>    void expand_matching_symbols
>      (struct objfile *,
>       const lookup_name_info &lookup_name,
> @@ -143,6 +144,14 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       int global,
>       symbol_compare_ftype *ordered_compare) override;
>
> +  void do_expand_matching_symbols
> +    (struct objfile *,
> +     const lookup_name_info &lookup_name,
> +     domain_enum domain,
> +     int global,
> +     symbol_compare_ftype *ordered_compare);
> +
> +  /* Calls do_expand_symtabs_matching and downloads debuginfo if necessary.  */
>    bool expand_symtabs_matching
>      (struct objfile *objfile,
>       gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> @@ -152,8 +161,59 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       block_search_flags search_flags,
>       domain_enum domain,
>       enum search_domain kind) override;
> +
> +  bool do_expand_symtabs_matching
> +    (struct objfile *objfile,
> +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> +     const lookup_name_info *lookup_name,
> +     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> +     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> +     block_search_flags search_flags,
> +     domain_enum domain,
> +     enum search_domain kind);
> +
> +  /* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
> +     debuginfo if necessary.  */
> +  void expand_all_symtabs (struct objfile *objfile) override;
> +
> +  /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> +     debuginfo if necessary.  */
> +  struct symtab *find_last_source_symtab (struct objfile *objfile) override;
>  };
>
> +void
> +dwarf2_gdb_index::expand_all_symtabs (struct objfile *objfile)
> +{
> +  try
> +    {
> +      dwarf2_base_index_functions::expand_all_symtabs (objfile);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +       exception_print (gdb_stderr, e);
> +      else
> +       read_full_dwarf_from_debuginfod (objfile, this);
> +    }
> +}
> +
> +struct symtab *
> +dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
> +{
> +  try
> +    {
> +      return dwarf2_base_index_functions::find_last_source_symtab (objfile);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +       exception_print (gdb_stderr, e);
> +      else
> +       read_full_dwarf_from_debuginfod (objfile, this);
> +      return nullptr;
> +    }
> +}
> +
>  /* This dumps minimal information about the index.
>     It is called via "mt print objfiles".
>     One use is to verify .gdb_index has been loaded by the
> @@ -315,7 +375,7 @@ dw2_symtab_iter_next (struct dw2_symtab_iterator *iter,
>  }
>
>  void
> -dwarf2_gdb_index::expand_matching_symbols
> +dwarf2_gdb_index::do_expand_matching_symbols
>    (struct objfile *objfile,
>     const lookup_name_info &name, domain_enum domain,
>     int global,
> @@ -353,6 +413,29 @@ dwarf2_gdb_index::expand_matching_symbols
>      }, per_objfile);
>  }
>
> +void
> +dwarf2_gdb_index::expand_matching_symbols
> +  (struct objfile *objfile,
> +   const lookup_name_info &lookup_name,
> +   domain_enum domain,
> +   int global,
> +   symbol_compare_ftype *ordered_compare)
> +{
> +  try
> +    {
> +      do_expand_matching_symbols (objfile, lookup_name, domain,
> +                                 global, ordered_compare);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +       exception_print (gdb_stderr, e);
> +      else
> +       read_full_dwarf_from_debuginfod (objfile, this);
> +      return;
> +    }
> +}
> +
>  /* Helper for dw2_expand_matching symtabs.  Called on each symbol
>     matched, to expand corresponding CUs that were marked.  IDX is the
>     index of the symbol name that matched.  */
> @@ -455,7 +538,7 @@ dw2_expand_marked_cus
>  }
>
>  bool
> -dwarf2_gdb_index::expand_symtabs_matching
> +dwarf2_gdb_index::do_expand_symtabs_matching
>      (struct objfile *objfile,
>       gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
>       const lookup_name_info *lookup_name,
> @@ -504,6 +587,39 @@ dwarf2_gdb_index::expand_symtabs_matching
>    return result;
>  }
>
> +bool
> +dwarf2_gdb_index::expand_symtabs_matching
> +    (struct objfile *objfile,
> +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> +     const lookup_name_info *lookup_name,
> +     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> +     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> +     block_search_flags search_flags,
> +     domain_enum domain,
> +     enum search_domain kind)
> +{
> +  if (objfile->flags & OBJF_READNEVER)
> +    return false;
> +
> +  try
> +    {
> +      return do_expand_symtabs_matching (objfile, file_matcher, lookup_name,
> +                                        symbol_matcher, expansion_notify,
> +                                        search_flags, domain, kind);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +       {
> +         exception_print (gdb_stderr, e);
> +         return false;
> +       }
> +
> +      read_full_dwarf_from_debuginfod (objfile, this);
> +      return true;
> +    }
> +}
> +
>  quick_symbol_functions_up
>  mapped_gdb_index::make_quick_functions () const
>  {
> @@ -797,28 +913,32 @@ dwarf2_read_gdb_index
>
>    /* If there is a .dwz file, read it so we can get its CU list as
>       well.  */
> -  dwz = dwarf2_get_dwz_file (per_bfd);
> -  if (dwz != NULL)
> +  if (get_gdb_index_contents_dwz != nullptr)
>      {
>        mapped_gdb_index dwz_map;
>        const gdb_byte *dwz_types_ignore;
>        offset_type dwz_types_elements_ignore;
> +      dwz = dwarf2_get_dwz_file (per_bfd);
>
> -      gdb::array_view<const gdb_byte> dwz_index_content
> -       = get_gdb_index_contents_dwz (objfile, dwz);
> -
> -      if (dwz_index_content.empty ())
> -       return 0;
> -
> -      if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()),
> -                                      1, dwz_index_content, &dwz_map,
> -                                      &dwz_list, &dwz_list_elements,
> -                                      &dwz_types_ignore,
> -                                      &dwz_types_elements_ignore))
> +      if (dwz != nullptr)
>         {
> -         warning (_("could not read '.gdb_index' section from %s; skipping"),
> -                  bfd_get_filename (dwz->dwz_bfd.get ()));
> -         return 0;
> +         gdb::array_view<const gdb_byte> dwz_index_content
> +           = get_gdb_index_contents_dwz (objfile, dwz);
> +
> +         if (dwz_index_content.empty ())
> +           return 0;
> +
> +         if (!read_gdb_index_from_buffer (bfd_get_filename
> +                                            (dwz->dwz_bfd.get ()),
> +                                          1, dwz_index_content, &dwz_map,
> +                                          &dwz_list, &dwz_list_elements,
> +                                          &dwz_types_ignore,
> +                                          &dwz_types_elements_ignore))
> +           {
> +             warning (_("could not read '.gdb_index' section from %s; skipping"),
> +                      bfd_get_filename (dwz->dwz_bfd.get ()));
> +               return 0;
> +           }
>         }
>      }
>
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index 4828409222c..96d1ff53d91 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -34,6 +34,7 @@
>  #include "dwarf2/attribute.h"
>  #include "dwarf2/comp-unit-head.h"
>  #include "dwarf2/cu.h"
> +#include "dwarf2/frame.h"
>  #include "dwarf2/index-cache.h"
>  #include "dwarf2/index-common.h"
>  #include "dwarf2/leb.h"
> @@ -95,6 +96,8 @@
>  #include "split-name.h"
>  #include "gdbsupport/parallel-for.h"
>  #include "gdbsupport/thread-pool.h"
> +#include "inferior.h"
> +#include "debuginfod-support.h"
>
>  /* When == 1, print basic high level tracing messages.
>     When > 1, be more verbose.
> @@ -3168,7 +3171,7 @@ dwarf2_base_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd,
>  }
>
>  struct compunit_symtab *
> -dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> +dwarf2_base_index_functions::do_find_pc_sect_compunit_symtab
>       (struct objfile *objfile,
>        struct bound_minimal_symbol msymbol,
>        CORE_ADDR pc,
> @@ -3199,6 +3202,32 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
>    return result;
>  }
>
> +struct compunit_symtab *
> +dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> +     (struct objfile *objfile,
> +      struct bound_minimal_symbol msymbol,
> +      CORE_ADDR pc,
> +      struct obj_section *section,
> +      int warn_if_readin)
> +{
> +  if (objfile->flags & OBJF_READNEVER)
> +    return nullptr;
> +
> +  try
> +    {
> +      return do_find_pc_sect_compunit_symtab (objfile, msymbol, pc,
> +                                             section, warn_if_readin);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +       exception_print (gdb_stderr, e);
> +      else
> +       read_full_dwarf_from_debuginfod (objfile, this);
> +      return nullptr;
> +    }
> +}
> +
>  void
>  dwarf2_base_index_functions::map_symbol_filenames
>       (struct objfile *objfile,
> @@ -3355,6 +3384,29 @@ get_gdb_index_contents_from_cache_dwz (objfile *obj, dwz_file *dwz)
>    return global_index_cache.lookup_gdb_index (build_id, &dwz->index_cache_res);
>  }
>
> +/* Query debuginfod for the .gdb_index matching OBJFILE's build-id.  Return the
> +   contents if successful.  */
> +
> +static gdb::array_view<const gdb_byte>
> +get_gdb_index_contents_from_debuginfod (objfile *objfile, dwarf2_per_bfd *per_bfd)
> +{
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  if (build_id == nullptr)
> +    return {};
> +
> +  gdb::unique_xmalloc_ptr<char> index_path;
> +  scoped_fd fd = debuginfod_section_query (build_id->data, build_id->size,
> +                                          bfd_get_filename
> +                                            (objfile->obfd.get ()),
> +                                          ".gdb_index",
> +                                          &index_path);
> +  if (fd.get () < 0)
> +    return {};
> +
> +  return global_index_cache.lookup_gdb_index_debuginfod
> +    (index_path.get (), &per_bfd->index_cache_res);
> +}
> +
>  static quick_symbol_functions_up make_cooked_index_funcs ();
>
>  /* See dwarf2/public.h.  */
> @@ -3420,10 +3472,102 @@ dwarf2_initialize_objfile (struct objfile *objfile)
>        return;
>      }
>
> +  if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED)
> +      && dwarf2_read_gdb_index (per_objfile,
> +                               get_gdb_index_contents_from_debuginfod,
> +                               nullptr))
> +    {
> +      dwarf_read_debug_printf ("found .gdb_index from debuginfod");
> +      objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
> +      objfile->qf.begin ()->get ()->from_separate_index = true;
> +      return;
> +    }
> +
>    global_index_cache.miss ();
>    objfile->qf.push_front (make_cooked_index_funcs ());
>  }
>
> +/* See read.h.  */
> +
> +void
> +read_full_dwarf_from_debuginfod (struct objfile *objfile,
> +                                dwarf2_base_index_functions *fncs)
> +{
> +  gdb_assert (objfile->flags & OBJF_DOWNLOAD_DEFERRED);
> +
> +  const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  const char *filename;
> +  gdb_bfd_ref_ptr debug_bfd;
> +  gdb::unique_xmalloc_ptr<char> symfile_path;
> +  scoped_fd fd;
> +
> +  if (build_id == nullptr)
> +    goto unset;
> +
> +  filename = bfd_get_filename (objfile->obfd.get ());
> +  fd = debuginfod_debuginfo_query (build_id->data, build_id->size,
> +                                  filename, &symfile_path);
> +  if (fd.get () < 0)
> +    goto unset;
> +
> +  /* Separate debuginfo successfully retrieved from server.  */
> +  debug_bfd = symfile_bfd_open (symfile_path.get ());
> +  if (debug_bfd == nullptr
> +      || !build_id_verify (debug_bfd.get (), build_id->size, build_id->data))
> +    {
> +      warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
> +              filename);
> +      goto unset;
> +    }
> +
> +  /* Clear frame data so it can be recalculated using DWARF.  */
> +  dwarf2_clear_frame_data (objfile);
> +
> +  /* This may also trigger a dwz download.  */
> +  symbol_file_add_separate (debug_bfd, symfile_path.get (),
> +                           current_inferior ()->symfile_flags, objfile);
> +
> +unset:
> +  objfile->remove_deferred_status ();
> +}
> +
> +/* See public.h.  */
> +
> +bool
> +dwarf2_has_separate_index (struct objfile *objfile)
> +{
> +  if (objfile->flags & OBJF_DOWNLOAD_DEFERRED)
> +    return true;
> +  if (objfile->flags & OBJF_MAINLINE)
> +    return false;
> +  if (!IS_DIR_SEPARATOR (*objfile_filename (objfile)))
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> index_path;
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +
> +  if (build_id == nullptr)
> +    return false;
> +
> +  scoped_fd fd = debuginfod_section_query (build_id->data,
> +                                          build_id->size,
> +                                          bfd_get_filename
> +                                            (objfile->obfd.get ()),
> +                                          ".gdb_index",
> +                                          &index_path);
> +
> +  if (fd.get () < 0)
> +    return false;
> +
> +  /* We found a separate .gdb_index file so a separate debuginfo file
> +     should exist, but we don't want to download it until necessary.
> +     Attach the index to this objfile and defer the debuginfo download
> +     until gdb needs to expand symtabs referenced by the index.  */
> +  objfile->flags |= OBJF_DOWNLOAD_DEFERRED;
> +  dwarf2_initialize_objfile (objfile);
> +  return true;
> +}
> +
>
>
>  /* Build a partial symbol table.  */
> diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> index 37023a20709..e3131693b81 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -866,6 +866,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
>       CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
>         override final;
>
> +  struct compunit_symtab *do_find_pc_sect_compunit_symtab
> +    (struct objfile *objfile, struct bound_minimal_symbol msymbol,
> +     CORE_ADDR pc, struct obj_section *section, int warn_if_readin);
> +
>    struct compunit_symtab *find_compunit_symtab_by_address
>      (struct objfile *objfile, CORE_ADDR address) override
>    {
> @@ -942,4 +946,10 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
>                                        dwarf2_section_info *section,
>                                        addrmap *mutable_map);
>
> +/* If OBJFILE contains information from a separately downloaded .gdb_index,
> +   attempt to download the full debuginfo.  */
> +
> +extern void read_full_dwarf_from_debuginfod (struct objfile *,
> +                                            dwarf2_base_index_functions *);
> +
>  #endif /* DWARF2READ_H */
> diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c
> index c9ef41893ee..8cb09e3381a 100644
> --- a/gdb/dwarf2/section.c
> +++ b/gdb/dwarf2/section.c
> @@ -54,7 +54,8 @@ dwarf2_section_info::get_bfd_owner () const
>        section = get_containing_section ();
>        gdb_assert (!section->is_virtual);
>      }
> -  gdb_assert (section->s.section != nullptr);
> +  if (section->s.section == nullptr)
> +    error (_("Can't find owner of DWARF section."));
>    return section->s.section->owner;
>  }
>
> diff --git a/gdb/elfread.c b/gdb/elfread.c
> index 799e3b914f8..133341ea615 100644
> --- a/gdb/elfread.c
> +++ b/gdb/elfread.c
> @@ -1242,7 +1242,7 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
>             symbol_file_add_separate (debug_bfd, debugfile.c_str (),
>                                       symfile_flags, objfile);
>         }
> -      else
> +      else if (!dwarf2_has_separate_index (objfile))
>         {
>           has_dwarf2 = false;
>           const struct bfd_build_id *build_id
> diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h
> index 9dee2ee51a0..fb3f741c899 100644
> --- a/gdb/objfile-flags.h
> +++ b/gdb/objfile-flags.h
> @@ -60,6 +60,10 @@ enum objfile_flag : unsigned
>      /* User requested that we do not read this objfile's symbolic
>         information.  */
>      OBJF_READNEVER = 1 << 6,
> +
> +    /* A separate .gdb_index has been downloaded for this objfile.
> +       Debuginfo for this objfile can be downloaded when required.  */
> +    OBJF_DOWNLOAD_DEFERRED = 1 << 7,
>    };
>
>  DEF_ENUM_FLAGS_TYPE (enum objfile_flag, objfile_flags);
> diff --git a/gdb/objfiles.h b/gdb/objfiles.h
> index bb7b0a4579d..57bc1d45965 100644
> --- a/gdb/objfiles.h
> +++ b/gdb/objfiles.h
> @@ -613,11 +613,22 @@ struct objfile
>    /* See quick_symbol_functions.  */
>    void require_partial_symbols (bool verbose);
>
> -  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
> -  void remove_partial_symbol (quick_symbol_functions *target)
> +  /*  Indicate that the aquisition of this objfile's separate debug objfile
> +      is no longer deferred.  Used when the debug objfile has been aquired
> +      or could not be found.  */
> +  void remove_deferred_status ()
>    {
> +    flags &= ~OBJF_DOWNLOAD_DEFERRED;
> +
> +   /* Remove quick_symbol_functions derived from a separately downloaded
> +      index.  If available the separate debug objfile's index will be used
> +      instead, since that objfile actually contains the symbols and CUs
> +      referenced in the index.
> +
> +      No more than one element of qf should have from_separate_index set
> +      to true.  */
>      for (quick_symbol_functions_up &qf_up : qf)
> -      if (qf_up.get () == target)
> +      if (qf_up->from_separate_index)
>         {
>           qf.remove (qf_up);
>           return;
> diff --git a/gdb/quick-symbol.h b/gdb/quick-symbol.h
> index a7fea2ccb49..e7163503e39 100644
> --- a/gdb/quick-symbol.h
> +++ b/gdb/quick-symbol.h
> @@ -225,6 +225,10 @@ struct quick_symbol_functions
>    virtual void read_partial_symbols (struct objfile *objfile)
>    {
>    }
> +
> +  /* True if this quick_symbol_functions is derived from a separately
> +     downloaded index.  */
> +  bool from_separate_index = false;
>  };
>
>  typedef std::unique_ptr<quick_symbol_functions> quick_symbol_functions_up;
> diff --git a/gdb/symfile.c b/gdb/symfile.c
> index 96239679c77..c476196184a 100644
> --- a/gdb/symfile.c
> +++ b/gdb/symfile.c
> @@ -991,6 +991,10 @@ syms_from_objfile (struct objfile *objfile,
>  static void
>  finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
>  {
> +  struct objfile *parent = objfile->separate_debug_objfile_backlink;
> +  bool was_deferred
> +    = (parent != nullptr) && (parent->flags & OBJF_DOWNLOAD_DEFERRED);
> +
>    /* If this is the main symbol file we have to clean up all users of the
>       old main symbol file.  Otherwise it is sufficient to fixup all the
>       breakpoints that may have been redefined by this symbol file.  */
> @@ -1001,7 +1005,8 @@ finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
>
>        clear_symtab_users (add_flags);
>      }
> -  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0)
> +  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0
> +          && !was_deferred)
>      {
>        breakpoint_re_set ();
>      }
> @@ -1127,6 +1132,12 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name,
>
>    finish_new_objfile (objfile, add_flags);
>
> +  /* Remove deferred status now in case any observers trigger symtab
> +     expansion.  Otherwise gdb might try to read parent for psymbols
> +     when it should read the separate debug objfile instead.  */
> +  if (parent != nullptr && (parent->flags & OBJF_DOWNLOAD_DEFERRED))
> +    parent->remove_deferred_status ();
> +
>    gdb::observers::new_objfile.notify (objfile);
>
>    bfd_cache_close_all ();
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index 5e1b9d91879..2408725fa73 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -2897,14 +2897,30 @@ find_pc_sect_compunit_symtab (CORE_ADDR pc, struct obj_section *section)
>    if (best_cust != NULL)
>      return best_cust;
>
> +  int warn_if_readin = 1;
> +
>    /* Not found in symtabs, search the "quick" symtabs (e.g. psymtabs).  */
>
>    for (objfile *objf : current_program_space->objfiles ())
>      {
> +      bool was_deferred = objf->flags & OBJF_DOWNLOAD_DEFERRED;
> +
>        struct compunit_symtab *result
> -       = objf->find_pc_sect_compunit_symtab (msymbol, pc, section, 1);
> +       = objf->find_pc_sect_compunit_symtab (msymbol, pc, section,
> +                                             warn_if_readin);
> +
>        if (result != NULL)
>         return result;
> +
> +      /* If objf's separate debug info was just acquired, disable
> +        warn_if_readin for the next iteration of this loop.  This prevents
> +        a spurious warning in case an observer already triggered expansion
> +        of the separate debug objfile's symtabs.  */
> +      if (was_deferred && objf->separate_debug_objfile != nullptr
> +         && (objf->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +       warn_if_readin = 0;
> +      else if (warn_if_readin == 0)
> +       warn_if_readin = 1;
>      }
>
>    return NULL;
> diff --git a/gdb/testsuite/gdb.debuginfod/libsection1.c b/gdb/testsuite/gdb.debuginfod/libsection1.c
> new file mode 100644
> index 00000000000..60824b415c6
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/libsection1.c
> @@ -0,0 +1,40 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 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 <stdio.h>
> +#include <pthread.h>
> +#include <unistd.h>
> +
> +extern void libsection2_test ();
> +extern void *libsection2_thread_test (void *);
> +
> +void
> +libsection1_test ()
> +{
> +  pthread_t thr;
> +
> +  printf ("In libsection1\n");
> +  libsection2_test ();
> +
> +  pthread_create (&thr, NULL, libsection2_thread_test, NULL);
> +
> +  /* Give the new thread a chance to actually enter libsection2_thread_test.  */
> +  sleep (3);
> +  printf ("Cancelling thread\n");
> +
> +  pthread_cancel (thr);
> +}
> diff --git a/gdb/testsuite/gdb.debuginfod/libsection2.c b/gdb/testsuite/gdb.debuginfod/libsection2.c
> new file mode 100644
> index 00000000000..629a67f94a5
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/libsection2.c
> @@ -0,0 +1,37 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 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 <stdio.h>
> +
> +void
> +libsection2_test ()
> +{
> +  printf ("In libsection2\n");
> +}
> +
> +void *
> +libsection2_thread_test (void *arg)
> +{
> +  (void) arg;
> +
> +  printf ("In thread test\n");
> +
> +  while (1)
> +    ;
> +
> +  return NULL;
> +}
> diff --git a/gdb/testsuite/gdb.debuginfod/section.c b/gdb/testsuite/gdb.debuginfod/section.c
> new file mode 100644
> index 00000000000..d391a8f898e
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/section.c
> @@ -0,0 +1,29 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 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 <stdio.h>
> +
> +extern void libsection1_test ();
> +
> +int
> +main()
> +{
> +  libsection1_test ();
> +  printf ("in section exec\n");
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
> new file mode 100644
> index 00000000000..96e9750cd38
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> @@ -0,0 +1,181 @@
> +# Copyright 2023 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 debuginfod functionality
> +
> +standard_testfile
> +
> +load_lib debuginfod-support.exp
> +
> +require allow_debuginfod_tests
> +
> +set sourcetmp [standard_output_file tmp-${srcfile}]
> +set outputdir [standard_output_file {}]
> +
> +# SECTEXEC is an executable which calls a function from LIB_SL1.
> +set sectfile "section"
> +set sectsrc $srcdir/$subdir/section.c
> +set sectexec [standard_output_file $sectfile]
> +
> +# Solib LIB_SL1 calls functions from LIB_SL2.
> +set libfile1 "libsection1"
> +set libsrc1 $srcdir/$subdir/$libfile1.c
> +set lib_sl1 [standard_output_file $libfile1.sl]
> +
> +set libfile2 "libsection2"
> +set libsrc2 $srcdir/$subdir/$libfile2.c
> +set lib_sl2 [standard_output_file $libfile2.sl]
> +
> +set lib_opts1 [list debug build-id shlib=$lib_sl2]
> +set lib_opts2 [list debug build-id]
> +set exec_opts [list debug build-id shlib=$lib_sl1 shlib=$lib_sl2]
> +
> +clean_restart
> +
> +if {[enable_section_downloads] == 0} {
> +    untested "GDB does not support debuginfod section downloads"
> +    return -1
> +}
> +
> +# Compile SECTEXEC, LIB_SL1 and LIB_SL2.
> +if { [gdb_compile_shlib $libsrc2 $lib_sl2 $lib_opts2] != "" } {
> +    untested "failed to compile $libfile2"
> +    return -1
> +}
> +
> +if { [gdb_compile_shlib_pthreads $libsrc1 $lib_sl1 $lib_opts1] != "" } {
> +    untested "failed to compile $libfile1"
> +    return -1
> +}
> +
> +if { [gdb_compile $sectsrc $sectexec executable $exec_opts] != "" } {
> +    untested "failed to compile $sectfile"
> +    return -1
> +}
> +
> +# Add .gdb_index to solibs.
> +if { [have_index $lib_sl1] != "gdb_index"
> +     && [add_gdb_index $lib_sl1] == 0 } {
> +    untested "failed to add .gdb_index to $libfile1"
> +    return -1
> +}
> +
> +if { [have_index $lib_sl2] != "gdb_index"
> +     && [add_gdb_index $lib_sl2] == 0 } {
> +    untested "failed to add .gdb_index to $libfile2"
> +    return -1
> +}
> +
> +# Strip solib debuginfo into separate files.
> +if { [gdb_gnu_strip_debug $lib_sl1 ""] != 0} {
> +   fail "strip $lib_sl1 debuginfo"
> +   return -1
> +}
> +
> +if { [gdb_gnu_strip_debug $lib_sl2 ""] != 0} {
> +   fail "strip $lib_sl2 debuginfo"
> +   return -1
> +}
> +
> +# Move debuginfo files into directory that debuginfod will serve from.
> +set debugdir [standard_output_file "debug"]
> +set debuginfo_sl1 [standard_output_file $libfile1.sl.debug]
> +set debuginfo_sl2 [standard_output_file $libfile2.sl.debug]
> +
> +file mkdir $debugdir
> +file rename -force $debuginfo_sl1 $debugdir
> +file rename -force $debuginfo_sl2 $debugdir
> +
> +# Restart GDB and clear the debuginfod client cache. Then load BINFILE into
> +# GDB and start running it.  Match output with pattern RES and use TESTNAME
> +# as the test name.
> +proc_with_prefix clean_restart_with_prompt { binfile res testname } {
> +    global cache
> +
> +    clean_restart
> +
> +    # Delete client cache so debuginfo downloads again.
> +    file delete -force $cache
> +
> +    gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
> +    gdb_test "start" $res "file [file tail $binfile] start $testname" \
> +            ".*Enable debuginfod.*" "y"
> +}
> +
> +# Tests with no debuginfod server running.
> +proc_with_prefix no_url { } {
> +    global sectexec libfile1 libfile2
> +
> +    # Check that no section is downloaded and no debuginfo is found.
> +    gdb_test "file $sectexec" "" "file [file tail $sectexec] file no url"
> +    gdb_test "start" "" "file [file tail $sectexec] start no url"
> +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile1.*" \
> +            "file [file tail $libfile1] found no url"
> +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile2.*" \
> +            "file [file tail $libfile2] found no url"
> +}
> +
> +# Tests with a debuginfod server running.
> +proc_with_prefix local_url { } {
> +    global sectexec
> +    global libsrc1 lib_sl1 libfile1
> +    global libsrc2 lib_sl2 libfile2
> +    global debugdir db
> +
> +    set url [start_debuginfod $db $debugdir]
> +    if { $url == "" } {
> +       unresolved "failed to start debuginfod server"
> +       return
> +    }
> +
> +    # Point GDB to the server.
> +    setenv DEBUGINFOD_URLS $url
> +
> +    # Download .gdb_index for solibs.
> +    set res ".*section \.gdb_index for $lib_sl1.*\
> +       section \.gdb_index for $lib_sl2.*"
> +    clean_restart_with_prompt $sectexec $res "index"
> +
> +    # Download debuginfo when stepping into a function.
> +    set res ".*separate debug info for $lib_sl1.*\"In ${libfile1}\\\\n\".*"
> +    gdb_test "step" $res "file [file tail $lib_sl1] step"
> +
> +    clean_restart_with_prompt $sectexec "" "break"
> +
> +    # Download debuginfo when setting a breakpoint.
> +    set res "Download.*separate debug info for $lib_sl2.*"
> +    gdb_test "br libsection2_test" $res "file [file tail $sectexec] break set"
> +
> +    # Hit the breakpoint.
> +    set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
> +    gdb_test "c" $res "file [file tail $sectexec] break continue"
> +
> +    # Check that download progress message is correctly formatted
> +    # during backtrace.
> +    set res ".* separate debug info for $lib_sl1.*#0  libsection2_test\
> +       \\(\\) at.*"
> +    set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
> +    gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
> +}
> +
> +# Create CACHE and DB directories ready for debuginfod to use.
> +prepare_for_debuginfod cache db
> +
> +with_debuginfod_env $cache {
> +    no_url
> +    local_url
> +}
> +
> +stop_debuginfod
> diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
> index 50a8b512a4a..6368c27e9d0 100644
> --- a/gdb/testsuite/lib/debuginfod-support.exp
> +++ b/gdb/testsuite/lib/debuginfod-support.exp
> @@ -194,3 +194,33 @@ proc stop_debuginfod { } {
>         unset debuginfod_spawn_id
>      }
>  }
> +
> +# Return 1 if gdb is configured to download ELF/DWARF sections from
> +# debuginfod servers.  Otherwise return 0.
> +proc enable_section_downloads { } {
> +    global gdb_prompt
> +
> +    set cmd "maint set debuginfod download-sections on"
> +    set msg "enable section downloads"
> +
> +    gdb_test_multiple $cmd $msg {
> +       -re ".*Undefined maintenance.*" {
> +           perror "Undefined command: \"$cmd\""
> +           return 0
> +       }
> +       -re ".*not compiled into GDB.*" {
> +           perror "Unsupported command: \"$cmd\""
> +           return 0
> +       }
> +       -re "\r\n${gdb_prompt} $" {
> +           return 1
> +       }
> +       timeout {
> +           perror "timeout: \"$cmd\""
> +           return 0
> +       }
> +    }
> +
> +    perror "Unexpected output for \"$cmd\""
> +    return 0
> +}
> --
> 2.40.1
>


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 6/6 v3] gdb/debuginfod: Add .debug_line downloading
  2023-06-01  1:43 ` [PATCH 6/6 v3] gdb/debuginfod: Add .debug_line downloading Aaron Merey
@ 2023-06-15 13:45   ` Aaron Merey
  2023-07-03 17:40     ` [PING*2][PATCH " Aaron Merey
  0 siblings, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-06-15 13:45 UTC (permalink / raw)
  To: gdb-patches; +Cc: aburgess

Ping

Thanks,
Aaron

On Wed, May 31, 2023 at 9:44 PM Aaron Merey <amerey@redhat.com> wrote:
>
> v2: https://sourceware.org/pipermail/gdb-patches/2023-April/198946.html
>
> v3 adds tests for .debug_line downloading and modifies logging for
> the testsuite debuginfod server (see commit message).
>
> Commit message:
>
> ELF/DWARF section downloading allows gdb to download .gdb_index files in
> order to defer full debuginfo downloads.  However .gdb_index does not
> contain any information regarding source filenames.  When a gdb command
> includes a filename argument (ex. 'break main.c:50'), this results in
> the mass downloading of all deferred debuginfo so that gdb can search the
> debuginfo for matching source filenames.  This can result in unnecessary
> downloads.
>
> To improve this, have gdb instead download each debuginfo's .debug_line
> (and .debug_line_str if using DWARF5) when executing these commands.
> Download full debuginfo only when its .debug_line contains a matching
> filename.
>
> Since the combined size of .debug_line and .debug_line_str is only about
> 1% the size of the corresponding debuginfo, significant time can be saved
> by checking these sections before choosing to download an entire debuginfo.
>
> This patch also redirects stdout and stderr of the debuginfod server
> used by testsuite/gdb.debuginfod tests to a server_log standard output
> file.  While adding tests for this patch I ran into an issue where the
> test server would block when logging to stderr, presumably because the
> stderr buffer filled up and wasn't being read from.  Redirecting the
> log to a file fixes this and also makes the server log more accessible
> when debugging test failures.
> ---
>  gdb/dwarf2/line-header.c                 | 215 +++++++++++++++--------
>  gdb/dwarf2/line-header.h                 |  10 ++
>  gdb/dwarf2/read-gdb-index.c              |  27 +++
>  gdb/dwarf2/read.c                        | 165 +++++++++++++++++
>  gdb/dwarf2/read.h                        |  31 ++++
>  gdb/testsuite/gdb.debuginfod/section.exp |  23 ++-
>  gdb/testsuite/lib/debuginfod-support.exp |   5 +-
>  7 files changed, 397 insertions(+), 79 deletions(-)
>
> diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> index d072a91bac9..b9210d84f6b 100644
> --- a/gdb/dwarf2/line-header.c
> +++ b/gdb/dwarf2/line-header.c
> @@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
>  {
>    LONGEST length = read_initial_length (abfd, buf, bytes_read);
>
> -  gdb_assert (cu_header->initial_length_size == 4
> -             || cu_header->initial_length_size == 8
> -             || cu_header->initial_length_size == 12);
> +  if (cu_header != nullptr)
> +    {
> +      gdb_assert (cu_header->initial_length_size == 4
> +                 || cu_header->initial_length_size == 8
> +                 || cu_header->initial_length_size == 12);
>
> -  if (cu_header->initial_length_size != *bytes_read)
> -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> +      if (cu_header->initial_length_size != *bytes_read)
> +       complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> +    }
>
>    *offset_size = (*bytes_read == 4) ? 4 : 8;
>    return length;
>  }
>
> -/* Read directory or file name entry format, starting with byte of
> -   format count entries, ULEB128 pairs of entry formats, ULEB128 of
> -   entries count and the entries themselves in the described entry
> -   format.  */
> +
> +/* Like read_formatted_entries but the .debug_line and .debug_line_str
> +   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
> +   where these sections are read from separate files without necessarily
> +   having access to the entire debuginfo file they originate from.  */
>
>  static void
> -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> -                       const gdb_byte **bufp, struct line_header *lh,
> -                       unsigned int offset_size,
> -                       void (*callback) (struct line_header *lh,
> -                                         const char *name,
> -                                         dir_index d_index,
> -                                         unsigned int mod_time,
> -                                         unsigned int length))
> +read_formatted_entries
> +  (bfd *parent_bfd, const gdb_byte **line_bufp,
> +   const gdb::array_view<const gdb_byte> line_str_data,
> +   struct line_header *lh,
> +   unsigned int offset_size,
> +   void (*callback) (struct line_header *lh,
> +                    const char *name,
> +                    dir_index d_index,
> +                    unsigned int mod_time,
> +                    unsigned int length))
>  {
>    gdb_byte format_count, formati;
>    ULONGEST data_count, datai;
> -  const gdb_byte *buf = *bufp;
> +  const gdb_byte *buf = *line_bufp;
> +  const gdb_byte *str_buf = line_str_data.data ();
>    const gdb_byte *format_header_data;
>    unsigned int bytes_read;
>
> -  format_count = read_1_byte (abfd, buf);
> +  format_count = read_1_byte (parent_bfd, buf);
>    buf += 1;
>    format_header_data = buf;
>    for (formati = 0; formati < format_count; formati++)
>      {
> -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
>        buf += bytes_read;
> -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
>        buf += bytes_read;
>      }
>
> -  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
> +  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
>    buf += bytes_read;
>    for (datai = 0; datai < data_count; datai++)
>      {
> @@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>
>        for (formati = 0; formati < format_count; formati++)
>         {
> -         ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
> +         ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
>           format += bytes_read;
>
> -         ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> +         ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
>           format += bytes_read;
>
>           gdb::optional<const char *> string;
> @@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>           switch (form)
>             {
>             case DW_FORM_string:
> -             string.emplace (read_direct_string (abfd, buf, &bytes_read));
> +             string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
>               buf += bytes_read;
>               break;
>
>             case DW_FORM_line_strp:
>               {
> -               const char *str
> -                 = per_objfile->read_line_string (buf, offset_size);
> +               if (line_str_data.empty ())
> +                 error (_("Dwarf Error: DW_FORM_line_strp used without " \
> +                          "required section"));
> +               if (line_str_data.size () <= offset_size)
> +                 error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> +                          "of section .debug_line"));
> +
> +               ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
> +
> +               const char *str;
> +               if (str_buf[str_offset] == '\0')
> +                 str = nullptr;
> +               else
> +                 str = (const char *) (str_buf + str_offset);
>                 string.emplace (str);
>                 buf += offset_size;
> +               break;
>               }
> -             break;
>
>             case DW_FORM_data1:
> -             uint.emplace (read_1_byte (abfd, buf));
> +             uint.emplace (read_1_byte (parent_bfd, buf));
>               buf += 1;
>               break;
>
>             case DW_FORM_data2:
> -             uint.emplace (read_2_bytes (abfd, buf));
> +             uint.emplace (read_2_bytes (parent_bfd, buf));
>               buf += 2;
>               break;
>
>             case DW_FORM_data4:
> -             uint.emplace (read_4_bytes (abfd, buf));
> +             uint.emplace (read_4_bytes (parent_bfd, buf));
>               buf += 4;
>               break;
>
>             case DW_FORM_data8:
> -             uint.emplace (read_8_bytes (abfd, buf));
> +             uint.emplace (read_8_bytes (parent_bfd, buf));
>               buf += 8;
>               break;
>
> @@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>               break;
>
>             case DW_FORM_udata:
> -             uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
> +             uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
>               buf += bytes_read;
>               break;
>
> @@ -248,28 +267,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
>      }
>
> -  *bufp = buf;
> +  *line_bufp = buf;
>  }
>
>  /* See line-header.h.  */
>
>  line_header_up
> -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> -                          dwarf2_per_objfile *per_objfile,
> -                          struct dwarf2_section_info *section,
> -                          const struct comp_unit_head *cu_header,
> -                          const char *comp_dir)
> +dwarf_decode_line_header (bfd *parent_bfd,
> +                         gdb::array_view<const gdb_byte> line_data,
> +                         gdb::array_view<const gdb_byte> line_str_data,
> +                         const gdb_byte **debug_line_ptr,
> +                         bool is_dwz,
> +                         const struct comp_unit_head *cu_header,
> +                         const char *comp_dir)
>  {
> -  const gdb_byte *line_ptr;
> +  const gdb_byte *line_ptr, *buf;
>    unsigned int bytes_read, offset_size;
>    int i;
>    const char *cur_dir, *cur_file;
>
> -  bfd *abfd = section->get_bfd_owner ();
> +  buf = *debug_line_ptr;
>
>    /* Make sure that at least there's room for the total_length field.
>       That could be 12 bytes long, but we're just going to fudge that.  */
> -  if (to_underlying (sect_off) + 4 >= section->size)
> +  if (buf + 4 >= line_data.data () + line_data.size ())
>      {
>        dwarf2_statement_list_fits_in_line_number_section_complaint ();
>        return 0;
> @@ -277,62 +298,65 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>
>    line_header_up lh (new line_header (comp_dir));
>
> -  lh->sect_off = sect_off;
> +  lh->sect_off = (sect_offset) (buf - line_data.data ());
>    lh->offset_in_dwz = is_dwz;
>
> -  line_ptr = section->buffer + to_underlying (sect_off);
> +  line_ptr = buf;
>
>    /* Read in the header.  */
>    LONGEST unit_length
> -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> +    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
>                                               &bytes_read, &offset_size);
> -  line_ptr += bytes_read;
>
> -  const gdb_byte *start_here = line_ptr;
> +  line_ptr += bytes_read;
>
> -  if (line_ptr + unit_length > (section->buffer + section->size))
> +  if (line_ptr + unit_length > buf + line_data.size ())
>      {
>        dwarf2_statement_list_fits_in_line_number_section_complaint ();
>        return 0;
>      }
> +
> +  const gdb_byte *start_here = line_ptr;
> +
>    lh->statement_program_end = start_here + unit_length;
> -  lh->version = read_2_bytes (abfd, line_ptr);
> +  lh->version = read_2_bytes (parent_bfd, line_ptr);
>    line_ptr += 2;
>    if (lh->version > 5)
>      {
>        /* This is a version we don't understand.  The format could have
>          changed in ways we don't handle properly so just punt.  */
>        complaint (_("unsupported version in .debug_line section"));
> -      return NULL;
> +      return nullptr;
>      }
>    if (lh->version >= 5)
>      {
>        gdb_byte segment_selector_size;
>
>        /* Skip address size.  */
> -      read_1_byte (abfd, line_ptr);
> +      read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>
> -      segment_selector_size = read_1_byte (abfd, line_ptr);
> +      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>        if (segment_selector_size != 0)
>         {
>           complaint (_("unsupported segment selector size %u "
>                        "in .debug_line section"),
>                      segment_selector_size);
> -         return NULL;
> +         return nullptr;
>         }
>      }
>
> -  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
> +  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
>    line_ptr += offset_size;
>    lh->statement_program_start = line_ptr + header_length;
> -  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
> +
> +  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
>
>    if (lh->version >= 4)
>      {
> -      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
> +      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>      }
>    else
> @@ -345,41 +369,47 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>                    "in `.debug_line' section"));
>      }
>
> -  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
> +  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> -  lh->line_base = read_1_signed_byte (abfd, line_ptr);
> +
> +  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> -  lh->line_range = read_1_byte (abfd, line_ptr);
> +
> +  lh->line_range = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> -  lh->opcode_base = read_1_byte (abfd, line_ptr);
> +
> +  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> +
>    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
>
>    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
>    for (i = 1; i < lh->opcode_base; ++i)
>      {
> -      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
> +      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>      }
>
>    if (lh->version >= 5)
>      {
>        /* Read directory table.  */
> -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> -                             offset_size,
> -                             [] (struct line_header *header, const char *name,
> -                                 dir_index d_index, unsigned int mod_time,
> -                                 unsigned int length)
> +      read_formatted_entries
> +       (parent_bfd, &line_ptr, line_str_data,
> +        lh.get (), offset_size,
> +        [] (struct line_header *header, const char *name,
> +            dir_index d_index, unsigned int mod_time,
> +            unsigned int length)
>         {
>           header->add_include_dir (name);
>         });
>
>        /* Read file name table.  */
> -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> -                             offset_size,
> -                             [] (struct line_header *header, const char *name,
> -                                 dir_index d_index, unsigned int mod_time,
> -                                 unsigned int length)
> +      read_formatted_entries
> +       (parent_bfd, &line_ptr, line_str_data,
> +        lh.get (), offset_size,
> +        [] (struct line_header *header, const char *name,
> +            dir_index d_index, unsigned int mod_time,
> +            unsigned int length)
>         {
>           header->add_file_name (name, d_index, mod_time, length);
>         });
> @@ -387,7 +417,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>    else
>      {
>        /* Read directory table.  */
> -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> +      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
>         {
>           line_ptr += bytes_read;
>           lh->add_include_dir (cur_dir);
> @@ -395,17 +425,17 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>        line_ptr += bytes_read;
>
>        /* Read file name table.  */
> -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> +      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
>         {
>           unsigned int mod_time, length;
>           dir_index d_index;
>
>           line_ptr += bytes_read;
> -         d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> +         d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
>           line_ptr += bytes_read;
> -         mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> +         mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
>           line_ptr += bytes_read;
> -         length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> +         length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
>           line_ptr += bytes_read;
>
>           lh->add_file_name (cur_file, d_index, mod_time, length);
> @@ -413,9 +443,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>        line_ptr += bytes_read;
>      }
>
> -  if (line_ptr > (section->buffer + section->size))
> +  if (line_ptr > (buf + line_data.size ()))
>      complaint (_("line number info header doesn't "
>                  "fit in `.debug_line' section"));
>
> +  *debug_line_ptr += unit_length + offset_size;
>    return lh;
>  }
> +
> +line_header_up
> +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> +                          dwarf2_per_objfile *per_objfile,
> +                          struct dwarf2_section_info *section,
> +                          const struct comp_unit_head *cu_header,
> +                          const char *comp_dir)
> +{
> +  struct objfile *objfile = per_objfile->objfile;
> +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> +
> +  /* Read .debug_line.  */
> +  dwarf2_section_info *line_sec = &per_bfd->line;
> +  bfd_size_type line_size = line_sec->get_size (objfile);
> +
> +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> +
> +  /* Read .debug_line_str.  */
> +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> +
> +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> +                                           line_str_size);
> +
> +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> +
> +  return dwarf_decode_line_header
> +    (per_bfd->obfd, line, line_str, &line_ptr,
> +     is_dwz, cu_header, comp_dir);
> +}
> diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> index 59a42e336f5..44e32828ddb 100644
> --- a/gdb/dwarf2/line-header.h
> +++ b/gdb/dwarf2/line-header.h
> @@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
>     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
>     const char *comp_dir);
>
> +/* Like above but the .debug_line and .debug_line_str are stored in
> +   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
> +   statement program header within LINE_DATA.  */
> +
> +extern line_header_up dwarf_decode_line_header
> +  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
> +   gdb::array_view<const gdb_byte> line_str_data,
> +   const gdb_byte **debug_line_ptr, bool is_dwz,
> +  const comp_unit_head *cu_header, const char *comp_dir);
> +
>  #endif /* DWARF2_LINE_HEADER_H */
> diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> index d3516e92361..64f202fddd3 100644
> --- a/gdb/dwarf2/read-gdb-index.c
> +++ b/gdb/dwarf2/read-gdb-index.c
> @@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
>    }
>  };
>
> +struct mapped_debug_line;
> +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> +
>  struct dwarf2_gdb_index : public dwarf2_base_index_functions
>  {
>    /* This dumps minimal information about the index.
> @@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
>       debuginfo if necessary.  */
>    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> +
> +  /* Filename information related to this .gdb_index.  */
> +  mapped_debug_line_up mdl;
> +
> +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> +  bool filename_in_debug_line
> +  (objfile *objfile,
> +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
>  };
>
>  void
> @@ -587,6 +599,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
>    return result;
>  }
>
> +bool
> +dwarf2_gdb_index::filename_in_debug_line
> +  (objfile *objfile,
> +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  if (mdl == nullptr)
> +    mdl.reset (new mapped_debug_line (objfile));
> +
> +  return mdl->contains_matching_filename (file_matcher);
> +}
> +
>  bool
>  dwarf2_gdb_index::expand_symtabs_matching
>      (struct objfile *objfile,
> @@ -615,6 +638,10 @@ dwarf2_gdb_index::expand_symtabs_matching
>           return false;
>         }
>
> +      if (file_matcher != nullptr
> +         && !filename_in_debug_line (objfile, file_matcher))
> +       return true;
> +
>        read_full_dwarf_from_debuginfod (objfile, this);
>        return true;
>      }
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index 96d1ff53d91..715b3c06a56 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -81,6 +81,7 @@
>  #include "gdbsupport/gdb_optional.h"
>  #include "gdbsupport/underlying.h"
>  #include "gdbsupport/hash_enum.h"
> +#include "gdbsupport/scoped_mmap.h"
>  #include "filename-seen-cache.h"
>  #include "producer.h"
>  #include <fcntl.h>
> @@ -2115,6 +2116,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
>    return this_cu->file_names;
>  }
>
> +#if !HAVE_SYS_MMAN_H
> +
> +bool
> +mapped_debug_line::contains_matching_filename
> +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  return false;
> +}
> +
> +gdb::array_view<const gdb_byte>
> +mapped_debug_line::read_debug_line_separate
> +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> +{
> +  return {};
> +}
> +
> +bool
> +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> +{
> +  return false;
> +}
> +
> +#else /* !HAVE_SYS_MMAN_H */
> +
> +struct line_resource_mmap final : public index_cache_resource
> +{
> +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> +     file doesn't exist. */
> +  line_resource_mmap (const char *filename)
> +    : mapping (mmap_file (filename))
> +  {}
> +
> +  scoped_mmap mapping;
> +};
> +
> +/* See read.h.  */
> +
> +bool
> +mapped_debug_line::contains_matching_filename
> +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  for (line_header_up &lh : line_headers)
> +    for (file_entry &fe : lh->file_names ())
> +      {
> +       const char *filename = fe.name;
> +
> +       if (file_matcher (fe.name, false))
> +         return true;
> +
> +       bool basename_match = file_matcher (lbasename (fe.name), true);
> +
> +       if (!basenames_may_differ && !basename_match)
> +         continue;
> +
> +       /* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> +          until DWARF5.  Since we don't have access to the CU at this
> +          point we just check for a partial match on the filename.
> +          If there is a match, the full debuginfo will be downloaded
> +          ane the match will be re-evalute with DW_AT_comp_dir.  */
> +       if (lh->version < 5 && fe.d_index == 0)
> +         return basename_match;
> +
> +       const char *dirname = fe.include_dir (&*lh);
> +       std::string fullname;
> +
> +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> +         fullname = filename;
> +       else
> +         fullname = std::string (dirname) + SLASH_STRING + filename;
> +
> +       gdb::unique_xmalloc_ptr<char> rewritten
> +         = rewrite_source_path (fullname.c_str ());
> +       if (rewritten != nullptr)
> +         fullname = rewritten.release ();
> +
> +       if (file_matcher (fullname.c_str (), false))
> +         return true;
> +      }
> +
> +  return false;
> +}
> +
> +/* See read.h.  */
> +
> +gdb::array_view<const gdb_byte>
> +mapped_debug_line::read_debug_line_separate
> +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> +{
> +  if (filename == nullptr)
> +    return {};
> +
> +  try
> +  {
> +    line_resource_mmap *mmap_resource
> +      = new line_resource_mmap (filename);
> +
> +    resource->reset (mmap_resource);
> +
> +    return gdb::array_view<const gdb_byte>
> +      ((const gdb_byte *) mmap_resource->mapping.get (),
> +       mmap_resource->mapping.size ());
> +  }
> +  catch (const gdb_exception &except)
> +  {
> +    exception_print (gdb_stderr, except);
> +  }
> +
> +  return {};
> +}
> +
> +/* See read.h.  */
> +
> +bool
> +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> +{
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  if (build_id == nullptr)
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> line_path;
> +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> +                                               build_id->size,
> +                                               bfd_get_filename
> +                                                 (objfile->obfd.get ()),
> +                                               ".debug_line",
> +                                               &line_path);
> +
> +  if (line_fd.get () < 0)
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> line_str_path;
> +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> +                                                   build_id->size,
> +                                                   bfd_get_filename
> +                                                     (objfile->obfd.get ()),
> +                                                   ".debug_line_str",
> +                                                   &line_str_path);
> +
> +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> +  line_str_data = read_debug_line_separate (line_str_path.get (),
> +                                           &line_str_resource);
> +
> +  const gdb_byte *line_ptr = line_data.data ();
> +
> +  while (line_ptr < line_data.data () + line_data.size ())
> +    {
> +      line_header_up lh
> +       = dwarf_decode_line_header (objfile->obfd.get (),
> +                                   line_data, line_str_data,
> +                                   &line_ptr, false,
> +                                   nullptr, nullptr);
> +      line_headers.emplace_back (lh.release ());
> +    }
> +
> +  return true;
> +}
> +#endif /* !HAVE_SYS_MMAN_H */
> +
> +mapped_debug_line::mapped_debug_line (objfile *objfile)
> +{
> +  if (!read_debug_line_from_debuginfod (objfile))
> +    line_headers.clear ();
> +}
> +
>  /* A helper for the "quick" functions which computes and caches the
>     real path for a given file name from the line table.  */
>
> diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> index e3131693b81..b8a8b76bde0 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -34,6 +34,7 @@
>  #include "gdbsupport/hash_enum.h"
>  #include "gdbsupport/function-view.h"
>  #include "gdbsupport/packed.h"
> +#include "dwarf2/line-header.h"
>
>  /* Hold 'maintenance (set|show) dwarf' commands.  */
>  extern struct cmd_list_element *set_dwarf_cmdlist;
> @@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
>  extern void read_full_dwarf_from_debuginfod (struct objfile *,
>                                              dwarf2_base_index_functions *);
>
> +struct mapped_debug_line
> +{
> +  mapped_debug_line (objfile *objfile);
> +
> +  /* Return true if any of the mapped .debug_line's filenames match
> +     FILE_MATCHER.  */
> +
> +  bool contains_matching_filename
> +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> +
> +private:
> +  std::vector<line_header_up> line_headers;
> +
> +  gdb::array_view<const gdb_byte> line_data;
> +  gdb::array_view<const gdb_byte> line_str_data;
> +
> +  std::unique_ptr<index_cache_resource> line_resource;
> +  std::unique_ptr<index_cache_resource> line_str_resource;
> +
> +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> +     and populate line_headers.  */
> +
> +  bool read_debug_line_from_debuginfod (objfile *objfile);
> +
> +  /* Initialize line_data and line_str_data with the .debug_line and
> +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> +
> +  gdb::array_view<const gdb_byte> read_debug_line_separate
> +    (char *filename, std::unique_ptr<index_cache_resource> *resource);
> +};
>  #endif /* DWARF2READ_H */
> diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
> index 96e9750cd38..1380eb7ef78 100644
> --- a/gdb/testsuite/gdb.debuginfod/section.exp
> +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> @@ -106,7 +106,7 @@ proc_with_prefix clean_restart_with_prompt { binfile res testname } {
>
>      clean_restart
>
> -    # Delete client cache so debuginfo downloads again.
> +    # Delete client cache so progress messages always appear.
>      file delete -force $cache
>
>      gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
> @@ -168,6 +168,27 @@ proc_with_prefix local_url { } {
>         \\(\\) at.*"
>      set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
>      gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
> +
> +    clean_restart_with_prompt $sectexec "" "line 1"
> +
> +    # List source file using .debug_line download.
> +    set res ".*\.debug_line.*$lib_sl1.*21.*extern void libsection2_test.*"
> +    gdb_test "list $libsrc1:21" $res "file [file tail $sectexec] line 1 list"
> +
> +    clean_restart_with_prompt $sectexec "" "line 2"
> +
> +    # Set breakpoint using .debug_line download.
> +    set res ".*section \.debug_line for $lib_sl1.*Breakpoint 2 at.*$libsrc1.*"
> +    gdb_test "br $libsrc1:37" $res "file [file tail $sectexec] line 2 br"
> +
> +    # Continue to breakpoint.
> +    set res "Breakpoint 2, libsection1_test.*\"Cancelling thread\\\\n\".*"
> +    gdb_test "c" $res "file [file tail $sectexec] line 2 continue"
> +
> +    # Check that download progress message is correctly formatted
> +    # when printing threads.
> +    set res ".*separate debug info for $lib_sl2\.\.\.\r\n.* 2    Thread.*"
> +    gdb_test "info thr" $res "file [file tail $libfile2] line thread"
>  }
>
>  # Create CACHE and DB directories ready for debuginfod to use.
> diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
> index 6368c27e9d0..07ea23999bd 100644
> --- a/gdb/testsuite/lib/debuginfod-support.exp
> +++ b/gdb/testsuite/lib/debuginfod-support.exp
> @@ -113,6 +113,8 @@ proc with_debuginfod_env { cache body } {
>  proc start_debuginfod { db debugdir } {
>      global debuginfod_spawn_id spawn_id
>
> +    set logfile [standard_output_file "server_log"]
> +
>      # Find an unused port.
>      set port 7999
>      set found false
> @@ -127,7 +129,8 @@ proc start_debuginfod { db debugdir } {
>             set old_spawn_id $spawn_id
>         }
>
> -       spawn debuginfod -vvvv -d $db -p $port -F $debugdir
> +       spawn sh -c "debuginfod -vvvv -d $db -p $port -F $debugdir 2>&1 \
> +               | tee $logfile"
>         set debuginfod_spawn_id $spawn_id
>
>         if { [info exists old_spawn_id] } {
> --
> 2.40.1
>


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PING*2][PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator
  2023-06-15 13:44   ` Aaron Merey
@ 2023-07-03 17:39     ` Aaron Merey
  2023-07-19 14:32       ` [PING*3][PATCH " Aaron Merey
  0 siblings, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-07-03 17:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Ping

Thanks,
Aaron

On Thu, Jun 15, 2023 at 9:44 AM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Wed, May 31, 2023 at 9:44 PM Aaron Merey <amerey@redhat.com> wrote:
> >
> > To facilitate the deletion of objfiles, progspace objects use a safe
> > iterator that holds a reference to the next objfile in the progspace's
> > objfile_list.  This allows objfiles to be deleted in a loop without
> > invalidating the loop's iterator.  progspace also uses an unwrapping
> > iterator over std::unique_ptr<objfile> that automatically deferences
> > the unique_ptr.
> >
> > This patch changes the objfile safe iterator to be a reverse safe
> > iterator.  It changes the unwrapping iterator into a template.  It
> > also modifies objfile_list insertion so that separate debug objfiles
> > are placed into the list after the parent objfile, instead of before.
> >
> > These changes are intended to prepare gdb for on-demand debuginfo
> > downloading and the downloading of .gdb_index sections.
> >
> > With on-demand downloading enabled, gdb might download a debuginfo
> > file during symtab expansion.  In this case an objfile could be added
> > to an objfiles_list during iteration over the list (for example, in
> > iterate_over_symtabs).  We want these loops to also iterate over newly
> > downloaded objfiles. So objfiles need to be inserted into objfiles_list
> > after their parent since it is during the search of the parent
> > objfile for some symbol or filename that the separate debug objfile
> > might be downloaded.
> >
> > The unwrapping iterator is changed into a template in order to
> > use it with objfile qf_require_partial_symbols, which is now also
> > uses with a safe iterator.  This is because after a separate debug
> > objfile is downloaded on-demand, we want to remove any .gdb_index
> > quick_symbol_functions from the parent objfile during iteration over
> > the parent's quick_symbol_functions.  The newly downloaded separate
> > debug objfile contains the index and all of the related symbols
> > so the .gdb_index should not be associated with the parent objfile
> > any longer.
> >
> > Finally a safe reverse iterator is now used during progspace objfile
> > deletion in order to prevent iterator invalidation during the loop
> > in which objfiles are deleted.  This could happen during forward
> > iteration over objfiles_list when a separate debug objfile immediately
> > follows it's parent objfile in the list (which is now possible since
> > objfiles are inserted into the list after their parent).  Deletion
> > of the parent would cause deletion of the separate debug objfile,
> > which would invalidate the safe forward iterator's reference to the
> > next objfile in the list.  A safe reverse iterator deletes separate
> > debug objfiles before their parent, so the iterator's reference to
> > the next objfile always stays valid.
> >
> > A small change was also made to a testcase in py-objfile.exp to
> > account for the new placement of separate debug objfiles in
> > objfiles_list.
> > ---
> >  gdb/objfiles.h                          |  22 +++-
> >  gdb/progspace.c                         |   8 +-
> >  gdb/progspace.h                         | 159 +++++++++++++++++++-----
> >  gdb/symfile-debug.c                     | 136 ++++++++++----------
> >  gdb/testsuite/gdb.python/py-objfile.exp |   2 +-
> >  5 files changed, 218 insertions(+), 109 deletions(-)
> >
> > diff --git a/gdb/objfiles.h b/gdb/objfiles.h
> > index 189856f0a51..bb7b0a4579d 100644
> > --- a/gdb/objfiles.h
> > +++ b/gdb/objfiles.h
> > @@ -613,6 +613,17 @@ struct objfile
> >    /* See quick_symbol_functions.  */
> >    void require_partial_symbols (bool verbose);
> >
> > +  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
> > +  void remove_partial_symbol (quick_symbol_functions *target)
> > +  {
> > +    for (quick_symbol_functions_up &qf_up : qf)
> > +      if (qf_up.get () == target)
> > +       {
> > +         qf.remove (qf_up);
> > +         return;
> > +       }
> > +  }
> > +
> >    /* Return the relocation offset applied to SECTION.  */
> >    CORE_ADDR section_offset (bfd_section *section) const
> >    {
> > @@ -699,13 +710,20 @@ struct objfile
> >
> >  private:
> >
> > +  using qf_list = std::forward_list<quick_symbol_functions_up>;
> > +  using unwrapping_qf_range = iterator_range<unwrapping_iterator<qf_list::iterator>>;
> > +  using qf_safe_range = basic_safe_range<unwrapping_qf_range>;
> > +
> >    /* Ensure that partial symbols have been read and return the "quick" (aka
> >       partial) symbol functions for this symbol reader.  */
> > -  const std::forward_list<quick_symbol_functions_up> &
> > +  qf_safe_range
> >    qf_require_partial_symbols ()
> >    {
> >      this->require_partial_symbols (true);
> > -    return qf;
> > +    return qf_safe_range
> > +      (unwrapping_qf_range
> > +       (unwrapping_iterator<qf_list::iterator> (qf.begin ()),
> > +        unwrapping_iterator<qf_list::iterator> (qf.end ())));
> >    }
> >
> >  public:
> > diff --git a/gdb/progspace.c b/gdb/progspace.c
> > index 32bdfebcf7c..1ed75eef2f9 100644
> > --- a/gdb/progspace.c
> > +++ b/gdb/progspace.c
> > @@ -139,19 +139,19 @@ program_space::free_all_objfiles ()
> >
> >  void
> >  program_space::add_objfile (std::unique_ptr<objfile> &&objfile,
> > -                           struct objfile *before)
> > +                           struct objfile *after)
> >  {
> > -  if (before == nullptr)
> > +  if (after == nullptr)
> >      objfiles_list.push_back (std::move (objfile));
> >    else
> >      {
> >        auto iter = std::find_if (objfiles_list.begin (), objfiles_list.end (),
> >                                 [=] (const std::unique_ptr<::objfile> &objf)
> >                                 {
> > -                                 return objf.get () == before;
> > +                                 return objf.get () == after;
> >                                 });
> >        gdb_assert (iter != objfiles_list.end ());
> > -      objfiles_list.insert (iter, std::move (objfile));
> > +      objfiles_list.insert (++iter, std::move (objfile));
> >      }
> >  }
> >
> > diff --git a/gdb/progspace.h b/gdb/progspace.h
> > index 85215f0e2f1..6e33e48c88e 100644
> > --- a/gdb/progspace.h
> > +++ b/gdb/progspace.h
> > @@ -40,56 +40,141 @@ struct address_space;
> >  struct program_space;
> >  struct so_list;
> >
> > +/* An iterator that wraps an iterator over std::unique_ptr, and dereferences
> > +   the returned object.  This is useful for iterating over a list of shared
> > +   pointers and returning raw pointers -- which helped avoid touching a lot
> > +   of code when changing how objfiles are managed.  */
> > +
> > +template<typename UniquePtrIter>
> > +class unwrapping_iterator
> > +{
> > +public:
> > +  typedef unwrapping_iterator self_type;
> > +  typedef typename UniquePtrIter::value_type::pointer value_type;
> > +  typedef typename UniquePtrIter::reference  reference;
> > +  typedef typename UniquePtrIter::pointer pointer;
> > +  typedef typename UniquePtrIter::iterator_category iterator_category;
> > +  typedef typename UniquePtrIter::difference_type difference_type;
> > +
> > +  unwrapping_iterator (UniquePtrIter iter)
> > +    : m_iter (std::move (iter))
> > +  {
> > +  }
> > +
> > +  value_type operator* () const
> > +  {
> > +    return m_iter->get ();
> > +  }
> > +
> > +  unwrapping_iterator operator++ ()
> > +  {
> > +    ++m_iter;
> > +    return *this;
> > +  }
> > +
> > +  bool operator!= (const unwrapping_iterator &other) const
> > +  {
> > +    return m_iter != other.m_iter;
> > +  }
> > +
> > +private:
> > +  /* The underlying iterator.  */
> > +  UniquePtrIter m_iter;
> > +};
> > +
> >  typedef std::list<std::unique_ptr<objfile>> objfile_list;
> >
> > -/* An iterator that wraps an iterator over std::unique_ptr<objfile>,
> > -   and dereferences the returned object.  This is useful for iterating
> > -   over a list of shared pointers and returning raw pointers -- which
> > -   helped avoid touching a lot of code when changing how objfiles are
> > -   managed.  */
> > +/* An reverse iterator that wraps an iterator over objfile_list, and
> > +   dereferences the returned object.  This is useful for reverse iterating
> > +   over a list of shared pointers and returning raw pointers -- which helped
> > +   avoid touching a lot of code when changing how objfiles are managed.  */
> >
> > -class unwrapping_objfile_iterator
> > +class unwrapping_reverse_objfile_iterator
> >  {
> >  public:
> > -
> > -  typedef unwrapping_objfile_iterator self_type;
> > +  typedef unwrapping_reverse_objfile_iterator self_type;
> >    typedef typename ::objfile *value_type;
> >    typedef typename ::objfile &reference;
> >    typedef typename ::objfile **pointer;
> >    typedef typename objfile_list::iterator::iterator_category iterator_category;
> >    typedef typename objfile_list::iterator::difference_type difference_type;
> >
> > -  unwrapping_objfile_iterator (objfile_list::iterator iter)
> > -    : m_iter (std::move (iter))
> > -  {
> > -  }
> > -
> > -  objfile *operator* () const
> > +  value_type operator* () const
> >    {
> >      return m_iter->get ();
> >    }
> >
> > -  unwrapping_objfile_iterator operator++ ()
> > +  unwrapping_reverse_objfile_iterator operator++ ()
> >    {
> > -    ++m_iter;
> > +    if (m_iter != m_begin)
> > +      --m_iter;
> > +    else
> > +      {
> > +       /* We can't decrement M_ITER since it is the begin iterator of the
> > +          objfile list.  Set M_ITER to the list's end iterator to indicate
> > +          this is now one-past-the-end.  */
> > +       m_iter = m_end;
> > +
> > +       /* Overwrite M_BEGIN to avoid possibly copying an invalid iterator.  */
> > +       m_begin = m_end;
> > +      }
> > +
> >      return *this;
> >    }
> >
> > -  bool operator!= (const unwrapping_objfile_iterator &other) const
> > +  bool operator!= (const unwrapping_reverse_objfile_iterator &other) const
> >    {
> >      return m_iter != other.m_iter;
> >    }
> >
> > +  /* Return an unwrapping reverse iterator starting at the last element of
> > +     OBJF_LIST.  */
> > +  static unwrapping_reverse_objfile_iterator begin (objfile_list &objf_list)
> > +  {
> > +    auto begin = objf_list.begin ();
> > +    auto end = objf_list.end ();
> > +    auto rev_begin = objf_list.end ();
> > +
> > +    /* Start REV_BEGIN on the last objfile in OBJF_LIST.  */
> > +    if (begin != end)
> > +      --rev_begin;
> > +
> > +    return unwrapping_reverse_objfile_iterator (rev_begin, begin, end);
> > +  }
> > +
> > +  /* Return a one-past-the-end unwrapping reverse iterator.  */
> > +  static unwrapping_reverse_objfile_iterator end (objfile_list &objf_list)
> > +  {
> > +    return unwrapping_reverse_objfile_iterator (objf_list.end (),
> > +                                               objf_list.end (),
> > +                                               objf_list.end ());
> > +  }
> > +
> >  private:
> > +  /* begin and end methods should be used to create these objects.  */
> > +  unwrapping_reverse_objfile_iterator (objfile_list::iterator iter,
> > +                                      objfile_list::iterator begin,
> > +                                      objfile_list::iterator end)
> > +    : m_iter (std::move (iter)), m_begin (std::move (begin)),
> > +      m_end (std::move (end))
> > +  {
> > +  }
> >
> > -  /* The underlying iterator.  */
> > -  objfile_list::iterator m_iter;
> > -};
> > + /* The underlying iterator.  */
> > + objfile_list::iterator m_iter;
> >
> > + /* The underlying iterator pointing to the first objfile in the sequence.  Used
> > +    to track when to stop decrementing M_ITER.  */
> > + objfile_list::iterator m_begin;
> >
> > -/* A range that returns unwrapping_objfile_iterators.  */
> > +  /* The underlying iterator's one-past-the-end.  */
> > + objfile_list::iterator m_end;
> > +};
> >
> > -using unwrapping_objfile_range = iterator_range<unwrapping_objfile_iterator>;
> > +/* A range that returns unwrapping_iterators.  */
> > +
> > +using unwrapping_objfile_range
> > +  = iterator_range<unwrapping_iterator<objfile_list::iterator>>;
> >
> >  /* A program space represents a symbolic view of an address space.
> >     Roughly speaking, it holds all the data associated with a
> > @@ -209,11 +294,12 @@ struct program_space
> >    objfiles_range objfiles ()
> >    {
> >      return objfiles_range
> > -      (unwrapping_objfile_iterator (objfiles_list.begin ()),
> > -       unwrapping_objfile_iterator (objfiles_list.end ()));
> > +      (unwrapping_iterator<objfile_list::iterator> (objfiles_list.begin ()),
> > +       unwrapping_iterator<objfile_list::iterator> (objfiles_list.end ()));
> >    }
> >
> > -  using objfiles_safe_range = basic_safe_range<objfiles_range>;
> > +  using objfiles_reverse_range = iterator_range<unwrapping_reverse_objfile_iterator>;
> > +  using objfiles_safe_reverse_range = basic_safe_range<objfiles_reverse_range>;
> >
> >    /* An iterable object that can be used to iterate over all objfiles.
> >       The basic use is in a foreach, like:
> > @@ -221,20 +307,25 @@ struct program_space
> >       for (objfile *objf : pspace->objfiles_safe ()) { ... }
> >
> >       This variant uses a basic_safe_iterator so that objfiles can be
> > -     deleted during iteration.  */
> > -  objfiles_safe_range objfiles_safe ()
> > +     deleted during iteration.
> > +
> > +     The use of a reverse iterator helps ensure that separate debug
> > +     objfiles are deleted before their parent objfile.  This prevents
> > +     the invalidation of an iterator due to the deletion of a parent
> > +     objfile.  */
> > +  objfiles_safe_reverse_range objfiles_safe ()
> >    {
> > -    return objfiles_safe_range
> > -      (objfiles_range
> > -        (unwrapping_objfile_iterator (objfiles_list.begin ()),
> > -         unwrapping_objfile_iterator (objfiles_list.end ())));
> > +    return objfiles_safe_reverse_range
> > +      (objfiles_reverse_range
> > +       (unwrapping_reverse_objfile_iterator::begin (objfiles_list),
> > +        unwrapping_reverse_objfile_iterator::end (objfiles_list)));
> >    }
> >
> > -  /* Add OBJFILE to the list of objfiles, putting it just before
> > -     BEFORE.  If BEFORE is nullptr, it will go at the end of the
> > +  /* Add OBJFILE to the list of objfiles, putting it just after
> > +     AFTER.  If AFTER is nullptr, it will go at the end of the
> >       list.  */
> >    void add_objfile (std::unique_ptr<objfile> &&objfile,
> > -                   struct objfile *before);
> > +                   struct objfile *after);
> >
> >    /* Remove OBJFILE from the list of objfiles.  */
> >    void remove_objfile (struct objfile *objfile);
> > diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
> > index 9db5c47a8ce..784b81b5ca6 100644
> > --- a/gdb/symfile-debug.c
> > +++ b/gdb/symfile-debug.c
> > @@ -109,9 +109,9 @@ objfile::has_unexpanded_symtabs ()
> >                 objfile_debug_name (this));
> >
> >    bool result = false;
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> >      {
> > -      if (iter->has_unexpanded_symtabs (this))
> > +      if (qf->has_unexpanded_symtabs (this))
> >         {
> >           result = true;
> >           break;
> > @@ -134,9 +134,9 @@ objfile::find_last_source_symtab ()
> >      gdb_printf (gdb_stdlog, "qf->find_last_source_symtab (%s)\n",
> >                 objfile_debug_name (this));
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> >      {
> > -      retval = iter->find_last_source_symtab (this);
> > +      retval = qf->find_last_source_symtab (this);
> >        if (retval != nullptr)
> >         break;
> >      }
> > @@ -167,8 +167,8 @@ objfile::forget_cached_source_info ()
> >         }
> >      }
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > -    iter->forget_cached_source_info (this);
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > +    qf->forget_cached_source_info (this);
> >  }
> >
> >  bool
> > @@ -214,17 +214,17 @@ objfile::map_symtabs_matching_filename
> >      return result;
> >    };
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> >      {
> > -      if (!iter->expand_symtabs_matching (this,
> > -                                         match_one_filename,
> > -                                         nullptr,
> > -                                         nullptr,
> > -                                         on_expansion,
> > -                                         (SEARCH_GLOBAL_BLOCK
> > -                                          | SEARCH_STATIC_BLOCK),
> > -                                         UNDEF_DOMAIN,
> > -                                         ALL_DOMAIN))
> > +      if (!qf->expand_symtabs_matching (this,
> > +                                       match_one_filename,
> > +                                       nullptr,
> > +                                       nullptr,
> > +                                       on_expansion,
> > +                                       (SEARCH_GLOBAL_BLOCK
> > +                                        | SEARCH_STATIC_BLOCK),
> > +                                       UNDEF_DOMAIN,
> > +                                       ALL_DOMAIN))
> >         {
> >           retval = false;
> >           break;
> > @@ -283,18 +283,18 @@ objfile::lookup_symbol (block_enum kind, const char *name, domain_enum domain)
> >      return true;
> >    };
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> >      {
> > -      if (!iter->expand_symtabs_matching (this,
> > -                                         nullptr,
> > -                                         &lookup_name,
> > -                                         nullptr,
> > -                                         search_one_symtab,
> > -                                         kind == GLOBAL_BLOCK
> > -                                         ? SEARCH_GLOBAL_BLOCK
> > -                                         : SEARCH_STATIC_BLOCK,
> > -                                         domain,
> > -                                         ALL_DOMAIN))
> > +      if (!qf->expand_symtabs_matching (this,
> > +                                       nullptr,
> > +                                       &lookup_name,
> > +                                       nullptr,
> > +                                       search_one_symtab,
> > +                                       kind == GLOBAL_BLOCK
> > +                                       ? SEARCH_GLOBAL_BLOCK
> > +                                       : SEARCH_STATIC_BLOCK,
> > +                                       domain,
> > +                                       ALL_DOMAIN))
> >         break;
> >      }
> >
> > @@ -314,8 +314,8 @@ objfile::print_stats (bool print_bcache)
> >      gdb_printf (gdb_stdlog, "qf->print_stats (%s, %d)\n",
> >                 objfile_debug_name (this), print_bcache);
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > -    iter->print_stats (this, print_bcache);
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > +    qf->print_stats (this, print_bcache);
> >  }
> >
> >  void
> > @@ -340,16 +340,16 @@ objfile::expand_symtabs_for_function (const char *func_name)
> >    lookup_name_info base_lookup (func_name, symbol_name_match_type::FULL);
> >    lookup_name_info lookup_name = base_lookup.make_ignore_params ();
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > -    iter->expand_symtabs_matching (this,
> > -                                  nullptr,
> > -                                  &lookup_name,
> > -                                  nullptr,
> > -                                  nullptr,
> > -                                  (SEARCH_GLOBAL_BLOCK
> > -                                   | SEARCH_STATIC_BLOCK),
> > -                                  VAR_DOMAIN,
> > -                                  ALL_DOMAIN);
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > +    qf->expand_symtabs_matching (this,
> > +                                nullptr,
> > +                                &lookup_name,
> > +                                nullptr,
> > +                                nullptr,
> > +                                (SEARCH_GLOBAL_BLOCK
> > +                                 | SEARCH_STATIC_BLOCK),
> > +                                VAR_DOMAIN,
> > +                                ALL_DOMAIN);
> >  }
> >
> >  void
> > @@ -359,8 +359,8 @@ objfile::expand_all_symtabs ()
> >      gdb_printf (gdb_stdlog, "qf->expand_all_symtabs (%s)\n",
> >                 objfile_debug_name (this));
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > -    iter->expand_all_symtabs (this);
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > +    qf->expand_all_symtabs (this);
> >  }
> >
> >  void
> > @@ -377,16 +377,16 @@ objfile::expand_symtabs_with_fullname (const char *fullname)
> >      return filename_cmp (basenames ? basename : fullname, filename) == 0;
> >    };
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > -    iter->expand_symtabs_matching (this,
> > -                                  file_matcher,
> > -                                  nullptr,
> > -                                  nullptr,
> > -                                  nullptr,
> > -                                  (SEARCH_GLOBAL_BLOCK
> > -                                   | SEARCH_STATIC_BLOCK),
> > -                                  UNDEF_DOMAIN,
> > -                                  ALL_DOMAIN);
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > +    qf->expand_symtabs_matching (this,
> > +                                file_matcher,
> > +                                nullptr,
> > +                                nullptr,
> > +                                nullptr,
> > +                                (SEARCH_GLOBAL_BLOCK
> > +                                 | SEARCH_STATIC_BLOCK),
> > +                                UNDEF_DOMAIN,
> > +                                ALL_DOMAIN);
> >  }
> >
> >  void
> > @@ -402,9 +402,9 @@ objfile::expand_matching_symbols
> >                 domain_name (domain), global,
> >                 host_address_to_string (ordered_compare));
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > -    iter->expand_matching_symbols (this, name, domain, global,
> > -                                  ordered_compare);
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > +    qf->expand_matching_symbols (this, name, domain, global,
> > +                                ordered_compare);
> >  }
> >
> >  bool
> > @@ -429,10 +429,10 @@ objfile::expand_symtabs_matching
> >                 host_address_to_string (&expansion_notify),
> >                 search_domain_name (kind));
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > -    if (!iter->expand_symtabs_matching (this, file_matcher, lookup_name,
> > -                                       symbol_matcher, expansion_notify,
> > -                                       search_flags, domain, kind))
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > +    if (!qf->expand_symtabs_matching (this, file_matcher, lookup_name,
> > +                                     symbol_matcher, expansion_notify,
> > +                                     search_flags, domain, kind))
> >        return false;
> >    return true;
> >  }
> > @@ -454,10 +454,10 @@ objfile::find_pc_sect_compunit_symtab (struct bound_minimal_symbol msymbol,
> >                 host_address_to_string (section),
> >                 warn_if_readin);
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> >      {
> > -      retval = iter->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
> > -                                                  warn_if_readin);
> > +      retval = qf->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
> > +                                                warn_if_readin);
> >        if (retval != nullptr)
> >         break;
> >      }
> > @@ -482,8 +482,8 @@ objfile::map_symbol_filenames (gdb::function_view<symbol_filename_ftype> fun,
> >                 objfile_debug_name (this),
> >                 need_fullname);
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > -    iter->map_symbol_filenames (this, fun, need_fullname);
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > +    qf->map_symbol_filenames (this, fun, need_fullname);
> >  }
> >
> >  struct compunit_symtab *
> > @@ -496,9 +496,9 @@ objfile::find_compunit_symtab_by_address (CORE_ADDR address)
> >                 hex_string (address));
> >
> >    struct compunit_symtab *result = NULL;
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> >      {
> > -      result = iter->find_compunit_symtab_by_address (this, address);
> > +      result = qf->find_compunit_symtab_by_address (this, address);
> >        if (result != nullptr)
> >         break;
> >      }
> > @@ -521,10 +521,10 @@ objfile::lookup_global_symbol_language (const char *name,
> >    enum language result = language_unknown;
> >    *symbol_found_p = false;
> >
> > -  for (const auto &iter : qf_require_partial_symbols ())
> > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> >      {
> > -      result = iter->lookup_global_symbol_language (this, name, domain,
> > -                                                   symbol_found_p);
> > +      result = qf->lookup_global_symbol_language (this, name, domain,
> > +                                                 symbol_found_p);
> >        if (*symbol_found_p)
> >         break;
> >      }
> > diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp
> > index 61b9942de79..0bf49976b73 100644
> > --- a/gdb/testsuite/gdb.python/py-objfile.exp
> > +++ b/gdb/testsuite/gdb.python/py-objfile.exp
> > @@ -135,7 +135,7 @@ gdb_test "p main" "= {<text variable, no debug info>} $hex <main>" \
> >  gdb_py_test_silent_cmd "python objfile.add_separate_debug_file(\"${binfile}\")" \
> >      "Add separate debug file file" 1
> >
> > -gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[0\]" \
> > +gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[1\]" \
> >      "Get separate debug info objfile" 1
> >
> >  gdb_test "python print (sep_objfile.owner.filename)" "${testfile}2" \
> > --
> > 2.40.1
> >


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PING*2][PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading
  2023-06-15 13:44   ` Aaron Merey
@ 2023-07-03 17:39     ` Aaron Merey
  0 siblings, 0 replies; 33+ messages in thread
From: Aaron Merey @ 2023-07-03 17:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Ping

Thanks,
Aaron

On Thu, Jun 15, 2023 at 9:44 AM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Wed, May 31, 2023 at 9:44 PM Aaron Merey <amerey@redhat.com> wrote:
> >
> > v2.2: https://sourceware.org/pipermail/gdb-patches/2023-May/199326.html
> >
> > v3 includes testcases instead of adding them in a separate patch.
> > Tests related to section downloading are now included in
> > testsuite/gdb.debuginfod/section.exp.
> >
> > Commit message:
> >
> > At the beginning of a session, gdb may attempt to download debuginfo
> > for all shared libraries associated with the process or core file
> > being debugged.  This can be a waste of time and storage space when much
> > of the debuginfo ends up not being used during the session.
> >
> > To reduce the gdb's startup latency and to download only the debuginfo
> > that is really needed, this patch adds on-demand downloading of debuginfo.
> >
> > 'set debuginfo enabled on' now causes gdb to attempt to download a .gdb_index
> > for each shared library instead of its full debuginfo.  Each corresponding
> > separate debuginfo will be deferred until gdb needs to expand symtabs
> > associated with the debuginfo's index.
> >
> > Because these indices are significantly smaller than their corresponding
> > debuginfo, this generally reduces the total amount of data gdb downloads.
> > Reductions of 80%-95% have been observed when debugging large GUI programs.
> >
> >     (gdb) set debuginfod enabled on
> >     (gdb) start
> >     Downloading section .gdb_index for /lib64/libcurl.so.4
> >     [...]
> >     1826        client->server_mhandle = curl_multi_init ();
> >     (gdb) step
> >     Downloading separate debug info for /lib64/libcurl.so.4
> >     Downloading separate debug info for [libcurl dwz]
> >     Downloading source file /usr/src/debug/curl-7.85.0-6.fc37.x86_64/build-full/lib/../../lib/multi.c
> >     curl_multi_init () at ../../lib/multi.c:457
> >     457     {
> >     (gdb)
> >
> > Some of the key functions below include dwarf2_has_separate_index which
> > downloads the separate .gdb_index.  If successful, the shared library
> > objfile owns the index until the separate debug objfile is downloaded
> > or confirmed to not be available.
> >
> > read_full_dwarf_from_debuginfod downloads the full debuginfo and
> > initializes the separate debug objfile.  It is called by functions
> > such as dwarf2_gdb_index::expand_symtabs_matching and
> > dwarf2_base_index_functions::find_pc_sect_compunit_symtab when symtab
> > expansion is required.
> > ---
> >  gdb/dwarf2/frame.c                         |  13 ++
> >  gdb/dwarf2/frame.h                         |   4 +
> >  gdb/dwarf2/index-cache.c                   |  33 ++++
> >  gdb/dwarf2/index-cache.h                   |  13 ++
> >  gdb/dwarf2/public.h                        |   7 +
> >  gdb/dwarf2/read-gdb-index.c                | 156 ++++++++++++++++--
> >  gdb/dwarf2/read.c                          | 146 ++++++++++++++++-
> >  gdb/dwarf2/read.h                          |  10 ++
> >  gdb/dwarf2/section.c                       |   3 +-
> >  gdb/elfread.c                              |   2 +-
> >  gdb/objfile-flags.h                        |   4 +
> >  gdb/objfiles.h                             |  17 +-
> >  gdb/quick-symbol.h                         |   4 +
> >  gdb/symfile.c                              |  13 +-
> >  gdb/symtab.c                               |  18 +-
> >  gdb/testsuite/gdb.debuginfod/libsection1.c |  40 +++++
> >  gdb/testsuite/gdb.debuginfod/libsection2.c |  37 +++++
> >  gdb/testsuite/gdb.debuginfod/section.c     |  29 ++++
> >  gdb/testsuite/gdb.debuginfod/section.exp   | 181 +++++++++++++++++++++
> >  gdb/testsuite/lib/debuginfod-support.exp   |  30 ++++
> >  20 files changed, 734 insertions(+), 26 deletions(-)
> >  create mode 100644 gdb/testsuite/gdb.debuginfod/libsection1.c
> >  create mode 100644 gdb/testsuite/gdb.debuginfod/libsection2.c
> >  create mode 100644 gdb/testsuite/gdb.debuginfod/section.c
> >  create mode 100644 gdb/testsuite/gdb.debuginfod/section.exp
> >
> > diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
> > index a561aaf3100..3613f8252a7 100644
> > --- a/gdb/dwarf2/frame.c
> > +++ b/gdb/dwarf2/frame.c
> > @@ -1609,6 +1609,19 @@ set_comp_unit (struct objfile *objfile, struct comp_unit *unit)
> >    return dwarf2_frame_bfd_data.set (abfd, unit);
> >  }
> >
> > +/* See frame.h.  */
> > +
> > +void
> > +dwarf2_clear_frame_data (struct objfile *objfile)
> > +{
> > +  bfd *abfd = objfile->obfd.get ();
> > +
> > +  if (gdb_bfd_requires_relocations (abfd))
> > +    dwarf2_frame_objfile_data.clear (objfile);
> > +  else
> > +    dwarf2_frame_bfd_data.clear (abfd);
> > +}
> > +
> >  /* Find the FDE for *PC.  Return a pointer to the FDE, and store the
> >     initial location associated with it into *PC.  */
> >
> > diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
> > index 5643e557513..2391e313e7c 100644
> > --- a/gdb/dwarf2/frame.h
> > +++ b/gdb/dwarf2/frame.h
> > @@ -238,6 +238,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
> >  extern const struct frame_base *
> >    dwarf2_frame_base_sniffer (frame_info_ptr this_frame);
> >
> > +/* Delete OBJFILEs comp_unit.  */
> > +
> > +extern void dwarf2_clear_frame_data (struct objfile * objfile);
> > +
> >  /* Compute the DWARF CFA for a frame.  */
> >
> >  CORE_ADDR dwarf2_frame_cfa (frame_info_ptr this_frame);
> > diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
> > index 79ab706ee9d..bbafcd321b2 100644
> > --- a/gdb/dwarf2/index-cache.c
> > +++ b/gdb/dwarf2/index-cache.c
> > @@ -216,6 +216,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
> >    return {};
> >  }
> >
> > +/* See index-cache.h.  */
> > +
> > +gdb::array_view<const gdb_byte>
> > +index_cache::lookup_gdb_index_debuginfod (const char *index_path,
> > +                                         std::unique_ptr<index_cache_resource> *resource)
> > +{
> > +  try
> > +    {
> > +      /* Try to map that file.  */
> > +      index_cache_resource_mmap *mmap_resource
> > +       = new index_cache_resource_mmap (index_path);
> > +
> > +      /* Hand the resource to the caller.  */
> > +      resource->reset (mmap_resource);
> > +
> > +      return gdb::array_view<const gdb_byte>
> > +         ((const gdb_byte *) mmap_resource->mapping.get (),
> > +          mmap_resource->mapping.size ());
> > +    }
> > +  catch (const gdb_exception_error &except)
> > +    {
> > +      warning (_("Unable to read %s: %s"), index_path, except.what ());
> > +    }
> > +
> > +  return {};
> > +}
> > +
> >  #else /* !HAVE_SYS_MMAN_H */
> >
> >  /* See dwarf-index-cache.h.  This is a no-op on unsupported systems.  */
> > @@ -227,6 +254,12 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
> >    return {};
> >  }
> >
> > +gdb::array_view<const gdb_byte>
> > +index_cache::lookup_gdb_index_debuginfod (const char *index_path,
> > +                                         std::unique_ptr<index_cache_resource> *resource)
> > +{
> > +  return {};
> > +}
> >  #endif
> >
> >  /* See dwarf-index-cache.h.  */
> > diff --git a/gdb/dwarf2/index-cache.h b/gdb/dwarf2/index-cache.h
> > index 1efff17049f..e400afd5123 100644
> > --- a/gdb/dwarf2/index-cache.h
> > +++ b/gdb/dwarf2/index-cache.h
> > @@ -67,6 +67,19 @@ class index_cache
> >    lookup_gdb_index (const bfd_build_id *build_id,
> >                     std::unique_ptr<index_cache_resource> *resource);
> >
> > +  /* Look for an index file located at INDEX_PATH in the debuginfod cache.
> > +     Unlike lookup_gdb_index, this function does not exit early if the
> > +     index cache has not been enabled.
> > +
> > +     If found, return the contents as an array_view and store the underlying
> > +     resources (allocated memory, mapped file, etc) in RESOURCE.  The returned
> > +     array_view is valid as long as RESOURCE is not destroyed.
> > +
> > +     If no matching index file is found, return an empty array view.  */
> > +  gdb::array_view<const gdb_byte>
> > +  lookup_gdb_index_debuginfod (const char *index_path,
> > +                              std::unique_ptr<index_cache_resource> *resource);
> > +
> >    /* Return the number of cache hits.  */
> >    unsigned int n_hits () const
> >    { return m_n_hits; }
> > diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h
> > index 0e74857eb1a..4a44cdbc223 100644
> > --- a/gdb/dwarf2/public.h
> > +++ b/gdb/dwarf2/public.h
> > @@ -40,4 +40,11 @@ extern void dwarf2_initialize_objfile (struct objfile *objfile);
> >
> >  extern void dwarf2_build_frame_info (struct objfile *);
> >
> > +/* Query debuginfod for the .gdb_index associated with OBJFILE.  If
> > +   successful, create an objfile to hold the .gdb_index information
> > +   and act as a placeholder until the full debuginfo needs to be
> > +   downloaded.  */
> > +
> > +extern bool dwarf2_has_separate_index (struct objfile *);
> > +
> >  #endif /* DWARF2_PUBLIC_H */
> > diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> > index 1006386cb2d..d3516e92361 100644
> > --- a/gdb/dwarf2/read-gdb-index.c
> > +++ b/gdb/dwarf2/read-gdb-index.c
> > @@ -136,6 +136,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >       gdb.dwarf2/gdb-index.exp testcase.  */
> >    void dump (struct objfile *objfile) override;
> >
> > +  /* Calls do_expand_matching_symbols and downloads debuginfo if necessary.  */
> >    void expand_matching_symbols
> >      (struct objfile *,
> >       const lookup_name_info &lookup_name,
> > @@ -143,6 +144,14 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >       int global,
> >       symbol_compare_ftype *ordered_compare) override;
> >
> > +  void do_expand_matching_symbols
> > +    (struct objfile *,
> > +     const lookup_name_info &lookup_name,
> > +     domain_enum domain,
> > +     int global,
> > +     symbol_compare_ftype *ordered_compare);
> > +
> > +  /* Calls do_expand_symtabs_matching and downloads debuginfo if necessary.  */
> >    bool expand_symtabs_matching
> >      (struct objfile *objfile,
> >       gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> > @@ -152,8 +161,59 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >       block_search_flags search_flags,
> >       domain_enum domain,
> >       enum search_domain kind) override;
> > +
> > +  bool do_expand_symtabs_matching
> > +    (struct objfile *objfile,
> > +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> > +     const lookup_name_info *lookup_name,
> > +     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> > +     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> > +     block_search_flags search_flags,
> > +     domain_enum domain,
> > +     enum search_domain kind);
> > +
> > +  /* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
> > +     debuginfo if necessary.  */
> > +  void expand_all_symtabs (struct objfile *objfile) override;
> > +
> > +  /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> > +     debuginfo if necessary.  */
> > +  struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> >  };
> >
> > +void
> > +dwarf2_gdb_index::expand_all_symtabs (struct objfile *objfile)
> > +{
> > +  try
> > +    {
> > +      dwarf2_base_index_functions::expand_all_symtabs (objfile);
> > +    }
> > +  catch (gdb_exception e)
> > +    {
> > +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> > +       exception_print (gdb_stderr, e);
> > +      else
> > +       read_full_dwarf_from_debuginfod (objfile, this);
> > +    }
> > +}
> > +
> > +struct symtab *
> > +dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
> > +{
> > +  try
> > +    {
> > +      return dwarf2_base_index_functions::find_last_source_symtab (objfile);
> > +    }
> > +  catch (gdb_exception e)
> > +    {
> > +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> > +       exception_print (gdb_stderr, e);
> > +      else
> > +       read_full_dwarf_from_debuginfod (objfile, this);
> > +      return nullptr;
> > +    }
> > +}
> > +
> >  /* This dumps minimal information about the index.
> >     It is called via "mt print objfiles".
> >     One use is to verify .gdb_index has been loaded by the
> > @@ -315,7 +375,7 @@ dw2_symtab_iter_next (struct dw2_symtab_iterator *iter,
> >  }
> >
> >  void
> > -dwarf2_gdb_index::expand_matching_symbols
> > +dwarf2_gdb_index::do_expand_matching_symbols
> >    (struct objfile *objfile,
> >     const lookup_name_info &name, domain_enum domain,
> >     int global,
> > @@ -353,6 +413,29 @@ dwarf2_gdb_index::expand_matching_symbols
> >      }, per_objfile);
> >  }
> >
> > +void
> > +dwarf2_gdb_index::expand_matching_symbols
> > +  (struct objfile *objfile,
> > +   const lookup_name_info &lookup_name,
> > +   domain_enum domain,
> > +   int global,
> > +   symbol_compare_ftype *ordered_compare)
> > +{
> > +  try
> > +    {
> > +      do_expand_matching_symbols (objfile, lookup_name, domain,
> > +                                 global, ordered_compare);
> > +    }
> > +  catch (gdb_exception e)
> > +    {
> > +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> > +       exception_print (gdb_stderr, e);
> > +      else
> > +       read_full_dwarf_from_debuginfod (objfile, this);
> > +      return;
> > +    }
> > +}
> > +
> >  /* Helper for dw2_expand_matching symtabs.  Called on each symbol
> >     matched, to expand corresponding CUs that were marked.  IDX is the
> >     index of the symbol name that matched.  */
> > @@ -455,7 +538,7 @@ dw2_expand_marked_cus
> >  }
> >
> >  bool
> > -dwarf2_gdb_index::expand_symtabs_matching
> > +dwarf2_gdb_index::do_expand_symtabs_matching
> >      (struct objfile *objfile,
> >       gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> >       const lookup_name_info *lookup_name,
> > @@ -504,6 +587,39 @@ dwarf2_gdb_index::expand_symtabs_matching
> >    return result;
> >  }
> >
> > +bool
> > +dwarf2_gdb_index::expand_symtabs_matching
> > +    (struct objfile *objfile,
> > +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> > +     const lookup_name_info *lookup_name,
> > +     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> > +     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> > +     block_search_flags search_flags,
> > +     domain_enum domain,
> > +     enum search_domain kind)
> > +{
> > +  if (objfile->flags & OBJF_READNEVER)
> > +    return false;
> > +
> > +  try
> > +    {
> > +      return do_expand_symtabs_matching (objfile, file_matcher, lookup_name,
> > +                                        symbol_matcher, expansion_notify,
> > +                                        search_flags, domain, kind);
> > +    }
> > +  catch (gdb_exception e)
> > +    {
> > +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> > +       {
> > +         exception_print (gdb_stderr, e);
> > +         return false;
> > +       }
> > +
> > +      read_full_dwarf_from_debuginfod (objfile, this);
> > +      return true;
> > +    }
> > +}
> > +
> >  quick_symbol_functions_up
> >  mapped_gdb_index::make_quick_functions () const
> >  {
> > @@ -797,28 +913,32 @@ dwarf2_read_gdb_index
> >
> >    /* If there is a .dwz file, read it so we can get its CU list as
> >       well.  */
> > -  dwz = dwarf2_get_dwz_file (per_bfd);
> > -  if (dwz != NULL)
> > +  if (get_gdb_index_contents_dwz != nullptr)
> >      {
> >        mapped_gdb_index dwz_map;
> >        const gdb_byte *dwz_types_ignore;
> >        offset_type dwz_types_elements_ignore;
> > +      dwz = dwarf2_get_dwz_file (per_bfd);
> >
> > -      gdb::array_view<const gdb_byte> dwz_index_content
> > -       = get_gdb_index_contents_dwz (objfile, dwz);
> > -
> > -      if (dwz_index_content.empty ())
> > -       return 0;
> > -
> > -      if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()),
> > -                                      1, dwz_index_content, &dwz_map,
> > -                                      &dwz_list, &dwz_list_elements,
> > -                                      &dwz_types_ignore,
> > -                                      &dwz_types_elements_ignore))
> > +      if (dwz != nullptr)
> >         {
> > -         warning (_("could not read '.gdb_index' section from %s; skipping"),
> > -                  bfd_get_filename (dwz->dwz_bfd.get ()));
> > -         return 0;
> > +         gdb::array_view<const gdb_byte> dwz_index_content
> > +           = get_gdb_index_contents_dwz (objfile, dwz);
> > +
> > +         if (dwz_index_content.empty ())
> > +           return 0;
> > +
> > +         if (!read_gdb_index_from_buffer (bfd_get_filename
> > +                                            (dwz->dwz_bfd.get ()),
> > +                                          1, dwz_index_content, &dwz_map,
> > +                                          &dwz_list, &dwz_list_elements,
> > +                                          &dwz_types_ignore,
> > +                                          &dwz_types_elements_ignore))
> > +           {
> > +             warning (_("could not read '.gdb_index' section from %s; skipping"),
> > +                      bfd_get_filename (dwz->dwz_bfd.get ()));
> > +               return 0;
> > +           }
> >         }
> >      }
> >
> > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> > index 4828409222c..96d1ff53d91 100644
> > --- a/gdb/dwarf2/read.c
> > +++ b/gdb/dwarf2/read.c
> > @@ -34,6 +34,7 @@
> >  #include "dwarf2/attribute.h"
> >  #include "dwarf2/comp-unit-head.h"
> >  #include "dwarf2/cu.h"
> > +#include "dwarf2/frame.h"
> >  #include "dwarf2/index-cache.h"
> >  #include "dwarf2/index-common.h"
> >  #include "dwarf2/leb.h"
> > @@ -95,6 +96,8 @@
> >  #include "split-name.h"
> >  #include "gdbsupport/parallel-for.h"
> >  #include "gdbsupport/thread-pool.h"
> > +#include "inferior.h"
> > +#include "debuginfod-support.h"
> >
> >  /* When == 1, print basic high level tracing messages.
> >     When > 1, be more verbose.
> > @@ -3168,7 +3171,7 @@ dwarf2_base_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd,
> >  }
> >
> >  struct compunit_symtab *
> > -dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> > +dwarf2_base_index_functions::do_find_pc_sect_compunit_symtab
> >       (struct objfile *objfile,
> >        struct bound_minimal_symbol msymbol,
> >        CORE_ADDR pc,
> > @@ -3199,6 +3202,32 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> >    return result;
> >  }
> >
> > +struct compunit_symtab *
> > +dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> > +     (struct objfile *objfile,
> > +      struct bound_minimal_symbol msymbol,
> > +      CORE_ADDR pc,
> > +      struct obj_section *section,
> > +      int warn_if_readin)
> > +{
> > +  if (objfile->flags & OBJF_READNEVER)
> > +    return nullptr;
> > +
> > +  try
> > +    {
> > +      return do_find_pc_sect_compunit_symtab (objfile, msymbol, pc,
> > +                                             section, warn_if_readin);
> > +    }
> > +  catch (gdb_exception e)
> > +    {
> > +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> > +       exception_print (gdb_stderr, e);
> > +      else
> > +       read_full_dwarf_from_debuginfod (objfile, this);
> > +      return nullptr;
> > +    }
> > +}
> > +
> >  void
> >  dwarf2_base_index_functions::map_symbol_filenames
> >       (struct objfile *objfile,
> > @@ -3355,6 +3384,29 @@ get_gdb_index_contents_from_cache_dwz (objfile *obj, dwz_file *dwz)
> >    return global_index_cache.lookup_gdb_index (build_id, &dwz->index_cache_res);
> >  }
> >
> > +/* Query debuginfod for the .gdb_index matching OBJFILE's build-id.  Return the
> > +   contents if successful.  */
> > +
> > +static gdb::array_view<const gdb_byte>
> > +get_gdb_index_contents_from_debuginfod (objfile *objfile, dwarf2_per_bfd *per_bfd)
> > +{
> > +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > +  if (build_id == nullptr)
> > +    return {};
> > +
> > +  gdb::unique_xmalloc_ptr<char> index_path;
> > +  scoped_fd fd = debuginfod_section_query (build_id->data, build_id->size,
> > +                                          bfd_get_filename
> > +                                            (objfile->obfd.get ()),
> > +                                          ".gdb_index",
> > +                                          &index_path);
> > +  if (fd.get () < 0)
> > +    return {};
> > +
> > +  return global_index_cache.lookup_gdb_index_debuginfod
> > +    (index_path.get (), &per_bfd->index_cache_res);
> > +}
> > +
> >  static quick_symbol_functions_up make_cooked_index_funcs ();
> >
> >  /* See dwarf2/public.h.  */
> > @@ -3420,10 +3472,102 @@ dwarf2_initialize_objfile (struct objfile *objfile)
> >        return;
> >      }
> >
> > +  if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED)
> > +      && dwarf2_read_gdb_index (per_objfile,
> > +                               get_gdb_index_contents_from_debuginfod,
> > +                               nullptr))
> > +    {
> > +      dwarf_read_debug_printf ("found .gdb_index from debuginfod");
> > +      objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
> > +      objfile->qf.begin ()->get ()->from_separate_index = true;
> > +      return;
> > +    }
> > +
> >    global_index_cache.miss ();
> >    objfile->qf.push_front (make_cooked_index_funcs ());
> >  }
> >
> > +/* See read.h.  */
> > +
> > +void
> > +read_full_dwarf_from_debuginfod (struct objfile *objfile,
> > +                                dwarf2_base_index_functions *fncs)
> > +{
> > +  gdb_assert (objfile->flags & OBJF_DOWNLOAD_DEFERRED);
> > +
> > +  const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > +  const char *filename;
> > +  gdb_bfd_ref_ptr debug_bfd;
> > +  gdb::unique_xmalloc_ptr<char> symfile_path;
> > +  scoped_fd fd;
> > +
> > +  if (build_id == nullptr)
> > +    goto unset;
> > +
> > +  filename = bfd_get_filename (objfile->obfd.get ());
> > +  fd = debuginfod_debuginfo_query (build_id->data, build_id->size,
> > +                                  filename, &symfile_path);
> > +  if (fd.get () < 0)
> > +    goto unset;
> > +
> > +  /* Separate debuginfo successfully retrieved from server.  */
> > +  debug_bfd = symfile_bfd_open (symfile_path.get ());
> > +  if (debug_bfd == nullptr
> > +      || !build_id_verify (debug_bfd.get (), build_id->size, build_id->data))
> > +    {
> > +      warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
> > +              filename);
> > +      goto unset;
> > +    }
> > +
> > +  /* Clear frame data so it can be recalculated using DWARF.  */
> > +  dwarf2_clear_frame_data (objfile);
> > +
> > +  /* This may also trigger a dwz download.  */
> > +  symbol_file_add_separate (debug_bfd, symfile_path.get (),
> > +                           current_inferior ()->symfile_flags, objfile);
> > +
> > +unset:
> > +  objfile->remove_deferred_status ();
> > +}
> > +
> > +/* See public.h.  */
> > +
> > +bool
> > +dwarf2_has_separate_index (struct objfile *objfile)
> > +{
> > +  if (objfile->flags & OBJF_DOWNLOAD_DEFERRED)
> > +    return true;
> > +  if (objfile->flags & OBJF_MAINLINE)
> > +    return false;
> > +  if (!IS_DIR_SEPARATOR (*objfile_filename (objfile)))
> > +    return false;
> > +
> > +  gdb::unique_xmalloc_ptr<char> index_path;
> > +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > +
> > +  if (build_id == nullptr)
> > +    return false;
> > +
> > +  scoped_fd fd = debuginfod_section_query (build_id->data,
> > +                                          build_id->size,
> > +                                          bfd_get_filename
> > +                                            (objfile->obfd.get ()),
> > +                                          ".gdb_index",
> > +                                          &index_path);
> > +
> > +  if (fd.get () < 0)
> > +    return false;
> > +
> > +  /* We found a separate .gdb_index file so a separate debuginfo file
> > +     should exist, but we don't want to download it until necessary.
> > +     Attach the index to this objfile and defer the debuginfo download
> > +     until gdb needs to expand symtabs referenced by the index.  */
> > +  objfile->flags |= OBJF_DOWNLOAD_DEFERRED;
> > +  dwarf2_initialize_objfile (objfile);
> > +  return true;
> > +}
> > +
> >
> >
> >  /* Build a partial symbol table.  */
> > diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> > index 37023a20709..e3131693b81 100644
> > --- a/gdb/dwarf2/read.h
> > +++ b/gdb/dwarf2/read.h
> > @@ -866,6 +866,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
> >       CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
> >         override final;
> >
> > +  struct compunit_symtab *do_find_pc_sect_compunit_symtab
> > +    (struct objfile *objfile, struct bound_minimal_symbol msymbol,
> > +     CORE_ADDR pc, struct obj_section *section, int warn_if_readin);
> > +
> >    struct compunit_symtab *find_compunit_symtab_by_address
> >      (struct objfile *objfile, CORE_ADDR address) override
> >    {
> > @@ -942,4 +946,10 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
> >                                        dwarf2_section_info *section,
> >                                        addrmap *mutable_map);
> >
> > +/* If OBJFILE contains information from a separately downloaded .gdb_index,
> > +   attempt to download the full debuginfo.  */
> > +
> > +extern void read_full_dwarf_from_debuginfod (struct objfile *,
> > +                                            dwarf2_base_index_functions *);
> > +
> >  #endif /* DWARF2READ_H */
> > diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c
> > index c9ef41893ee..8cb09e3381a 100644
> > --- a/gdb/dwarf2/section.c
> > +++ b/gdb/dwarf2/section.c
> > @@ -54,7 +54,8 @@ dwarf2_section_info::get_bfd_owner () const
> >        section = get_containing_section ();
> >        gdb_assert (!section->is_virtual);
> >      }
> > -  gdb_assert (section->s.section != nullptr);
> > +  if (section->s.section == nullptr)
> > +    error (_("Can't find owner of DWARF section."));
> >    return section->s.section->owner;
> >  }
> >
> > diff --git a/gdb/elfread.c b/gdb/elfread.c
> > index 799e3b914f8..133341ea615 100644
> > --- a/gdb/elfread.c
> > +++ b/gdb/elfread.c
> > @@ -1242,7 +1242,7 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
> >             symbol_file_add_separate (debug_bfd, debugfile.c_str (),
> >                                       symfile_flags, objfile);
> >         }
> > -      else
> > +      else if (!dwarf2_has_separate_index (objfile))
> >         {
> >           has_dwarf2 = false;
> >           const struct bfd_build_id *build_id
> > diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h
> > index 9dee2ee51a0..fb3f741c899 100644
> > --- a/gdb/objfile-flags.h
> > +++ b/gdb/objfile-flags.h
> > @@ -60,6 +60,10 @@ enum objfile_flag : unsigned
> >      /* User requested that we do not read this objfile's symbolic
> >         information.  */
> >      OBJF_READNEVER = 1 << 6,
> > +
> > +    /* A separate .gdb_index has been downloaded for this objfile.
> > +       Debuginfo for this objfile can be downloaded when required.  */
> > +    OBJF_DOWNLOAD_DEFERRED = 1 << 7,
> >    };
> >
> >  DEF_ENUM_FLAGS_TYPE (enum objfile_flag, objfile_flags);
> > diff --git a/gdb/objfiles.h b/gdb/objfiles.h
> > index bb7b0a4579d..57bc1d45965 100644
> > --- a/gdb/objfiles.h
> > +++ b/gdb/objfiles.h
> > @@ -613,11 +613,22 @@ struct objfile
> >    /* See quick_symbol_functions.  */
> >    void require_partial_symbols (bool verbose);
> >
> > -  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
> > -  void remove_partial_symbol (quick_symbol_functions *target)
> > +  /*  Indicate that the aquisition of this objfile's separate debug objfile
> > +      is no longer deferred.  Used when the debug objfile has been aquired
> > +      or could not be found.  */
> > +  void remove_deferred_status ()
> >    {
> > +    flags &= ~OBJF_DOWNLOAD_DEFERRED;
> > +
> > +   /* Remove quick_symbol_functions derived from a separately downloaded
> > +      index.  If available the separate debug objfile's index will be used
> > +      instead, since that objfile actually contains the symbols and CUs
> > +      referenced in the index.
> > +
> > +      No more than one element of qf should have from_separate_index set
> > +      to true.  */
> >      for (quick_symbol_functions_up &qf_up : qf)
> > -      if (qf_up.get () == target)
> > +      if (qf_up->from_separate_index)
> >         {
> >           qf.remove (qf_up);
> >           return;
> > diff --git a/gdb/quick-symbol.h b/gdb/quick-symbol.h
> > index a7fea2ccb49..e7163503e39 100644
> > --- a/gdb/quick-symbol.h
> > +++ b/gdb/quick-symbol.h
> > @@ -225,6 +225,10 @@ struct quick_symbol_functions
> >    virtual void read_partial_symbols (struct objfile *objfile)
> >    {
> >    }
> > +
> > +  /* True if this quick_symbol_functions is derived from a separately
> > +     downloaded index.  */
> > +  bool from_separate_index = false;
> >  };
> >
> >  typedef std::unique_ptr<quick_symbol_functions> quick_symbol_functions_up;
> > diff --git a/gdb/symfile.c b/gdb/symfile.c
> > index 96239679c77..c476196184a 100644
> > --- a/gdb/symfile.c
> > +++ b/gdb/symfile.c
> > @@ -991,6 +991,10 @@ syms_from_objfile (struct objfile *objfile,
> >  static void
> >  finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
> >  {
> > +  struct objfile *parent = objfile->separate_debug_objfile_backlink;
> > +  bool was_deferred
> > +    = (parent != nullptr) && (parent->flags & OBJF_DOWNLOAD_DEFERRED);
> > +
> >    /* If this is the main symbol file we have to clean up all users of the
> >       old main symbol file.  Otherwise it is sufficient to fixup all the
> >       breakpoints that may have been redefined by this symbol file.  */
> > @@ -1001,7 +1005,8 @@ finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
> >
> >        clear_symtab_users (add_flags);
> >      }
> > -  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0)
> > +  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0
> > +          && !was_deferred)
> >      {
> >        breakpoint_re_set ();
> >      }
> > @@ -1127,6 +1132,12 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name,
> >
> >    finish_new_objfile (objfile, add_flags);
> >
> > +  /* Remove deferred status now in case any observers trigger symtab
> > +     expansion.  Otherwise gdb might try to read parent for psymbols
> > +     when it should read the separate debug objfile instead.  */
> > +  if (parent != nullptr && (parent->flags & OBJF_DOWNLOAD_DEFERRED))
> > +    parent->remove_deferred_status ();
> > +
> >    gdb::observers::new_objfile.notify (objfile);
> >
> >    bfd_cache_close_all ();
> > diff --git a/gdb/symtab.c b/gdb/symtab.c
> > index 5e1b9d91879..2408725fa73 100644
> > --- a/gdb/symtab.c
> > +++ b/gdb/symtab.c
> > @@ -2897,14 +2897,30 @@ find_pc_sect_compunit_symtab (CORE_ADDR pc, struct obj_section *section)
> >    if (best_cust != NULL)
> >      return best_cust;
> >
> > +  int warn_if_readin = 1;
> > +
> >    /* Not found in symtabs, search the "quick" symtabs (e.g. psymtabs).  */
> >
> >    for (objfile *objf : current_program_space->objfiles ())
> >      {
> > +      bool was_deferred = objf->flags & OBJF_DOWNLOAD_DEFERRED;
> > +
> >        struct compunit_symtab *result
> > -       = objf->find_pc_sect_compunit_symtab (msymbol, pc, section, 1);
> > +       = objf->find_pc_sect_compunit_symtab (msymbol, pc, section,
> > +                                             warn_if_readin);
> > +
> >        if (result != NULL)
> >         return result;
> > +
> > +      /* If objf's separate debug info was just acquired, disable
> > +        warn_if_readin for the next iteration of this loop.  This prevents
> > +        a spurious warning in case an observer already triggered expansion
> > +        of the separate debug objfile's symtabs.  */
> > +      if (was_deferred && objf->separate_debug_objfile != nullptr
> > +         && (objf->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> > +       warn_if_readin = 0;
> > +      else if (warn_if_readin == 0)
> > +       warn_if_readin = 1;
> >      }
> >
> >    return NULL;
> > diff --git a/gdb/testsuite/gdb.debuginfod/libsection1.c b/gdb/testsuite/gdb.debuginfod/libsection1.c
> > new file mode 100644
> > index 00000000000..60824b415c6
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.debuginfod/libsection1.c
> > @@ -0,0 +1,40 @@
> > +/* This testcase is part of GDB, the GNU debugger.
> > +
> > +   Copyright 2023 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 <stdio.h>
> > +#include <pthread.h>
> > +#include <unistd.h>
> > +
> > +extern void libsection2_test ();
> > +extern void *libsection2_thread_test (void *);
> > +
> > +void
> > +libsection1_test ()
> > +{
> > +  pthread_t thr;
> > +
> > +  printf ("In libsection1\n");
> > +  libsection2_test ();
> > +
> > +  pthread_create (&thr, NULL, libsection2_thread_test, NULL);
> > +
> > +  /* Give the new thread a chance to actually enter libsection2_thread_test.  */
> > +  sleep (3);
> > +  printf ("Cancelling thread\n");
> > +
> > +  pthread_cancel (thr);
> > +}
> > diff --git a/gdb/testsuite/gdb.debuginfod/libsection2.c b/gdb/testsuite/gdb.debuginfod/libsection2.c
> > new file mode 100644
> > index 00000000000..629a67f94a5
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.debuginfod/libsection2.c
> > @@ -0,0 +1,37 @@
> > +/* This testcase is part of GDB, the GNU debugger.
> > +
> > +   Copyright 2023 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 <stdio.h>
> > +
> > +void
> > +libsection2_test ()
> > +{
> > +  printf ("In libsection2\n");
> > +}
> > +
> > +void *
> > +libsection2_thread_test (void *arg)
> > +{
> > +  (void) arg;
> > +
> > +  printf ("In thread test\n");
> > +
> > +  while (1)
> > +    ;
> > +
> > +  return NULL;
> > +}
> > diff --git a/gdb/testsuite/gdb.debuginfod/section.c b/gdb/testsuite/gdb.debuginfod/section.c
> > new file mode 100644
> > index 00000000000..d391a8f898e
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.debuginfod/section.c
> > @@ -0,0 +1,29 @@
> > +/* This testcase is part of GDB, the GNU debugger.
> > +
> > +   Copyright 2023 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 <stdio.h>
> > +
> > +extern void libsection1_test ();
> > +
> > +int
> > +main()
> > +{
> > +  libsection1_test ();
> > +  printf ("in section exec\n");
> > +
> > +  return 0;
> > +}
> > diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
> > new file mode 100644
> > index 00000000000..96e9750cd38
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> > @@ -0,0 +1,181 @@
> > +# Copyright 2023 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 debuginfod functionality
> > +
> > +standard_testfile
> > +
> > +load_lib debuginfod-support.exp
> > +
> > +require allow_debuginfod_tests
> > +
> > +set sourcetmp [standard_output_file tmp-${srcfile}]
> > +set outputdir [standard_output_file {}]
> > +
> > +# SECTEXEC is an executable which calls a function from LIB_SL1.
> > +set sectfile "section"
> > +set sectsrc $srcdir/$subdir/section.c
> > +set sectexec [standard_output_file $sectfile]
> > +
> > +# Solib LIB_SL1 calls functions from LIB_SL2.
> > +set libfile1 "libsection1"
> > +set libsrc1 $srcdir/$subdir/$libfile1.c
> > +set lib_sl1 [standard_output_file $libfile1.sl]
> > +
> > +set libfile2 "libsection2"
> > +set libsrc2 $srcdir/$subdir/$libfile2.c
> > +set lib_sl2 [standard_output_file $libfile2.sl]
> > +
> > +set lib_opts1 [list debug build-id shlib=$lib_sl2]
> > +set lib_opts2 [list debug build-id]
> > +set exec_opts [list debug build-id shlib=$lib_sl1 shlib=$lib_sl2]
> > +
> > +clean_restart
> > +
> > +if {[enable_section_downloads] == 0} {
> > +    untested "GDB does not support debuginfod section downloads"
> > +    return -1
> > +}
> > +
> > +# Compile SECTEXEC, LIB_SL1 and LIB_SL2.
> > +if { [gdb_compile_shlib $libsrc2 $lib_sl2 $lib_opts2] != "" } {
> > +    untested "failed to compile $libfile2"
> > +    return -1
> > +}
> > +
> > +if { [gdb_compile_shlib_pthreads $libsrc1 $lib_sl1 $lib_opts1] != "" } {
> > +    untested "failed to compile $libfile1"
> > +    return -1
> > +}
> > +
> > +if { [gdb_compile $sectsrc $sectexec executable $exec_opts] != "" } {
> > +    untested "failed to compile $sectfile"
> > +    return -1
> > +}
> > +
> > +# Add .gdb_index to solibs.
> > +if { [have_index $lib_sl1] != "gdb_index"
> > +     && [add_gdb_index $lib_sl1] == 0 } {
> > +    untested "failed to add .gdb_index to $libfile1"
> > +    return -1
> > +}
> > +
> > +if { [have_index $lib_sl2] != "gdb_index"
> > +     && [add_gdb_index $lib_sl2] == 0 } {
> > +    untested "failed to add .gdb_index to $libfile2"
> > +    return -1
> > +}
> > +
> > +# Strip solib debuginfo into separate files.
> > +if { [gdb_gnu_strip_debug $lib_sl1 ""] != 0} {
> > +   fail "strip $lib_sl1 debuginfo"
> > +   return -1
> > +}
> > +
> > +if { [gdb_gnu_strip_debug $lib_sl2 ""] != 0} {
> > +   fail "strip $lib_sl2 debuginfo"
> > +   return -1
> > +}
> > +
> > +# Move debuginfo files into directory that debuginfod will serve from.
> > +set debugdir [standard_output_file "debug"]
> > +set debuginfo_sl1 [standard_output_file $libfile1.sl.debug]
> > +set debuginfo_sl2 [standard_output_file $libfile2.sl.debug]
> > +
> > +file mkdir $debugdir
> > +file rename -force $debuginfo_sl1 $debugdir
> > +file rename -force $debuginfo_sl2 $debugdir
> > +
> > +# Restart GDB and clear the debuginfod client cache. Then load BINFILE into
> > +# GDB and start running it.  Match output with pattern RES and use TESTNAME
> > +# as the test name.
> > +proc_with_prefix clean_restart_with_prompt { binfile res testname } {
> > +    global cache
> > +
> > +    clean_restart
> > +
> > +    # Delete client cache so debuginfo downloads again.
> > +    file delete -force $cache
> > +
> > +    gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
> > +    gdb_test "start" $res "file [file tail $binfile] start $testname" \
> > +            ".*Enable debuginfod.*" "y"
> > +}
> > +
> > +# Tests with no debuginfod server running.
> > +proc_with_prefix no_url { } {
> > +    global sectexec libfile1 libfile2
> > +
> > +    # Check that no section is downloaded and no debuginfo is found.
> > +    gdb_test "file $sectexec" "" "file [file tail $sectexec] file no url"
> > +    gdb_test "start" "" "file [file tail $sectexec] start no url"
> > +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile1.*" \
> > +            "file [file tail $libfile1] found no url"
> > +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile2.*" \
> > +            "file [file tail $libfile2] found no url"
> > +}
> > +
> > +# Tests with a debuginfod server running.
> > +proc_with_prefix local_url { } {
> > +    global sectexec
> > +    global libsrc1 lib_sl1 libfile1
> > +    global libsrc2 lib_sl2 libfile2
> > +    global debugdir db
> > +
> > +    set url [start_debuginfod $db $debugdir]
> > +    if { $url == "" } {
> > +       unresolved "failed to start debuginfod server"
> > +       return
> > +    }
> > +
> > +    # Point GDB to the server.
> > +    setenv DEBUGINFOD_URLS $url
> > +
> > +    # Download .gdb_index for solibs.
> > +    set res ".*section \.gdb_index for $lib_sl1.*\
> > +       section \.gdb_index for $lib_sl2.*"
> > +    clean_restart_with_prompt $sectexec $res "index"
> > +
> > +    # Download debuginfo when stepping into a function.
> > +    set res ".*separate debug info for $lib_sl1.*\"In ${libfile1}\\\\n\".*"
> > +    gdb_test "step" $res "file [file tail $lib_sl1] step"
> > +
> > +    clean_restart_with_prompt $sectexec "" "break"
> > +
> > +    # Download debuginfo when setting a breakpoint.
> > +    set res "Download.*separate debug info for $lib_sl2.*"
> > +    gdb_test "br libsection2_test" $res "file [file tail $sectexec] break set"
> > +
> > +    # Hit the breakpoint.
> > +    set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
> > +    gdb_test "c" $res "file [file tail $sectexec] break continue"
> > +
> > +    # Check that download progress message is correctly formatted
> > +    # during backtrace.
> > +    set res ".* separate debug info for $lib_sl1.*#0  libsection2_test\
> > +       \\(\\) at.*"
> > +    set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
> > +    gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
> > +}
> > +
> > +# Create CACHE and DB directories ready for debuginfod to use.
> > +prepare_for_debuginfod cache db
> > +
> > +with_debuginfod_env $cache {
> > +    no_url
> > +    local_url
> > +}
> > +
> > +stop_debuginfod
> > diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
> > index 50a8b512a4a..6368c27e9d0 100644
> > --- a/gdb/testsuite/lib/debuginfod-support.exp
> > +++ b/gdb/testsuite/lib/debuginfod-support.exp
> > @@ -194,3 +194,33 @@ proc stop_debuginfod { } {
> >         unset debuginfod_spawn_id
> >      }
> >  }
> > +
> > +# Return 1 if gdb is configured to download ELF/DWARF sections from
> > +# debuginfod servers.  Otherwise return 0.
> > +proc enable_section_downloads { } {
> > +    global gdb_prompt
> > +
> > +    set cmd "maint set debuginfod download-sections on"
> > +    set msg "enable section downloads"
> > +
> > +    gdb_test_multiple $cmd $msg {
> > +       -re ".*Undefined maintenance.*" {
> > +           perror "Undefined command: \"$cmd\""
> > +           return 0
> > +       }
> > +       -re ".*not compiled into GDB.*" {
> > +           perror "Unsupported command: \"$cmd\""
> > +           return 0
> > +       }
> > +       -re "\r\n${gdb_prompt} $" {
> > +           return 1
> > +       }
> > +       timeout {
> > +           perror "timeout: \"$cmd\""
> > +           return 0
> > +       }
> > +    }
> > +
> > +    perror "Unexpected output for \"$cmd\""
> > +    return 0
> > +}
> > --
> > 2.40.1
> >


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PING*2][PATCH 6/6 v3] gdb/debuginfod: Add .debug_line downloading
  2023-06-15 13:45   ` Aaron Merey
@ 2023-07-03 17:40     ` Aaron Merey
  2023-07-19 14:33       ` [PING*3][PATCH " Aaron Merey
  0 siblings, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-07-03 17:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Ping

Thanks,
Aaron

On Thu, Jun 15, 2023 at 9:45 AM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Wed, May 31, 2023 at 9:44 PM Aaron Merey <amerey@redhat.com> wrote:
> >
> > v2: https://sourceware.org/pipermail/gdb-patches/2023-April/198946.html
> >
> > v3 adds tests for .debug_line downloading and modifies logging for
> > the testsuite debuginfod server (see commit message).
> >
> > Commit message:
> >
> > ELF/DWARF section downloading allows gdb to download .gdb_index files in
> > order to defer full debuginfo downloads.  However .gdb_index does not
> > contain any information regarding source filenames.  When a gdb command
> > includes a filename argument (ex. 'break main.c:50'), this results in
> > the mass downloading of all deferred debuginfo so that gdb can search the
> > debuginfo for matching source filenames.  This can result in unnecessary
> > downloads.
> >
> > To improve this, have gdb instead download each debuginfo's .debug_line
> > (and .debug_line_str if using DWARF5) when executing these commands.
> > Download full debuginfo only when its .debug_line contains a matching
> > filename.
> >
> > Since the combined size of .debug_line and .debug_line_str is only about
> > 1% the size of the corresponding debuginfo, significant time can be saved
> > by checking these sections before choosing to download an entire debuginfo.
> >
> > This patch also redirects stdout and stderr of the debuginfod server
> > used by testsuite/gdb.debuginfod tests to a server_log standard output
> > file.  While adding tests for this patch I ran into an issue where the
> > test server would block when logging to stderr, presumably because the
> > stderr buffer filled up and wasn't being read from.  Redirecting the
> > log to a file fixes this and also makes the server log more accessible
> > when debugging test failures.
> > ---
> >  gdb/dwarf2/line-header.c                 | 215 +++++++++++++++--------
> >  gdb/dwarf2/line-header.h                 |  10 ++
> >  gdb/dwarf2/read-gdb-index.c              |  27 +++
> >  gdb/dwarf2/read.c                        | 165 +++++++++++++++++
> >  gdb/dwarf2/read.h                        |  31 ++++
> >  gdb/testsuite/gdb.debuginfod/section.exp |  23 ++-
> >  gdb/testsuite/lib/debuginfod-support.exp |   5 +-
> >  7 files changed, 397 insertions(+), 79 deletions(-)
> >
> > diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> > index d072a91bac9..b9210d84f6b 100644
> > --- a/gdb/dwarf2/line-header.c
> > +++ b/gdb/dwarf2/line-header.c
> > @@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
> >  {
> >    LONGEST length = read_initial_length (abfd, buf, bytes_read);
> >
> > -  gdb_assert (cu_header->initial_length_size == 4
> > -             || cu_header->initial_length_size == 8
> > -             || cu_header->initial_length_size == 12);
> > +  if (cu_header != nullptr)
> > +    {
> > +      gdb_assert (cu_header->initial_length_size == 4
> > +                 || cu_header->initial_length_size == 8
> > +                 || cu_header->initial_length_size == 12);
> >
> > -  if (cu_header->initial_length_size != *bytes_read)
> > -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > +      if (cu_header->initial_length_size != *bytes_read)
> > +       complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > +    }
> >
> >    *offset_size = (*bytes_read == 4) ? 4 : 8;
> >    return length;
> >  }
> >
> > -/* Read directory or file name entry format, starting with byte of
> > -   format count entries, ULEB128 pairs of entry formats, ULEB128 of
> > -   entries count and the entries themselves in the described entry
> > -   format.  */
> > +
> > +/* Like read_formatted_entries but the .debug_line and .debug_line_str
> > +   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
> > +   where these sections are read from separate files without necessarily
> > +   having access to the entire debuginfo file they originate from.  */
> >
> >  static void
> > -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > -                       const gdb_byte **bufp, struct line_header *lh,
> > -                       unsigned int offset_size,
> > -                       void (*callback) (struct line_header *lh,
> > -                                         const char *name,
> > -                                         dir_index d_index,
> > -                                         unsigned int mod_time,
> > -                                         unsigned int length))
> > +read_formatted_entries
> > +  (bfd *parent_bfd, const gdb_byte **line_bufp,
> > +   const gdb::array_view<const gdb_byte> line_str_data,
> > +   struct line_header *lh,
> > +   unsigned int offset_size,
> > +   void (*callback) (struct line_header *lh,
> > +                    const char *name,
> > +                    dir_index d_index,
> > +                    unsigned int mod_time,
> > +                    unsigned int length))
> >  {
> >    gdb_byte format_count, formati;
> >    ULONGEST data_count, datai;
> > -  const gdb_byte *buf = *bufp;
> > +  const gdb_byte *buf = *line_bufp;
> > +  const gdb_byte *str_buf = line_str_data.data ();
> >    const gdb_byte *format_header_data;
> >    unsigned int bytes_read;
> >
> > -  format_count = read_1_byte (abfd, buf);
> > +  format_count = read_1_byte (parent_bfd, buf);
> >    buf += 1;
> >    format_header_data = buf;
> >    for (formati = 0; formati < format_count; formati++)
> >      {
> > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> >        buf += bytes_read;
> > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> >        buf += bytes_read;
> >      }
> >
> > -  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
> > +  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> >    buf += bytes_read;
> >    for (datai = 0; datai < data_count; datai++)
> >      {
> > @@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >
> >        for (formati = 0; formati < format_count; formati++)
> >         {
> > -         ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
> > +         ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> >           format += bytes_read;
> >
> > -         ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> > +         ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> >           format += bytes_read;
> >
> >           gdb::optional<const char *> string;
> > @@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >           switch (form)
> >             {
> >             case DW_FORM_string:
> > -             string.emplace (read_direct_string (abfd, buf, &bytes_read));
> > +             string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
> >               buf += bytes_read;
> >               break;
> >
> >             case DW_FORM_line_strp:
> >               {
> > -               const char *str
> > -                 = per_objfile->read_line_string (buf, offset_size);
> > +               if (line_str_data.empty ())
> > +                 error (_("Dwarf Error: DW_FORM_line_strp used without " \
> > +                          "required section"));
> > +               if (line_str_data.size () <= offset_size)
> > +                 error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> > +                          "of section .debug_line"));
> > +
> > +               ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
> > +
> > +               const char *str;
> > +               if (str_buf[str_offset] == '\0')
> > +                 str = nullptr;
> > +               else
> > +                 str = (const char *) (str_buf + str_offset);
> >                 string.emplace (str);
> >                 buf += offset_size;
> > +               break;
> >               }
> > -             break;
> >
> >             case DW_FORM_data1:
> > -             uint.emplace (read_1_byte (abfd, buf));
> > +             uint.emplace (read_1_byte (parent_bfd, buf));
> >               buf += 1;
> >               break;
> >
> >             case DW_FORM_data2:
> > -             uint.emplace (read_2_bytes (abfd, buf));
> > +             uint.emplace (read_2_bytes (parent_bfd, buf));
> >               buf += 2;
> >               break;
> >
> >             case DW_FORM_data4:
> > -             uint.emplace (read_4_bytes (abfd, buf));
> > +             uint.emplace (read_4_bytes (parent_bfd, buf));
> >               buf += 4;
> >               break;
> >
> >             case DW_FORM_data8:
> > -             uint.emplace (read_8_bytes (abfd, buf));
> > +             uint.emplace (read_8_bytes (parent_bfd, buf));
> >               buf += 8;
> >               break;
> >
> > @@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >               break;
> >
> >             case DW_FORM_udata:
> > -             uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
> > +             uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
> >               buf += bytes_read;
> >               break;
> >
> > @@ -248,28 +267,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
> >      }
> >
> > -  *bufp = buf;
> > +  *line_bufp = buf;
> >  }
> >
> >  /* See line-header.h.  */
> >
> >  line_header_up
> > -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > -                          dwarf2_per_objfile *per_objfile,
> > -                          struct dwarf2_section_info *section,
> > -                          const struct comp_unit_head *cu_header,
> > -                          const char *comp_dir)
> > +dwarf_decode_line_header (bfd *parent_bfd,
> > +                         gdb::array_view<const gdb_byte> line_data,
> > +                         gdb::array_view<const gdb_byte> line_str_data,
> > +                         const gdb_byte **debug_line_ptr,
> > +                         bool is_dwz,
> > +                         const struct comp_unit_head *cu_header,
> > +                         const char *comp_dir)
> >  {
> > -  const gdb_byte *line_ptr;
> > +  const gdb_byte *line_ptr, *buf;
> >    unsigned int bytes_read, offset_size;
> >    int i;
> >    const char *cur_dir, *cur_file;
> >
> > -  bfd *abfd = section->get_bfd_owner ();
> > +  buf = *debug_line_ptr;
> >
> >    /* Make sure that at least there's room for the total_length field.
> >       That could be 12 bytes long, but we're just going to fudge that.  */
> > -  if (to_underlying (sect_off) + 4 >= section->size)
> > +  if (buf + 4 >= line_data.data () + line_data.size ())
> >      {
> >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> >        return 0;
> > @@ -277,62 +298,65 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >
> >    line_header_up lh (new line_header (comp_dir));
> >
> > -  lh->sect_off = sect_off;
> > +  lh->sect_off = (sect_offset) (buf - line_data.data ());
> >    lh->offset_in_dwz = is_dwz;
> >
> > -  line_ptr = section->buffer + to_underlying (sect_off);
> > +  line_ptr = buf;
> >
> >    /* Read in the header.  */
> >    LONGEST unit_length
> > -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> > +    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
> >                                               &bytes_read, &offset_size);
> > -  line_ptr += bytes_read;
> >
> > -  const gdb_byte *start_here = line_ptr;
> > +  line_ptr += bytes_read;
> >
> > -  if (line_ptr + unit_length > (section->buffer + section->size))
> > +  if (line_ptr + unit_length > buf + line_data.size ())
> >      {
> >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> >        return 0;
> >      }
> > +
> > +  const gdb_byte *start_here = line_ptr;
> > +
> >    lh->statement_program_end = start_here + unit_length;
> > -  lh->version = read_2_bytes (abfd, line_ptr);
> > +  lh->version = read_2_bytes (parent_bfd, line_ptr);
> >    line_ptr += 2;
> >    if (lh->version > 5)
> >      {
> >        /* This is a version we don't understand.  The format could have
> >          changed in ways we don't handle properly so just punt.  */
> >        complaint (_("unsupported version in .debug_line section"));
> > -      return NULL;
> > +      return nullptr;
> >      }
> >    if (lh->version >= 5)
> >      {
> >        gdb_byte segment_selector_size;
> >
> >        /* Skip address size.  */
> > -      read_1_byte (abfd, line_ptr);
> > +      read_1_byte (parent_bfd, line_ptr);
> >        line_ptr += 1;
> >
> > -      segment_selector_size = read_1_byte (abfd, line_ptr);
> > +      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
> >        line_ptr += 1;
> >        if (segment_selector_size != 0)
> >         {
> >           complaint (_("unsupported segment selector size %u "
> >                        "in .debug_line section"),
> >                      segment_selector_size);
> > -         return NULL;
> > +         return nullptr;
> >         }
> >      }
> >
> > -  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
> > +  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
> >    line_ptr += offset_size;
> >    lh->statement_program_start = line_ptr + header_length;
> > -  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
> > +
> > +  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> >
> >    if (lh->version >= 4)
> >      {
> > -      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
> > +      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
> >        line_ptr += 1;
> >      }
> >    else
> > @@ -345,41 +369,47 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >                    "in `.debug_line' section"));
> >      }
> >
> > -  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
> > +  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> > -  lh->line_base = read_1_signed_byte (abfd, line_ptr);
> > +
> > +  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> > -  lh->line_range = read_1_byte (abfd, line_ptr);
> > +
> > +  lh->line_range = read_1_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> > -  lh->opcode_base = read_1_byte (abfd, line_ptr);
> > +
> > +  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> > +
> >    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
> >
> >    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
> >    for (i = 1; i < lh->opcode_base; ++i)
> >      {
> > -      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
> > +      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
> >        line_ptr += 1;
> >      }
> >
> >    if (lh->version >= 5)
> >      {
> >        /* Read directory table.  */
> > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > -                             offset_size,
> > -                             [] (struct line_header *header, const char *name,
> > -                                 dir_index d_index, unsigned int mod_time,
> > -                                 unsigned int length)
> > +      read_formatted_entries
> > +       (parent_bfd, &line_ptr, line_str_data,
> > +        lh.get (), offset_size,
> > +        [] (struct line_header *header, const char *name,
> > +            dir_index d_index, unsigned int mod_time,
> > +            unsigned int length)
> >         {
> >           header->add_include_dir (name);
> >         });
> >
> >        /* Read file name table.  */
> > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > -                             offset_size,
> > -                             [] (struct line_header *header, const char *name,
> > -                                 dir_index d_index, unsigned int mod_time,
> > -                                 unsigned int length)
> > +      read_formatted_entries
> > +       (parent_bfd, &line_ptr, line_str_data,
> > +        lh.get (), offset_size,
> > +        [] (struct line_header *header, const char *name,
> > +            dir_index d_index, unsigned int mod_time,
> > +            unsigned int length)
> >         {
> >           header->add_file_name (name, d_index, mod_time, length);
> >         });
> > @@ -387,7 +417,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >    else
> >      {
> >        /* Read directory table.  */
> > -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > +      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> >         {
> >           line_ptr += bytes_read;
> >           lh->add_include_dir (cur_dir);
> > @@ -395,17 +425,17 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >        line_ptr += bytes_read;
> >
> >        /* Read file name table.  */
> > -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > +      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> >         {
> >           unsigned int mod_time, length;
> >           dir_index d_index;
> >
> >           line_ptr += bytes_read;
> > -         d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > +         d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> >           line_ptr += bytes_read;
> > -         mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > +         mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> >           line_ptr += bytes_read;
> > -         length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > +         length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> >           line_ptr += bytes_read;
> >
> >           lh->add_file_name (cur_file, d_index, mod_time, length);
> > @@ -413,9 +443,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >        line_ptr += bytes_read;
> >      }
> >
> > -  if (line_ptr > (section->buffer + section->size))
> > +  if (line_ptr > (buf + line_data.size ()))
> >      complaint (_("line number info header doesn't "
> >                  "fit in `.debug_line' section"));
> >
> > +  *debug_line_ptr += unit_length + offset_size;
> >    return lh;
> >  }
> > +
> > +line_header_up
> > +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > +                          dwarf2_per_objfile *per_objfile,
> > +                          struct dwarf2_section_info *section,
> > +                          const struct comp_unit_head *cu_header,
> > +                          const char *comp_dir)
> > +{
> > +  struct objfile *objfile = per_objfile->objfile;
> > +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> > +
> > +  /* Read .debug_line.  */
> > +  dwarf2_section_info *line_sec = &per_bfd->line;
> > +  bfd_size_type line_size = line_sec->get_size (objfile);
> > +
> > +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> > +
> > +  /* Read .debug_line_str.  */
> > +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> > +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> > +
> > +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> > +                                           line_str_size);
> > +
> > +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> > +
> > +  return dwarf_decode_line_header
> > +    (per_bfd->obfd, line, line_str, &line_ptr,
> > +     is_dwz, cu_header, comp_dir);
> > +}
> > diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> > index 59a42e336f5..44e32828ddb 100644
> > --- a/gdb/dwarf2/line-header.h
> > +++ b/gdb/dwarf2/line-header.h
> > @@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
> >     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
> >     const char *comp_dir);
> >
> > +/* Like above but the .debug_line and .debug_line_str are stored in
> > +   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
> > +   statement program header within LINE_DATA.  */
> > +
> > +extern line_header_up dwarf_decode_line_header
> > +  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
> > +   gdb::array_view<const gdb_byte> line_str_data,
> > +   const gdb_byte **debug_line_ptr, bool is_dwz,
> > +  const comp_unit_head *cu_header, const char *comp_dir);
> > +
> >  #endif /* DWARF2_LINE_HEADER_H */
> > diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> > index d3516e92361..64f202fddd3 100644
> > --- a/gdb/dwarf2/read-gdb-index.c
> > +++ b/gdb/dwarf2/read-gdb-index.c
> > @@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
> >    }
> >  };
> >
> > +struct mapped_debug_line;
> > +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> > +
> >  struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >  {
> >    /* This dumps minimal information about the index.
> > @@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> >       debuginfo if necessary.  */
> >    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> > +
> > +  /* Filename information related to this .gdb_index.  */
> > +  mapped_debug_line_up mdl;
> > +
> > +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> > +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> > +  bool filename_in_debug_line
> > +  (objfile *objfile,
> > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> >  };
> >
> >  void
> > @@ -587,6 +599,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
> >    return result;
> >  }
> >
> > +bool
> > +dwarf2_gdb_index::filename_in_debug_line
> > +  (objfile *objfile,
> > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > +{
> > +  if (mdl == nullptr)
> > +    mdl.reset (new mapped_debug_line (objfile));
> > +
> > +  return mdl->contains_matching_filename (file_matcher);
> > +}
> > +
> >  bool
> >  dwarf2_gdb_index::expand_symtabs_matching
> >      (struct objfile *objfile,
> > @@ -615,6 +638,10 @@ dwarf2_gdb_index::expand_symtabs_matching
> >           return false;
> >         }
> >
> > +      if (file_matcher != nullptr
> > +         && !filename_in_debug_line (objfile, file_matcher))
> > +       return true;
> > +
> >        read_full_dwarf_from_debuginfod (objfile, this);
> >        return true;
> >      }
> > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> > index 96d1ff53d91..715b3c06a56 100644
> > --- a/gdb/dwarf2/read.c
> > +++ b/gdb/dwarf2/read.c
> > @@ -81,6 +81,7 @@
> >  #include "gdbsupport/gdb_optional.h"
> >  #include "gdbsupport/underlying.h"
> >  #include "gdbsupport/hash_enum.h"
> > +#include "gdbsupport/scoped_mmap.h"
> >  #include "filename-seen-cache.h"
> >  #include "producer.h"
> >  #include <fcntl.h>
> > @@ -2115,6 +2116,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
> >    return this_cu->file_names;
> >  }
> >
> > +#if !HAVE_SYS_MMAN_H
> > +
> > +bool
> > +mapped_debug_line::contains_matching_filename
> > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > +{
> > +  return false;
> > +}
> > +
> > +gdb::array_view<const gdb_byte>
> > +mapped_debug_line::read_debug_line_separate
> > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > +{
> > +  return {};
> > +}
> > +
> > +bool
> > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > +{
> > +  return false;
> > +}
> > +
> > +#else /* !HAVE_SYS_MMAN_H */
> > +
> > +struct line_resource_mmap final : public index_cache_resource
> > +{
> > +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> > +     file doesn't exist. */
> > +  line_resource_mmap (const char *filename)
> > +    : mapping (mmap_file (filename))
> > +  {}
> > +
> > +  scoped_mmap mapping;
> > +};
> > +
> > +/* See read.h.  */
> > +
> > +bool
> > +mapped_debug_line::contains_matching_filename
> > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > +{
> > +  for (line_header_up &lh : line_headers)
> > +    for (file_entry &fe : lh->file_names ())
> > +      {
> > +       const char *filename = fe.name;
> > +
> > +       if (file_matcher (fe.name, false))
> > +         return true;
> > +
> > +       bool basename_match = file_matcher (lbasename (fe.name), true);
> > +
> > +       if (!basenames_may_differ && !basename_match)
> > +         continue;
> > +
> > +       /* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> > +          until DWARF5.  Since we don't have access to the CU at this
> > +          point we just check for a partial match on the filename.
> > +          If there is a match, the full debuginfo will be downloaded
> > +          ane the match will be re-evalute with DW_AT_comp_dir.  */
> > +       if (lh->version < 5 && fe.d_index == 0)
> > +         return basename_match;
> > +
> > +       const char *dirname = fe.include_dir (&*lh);
> > +       std::string fullname;
> > +
> > +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> > +         fullname = filename;
> > +       else
> > +         fullname = std::string (dirname) + SLASH_STRING + filename;
> > +
> > +       gdb::unique_xmalloc_ptr<char> rewritten
> > +         = rewrite_source_path (fullname.c_str ());
> > +       if (rewritten != nullptr)
> > +         fullname = rewritten.release ();
> > +
> > +       if (file_matcher (fullname.c_str (), false))
> > +         return true;
> > +      }
> > +
> > +  return false;
> > +}
> > +
> > +/* See read.h.  */
> > +
> > +gdb::array_view<const gdb_byte>
> > +mapped_debug_line::read_debug_line_separate
> > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > +{
> > +  if (filename == nullptr)
> > +    return {};
> > +
> > +  try
> > +  {
> > +    line_resource_mmap *mmap_resource
> > +      = new line_resource_mmap (filename);
> > +
> > +    resource->reset (mmap_resource);
> > +
> > +    return gdb::array_view<const gdb_byte>
> > +      ((const gdb_byte *) mmap_resource->mapping.get (),
> > +       mmap_resource->mapping.size ());
> > +  }
> > +  catch (const gdb_exception &except)
> > +  {
> > +    exception_print (gdb_stderr, except);
> > +  }
> > +
> > +  return {};
> > +}
> > +
> > +/* See read.h.  */
> > +
> > +bool
> > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > +{
> > +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > +  if (build_id == nullptr)
> > +    return false;
> > +
> > +  gdb::unique_xmalloc_ptr<char> line_path;
> > +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> > +                                               build_id->size,
> > +                                               bfd_get_filename
> > +                                                 (objfile->obfd.get ()),
> > +                                               ".debug_line",
> > +                                               &line_path);
> > +
> > +  if (line_fd.get () < 0)
> > +    return false;
> > +
> > +  gdb::unique_xmalloc_ptr<char> line_str_path;
> > +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> > +                                                   build_id->size,
> > +                                                   bfd_get_filename
> > +                                                     (objfile->obfd.get ()),
> > +                                                   ".debug_line_str",
> > +                                                   &line_str_path);
> > +
> > +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> > +  line_str_data = read_debug_line_separate (line_str_path.get (),
> > +                                           &line_str_resource);
> > +
> > +  const gdb_byte *line_ptr = line_data.data ();
> > +
> > +  while (line_ptr < line_data.data () + line_data.size ())
> > +    {
> > +      line_header_up lh
> > +       = dwarf_decode_line_header (objfile->obfd.get (),
> > +                                   line_data, line_str_data,
> > +                                   &line_ptr, false,
> > +                                   nullptr, nullptr);
> > +      line_headers.emplace_back (lh.release ());
> > +    }
> > +
> > +  return true;
> > +}
> > +#endif /* !HAVE_SYS_MMAN_H */
> > +
> > +mapped_debug_line::mapped_debug_line (objfile *objfile)
> > +{
> > +  if (!read_debug_line_from_debuginfod (objfile))
> > +    line_headers.clear ();
> > +}
> > +
> >  /* A helper for the "quick" functions which computes and caches the
> >     real path for a given file name from the line table.  */
> >
> > diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> > index e3131693b81..b8a8b76bde0 100644
> > --- a/gdb/dwarf2/read.h
> > +++ b/gdb/dwarf2/read.h
> > @@ -34,6 +34,7 @@
> >  #include "gdbsupport/hash_enum.h"
> >  #include "gdbsupport/function-view.h"
> >  #include "gdbsupport/packed.h"
> > +#include "dwarf2/line-header.h"
> >
> >  /* Hold 'maintenance (set|show) dwarf' commands.  */
> >  extern struct cmd_list_element *set_dwarf_cmdlist;
> > @@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
> >  extern void read_full_dwarf_from_debuginfod (struct objfile *,
> >                                              dwarf2_base_index_functions *);
> >
> > +struct mapped_debug_line
> > +{
> > +  mapped_debug_line (objfile *objfile);
> > +
> > +  /* Return true if any of the mapped .debug_line's filenames match
> > +     FILE_MATCHER.  */
> > +
> > +  bool contains_matching_filename
> > +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> > +
> > +private:
> > +  std::vector<line_header_up> line_headers;
> > +
> > +  gdb::array_view<const gdb_byte> line_data;
> > +  gdb::array_view<const gdb_byte> line_str_data;
> > +
> > +  std::unique_ptr<index_cache_resource> line_resource;
> > +  std::unique_ptr<index_cache_resource> line_str_resource;
> > +
> > +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> > +     and populate line_headers.  */
> > +
> > +  bool read_debug_line_from_debuginfod (objfile *objfile);
> > +
> > +  /* Initialize line_data and line_str_data with the .debug_line and
> > +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> > +
> > +  gdb::array_view<const gdb_byte> read_debug_line_separate
> > +    (char *filename, std::unique_ptr<index_cache_resource> *resource);
> > +};
> >  #endif /* DWARF2READ_H */
> > diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
> > index 96e9750cd38..1380eb7ef78 100644
> > --- a/gdb/testsuite/gdb.debuginfod/section.exp
> > +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> > @@ -106,7 +106,7 @@ proc_with_prefix clean_restart_with_prompt { binfile res testname } {
> >
> >      clean_restart
> >
> > -    # Delete client cache so debuginfo downloads again.
> > +    # Delete client cache so progress messages always appear.
> >      file delete -force $cache
> >
> >      gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
> > @@ -168,6 +168,27 @@ proc_with_prefix local_url { } {
> >         \\(\\) at.*"
> >      set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
> >      gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
> > +
> > +    clean_restart_with_prompt $sectexec "" "line 1"
> > +
> > +    # List source file using .debug_line download.
> > +    set res ".*\.debug_line.*$lib_sl1.*21.*extern void libsection2_test.*"
> > +    gdb_test "list $libsrc1:21" $res "file [file tail $sectexec] line 1 list"
> > +
> > +    clean_restart_with_prompt $sectexec "" "line 2"
> > +
> > +    # Set breakpoint using .debug_line download.
> > +    set res ".*section \.debug_line for $lib_sl1.*Breakpoint 2 at.*$libsrc1.*"
> > +    gdb_test "br $libsrc1:37" $res "file [file tail $sectexec] line 2 br"
> > +
> > +    # Continue to breakpoint.
> > +    set res "Breakpoint 2, libsection1_test.*\"Cancelling thread\\\\n\".*"
> > +    gdb_test "c" $res "file [file tail $sectexec] line 2 continue"
> > +
> > +    # Check that download progress message is correctly formatted
> > +    # when printing threads.
> > +    set res ".*separate debug info for $lib_sl2\.\.\.\r\n.* 2    Thread.*"
> > +    gdb_test "info thr" $res "file [file tail $libfile2] line thread"
> >  }
> >
> >  # Create CACHE and DB directories ready for debuginfod to use.
> > diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
> > index 6368c27e9d0..07ea23999bd 100644
> > --- a/gdb/testsuite/lib/debuginfod-support.exp
> > +++ b/gdb/testsuite/lib/debuginfod-support.exp
> > @@ -113,6 +113,8 @@ proc with_debuginfod_env { cache body } {
> >  proc start_debuginfod { db debugdir } {
> >      global debuginfod_spawn_id spawn_id
> >
> > +    set logfile [standard_output_file "server_log"]
> > +
> >      # Find an unused port.
> >      set port 7999
> >      set found false
> > @@ -127,7 +129,8 @@ proc start_debuginfod { db debugdir } {
> >             set old_spawn_id $spawn_id
> >         }
> >
> > -       spawn debuginfod -vvvv -d $db -p $port -F $debugdir
> > +       spawn sh -c "debuginfod -vvvv -d $db -p $port -F $debugdir 2>&1 \
> > +               | tee $logfile"
> >         set debuginfod_spawn_id $spawn_id
> >
> >         if { [info exists old_spawn_id] } {
> > --
> > 2.40.1
> >


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading
  2023-06-01  1:43 ` [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading Aaron Merey
  2023-06-15 13:44   ` Aaron Merey
@ 2023-07-07 14:18   ` Andrew Burgess
  2023-07-10 21:01     ` Aaron Merey
  2023-07-27 10:24   ` [PATCH " Andrew Burgess
  2 siblings, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2023-07-07 14:18 UTC (permalink / raw)
  To: Aaron Merey, gdb-patches; +Cc: Aaron Merey

Aaron Merey <amerey@redhat.com> writes:

> v2.2: https://sourceware.org/pipermail/gdb-patches/2023-May/199326.html
>
> v3 includes testcases instead of adding them in a separate patch.
> Tests related to section downloading are now included in
> testsuite/gdb.debuginfod/section.exp.
>
> Commit message:
>
> At the beginning of a session, gdb may attempt to download debuginfo
> for all shared libraries associated with the process or core file
> being debugged.  This can be a waste of time and storage space when much
> of the debuginfo ends up not being used during the session.
>
> To reduce the gdb's startup latency and to download only the debuginfo
> that is really needed, this patch adds on-demand downloading of debuginfo.
>
> 'set debuginfo enabled on' now causes gdb to attempt to download a .gdb_index
> for each shared library instead of its full debuginfo.  Each corresponding
> separate debuginfo will be deferred until gdb needs to expand symtabs
> associated with the debuginfo's index.
>
> Because these indices are significantly smaller than their corresponding
> debuginfo, this generally reduces the total amount of data gdb downloads.
> Reductions of 80%-95% have been observed when debugging large GUI programs.
>
>     (gdb) set debuginfod enabled on
>     (gdb) start
>     Downloading section .gdb_index for /lib64/libcurl.so.4
>     [...]
>     1826        client->server_mhandle = curl_multi_init ();
>     (gdb) step
>     Downloading separate debug info for /lib64/libcurl.so.4
>     Downloading separate debug info for [libcurl dwz]
>     Downloading source file /usr/src/debug/curl-7.85.0-6.fc37.x86_64/build-full/lib/../../lib/multi.c
>     curl_multi_init () at ../../lib/multi.c:457
>     457     {
>     (gdb)
>
> Some of the key functions below include dwarf2_has_separate_index which
> downloads the separate .gdb_index.  If successful, the shared library
> objfile owns the index until the separate debug objfile is downloaded
> or confirmed to not be available.
>
> read_full_dwarf_from_debuginfod downloads the full debuginfo and
> initializes the separate debug objfile.  It is called by functions
> such as dwarf2_gdb_index::expand_symtabs_matching and
> dwarf2_base_index_functions::find_pc_sect_compunit_symtab when symtab
> expansion is required.
> ---
>  gdb/dwarf2/frame.c                         |  13 ++
>  gdb/dwarf2/frame.h                         |   4 +
>  gdb/dwarf2/index-cache.c                   |  33 ++++
>  gdb/dwarf2/index-cache.h                   |  13 ++
>  gdb/dwarf2/public.h                        |   7 +
>  gdb/dwarf2/read-gdb-index.c                | 156 ++++++++++++++++--
>  gdb/dwarf2/read.c                          | 146 ++++++++++++++++-
>  gdb/dwarf2/read.h                          |  10 ++
>  gdb/dwarf2/section.c                       |   3 +-
>  gdb/elfread.c                              |   2 +-
>  gdb/objfile-flags.h                        |   4 +
>  gdb/objfiles.h                             |  17 +-
>  gdb/quick-symbol.h                         |   4 +
>  gdb/symfile.c                              |  13 +-
>  gdb/symtab.c                               |  18 +-
>  gdb/testsuite/gdb.debuginfod/libsection1.c |  40 +++++
>  gdb/testsuite/gdb.debuginfod/libsection2.c |  37 +++++
>  gdb/testsuite/gdb.debuginfod/section.c     |  29 ++++
>  gdb/testsuite/gdb.debuginfod/section.exp   | 181 +++++++++++++++++++++
>  gdb/testsuite/lib/debuginfod-support.exp   |  30 ++++
>  20 files changed, 734 insertions(+), 26 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.debuginfod/libsection1.c
>  create mode 100644 gdb/testsuite/gdb.debuginfod/libsection2.c
>  create mode 100644 gdb/testsuite/gdb.debuginfod/section.c
>  create mode 100644 gdb/testsuite/gdb.debuginfod/section.exp
>
> diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
> index a561aaf3100..3613f8252a7 100644
> --- a/gdb/dwarf2/frame.c
> +++ b/gdb/dwarf2/frame.c
> @@ -1609,6 +1609,19 @@ set_comp_unit (struct objfile *objfile, struct comp_unit *unit)
>    return dwarf2_frame_bfd_data.set (abfd, unit);
>  }
>  
> +/* See frame.h.  */
> +
> +void
> +dwarf2_clear_frame_data (struct objfile *objfile)
> +{
> +  bfd *abfd = objfile->obfd.get ();
> +
> +  if (gdb_bfd_requires_relocations (abfd))
> +    dwarf2_frame_objfile_data.clear (objfile);
> +  else
> +    dwarf2_frame_bfd_data.clear (abfd);
> +}
> +
>  /* Find the FDE for *PC.  Return a pointer to the FDE, and store the
>     initial location associated with it into *PC.  */
>  
> diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
> index 5643e557513..2391e313e7c 100644
> --- a/gdb/dwarf2/frame.h
> +++ b/gdb/dwarf2/frame.h
> @@ -238,6 +238,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
>  extern const struct frame_base *
>    dwarf2_frame_base_sniffer (frame_info_ptr this_frame);
>  
> +/* Delete OBJFILEs comp_unit.  */
> +
> +extern void dwarf2_clear_frame_data (struct objfile * objfile);
> +
>  /* Compute the DWARF CFA for a frame.  */
>  
>  CORE_ADDR dwarf2_frame_cfa (frame_info_ptr this_frame);
> diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
> index 79ab706ee9d..bbafcd321b2 100644
> --- a/gdb/dwarf2/index-cache.c
> +++ b/gdb/dwarf2/index-cache.c
> @@ -216,6 +216,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
>    return {};
>  }
>  
> +/* See index-cache.h.  */
> +
> +gdb::array_view<const gdb_byte>
> +index_cache::lookup_gdb_index_debuginfod (const char *index_path,
> +					  std::unique_ptr<index_cache_resource> *resource)
> +{
> +  try
> +    {
> +      /* Try to map that file.  */
> +      index_cache_resource_mmap *mmap_resource
> +	= new index_cache_resource_mmap (index_path);
> +
> +      /* Hand the resource to the caller.  */
> +      resource->reset (mmap_resource);
> +
> +      return gdb::array_view<const gdb_byte>
> +	  ((const gdb_byte *) mmap_resource->mapping.get (),
> +	   mmap_resource->mapping.size ());
> +    }
> +  catch (const gdb_exception_error &except)
> +    {
> +      warning (_("Unable to read %s: %s"), index_path, except.what ());
> +    }
> +
> +  return {};
> +}
> +
>  #else /* !HAVE_SYS_MMAN_H */
>  
>  /* See dwarf-index-cache.h.  This is a no-op on unsupported systems.  */
> @@ -227,6 +254,12 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
>    return {};
>  }
>  
> +gdb::array_view<const gdb_byte>
> +index_cache::lookup_gdb_index_debuginfod (const char *index_path,
> +					  std::unique_ptr<index_cache_resource> *resource)
> +{
> +  return {};
> +}
>  #endif
>  
>  /* See dwarf-index-cache.h.  */
> diff --git a/gdb/dwarf2/index-cache.h b/gdb/dwarf2/index-cache.h
> index 1efff17049f..e400afd5123 100644
> --- a/gdb/dwarf2/index-cache.h
> +++ b/gdb/dwarf2/index-cache.h
> @@ -67,6 +67,19 @@ class index_cache
>    lookup_gdb_index (const bfd_build_id *build_id,
>  		    std::unique_ptr<index_cache_resource> *resource);
>  
> +  /* Look for an index file located at INDEX_PATH in the debuginfod cache.
> +     Unlike lookup_gdb_index, this function does not exit early if the
> +     index cache has not been enabled.
> +
> +     If found, return the contents as an array_view and store the underlying
> +     resources (allocated memory, mapped file, etc) in RESOURCE.  The returned
> +     array_view is valid as long as RESOURCE is not destroyed.
> +
> +     If no matching index file is found, return an empty array view.  */
> +  gdb::array_view<const gdb_byte>
> +  lookup_gdb_index_debuginfod (const char *index_path,
> +			       std::unique_ptr<index_cache_resource> *resource);
> +
>    /* Return the number of cache hits.  */
>    unsigned int n_hits () const
>    { return m_n_hits; }
> diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h
> index 0e74857eb1a..4a44cdbc223 100644
> --- a/gdb/dwarf2/public.h
> +++ b/gdb/dwarf2/public.h
> @@ -40,4 +40,11 @@ extern void dwarf2_initialize_objfile (struct objfile *objfile);
>  
>  extern void dwarf2_build_frame_info (struct objfile *);
>  
> +/* Query debuginfod for the .gdb_index associated with OBJFILE.  If
> +   successful, create an objfile to hold the .gdb_index information
> +   and act as a placeholder until the full debuginfo needs to be
> +   downloaded.  */
> +
> +extern bool dwarf2_has_separate_index (struct objfile *);
> +
>  #endif /* DWARF2_PUBLIC_H */
> diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> index 1006386cb2d..d3516e92361 100644
> --- a/gdb/dwarf2/read-gdb-index.c
> +++ b/gdb/dwarf2/read-gdb-index.c
> @@ -136,6 +136,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       gdb.dwarf2/gdb-index.exp testcase.  */
>    void dump (struct objfile *objfile) override;
>  
> +  /* Calls do_expand_matching_symbols and downloads debuginfo if necessary.  */
>    void expand_matching_symbols
>      (struct objfile *,
>       const lookup_name_info &lookup_name,
> @@ -143,6 +144,14 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       int global,
>       symbol_compare_ftype *ordered_compare) override;
>  
> +  void do_expand_matching_symbols
> +    (struct objfile *,
> +     const lookup_name_info &lookup_name,
> +     domain_enum domain,
> +     int global,
> +     symbol_compare_ftype *ordered_compare);
> +
> +  /* Calls do_expand_symtabs_matching and downloads debuginfo if necessary.  */
>    bool expand_symtabs_matching
>      (struct objfile *objfile,
>       gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> @@ -152,8 +161,59 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       block_search_flags search_flags,
>       domain_enum domain,
>       enum search_domain kind) override;
> +
> +  bool do_expand_symtabs_matching
> +    (struct objfile *objfile,
> +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> +     const lookup_name_info *lookup_name,
> +     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> +     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> +     block_search_flags search_flags,
> +     domain_enum domain,
> +     enum search_domain kind);
> +
> +  /* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
> +     debuginfo if necessary.  */
> +  void expand_all_symtabs (struct objfile *objfile) override;
> +
> +  /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> +     debuginfo if necessary.  */
> +  struct symtab *find_last_source_symtab (struct objfile *objfile) override;
>  };
>  
> +void
> +dwarf2_gdb_index::expand_all_symtabs (struct objfile *objfile)
> +{
> +  try
> +    {
> +      dwarf2_base_index_functions::expand_all_symtabs (objfile);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	exception_print (gdb_stderr, e);
> +      else
> +	read_full_dwarf_from_debuginfod (objfile, this);
> +    }
> +}
> +
> +struct symtab *
> +dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
> +{
> +  try
> +    {
> +      return dwarf2_base_index_functions::find_last_source_symtab (objfile);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	exception_print (gdb_stderr, e);
> +      else
> +	read_full_dwarf_from_debuginfod (objfile, this);
> +      return nullptr;
> +    }
> +}
> +
>  /* This dumps minimal information about the index.
>     It is called via "mt print objfiles".
>     One use is to verify .gdb_index has been loaded by the
> @@ -315,7 +375,7 @@ dw2_symtab_iter_next (struct dw2_symtab_iterator *iter,
>  }
>  
>  void
> -dwarf2_gdb_index::expand_matching_symbols
> +dwarf2_gdb_index::do_expand_matching_symbols
>    (struct objfile *objfile,
>     const lookup_name_info &name, domain_enum domain,
>     int global,
> @@ -353,6 +413,29 @@ dwarf2_gdb_index::expand_matching_symbols
>      }, per_objfile);
>  }
>  
> +void
> +dwarf2_gdb_index::expand_matching_symbols
> +  (struct objfile *objfile,
> +   const lookup_name_info &lookup_name,
> +   domain_enum domain,
> +   int global,
> +   symbol_compare_ftype *ordered_compare)
> +{
> +  try
> +    {
> +      do_expand_matching_symbols (objfile, lookup_name, domain,
> +				  global, ordered_compare);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	exception_print (gdb_stderr, e);
> +      else
> +	read_full_dwarf_from_debuginfod (objfile, this);
> +      return;
> +    }
> +}
> +
>  /* Helper for dw2_expand_matching symtabs.  Called on each symbol
>     matched, to expand corresponding CUs that were marked.  IDX is the
>     index of the symbol name that matched.  */
> @@ -455,7 +538,7 @@ dw2_expand_marked_cus
>  }
>  
>  bool
> -dwarf2_gdb_index::expand_symtabs_matching
> +dwarf2_gdb_index::do_expand_symtabs_matching
>      (struct objfile *objfile,
>       gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
>       const lookup_name_info *lookup_name,
> @@ -504,6 +587,39 @@ dwarf2_gdb_index::expand_symtabs_matching
>    return result;
>  }
>  
> +bool
> +dwarf2_gdb_index::expand_symtabs_matching
> +    (struct objfile *objfile,
> +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> +     const lookup_name_info *lookup_name,
> +     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> +     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> +     block_search_flags search_flags,
> +     domain_enum domain,
> +     enum search_domain kind)
> +{
> +  if (objfile->flags & OBJF_READNEVER)
> +    return false;
> +
> +  try
> +    {
> +      return do_expand_symtabs_matching (objfile, file_matcher, lookup_name,
> +					 symbol_matcher, expansion_notify,
> +					 search_flags, domain, kind);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	{
> +	  exception_print (gdb_stderr, e);
> +	  return false;
> +	}
> +
> +      read_full_dwarf_from_debuginfod (objfile, this);
> +      return true;
> +    }
> +}
> +
>  quick_symbol_functions_up
>  mapped_gdb_index::make_quick_functions () const
>  {
> @@ -797,28 +913,32 @@ dwarf2_read_gdb_index
>  
>    /* If there is a .dwz file, read it so we can get its CU list as
>       well.  */
> -  dwz = dwarf2_get_dwz_file (per_bfd);
> -  if (dwz != NULL)
> +  if (get_gdb_index_contents_dwz != nullptr)
>      {
>        mapped_gdb_index dwz_map;
>        const gdb_byte *dwz_types_ignore;
>        offset_type dwz_types_elements_ignore;
> +      dwz = dwarf2_get_dwz_file (per_bfd);
>  
> -      gdb::array_view<const gdb_byte> dwz_index_content
> -	= get_gdb_index_contents_dwz (objfile, dwz);
> -
> -      if (dwz_index_content.empty ())
> -	return 0;
> -
> -      if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()),
> -				       1, dwz_index_content, &dwz_map,
> -				       &dwz_list, &dwz_list_elements,
> -				       &dwz_types_ignore,
> -				       &dwz_types_elements_ignore))
> +      if (dwz != nullptr)
>  	{
> -	  warning (_("could not read '.gdb_index' section from %s; skipping"),
> -		   bfd_get_filename (dwz->dwz_bfd.get ()));
> -	  return 0;
> +	  gdb::array_view<const gdb_byte> dwz_index_content
> +	    = get_gdb_index_contents_dwz (objfile, dwz);
> +
> +	  if (dwz_index_content.empty ())
> +	    return 0;
> +
> +	  if (!read_gdb_index_from_buffer (bfd_get_filename
> +					     (dwz->dwz_bfd.get ()),
> +					   1, dwz_index_content, &dwz_map,
> +					   &dwz_list, &dwz_list_elements,
> +					   &dwz_types_ignore,
> +					   &dwz_types_elements_ignore))
> +	    {
> +	      warning (_("could not read '.gdb_index' section from %s; skipping"),
> +		       bfd_get_filename (dwz->dwz_bfd.get ()));
> +		return 0;
> +	    }
>  	}
>      }
>  
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index 4828409222c..96d1ff53d91 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -34,6 +34,7 @@
>  #include "dwarf2/attribute.h"
>  #include "dwarf2/comp-unit-head.h"
>  #include "dwarf2/cu.h"
> +#include "dwarf2/frame.h"
>  #include "dwarf2/index-cache.h"
>  #include "dwarf2/index-common.h"
>  #include "dwarf2/leb.h"
> @@ -95,6 +96,8 @@
>  #include "split-name.h"
>  #include "gdbsupport/parallel-for.h"
>  #include "gdbsupport/thread-pool.h"
> +#include "inferior.h"
> +#include "debuginfod-support.h"
>  
>  /* When == 1, print basic high level tracing messages.
>     When > 1, be more verbose.
> @@ -3168,7 +3171,7 @@ dwarf2_base_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd,
>  }
>  
>  struct compunit_symtab *
> -dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> +dwarf2_base_index_functions::do_find_pc_sect_compunit_symtab
>       (struct objfile *objfile,
>        struct bound_minimal_symbol msymbol,
>        CORE_ADDR pc,
> @@ -3199,6 +3202,32 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
>    return result;
>  }
>  
> +struct compunit_symtab *
> +dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> +     (struct objfile *objfile,
> +      struct bound_minimal_symbol msymbol,
> +      CORE_ADDR pc,
> +      struct obj_section *section,
> +      int warn_if_readin)
> +{
> +  if (objfile->flags & OBJF_READNEVER)
> +    return nullptr;
> +
> +  try
> +    {
> +      return do_find_pc_sect_compunit_symtab (objfile, msymbol, pc,
> +					      section, warn_if_readin);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	exception_print (gdb_stderr, e);
> +      else
> +	read_full_dwarf_from_debuginfod (objfile, this);
> +      return nullptr;
> +    }
> +}
> +
>  void
>  dwarf2_base_index_functions::map_symbol_filenames
>       (struct objfile *objfile,
> @@ -3355,6 +3384,29 @@ get_gdb_index_contents_from_cache_dwz (objfile *obj, dwz_file *dwz)
>    return global_index_cache.lookup_gdb_index (build_id, &dwz->index_cache_res);
>  }
>  
> +/* Query debuginfod for the .gdb_index matching OBJFILE's build-id.  Return the
> +   contents if successful.  */
> +
> +static gdb::array_view<const gdb_byte>
> +get_gdb_index_contents_from_debuginfod (objfile *objfile, dwarf2_per_bfd *per_bfd)
> +{
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  if (build_id == nullptr)
> +    return {};
> +
> +  gdb::unique_xmalloc_ptr<char> index_path;
> +  scoped_fd fd = debuginfod_section_query (build_id->data, build_id->size,
> +					   bfd_get_filename
> +					     (objfile->obfd.get ()),
> +					   ".gdb_index",
> +					   &index_path);
> +  if (fd.get () < 0)
> +    return {};
> +
> +  return global_index_cache.lookup_gdb_index_debuginfod
> +    (index_path.get (), &per_bfd->index_cache_res);
> +}
> +
>  static quick_symbol_functions_up make_cooked_index_funcs ();
>  
>  /* See dwarf2/public.h.  */
> @@ -3420,10 +3472,102 @@ dwarf2_initialize_objfile (struct objfile *objfile)
>        return;
>      }
>  
> +  if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED)
> +      && dwarf2_read_gdb_index (per_objfile,
> +				get_gdb_index_contents_from_debuginfod,
> +				nullptr))
> +    {
> +      dwarf_read_debug_printf ("found .gdb_index from debuginfod");
> +      objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
> +      objfile->qf.begin ()->get ()->from_separate_index = true;
> +      return;
> +    }
> +
>    global_index_cache.miss ();
>    objfile->qf.push_front (make_cooked_index_funcs ());
>  }
>  
> +/* See read.h.  */
> +
> +void
> +read_full_dwarf_from_debuginfod (struct objfile *objfile,
> +				 dwarf2_base_index_functions *fncs)
> +{
> +  gdb_assert (objfile->flags & OBJF_DOWNLOAD_DEFERRED);
> +
> +  const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  const char *filename;
> +  gdb_bfd_ref_ptr debug_bfd;
> +  gdb::unique_xmalloc_ptr<char> symfile_path;
> +  scoped_fd fd;
> +
> +  if (build_id == nullptr)
> +    goto unset;
> +
> +  filename = bfd_get_filename (objfile->obfd.get ());
> +  fd = debuginfod_debuginfo_query (build_id->data, build_id->size,
> +				   filename, &symfile_path);
> +  if (fd.get () < 0)
> +    goto unset;
> +
> +  /* Separate debuginfo successfully retrieved from server.  */
> +  debug_bfd = symfile_bfd_open (symfile_path.get ());
> +  if (debug_bfd == nullptr
> +      || !build_id_verify (debug_bfd.get (), build_id->size, build_id->data))
> +    {
> +      warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
> +	       filename);
> +      goto unset;
> +    }
> +
> +  /* Clear frame data so it can be recalculated using DWARF.  */
> +  dwarf2_clear_frame_data (objfile);
> +
> +  /* This may also trigger a dwz download.  */
> +  symbol_file_add_separate (debug_bfd, symfile_path.get (),
> +			    current_inferior ()->symfile_flags, objfile);
> +
> +unset:
> +  objfile->remove_deferred_status ();
> +}
> +
> +/* See public.h.  */
> +
> +bool
> +dwarf2_has_separate_index (struct objfile *objfile)
> +{
> +  if (objfile->flags & OBJF_DOWNLOAD_DEFERRED)
> +    return true;
> +  if (objfile->flags & OBJF_MAINLINE)
> +    return false;
> +  if (!IS_DIR_SEPARATOR (*objfile_filename (objfile)))
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> index_path;
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +
> +  if (build_id == nullptr)
> +    return false;
> +
> +  scoped_fd fd = debuginfod_section_query (build_id->data,
> +					   build_id->size,
> +					   bfd_get_filename
> +					     (objfile->obfd.get ()),
> +					   ".gdb_index",
> +					   &index_path);
> +
> +  if (fd.get () < 0)
> +    return false;
> +
> +  /* We found a separate .gdb_index file so a separate debuginfo file
> +     should exist, but we don't want to download it until necessary.
> +     Attach the index to this objfile and defer the debuginfo download
> +     until gdb needs to expand symtabs referenced by the index.  */
> +  objfile->flags |= OBJF_DOWNLOAD_DEFERRED;
> +  dwarf2_initialize_objfile (objfile);
> +  return true;
> +}
> +
>  \f
>  
>  /* Build a partial symbol table.  */
> diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> index 37023a20709..e3131693b81 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -866,6 +866,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
>       CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
>         override final;
>  
> +  struct compunit_symtab *do_find_pc_sect_compunit_symtab
> +    (struct objfile *objfile, struct bound_minimal_symbol msymbol,
> +     CORE_ADDR pc, struct obj_section *section, int warn_if_readin);
> +
>    struct compunit_symtab *find_compunit_symtab_by_address
>      (struct objfile *objfile, CORE_ADDR address) override
>    {
> @@ -942,4 +946,10 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
>  				       dwarf2_section_info *section,
>  				       addrmap *mutable_map);
>  
> +/* If OBJFILE contains information from a separately downloaded .gdb_index,
> +   attempt to download the full debuginfo.  */
> +
> +extern void read_full_dwarf_from_debuginfod (struct objfile *,
> +					     dwarf2_base_index_functions *);
> +
>  #endif /* DWARF2READ_H */
> diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c
> index c9ef41893ee..8cb09e3381a 100644
> --- a/gdb/dwarf2/section.c
> +++ b/gdb/dwarf2/section.c
> @@ -54,7 +54,8 @@ dwarf2_section_info::get_bfd_owner () const
>        section = get_containing_section ();
>        gdb_assert (!section->is_virtual);
>      }
> -  gdb_assert (section->s.section != nullptr);
> +  if (section->s.section == nullptr)
> +    error (_("Can't find owner of DWARF section."));
>    return section->s.section->owner;
>  }
>  
> diff --git a/gdb/elfread.c b/gdb/elfread.c
> index 799e3b914f8..133341ea615 100644
> --- a/gdb/elfread.c
> +++ b/gdb/elfread.c
> @@ -1242,7 +1242,7 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
>  	    symbol_file_add_separate (debug_bfd, debugfile.c_str (),
>  				      symfile_flags, objfile);
>  	}
> -      else
> +      else if (!dwarf2_has_separate_index (objfile))
>  	{
>  	  has_dwarf2 = false;
>  	  const struct bfd_build_id *build_id
> diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h
> index 9dee2ee51a0..fb3f741c899 100644
> --- a/gdb/objfile-flags.h
> +++ b/gdb/objfile-flags.h
> @@ -60,6 +60,10 @@ enum objfile_flag : unsigned
>      /* User requested that we do not read this objfile's symbolic
>         information.  */
>      OBJF_READNEVER = 1 << 6,
> +
> +    /* A separate .gdb_index has been downloaded for this objfile.
> +       Debuginfo for this objfile can be downloaded when required.  */
> +    OBJF_DOWNLOAD_DEFERRED = 1 << 7,
>    };
>  
>  DEF_ENUM_FLAGS_TYPE (enum objfile_flag, objfile_flags);
> diff --git a/gdb/objfiles.h b/gdb/objfiles.h
> index bb7b0a4579d..57bc1d45965 100644
> --- a/gdb/objfiles.h
> +++ b/gdb/objfiles.h
> @@ -613,11 +613,22 @@ struct objfile
>    /* See quick_symbol_functions.  */
>    void require_partial_symbols (bool verbose);
>  
> -  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
> -  void remove_partial_symbol (quick_symbol_functions *target)
> +  /*  Indicate that the aquisition of this objfile's separate debug objfile
> +      is no longer deferred.  Used when the debug objfile has been aquired
> +      or could not be found.  */
> +  void remove_deferred_status ()
>    {
> +    flags &= ~OBJF_DOWNLOAD_DEFERRED;
> +
> +   /* Remove quick_symbol_functions derived from a separately downloaded
> +      index.  If available the separate debug objfile's index will be used
> +      instead, since that objfile actually contains the symbols and CUs
> +      referenced in the index.
> +
> +      No more than one element of qf should have from_separate_index set
> +      to true.  */
>      for (quick_symbol_functions_up &qf_up : qf)
> -      if (qf_up.get () == target)
> +      if (qf_up->from_separate_index)
>  	{
>  	  qf.remove (qf_up);
>  	  return;
> diff --git a/gdb/quick-symbol.h b/gdb/quick-symbol.h
> index a7fea2ccb49..e7163503e39 100644
> --- a/gdb/quick-symbol.h
> +++ b/gdb/quick-symbol.h
> @@ -225,6 +225,10 @@ struct quick_symbol_functions
>    virtual void read_partial_symbols (struct objfile *objfile)
>    {
>    }
> +
> +  /* True if this quick_symbol_functions is derived from a separately
> +     downloaded index.  */
> +  bool from_separate_index = false;
>  };
>  
>  typedef std::unique_ptr<quick_symbol_functions> quick_symbol_functions_up;
> diff --git a/gdb/symfile.c b/gdb/symfile.c
> index 96239679c77..c476196184a 100644
> --- a/gdb/symfile.c
> +++ b/gdb/symfile.c
> @@ -991,6 +991,10 @@ syms_from_objfile (struct objfile *objfile,
>  static void
>  finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
>  {
> +  struct objfile *parent = objfile->separate_debug_objfile_backlink;
> +  bool was_deferred
> +    = (parent != nullptr) && (parent->flags & OBJF_DOWNLOAD_DEFERRED);
> +
>    /* If this is the main symbol file we have to clean up all users of the
>       old main symbol file.  Otherwise it is sufficient to fixup all the
>       breakpoints that may have been redefined by this symbol file.  */
> @@ -1001,7 +1005,8 @@ finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
>  
>        clear_symtab_users (add_flags);
>      }
> -  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0)
> +  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0
> +	   && !was_deferred)
>      {
>        breakpoint_re_set ();
>      }
> @@ -1127,6 +1132,12 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name,
>  
>    finish_new_objfile (objfile, add_flags);
>  
> +  /* Remove deferred status now in case any observers trigger symtab
> +     expansion.  Otherwise gdb might try to read parent for psymbols
> +     when it should read the separate debug objfile instead.  */
> +  if (parent != nullptr && (parent->flags & OBJF_DOWNLOAD_DEFERRED))
> +    parent->remove_deferred_status ();
> +
>    gdb::observers::new_objfile.notify (objfile);
>  
>    bfd_cache_close_all ();
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index 5e1b9d91879..2408725fa73 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -2897,14 +2897,30 @@ find_pc_sect_compunit_symtab (CORE_ADDR pc, struct obj_section *section)
>    if (best_cust != NULL)
>      return best_cust;
>  
> +  int warn_if_readin = 1;
> +
>    /* Not found in symtabs, search the "quick" symtabs (e.g. psymtabs).  */
>  
>    for (objfile *objf : current_program_space->objfiles ())
>      {
> +      bool was_deferred = objf->flags & OBJF_DOWNLOAD_DEFERRED;
> +
>        struct compunit_symtab *result
> -	= objf->find_pc_sect_compunit_symtab (msymbol, pc, section, 1);
> +	= objf->find_pc_sect_compunit_symtab (msymbol, pc, section,
> +					      warn_if_readin);
> +
>        if (result != NULL)
>  	return result;
> +
> +      /* If objf's separate debug info was just acquired, disable
> +	 warn_if_readin for the next iteration of this loop.  This prevents
> +	 a spurious warning in case an observer already triggered expansion
> +	 of the separate debug objfile's symtabs.  */
> +      if (was_deferred && objf->separate_debug_objfile != nullptr
> +	  && (objf->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	warn_if_readin = 0;
> +      else if (warn_if_readin == 0)
> +	warn_if_readin = 1;
>      }
>  
>    return NULL;
> diff --git a/gdb/testsuite/gdb.debuginfod/libsection1.c b/gdb/testsuite/gdb.debuginfod/libsection1.c
> new file mode 100644
> index 00000000000..60824b415c6
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/libsection1.c
> @@ -0,0 +1,40 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 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 <stdio.h>
> +#include <pthread.h>
> +#include <unistd.h>
> +
> +extern void libsection2_test ();
> +extern void *libsection2_thread_test (void *);
> +
> +void
> +libsection1_test ()
> +{
> +  pthread_t thr;
> +
> +  printf ("In libsection1\n");
> +  libsection2_test ();
> +
> +  pthread_create (&thr, NULL, libsection2_thread_test, NULL);
> +
> +  /* Give the new thread a chance to actually enter libsection2_thread_test.  */
> +  sleep (3);
> +  printf ("Cancelling thread\n");
> +
> +  pthread_cancel (thr);
> +}
> diff --git a/gdb/testsuite/gdb.debuginfod/libsection2.c b/gdb/testsuite/gdb.debuginfod/libsection2.c
> new file mode 100644
> index 00000000000..629a67f94a5
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/libsection2.c
> @@ -0,0 +1,37 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 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 <stdio.h>
> +
> +void
> +libsection2_test ()
> +{
> +  printf ("In libsection2\n");
> +}
> +
> +void *
> +libsection2_thread_test (void *arg)
> +{
> +  (void) arg;
> +
> +  printf ("In thread test\n");
> +
> +  while (1)
> +    ;
> +
> +  return NULL;
> +}
> diff --git a/gdb/testsuite/gdb.debuginfod/section.c b/gdb/testsuite/gdb.debuginfod/section.c
> new file mode 100644
> index 00000000000..d391a8f898e
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/section.c
> @@ -0,0 +1,29 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 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 <stdio.h>
> +
> +extern void libsection1_test ();
> +
> +int
> +main()
> +{
> +  libsection1_test ();
> +  printf ("in section exec\n");
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
> new file mode 100644
> index 00000000000..96e9750cd38
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> @@ -0,0 +1,181 @@
> +# Copyright 2023 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 debuginfod functionality
> +
> +standard_testfile
> +
> +load_lib debuginfod-support.exp
> +
> +require allow_debuginfod_tests
> +
> +set sourcetmp [standard_output_file tmp-${srcfile}]
> +set outputdir [standard_output_file {}]
> +
> +# SECTEXEC is an executable which calls a function from LIB_SL1.
> +set sectfile "section"
> +set sectsrc $srcdir/$subdir/section.c
> +set sectexec [standard_output_file $sectfile]
> +
> +# Solib LIB_SL1 calls functions from LIB_SL2.
> +set libfile1 "libsection1"
> +set libsrc1 $srcdir/$subdir/$libfile1.c
> +set lib_sl1 [standard_output_file $libfile1.sl]
> +
> +set libfile2 "libsection2"
> +set libsrc2 $srcdir/$subdir/$libfile2.c
> +set lib_sl2 [standard_output_file $libfile2.sl]
> +
> +set lib_opts1 [list debug build-id shlib=$lib_sl2]
> +set lib_opts2 [list debug build-id]
> +set exec_opts [list debug build-id shlib=$lib_sl1 shlib=$lib_sl2]
> +
> +clean_restart
> +
> +if {[enable_section_downloads] == 0} {
> +    untested "GDB does not support debuginfod section downloads"
> +    return -1
> +}
> +
> +# Compile SECTEXEC, LIB_SL1 and LIB_SL2.
> +if { [gdb_compile_shlib $libsrc2 $lib_sl2 $lib_opts2] != "" } {
> +    untested "failed to compile $libfile2"
> +    return -1
> +}
> +
> +if { [gdb_compile_shlib_pthreads $libsrc1 $lib_sl1 $lib_opts1] != "" } {
> +    untested "failed to compile $libfile1"
> +    return -1
> +}
> +
> +if { [gdb_compile $sectsrc $sectexec executable $exec_opts] != "" } {
> +    untested "failed to compile $sectfile"
> +    return -1
> +}
> +
> +# Add .gdb_index to solibs.
> +if { [have_index $lib_sl1] != "gdb_index"
> +     && [add_gdb_index $lib_sl1] == 0 } {
> +    untested "failed to add .gdb_index to $libfile1"
> +    return -1
> +}
> +
> +if { [have_index $lib_sl2] != "gdb_index"
> +     && [add_gdb_index $lib_sl2] == 0 } {
> +    untested "failed to add .gdb_index to $libfile2"
> +    return -1
> +}
> +
> +# Strip solib debuginfo into separate files.
> +if { [gdb_gnu_strip_debug $lib_sl1 ""] != 0} {
> +   fail "strip $lib_sl1 debuginfo"
> +   return -1
> +}
> +
> +if { [gdb_gnu_strip_debug $lib_sl2 ""] != 0} {
> +   fail "strip $lib_sl2 debuginfo"
> +   return -1
> +}
> +
> +# Move debuginfo files into directory that debuginfod will serve from.
> +set debugdir [standard_output_file "debug"]
> +set debuginfo_sl1 [standard_output_file $libfile1.sl.debug]
> +set debuginfo_sl2 [standard_output_file $libfile2.sl.debug]
> +
> +file mkdir $debugdir
> +file rename -force $debuginfo_sl1 $debugdir
> +file rename -force $debuginfo_sl2 $debugdir
> +
> +# Restart GDB and clear the debuginfod client cache. Then load BINFILE into
> +# GDB and start running it.  Match output with pattern RES and use TESTNAME
> +# as the test name.
> +proc_with_prefix clean_restart_with_prompt { binfile res testname } {
> +    global cache
> +
> +    clean_restart
> +
> +    # Delete client cache so debuginfo downloads again.
> +    file delete -force $cache
> +
> +    gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
> +    gdb_test "start" $res "file [file tail $binfile] start $testname" \
> +	     ".*Enable debuginfod.*" "y"
> +}
> +
> +# Tests with no debuginfod server running.
> +proc_with_prefix no_url { } {
> +    global sectexec libfile1 libfile2
> +
> +    # Check that no section is downloaded and no debuginfo is found.
> +    gdb_test "file $sectexec" "" "file [file tail $sectexec] file no url"
> +    gdb_test "start" "" "file [file tail $sectexec] start no url"
> +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile1.*" \
> +	     "file [file tail $libfile1] found no url"
> +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile2.*" \
> +	     "file [file tail $libfile2] found no url"
> +}
> +
> +# Tests with a debuginfod server running.
> +proc_with_prefix local_url { } {
> +    global sectexec
> +    global libsrc1 lib_sl1 libfile1
> +    global libsrc2 lib_sl2 libfile2
> +    global debugdir db
> +
> +    set url [start_debuginfod $db $debugdir]
> +    if { $url == "" } {
> +	unresolved "failed to start debuginfod server"
> +	return
> +    }
> +
> +    # Point GDB to the server.
> +    setenv DEBUGINFOD_URLS $url
> +
> +    # Download .gdb_index for solibs.
> +    set res ".*section \.gdb_index for $lib_sl1.*\
> +	section \.gdb_index for $lib_sl2.*"
> +    clean_restart_with_prompt $sectexec $res "index"
> +
> +    # Download debuginfo when stepping into a function.
> +    set res ".*separate debug info for $lib_sl1.*\"In ${libfile1}\\\\n\".*"
> +    gdb_test "step" $res "file [file tail $lib_sl1] step"
> +
> +    clean_restart_with_prompt $sectexec "" "break"
> +
> +    # Download debuginfo when setting a breakpoint.
> +    set res "Download.*separate debug info for $lib_sl2.*"
> +    gdb_test "br libsection2_test" $res "file [file tail $sectexec] break set"
> +
> +    # Hit the breakpoint.
> +    set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
> +    gdb_test "c" $res "file [file tail $sectexec] break continue"
> +
> +    # Check that download progress message is correctly formatted
> +    # during backtrace.
> +    set res ".* separate debug info for $lib_sl1.*#0  libsection2_test\
> +	\\(\\) at.*"
> +    set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
> +    gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
> +}
> +
> +# Create CACHE and DB directories ready for debuginfod to use.
> +prepare_for_debuginfod cache db
> +
> +with_debuginfod_env $cache {
> +    no_url
> +    local_url
> +}
> +
> +stop_debuginfod
> diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
> index 50a8b512a4a..6368c27e9d0 100644
> --- a/gdb/testsuite/lib/debuginfod-support.exp
> +++ b/gdb/testsuite/lib/debuginfod-support.exp
> @@ -194,3 +194,33 @@ proc stop_debuginfod { } {
>  	unset debuginfod_spawn_id
>      }
>  }
> +
> +# Return 1 if gdb is configured to download ELF/DWARF sections from
> +# debuginfod servers.  Otherwise return 0.
> +proc enable_section_downloads { } {
> +    global gdb_prompt
> +
> +    set cmd "maint set debuginfod download-sections on"
> +    set msg "enable section downloads"
> +
> +    gdb_test_multiple $cmd $msg {
> +	-re ".*Undefined maintenance.*" {
> +	    perror "Undefined command: \"$cmd\""
> +	    return 0
> +	}
> +	-re ".*not compiled into GDB.*" {
> +	    perror "Unsupported command: \"$cmd\""

There shouldn't be a perror call here, this isn't an error case, it's
just GDB detecting that this feature isn't compiled in.  As it stands,
if we take this path then the 'untested' call in the caller will act as
'unresolved', which isn't what we want.

> +	    return 0
> +	}
> +	-re "\r\n${gdb_prompt} $" {
> +	    return 1
> +	}

I wonder if we can use:

  -re -wrap "^" {
    return 1
  }

here?  Which would allow us to make the "Undefined maintenance" case
more generic, something like:

  -re -wrap "" {
      fail "$gdb_test_name (unexpected output)"
      return 0
  }

> +	timeout {
> +	    perror "timeout: \"$cmd\""

I don't think we should use perror here either, but instead just call
fail, in which case, we can probably drop the timeout case completely as
gdb_test_multiple already handles it.  I think something like this:

  # Return 1 if gdb is configured to download ELF/DWARF sections from
  # debuginfod servers.  Otherwise return 0.
  proc enable_section_downloads { } {
      global gdb_prompt
  
      set cmd "maint set debuginfod download-sections on"
      set msg "enable section downloads"
  
      return [gdb_test_multiple $cmd $msg {
  	-re -wrap ".*not compiled into GDB.*" {
  	    return 0
  	}
  	-re -wrap "^" {
  	    return 1
  	}
  	-re -wrap "" {
  	    fail "$gdb_test_name (unexpected output)"
  	    return 0
  	}
      }]
  }

would do the job, though I don't currently have a machine with a recent
enough version of debuginfod installed to fully test this, but something
like this should do the job.

Thanks,
Andrew


> +	    return 0
> +	}
> +    }
> +
> +    perror "Unexpected output for \"$cmd\""
> +    return 0
> +}
> -- 
> 2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading
  2023-07-07 14:18   ` [PATCH " Andrew Burgess
@ 2023-07-10 21:01     ` Aaron Merey
  2023-07-11 12:01       ` Pedro Alves
  0 siblings, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-07-10 21:01 UTC (permalink / raw)
  To: aburgess; +Cc: gdb-patches, Aaron Merey

On Fri, Jul 7, 2023 at 10:18 AM Andrew Burgess <aburgess@redhat.com> wrote:
>
>   # Return 1 if gdb is configured to download ELF/DWARF sections from
>   # debuginfod servers.  Otherwise return 0.
>   proc enable_section_downloads { } {
>       global gdb_prompt
>
>       set cmd "maint set debuginfod download-sections on"
>       set msg "enable section downloads"
>
>       return [gdb_test_multiple $cmd $msg {
>         -re -wrap ".*not compiled into GDB.*" {
>             return 0
>         }
>         -re -wrap "^" {
>             return 1
>         }
>         -re -wrap "" {
>             fail "$gdb_test_name (unexpected output)"
>             return 0
>         }
>       }]
>   }
>
> would do the job, though I don't currently have a machine with a recent
> enough version of debuginfod installed to fully test this, but something
> like this should do the job.

Thanks Andrew, this works on my machine. I've updated the patch below with
this change.

Commit message:

At the beginning of a session, gdb may attempt to download debuginfo
for all shared libraries associated with the process or core file
being debugged.  This can be a waste of time and storage space when much
of the debuginfo ends up not being used during the session.

To reduce the gdb's startup latency and to download only the debuginfo
that is really needed, this patch adds on-demand downloading of debuginfo.

'set debuginfo enabled on' now causes gdb to attempt to download a .gdb_index
for each shared library instead of its full debuginfo.  Each corresponding
separate debuginfo will be deferred until gdb needs to expand symtabs
associated with the debuginfo's index.

Because these indices are significantly smaller than their corresponding
debuginfo, this generally reduces the total amount of data gdb downloads.
Reductions of 80%-95% have been observed when debugging large GUI programs.
---
 gdb/dwarf2/frame.c                         |  13 ++
 gdb/dwarf2/frame.h                         |   4 +
 gdb/dwarf2/index-cache.c                   |  33 ++++
 gdb/dwarf2/index-cache.h                   |  13 ++
 gdb/dwarf2/public.h                        |   7 +
 gdb/dwarf2/read-gdb-index.c                | 156 ++++++++++++++++--
 gdb/dwarf2/read.c                          | 146 ++++++++++++++++-
 gdb/dwarf2/read.h                          |  10 ++
 gdb/dwarf2/section.c                       |   3 +-
 gdb/elfread.c                              |   2 +-
 gdb/objfile-flags.h                        |   4 +
 gdb/objfiles.h                             |  17 +-
 gdb/quick-symbol.h                         |   4 +
 gdb/symfile.c                              |  13 +-
 gdb/symtab.c                               |  18 +-
 gdb/testsuite/gdb.debuginfod/libsection1.c |  40 +++++
 gdb/testsuite/gdb.debuginfod/libsection2.c |  37 +++++
 gdb/testsuite/gdb.debuginfod/section.c     |  29 ++++
 gdb/testsuite/gdb.debuginfod/section.exp   | 181 +++++++++++++++++++++
 gdb/testsuite/lib/debuginfod-support.exp   |  22 +++
 20 files changed, 726 insertions(+), 26 deletions(-)
 create mode 100644 gdb/testsuite/gdb.debuginfod/libsection1.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/libsection2.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/section.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/section.exp

diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 940a01e9612..84ee99f8453 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -1617,6 +1617,19 @@ set_comp_unit (struct objfile *objfile, struct comp_unit *unit)
   return dwarf2_frame_bfd_data.set (abfd, unit);
 }
 
+/* See frame.h.  */
+
+void
+dwarf2_clear_frame_data (struct objfile *objfile)
+{
+  bfd *abfd = objfile->obfd.get ();
+
+  if (gdb_bfd_requires_relocations (abfd))
+    dwarf2_frame_objfile_data.clear (objfile);
+  else
+    dwarf2_frame_bfd_data.clear (abfd);
+}
+
 /* Find the FDE for *PC.  Return a pointer to the FDE, and store the
    initial location associated with it into *PC.  */
 
diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
index 5643e557513..2391e313e7c 100644
--- a/gdb/dwarf2/frame.h
+++ b/gdb/dwarf2/frame.h
@@ -238,6 +238,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
 extern const struct frame_base *
   dwarf2_frame_base_sniffer (frame_info_ptr this_frame);
 
+/* Delete OBJFILEs comp_unit.  */
+
+extern void dwarf2_clear_frame_data (struct objfile * objfile);
+
 /* Compute the DWARF CFA for a frame.  */
 
 CORE_ADDR dwarf2_frame_cfa (frame_info_ptr this_frame);
diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
index 79ab706ee9d..bbafcd321b2 100644
--- a/gdb/dwarf2/index-cache.c
+++ b/gdb/dwarf2/index-cache.c
@@ -216,6 +216,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
   return {};
 }
 
+/* See index-cache.h.  */
+
+gdb::array_view<const gdb_byte>
+index_cache::lookup_gdb_index_debuginfod (const char *index_path,
+					  std::unique_ptr<index_cache_resource> *resource)
+{
+  try
+    {
+      /* Try to map that file.  */
+      index_cache_resource_mmap *mmap_resource
+	= new index_cache_resource_mmap (index_path);
+
+      /* Hand the resource to the caller.  */
+      resource->reset (mmap_resource);
+
+      return gdb::array_view<const gdb_byte>
+	  ((const gdb_byte *) mmap_resource->mapping.get (),
+	   mmap_resource->mapping.size ());
+    }
+  catch (const gdb_exception_error &except)
+    {
+      warning (_("Unable to read %s: %s"), index_path, except.what ());
+    }
+
+  return {};
+}
+
 #else /* !HAVE_SYS_MMAN_H */
 
 /* See dwarf-index-cache.h.  This is a no-op on unsupported systems.  */
@@ -227,6 +254,12 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
   return {};
 }
 
+gdb::array_view<const gdb_byte>
+index_cache::lookup_gdb_index_debuginfod (const char *index_path,
+					  std::unique_ptr<index_cache_resource> *resource)
+{
+  return {};
+}
 #endif
 
 /* See dwarf-index-cache.h.  */
diff --git a/gdb/dwarf2/index-cache.h b/gdb/dwarf2/index-cache.h
index 1efff17049f..e400afd5123 100644
--- a/gdb/dwarf2/index-cache.h
+++ b/gdb/dwarf2/index-cache.h
@@ -67,6 +67,19 @@ class index_cache
   lookup_gdb_index (const bfd_build_id *build_id,
 		    std::unique_ptr<index_cache_resource> *resource);
 
+  /* Look for an index file located at INDEX_PATH in the debuginfod cache.
+     Unlike lookup_gdb_index, this function does not exit early if the
+     index cache has not been enabled.
+
+     If found, return the contents as an array_view and store the underlying
+     resources (allocated memory, mapped file, etc) in RESOURCE.  The returned
+     array_view is valid as long as RESOURCE is not destroyed.
+
+     If no matching index file is found, return an empty array view.  */
+  gdb::array_view<const gdb_byte>
+  lookup_gdb_index_debuginfod (const char *index_path,
+			       std::unique_ptr<index_cache_resource> *resource);
+
   /* Return the number of cache hits.  */
   unsigned int n_hits () const
   { return m_n_hits; }
diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h
index 0e74857eb1a..4a44cdbc223 100644
--- a/gdb/dwarf2/public.h
+++ b/gdb/dwarf2/public.h
@@ -40,4 +40,11 @@ extern void dwarf2_initialize_objfile (struct objfile *objfile);
 
 extern void dwarf2_build_frame_info (struct objfile *);
 
+/* Query debuginfod for the .gdb_index associated with OBJFILE.  If
+   successful, create an objfile to hold the .gdb_index information
+   and act as a placeholder until the full debuginfo needs to be
+   downloaded.  */
+
+extern bool dwarf2_has_separate_index (struct objfile *);
+
 #endif /* DWARF2_PUBLIC_H */
diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
index 1127643e53a..76a56388d2c 100644
--- a/gdb/dwarf2/read-gdb-index.c
+++ b/gdb/dwarf2/read-gdb-index.c
@@ -136,6 +136,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
      gdb.dwarf2/gdb-index.exp testcase.  */
   void dump (struct objfile *objfile) override;
 
+  /* Calls do_expand_matching_symbols and downloads debuginfo if necessary.  */
   void expand_matching_symbols
     (struct objfile *,
      const lookup_name_info &lookup_name,
@@ -143,6 +144,14 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
      int global,
      symbol_compare_ftype *ordered_compare) override;
 
+  void do_expand_matching_symbols
+    (struct objfile *,
+     const lookup_name_info &lookup_name,
+     domain_enum domain,
+     int global,
+     symbol_compare_ftype *ordered_compare);
+
+  /* Calls do_expand_symtabs_matching and downloads debuginfo if necessary.  */
   bool expand_symtabs_matching
     (struct objfile *objfile,
      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
@@ -152,8 +161,59 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
      block_search_flags search_flags,
      domain_enum domain,
      enum search_domain kind) override;
+
+  bool do_expand_symtabs_matching
+    (struct objfile *objfile,
+     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+     const lookup_name_info *lookup_name,
+     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+     block_search_flags search_flags,
+     domain_enum domain,
+     enum search_domain kind);
+
+  /* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
+     debuginfo if necessary.  */
+  void expand_all_symtabs (struct objfile *objfile) override;
+
+  /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
+     debuginfo if necessary.  */
+  struct symtab *find_last_source_symtab (struct objfile *objfile) override;
 };
 
+void
+dwarf2_gdb_index::expand_all_symtabs (struct objfile *objfile)
+{
+  try
+    {
+      dwarf2_base_index_functions::expand_all_symtabs (objfile);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	exception_print (gdb_stderr, e);
+      else
+	read_full_dwarf_from_debuginfod (objfile, this);
+    }
+}
+
+struct symtab *
+dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
+{
+  try
+    {
+      return dwarf2_base_index_functions::find_last_source_symtab (objfile);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	exception_print (gdb_stderr, e);
+      else
+	read_full_dwarf_from_debuginfod (objfile, this);
+      return nullptr;
+    }
+}
+
 /* This dumps minimal information about the index.
    It is called via "mt print objfiles".
    One use is to verify .gdb_index has been loaded by the
@@ -315,7 +375,7 @@ dw2_symtab_iter_next (struct dw2_symtab_iterator *iter,
 }
 
 void
-dwarf2_gdb_index::expand_matching_symbols
+dwarf2_gdb_index::do_expand_matching_symbols
   (struct objfile *objfile,
    const lookup_name_info &name, domain_enum domain,
    int global,
@@ -353,6 +413,29 @@ dwarf2_gdb_index::expand_matching_symbols
     }, per_objfile);
 }
 
+void
+dwarf2_gdb_index::expand_matching_symbols
+  (struct objfile *objfile,
+   const lookup_name_info &lookup_name,
+   domain_enum domain,
+   int global,
+   symbol_compare_ftype *ordered_compare)
+{
+  try
+    {
+      do_expand_matching_symbols (objfile, lookup_name, domain,
+				  global, ordered_compare);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	exception_print (gdb_stderr, e);
+      else
+	read_full_dwarf_from_debuginfod (objfile, this);
+      return;
+    }
+}
+
 /* Helper for dw2_expand_matching symtabs.  Called on each symbol
    matched, to expand corresponding CUs that were marked.  IDX is the
    index of the symbol name that matched.  */
@@ -455,7 +538,7 @@ dw2_expand_marked_cus
 }
 
 bool
-dwarf2_gdb_index::expand_symtabs_matching
+dwarf2_gdb_index::do_expand_symtabs_matching
     (struct objfile *objfile,
      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
      const lookup_name_info *lookup_name,
@@ -504,6 +587,39 @@ dwarf2_gdb_index::expand_symtabs_matching
   return result;
 }
 
+bool
+dwarf2_gdb_index::expand_symtabs_matching
+    (struct objfile *objfile,
+     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+     const lookup_name_info *lookup_name,
+     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+     block_search_flags search_flags,
+     domain_enum domain,
+     enum search_domain kind)
+{
+  if (objfile->flags & OBJF_READNEVER)
+    return false;
+
+  try
+    {
+      return do_expand_symtabs_matching (objfile, file_matcher, lookup_name,
+					 symbol_matcher, expansion_notify,
+					 search_flags, domain, kind);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	{
+	  exception_print (gdb_stderr, e);
+	  return false;
+	}
+
+      read_full_dwarf_from_debuginfod (objfile, this);
+      return true;
+    }
+}
+
 quick_symbol_functions_up
 mapped_gdb_index::make_quick_functions () const
 {
@@ -792,28 +908,32 @@ dwarf2_read_gdb_index
 
   /* If there is a .dwz file, read it so we can get its CU list as
      well.  */
-  dwz = dwarf2_get_dwz_file (per_bfd);
-  if (dwz != NULL)
+  if (get_gdb_index_contents_dwz != nullptr)
     {
       mapped_gdb_index dwz_map;
       const gdb_byte *dwz_types_ignore;
       offset_type dwz_types_elements_ignore;
+      dwz = dwarf2_get_dwz_file (per_bfd);
 
-      gdb::array_view<const gdb_byte> dwz_index_content
-	= get_gdb_index_contents_dwz (objfile, dwz);
-
-      if (dwz_index_content.empty ())
-	return 0;
-
-      if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()),
-				       1, dwz_index_content, &dwz_map,
-				       &dwz_list, &dwz_list_elements,
-				       &dwz_types_ignore,
-				       &dwz_types_elements_ignore))
+      if (dwz != nullptr)
 	{
-	  warning (_("could not read '.gdb_index' section from %s; skipping"),
-		   bfd_get_filename (dwz->dwz_bfd.get ()));
-	  return 0;
+	  gdb::array_view<const gdb_byte> dwz_index_content
+	    = get_gdb_index_contents_dwz (objfile, dwz);
+
+	  if (dwz_index_content.empty ())
+	    return 0;
+
+	  if (!read_gdb_index_from_buffer (bfd_get_filename
+					     (dwz->dwz_bfd.get ()),
+					   1, dwz_index_content, &dwz_map,
+					   &dwz_list, &dwz_list_elements,
+					   &dwz_types_ignore,
+					   &dwz_types_elements_ignore))
+	    {
+	      warning (_("could not read '.gdb_index' section from %s; skipping"),
+		       bfd_get_filename (dwz->dwz_bfd.get ()));
+		return 0;
+	    }
 	}
     }
 
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 3508f2c29ee..c7c56e3fc4a 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -34,6 +34,7 @@
 #include "dwarf2/attribute.h"
 #include "dwarf2/comp-unit-head.h"
 #include "dwarf2/cu.h"
+#include "dwarf2/frame.h"
 #include "dwarf2/index-cache.h"
 #include "dwarf2/index-common.h"
 #include "dwarf2/leb.h"
@@ -95,6 +96,8 @@
 #include "split-name.h"
 #include "gdbsupport/parallel-for.h"
 #include "gdbsupport/thread-pool.h"
+#include "inferior.h"
+#include "debuginfod-support.h"
 
 /* When == 1, print basic high level tracing messages.
    When > 1, be more verbose.
@@ -3188,7 +3191,7 @@ dwarf2_base_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd,
 }
 
 struct compunit_symtab *
-dwarf2_base_index_functions::find_pc_sect_compunit_symtab
+dwarf2_base_index_functions::do_find_pc_sect_compunit_symtab
      (struct objfile *objfile,
       struct bound_minimal_symbol msymbol,
       CORE_ADDR pc,
@@ -3219,6 +3222,32 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
   return result;
 }
 
+struct compunit_symtab *
+dwarf2_base_index_functions::find_pc_sect_compunit_symtab
+     (struct objfile *objfile,
+      struct bound_minimal_symbol msymbol,
+      CORE_ADDR pc,
+      struct obj_section *section,
+      int warn_if_readin)
+{
+  if (objfile->flags & OBJF_READNEVER)
+    return nullptr;
+
+  try
+    {
+      return do_find_pc_sect_compunit_symtab (objfile, msymbol, pc,
+					      section, warn_if_readin);
+    }
+  catch (gdb_exception e)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	exception_print (gdb_stderr, e);
+      else
+	read_full_dwarf_from_debuginfod (objfile, this);
+      return nullptr;
+    }
+}
+
 void
 dwarf2_base_index_functions::map_symbol_filenames
      (struct objfile *objfile,
@@ -3375,6 +3404,29 @@ get_gdb_index_contents_from_cache_dwz (objfile *obj, dwz_file *dwz)
   return global_index_cache.lookup_gdb_index (build_id, &dwz->index_cache_res);
 }
 
+/* Query debuginfod for the .gdb_index matching OBJFILE's build-id.  Return the
+   contents if successful.  */
+
+static gdb::array_view<const gdb_byte>
+get_gdb_index_contents_from_debuginfod (objfile *objfile, dwarf2_per_bfd *per_bfd)
+{
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  if (build_id == nullptr)
+    return {};
+
+  gdb::unique_xmalloc_ptr<char> index_path;
+  scoped_fd fd = debuginfod_section_query (build_id->data, build_id->size,
+					   bfd_get_filename
+					     (objfile->obfd.get ()),
+					   ".gdb_index",
+					   &index_path);
+  if (fd.get () < 0)
+    return {};
+
+  return global_index_cache.lookup_gdb_index_debuginfod
+    (index_path.get (), &per_bfd->index_cache_res);
+}
+
 static quick_symbol_functions_up make_cooked_index_funcs ();
 
 /* See dwarf2/public.h.  */
@@ -3440,10 +3492,102 @@ dwarf2_initialize_objfile (struct objfile *objfile)
       return;
     }
 
+  if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED)
+      && dwarf2_read_gdb_index (per_objfile,
+				get_gdb_index_contents_from_debuginfod,
+				nullptr))
+    {
+      dwarf_read_debug_printf ("found .gdb_index from debuginfod");
+      objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
+      objfile->qf.begin ()->get ()->from_separate_index = true;
+      return;
+    }
+
   global_index_cache.miss ();
   objfile->qf.push_front (make_cooked_index_funcs ());
 }
 
+/* See read.h.  */
+
+void
+read_full_dwarf_from_debuginfod (struct objfile *objfile,
+                                dwarf2_base_index_functions *fncs)
+{
+  gdb_assert (objfile->flags & OBJF_DOWNLOAD_DEFERRED);
+
+  const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  const char *filename;
+  gdb_bfd_ref_ptr debug_bfd;
+  gdb::unique_xmalloc_ptr<char> symfile_path;
+  scoped_fd fd;
+
+  if (build_id == nullptr)
+    goto unset;
+
+  filename = bfd_get_filename (objfile->obfd.get ());
+  fd = debuginfod_debuginfo_query (build_id->data, build_id->size,
+				   filename, &symfile_path);
+  if (fd.get () < 0)
+    goto unset;
+
+  /* Separate debuginfo successfully retrieved from server.  */
+  debug_bfd = symfile_bfd_open (symfile_path.get ());
+  if (debug_bfd == nullptr
+      || !build_id_verify (debug_bfd.get (), build_id->size, build_id->data))
+    {
+      warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
+	       filename);
+      goto unset;
+    }
+
+  /* Clear frame data so it can be recalculated using DWARF.  */
+  dwarf2_clear_frame_data (objfile);
+
+  /* This may also trigger a dwz download.  */
+  symbol_file_add_separate (debug_bfd, symfile_path.get (),
+			    current_inferior ()->symfile_flags, objfile);
+
+unset:
+  objfile->remove_deferred_status ();
+}
+
+/* See public.h.  */
+
+bool
+dwarf2_has_separate_index (struct objfile *objfile)
+{
+  if (objfile->flags & OBJF_DOWNLOAD_DEFERRED)
+    return true;
+  if (objfile->flags & OBJF_MAINLINE)
+    return false;
+  if (!IS_DIR_SEPARATOR (*objfile_filename (objfile)))
+    return false;
+
+  gdb::unique_xmalloc_ptr<char> index_path;
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+
+  if (build_id == nullptr)
+    return false;
+
+  scoped_fd fd = debuginfod_section_query (build_id->data,
+					   build_id->size,
+					   bfd_get_filename
+					     (objfile->obfd.get ()),
+					   ".gdb_index",
+					   &index_path);
+
+  if (fd.get () < 0)
+    return false;
+
+  /* We found a separate .gdb_index file so a separate debuginfo file
+     should exist, but we don't want to download it until necessary.
+     Attach the index to this objfile and defer the debuginfo download
+     until gdb needs to expand symtabs referenced by the index.  */
+  objfile->flags |= OBJF_DOWNLOAD_DEFERRED;
+  dwarf2_initialize_objfile (objfile);
+  return true;
+}
+
 \f
 
 /* Build a partial symbol table.  */
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index a99299cbc6d..2b844338a5b 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -875,6 +875,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
      CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
        override final;
 
+  struct compunit_symtab *do_find_pc_sect_compunit_symtab
+    (struct objfile *objfile, struct bound_minimal_symbol msymbol,
+     CORE_ADDR pc, struct obj_section *section, int warn_if_readin);
+
   struct compunit_symtab *find_compunit_symtab_by_address
     (struct objfile *objfile, CORE_ADDR address) override
   {
@@ -951,4 +955,10 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
 				       dwarf2_section_info *section,
 				       addrmap *mutable_map);
 
+/* If OBJFILE contains information from a separately downloaded .gdb_index,
+   attempt to download the full debuginfo.  */
+
+extern void read_full_dwarf_from_debuginfod (struct objfile *,
+					     dwarf2_base_index_functions *);
+
 #endif /* DWARF2READ_H */
diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c
index c9ef41893ee..8cb09e3381a 100644
--- a/gdb/dwarf2/section.c
+++ b/gdb/dwarf2/section.c
@@ -54,7 +54,8 @@ dwarf2_section_info::get_bfd_owner () const
       section = get_containing_section ();
       gdb_assert (!section->is_virtual);
     }
-  gdb_assert (section->s.section != nullptr);
+  if (section->s.section == nullptr)
+    error (_("Can't find owner of DWARF section."));
   return section->s.section->owner;
 }
 
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 799e3b914f8..133341ea615 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -1242,7 +1242,7 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
 	    symbol_file_add_separate (debug_bfd, debugfile.c_str (),
 				      symfile_flags, objfile);
 	}
-      else
+      else if (!dwarf2_has_separate_index (objfile))
 	{
 	  has_dwarf2 = false;
 	  const struct bfd_build_id *build_id
diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h
index 9dee2ee51a0..fb3f741c899 100644
--- a/gdb/objfile-flags.h
+++ b/gdb/objfile-flags.h
@@ -60,6 +60,10 @@ enum objfile_flag : unsigned
     /* User requested that we do not read this objfile's symbolic
        information.  */
     OBJF_READNEVER = 1 << 6,
+
+    /* A separate .gdb_index has been downloaded for this objfile.
+       Debuginfo for this objfile can be downloaded when required.  */
+    OBJF_DOWNLOAD_DEFERRED = 1 << 7,
   };
 
 DEF_ENUM_FLAGS_TYPE (enum objfile_flag, objfile_flags);
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index bb7b0a4579d..57bc1d45965 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -613,11 +613,22 @@ struct objfile
   /* See quick_symbol_functions.  */
   void require_partial_symbols (bool verbose);
 
-  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
-  void remove_partial_symbol (quick_symbol_functions *target)
+  /*  Indicate that the aquisition of this objfile's separate debug objfile
+      is no longer deferred.  Used when the debug objfile has been aquired
+      or could not be found.  */
+  void remove_deferred_status ()
   {
+    flags &= ~OBJF_DOWNLOAD_DEFERRED;
+
+   /* Remove quick_symbol_functions derived from a separately downloaded
+      index.  If available the separate debug objfile's index will be used
+      instead, since that objfile actually contains the symbols and CUs
+      referenced in the index.
+
+      No more than one element of qf should have from_separate_index set
+      to true.  */
     for (quick_symbol_functions_up &qf_up : qf)
-      if (qf_up.get () == target)
+      if (qf_up->from_separate_index)
 	{
 	  qf.remove (qf_up);
 	  return;
diff --git a/gdb/quick-symbol.h b/gdb/quick-symbol.h
index a7fea2ccb49..e7163503e39 100644
--- a/gdb/quick-symbol.h
+++ b/gdb/quick-symbol.h
@@ -225,6 +225,10 @@ struct quick_symbol_functions
   virtual void read_partial_symbols (struct objfile *objfile)
   {
   }
+
+  /* True if this quick_symbol_functions is derived from a separately
+     downloaded index.  */
+  bool from_separate_index = false;
 };
 
 typedef std::unique_ptr<quick_symbol_functions> quick_symbol_functions_up;
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 3a0aee163ff..5fcfe14cefa 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -991,6 +991,10 @@ syms_from_objfile (struct objfile *objfile,
 static void
 finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
 {
+  struct objfile *parent = objfile->separate_debug_objfile_backlink;
+  bool was_deferred
+    = (parent != nullptr) && (parent->flags & OBJF_DOWNLOAD_DEFERRED);
+
   /* If this is the main symbol file we have to clean up all users of the
      old main symbol file.  Otherwise it is sufficient to fixup all the
      breakpoints that may have been redefined by this symbol file.  */
@@ -1001,7 +1005,8 @@ finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
 
       clear_symtab_users (add_flags);
     }
-  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0)
+  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0
+	   && !was_deferred)
     {
       breakpoint_re_set ();
     }
@@ -1127,6 +1132,12 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name,
 
   finish_new_objfile (objfile, add_flags);
 
+  /* Remove deferred status now in case any observers trigger symtab
+     expansion.  Otherwise gdb might try to read parent for psymbols
+     when it should read the separate debug objfile instead.  */
+  if (parent != nullptr && (parent->flags & OBJF_DOWNLOAD_DEFERRED))
+    parent->remove_deferred_status ();
+
   gdb::observers::new_objfile.notify (objfile);
 
   bfd_cache_close_all ();
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 0117a2a59d7..54d234c063b 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -2898,14 +2898,30 @@ find_pc_sect_compunit_symtab (CORE_ADDR pc, struct obj_section *section)
   if (best_cust != NULL)
     return best_cust;
 
+  int warn_if_readin = 1;
+
   /* Not found in symtabs, search the "quick" symtabs (e.g. psymtabs).  */
 
   for (objfile *objf : current_program_space->objfiles ())
     {
+      bool was_deferred = objf->flags & OBJF_DOWNLOAD_DEFERRED;
+
       struct compunit_symtab *result
-	= objf->find_pc_sect_compunit_symtab (msymbol, pc, section, 1);
+	= objf->find_pc_sect_compunit_symtab (msymbol, pc, section,
+					      warn_if_readin);
+
       if (result != NULL)
 	return result;
+
+      /* If objf's separate debug info was just acquired, disable
+	 warn_if_readin for the next iteration of this loop.  This prevents
+	 a spurious warning in case an observer already triggered expansion
+	 of the separate debug objfile's symtabs.  */
+      if (was_deferred && objf->separate_debug_objfile != nullptr
+	  && (objf->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	warn_if_readin = 0;
+      else if (warn_if_readin == 0)
+	warn_if_readin = 1;
     }
 
   return NULL;
diff --git a/gdb/testsuite/gdb.debuginfod/libsection1.c b/gdb/testsuite/gdb.debuginfod/libsection1.c
new file mode 100644
index 00000000000..60824b415c6
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/libsection1.c
@@ -0,0 +1,40 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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 <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+
+extern void libsection2_test ();
+extern void *libsection2_thread_test (void *);
+
+void
+libsection1_test ()
+{
+  pthread_t thr;
+
+  printf ("In libsection1\n");
+  libsection2_test ();
+
+  pthread_create (&thr, NULL, libsection2_thread_test, NULL);
+
+  /* Give the new thread a chance to actually enter libsection2_thread_test.  */
+  sleep (3);
+  printf ("Cancelling thread\n");
+
+  pthread_cancel (thr);
+}
diff --git a/gdb/testsuite/gdb.debuginfod/libsection2.c b/gdb/testsuite/gdb.debuginfod/libsection2.c
new file mode 100644
index 00000000000..629a67f94a5
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/libsection2.c
@@ -0,0 +1,37 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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 <stdio.h>
+
+void
+libsection2_test ()
+{
+  printf ("In libsection2\n");
+}
+
+void *
+libsection2_thread_test (void *arg)
+{
+  (void) arg;
+
+  printf ("In thread test\n");
+
+  while (1)
+    ;
+
+  return NULL;
+}
diff --git a/gdb/testsuite/gdb.debuginfod/section.c b/gdb/testsuite/gdb.debuginfod/section.c
new file mode 100644
index 00000000000..d391a8f898e
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/section.c
@@ -0,0 +1,29 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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 <stdio.h>
+
+extern void libsection1_test ();
+
+int
+main()
+{
+  libsection1_test ();
+  printf ("in section exec\n");
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
new file mode 100644
index 00000000000..96e9750cd38
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/section.exp
@@ -0,0 +1,181 @@
+# Copyright 2023 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 debuginfod functionality
+
+standard_testfile
+
+load_lib debuginfod-support.exp
+
+require allow_debuginfod_tests
+
+set sourcetmp [standard_output_file tmp-${srcfile}]
+set outputdir [standard_output_file {}]
+
+# SECTEXEC is an executable which calls a function from LIB_SL1.
+set sectfile "section"
+set sectsrc $srcdir/$subdir/section.c
+set sectexec [standard_output_file $sectfile]
+
+# Solib LIB_SL1 calls functions from LIB_SL2.
+set libfile1 "libsection1"
+set libsrc1 $srcdir/$subdir/$libfile1.c
+set lib_sl1 [standard_output_file $libfile1.sl]
+
+set libfile2 "libsection2"
+set libsrc2 $srcdir/$subdir/$libfile2.c
+set lib_sl2 [standard_output_file $libfile2.sl]
+
+set lib_opts1 [list debug build-id shlib=$lib_sl2]
+set lib_opts2 [list debug build-id]
+set exec_opts [list debug build-id shlib=$lib_sl1 shlib=$lib_sl2]
+
+clean_restart
+
+if {[enable_section_downloads] == 0} {
+    untested "GDB does not support debuginfod section downloads"
+    return -1
+}
+
+# Compile SECTEXEC, LIB_SL1 and LIB_SL2.
+if { [gdb_compile_shlib $libsrc2 $lib_sl2 $lib_opts2] != "" } {
+    untested "failed to compile $libfile2"
+    return -1
+}
+
+if { [gdb_compile_shlib_pthreads $libsrc1 $lib_sl1 $lib_opts1] != "" } {
+    untested "failed to compile $libfile1"
+    return -1
+}
+
+if { [gdb_compile $sectsrc $sectexec executable $exec_opts] != "" } {
+    untested "failed to compile $sectfile"
+    return -1
+}
+
+# Add .gdb_index to solibs.
+if { [have_index $lib_sl1] != "gdb_index"
+     && [add_gdb_index $lib_sl1] == 0 } {
+    untested "failed to add .gdb_index to $libfile1"
+    return -1
+}
+
+if { [have_index $lib_sl2] != "gdb_index"
+     && [add_gdb_index $lib_sl2] == 0 } {
+    untested "failed to add .gdb_index to $libfile2"
+    return -1
+}
+
+# Strip solib debuginfo into separate files.
+if { [gdb_gnu_strip_debug $lib_sl1 ""] != 0} {
+   fail "strip $lib_sl1 debuginfo"
+   return -1
+}
+
+if { [gdb_gnu_strip_debug $lib_sl2 ""] != 0} {
+   fail "strip $lib_sl2 debuginfo"
+   return -1
+}
+
+# Move debuginfo files into directory that debuginfod will serve from.
+set debugdir [standard_output_file "debug"]
+set debuginfo_sl1 [standard_output_file $libfile1.sl.debug]
+set debuginfo_sl2 [standard_output_file $libfile2.sl.debug]
+
+file mkdir $debugdir
+file rename -force $debuginfo_sl1 $debugdir
+file rename -force $debuginfo_sl2 $debugdir
+
+# Restart GDB and clear the debuginfod client cache. Then load BINFILE into
+# GDB and start running it.  Match output with pattern RES and use TESTNAME
+# as the test name.
+proc_with_prefix clean_restart_with_prompt { binfile res testname } {
+    global cache
+
+    clean_restart
+
+    # Delete client cache so debuginfo downloads again.
+    file delete -force $cache
+
+    gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
+    gdb_test "start" $res "file [file tail $binfile] start $testname" \
+	     ".*Enable debuginfod.*" "y"
+}
+
+# Tests with no debuginfod server running.
+proc_with_prefix no_url { } {
+    global sectexec libfile1 libfile2
+
+    # Check that no section is downloaded and no debuginfo is found.
+    gdb_test "file $sectexec" "" "file [file tail $sectexec] file no url"
+    gdb_test "start" "" "file [file tail $sectexec] start no url"
+    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile1.*" \
+	     "file [file tail $libfile1] found no url"
+    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile2.*" \
+	     "file [file tail $libfile2] found no url"
+}
+
+# Tests with a debuginfod server running.
+proc_with_prefix local_url { } {
+    global sectexec
+    global libsrc1 lib_sl1 libfile1
+    global libsrc2 lib_sl2 libfile2
+    global debugdir db
+
+    set url [start_debuginfod $db $debugdir]
+    if { $url == "" } {
+	unresolved "failed to start debuginfod server"
+	return
+    }
+
+    # Point GDB to the server.
+    setenv DEBUGINFOD_URLS $url
+
+    # Download .gdb_index for solibs.
+    set res ".*section \.gdb_index for $lib_sl1.*\
+	section \.gdb_index for $lib_sl2.*"
+    clean_restart_with_prompt $sectexec $res "index"
+
+    # Download debuginfo when stepping into a function.
+    set res ".*separate debug info for $lib_sl1.*\"In ${libfile1}\\\\n\".*"
+    gdb_test "step" $res "file [file tail $lib_sl1] step"
+
+    clean_restart_with_prompt $sectexec "" "break"
+
+    # Download debuginfo when setting a breakpoint.
+    set res "Download.*separate debug info for $lib_sl2.*"
+    gdb_test "br libsection2_test" $res "file [file tail $sectexec] break set"
+
+    # Hit the breakpoint.
+    set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
+    gdb_test "c" $res "file [file tail $sectexec] break continue"
+
+    # Check that download progress message is correctly formatted
+    # during backtrace.
+    set res ".* separate debug info for $lib_sl1.*#0  libsection2_test\
+	\\(\\) at.*"
+    set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
+    gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
+}
+
+# Create CACHE and DB directories ready for debuginfod to use.
+prepare_for_debuginfod cache db
+
+with_debuginfod_env $cache {
+    no_url
+    local_url
+}
+
+stop_debuginfod
diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
index 50a8b512a4a..cd6b9bfee38 100644
--- a/gdb/testsuite/lib/debuginfod-support.exp
+++ b/gdb/testsuite/lib/debuginfod-support.exp
@@ -194,3 +194,25 @@ proc stop_debuginfod { } {
 	unset debuginfod_spawn_id
     }
 }
+
+# Return 1 if gdb is configured to download ELF/DWARF sections from
+# debuginfod servers.  Otherwise return 0.
+proc enable_section_downloads { } {
+    global gdb_prompt
+
+    set cmd "maint set debuginfod download-sections on"
+    set msg "enable section downloads"
+
+    gdb_test_multiple $cmd $msg {
+	-re -wrap ".*not compiled into GDB.*" {
+	    return 0
+	}
+	-re -wrap "^" {
+	    return 1
+	}
+	-re -wrap "" {
+	    fail "$gdb_test_name (unexpected output)"
+	    return 0
+	}
+    }
+}
-- 
2.41.0


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading
  2023-07-10 21:01     ` Aaron Merey
@ 2023-07-11 12:01       ` Pedro Alves
  2023-07-11 15:00         ` Aaron Merey
  0 siblings, 1 reply; 33+ messages in thread
From: Pedro Alves @ 2023-07-11 12:01 UTC (permalink / raw)
  To: Aaron Merey, aburgess; +Cc: gdb-patches

On 2023-07-10 22:01, Aaron Merey via Gdb-patches wrote:
> +void
> +dwarf2_gdb_index::expand_all_symtabs (struct objfile *objfile)
> +{
> +  try
> +    {
> +      dwarf2_base_index_functions::expand_all_symtabs (objfile);
> +    }
> +  catch (gdb_exception e)

Write:

  catch (const gdb_exception &e)

... to avoid slicing.  

This catches QUIT exceptions as well.  Did you mean gdb_exception_error instead?

> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	exception_print (gdb_stderr, e);
> +      else
> +	read_full_dwarf_from_debuginfod (objfile, this);
> +    }
> +}


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading
  2023-07-11 12:01       ` Pedro Alves
@ 2023-07-11 15:00         ` Aaron Merey
  2023-07-19 14:33           ` [PING][PATCH " Aaron Merey
  0 siblings, 1 reply; 33+ messages in thread
From: Aaron Merey @ 2023-07-11 15:00 UTC (permalink / raw)
  To: Pedro Alves; +Cc: aburgess, gdb-patches

Hi Pedro,

On Tue, Jul 11, 2023 at 8:01 AM Pedro Alves <pedro@palves.net> wrote:
>
> On 2023-07-10 22:01, Aaron Merey via Gdb-patches wrote:
> > +void
> > +dwarf2_gdb_index::expand_all_symtabs (struct objfile *objfile)
> > +{
> > +  try
> > +    {
> > +      dwarf2_base_index_functions::expand_all_symtabs (objfile);
> > +    }
> > +  catch (gdb_exception e)
>
> Write:
>
>   catch (const gdb_exception &e)
>
> ... to avoid slicing.
>
> This catches QUIT exceptions as well.  Did you mean gdb_exception_error instead?

Yes it should be gdb_exception_error. I've updated the patch with these changes.

Thanks,
Aaron


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PING*3][PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator
  2023-07-03 17:39     ` [PING*2][PATCH " Aaron Merey
@ 2023-07-19 14:32       ` Aaron Merey
  0 siblings, 0 replies; 33+ messages in thread
From: Aaron Merey @ 2023-07-19 14:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Ping

Thanks,
Aaron

On Mon, Jul 3, 2023 at 1:39 PM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Thu, Jun 15, 2023 at 9:44 AM Aaron Merey <amerey@redhat.com> wrote:
> >
> > Ping
> >
> > Thanks,
> > Aaron
> >
> > On Wed, May 31, 2023 at 9:44 PM Aaron Merey <amerey@redhat.com> wrote:
> > >
> > > To facilitate the deletion of objfiles, progspace objects use a safe
> > > iterator that holds a reference to the next objfile in the progspace's
> > > objfile_list.  This allows objfiles to be deleted in a loop without
> > > invalidating the loop's iterator.  progspace also uses an unwrapping
> > > iterator over std::unique_ptr<objfile> that automatically deferences
> > > the unique_ptr.
> > >
> > > This patch changes the objfile safe iterator to be a reverse safe
> > > iterator.  It changes the unwrapping iterator into a template.  It
> > > also modifies objfile_list insertion so that separate debug objfiles
> > > are placed into the list after the parent objfile, instead of before.
> > >
> > > These changes are intended to prepare gdb for on-demand debuginfo
> > > downloading and the downloading of .gdb_index sections.
> > >
> > > With on-demand downloading enabled, gdb might download a debuginfo
> > > file during symtab expansion.  In this case an objfile could be added
> > > to an objfiles_list during iteration over the list (for example, in
> > > iterate_over_symtabs).  We want these loops to also iterate over newly
> > > downloaded objfiles. So objfiles need to be inserted into objfiles_list
> > > after their parent since it is during the search of the parent
> > > objfile for some symbol or filename that the separate debug objfile
> > > might be downloaded.
> > >
> > > The unwrapping iterator is changed into a template in order to
> > > use it with objfile qf_require_partial_symbols, which is now also
> > > uses with a safe iterator.  This is because after a separate debug
> > > objfile is downloaded on-demand, we want to remove any .gdb_index
> > > quick_symbol_functions from the parent objfile during iteration over
> > > the parent's quick_symbol_functions.  The newly downloaded separate
> > > debug objfile contains the index and all of the related symbols
> > > so the .gdb_index should not be associated with the parent objfile
> > > any longer.
> > >
> > > Finally a safe reverse iterator is now used during progspace objfile
> > > deletion in order to prevent iterator invalidation during the loop
> > > in which objfiles are deleted.  This could happen during forward
> > > iteration over objfiles_list when a separate debug objfile immediately
> > > follows it's parent objfile in the list (which is now possible since
> > > objfiles are inserted into the list after their parent).  Deletion
> > > of the parent would cause deletion of the separate debug objfile,
> > > which would invalidate the safe forward iterator's reference to the
> > > next objfile in the list.  A safe reverse iterator deletes separate
> > > debug objfiles before their parent, so the iterator's reference to
> > > the next objfile always stays valid.
> > >
> > > A small change was also made to a testcase in py-objfile.exp to
> > > account for the new placement of separate debug objfiles in
> > > objfiles_list.
> > > ---
> > >  gdb/objfiles.h                          |  22 +++-
> > >  gdb/progspace.c                         |   8 +-
> > >  gdb/progspace.h                         | 159 +++++++++++++++++++-----
> > >  gdb/symfile-debug.c                     | 136 ++++++++++----------
> > >  gdb/testsuite/gdb.python/py-objfile.exp |   2 +-
> > >  5 files changed, 218 insertions(+), 109 deletions(-)
> > >
> > > diff --git a/gdb/objfiles.h b/gdb/objfiles.h
> > > index 189856f0a51..bb7b0a4579d 100644
> > > --- a/gdb/objfiles.h
> > > +++ b/gdb/objfiles.h
> > > @@ -613,6 +613,17 @@ struct objfile
> > >    /* See quick_symbol_functions.  */
> > >    void require_partial_symbols (bool verbose);
> > >
> > > +  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
> > > +  void remove_partial_symbol (quick_symbol_functions *target)
> > > +  {
> > > +    for (quick_symbol_functions_up &qf_up : qf)
> > > +      if (qf_up.get () == target)
> > > +       {
> > > +         qf.remove (qf_up);
> > > +         return;
> > > +       }
> > > +  }
> > > +
> > >    /* Return the relocation offset applied to SECTION.  */
> > >    CORE_ADDR section_offset (bfd_section *section) const
> > >    {
> > > @@ -699,13 +710,20 @@ struct objfile
> > >
> > >  private:
> > >
> > > +  using qf_list = std::forward_list<quick_symbol_functions_up>;
> > > +  using unwrapping_qf_range = iterator_range<unwrapping_iterator<qf_list::iterator>>;
> > > +  using qf_safe_range = basic_safe_range<unwrapping_qf_range>;
> > > +
> > >    /* Ensure that partial symbols have been read and return the "quick" (aka
> > >       partial) symbol functions for this symbol reader.  */
> > > -  const std::forward_list<quick_symbol_functions_up> &
> > > +  qf_safe_range
> > >    qf_require_partial_symbols ()
> > >    {
> > >      this->require_partial_symbols (true);
> > > -    return qf;
> > > +    return qf_safe_range
> > > +      (unwrapping_qf_range
> > > +       (unwrapping_iterator<qf_list::iterator> (qf.begin ()),
> > > +        unwrapping_iterator<qf_list::iterator> (qf.end ())));
> > >    }
> > >
> > >  public:
> > > diff --git a/gdb/progspace.c b/gdb/progspace.c
> > > index 32bdfebcf7c..1ed75eef2f9 100644
> > > --- a/gdb/progspace.c
> > > +++ b/gdb/progspace.c
> > > @@ -139,19 +139,19 @@ program_space::free_all_objfiles ()
> > >
> > >  void
> > >  program_space::add_objfile (std::unique_ptr<objfile> &&objfile,
> > > -                           struct objfile *before)
> > > +                           struct objfile *after)
> > >  {
> > > -  if (before == nullptr)
> > > +  if (after == nullptr)
> > >      objfiles_list.push_back (std::move (objfile));
> > >    else
> > >      {
> > >        auto iter = std::find_if (objfiles_list.begin (), objfiles_list.end (),
> > >                                 [=] (const std::unique_ptr<::objfile> &objf)
> > >                                 {
> > > -                                 return objf.get () == before;
> > > +                                 return objf.get () == after;
> > >                                 });
> > >        gdb_assert (iter != objfiles_list.end ());
> > > -      objfiles_list.insert (iter, std::move (objfile));
> > > +      objfiles_list.insert (++iter, std::move (objfile));
> > >      }
> > >  }
> > >
> > > diff --git a/gdb/progspace.h b/gdb/progspace.h
> > > index 85215f0e2f1..6e33e48c88e 100644
> > > --- a/gdb/progspace.h
> > > +++ b/gdb/progspace.h
> > > @@ -40,56 +40,141 @@ struct address_space;
> > >  struct program_space;
> > >  struct so_list;
> > >
> > > +/* An iterator that wraps an iterator over std::unique_ptr, and dereferences
> > > +   the returned object.  This is useful for iterating over a list of shared
> > > +   pointers and returning raw pointers -- which helped avoid touching a lot
> > > +   of code when changing how objfiles are managed.  */
> > > +
> > > +template<typename UniquePtrIter>
> > > +class unwrapping_iterator
> > > +{
> > > +public:
> > > +  typedef unwrapping_iterator self_type;
> > > +  typedef typename UniquePtrIter::value_type::pointer value_type;
> > > +  typedef typename UniquePtrIter::reference  reference;
> > > +  typedef typename UniquePtrIter::pointer pointer;
> > > +  typedef typename UniquePtrIter::iterator_category iterator_category;
> > > +  typedef typename UniquePtrIter::difference_type difference_type;
> > > +
> > > +  unwrapping_iterator (UniquePtrIter iter)
> > > +    : m_iter (std::move (iter))
> > > +  {
> > > +  }
> > > +
> > > +  value_type operator* () const
> > > +  {
> > > +    return m_iter->get ();
> > > +  }
> > > +
> > > +  unwrapping_iterator operator++ ()
> > > +  {
> > > +    ++m_iter;
> > > +    return *this;
> > > +  }
> > > +
> > > +  bool operator!= (const unwrapping_iterator &other) const
> > > +  {
> > > +    return m_iter != other.m_iter;
> > > +  }
> > > +
> > > +private:
> > > +  /* The underlying iterator.  */
> > > +  UniquePtrIter m_iter;
> > > +};
> > > +
> > >  typedef std::list<std::unique_ptr<objfile>> objfile_list;
> > >
> > > -/* An iterator that wraps an iterator over std::unique_ptr<objfile>,
> > > -   and dereferences the returned object.  This is useful for iterating
> > > -   over a list of shared pointers and returning raw pointers -- which
> > > -   helped avoid touching a lot of code when changing how objfiles are
> > > -   managed.  */
> > > +/* An reverse iterator that wraps an iterator over objfile_list, and
> > > +   dereferences the returned object.  This is useful for reverse iterating
> > > +   over a list of shared pointers and returning raw pointers -- which helped
> > > +   avoid touching a lot of code when changing how objfiles are managed.  */
> > >
> > > -class unwrapping_objfile_iterator
> > > +class unwrapping_reverse_objfile_iterator
> > >  {
> > >  public:
> > > -
> > > -  typedef unwrapping_objfile_iterator self_type;
> > > +  typedef unwrapping_reverse_objfile_iterator self_type;
> > >    typedef typename ::objfile *value_type;
> > >    typedef typename ::objfile &reference;
> > >    typedef typename ::objfile **pointer;
> > >    typedef typename objfile_list::iterator::iterator_category iterator_category;
> > >    typedef typename objfile_list::iterator::difference_type difference_type;
> > >
> > > -  unwrapping_objfile_iterator (objfile_list::iterator iter)
> > > -    : m_iter (std::move (iter))
> > > -  {
> > > -  }
> > > -
> > > -  objfile *operator* () const
> > > +  value_type operator* () const
> > >    {
> > >      return m_iter->get ();
> > >    }
> > >
> > > -  unwrapping_objfile_iterator operator++ ()
> > > +  unwrapping_reverse_objfile_iterator operator++ ()
> > >    {
> > > -    ++m_iter;
> > > +    if (m_iter != m_begin)
> > > +      --m_iter;
> > > +    else
> > > +      {
> > > +       /* We can't decrement M_ITER since it is the begin iterator of the
> > > +          objfile list.  Set M_ITER to the list's end iterator to indicate
> > > +          this is now one-past-the-end.  */
> > > +       m_iter = m_end;
> > > +
> > > +       /* Overwrite M_BEGIN to avoid possibly copying an invalid iterator.  */
> > > +       m_begin = m_end;
> > > +      }
> > > +
> > >      return *this;
> > >    }
> > >
> > > -  bool operator!= (const unwrapping_objfile_iterator &other) const
> > > +  bool operator!= (const unwrapping_reverse_objfile_iterator &other) const
> > >    {
> > >      return m_iter != other.m_iter;
> > >    }
> > >
> > > +  /* Return an unwrapping reverse iterator starting at the last element of
> > > +     OBJF_LIST.  */
> > > +  static unwrapping_reverse_objfile_iterator begin (objfile_list &objf_list)
> > > +  {
> > > +    auto begin = objf_list.begin ();
> > > +    auto end = objf_list.end ();
> > > +    auto rev_begin = objf_list.end ();
> > > +
> > > +    /* Start REV_BEGIN on the last objfile in OBJF_LIST.  */
> > > +    if (begin != end)
> > > +      --rev_begin;
> > > +
> > > +    return unwrapping_reverse_objfile_iterator (rev_begin, begin, end);
> > > +  }
> > > +
> > > +  /* Return a one-past-the-end unwrapping reverse iterator.  */
> > > +  static unwrapping_reverse_objfile_iterator end (objfile_list &objf_list)
> > > +  {
> > > +    return unwrapping_reverse_objfile_iterator (objf_list.end (),
> > > +                                               objf_list.end (),
> > > +                                               objf_list.end ());
> > > +  }
> > > +
> > >  private:
> > > +  /* begin and end methods should be used to create these objects.  */
> > > +  unwrapping_reverse_objfile_iterator (objfile_list::iterator iter,
> > > +                                      objfile_list::iterator begin,
> > > +                                      objfile_list::iterator end)
> > > +    : m_iter (std::move (iter)), m_begin (std::move (begin)),
> > > +      m_end (std::move (end))
> > > +  {
> > > +  }
> > >
> > > -  /* The underlying iterator.  */
> > > -  objfile_list::iterator m_iter;
> > > -};
> > > + /* The underlying iterator.  */
> > > + objfile_list::iterator m_iter;
> > >
> > > + /* The underlying iterator pointing to the first objfile in the sequence.  Used
> > > +    to track when to stop decrementing M_ITER.  */
> > > + objfile_list::iterator m_begin;
> > >
> > > -/* A range that returns unwrapping_objfile_iterators.  */
> > > +  /* The underlying iterator's one-past-the-end.  */
> > > + objfile_list::iterator m_end;
> > > +};
> > >
> > > -using unwrapping_objfile_range = iterator_range<unwrapping_objfile_iterator>;
> > > +/* A range that returns unwrapping_iterators.  */
> > > +
> > > +using unwrapping_objfile_range
> > > +  = iterator_range<unwrapping_iterator<objfile_list::iterator>>;
> > >
> > >  /* A program space represents a symbolic view of an address space.
> > >     Roughly speaking, it holds all the data associated with a
> > > @@ -209,11 +294,12 @@ struct program_space
> > >    objfiles_range objfiles ()
> > >    {
> > >      return objfiles_range
> > > -      (unwrapping_objfile_iterator (objfiles_list.begin ()),
> > > -       unwrapping_objfile_iterator (objfiles_list.end ()));
> > > +      (unwrapping_iterator<objfile_list::iterator> (objfiles_list.begin ()),
> > > +       unwrapping_iterator<objfile_list::iterator> (objfiles_list.end ()));
> > >    }
> > >
> > > -  using objfiles_safe_range = basic_safe_range<objfiles_range>;
> > > +  using objfiles_reverse_range = iterator_range<unwrapping_reverse_objfile_iterator>;
> > > +  using objfiles_safe_reverse_range = basic_safe_range<objfiles_reverse_range>;
> > >
> > >    /* An iterable object that can be used to iterate over all objfiles.
> > >       The basic use is in a foreach, like:
> > > @@ -221,20 +307,25 @@ struct program_space
> > >       for (objfile *objf : pspace->objfiles_safe ()) { ... }
> > >
> > >       This variant uses a basic_safe_iterator so that objfiles can be
> > > -     deleted during iteration.  */
> > > -  objfiles_safe_range objfiles_safe ()
> > > +     deleted during iteration.
> > > +
> > > +     The use of a reverse iterator helps ensure that separate debug
> > > +     objfiles are deleted before their parent objfile.  This prevents
> > > +     the invalidation of an iterator due to the deletion of a parent
> > > +     objfile.  */
> > > +  objfiles_safe_reverse_range objfiles_safe ()
> > >    {
> > > -    return objfiles_safe_range
> > > -      (objfiles_range
> > > -        (unwrapping_objfile_iterator (objfiles_list.begin ()),
> > > -         unwrapping_objfile_iterator (objfiles_list.end ())));
> > > +    return objfiles_safe_reverse_range
> > > +      (objfiles_reverse_range
> > > +       (unwrapping_reverse_objfile_iterator::begin (objfiles_list),
> > > +        unwrapping_reverse_objfile_iterator::end (objfiles_list)));
> > >    }
> > >
> > > -  /* Add OBJFILE to the list of objfiles, putting it just before
> > > -     BEFORE.  If BEFORE is nullptr, it will go at the end of the
> > > +  /* Add OBJFILE to the list of objfiles, putting it just after
> > > +     AFTER.  If AFTER is nullptr, it will go at the end of the
> > >       list.  */
> > >    void add_objfile (std::unique_ptr<objfile> &&objfile,
> > > -                   struct objfile *before);
> > > +                   struct objfile *after);
> > >
> > >    /* Remove OBJFILE from the list of objfiles.  */
> > >    void remove_objfile (struct objfile *objfile);
> > > diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
> > > index 9db5c47a8ce..784b81b5ca6 100644
> > > --- a/gdb/symfile-debug.c
> > > +++ b/gdb/symfile-debug.c
> > > @@ -109,9 +109,9 @@ objfile::has_unexpanded_symtabs ()
> > >                 objfile_debug_name (this));
> > >
> > >    bool result = false;
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > >      {
> > > -      if (iter->has_unexpanded_symtabs (this))
> > > +      if (qf->has_unexpanded_symtabs (this))
> > >         {
> > >           result = true;
> > >           break;
> > > @@ -134,9 +134,9 @@ objfile::find_last_source_symtab ()
> > >      gdb_printf (gdb_stdlog, "qf->find_last_source_symtab (%s)\n",
> > >                 objfile_debug_name (this));
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > >      {
> > > -      retval = iter->find_last_source_symtab (this);
> > > +      retval = qf->find_last_source_symtab (this);
> > >        if (retval != nullptr)
> > >         break;
> > >      }
> > > @@ -167,8 +167,8 @@ objfile::forget_cached_source_info ()
> > >         }
> > >      }
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > -    iter->forget_cached_source_info (this);
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > > +    qf->forget_cached_source_info (this);
> > >  }
> > >
> > >  bool
> > > @@ -214,17 +214,17 @@ objfile::map_symtabs_matching_filename
> > >      return result;
> > >    };
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > >      {
> > > -      if (!iter->expand_symtabs_matching (this,
> > > -                                         match_one_filename,
> > > -                                         nullptr,
> > > -                                         nullptr,
> > > -                                         on_expansion,
> > > -                                         (SEARCH_GLOBAL_BLOCK
> > > -                                          | SEARCH_STATIC_BLOCK),
> > > -                                         UNDEF_DOMAIN,
> > > -                                         ALL_DOMAIN))
> > > +      if (!qf->expand_symtabs_matching (this,
> > > +                                       match_one_filename,
> > > +                                       nullptr,
> > > +                                       nullptr,
> > > +                                       on_expansion,
> > > +                                       (SEARCH_GLOBAL_BLOCK
> > > +                                        | SEARCH_STATIC_BLOCK),
> > > +                                       UNDEF_DOMAIN,
> > > +                                       ALL_DOMAIN))
> > >         {
> > >           retval = false;
> > >           break;
> > > @@ -283,18 +283,18 @@ objfile::lookup_symbol (block_enum kind, const char *name, domain_enum domain)
> > >      return true;
> > >    };
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > >      {
> > > -      if (!iter->expand_symtabs_matching (this,
> > > -                                         nullptr,
> > > -                                         &lookup_name,
> > > -                                         nullptr,
> > > -                                         search_one_symtab,
> > > -                                         kind == GLOBAL_BLOCK
> > > -                                         ? SEARCH_GLOBAL_BLOCK
> > > -                                         : SEARCH_STATIC_BLOCK,
> > > -                                         domain,
> > > -                                         ALL_DOMAIN))
> > > +      if (!qf->expand_symtabs_matching (this,
> > > +                                       nullptr,
> > > +                                       &lookup_name,
> > > +                                       nullptr,
> > > +                                       search_one_symtab,
> > > +                                       kind == GLOBAL_BLOCK
> > > +                                       ? SEARCH_GLOBAL_BLOCK
> > > +                                       : SEARCH_STATIC_BLOCK,
> > > +                                       domain,
> > > +                                       ALL_DOMAIN))
> > >         break;
> > >      }
> > >
> > > @@ -314,8 +314,8 @@ objfile::print_stats (bool print_bcache)
> > >      gdb_printf (gdb_stdlog, "qf->print_stats (%s, %d)\n",
> > >                 objfile_debug_name (this), print_bcache);
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > -    iter->print_stats (this, print_bcache);
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > > +    qf->print_stats (this, print_bcache);
> > >  }
> > >
> > >  void
> > > @@ -340,16 +340,16 @@ objfile::expand_symtabs_for_function (const char *func_name)
> > >    lookup_name_info base_lookup (func_name, symbol_name_match_type::FULL);
> > >    lookup_name_info lookup_name = base_lookup.make_ignore_params ();
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > -    iter->expand_symtabs_matching (this,
> > > -                                  nullptr,
> > > -                                  &lookup_name,
> > > -                                  nullptr,
> > > -                                  nullptr,
> > > -                                  (SEARCH_GLOBAL_BLOCK
> > > -                                   | SEARCH_STATIC_BLOCK),
> > > -                                  VAR_DOMAIN,
> > > -                                  ALL_DOMAIN);
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > > +    qf->expand_symtabs_matching (this,
> > > +                                nullptr,
> > > +                                &lookup_name,
> > > +                                nullptr,
> > > +                                nullptr,
> > > +                                (SEARCH_GLOBAL_BLOCK
> > > +                                 | SEARCH_STATIC_BLOCK),
> > > +                                VAR_DOMAIN,
> > > +                                ALL_DOMAIN);
> > >  }
> > >
> > >  void
> > > @@ -359,8 +359,8 @@ objfile::expand_all_symtabs ()
> > >      gdb_printf (gdb_stdlog, "qf->expand_all_symtabs (%s)\n",
> > >                 objfile_debug_name (this));
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > -    iter->expand_all_symtabs (this);
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > > +    qf->expand_all_symtabs (this);
> > >  }
> > >
> > >  void
> > > @@ -377,16 +377,16 @@ objfile::expand_symtabs_with_fullname (const char *fullname)
> > >      return filename_cmp (basenames ? basename : fullname, filename) == 0;
> > >    };
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > -    iter->expand_symtabs_matching (this,
> > > -                                  file_matcher,
> > > -                                  nullptr,
> > > -                                  nullptr,
> > > -                                  nullptr,
> > > -                                  (SEARCH_GLOBAL_BLOCK
> > > -                                   | SEARCH_STATIC_BLOCK),
> > > -                                  UNDEF_DOMAIN,
> > > -                                  ALL_DOMAIN);
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > > +    qf->expand_symtabs_matching (this,
> > > +                                file_matcher,
> > > +                                nullptr,
> > > +                                nullptr,
> > > +                                nullptr,
> > > +                                (SEARCH_GLOBAL_BLOCK
> > > +                                 | SEARCH_STATIC_BLOCK),
> > > +                                UNDEF_DOMAIN,
> > > +                                ALL_DOMAIN);
> > >  }
> > >
> > >  void
> > > @@ -402,9 +402,9 @@ objfile::expand_matching_symbols
> > >                 domain_name (domain), global,
> > >                 host_address_to_string (ordered_compare));
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > -    iter->expand_matching_symbols (this, name, domain, global,
> > > -                                  ordered_compare);
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > > +    qf->expand_matching_symbols (this, name, domain, global,
> > > +                                ordered_compare);
> > >  }
> > >
> > >  bool
> > > @@ -429,10 +429,10 @@ objfile::expand_symtabs_matching
> > >                 host_address_to_string (&expansion_notify),
> > >                 search_domain_name (kind));
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > -    if (!iter->expand_symtabs_matching (this, file_matcher, lookup_name,
> > > -                                       symbol_matcher, expansion_notify,
> > > -                                       search_flags, domain, kind))
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > > +    if (!qf->expand_symtabs_matching (this, file_matcher, lookup_name,
> > > +                                     symbol_matcher, expansion_notify,
> > > +                                     search_flags, domain, kind))
> > >        return false;
> > >    return true;
> > >  }
> > > @@ -454,10 +454,10 @@ objfile::find_pc_sect_compunit_symtab (struct bound_minimal_symbol msymbol,
> > >                 host_address_to_string (section),
> > >                 warn_if_readin);
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > >      {
> > > -      retval = iter->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
> > > -                                                  warn_if_readin);
> > > +      retval = qf->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
> > > +                                                warn_if_readin);
> > >        if (retval != nullptr)
> > >         break;
> > >      }
> > > @@ -482,8 +482,8 @@ objfile::map_symbol_filenames (gdb::function_view<symbol_filename_ftype> fun,
> > >                 objfile_debug_name (this),
> > >                 need_fullname);
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > -    iter->map_symbol_filenames (this, fun, need_fullname);
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > > +    qf->map_symbol_filenames (this, fun, need_fullname);
> > >  }
> > >
> > >  struct compunit_symtab *
> > > @@ -496,9 +496,9 @@ objfile::find_compunit_symtab_by_address (CORE_ADDR address)
> > >                 hex_string (address));
> > >
> > >    struct compunit_symtab *result = NULL;
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > >      {
> > > -      result = iter->find_compunit_symtab_by_address (this, address);
> > > +      result = qf->find_compunit_symtab_by_address (this, address);
> > >        if (result != nullptr)
> > >         break;
> > >      }
> > > @@ -521,10 +521,10 @@ objfile::lookup_global_symbol_language (const char *name,
> > >    enum language result = language_unknown;
> > >    *symbol_found_p = false;
> > >
> > > -  for (const auto &iter : qf_require_partial_symbols ())
> > > +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> > >      {
> > > -      result = iter->lookup_global_symbol_language (this, name, domain,
> > > -                                                   symbol_found_p);
> > > +      result = qf->lookup_global_symbol_language (this, name, domain,
> > > +                                                 symbol_found_p);
> > >        if (*symbol_found_p)
> > >         break;
> > >      }
> > > diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp
> > > index 61b9942de79..0bf49976b73 100644
> > > --- a/gdb/testsuite/gdb.python/py-objfile.exp
> > > +++ b/gdb/testsuite/gdb.python/py-objfile.exp
> > > @@ -135,7 +135,7 @@ gdb_test "p main" "= {<text variable, no debug info>} $hex <main>" \
> > >  gdb_py_test_silent_cmd "python objfile.add_separate_debug_file(\"${binfile}\")" \
> > >      "Add separate debug file file" 1
> > >
> > > -gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[0\]" \
> > > +gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[1\]" \
> > >      "Get separate debug info objfile" 1
> > >
> > >  gdb_test "python print (sep_objfile.owner.filename)" "${testfile}2" \
> > > --
> > > 2.40.1
> > >


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PING][PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading
  2023-07-11 15:00         ` Aaron Merey
@ 2023-07-19 14:33           ` Aaron Merey
  0 siblings, 0 replies; 33+ messages in thread
From: Aaron Merey @ 2023-07-19 14:33 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Pedro Alves, gdb-patches

On Mon, Jul 10, 2023 at 5:01 PM Aaron Merey <amerey@redhat.com> wrote:
>
> On Fri, Jul 7, 2023 at 10:18 AM Andrew Burgess <aburgess@redhat.com> wrote:
> >   proc enable_section_downloads { } {
> >       global gdb_prompt
> >
> >       set cmd "maint set debuginfod download-sections on"
> >       set msg "enable section downloads"
> >
> >       return [gdb_test_multiple $cmd $msg {
> >         -re -wrap ".*not compiled into GDB.*" {
> >             return 0
> >         }
> >         -re -wrap "^" {
> >             return 1
> >         }
> >         -re -wrap "" {
> >             fail "$gdb_test_name (unexpected output)"
> >             return 0
> >         }
> >       }]
> >   }
> >
> > would do the job, though I don't currently have a machine with a recent
> > enough version of debuginfod installed to fully test this, but something
> > like this should do the job.
>
> Thanks Andrew, this works on my machine. I've updated the patch below with
> this change.

On Tue, Jul 11, 2023 at 11:00 AM Aaron Merey <amerey@redhat.com> wrote:
>
> On Tue, Jul 11, 2023 at 8:01 AM Pedro Alves <pedro@palves.net> wrote:
> >
> > Write:
> >
> >   catch (const gdb_exception &e)
> >
> > ... to avoid slicing.
> >
> > This catches QUIT exceptions as well.  Did you mean gdb_exception_error instead?
>
> Yes it should be gdb_exception_error. I've updated the patch with these changes.

Is this patch ready for merging with the above changes?

Aaron


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PING*3][PATCH 6/6 v3] gdb/debuginfod: Add .debug_line downloading
  2023-07-03 17:40     ` [PING*2][PATCH " Aaron Merey
@ 2023-07-19 14:33       ` Aaron Merey
  0 siblings, 0 replies; 33+ messages in thread
From: Aaron Merey @ 2023-07-19 14:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Ping

Thanks,
Aaron

On Mon, Jul 3, 2023 at 1:40 PM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Thu, Jun 15, 2023 at 9:45 AM Aaron Merey <amerey@redhat.com> wrote:
> >
> > Ping
> >
> > Thanks,
> > Aaron
> >
> > On Wed, May 31, 2023 at 9:44 PM Aaron Merey <amerey@redhat.com> wrote:
> > >
> > > v2: https://sourceware.org/pipermail/gdb-patches/2023-April/198946.html
> > >
> > > v3 adds tests for .debug_line downloading and modifies logging for
> > > the testsuite debuginfod server (see commit message).
> > >
> > > Commit message:
> > >
> > > ELF/DWARF section downloading allows gdb to download .gdb_index files in
> > > order to defer full debuginfo downloads.  However .gdb_index does not
> > > contain any information regarding source filenames.  When a gdb command
> > > includes a filename argument (ex. 'break main.c:50'), this results in
> > > the mass downloading of all deferred debuginfo so that gdb can search the
> > > debuginfo for matching source filenames.  This can result in unnecessary
> > > downloads.
> > >
> > > To improve this, have gdb instead download each debuginfo's .debug_line
> > > (and .debug_line_str if using DWARF5) when executing these commands.
> > > Download full debuginfo only when its .debug_line contains a matching
> > > filename.
> > >
> > > Since the combined size of .debug_line and .debug_line_str is only about
> > > 1% the size of the corresponding debuginfo, significant time can be saved
> > > by checking these sections before choosing to download an entire debuginfo.
> > >
> > > This patch also redirects stdout and stderr of the debuginfod server
> > > used by testsuite/gdb.debuginfod tests to a server_log standard output
> > > file.  While adding tests for this patch I ran into an issue where the
> > > test server would block when logging to stderr, presumably because the
> > > stderr buffer filled up and wasn't being read from.  Redirecting the
> > > log to a file fixes this and also makes the server log more accessible
> > > when debugging test failures.
> > > ---
> > >  gdb/dwarf2/line-header.c                 | 215 +++++++++++++++--------
> > >  gdb/dwarf2/line-header.h                 |  10 ++
> > >  gdb/dwarf2/read-gdb-index.c              |  27 +++
> > >  gdb/dwarf2/read.c                        | 165 +++++++++++++++++
> > >  gdb/dwarf2/read.h                        |  31 ++++
> > >  gdb/testsuite/gdb.debuginfod/section.exp |  23 ++-
> > >  gdb/testsuite/lib/debuginfod-support.exp |   5 +-
> > >  7 files changed, 397 insertions(+), 79 deletions(-)
> > >
> > > diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> > > index d072a91bac9..b9210d84f6b 100644
> > > --- a/gdb/dwarf2/line-header.c
> > > +++ b/gdb/dwarf2/line-header.c
> > > @@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
> > >  {
> > >    LONGEST length = read_initial_length (abfd, buf, bytes_read);
> > >
> > > -  gdb_assert (cu_header->initial_length_size == 4
> > > -             || cu_header->initial_length_size == 8
> > > -             || cu_header->initial_length_size == 12);
> > > +  if (cu_header != nullptr)
> > > +    {
> > > +      gdb_assert (cu_header->initial_length_size == 4
> > > +                 || cu_header->initial_length_size == 8
> > > +                 || cu_header->initial_length_size == 12);
> > >
> > > -  if (cu_header->initial_length_size != *bytes_read)
> > > -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > > +      if (cu_header->initial_length_size != *bytes_read)
> > > +       complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > > +    }
> > >
> > >    *offset_size = (*bytes_read == 4) ? 4 : 8;
> > >    return length;
> > >  }
> > >
> > > -/* Read directory or file name entry format, starting with byte of
> > > -   format count entries, ULEB128 pairs of entry formats, ULEB128 of
> > > -   entries count and the entries themselves in the described entry
> > > -   format.  */
> > > +
> > > +/* Like read_formatted_entries but the .debug_line and .debug_line_str
> > > +   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
> > > +   where these sections are read from separate files without necessarily
> > > +   having access to the entire debuginfo file they originate from.  */
> > >
> > >  static void
> > > -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > > -                       const gdb_byte **bufp, struct line_header *lh,
> > > -                       unsigned int offset_size,
> > > -                       void (*callback) (struct line_header *lh,
> > > -                                         const char *name,
> > > -                                         dir_index d_index,
> > > -                                         unsigned int mod_time,
> > > -                                         unsigned int length))
> > > +read_formatted_entries
> > > +  (bfd *parent_bfd, const gdb_byte **line_bufp,
> > > +   const gdb::array_view<const gdb_byte> line_str_data,
> > > +   struct line_header *lh,
> > > +   unsigned int offset_size,
> > > +   void (*callback) (struct line_header *lh,
> > > +                    const char *name,
> > > +                    dir_index d_index,
> > > +                    unsigned int mod_time,
> > > +                    unsigned int length))
> > >  {
> > >    gdb_byte format_count, formati;
> > >    ULONGEST data_count, datai;
> > > -  const gdb_byte *buf = *bufp;
> > > +  const gdb_byte *buf = *line_bufp;
> > > +  const gdb_byte *str_buf = line_str_data.data ();
> > >    const gdb_byte *format_header_data;
> > >    unsigned int bytes_read;
> > >
> > > -  format_count = read_1_byte (abfd, buf);
> > > +  format_count = read_1_byte (parent_bfd, buf);
> > >    buf += 1;
> > >    format_header_data = buf;
> > >    for (formati = 0; formati < format_count; formati++)
> > >      {
> > > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> > >        buf += bytes_read;
> > > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> > >        buf += bytes_read;
> > >      }
> > >
> > > -  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
> > > +  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> > >    buf += bytes_read;
> > >    for (datai = 0; datai < data_count; datai++)
> > >      {
> > > @@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > >
> > >        for (formati = 0; formati < format_count; formati++)
> > >         {
> > > -         ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
> > > +         ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> > >           format += bytes_read;
> > >
> > > -         ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> > > +         ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> > >           format += bytes_read;
> > >
> > >           gdb::optional<const char *> string;
> > > @@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > >           switch (form)
> > >             {
> > >             case DW_FORM_string:
> > > -             string.emplace (read_direct_string (abfd, buf, &bytes_read));
> > > +             string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
> > >               buf += bytes_read;
> > >               break;
> > >
> > >             case DW_FORM_line_strp:
> > >               {
> > > -               const char *str
> > > -                 = per_objfile->read_line_string (buf, offset_size);
> > > +               if (line_str_data.empty ())
> > > +                 error (_("Dwarf Error: DW_FORM_line_strp used without " \
> > > +                          "required section"));
> > > +               if (line_str_data.size () <= offset_size)
> > > +                 error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> > > +                          "of section .debug_line"));
> > > +
> > > +               ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
> > > +
> > > +               const char *str;
> > > +               if (str_buf[str_offset] == '\0')
> > > +                 str = nullptr;
> > > +               else
> > > +                 str = (const char *) (str_buf + str_offset);
> > >                 string.emplace (str);
> > >                 buf += offset_size;
> > > +               break;
> > >               }
> > > -             break;
> > >
> > >             case DW_FORM_data1:
> > > -             uint.emplace (read_1_byte (abfd, buf));
> > > +             uint.emplace (read_1_byte (parent_bfd, buf));
> > >               buf += 1;
> > >               break;
> > >
> > >             case DW_FORM_data2:
> > > -             uint.emplace (read_2_bytes (abfd, buf));
> > > +             uint.emplace (read_2_bytes (parent_bfd, buf));
> > >               buf += 2;
> > >               break;
> > >
> > >             case DW_FORM_data4:
> > > -             uint.emplace (read_4_bytes (abfd, buf));
> > > +             uint.emplace (read_4_bytes (parent_bfd, buf));
> > >               buf += 4;
> > >               break;
> > >
> > >             case DW_FORM_data8:
> > > -             uint.emplace (read_8_bytes (abfd, buf));
> > > +             uint.emplace (read_8_bytes (parent_bfd, buf));
> > >               buf += 8;
> > >               break;
> > >
> > > @@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > >               break;
> > >
> > >             case DW_FORM_udata:
> > > -             uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
> > > +             uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
> > >               buf += bytes_read;
> > >               break;
> > >
> > > @@ -248,28 +267,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > >        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
> > >      }
> > >
> > > -  *bufp = buf;
> > > +  *line_bufp = buf;
> > >  }
> > >
> > >  /* See line-header.h.  */
> > >
> > >  line_header_up
> > > -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > -                          dwarf2_per_objfile *per_objfile,
> > > -                          struct dwarf2_section_info *section,
> > > -                          const struct comp_unit_head *cu_header,
> > > -                          const char *comp_dir)
> > > +dwarf_decode_line_header (bfd *parent_bfd,
> > > +                         gdb::array_view<const gdb_byte> line_data,
> > > +                         gdb::array_view<const gdb_byte> line_str_data,
> > > +                         const gdb_byte **debug_line_ptr,
> > > +                         bool is_dwz,
> > > +                         const struct comp_unit_head *cu_header,
> > > +                         const char *comp_dir)
> > >  {
> > > -  const gdb_byte *line_ptr;
> > > +  const gdb_byte *line_ptr, *buf;
> > >    unsigned int bytes_read, offset_size;
> > >    int i;
> > >    const char *cur_dir, *cur_file;
> > >
> > > -  bfd *abfd = section->get_bfd_owner ();
> > > +  buf = *debug_line_ptr;
> > >
> > >    /* Make sure that at least there's room for the total_length field.
> > >       That could be 12 bytes long, but we're just going to fudge that.  */
> > > -  if (to_underlying (sect_off) + 4 >= section->size)
> > > +  if (buf + 4 >= line_data.data () + line_data.size ())
> > >      {
> > >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> > >        return 0;
> > > @@ -277,62 +298,65 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >
> > >    line_header_up lh (new line_header (comp_dir));
> > >
> > > -  lh->sect_off = sect_off;
> > > +  lh->sect_off = (sect_offset) (buf - line_data.data ());
> > >    lh->offset_in_dwz = is_dwz;
> > >
> > > -  line_ptr = section->buffer + to_underlying (sect_off);
> > > +  line_ptr = buf;
> > >
> > >    /* Read in the header.  */
> > >    LONGEST unit_length
> > > -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> > > +    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
> > >                                               &bytes_read, &offset_size);
> > > -  line_ptr += bytes_read;
> > >
> > > -  const gdb_byte *start_here = line_ptr;
> > > +  line_ptr += bytes_read;
> > >
> > > -  if (line_ptr + unit_length > (section->buffer + section->size))
> > > +  if (line_ptr + unit_length > buf + line_data.size ())
> > >      {
> > >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> > >        return 0;
> > >      }
> > > +
> > > +  const gdb_byte *start_here = line_ptr;
> > > +
> > >    lh->statement_program_end = start_here + unit_length;
> > > -  lh->version = read_2_bytes (abfd, line_ptr);
> > > +  lh->version = read_2_bytes (parent_bfd, line_ptr);
> > >    line_ptr += 2;
> > >    if (lh->version > 5)
> > >      {
> > >        /* This is a version we don't understand.  The format could have
> > >          changed in ways we don't handle properly so just punt.  */
> > >        complaint (_("unsupported version in .debug_line section"));
> > > -      return NULL;
> > > +      return nullptr;
> > >      }
> > >    if (lh->version >= 5)
> > >      {
> > >        gdb_byte segment_selector_size;
> > >
> > >        /* Skip address size.  */
> > > -      read_1_byte (abfd, line_ptr);
> > > +      read_1_byte (parent_bfd, line_ptr);
> > >        line_ptr += 1;
> > >
> > > -      segment_selector_size = read_1_byte (abfd, line_ptr);
> > > +      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
> > >        line_ptr += 1;
> > >        if (segment_selector_size != 0)
> > >         {
> > >           complaint (_("unsupported segment selector size %u "
> > >                        "in .debug_line section"),
> > >                      segment_selector_size);
> > > -         return NULL;
> > > +         return nullptr;
> > >         }
> > >      }
> > >
> > > -  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
> > > +  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
> > >    line_ptr += offset_size;
> > >    lh->statement_program_start = line_ptr + header_length;
> > > -  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
> > > +
> > > +  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > >
> > >    if (lh->version >= 4)
> > >      {
> > > -      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
> > > +      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
> > >        line_ptr += 1;
> > >      }
> > >    else
> > > @@ -345,41 +369,47 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >                    "in `.debug_line' section"));
> > >      }
> > >
> > > -  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
> > > +  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > > -  lh->line_base = read_1_signed_byte (abfd, line_ptr);
> > > +
> > > +  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > > -  lh->line_range = read_1_byte (abfd, line_ptr);
> > > +
> > > +  lh->line_range = read_1_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > > -  lh->opcode_base = read_1_byte (abfd, line_ptr);
> > > +
> > > +  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > > +
> > >    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
> > >
> > >    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
> > >    for (i = 1; i < lh->opcode_base; ++i)
> > >      {
> > > -      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
> > > +      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
> > >        line_ptr += 1;
> > >      }
> > >
> > >    if (lh->version >= 5)
> > >      {
> > >        /* Read directory table.  */
> > > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > > -                             offset_size,
> > > -                             [] (struct line_header *header, const char *name,
> > > -                                 dir_index d_index, unsigned int mod_time,
> > > -                                 unsigned int length)
> > > +      read_formatted_entries
> > > +       (parent_bfd, &line_ptr, line_str_data,
> > > +        lh.get (), offset_size,
> > > +        [] (struct line_header *header, const char *name,
> > > +            dir_index d_index, unsigned int mod_time,
> > > +            unsigned int length)
> > >         {
> > >           header->add_include_dir (name);
> > >         });
> > >
> > >        /* Read file name table.  */
> > > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > > -                             offset_size,
> > > -                             [] (struct line_header *header, const char *name,
> > > -                                 dir_index d_index, unsigned int mod_time,
> > > -                                 unsigned int length)
> > > +      read_formatted_entries
> > > +       (parent_bfd, &line_ptr, line_str_data,
> > > +        lh.get (), offset_size,
> > > +        [] (struct line_header *header, const char *name,
> > > +            dir_index d_index, unsigned int mod_time,
> > > +            unsigned int length)
> > >         {
> > >           header->add_file_name (name, d_index, mod_time, length);
> > >         });
> > > @@ -387,7 +417,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >    else
> > >      {
> > >        /* Read directory table.  */
> > > -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > > +      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> > >         {
> > >           line_ptr += bytes_read;
> > >           lh->add_include_dir (cur_dir);
> > > @@ -395,17 +425,17 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >        line_ptr += bytes_read;
> > >
> > >        /* Read file name table.  */
> > > -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > > +      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> > >         {
> > >           unsigned int mod_time, length;
> > >           dir_index d_index;
> > >
> > >           line_ptr += bytes_read;
> > > -         d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > > +         d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> > >           line_ptr += bytes_read;
> > > -         mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > > +         mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> > >           line_ptr += bytes_read;
> > > -         length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > > +         length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> > >           line_ptr += bytes_read;
> > >
> > >           lh->add_file_name (cur_file, d_index, mod_time, length);
> > > @@ -413,9 +443,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >        line_ptr += bytes_read;
> > >      }
> > >
> > > -  if (line_ptr > (section->buffer + section->size))
> > > +  if (line_ptr > (buf + line_data.size ()))
> > >      complaint (_("line number info header doesn't "
> > >                  "fit in `.debug_line' section"));
> > >
> > > +  *debug_line_ptr += unit_length + offset_size;
> > >    return lh;
> > >  }
> > > +
> > > +line_header_up
> > > +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > +                          dwarf2_per_objfile *per_objfile,
> > > +                          struct dwarf2_section_info *section,
> > > +                          const struct comp_unit_head *cu_header,
> > > +                          const char *comp_dir)
> > > +{
> > > +  struct objfile *objfile = per_objfile->objfile;
> > > +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> > > +
> > > +  /* Read .debug_line.  */
> > > +  dwarf2_section_info *line_sec = &per_bfd->line;
> > > +  bfd_size_type line_size = line_sec->get_size (objfile);
> > > +
> > > +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> > > +
> > > +  /* Read .debug_line_str.  */
> > > +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> > > +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> > > +
> > > +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> > > +                                           line_str_size);
> > > +
> > > +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> > > +
> > > +  return dwarf_decode_line_header
> > > +    (per_bfd->obfd, line, line_str, &line_ptr,
> > > +     is_dwz, cu_header, comp_dir);
> > > +}
> > > diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> > > index 59a42e336f5..44e32828ddb 100644
> > > --- a/gdb/dwarf2/line-header.h
> > > +++ b/gdb/dwarf2/line-header.h
> > > @@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
> > >     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
> > >     const char *comp_dir);
> > >
> > > +/* Like above but the .debug_line and .debug_line_str are stored in
> > > +   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
> > > +   statement program header within LINE_DATA.  */
> > > +
> > > +extern line_header_up dwarf_decode_line_header
> > > +  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
> > > +   gdb::array_view<const gdb_byte> line_str_data,
> > > +   const gdb_byte **debug_line_ptr, bool is_dwz,
> > > +  const comp_unit_head *cu_header, const char *comp_dir);
> > > +
> > >  #endif /* DWARF2_LINE_HEADER_H */
> > > diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> > > index d3516e92361..64f202fddd3 100644
> > > --- a/gdb/dwarf2/read-gdb-index.c
> > > +++ b/gdb/dwarf2/read-gdb-index.c
> > > @@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
> > >    }
> > >  };
> > >
> > > +struct mapped_debug_line;
> > > +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> > > +
> > >  struct dwarf2_gdb_index : public dwarf2_base_index_functions
> > >  {
> > >    /* This dumps minimal information about the index.
> > > @@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> > >    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> > >       debuginfo if necessary.  */
> > >    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> > > +
> > > +  /* Filename information related to this .gdb_index.  */
> > > +  mapped_debug_line_up mdl;
> > > +
> > > +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> > > +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> > > +  bool filename_in_debug_line
> > > +  (objfile *objfile,
> > > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> > >  };
> > >
> > >  void
> > > @@ -587,6 +599,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
> > >    return result;
> > >  }
> > >
> > > +bool
> > > +dwarf2_gdb_index::filename_in_debug_line
> > > +  (objfile *objfile,
> > > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > > +{
> > > +  if (mdl == nullptr)
> > > +    mdl.reset (new mapped_debug_line (objfile));
> > > +
> > > +  return mdl->contains_matching_filename (file_matcher);
> > > +}
> > > +
> > >  bool
> > >  dwarf2_gdb_index::expand_symtabs_matching
> > >      (struct objfile *objfile,
> > > @@ -615,6 +638,10 @@ dwarf2_gdb_index::expand_symtabs_matching
> > >           return false;
> > >         }
> > >
> > > +      if (file_matcher != nullptr
> > > +         && !filename_in_debug_line (objfile, file_matcher))
> > > +       return true;
> > > +
> > >        read_full_dwarf_from_debuginfod (objfile, this);
> > >        return true;
> > >      }
> > > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> > > index 96d1ff53d91..715b3c06a56 100644
> > > --- a/gdb/dwarf2/read.c
> > > +++ b/gdb/dwarf2/read.c
> > > @@ -81,6 +81,7 @@
> > >  #include "gdbsupport/gdb_optional.h"
> > >  #include "gdbsupport/underlying.h"
> > >  #include "gdbsupport/hash_enum.h"
> > > +#include "gdbsupport/scoped_mmap.h"
> > >  #include "filename-seen-cache.h"
> > >  #include "producer.h"
> > >  #include <fcntl.h>
> > > @@ -2115,6 +2116,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
> > >    return this_cu->file_names;
> > >  }
> > >
> > > +#if !HAVE_SYS_MMAN_H
> > > +
> > > +bool
> > > +mapped_debug_line::contains_matching_filename
> > > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > > +{
> > > +  return false;
> > > +}
> > > +
> > > +gdb::array_view<const gdb_byte>
> > > +mapped_debug_line::read_debug_line_separate
> > > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > > +{
> > > +  return {};
> > > +}
> > > +
> > > +bool
> > > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > > +{
> > > +  return false;
> > > +}
> > > +
> > > +#else /* !HAVE_SYS_MMAN_H */
> > > +
> > > +struct line_resource_mmap final : public index_cache_resource
> > > +{
> > > +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> > > +     file doesn't exist. */
> > > +  line_resource_mmap (const char *filename)
> > > +    : mapping (mmap_file (filename))
> > > +  {}
> > > +
> > > +  scoped_mmap mapping;
> > > +};
> > > +
> > > +/* See read.h.  */
> > > +
> > > +bool
> > > +mapped_debug_line::contains_matching_filename
> > > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > > +{
> > > +  for (line_header_up &lh : line_headers)
> > > +    for (file_entry &fe : lh->file_names ())
> > > +      {
> > > +       const char *filename = fe.name;
> > > +
> > > +       if (file_matcher (fe.name, false))
> > > +         return true;
> > > +
> > > +       bool basename_match = file_matcher (lbasename (fe.name), true);
> > > +
> > > +       if (!basenames_may_differ && !basename_match)
> > > +         continue;
> > > +
> > > +       /* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> > > +          until DWARF5.  Since we don't have access to the CU at this
> > > +          point we just check for a partial match on the filename.
> > > +          If there is a match, the full debuginfo will be downloaded
> > > +          ane the match will be re-evalute with DW_AT_comp_dir.  */
> > > +       if (lh->version < 5 && fe.d_index == 0)
> > > +         return basename_match;
> > > +
> > > +       const char *dirname = fe.include_dir (&*lh);
> > > +       std::string fullname;
> > > +
> > > +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> > > +         fullname = filename;
> > > +       else
> > > +         fullname = std::string (dirname) + SLASH_STRING + filename;
> > > +
> > > +       gdb::unique_xmalloc_ptr<char> rewritten
> > > +         = rewrite_source_path (fullname.c_str ());
> > > +       if (rewritten != nullptr)
> > > +         fullname = rewritten.release ();
> > > +
> > > +       if (file_matcher (fullname.c_str (), false))
> > > +         return true;
> > > +      }
> > > +
> > > +  return false;
> > > +}
> > > +
> > > +/* See read.h.  */
> > > +
> > > +gdb::array_view<const gdb_byte>
> > > +mapped_debug_line::read_debug_line_separate
> > > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > > +{
> > > +  if (filename == nullptr)
> > > +    return {};
> > > +
> > > +  try
> > > +  {
> > > +    line_resource_mmap *mmap_resource
> > > +      = new line_resource_mmap (filename);
> > > +
> > > +    resource->reset (mmap_resource);
> > > +
> > > +    return gdb::array_view<const gdb_byte>
> > > +      ((const gdb_byte *) mmap_resource->mapping.get (),
> > > +       mmap_resource->mapping.size ());
> > > +  }
> > > +  catch (const gdb_exception &except)
> > > +  {
> > > +    exception_print (gdb_stderr, except);
> > > +  }
> > > +
> > > +  return {};
> > > +}
> > > +
> > > +/* See read.h.  */
> > > +
> > > +bool
> > > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > > +{
> > > +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > > +  if (build_id == nullptr)
> > > +    return false;
> > > +
> > > +  gdb::unique_xmalloc_ptr<char> line_path;
> > > +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> > > +                                               build_id->size,
> > > +                                               bfd_get_filename
> > > +                                                 (objfile->obfd.get ()),
> > > +                                               ".debug_line",
> > > +                                               &line_path);
> > > +
> > > +  if (line_fd.get () < 0)
> > > +    return false;
> > > +
> > > +  gdb::unique_xmalloc_ptr<char> line_str_path;
> > > +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> > > +                                                   build_id->size,
> > > +                                                   bfd_get_filename
> > > +                                                     (objfile->obfd.get ()),
> > > +                                                   ".debug_line_str",
> > > +                                                   &line_str_path);
> > > +
> > > +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> > > +  line_str_data = read_debug_line_separate (line_str_path.get (),
> > > +                                           &line_str_resource);
> > > +
> > > +  const gdb_byte *line_ptr = line_data.data ();
> > > +
> > > +  while (line_ptr < line_data.data () + line_data.size ())
> > > +    {
> > > +      line_header_up lh
> > > +       = dwarf_decode_line_header (objfile->obfd.get (),
> > > +                                   line_data, line_str_data,
> > > +                                   &line_ptr, false,
> > > +                                   nullptr, nullptr);
> > > +      line_headers.emplace_back (lh.release ());
> > > +    }
> > > +
> > > +  return true;
> > > +}
> > > +#endif /* !HAVE_SYS_MMAN_H */
> > > +
> > > +mapped_debug_line::mapped_debug_line (objfile *objfile)
> > > +{
> > > +  if (!read_debug_line_from_debuginfod (objfile))
> > > +    line_headers.clear ();
> > > +}
> > > +
> > >  /* A helper for the "quick" functions which computes and caches the
> > >     real path for a given file name from the line table.  */
> > >
> > > diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> > > index e3131693b81..b8a8b76bde0 100644
> > > --- a/gdb/dwarf2/read.h
> > > +++ b/gdb/dwarf2/read.h
> > > @@ -34,6 +34,7 @@
> > >  #include "gdbsupport/hash_enum.h"
> > >  #include "gdbsupport/function-view.h"
> > >  #include "gdbsupport/packed.h"
> > > +#include "dwarf2/line-header.h"
> > >
> > >  /* Hold 'maintenance (set|show) dwarf' commands.  */
> > >  extern struct cmd_list_element *set_dwarf_cmdlist;
> > > @@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
> > >  extern void read_full_dwarf_from_debuginfod (struct objfile *,
> > >                                              dwarf2_base_index_functions *);
> > >
> > > +struct mapped_debug_line
> > > +{
> > > +  mapped_debug_line (objfile *objfile);
> > > +
> > > +  /* Return true if any of the mapped .debug_line's filenames match
> > > +     FILE_MATCHER.  */
> > > +
> > > +  bool contains_matching_filename
> > > +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> > > +
> > > +private:
> > > +  std::vector<line_header_up> line_headers;
> > > +
> > > +  gdb::array_view<const gdb_byte> line_data;
> > > +  gdb::array_view<const gdb_byte> line_str_data;
> > > +
> > > +  std::unique_ptr<index_cache_resource> line_resource;
> > > +  std::unique_ptr<index_cache_resource> line_str_resource;
> > > +
> > > +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> > > +     and populate line_headers.  */
> > > +
> > > +  bool read_debug_line_from_debuginfod (objfile *objfile);
> > > +
> > > +  /* Initialize line_data and line_str_data with the .debug_line and
> > > +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> > > +
> > > +  gdb::array_view<const gdb_byte> read_debug_line_separate
> > > +    (char *filename, std::unique_ptr<index_cache_resource> *resource);
> > > +};
> > >  #endif /* DWARF2READ_H */
> > > diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
> > > index 96e9750cd38..1380eb7ef78 100644
> > > --- a/gdb/testsuite/gdb.debuginfod/section.exp
> > > +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> > > @@ -106,7 +106,7 @@ proc_with_prefix clean_restart_with_prompt { binfile res testname } {
> > >
> > >      clean_restart
> > >
> > > -    # Delete client cache so debuginfo downloads again.
> > > +    # Delete client cache so progress messages always appear.
> > >      file delete -force $cache
> > >
> > >      gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
> > > @@ -168,6 +168,27 @@ proc_with_prefix local_url { } {
> > >         \\(\\) at.*"
> > >      set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
> > >      gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
> > > +
> > > +    clean_restart_with_prompt $sectexec "" "line 1"
> > > +
> > > +    # List source file using .debug_line download.
> > > +    set res ".*\.debug_line.*$lib_sl1.*21.*extern void libsection2_test.*"
> > > +    gdb_test "list $libsrc1:21" $res "file [file tail $sectexec] line 1 list"
> > > +
> > > +    clean_restart_with_prompt $sectexec "" "line 2"
> > > +
> > > +    # Set breakpoint using .debug_line download.
> > > +    set res ".*section \.debug_line for $lib_sl1.*Breakpoint 2 at.*$libsrc1.*"
> > > +    gdb_test "br $libsrc1:37" $res "file [file tail $sectexec] line 2 br"
> > > +
> > > +    # Continue to breakpoint.
> > > +    set res "Breakpoint 2, libsection1_test.*\"Cancelling thread\\\\n\".*"
> > > +    gdb_test "c" $res "file [file tail $sectexec] line 2 continue"
> > > +
> > > +    # Check that download progress message is correctly formatted
> > > +    # when printing threads.
> > > +    set res ".*separate debug info for $lib_sl2\.\.\.\r\n.* 2    Thread.*"
> > > +    gdb_test "info thr" $res "file [file tail $libfile2] line thread"
> > >  }
> > >
> > >  # Create CACHE and DB directories ready for debuginfod to use.
> > > diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
> > > index 6368c27e9d0..07ea23999bd 100644
> > > --- a/gdb/testsuite/lib/debuginfod-support.exp
> > > +++ b/gdb/testsuite/lib/debuginfod-support.exp
> > > @@ -113,6 +113,8 @@ proc with_debuginfod_env { cache body } {
> > >  proc start_debuginfod { db debugdir } {
> > >      global debuginfod_spawn_id spawn_id
> > >
> > > +    set logfile [standard_output_file "server_log"]
> > > +
> > >      # Find an unused port.
> > >      set port 7999
> > >      set found false
> > > @@ -127,7 +129,8 @@ proc start_debuginfod { db debugdir } {
> > >             set old_spawn_id $spawn_id
> > >         }
> > >
> > > -       spawn debuginfod -vvvv -d $db -p $port -F $debugdir
> > > +       spawn sh -c "debuginfod -vvvv -d $db -p $port -F $debugdir 2>&1 \
> > > +               | tee $logfile"
> > >         set debuginfod_spawn_id $spawn_id
> > >
> > >         if { [info exists old_spawn_id] } {
> > > --
> > > 2.40.1
> > >


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading
  2023-06-01  1:43 ` [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading Aaron Merey
  2023-06-15 13:44   ` Aaron Merey
  2023-07-07 14:18   ` [PATCH " Andrew Burgess
@ 2023-07-27 10:24   ` Andrew Burgess
  2 siblings, 0 replies; 33+ messages in thread
From: Andrew Burgess @ 2023-07-27 10:24 UTC (permalink / raw)
  To: Aaron Merey, gdb-patches; +Cc: Aaron Merey

Aaron Merey <amerey@redhat.com> writes:

> v2.2: https://sourceware.org/pipermail/gdb-patches/2023-May/199326.html
>
> v3 includes testcases instead of adding them in a separate patch.
> Tests related to section downloading are now included in
> testsuite/gdb.debuginfod/section.exp.
>
> Commit message:
>
> At the beginning of a session, gdb may attempt to download debuginfo
> for all shared libraries associated with the process or core file
> being debugged.  This can be a waste of time and storage space when much
> of the debuginfo ends up not being used during the session.
>
> To reduce the gdb's startup latency and to download only the debuginfo
> that is really needed, this patch adds on-demand downloading of debuginfo.
>
> 'set debuginfo enabled on' now causes gdb to attempt to download a .gdb_index
> for each shared library instead of its full debuginfo.  Each corresponding
> separate debuginfo will be deferred until gdb needs to expand symtabs
> associated with the debuginfo's index.
>
> Because these indices are significantly smaller than their corresponding
> debuginfo, this generally reduces the total amount of data gdb downloads.
> Reductions of 80%-95% have been observed when debugging large GUI programs.
>
>     (gdb) set debuginfod enabled on
>     (gdb) start
>     Downloading section .gdb_index for /lib64/libcurl.so.4
>     [...]
>     1826        client->server_mhandle = curl_multi_init ();
>     (gdb) step
>     Downloading separate debug info for /lib64/libcurl.so.4
>     Downloading separate debug info for [libcurl dwz]
>     Downloading source file /usr/src/debug/curl-7.85.0-6.fc37.x86_64/build-full/lib/../../lib/multi.c
>     curl_multi_init () at ../../lib/multi.c:457
>     457     {
>     (gdb)
>
> Some of the key functions below include dwarf2_has_separate_index which
> downloads the separate .gdb_index.  If successful, the shared library
> objfile owns the index until the separate debug objfile is downloaded
> or confirmed to not be available.
>
> read_full_dwarf_from_debuginfod downloads the full debuginfo and
> initializes the separate debug objfile.  It is called by functions
> such as dwarf2_gdb_index::expand_symtabs_matching and
> dwarf2_base_index_functions::find_pc_sect_compunit_symtab when symtab
> expansion is required.
> ---
>  gdb/dwarf2/frame.c                         |  13 ++
>  gdb/dwarf2/frame.h                         |   4 +
>  gdb/dwarf2/index-cache.c                   |  33 ++++
>  gdb/dwarf2/index-cache.h                   |  13 ++
>  gdb/dwarf2/public.h                        |   7 +
>  gdb/dwarf2/read-gdb-index.c                | 156 ++++++++++++++++--
>  gdb/dwarf2/read.c                          | 146 ++++++++++++++++-
>  gdb/dwarf2/read.h                          |  10 ++
>  gdb/dwarf2/section.c                       |   3 +-
>  gdb/elfread.c                              |   2 +-
>  gdb/objfile-flags.h                        |   4 +
>  gdb/objfiles.h                             |  17 +-
>  gdb/quick-symbol.h                         |   4 +
>  gdb/symfile.c                              |  13 +-
>  gdb/symtab.c                               |  18 +-
>  gdb/testsuite/gdb.debuginfod/libsection1.c |  40 +++++
>  gdb/testsuite/gdb.debuginfod/libsection2.c |  37 +++++
>  gdb/testsuite/gdb.debuginfod/section.c     |  29 ++++
>  gdb/testsuite/gdb.debuginfod/section.exp   | 181 +++++++++++++++++++++
>  gdb/testsuite/lib/debuginfod-support.exp   |  30 ++++
>  20 files changed, 734 insertions(+), 26 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.debuginfod/libsection1.c
>  create mode 100644 gdb/testsuite/gdb.debuginfod/libsection2.c
>  create mode 100644 gdb/testsuite/gdb.debuginfod/section.c
>  create mode 100644 gdb/testsuite/gdb.debuginfod/section.exp
>
> diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
> index a561aaf3100..3613f8252a7 100644
> --- a/gdb/dwarf2/frame.c
> +++ b/gdb/dwarf2/frame.c
> @@ -1609,6 +1609,19 @@ set_comp_unit (struct objfile *objfile, struct comp_unit *unit)
>    return dwarf2_frame_bfd_data.set (abfd, unit);
>  }
>  
> +/* See frame.h.  */
> +
> +void
> +dwarf2_clear_frame_data (struct objfile *objfile)
> +{
> +  bfd *abfd = objfile->obfd.get ();
> +
> +  if (gdb_bfd_requires_relocations (abfd))
> +    dwarf2_frame_objfile_data.clear (objfile);
> +  else
> +    dwarf2_frame_bfd_data.clear (abfd);
> +}
> +
>  /* Find the FDE for *PC.  Return a pointer to the FDE, and store the
>     initial location associated with it into *PC.  */
>  
> diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
> index 5643e557513..2391e313e7c 100644
> --- a/gdb/dwarf2/frame.h
> +++ b/gdb/dwarf2/frame.h
> @@ -238,6 +238,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
>  extern const struct frame_base *
>    dwarf2_frame_base_sniffer (frame_info_ptr this_frame);
>  
> +/* Delete OBJFILEs comp_unit.  */
> +
> +extern void dwarf2_clear_frame_data (struct objfile * objfile);
> +
>  /* Compute the DWARF CFA for a frame.  */
>  
>  CORE_ADDR dwarf2_frame_cfa (frame_info_ptr this_frame);
> diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
> index 79ab706ee9d..bbafcd321b2 100644
> --- a/gdb/dwarf2/index-cache.c
> +++ b/gdb/dwarf2/index-cache.c
> @@ -216,6 +216,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
>    return {};
>  }
>  
> +/* See index-cache.h.  */
> +
> +gdb::array_view<const gdb_byte>
> +index_cache::lookup_gdb_index_debuginfod (const char *index_path,
> +					  std::unique_ptr<index_cache_resource> *resource)
> +{
> +  try
> +    {
> +      /* Try to map that file.  */
> +      index_cache_resource_mmap *mmap_resource
> +	= new index_cache_resource_mmap (index_path);
> +
> +      /* Hand the resource to the caller.  */
> +      resource->reset (mmap_resource);
> +
> +      return gdb::array_view<const gdb_byte>
> +	  ((const gdb_byte *) mmap_resource->mapping.get (),
> +	   mmap_resource->mapping.size ());
> +    }
> +  catch (const gdb_exception_error &except)
> +    {
> +      warning (_("Unable to read %s: %s"), index_path, except.what ());
> +    }
> +
> +  return {};
> +}
> +
>  #else /* !HAVE_SYS_MMAN_H */
>  
>  /* See dwarf-index-cache.h.  This is a no-op on unsupported systems.  */
> @@ -227,6 +254,12 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
>    return {};
>  }
>  
> +gdb::array_view<const gdb_byte>
> +index_cache::lookup_gdb_index_debuginfod (const char *index_path,
> +					  std::unique_ptr<index_cache_resource> *resource)
> +{
> +  return {};
> +}
>  #endif
>  
>  /* See dwarf-index-cache.h.  */
> diff --git a/gdb/dwarf2/index-cache.h b/gdb/dwarf2/index-cache.h
> index 1efff17049f..e400afd5123 100644
> --- a/gdb/dwarf2/index-cache.h
> +++ b/gdb/dwarf2/index-cache.h
> @@ -67,6 +67,19 @@ class index_cache
>    lookup_gdb_index (const bfd_build_id *build_id,
>  		    std::unique_ptr<index_cache_resource> *resource);
>  
> +  /* Look for an index file located at INDEX_PATH in the debuginfod cache.
> +     Unlike lookup_gdb_index, this function does not exit early if the
> +     index cache has not been enabled.
> +
> +     If found, return the contents as an array_view and store the underlying
> +     resources (allocated memory, mapped file, etc) in RESOURCE.  The returned
> +     array_view is valid as long as RESOURCE is not destroyed.
> +
> +     If no matching index file is found, return an empty array view.  */
> +  gdb::array_view<const gdb_byte>
> +  lookup_gdb_index_debuginfod (const char *index_path,
> +			       std::unique_ptr<index_cache_resource> *resource);
> +
>    /* Return the number of cache hits.  */
>    unsigned int n_hits () const
>    { return m_n_hits; }
> diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h
> index 0e74857eb1a..4a44cdbc223 100644
> --- a/gdb/dwarf2/public.h
> +++ b/gdb/dwarf2/public.h
> @@ -40,4 +40,11 @@ extern void dwarf2_initialize_objfile (struct objfile *objfile);
>  
>  extern void dwarf2_build_frame_info (struct objfile *);
>  
> +/* Query debuginfod for the .gdb_index associated with OBJFILE.  If
> +   successful, create an objfile to hold the .gdb_index information
> +   and act as a placeholder until the full debuginfo needs to be
> +   downloaded.  */
> +
> +extern bool dwarf2_has_separate_index (struct objfile *);
> +
>  #endif /* DWARF2_PUBLIC_H */
> diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> index 1006386cb2d..d3516e92361 100644
> --- a/gdb/dwarf2/read-gdb-index.c
> +++ b/gdb/dwarf2/read-gdb-index.c
> @@ -136,6 +136,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       gdb.dwarf2/gdb-index.exp testcase.  */
>    void dump (struct objfile *objfile) override;
>  
> +  /* Calls do_expand_matching_symbols and downloads debuginfo if necessary.  */
>    void expand_matching_symbols
>      (struct objfile *,
>       const lookup_name_info &lookup_name,
> @@ -143,6 +144,14 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       int global,
>       symbol_compare_ftype *ordered_compare) override;
>  
> +  void do_expand_matching_symbols
> +    (struct objfile *,
> +     const lookup_name_info &lookup_name,
> +     domain_enum domain,
> +     int global,
> +     symbol_compare_ftype *ordered_compare);
> +
> +  /* Calls do_expand_symtabs_matching and downloads debuginfo if necessary.  */
>    bool expand_symtabs_matching
>      (struct objfile *objfile,
>       gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> @@ -152,8 +161,59 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       block_search_flags search_flags,
>       domain_enum domain,
>       enum search_domain kind) override;
> +
> +  bool do_expand_symtabs_matching
> +    (struct objfile *objfile,
> +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> +     const lookup_name_info *lookup_name,
> +     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> +     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> +     block_search_flags search_flags,
> +     domain_enum domain,
> +     enum search_domain kind);
> +
> +  /* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
> +     debuginfo if necessary.  */
> +  void expand_all_symtabs (struct objfile *objfile) override;
> +
> +  /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> +     debuginfo if necessary.  */
> +  struct symtab *find_last_source_symtab (struct objfile *objfile) override;
>  };
>  
> +void
> +dwarf2_gdb_index::expand_all_symtabs (struct objfile *objfile)
> +{
> +  try
> +    {
> +      dwarf2_base_index_functions::expand_all_symtabs (objfile);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	exception_print (gdb_stderr, e);
> +      else
> +	read_full_dwarf_from_debuginfod (objfile, this);
> +    }
> +}
> +
> +struct symtab *
> +dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
> +{
> +  try
> +    {
> +      return dwarf2_base_index_functions::find_last_source_symtab (objfile);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	exception_print (gdb_stderr, e);
> +      else
> +	read_full_dwarf_from_debuginfod (objfile, this);
> +      return nullptr;
> +    }
> +}
> +
>  /* This dumps minimal information about the index.
>     It is called via "mt print objfiles".
>     One use is to verify .gdb_index has been loaded by the
> @@ -315,7 +375,7 @@ dw2_symtab_iter_next (struct dw2_symtab_iterator *iter,
>  }
>  
>  void
> -dwarf2_gdb_index::expand_matching_symbols
> +dwarf2_gdb_index::do_expand_matching_symbols
>    (struct objfile *objfile,
>     const lookup_name_info &name, domain_enum domain,
>     int global,
> @@ -353,6 +413,29 @@ dwarf2_gdb_index::expand_matching_symbols
>      }, per_objfile);
>  }
>  
> +void
> +dwarf2_gdb_index::expand_matching_symbols
> +  (struct objfile *objfile,
> +   const lookup_name_info &lookup_name,
> +   domain_enum domain,
> +   int global,
> +   symbol_compare_ftype *ordered_compare)
> +{
> +  try
> +    {
> +      do_expand_matching_symbols (objfile, lookup_name, domain,
> +				  global, ordered_compare);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	exception_print (gdb_stderr, e);
> +      else
> +	read_full_dwarf_from_debuginfod (objfile, this);
> +      return;
> +    }
> +}
> +
>  /* Helper for dw2_expand_matching symtabs.  Called on each symbol
>     matched, to expand corresponding CUs that were marked.  IDX is the
>     index of the symbol name that matched.  */
> @@ -455,7 +538,7 @@ dw2_expand_marked_cus
>  }
>  
>  bool
> -dwarf2_gdb_index::expand_symtabs_matching
> +dwarf2_gdb_index::do_expand_symtabs_matching
>      (struct objfile *objfile,
>       gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
>       const lookup_name_info *lookup_name,
> @@ -504,6 +587,39 @@ dwarf2_gdb_index::expand_symtabs_matching
>    return result;
>  }
>  
> +bool
> +dwarf2_gdb_index::expand_symtabs_matching
> +    (struct objfile *objfile,
> +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> +     const lookup_name_info *lookup_name,
> +     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> +     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> +     block_search_flags search_flags,
> +     domain_enum domain,
> +     enum search_domain kind)
> +{
> +  if (objfile->flags & OBJF_READNEVER)
> +    return false;
> +
> +  try
> +    {
> +      return do_expand_symtabs_matching (objfile, file_matcher, lookup_name,
> +					 symbol_matcher, expansion_notify,
> +					 search_flags, domain, kind);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	{
> +	  exception_print (gdb_stderr, e);
> +	  return false;
> +	}
> +
> +      read_full_dwarf_from_debuginfod (objfile, this);
> +      return true;
> +    }
> +}
> +
>  quick_symbol_functions_up
>  mapped_gdb_index::make_quick_functions () const
>  {
> @@ -797,28 +913,32 @@ dwarf2_read_gdb_index
>  
>    /* If there is a .dwz file, read it so we can get its CU list as
>       well.  */
> -  dwz = dwarf2_get_dwz_file (per_bfd);
> -  if (dwz != NULL)
> +  if (get_gdb_index_contents_dwz != nullptr)
>      {
>        mapped_gdb_index dwz_map;
>        const gdb_byte *dwz_types_ignore;
>        offset_type dwz_types_elements_ignore;
> +      dwz = dwarf2_get_dwz_file (per_bfd);
>  
> -      gdb::array_view<const gdb_byte> dwz_index_content
> -	= get_gdb_index_contents_dwz (objfile, dwz);
> -
> -      if (dwz_index_content.empty ())
> -	return 0;
> -
> -      if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()),
> -				       1, dwz_index_content, &dwz_map,
> -				       &dwz_list, &dwz_list_elements,
> -				       &dwz_types_ignore,
> -				       &dwz_types_elements_ignore))
> +      if (dwz != nullptr)
>  	{
> -	  warning (_("could not read '.gdb_index' section from %s; skipping"),
> -		   bfd_get_filename (dwz->dwz_bfd.get ()));
> -	  return 0;
> +	  gdb::array_view<const gdb_byte> dwz_index_content
> +	    = get_gdb_index_contents_dwz (objfile, dwz);
> +
> +	  if (dwz_index_content.empty ())
> +	    return 0;
> +
> +	  if (!read_gdb_index_from_buffer (bfd_get_filename
> +					     (dwz->dwz_bfd.get ()),
> +					   1, dwz_index_content, &dwz_map,
> +					   &dwz_list, &dwz_list_elements,
> +					   &dwz_types_ignore,
> +					   &dwz_types_elements_ignore))
> +	    {
> +	      warning (_("could not read '.gdb_index' section from %s; skipping"),
> +		       bfd_get_filename (dwz->dwz_bfd.get ()));
> +		return 0;
> +	    }
>  	}
>      }
>  
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index 4828409222c..96d1ff53d91 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -34,6 +34,7 @@
>  #include "dwarf2/attribute.h"
>  #include "dwarf2/comp-unit-head.h"
>  #include "dwarf2/cu.h"
> +#include "dwarf2/frame.h"
>  #include "dwarf2/index-cache.h"
>  #include "dwarf2/index-common.h"
>  #include "dwarf2/leb.h"
> @@ -95,6 +96,8 @@
>  #include "split-name.h"
>  #include "gdbsupport/parallel-for.h"
>  #include "gdbsupport/thread-pool.h"
> +#include "inferior.h"
> +#include "debuginfod-support.h"
>  
>  /* When == 1, print basic high level tracing messages.
>     When > 1, be more verbose.
> @@ -3168,7 +3171,7 @@ dwarf2_base_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd,
>  }
>  
>  struct compunit_symtab *
> -dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> +dwarf2_base_index_functions::do_find_pc_sect_compunit_symtab
>       (struct objfile *objfile,
>        struct bound_minimal_symbol msymbol,
>        CORE_ADDR pc,
> @@ -3199,6 +3202,32 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
>    return result;
>  }
>  
> +struct compunit_symtab *
> +dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> +     (struct objfile *objfile,
> +      struct bound_minimal_symbol msymbol,
> +      CORE_ADDR pc,
> +      struct obj_section *section,
> +      int warn_if_readin)
> +{
> +  if (objfile->flags & OBJF_READNEVER)
> +    return nullptr;
> +
> +  try
> +    {
> +      return do_find_pc_sect_compunit_symtab (objfile, msymbol, pc,
> +					      section, warn_if_readin);
> +    }
> +  catch (gdb_exception e)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	exception_print (gdb_stderr, e);
> +      else
> +	read_full_dwarf_from_debuginfod (objfile, this);
> +      return nullptr;
> +    }
> +}
> +
>  void
>  dwarf2_base_index_functions::map_symbol_filenames
>       (struct objfile *objfile,
> @@ -3355,6 +3384,29 @@ get_gdb_index_contents_from_cache_dwz (objfile *obj, dwz_file *dwz)
>    return global_index_cache.lookup_gdb_index (build_id, &dwz->index_cache_res);
>  }
>  
> +/* Query debuginfod for the .gdb_index matching OBJFILE's build-id.  Return the
> +   contents if successful.  */
> +
> +static gdb::array_view<const gdb_byte>
> +get_gdb_index_contents_from_debuginfod (objfile *objfile, dwarf2_per_bfd *per_bfd)
> +{
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  if (build_id == nullptr)
> +    return {};
> +
> +  gdb::unique_xmalloc_ptr<char> index_path;
> +  scoped_fd fd = debuginfod_section_query (build_id->data, build_id->size,
> +					   bfd_get_filename
> +					     (objfile->obfd.get ()),
> +					   ".gdb_index",
> +					   &index_path);
> +  if (fd.get () < 0)
> +    return {};
> +
> +  return global_index_cache.lookup_gdb_index_debuginfod
> +    (index_path.get (), &per_bfd->index_cache_res);
> +}
> +
>  static quick_symbol_functions_up make_cooked_index_funcs ();
>  
>  /* See dwarf2/public.h.  */
> @@ -3420,10 +3472,102 @@ dwarf2_initialize_objfile (struct objfile *objfile)
>        return;
>      }
>  
> +  if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED)
> +      && dwarf2_read_gdb_index (per_objfile,
> +				get_gdb_index_contents_from_debuginfod,
> +				nullptr))
> +    {
> +      dwarf_read_debug_printf ("found .gdb_index from debuginfod");
> +      objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
> +      objfile->qf.begin ()->get ()->from_separate_index = true;
> +      return;
> +    }
> +
>    global_index_cache.miss ();
>    objfile->qf.push_front (make_cooked_index_funcs ());
>  }
>  
> +/* See read.h.  */
> +
> +void
> +read_full_dwarf_from_debuginfod (struct objfile *objfile,
> +				 dwarf2_base_index_functions *fncs)
> +{
> +  gdb_assert (objfile->flags & OBJF_DOWNLOAD_DEFERRED);
> +
> +  const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  const char *filename;
> +  gdb_bfd_ref_ptr debug_bfd;
> +  gdb::unique_xmalloc_ptr<char> symfile_path;
> +  scoped_fd fd;
> +
> +  if (build_id == nullptr)
> +    goto unset;
> +
> +  filename = bfd_get_filename (objfile->obfd.get ());
> +  fd = debuginfod_debuginfo_query (build_id->data, build_id->size,
> +				   filename, &symfile_path);
> +  if (fd.get () < 0)
> +    goto unset;
> +
> +  /* Separate debuginfo successfully retrieved from server.  */
> +  debug_bfd = symfile_bfd_open (symfile_path.get ());
> +  if (debug_bfd == nullptr
> +      || !build_id_verify (debug_bfd.get (), build_id->size, build_id->data))
> +    {
> +      warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
> +	       filename);
> +      goto unset;
> +    }
> +
> +  /* Clear frame data so it can be recalculated using DWARF.  */
> +  dwarf2_clear_frame_data (objfile);
> +
> +  /* This may also trigger a dwz download.  */
> +  symbol_file_add_separate (debug_bfd, symfile_path.get (),
> +			    current_inferior ()->symfile_flags, objfile);
> +
> +unset:
> +  objfile->remove_deferred_status ();
> +}
> +
> +/* See public.h.  */
> +
> +bool
> +dwarf2_has_separate_index (struct objfile *objfile)
> +{
> +  if (objfile->flags & OBJF_DOWNLOAD_DEFERRED)
> +    return true;
> +  if (objfile->flags & OBJF_MAINLINE)
> +    return false;
> +  if (!IS_DIR_SEPARATOR (*objfile_filename (objfile)))
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> index_path;
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +
> +  if (build_id == nullptr)
> +    return false;
> +
> +  scoped_fd fd = debuginfod_section_query (build_id->data,
> +					   build_id->size,
> +					   bfd_get_filename
> +					     (objfile->obfd.get ()),
> +					   ".gdb_index",
> +					   &index_path);
> +
> +  if (fd.get () < 0)
> +    return false;
> +
> +  /* We found a separate .gdb_index file so a separate debuginfo file
> +     should exist, but we don't want to download it until necessary.
> +     Attach the index to this objfile and defer the debuginfo download
> +     until gdb needs to expand symtabs referenced by the index.  */
> +  objfile->flags |= OBJF_DOWNLOAD_DEFERRED;
> +  dwarf2_initialize_objfile (objfile);
> +  return true;
> +}
> +
>  \f
>  
>  /* Build a partial symbol table.  */
> diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> index 37023a20709..e3131693b81 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -866,6 +866,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
>       CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
>         override final;
>  
> +  struct compunit_symtab *do_find_pc_sect_compunit_symtab
> +    (struct objfile *objfile, struct bound_minimal_symbol msymbol,
> +     CORE_ADDR pc, struct obj_section *section, int warn_if_readin);
> +
>    struct compunit_symtab *find_compunit_symtab_by_address
>      (struct objfile *objfile, CORE_ADDR address) override
>    {
> @@ -942,4 +946,10 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
>  				       dwarf2_section_info *section,
>  				       addrmap *mutable_map);
>  
> +/* If OBJFILE contains information from a separately downloaded .gdb_index,
> +   attempt to download the full debuginfo.  */
> +
> +extern void read_full_dwarf_from_debuginfod (struct objfile *,
> +					     dwarf2_base_index_functions *);
> +
>  #endif /* DWARF2READ_H */
> diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c
> index c9ef41893ee..8cb09e3381a 100644
> --- a/gdb/dwarf2/section.c
> +++ b/gdb/dwarf2/section.c
> @@ -54,7 +54,8 @@ dwarf2_section_info::get_bfd_owner () const
>        section = get_containing_section ();
>        gdb_assert (!section->is_virtual);
>      }
> -  gdb_assert (section->s.section != nullptr);
> +  if (section->s.section == nullptr)
> +    error (_("Can't find owner of DWARF section."));
>    return section->s.section->owner;
>  }
>  
> diff --git a/gdb/elfread.c b/gdb/elfread.c
> index 799e3b914f8..133341ea615 100644
> --- a/gdb/elfread.c
> +++ b/gdb/elfread.c
> @@ -1242,7 +1242,7 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
>  	    symbol_file_add_separate (debug_bfd, debugfile.c_str (),
>  				      symfile_flags, objfile);
>  	}
> -      else
> +      else if (!dwarf2_has_separate_index (objfile))
>  	{
>  	  has_dwarf2 = false;
>  	  const struct bfd_build_id *build_id
> diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h
> index 9dee2ee51a0..fb3f741c899 100644
> --- a/gdb/objfile-flags.h
> +++ b/gdb/objfile-flags.h
> @@ -60,6 +60,10 @@ enum objfile_flag : unsigned
>      /* User requested that we do not read this objfile's symbolic
>         information.  */
>      OBJF_READNEVER = 1 << 6,
> +
> +    /* A separate .gdb_index has been downloaded for this objfile.
> +       Debuginfo for this objfile can be downloaded when required.  */
> +    OBJF_DOWNLOAD_DEFERRED = 1 << 7,
>    };
>  
>  DEF_ENUM_FLAGS_TYPE (enum objfile_flag, objfile_flags);
> diff --git a/gdb/objfiles.h b/gdb/objfiles.h
> index bb7b0a4579d..57bc1d45965 100644
> --- a/gdb/objfiles.h
> +++ b/gdb/objfiles.h
> @@ -613,11 +613,22 @@ struct objfile
>    /* See quick_symbol_functions.  */
>    void require_partial_symbols (bool verbose);
>  
> -  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
> -  void remove_partial_symbol (quick_symbol_functions *target)
> +  /*  Indicate that the aquisition of this objfile's separate debug objfile
> +      is no longer deferred.  Used when the debug objfile has been aquired
> +      or could not be found.  */
> +  void remove_deferred_status ()
>    {
> +    flags &= ~OBJF_DOWNLOAD_DEFERRED;
> +
> +   /* Remove quick_symbol_functions derived from a separately downloaded
> +      index.  If available the separate debug objfile's index will be used
> +      instead, since that objfile actually contains the symbols and CUs
> +      referenced in the index.
> +
> +      No more than one element of qf should have from_separate_index set
> +      to true.  */
>      for (quick_symbol_functions_up &qf_up : qf)
> -      if (qf_up.get () == target)
> +      if (qf_up->from_separate_index)
>  	{
>  	  qf.remove (qf_up);
>  	  return;
> diff --git a/gdb/quick-symbol.h b/gdb/quick-symbol.h
> index a7fea2ccb49..e7163503e39 100644
> --- a/gdb/quick-symbol.h
> +++ b/gdb/quick-symbol.h
> @@ -225,6 +225,10 @@ struct quick_symbol_functions
>    virtual void read_partial_symbols (struct objfile *objfile)
>    {
>    }
> +
> +  /* True if this quick_symbol_functions is derived from a separately
> +     downloaded index.  */
> +  bool from_separate_index = false;
>  };
>  
>  typedef std::unique_ptr<quick_symbol_functions> quick_symbol_functions_up;
> diff --git a/gdb/symfile.c b/gdb/symfile.c
> index 96239679c77..c476196184a 100644
> --- a/gdb/symfile.c
> +++ b/gdb/symfile.c
> @@ -991,6 +991,10 @@ syms_from_objfile (struct objfile *objfile,
>  static void
>  finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
>  {
> +  struct objfile *parent = objfile->separate_debug_objfile_backlink;
> +  bool was_deferred
> +    = (parent != nullptr) && (parent->flags & OBJF_DOWNLOAD_DEFERRED);
> +
>    /* If this is the main symbol file we have to clean up all users of the
>       old main symbol file.  Otherwise it is sufficient to fixup all the
>       breakpoints that may have been redefined by this symbol file.  */
> @@ -1001,7 +1005,8 @@ finish_new_objfile (struct objfile *objfile, symfile_add_flags add_flags)
>  
>        clear_symtab_users (add_flags);
>      }
> -  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0)
> +  else if ((add_flags & SYMFILE_DEFER_BP_RESET) == 0
> +	   && !was_deferred)
>      {
>        breakpoint_re_set ();
>      }
> @@ -1127,6 +1132,12 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name,
>  
>    finish_new_objfile (objfile, add_flags);
>  
> +  /* Remove deferred status now in case any observers trigger symtab
> +     expansion.  Otherwise gdb might try to read parent for psymbols
> +     when it should read the separate debug objfile instead.  */
> +  if (parent != nullptr && (parent->flags & OBJF_DOWNLOAD_DEFERRED))
> +    parent->remove_deferred_status ();
> +
>    gdb::observers::new_objfile.notify (objfile);
>  
>    bfd_cache_close_all ();
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index 5e1b9d91879..2408725fa73 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -2897,14 +2897,30 @@ find_pc_sect_compunit_symtab (CORE_ADDR pc, struct obj_section *section)
>    if (best_cust != NULL)
>      return best_cust;
>  
> +  int warn_if_readin = 1;
> +
>    /* Not found in symtabs, search the "quick" symtabs (e.g. psymtabs).  */
>  
>    for (objfile *objf : current_program_space->objfiles ())
>      {
> +      bool was_deferred = objf->flags & OBJF_DOWNLOAD_DEFERRED;
> +
>        struct compunit_symtab *result
> -	= objf->find_pc_sect_compunit_symtab (msymbol, pc, section, 1);
> +	= objf->find_pc_sect_compunit_symtab (msymbol, pc, section,
> +					      warn_if_readin);
> +
>        if (result != NULL)
>  	return result;
> +
> +      /* If objf's separate debug info was just acquired, disable
> +	 warn_if_readin for the next iteration of this loop.  This prevents
> +	 a spurious warning in case an observer already triggered expansion
> +	 of the separate debug objfile's symtabs.  */
> +      if (was_deferred && objf->separate_debug_objfile != nullptr
> +	  && (objf->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +	warn_if_readin = 0;
> +      else if (warn_if_readin == 0)
> +	warn_if_readin = 1;
>      }
>  
>    return NULL;
> diff --git a/gdb/testsuite/gdb.debuginfod/libsection1.c b/gdb/testsuite/gdb.debuginfod/libsection1.c
> new file mode 100644
> index 00000000000..60824b415c6
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/libsection1.c
> @@ -0,0 +1,40 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 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 <stdio.h>
> +#include <pthread.h>
> +#include <unistd.h>
> +
> +extern void libsection2_test ();
> +extern void *libsection2_thread_test (void *);
> +
> +void
> +libsection1_test ()
> +{
> +  pthread_t thr;
> +
> +  printf ("In libsection1\n");
> +  libsection2_test ();
> +
> +  pthread_create (&thr, NULL, libsection2_thread_test, NULL);
> +
> +  /* Give the new thread a chance to actually enter libsection2_thread_test.  */
> +  sleep (3);
> +  printf ("Cancelling thread\n");
> +
> +  pthread_cancel (thr);
> +}
> diff --git a/gdb/testsuite/gdb.debuginfod/libsection2.c b/gdb/testsuite/gdb.debuginfod/libsection2.c
> new file mode 100644
> index 00000000000..629a67f94a5
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/libsection2.c
> @@ -0,0 +1,37 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 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 <stdio.h>
> +
> +void
> +libsection2_test ()
> +{
> +  printf ("In libsection2\n");
> +}
> +
> +void *
> +libsection2_thread_test (void *arg)
> +{
> +  (void) arg;
> +
> +  printf ("In thread test\n");
> +
> +  while (1)
> +    ;
> +
> +  return NULL;
> +}
> diff --git a/gdb/testsuite/gdb.debuginfod/section.c b/gdb/testsuite/gdb.debuginfod/section.c
> new file mode 100644
> index 00000000000..d391a8f898e
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/section.c
> @@ -0,0 +1,29 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 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 <stdio.h>
> +
> +extern void libsection1_test ();
> +
> +int
> +main()
> +{
> +  libsection1_test ();
> +  printf ("in section exec\n");
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
> new file mode 100644
> index 00000000000..96e9750cd38
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> @@ -0,0 +1,181 @@
> +# Copyright 2023 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 debuginfod functionality
> +
> +standard_testfile
> +
> +load_lib debuginfod-support.exp
> +
> +require allow_debuginfod_tests
> +
> +set sourcetmp [standard_output_file tmp-${srcfile}]
> +set outputdir [standard_output_file {}]
> +
> +# SECTEXEC is an executable which calls a function from LIB_SL1.
> +set sectfile "section"
> +set sectsrc $srcdir/$subdir/section.c
> +set sectexec [standard_output_file $sectfile]
> +
> +# Solib LIB_SL1 calls functions from LIB_SL2.
> +set libfile1 "libsection1"
> +set libsrc1 $srcdir/$subdir/$libfile1.c
> +set lib_sl1 [standard_output_file $libfile1.sl]
> +
> +set libfile2 "libsection2"
> +set libsrc2 $srcdir/$subdir/$libfile2.c
> +set lib_sl2 [standard_output_file $libfile2.sl]
> +
> +set lib_opts1 [list debug build-id shlib=$lib_sl2]
> +set lib_opts2 [list debug build-id]
> +set exec_opts [list debug build-id shlib=$lib_sl1 shlib=$lib_sl2]
> +
> +clean_restart
> +
> +if {[enable_section_downloads] == 0} {
> +    untested "GDB does not support debuginfod section downloads"
> +    return -1
> +}
> +
> +# Compile SECTEXEC, LIB_SL1 and LIB_SL2.
> +if { [gdb_compile_shlib $libsrc2 $lib_sl2 $lib_opts2] != "" } {
> +    untested "failed to compile $libfile2"
> +    return -1
> +}
> +
> +if { [gdb_compile_shlib_pthreads $libsrc1 $lib_sl1 $lib_opts1] != "" } {
> +    untested "failed to compile $libfile1"
> +    return -1
> +}
> +
> +if { [gdb_compile $sectsrc $sectexec executable $exec_opts] != "" } {
> +    untested "failed to compile $sectfile"
> +    return -1
> +}
> +
> +# Add .gdb_index to solibs.
> +if { [have_index $lib_sl1] != "gdb_index"
> +     && [add_gdb_index $lib_sl1] == 0 } {
> +    untested "failed to add .gdb_index to $libfile1"
> +    return -1
> +}
> +
> +if { [have_index $lib_sl2] != "gdb_index"
> +     && [add_gdb_index $lib_sl2] == 0 } {
> +    untested "failed to add .gdb_index to $libfile2"
> +    return -1
> +}
> +
> +# Strip solib debuginfo into separate files.
> +if { [gdb_gnu_strip_debug $lib_sl1 ""] != 0} {
> +   fail "strip $lib_sl1 debuginfo"
> +   return -1
> +}
> +
> +if { [gdb_gnu_strip_debug $lib_sl2 ""] != 0} {
> +   fail "strip $lib_sl2 debuginfo"
> +   return -1
> +}
> +
> +# Move debuginfo files into directory that debuginfod will serve from.
> +set debugdir [standard_output_file "debug"]
> +set debuginfo_sl1 [standard_output_file $libfile1.sl.debug]
> +set debuginfo_sl2 [standard_output_file $libfile2.sl.debug]
> +
> +file mkdir $debugdir
> +file rename -force $debuginfo_sl1 $debugdir
> +file rename -force $debuginfo_sl2 $debugdir
> +
> +# Restart GDB and clear the debuginfod client cache. Then load BINFILE into
> +# GDB and start running it.  Match output with pattern RES and use TESTNAME
> +# as the test name.
> +proc_with_prefix clean_restart_with_prompt { binfile res testname } {
> +    global cache
> +
> +    clean_restart
> +
> +    # Delete client cache so debuginfo downloads again.
> +    file delete -force $cache
> +
> +    gdb_test "file $binfile" "" "file [file tail $binfile] file $testname"
> +    gdb_test "start" $res "file [file tail $binfile] start $testname" \
> +	     ".*Enable debuginfod.*" "y"

The use of [file tail ....] in all the test names is pretty confusing,
not really GDB style, and I don't think is necessary.  This proc is
already defined as 'proc_with_prefix' as are most of the tests in this
script, so you can probably just give these tests descriptive names
e.g. 'load executable', 'start inferior', which are much easier to
search for when trying to find a failing test.  This issue crops up
throughout this script.

However, in this case, I think we have a bigger problem.  If you try to
run this test as:

  make check-gdb TESTS="gdb.debuginfod/section.exp" RUNTESTFLAGS="--target_board=native-gdbserver"

You'll find the test fails -- we can't use 'start' when testing with the
remote protocol (as the native-gdbserver board does).

We usually avoid using 'start' directly, and instead use runto_main.  In
this case I see you're checking for the enable debuginfod message and
enabling debuginfod.  I think I'd just split this test in to two
scripts, have one script which tests 'start' and ensures that you see
the 'enable debuginfod' message, that script would focus on just testing
that one feature, and would be skipped when using remote protocol (I
think just calling [use_gdb_stub] is good enough for that).  Then back
in this test, you can just specifically enable debuginfod before calling
runto_main, which will avoid the additional prompt (I think).

We also need to avoid calling 'file' directly too.  For some targets
there's additional work required when loading a file, so for that we
have gdb_file_cmd proc.

> +}
> +
> +# Tests with no debuginfod server running.
> +proc_with_prefix no_url { } {
> +    global sectexec libfile1 libfile2
> +
> +    # Check that no section is downloaded and no debuginfo is found.
> +    gdb_test "file $sectexec" "" "file [file tail $sectexec] file no url"
> +    gdb_test "start" "" "file [file tail $sectexec] start no url"

There's a couple of issues here.  First there's the problem outlined
above that we shouldn't call file/start directly... though maybe that's
unavoidable for this test?

However, the comment says 'check that no section is downloaded', are you
expecting the use of "" here to enforce that?  Because all that "" does
is check that the command returns a prompt, anything before the prompt
will be ignored, so if something was downloaded, this test isn't going
to fail.

But maybe the next part....

> +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile1.*" \
> +	     "file [file tail $libfile1] found no url"
> +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile2.*" \
> +	     "file [file tail $libfile2] found no url"

... is what confirms that nothing was downloaded?  It's not immediately
obvious what these two commands are actually checking for, so I think
these would for sure be worth a comment.

If these commands are what performs the check then we should be able to
replace the earlier two calls with gdb_file_cmd and runto_main maybe?

> +}
> +
> +# Tests with a debuginfod server running.
> +proc_with_prefix local_url { } {
> +    global sectexec
> +    global libsrc1 lib_sl1 libfile1
> +    global libsrc2 lib_sl2 libfile2
> +    global debugdir db
> +
> +    set url [start_debuginfod $db $debugdir]
> +    if { $url == "" } {
> +	unresolved "failed to start debuginfod server"
> +	return
> +    }
> +
> +    # Point GDB to the server.
> +    setenv DEBUGINFOD_URLS $url
> +
> +    # Download .gdb_index for solibs.
> +    set res ".*section \.gdb_index for $lib_sl1.*\
> +	section \.gdb_index for $lib_sl2.*"
> +    clean_restart_with_prompt $sectexec $res "index"
> +
> +    # Download debuginfo when stepping into a function.
> +    set res ".*separate debug info for $lib_sl1.*\"In ${libfile1}\\\\n\".*"
> +    gdb_test "step" $res "file [file tail $lib_sl1] step"
> +
> +    clean_restart_with_prompt $sectexec "" "break"
> +
> +    # Download debuginfo when setting a breakpoint.
> +    set res "Download.*separate debug info for $lib_sl2.*"
> +    gdb_test "br libsection2_test" $res "file [file tail $sectexec] break set"
> +
> +    # Hit the breakpoint.
> +    set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
> +    gdb_test "c" $res "file [file tail $sectexec] break continue"
> +
> +    # Check that download progress message is correctly formatted
> +    # during backtrace.
> +    set res ".* separate debug info for $lib_sl1.*#0  libsection2_test\
> +	\\(\\) at.*"
> +    set res "Download.*debug info.*$lib_sl1.*#0  libsection2_test \\(\\) at.*"
> +    gdb_test "bt" $res "file [file tail $sectexec] break backtrace"
> +}
> +
> +# Create CACHE and DB directories ready for debuginfod to use.
> +prepare_for_debuginfod cache db
> +
> +with_debuginfod_env $cache {
> +    no_url
> +    local_url
> +}
> +
> +stop_debuginfod
> diff --git a/gdb/testsuite/lib/debuginfod-support.exp b/gdb/testsuite/lib/debuginfod-support.exp
> index 50a8b512a4a..6368c27e9d0 100644
> --- a/gdb/testsuite/lib/debuginfod-support.exp
> +++ b/gdb/testsuite/lib/debuginfod-support.exp
> @@ -194,3 +194,33 @@ proc stop_debuginfod { } {
>  	unset debuginfod_spawn_id
>      }
>  }
> +
> +# Return 1 if gdb is configured to download ELF/DWARF sections from
> +# debuginfod servers.  Otherwise return 0.
> +proc enable_section_downloads { } {
> +    global gdb_prompt
> +
> +    set cmd "maint set debuginfod download-sections on"
> +    set msg "enable section downloads"
> +
> +    gdb_test_multiple $cmd $msg {
> +	-re ".*Undefined maintenance.*" {
> +	    perror "Undefined command: \"$cmd\""
> +	    return 0
> +	}

Drop this pattern, gdb_test_multiple already protects against undefined
commands.

> +	-re ".*not compiled into GDB.*" {
> +	    perror "Unsupported command: \"$cmd\""
> +	    return 0
> +	}

I don't think a perror call is needed here.  I really don't see this as
an error condition, and the user of this proc should call unsupported if
appropriate.

> +	-re "\r\n${gdb_prompt} $" {
> +	    return 1
> +	}

You should probably use '-re -wrap' for each of these patterns, the
-wrap will add the gdb_prompt pattern just like gdb_test does.
Obviously, for the last one we don't need two prompts, so:

  -re -wrap "" {
    return 1
  }

will be just fine.

> +	timeout {
> +	    perror "timeout: \"$cmd\""
> +	    return 0
> +	}

Again, timeout is handled by gdb_test_multiple, so just let that handle
this case.

> +    }
> +
> +    perror "Unexpected output for \"$cmd\""
> +    return 0

I don't think you'll ever hit this case, the final pattern, which just
matches the prompt will match against anything.  So, I'd probably update
the final pattern to:

  -re -wrap "^" {
    return 1
  }

The '^' anchors the match to the start of the output buffer and -wrap
appends the prompt, so this ensures you only pass if the command
completes with no output.

Thanks,
Andrew


> +}
> -- 
> 2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 1/6 v2] gdb/debuginfod: Add debuginfod_section_query
  2023-06-07 13:35   ` Andrew Burgess
@ 2023-07-27 11:04     ` Andrew Burgess
  0 siblings, 0 replies; 33+ messages in thread
From: Andrew Burgess @ 2023-07-27 11:04 UTC (permalink / raw)
  To: Aaron Merey, gdb-patches; +Cc: Aaron Merey

Andrew Burgess <aburgess@redhat.com> writes:

> Aaron Merey <amerey@redhat.com> writes:
>
>> v1: https://sourceware.org/pipermail/gdb-patches/2023-February/197455.html
>>
>> v2 improves the description of AC_DEBUGINFOD_SECTION and associated
>> macro names.  An if-statement in debuginfod_section_query is also
>> replaced with a gdb_assert.
>>
>> Commit message:
>>
>> Add new function debuginfod_section_query.  This function queries
>> debuginfod servers for an individual ELF/DWARF section associated with
>> a given build-id.
>>
>> Also check for libdebuginfod version >= 0.188 at configure time.
>> debuginfod_section_query simply returns -ENOSYS if this condition
>> is not met.
>
> This LGTM.
>
> I don't think this should be merged until there's a use for the new GDB
> functions (when later patches are also approved).
>
> Approved-By: Andrew Burgess <aburgess@redhat.com>

Actually, on thinking about this further, I'd like to withdraw this
approval, and ask a question / make a suggestion:

Why do we need AC_DEBUGINFOD and AC_DEBUGINFOD_SECTION?  Couldn't you
just replace AC_DEBUGINFOD with AC_DEBUGINFOD_SECTION?  The SECTION
version checks for 0.179 and 0.188 and sets the various flags based on
whatever is found.

You would need to update binutils by running autoreconf, which would set
the new define, but binutils would just ignore that define and all would
be good.

I think I would split this first patch in two, the first part would
update the autoconf macro and then run autoreconf in binutils and gdb.
Cross post this to both mailing lists, and get this merged.

Then rebase this GDB series on top of that, and now GDB can just start
making use of the new #define.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections'
  2023-06-01  1:43 ` [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections' Aaron Merey
  2023-06-01  6:13   ` Eli Zaretskii
  2023-06-07 13:57   ` Andrew Burgess
@ 2023-07-27 12:04   ` Andrew Burgess
  2023-07-27 12:19   ` Andrew Burgess
  3 siblings, 0 replies; 33+ messages in thread
From: Andrew Burgess @ 2023-07-27 12:04 UTC (permalink / raw)
  To: Aaron Merey, gdb-patches; +Cc: Aaron Merey

Aaron Merey <amerey@redhat.com> writes:

> v1: https://sourceware.org/pipermail/gdb-patches/2023-February/197461.html
>
> v2 removes 'set debuginfod enabled lazy' and replaces it with
> 'maint set debuginfod download-sections'.
>
> Commit message:
>
> This setting controls whether GDB may attempt to download ELF/DWARF
> sections from debuginfod servers.
>
> This setting is enabled by default if gdb is built with debuginfod
> section download support (requires libdebuginfod 0.188).
>
> Also update debuginfod command help text and gdb.texinfo with
> information on section downloading and the new command.
> ---
>  gdb/debuginfod-support.c | 66 ++++++++++++++++++++++++++++++++++++++--
>  gdb/doc/gdb.texinfo      | 16 ++++++++--
>  2 files changed, 77 insertions(+), 5 deletions(-)
>
> diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
> index 8be43a91dcc..6d0521b64e2 100644
> --- a/gdb/debuginfod-support.c
> +++ b/gdb/debuginfod-support.c
> @@ -31,6 +31,10 @@
>  static cmd_list_element *set_debuginfod_prefix_list;
>  static cmd_list_element *show_debuginfod_prefix_list;
>  
> +/* maint set/show debuginfod commands.  */
> +static cmd_list_element *maint_set_debuginfod_cmdlist;
> +static cmd_list_element *maint_show_debuginfod_cmdlist;
> +
>  static const char debuginfod_on[] = "on";
>  static const char debuginfod_off[] = "off";
>  static const char debuginfod_ask[] = "ask";
> @@ -50,6 +54,13 @@ static const char *debuginfod_enabled =
>    debuginfod_off;
>  #endif
>  
> +static bool debuginfod_download_sections =
> +#if defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
> +  true;
> +#else
> +  false;
> +#endif
> +
>  static unsigned int debuginfod_verbose = 1;
>  
>  #ifndef HAVE_LIBDEBUGINFOD
> @@ -424,7 +435,7 @@ debuginfod_section_query (const unsigned char *build_id,
>    return scoped_fd (-ENOSYS);
>  #else
>  
> - if (!debuginfod_is_enabled ())
> + if (!debuginfod_download_sections || !debuginfod_is_enabled ())
>      return scoped_fd (-ENOSYS);
>  
>    debuginfod_client *c = get_debuginfod_client ();
> @@ -550,6 +561,31 @@ show_debuginfod_verbose_command (ui_file *file, int from_tty,
>  	      value);
>  }
>  
> +/* Set callback for "maint set debuginfod download-sections".  */
> +
> +static void
> +maint_set_debuginfod_download_sections (bool value)
> +{
> +#if !defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
> +  if (value)
> +    {
> +      error (_("Support for section downloading is not compiled into GDB. " \
> +"Defaulting to \"off\"."));
> +      return;
> +    }
> +#endif
> +
> +  debuginfod_download_sections = value;
> +}
> +
> +/* Get callback for "maint set debuginfod download-sections".  */
> +
> +static bool
> +maint_get_debuginfod_download_sections ()
> +{
> +  return debuginfod_download_sections;
> +}
> +
>  /* Register debuginfod commands.  */
>  
>  void _initialize_debuginfod ();
> @@ -568,8 +604,11 @@ _initialize_debuginfod ()
>  			_("Set whether to use debuginfod."),
>  			_("Show whether to use debuginfod."),
>  			_("\
> -When on, enable the use of debuginfod to download missing debug info and\n\
> -source files."),
> +When set to \"on\", enable the use of debuginfod to download missing\n\
> +debug info and source files. GDB may also download components of debug\n\
> +info instead of entire files. \"off\" disables the use of debuginfod.\n\
> +When set to \"ask\", a prompt may ask whether to enable or disable\n\
> +debuginfod." ),
>  			set_debuginfod_enabled,
>  			get_debuginfod_enabled,
>  			show_debuginfod_enabled,
> @@ -600,4 +639,25 @@ query.\nTo disable, set to zero.  Verbose output is displayed by default."),
>  			     show_debuginfod_verbose_command,
>  			     &set_debuginfod_prefix_list,
>  			     &show_debuginfod_prefix_list);
> +
> +  /* maint set/show debuginfod */
> +  add_setshow_prefix_cmd ("debuginfod", class_maintenance,
> +			  _("Set debuginfod specific variables."),
> +			  _("Show debuginfod specific variables."),
> +			  &maint_set_debuginfod_cmdlist,
> +			  &maint_show_debuginfod_cmdlist,
> +			  &maintenance_set_cmdlist, &maintenance_show_cmdlist);
> +
> +  /* maint set/show debuginfod download-sections */
> +  add_setshow_boolean_cmd ("download-sections", class_maintenance, _("\
> +Set whether debuginfod may download individual ELF/DWARF sections."), _("\
> +Show whether debuginfod may download individual ELF/DWARF sections."), _("\
> +When enabled, debuginfod may attempt to download individual ELF/DWARF \
> +sections from debug info files.\nIf disabled, only whole debug info files \
> +may be downloaded."),
> +			  maint_set_debuginfod_download_sections,
> +			  maint_get_debuginfod_download_sections,
> +			  nullptr,
> +			  &maint_set_debuginfod_cmdlist,
> +			  &maint_show_debuginfod_cmdlist);
>  }
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index fc55c4e7b43..91da9777fd6 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -40872,6 +40872,16 @@ Create a core file? (y or n) @kbd{n}
>  (@value{GDBP})
>  @end smallexample
>  
> +@kindex maint set debuginfod
> +@kindex maint show debuginfod

I think these should be:

  @kindex maint set debuginfod download-sections
  @kindex maint show debuginfod download-sections

Other places in the manual where we have multi "level" commands like
this, all levels seem to be included in the @kindex entry.

Thanks,
Andrew


> +@item maint set debuginfod download-sections
> +@itemx maint set debuginfod download-sections @r{[}on|off@r{]}
> +@itemx maint show debuginfod download-sections
> +Controls whether @value{GDBN} will attempt to download individual
> +ELF/DWARF sections from @code{debuginfod}.  If disabled, only
> +whole debug info files will be downloaded.  This could result
> +in @value{GDBN} downloading larger amounts of data.
> +
>  @cindex @value{GDBN} internal error
>  @cindex internal errors, control of @value{GDBN} behavior
>  @cindex demangler crashes
> @@ -49382,8 +49392,10 @@ regarding @code{debuginfod}.
>  @item set debuginfod enabled
>  @itemx set debuginfod enabled on
>  @cindex enable debuginfod
> -@value{GDBN} will attempt to query @code{debuginfod} servers when missing debug
> -info or source files.
> +@value{GDBN} will attempt to query @code{debuginfod} servers for missing debug
> +info or source files.  @value{GDBN} may attempt to download individual ELF/DWARF
> +sections such as @code{.gdb_index} to help reduce the total amount of data
> +downloaded from debuginfod servers.
>  
>  @item set debuginfod enabled off
>  @value{GDBN} will not attempt to query @code{debuginfod} servers when missing
> -- 
> 2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections'
  2023-06-01  1:43 ` [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections' Aaron Merey
                     ` (2 preceding siblings ...)
  2023-07-27 12:04   ` Andrew Burgess
@ 2023-07-27 12:19   ` Andrew Burgess
  3 siblings, 0 replies; 33+ messages in thread
From: Andrew Burgess @ 2023-07-27 12:19 UTC (permalink / raw)
  To: Aaron Merey, gdb-patches; +Cc: Aaron Merey

Aaron Merey <amerey@redhat.com> writes:

> v1: https://sourceware.org/pipermail/gdb-patches/2023-February/197461.html
>
> v2 removes 'set debuginfod enabled lazy' and replaces it with
> 'maint set debuginfod download-sections'.
>
> Commit message:
>
> This setting controls whether GDB may attempt to download ELF/DWARF
> sections from debuginfod servers.
>
> This setting is enabled by default if gdb is built with debuginfod
> section download support (requires libdebuginfod 0.188).
>
> Also update debuginfod command help text and gdb.texinfo with
> information on section downloading and the new command.
> ---
>  gdb/debuginfod-support.c | 66 ++++++++++++++++++++++++++++++++++++++--
>  gdb/doc/gdb.texinfo      | 16 ++++++++--
>  2 files changed, 77 insertions(+), 5 deletions(-)
>
> diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
> index 8be43a91dcc..6d0521b64e2 100644
> --- a/gdb/debuginfod-support.c
> +++ b/gdb/debuginfod-support.c
> @@ -31,6 +31,10 @@
>  static cmd_list_element *set_debuginfod_prefix_list;
>  static cmd_list_element *show_debuginfod_prefix_list;
>  
> +/* maint set/show debuginfod commands.  */
> +static cmd_list_element *maint_set_debuginfod_cmdlist;
> +static cmd_list_element *maint_show_debuginfod_cmdlist;
> +
>  static const char debuginfod_on[] = "on";
>  static const char debuginfod_off[] = "off";
>  static const char debuginfod_ask[] = "ask";
> @@ -50,6 +54,13 @@ static const char *debuginfod_enabled =
>    debuginfod_off;
>  #endif
>  
> +static bool debuginfod_download_sections =
> +#if defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
> +  true;
> +#else
> +  false;
> +#endif
> +
>  static unsigned int debuginfod_verbose = 1;
>  
>  #ifndef HAVE_LIBDEBUGINFOD
> @@ -424,7 +435,7 @@ debuginfod_section_query (const unsigned char *build_id,
>    return scoped_fd (-ENOSYS);
>  #else
>  
> - if (!debuginfod_is_enabled ())
> + if (!debuginfod_download_sections || !debuginfod_is_enabled ())
>      return scoped_fd (-ENOSYS);
>  
>    debuginfod_client *c = get_debuginfod_client ();
> @@ -550,6 +561,31 @@ show_debuginfod_verbose_command (ui_file *file, int from_tty,
>  	      value);
>  }
>  
> +/* Set callback for "maint set debuginfod download-sections".  */
> +
> +static void
> +maint_set_debuginfod_download_sections (bool value)
> +{
> +#if !defined(HAVE_LIBDEBUGINFOD_FIND_SECTION)
> +  if (value)
> +    {
> +      error (_("Support for section downloading is not compiled into GDB. " \
> +"Defaulting to \"off\"."));
> +      return;
> +    }
> +#endif
> +
> +  debuginfod_download_sections = value;
> +}
> +
> +/* Get callback for "maint set debuginfod download-sections".  */
> +
> +static bool
> +maint_get_debuginfod_download_sections ()
> +{
> +  return debuginfod_download_sections;
> +}
> +
>  /* Register debuginfod commands.  */
>  
>  void _initialize_debuginfod ();
> @@ -568,8 +604,11 @@ _initialize_debuginfod ()
>  			_("Set whether to use debuginfod."),
>  			_("Show whether to use debuginfod."),
>  			_("\
> -When on, enable the use of debuginfod to download missing debug info and\n\
> -source files."),
> +When set to \"on\", enable the use of debuginfod to download missing\n\
> +debug info and source files. GDB may also download components of debug\n\
> +info instead of entire files. \"off\" disables the use of debuginfod.\n\
> +When set to \"ask\", a prompt may ask whether to enable or disable\n\
> +debuginfod." ),
>  			set_debuginfod_enabled,
>  			get_debuginfod_enabled,
>  			show_debuginfod_enabled,
> @@ -600,4 +639,25 @@ query.\nTo disable, set to zero.  Verbose output is displayed by default."),
>  			     show_debuginfod_verbose_command,
>  			     &set_debuginfod_prefix_list,
>  			     &show_debuginfod_prefix_list);
> +
> +  /* maint set/show debuginfod */
> +  add_setshow_prefix_cmd ("debuginfod", class_maintenance,
> +			  _("Set debuginfod specific variables."),
> +			  _("Show debuginfod specific variables."),
> +			  &maint_set_debuginfod_cmdlist,
> +			  &maint_show_debuginfod_cmdlist,
> +			  &maintenance_set_cmdlist, &maintenance_show_cmdlist);
> +
> +  /* maint set/show debuginfod download-sections */
> +  add_setshow_boolean_cmd ("download-sections", class_maintenance, _("\
> +Set whether debuginfod may download individual ELF/DWARF sections."), _("\
> +Show whether debuginfod may download individual ELF/DWARF sections."), _("\
> +When enabled, debuginfod may attempt to download individual ELF/DWARF \
> +sections from debug info files.\nIf disabled, only whole debug info files \
> +may be downloaded."),
> +			  maint_set_debuginfod_download_sections,
> +			  maint_get_debuginfod_download_sections,
> +			  nullptr,
> +			  &maint_set_debuginfod_cmdlist,
> +			  &maint_show_debuginfod_cmdlist);
>  }
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index fc55c4e7b43..91da9777fd6 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -40872,6 +40872,16 @@ Create a core file? (y or n) @kbd{n}
>  (@value{GDBP})
>  @end smallexample
>  
> +@kindex maint set debuginfod
> +@kindex maint show debuginfod

We should probably add something like:

 @cindex debuginfod, maintenance commands

to help folk find this.

Thanks,
Andrew

> +@item maint set debuginfod download-sections
> +@itemx maint set debuginfod download-sections @r{[}on|off@r{]}
> +@itemx maint show debuginfod download-sections
> +Controls whether @value{GDBN} will attempt to download individual
> +ELF/DWARF sections from @code{debuginfod}.  If disabled, only
> +whole debug info files will be downloaded.  This could result
> +in @value{GDBN} downloading larger amounts of data.
> +
>  @cindex @value{GDBN} internal error
>  @cindex internal errors, control of @value{GDBN} behavior
>  @cindex demangler crashes
> @@ -49382,8 +49392,10 @@ regarding @code{debuginfod}.
>  @item set debuginfod enabled
>  @itemx set debuginfod enabled on
>  @cindex enable debuginfod
> -@value{GDBN} will attempt to query @code{debuginfod} servers when missing debug
> -info or source files.
> +@value{GDBN} will attempt to query @code{debuginfod} servers for missing debug
> +info or source files.  @value{GDBN} may attempt to download individual ELF/DWARF
> +sections such as @code{.gdb_index} to help reduce the total amount of data
> +downloaded from debuginfod servers.
>  
>  @item set debuginfod enabled off
>  @value{GDBN} will not attempt to query @code{debuginfod} servers when missing
> -- 
> 2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator
  2023-06-01  1:43 ` [PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator Aaron Merey
  2023-06-15 13:44   ` Aaron Merey
@ 2023-07-31 10:11   ` Andrew Burgess
  1 sibling, 0 replies; 33+ messages in thread
From: Andrew Burgess @ 2023-07-31 10:11 UTC (permalink / raw)
  To: Aaron Merey, gdb-patches; +Cc: Aaron Merey

Aaron Merey <amerey@redhat.com> writes:

> To facilitate the deletion of objfiles, progspace objects use a safe
> iterator that holds a reference to the next objfile in the progspace's
> objfile_list.  This allows objfiles to be deleted in a loop without
> invalidating the loop's iterator.  progspace also uses an unwrapping
> iterator over std::unique_ptr<objfile> that automatically deferences
> the unique_ptr.
>
> This patch changes the objfile safe iterator to be a reverse safe
> iterator.  It changes the unwrapping iterator into a template.  It
> also modifies objfile_list insertion so that separate debug objfiles
> are placed into the list after the parent objfile, instead of before.
>
> These changes are intended to prepare gdb for on-demand debuginfo
> downloading and the downloading of .gdb_index sections.
>
> With on-demand downloading enabled, gdb might download a debuginfo
> file during symtab expansion.  In this case an objfile could be added
> to an objfiles_list during iteration over the list (for example, in
> iterate_over_symtabs).  We want these loops to also iterate over newly
> downloaded objfiles. So objfiles need to be inserted into objfiles_list
> after their parent since it is during the search of the parent
> objfile for some symbol or filename that the separate debug objfile
> might be downloaded.
>
> The unwrapping iterator is changed into a template in order to
> use it with objfile qf_require_partial_symbols, which is now also
> uses with a safe iterator.  This is because after a separate debug
> objfile is downloaded on-demand, we want to remove any .gdb_index
> quick_symbol_functions from the parent objfile during iteration over
> the parent's quick_symbol_functions.  The newly downloaded separate
> debug objfile contains the index and all of the related symbols
> so the .gdb_index should not be associated with the parent objfile
> any longer.

This doesn't really explain why we need to use the unwrapping iterator
though, couldn't we just use basic_safe_range instead?  This would
remove the need for some of the updates in symfile-debug.c.

>
> Finally a safe reverse iterator is now used during progspace objfile
> deletion in order to prevent iterator invalidation during the loop
> in which objfiles are deleted.  This could happen during forward
> iteration over objfiles_list when a separate debug objfile immediately
> follows it's parent objfile in the list (which is now possible since
> objfiles are inserted into the list after their parent).  Deletion
> of the parent would cause deletion of the separate debug objfile,
> which would invalidate the safe forward iterator's reference to the
> next objfile in the list.  A safe reverse iterator deletes separate
> debug objfiles before their parent, so the iterator's reference to
> the next objfile always stays valid.
>
> A small change was also made to a testcase in py-objfile.exp to
> account for the new placement of separate debug objfiles in
> objfiles_list.
> ---
>  gdb/objfiles.h                          |  22 +++-
>  gdb/progspace.c                         |   8 +-
>  gdb/progspace.h                         | 159 +++++++++++++++++++-----
>  gdb/symfile-debug.c                     | 136 ++++++++++----------
>  gdb/testsuite/gdb.python/py-objfile.exp |   2 +-
>  5 files changed, 218 insertions(+), 109 deletions(-)
>
> diff --git a/gdb/objfiles.h b/gdb/objfiles.h
> index 189856f0a51..bb7b0a4579d 100644
> --- a/gdb/objfiles.h
> +++ b/gdb/objfiles.h
> @@ -613,6 +613,17 @@ struct objfile
>    /* See quick_symbol_functions.  */
>    void require_partial_symbols (bool verbose);
>  
> +  /* Remove TARGET from this objfile's collection of quick_symbol_functions.  */
> +  void remove_partial_symbol (quick_symbol_functions *target)
> +  {
> +    for (quick_symbol_functions_up &qf_up : qf)
> +      if (qf_up.get () == target)
> +	{
> +	  qf.remove (qf_up);
> +	  return;
> +	}

I think the more C++ way of writing this would be:

  qf.remove_if ([&] (const quick_symbol_functions_up &qf_up)
                {
                  return qf_up.get () == target;
                });

The remove function already walks the whole of `qf` to remove every item
matching qf_up, so the early return isn't actually saving anything.

> +  }
> +
>    /* Return the relocation offset applied to SECTION.  */
>    CORE_ADDR section_offset (bfd_section *section) const
>    {
> @@ -699,13 +710,20 @@ struct objfile
>  
>  private:
>  
> +  using qf_list = std::forward_list<quick_symbol_functions_up>;
> +  using unwrapping_qf_range = iterator_range<unwrapping_iterator<qf_list::iterator>>;
> +  using qf_safe_range = basic_safe_range<unwrapping_qf_range>;
> +
>    /* Ensure that partial symbols have been read and return the "quick" (aka
>       partial) symbol functions for this symbol reader.  */
> -  const std::forward_list<quick_symbol_functions_up> &
> +  qf_safe_range
>    qf_require_partial_symbols ()
>    {
>      this->require_partial_symbols (true);
> -    return qf;
> +    return qf_safe_range
> +      (unwrapping_qf_range
> +	(unwrapping_iterator<qf_list::iterator> (qf.begin ()),
> +	 unwrapping_iterator<qf_list::iterator> (qf.end ())));
>    }
>  
>  public:
> diff --git a/gdb/progspace.c b/gdb/progspace.c
> index 32bdfebcf7c..1ed75eef2f9 100644
> --- a/gdb/progspace.c
> +++ b/gdb/progspace.c
> @@ -139,19 +139,19 @@ program_space::free_all_objfiles ()
>  
>  void
>  program_space::add_objfile (std::unique_ptr<objfile> &&objfile,
> -			    struct objfile *before)
> +			    struct objfile *after)
>  {
> -  if (before == nullptr)
> +  if (after == nullptr)
>      objfiles_list.push_back (std::move (objfile));
>    else
>      {
>        auto iter = std::find_if (objfiles_list.begin (), objfiles_list.end (),
>  				[=] (const std::unique_ptr<::objfile> &objf)
>  				{
> -				  return objf.get () == before;
> +				  return objf.get () == after;
>  				});
>        gdb_assert (iter != objfiles_list.end ());
> -      objfiles_list.insert (iter, std::move (objfile));
> +      objfiles_list.insert (++iter, std::move (objfile));
>      }
>  }
>  
> diff --git a/gdb/progspace.h b/gdb/progspace.h
> index 85215f0e2f1..6e33e48c88e 100644
> --- a/gdb/progspace.h
> +++ b/gdb/progspace.h
> @@ -40,56 +40,141 @@ struct address_space;
>  struct program_space;
>  struct so_list;
>  
> +/* An iterator that wraps an iterator over std::unique_ptr, and dereferences
> +   the returned object.  This is useful for iterating over a list of shared
> +   pointers and returning raw pointers -- which helped avoid touching a lot
> +   of code when changing how objfiles are managed.  */
> +
> +template<typename UniquePtrIter>
> +class unwrapping_iterator

If my comment above about qf using basic_safe_range is correct, then
this change might not be needed.

> +{
> +public:
> +  typedef unwrapping_iterator self_type;
> +  typedef typename UniquePtrIter::value_type::pointer value_type;
> +  typedef typename UniquePtrIter::reference  reference;
> +  typedef typename UniquePtrIter::pointer pointer;
> +  typedef typename UniquePtrIter::iterator_category iterator_category;
> +  typedef typename UniquePtrIter::difference_type difference_type;
> +
> +  unwrapping_iterator (UniquePtrIter iter)
> +    : m_iter (std::move (iter))
> +  {
> +  }
> +
> +  value_type operator* () const
> +  {
> +    return m_iter->get ();
> +  }
> +
> +  unwrapping_iterator operator++ ()
> +  {
> +    ++m_iter;
> +    return *this;
> +  }
> +
> +  bool operator!= (const unwrapping_iterator &other) const
> +  {
> +    return m_iter != other.m_iter;
> +  }
> +
> +private:
> +  /* The underlying iterator.  */
> +  UniquePtrIter m_iter;
> +};
> +
>  typedef std::list<std::unique_ptr<objfile>> objfile_list;
>  
> -/* An iterator that wraps an iterator over std::unique_ptr<objfile>,
> -   and dereferences the returned object.  This is useful for iterating
> -   over a list of shared pointers and returning raw pointers -- which
> -   helped avoid touching a lot of code when changing how objfiles are
> -   managed.  */
> +/* An reverse iterator that wraps an iterator over objfile_list, and
> +   dereferences the returned object.  This is useful for reverse iterating
> +   over a list of shared pointers and returning raw pointers -- which helped
> +   avoid touching a lot of code when changing how objfiles are managed.  */
>  
> -class unwrapping_objfile_iterator
> +class unwrapping_reverse_objfile_iterator

I see what you did here, but I wonder if this is the right approach.

Rather than using basic_safe_range, maybe we should instead create
basic_safe_reverse_range, which would imply creating a
basic_safe_reverse_iterator.  The basic_safe_reverse_range would take a
"normal" forward range, but walk over it backwards.

I also think it would be super useful to have some comments about why we
can't just use the standard reverse iterators here, my initial thought
was that this should be possible, and it was only once I tried it out
that I discovered what the problems were -- I guess you already ran into
those issues, but neither the commit message, nor any comments really
explain why _this_ implementation.

Additionally, I question the need to use an unwrapping iterator here.
The comment (clearly copy & paste) says "which helped avoid touching a
lot of code when changing how objfiles are managed.", however, for this
reverse case, as far as I can tell, there's only two users, so, I'd be
tempted to say we should just update those two users to make use of
iterators...

... but both the users of progspace::objfiles_safe() actually just do:

  for (objfile *objf : current_program_space->objfiles_safe ())
    {
      if (predicate (objf))
	objf->unlink ();
    }

So I wonder if a better solution would be to offer:

  void
  progspace::unlink_objfiles_if (gdb::function_view<bool (const objfile *objfile)> predicate);

which would mean that the callers can focus on the logic of _what_ to
unlink, while progspace.c can focus on the mechanism of _how_ to unlink?

Thanks,
Andrew

>  {
>  public:
> -
> -  typedef unwrapping_objfile_iterator self_type;
> +  typedef unwrapping_reverse_objfile_iterator self_type;
>    typedef typename ::objfile *value_type;
>    typedef typename ::objfile &reference;
>    typedef typename ::objfile **pointer;
>    typedef typename objfile_list::iterator::iterator_category iterator_category;
>    typedef typename objfile_list::iterator::difference_type difference_type;
>  
> -  unwrapping_objfile_iterator (objfile_list::iterator iter)
> -    : m_iter (std::move (iter))
> -  {
> -  }
> -
> -  objfile *operator* () const
> +  value_type operator* () const
>    {
>      return m_iter->get ();
>    }
>  
> -  unwrapping_objfile_iterator operator++ ()
> +  unwrapping_reverse_objfile_iterator operator++ ()
>    {
> -    ++m_iter;
> +    if (m_iter != m_begin)
> +      --m_iter;
> +    else
> +      {
> +	/* We can't decrement M_ITER since it is the begin iterator of the
> +	   objfile list.  Set M_ITER to the list's end iterator to indicate
> +	   this is now one-past-the-end.  */
> +	m_iter = m_end;
> +
> +	/* Overwrite M_BEGIN to avoid possibly copying an invalid iterator.  */
> +	m_begin = m_end;
> +      }
> +
>      return *this;
>    }
>  
> -  bool operator!= (const unwrapping_objfile_iterator &other) const
> +  bool operator!= (const unwrapping_reverse_objfile_iterator &other) const
>    {
>      return m_iter != other.m_iter;
>    }
>  
> +  /* Return an unwrapping reverse iterator starting at the last element of
> +     OBJF_LIST.  */
> +  static unwrapping_reverse_objfile_iterator begin (objfile_list &objf_list)
> +  {
> +    auto begin = objf_list.begin ();
> +    auto end = objf_list.end ();
> +    auto rev_begin = objf_list.end ();
> +
> +    /* Start REV_BEGIN on the last objfile in OBJF_LIST.  */
> +    if (begin != end)
> +      --rev_begin;
> +
> +    return unwrapping_reverse_objfile_iterator (rev_begin, begin, end);
> +  }
> +
> +  /* Return a one-past-the-end unwrapping reverse iterator.  */
> +  static unwrapping_reverse_objfile_iterator end (objfile_list &objf_list)
> +  {
> +    return unwrapping_reverse_objfile_iterator (objf_list.end (),
> +						objf_list.end (),
> +						objf_list.end ());
> +  }
> +
>  private:
> +  /* begin and end methods should be used to create these objects.  */
> +  unwrapping_reverse_objfile_iterator (objfile_list::iterator iter,
> +				       objfile_list::iterator begin,
> +				       objfile_list::iterator end)
> +    : m_iter (std::move (iter)), m_begin (std::move (begin)),
> +      m_end (std::move (end))
> +  {
> +  }
>  
> -  /* The underlying iterator.  */
> -  objfile_list::iterator m_iter;
> -};
> + /* The underlying iterator.  */
> + objfile_list::iterator m_iter;
>  
> + /* The underlying iterator pointing to the first objfile in the sequence.  Used
> +    to track when to stop decrementing M_ITER.  */
> + objfile_list::iterator m_begin;
>  
> -/* A range that returns unwrapping_objfile_iterators.  */
> +  /* The underlying iterator's one-past-the-end.  */
> + objfile_list::iterator m_end;
> +};
>  
> -using unwrapping_objfile_range = iterator_range<unwrapping_objfile_iterator>;
> +/* A range that returns unwrapping_iterators.  */
> +
> +using unwrapping_objfile_range
> +  = iterator_range<unwrapping_iterator<objfile_list::iterator>>;
>  
>  /* A program space represents a symbolic view of an address space.
>     Roughly speaking, it holds all the data associated with a
> @@ -209,11 +294,12 @@ struct program_space
>    objfiles_range objfiles ()
>    {
>      return objfiles_range
> -      (unwrapping_objfile_iterator (objfiles_list.begin ()),
> -       unwrapping_objfile_iterator (objfiles_list.end ()));
> +      (unwrapping_iterator<objfile_list::iterator> (objfiles_list.begin ()),
> +       unwrapping_iterator<objfile_list::iterator> (objfiles_list.end ()));
>    }
>  
> -  using objfiles_safe_range = basic_safe_range<objfiles_range>;
> +  using objfiles_reverse_range = iterator_range<unwrapping_reverse_objfile_iterator>;
> +  using objfiles_safe_reverse_range = basic_safe_range<objfiles_reverse_range>;
>  
>    /* An iterable object that can be used to iterate over all objfiles.
>       The basic use is in a foreach, like:
> @@ -221,20 +307,25 @@ struct program_space
>       for (objfile *objf : pspace->objfiles_safe ()) { ... }
>  
>       This variant uses a basic_safe_iterator so that objfiles can be
> -     deleted during iteration.  */
> -  objfiles_safe_range objfiles_safe ()
> +     deleted during iteration.
> +
> +     The use of a reverse iterator helps ensure that separate debug
> +     objfiles are deleted before their parent objfile.  This prevents
> +     the invalidation of an iterator due to the deletion of a parent
> +     objfile.  */
> +  objfiles_safe_reverse_range objfiles_safe ()
>    {
> -    return objfiles_safe_range
> -      (objfiles_range
> -	 (unwrapping_objfile_iterator (objfiles_list.begin ()),
> -	  unwrapping_objfile_iterator (objfiles_list.end ())));
> +    return objfiles_safe_reverse_range
> +      (objfiles_reverse_range
> +	(unwrapping_reverse_objfile_iterator::begin (objfiles_list),
> +	 unwrapping_reverse_objfile_iterator::end (objfiles_list)));
>    }
>  
> -  /* Add OBJFILE to the list of objfiles, putting it just before
> -     BEFORE.  If BEFORE is nullptr, it will go at the end of the
> +  /* Add OBJFILE to the list of objfiles, putting it just after
> +     AFTER.  If AFTER is nullptr, it will go at the end of the
>       list.  */
>    void add_objfile (std::unique_ptr<objfile> &&objfile,
> -		    struct objfile *before);
> +		    struct objfile *after);
>  
>    /* Remove OBJFILE from the list of objfiles.  */
>    void remove_objfile (struct objfile *objfile);
> diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
> index 9db5c47a8ce..784b81b5ca6 100644
> --- a/gdb/symfile-debug.c
> +++ b/gdb/symfile-debug.c
> @@ -109,9 +109,9 @@ objfile::has_unexpanded_symtabs ()
>  		objfile_debug_name (this));
>  
>    bool result = false;
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      if (iter->has_unexpanded_symtabs (this))
> +      if (qf->has_unexpanded_symtabs (this))
>  	{
>  	  result = true;
>  	  break;
> @@ -134,9 +134,9 @@ objfile::find_last_source_symtab ()
>      gdb_printf (gdb_stdlog, "qf->find_last_source_symtab (%s)\n",
>  		objfile_debug_name (this));
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      retval = iter->find_last_source_symtab (this);
> +      retval = qf->find_last_source_symtab (this);
>        if (retval != nullptr)
>  	break;
>      }
> @@ -167,8 +167,8 @@ objfile::forget_cached_source_info ()
>  	}
>      }
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->forget_cached_source_info (this);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->forget_cached_source_info (this);
>  }
>  
>  bool
> @@ -214,17 +214,17 @@ objfile::map_symtabs_matching_filename
>      return result;
>    };
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      if (!iter->expand_symtabs_matching (this,
> -					  match_one_filename,
> -					  nullptr,
> -					  nullptr,
> -					  on_expansion,
> -					  (SEARCH_GLOBAL_BLOCK
> -					   | SEARCH_STATIC_BLOCK),
> -					  UNDEF_DOMAIN,
> -					  ALL_DOMAIN))
> +      if (!qf->expand_symtabs_matching (this,
> +					match_one_filename,
> +					nullptr,
> +					nullptr,
> +					on_expansion,
> +					(SEARCH_GLOBAL_BLOCK
> +					 | SEARCH_STATIC_BLOCK),
> +					UNDEF_DOMAIN,
> +					ALL_DOMAIN))
>  	{
>  	  retval = false;
>  	  break;
> @@ -283,18 +283,18 @@ objfile::lookup_symbol (block_enum kind, const char *name, domain_enum domain)
>      return true;
>    };
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      if (!iter->expand_symtabs_matching (this,
> -					  nullptr,
> -					  &lookup_name,
> -					  nullptr,
> -					  search_one_symtab,
> -					  kind == GLOBAL_BLOCK
> -					  ? SEARCH_GLOBAL_BLOCK
> -					  : SEARCH_STATIC_BLOCK,
> -					  domain,
> -					  ALL_DOMAIN))
> +      if (!qf->expand_symtabs_matching (this,
> +					nullptr,
> +					&lookup_name,
> +					nullptr,
> +					search_one_symtab,
> +					kind == GLOBAL_BLOCK
> +					? SEARCH_GLOBAL_BLOCK
> +					: SEARCH_STATIC_BLOCK,
> +					domain,
> +					ALL_DOMAIN))
>  	break;
>      }
>  
> @@ -314,8 +314,8 @@ objfile::print_stats (bool print_bcache)
>      gdb_printf (gdb_stdlog, "qf->print_stats (%s, %d)\n",
>  		objfile_debug_name (this), print_bcache);
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->print_stats (this, print_bcache);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->print_stats (this, print_bcache);
>  }
>  
>  void
> @@ -340,16 +340,16 @@ objfile::expand_symtabs_for_function (const char *func_name)
>    lookup_name_info base_lookup (func_name, symbol_name_match_type::FULL);
>    lookup_name_info lookup_name = base_lookup.make_ignore_params ();
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->expand_symtabs_matching (this,
> -				   nullptr,
> -				   &lookup_name,
> -				   nullptr,
> -				   nullptr,
> -				   (SEARCH_GLOBAL_BLOCK
> -				    | SEARCH_STATIC_BLOCK),
> -				   VAR_DOMAIN,
> -				   ALL_DOMAIN);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->expand_symtabs_matching (this,
> +				 nullptr,
> +				 &lookup_name,
> +				 nullptr,
> +				 nullptr,
> +				 (SEARCH_GLOBAL_BLOCK
> +				  | SEARCH_STATIC_BLOCK),
> +				 VAR_DOMAIN,
> +				 ALL_DOMAIN);
>  }
>  
>  void
> @@ -359,8 +359,8 @@ objfile::expand_all_symtabs ()
>      gdb_printf (gdb_stdlog, "qf->expand_all_symtabs (%s)\n",
>  		objfile_debug_name (this));
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->expand_all_symtabs (this);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->expand_all_symtabs (this);
>  }
>  
>  void
> @@ -377,16 +377,16 @@ objfile::expand_symtabs_with_fullname (const char *fullname)
>      return filename_cmp (basenames ? basename : fullname, filename) == 0;
>    };
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->expand_symtabs_matching (this,
> -				   file_matcher,
> -				   nullptr,
> -				   nullptr,
> -				   nullptr,
> -				   (SEARCH_GLOBAL_BLOCK
> -				    | SEARCH_STATIC_BLOCK),
> -				   UNDEF_DOMAIN,
> -				   ALL_DOMAIN);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->expand_symtabs_matching (this,
> +				 file_matcher,
> +				 nullptr,
> +				 nullptr,
> +				 nullptr,
> +				 (SEARCH_GLOBAL_BLOCK
> +				  | SEARCH_STATIC_BLOCK),
> +				 UNDEF_DOMAIN,
> +				 ALL_DOMAIN);
>  }
>  
>  void
> @@ -402,9 +402,9 @@ objfile::expand_matching_symbols
>  		domain_name (domain), global,
>  		host_address_to_string (ordered_compare));
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->expand_matching_symbols (this, name, domain, global,
> -				   ordered_compare);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->expand_matching_symbols (this, name, domain, global,
> +				 ordered_compare);
>  }
>  
>  bool
> @@ -429,10 +429,10 @@ objfile::expand_symtabs_matching
>  		host_address_to_string (&expansion_notify),
>  		search_domain_name (kind));
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    if (!iter->expand_symtabs_matching (this, file_matcher, lookup_name,
> -					symbol_matcher, expansion_notify,
> -					search_flags, domain, kind))
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    if (!qf->expand_symtabs_matching (this, file_matcher, lookup_name,
> +				      symbol_matcher, expansion_notify,
> +				      search_flags, domain, kind))
>        return false;
>    return true;
>  }
> @@ -454,10 +454,10 @@ objfile::find_pc_sect_compunit_symtab (struct bound_minimal_symbol msymbol,
>  		host_address_to_string (section),
>  		warn_if_readin);
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      retval = iter->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
> -						   warn_if_readin);
> +      retval = qf->find_pc_sect_compunit_symtab (this, msymbol, pc, section,
> +						 warn_if_readin);
>        if (retval != nullptr)
>  	break;
>      }
> @@ -482,8 +482,8 @@ objfile::map_symbol_filenames (gdb::function_view<symbol_filename_ftype> fun,
>  		objfile_debug_name (this),
>  		need_fullname);
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> -    iter->map_symbol_filenames (this, fun, need_fullname);
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
> +    qf->map_symbol_filenames (this, fun, need_fullname);
>  }
>  
>  struct compunit_symtab *
> @@ -496,9 +496,9 @@ objfile::find_compunit_symtab_by_address (CORE_ADDR address)
>  		hex_string (address));
>  
>    struct compunit_symtab *result = NULL;
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      result = iter->find_compunit_symtab_by_address (this, address);
> +      result = qf->find_compunit_symtab_by_address (this, address);
>        if (result != nullptr)
>  	break;
>      }
> @@ -521,10 +521,10 @@ objfile::lookup_global_symbol_language (const char *name,
>    enum language result = language_unknown;
>    *symbol_found_p = false;
>  
> -  for (const auto &iter : qf_require_partial_symbols ())
> +  for (quick_symbol_functions *qf : qf_require_partial_symbols ())
>      {
> -      result = iter->lookup_global_symbol_language (this, name, domain,
> -						    symbol_found_p);
> +      result = qf->lookup_global_symbol_language (this, name, domain,
> +						  symbol_found_p);
>        if (*symbol_found_p)
>  	break;
>      }
> diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp
> index 61b9942de79..0bf49976b73 100644
> --- a/gdb/testsuite/gdb.python/py-objfile.exp
> +++ b/gdb/testsuite/gdb.python/py-objfile.exp
> @@ -135,7 +135,7 @@ gdb_test "p main" "= {<text variable, no debug info>} $hex <main>" \
>  gdb_py_test_silent_cmd "python objfile.add_separate_debug_file(\"${binfile}\")" \
>      "Add separate debug file file" 1
>  
> -gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[0\]" \
> +gdb_py_test_silent_cmd "python sep_objfile = gdb.objfiles()\[1\]" \
>      "Get separate debug info objfile" 1
>  
>  gdb_test "python print (sep_objfile.owner.filename)" "${testfile}2" \
> -- 
> 2.40.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2023-07-31 10:11 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-01  1:43 [PATCH 0/6 v3] gdb/debuginfod: Add on-demand debuginfo downloading Aaron Merey
2023-06-01  1:43 ` [PATCH 1/6 v2] gdb/debuginfod: Add debuginfod_section_query Aaron Merey
2023-06-07 13:35   ` Andrew Burgess
2023-07-27 11:04     ` Andrew Burgess
2023-06-01  1:43 ` [PATCH 2/6 v2] gdb: Add command 'maint set/show debuginfod download-sections' Aaron Merey
2023-06-01  6:13   ` Eli Zaretskii
2023-06-01 22:35     ` Aaron Merey
2023-06-02  6:49       ` Eli Zaretskii
2023-06-07 13:57   ` Andrew Burgess
2023-07-27 12:04   ` Andrew Burgess
2023-07-27 12:19   ` Andrew Burgess
2023-06-01  1:43 ` [PATCH 3/6 v3] gdb: Buffer gdb_stdout during events that might download deferred debuginfo Aaron Merey
2023-06-01  6:16   ` Eli Zaretskii
2023-06-01 22:36     ` Aaron Merey
2023-06-07 13:25   ` Andrew Burgess
2023-06-01  1:43 ` [PATCH 4/6] gdb/progspace: Add reverse safe iterator and template for unwrapping iterator Aaron Merey
2023-06-15 13:44   ` Aaron Merey
2023-07-03 17:39     ` [PING*2][PATCH " Aaron Merey
2023-07-19 14:32       ` [PING*3][PATCH " Aaron Merey
2023-07-31 10:11   ` [PATCH " Andrew Burgess
2023-06-01  1:43 ` [PATCH 5/6 v3] gdb/debuginfod: Support on-demand debuginfo downloading Aaron Merey
2023-06-15 13:44   ` Aaron Merey
2023-07-03 17:39     ` [PING*2][PATCH " Aaron Merey
2023-07-07 14:18   ` [PATCH " Andrew Burgess
2023-07-10 21:01     ` Aaron Merey
2023-07-11 12:01       ` Pedro Alves
2023-07-11 15:00         ` Aaron Merey
2023-07-19 14:33           ` [PING][PATCH " Aaron Merey
2023-07-27 10:24   ` [PATCH " Andrew Burgess
2023-06-01  1:43 ` [PATCH 6/6 v3] gdb/debuginfod: Add .debug_line downloading Aaron Merey
2023-06-15 13:45   ` Aaron Merey
2023-07-03 17:40     ` [PING*2][PATCH " Aaron Merey
2023-07-19 14:33       ` [PING*3][PATCH " Aaron Merey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).