Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andrew Burgess <aburgess@redhat.com>
To: Aaron Merey <amerey@redhat.com>, gdb-patches@sourceware.org
Cc: thiago.bauermann@linaro.org, Aaron Merey <amerey@redhat.com>
Subject: Re: [PATCH 3/4 v5] gdb/debuginfod: Support on-demand debuginfo downloading
Date: Thu, 18 Jan 2024 15:34:05 +0000	[thread overview]
Message-ID: <87jzo664gi.fsf@redhat.com> (raw)
In-Reply-To: <20240117154855.2057850-4-amerey@redhat.com>

Aaron Merey <amerey@redhat.com> writes:

> v5 changes: Some tweaks needed rebase onto
> objfile::find_and_add_separate_symbol_file changes in commit 661d98a.
> v5 also implements suggestions from Thiago's review:
> https://sourceware.org/pipermail/gdb-patches/2023-December/205506.html
>
> 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                | 125 +++++++++++++--
>  gdb/dwarf2/read.c                          | 147 +++++++++++++++++-
>  gdb/dwarf2/read.h                          |  10 ++
>  gdb/dwarf2/section.c                       |   3 +-
>  gdb/elfread.c                              |   3 +-
>  gdb/frame.c                                |   7 +
>  gdb/objfile-flags.h                        |   4 +
>  gdb/objfiles.h                             |  20 +++
>  gdb/quick-symbol.h                         |   4 +
>  gdb/symfile-debug.c                        |  11 ++
>  gdb/symfile.c                              |  13 +-
>  gdb/symtab.c                               |  18 ++-
>  gdb/testsuite/gdb.debuginfod/libsection1.c |  43 ++++++
>  gdb/testsuite/gdb.debuginfod/libsection2.c |  37 +++++
>  gdb/testsuite/gdb.debuginfod/section.c     |  29 ++++
>  gdb/testsuite/gdb.debuginfod/section.exp   | 172 +++++++++++++++++++++
>  gdb/testsuite/lib/debuginfod-support.exp   |  27 +++-
>  gdb/testsuite/lib/gdb.exp                  |   8 +-
>  23 files changed, 725 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 d3d1ecdf1f5..9e290af79f7 100644
> --- a/gdb/dwarf2/frame.c
> +++ b/gdb/dwarf2/frame.c
> @@ -1620,6 +1620,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 69f70642dc6..8c969ecd590 100644
> --- a/gdb/dwarf2/index-cache.c
> +++ b/gdb/dwarf2/index-cache.c
> @@ -240,6 +240,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.  */
> @@ -251,6 +278,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 023fc86fc89..0d845ebcadd 100644
> --- a/gdb/dwarf2/index-cache.h
> +++ b/gdb/dwarf2/index-cache.h
> @@ -90,6 +90,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 efb754b5fe8..4d3776465c3 100644
> --- a/gdb/dwarf2/public.h
> +++ b/gdb/dwarf2/public.h
> @@ -44,4 +44,11 @@ extern bool dwarf2_initialize_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 4ca44540296..55b04391eb5 100644
> --- a/gdb/dwarf2/read-gdb-index.c
> +++ b/gdb/dwarf2/read-gdb-index.c
> @@ -139,6 +139,8 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       gdb.dwarf2/gdb-index.exp testcase.  */
>    void dump (struct objfile *objfile) override;
>  
> +  /* Calls do_expand_symtabs_matching and triggers debuginfo downloading
> +     if necessary.  */
>    bool expand_symtabs_matching
>      (struct objfile *objfile,
>       gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> @@ -148,8 +150,60 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       block_search_flags search_flags,
>       domain_enum domain,
>       enum search_domain kind) override;
> +
> +  /* Expand symtabs matching a given symbol or file.  */
> +  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 (const 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 (const 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
> @@ -268,7 +322,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,
> @@ -317,6 +371,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)

Should be:

  if ((objfile->flags & OBJF_READNEVER) != 0)

> +    return false;
> +
> +  try
> +    {
> +      return do_expand_symtabs_matching (objfile, file_matcher, lookup_name,
> +					 symbol_matcher, expansion_notify,
> +					 search_flags, domain, kind);
> +    }
> +  catch (const 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
>  {
> @@ -652,28 +739,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 76dbeb8d536..8725a2fbcdb 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -35,6 +35,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"
> @@ -96,6 +97,8 @@
>  #include "split-name.h"
>  #include "gdbsupport/thread-pool.h"
>  #include "run-on-main-thread.h"
> +#include "inferior.h"
> +#include "debuginfod-support.h"
>  
>  /* When == 1, print basic high level tracing messages.
>     When > 1, be more verbose.
> @@ -3003,7 +3006,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,
> @@ -3034,6 +3037,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)

Should be:

  if ((objfile->flags & OBJF_READNEVER) != 0)

> +    return nullptr;
> +
> +  try
> +    {
> +      return do_find_pc_sect_compunit_symtab (objfile, msymbol, pc,
> +					      section, warn_if_readin);
> +    }
> +  catch (const 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,
> @@ -3190,6 +3219,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
>       (dwarf2_per_objfile *);
>  
> @@ -3200,7 +3252,8 @@ dwarf2_initialize_objfile (struct objfile *objfile,
>  			   const struct dwarf2_debug_sections *names,
>  			   bool can_copy)
>  {
> -  if (!dwarf2_has_info (objfile, names, can_copy))
> +  if (!dwarf2_has_info (objfile, names, can_copy)
> +      && (objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
>      return false;
>  
>    dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
> @@ -3262,6 +3315,16 @@ dwarf2_initialize_objfile (struct objfile *objfile,
>        global_index_cache.hit ();
>        objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
>      }
> +  /* Try to read just a separately downloaded gdb index.  */
> +  else if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED)

Should be:

  (objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0

> +	   && 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;
> +    }
>    else
>      {
>        global_index_cache.miss ();
> @@ -3270,6 +3333,86 @@ dwarf2_initialize_objfile (struct objfile *objfile,
>    return true;
>  }
>  
> +/* See read.h.  */
> +
> +void
> +read_full_dwarf_from_debuginfod (struct objfile *objfile,
> +				 dwarf2_base_index_functions *fncs)
> +{
> +  gdb_assert (objfile->flags & OBJF_DOWNLOAD_DEFERRED);

Should be:

  gdb_assert ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0);

OK, I'm going to stop commenting on these now, but there's a few more of
them in the rest of the patch.  GDB style is not to rely on implicit
int -> bool conversion.

> +
> +  SCOPE_EXIT { objfile->remove_deferred_status (); };
> +
> +  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;

Of these, filename, debug_bfd, and fd can be declared where they are
first defined.  You can probably move symfile_path closer to where it's
used too.

> +
> +  if (build_id == nullptr)
> +    return;
> +
> +  filename = bfd_get_filename (objfile->obfd.get ());
> +  fd = debuginfod_debuginfo_query (build_id->data, build_id->size,
> +				   filename, &symfile_path);
> +  if (fd.get () < 0)
> +    return;
> +
> +  /* 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);
> +      return;
> +    }
> +
> +  /* 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);
> +}
> +
> +/* 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;

Flag checks missing '!= 0' again.


> +  if (!IS_DIR_SEPARATOR (*objfile_filename (objfile)))
> +    return false;
> +
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +
> +  if (build_id == nullptr)
> +    return false;
> +
> +  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 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
>  
>  /* Find the base address of the compilation unit for range lists and
> diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> index d6631d45a54..8b6f1864458 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -900,6 +900,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
>       CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
>         override;
>  
> +  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
>    {
> @@ -972,4 +976,10 @@ extern void create_all_units (dwarf2_per_objfile *per_objfile);
>  
>  extern htab_up create_quick_file_names_table (unsigned int nr_initial_entries);
>  
> +/* 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 1235f293f45..b674103c72f 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."));

This seems like an interesting change.  There's no test that triggers
this case, but I guess you ran into this at some point.  Could you
explain more about what's going on here?

>    return section->s.section->owner;
>  }
>  
> diff --git a/gdb/elfread.c b/gdb/elfread.c
> index c5ba8837217..764e991dfc2 100644
> --- a/gdb/elfread.c
> +++ b/gdb/elfread.c
> @@ -1219,7 +1219,8 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
>  	   && objfile->separate_debug_objfile_backlink == NULL)
>      {
>        if (objfile->find_and_add_separate_symbol_file (symfile_flags))
> -	gdb_assert (objfile->separate_debug_objfile != nullptr);
> +	gdb_assert (objfile->separate_debug_objfile != nullptr
> +		    || objfile->flags & OBJF_DOWNLOAD_DEFERRED);
>        else
>  	has_dwarf2 = false;
>      }
> diff --git a/gdb/frame.c b/gdb/frame.c
> index 41003cc7771..d3409c8fd97 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -1889,6 +1889,13 @@ get_selected_frame (const char *message)
>  	error (("%s"), message);
>  
>        lookup_selected_frame (selected_frame_id, selected_frame_level);
> +
> +      /* It is possible for lookup_selected_frame to cause a new objfile
> +	 to be loaded.  Some objfile observers may choose to clear
> +	 selected_frame when an objfile is loaded.  Work around this by
> +	 calling lookup_selected_frame again if the first try failed.  */
> +      if (selected_frame == nullptr)
> +	lookup_selected_frame (selected_frame_id, selected_frame_level);

I notice this case wasn't tested -- I added an abort here and reran the
test, and didn't see it hit.

I ask because this seems both interesting, and this doesn't feel like
the right solution... As a minimum you'd need a comment on
lookup_selected_frame that says "hey, you will need to call this
function twice to guarantee that it did what it promised".  So then I'd
suggest we should just rename lookup_selected_frame to
lookup_selected_frame_1, and have lookup_selected_frame be a wrapper
which does the double lookup.

But I was kind of hoping that we'd have a test that hit this case so I
could look at the stack trace and better understand what exactly is
happening when the selected frame is cleared.

>      }
>    /* There is always a frame.  */
>    gdb_assert (selected_frame != NULL);
> diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h
> index 74aea1a88d3..3f922d9e84f 100644
> --- a/gdb/objfile-flags.h
> +++ b/gdb/objfile-flags.h
> @@ -56,6 +56,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 34b3dac0b26..69222092b61 100644
> --- a/gdb/objfiles.h
> +++ b/gdb/objfiles.h
> @@ -622,6 +622,26 @@ struct objfile
>  					       domain_enum domain,
>  					       bool *symbol_found_p);
>  
> +  /* Indicate that the acquisition of this objfile's separate debug objfile
> +     is no longer deferred.  Used when the debug objfile has been acquired
> +     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.  */
> +    qf.remove_if ([&] (const quick_symbol_functions_up &qf_up)
> +      {
> +	return qf_up->from_separate_index;
> +      });
> +  }
> +
>    /* Return the relocation offset applied to SECTION.  */
>    CORE_ADDR section_offset (bfd_section *section) const
>    {
> diff --git a/gdb/quick-symbol.h b/gdb/quick-symbol.h
> index 4cd234162ae..01a228b446c 100644
> --- a/gdb/quick-symbol.h
> +++ b/gdb/quick-symbol.h
> @@ -193,6 +193,10 @@ struct quick_symbol_functions
>    virtual void compute_main_name (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-debug.c b/gdb/symfile-debug.c
> index c684f9cdd43..3ed31661300 100644
> --- a/gdb/symfile-debug.c
> +++ b/gdb/symfile-debug.c
> @@ -37,6 +37,7 @@
>  #include "cli/cli-style.h"
>  #include "build-id.h"
>  #include "debuginfod-support.h"
> +#include "dwarf2/public.h"
>  
>  /* We need to save a pointer to the real symbol functions.
>     Plus, the debug versions are malloc'd because we have to NULL out the
> @@ -617,6 +618,16 @@ objfile::find_and_add_separate_symbol_file (symfile_add_flags symfile_flags)
>  	  = simple_find_and_open_separate_symbol_file
>  	      (this, find_separate_debug_file_by_debuglink, &warnings);
>  
> +      /* Attempt to download only an index from the separate debug info.
> +	 As with debuginfod_find_and_open_separate_symbol_file, only attempt
> +	 this once.  */
> +      if (debug_bfd == nullptr && attempt == 0
> +	  && dwarf2_has_separate_index (this))
> +	{
> +	  has_dwarf2 = true;
> +	  break;
> +	}
> +
>        /* Only try debuginfod on the first attempt.  Sure, we could imagine
>  	 an extension that somehow adds the required debug info to the
>  	 debuginfod server but, at least for now, we don't support this
> diff --git a/gdb/symfile.c b/gdb/symfile.c
> index bdcc27e7ee5..ec4b4da12ed 100644
> --- a/gdb/symfile.c
> +++ b/gdb/symfile.c
> @@ -989,6 +989,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.  */
> @@ -999,7 +1003,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 ();
>      }
> @@ -1120,6 +1125,12 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name,
>    if (objfile->sf != nullptr)
>      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);
>  
>    return objfile;
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index cc0bdb80c7c..ac9513608d7 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -2926,14 +2926,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..7ad19c276c4
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/libsection1.c
> @@ -0,0 +1,43 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2023 Free Software Foundation, Inc.

