[7/7,v2] gdb/debuginfod: Add .debug_line downloading

Message ID 20230417180743.1213952-1-amerey@redhat.com
State New
Headers
Series None |

Commit Message

Aaron Merey April 17, 2023, 6:07 p.m. UTC
  v1 can be found here:
https://sourceware.org/pipermail/gdb-patches/2023-February/197459.html

v2 merges dwarf_decode_line_header_separate with
dwarf_decode_line_header and read_formatted_entries_separate with
read_formatted_entries in order to reduce code duplication.

---

'set debuginfod enabled lazy' 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 gdb can search the
debuginfo for matching source filenames.  This can result in unnecessary
downloading.

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 a deferred debuginfo.
---
 gdb/dwarf2/line-header.c    | 215 +++++++++++++++++++++++-------------
 gdb/dwarf2/line-header.h    |  10 ++
 gdb/dwarf2/read-gdb-index.c |  27 +++++
 gdb/dwarf2/read.c           | 165 +++++++++++++++++++++++++++
 gdb/dwarf2/read.h           |  31 ++++++
 5 files changed, 371 insertions(+), 77 deletions(-)
  

Comments

Aaron Merey May 2, 2023, 2:25 p.m. UTC | #1
Ping

Thanks,
Aaron

On Mon, Apr 17, 2023 at 2:07 PM Aaron Merey <amerey@redhat.com> wrote:
>
> v1 can be found here:
> https://sourceware.org/pipermail/gdb-patches/2023-February/197459.html
>
> v2 merges dwarf_decode_line_header_separate with
> dwarf_decode_line_header and read_formatted_entries_separate with
> read_formatted_entries in order to reduce code duplication.
>
> ---
>
> 'set debuginfod enabled lazy' 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 gdb can search the
> debuginfo for matching source filenames.  This can result in unnecessary
> downloading.
>
> 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 a deferred debuginfo.
> ---
>  gdb/dwarf2/line-header.c    | 215 +++++++++++++++++++++++-------------
>  gdb/dwarf2/line-header.h    |  10 ++
>  gdb/dwarf2/read-gdb-index.c |  27 +++++
>  gdb/dwarf2/read.c           | 165 +++++++++++++++++++++++++++
>  gdb/dwarf2/read.h           |  31 ++++++
>  5 files changed, 371 insertions(+), 77 deletions(-)
>
> diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> index 9d74c8fe75b..5eaff9c5a48 100644
> --- a/gdb/dwarf2/line-header.c
> +++ b/gdb/dwarf2/line-header.c
> @@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
>  {
>    LONGEST length = read_initial_length (abfd, buf, bytes_read);
>
> -  gdb_assert (cu_header->initial_length_size == 4
> -             || cu_header->initial_length_size == 8
> -             || cu_header->initial_length_size == 12);
> +  if (cu_header != nullptr)
> +    {
> +      gdb_assert (cu_header->initial_length_size == 4
> +                 || cu_header->initial_length_size == 8
> +                 || cu_header->initial_length_size == 12);
>
> -  if (cu_header->initial_length_size != *bytes_read)
> -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> +      if (cu_header->initial_length_size != *bytes_read)
> +       complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> +    }
>
>    *offset_size = (*bytes_read == 4) ? 4 : 8;
>    return length;
>  }
>
> -/* Read directory or file name entry format, starting with byte of
> -   format count entries, ULEB128 pairs of entry formats, ULEB128 of
> -   entries count and the entries themselves in the described entry
> -   format.  */
> +
> +/* Like read_formatted_entries but the .debug_line and .debug_line_str
> +   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
> +   where these sections are read from separate files without necessarily
> +   having access to the entire debuginfo file they originate from.  */
>
>  static void
> -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> -                       const gdb_byte **bufp, struct line_header *lh,
> -                       unsigned int offset_size,
> -                       void (*callback) (struct line_header *lh,
> -                                         const char *name,
> -                                         dir_index d_index,
> -                                         unsigned int mod_time,
> -                                         unsigned int length))
> +read_formatted_entries
> +  (bfd *parent_bfd, const gdb_byte **line_bufp,
> +   const gdb::array_view<const gdb_byte> line_str_data,
> +   struct line_header *lh,
> +   unsigned int offset_size,
> +   void (*callback) (struct line_header *lh,
> +                    const char *name,
> +                    dir_index d_index,
> +                    unsigned int mod_time,
> +                    unsigned int length))
>  {
>    gdb_byte format_count, formati;
>    ULONGEST data_count, datai;
> -  const gdb_byte *buf = *bufp;
> +  const gdb_byte *buf = *line_bufp;
> +  const gdb_byte *str_buf = line_str_data.data ();
>    const gdb_byte *format_header_data;
>    unsigned int bytes_read;
>
> -  format_count = read_1_byte (abfd, buf);
> +  format_count = read_1_byte (parent_bfd, buf);
>    buf += 1;
>    format_header_data = buf;
>    for (formati = 0; formati < format_count; formati++)
>      {
> -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
>        buf += bytes_read;
> -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
>        buf += bytes_read;
>      }
>
> -  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
> +  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
>    buf += bytes_read;
>    for (datai = 0; datai < data_count; datai++)
>      {
> @@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>
>        for (formati = 0; formati < format_count; formati++)
>         {
> -         ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
> +         ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
>           format += bytes_read;
>
> -         ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> +         ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
>           format += bytes_read;
>
>           gdb::optional<const char *> string;
> @@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>           switch (form)
>             {
>             case DW_FORM_string:
> -             string.emplace (read_direct_string (abfd, buf, &bytes_read));
> +             string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
>               buf += bytes_read;
>               break;
>
>             case DW_FORM_line_strp:
>               {
> -               const char *str
> -                 = per_objfile->read_line_string (buf, offset_size);
> +               if (line_str_data.empty ())
> +                 error (_("Dwarf Error: DW_FORM_line_strp used without " \
> +                          "required section"));
> +               if (line_str_data.size () <= offset_size)
> +                 error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> +                          "of section .debug_line"));
> +
> +               ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
> +
> +               const char *str;
> +               if (str_buf[str_offset] == '\0')
> +                 str = nullptr;
> +               else
> +                 str = (const char *) (str_buf + str_offset);
>                 string.emplace (str);
>                 buf += offset_size;
> +               break;
>               }
> -             break;
>
>             case DW_FORM_data1:
> -             uint.emplace (read_1_byte (abfd, buf));
> +             uint.emplace (read_1_byte (parent_bfd, buf));
>               buf += 1;
>               break;
>
>             case DW_FORM_data2:
> -             uint.emplace (read_2_bytes (abfd, buf));
> +             uint.emplace (read_2_bytes (parent_bfd, buf));
>               buf += 2;
>               break;
>
>             case DW_FORM_data4:
> -             uint.emplace (read_4_bytes (abfd, buf));
> +             uint.emplace (read_4_bytes (parent_bfd, buf));
>               buf += 4;
>               break;
>
>             case DW_FORM_data8:
> -             uint.emplace (read_8_bytes (abfd, buf));
> +             uint.emplace (read_8_bytes (parent_bfd, buf));
>               buf += 8;
>               break;
>
> @@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>               break;
>
>             case DW_FORM_udata:
> -             uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
> +             uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
>               buf += bytes_read;
>               break;
>
> @@ -244,28 +263,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
>      }
>
> -  *bufp = buf;
> +  *line_bufp = buf;
>  }
>
>  /* See line-header.h.  */
>
>  line_header_up
> -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> -                          dwarf2_per_objfile *per_objfile,
> -                          struct dwarf2_section_info *section,
> -                          const struct comp_unit_head *cu_header,
> -                          const char *comp_dir)
> +dwarf_decode_line_header (bfd *parent_bfd,
> +                         gdb::array_view<const gdb_byte> line_data,
> +                         gdb::array_view<const gdb_byte> line_str_data,
> +                         const gdb_byte **debug_line_ptr,
> +                         bool is_dwz,
> +                         const struct comp_unit_head *cu_header,
> +                         const char *comp_dir)
>  {
> -  const gdb_byte *line_ptr;
> +  const gdb_byte *line_ptr, *buf;
>    unsigned int bytes_read, offset_size;
>    int i;
>    const char *cur_dir, *cur_file;
>
> -  bfd *abfd = section->get_bfd_owner ();
> +  buf = *debug_line_ptr;
>
>    /* Make sure that at least there's room for the total_length field.
>       That could be 12 bytes long, but we're just going to fudge that.  */
> -  if (to_underlying (sect_off) + 4 >= section->size)
> +  if (buf + 4 >= line_data.data () + line_data.size ())
>      {
>        dwarf2_statement_list_fits_in_line_number_section_complaint ();
>        return 0;
> @@ -273,62 +294,65 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>
>    line_header_up lh (new line_header (comp_dir));
>
> -  lh->sect_off = sect_off;
> +  lh->sect_off = (sect_offset) (buf - line_data.data ());
>    lh->offset_in_dwz = is_dwz;
>
> -  line_ptr = section->buffer + to_underlying (sect_off);
> +  line_ptr = buf;
>
>    /* Read in the header.  */
>    LONGEST unit_length
> -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> +    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
>                                               &bytes_read, &offset_size);
> -  line_ptr += bytes_read;
>
> -  const gdb_byte *start_here = line_ptr;
> +  line_ptr += bytes_read;
>
> -  if (line_ptr + unit_length > (section->buffer + section->size))
> +  if (line_ptr + unit_length > buf + line_data.size ())
>      {
>        dwarf2_statement_list_fits_in_line_number_section_complaint ();
>        return 0;
>      }
> +
> +  const gdb_byte *start_here = line_ptr;
> +
>    lh->statement_program_end = start_here + unit_length;
> -  lh->version = read_2_bytes (abfd, line_ptr);
> +  lh->version = read_2_bytes (parent_bfd, line_ptr);
>    line_ptr += 2;
>    if (lh->version > 5)
>      {
>        /* This is a version we don't understand.  The format could have
>          changed in ways we don't handle properly so just punt.  */
>        complaint (_("unsupported version in .debug_line section"));
> -      return NULL;
> +      return nullptr;
>      }
>    if (lh->version >= 5)
>      {
>        gdb_byte segment_selector_size;
>
>        /* Skip address size.  */
> -      read_1_byte (abfd, line_ptr);
> +      read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>
> -      segment_selector_size = read_1_byte (abfd, line_ptr);
> +      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>        if (segment_selector_size != 0)
>         {
>           complaint (_("unsupported segment selector size %u "
>                        "in .debug_line section"),
>                      segment_selector_size);
> -         return NULL;
> +         return nullptr;
>         }
>      }
>
> -  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
> +  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
>    line_ptr += offset_size;
>    lh->statement_program_start = line_ptr + header_length;
> -  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
> +
> +  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
>
>    if (lh->version >= 4)
>      {
> -      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
> +      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>      }
>    else
> @@ -341,41 +365,47 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>                    "in `.debug_line' section"));
>      }
>
> -  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
> +  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> -  lh->line_base = read_1_signed_byte (abfd, line_ptr);
> +
> +  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> -  lh->line_range = read_1_byte (abfd, line_ptr);
> +
> +  lh->line_range = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> -  lh->opcode_base = read_1_byte (abfd, line_ptr);
> +
> +  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> +
>    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
>
>    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
>    for (i = 1; i < lh->opcode_base; ++i)
>      {
> -      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
> +      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>      }
>
>    if (lh->version >= 5)
>      {
>        /* Read directory table.  */
> -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> -                             offset_size,
> -                             [] (struct line_header *header, const char *name,
> -                                 dir_index d_index, unsigned int mod_time,
> -                                 unsigned int length)
> +      read_formatted_entries
> +       (parent_bfd, &line_ptr, line_str_data,
> +        lh.get (), offset_size,
> +        [] (struct line_header *header, const char *name,
> +            dir_index d_index, unsigned int mod_time,
> +            unsigned int length)
>         {
>           header->add_include_dir (name);
>         });
>
>        /* Read file name table.  */
> -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> -                             offset_size,
> -                             [] (struct line_header *header, const char *name,
> -                                 dir_index d_index, unsigned int mod_time,
> -                                 unsigned int length)
> +      read_formatted_entries
> +       (parent_bfd, &line_ptr, line_str_data,
> +        lh.get (), offset_size,
> +        [] (struct line_header *header, const char *name,
> +            dir_index d_index, unsigned int mod_time,
> +            unsigned int length)
>         {
>           header->add_file_name (name, d_index, mod_time, length);
>         });
> @@ -383,7 +413,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>    else
>      {
>        /* Read directory table.  */
> -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> +      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
>         {
>           line_ptr += bytes_read;
>           lh->add_include_dir (cur_dir);
> @@ -391,17 +421,17 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>        line_ptr += bytes_read;
>
>        /* Read file name table.  */
> -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> +      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
>         {
>           unsigned int mod_time, length;
>           dir_index d_index;
>
>           line_ptr += bytes_read;
> -         d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> +         d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
>           line_ptr += bytes_read;
> -         mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> +         mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
>           line_ptr += bytes_read;
> -         length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> +         length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
>           line_ptr += bytes_read;
>
>           lh->add_file_name (cur_file, d_index, mod_time, length);
> @@ -409,9 +439,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>        line_ptr += bytes_read;
>      }
>
> -  if (line_ptr > (section->buffer + section->size))
> +  if (line_ptr > (buf + line_data.size ()))
>      complaint (_("line number info header doesn't "
>                  "fit in `.debug_line' section"));
>
> +  *debug_line_ptr += unit_length + offset_size;
>    return lh;
>  }
> +
> +line_header_up
> +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> +                          dwarf2_per_objfile *per_objfile,
> +                          struct dwarf2_section_info *section,
> +                          const struct comp_unit_head *cu_header,
> +                          const char *comp_dir)
> +{
> +  struct objfile *objfile = per_objfile->objfile;
> +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> +
> +  /* Read .debug_line.  */
> +  dwarf2_section_info *line_sec = &per_bfd->line;
> +  bfd_size_type line_size = line_sec->get_size (objfile);
> +
> +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> +
> +  /* Read .debug_line_str.  */
> +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> +
> +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> +                                           line_str_size);
> +
> +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> +
> +  return dwarf_decode_line_header
> +    (per_bfd->obfd, line, line_str, &line_ptr,
> +     is_dwz, cu_header, comp_dir);
> +}
> diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> index 59a42e336f5..44e32828ddb 100644
> --- a/gdb/dwarf2/line-header.h
> +++ b/gdb/dwarf2/line-header.h
> @@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
>     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
>     const char *comp_dir);
>
> +/* Like above but the .debug_line and .debug_line_str are stored in
> +   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
> +   statement program header within LINE_DATA.  */
> +
> +extern line_header_up dwarf_decode_line_header
> +  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
> +   gdb::array_view<const gdb_byte> line_str_data,
> +   const gdb_byte **debug_line_ptr, bool is_dwz,
> +  const comp_unit_head *cu_header, const char *comp_dir);
> +
>  #endif /* DWARF2_LINE_HEADER_H */
> diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> index 895fbede7c2..fe33f23fdfa 100644
> --- a/gdb/dwarf2/read-gdb-index.c
> +++ b/gdb/dwarf2/read-gdb-index.c
> @@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
>    }
>  };
>
> +struct mapped_debug_line;
> +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> +
>  struct dwarf2_gdb_index : public dwarf2_base_index_functions
>  {
>    /* This dumps minimal information about the index.
> @@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
>       debuginfo if necessary.  */
>    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> +
> +  /* Filename information related to this .gdb_index.  */
> +  mapped_debug_line_up mdl;
> +
> +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> +  bool filename_in_debug_line
> +  (objfile *objfile,
> +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
>  };
>
>  void
> @@ -588,6 +600,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
>    return result;
>  }
>
> +bool
> +dwarf2_gdb_index::filename_in_debug_line
> +  (objfile *objfile,
> +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  if (mdl == nullptr)
> +    mdl.reset (new mapped_debug_line (objfile));
> +
> +  return mdl->contains_matching_filename (file_matcher);
> +}
> +
>  bool
>  dwarf2_gdb_index::expand_symtabs_matching
>      (struct objfile *objfile,
> @@ -616,6 +639,10 @@ dwarf2_gdb_index::expand_symtabs_matching
>           return false;
>         }
>
> +      if (file_matcher != nullptr
> +         && !filename_in_debug_line (objfile, file_matcher))
> +       return true;
> +
>        read_full_dwarf_from_debuginfod (objfile, this);
>        return true;
>      }
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index e561ec035e7..39ee56d7204 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -81,6 +81,7 @@
>  #include "gdbsupport/gdb_optional.h"
>  #include "gdbsupport/underlying.h"
>  #include "gdbsupport/hash_enum.h"
> +#include "gdbsupport/scoped_mmap.h"
>  #include "filename-seen-cache.h"
>  #include "producer.h"
>  #include <fcntl.h>
> @@ -2110,6 +2111,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
>    return this_cu->file_names;
>  }
>
> +#if !HAVE_SYS_MMAN_H
> +
> +bool
> +mapped_debug_line::contains_matching_filename
> +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  return false;
> +}
> +
> +gdb::array_view<const gdb_byte>
> +mapped_debug_line::read_debug_line_separate
> +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> +{
> +  return {};
> +}
> +
> +bool
> +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> +{
> +  return false;
> +}
> +
> +#else /* !HAVE_SYS_MMAN_H */
> +
> +struct line_resource_mmap final : public index_cache_resource
> +{
> +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> +     file doesn't exist. */
> +  line_resource_mmap (const char *filename)
> +    : mapping (mmap_file (filename))
> +  {}
> +
> +  scoped_mmap mapping;
> +};
> +
> +/* See read.h.  */
> +
> +bool
> +mapped_debug_line::contains_matching_filename
> +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  for (line_header_up &lh : line_headers)
> +    for (file_entry &fe : lh->file_names ())
> +      {
> +       const char *filename = fe.name;
> +
> +       if (file_matcher (fe.name, false))
> +         return true;
> +
> +       bool basename_match = file_matcher (lbasename (fe.name), true);
> +
> +       if (!basenames_may_differ && !basename_match)
> +         continue;
> +
> +       /* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> +          until DWARF5.  Since we don't have access to the CU at this
> +          point we just check for a partial match on the filename.
> +          If there is a match, the full debuginfo will be downloaded
> +          ane the match will be re-evalute with DW_AT_comp_dir.  */
> +       if (lh->version < 5 && fe.d_index == 0)
> +         return basename_match;
> +
> +       const char *dirname = fe.include_dir (&*lh);
> +       std::string fullname;
> +
> +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> +         fullname = filename;
> +       else
> +         fullname = std::string (dirname) + SLASH_STRING + filename;
> +
> +       gdb::unique_xmalloc_ptr<char> rewritten
> +         = rewrite_source_path (fullname.c_str ());
> +       if (rewritten != nullptr)
> +         fullname = rewritten.release ();
> +
> +       if (file_matcher (fullname.c_str (), false))
> +         return true;
> +      }
> +
> +  return false;
> +}
> +
> +/* See read.h.  */
> +
> +gdb::array_view<const gdb_byte>
> +mapped_debug_line::read_debug_line_separate
> +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> +{
> +  if (filename == nullptr)
> +    return {};
> +
> +  try
> +  {
> +    line_resource_mmap *mmap_resource
> +      = new line_resource_mmap (filename);
> +
> +    resource->reset (mmap_resource);
> +
> +    return gdb::array_view<const gdb_byte>
> +      ((const gdb_byte *) mmap_resource->mapping.get (),
> +       mmap_resource->mapping.size ());
> +  }
> +  catch (const gdb_exception &except)
> +  {
> +    exception_print (gdb_stderr, except);
> +  }
> +
> +  return {};
> +}
> +
> +/* See read.h.  */
> +
> +bool
> +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> +{
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  if (build_id == nullptr)
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> line_path;
> +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> +                                               build_id->size,
> +                                               bfd_get_filename
> +                                                 (objfile->obfd.get ()),
> +                                               ".debug_line",
> +                                               &line_path);
> +
> +  if (line_fd.get () < 0)
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> line_str_path;
> +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> +                                                   build_id->size,
> +                                                   bfd_get_filename
> +                                                     (objfile->obfd.get ()),
> +                                                   ".debug_line_str",
> +                                                   &line_str_path);
> +
> +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> +  line_str_data = read_debug_line_separate (line_str_path.get (),
> +                                           &line_str_resource);
> +
> +  const gdb_byte *line_ptr = line_data.data ();
> +
> +  while (line_ptr < line_data.data () + line_data.size ())
> +    {
> +      line_header_up lh
> +       = dwarf_decode_line_header (objfile->obfd.get (),
> +                                   line_data, line_str_data,
> +                                   &line_ptr, false,
> +                                   nullptr, nullptr);
> +      line_headers.emplace_back (lh.release ());
> +    }
> +
> +  return true;
> +}
> +#endif /* !HAVE_SYS_MMAN_H */
> +
> +mapped_debug_line::mapped_debug_line (objfile *objfile)
> +{
> +  if (!read_debug_line_from_debuginfod (objfile))
> +    line_headers.clear ();
> +}
> +
>  /* A helper for the "quick" functions which computes and caches the
>     real path for a given file name from the line table.  */
>
> diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> index e3131693b81..b8a8b76bde0 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -34,6 +34,7 @@
>  #include "gdbsupport/hash_enum.h"
>  #include "gdbsupport/function-view.h"
>  #include "gdbsupport/packed.h"
> +#include "dwarf2/line-header.h"
>
>  /* Hold 'maintenance (set|show) dwarf' commands.  */
>  extern struct cmd_list_element *set_dwarf_cmdlist;
> @@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
>  extern void read_full_dwarf_from_debuginfod (struct objfile *,
>                                              dwarf2_base_index_functions *);
>
> +struct mapped_debug_line
> +{
> +  mapped_debug_line (objfile *objfile);
> +
> +  /* Return true if any of the mapped .debug_line's filenames match
> +     FILE_MATCHER.  */
> +
> +  bool contains_matching_filename
> +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> +
> +private:
> +  std::vector<line_header_up> line_headers;
> +
> +  gdb::array_view<const gdb_byte> line_data;
> +  gdb::array_view<const gdb_byte> line_str_data;
> +
> +  std::unique_ptr<index_cache_resource> line_resource;
> +  std::unique_ptr<index_cache_resource> line_str_resource;
> +
> +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> +     and populate line_headers.  */
> +
> +  bool read_debug_line_from_debuginfod (objfile *objfile);
> +
> +  /* Initialize line_data and line_str_data with the .debug_line and
> +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> +
> +  gdb::array_view<const gdb_byte> read_debug_line_separate
> +    (char *filename, std::unique_ptr<index_cache_resource> *resource);
> +};
>  #endif /* DWARF2READ_H */
> --
> 2.39.2
>
  
Aaron Merey May 9, 2023, 1:49 p.m. UTC | #2
Ping

Thanks,
Aaron

On Tue, May 2, 2023 at 10:25 AM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Mon, Apr 17, 2023 at 2:07 PM Aaron Merey <amerey@redhat.com> wrote:
> >
> > v1 can be found here:
> > https://sourceware.org/pipermail/gdb-patches/2023-February/197459.html
> >
> > v2 merges dwarf_decode_line_header_separate with
> > dwarf_decode_line_header and read_formatted_entries_separate with
> > read_formatted_entries in order to reduce code duplication.
> >
> > ---
> >
> > 'set debuginfod enabled lazy' 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 gdb can search the
> > debuginfo for matching source filenames.  This can result in unnecessary
> > downloading.
> >
> > 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 a deferred debuginfo.
> > ---
> >  gdb/dwarf2/line-header.c    | 215 +++++++++++++++++++++++-------------
> >  gdb/dwarf2/line-header.h    |  10 ++
> >  gdb/dwarf2/read-gdb-index.c |  27 +++++
> >  gdb/dwarf2/read.c           | 165 +++++++++++++++++++++++++++
> >  gdb/dwarf2/read.h           |  31 ++++++
> >  5 files changed, 371 insertions(+), 77 deletions(-)
> >
> > diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> > index 9d74c8fe75b..5eaff9c5a48 100644
> > --- a/gdb/dwarf2/line-header.c
> > +++ b/gdb/dwarf2/line-header.c
> > @@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
> >  {
> >    LONGEST length = read_initial_length (abfd, buf, bytes_read);
> >
> > -  gdb_assert (cu_header->initial_length_size == 4
> > -             || cu_header->initial_length_size == 8
> > -             || cu_header->initial_length_size == 12);
> > +  if (cu_header != nullptr)
> > +    {
> > +      gdb_assert (cu_header->initial_length_size == 4
> > +                 || cu_header->initial_length_size == 8
> > +                 || cu_header->initial_length_size == 12);
> >
> > -  if (cu_header->initial_length_size != *bytes_read)
> > -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > +      if (cu_header->initial_length_size != *bytes_read)
> > +       complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > +    }
> >
> >    *offset_size = (*bytes_read == 4) ? 4 : 8;
> >    return length;
> >  }
> >
> > -/* Read directory or file name entry format, starting with byte of
> > -   format count entries, ULEB128 pairs of entry formats, ULEB128 of
> > -   entries count and the entries themselves in the described entry
> > -   format.  */
> > +
> > +/* Like read_formatted_entries but the .debug_line and .debug_line_str
> > +   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
> > +   where these sections are read from separate files without necessarily
> > +   having access to the entire debuginfo file they originate from.  */
> >
> >  static void
> > -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > -                       const gdb_byte **bufp, struct line_header *lh,
> > -                       unsigned int offset_size,
> > -                       void (*callback) (struct line_header *lh,
> > -                                         const char *name,
> > -                                         dir_index d_index,
> > -                                         unsigned int mod_time,
> > -                                         unsigned int length))
> > +read_formatted_entries
> > +  (bfd *parent_bfd, const gdb_byte **line_bufp,
> > +   const gdb::array_view<const gdb_byte> line_str_data,
> > +   struct line_header *lh,
> > +   unsigned int offset_size,
> > +   void (*callback) (struct line_header *lh,
> > +                    const char *name,
> > +                    dir_index d_index,
> > +                    unsigned int mod_time,
> > +                    unsigned int length))
> >  {
> >    gdb_byte format_count, formati;
> >    ULONGEST data_count, datai;
> > -  const gdb_byte *buf = *bufp;
> > +  const gdb_byte *buf = *line_bufp;
> > +  const gdb_byte *str_buf = line_str_data.data ();
> >    const gdb_byte *format_header_data;
> >    unsigned int bytes_read;
> >
> > -  format_count = read_1_byte (abfd, buf);
> > +  format_count = read_1_byte (parent_bfd, buf);
> >    buf += 1;
> >    format_header_data = buf;
> >    for (formati = 0; formati < format_count; formati++)
> >      {
> > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> >        buf += bytes_read;
> > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> >        buf += bytes_read;
> >      }
> >
> > -  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
> > +  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> >    buf += bytes_read;
> >    for (datai = 0; datai < data_count; datai++)
> >      {
> > @@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >
> >        for (formati = 0; formati < format_count; formati++)
> >         {
> > -         ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
> > +         ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> >           format += bytes_read;
> >
> > -         ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> > +         ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> >           format += bytes_read;
> >
> >           gdb::optional<const char *> string;
> > @@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >           switch (form)
> >             {
> >             case DW_FORM_string:
> > -             string.emplace (read_direct_string (abfd, buf, &bytes_read));
> > +             string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
> >               buf += bytes_read;
> >               break;
> >
> >             case DW_FORM_line_strp:
> >               {
> > -               const char *str
> > -                 = per_objfile->read_line_string (buf, offset_size);
> > +               if (line_str_data.empty ())
> > +                 error (_("Dwarf Error: DW_FORM_line_strp used without " \
> > +                          "required section"));
> > +               if (line_str_data.size () <= offset_size)
> > +                 error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> > +                          "of section .debug_line"));
> > +
> > +               ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
> > +
> > +               const char *str;
> > +               if (str_buf[str_offset] == '\0')
> > +                 str = nullptr;
> > +               else
> > +                 str = (const char *) (str_buf + str_offset);
> >                 string.emplace (str);
> >                 buf += offset_size;
> > +               break;
> >               }
> > -             break;
> >
> >             case DW_FORM_data1:
> > -             uint.emplace (read_1_byte (abfd, buf));
> > +             uint.emplace (read_1_byte (parent_bfd, buf));
> >               buf += 1;
> >               break;
> >
> >             case DW_FORM_data2:
> > -             uint.emplace (read_2_bytes (abfd, buf));
> > +             uint.emplace (read_2_bytes (parent_bfd, buf));
> >               buf += 2;
> >               break;
> >
> >             case DW_FORM_data4:
> > -             uint.emplace (read_4_bytes (abfd, buf));
> > +             uint.emplace (read_4_bytes (parent_bfd, buf));
> >               buf += 4;
> >               break;
> >
> >             case DW_FORM_data8:
> > -             uint.emplace (read_8_bytes (abfd, buf));
> > +             uint.emplace (read_8_bytes (parent_bfd, buf));
> >               buf += 8;
> >               break;
> >
> > @@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >               break;
> >
> >             case DW_FORM_udata:
> > -             uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
> > +             uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
> >               buf += bytes_read;
> >               break;
> >
> > @@ -244,28 +263,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> >        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
> >      }
> >
> > -  *bufp = buf;
> > +  *line_bufp = buf;
> >  }
> >
> >  /* See line-header.h.  */
> >
> >  line_header_up
> > -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > -                          dwarf2_per_objfile *per_objfile,
> > -                          struct dwarf2_section_info *section,
> > -                          const struct comp_unit_head *cu_header,
> > -                          const char *comp_dir)
> > +dwarf_decode_line_header (bfd *parent_bfd,
> > +                         gdb::array_view<const gdb_byte> line_data,
> > +                         gdb::array_view<const gdb_byte> line_str_data,
> > +                         const gdb_byte **debug_line_ptr,
> > +                         bool is_dwz,
> > +                         const struct comp_unit_head *cu_header,
> > +                         const char *comp_dir)
> >  {
> > -  const gdb_byte *line_ptr;
> > +  const gdb_byte *line_ptr, *buf;
> >    unsigned int bytes_read, offset_size;
> >    int i;
> >    const char *cur_dir, *cur_file;
> >
> > -  bfd *abfd = section->get_bfd_owner ();
> > +  buf = *debug_line_ptr;
> >
> >    /* Make sure that at least there's room for the total_length field.
> >       That could be 12 bytes long, but we're just going to fudge that.  */
> > -  if (to_underlying (sect_off) + 4 >= section->size)
> > +  if (buf + 4 >= line_data.data () + line_data.size ())
> >      {
> >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> >        return 0;
> > @@ -273,62 +294,65 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >
> >    line_header_up lh (new line_header (comp_dir));
> >
> > -  lh->sect_off = sect_off;
> > +  lh->sect_off = (sect_offset) (buf - line_data.data ());
> >    lh->offset_in_dwz = is_dwz;
> >
> > -  line_ptr = section->buffer + to_underlying (sect_off);
> > +  line_ptr = buf;
> >
> >    /* Read in the header.  */
> >    LONGEST unit_length
> > -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> > +    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
> >                                               &bytes_read, &offset_size);
> > -  line_ptr += bytes_read;
> >
> > -  const gdb_byte *start_here = line_ptr;
> > +  line_ptr += bytes_read;
> >
> > -  if (line_ptr + unit_length > (section->buffer + section->size))
> > +  if (line_ptr + unit_length > buf + line_data.size ())
> >      {
> >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> >        return 0;
> >      }
> > +
> > +  const gdb_byte *start_here = line_ptr;
> > +
> >    lh->statement_program_end = start_here + unit_length;
> > -  lh->version = read_2_bytes (abfd, line_ptr);
> > +  lh->version = read_2_bytes (parent_bfd, line_ptr);
> >    line_ptr += 2;
> >    if (lh->version > 5)
> >      {
> >        /* This is a version we don't understand.  The format could have
> >          changed in ways we don't handle properly so just punt.  */
> >        complaint (_("unsupported version in .debug_line section"));
> > -      return NULL;
> > +      return nullptr;
> >      }
> >    if (lh->version >= 5)
> >      {
> >        gdb_byte segment_selector_size;
> >
> >        /* Skip address size.  */
> > -      read_1_byte (abfd, line_ptr);
> > +      read_1_byte (parent_bfd, line_ptr);
> >        line_ptr += 1;
> >
> > -      segment_selector_size = read_1_byte (abfd, line_ptr);
> > +      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
> >        line_ptr += 1;
> >        if (segment_selector_size != 0)
> >         {
> >           complaint (_("unsupported segment selector size %u "
> >                        "in .debug_line section"),
> >                      segment_selector_size);
> > -         return NULL;
> > +         return nullptr;
> >         }
> >      }
> >
> > -  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
> > +  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
> >    line_ptr += offset_size;
> >    lh->statement_program_start = line_ptr + header_length;
> > -  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
> > +
> > +  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> >
> >    if (lh->version >= 4)
> >      {
> > -      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
> > +      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
> >        line_ptr += 1;
> >      }
> >    else
> > @@ -341,41 +365,47 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >                    "in `.debug_line' section"));
> >      }
> >
> > -  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
> > +  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> > -  lh->line_base = read_1_signed_byte (abfd, line_ptr);
> > +
> > +  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> > -  lh->line_range = read_1_byte (abfd, line_ptr);
> > +
> > +  lh->line_range = read_1_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> > -  lh->opcode_base = read_1_byte (abfd, line_ptr);
> > +
> > +  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
> >    line_ptr += 1;
> > +
> >    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
> >
> >    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
> >    for (i = 1; i < lh->opcode_base; ++i)
> >      {
> > -      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
> > +      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
> >        line_ptr += 1;
> >      }
> >
> >    if (lh->version >= 5)
> >      {
> >        /* Read directory table.  */
> > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > -                             offset_size,
> > -                             [] (struct line_header *header, const char *name,
> > -                                 dir_index d_index, unsigned int mod_time,
> > -                                 unsigned int length)
> > +      read_formatted_entries
> > +       (parent_bfd, &line_ptr, line_str_data,
> > +        lh.get (), offset_size,
> > +        [] (struct line_header *header, const char *name,
> > +            dir_index d_index, unsigned int mod_time,
> > +            unsigned int length)
> >         {
> >           header->add_include_dir (name);
> >         });
> >
> >        /* Read file name table.  */
> > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > -                             offset_size,
> > -                             [] (struct line_header *header, const char *name,
> > -                                 dir_index d_index, unsigned int mod_time,
> > -                                 unsigned int length)
> > +      read_formatted_entries
> > +       (parent_bfd, &line_ptr, line_str_data,
> > +        lh.get (), offset_size,
> > +        [] (struct line_header *header, const char *name,
> > +            dir_index d_index, unsigned int mod_time,
> > +            unsigned int length)
> >         {
> >           header->add_file_name (name, d_index, mod_time, length);
> >         });
> > @@ -383,7 +413,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >    else
> >      {
> >        /* Read directory table.  */
> > -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > +      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> >         {
> >           line_ptr += bytes_read;
> >           lh->add_include_dir (cur_dir);
> > @@ -391,17 +421,17 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >        line_ptr += bytes_read;
> >
> >        /* Read file name table.  */
> > -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > +      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> >         {
> >           unsigned int mod_time, length;
> >           dir_index d_index;
> >
> >           line_ptr += bytes_read;
> > -         d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > +         d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> >           line_ptr += bytes_read;
> > -         mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > +         mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> >           line_ptr += bytes_read;
> > -         length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > +         length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> >           line_ptr += bytes_read;
> >
> >           lh->add_file_name (cur_file, d_index, mod_time, length);
> > @@ -409,9 +439,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> >        line_ptr += bytes_read;
> >      }
> >
> > -  if (line_ptr > (section->buffer + section->size))
> > +  if (line_ptr > (buf + line_data.size ()))
> >      complaint (_("line number info header doesn't "
> >                  "fit in `.debug_line' section"));
> >
> > +  *debug_line_ptr += unit_length + offset_size;
> >    return lh;
> >  }
> > +
> > +line_header_up
> > +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > +                          dwarf2_per_objfile *per_objfile,
> > +                          struct dwarf2_section_info *section,
> > +                          const struct comp_unit_head *cu_header,
> > +                          const char *comp_dir)
> > +{
> > +  struct objfile *objfile = per_objfile->objfile;
> > +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> > +
> > +  /* Read .debug_line.  */
> > +  dwarf2_section_info *line_sec = &per_bfd->line;
> > +  bfd_size_type line_size = line_sec->get_size (objfile);
> > +
> > +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> > +
> > +  /* Read .debug_line_str.  */
> > +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> > +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> > +
> > +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> > +                                           line_str_size);
> > +
> > +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> > +
> > +  return dwarf_decode_line_header
> > +    (per_bfd->obfd, line, line_str, &line_ptr,
> > +     is_dwz, cu_header, comp_dir);
> > +}
> > diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> > index 59a42e336f5..44e32828ddb 100644
> > --- a/gdb/dwarf2/line-header.h
> > +++ b/gdb/dwarf2/line-header.h
> > @@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
> >     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
> >     const char *comp_dir);
> >
> > +/* Like above but the .debug_line and .debug_line_str are stored in
> > +   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
> > +   statement program header within LINE_DATA.  */
> > +
> > +extern line_header_up dwarf_decode_line_header
> > +  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
> > +   gdb::array_view<const gdb_byte> line_str_data,
> > +   const gdb_byte **debug_line_ptr, bool is_dwz,
> > +  const comp_unit_head *cu_header, const char *comp_dir);
> > +
> >  #endif /* DWARF2_LINE_HEADER_H */
> > diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> > index 895fbede7c2..fe33f23fdfa 100644
> > --- a/gdb/dwarf2/read-gdb-index.c
> > +++ b/gdb/dwarf2/read-gdb-index.c
> > @@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
> >    }
> >  };
> >
> > +struct mapped_debug_line;
> > +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> > +
> >  struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >  {
> >    /* This dumps minimal information about the index.
> > @@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> >    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> >       debuginfo if necessary.  */
> >    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> > +
> > +  /* Filename information related to this .gdb_index.  */
> > +  mapped_debug_line_up mdl;
> > +
> > +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> > +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> > +  bool filename_in_debug_line
> > +  (objfile *objfile,
> > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> >  };
> >
> >  void
> > @@ -588,6 +600,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
> >    return result;
> >  }
> >
> > +bool
> > +dwarf2_gdb_index::filename_in_debug_line
> > +  (objfile *objfile,
> > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > +{
> > +  if (mdl == nullptr)
> > +    mdl.reset (new mapped_debug_line (objfile));
> > +
> > +  return mdl->contains_matching_filename (file_matcher);
> > +}
> > +
> >  bool
> >  dwarf2_gdb_index::expand_symtabs_matching
> >      (struct objfile *objfile,
> > @@ -616,6 +639,10 @@ dwarf2_gdb_index::expand_symtabs_matching
> >           return false;
> >         }
> >
> > +      if (file_matcher != nullptr
> > +         && !filename_in_debug_line (objfile, file_matcher))
> > +       return true;
> > +
> >        read_full_dwarf_from_debuginfod (objfile, this);
> >        return true;
> >      }
> > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> > index e561ec035e7..39ee56d7204 100644
> > --- a/gdb/dwarf2/read.c
> > +++ b/gdb/dwarf2/read.c
> > @@ -81,6 +81,7 @@
> >  #include "gdbsupport/gdb_optional.h"
> >  #include "gdbsupport/underlying.h"
> >  #include "gdbsupport/hash_enum.h"
> > +#include "gdbsupport/scoped_mmap.h"
> >  #include "filename-seen-cache.h"
> >  #include "producer.h"
> >  #include <fcntl.h>
> > @@ -2110,6 +2111,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
> >    return this_cu->file_names;
> >  }
> >
> > +#if !HAVE_SYS_MMAN_H
> > +
> > +bool
> > +mapped_debug_line::contains_matching_filename
> > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > +{
> > +  return false;
> > +}
> > +
> > +gdb::array_view<const gdb_byte>
> > +mapped_debug_line::read_debug_line_separate
> > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > +{
> > +  return {};
> > +}
> > +
> > +bool
> > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > +{
> > +  return false;
> > +}
> > +
> > +#else /* !HAVE_SYS_MMAN_H */
> > +
> > +struct line_resource_mmap final : public index_cache_resource
> > +{
> > +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> > +     file doesn't exist. */
> > +  line_resource_mmap (const char *filename)
> > +    : mapping (mmap_file (filename))
> > +  {}
> > +
> > +  scoped_mmap mapping;
> > +};
> > +
> > +/* See read.h.  */
> > +
> > +bool
> > +mapped_debug_line::contains_matching_filename
> > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > +{
> > +  for (line_header_up &lh : line_headers)
> > +    for (file_entry &fe : lh->file_names ())
> > +      {
> > +       const char *filename = fe.name;
> > +
> > +       if (file_matcher (fe.name, false))
> > +         return true;
> > +
> > +       bool basename_match = file_matcher (lbasename (fe.name), true);
> > +
> > +       if (!basenames_may_differ && !basename_match)
> > +         continue;
> > +
> > +       /* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> > +          until DWARF5.  Since we don't have access to the CU at this
> > +          point we just check for a partial match on the filename.
> > +          If there is a match, the full debuginfo will be downloaded
> > +          ane the match will be re-evalute with DW_AT_comp_dir.  */
> > +       if (lh->version < 5 && fe.d_index == 0)
> > +         return basename_match;
> > +
> > +       const char *dirname = fe.include_dir (&*lh);
> > +       std::string fullname;
> > +
> > +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> > +         fullname = filename;
> > +       else
> > +         fullname = std::string (dirname) + SLASH_STRING + filename;
> > +
> > +       gdb::unique_xmalloc_ptr<char> rewritten
> > +         = rewrite_source_path (fullname.c_str ());
> > +       if (rewritten != nullptr)
> > +         fullname = rewritten.release ();
> > +
> > +       if (file_matcher (fullname.c_str (), false))
> > +         return true;
> > +      }
> > +
> > +  return false;
> > +}
> > +
> > +/* See read.h.  */
> > +
> > +gdb::array_view<const gdb_byte>
> > +mapped_debug_line::read_debug_line_separate
> > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > +{
> > +  if (filename == nullptr)
> > +    return {};
> > +
> > +  try
> > +  {
> > +    line_resource_mmap *mmap_resource
> > +      = new line_resource_mmap (filename);
> > +
> > +    resource->reset (mmap_resource);
> > +
> > +    return gdb::array_view<const gdb_byte>
> > +      ((const gdb_byte *) mmap_resource->mapping.get (),
> > +       mmap_resource->mapping.size ());
> > +  }
> > +  catch (const gdb_exception &except)
> > +  {
> > +    exception_print (gdb_stderr, except);
> > +  }
> > +
> > +  return {};
> > +}
> > +
> > +/* See read.h.  */
> > +
> > +bool
> > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > +{
> > +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > +  if (build_id == nullptr)
> > +    return false;
> > +
> > +  gdb::unique_xmalloc_ptr<char> line_path;
> > +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> > +                                               build_id->size,
> > +                                               bfd_get_filename
> > +                                                 (objfile->obfd.get ()),
> > +                                               ".debug_line",
> > +                                               &line_path);
> > +
> > +  if (line_fd.get () < 0)
> > +    return false;
> > +
> > +  gdb::unique_xmalloc_ptr<char> line_str_path;
> > +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> > +                                                   build_id->size,
> > +                                                   bfd_get_filename
> > +                                                     (objfile->obfd.get ()),
> > +                                                   ".debug_line_str",
> > +                                                   &line_str_path);
> > +
> > +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> > +  line_str_data = read_debug_line_separate (line_str_path.get (),
> > +                                           &line_str_resource);
> > +
> > +  const gdb_byte *line_ptr = line_data.data ();
> > +
> > +  while (line_ptr < line_data.data () + line_data.size ())
> > +    {
> > +      line_header_up lh
> > +       = dwarf_decode_line_header (objfile->obfd.get (),
> > +                                   line_data, line_str_data,
> > +                                   &line_ptr, false,
> > +                                   nullptr, nullptr);
> > +      line_headers.emplace_back (lh.release ());
> > +    }
> > +
> > +  return true;
> > +}
> > +#endif /* !HAVE_SYS_MMAN_H */
> > +
> > +mapped_debug_line::mapped_debug_line (objfile *objfile)
> > +{
> > +  if (!read_debug_line_from_debuginfod (objfile))
> > +    line_headers.clear ();
> > +}
> > +
> >  /* A helper for the "quick" functions which computes and caches the
> >     real path for a given file name from the line table.  */
> >
> > diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> > index e3131693b81..b8a8b76bde0 100644
> > --- a/gdb/dwarf2/read.h
> > +++ b/gdb/dwarf2/read.h
> > @@ -34,6 +34,7 @@
> >  #include "gdbsupport/hash_enum.h"
> >  #include "gdbsupport/function-view.h"
> >  #include "gdbsupport/packed.h"
> > +#include "dwarf2/line-header.h"
> >
> >  /* Hold 'maintenance (set|show) dwarf' commands.  */
> >  extern struct cmd_list_element *set_dwarf_cmdlist;
> > @@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
> >  extern void read_full_dwarf_from_debuginfod (struct objfile *,
> >                                              dwarf2_base_index_functions *);
> >
> > +struct mapped_debug_line
> > +{
> > +  mapped_debug_line (objfile *objfile);
> > +
> > +  /* Return true if any of the mapped .debug_line's filenames match
> > +     FILE_MATCHER.  */
> > +
> > +  bool contains_matching_filename
> > +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> > +
> > +private:
> > +  std::vector<line_header_up> line_headers;
> > +
> > +  gdb::array_view<const gdb_byte> line_data;
> > +  gdb::array_view<const gdb_byte> line_str_data;
> > +
> > +  std::unique_ptr<index_cache_resource> line_resource;
> > +  std::unique_ptr<index_cache_resource> line_str_resource;
> > +
> > +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> > +     and populate line_headers.  */
> > +
> > +  bool read_debug_line_from_debuginfod (objfile *objfile);
> > +
> > +  /* Initialize line_data and line_str_data with the .debug_line and
> > +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> > +
> > +  gdb::array_view<const gdb_byte> read_debug_line_separate
> > +    (char *filename, std::unique_ptr<index_cache_resource> *resource);
> > +};
> >  #endif /* DWARF2READ_H */
> > --
> > 2.39.2
> >
  
Aaron Merey May 16, 2023, 2:50 p.m. UTC | #3
Ping

Thanks,
Aaron

On Tue, May 9, 2023 at 9:49 AM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Tue, May 2, 2023 at 10:25 AM Aaron Merey <amerey@redhat.com> wrote:
> >
> > Ping
> >
> > Thanks,
> > Aaron
> >
> > On Mon, Apr 17, 2023 at 2:07 PM Aaron Merey <amerey@redhat.com> wrote:
> > >
> > > v1 can be found here:
> > > https://sourceware.org/pipermail/gdb-patches/2023-February/197459.html
> > >
> > > v2 merges dwarf_decode_line_header_separate with
> > > dwarf_decode_line_header and read_formatted_entries_separate with
> > > read_formatted_entries in order to reduce code duplication.
> > >
> > > ---
> > >
> > > 'set debuginfod enabled lazy' 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 gdb can search the
> > > debuginfo for matching source filenames.  This can result in unnecessary
> > > downloading.
> > >
> > > 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 a deferred debuginfo.
> > > ---
> > >  gdb/dwarf2/line-header.c    | 215 +++++++++++++++++++++++-------------
> > >  gdb/dwarf2/line-header.h    |  10 ++
> > >  gdb/dwarf2/read-gdb-index.c |  27 +++++
> > >  gdb/dwarf2/read.c           | 165 +++++++++++++++++++++++++++
> > >  gdb/dwarf2/read.h           |  31 ++++++
> > >  5 files changed, 371 insertions(+), 77 deletions(-)
> > >
> > > diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> > > index 9d74c8fe75b..5eaff9c5a48 100644
> > > --- a/gdb/dwarf2/line-header.c
> > > +++ b/gdb/dwarf2/line-header.c
> > > @@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
> > >  {
> > >    LONGEST length = read_initial_length (abfd, buf, bytes_read);
> > >
> > > -  gdb_assert (cu_header->initial_length_size == 4
> > > -             || cu_header->initial_length_size == 8
> > > -             || cu_header->initial_length_size == 12);
> > > +  if (cu_header != nullptr)
> > > +    {
> > > +      gdb_assert (cu_header->initial_length_size == 4
> > > +                 || cu_header->initial_length_size == 8
> > > +                 || cu_header->initial_length_size == 12);
> > >
> > > -  if (cu_header->initial_length_size != *bytes_read)
> > > -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > > +      if (cu_header->initial_length_size != *bytes_read)
> > > +       complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > > +    }
> > >
> > >    *offset_size = (*bytes_read == 4) ? 4 : 8;
> > >    return length;
> > >  }
> > >
> > > -/* Read directory or file name entry format, starting with byte of
> > > -   format count entries, ULEB128 pairs of entry formats, ULEB128 of
> > > -   entries count and the entries themselves in the described entry
> > > -   format.  */
> > > +
> > > +/* Like read_formatted_entries but the .debug_line and .debug_line_str
> > > +   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
> > > +   where these sections are read from separate files without necessarily
> > > +   having access to the entire debuginfo file they originate from.  */
> > >
> > >  static void
> > > -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > > -                       const gdb_byte **bufp, struct line_header *lh,
> > > -                       unsigned int offset_size,
> > > -                       void (*callback) (struct line_header *lh,
> > > -                                         const char *name,
> > > -                                         dir_index d_index,
> > > -                                         unsigned int mod_time,
> > > -                                         unsigned int length))
> > > +read_formatted_entries
> > > +  (bfd *parent_bfd, const gdb_byte **line_bufp,
> > > +   const gdb::array_view<const gdb_byte> line_str_data,
> > > +   struct line_header *lh,
> > > +   unsigned int offset_size,
> > > +   void (*callback) (struct line_header *lh,
> > > +                    const char *name,
> > > +                    dir_index d_index,
> > > +                    unsigned int mod_time,
> > > +                    unsigned int length))
> > >  {
> > >    gdb_byte format_count, formati;
> > >    ULONGEST data_count, datai;
> > > -  const gdb_byte *buf = *bufp;
> > > +  const gdb_byte *buf = *line_bufp;
> > > +  const gdb_byte *str_buf = line_str_data.data ();
> > >    const gdb_byte *format_header_data;
> > >    unsigned int bytes_read;
> > >
> > > -  format_count = read_1_byte (abfd, buf);
> > > +  format_count = read_1_byte (parent_bfd, buf);
> > >    buf += 1;
> > >    format_header_data = buf;
> > >    for (formati = 0; formati < format_count; formati++)
> > >      {
> > > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> > >        buf += bytes_read;
> > > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> > >        buf += bytes_read;
> > >      }
> > >
> > > -  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
> > > +  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> > >    buf += bytes_read;
> > >    for (datai = 0; datai < data_count; datai++)
> > >      {
> > > @@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > >
> > >        for (formati = 0; formati < format_count; formati++)
> > >         {
> > > -         ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
> > > +         ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> > >           format += bytes_read;
> > >
> > > -         ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> > > +         ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> > >           format += bytes_read;
> > >
> > >           gdb::optional<const char *> string;
> > > @@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > >           switch (form)
> > >             {
> > >             case DW_FORM_string:
> > > -             string.emplace (read_direct_string (abfd, buf, &bytes_read));
> > > +             string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
> > >               buf += bytes_read;
> > >               break;
> > >
> > >             case DW_FORM_line_strp:
> > >               {
> > > -               const char *str
> > > -                 = per_objfile->read_line_string (buf, offset_size);
> > > +               if (line_str_data.empty ())
> > > +                 error (_("Dwarf Error: DW_FORM_line_strp used without " \
> > > +                          "required section"));
> > > +               if (line_str_data.size () <= offset_size)
> > > +                 error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> > > +                          "of section .debug_line"));
> > > +
> > > +               ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
> > > +
> > > +               const char *str;
> > > +               if (str_buf[str_offset] == '\0')
> > > +                 str = nullptr;
> > > +               else
> > > +                 str = (const char *) (str_buf + str_offset);
> > >                 string.emplace (str);
> > >                 buf += offset_size;
> > > +               break;
> > >               }
> > > -             break;
> > >
> > >             case DW_FORM_data1:
> > > -             uint.emplace (read_1_byte (abfd, buf));
> > > +             uint.emplace (read_1_byte (parent_bfd, buf));
> > >               buf += 1;
> > >               break;
> > >
> > >             case DW_FORM_data2:
> > > -             uint.emplace (read_2_bytes (abfd, buf));
> > > +             uint.emplace (read_2_bytes (parent_bfd, buf));
> > >               buf += 2;
> > >               break;
> > >
> > >             case DW_FORM_data4:
> > > -             uint.emplace (read_4_bytes (abfd, buf));
> > > +             uint.emplace (read_4_bytes (parent_bfd, buf));
> > >               buf += 4;
> > >               break;
> > >
> > >             case DW_FORM_data8:
> > > -             uint.emplace (read_8_bytes (abfd, buf));
> > > +             uint.emplace (read_8_bytes (parent_bfd, buf));
> > >               buf += 8;
> > >               break;
> > >
> > > @@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > >               break;
> > >
> > >             case DW_FORM_udata:
> > > -             uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
> > > +             uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
> > >               buf += bytes_read;
> > >               break;
> > >
> > > @@ -244,28 +263,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > >        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
> > >      }
> > >
> > > -  *bufp = buf;
> > > +  *line_bufp = buf;
> > >  }
> > >
> > >  /* See line-header.h.  */
> > >
> > >  line_header_up
> > > -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > -                          dwarf2_per_objfile *per_objfile,
> > > -                          struct dwarf2_section_info *section,
> > > -                          const struct comp_unit_head *cu_header,
> > > -                          const char *comp_dir)
> > > +dwarf_decode_line_header (bfd *parent_bfd,
> > > +                         gdb::array_view<const gdb_byte> line_data,
> > > +                         gdb::array_view<const gdb_byte> line_str_data,
> > > +                         const gdb_byte **debug_line_ptr,
> > > +                         bool is_dwz,
> > > +                         const struct comp_unit_head *cu_header,
> > > +                         const char *comp_dir)
> > >  {
> > > -  const gdb_byte *line_ptr;
> > > +  const gdb_byte *line_ptr, *buf;
> > >    unsigned int bytes_read, offset_size;
> > >    int i;
> > >    const char *cur_dir, *cur_file;
> > >
> > > -  bfd *abfd = section->get_bfd_owner ();
> > > +  buf = *debug_line_ptr;
> > >
> > >    /* Make sure that at least there's room for the total_length field.
> > >       That could be 12 bytes long, but we're just going to fudge that.  */
> > > -  if (to_underlying (sect_off) + 4 >= section->size)
> > > +  if (buf + 4 >= line_data.data () + line_data.size ())
> > >      {
> > >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> > >        return 0;
> > > @@ -273,62 +294,65 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >
> > >    line_header_up lh (new line_header (comp_dir));
> > >
> > > -  lh->sect_off = sect_off;
> > > +  lh->sect_off = (sect_offset) (buf - line_data.data ());
> > >    lh->offset_in_dwz = is_dwz;
> > >
> > > -  line_ptr = section->buffer + to_underlying (sect_off);
> > > +  line_ptr = buf;
> > >
> > >    /* Read in the header.  */
> > >    LONGEST unit_length
> > > -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> > > +    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
> > >                                               &bytes_read, &offset_size);
> > > -  line_ptr += bytes_read;
> > >
> > > -  const gdb_byte *start_here = line_ptr;
> > > +  line_ptr += bytes_read;
> > >
> > > -  if (line_ptr + unit_length > (section->buffer + section->size))
> > > +  if (line_ptr + unit_length > buf + line_data.size ())
> > >      {
> > >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> > >        return 0;
> > >      }
> > > +
> > > +  const gdb_byte *start_here = line_ptr;
> > > +
> > >    lh->statement_program_end = start_here + unit_length;
> > > -  lh->version = read_2_bytes (abfd, line_ptr);
> > > +  lh->version = read_2_bytes (parent_bfd, line_ptr);
> > >    line_ptr += 2;
> > >    if (lh->version > 5)
> > >      {
> > >        /* This is a version we don't understand.  The format could have
> > >          changed in ways we don't handle properly so just punt.  */
> > >        complaint (_("unsupported version in .debug_line section"));
> > > -      return NULL;
> > > +      return nullptr;
> > >      }
> > >    if (lh->version >= 5)
> > >      {
> > >        gdb_byte segment_selector_size;
> > >
> > >        /* Skip address size.  */
> > > -      read_1_byte (abfd, line_ptr);
> > > +      read_1_byte (parent_bfd, line_ptr);
> > >        line_ptr += 1;
> > >
> > > -      segment_selector_size = read_1_byte (abfd, line_ptr);
> > > +      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
> > >        line_ptr += 1;
> > >        if (segment_selector_size != 0)
> > >         {
> > >           complaint (_("unsupported segment selector size %u "
> > >                        "in .debug_line section"),
> > >                      segment_selector_size);
> > > -         return NULL;
> > > +         return nullptr;
> > >         }
> > >      }
> > >
> > > -  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
> > > +  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
> > >    line_ptr += offset_size;
> > >    lh->statement_program_start = line_ptr + header_length;
> > > -  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
> > > +
> > > +  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > >
> > >    if (lh->version >= 4)
> > >      {
> > > -      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
> > > +      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
> > >        line_ptr += 1;
> > >      }
> > >    else
> > > @@ -341,41 +365,47 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >                    "in `.debug_line' section"));
> > >      }
> > >
> > > -  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
> > > +  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > > -  lh->line_base = read_1_signed_byte (abfd, line_ptr);
> > > +
> > > +  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > > -  lh->line_range = read_1_byte (abfd, line_ptr);
> > > +
> > > +  lh->line_range = read_1_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > > -  lh->opcode_base = read_1_byte (abfd, line_ptr);
> > > +
> > > +  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
> > >    line_ptr += 1;
> > > +
> > >    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
> > >
> > >    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
> > >    for (i = 1; i < lh->opcode_base; ++i)
> > >      {
> > > -      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
> > > +      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
> > >        line_ptr += 1;
> > >      }
> > >
> > >    if (lh->version >= 5)
> > >      {
> > >        /* Read directory table.  */
> > > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > > -                             offset_size,
> > > -                             [] (struct line_header *header, const char *name,
> > > -                                 dir_index d_index, unsigned int mod_time,
> > > -                                 unsigned int length)
> > > +      read_formatted_entries
> > > +       (parent_bfd, &line_ptr, line_str_data,
> > > +        lh.get (), offset_size,
> > > +        [] (struct line_header *header, const char *name,
> > > +            dir_index d_index, unsigned int mod_time,
> > > +            unsigned int length)
> > >         {
> > >           header->add_include_dir (name);
> > >         });
> > >
> > >        /* Read file name table.  */
> > > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > > -                             offset_size,
> > > -                             [] (struct line_header *header, const char *name,
> > > -                                 dir_index d_index, unsigned int mod_time,
> > > -                                 unsigned int length)
> > > +      read_formatted_entries
> > > +       (parent_bfd, &line_ptr, line_str_data,
> > > +        lh.get (), offset_size,
> > > +        [] (struct line_header *header, const char *name,
> > > +            dir_index d_index, unsigned int mod_time,
> > > +            unsigned int length)
> > >         {
> > >           header->add_file_name (name, d_index, mod_time, length);
> > >         });
> > > @@ -383,7 +413,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >    else
> > >      {
> > >        /* Read directory table.  */
> > > -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > > +      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> > >         {
> > >           line_ptr += bytes_read;
> > >           lh->add_include_dir (cur_dir);
> > > @@ -391,17 +421,17 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >        line_ptr += bytes_read;
> > >
> > >        /* Read file name table.  */
> > > -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > > +      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> > >         {
> > >           unsigned int mod_time, length;
> > >           dir_index d_index;
> > >
> > >           line_ptr += bytes_read;
> > > -         d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > > +         d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> > >           line_ptr += bytes_read;
> > > -         mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > > +         mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> > >           line_ptr += bytes_read;
> > > -         length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > > +         length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> > >           line_ptr += bytes_read;
> > >
> > >           lh->add_file_name (cur_file, d_index, mod_time, length);
> > > @@ -409,9 +439,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > >        line_ptr += bytes_read;
> > >      }
> > >
> > > -  if (line_ptr > (section->buffer + section->size))
> > > +  if (line_ptr > (buf + line_data.size ()))
> > >      complaint (_("line number info header doesn't "
> > >                  "fit in `.debug_line' section"));
> > >
> > > +  *debug_line_ptr += unit_length + offset_size;
> > >    return lh;
> > >  }
> > > +
> > > +line_header_up
> > > +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > +                          dwarf2_per_objfile *per_objfile,
> > > +                          struct dwarf2_section_info *section,
> > > +                          const struct comp_unit_head *cu_header,
> > > +                          const char *comp_dir)
> > > +{
> > > +  struct objfile *objfile = per_objfile->objfile;
> > > +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> > > +
> > > +  /* Read .debug_line.  */
> > > +  dwarf2_section_info *line_sec = &per_bfd->line;
> > > +  bfd_size_type line_size = line_sec->get_size (objfile);
> > > +
> > > +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> > > +
> > > +  /* Read .debug_line_str.  */
> > > +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> > > +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> > > +
> > > +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> > > +                                           line_str_size);
> > > +
> > > +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> > > +
> > > +  return dwarf_decode_line_header
> > > +    (per_bfd->obfd, line, line_str, &line_ptr,
> > > +     is_dwz, cu_header, comp_dir);
> > > +}
> > > diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> > > index 59a42e336f5..44e32828ddb 100644
> > > --- a/gdb/dwarf2/line-header.h
> > > +++ b/gdb/dwarf2/line-header.h
> > > @@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
> > >     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
> > >     const char *comp_dir);
> > >
> > > +/* Like above but the .debug_line and .debug_line_str are stored in
> > > +   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
> > > +   statement program header within LINE_DATA.  */
> > > +
> > > +extern line_header_up dwarf_decode_line_header
> > > +  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
> > > +   gdb::array_view<const gdb_byte> line_str_data,
> > > +   const gdb_byte **debug_line_ptr, bool is_dwz,
> > > +  const comp_unit_head *cu_header, const char *comp_dir);
> > > +
> > >  #endif /* DWARF2_LINE_HEADER_H */
> > > diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> > > index 895fbede7c2..fe33f23fdfa 100644
> > > --- a/gdb/dwarf2/read-gdb-index.c
> > > +++ b/gdb/dwarf2/read-gdb-index.c
> > > @@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
> > >    }
> > >  };
> > >
> > > +struct mapped_debug_line;
> > > +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> > > +
> > >  struct dwarf2_gdb_index : public dwarf2_base_index_functions
> > >  {
> > >    /* This dumps minimal information about the index.
> > > @@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> > >    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> > >       debuginfo if necessary.  */
> > >    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> > > +
> > > +  /* Filename information related to this .gdb_index.  */
> > > +  mapped_debug_line_up mdl;
> > > +
> > > +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> > > +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> > > +  bool filename_in_debug_line
> > > +  (objfile *objfile,
> > > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> > >  };
> > >
> > >  void
> > > @@ -588,6 +600,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
> > >    return result;
> > >  }
> > >
> > > +bool
> > > +dwarf2_gdb_index::filename_in_debug_line
> > > +  (objfile *objfile,
> > > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > > +{
> > > +  if (mdl == nullptr)
> > > +    mdl.reset (new mapped_debug_line (objfile));
> > > +
> > > +  return mdl->contains_matching_filename (file_matcher);
> > > +}
> > > +
> > >  bool
> > >  dwarf2_gdb_index::expand_symtabs_matching
> > >      (struct objfile *objfile,
> > > @@ -616,6 +639,10 @@ dwarf2_gdb_index::expand_symtabs_matching
> > >           return false;
> > >         }
> > >
> > > +      if (file_matcher != nullptr
> > > +         && !filename_in_debug_line (objfile, file_matcher))
> > > +       return true;
> > > +
> > >        read_full_dwarf_from_debuginfod (objfile, this);
> > >        return true;
> > >      }
> > > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> > > index e561ec035e7..39ee56d7204 100644
> > > --- a/gdb/dwarf2/read.c
> > > +++ b/gdb/dwarf2/read.c
> > > @@ -81,6 +81,7 @@
> > >  #include "gdbsupport/gdb_optional.h"
> > >  #include "gdbsupport/underlying.h"
> > >  #include "gdbsupport/hash_enum.h"
> > > +#include "gdbsupport/scoped_mmap.h"
> > >  #include "filename-seen-cache.h"
> > >  #include "producer.h"
> > >  #include <fcntl.h>
> > > @@ -2110,6 +2111,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
> > >    return this_cu->file_names;
> > >  }
> > >
> > > +#if !HAVE_SYS_MMAN_H
> > > +
> > > +bool
> > > +mapped_debug_line::contains_matching_filename
> > > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > > +{
> > > +  return false;
> > > +}
> > > +
> > > +gdb::array_view<const gdb_byte>
> > > +mapped_debug_line::read_debug_line_separate
> > > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > > +{
> > > +  return {};
> > > +}
> > > +
> > > +bool
> > > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > > +{
> > > +  return false;
> > > +}
> > > +
> > > +#else /* !HAVE_SYS_MMAN_H */
> > > +
> > > +struct line_resource_mmap final : public index_cache_resource
> > > +{
> > > +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> > > +     file doesn't exist. */
> > > +  line_resource_mmap (const char *filename)
> > > +    : mapping (mmap_file (filename))
> > > +  {}
> > > +
> > > +  scoped_mmap mapping;
> > > +};
> > > +
> > > +/* See read.h.  */
> > > +
> > > +bool
> > > +mapped_debug_line::contains_matching_filename
> > > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > > +{
> > > +  for (line_header_up &lh : line_headers)
> > > +    for (file_entry &fe : lh->file_names ())
> > > +      {
> > > +       const char *filename = fe.name;
> > > +
> > > +       if (file_matcher (fe.name, false))
> > > +         return true;
> > > +
> > > +       bool basename_match = file_matcher (lbasename (fe.name), true);
> > > +
> > > +       if (!basenames_may_differ && !basename_match)
> > > +         continue;
> > > +
> > > +       /* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> > > +          until DWARF5.  Since we don't have access to the CU at this
> > > +          point we just check for a partial match on the filename.
> > > +          If there is a match, the full debuginfo will be downloaded
> > > +          ane the match will be re-evalute with DW_AT_comp_dir.  */
> > > +       if (lh->version < 5 && fe.d_index == 0)
> > > +         return basename_match;
> > > +
> > > +       const char *dirname = fe.include_dir (&*lh);
> > > +       std::string fullname;
> > > +
> > > +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> > > +         fullname = filename;
> > > +       else
> > > +         fullname = std::string (dirname) + SLASH_STRING + filename;
> > > +
> > > +       gdb::unique_xmalloc_ptr<char> rewritten
> > > +         = rewrite_source_path (fullname.c_str ());
> > > +       if (rewritten != nullptr)
> > > +         fullname = rewritten.release ();
> > > +
> > > +       if (file_matcher (fullname.c_str (), false))
> > > +         return true;
> > > +      }
> > > +
> > > +  return false;
> > > +}
> > > +
> > > +/* See read.h.  */
> > > +
> > > +gdb::array_view<const gdb_byte>
> > > +mapped_debug_line::read_debug_line_separate
> > > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > > +{
> > > +  if (filename == nullptr)
> > > +    return {};
> > > +
> > > +  try
> > > +  {
> > > +    line_resource_mmap *mmap_resource
> > > +      = new line_resource_mmap (filename);
> > > +
> > > +    resource->reset (mmap_resource);
> > > +
> > > +    return gdb::array_view<const gdb_byte>
> > > +      ((const gdb_byte *) mmap_resource->mapping.get (),
> > > +       mmap_resource->mapping.size ());
> > > +  }
> > > +  catch (const gdb_exception &except)
> > > +  {
> > > +    exception_print (gdb_stderr, except);
> > > +  }
> > > +
> > > +  return {};
> > > +}
> > > +
> > > +/* See read.h.  */
> > > +
> > > +bool
> > > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > > +{
> > > +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > > +  if (build_id == nullptr)
> > > +    return false;
> > > +
> > > +  gdb::unique_xmalloc_ptr<char> line_path;
> > > +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> > > +                                               build_id->size,
> > > +                                               bfd_get_filename
> > > +                                                 (objfile->obfd.get ()),
> > > +                                               ".debug_line",
> > > +                                               &line_path);
> > > +
> > > +  if (line_fd.get () < 0)
> > > +    return false;
> > > +
> > > +  gdb::unique_xmalloc_ptr<char> line_str_path;
> > > +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> > > +                                                   build_id->size,
> > > +                                                   bfd_get_filename
> > > +                                                     (objfile->obfd.get ()),
> > > +                                                   ".debug_line_str",
> > > +                                                   &line_str_path);
> > > +
> > > +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> > > +  line_str_data = read_debug_line_separate (line_str_path.get (),
> > > +                                           &line_str_resource);
> > > +
> > > +  const gdb_byte *line_ptr = line_data.data ();
> > > +
> > > +  while (line_ptr < line_data.data () + line_data.size ())
> > > +    {
> > > +      line_header_up lh
> > > +       = dwarf_decode_line_header (objfile->obfd.get (),
> > > +                                   line_data, line_str_data,
> > > +                                   &line_ptr, false,
> > > +                                   nullptr, nullptr);
> > > +      line_headers.emplace_back (lh.release ());
> > > +    }
> > > +
> > > +  return true;
> > > +}
> > > +#endif /* !HAVE_SYS_MMAN_H */
> > > +
> > > +mapped_debug_line::mapped_debug_line (objfile *objfile)
> > > +{
> > > +  if (!read_debug_line_from_debuginfod (objfile))
> > > +    line_headers.clear ();
> > > +}
> > > +
> > >  /* A helper for the "quick" functions which computes and caches the
> > >     real path for a given file name from the line table.  */
> > >
> > > diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> > > index e3131693b81..b8a8b76bde0 100644
> > > --- a/gdb/dwarf2/read.h
> > > +++ b/gdb/dwarf2/read.h
> > > @@ -34,6 +34,7 @@
> > >  #include "gdbsupport/hash_enum.h"
> > >  #include "gdbsupport/function-view.h"
> > >  #include "gdbsupport/packed.h"
> > > +#include "dwarf2/line-header.h"
> > >
> > >  /* Hold 'maintenance (set|show) dwarf' commands.  */
> > >  extern struct cmd_list_element *set_dwarf_cmdlist;
> > > @@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
> > >  extern void read_full_dwarf_from_debuginfod (struct objfile *,
> > >                                              dwarf2_base_index_functions *);
> > >
> > > +struct mapped_debug_line
> > > +{
> > > +  mapped_debug_line (objfile *objfile);
> > > +
> > > +  /* Return true if any of the mapped .debug_line's filenames match
> > > +     FILE_MATCHER.  */
> > > +
> > > +  bool contains_matching_filename
> > > +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> > > +
> > > +private:
> > > +  std::vector<line_header_up> line_headers;
> > > +
> > > +  gdb::array_view<const gdb_byte> line_data;
> > > +  gdb::array_view<const gdb_byte> line_str_data;
> > > +
> > > +  std::unique_ptr<index_cache_resource> line_resource;
> > > +  std::unique_ptr<index_cache_resource> line_str_resource;
> > > +
> > > +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> > > +     and populate line_headers.  */
> > > +
> > > +  bool read_debug_line_from_debuginfod (objfile *objfile);
> > > +
> > > +  /* Initialize line_data and line_str_data with the .debug_line and
> > > +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> > > +
> > > +  gdb::array_view<const gdb_byte> read_debug_line_separate
> > > +    (char *filename, std::unique_ptr<index_cache_resource> *resource);
> > > +};
> > >  #endif /* DWARF2READ_H */
> > > --
> > > 2.39.2
> > >
  
Aaron Merey May 23, 2023, 8:55 p.m. UTC | #4
Ping

Thanks,
Aaron

On Tue, May 16, 2023 at 10:50 AM Aaron Merey <amerey@redhat.com> wrote:
>
> Ping
>
> Thanks,
> Aaron
>
> On Tue, May 9, 2023 at 9:49 AM Aaron Merey <amerey@redhat.com> wrote:
> >
> > Ping
> >
> > Thanks,
> > Aaron
> >
> > On Tue, May 2, 2023 at 10:25 AM Aaron Merey <amerey@redhat.com> wrote:
> > >
> > > Ping
> > >
> > > Thanks,
> > > Aaron
> > >
> > > On Mon, Apr 17, 2023 at 2:07 PM Aaron Merey <amerey@redhat.com> wrote:
> > > >
> > > > v1 can be found here:
> > > > https://sourceware.org/pipermail/gdb-patches/2023-February/197459.html
> > > >
> > > > v2 merges dwarf_decode_line_header_separate with
> > > > dwarf_decode_line_header and read_formatted_entries_separate with
> > > > read_formatted_entries in order to reduce code duplication.
> > > >
> > > > ---
> > > >
> > > > 'set debuginfod enabled lazy' 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 gdb can search the
> > > > debuginfo for matching source filenames.  This can result in unnecessary
> > > > downloading.
> > > >
> > > > 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 a deferred debuginfo.
> > > > ---
> > > >  gdb/dwarf2/line-header.c    | 215 +++++++++++++++++++++++-------------
> > > >  gdb/dwarf2/line-header.h    |  10 ++
> > > >  gdb/dwarf2/read-gdb-index.c |  27 +++++
> > > >  gdb/dwarf2/read.c           | 165 +++++++++++++++++++++++++++
> > > >  gdb/dwarf2/read.h           |  31 ++++++
> > > >  5 files changed, 371 insertions(+), 77 deletions(-)
> > > >
> > > > diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> > > > index 9d74c8fe75b..5eaff9c5a48 100644
> > > > --- a/gdb/dwarf2/line-header.c
> > > > +++ b/gdb/dwarf2/line-header.c
> > > > @@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
> > > >  {
> > > >    LONGEST length = read_initial_length (abfd, buf, bytes_read);
> > > >
> > > > -  gdb_assert (cu_header->initial_length_size == 4
> > > > -             || cu_header->initial_length_size == 8
> > > > -             || cu_header->initial_length_size == 12);
> > > > +  if (cu_header != nullptr)
> > > > +    {
> > > > +      gdb_assert (cu_header->initial_length_size == 4
> > > > +                 || cu_header->initial_length_size == 8
> > > > +                 || cu_header->initial_length_size == 12);
> > > >
> > > > -  if (cu_header->initial_length_size != *bytes_read)
> > > > -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > > > +      if (cu_header->initial_length_size != *bytes_read)
> > > > +       complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> > > > +    }
> > > >
> > > >    *offset_size = (*bytes_read == 4) ? 4 : 8;
> > > >    return length;
> > > >  }
> > > >
> > > > -/* Read directory or file name entry format, starting with byte of
> > > > -   format count entries, ULEB128 pairs of entry formats, ULEB128 of
> > > > -   entries count and the entries themselves in the described entry
> > > > -   format.  */
> > > > +
> > > > +/* Like read_formatted_entries but the .debug_line and .debug_line_str
> > > > +   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
> > > > +   where these sections are read from separate files without necessarily
> > > > +   having access to the entire debuginfo file they originate from.  */
> > > >
> > > >  static void
> > > > -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > > > -                       const gdb_byte **bufp, struct line_header *lh,
> > > > -                       unsigned int offset_size,
> > > > -                       void (*callback) (struct line_header *lh,
> > > > -                                         const char *name,
> > > > -                                         dir_index d_index,
> > > > -                                         unsigned int mod_time,
> > > > -                                         unsigned int length))
> > > > +read_formatted_entries
> > > > +  (bfd *parent_bfd, const gdb_byte **line_bufp,
> > > > +   const gdb::array_view<const gdb_byte> line_str_data,
> > > > +   struct line_header *lh,
> > > > +   unsigned int offset_size,
> > > > +   void (*callback) (struct line_header *lh,
> > > > +                    const char *name,
> > > > +                    dir_index d_index,
> > > > +                    unsigned int mod_time,
> > > > +                    unsigned int length))
> > > >  {
> > > >    gdb_byte format_count, formati;
> > > >    ULONGEST data_count, datai;
> > > > -  const gdb_byte *buf = *bufp;
> > > > +  const gdb_byte *buf = *line_bufp;
> > > > +  const gdb_byte *str_buf = line_str_data.data ();
> > > >    const gdb_byte *format_header_data;
> > > >    unsigned int bytes_read;
> > > >
> > > > -  format_count = read_1_byte (abfd, buf);
> > > > +  format_count = read_1_byte (parent_bfd, buf);
> > > >    buf += 1;
> > > >    format_header_data = buf;
> > > >    for (formati = 0; formati < format_count; formati++)
> > > >      {
> > > > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > > > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> > > >        buf += bytes_read;
> > > > -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> > > > +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> > > >        buf += bytes_read;
> > > >      }
> > > >
> > > > -  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
> > > > +  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
> > > >    buf += bytes_read;
> > > >    for (datai = 0; datai < data_count; datai++)
> > > >      {
> > > > @@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > > >
> > > >        for (formati = 0; formati < format_count; formati++)
> > > >         {
> > > > -         ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
> > > > +         ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> > > >           format += bytes_read;
> > > >
> > > > -         ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> > > > +         ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
> > > >           format += bytes_read;
> > > >
> > > >           gdb::optional<const char *> string;
> > > > @@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > > >           switch (form)
> > > >             {
> > > >             case DW_FORM_string:
> > > > -             string.emplace (read_direct_string (abfd, buf, &bytes_read));
> > > > +             string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
> > > >               buf += bytes_read;
> > > >               break;
> > > >
> > > >             case DW_FORM_line_strp:
> > > >               {
> > > > -               const char *str
> > > > -                 = per_objfile->read_line_string (buf, offset_size);
> > > > +               if (line_str_data.empty ())
> > > > +                 error (_("Dwarf Error: DW_FORM_line_strp used without " \
> > > > +                          "required section"));
> > > > +               if (line_str_data.size () <= offset_size)
> > > > +                 error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> > > > +                          "of section .debug_line"));
> > > > +
> > > > +               ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
> > > > +
> > > > +               const char *str;
> > > > +               if (str_buf[str_offset] == '\0')
> > > > +                 str = nullptr;
> > > > +               else
> > > > +                 str = (const char *) (str_buf + str_offset);
> > > >                 string.emplace (str);
> > > >                 buf += offset_size;
> > > > +               break;
> > > >               }
> > > > -             break;
> > > >
> > > >             case DW_FORM_data1:
> > > > -             uint.emplace (read_1_byte (abfd, buf));
> > > > +             uint.emplace (read_1_byte (parent_bfd, buf));
> > > >               buf += 1;
> > > >               break;
> > > >
> > > >             case DW_FORM_data2:
> > > > -             uint.emplace (read_2_bytes (abfd, buf));
> > > > +             uint.emplace (read_2_bytes (parent_bfd, buf));
> > > >               buf += 2;
> > > >               break;
> > > >
> > > >             case DW_FORM_data4:
> > > > -             uint.emplace (read_4_bytes (abfd, buf));
> > > > +             uint.emplace (read_4_bytes (parent_bfd, buf));
> > > >               buf += 4;
> > > >               break;
> > > >
> > > >             case DW_FORM_data8:
> > > > -             uint.emplace (read_8_bytes (abfd, buf));
> > > > +             uint.emplace (read_8_bytes (parent_bfd, buf));
> > > >               buf += 8;
> > > >               break;
> > > >
> > > > @@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > > >               break;
> > > >
> > > >             case DW_FORM_udata:
> > > > -             uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
> > > > +             uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
> > > >               buf += bytes_read;
> > > >               break;
> > > >
> > > > @@ -244,28 +263,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> > > >        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
> > > >      }
> > > >
> > > > -  *bufp = buf;
> > > > +  *line_bufp = buf;
> > > >  }
> > > >
> > > >  /* See line-header.h.  */
> > > >
> > > >  line_header_up
> > > > -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > > -                          dwarf2_per_objfile *per_objfile,
> > > > -                          struct dwarf2_section_info *section,
> > > > -                          const struct comp_unit_head *cu_header,
> > > > -                          const char *comp_dir)
> > > > +dwarf_decode_line_header (bfd *parent_bfd,
> > > > +                         gdb::array_view<const gdb_byte> line_data,
> > > > +                         gdb::array_view<const gdb_byte> line_str_data,
> > > > +                         const gdb_byte **debug_line_ptr,
> > > > +                         bool is_dwz,
> > > > +                         const struct comp_unit_head *cu_header,
> > > > +                         const char *comp_dir)
> > > >  {
> > > > -  const gdb_byte *line_ptr;
> > > > +  const gdb_byte *line_ptr, *buf;
> > > >    unsigned int bytes_read, offset_size;
> > > >    int i;
> > > >    const char *cur_dir, *cur_file;
> > > >
> > > > -  bfd *abfd = section->get_bfd_owner ();
> > > > +  buf = *debug_line_ptr;
> > > >
> > > >    /* Make sure that at least there's room for the total_length field.
> > > >       That could be 12 bytes long, but we're just going to fudge that.  */
> > > > -  if (to_underlying (sect_off) + 4 >= section->size)
> > > > +  if (buf + 4 >= line_data.data () + line_data.size ())
> > > >      {
> > > >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> > > >        return 0;
> > > > @@ -273,62 +294,65 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > >
> > > >    line_header_up lh (new line_header (comp_dir));
> > > >
> > > > -  lh->sect_off = sect_off;
> > > > +  lh->sect_off = (sect_offset) (buf - line_data.data ());
> > > >    lh->offset_in_dwz = is_dwz;
> > > >
> > > > -  line_ptr = section->buffer + to_underlying (sect_off);
> > > > +  line_ptr = buf;
> > > >
> > > >    /* Read in the header.  */
> > > >    LONGEST unit_length
> > > > -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> > > > +    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
> > > >                                               &bytes_read, &offset_size);
> > > > -  line_ptr += bytes_read;
> > > >
> > > > -  const gdb_byte *start_here = line_ptr;
> > > > +  line_ptr += bytes_read;
> > > >
> > > > -  if (line_ptr + unit_length > (section->buffer + section->size))
> > > > +  if (line_ptr + unit_length > buf + line_data.size ())
> > > >      {
> > > >        dwarf2_statement_list_fits_in_line_number_section_complaint ();
> > > >        return 0;
> > > >      }
> > > > +
> > > > +  const gdb_byte *start_here = line_ptr;
> > > > +
> > > >    lh->statement_program_end = start_here + unit_length;
> > > > -  lh->version = read_2_bytes (abfd, line_ptr);
> > > > +  lh->version = read_2_bytes (parent_bfd, line_ptr);
> > > >    line_ptr += 2;
> > > >    if (lh->version > 5)
> > > >      {
> > > >        /* This is a version we don't understand.  The format could have
> > > >          changed in ways we don't handle properly so just punt.  */
> > > >        complaint (_("unsupported version in .debug_line section"));
> > > > -      return NULL;
> > > > +      return nullptr;
> > > >      }
> > > >    if (lh->version >= 5)
> > > >      {
> > > >        gdb_byte segment_selector_size;
> > > >
> > > >        /* Skip address size.  */
> > > > -      read_1_byte (abfd, line_ptr);
> > > > +      read_1_byte (parent_bfd, line_ptr);
> > > >        line_ptr += 1;
> > > >
> > > > -      segment_selector_size = read_1_byte (abfd, line_ptr);
> > > > +      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
> > > >        line_ptr += 1;
> > > >        if (segment_selector_size != 0)
> > > >         {
> > > >           complaint (_("unsupported segment selector size %u "
> > > >                        "in .debug_line section"),
> > > >                      segment_selector_size);
> > > > -         return NULL;
> > > > +         return nullptr;
> > > >         }
> > > >      }
> > > >
> > > > -  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
> > > > +  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
> > > >    line_ptr += offset_size;
> > > >    lh->statement_program_start = line_ptr + header_length;
> > > > -  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
> > > > +
> > > > +  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
> > > >    line_ptr += 1;
> > > >
> > > >    if (lh->version >= 4)
> > > >      {
> > > > -      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
> > > > +      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
> > > >        line_ptr += 1;
> > > >      }
> > > >    else
> > > > @@ -341,41 +365,47 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > >                    "in `.debug_line' section"));
> > > >      }
> > > >
> > > > -  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
> > > > +  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
> > > >    line_ptr += 1;
> > > > -  lh->line_base = read_1_signed_byte (abfd, line_ptr);
> > > > +
> > > > +  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
> > > >    line_ptr += 1;
> > > > -  lh->line_range = read_1_byte (abfd, line_ptr);
> > > > +
> > > > +  lh->line_range = read_1_byte (parent_bfd, line_ptr);
> > > >    line_ptr += 1;
> > > > -  lh->opcode_base = read_1_byte (abfd, line_ptr);
> > > > +
> > > > +  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
> > > >    line_ptr += 1;
> > > > +
> > > >    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
> > > >
> > > >    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
> > > >    for (i = 1; i < lh->opcode_base; ++i)
> > > >      {
> > > > -      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
> > > > +      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
> > > >        line_ptr += 1;
> > > >      }
> > > >
> > > >    if (lh->version >= 5)
> > > >      {
> > > >        /* Read directory table.  */
> > > > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > > > -                             offset_size,
> > > > -                             [] (struct line_header *header, const char *name,
> > > > -                                 dir_index d_index, unsigned int mod_time,
> > > > -                                 unsigned int length)
> > > > +      read_formatted_entries
> > > > +       (parent_bfd, &line_ptr, line_str_data,
> > > > +        lh.get (), offset_size,
> > > > +        [] (struct line_header *header, const char *name,
> > > > +            dir_index d_index, unsigned int mod_time,
> > > > +            unsigned int length)
> > > >         {
> > > >           header->add_include_dir (name);
> > > >         });
> > > >
> > > >        /* Read file name table.  */
> > > > -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> > > > -                             offset_size,
> > > > -                             [] (struct line_header *header, const char *name,
> > > > -                                 dir_index d_index, unsigned int mod_time,
> > > > -                                 unsigned int length)
> > > > +      read_formatted_entries
> > > > +       (parent_bfd, &line_ptr, line_str_data,
> > > > +        lh.get (), offset_size,
> > > > +        [] (struct line_header *header, const char *name,
> > > > +            dir_index d_index, unsigned int mod_time,
> > > > +            unsigned int length)
> > > >         {
> > > >           header->add_file_name (name, d_index, mod_time, length);
> > > >         });
> > > > @@ -383,7 +413,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > >    else
> > > >      {
> > > >        /* Read directory table.  */
> > > > -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > > > +      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> > > >         {
> > > >           line_ptr += bytes_read;
> > > >           lh->add_include_dir (cur_dir);
> > > > @@ -391,17 +421,17 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > >        line_ptr += bytes_read;
> > > >
> > > >        /* Read file name table.  */
> > > > -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> > > > +      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
> > > >         {
> > > >           unsigned int mod_time, length;
> > > >           dir_index d_index;
> > > >
> > > >           line_ptr += bytes_read;
> > > > -         d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > > > +         d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> > > >           line_ptr += bytes_read;
> > > > -         mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > > > +         mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> > > >           line_ptr += bytes_read;
> > > > -         length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> > > > +         length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
> > > >           line_ptr += bytes_read;
> > > >
> > > >           lh->add_file_name (cur_file, d_index, mod_time, length);
> > > > @@ -409,9 +439,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > >        line_ptr += bytes_read;
> > > >      }
> > > >
> > > > -  if (line_ptr > (section->buffer + section->size))
> > > > +  if (line_ptr > (buf + line_data.size ()))
> > > >      complaint (_("line number info header doesn't "
> > > >                  "fit in `.debug_line' section"));
> > > >
> > > > +  *debug_line_ptr += unit_length + offset_size;
> > > >    return lh;
> > > >  }
> > > > +
> > > > +line_header_up
> > > > +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> > > > +                          dwarf2_per_objfile *per_objfile,
> > > > +                          struct dwarf2_section_info *section,
> > > > +                          const struct comp_unit_head *cu_header,
> > > > +                          const char *comp_dir)
> > > > +{
> > > > +  struct objfile *objfile = per_objfile->objfile;
> > > > +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> > > > +
> > > > +  /* Read .debug_line.  */
> > > > +  dwarf2_section_info *line_sec = &per_bfd->line;
> > > > +  bfd_size_type line_size = line_sec->get_size (objfile);
> > > > +
> > > > +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> > > > +
> > > > +  /* Read .debug_line_str.  */
> > > > +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> > > > +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> > > > +
> > > > +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> > > > +                                           line_str_size);
> > > > +
> > > > +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> > > > +
> > > > +  return dwarf_decode_line_header
> > > > +    (per_bfd->obfd, line, line_str, &line_ptr,
> > > > +     is_dwz, cu_header, comp_dir);
> > > > +}
> > > > diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> > > > index 59a42e336f5..44e32828ddb 100644
> > > > --- a/gdb/dwarf2/line-header.h
> > > > +++ b/gdb/dwarf2/line-header.h
> > > > @@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
> > > >     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
> > > >     const char *comp_dir);
> > > >
> > > > +/* Like above but the .debug_line and .debug_line_str are stored in
> > > > +   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
> > > > +   statement program header within LINE_DATA.  */
> > > > +
> > > > +extern line_header_up dwarf_decode_line_header
> > > > +  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
> > > > +   gdb::array_view<const gdb_byte> line_str_data,
> > > > +   const gdb_byte **debug_line_ptr, bool is_dwz,
> > > > +  const comp_unit_head *cu_header, const char *comp_dir);
> > > > +
> > > >  #endif /* DWARF2_LINE_HEADER_H */
> > > > diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> > > > index 895fbede7c2..fe33f23fdfa 100644
> > > > --- a/gdb/dwarf2/read-gdb-index.c
> > > > +++ b/gdb/dwarf2/read-gdb-index.c
> > > > @@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
> > > >    }
> > > >  };
> > > >
> > > > +struct mapped_debug_line;
> > > > +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> > > > +
> > > >  struct dwarf2_gdb_index : public dwarf2_base_index_functions
> > > >  {
> > > >    /* This dumps minimal information about the index.
> > > > @@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
> > > >    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
> > > >       debuginfo if necessary.  */
> > > >    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> > > > +
> > > > +  /* Filename information related to this .gdb_index.  */
> > > > +  mapped_debug_line_up mdl;
> > > > +
> > > > +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> > > > +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> > > > +  bool filename_in_debug_line
> > > > +  (objfile *objfile,
> > > > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> > > >  };
> > > >
> > > >  void
> > > > @@ -588,6 +600,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
> > > >    return result;
> > > >  }
> > > >
> > > > +bool
> > > > +dwarf2_gdb_index::filename_in_debug_line
> > > > +  (objfile *objfile,
> > > > +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > > > +{
> > > > +  if (mdl == nullptr)
> > > > +    mdl.reset (new mapped_debug_line (objfile));
> > > > +
> > > > +  return mdl->contains_matching_filename (file_matcher);
> > > > +}
> > > > +
> > > >  bool
> > > >  dwarf2_gdb_index::expand_symtabs_matching
> > > >      (struct objfile *objfile,
> > > > @@ -616,6 +639,10 @@ dwarf2_gdb_index::expand_symtabs_matching
> > > >           return false;
> > > >         }
> > > >
> > > > +      if (file_matcher != nullptr
> > > > +         && !filename_in_debug_line (objfile, file_matcher))
> > > > +       return true;
> > > > +
> > > >        read_full_dwarf_from_debuginfod (objfile, this);
> > > >        return true;
> > > >      }
> > > > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> > > > index e561ec035e7..39ee56d7204 100644
> > > > --- a/gdb/dwarf2/read.c
> > > > +++ b/gdb/dwarf2/read.c
> > > > @@ -81,6 +81,7 @@
> > > >  #include "gdbsupport/gdb_optional.h"
> > > >  #include "gdbsupport/underlying.h"
> > > >  #include "gdbsupport/hash_enum.h"
> > > > +#include "gdbsupport/scoped_mmap.h"
> > > >  #include "filename-seen-cache.h"
> > > >  #include "producer.h"
> > > >  #include <fcntl.h>
> > > > @@ -2110,6 +2111,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
> > > >    return this_cu->file_names;
> > > >  }
> > > >
> > > > +#if !HAVE_SYS_MMAN_H
> > > > +
> > > > +bool
> > > > +mapped_debug_line::contains_matching_filename
> > > > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > > > +{
> > > > +  return false;
> > > > +}
> > > > +
> > > > +gdb::array_view<const gdb_byte>
> > > > +mapped_debug_line::read_debug_line_separate
> > > > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > > > +{
> > > > +  return {};
> > > > +}
> > > > +
> > > > +bool
> > > > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > > > +{
> > > > +  return false;
> > > > +}
> > > > +
> > > > +#else /* !HAVE_SYS_MMAN_H */
> > > > +
> > > > +struct line_resource_mmap final : public index_cache_resource
> > > > +{
> > > > +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> > > > +     file doesn't exist. */
> > > > +  line_resource_mmap (const char *filename)
> > > > +    : mapping (mmap_file (filename))
> > > > +  {}
> > > > +
> > > > +  scoped_mmap mapping;
> > > > +};
> > > > +
> > > > +/* See read.h.  */
> > > > +
> > > > +bool
> > > > +mapped_debug_line::contains_matching_filename
> > > > +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> > > > +{
> > > > +  for (line_header_up &lh : line_headers)
> > > > +    for (file_entry &fe : lh->file_names ())
> > > > +      {
> > > > +       const char *filename = fe.name;
> > > > +
> > > > +       if (file_matcher (fe.name, false))
> > > > +         return true;
> > > > +
> > > > +       bool basename_match = file_matcher (lbasename (fe.name), true);
> > > > +
> > > > +       if (!basenames_may_differ && !basename_match)
> > > > +         continue;
> > > > +
> > > > +       /* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> > > > +          until DWARF5.  Since we don't have access to the CU at this
> > > > +          point we just check for a partial match on the filename.
> > > > +          If there is a match, the full debuginfo will be downloaded
> > > > +          ane the match will be re-evalute with DW_AT_comp_dir.  */
> > > > +       if (lh->version < 5 && fe.d_index == 0)
> > > > +         return basename_match;
> > > > +
> > > > +       const char *dirname = fe.include_dir (&*lh);
> > > > +       std::string fullname;
> > > > +
> > > > +       if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> > > > +         fullname = filename;
> > > > +       else
> > > > +         fullname = std::string (dirname) + SLASH_STRING + filename;
> > > > +
> > > > +       gdb::unique_xmalloc_ptr<char> rewritten
> > > > +         = rewrite_source_path (fullname.c_str ());
> > > > +       if (rewritten != nullptr)
> > > > +         fullname = rewritten.release ();
> > > > +
> > > > +       if (file_matcher (fullname.c_str (), false))
> > > > +         return true;
> > > > +      }
> > > > +
> > > > +  return false;
> > > > +}
> > > > +
> > > > +/* See read.h.  */
> > > > +
> > > > +gdb::array_view<const gdb_byte>
> > > > +mapped_debug_line::read_debug_line_separate
> > > > +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> > > > +{
> > > > +  if (filename == nullptr)
> > > > +    return {};
> > > > +
> > > > +  try
> > > > +  {
> > > > +    line_resource_mmap *mmap_resource
> > > > +      = new line_resource_mmap (filename);
> > > > +
> > > > +    resource->reset (mmap_resource);
> > > > +
> > > > +    return gdb::array_view<const gdb_byte>
> > > > +      ((const gdb_byte *) mmap_resource->mapping.get (),
> > > > +       mmap_resource->mapping.size ());
> > > > +  }
> > > > +  catch (const gdb_exception &except)
> > > > +  {
> > > > +    exception_print (gdb_stderr, except);
> > > > +  }
> > > > +
> > > > +  return {};
> > > > +}
> > > > +
> > > > +/* See read.h.  */
> > > > +
> > > > +bool
> > > > +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> > > > +{
> > > > +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> > > > +  if (build_id == nullptr)
> > > > +    return false;
> > > > +
> > > > +  gdb::unique_xmalloc_ptr<char> line_path;
> > > > +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> > > > +                                               build_id->size,
> > > > +                                               bfd_get_filename
> > > > +                                                 (objfile->obfd.get ()),
> > > > +                                               ".debug_line",
> > > > +                                               &line_path);
> > > > +
> > > > +  if (line_fd.get () < 0)
> > > > +    return false;
> > > > +
> > > > +  gdb::unique_xmalloc_ptr<char> line_str_path;
> > > > +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> > > > +                                                   build_id->size,
> > > > +                                                   bfd_get_filename
> > > > +                                                     (objfile->obfd.get ()),
> > > > +                                                   ".debug_line_str",
> > > > +                                                   &line_str_path);
> > > > +
> > > > +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> > > > +  line_str_data = read_debug_line_separate (line_str_path.get (),
> > > > +                                           &line_str_resource);
> > > > +
> > > > +  const gdb_byte *line_ptr = line_data.data ();
> > > > +
> > > > +  while (line_ptr < line_data.data () + line_data.size ())
> > > > +    {
> > > > +      line_header_up lh
> > > > +       = dwarf_decode_line_header (objfile->obfd.get (),
> > > > +                                   line_data, line_str_data,
> > > > +                                   &line_ptr, false,
> > > > +                                   nullptr, nullptr);
> > > > +      line_headers.emplace_back (lh.release ());
> > > > +    }
> > > > +
> > > > +  return true;
> > > > +}
> > > > +#endif /* !HAVE_SYS_MMAN_H */
> > > > +
> > > > +mapped_debug_line::mapped_debug_line (objfile *objfile)
> > > > +{
> > > > +  if (!read_debug_line_from_debuginfod (objfile))
> > > > +    line_headers.clear ();
> > > > +}
> > > > +
> > > >  /* A helper for the "quick" functions which computes and caches the
> > > >     real path for a given file name from the line table.  */
> > > >
> > > > diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> > > > index e3131693b81..b8a8b76bde0 100644
> > > > --- a/gdb/dwarf2/read.h
> > > > +++ b/gdb/dwarf2/read.h
> > > > @@ -34,6 +34,7 @@
> > > >  #include "gdbsupport/hash_enum.h"
> > > >  #include "gdbsupport/function-view.h"
> > > >  #include "gdbsupport/packed.h"
> > > > +#include "dwarf2/line-header.h"
> > > >
> > > >  /* Hold 'maintenance (set|show) dwarf' commands.  */
> > > >  extern struct cmd_list_element *set_dwarf_cmdlist;
> > > > @@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
> > > >  extern void read_full_dwarf_from_debuginfod (struct objfile *,
> > > >                                              dwarf2_base_index_functions *);
> > > >
> > > > +struct mapped_debug_line
> > > > +{
> > > > +  mapped_debug_line (objfile *objfile);
> > > > +
> > > > +  /* Return true if any of the mapped .debug_line's filenames match
> > > > +     FILE_MATCHER.  */
> > > > +
> > > > +  bool contains_matching_filename
> > > > +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> > > > +
> > > > +private:
> > > > +  std::vector<line_header_up> line_headers;
> > > > +
> > > > +  gdb::array_view<const gdb_byte> line_data;
> > > > +  gdb::array_view<const gdb_byte> line_str_data;
> > > > +
> > > > +  std::unique_ptr<index_cache_resource> line_resource;
> > > > +  std::unique_ptr<index_cache_resource> line_str_resource;
> > > > +
> > > > +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> > > > +     and populate line_headers.  */
> > > > +
> > > > +  bool read_debug_line_from_debuginfod (objfile *objfile);
> > > > +
> > > > +  /* Initialize line_data and line_str_data with the .debug_line and
> > > > +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> > > > +
> > > > +  gdb::array_view<const gdb_byte> read_debug_line_separate
> > > > +    (char *filename, std::unique_ptr<index_cache_resource> *resource);
> > > > +};
> > > >  #endif /* DWARF2READ_H */
> > > > --
> > > > 2.39.2
> > > >
  
Andrew Burgess May 24, 2023, 10:22 a.m. UTC | #5
Aaron Merey via Gdb-patches <gdb-patches@sourceware.org> writes:

> v1 can be found here:
> https://sourceware.org/pipermail/gdb-patches/2023-February/197459.html
>
> v2 merges dwarf_decode_line_header_separate with
> dwarf_decode_line_header and read_formatted_entries_separate with
> read_formatted_entries in order to reduce code duplication.
>
> ---
>
> 'set debuginfod enabled lazy' 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 gdb can search the
> debuginfo for matching source filenames.  This can result in unnecessary
> downloading.
>
> 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 a deferred debuginfo.
> ---
>  gdb/dwarf2/line-header.c    | 215 +++++++++++++++++++++++-------------
>  gdb/dwarf2/line-header.h    |  10 ++
>  gdb/dwarf2/read-gdb-index.c |  27 +++++
>  gdb/dwarf2/read.c           | 165 +++++++++++++++++++++++++++
>  gdb/dwarf2/read.h           |  31 ++++++
>  5 files changed, 371 insertions(+), 77 deletions(-)

This needs some tests.

Thanks,
Andrew


>
> diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
> index 9d74c8fe75b..5eaff9c5a48 100644
> --- a/gdb/dwarf2/line-header.c
> +++ b/gdb/dwarf2/line-header.c
> @@ -102,50 +102,57 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
>  {
>    LONGEST length = read_initial_length (abfd, buf, bytes_read);
>  
> -  gdb_assert (cu_header->initial_length_size == 4
> -	      || cu_header->initial_length_size == 8
> -	      || cu_header->initial_length_size == 12);
> +  if (cu_header != nullptr)
> +    {
> +      gdb_assert (cu_header->initial_length_size == 4
> +		  || cu_header->initial_length_size == 8
> +		  || cu_header->initial_length_size == 12);
>  
> -  if (cu_header->initial_length_size != *bytes_read)
> -    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> +      if (cu_header->initial_length_size != *bytes_read)
> +	complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
> +    }
>  
>    *offset_size = (*bytes_read == 4) ? 4 : 8;
>    return length;
>  }
>  
> -/* Read directory or file name entry format, starting with byte of
> -   format count entries, ULEB128 pairs of entry formats, ULEB128 of
> -   entries count and the entries themselves in the described entry
> -   format.  */
> +
> +/* Like read_formatted_entries but the .debug_line and .debug_line_str
> +   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
> +   where these sections are read from separate files without necessarily
> +   having access to the entire debuginfo file they originate from.  */
>  
>  static void
> -read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
> -			const gdb_byte **bufp, struct line_header *lh,
> -			unsigned int offset_size,
> -			void (*callback) (struct line_header *lh,
> -					  const char *name,
> -					  dir_index d_index,
> -					  unsigned int mod_time,
> -					  unsigned int length))
> +read_formatted_entries
> +  (bfd *parent_bfd, const gdb_byte **line_bufp,
> +   const gdb::array_view<const gdb_byte> line_str_data,
> +   struct line_header *lh,
> +   unsigned int offset_size,
> +   void (*callback) (struct line_header *lh,
> +		     const char *name,
> +		     dir_index d_index,
> +		     unsigned int mod_time,
> +		     unsigned int length))
>  {
>    gdb_byte format_count, formati;
>    ULONGEST data_count, datai;
> -  const gdb_byte *buf = *bufp;
> +  const gdb_byte *buf = *line_bufp;
> +  const gdb_byte *str_buf = line_str_data.data ();
>    const gdb_byte *format_header_data;
>    unsigned int bytes_read;
>  
> -  format_count = read_1_byte (abfd, buf);
> +  format_count = read_1_byte (parent_bfd, buf);
>    buf += 1;
>    format_header_data = buf;
>    for (formati = 0; formati < format_count; formati++)
>      {
> -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
>        buf += bytes_read;
> -      read_unsigned_leb128 (abfd, buf, &bytes_read);
> +      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
>        buf += bytes_read;
>      }
>  
> -  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
> +  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
>    buf += bytes_read;
>    for (datai = 0; datai < data_count; datai++)
>      {
> @@ -154,10 +161,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>  
>        for (formati = 0; formati < format_count; formati++)
>  	{
> -	  ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
> +	  ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
>  	  format += bytes_read;
>  
> -	  ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
> +	  ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
>  	  format += bytes_read;
>  
>  	  gdb::optional<const char *> string;
> @@ -166,36 +173,48 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>  	  switch (form)
>  	    {
>  	    case DW_FORM_string:
> -	      string.emplace (read_direct_string (abfd, buf, &bytes_read));
> +	      string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
>  	      buf += bytes_read;
>  	      break;
>  
>  	    case DW_FORM_line_strp:
>  	      {
> -		const char *str
> -		  = per_objfile->read_line_string (buf, offset_size);
> +		if (line_str_data.empty ())
> +		  error (_("Dwarf Error: DW_FORM_line_strp used without " \
> +			   "required section"));
> +		if (line_str_data.size () <= offset_size)
> +		  error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
> +			   "of section .debug_line"));
> +
> +		ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
> +
> +		const char *str;
> +		if (str_buf[str_offset] == '\0')
> +		  str = nullptr;
> +		else
> +		  str = (const char *) (str_buf + str_offset);
>  		string.emplace (str);
>  		buf += offset_size;
> +		break;
>  	      }
> -	      break;
>  
>  	    case DW_FORM_data1:
> -	      uint.emplace (read_1_byte (abfd, buf));
> +	      uint.emplace (read_1_byte (parent_bfd, buf));
>  	      buf += 1;
>  	      break;
>  
>  	    case DW_FORM_data2:
> -	      uint.emplace (read_2_bytes (abfd, buf));
> +	      uint.emplace (read_2_bytes (parent_bfd, buf));
>  	      buf += 2;
>  	      break;
>  
>  	    case DW_FORM_data4:
> -	      uint.emplace (read_4_bytes (abfd, buf));
> +	      uint.emplace (read_4_bytes (parent_bfd, buf));
>  	      buf += 4;
>  	      break;
>  
>  	    case DW_FORM_data8:
> -	      uint.emplace (read_8_bytes (abfd, buf));
> +	      uint.emplace (read_8_bytes (parent_bfd, buf));
>  	      buf += 8;
>  	      break;
>  
> @@ -205,7 +224,7 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>  	      break;
>  
>  	    case DW_FORM_udata:
> -	      uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
> +	      uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
>  	      buf += bytes_read;
>  	      break;
>  
> @@ -244,28 +263,30 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
>        callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
>      }
>  
> -  *bufp = buf;
> +  *line_bufp = buf;
>  }
>  
>  /* See line-header.h.  */
>  
>  line_header_up
> -dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> -			   dwarf2_per_objfile *per_objfile,
> -			   struct dwarf2_section_info *section,
> -			   const struct comp_unit_head *cu_header,
> -			   const char *comp_dir)
> +dwarf_decode_line_header (bfd *parent_bfd,
> +			  gdb::array_view<const gdb_byte> line_data,
> +			  gdb::array_view<const gdb_byte> line_str_data,
> +			  const gdb_byte **debug_line_ptr,
> +			  bool is_dwz,
> +			  const struct comp_unit_head *cu_header,
> +			  const char *comp_dir)
>  {
> -  const gdb_byte *line_ptr;
> +  const gdb_byte *line_ptr, *buf;
>    unsigned int bytes_read, offset_size;
>    int i;
>    const char *cur_dir, *cur_file;
>  
> -  bfd *abfd = section->get_bfd_owner ();
> +  buf = *debug_line_ptr;
>  
>    /* Make sure that at least there's room for the total_length field.
>       That could be 12 bytes long, but we're just going to fudge that.  */
> -  if (to_underlying (sect_off) + 4 >= section->size)
> +  if (buf + 4 >= line_data.data () + line_data.size ())
>      {
>        dwarf2_statement_list_fits_in_line_number_section_complaint ();
>        return 0;
> @@ -273,62 +294,65 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>  
>    line_header_up lh (new line_header (comp_dir));
>  
> -  lh->sect_off = sect_off;
> +  lh->sect_off = (sect_offset) (buf - line_data.data ());
>    lh->offset_in_dwz = is_dwz;
>  
> -  line_ptr = section->buffer + to_underlying (sect_off);
> +  line_ptr = buf;
>  
>    /* Read in the header.  */
>    LONGEST unit_length
> -    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
> +    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
>  					      &bytes_read, &offset_size);
> -  line_ptr += bytes_read;
>  
> -  const gdb_byte *start_here = line_ptr;
> +  line_ptr += bytes_read;
>  
> -  if (line_ptr + unit_length > (section->buffer + section->size))
> +  if (line_ptr + unit_length > buf + line_data.size ())
>      {
>        dwarf2_statement_list_fits_in_line_number_section_complaint ();
>        return 0;
>      }
> +
> +  const gdb_byte *start_here = line_ptr;
> +
>    lh->statement_program_end = start_here + unit_length;
> -  lh->version = read_2_bytes (abfd, line_ptr);
> +  lh->version = read_2_bytes (parent_bfd, line_ptr);
>    line_ptr += 2;
>    if (lh->version > 5)
>      {
>        /* This is a version we don't understand.  The format could have
>  	 changed in ways we don't handle properly so just punt.  */
>        complaint (_("unsupported version in .debug_line section"));
> -      return NULL;
> +      return nullptr;
>      }
>    if (lh->version >= 5)
>      {
>        gdb_byte segment_selector_size;
>  
>        /* Skip address size.  */
> -      read_1_byte (abfd, line_ptr);
> +      read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>  
> -      segment_selector_size = read_1_byte (abfd, line_ptr);
> +      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>        if (segment_selector_size != 0)
>  	{
>  	  complaint (_("unsupported segment selector size %u "
>  		       "in .debug_line section"),
>  		     segment_selector_size);
> -	  return NULL;
> +	  return nullptr;
>  	}
>      }
>  
> -  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
> +  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
>    line_ptr += offset_size;
>    lh->statement_program_start = line_ptr + header_length;
> -  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
> +
> +  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
>  
>    if (lh->version >= 4)
>      {
> -      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
> +      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>      }
>    else
> @@ -341,41 +365,47 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>  		   "in `.debug_line' section"));
>      }
>  
> -  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
> +  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> -  lh->line_base = read_1_signed_byte (abfd, line_ptr);
> +
> +  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> -  lh->line_range = read_1_byte (abfd, line_ptr);
> +
> +  lh->line_range = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> -  lh->opcode_base = read_1_byte (abfd, line_ptr);
> +
> +  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
>    line_ptr += 1;
> +
>    lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
>  
>    lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
>    for (i = 1; i < lh->opcode_base; ++i)
>      {
> -      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
> +      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
>        line_ptr += 1;
>      }
>  
>    if (lh->version >= 5)
>      {
>        /* Read directory table.  */
> -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> -			      offset_size,
> -			      [] (struct line_header *header, const char *name,
> -				  dir_index d_index, unsigned int mod_time,
> -				  unsigned int length)
> +      read_formatted_entries
> +	(parent_bfd, &line_ptr, line_str_data,
> +	 lh.get (), offset_size,
> +	 [] (struct line_header *header, const char *name,
> +	     dir_index d_index, unsigned int mod_time,
> +	     unsigned int length)
>  	{
>  	  header->add_include_dir (name);
>  	});
>  
>        /* Read file name table.  */
> -      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
> -			      offset_size,
> -			      [] (struct line_header *header, const char *name,
> -				  dir_index d_index, unsigned int mod_time,
> -				  unsigned int length)
> +      read_formatted_entries
> +	(parent_bfd, &line_ptr, line_str_data,
> +	 lh.get (), offset_size,
> +	 [] (struct line_header *header, const char *name,
> +	     dir_index d_index, unsigned int mod_time,
> +	     unsigned int length)
>  	{
>  	  header->add_file_name (name, d_index, mod_time, length);
>  	});
> @@ -383,7 +413,7 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>    else
>      {
>        /* Read directory table.  */
> -      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> +      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
>  	{
>  	  line_ptr += bytes_read;
>  	  lh->add_include_dir (cur_dir);
> @@ -391,17 +421,17 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>        line_ptr += bytes_read;
>  
>        /* Read file name table.  */
> -      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
> +      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
>  	{
>  	  unsigned int mod_time, length;
>  	  dir_index d_index;
>  
>  	  line_ptr += bytes_read;
> -	  d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> +	  d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
>  	  line_ptr += bytes_read;
> -	  mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> +	  mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
>  	  line_ptr += bytes_read;
> -	  length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
> +	  length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
>  	  line_ptr += bytes_read;
>  
>  	  lh->add_file_name (cur_file, d_index, mod_time, length);
> @@ -409,9 +439,40 @@ dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
>        line_ptr += bytes_read;
>      }
>  
> -  if (line_ptr > (section->buffer + section->size))
> +  if (line_ptr > (buf + line_data.size ()))
>      complaint (_("line number info header doesn't "
>  		 "fit in `.debug_line' section"));
>  
> +  *debug_line_ptr += unit_length + offset_size;
>    return lh;
>  }
> +
> +line_header_up
> +dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
> +			   dwarf2_per_objfile *per_objfile,
> +			   struct dwarf2_section_info *section,
> +			   const struct comp_unit_head *cu_header,
> +			   const char *comp_dir)
> +{
> +  struct objfile *objfile = per_objfile->objfile;
> +  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
> +
> +  /* Read .debug_line.  */
> +  dwarf2_section_info *line_sec = &per_bfd->line;
> +  bfd_size_type line_size = line_sec->get_size (objfile);
> +
> +  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
> +
> +  /* Read .debug_line_str.  */
> +  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
> +  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
> +
> +  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
> +					    line_str_size);
> +
> +  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
> +
> +  return dwarf_decode_line_header
> +    (per_bfd->obfd, line, line_str, &line_ptr,
> +     is_dwz, cu_header, comp_dir);
> +}
> diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
> index 59a42e336f5..44e32828ddb 100644
> --- a/gdb/dwarf2/line-header.h
> +++ b/gdb/dwarf2/line-header.h
> @@ -217,4 +217,14 @@ extern line_header_up dwarf_decode_line_header
>     struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
>     const char *comp_dir);
>  
> +/* Like above but the .debug_line and .debug_line_str are stored in
> +   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
> +   statement program header within LINE_DATA.  */
> +
> +extern line_header_up dwarf_decode_line_header
> +  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
> +   gdb::array_view<const gdb_byte> line_str_data,
> +   const gdb_byte **debug_line_ptr, bool is_dwz,
> +  const comp_unit_head *cu_header, const char *comp_dir);
> +
>  #endif /* DWARF2_LINE_HEADER_H */
> diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
> index 895fbede7c2..fe33f23fdfa 100644
> --- a/gdb/dwarf2/read-gdb-index.c
> +++ b/gdb/dwarf2/read-gdb-index.c
> @@ -128,6 +128,9 @@ struct mapped_gdb_index final : public mapped_index_base
>    }
>  };
>  
> +struct mapped_debug_line;
> +typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
> +
>  struct dwarf2_gdb_index : public dwarf2_base_index_functions
>  {
>    /* This dumps minimal information about the index.
> @@ -179,6 +182,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions
>    /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
>       debuginfo if necessary.  */
>    struct symtab *find_last_source_symtab (struct objfile *objfile) override;
> +
> +  /* Filename information related to this .gdb_index.  */
> +  mapped_debug_line_up mdl;
> +
> +  /* Return true if any of the filenames in this .gdb_index's .debug_line
> +     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
> +  bool filename_in_debug_line
> +  (objfile *objfile,
> +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
>  };
>  
>  void
> @@ -588,6 +600,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching
>    return result;
>  }
>  
> +bool
> +dwarf2_gdb_index::filename_in_debug_line
> +  (objfile *objfile,
> +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  if (mdl == nullptr)
> +    mdl.reset (new mapped_debug_line (objfile));
> +
> +  return mdl->contains_matching_filename (file_matcher);
> +}
> +
>  bool
>  dwarf2_gdb_index::expand_symtabs_matching
>      (struct objfile *objfile,
> @@ -616,6 +639,10 @@ dwarf2_gdb_index::expand_symtabs_matching
>  	  return false;
>  	}
>  
> +      if (file_matcher != nullptr
> +	  && !filename_in_debug_line (objfile, file_matcher))
> +	return true;
> +
>        read_full_dwarf_from_debuginfod (objfile, this);
>        return true;
>      }
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index e561ec035e7..39ee56d7204 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -81,6 +81,7 @@
>  #include "gdbsupport/gdb_optional.h"
>  #include "gdbsupport/underlying.h"
>  #include "gdbsupport/hash_enum.h"
> +#include "gdbsupport/scoped_mmap.h"
>  #include "filename-seen-cache.h"
>  #include "producer.h"
>  #include <fcntl.h>
> @@ -2110,6 +2111,170 @@ dw2_get_file_names (dwarf2_per_cu_data *this_cu,
>    return this_cu->file_names;
>  }
>  
> +#if !HAVE_SYS_MMAN_H
> +
> +bool
> +mapped_debug_line::contains_matching_filename
> +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  return false;
> +}
> +
> +gdb::array_view<const gdb_byte>
> +mapped_debug_line::read_debug_line_separate
> +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> +{
> +  return {};
> +}
> +
> +bool
> +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> +{
> +  return false;
> +}
> +
> +#else /* !HAVE_SYS_MMAN_H */
> +
> +struct line_resource_mmap final : public index_cache_resource
> +{
> +  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
> +     file doesn't exist. */
> +  line_resource_mmap (const char *filename)
> +    : mapping (mmap_file (filename))
> +  {}
> +
> +  scoped_mmap mapping;
> +};
> +
> +/* See read.h.  */
> +
> +bool
> +mapped_debug_line::contains_matching_filename
> +  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
> +{
> +  for (line_header_up &lh : line_headers)
> +    for (file_entry &fe : lh->file_names ())
> +      {
> +	const char *filename = fe.name;
> +
> +	if (file_matcher (fe.name, false))
> +	  return true;
> +
> +	bool basename_match = file_matcher (lbasename (fe.name), true);
> +
> +	if (!basenames_may_differ && !basename_match)
> +	  continue;
> +
> +	/* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
> +	   until DWARF5.  Since we don't have access to the CU at this
> +	   point we just check for a partial match on the filename.
> +	   If there is a match, the full debuginfo will be downloaded
> +	   ane the match will be re-evalute with DW_AT_comp_dir.  */
> +	if (lh->version < 5 && fe.d_index == 0)
> +	  return basename_match;
> +
> +	const char *dirname = fe.include_dir (&*lh);
> +	std::string fullname;
> +
> +	if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
> +	  fullname = filename;
> +	else
> +	  fullname = std::string (dirname) + SLASH_STRING + filename;
> +
> +	gdb::unique_xmalloc_ptr<char> rewritten
> +	  = rewrite_source_path (fullname.c_str ());
> +	if (rewritten != nullptr)
> +	  fullname = rewritten.release ();
> +
> +	if (file_matcher (fullname.c_str (), false))
> +	  return true;
> +      }
> +
> +  return false;
> +}
> +
> +/* See read.h.  */
> +
> +gdb::array_view<const gdb_byte>
> +mapped_debug_line::read_debug_line_separate
> +  (char *filename, std::unique_ptr<index_cache_resource> *resource)
> +{
> +  if (filename == nullptr)
> +    return {};
> +
> +  try
> +  {
> +    line_resource_mmap *mmap_resource
> +      = new line_resource_mmap (filename);
> +
> +    resource->reset (mmap_resource);
> +
> +    return gdb::array_view<const gdb_byte>
> +      ((const gdb_byte *) mmap_resource->mapping.get (),
> +       mmap_resource->mapping.size ());
> +  }
> +  catch (const gdb_exception &except)
> +  {
> +    exception_print (gdb_stderr, except);
> +  }
> +
> +  return {};
> +}
> +
> +/* See read.h.  */
> +
> +bool
> +mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
> +{
> +  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
> +  if (build_id == nullptr)
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> line_path;
> +  scoped_fd line_fd = debuginfod_section_query (build_id->data,
> +						build_id->size,
> +						bfd_get_filename
> +						  (objfile->obfd.get ()),
> +						".debug_line",
> +						&line_path);
> +
> +  if (line_fd.get () < 0)
> +    return false;
> +
> +  gdb::unique_xmalloc_ptr<char> line_str_path;
> +  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
> +						    build_id->size,
> +						    bfd_get_filename
> +						      (objfile->obfd.get ()),
> +						    ".debug_line_str",
> +						    &line_str_path);
> +
> +  line_data = read_debug_line_separate (line_path.get (), &line_resource);
> +  line_str_data = read_debug_line_separate (line_str_path.get (),
> +					    &line_str_resource);
> +
> +  const gdb_byte *line_ptr = line_data.data ();
> +
> +  while (line_ptr < line_data.data () + line_data.size ())
> +    {
> +      line_header_up lh
> +	= dwarf_decode_line_header (objfile->obfd.get (),
> +				    line_data, line_str_data,
> +				    &line_ptr, false,
> +				    nullptr, nullptr);
> +      line_headers.emplace_back (lh.release ());
> +    }
> +
> +  return true;
> +}
> +#endif /* !HAVE_SYS_MMAN_H */
> +
> +mapped_debug_line::mapped_debug_line (objfile *objfile)
> +{
> +  if (!read_debug_line_from_debuginfod (objfile))
> +    line_headers.clear ();
> +}
> +
>  /* A helper for the "quick" functions which computes and caches the
>     real path for a given file name from the line table.  */
>  
> diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> index e3131693b81..b8a8b76bde0 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -34,6 +34,7 @@
>  #include "gdbsupport/hash_enum.h"
>  #include "gdbsupport/function-view.h"
>  #include "gdbsupport/packed.h"
> +#include "dwarf2/line-header.h"
>  
>  /* Hold 'maintenance (set|show) dwarf' commands.  */
>  extern struct cmd_list_element *set_dwarf_cmdlist;
> @@ -952,4 +953,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
>  extern void read_full_dwarf_from_debuginfod (struct objfile *,
>  					     dwarf2_base_index_functions *);
>  
> +struct mapped_debug_line
> +{
> +  mapped_debug_line (objfile *objfile);
> +
> +  /* Return true if any of the mapped .debug_line's filenames match
> +     FILE_MATCHER.  */
> +
> +  bool contains_matching_filename
> +    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
> +
> +private:
> +  std::vector<line_header_up> line_headers;
> +
> +  gdb::array_view<const gdb_byte> line_data;
> +  gdb::array_view<const gdb_byte> line_str_data;
> +
> +  std::unique_ptr<index_cache_resource> line_resource;
> +  std::unique_ptr<index_cache_resource> line_str_resource;
> +
> +  /* Download the .debug_line and .debug_line_str associated with OBJFILE
> +     and populate line_headers.  */
> +
> +  bool read_debug_line_from_debuginfod (objfile *objfile);
> +
> +  /* Initialize line_data and line_str_data with the .debug_line and
> +    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
> +
> +  gdb::array_view<const gdb_byte> read_debug_line_separate
> +    (char *filename, std::unique_ptr<index_cache_resource> *resource);
> +};
>  #endif /* DWARF2READ_H */
> -- 
> 2.39.2
  

Patch

diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c
index 9d74c8fe75b..5eaff9c5a48 100644
--- a/gdb/dwarf2/line-header.c
+++ b/gdb/dwarf2/line-header.c
@@ -102,50 +102,57 @@  read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
 {
   LONGEST length = read_initial_length (abfd, buf, bytes_read);
 
-  gdb_assert (cu_header->initial_length_size == 4
-	      || cu_header->initial_length_size == 8
-	      || cu_header->initial_length_size == 12);
+  if (cu_header != nullptr)
+    {
+      gdb_assert (cu_header->initial_length_size == 4
+		  || cu_header->initial_length_size == 8
+		  || cu_header->initial_length_size == 12);
 
-  if (cu_header->initial_length_size != *bytes_read)
-    complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
+      if (cu_header->initial_length_size != *bytes_read)
+	complaint (_("intermixed 32-bit and 64-bit DWARF sections"));
+    }
 
   *offset_size = (*bytes_read == 4) ? 4 : 8;
   return length;
 }
 
-/* Read directory or file name entry format, starting with byte of
-   format count entries, ULEB128 pairs of entry formats, ULEB128 of
-   entries count and the entries themselves in the described entry
-   format.  */
+
+/* Like read_formatted_entries but the .debug_line and .debug_line_str
+   are stored in LINE_BUFP and LINE_STR_DATA.  This is used for cases
+   where these sections are read from separate files without necessarily
+   having access to the entire debuginfo file they originate from.  */
 
 static void
-read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
-			const gdb_byte **bufp, struct line_header *lh,
-			unsigned int offset_size,
-			void (*callback) (struct line_header *lh,
-					  const char *name,
-					  dir_index d_index,
-					  unsigned int mod_time,
-					  unsigned int length))
+read_formatted_entries
+  (bfd *parent_bfd, const gdb_byte **line_bufp,
+   const gdb::array_view<const gdb_byte> line_str_data,
+   struct line_header *lh,
+   unsigned int offset_size,
+   void (*callback) (struct line_header *lh,
+		     const char *name,
+		     dir_index d_index,
+		     unsigned int mod_time,
+		     unsigned int length))
 {
   gdb_byte format_count, formati;
   ULONGEST data_count, datai;
-  const gdb_byte *buf = *bufp;
+  const gdb_byte *buf = *line_bufp;
+  const gdb_byte *str_buf = line_str_data.data ();
   const gdb_byte *format_header_data;
   unsigned int bytes_read;
 
-  format_count = read_1_byte (abfd, buf);
+  format_count = read_1_byte (parent_bfd, buf);
   buf += 1;
   format_header_data = buf;
   for (formati = 0; formati < format_count; formati++)
     {
-      read_unsigned_leb128 (abfd, buf, &bytes_read);
+      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
       buf += bytes_read;
-      read_unsigned_leb128 (abfd, buf, &bytes_read);
+      read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
       buf += bytes_read;
     }
 
-  data_count = read_unsigned_leb128 (abfd, buf, &bytes_read);
+  data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read);
   buf += bytes_read;
   for (datai = 0; datai < data_count; datai++)
     {
@@ -154,10 +161,10 @@  read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
 
       for (formati = 0; formati < format_count; formati++)
 	{
-	  ULONGEST content_type = read_unsigned_leb128 (abfd, format, &bytes_read);
+	  ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
 	  format += bytes_read;
 
-	  ULONGEST form  = read_unsigned_leb128 (abfd, format, &bytes_read);
+	  ULONGEST form  = read_unsigned_leb128 (parent_bfd, format, &bytes_read);
 	  format += bytes_read;
 
 	  gdb::optional<const char *> string;
@@ -166,36 +173,48 @@  read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
 	  switch (form)
 	    {
 	    case DW_FORM_string:
-	      string.emplace (read_direct_string (abfd, buf, &bytes_read));
+	      string.emplace (read_direct_string (parent_bfd, buf, &bytes_read));
 	      buf += bytes_read;
 	      break;
 
 	    case DW_FORM_line_strp:
 	      {
-		const char *str
-		  = per_objfile->read_line_string (buf, offset_size);
+		if (line_str_data.empty ())
+		  error (_("Dwarf Error: DW_FORM_line_strp used without " \
+			   "required section"));
+		if (line_str_data.size () <= offset_size)
+		  error (_("Dwarf Error: DW_FORM_line_strp pointing outside " \
+			   "of section .debug_line"));
+
+		ULONGEST str_offset = read_offset (parent_bfd, buf, offset_size);
+
+		const char *str;
+		if (str_buf[str_offset] == '\0')
+		  str = nullptr;
+		else
+		  str = (const char *) (str_buf + str_offset);
 		string.emplace (str);
 		buf += offset_size;
+		break;
 	      }
-	      break;
 
 	    case DW_FORM_data1:
-	      uint.emplace (read_1_byte (abfd, buf));
+	      uint.emplace (read_1_byte (parent_bfd, buf));
 	      buf += 1;
 	      break;
 
 	    case DW_FORM_data2:
-	      uint.emplace (read_2_bytes (abfd, buf));
+	      uint.emplace (read_2_bytes (parent_bfd, buf));
 	      buf += 2;
 	      break;
 
 	    case DW_FORM_data4:
-	      uint.emplace (read_4_bytes (abfd, buf));
+	      uint.emplace (read_4_bytes (parent_bfd, buf));
 	      buf += 4;
 	      break;
 
 	    case DW_FORM_data8:
-	      uint.emplace (read_8_bytes (abfd, buf));
+	      uint.emplace (read_8_bytes (parent_bfd, buf));
 	      buf += 8;
 	      break;
 
@@ -205,7 +224,7 @@  read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
 	      break;
 
 	    case DW_FORM_udata:
-	      uint.emplace (read_unsigned_leb128 (abfd, buf, &bytes_read));
+	      uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read));
 	      buf += bytes_read;
 	      break;
 
@@ -244,28 +263,30 @@  read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
       callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length);
     }
 
