Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Aaron Merey <amerey@redhat.com>
To: Andrew Burgess <aburgess@redhat.com>
Cc: gdb-patches@sourceware.org,
	 Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Subject: [PING*2][PATCH 3/4 v5] gdb/debuginfod: Support on-demand debuginfo downloading
Date: Thu, 22 Feb 2024 17:23:33 -0500	[thread overview]
Message-ID: <CAJDtP-SvFB=Rvf2SKQng9+NfbRBb-YZ6FHewPJgFfDwTTA1HRg@mail.gmail.com> (raw)
In-Reply-To: <CAJDtP-S+i=89fDW3JOSX7pQ_pQf9kTjgPye+nSWQQxJg7YzPRw@mail.gmail.com>

Ping

Thanks,
Aaron

On Wed, Feb 7, 2024 at 4:25 PM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Fri, Jan 19, 2024 at 12:12 AM Aaron Merey <amerey@redhat.com> wrote:
> >
> > Hi Andrew,
> >
> > Thanks for the review.
> >
> > On Thu, Jan 18, 2024 at 10:34 AM Andrew Burgess <aburgess@redhat.com> wrote:
> > >
> > > 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.
> >
> > Fixed.
> >
> > >
> > > > +
> > > > +  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.
> > >
> >
> > Fixed.
> >
> > > > 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?
> >
> > This can happen if gdb mistakenly tries to read debug info that
> > has been deferred and not yet downloaded.  For example if gdb attempted
> > to read symbols from a debuginfo-stripped parent objfile without first
> > downloading the full separate debug info, then this error would trigger.
> >
> > > > @@ -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.
> >
> > With the 2nd call to lookup_selected_frame removed, the test
> > "gdb.debuginfod/section.exp: local_url: line thread" becomes unresolved
> > when testing with --target_board=native-gdbserver.
> >
> > >
> > > 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.
> >
> > This is a better solution. Fixed.
> >
> > > 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.
> > >
> > > > 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.
> >
> > Fixed.
> >
> > > > +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?
> >
> > Fixed.
> >
> > >
> > > > +
> > > > +# 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.
> >
> > Fixed.
> >
> > > > +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
> > >   }
> >
> > Fixed.
> >
> > > > +    gdb_test "set debuginfod enabled on" "" "clean_restart enable $testname"
> > >
> > > You can use gdb_test_no_output here rather than passing an empty pattern.
> >
> > Fixed.
> >
> > > > +# 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.
> >
> > Fixed.  I've attached the updated patch below.
> >
> > Aaron
> >
> > 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                | 125 +++++++++++++---
> >  gdb/dwarf2/read.c                          | 145 ++++++++++++++++++-
> >  gdb/dwarf2/read.h                          |  10 ++
> >  gdb/dwarf2/section.c                       |   3 +-
> >  gdb/elfread.c                              |   3 +-
> >  gdb/frame.c                                |  27 +++-
> >  gdb/objfile-flags.h                        |   4 +
> >  gdb/objfiles.h                             |  20 +++
> >  gdb/quick-symbol.h                         |   4 +
> >  gdb/symfile-debug.c                        |  11 ++
> >  gdb/symfile.c                              |  12 +-
> >  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   | 157 +++++++++++++++++++++
> >  gdb/testsuite/lib/debuginfod-support.exp   |  25 +++-
> >  gdb/testsuite/lib/gdb.exp                  |   8 +-
> >  23 files changed, 719 insertions(+), 32 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..f4d77ed2d04 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) != 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..062f0e34938 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) != 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) != 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,84 @@ 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) != 0);
> > +
> > +  SCOPE_EXIT { objfile->remove_deferred_status (); };
> > +
> > +  const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > +  if (build_id == nullptr)
> > +    return;
> > +
> > +  const char *filename = bfd_get_filename (objfile->obfd.get ());
> > +  gdb::unique_xmalloc_ptr<char> symfile_path;
> > +  scoped_fd fd;
> > +
> > +  fd = debuginfod_debuginfo_query (build_id->data, build_id->size,
> > +                                  filename, &symfile_path);
> > +  if (fd.get () < 0)
> > +    return;
> > +
> > +  /* Separate debuginfo successfully retrieved from server.  */
> > +  gdb_bfd_ref_ptr 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) != 0)
> > +    return true;
> > +  if ((objfile->flags & OBJF_MAINLINE) != 0)
> > +    return false;
> > +  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;
> > +}
> > +
> >
> >
> >  /* 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."));
> >    return section->s.section->owner;
> >  }
> >
> > diff --git a/gdb/elfread.c b/gdb/elfread.c
> > index c5ba8837217..f14dc1bf80b 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) != 0);
> >        else
> >         has_dwarf2 = false;
> >      }
> > diff --git a/gdb/frame.c b/gdb/frame.c
> > index 41003cc7771..7f57cf80a1b 100644
> > --- a/gdb/frame.c
> > +++ b/gdb/frame.c
> > @@ -1772,14 +1772,10 @@ restore_selected_frame (frame_id frame_id, int frame_level)
> >    selected_frame = nullptr;
> >  }
> >
> > -/* Lookup the frame_info object for the selected frame FRAME_ID /
> > -   FRAME_LEVEL and cache the result.
> > -
> > -   If FRAME_LEVEL > 0 and the originally selected frame isn't found,
> > -   warn and select the innermost (current) frame.  */
> > +/* See lookup_selected_frame.  */
> >
> >  static void
> > -lookup_selected_frame (struct frame_id a_frame_id, int frame_level)
> > +lookup_selected_frame_1 (struct frame_id &a_frame_id, int frame_level)
> >  {
> >    frame_info_ptr frame = NULL;
> >    int count;
> > @@ -1851,6 +1847,25 @@ lookup_selected_frame (struct frame_id a_frame_id, int frame_level)
> >      }
> >  }
> >
> > +/* Lookup the frame_info object for the selected frame FRAME_ID /
> > +   FRAME_LEVEL and cache the result.
> > +
> > +   If FRAME_LEVEL > 0 and the originally selected frame isn't found,
> > +   warn and select the innermost (current) frame.  */
> > +
> > +static void
> > +lookup_selected_frame (struct frame_id a_frame_id, int frame_level)
> > +{
> > +  lookup_selected_frame_1 (selected_frame_id, selected_frame_level);
> > +
> > +  /* It is possible for lookup_selected_frame_1 to cause a new objfile
> > +     to be loaded.  However some objfile observers may choose to clear
> > +     selected_frame when an objfile is loaded.  Work around this by
> > +     calling lookup_selected_frame_1 again if the first call failed.  */
> > +  if (selected_frame == nullptr)
> > +    lookup_selected_frame_1 (selected_frame_id, selected_frame_level);
> > +}
> > +
> >  bool
> >  has_stack_frames ()
> >  {
> > 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 b917886988d..4a4db1e7058 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..820eb061aca 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) != 0);
> > +
> >    /* 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,7 @@ 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 +1124,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) != 0))
> > +    parent->remove_deferred_status ();
> > +
> >    gdb::observers::new_objfile.notify (objfile);
> >
> >    return objfile;
> > diff --git a/gdb/symtab.c b/gdb/symtab.c
> > index cc0bdb80c7c..d0219da6317 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) != 0;
> > +
> >        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..e4f05db0a79
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.debuginfod/libsection1.c
> > @@ -0,0 +1,43 @@
> > +/* This testcase is part of GDB, the GNU debugger.
> > +
> > +   Copyright 2023-2024 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 *);
> > +
> > +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..3c82808e5fe
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.debuginfod/libsection2.c
> > @@ -0,0 +1,37 @@
> > +/* This testcase is part of GDB, the GNU debugger.
> > +
> > +   Copyright 2023-2024 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..fa9ac536a6e
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.debuginfod/section.c
> > @@ -0,0 +1,29 @@
> > +/* This testcase is part of GDB, the GNU debugger.
> > +
> > +   Copyright 2023-2024 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..35086d15705
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> > @@ -0,0 +1,157 @@
> > +# Copyright 2023-2024 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
> > +
> > +clean_restart
> > +require allow_debuginfod_tests
> > +require allow_debuginfod_section_downloads
> > +
> > +# BINFILE calls a function from LIB_SL1.
> > +set libfile1 "libsection1"
> > +set libsrc1 $srcdir/$subdir/$libfile1.c
> > +set lib_sl1 [standard_output_file $libfile1.sl]
> > +
> > +# LIB_SL1 calls functions from LIB_SL2.
> > +set libfile2 "libsection2"
> > +set libsrc2 $srcdir/$subdir/$libfile2.c
> > +set lib_sl2 [standard_output_file $libfile2.sl]
> > +
> > +# Build LIB_SL2, LIB_SL1 and BINFILE.
> > +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" $binfile $srcfile \
> > +       [list debug build-id shlib=$lib_sl1 shlib=$lib_sl2]] != 0 } {
> > +    return -1
> > +}
> > +
> > +# Make sure libsection1 and libsection2 contain .gdb_index.
> > +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_no_output "set debuginfod enabled on" \
> > +                      "clean_restart enable $testname"
> > +    gdb_load $binfile
> > +
> > +    runto_main
> > +}
> > +
> > +# Tests with no debuginfod server running.
> > +proc_with_prefix no_url { } {
> > +    global binfile libfile1 libfile2
> > +
> > +    gdb_load $binfile
> > +    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 binfile
> > +    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 $binfile "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 $binfile "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..71964a90564 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,23 @@ proc stop_debuginfod { } {
> >         unset debuginfod_spawn_id
> >      }
> >  }
> > +
> > +# Return true if gdb is configured to download ELF/DWARF sections from
> > +# debuginfod servers.  Otherwise return false.
> > +proc allow_debuginfod_section_downloads { } {
> > +    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 false
> > +       }
> > +       -re -wrap "^" {
> > +           return true
> > +       }
> > +       -re -wrap "" {
> > +           fail "$gdb_test_name (unexpected output)"
> > +           return false
> > +       }
> > +    }
> > +}
> > 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-02-22 22:24 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
2024-01-19  5:12     ` Aaron Merey
2024-02-07 21:25       ` [PING][PATCH " Aaron Merey
2024-02-22 22:23         ` Aaron Merey [this message]
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='CAJDtP-SvFB=Rvf2SKQng9+NfbRBb-YZ6FHewPJgFfDwTTA1HRg@mail.gmail.com' \
    --to=amerey@redhat.com \
    --cc=aburgess@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