[2/3,v7] gdb/debuginfod: Support on-demand debuginfo downloading
Checks
Context |
Check |
Description |
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gdb_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 |
fail
|
Test failed
|
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 in this patch include dwarf2_has_separate_index
which downloads the separate .gdb_index. If successful, a shared library
objfile owns the index until its separate debug objfile is downloaded
or found to be unavailable.
read_full_dwarf_from_debuginfod downloads the full debuginfo and
initializes the separate debug objfile. It is called by functions
such as dw2_expand_symtabs_matching_symbol and
dwarf2_gdb_index::expand_all_symtabs when symtab expansion is required.
---
v7 changes:
No longer use try/catch statements to trigger the download of deferred
debuginfo. These have been replaced with calls to
read_full_dwarf_from_debuginfod placed where the need for full debuginfo
is detected.
gdb/dwarf2/frame.c | 13 ++
gdb/dwarf2/frame.h | 4 +
gdb/dwarf2/index-cache.c | 22 ++-
gdb/dwarf2/index-cache.h | 11 ++
gdb/dwarf2/public.h | 5 +
gdb/dwarf2/read-gdb-index.c | 63 ++++++---
gdb/dwarf2/read.c | 142 ++++++++++++++++++-
gdb/dwarf2/read.h | 5 +
gdb/dwarf2/section.c | 6 +-
gdb/elfread.c | 3 +-
gdb/frame.c | 27 +++-
gdb/objfile-flags.h | 4 +
gdb/objfiles.h | 19 +++
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, 634 insertions(+), 34 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
Comments
Ping
Thanks,
Aaron
On Tue, Sep 3, 2024 at 11:11 PM Aaron Merey <amerey@redhat.com> wrote:
>
> 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 in this patch include dwarf2_has_separate_index
> which downloads the separate .gdb_index. If successful, a shared library
> objfile owns the index until its separate debug objfile is downloaded
> or found to be unavailable.
>
> read_full_dwarf_from_debuginfod downloads the full debuginfo and
> initializes the separate debug objfile. It is called by functions
> such as dw2_expand_symtabs_matching_symbol and
> dwarf2_gdb_index::expand_all_symtabs when symtab expansion is required.
> ---
> v7 changes:
> No longer use try/catch statements to trigger the download of deferred
> debuginfo. These have been replaced with calls to
> read_full_dwarf_from_debuginfod placed where the need for full debuginfo
> is detected.
>
> gdb/dwarf2/frame.c | 13 ++
> gdb/dwarf2/frame.h | 4 +
> gdb/dwarf2/index-cache.c | 22 ++-
> gdb/dwarf2/index-cache.h | 11 ++
> gdb/dwarf2/public.h | 5 +
> gdb/dwarf2/read-gdb-index.c | 63 ++++++---
> gdb/dwarf2/read.c | 142 ++++++++++++++++++-
> gdb/dwarf2/read.h | 5 +
> gdb/dwarf2/section.c | 6 +-
> gdb/elfread.c | 3 +-
> gdb/frame.c | 27 +++-
> gdb/objfile-flags.h | 4 +
> gdb/objfiles.h | 19 +++
> 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, 634 insertions(+), 34 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 79ef065bfcb..a4675b0e7ef 100644
> --- a/gdb/dwarf2/frame.c
> +++ b/gdb/dwarf2/frame.c
> @@ -1619,6 +1619,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 2167310fbdf..423a9355b8a 100644
> --- a/gdb/dwarf2/frame.h
> +++ b/gdb/dwarf2/frame.h
> @@ -237,6 +237,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
> extern const struct frame_base *
> dwarf2_frame_base_sniffer (const 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 (const frame_info_ptr &this_frame);
> diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
> index a04d5d662d8..3d52693a38d 100644
> --- a/gdb/dwarf2/index-cache.c
> +++ b/gdb/dwarf2/index-cache.c
> @@ -217,14 +217,22 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
> /* Compute where we would expect a gdb index file for this build id to be. */
> std::string filename = make_index_filename (build_id, INDEX4_SUFFIX);
>
> + return lookup_gdb_index (filename.c_str (), resource);
> +}
> +
> +/* See index-cache.h. */
> +
> +gdb::array_view<const gdb_byte>
> +index_cache::lookup_gdb_index (const char *filename,
> + std::unique_ptr<index_cache_resource> *resource)
> +{
> try
> {
> - index_cache_debug ("trying to read %s",
> - filename.c_str ());
> + index_cache_debug ("trying to read %s", filename);
>
> /* Try to map that file. */
> index_cache_resource_mmap *mmap_resource
> - = new index_cache_resource_mmap (filename.c_str ());
> + = new index_cache_resource_mmap (filename);
>
> /* Yay, it worked! Hand the resource to the caller. */
> resource->reset (mmap_resource);
> @@ -236,7 +244,7 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
> catch (const gdb_exception_error &except)
> {
> index_cache_debug ("couldn't read %s: %s",
> - filename.c_str (), except.what ());
> + filename, except.what ());
> }
>
> return {};
> @@ -253,6 +261,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 08a2d56493c..0c3f574e1b9 100644
> --- a/gdb/dwarf2/index-cache.h
> +++ b/gdb/dwarf2/index-cache.h
> @@ -91,6 +91,17 @@ 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. 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. This
> + function does not exit early if the index cache has not been enabled. */
> + gdb::array_view<const gdb_byte>
> + lookup_gdb_index (const char *filename,
> + 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 bc419ff208f..6523854386c 100644
> --- a/gdb/dwarf2/public.h
> +++ b/gdb/dwarf2/public.h
> @@ -44,4 +44,9 @@ extern bool dwarf2_initialize_objfile
>
> extern void dwarf2_build_frame_info (struct objfile *);
>
> +/* Query debuginfod for the .gdb_index associated with OBJFILE.
> + Used to defer separate debuginfo downloading until necessary. */
> +
> +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 8cd665cee37..5938440a832 100644
> --- a/gdb/dwarf2/read-gdb-index.c
> +++ b/gdb/dwarf2/read-gdb-index.c
> @@ -151,6 +151,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> gdb.dwarf2/gdb-index.exp testcase. */
> void dump (struct objfile *objfile) override;
>
> + /* Expand symtabs matching a given symbol or file. */
> bool expand_symtabs_matching
> (struct objfile *objfile,
> gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> @@ -159,8 +160,34 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> block_search_flags search_flags,
> domain_search_flags domain) override;
> +
> + /* 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)
> +{
> + if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> + read_full_dwarf_from_debuginfod (objfile);
> +
> + dwarf2_base_index_functions::expand_all_symtabs (objfile);
> +}
> +
> +struct symtab *
> +dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
> +{
> + if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> + read_full_dwarf_from_debuginfod (objfile);
> +
> + return dwarf2_base_index_functions::find_last_source_symtab (objfile);
> +}
> +
> /* 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
> @@ -656,28 +683,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 769ca91facc..d07e0d86970 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -33,6 +33,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 "run-on-main-thread.h"
> #include "dwarf2/parent-map.h"
> #include "dwarf2/error.h"
> +#include "inferior.h"
> +#include "debuginfod-support.h"
>
> /* When == 1, print basic high level tracing messages.
> When > 1, be more verbose.
> @@ -2367,6 +2370,15 @@ dw2_expand_symtabs_matching_symbol
> || (symbol_matcher != NULL && !symbol_matcher (qualified)))
> continue;
>
> + /* There is a match but this objfile's debuginfo has not been
> + acquired yet. Download it then return early to expand CUs
> + from the debuginfo instead. */
> + if ((per_objfile->objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> + {
> + read_full_dwarf_from_debuginfod (per_objfile->objfile);
> + return false;
> + }
> +
> matches.push_back (bounds.first->idx);
> }
> }
> @@ -3016,6 +3028,15 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> if (data == nullptr)
> return nullptr;
>
> + if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> + {
> + /* PC matches a symbol in the index but full debuginfo hasn't
> + been acquired yet. Download it and return early in order to
> + expand symtabs in the debuginfo. */
> + read_full_dwarf_from_debuginfod (objfile);
> + return nullptr;
> + }
> +
> if (warn_if_readin && per_objfile->symtab_set_p (data))
> warning (_("(Internal error: pc %s in read in CU, but not in symtab.)"),
> paddress (objfile->arch (), pc));
> @@ -3117,6 +3138,9 @@ dwarf2_base_index_functions::has_symbols (struct objfile *objfile)
> bool
> dwarf2_base_index_functions::has_unexpanded_symtabs (struct objfile *objfile)
> {
> + if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> + return true;
> +
> dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
>
> for (const auto &per_cu : per_objfile->per_bfd->all_units)
> @@ -3186,6 +3210,31 @@ 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
> + (index_path.get (), &per_bfd->index_cache_res);
> +}
> +
> +
> static void start_debug_info_reader (dwarf2_per_objfile *);
>
> /* See dwarf2/public.h. */
> @@ -3195,11 +3244,13 @@ 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);
> dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> + bool separate_index = false;
>
> dwarf_read_debug_printf ("called");
>
> @@ -3235,6 +3286,15 @@ dwarf2_initialize_objfile (struct objfile *objfile,
> dwarf_read_debug_printf ("found gdb index from cache");
> global_index_cache.hit ();
> }
> + /* 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");
> + separate_index = true;
> + }
> else
> {
> global_index_cache.miss ();
> @@ -3246,11 +3306,91 @@ dwarf2_initialize_objfile (struct objfile *objfile,
> if (dwarf_synchronous)
> per_bfd->index_table->wait_completely ();
> objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
> +
> + if (separate_index)
> + objfile->qf.begin ()->get ()->from_separate_index = true;
> }
>
> return true;
> }
>
> +/* See read.h. */
> +
> +void
> +read_full_dwarf_from_debuginfod (struct objfile *objfile)
> +{
> + 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 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. Associate the
> + index with this objfile and defer the debuginfo download until symtabs
> + referenced by the index need to be expanded. */
> + 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 b23972b7a4e..49ba9d82117 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -919,4 +919,9 @@ 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 *);
> +
> #endif /* DWARF2READ_H */
> diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c
> index e0b63fd0e08..e5bcd6eb724 100644
> --- a/gdb/dwarf2/section.c
> +++ b/gdb/dwarf2/section.c
> @@ -54,7 +54,11 @@ dwarf2_section_info::get_bfd_owner () const
> section = get_containing_section ();
> gdb_assert (!section->is_virtual);
> }
> - gdb_assert (section->s.section != nullptr);
> +
> + /* This error may occur when attempting to expand symtabs for an objfile
> + with OBJF_DOWNLOAD_DEFERRED set. */
> + 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 5371b99f051..7fb34c1f00c 100644
> --- a/gdb/elfread.c
> +++ b/gdb/elfread.c
> @@ -1217,7 +1217,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 a6900b28072..54c632079bd 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -1778,14 +1778,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;
> @@ -1857,6 +1853,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 43d888e2a5a..08e4a75a9f2 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 7d8181c3093..f939517882a 100644
> --- a/gdb/objfiles.h
> +++ b/gdb/objfiles.h
> @@ -626,6 +626,25 @@ struct objfile
> domain_search_flags domain,
> bool *symbol_found_p);
>
> + /* Used to clear OBJF_DOWNLOAD_DEFERRED status when the debug objfile has
> + either 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 d6de7340065..149a2a683f3 100644
> --- a/gdb/quick-symbol.h
> +++ b/gdb/quick-symbol.h
> @@ -191,6 +191,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 07c90c9c941..376343ac7f4 100644
> --- a/gdb/symfile-debug.c
> +++ b/gdb/symfile-debug.c
> @@ -36,6 +36,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
> @@ -601,6 +602,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 cf7ab0b94e6..10c9aa0cea6 100644
> --- a/gdb/symfile.c
> +++ b/gdb/symfile.c
> @@ -979,6 +979,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. */
> @@ -989,7 +993,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 ();
> }
> @@ -1112,6 +1116,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 ce5e2520bd1..df0494042f8 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -2989,14 +2989,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
> + 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..381791bca50
> --- /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 ".*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 0096448567e..4d8bdcdc7cb 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 d4d4acb2313..de9a06e87bb 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -9905,10 +9905,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.46.0
>
Ping
Thanks,
Aaron
On Thu, Sep 19, 2024 at 9:47 AM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Tue, Sep 3, 2024 at 11:11 PM Aaron Merey <amerey@redhat.com> wrote:
> >
> > 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 in this patch include dwarf2_has_separate_index
> > which downloads the separate .gdb_index. If successful, a shared library
> > objfile owns the index until its separate debug objfile is downloaded
> > or found to be unavailable.
> >
> > read_full_dwarf_from_debuginfod downloads the full debuginfo and
> > initializes the separate debug objfile. It is called by functions
> > such as dw2_expand_symtabs_matching_symbol and
> > dwarf2_gdb_index::expand_all_symtabs when symtab expansion is required.
> > ---
> > v7 changes:
> > No longer use try/catch statements to trigger the download of deferred
> > debuginfo. These have been replaced with calls to
> > read_full_dwarf_from_debuginfod placed where the need for full debuginfo
> > is detected.
> >
> > gdb/dwarf2/frame.c | 13 ++
> > gdb/dwarf2/frame.h | 4 +
> > gdb/dwarf2/index-cache.c | 22 ++-
> > gdb/dwarf2/index-cache.h | 11 ++
> > gdb/dwarf2/public.h | 5 +
> > gdb/dwarf2/read-gdb-index.c | 63 ++++++---
> > gdb/dwarf2/read.c | 142 ++++++++++++++++++-
> > gdb/dwarf2/read.h | 5 +
> > gdb/dwarf2/section.c | 6 +-
> > gdb/elfread.c | 3 +-
> > gdb/frame.c | 27 +++-
> > gdb/objfile-flags.h | 4 +
> > gdb/objfiles.h | 19 +++
> > 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, 634 insertions(+), 34 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 79ef065bfcb..a4675b0e7ef 100644
> > --- a/gdb/dwarf2/frame.c
> > +++ b/gdb/dwarf2/frame.c
> > @@ -1619,6 +1619,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 2167310fbdf..423a9355b8a 100644
> > --- a/gdb/dwarf2/frame.h
> > +++ b/gdb/dwarf2/frame.h
> > @@ -237,6 +237,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
> > extern const struct frame_base *
> > dwarf2_frame_base_sniffer (const 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 (const frame_info_ptr &this_frame);
> > diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
> > index a04d5d662d8..3d52693a38d 100644
> > --- a/gdb/dwarf2/index-cache.c
> > +++ b/gdb/dwarf2/index-cache.c
> > @@ -217,14 +217,22 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
> > /* Compute where we would expect a gdb index file for this build id to be. */
> > std::string filename = make_index_filename (build_id, INDEX4_SUFFIX);
> >
> > + return lookup_gdb_index (filename.c_str (), resource);
> > +}
> > +
> > +/* See index-cache.h. */
> > +
> > +gdb::array_view<const gdb_byte>
> > +index_cache::lookup_gdb_index (const char *filename,
> > + std::unique_ptr<index_cache_resource> *resource)
> > +{
> > try
> > {
> > - index_cache_debug ("trying to read %s",
> > - filename.c_str ());
> > + index_cache_debug ("trying to read %s", filename);
> >
> > /* Try to map that file. */
> > index_cache_resource_mmap *mmap_resource
> > - = new index_cache_resource_mmap (filename.c_str ());
> > + = new index_cache_resource_mmap (filename);
> >
> > /* Yay, it worked! Hand the resource to the caller. */
> > resource->reset (mmap_resource);
> > @@ -236,7 +244,7 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
> > catch (const gdb_exception_error &except)
> > {
> > index_cache_debug ("couldn't read %s: %s",
> > - filename.c_str (), except.what ());
> > + filename, except.what ());
> > }
> >
> > return {};
> > @@ -253,6 +261,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 08a2d56493c..0c3f574e1b9 100644
> > --- a/gdb/dwarf2/index-cache.h
> > +++ b/gdb/dwarf2/index-cache.h
> > @@ -91,6 +91,17 @@ 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. 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. This
> > + function does not exit early if the index cache has not been enabled. */
> > + gdb::array_view<const gdb_byte>
> > + lookup_gdb_index (const char *filename,
> > + 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 bc419ff208f..6523854386c 100644
> > --- a/gdb/dwarf2/public.h
> > +++ b/gdb/dwarf2/public.h
> > @@ -44,4 +44,9 @@ extern bool dwarf2_initialize_objfile
> >
> > extern void dwarf2_build_frame_info (struct objfile *);
> >
> > +/* Query debuginfod for the .gdb_index associated with OBJFILE.
> > + Used to defer separate debuginfo downloading until necessary. */
> > +
> > +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 8cd665cee37..5938440a832 100644
> > --- a/gdb/dwarf2/read-gdb-index.c
> > +++ b/gdb/dwarf2/read-gdb-index.c
> > @@ -151,6 +151,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> > gdb.dwarf2/gdb-index.exp testcase. */
> > void dump (struct objfile *objfile) override;
> >
> > + /* Expand symtabs matching a given symbol or file. */
> > bool expand_symtabs_matching
> > (struct objfile *objfile,
> > gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> > @@ -159,8 +160,34 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> > gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> > block_search_flags search_flags,
> > domain_search_flags domain) override;
> > +
> > + /* 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)
> > +{
> > + if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> > + read_full_dwarf_from_debuginfod (objfile);
> > +
> > + dwarf2_base_index_functions::expand_all_symtabs (objfile);
> > +}
> > +
> > +struct symtab *
> > +dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
> > +{
> > + if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> > + read_full_dwarf_from_debuginfod (objfile);
> > +
> > + return dwarf2_base_index_functions::find_last_source_symtab (objfile);
> > +}
> > +
> > /* 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
> > @@ -656,28 +683,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 769ca91facc..d07e0d86970 100644
> > --- a/gdb/dwarf2/read.c
> > +++ b/gdb/dwarf2/read.c
> > @@ -33,6 +33,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 "run-on-main-thread.h"
> > #include "dwarf2/parent-map.h"
> > #include "dwarf2/error.h"
> > +#include "inferior.h"
> > +#include "debuginfod-support.h"
> >
> > /* When == 1, print basic high level tracing messages.
> > When > 1, be more verbose.
> > @@ -2367,6 +2370,15 @@ dw2_expand_symtabs_matching_symbol
> > || (symbol_matcher != NULL && !symbol_matcher (qualified)))
> > continue;
> >
> > + /* There is a match but this objfile's debuginfo has not been
> > + acquired yet. Download it then return early to expand CUs
> > + from the debuginfo instead. */
> > + if ((per_objfile->objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> > + {
> > + read_full_dwarf_from_debuginfod (per_objfile->objfile);
> > + return false;
> > + }
> > +
> > matches.push_back (bounds.first->idx);
> > }
> > }
> > @@ -3016,6 +3028,15 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
> > if (data == nullptr)
> > return nullptr;
> >
> > + if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> > + {
> > + /* PC matches a symbol in the index but full debuginfo hasn't
> > + been acquired yet. Download it and return early in order to
> > + expand symtabs in the debuginfo. */
> > + read_full_dwarf_from_debuginfod (objfile);
> > + return nullptr;
> > + }
> > +
> > if (warn_if_readin && per_objfile->symtab_set_p (data))
> > warning (_("(Internal error: pc %s in read in CU, but not in symtab.)"),
> > paddress (objfile->arch (), pc));
> > @@ -3117,6 +3138,9 @@ dwarf2_base_index_functions::has_symbols (struct objfile *objfile)
> > bool
> > dwarf2_base_index_functions::has_unexpanded_symtabs (struct objfile *objfile)
> > {
> > + if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> > + return true;
> > +
> > dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
> >
> > for (const auto &per_cu : per_objfile->per_bfd->all_units)
> > @@ -3186,6 +3210,31 @@ 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
> > + (index_path.get (), &per_bfd->index_cache_res);
> > +}
> > +
> > +
> > static void start_debug_info_reader (dwarf2_per_objfile *);
> >
> > /* See dwarf2/public.h. */
> > @@ -3195,11 +3244,13 @@ 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);
> > dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> > + bool separate_index = false;
> >
> > dwarf_read_debug_printf ("called");
> >
> > @@ -3235,6 +3286,15 @@ dwarf2_initialize_objfile (struct objfile *objfile,
> > dwarf_read_debug_printf ("found gdb index from cache");
> > global_index_cache.hit ();
> > }
> > + /* 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");
> > + separate_index = true;
> > + }
> > else
> > {
> > global_index_cache.miss ();
> > @@ -3246,11 +3306,91 @@ dwarf2_initialize_objfile (struct objfile *objfile,
> > if (dwarf_synchronous)
> > per_bfd->index_table->wait_completely ();
> > objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
> > +
> > + if (separate_index)
> > + objfile->qf.begin ()->get ()->from_separate_index = true;
> > }
> >
> > return true;
> > }
> >
> > +/* See read.h. */
> > +
> > +void
> > +read_full_dwarf_from_debuginfod (struct objfile *objfile)
> > +{
> > + 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 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. Associate the
> > + index with this objfile and defer the debuginfo download until symtabs
> > + referenced by the index need to be expanded. */
> > + 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 b23972b7a4e..49ba9d82117 100644
> > --- a/gdb/dwarf2/read.h
> > +++ b/gdb/dwarf2/read.h
> > @@ -919,4 +919,9 @@ 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 *);
> > +
> > #endif /* DWARF2READ_H */
> > diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c
> > index e0b63fd0e08..e5bcd6eb724 100644
> > --- a/gdb/dwarf2/section.c
> > +++ b/gdb/dwarf2/section.c
> > @@ -54,7 +54,11 @@ dwarf2_section_info::get_bfd_owner () const
> > section = get_containing_section ();
> > gdb_assert (!section->is_virtual);
> > }
> > - gdb_assert (section->s.section != nullptr);
> > +
> > + /* This error may occur when attempting to expand symtabs for an objfile
> > + with OBJF_DOWNLOAD_DEFERRED set. */
> > + 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 5371b99f051..7fb34c1f00c 100644
> > --- a/gdb/elfread.c
> > +++ b/gdb/elfread.c
> > @@ -1217,7 +1217,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 a6900b28072..54c632079bd 100644
> > --- a/gdb/frame.c
> > +++ b/gdb/frame.c
> > @@ -1778,14 +1778,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;
> > @@ -1857,6 +1853,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 43d888e2a5a..08e4a75a9f2 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 7d8181c3093..f939517882a 100644
> > --- a/gdb/objfiles.h
> > +++ b/gdb/objfiles.h
> > @@ -626,6 +626,25 @@ struct objfile
> > domain_search_flags domain,
> > bool *symbol_found_p);
> >
> > + /* Used to clear OBJF_DOWNLOAD_DEFERRED status when the debug objfile has
> > + either 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 d6de7340065..149a2a683f3 100644
> > --- a/gdb/quick-symbol.h
> > +++ b/gdb/quick-symbol.h
> > @@ -191,6 +191,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 07c90c9c941..376343ac7f4 100644
> > --- a/gdb/symfile-debug.c
> > +++ b/gdb/symfile-debug.c
> > @@ -36,6 +36,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
> > @@ -601,6 +602,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 cf7ab0b94e6..10c9aa0cea6 100644
> > --- a/gdb/symfile.c
> > +++ b/gdb/symfile.c
> > @@ -979,6 +979,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. */
> > @@ -989,7 +993,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 ();
> > }
> > @@ -1112,6 +1116,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 ce5e2520bd1..df0494042f8 100644
> > --- a/gdb/symtab.c
> > +++ b/gdb/symtab.c
> > @@ -2989,14 +2989,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
> > + 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..381791bca50
> > --- /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 ".*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 0096448567e..4d8bdcdc7cb 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 d4d4acb2313..de9a06e87bb 100644
> > --- a/gdb/testsuite/lib/gdb.exp
> > +++ b/gdb/testsuite/lib/gdb.exp
> > @@ -9905,10 +9905,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.46.0
> >
@@ -1619,6 +1619,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. */
@@ -237,6 +237,10 @@ void dwarf2_append_unwinders (struct gdbarch *gdbarch);
extern const struct frame_base *
dwarf2_frame_base_sniffer (const 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 (const frame_info_ptr &this_frame);
@@ -217,14 +217,22 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
/* Compute where we would expect a gdb index file for this build id to be. */
std::string filename = make_index_filename (build_id, INDEX4_SUFFIX);
+ return lookup_gdb_index (filename.c_str (), resource);
+}
+
+/* See index-cache.h. */
+
+gdb::array_view<const gdb_byte>
+index_cache::lookup_gdb_index (const char *filename,
+ std::unique_ptr<index_cache_resource> *resource)
+{
try
{
- index_cache_debug ("trying to read %s",
- filename.c_str ());
+ index_cache_debug ("trying to read %s", filename);
/* Try to map that file. */
index_cache_resource_mmap *mmap_resource
- = new index_cache_resource_mmap (filename.c_str ());
+ = new index_cache_resource_mmap (filename);
/* Yay, it worked! Hand the resource to the caller. */
resource->reset (mmap_resource);
@@ -236,7 +244,7 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id,
catch (const gdb_exception_error &except)
{
index_cache_debug ("couldn't read %s: %s",
- filename.c_str (), except.what ());
+ filename, except.what ());
}
return {};
@@ -253,6 +261,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. */
@@ -91,6 +91,17 @@ 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. 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. This
+ function does not exit early if the index cache has not been enabled. */
+ gdb::array_view<const gdb_byte>
+ lookup_gdb_index (const char *filename,
+ std::unique_ptr<index_cache_resource> *resource);
+
/* Return the number of cache hits. */
unsigned int n_hits () const
{ return m_n_hits; }
@@ -44,4 +44,9 @@ extern bool dwarf2_initialize_objfile
extern void dwarf2_build_frame_info (struct objfile *);
+/* Query debuginfod for the .gdb_index associated with OBJFILE.
+ Used to defer separate debuginfo downloading until necessary. */
+
+extern bool dwarf2_has_separate_index (struct objfile *);
+
#endif /* DWARF2_PUBLIC_H */
@@ -151,6 +151,7 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
gdb.dwarf2/gdb-index.exp testcase. */
void dump (struct objfile *objfile) override;
+ /* Expand symtabs matching a given symbol or file. */
bool expand_symtabs_matching
(struct objfile *objfile,
gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
@@ -159,8 +160,34 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
block_search_flags search_flags,
domain_search_flags domain) override;
+
+ /* 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)
+{
+ if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
+ read_full_dwarf_from_debuginfod (objfile);
+
+ dwarf2_base_index_functions::expand_all_symtabs (objfile);
+}
+
+struct symtab *
+dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
+{
+ if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
+ read_full_dwarf_from_debuginfod (objfile);
+
+ return dwarf2_base_index_functions::find_last_source_symtab (objfile);
+}
+
/* 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
@@ -656,28 +683,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;
+ }
}
}
@@ -33,6 +33,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 "run-on-main-thread.h"
#include "dwarf2/parent-map.h"
#include "dwarf2/error.h"
+#include "inferior.h"
+#include "debuginfod-support.h"
/* When == 1, print basic high level tracing messages.
When > 1, be more verbose.
@@ -2367,6 +2370,15 @@ dw2_expand_symtabs_matching_symbol
|| (symbol_matcher != NULL && !symbol_matcher (qualified)))
continue;
+ /* There is a match but this objfile's debuginfo has not been
+ acquired yet. Download it then return early to expand CUs
+ from the debuginfo instead. */
+ if ((per_objfile->objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
+ {
+ read_full_dwarf_from_debuginfod (per_objfile->objfile);
+ return false;
+ }
+
matches.push_back (bounds.first->idx);
}
}
@@ -3016,6 +3028,15 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab
if (data == nullptr)
return nullptr;
+ if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
+ {
+ /* PC matches a symbol in the index but full debuginfo hasn't
+ been acquired yet. Download it and return early in order to
+ expand symtabs in the debuginfo. */
+ read_full_dwarf_from_debuginfod (objfile);
+ return nullptr;
+ }
+
if (warn_if_readin && per_objfile->symtab_set_p (data))
warning (_("(Internal error: pc %s in read in CU, but not in symtab.)"),
paddress (objfile->arch (), pc));
@@ -3117,6 +3138,9 @@ dwarf2_base_index_functions::has_symbols (struct objfile *objfile)
bool
dwarf2_base_index_functions::has_unexpanded_symtabs (struct objfile *objfile)
{
+ if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
+ return true;
+
dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
for (const auto &per_cu : per_objfile->per_bfd->all_units)
@@ -3186,6 +3210,31 @@ 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
+ (index_path.get (), &per_bfd->index_cache_res);
+}
+
+
static void start_debug_info_reader (dwarf2_per_objfile *);
/* See dwarf2/public.h. */
@@ -3195,11 +3244,13 @@ 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);
dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
+ bool separate_index = false;
dwarf_read_debug_printf ("called");
@@ -3235,6 +3286,15 @@ dwarf2_initialize_objfile (struct objfile *objfile,
dwarf_read_debug_printf ("found gdb index from cache");
global_index_cache.hit ();
}
+ /* 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");
+ separate_index = true;
+ }
else
{
global_index_cache.miss ();
@@ -3246,11 +3306,91 @@ dwarf2_initialize_objfile (struct objfile *objfile,
if (dwarf_synchronous)
per_bfd->index_table->wait_completely ();
objfile->qf.push_front (per_bfd->index_table->make_quick_functions ());
+
+ if (separate_index)
+ objfile->qf.begin ()->get ()->from_separate_index = true;
}
return true;
}
+/* See read.h. */
+
+void
+read_full_dwarf_from_debuginfod (struct objfile *objfile)
+{
+ 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 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. Associate the
+ index with this objfile and defer the debuginfo download until symtabs
+ referenced by the index need to be expanded. */
+ objfile->flags |= OBJF_DOWNLOAD_DEFERRED;
+ dwarf2_initialize_objfile (objfile);
+ return true;
+}
+
/* Find the base address of the compilation unit for range lists and
@@ -919,4 +919,9 @@ 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 *);
+
#endif /* DWARF2READ_H */
@@ -54,7 +54,11 @@ dwarf2_section_info::get_bfd_owner () const
section = get_containing_section ();
gdb_assert (!section->is_virtual);
}
- gdb_assert (section->s.section != nullptr);
+
+ /* This error may occur when attempting to expand symtabs for an objfile
+ with OBJF_DOWNLOAD_DEFERRED set. */
+ if (section->s.section == nullptr)
+ error (_("Can't find owner of DWARF section"));
return section->s.section->owner;
}
@@ -1217,7 +1217,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;
}
@@ -1778,14 +1778,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;
@@ -1857,6 +1853,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 ()
{
@@ -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);
@@ -626,6 +626,25 @@ struct objfile
domain_search_flags domain,
bool *symbol_found_p);
+ /* Used to clear OBJF_DOWNLOAD_DEFERRED status when the debug objfile has
+ either 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
{
@@ -191,6 +191,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;
@@ -36,6 +36,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
@@ -601,6 +602,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
@@ -979,6 +979,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. */
@@ -989,7 +993,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 ();
}
@@ -1112,6 +1116,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;
@@ -2989,14 +2989,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
+ warn_if_readin = 1;
}
return NULL;
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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 ".*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
@@ -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
+ }
+ }
+}
@@ -9905,10 +9905,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" } {