-  *bufp = buf;
+  *line_bufp = buf;
 }
 
 /* See line-header.h.  */
 
 line_header_up
-dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
-			   dwarf2_per_objfile *per_objfile,
-			   struct dwarf2_section_info *section,
-			   const struct comp_unit_head *cu_header,
-			   const char *comp_dir)
+dwarf_decode_line_header (bfd *parent_bfd,
+			  gdb::array_view<const gdb_byte> line_data,
+			  gdb::array_view<const gdb_byte> line_str_data,
+			  const gdb_byte **debug_line_ptr,
+			  bool is_dwz,
+			  const struct comp_unit_head *cu_header,
+			  const char *comp_dir)
 {
-  const gdb_byte *line_ptr;
+  const gdb_byte *line_ptr, *buf;
   unsigned int bytes_read, offset_size;
   int i;
   const char *cur_dir, *cur_file;
 
-  bfd *abfd = section->get_bfd_owner ();
+  buf = *debug_line_ptr;
 
   /* Make sure that at least there's room for the total_length field.
      That could be 12 bytes long, but we're just going to fudge that.  */
-  if (to_underlying (sect_off) + 4 >= section->size)
+  if (buf + 4 >= line_data.data () + line_data.size ())
     {
       dwarf2_statement_list_fits_in_line_number_section_complaint ();
       return 0;
@@ -273,62 +294,65 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
 
   line_header_up lh (new line_header (comp_dir));
 
-  lh->sect_off = sect_off;
+  lh->sect_off = (sect_offset) (buf - line_data.data ());
   lh->offset_in_dwz = is_dwz;
 
-  line_ptr = section->buffer + to_underlying (sect_off);
+  line_ptr = buf;
 
   /* Read in the header.  */
   LONGEST unit_length
-    = read_checked_initial_length_and_offset (abfd, line_ptr, cu_header,
+    = read_checked_initial_length_and_offset (parent_bfd, buf, cu_header,
 					      &bytes_read, &offset_size);
-  line_ptr += bytes_read;
 
-  const gdb_byte *start_here = line_ptr;
+  line_ptr += bytes_read;
 
-  if (line_ptr + unit_length > (section->buffer + section->size))
+  if (line_ptr + unit_length > buf + line_data.size ())
     {
       dwarf2_statement_list_fits_in_line_number_section_complaint ();
       return 0;
     }
+
+  const gdb_byte *start_here = line_ptr;
+
   lh->statement_program_end = start_here + unit_length;
-  lh->version = read_2_bytes (abfd, line_ptr);
+  lh->version = read_2_bytes (parent_bfd, line_ptr);
   line_ptr += 2;
   if (lh->version > 5)
     {
       /* This is a version we don't understand.  The format could have
 	 changed in ways we don't handle properly so just punt.  */
       complaint (_("unsupported version in .debug_line section"));
-      return NULL;
+      return nullptr;
     }
   if (lh->version >= 5)
     {
       gdb_byte segment_selector_size;
 
       /* Skip address size.  */
-      read_1_byte (abfd, line_ptr);
+      read_1_byte (parent_bfd, line_ptr);
       line_ptr += 1;
 
-      segment_selector_size = read_1_byte (abfd, line_ptr);
+      segment_selector_size = read_1_byte (parent_bfd, line_ptr);
       line_ptr += 1;
       if (segment_selector_size != 0)
 	{
 	  complaint (_("unsupported segment selector size %u "
 		       "in .debug_line section"),
 		     segment_selector_size);
-	  return NULL;
+	  return nullptr;
 	}
     }
 
-  LONGEST header_length = read_offset (abfd, line_ptr, offset_size);
+  LONGEST header_length = read_offset (parent_bfd, line_ptr, offset_size);
   line_ptr += offset_size;
   lh->statement_program_start = line_ptr + header_length;
-  lh->minimum_instruction_length = read_1_byte (abfd, line_ptr);
+
+  lh->minimum_instruction_length = read_1_byte (parent_bfd, line_ptr);
   line_ptr += 1;
 
   if (lh->version >= 4)
     {
-      lh->maximum_ops_per_instruction = read_1_byte (abfd, line_ptr);
+      lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr);
       line_ptr += 1;
     }
   else
@@ -341,41 +365,47 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
 		   "in `.debug_line' section"));
     }
 
-  lh->default_is_stmt = read_1_byte (abfd, line_ptr);
+  lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr);
   line_ptr += 1;
-  lh->line_base = read_1_signed_byte (abfd, line_ptr);
+
+  lh->line_base = read_1_signed_byte (parent_bfd, line_ptr);
   line_ptr += 1;
-  lh->line_range = read_1_byte (abfd, line_ptr);
+
+  lh->line_range = read_1_byte (parent_bfd, line_ptr);
   line_ptr += 1;
-  lh->opcode_base = read_1_byte (abfd, line_ptr);
+
+  lh->opcode_base = read_1_byte (parent_bfd, line_ptr);
   line_ptr += 1;
+
   lh->standard_opcode_lengths.reset (new unsigned char[lh->opcode_base]);
 
   lh->standard_opcode_lengths[0] = 1;  /* This should never be used anyway.  */
   for (i = 1; i < lh->opcode_base; ++i)
     {
-      lh->standard_opcode_lengths[i] = read_1_byte (abfd, line_ptr);
+      lh->standard_opcode_lengths[i] = read_1_byte (parent_bfd, line_ptr);
       line_ptr += 1;
     }
 
   if (lh->version >= 5)
     {
       /* Read directory table.  */
-      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
-			      offset_size,
-			      [] (struct line_header *header, const char *name,
-				  dir_index d_index, unsigned int mod_time,
-				  unsigned int length)
+      read_formatted_entries
+	(parent_bfd, &line_ptr, line_str_data,
+	 lh.get (), offset_size,
+	 [] (struct line_header *header, const char *name,
+	     dir_index d_index, unsigned int mod_time,
+	     unsigned int length)
 	{
 	  header->add_include_dir (name);
 	});
 
       /* Read file name table.  */
-      read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
-			      offset_size,
-			      [] (struct line_header *header, const char *name,
-				  dir_index d_index, unsigned int mod_time,
-				  unsigned int length)
+      read_formatted_entries
+	(parent_bfd, &line_ptr, line_str_data,
+	 lh.get (), offset_size,
+	 [] (struct line_header *header, const char *name,
+	     dir_index d_index, unsigned int mod_time,
+	     unsigned int length)
 	{
 	  header->add_file_name (name, d_index, mod_time, length);
 	});
@@ -383,7 +413,7 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
   else
     {
       /* Read directory table.  */
-      while ((cur_dir = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
+      while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
 	{
 	  line_ptr += bytes_read;
 	  lh->add_include_dir (cur_dir);
@@ -391,17 +421,17 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
       line_ptr += bytes_read;
 
       /* Read file name table.  */
-      while ((cur_file = read_direct_string (abfd, line_ptr, &bytes_read)) != NULL)
+      while ((cur_file = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr)
 	{
 	  unsigned int mod_time, length;
 	  dir_index d_index;
 
 	  line_ptr += bytes_read;
-	  d_index = (dir_index) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+	  d_index = (dir_index) read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
 	  line_ptr += bytes_read;
-	  mod_time = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+	  mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
 	  line_ptr += bytes_read;
-	  length = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+	  length = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read);
 	  line_ptr += bytes_read;
 
 	  lh->add_file_name (cur_file, d_index, mod_time, length);
@@ -409,9 +439,40 @@  dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
       line_ptr += bytes_read;
     }
 
-  if (line_ptr > (section->buffer + section->size))
+  if (line_ptr > (buf + line_data.size ()))
     complaint (_("line number info header doesn't "
 		 "fit in `.debug_line' section"));
 
+  *debug_line_ptr += unit_length + offset_size;
   return lh;
 }
+
+line_header_up
+dwarf_decode_line_header  (sect_offset sect_off, bool is_dwz,
+			   dwarf2_per_objfile *per_objfile,
+			   struct dwarf2_section_info *section,
+			   const struct comp_unit_head *cu_header,
+			   const char *comp_dir)
+{
+  struct objfile *objfile = per_objfile->objfile;
+  struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
+
+  /* Read .debug_line.  */
+  dwarf2_section_info *line_sec = &per_bfd->line;
+  bfd_size_type line_size = line_sec->get_size (objfile);
+
+  gdb::array_view<const gdb_byte> line (line_sec->buffer, line_size);
+
+  /* Read .debug_line_str.  */
+  dwarf2_section_info *line_str_sec = &per_bfd->line_str;
+  bfd_size_type line_str_size = line_str_sec->get_size (objfile);
+
+  gdb::array_view<const gdb_byte> line_str (line_str_sec->buffer,
+					    line_str_size);
+
+  const gdb_byte *line_ptr = line.data () + to_underlying (sect_off);
+
+  return dwarf_decode_line_header
+    (per_bfd->obfd, line, line_str, &line_ptr,
+     is_dwz, cu_header, comp_dir);
+}
diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
index 59a42e336f5..44e32828ddb 100644
--- a/gdb/dwarf2/line-header.h
+++ b/gdb/dwarf2/line-header.h
@@ -217,4 +217,14 @@  extern line_header_up dwarf_decode_line_header
    struct dwarf2_section_info *section, const struct comp_unit_head *cu_header,
    const char *comp_dir);
 
+/* Like above but the .debug_line and .debug_line_str are stored in
+   LINE_DATA and LINE_STR_DATA. *DEBUG_LINE_PTR should point to a
+   statement program header within LINE_DATA.  */
+
+extern line_header_up dwarf_decode_line_header
+  (bfd *parent_bfd, gdb::array_view<const gdb_byte> line_data,
+   gdb::array_view<const gdb_byte> line_str_data,
+   const gdb_byte **debug_line_ptr, bool is_dwz,
+  const comp_unit_head *cu_header, const char *comp_dir);
+
 #endif /* DWARF2_LINE_HEADER_H */
diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c
index 895fbede7c2..fe33f23fdfa 100644
--- a/gdb/dwarf2/read-gdb-index.c
+++ b/gdb/dwarf2/read-gdb-index.c
@@ -128,6 +128,9 @@  struct mapped_gdb_index final : public mapped_index_base
   }
 };
 
+struct mapped_debug_line;
+typedef std::unique_ptr<mapped_debug_line> mapped_debug_line_up;
+
 struct dwarf2_gdb_index : public dwarf2_base_index_functions
 {
   /* This dumps minimal information about the index.
@@ -179,6 +182,15 @@  struct dwarf2_gdb_index : public dwarf2_base_index_functions
   /* Calls dwarf2_base_index_functions::find_last_source_symtab and downloads
      debuginfo if necessary.  */
   struct symtab *find_last_source_symtab (struct objfile *objfile) override;
+
+  /* Filename information related to this .gdb_index.  */
+  mapped_debug_line_up mdl;
+
+  /* Return true if any of the filenames in this .gdb_index's .debug_line
+     mapping match FILE_MATCHER.  Initializes the mapping if necessary.  */
+  bool filename_in_debug_line
+  (objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
 };
 
 void
@@ -588,6 +600,17 @@  dwarf2_gdb_index::do_expand_symtabs_matching
   return result;
 }
 
+bool
+dwarf2_gdb_index::filename_in_debug_line
+  (objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  if (mdl == nullptr)
+    mdl.reset (new mapped_debug_line (objfile));
+
+  return mdl->contains_matching_filename (file_matcher);
+}
+
 bool
 dwarf2_gdb_index::expand_symtabs_matching
     (struct objfile *objfile,
@@ -616,6 +639,10 @@  dwarf2_gdb_index::expand_symtabs_matching
 	  return false;
 	}
 
+      if (file_matcher != nullptr
+	  && !filename_in_debug_line (objfile, file_matcher))
+	return true;
+
       read_full_dwarf_from_debuginfod (objfile, this);
       return true;
     }
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index e561ec035e7..39ee56d7204 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -81,6 +81,7 @@ 
 #include "gdbsupport/gdb_optional.h"
 #include "gdbsupport/underlying.h"
 #include "gdbsupport/hash_enum.h"
+#include "gdbsupport/scoped_mmap.h"
 #include "filename-seen-cache.h"
 #include "producer.h"
 #include <fcntl.h>
@@ -2110,6 +2111,170 @@  dw2_get_file_names (dwarf2_per_cu_data *this_cu,
   return this_cu->file_names;
 }
 
+#if !HAVE_SYS_MMAN_H
+
+bool
+mapped_debug_line::contains_matching_filename
+  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  return false;
+}
+
+gdb::array_view<const gdb_byte>
+mapped_debug_line::read_debug_line_separate
+  (char *filename, std::unique_ptr<index_cache_resource> *resource)
+{
+  return {};
+}
+
+bool
+mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
+{
+  return false;
+}
+
+#else /* !HAVE_SYS_MMAN_H */
+
+struct line_resource_mmap final : public index_cache_resource
+{
+  /* Try to mmap FILENAME.  Throw an exception on failure, including if the
+     file doesn't exist. */
+  line_resource_mmap (const char *filename)
+    : mapping (mmap_file (filename))
+  {}
+
+  scoped_mmap mapping;
+};
+
+/* See read.h.  */
+
+bool
+mapped_debug_line::contains_matching_filename
+  (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher)
+{
+  for (line_header_up &lh : line_headers)
+    for (file_entry &fe : lh->file_names ())
+      {
+	const char *filename = fe.name;
+
+	if (file_matcher (fe.name, false))
+	  return true;
+
+	bool basename_match = file_matcher (lbasename (fe.name), true);
+
+	if (!basenames_may_differ && !basename_match)
+	  continue;
+
+	/* DW_AT_comp_dir is not explicitly mentioned in the .debug_line
+	   until DWARF5.  Since we don't have access to the CU at this
+	   point we just check for a partial match on the filename.
+	   If there is a match, the full debuginfo will be downloaded
+	   ane the match will be re-evalute with DW_AT_comp_dir.  */
+	if (lh->version < 5 && fe.d_index == 0)
+	  return basename_match;
+
+	const char *dirname = fe.include_dir (&*lh);
+	std::string fullname;
+
+	if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
+	  fullname = filename;
+	else
+	  fullname = std::string (dirname) + SLASH_STRING + filename;
+
+	gdb::unique_xmalloc_ptr<char> rewritten
+	  = rewrite_source_path (fullname.c_str ());
+	if (rewritten != nullptr)
+	  fullname = rewritten.release ();
+
+	if (file_matcher (fullname.c_str (), false))
+	  return true;
+      }
+
+  return false;
+}
+
+/* See read.h.  */
+
+gdb::array_view<const gdb_byte>
+mapped_debug_line::read_debug_line_separate
+  (char *filename, std::unique_ptr<index_cache_resource> *resource)
+{
+  if (filename == nullptr)
+    return {};
+
+  try
+  {
+    line_resource_mmap *mmap_resource
+      = new line_resource_mmap (filename);
+
+    resource->reset (mmap_resource);
+
+    return gdb::array_view<const gdb_byte>
+      ((const gdb_byte *) mmap_resource->mapping.get (),
+       mmap_resource->mapping.size ());
+  }
+  catch (const gdb_exception &except)
+  {
+    exception_print (gdb_stderr, except);
+  }
+
+  return {};
+}
+
+/* See read.h.  */
+
+bool
+mapped_debug_line::read_debug_line_from_debuginfod (objfile *objfile)
+{
+  const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ());
+  if (build_id == nullptr)
+    return false;
+
+  gdb::unique_xmalloc_ptr<char> line_path;
+  scoped_fd line_fd = debuginfod_section_query (build_id->data,
+						build_id->size,
+						bfd_get_filename
+						  (objfile->obfd.get ()),
+						".debug_line",
+						&line_path);
+
+  if (line_fd.get () < 0)
+    return false;
+
+  gdb::unique_xmalloc_ptr<char> line_str_path;
+  scoped_fd line_str_fd = debuginfod_section_query (build_id->data,
+						    build_id->size,
+						    bfd_get_filename
+						      (objfile->obfd.get ()),
+						    ".debug_line_str",
+						    &line_str_path);
+
+  line_data = read_debug_line_separate (line_path.get (), &line_resource);
+  line_str_data = read_debug_line_separate (line_str_path.get (),
+					    &line_str_resource);
+
+  const gdb_byte *line_ptr = line_data.data ();
+
+  while (line_ptr < line_data.data () + line_data.size ())
+    {
+      line_header_up lh
+	= dwarf_decode_line_header (objfile->obfd.get (),
+				    line_data, line_str_data,
+				    &line_ptr, false,
+				    nullptr, nullptr);
+      line_headers.emplace_back (lh.release ());
+    }
+
+  return true;
+}
+#endif /* !HAVE_SYS_MMAN_H */
+
+mapped_debug_line::mapped_debug_line (objfile *objfile)
+{
+  if (!read_debug_line_from_debuginfod (objfile))
+    line_headers.clear ();
+}
+
 /* A helper for the "quick" functions which computes and caches the
    real path for a given file name from the line table.  */
 
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index e3131693b81..b8a8b76bde0 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -34,6 +34,7 @@ 
 #include "gdbsupport/hash_enum.h"
 #include "gdbsupport/function-view.h"
 #include "gdbsupport/packed.h"
+#include "dwarf2/line-header.h"
 
 /* Hold 'maintenance (set|show) dwarf' commands.  */
 extern struct cmd_list_element *set_dwarf_cmdlist;
@@ -952,4 +953,34 @@  extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
 extern void read_full_dwarf_from_debuginfod (struct objfile *,
 					     dwarf2_base_index_functions *);
 
+struct mapped_debug_line
+{
+  mapped_debug_line (objfile *objfile);
+
+  /* Return true if any of the mapped .debug_line's filenames match
+     FILE_MATCHER.  */
+
+  bool contains_matching_filename
+    (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher);
+
+private:
+  std::vector<line_header_up> line_headers;
+
+  gdb::array_view<const gdb_byte> line_data;
+  gdb::array_view<const gdb_byte> line_str_data;
+
+  std::unique_ptr<index_cache_resource> line_resource;
+  std::unique_ptr<index_cache_resource> line_str_resource;
+
+  /* Download the .debug_line and .debug_line_str associated with OBJFILE
+     and populate line_headers.  */
+
+  bool read_debug_line_from_debuginfod (objfile *objfile);
+
+  /* Initialize line_data and line_str_data with the .debug_line and
+    .debug_line_str downloaded read_debug_line_from_debuginfod.  */
+
+  gdb::array_view<const gdb_byte> read_debug_line_separate
+    (char *filename, std::unique_ptr<index_cache_resource> *resource);
+};
 #endif /* DWARF2READ_H */