From patchwork Mon Feb 27 19:42:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Merey X-Patchwork-Id: 65697 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 549CA3858D3C for ; Mon, 27 Feb 2023 19:43:45 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 549CA3858D3C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1677527025; bh=c0U6K46Dms5IHvgnQbm1jv2YR/lYMT0GNB4M3G+toaM=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=WvcCPW69bBrxguGGIKEHuprnocRQRCf7GghqMjyVf0rt3DlnXu6uPY3rT3mDlmbh3 kke04k9gXMdDtpzuUUAJu7Ft/SiXVbpFT8b+ETi9QVIKCTYlKUoXsfMITfDqfFNjzG A55bd6kT2hNbEhgytLiWAoq966up1CZicjhtp9rs= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 17FDC3858C54 for ; Mon, 27 Feb 2023 19:42:18 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 17FDC3858C54 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-281-8XQsCKhfPM2JvA33c3IdOg-1; Mon, 27 Feb 2023 14:42:16 -0500 X-MC-Unique: 8XQsCKhfPM2JvA33c3IdOg-1 Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.rdu2.redhat.com [10.11.54.9]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 55CD1101A521 for ; Mon, 27 Feb 2023 19:42:16 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.22.18.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 258F1492C3E; Mon, 27 Feb 2023 19:42:16 +0000 (UTC) To: gdb-patches@sourceware.org Cc: Aaron Merey Subject: [PATCH 7/7] gdb/debuginfod: Add .debug_line downloading Date: Mon, 27 Feb 2023 14:42:12 -0500 Message-Id: <20230227194212.348003-7-amerey@redhat.com> In-Reply-To: <20230227194212.348003-1-amerey@redhat.com> References: <20230227194212.348003-1-amerey@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.9 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Aaron Merey via Gdb-patches From: Aaron Merey Reply-To: Aaron Merey Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" '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. 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 is saved by checking these sections before choosing to download a deferred debuginfo. This patch adds functions read_formatted_entries_separate and dwarf_decode_line_header_separate. They are similar to read_formatted_entries and dwarf_decode_line_header except that they are able to work with .debug_line sections originating from separately downloaded files. dwarf2_gdb_index::expand_symtabs_matching initiates the downloading and checking of an index's associated .debug_line when there is a filename argument that must be matched. The .debug_line information is managed by the new structs line_resource_mmap and mapped_debug_line. --- gdb/dwarf2/line-header.c | 332 ++++++++++++++++++++++++++++++++++++ gdb/dwarf2/line-header.h | 10 ++ gdb/dwarf2/read-gdb-index.c | 26 +++ gdb/dwarf2/read.c | 164 ++++++++++++++++++ gdb/dwarf2/read.h | 31 ++++ 5 files changed, 563 insertions(+) diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c index 9d74c8fe75b..7c51fb5bb12 100644 --- a/gdb/dwarf2/line-header.c +++ b/gdb/dwarf2/line-header.c @@ -113,6 +113,156 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf, return length; } + +/* 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_separate + (bfd *parent_bfd, const gdb_byte **line_bufp, + const gdb::array_view 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 = *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 (parent_bfd, buf); + buf += 1; + format_header_data = buf; + for (formati = 0; formati < format_count; formati++) + { + read_unsigned_leb128 (parent_bfd, buf, &bytes_read); + buf += bytes_read; + read_unsigned_leb128 (parent_bfd, buf, &bytes_read); + buf += bytes_read; + } + + data_count = read_unsigned_leb128 (parent_bfd, buf, &bytes_read); + buf += bytes_read; + for (datai = 0; datai < data_count; datai++) + { + const gdb_byte *format = format_header_data; + struct file_entry fe; + + for (formati = 0; formati < format_count; formati++) + { + ULONGEST content_type = read_unsigned_leb128 (parent_bfd, format, &bytes_read); + format += bytes_read; + + ULONGEST form = read_unsigned_leb128 (parent_bfd, format, &bytes_read); + format += bytes_read; + + gdb::optional string; + gdb::optional uint; + + switch (form) + { + case DW_FORM_string: + string.emplace (read_direct_string (parent_bfd, buf, &bytes_read)); + buf += bytes_read; + break; + + case DW_FORM_line_strp: + { + 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; + } + + case DW_FORM_data1: + uint.emplace (read_1_byte (parent_bfd, buf)); + buf += 1; + break; + + case DW_FORM_data2: + uint.emplace (read_2_bytes (parent_bfd, buf)); + buf += 2; + break; + + case DW_FORM_data4: + uint.emplace (read_4_bytes (parent_bfd, buf)); + buf += 4; + break; + + case DW_FORM_data8: + uint.emplace (read_8_bytes (parent_bfd, buf)); + buf += 8; + break; + + case DW_FORM_data16: + /* This is used for MD5, but file_entry does not record MD5s. */ + buf += 16; + break; + + case DW_FORM_udata: + uint.emplace (read_unsigned_leb128 (parent_bfd, buf, &bytes_read)); + buf += bytes_read; + break; + + case DW_FORM_block: + /* It is valid only for DW_LNCT_timestamp which is ignored by + current GDB. */ + break; + } + + switch (content_type) + { + case DW_LNCT_path: + if (string.has_value ()) + fe.name = *string; + break; + case DW_LNCT_directory_index: + if (uint.has_value ()) + fe.d_index = (dir_index) *uint; + break; + case DW_LNCT_timestamp: + if (uint.has_value ()) + fe.mod_time = *uint; + break; + case DW_LNCT_size: + if (uint.has_value ()) + fe.length = *uint; + break; + case DW_LNCT_MD5: + break; + default: + complaint (_("Unknown format content type %s"), + pulongest (content_type)); + } + } + + callback (lh, fe.name, fe.d_index, fe.mod_time, fe.length); + } + + *line_bufp = buf; +} + /* Read directory or file name entry format, starting with byte of format count entries, ULEB128 pairs of entry formats, ULEB128 of entries count and the entries themselves in the described entry @@ -247,6 +397,188 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd, *bufp = buf; } +/* See line-header.h. */ + +line_header_up +dwarf_decode_line_header_separate (bfd *parent_bfd, + gdb::array_view + line_data, + gdb::array_view + line_str_data, + const gdb_byte **debug_line_ptr) +{ + const gdb_byte *line_ptr, *buf; + unsigned int bytes_read, offset_size; + int i; + const char *cur_dir, *cur_file; + + 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 (buf + 4 >= line_data.data () + line_data.size ()) + { + dwarf2_statement_list_fits_in_line_number_section_complaint (); // need to print + return 0; + } + + /* We don't have access to the CU DIE yet so we don't know the comp_dir. */ + line_header_up lh (new line_header (nullptr)); + + lh->sect_off = (sect_offset) (buf - line_data.data ()); + lh->offset_in_dwz = false; + + line_ptr = buf; + + /* Read in the header. */ + + LONGEST unit_length = read_initial_length (parent_bfd, buf, &bytes_read); + offset_size = (bytes_read == 4) ? 4 : 8; + + line_ptr += bytes_read; + + 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 (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 nullptr; + } + if (lh->version >= 5) + { + gdb_byte segment_selector_size; + + /* Skip address size. */ + read_1_byte (parent_bfd, line_ptr); + line_ptr += 1; + + 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 nullptr; + } + } + + 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 (parent_bfd, line_ptr); + line_ptr += 1; + + if (lh->version >= 4) + { + lh->maximum_ops_per_instruction = read_1_byte (parent_bfd, line_ptr); + line_ptr += 1; + } + else + lh->maximum_ops_per_instruction = 1; + + if (lh->maximum_ops_per_instruction == 0) + { + lh->maximum_ops_per_instruction = 1; + complaint (_("invalid maximum_ops_per_instruction " + "in `.debug_line' section")); + } + + lh->default_is_stmt = read_1_byte (parent_bfd, line_ptr); + line_ptr += 1; + + lh->line_base = read_1_signed_byte (parent_bfd, line_ptr); + line_ptr += 1; + + lh->line_range = read_1_byte (parent_bfd, line_ptr); + line_ptr += 1; + + 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 (parent_bfd, line_ptr); + line_ptr += 1; + } + + if (lh->version >= 5) + { + /* Read directory table. */ + read_formatted_entries_separate + (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_separate + (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); + }); + } + else + { + /* Read directory table. */ + while ((cur_dir = read_direct_string (parent_bfd, line_ptr, &bytes_read)) != nullptr) + { + line_ptr += bytes_read; + lh->add_include_dir (cur_dir); + } + line_ptr += bytes_read; + + /* Read file name table. */ + 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 (parent_bfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + mod_time = read_unsigned_leb128 (parent_bfd, line_ptr, &bytes_read); + 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); + } + line_ptr += bytes_read; + } + + 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; +} + + /* See line-header.h. */ line_header_up diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h index 59a42e336f5..18af953343c 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 dwarf_decode_line_header but the .debug_line and .debug_line_str + are stored in LINE_DATA and LINE_STR_DATA. This is used when these + sections are read from separate files without necessarily having + access to the entire debuginfo file they originate from. */ + +extern line_header_up dwarf_decode_line_header_separate + (bfd *parent_bfd, gdb::array_view line_data, + gdb::array_view line_str_data, + const gdb_byte **debug_line_ptr); + #endif /* DWARF2_LINE_HEADER_H */ diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c index f066d55bd1a..15f7d79e2d3 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_up; + struct dwarf2_gdb_index : public dwarf2_base_index_functions { /* This dumps minimal information about the index. @@ -165,6 +168,15 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions block_search_flags search_flags, domain_enum domain, enum search_domain kind); + + /* 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 file_matcher); }; /* This dumps minimal information about the index. @@ -517,6 +529,17 @@ dwarf2_gdb_index::do_expand_symtabs_matching return result; } +bool +dwarf2_gdb_index::filename_in_debug_line + (objfile *objfile, + gdb::function_view 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, @@ -547,6 +570,9 @@ dwarf2_gdb_index::expand_symtabs_matching /* Objfile is a stub holding only index information. Try to reinitialize objfile with the full debuginfo. */ + if (file_matcher != nullptr + && !filename_in_debug_line (objfile, file_matcher)) + return true; if (!read_full_dwarf_from_debuginfod (objfile)) return false; return do_expand_symtabs_matching (objfile, file_matcher, lookup_name, diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 12b0dbd1ee8..8bcdf9824cc 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 @@ -2103,6 +2104,169 @@ 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 file_matcher) +{ + return false; +} + +gdb::array_view +mapped_debug_line::read_debug_line_separate + (char *filename, std::unique_ptr *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 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 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 +mapped_debug_line::read_debug_line_separate + (char *filename, std::unique_ptr *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 *) 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 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); + + gdb::unique_xmalloc_ptr 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); + + if (line_fd.get () < 0) + return false; + + 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_separate (objfile->obfd.get (), + line_data, line_str_data, + &line_ptr); + 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 4e6c35cba5e..63d804f7b44 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; @@ -951,4 +952,34 @@ extern bool read_addrmap_from_aranges (dwarf2_per_objfile *per_objfile, extern bool read_full_dwarf_from_debuginfod (struct objfile *objfile); +struct mapped_debug_line +{ + mapped_debug_line (objfile *objfile); + + /* Return true if any of the mapped .debug_line's filenames match + FILE_MATCHER. */ + + bool contains_matching_filename + (gdb::function_view file_matcher); + +private: + std::vector line_headers; + + gdb::array_view line_data; + gdb::array_view line_str_data; + + std::unique_ptr line_resource; + std::unique_ptr 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 read_debug_line_separate + (char *filename, std::unique_ptr *resource); +}; #endif /* DWARF2READ_H */