Remember to update your copyright dates to '2023-2024' throughout.

> +
> +   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 *);
> +
> +static volatile int flag = 0;
> +
> +void
> +libsection1_test ()
> +{
> +  pthread_t thr;
> +
> +  printf ("In libsection1\n");
> +  libsection2_test ();
> +
> +  pthread_create (&thr, NULL, libsection2_thread_test, (void *) &flag);
> +
> +  /* Give the new thread a chance to actually enter libsection2_thread_test.  */
> +  while (!flag)
> +    ;
> +
> +  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..20edd68166b
> --- /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>
> +#include <pthread.h>
> +
> +void
> +libsection2_test ()
> +{
> +  printf ("In libsection2\n");
> +}
> +
> +void *
> +libsection2_thread_test (void *arg)
> +{
> +  int *flag = (int *) arg;
> +  printf ("In thread test\n");
> +
> +  while (1)
> +    *flag = 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..33e4faf2cac
> --- /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..c3872ecdfc9
> --- /dev/null
> +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> @@ -0,0 +1,172 @@
> +# 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 {}]

Not sure what these two are for.  Maybe a copy&paste issue?

> +
> +# 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]

I'd have expected you to use ${srcfile} and ${binfile} here which are
setup by `standard_testfile` based on the name of the test script.  So
section.exp will setup `section.c` and `/path/to/.../section`
respectively.

The source file doesn't have the directory prefix, but the compile procs
know how to find the source file, so that's OK.

standard_testfile also gives you `testfile` which is $binfile without
the path prefix, if that's needed.

It's good practice to use $binfile throughout the test unless there's
some compelling reason not too everyone knows what $binfile is.

> +
> +# 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
> +}

Instead of calling all these compile worker functions directly, the
better approach is to call build_executable and pass through the correct
flags.  This seems to work for me:

  if { [build_executable "build $libfile2" $lib_sl2 $libsrc2 \
  	  {debug build-id shlib}] != 0 } {
      return -1
  }
  
  if { [build_executable "build $libfile1" $lib_sl1 $libsrc1 \
  	  [list debug build-id shlib_pthreads shlib=$lib_sl2]] != 0 } {
      return -1
  }
  
  if { [build_executable "build executable" $sectexec $sectsrc \
  	  [list debug build-id shlib=$lib_sl1 shlib=$lib_sl2]] != 0 } {
      return -1
  }

I've inlined the flag lists here.  If you'd rather define them
separately still, that's fine.  Notice also that I removed the calls to
'untested', these are not needed, the testsuite will call 'untested' for
you if the build fails.

> +
> +# Add .gdb_index to solibs.
> +if { [ensure_gdb_index $lib_sl1 "" "libsection1"] != 1 } {
> +    untested "failed to add .gdb_index to $libfile1"
> +    return -1
> +}
> +
> +if { [ensure_gdb_index $lib_sl2 "" "libsection2"] != 1 } {
> +    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 testname } {
> +    global cache
> +
> +    # Delete client cache so debuginfo downloads again.
> +    file delete -force $cache
> +    clean_restart
> +
> +    gdb_test "set debuginfod enabled on" "" "clean_restart enable $testname"

You can use gdb_test_no_output here rather than passing an empty pattern.

> +    gdb_load $binfile
> +
> +    runto_main
> +}
> +
> +# Tests with no debuginfod server running.
> +proc_with_prefix no_url { } {
> +    global sectexec libfile1 libfile2
> +
> +    gdb_load $sectexec
> +    if {![runto_main]} {
> +       return
> +    }
> +
> +    # Check that no section is downloaded and no debuginfo is found.
> +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile1.*" \
> +	     "found no url lib1"
> +    gdb_test "info sharedlibrary" ".*Yes \\(\\*\\).*$libfile2.*" \
> +	     "found no url lib2"
> +}
> +
> +# 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
> +
> +    clean_restart_with_prompt $sectexec "index"
> +
> +    # Download debuginfo when stepping into a function.
> +    set res ".*separate debug info for $lib_sl1.*\"In ${libfile1}\\\\n\".*"
> +    gdb_test "step" $res "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 "break set"
> +
> +    # Hit the breakpoint.
> +    set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
> +    gdb_test "c" $res "break continue"
> +}
> +
> +# 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..e0b3dc39f51 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] } {
> @@ -194,3 +197,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.

