[3/3,v6] gdb/debuginfod: Add .debug_line downloading

Message ID 20240904030931.3408937-4-amerey@redhat.com
State New
Headers
Series 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

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

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

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

This patch also redirects stdout and stderr of the debuginfod server
used by testsuite/gdb.debuginfod tests to a server_log standard output
file.  While adding tests for this patch I ran into an issue where the
test server would block when logging to stderr, presumably because the
stderr buffer filled up and wasn't being read from.  Redirecting the
log to a file fixes this and also makes the server log more accessible
when debugging test failures.
---
 gdb/cli-out.c                            |  11 +-
 gdb/completer.c                          |  18 +-
 gdb/dwarf2/line-header.c                 | 161 +++++++++++------
 gdb/dwarf2/line-header.h                 |  10 ++
 gdb/dwarf2/read-gdb-index.c              |  66 ++++++-
 gdb/dwarf2/read.c                        | 209 +++++++++++++++++++++++
 gdb/dwarf2/read.h                        |  37 ++++
 gdb/mi/mi-out.c                          |   9 +-
 gdb/testsuite/gdb.debuginfod/section.exp |  28 +++
 gdb/ui-out.c                             |   3 +
 gdb/ui-out.h                             |  20 +++
 11 files changed, 518 insertions(+), 54 deletions(-)
  

Comments

Aaron Merey Sept. 19, 2024, 1:47 p.m. UTC | #1
Ping

Thanks,
Aaron

On Tue, Sep 3, 2024 at 11:11 PM Aaron Merey <amerey@redhat.com> wrote:
>
> ELF/DWARF section downloading allows gdb to download .gdb_index files in
> order to defer full debuginfo downloads.  However .gdb_index does not
> contain any information regarding source filenames.  When a gdb command
> includes a filename argument (ex. 'break main.c:50'), this results in
> the mass downloading of all deferred debuginfo so that gdb can search the
> debuginfo for matching source filenames.  This can result in unnecessary
> downloads.
>
> To improve this, have gdb instead download each debuginfo's .debug_line
> (and .debug_line_str if using DWARF5) when executing these commands.
> Download full debuginfo only when its .debug_line contains a matching
> filename.
>
> Since the combined size of .debug_line and .debug_line_str is only about
> 1% the size of the corresponding debuginfo, significant time can be saved
> by checking these sections before choosing to download an entire debuginfo.
>
> This patch also redirects stdout and stderr of the debuginfod server
> used by testsuite/gdb.debuginfod tests to a server_log standard output
> file.  While adding tests for this patch I ran into an issue where the
> test server would block when logging to stderr, presumably because the
> stderr buffer filled up and wasn't being read from.  Redirecting the
> log to a file fixes this and also makes the server log more accessible
> when debugging test failures.
> ---
>  gdb/cli-out.c                            |  11 +-
>  gdb/completer.c                          |  18 +-
>  gdb/dwarf2/line-header.c                 | 161 +++++++++++------
>  gdb/dwarf2/line-header.h                 |  10 ++
>  gdb/dwarf2/read-gdb-index.c              |  66 ++++++-
>  gdb/dwarf2/read.c                        | 209 +++++++++++++++++++++++
>  gdb/dwarf2/read.h                        |  37 ++++
>  gdb/mi/mi-out.c                          |   9 +-
>  gdb/testsuite/gdb.debuginfod/section.exp |  28 +++
>  gdb/ui-out.c                             |   3 +
>  gdb/ui-out.h                             |  20 +++
>  11 files changed, 518 insertions(+), 54 deletions(-)
>
> diff --git a/gdb/cli-out.c b/gdb/cli-out.c
> index 1c303f09662..d65b69ebe01 100644
> --- a/gdb/cli-out.c
> +++ b/gdb/cli-out.c
> @@ -306,16 +306,23 @@ cli_ui_out::do_progress_notify (const std::string &msg,
>
>    if (info.state == progress_update::START)
>      {
> +      std::string prefix;
> +      if (cur_prefix_state == prefix_state_t::NEWLINE_NEEDED)
> +       {
> +         prefix = "\n";
> +         cur_prefix_state = prefix_state_t::NEWLINE_PRINTED;
> +       }
> +
>        if (stream->isatty ()
>           && current_ui->input_interactive_p ()
>           && chars_per_line >= MIN_CHARS_PER_LINE)
>         {
> -         gdb_printf (stream, "%s\n", msg.c_str ());
> +         gdb_printf (stream, "%s\n", (prefix + msg).c_str ());
>           info.state = progress_update::BAR;
>         }
>        else
>         {
> -         gdb_printf (stream, "%s...\n", msg.c_str ());
> +         gdb_printf (stream, "%s...\n", (prefix + msg).c_str ());
>           info.state = progress_update::WORKING;
>         }
>      }
> diff --git a/gdb/completer.c b/gdb/completer.c
> index 1008ec23ba5..4bb054f6034 100644
> --- a/gdb/completer.c
> +++ b/gdb/completer.c
> @@ -1354,6 +1354,10 @@ complete_line_internal_1 (completion_tracker &tracker,
>      {
>        /* We've recognized a full command.  */
>
> +      /* Disable pagination since responding to the pagination prompt
> +        overwrites rl_line_buffer.  */
> +      scoped_restore pag_restore = make_scoped_restore (&pagination_enabled, false);
> +
>        if (p == tmp_command + point)
>         {
>           /* There is no non-whitespace in the line beyond the
> @@ -1453,7 +1457,8 @@ complete_line_internal_1 (completion_tracker &tracker,
>  }
>
>  /* Wrapper around complete_line_internal_1 to handle
> -   MAX_COMPLETIONS_REACHED_ERROR.  */
> +   MAX_COMPLETIONS_REACHED_ERROR and possible progress update
> +   interactions.  */
>
>  static void
>  complete_line_internal (completion_tracker &tracker,
> @@ -1461,6 +1466,11 @@ complete_line_internal (completion_tracker &tracker,
>                         const char *line_buffer, int point,
>                         complete_line_internal_reason reason)
>  {
> +  scoped_restore restore_prefix_state
> +    = make_scoped_restore
> +      (&cur_prefix_state,
> +       ui_out::progress_update::prefix_state::NEWLINE_NEEDED);
> +
>    try
>      {
>        complete_line_internal_1 (tracker, text, line_buffer, point, reason);
> @@ -1470,6 +1480,12 @@ complete_line_internal (completion_tracker &tracker,
>        if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
>         throw;
>      }
> +
> +  /* If progress update messages printed, then the text being completed
> +     needs to be printed again.  */
> +  if (cur_prefix_state
> +      == ui_out::progress_update::prefix_state::NEWLINE_PRINTED)
> +    rl_forced_update_display ();
>  }
>
>  /* See completer.h.  */
> diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> index eddb2ef7ae8..1d24386c0eb 100644
> --- a/gdb/dwarf2/line-header.c
> +++ b/gdb/dwarf2/line-header.c
> @@ -101,12 +101,15 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
>  {
>    LONGEST length = read_initial_length (abfd, buf, bytes_read);
>
> -  gdb_assert (cu_header->initial_length_size == 4
> -             || cu_header->initial_length_size == 8
> -             || cu_header->initial_length_size == 12);
> +  if (cu_header != nullptr)
> +    {
> +      gdb_assert (cu_header->initial_length_size == 4
> +                 || cu_header->initial_length_size == 8
> +                 || cu_header->initial_length_size == 12);
>
> -  if (cu_header->initial_length_size != *bytes_read)
> -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> +      if (cu_header->initial_length_size != *bytes_read)
> +       complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> +    }
>
>    *offset_size = (*bytes_read == 4) ? 4 : 8;
>    return length;
> @@ -115,21 +118,27 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
>  /* Read directory or file name entry format, starting with byte of
>     format count entries, ULEB128 pairs of entry formats, ULEB128 of
>     entries count and the entries themselves in the described entry
> -   format.  */
> +   format.
> +
> +   .debug_line and .debug_line_str sections are stored in LINE_BUFP
> +   and LINE_STR_DATA respectively.  */
>
>  static void
> -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> -                       const gdb_byte **bufp, struct line_header *lh,
> -                       unsigned int offset_size,
> -                       void (*callback) (struct line_header *lh,
> -                                         const char *name,
> -                                         dir_index d_index,
> -                                         unsigned int mod_time,
> -                                         unsigned int length))
> +read_formatted_entries
> +  (bfd *abfd, const gdb_byte **line_bufp,
> +   const gdb::array_view<const gdb_byte> line_str_data,
> +   struct line_header *lh,
> +   unsigned int offset_size,
> +   void (*callback) (struct line_header *lh,
> +                    const char *name,
> +                    dir_index d_index,
> +                    unsigned int mod_time,
> +                    unsigned int length))
>  {
>    gdb_byte format_count, formati;
>    ULONGEST data_count, datai;
> -  const gdb_byte *buf = *bufp;
> +  const gdb_byte *buf = *line_bufp;
> +  const gdb_byte *str_buf = line_str_data.data ();
>    const gdb_byte *format_header_data;
>    unsigned int bytes_read;
>
> @@ -156,7 +165,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>           ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
>           format += bytes_read;
>
> -         ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> +         ULONGEST form = read_unsigned_leb128 (abfd, format, &bytes_read);
>           format += bytes_read;
>
>           std::optional<const char *> string;
> @@ -171,12 +180,24 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>
>             case DW_FORM_line_strp:
>               {
> -               const char *str
> -                 = per_objfile->read_line_string (buf, offset_size);
> +               if (line_str_data.empty ())
> +                 warning (_("Dwarf Error: DW_FORM_line_strp used without " \
> +                            "required section"));
> +
> +               if (line_str_data.size () <= offset_size)
> +                 warning (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> +                            "of section .debug_line_str"));
> +
> +               ULONGEST str_offset = read_offset (abfd, buf, offset_size);
> +               const char *str;
> +               if (str_buf[str_offset] == '\0')
> +                 str = nullptr;
> +               else
> +                 str = (const char *) (str_buf + str_offset);
>                 string.emplace (str);
>                 buf += offset_size;
> +               break;
>               }
> -             break;
>
>             case DW_FORM_data1:
>               uint.emplace (read_1_byte (abfd, buf));
> @@ -247,28 +268,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
>      }
>
> -  *bufp = buf;
> +  *line_bufp = buf;
>  }
>
>  /* See line-header.h.  */
>
>  line_header_up
> -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> -                          dwarf2_per_objfile *per_objfile,
> -                          struct dwarf2_section_info *section,
> -                          const struct comp_unit_head *cu_header,
> -                          const char *comp_dir)
> +dwarf_decode_line_header (bfd *abfd,
> +                         gdb::array_view<const gdb_byte> line_data,
> +                         gdb::array_view<const gdb_byte> line_str_data,
> +                         const gdb_byte **debug_line_ptr,
> +                         bool is_dwz,
> +                         const struct comp_unit_head *cu_header,
> +                         const char *comp_dir)
>  {
> -  const gdb_byte *line_ptr;
> +  const gdb_byte *line_ptr, *buf;
>    unsigned int bytes_read, offset_size;
>    int i;
>    const char *cur_dir, *cur_file;
>
> -  bfd *abfd = section->get_bfd_owner ();
> +  buf = *debug_line_ptr;
>
>    /* Make sure that at least there's room for the total_length field.
>       That could be 12 bytes long, but we're just going to fudge that.  */
> -  if (to_underlying (sect_off) + 4 >= section->size)
> +  if (buf + 4 >= line_data.data () + line_data.size ())
>      {
>        dwarf2_statement_list_fits_in_line_number_section_complaint ();
>        return 0;
> @@ -276,24 +299,26 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>
>    line_header_up lh (new line_header (comp_dir));
>
> -  lh->sect_off = sect_off;
> +  lh->sect_off = (sect_offset) (buf - line_data.data ());
>    lh->offset_in_dwz = is_dwz;
>
> -  line_ptr = section->buffer + to_underlying (sect_off);
> +  line_ptr = buf;
>
>    /* Read in the header.  */
>    LONGEST unit_length
> -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> +    = read_checked_initial_length_and_offset (abfd, buf, cu_header,
>                                               &bytes_read, &offset_size);
> -  line_ptr += bytes_read;
>
> -  const gdb_byte *start_here = line_ptr;
> +  line_ptr += bytes_read;
>
> -  if (line_ptr + unit_length > (section->buffer + section->size))
> +  if (line_ptr + unit_length > buf + line_data.size ())
>      {
>        dwarf2_statement_list_fits_in_line_number_section_complaint ();
>        return 0;
>      }
> +
> +  const gdb_byte *start_here = line_ptr;
> +
>    lh->statement_program_end = start_here + unit_length;
>    lh->version = read_2_bytes (abfd, line_ptr);
>    line_ptr += 2;
> @@ -302,7 +327,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>        /* This is a version we don't understand.  The format could have
>          changed in ways we don't handle properly so just punt.  */
>        complaint (_("unsupported version in .debug_line section"));
> -      return NULL;
> +      return nullptr;
>      }
>    if (lh->version >= 5)
>      {
> @@ -319,13 +344,14 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>           complaint (_("unsupported segment selector size %u "
>                        "in .debug_line section"),
>                      segment_selector_size);
> -         return NULL;
> +         return nullptr;
>         }
>      }
>
>    LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
>    line_ptr += offset_size;
>    lh->statement_program_start = line_ptr + header_length;
> +
>    lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
>    line_ptr += 1;
>
> @@ -346,12 +372,16 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>
>    lh->default_is_stmt = read_1_byte (abfd, line_ptr);
>    line_ptr += 1;
> +
>    lh->line_base = read_1_signed_byte (abfd, line_ptr);
>    line_ptr += 1;
> +
>    lh->line_range = read_1_byte (abfd, line_ptr);
>    line_ptr += 1;
> +
>    lh->opcode_base = read_1_byte (abfd, line_ptr);
>    line_ptr += 1;
> +
>    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
>
>    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
> @@ -364,21 +394,23 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>    if (lh->version >= 5)
>      {
>        /* Read directory table.  */
> -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> -                             offset_size,
> -                             [] (struct line_header *header, const char *name,
> -                                 dir_index d_index, unsigned int mod_time,
> -                                 unsigned int length)
> +      read_formatted_entries
> +       (abfd, &line_ptr, line_str_data,
> +        lh.get (), offset_size,
> +        [] (struct line_header *header, const char *name,
> +            dir_index d_index, unsigned int mod_time,
> +            unsigned int length)
>         {
>           header->add_include_dir (name);
>         });
>
>        /* Read file name table.  */
> -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> -                             offset_size,
> -                             [] (struct line_header *header, const char *name,
> -                                 dir_index d_index, unsigned int mod_time,
> -                                 unsigned int length)
> +      read_formatted_entries
> +       (abfd, &line_ptr, line_str_data,
> +        lh.get (), offset_size,
> +        [] (struct line_header *header, const char *name,
> +            dir_index d_index, unsigned int mod_time,
> +            unsigned int length)
>         {
>           header->add_file_name (name, d_index, mod_time, length);
>         });
> @@ -386,7 +418,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>    else
>      {
>        /* Read directory table.  */
> -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> +      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != nullptr)
>         {
>           line_ptr += bytes_read;
>           lh->add_include_dir (cur_dir);
> @@ -394,7 +426,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>        line_ptr += bytes_read;
>
>        /* Read file name table.  */
> -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> +      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != nullptr)
>         {
>           unsigned int mod_time, length;
>           dir_index d_index;
> @@ -412,9 +444,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>        line_ptr += bytes_read;
>      }
>
> -  if (line_ptr > (section->buffer + section->size))
> +  if (line_ptr > (buf + line_data.size ()))
>      complaint (_("line number info header doesn't "
>                  "fit in `.debug_line' section"));
>
> +  *debug_line_ptr += unit_length + offset_size;
>    return lh;
>  }
> +
> +line_header_up
> +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> +                          dwarf2_per_objfile *per_objfile,
> +                          struct dwarf2_section_info *section,
> +                          const struct comp_unit_head *cu_header,
> +                          const char *comp_dir)
> +{
> +  struct objfile *objfile = per_objfile->objfile;
> +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> +
> +  /* Read .debug_line.  */
> +  dwarf2_section_info *line_sec = &per_bfd->line;
> +  bfd_size_type line_size = line_sec->get_size (objfile);
> +
> +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> +
> +  /* Read .debug_line_str.  */
> +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> +
> +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> +                                           line_str_size);
> +
> +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> +
> +  return dwarf_decode_line_header
> +    (per_bfd->obfd, line, line_str, &line_ptr,
> +     is_dwz, cu_header, comp_dir);
> +}
> diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> index 7da59725c98..a6288fb6773 100644
> --- a/gdb/dwarf2/line-header.h
> +++ b/gdb/dwarf2/line-header.h
> @@ -221,4 +221,14 @@ extern line_header_up dwarf_decode_line_header
>     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
>     const char *comp_dir);
>
> +/* Like above but the .debug_line and .debug_line_str are stored in
> +   LINE_DATA and LINE_STR_DATA.  *DEBUG_LINE_PTR should point to a
> +   statement program header within LINE_DATA.  */
> +
> +extern line_header_up dwarf_decode_line_header
> +  (bfd *abfd, gdb::array_view<const gdb_byte> line_data,
> +   gdb::array_view<const gdb_byte> line_str_data,
> +   const gdb_byte **debug_line_ptr, bool is_dwz,
> +  const comp_unit_head *cu_header, const char *comp_dir);
> +
>  #endif /* DWARF2_LINE_HEADER_H */
> diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> index 5938440a832..e5f0fffca97 100644
> --- a/gdb/dwarf2/read-gdb-index.c
> +++ b/gdb/dwarf2/read-gdb-index.c
> @@ -143,6 +143,9 @@ struct mapped_gdb_index final : public mapped_index_base
>    }
>  };
>
> +struct mapped_debug_line;
> +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> +
>  struct dwarf2_gdb_index : public dwarf2_base_index_functions
>  {
>    /* This dumps minimal information about the index.
> @@ -161,6 +164,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>       block_search_flags search_flags,
>       domain_search_flags domain) override;
>
> +  /* If OBJFILE's debuginfo download has been deferred, use a mapped_debug_line
> +     to generate filenames.
> +
> +     Otherwise call dwarf2_base_index_functions::map_symbol_filenames.  */
> +
> +  void map_symbol_filenames (struct objfile *objfile,
> +                            gdb::function_view<symbol_filename_ftype> fun,
> +                            bool need_fullname) override;
> +
>    /* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
>       debuginfo if necessary.  */
>    void expand_all_symtabs (struct objfile *objfile) override;
> @@ -168,6 +180,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
>       debuginfo if necessary.  */
>    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> +
> +  /* Filename information related to this .gdb_index.  */
> +  mapped_debug_line_up mdl;
> +
> +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> +  bool filename_in_debug_line
> +    (objfile *objfile,
> +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
>  };
>
>  void
> @@ -188,6 +209,23 @@ dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
>    return dwarf2_base_index_functions::find_last_source_symtab (objfile);
>  }
>
> +void
> +dwarf2_gdb_index::map_symbol_filenames
> +     (struct objfile *objfile,
> +      gdb::function_view<symbol_filename_ftype> fun,
> +      bool need_fullname)
> +{
> +  if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> +    {
> +      if (mdl == nullptr)
> +       mdl.reset (new mapped_debug_line (objfile));
> +      mdl->map_filenames (fun, need_fullname);
> +    }
> +  else
> +    dwarf2_base_index_functions::map_symbol_filenames (objfile, fun,
> +                                                      need_fullname);
> +}
> +
>  /* 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
> @@ -302,6 +340,17 @@ dw2_expand_marked_cus
>    return true;
>  }
>
> +bool
> +dwarf2_gdb_index::filename_in_debug_line
> +     (objfile *objfile,
> +      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  if (mdl == nullptr)
> +    mdl.reset (new mapped_debug_line (objfile));
> +
> +  return mdl->contains_matching_filename (file_matcher);
> +}
> +
>  bool
>  dwarf2_gdb_index::expand_symtabs_matching
>      (struct objfile *objfile,
> @@ -314,7 +363,22 @@ dwarf2_gdb_index::expand_symtabs_matching
>  {
>    dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
>
> -  dw_expand_symtabs_matching_file_matcher (per_objfile, file_matcher);
> +  if (file_matcher != nullptr)
> +    {
> +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> +       dw_expand_symtabs_matching_file_matcher (per_objfile, file_matcher);
> +      else if (filename_in_debug_line (objfile, file_matcher))
> +       {
> +         /* If OBJFILE's debuginfo hasn't been downloaded and there is a file
> +            name to be matched, download the .debug_line (and possibly
> +            .debug_line_str) separately.  Search the separate .debug_line for
> +            a match.  If there is a match, then download the full debuginfo.
> +            Return early to move on to expanding symtabs in the debuginfo
> +            objfile.  */
> +         read_full_dwarf_from_debuginfod (objfile);
> +         return true;
> +       }
> +    }
>
>    /* This invariant is documented in quick-functions.h.  */
>    gdb_assert (lookup_name != nullptr || symbol_matcher == nullptr);
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index d07e0d86970..5cb222bd2cc 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -81,6 +81,7 @@
>  #include <optional>
>  #include "gdbsupport/underlying.h"
>  #include "gdbsupport/hash_enum.h"
> +#include "gdbsupport/scoped_mmap.h"
>  #include "filename-seen-cache.h"
>  #include "producer.h"
>  #include <fcntl.h>
> @@ -99,6 +100,7 @@
>  #include "dwarf2/error.h"
>  #include "inferior.h"
>  #include "debuginfod-support.h"
> +#include "exceptions.h"
>
>  /* When == 1, print basic high level tracing messages.
>     When > 1, be more verbose.
> @@ -1953,6 +1955,213 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
>    return this_cu->file_names;
>  }
>
> +#if !HAVE_SYS_MMAN_H
> +
> +bool
> +mapped_debug_line::contains_matching_filename
> +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  return false;
> +}
> +
> +gdb::array_view<const gdb_byte>
> +mapped_debug_line::read_debug_line_separate
> +  (const char *filename, std::unique_ptr<index_cache_resource> *resource)
> +{
> +  return {};
> +}
> +
> +bool
> +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> +{
> +  return false;
> +}
> +
> +void
> +mapped_debug_line::map_filenames
> +  (gdb::function_view<symbol_filename_ftype> fun,
> +   bool need_fullname)
> +{
> +  return;
> +}
> +
> +#else /* !HAVE_SYS_MMAN_H */
> +
> +struct line_resource_mmap final : public index_cache_resource
> +{
> +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> +     file doesn't exist.  */
> +  line_resource_mmap (const char *filename)
> +    : mapping (mmap_file (filename))
> +  {}
> +
> +  scoped_mmap mapping;
> +};
> +
> +/* See read.h.  */
> +
> +bool
> +mapped_debug_line::contains_matching_filename
> +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  for (line_header_up &lh : line_headers)
> +    for (file_entry &fe : lh->file_names ())
> +      {
> +       const char *filename = fe.name;
> +
> +       if (file_matcher (fe.name, false))
> +         return true;
> +
> +       bool basename_match = file_matcher (lbasename (fe.name), true);
> +
> +       if (!basenames_may_differ && !basename_match)
> +         continue;
> +
> +       /* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> +          until DWARF5.  Since we don't have access to the CU at this
> +          point we just check for a partial match on the filename.
> +          If there is a match, the full debuginfo will be downloaded
> +          ane the match will be re-evalute with DW_AT_comp_dir.  */
> +       if (lh->version < 5 && fe.d_index == 0)
> +         return basename_match;
> +
> +       const char *dirname = fe.include_dir (&*lh);
> +       std::string fullname;
> +
> +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> +         fullname = filename;
> +       else
> +         fullname = std::string (dirname) + SLASH_STRING + filename;
> +
> +       gdb::unique_xmalloc_ptr<char> rewritten
> +         = rewrite_source_path (fullname.c_str ());
> +       if (rewritten != nullptr)
> +         fullname = rewritten.release ();
> +
> +       if (file_matcher (fullname.c_str (), false))
> +         return true;
> +      }
> +
> +  return false;
> +}
> +
> +/* See read.h.  */
> +
> +void
> +mapped_debug_line::map_filenames
> +  (gdb::function_view<symbol_filename_ftype> fun,
> +   bool need_fullname)
> +{
> +  for (line_header_up &lh : line_headers)
> +    for (file_entry &fe : lh->file_names ())
> +      {
> +       const char *filename = fe.name;
> +
> +       if (!need_fullname)
> +         {
> +           fun (filename, nullptr);
> +           continue;
> +         }
> +
> +       const char *dirname = fe.include_dir (&*lh);
> +       std::string fullname;
> +
> +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> +         fullname = filename;
> +       else
> +         fullname = std::string (dirname) + SLASH_STRING + filename;
> +
> +       gdb::unique_xmalloc_ptr<char> rewritten
> +         = rewrite_source_path (fullname.c_str ());
> +       if (rewritten != nullptr)
> +         fullname = rewritten.release ();
> +
> +       fun (filename, fullname.c_str ());
> +      }
> +}
> +
> +/* See read.h.  */
> +
> +gdb::array_view<const gdb_byte>
> +mapped_debug_line::read_debug_line_separate
> +  (const char *filename, std::unique_ptr<index_cache_resource> *resource)
> +{
> +  if (filename == nullptr)
> +    return {};
> +
> +  try
> +  {
> +    line_resource_mmap *mmap_resource
> +      = new line_resource_mmap (filename);
> +
> +    resource->reset (mmap_resource);
> +
> +    return gdb::array_view<const gdb_byte>
> +      ((const gdb_byte *) mmap_resource->mapping.get (),
> +       mmap_resource->mapping.size ());
> +  }
> +  catch (const gdb_exception &except)
> +  {
> +    exception_print (gdb_stderr, except);
> +  }
> +
> +  return {};
> +}
> +
> +/* See read.h.  */
> +
> +bool
> +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> +{
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  if (build_id == nullptr)
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> line_path;
> +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> +                                               build_id->size,
> +                                               bfd_get_filename
> +                                                 (objfile->obfd.get ()),
> +                                               ".debug_line",
> +                                               &line_path);
> +
> +  if (line_fd.get () < 0)
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> line_str_path;
> +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> +                                                   build_id->size,
> +                                                   bfd_get_filename
> +                                                     (objfile->obfd.get ()),
> +                                                   ".debug_line_str",
> +                                                   &line_str_path);
> +
> +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> +  line_str_data = read_debug_line_separate (line_str_path.get (),
> +                                           &line_str_resource);
> +
> +  const gdb_byte *line_ptr = line_data.data ();
> +
> +  while (line_ptr < line_data.data () + line_data.size ())
> +    {
> +      line_header_up lh
> +       = dwarf_decode_line_header (objfile->obfd.get (),
> +                                   line_data, line_str_data,
> +                                   &line_ptr, false,
> +                                   nullptr, nullptr);
> +      line_headers.emplace_back (std::move (lh));
> +    }
> +
> +  return true;
> +}
> +#endif /* !HAVE_SYS_MMAN_H */
> +
> +mapped_debug_line::mapped_debug_line (objfile *objfile)
> +{
> +  if (!read_debug_line_from_debuginfod (objfile))
> +    line_headers.clear ();
> +}
> +
>  /* A helper for the "quick" functions which computes and caches the
>     real path for a given file name from the line table.  */
>
> diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> index 49ba9d82117..aba4787e638 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -32,6 +32,7 @@
>  #include "gdbsupport/hash_enum.h"
>  #include "gdbsupport/function-view.h"
>  #include "gdbsupport/packed.h"
> +#include "dwarf2/line-header.h"
>
>  /* Hold 'maintenance (set|show) dwarf' commands.  */
>  extern struct cmd_list_element *set_dwarf_cmdlist;
> @@ -924,4 +925,40 @@ extern htab_up create_quick_file_names_table (unsigned int nr_initial_entries);
>
>  extern void read_full_dwarf_from_debuginfod (struct objfile *);
>
> +struct mapped_debug_line
> +{
> +  mapped_debug_line (objfile *objfile);
> +
> +  /* Return true if any of the mapped .debug_line's filenames match
> +     FILE_MATCHER.  */
> +
> +  bool contains_matching_filename
> +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> +
> +  /* Call FUN with each filename in this mapped .debug_line.  Include
> +     each file's fullname if NEED_FULLNAME is true.  */
> +
> +  void map_filenames (gdb::function_view<symbol_filename_ftype> fun,
> +                     bool need_fullname);
> +
> +private:
> +  std::vector<line_header_up> line_headers;
> +
> +  gdb::array_view<const gdb_byte> line_data;
> +  gdb::array_view<const gdb_byte> line_str_data;
> +
> +  std::unique_ptr<index_cache_resource> line_resource;
> +  std::unique_ptr<index_cache_resource> line_str_resource;
> +
> +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> +     and populate line_headers.  */
> +
> +  bool read_debug_line_from_debuginfod (objfile *objfile);
> +
> +  /* Initialize line_data and line_str_data with the .debug_line and
> +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> +
> +  gdb::array_view<const gdb_byte> read_debug_line_separate
> +    (const char *filename, std::unique_ptr<index_cache_resource> *resource);
> +};
>  #endif /* DWARF2READ_H */
> diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c
> index ff93d2cd448..8ad2c7f4497 100644
> --- a/gdb/mi/mi-out.c
> +++ b/gdb/mi/mi-out.c
> @@ -277,7 +277,14 @@ mi_ui_out::do_progress_notify (const std::string &msg, const char *unit,
>
>    if (info.state == progress_update::START)
>      {
> -      gdb_printf ("%s...\n", msg.c_str ());
> +      std::string prefix;
> +      if (cur_prefix_state == prefix_state_t::NEWLINE_NEEDED)
> +       {
> +         prefix = "\n";
> +         cur_prefix_state = prefix_state_t::NEWLINE_PRINTED;
> +       }
> +
> +      gdb_printf ("%s...\n", (prefix + msg).c_str ());
>        info.state = progress_update::WORKING;
>      }
>  }
> diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
> index 381791bca50..8e482364434 100644
> --- a/gdb/testsuite/gdb.debuginfod/section.exp
> +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> @@ -18,6 +18,7 @@
>  standard_testfile
>
>  load_lib debuginfod-support.exp
> +load_lib completion-support.exp
>
>  clean_restart
>  require allow_debuginfod_tests
> @@ -144,6 +145,33 @@ proc_with_prefix local_url { } {
>      # Hit the breakpoint.
>      set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
>      gdb_test "c" $res "break continue"
> +
> +    clean_restart_with_prompt $binfile "line 1"
> +
> +    # List source file using .debug_line download.
> +    set res ".*\.debug_line.*$lib_sl1.*21.*extern void libsection2_test.*"
> +    gdb_test "list $libsrc1:21" $res "line 1 list"
> +
> +    clean_restart_with_prompt $binfile "line 2"
> +
> +    # Set breakpoint using .debug_line download.
> +    set res ".*section \.debug_line for $lib_sl1.*Breakpoint 2 at.*$libsrc1.*"
> +    gdb_test "br $libsrc1:41" $res "line 2 br"
> +
> +    # Continue to breakpoint.
> +    set res "Breakpoint 2, libsection1_test.*\"Cancelling thread\\\\n\".*"
> +    gdb_test "c" $res "line 2 continue"
> +
> +    # Check that download progress message is correctly formatted
> +    # when printing threads.
> +    set res ".*separate debug info for $lib_sl2\.\.\.\r\n.* 2    Thread.*"
> +    gdb_test "info thr" $res "line thread"
> +
> +    clean_restart_with_prompt $binfile "autocomplete"
> +
> +    # Download debuginfo during autocompletion.
> +    test_gdb_complete_tab_unique "br lib" \
> +       ".*separate debug info for $lib_sl1.*" ""
>  }
>
>  # Create CACHE and DB directories ready for debuginfod to use.
> diff --git a/gdb/ui-out.c b/gdb/ui-out.c
> index 7e9f238fcf6..ad628b22c66 100644
> --- a/gdb/ui-out.c
> +++ b/gdb/ui-out.c
> @@ -31,6 +31,9 @@
>  #include <memory>
>  #include <string>
>
> +/* Current state of newline prefixing for progress update messages.  */
> +prefix_state_t cur_prefix_state = prefix_state_t::NEWLINE_OFF;
> +
>  namespace {
>
>  /* A header of a ui_out_table.  */
> diff --git a/gdb/ui-out.h b/gdb/ui-out.h
> index ef9ce4fad33..fa06a29e268 100644
> --- a/gdb/ui-out.h
> +++ b/gdb/ui-out.h
> @@ -299,6 +299,21 @@ class ui_out
>        BAR
>      };
>
> +    /* Used to communicate the status of a newline prefix for the next progress
> +       update message.  */
> +    enum prefix_state
> +    {
> +      /* Do not modify the next progress update message.  */
> +      NEWLINE_OFF,
> +
> +      /* The next progress update message should include a newline prefix.  */
> +      NEWLINE_NEEDED,
> +
> +      /* A newline prefix was included in a debuginfod progress update
> +        message.  */
> +      NEWLINE_PRINTED
> +    };
> +
>      /* SHOULD_PRINT indicates whether something should be printed for a tty.  */
>      progress_update ()
>      {
> @@ -396,6 +411,11 @@ class ui_out
>    ui_out_level *current_level () const;
>  };
>
> +typedef ui_out::progress_update::prefix_state prefix_state_t;
> +
> +/* Current state of the newline prefix.  */
> +extern prefix_state_t cur_prefix_state;
> +
>  /* Start a new tuple or list on construction, and end it on
>     destruction.  Normally this is used via the typedefs
>     ui_out_emit_tuple and ui_out_emit_list.  */
> --
> 2.46.0
>
  
Aaron Merey Oct. 1, 2024, 5:30 p.m. UTC | #2
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:
> >
> > ELF/DWARF section downloading allows gdb to download .gdb_index files in
> > order to defer full debuginfo downloads.  However .gdb_index does not
> > contain any information regarding source filenames.  When a gdb command
> > includes a filename argument (ex. 'break main.c:50'), this results in
> > the mass downloading of all deferred debuginfo so that gdb can search the
> > debuginfo for matching source filenames.  This can result in unnecessary
> > downloads.
> >
> > To improve this, have gdb instead download each debuginfo's .debug_line
> > (and .debug_line_str if using DWARF5) when executing these commands.
> > Download full debuginfo only when its .debug_line contains a matching
> > filename.
> >
> > Since the combined size of .debug_line and .debug_line_str is only about
> > 1% the size of the corresponding debuginfo, significant time can be saved
> > by checking these sections before choosing to download an entire debuginfo.
> >
> > This patch also redirects stdout and stderr of the debuginfod server
> > used by testsuite/gdb.debuginfod tests to a server_log standard output
> > file.  While adding tests for this patch I ran into an issue where the
> > test server would block when logging to stderr, presumably because the
> > stderr buffer filled up and wasn't being read from.  Redirecting the
> > log to a file fixes this and also makes the server log more accessible
> > when debugging test failures.
> > ---
> >  gdb/cli-out.c                            |  11 +-
> >  gdb/completer.c                          |  18 +-
> >  gdb/dwarf2/line-header.c                 | 161 +++++++++++------
> >  gdb/dwarf2/line-header.h                 |  10 ++
> >  gdb/dwarf2/read-gdb-index.c              |  66 ++++++-
> >  gdb/dwarf2/read.c                        | 209 +++++++++++++++++++++++
> >  gdb/dwarf2/read.h                        |  37 ++++
> >  gdb/mi/mi-out.c                          |   9 +-
> >  gdb/testsuite/gdb.debuginfod/section.exp |  28 +++
> >  gdb/ui-out.c                             |   3 +
> >  gdb/ui-out.h                             |  20 +++
> >  11 files changed, 518 insertions(+), 54 deletions(-)
> >
> > diff --git a/gdb/cli-out.c b/gdb/cli-out.c
> > index 1c303f09662..d65b69ebe01 100644
> > --- a/gdb/cli-out.c
> > +++ b/gdb/cli-out.c
> > @@ -306,16 +306,23 @@ cli_ui_out::do_progress_notify (const std::string &msg,
> >
> >    if (info.state == progress_update::START)
> >      {
> > +      std::string prefix;
> > +      if (cur_prefix_state == prefix_state_t::NEWLINE_NEEDED)
> > +       {
> > +         prefix = "\n";
> > +         cur_prefix_state = prefix_state_t::NEWLINE_PRINTED;
> > +       }
> > +
> >        if (stream->isatty ()
> >           && current_ui->input_interactive_p ()
> >           && chars_per_line >= MIN_CHARS_PER_LINE)
> >         {
> > -         gdb_printf (stream, "%s\n", msg.c_str ());
> > +         gdb_printf (stream, "%s\n", (prefix + msg).c_str ());
> >           info.state = progress_update::BAR;
> >         }
> >        else
> >         {
> > -         gdb_printf (stream, "%s...\n", msg.c_str ());
> > +         gdb_printf (stream, "%s...\n", (prefix + msg).c_str ());
> >           info.state = progress_update::WORKING;
> >         }
> >      }
> > diff --git a/gdb/completer.c b/gdb/completer.c
> > index 1008ec23ba5..4bb054f6034 100644
> > --- a/gdb/completer.c
> > +++ b/gdb/completer.c
> > @@ -1354,6 +1354,10 @@ complete_line_internal_1 (completion_tracker &tracker,
> >      {
> >        /* We've recognized a full command.  */
> >
> > +      /* Disable pagination since responding to the pagination prompt
> > +        overwrites rl_line_buffer.  */
> > +      scoped_restore pag_restore = make_scoped_restore (&pagination_enabled, false);
> > +
> >        if (p == tmp_command + point)
> >         {
> >           /* There is no non-whitespace in the line beyond the
> > @@ -1453,7 +1457,8 @@ complete_line_internal_1 (completion_tracker &tracker,
> >  }
> >
> >  /* Wrapper around complete_line_internal_1 to handle
> > -   MAX_COMPLETIONS_REACHED_ERROR.  */
> > +   MAX_COMPLETIONS_REACHED_ERROR and possible progress update
> > +   interactions.  */
> >
> >  static void
> >  complete_line_internal (completion_tracker &tracker,
> > @@ -1461,6 +1466,11 @@ complete_line_internal (completion_tracker &tracker,
> >                         const char *line_buffer, int point,
> >                         complete_line_internal_reason reason)
> >  {
> > +  scoped_restore restore_prefix_state
> > +    = make_scoped_restore
> > +      (&cur_prefix_state,
> > +       ui_out::progress_update::prefix_state::NEWLINE_NEEDED);
> > +
> >    try
> >      {
> >        complete_line_internal_1 (tracker, text, line_buffer, point, reason);
> > @@ -1470,6 +1480,12 @@ complete_line_internal (completion_tracker &tracker,
> >        if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
> >         throw;
> >      }
> > +
> > +  /* If progress update messages printed, then the text being completed
> > +     needs to be printed again.  */
> > +  if (cur_prefix_state
> > +      == ui_out::progress_update::prefix_state::NEWLINE_PRINTED)
> > +    rl_forced_update_display ();
> >  }
> >
> >  /* See completer.h.  */
> > diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> > index eddb2ef7ae8..1d24386c0eb 100644
> > --- a/gdb/dwarf2/line-header.c
> > +++ b/gdb/dwarf2/line-header.c
> > @@ -101,12 +101,15 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
> >  {
> >    LONGEST length = read_initial_length (abfd, buf, bytes_read);
> >
> > -  gdb_assert (cu_header->initial_length_size == 4
> > -             || cu_header->initial_length_size == 8
> > -             || cu_header->initial_length_size == 12);
> > +  if (cu_header != nullptr)
> > +    {
> > +      gdb_assert (cu_header->initial_length_size == 4
> > +                 || cu_header->initial_length_size == 8
> > +                 || cu_header->initial_length_size == 12);
> >
> > -  if (cu_header->initial_length_size != *bytes_read)
> > -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > +      if (cu_header->initial_length_size != *bytes_read)
> > +       complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > +    }
> >
> >    *offset_size = (*bytes_read == 4) ? 4 : 8;
> >    return length;
> > @@ -115,21 +118,27 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
> >  /* Read directory or file name entry format, starting with byte of
> >     format count entries, ULEB128 pairs of entry formats, ULEB128 of
> >     entries count and the entries themselves in the described entry
> > -   format.  */
> > +   format.
> > +
> > +   .debug_line and .debug_line_str sections are stored in LINE_BUFP
> > +   and LINE_STR_DATA respectively.  */
> >
> >  static void
> > -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > -                       const gdb_byte **bufp, struct line_header *lh,
> > -                       unsigned int offset_size,
> > -                       void (*callback) (struct line_header *lh,
> > -                                         const char *name,
> > -                                         dir_index d_index,
> > -                                         unsigned int mod_time,
> > -                                         unsigned int length))
> > +read_formatted_entries
> > +  (bfd *abfd, const gdb_byte **line_bufp,
> > +   const gdb::array_view<const gdb_byte> line_str_data,
> > +   struct line_header *lh,
> > +   unsigned int offset_size,
> > +   void (*callback) (struct line_header *lh,
> > +                    const char *name,
> > +                    dir_index d_index,
> > +                    unsigned int mod_time,
> > +                    unsigned int length))
> >  {
> >    gdb_byte format_count, formati;
> >    ULONGEST data_count, datai;
> > -  const gdb_byte *buf = *bufp;
> > +  const gdb_byte *buf = *line_bufp;
> > +  const gdb_byte *str_buf = line_str_data.data ();
> >    const gdb_byte *format_header_data;
> >    unsigned int bytes_read;
> >
> > @@ -156,7 +165,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >           ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
> >           format += bytes_read;
> >
> > -         ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> > +         ULONGEST form = read_unsigned_leb128 (abfd, format, &bytes_read);
> >           format += bytes_read;
> >
> >           std::optional<const char *> string;
> > @@ -171,12 +180,24 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >
> >             case DW_FORM_line_strp:
> >               {
> > -               const char *str
> > -                 = per_objfile->read_line_string (buf, offset_size);
> > +               if (line_str_data.empty ())
> > +                 warning (_("Dwarf Error: DW_FORM_line_strp used without " \
> > +                            "required section"));
> > +
> > +               if (line_str_data.size () <= offset_size)
> > +                 warning (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> > +                            "of section .debug_line_str"));
> > +
> > +               ULONGEST str_offset = read_offset (abfd, buf, offset_size);
> > +               const char *str;
> > +               if (str_buf[str_offset] == '\0')
> > +                 str = nullptr;
> > +               else
> > +                 str = (const char *) (str_buf + str_offset);
> >                 string.emplace (str);
> >                 buf += offset_size;
> > +               break;
> >               }
> > -             break;
> >
> >             case DW_FORM_data1:
> >               uint.emplace (read_1_byte (abfd, buf));
> > @@ -247,28 +268,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
> >      }
> >
> > -  *bufp = buf;
> > +  *line_bufp = buf;
> >  }
> >
> >  /* See line-header.h.  */
> >
> >  line_header_up
> > -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > -                          dwarf2_per_objfile *per_objfile,
> > -                          struct dwarf2_section_info *section,
> > -                          const struct comp_unit_head *cu_header,
> > -                          const char *comp_dir)
> > +dwarf_decode_line_header (bfd *abfd,
> > +                         gdb::array_view<const gdb_byte> line_data,
> > +                         gdb::array_view<const gdb_byte> line_str_data,
> > +                         const gdb_byte **debug_line_ptr,
> > +                         bool is_dwz,
> > +                         const struct comp_unit_head *cu_header,
> > +                         const char *comp_dir)
> >  {
> > -  const gdb_byte *line_ptr;
> > +  const gdb_byte *line_ptr, *buf;
> >    unsigned int bytes_read, offset_size;
> >    int i;
> >    const char *cur_dir, *cur_file;
> >
> > -  bfd *abfd = section->get_bfd_owner ();
> > +  buf = *debug_line_ptr;
> >
> >    /* Make sure that at least there's room for the total_length field.
> >       That could be 12 bytes long, but we're just going to fudge that.  */
> > -  if (to_underlying (sect_off) + 4 >= section->size)
> > +  if (buf + 4 >= line_data.data () + line_data.size ())
> >      {
> >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> >        return 0;
> > @@ -276,24 +299,26 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >
> >    line_header_up lh (new line_header (comp_dir));
> >
> > -  lh->sect_off = sect_off;
> > +  lh->sect_off = (sect_offset) (buf - line_data.data ());
> >    lh->offset_in_dwz = is_dwz;
> >
> > -  line_ptr = section->buffer + to_underlying (sect_off);
> > +  line_ptr = buf;
> >
> >    /* Read in the header.  */
> >    LONGEST unit_length
> > -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> > +    = read_checked_initial_length_and_offset (abfd, buf, cu_header,
> >                                               &bytes_read, &offset_size);
> > -  line_ptr += bytes_read;
> >
> > -  const gdb_byte *start_here = line_ptr;
> > +  line_ptr += bytes_read;
> >
> > -  if (line_ptr + unit_length > (section->buffer + section->size))
> > +  if (line_ptr + unit_length > buf + line_data.size ())
> >      {
> >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> >        return 0;
> >      }
> > +
> > +  const gdb_byte *start_here = line_ptr;
> > +
> >    lh->statement_program_end = start_here + unit_length;
> >    lh->version = read_2_bytes (abfd, line_ptr);
> >    line_ptr += 2;
> > @@ -302,7 +327,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >        /* This is a version we don't understand.  The format could have
> >          changed in ways we don't handle properly so just punt.  */
> >        complaint (_("unsupported version in .debug_line section"));
> > -      return NULL;
> > +      return nullptr;
> >      }
> >    if (lh->version >= 5)
> >      {
> > @@ -319,13 +344,14 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >           complaint (_("unsupported segment selector size %u "
> >                        "in .debug_line section"),
> >                      segment_selector_size);
> > -         return NULL;
> > +         return nullptr;
> >         }
> >      }
> >
> >    LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
> >    line_ptr += offset_size;
> >    lh->statement_program_start = line_ptr + header_length;
> > +
> >    lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
> >    line_ptr += 1;
> >
> > @@ -346,12 +372,16 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >
> >    lh->default_is_stmt = read_1_byte (abfd, line_ptr);
> >    line_ptr += 1;
> > +
> >    lh->line_base = read_1_signed_byte (abfd, line_ptr);
> >    line_ptr += 1;
> > +
> >    lh->line_range = read_1_byte (abfd, line_ptr);
> >    line_ptr += 1;
> > +
> >    lh->opcode_base = read_1_byte (abfd, line_ptr);
> >    line_ptr += 1;
> > +
> >    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
> >
> >    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
> > @@ -364,21 +394,23 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >    if (lh->version >= 5)
> >      {
> >        /* Read directory table.  */
> > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > -                             offset_size,
> > -                             [] (struct line_header *header, const char *name,
> > -                                 dir_index d_index, unsigned int mod_time,
> > -                                 unsigned int length)
> > +      read_formatted_entries
> > +       (abfd, &line_ptr, line_str_data,
> > +        lh.get (), offset_size,
> > +        [] (struct line_header *header, const char *name,
> > +            dir_index d_index, unsigned int mod_time,
> > +            unsigned int length)
> >         {
> >           header->add_include_dir (name);
> >         });
> >
> >        /* Read file name table.  */
> > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > -                             offset_size,
> > -                             [] (struct line_header *header, const char *name,
> > -                                 dir_index d_index, unsigned int mod_time,
> > -                                 unsigned int length)
> > +      read_formatted_entries
> > +       (abfd, &line_ptr, line_str_data,
> > +        lh.get (), offset_size,
> > +        [] (struct line_header *header, const char *name,
> > +            dir_index d_index, unsigned int mod_time,
> > +            unsigned int length)
> >         {
> >           header->add_file_name (name, d_index, mod_time, length);
> >         });
> > @@ -386,7 +418,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >    else
> >      {
> >        /* Read directory table.  */
> > -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > +      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != nullptr)
> >         {
> >           line_ptr += bytes_read;
> >           lh->add_include_dir (cur_dir);
> > @@ -394,7 +426,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >        line_ptr += bytes_read;
> >
> >        /* Read file name table.  */
> > -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > +      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != nullptr)
> >         {
> >           unsigned int mod_time, length;
> >           dir_index d_index;
> > @@ -412,9 +444,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >        line_ptr += bytes_read;
> >      }
> >
> > -  if (line_ptr > (section->buffer + section->size))
> > +  if (line_ptr > (buf + line_data.size ()))
> >      complaint (_("line number info header doesn't "
> >                  "fit in `.debug_line' section"));
> >
> > +  *debug_line_ptr += unit_length + offset_size;
> >    return lh;
> >  }
> > +
> > +line_header_up
> > +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > +                          dwarf2_per_objfile *per_objfile,
> > +                          struct dwarf2_section_info *section,
> > +                          const struct comp_unit_head *cu_header,
> > +                          const char *comp_dir)
> > +{
> > +  struct objfile *objfile = per_objfile->objfile;
> > +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> > +
> > +  /* Read .debug_line.  */
> > +  dwarf2_section_info *line_sec = &per_bfd->line;
> > +  bfd_size_type line_size = line_sec->get_size (objfile);
> > +
> > +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> > +
> > +  /* Read .debug_line_str.  */
> > +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> > +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> > +
> > +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> > +                                           line_str_size);
> > +
> > +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> > +
> > +  return dwarf_decode_line_header
> > +    (per_bfd->obfd, line, line_str, &line_ptr,
> > +     is_dwz, cu_header, comp_dir);
> > +}
> > diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> > index 7da59725c98..a6288fb6773 100644
> > --- a/gdb/dwarf2/line-header.h
> > +++ b/gdb/dwarf2/line-header.h
> > @@ -221,4 +221,14 @@ extern line_header_up dwarf_decode_line_header
> >     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
> >     const char *comp_dir);
> >
> > +/* Like above but the .debug_line and .debug_line_str are stored in
> > +   LINE_DATA and LINE_STR_DATA.  *DEBUG_LINE_PTR should point to a
> > +   statement program header within LINE_DATA.  */
> > +
> > +extern line_header_up dwarf_decode_line_header
> > +  (bfd *abfd, gdb::array_view<const gdb_byte> line_data,
> > +   gdb::array_view<const gdb_byte> line_str_data,
> > +   const gdb_byte **debug_line_ptr, bool is_dwz,
> > +  const comp_unit_head *cu_header, const char *comp_dir);
> > +
> >  #endif /* DWARF2_LINE_HEADER_H */
> > diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> > index 5938440a832..e5f0fffca97 100644
> > --- a/gdb/dwarf2/read-gdb-index.c
> > +++ b/gdb/dwarf2/read-gdb-index.c
> > @@ -143,6 +143,9 @@ struct mapped_gdb_index final : public mapped_index_base
> >    }
> >  };
> >
> > +struct mapped_debug_line;
> > +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> > +
> >  struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >  {
> >    /* This dumps minimal information about the index.
> > @@ -161,6 +164,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >       block_search_flags search_flags,
> >       domain_search_flags domain) override;
> >
> > +  /* If OBJFILE's debuginfo download has been deferred, use a mapped_debug_line
> > +     to generate filenames.
> > +
> > +     Otherwise call dwarf2_base_index_functions::map_symbol_filenames.  */
> > +
> > +  void map_symbol_filenames (struct objfile *objfile,
> > +                            gdb::function_view<symbol_filename_ftype> fun,
> > +                            bool need_fullname) override;
> > +
> >    /* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
> >       debuginfo if necessary.  */
> >    void expand_all_symtabs (struct objfile *objfile) override;
> > @@ -168,6 +180,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> >       debuginfo if necessary.  */
> >    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> > +
> > +  /* Filename information related to this .gdb_index.  */
> > +  mapped_debug_line_up mdl;
> > +
> > +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> > +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> > +  bool filename_in_debug_line
> > +    (objfile *objfile,
> > +     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> >  };
> >
> >  void
> > @@ -188,6 +209,23 @@ dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
> >    return dwarf2_base_index_functions::find_last_source_symtab (objfile);
> >  }
> >
> > +void
> > +dwarf2_gdb_index::map_symbol_filenames
> > +     (struct objfile *objfile,
> > +      gdb::function_view<symbol_filename_ftype> fun,
> > +      bool need_fullname)
> > +{
> > +  if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
> > +    {
> > +      if (mdl == nullptr)
> > +       mdl.reset (new mapped_debug_line (objfile));
> > +      mdl->map_filenames (fun, need_fullname);
> > +    }
> > +  else
> > +    dwarf2_base_index_functions::map_symbol_filenames (objfile, fun,
> > +                                                      need_fullname);
> > +}
> > +
> >  /* 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
> > @@ -302,6 +340,17 @@ dw2_expand_marked_cus
> >    return true;
> >  }
> >
> > +bool
> > +dwarf2_gdb_index::filename_in_debug_line
> > +     (objfile *objfile,
> > +      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > +{
> > +  if (mdl == nullptr)
> > +    mdl.reset (new mapped_debug_line (objfile));
> > +
> > +  return mdl->contains_matching_filename (file_matcher);
> > +}
> > +
> >  bool
> >  dwarf2_gdb_index::expand_symtabs_matching
> >      (struct objfile *objfile,
> > @@ -314,7 +363,22 @@ dwarf2_gdb_index::expand_symtabs_matching
> >  {
> >    dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
> >
> > -  dw_expand_symtabs_matching_file_matcher (per_objfile, file_matcher);
> > +  if (file_matcher != nullptr)
> > +    {
> > +      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
> > +       dw_expand_symtabs_matching_file_matcher (per_objfile, file_matcher);
> > +      else if (filename_in_debug_line (objfile, file_matcher))
> > +       {
> > +         /* If OBJFILE's debuginfo hasn't been downloaded and there is a file
> > +            name to be matched, download the .debug_line (and possibly
> > +            .debug_line_str) separately.  Search the separate .debug_line for
> > +            a match.  If there is a match, then download the full debuginfo.
> > +            Return early to move on to expanding symtabs in the debuginfo
> > +            objfile.  */
> > +         read_full_dwarf_from_debuginfod (objfile);
> > +         return true;
> > +       }
> > +    }
> >
> >    /* This invariant is documented in quick-functions.h.  */
> >    gdb_assert (lookup_name != nullptr || symbol_matcher == nullptr);
> > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> > index d07e0d86970..5cb222bd2cc 100644
> > --- a/gdb/dwarf2/read.c
> > +++ b/gdb/dwarf2/read.c
> > @@ -81,6 +81,7 @@
> >  #include <optional>
> >  #include "gdbsupport/underlying.h"
> >  #include "gdbsupport/hash_enum.h"
> > +#include "gdbsupport/scoped_mmap.h"
> >  #include "filename-seen-cache.h"
> >  #include "producer.h"
> >  #include <fcntl.h>
> > @@ -99,6 +100,7 @@
> >  #include "dwarf2/error.h"
> >  #include "inferior.h"
> >  #include "debuginfod-support.h"
> > +#include "exceptions.h"
> >
> >  /* When == 1, print basic high level tracing messages.
> >     When > 1, be more verbose.
> > @@ -1953,6 +1955,213 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
> >    return this_cu->file_names;
> >  }
> >
> > +#if !HAVE_SYS_MMAN_H
> > +
> > +bool
> > +mapped_debug_line::contains_matching_filename
> > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > +{
> > +  return false;
> > +}
> > +
> > +gdb::array_view<const gdb_byte>
> > +mapped_debug_line::read_debug_line_separate
> > +  (const char *filename, std::unique_ptr<index_cache_resource> *resource)
> > +{
> > +  return {};
> > +}
> > +
> > +bool
> > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > +{
> > +  return false;
> > +}
> > +
> > +void
> > +mapped_debug_line::map_filenames
> > +  (gdb::function_view<symbol_filename_ftype> fun,
> > +   bool need_fullname)
> > +{
> > +  return;
> > +}
> > +
> > +#else /* !HAVE_SYS_MMAN_H */
> > +
> > +struct line_resource_mmap final : public index_cache_resource
> > +{
> > +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> > +     file doesn't exist.  */
> > +  line_resource_mmap (const char *filename)
> > +    : mapping (mmap_file (filename))
> > +  {}
> > +
> > +  scoped_mmap mapping;
> > +};
> > +
> > +/* See read.h.  */
> > +
> > +bool
> > +mapped_debug_line::contains_matching_filename
> > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > +{
> > +  for (line_header_up &lh : line_headers)
> > +    for (file_entry &fe : lh->file_names ())
> > +      {
> > +       const char *filename = fe.name;
> > +
> > +       if (file_matcher (fe.name, false))
> > +         return true;
> > +
> > +       bool basename_match = file_matcher (lbasename (fe.name), true);
> > +
> > +       if (!basenames_may_differ && !basename_match)
> > +         continue;
> > +
> > +       /* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> > +          until DWARF5.  Since we don't have access to the CU at this
> > +          point we just check for a partial match on the filename.
> > +          If there is a match, the full debuginfo will be downloaded
> > +          ane the match will be re-evalute with DW_AT_comp_dir.  */
> > +       if (lh->version < 5 && fe.d_index == 0)
> > +         return basename_match;
> > +
> > +       const char *dirname = fe.include_dir (&*lh);
> > +       std::string fullname;
> > +
> > +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> > +         fullname = filename;
> > +       else
> > +         fullname = std::string (dirname) + SLASH_STRING + filename;
> > +
> > +       gdb::unique_xmalloc_ptr<char> rewritten
> > +         = rewrite_source_path (fullname.c_str ());
> > +       if (rewritten != nullptr)
> > +         fullname = rewritten.release ();
> > +
> > +       if (file_matcher (fullname.c_str (), false))
> > +         return true;
> > +      }
> > +
> > +  return false;
> > +}
> > +
> > +/* See read.h.  */
> > +
> > +void
> > +mapped_debug_line::map_filenames
> > +  (gdb::function_view<symbol_filename_ftype> fun,
> > +   bool need_fullname)
> > +{
> > +  for (line_header_up &lh : line_headers)
> > +    for (file_entry &fe : lh->file_names ())
> > +      {
> > +       const char *filename = fe.name;
> > +
> > +       if (!need_fullname)
> > +         {
> > +           fun (filename, nullptr);
> > +           continue;
> > +         }
> > +
> > +       const char *dirname = fe.include_dir (&*lh);
> > +       std::string fullname;
> > +
> > +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> > +         fullname = filename;
> > +       else
> > +         fullname = std::string (dirname) + SLASH_STRING + filename;
> > +
> > +       gdb::unique_xmalloc_ptr<char> rewritten
> > +         = rewrite_source_path (fullname.c_str ());
> > +       if (rewritten != nullptr)
> > +         fullname = rewritten.release ();
> > +
> > +       fun (filename, fullname.c_str ());
> > +      }
> > +}
> > +
> > +/* See read.h.  */
> > +
> > +gdb::array_view<const gdb_byte>
> > +mapped_debug_line::read_debug_line_separate
> > +  (const char *filename, std::unique_ptr<index_cache_resource> *resource)
> > +{
> > +  if (filename == nullptr)
> > +    return {};
> > +
> > +  try
> > +  {
> > +    line_resource_mmap *mmap_resource
> > +      = new line_resource_mmap (filename);
> > +
> > +    resource->reset (mmap_resource);
> > +
> > +    return gdb::array_view<const gdb_byte>
> > +      ((const gdb_byte *) mmap_resource->mapping.get (),
> > +       mmap_resource->mapping.size ());
> > +  }
> > +  catch (const gdb_exception &except)
> > +  {
> > +    exception_print (gdb_stderr, except);
> > +  }
> > +
> > +  return {};
> > +}
> > +
> > +/* See read.h.  */
> > +
> > +bool
> > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > +{
> > +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > +  if (build_id == nullptr)
> > +    return false;
> > +
> > +  gdb::unique_xmalloc_ptr<char> line_path;
> > +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> > +                                               build_id->size,
> > +                                               bfd_get_filename
> > +                                                 (objfile->obfd.get ()),
> > +                                               ".debug_line",
> > +                                               &line_path);
> > +
> > +  if (line_fd.get () < 0)
> > +    return false;
> > +
> > +  gdb::unique_xmalloc_ptr<char> line_str_path;
> > +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> > +                                                   build_id->size,
> > +                                                   bfd_get_filename
> > +                                                     (objfile->obfd.get ()),
> > +                                                   ".debug_line_str",
> > +                                                   &line_str_path);
> > +
> > +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> > +  line_str_data = read_debug_line_separate (line_str_path.get (),
> > +                                           &line_str_resource);
> > +
> > +  const gdb_byte *line_ptr = line_data.data ();
> > +
> > +  while (line_ptr < line_data.data () + line_data.size ())
> > +    {
> > +      line_header_up lh
> > +       = dwarf_decode_line_header (objfile->obfd.get (),
> > +                                   line_data, line_str_data,
> > +                                   &line_ptr, false,
> > +                                   nullptr, nullptr);
> > +      line_headers.emplace_back (std::move (lh));
> > +    }
> > +
> > +  return true;
> > +}
> > +#endif /* !HAVE_SYS_MMAN_H */
> > +
> > +mapped_debug_line::mapped_debug_line (objfile *objfile)
> > +{
> > +  if (!read_debug_line_from_debuginfod (objfile))
> > +    line_headers.clear ();
> > +}
> > +
> >  /* A helper for the "quick" functions which computes and caches the
> >     real path for a given file name from the line table.  */
> >
> > diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> > index 49ba9d82117..aba4787e638 100644
> > --- a/gdb/dwarf2/read.h
> > +++ b/gdb/dwarf2/read.h
> > @@ -32,6 +32,7 @@
> >  #include "gdbsupport/hash_enum.h"
> >  #include "gdbsupport/function-view.h"
> >  #include "gdbsupport/packed.h"
> > +#include "dwarf2/line-header.h"
> >
> >  /* Hold 'maintenance (set|show) dwarf' commands.  */
> >  extern struct cmd_list_element *set_dwarf_cmdlist;
> > @@ -924,4 +925,40 @@ extern htab_up create_quick_file_names_table (unsigned int nr_initial_entries);
> >
> >  extern void read_full_dwarf_from_debuginfod (struct objfile *);
> >
> > +struct mapped_debug_line
> > +{
> > +  mapped_debug_line (objfile *objfile);
> > +
> > +  /* Return true if any of the mapped .debug_line's filenames match
> > +     FILE_MATCHER.  */
> > +
> > +  bool contains_matching_filename
> > +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> > +
> > +  /* Call FUN with each filename in this mapped .debug_line.  Include
> > +     each file's fullname if NEED_FULLNAME is true.  */
> > +
> > +  void map_filenames (gdb::function_view<symbol_filename_ftype> fun,
> > +                     bool need_fullname);
> > +
> > +private:
> > +  std::vector<line_header_up> line_headers;
> > +
> > +  gdb::array_view<const gdb_byte> line_data;
> > +  gdb::array_view<const gdb_byte> line_str_data;
> > +
> > +  std::unique_ptr<index_cache_resource> line_resource;
> > +  std::unique_ptr<index_cache_resource> line_str_resource;
> > +
> > +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> > +     and populate line_headers.  */
> > +
> > +  bool read_debug_line_from_debuginfod (objfile *objfile);
> > +
> > +  /* Initialize line_data and line_str_data with the .debug_line and
> > +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> > +
> > +  gdb::array_view<const gdb_byte> read_debug_line_separate
> > +    (const char *filename, std::unique_ptr<index_cache_resource> *resource);
> > +};
> >  #endif /* DWARF2READ_H */
> > diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c
> > index ff93d2cd448..8ad2c7f4497 100644
> > --- a/gdb/mi/mi-out.c
> > +++ b/gdb/mi/mi-out.c
> > @@ -277,7 +277,14 @@ mi_ui_out::do_progress_notify (const std::string &msg, const char *unit,
> >
> >    if (info.state == progress_update::START)
> >      {
> > -      gdb_printf ("%s...\n", msg.c_str ());
> > +      std::string prefix;
> > +      if (cur_prefix_state == prefix_state_t::NEWLINE_NEEDED)
> > +       {
> > +         prefix = "\n";
> > +         cur_prefix_state = prefix_state_t::NEWLINE_PRINTED;
> > +       }
> > +
> > +      gdb_printf ("%s...\n", (prefix + msg).c_str ());
> >        info.state = progress_update::WORKING;
> >      }
> >  }
> > diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
> > index 381791bca50..8e482364434 100644
> > --- a/gdb/testsuite/gdb.debuginfod/section.exp
> > +++ b/gdb/testsuite/gdb.debuginfod/section.exp
> > @@ -18,6 +18,7 @@
> >  standard_testfile
> >
> >  load_lib debuginfod-support.exp
> > +load_lib completion-support.exp
> >
> >  clean_restart
> >  require allow_debuginfod_tests
> > @@ -144,6 +145,33 @@ proc_with_prefix local_url { } {
> >      # Hit the breakpoint.
> >      set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
> >      gdb_test "c" $res "break continue"
> > +
> > +    clean_restart_with_prompt $binfile "line 1"
> > +
> > +    # List source file using .debug_line download.
> > +    set res ".*\.debug_line.*$lib_sl1.*21.*extern void libsection2_test.*"
> > +    gdb_test "list $libsrc1:21" $res "line 1 list"
> > +
> > +    clean_restart_with_prompt $binfile "line 2"
> > +
> > +    # Set breakpoint using .debug_line download.
> > +    set res ".*section \.debug_line for $lib_sl1.*Breakpoint 2 at.*$libsrc1.*"
> > +    gdb_test "br $libsrc1:41" $res "line 2 br"
> > +
> > +    # Continue to breakpoint.
> > +    set res "Breakpoint 2, libsection1_test.*\"Cancelling thread\\\\n\".*"
> > +    gdb_test "c" $res "line 2 continue"
> > +
> > +    # Check that download progress message is correctly formatted
> > +    # when printing threads.
> > +    set res ".*separate debug info for $lib_sl2\.\.\.\r\n.* 2    Thread.*"
> > +    gdb_test "info thr" $res "line thread"
> > +
> > +    clean_restart_with_prompt $binfile "autocomplete"
> > +
> > +    # Download debuginfo during autocompletion.
> > +    test_gdb_complete_tab_unique "br lib" \
> > +       ".*separate debug info for $lib_sl1.*" ""
> >  }
> >
> >  # Create CACHE and DB directories ready for debuginfod to use.
> > diff --git a/gdb/ui-out.c b/gdb/ui-out.c
> > index 7e9f238fcf6..ad628b22c66 100644
> > --- a/gdb/ui-out.c
> > +++ b/gdb/ui-out.c
> > @@ -31,6 +31,9 @@
> >  #include <memory>
> >  #include <string>
> >
> > +/* Current state of newline prefixing for progress update messages.  */
> > +prefix_state_t cur_prefix_state = prefix_state_t::NEWLINE_OFF;
> > +
> >  namespace {
> >
> >  /* A header of a ui_out_table.  */
> > diff --git a/gdb/ui-out.h b/gdb/ui-out.h
> > index ef9ce4fad33..fa06a29e268 100644
> > --- a/gdb/ui-out.h
> > +++ b/gdb/ui-out.h
> > @@ -299,6 +299,21 @@ class ui_out
> >        BAR
> >      };
> >
> > +    /* Used to communicate the status of a newline prefix for the next progress
> > +       update message.  */
> > +    enum prefix_state
> > +    {
> > +      /* Do not modify the next progress update message.  */
> > +      NEWLINE_OFF,
> > +
> > +      /* The next progress update message should include a newline prefix.  */
> > +      NEWLINE_NEEDED,
> > +
> > +      /* A newline prefix was included in a debuginfod progress update
> > +        message.  */
> > +      NEWLINE_PRINTED
> > +    };
> > +
> >      /* SHOULD_PRINT indicates whether something should be printed for a tty.  */
> >      progress_update ()
> >      {
> > @@ -396,6 +411,11 @@ class ui_out
> >    ui_out_level *current_level () const;
> >  };
> >
> > +typedef ui_out::progress_update::prefix_state prefix_state_t;
> > +
> > +/* Current state of the newline prefix.  */
> > +extern prefix_state_t cur_prefix_state;
> > +
> >  /* Start a new tuple or list on construction, and end it on
> >     destruction.  Normally this is used via the typedefs
> >     ui_out_emit_tuple and ui_out_emit_list.  */
> > --
> > 2.46.0
> >
  

Patch

diff --git a/gdb/cli-out.c b/gdb/cli-out.c
index 1c303f09662..d65b69ebe01 100644
--- a/gdb/cli-out.c
+++ b/gdb/cli-out.c
@@ -306,16 +306,23 @@  cli_ui_out::do_progress_notify (const std::string &msg,
 
   if (info.state == progress_update::START)
     {
+      std::string prefix;
+      if (cur_prefix_state == prefix_state_t::NEWLINE_NEEDED)
+	{
+	  prefix = "\n";
+	  cur_prefix_state = prefix_state_t::NEWLINE_PRINTED;
+	}
+
       if (stream->isatty ()
 	  && current_ui->input_interactive_p ()
 	  && chars_per_line >= MIN_CHARS_PER_LINE)
 	{
-	  gdb_printf (stream, "%s\n", msg.c_str ());
+	  gdb_printf (stream, "%s\n", (prefix + msg).c_str ());
 	  info.state = progress_update::BAR;
 	}
       else
 	{
-	  gdb_printf (stream, "%s...\n", msg.c_str ());
+	  gdb_printf (stream, "%s...\n", (prefix + msg).c_str ());
 	  info.state = progress_update::WORKING;
 	}
     }
diff --git a/gdb/completer.c b/gdb/completer.c
index 1008ec23ba5..4bb054f6034 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1354,6 +1354,10 @@  complete_line_internal_1 (completion_tracker &tracker,
     {
       /* We've recognized a full command.  */
 
+      /* Disable pagination since responding to the pagination prompt
+	 overwrites rl_line_buffer.  */
+      scoped_restore pag_restore = make_scoped_restore (&pagination_enabled, false);
+
       if (p == tmp_command + point)
 	{
 	  /* There is no non-whitespace in the line beyond the
@@ -1453,7 +1457,8 @@  complete_line_internal_1 (completion_tracker &tracker,
 }
 
 /* Wrapper around complete_line_internal_1 to handle
-   MAX_COMPLETIONS_REACHED_ERROR.  */
+   MAX_COMPLETIONS_REACHED_ERROR and possible progress update
+   interactions.  */
 
 static void
 complete_line_internal (completion_tracker &tracker,
@@ -1461,6 +1466,11 @@  complete_line_internal (completion_tracker &tracker,
 			const char *line_buffer, int point,
 			complete_line_internal_reason reason)
 {
+  scoped_restore restore_prefix_state
+    = make_scoped_restore
+      (&cur_prefix_state,
+       ui_out::progress_update::prefix_state::NEWLINE_NEEDED);
+
   try
     {
       complete_line_internal_1 (tracker, text, line_buffer, point, reason);
@@ -1470,6 +1480,12 @@  complete_line_internal (completion_tracker &tracker,
       if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
 	throw;
     }
+
+  /* If progress update messages printed, then the text being completed
+     needs to be printed again.  */
+  if (cur_prefix_state
+      == ui_out::progress_update::prefix_state::NEWLINE_PRINTED)
+    rl_forced_update_display ();
 }
 
 /* See completer.h.  */
diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
index eddb2ef7ae8..1d24386c0eb 100644
--- a/gdb/dwarf2/line-header.c
+++ b/gdb/dwarf2/line-header.c
@@ -101,12 +101,15 @@  read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
 {
   LONGEST length = read_initial_length (abfd, buf, bytes_read);
 
-  gdb_assert (cu_header->initial_length_size == 4
-	      || cu_header->initial_length_size == 8
-	      || cu_header->initial_length_size == 12);
+  if (cu_header != nullptr)
+    {
+      gdb_assert (cu_header->initial_length_size == 4
+		  || cu_header->initial_length_size == 8
+		  || cu_header->initial_length_size == 12);
 
-  if (cu_header->initial_length_size != *bytes_read)
-    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
+      if (cu_header->initial_length_size != *bytes_read)
+	complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
+    }
 
   *offset_size = (*bytes_read == 4) ? 4 : 8;
   return length;
@@ -115,21 +118,27 @@  read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
 /* Read directory or file name entry format, starting with byte of
    format count entries, ULEB128 pairs of entry formats, ULEB128 of
    entries count and the entries themselves in the described entry
-   format.  */
+   format.
+
+   .debug_line and .debug_line_str sections are stored in LINE_BUFP
+   and LINE_STR_DATA respectively.  */
 
 static void
-read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
-			const gdb_byte **bufp, struct line_header *lh,
-			unsigned int offset_size,
-			void (*callback) (struct line_header *lh,
-					  const char *name,
-					  dir_index d_index,
-					  unsigned int mod_time,
-					  unsigned int length))
+read_formatted_entries
+  (bfd *abfd, const gdb_byte **line_bufp,
+   const gdb::array_view<const gdb_byte> line_str_data,
+   struct line_header *lh,
+   unsigned int offset_size,
+   void (*callback) (struct line_header *lh,
+		     const char *name,
+		     dir_index d_index,
+		     unsigned int mod_time,
+		     unsigned int length))
 {
   gdb_byte format_count, formati;
   ULONGEST data_count, datai;
-  const gdb_byte *buf = *bufp;
+  const gdb_byte *buf = *line_bufp;
+  const gdb_byte *str_buf = line_str_data.data ();
   const gdb_byte *format_header_data;
   unsigned int bytes_read;
 
@@ -156,7 +165,7 @@  read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
 	  ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
 	  format += bytes_read;
 
-	  ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
+	  ULONGEST form = read_unsigned_leb128 (abfd, format, &bytes_read);
 	  format += bytes_read;
 
 	  std::optional<const char *> string;
@@ -171,12 +180,24 @@  read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
 
 	    case DW_FORM_line_strp:
 	      {
-		const char *str
-		  = per_objfile->read_line_string (buf, offset_size);
+		if (line_str_data.empty ())
+		  warning (_("Dwarf Error: DW_FORM_line_strp used without " \
+			     "required section"));
+
+		if (line_str_data.size () <= offset_size)
+		  warning (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
+			     "of section .debug_line_str"));
+
+		ULONGEST str_offset = read_offset (abfd, buf, offset_size);
+		const char *str;
+		if (str_buf[str_offset] == '\0')
+		  str = nullptr;
+		else
+		  str = (const char *) (str_buf + str_offset);
 		string.emplace (str);
 		buf += offset_size;
+		break;
 	      }
-	      break;
 
 	    case DW_FORM_data1:
 	      uint.emplace (read_1_byte (abfd, buf));
@@ -247,28 +268,30 @@  read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
       callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
     }
 
-  *bufp = buf;
+  *line_bufp = buf;
 }
 
 /* See line-header.h.  */
 
 line_header_up
-dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
-			   dwarf2_per_objfile *per_objfile,
-			   struct dwarf2_section_info *section,
-			   const struct comp_unit_head *cu_header,
-			   const char *comp_dir)
+dwarf_decode_line_header (bfd *abfd,
+			  gdb::array_view<const gdb_byte> line_data,
+			  gdb::array_view<const gdb_byte> line_str_data,
+			  const gdb_byte **debug_line_ptr,
+			  bool is_dwz,
+			  const struct comp_unit_head *cu_header,
+			  const char *comp_dir)
 {
-  const gdb_byte *line_ptr;
+  const gdb_byte *line_ptr, *buf;
   unsigned int bytes_read, offset_size;
   int i;
   const char *cur_dir, *cur_file;
 
-  bfd *abfd = section->get_bfd_owner ();
+  buf = *debug_line_ptr;
 
   /* Make sure that at least there's room for the total_length field.
      That could be 12 bytes long, but we're just going to fudge that.  */
-  if (to_underlying (sect_off) + 4 >= section->size)
+  if (buf + 4 >= line_data.data () + line_data.size ())
     {
       dwarf2_statement_list_fits_in_line_number_section_complaint ();
       return 0;
@@ -276,24 +299,26 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
 
   line_header_up lh (new line_header (comp_dir));
 
-  lh->sect_off = sect_off;
+  lh->sect_off = (sect_offset) (buf - line_data.data ());
   lh->offset_in_dwz = is_dwz;
 
-  line_ptr = section->buffer + to_underlying (sect_off);
+  line_ptr = buf;
 
   /* Read in the header.  */
   LONGEST unit_length
-    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
+    = read_checked_initial_length_and_offset (abfd, buf, cu_header,
 					      &bytes_read, &offset_size);
-  line_ptr += bytes_read;
 
-  const gdb_byte *start_here = line_ptr;
+  line_ptr += bytes_read;
 
-  if (line_ptr + unit_length > (section->buffer + section->size))
+  if (line_ptr + unit_length > buf + line_data.size ())
     {
       dwarf2_statement_list_fits_in_line_number_section_complaint ();
       return 0;
     }
+
+  const gdb_byte *start_here = line_ptr;
+
   lh->statement_program_end = start_here + unit_length;
   lh->version = read_2_bytes (abfd, line_ptr);
   line_ptr += 2;
@@ -302,7 +327,7 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
       /* This is a version we don't understand.  The format could have
 	 changed in ways we don't handle properly so just punt.  */
       complaint (_("unsupported version in .debug_line section"));
-      return NULL;
+      return nullptr;
     }
   if (lh->version >= 5)
     {
@@ -319,13 +344,14 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
 	  complaint (_("unsupported segment selector size %u "
 		       "in .debug_line section"),
 		     segment_selector_size);
-	  return NULL;
+	  return nullptr;
 	}
     }
 
   LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
   line_ptr += offset_size;
   lh->statement_program_start = line_ptr + header_length;
+
   lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
   line_ptr += 1;
 
@@ -346,12 +372,16 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
 
   lh->default_is_stmt = read_1_byte (abfd, line_ptr);
   line_ptr += 1;
+
   lh->line_base = read_1_signed_byte (abfd, line_ptr);
   line_ptr += 1;
+
   lh->line_range = read_1_byte (abfd, line_ptr);
   line_ptr += 1;
+
   lh->opcode_base = read_1_byte (abfd, line_ptr);
   line_ptr += 1;
+
   lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
 
   lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
@@ -364,21 +394,23 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
   if (lh->version >= 5)
     {
       /* Read directory table.  */
-      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
-			      offset_size,
-			      [] (struct line_header *header, const char *name,
-				  dir_index d_index, unsigned int mod_time,
-				  unsigned int length)
+      read_formatted_entries
+	(abfd, &line_ptr, line_str_data,
+	 lh.get (), offset_size,
+	 [] (struct line_header *header, const char *name,
+	     dir_index d_index, unsigned int mod_time,
+	     unsigned int length)
 	{
 	  header->add_include_dir (name);
 	});
 
       /* Read file name table.  */
-      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
-			      offset_size,
-			      [] (struct line_header *header, const char *name,
-				  dir_index d_index, unsigned int mod_time,
-				  unsigned int length)
+      read_formatted_entries
+	(abfd, &line_ptr, line_str_data,
+	 lh.get (), offset_size,
+	 [] (struct line_header *header, const char *name,
+	     dir_index d_index, unsigned int mod_time,
+	     unsigned int length)
 	{
 	  header->add_file_name (name, d_index, mod_time, length);
 	});
@@ -386,7 +418,7 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
   else
     {
       /* Read directory table.  */
-      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
+      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != nullptr)
 	{
 	  line_ptr += bytes_read;
 	  lh->add_include_dir (cur_dir);
@@ -394,7 +426,7 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
       line_ptr += bytes_read;
 
       /* Read file name table.  */
-      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
+      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != nullptr)
 	{
 	  unsigned int mod_time, length;
 	  dir_index d_index;
@@ -412,9 +444,40 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
       line_ptr += bytes_read;
     }
 
-  if (line_ptr > (section->buffer + section->size))
+  if (line_ptr > (buf + line_data.size ()))
     complaint (_("line number info header doesn't "
 		 "fit in `.debug_line' section"));
 
+  *debug_line_ptr += unit_length + offset_size;
   return lh;
 }
+
+line_header_up
+dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
+			   dwarf2_per_objfile *per_objfile,
+			   struct dwarf2_section_info *section,
+			   const struct comp_unit_head *cu_header,
+			   const char *comp_dir)
+{
+  struct objfile *objfile = per_objfile->objfile;
+  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
+
+  /* Read .debug_line.  */
+  dwarf2_section_info *line_sec = &per_bfd->line;
+  bfd_size_type line_size = line_sec->get_size (objfile);
+
+  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
+
+  /* Read .debug_line_str.  */
+  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
+  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
+
+  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
+					    line_str_size);
+
+  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
+
+  return dwarf_decode_line_header
+    (per_bfd->obfd, line, line_str, &line_ptr,
+     is_dwz, cu_header, comp_dir);
+}
diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
index 7da59725c98..a6288fb6773 100644
--- a/gdb/dwarf2/line-header.h
+++ b/gdb/dwarf2/line-header.h
@@ -221,4 +221,14 @@  extern line_header_up dwarf_decode_line_header
    struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
    const char *comp_dir);
 
+/* Like above but the .debug_line and .debug_line_str are stored in
+   LINE_DATA and LINE_STR_DATA.  *DEBUG_LINE_PTR should point to a
+   statement program header within LINE_DATA.  */
+
+extern line_header_up dwarf_decode_line_header
+  (bfd *abfd, gdb::array_view<const gdb_byte> line_data,
+   gdb::array_view<const gdb_byte> line_str_data,
+   const gdb_byte **debug_line_ptr, bool is_dwz,
+  const comp_unit_head *cu_header, const char *comp_dir);
+
 #endif /* DWARF2_LINE_HEADER_H */
diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
index 5938440a832..e5f0fffca97 100644
--- a/gdb/dwarf2/read-gdb-index.c
+++ b/gdb/dwarf2/read-gdb-index.c
@@ -143,6 +143,9 @@  struct mapped_gdb_index final : public mapped_index_base
   }
 };
 
+struct mapped_debug_line;
+typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
+
 struct dwarf2_gdb_index : public dwarf2_base_index_functions
 {
   /* This dumps minimal information about the index.
@@ -161,6 +164,15 @@  struct dwarf2_gdb_index : public dwarf2_base_index_functions
      block_search_flags search_flags,
      domain_search_flags domain) override;
 
+  /* If OBJFILE's debuginfo download has been deferred, use a mapped_debug_line
+     to generate filenames.
+
+     Otherwise call dwarf2_base_index_functions::map_symbol_filenames.  */
+
+  void map_symbol_filenames (struct objfile *objfile,
+			     gdb::function_view<symbol_filename_ftype> fun,
+			     bool need_fullname) override;
+
   /* Calls dwarf2_base_index_functions::expand_all_symtabs and downloads
      debuginfo if necessary.  */
   void expand_all_symtabs (struct objfile *objfile) override;
@@ -168,6 +180,15 @@  struct dwarf2_gdb_index : public dwarf2_base_index_functions
   /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
      debuginfo if necessary.  */
   struct symtab *find_last_source_symtab (struct objfile *objfile) override;
+
+  /* Filename information related to this .gdb_index.  */
+  mapped_debug_line_up mdl;
+
+  /* Return true if any of the filenames in this .gdb_index's .debug_line
+     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
+  bool filename_in_debug_line
+    (objfile *objfile,
+     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
 };
 
 void
@@ -188,6 +209,23 @@  dwarf2_gdb_index::find_last_source_symtab (struct objfile *objfile)
   return dwarf2_base_index_functions::find_last_source_symtab (objfile);
 }
 
+void
+dwarf2_gdb_index::map_symbol_filenames
+     (struct objfile *objfile,
+      gdb::function_view<symbol_filename_ftype> fun,
+      bool need_fullname)
+{
+  if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) != 0)
+    {
+      if (mdl == nullptr)
+	mdl.reset (new mapped_debug_line (objfile));
+      mdl->map_filenames (fun, need_fullname);
+    }
+  else
+    dwarf2_base_index_functions::map_symbol_filenames (objfile, fun,
+						       need_fullname);
+}
+
 /* 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
@@ -302,6 +340,17 @@  dw2_expand_marked_cus
   return true;
 }
 
+bool
+dwarf2_gdb_index::filename_in_debug_line
+     (objfile *objfile,
+      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  if (mdl == nullptr)
+    mdl.reset (new mapped_debug_line (objfile));
+
+  return mdl->contains_matching_filename (file_matcher);
+}
+
 bool
 dwarf2_gdb_index::expand_symtabs_matching
     (struct objfile *objfile,
@@ -314,7 +363,22 @@  dwarf2_gdb_index::expand_symtabs_matching
 {
   dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
 
-  dw_expand_symtabs_matching_file_matcher (per_objfile, file_matcher);
+  if (file_matcher != nullptr)
+    {
+      if ((objfile->flags & OBJF_DOWNLOAD_DEFERRED) == 0)
+	dw_expand_symtabs_matching_file_matcher (per_objfile, file_matcher);
+      else if (filename_in_debug_line (objfile, file_matcher))
+	{
+	  /* If OBJFILE's debuginfo hasn't been downloaded and there is a file
+	     name to be matched, download the .debug_line (and possibly
+	     .debug_line_str) separately.  Search the separate .debug_line for
+	     a match.  If there is a match, then download the full debuginfo.
+	     Return early to move on to expanding symtabs in the debuginfo
+	     objfile.  */
+	  read_full_dwarf_from_debuginfod (objfile);
+	  return true;
+	}
+    }
 
   /* This invariant is documented in quick-functions.h.  */
   gdb_assert (lookup_name != nullptr || symbol_matcher == nullptr);
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index d07e0d86970..5cb222bd2cc 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -81,6 +81,7 @@ 
 #include <optional>
 #include "gdbsupport/underlying.h"
 #include "gdbsupport/hash_enum.h"
+#include "gdbsupport/scoped_mmap.h"
 #include "filename-seen-cache.h"
 #include "producer.h"
 #include <fcntl.h>
@@ -99,6 +100,7 @@ 
 #include "dwarf2/error.h"
 #include "inferior.h"
 #include "debuginfod-support.h"
+#include "exceptions.h"
 
 /* When == 1, print basic high level tracing messages.
    When > 1, be more verbose.
@@ -1953,6 +1955,213 @@  dw2_get_file_names (dwarf2_per_cu_data *this_cu,
   return this_cu->file_names;
 }
 
+#if !HAVE_SYS_MMAN_H
+
+bool
+mapped_debug_line::contains_matching_filename
+  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  return false;
+}
+
+gdb::array_view<const gdb_byte>
+mapped_debug_line::read_debug_line_separate
+  (const char *filename, std::unique_ptr<index_cache_resource> *resource)
+{
+  return {};
+}
+
+bool
+mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
+{
+  return false;
+}
+
+void
+mapped_debug_line::map_filenames
+  (gdb::function_view<symbol_filename_ftype> fun,
+   bool need_fullname)
+{
+  return;
+}
+
+#else /* !HAVE_SYS_MMAN_H */
+
+struct line_resource_mmap final : public index_cache_resource
+{
+  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
+     file doesn't exist.  */
+  line_resource_mmap (const char *filename)
+    : mapping (mmap_file (filename))
+  {}
+
+  scoped_mmap mapping;
+};
+
+/* See read.h.  */
+
+bool
+mapped_debug_line::contains_matching_filename
+  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  for (line_header_up &lh : line_headers)
+    for (file_entry &fe : lh->file_names ())
+      {
+	const char *filename = fe.name;
+
+	if (file_matcher (fe.name, false))
+	  return true;
+
+	bool basename_match = file_matcher (lbasename (fe.name), true);
+
+	if (!basenames_may_differ && !basename_match)
+	  continue;
+
+	/* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
+	   until DWARF5.  Since we don't have access to the CU at this
+	   point we just check for a partial match on the filename.
+	   If there is a match, the full debuginfo will be downloaded
+	   ane the match will be re-evalute with DW_AT_comp_dir.  */
+	if (lh->version < 5 && fe.d_index == 0)
+	  return basename_match;
+
+	const char *dirname = fe.include_dir (&*lh);
+	std::string fullname;
+
+	if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
+	  fullname = filename;
+	else
+	  fullname = std::string (dirname) + SLASH_STRING + filename;
+
+	gdb::unique_xmalloc_ptr<char> rewritten
+	  = rewrite_source_path (fullname.c_str ());
+	if (rewritten != nullptr)
+	  fullname = rewritten.release ();
+
+	if (file_matcher (fullname.c_str (), false))
+	  return true;
+      }
+
+  return false;
+}
+
+/* See read.h.  */
+
+void
+mapped_debug_line::map_filenames
+  (gdb::function_view<symbol_filename_ftype> fun,
+   bool need_fullname)
+{
+  for (line_header_up &lh : line_headers)
+    for (file_entry &fe : lh->file_names ())
+      {
+	const char *filename = fe.name;
+
+	if (!need_fullname)
+	  {
+	    fun (filename, nullptr);
+	    continue;
+	  }
+
+	const char *dirname = fe.include_dir (&*lh);
+	std::string fullname;
+
+	if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
+	  fullname = filename;
+	else
+	  fullname = std::string (dirname) + SLASH_STRING + filename;
+
+	gdb::unique_xmalloc_ptr<char> rewritten
+	  = rewrite_source_path (fullname.c_str ());
+	if (rewritten != nullptr)
+	  fullname = rewritten.release ();
+
+	fun (filename, fullname.c_str ());
+      }
+}
+
+/* See read.h.  */
+
+gdb::array_view<const gdb_byte>
+mapped_debug_line::read_debug_line_separate
+  (const char *filename, std::unique_ptr<index_cache_resource> *resource)
+{
+  if (filename == nullptr)
+    return {};
+
+  try
+  {
+    line_resource_mmap *mmap_resource
+      = new line_resource_mmap (filename);
+
+    resource->reset (mmap_resource);
+
+    return gdb::array_view<const gdb_byte>
+      ((const gdb_byte *) mmap_resource->mapping.get (),
+       mmap_resource->mapping.size ());
+  }
+  catch (const gdb_exception &except)
+  {
+    exception_print (gdb_stderr, except);
+  }
+
+  return {};
+}
+
+/* See read.h.  */
+
+bool
+mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
+{
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  if (build_id == nullptr)
+    return false;
+
+  gdb::unique_xmalloc_ptr<char> line_path;
+  scoped_fd line_fd = debuginfod_section_query (build_id->data,
+						build_id->size,
+						bfd_get_filename
+						  (objfile->obfd.get ()),
+						".debug_line",
+						&line_path);
+
+  if (line_fd.get () < 0)
+    return false;
+
+  gdb::unique_xmalloc_ptr<char> line_str_path;
+  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
+						    build_id->size,
+						    bfd_get_filename
+						      (objfile->obfd.get ()),
+						    ".debug_line_str",
+						    &line_str_path);
+
+  line_data = read_debug_line_separate (line_path.get (), &line_resource);
+  line_str_data = read_debug_line_separate (line_str_path.get (),
+					    &line_str_resource);
+
+  const gdb_byte *line_ptr = line_data.data ();
+
+  while (line_ptr < line_data.data () + line_data.size ())
+    {
+      line_header_up lh
+	= dwarf_decode_line_header (objfile->obfd.get (),
+				    line_data, line_str_data,
+				    &line_ptr, false,
+				    nullptr, nullptr);
+      line_headers.emplace_back (std::move (lh));
+    }
+
+  return true;
+}
+#endif /* !HAVE_SYS_MMAN_H */
+
+mapped_debug_line::mapped_debug_line (objfile *objfile)
+{
+  if (!read_debug_line_from_debuginfod (objfile))
+    line_headers.clear ();
+}
+
 /* A helper for the "quick" functions which computes and caches the
    real path for a given file name from the line table.  */
 
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 49ba9d82117..aba4787e638 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -32,6 +32,7 @@ 
 #include "gdbsupport/hash_enum.h"
 #include "gdbsupport/function-view.h"
 #include "gdbsupport/packed.h"
+#include "dwarf2/line-header.h"
 
 /* Hold 'maintenance (set|show) dwarf' commands.  */
 extern struct cmd_list_element *set_dwarf_cmdlist;
@@ -924,4 +925,40 @@  extern htab_up create_quick_file_names_table (unsigned int nr_initial_entries);
 
 extern void read_full_dwarf_from_debuginfod (struct objfile *);
 
+struct mapped_debug_line
+{
+  mapped_debug_line (objfile *objfile);
+
+  /* Return true if any of the mapped .debug_line's filenames match
+     FILE_MATCHER.  */
+
+  bool contains_matching_filename
+    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
+
+  /* Call FUN with each filename in this mapped .debug_line.  Include
+     each file's fullname if NEED_FULLNAME is true.  */
+
+  void map_filenames (gdb::function_view<symbol_filename_ftype> fun,
+		      bool need_fullname);
+
+private:
+  std::vector<line_header_up> line_headers;
+
+  gdb::array_view<const gdb_byte> line_data;
+  gdb::array_view<const gdb_byte> line_str_data;
+
+  std::unique_ptr<index_cache_resource> line_resource;
+  std::unique_ptr<index_cache_resource> line_str_resource;
+
+  /* Download the .debug_line and .debug_line_str associated with OBJFILE
+     and populate line_headers.  */
+
+  bool read_debug_line_from_debuginfod (objfile *objfile);
+
+  /* Initialize line_data and line_str_data with the .debug_line and
+    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
+
+  gdb::array_view<const gdb_byte> read_debug_line_separate
+    (const char *filename, std::unique_ptr<index_cache_resource> *resource);
+};
 #endif /* DWARF2READ_H */
diff --git a/gdb/mi/mi-out.c b/gdb/mi/mi-out.c
index ff93d2cd448..8ad2c7f4497 100644
--- a/gdb/mi/mi-out.c
+++ b/gdb/mi/mi-out.c
@@ -277,7 +277,14 @@  mi_ui_out::do_progress_notify (const std::string &msg, const char *unit,
 
   if (info.state == progress_update::START)
     {
-      gdb_printf ("%s...\n", msg.c_str ());
+      std::string prefix;
+      if (cur_prefix_state == prefix_state_t::NEWLINE_NEEDED)
+	{
+	  prefix = "\n";
+	  cur_prefix_state = prefix_state_t::NEWLINE_PRINTED;
+	}
+
+      gdb_printf ("%s...\n", (prefix + msg).c_str ());
       info.state = progress_update::WORKING;
     }
 }
diff --git a/gdb/testsuite/gdb.debuginfod/section.exp b/gdb/testsuite/gdb.debuginfod/section.exp
index 381791bca50..8e482364434 100644
--- a/gdb/testsuite/gdb.debuginfod/section.exp
+++ b/gdb/testsuite/gdb.debuginfod/section.exp
@@ -18,6 +18,7 @@ 
 standard_testfile
 
 load_lib debuginfod-support.exp
+load_lib completion-support.exp
 
 clean_restart
 require allow_debuginfod_tests
@@ -144,6 +145,33 @@  proc_with_prefix local_url { } {
     # Hit the breakpoint.
     set res ".*Breakpoint 2, libsection2_test.*\"In ${libfile2}\\\\n\".*"
     gdb_test "c" $res "break continue"
+
+    clean_restart_with_prompt $binfile "line 1"
+
+    # List source file using .debug_line download.
+    set res ".*\.debug_line.*$lib_sl1.*21.*extern void libsection2_test.*"
+    gdb_test "list $libsrc1:21" $res "line 1 list"
+
+    clean_restart_with_prompt $binfile "line 2"
+
+    # Set breakpoint using .debug_line download.
+    set res ".*section \.debug_line for $lib_sl1.*Breakpoint 2 at.*$libsrc1.*"
+    gdb_test "br $libsrc1:41" $res "line 2 br"
+
+    # Continue to breakpoint.
+    set res "Breakpoint 2, libsection1_test.*\"Cancelling thread\\\\n\".*"
+    gdb_test "c" $res "line 2 continue"
+
+    # Check that download progress message is correctly formatted
+    # when printing threads.
+    set res ".*separate debug info for $lib_sl2\.\.\.\r\n.* 2    Thread.*"
+    gdb_test "info thr" $res "line thread"
+
+    clean_restart_with_prompt $binfile "autocomplete"
+
+    # Download debuginfo during autocompletion.
+    test_gdb_complete_tab_unique "br lib" \
+	".*separate debug info for $lib_sl1.*" ""
 }
 
 # Create CACHE and DB directories ready for debuginfod to use.
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index 7e9f238fcf6..ad628b22c66 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -31,6 +31,9 @@ 
 #include <memory>
 #include <string>
 
+/* Current state of newline prefixing for progress update messages.  */
+prefix_state_t cur_prefix_state = prefix_state_t::NEWLINE_OFF;
+
 namespace {
 
 /* A header of a ui_out_table.  */
diff --git a/gdb/ui-out.h b/gdb/ui-out.h
index ef9ce4fad33..fa06a29e268 100644
--- a/gdb/ui-out.h
+++ b/gdb/ui-out.h
@@ -299,6 +299,21 @@  class ui_out
       BAR
     };
 
+    /* Used to communicate the status of a newline prefix for the next progress
+       update message.  */
+    enum prefix_state
+    {
+      /* Do not modify the next progress update message.  */
+      NEWLINE_OFF,
+
+      /* The next progress update message should include a newline prefix.  */
+      NEWLINE_NEEDED,
+
+      /* A newline prefix was included in a debuginfod progress update
+	 message.  */
+      NEWLINE_PRINTED
+    };
+
     /* SHOULD_PRINT indicates whether something should be printed for a tty.  */
     progress_update ()
     {
@@ -396,6 +411,11 @@  class ui_out
   ui_out_level *current_level () const;
 };
 
+typedef ui_out::progress_update::prefix_state prefix_state_t;
+
+/* Current state of the newline prefix.  */
+extern prefix_state_t cur_prefix_state;
+
 /* Start a new tuple or list on construction, and end it on
    destruction.  Normally this is used via the typedefs
    ui_out_emit_tuple and ui_out_emit_list.  */