We can use 'true' and 'false' in TCL code, see allow_debuginfod_tests as
an example.

> +proc enable_section_downloads { } {

I think this should be renamed to something like allow_section_downloads
(though I about allow_debuginfod_section_downloads to make it clear it's
a debuginfod function).  Then, in the test script you would say:

  require allow_debuginfod_section_downloads

Rather than the `if` check that you have right now.


> +    global gdb_prompt

This line isn't needed, gdb_prompt isn't used.

Thanks,
Andrew

> +
> +    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
> +	}
> +    }
> +}
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 9ac707be696..9b3c7e6a02c 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -9414,10 +9414,14 @@ proc get_index_type { objfile { testname "" } } {
>  # STYLE controls which style of index to add, if needed.  The empty
>  # string (the default) means .gdb_index; "-dwarf-5" means .debug_names.
>  
> -proc ensure_gdb_index { binfile {style ""} } {
> +proc ensure_gdb_index { binfile {style ""} {testname_prefix ""} } {
>      set testfile [file tail $binfile]
> -
>      set test "check if index present"
> +
> +    if { $testname_prefix != "" } {
> +      set test "$testname_prefix $test"
> +    }
> +
>      set index_type [get_index_type $testfile $test]
>  
>      if { $index_type eq "gdb" || $index_type eq "dwarf5" } {
> -- 
> 2.43.0


  reply	other threads:[~2024-01-18 15:34 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-17 15:48 [PATCH 0/4] On-demand " Aaron Merey
2024-01-17 15:48 ` [PATCH 1/4 v8] gdb: Buffer output streams during events that might download debuginfo Aaron Merey
2024-01-18 11:22   ` Andrew Burgess
2024-01-19  5:28     ` Aaron Merey
2024-01-19 14:35       ` Luis Machado
2024-01-19 17:13         ` Aaron Merey
2024-01-19 19:34           ` Tom de Vries
2024-01-19 19:52             ` Aaron Merey
2024-01-17 15:48 ` [PATCH 2/4 v3] gdb/progspace: Add reverse safe iterator Aaron Merey
2024-01-18 12:02   ` Andrew Burgess
2024-01-19  5:09     ` Aaron Merey
2024-02-07 21:24       ` [PING][PATCH " Aaron Merey
2024-02-22 22:22         ` [PING*2][PATCH " Aaron Merey
2024-03-01  0:09           ` [PING*3][PATCH " Aaron Merey
2024-03-12 22:14           ` Aaron Merey
2024-01-17 15:48 ` [PATCH 3/4 v5] gdb/debuginfod: Support on-demand debuginfo downloading Aaron Merey
2024-01-18 15:34   ` Andrew Burgess [this message]
2024-01-19  5:12     ` Aaron Merey
2024-02-07 21:25       ` [PING][PATCH " Aaron Merey
2024-02-22 22:23         ` [PING*2][PATCH " Aaron Merey
2024-03-01  0:09           ` [PING*3][PATCH " Aaron Merey
2024-03-12 22:15           ` Aaron Merey
2024-03-27 10:58       ` [PATCH " Andrew Burgess
2024-03-27 23:56         ` Aaron Merey
2024-01-17 15:48 ` [PATCH 4/4 v6] gdb/debuginfod: Add .debug_line downloading Aaron Merey
2024-02-07 21:25   ` [PING][PATCH " Aaron Merey
2024-02-22 22:23     ` [PING*2][PATCH " Aaron Merey
2024-03-01  0:10       ` [PING*3][PATCH " Aaron Merey
2024-03-12 22:15       ` Aaron Merey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87jzo664gi.fsf@redhat.com \
    --to=aburgess@redhat.com \
    --cc=amerey@redhat.com \
    --cc=gdb-patches@sourceware.org \
    --cc=thiago.bauermann@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox